From 785b353773deea3ae19633eab56b48a9388603de Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sun, 21 Jan 2024 20:21:12 +0900 Subject: [PATCH 01/25] Add NacosClient and NacosEndpointGroup --- nacos/build.gradle.kts | 4 + .../client/nacos/NacosEndpointGroup.java | 167 +++++++++++++ .../nacos/NacosEndpointGroupBuilder.java | 203 ++++++++++++++++ .../armeria/client/nacos/package-info.java | 25 ++ .../common/nacos/NacosConfigSetters.java | 43 ++++ .../armeria/common/nacos/package-info.java | 25 ++ .../armeria/internal/nacos/LoginClient.java | 130 ++++++++++ .../armeria/internal/nacos/NacosClient.java | 123 ++++++++++ .../internal/nacos/NacosClientBuilder.java | 77 ++++++ .../internal/nacos/NacosClientUtil.java | 83 +++++++ .../internal/nacos/QueryInstancesClient.java | 222 ++++++++++++++++++ .../nacos/RegisterInstanceClient.java | 117 +++++++++ .../armeria/internal/nacos/package-info.java | 23 ++ settings.gradle | 1 + 14 files changed, 1243 insertions(+) create mode 100644 nacos/build.gradle.kts create mode 100644 nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/client/nacos/package-info.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/common/nacos/package-info.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/internal/nacos/package-info.java diff --git a/nacos/build.gradle.kts b/nacos/build.gradle.kts new file mode 100644 index 00000000000..7a445eafc48 --- /dev/null +++ b/nacos/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + // Caffeine + implementation(libs.caffeine) +} \ No newline at end of file diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java new file mode 100644 index 00000000000..a321b121a23 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java @@ -0,0 +1,167 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.client.nacos; + +import static java.util.Objects.requireNonNull; + +import java.net.URI; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +import com.linecorp.armeria.client.ClientRequestContextCaptor; +import com.linecorp.armeria.client.Clients; +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.client.endpoint.DynamicEndpointGroup; +import com.linecorp.armeria.client.endpoint.EndpointGroup; +import com.linecorp.armeria.client.endpoint.EndpointSelectionStrategy; +import com.linecorp.armeria.common.annotation.Nullable; +import com.linecorp.armeria.internal.nacos.NacosClient; + +import io.netty.channel.EventLoop; + +/** + * A Nacos-based {@link EndpointGroup} implementation that retrieves the list of {@link Endpoint} from Nacos + * using Nacos's HTTP Open API + * and updates the {@link Endpoint}s periodically. + */ +public class NacosEndpointGroup extends DynamicEndpointGroup { + private static final Logger logger = LoggerFactory.getLogger(NacosEndpointGroup.class); + + /** + * Returns a {@link NacosEndpointGroup} with the specified {@code serviceName}. + * + * @param nacosUri the URI of Nacos API service + * @param serviceName the service name to register + */ + public static NacosEndpointGroup of(URI nacosUri, String serviceName) { + return builder(nacosUri, serviceName).build(); + } + + /** + * Returns a newly-created {@link NacosEndpointGroupBuilder} with the specified {@code nacosUri} + * and {@code serviceName} to build {@link NacosEndpointGroupBuilder}. + * + * @param nacosUri the URI of Nacos API service + * @param serviceName the service name to register + */ + public static NacosEndpointGroupBuilder builder(URI nacosUri, String serviceName) { + return new NacosEndpointGroupBuilder(nacosUri, serviceName); + } + + private final NacosClient nacosClient; + + private final String serviceName; + + private final long registryFetchIntervalMillis; + + @Nullable + private final String namespaceId; + + @Nullable + private final String groupName; + + @Nullable + private final String clusterName; + + @Nullable + private final String app; + + private final boolean useHealthyEndpoints; + + @Nullable + private volatile ScheduledFuture scheduledFuture; + + NacosEndpointGroup(EndpointSelectionStrategy selectionStrategy, boolean allowEmptyEndpoints, + long selectionTimeoutMillis, NacosClient nacosClient, String serviceName, + long registryFetchIntervalMillis, @Nullable String namespaceId, + @Nullable String groupName, @Nullable String clusterName, + @Nullable String app, boolean useHealthyEndpoints) { + super(selectionStrategy, allowEmptyEndpoints, selectionTimeoutMillis); + this.nacosClient = requireNonNull(nacosClient, "nacosClient"); + this.serviceName = requireNonNull(serviceName, "serviceName"); + this.registryFetchIntervalMillis = registryFetchIntervalMillis; + this.namespaceId = namespaceId; + this.groupName = groupName; + this.clusterName = clusterName; + this.app = app; + this.useHealthyEndpoints = useHealthyEndpoints; + + update(); + } + + private void update() { + if (isClosing()) { + return; + } + + final CompletableFuture> response; + final EventLoop eventLoop; + + try (ClientRequestContextCaptor captor = Clients.newContextCaptor()) { + response = nacosClient.endpoints(serviceName, namespaceId, groupName, clusterName, + useHealthyEndpoints, app); + eventLoop = captor.get().eventLoop().withoutContext(); + } + + response.handle((endpoints, cause) -> { + if (isClosing()) { + return null; + } + if (cause != null) { + logger.warn("Unexpected exception while fetching the registry from: {}" + + " (serviceName: {})", nacosClient.uri(), serviceName, cause); + } else { + setEndpoints(endpoints); + } + + scheduledFuture = eventLoop.schedule(this::update, + registryFetchIntervalMillis, TimeUnit.MILLISECONDS); + return null; + }); + } + + @Override + protected void doCloseAsync(CompletableFuture future) { + final ScheduledFuture scheduledFuture = this.scheduledFuture; + if (scheduledFuture != null) { + scheduledFuture.cancel(true); + } + future.complete(null); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .omitNullValues() + .add("serviceName", serviceName) + .add("registryFetchIntervalMillis", registryFetchIntervalMillis) + .add("namespaceId", namespaceId) + .add("groupName", groupName) + .add("clusterName", clusterName) + .add("app", app) + .add("useHealthyEndpoints", useHealthyEndpoints) + .add("clusterName", clusterName) + .add("serviceName", serviceName) + .toString(); + } +} diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java new file mode 100644 index 00000000000..179cca3a3cb --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java @@ -0,0 +1,203 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.client.nacos; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +import java.net.URI; +import java.time.Duration; + +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.client.endpoint.AbstractDynamicEndpointGroupBuilder; +import com.linecorp.armeria.client.endpoint.EndpointSelectionStrategy; +import com.linecorp.armeria.common.Flags; +import com.linecorp.armeria.common.annotation.Nullable; +import com.linecorp.armeria.internal.nacos.NacosClient; +import com.linecorp.armeria.internal.nacos.NacosClientBuilder; + +/** + * A builder class for {@link NacosEndpointGroup}. + *

Examples

+ *
{@code
+ * NacosEndpointGroup endpointGroup = NacosEndpointGroup.builder(nacosUri, "myService")
+ *                                                      .build();
+ * List endpoints = endpointGroup.endpoints();
+ * sb.serverListener(listener);
+ * }
+ */ +public class NacosEndpointGroupBuilder extends AbstractDynamicEndpointGroupBuilder { + + private static final long DEFAULT_CHECK_INTERVAL_MILLIS = 30_000; + + private EndpointSelectionStrategy selectionStrategy = EndpointSelectionStrategy.weightedRoundRobin(); + + private final String serviceName; + + private long registryFetchIntervalMillis = DEFAULT_CHECK_INTERVAL_MILLIS; + + @Nullable + private String namespaceId; + + @Nullable + private String groupName; + + @Nullable + private String clusterName; + + @Nullable + private String app; + + private boolean useHealthyEndpoints; + + private final NacosClientBuilder nacosClientBuilder; + + NacosEndpointGroupBuilder(URI nacosUri, String serviceName) { + super(Flags.defaultResponseTimeoutMillis()); + this.serviceName = requireNonNull(serviceName, "serviceName"); + nacosClientBuilder = NacosClient.builder(nacosUri); + } + + /** + * Sets the {@link EndpointSelectionStrategy} of the {@link NacosEndpointGroup}. + */ + public NacosEndpointGroupBuilder selectionStrategy(EndpointSelectionStrategy selectionStrategy) { + this.selectionStrategy = requireNonNull(selectionStrategy, "selectionStrategy"); + return this; + } + + /** + * Sets the 'namespaceId' parameter used to filter instances in a Nacos query. + * This method configures the NacosEndpointGroup to query only instances + * that match the specified 'namespaceId' value. + */ + public NacosEndpointGroupBuilder namespaceId(String namespaceId) { + this.namespaceId = requireNonNull(namespaceId, "namespaceId"); + return this; + } + + /** + * Sets the 'groupName' parameter used to filter instances in a Nacos query. + * This method configures the NacosEndpointGroup to query only instances + * that match the specified 'groupName' value. + */ + public NacosEndpointGroupBuilder groupName(String groupName) { + this.groupName = requireNonNull(groupName); + return this; + } + + /** + * Sets the 'clusterName' parameter used to filter instances in a Nacos query. + * This method configures the NacosEndpointGroup to query only instances + * that match the specified 'clusterName' value. + */ + public NacosEndpointGroupBuilder clusterName(String clusterName) { + this.clusterName = requireNonNull(clusterName); + return this; + } + + /** + * Sets the 'app' parameter used to filter instances in a Nacos query. + * This method configures the NacosEndpointGroup + * to query only instances that match the specified 'app' value. + */ + public NacosEndpointGroupBuilder app(String app) { + this.app = requireNonNull(app); + return this; + } + + /** + * Sets the healthy to retrieve only healthy instances from Nacos. + * Make sure that your target endpoints are health-checked by Nacos before enabling this feature. + * If not set, false is used by default. + */ + public NacosEndpointGroupBuilder useHealthyEndpoints(boolean useHealthyEndpoints) { + this.useHealthyEndpoints = useHealthyEndpoints; + return this; + } + + /** + * Sets the interval between fetching registry requests. + * If not set, {@value #DEFAULT_CHECK_INTERVAL_MILLIS} milliseconds is used by default. + */ + public NacosEndpointGroupBuilder registryFetchInterval(Duration registryFetchInterval) { + requireNonNull(registryFetchInterval, "registryFetchInterval"); + checkArgument(!registryFetchInterval.isZero() && !registryFetchInterval.isNegative(), + "registryFetchInterval: %s (expected: > 0)", + registryFetchInterval); + return registryFetchIntervalMillis(registryFetchInterval.toMillis()); + } + + /** + * Sets the interval between fetching registry requests. + * If not set, {@value #DEFAULT_CHECK_INTERVAL_MILLIS} milliseconds is used by default. + */ + public NacosEndpointGroupBuilder registryFetchIntervalMillis(long registryFetchIntervalMillis) { + checkArgument(registryFetchIntervalMillis > 0, "registryFetchIntervalMillis: %s (expected: > 0)", + registryFetchIntervalMillis); + this.registryFetchIntervalMillis = registryFetchIntervalMillis; + return this; + } + + /** + * Sets the username and password pair for Nacos's API. + * Please refer to the + * Nacos Authentication Document + * for more details. + * + * @param username the username for access Nacos API, default: {@code null} + * @param password the password for access Nacos API, default: {@code null} + */ + public NacosEndpointGroupBuilder authorization(String username, String password) { + nacosClientBuilder.authorization(username, password); + return this; + } + + /** + * Returns a newly-created {@link NacosEndpointGroup}. + */ + public NacosEndpointGroup build() { + return new NacosEndpointGroup(selectionStrategy, shouldAllowEmptyEndpoints(), + selectionTimeoutMillis(), nacosClientBuilder.build(), + serviceName, registryFetchIntervalMillis, namespaceId, + groupName, clusterName, app, useHealthyEndpoints); + } + + @Override + public NacosEndpointGroupBuilder allowEmptyEndpoints(boolean allowEmptyEndpoints) { + return (NacosEndpointGroupBuilder) super.allowEmptyEndpoints(allowEmptyEndpoints); + } + + /** + * Sets the timeout to wait until a successful {@link Endpoint} selection. + * {@link Duration#ZERO} disables the timeout. + * If unspecified, {@link Flags#defaultResponseTimeoutMillis()} is used by default. + */ + @Override + public NacosEndpointGroupBuilder selectionTimeout(Duration selectionTimeout) { + return (NacosEndpointGroupBuilder) super.selectionTimeout(selectionTimeout); + } + + /** + * Sets the timeout to wait until a successful {@link Endpoint} selection. + * {@code 0} disables the timeout. + * If unspecified, {@link Flags#defaultResponseTimeoutMillis()} is used by default. + */ + @Override + public NacosEndpointGroupBuilder selectionTimeoutMillis(long selectionTimeoutMillis) { + return (NacosEndpointGroupBuilder) super.selectionTimeoutMillis(selectionTimeoutMillis); + } +} diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/package-info.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/package-info.java new file mode 100644 index 00000000000..dda63589679 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/** + * Nacos-based {@link com.linecorp.armeria.client.endpoint.EndpointGroup} implementation. + */ +@NonNullByDefault +@UnstableApi +package com.linecorp.armeria.client.nacos; + +import com.linecorp.armeria.common.annotation.NonNullByDefault; +import com.linecorp.armeria.common.annotation.UnstableApi; diff --git a/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java b/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java new file mode 100644 index 00000000000..432d1adcf11 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java @@ -0,0 +1,43 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.common.nacos; + +import com.linecorp.armeria.common.annotation.UnstableApi; +import com.linecorp.armeria.internal.nacos.NacosClientBuilder; + +/** + * Sets properties for building a Nacos client. + */ +@UnstableApi +public interface NacosConfigSetters { + /** + * Sets the specified Nacos's API version. + * @param nacosApiVersion the version of Nacos API service, default: {@value + * NacosClientBuilder#DEFAULT_NACOS_API_VERSION} + */ + NacosConfigSetters nacosApiVersion(String nacosApiVersion); + + /** + * Sets the username and password pair for Nacos's API. + * Please refer to the + * Nacos Authentication Document + * for more details. + * + * @param username the username for access Nacos API, default: {@code null} + * @param password the password for access Nacos API, default: {@code null} + */ + NacosConfigSetters authorization(String username, String password); +} diff --git a/nacos/src/main/java/com/linecorp/armeria/common/nacos/package-info.java b/nacos/src/main/java/com/linecorp/armeria/common/nacos/package-info.java new file mode 100644 index 00000000000..6ff5ddbc7c9 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/common/nacos/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/** + * Various classes used internally. Anything in this package can be changed or removed at any time. + */ +@NonNullByDefault +@UnstableApi +package com.linecorp.armeria.common.nacos; + +import com.linecorp.armeria.common.annotation.NonNullByDefault; +import com.linecorp.armeria.common.annotation.UnstableApi; diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java new file mode 100644 index 00000000000..424429cbd44 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -0,0 +1,130 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.internal.nacos; + +import static java.util.Objects.requireNonNull; + +import java.util.concurrent.CompletableFuture; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.benmanes.caffeine.cache.AsyncLoadingCache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.Expiry; +import com.google.common.base.MoreObjects; + +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.HttpEntity; +import com.linecorp.armeria.common.MediaType; +import com.linecorp.armeria.common.QueryParams; +import com.linecorp.armeria.common.annotation.Nullable; + +/** + * A Nacos client that is responsible for + * Nacos Authentication. + */ +final class LoginClient { + + static LoginClient of(NacosClient nacosClient, String username, String password) { + return new LoginClient(nacosClient, username, password); + } + + private static final String NACOS_ACCESS_TOKEN_CACHE_KEY = "NACOS_ACCESS_TOKEN_CACHE_KEY"; + private final AsyncLoadingCache tokenCache = Caffeine.newBuilder() + .maximumSize(1) + .expireAfter(new Expiry() { + @Override + public long expireAfterCreate(@NonNull String key, @NonNull LoginResult loginResult, + long currentTime) { + return loginResult.tokenTtl.longValue(); + } + + @Override + public long expireAfterUpdate(@NonNull String key, @NonNull LoginResult loginResult, + long currentTime, long currentDuration) { + return loginResult.tokenTtl.longValue(); + } + + @Override + public long expireAfterRead(@NonNull String key, @NonNull LoginResult loginResult, + long currentTime, long currentDuration) { + return currentDuration; + } + }) + .buildAsync((key, executor) -> loginInternal()); + + private final WebClient webClient; + + private final String username; + + private final String password; + + LoginClient(NacosClient nacosClient, String username, String password) { + requireNonNull(nacosClient, "nacosClient"); + webClient = nacosClient.nacosWebClient(); + + this.username = requireNonNull(username, "username"); + this.password = requireNonNull(password, "password"); + } + + public CompletableFuture login() { + return tokenCache.get(NACOS_ACCESS_TOKEN_CACHE_KEY) + .thenApply(loginResult -> loginResult.accessToken); + } + + private CompletableFuture loginInternal() { + return webClient.prepare().post("/v1/auth/login") + .content(MediaType.FORM_DATA, + QueryParams.builder() + .add("username", username) + .add("password", password) + .toQueryString()) + .asJson(LoginResult.class) + .as(HttpEntity::content) + .execute(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static final class LoginResult { + String accessToken; + + Integer tokenTtl; + + @Nullable + Boolean globalAdmin; + + @JsonCreator + LoginResult(@JsonProperty("accessToken") String accessToken, @JsonProperty("tokenTtl") Integer tokenTtl, + @JsonProperty("globalAdmin") @Nullable Boolean globalAdmin) { + this.accessToken = accessToken; + this.tokenTtl = tokenTtl; + this.globalAdmin = globalAdmin; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .omitNullValues() + .add("accessToken", accessToken) + .add("tokenTtl", tokenTtl) + .add("globalAdmin", globalAdmin) + .toString(); + } + } +} diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java new file mode 100644 index 00000000000..e587d7b4136 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java @@ -0,0 +1,123 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.internal.nacos; + +import java.net.URI; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; + +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.client.WebClientBuilder; +import com.linecorp.armeria.client.retry.RetryConfig; +import com.linecorp.armeria.client.retry.RetryRule; +import com.linecorp.armeria.client.retry.RetryingClient; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.annotation.Nullable; + +public final class NacosClient { + + private static final Function retryingClientDecorator = + RetryingClient.newDecorator(RetryConfig.builder(RetryRule.onServerErrorStatus()) + .maxTotalAttempts(3) + .build()); + + public static NacosClientBuilder builder(URI nacosUri) { + return new NacosClientBuilder(nacosUri); + } + + private final WebClient webClient; + + private final QueryInstancesClient queryInstancesClient; + + private final RegisterInstanceClient registerInstanceClient; + + NacosClient(URI uri, String nacosApiVersion, @Nullable String username, @Nullable String password) { + final WebClientBuilder builder = WebClient.builder(uri) + .decorator(retryingClientDecorator); + + webClient = builder.build(); + + final LoginClient loginClient; + if (username != null && password != null) { + loginClient = LoginClient.of(this, username, password); + } else { + loginClient = null; + } + + queryInstancesClient = QueryInstancesClient.of(this, loginClient, nacosApiVersion); + registerInstanceClient = RegisterInstanceClient.of(this, loginClient, nacosApiVersion); + } + + public CompletableFuture> endpoints(String serviceName, @Nullable String namespaceId, + @Nullable String groupName, @Nullable String clusterName, + @Nullable Boolean healthyOnly, @Nullable String app) { + return queryInstancesClient.endpoints(serviceName, namespaceId, groupName, + clusterName, healthyOnly, app); + } + + /** + * Registers a instance to Nacos with service name. + * + * @param serviceName a service name that identifying a service. + * @param endpoint an endpoint of service to register + * @param namespaceId the namespace ID of the service instance. + * @param groupName the group name of the service. + * @param clusterName the cluster name of the service. + * @param app the application name associated with the service. + * @return a {@link HttpResponse} indicating the result of the registration operation. + */ + public HttpResponse register(String serviceName, Endpoint endpoint, @Nullable String namespaceId, + @Nullable String groupName, @Nullable String clusterName, + @Nullable String app) { + return registerInstanceClient.register(serviceName, endpoint.host(), endpoint.port(), endpoint.weight(), + namespaceId, groupName, clusterName, app); + } + + /** + * De-registers a instance to Nacos with service name. + * + * @param serviceName a service name that identifying a service. + * @param endpoint an endpoint of service to register + * @param namespaceId the namespace ID of the service instance. + * @param groupName the group name of the service. + * @param clusterName the cluster name of the service. + * @param app the application name associated with the service. + * @return a {@link HttpResponse} indicating the result of the de-registration operation. + */ + public HttpResponse deregister(String serviceName, Endpoint endpoint, @Nullable String namespaceId, + @Nullable String groupName, @Nullable String clusterName, + @Nullable String app) { + return registerInstanceClient.deregister(serviceName, endpoint.host(), endpoint.port(), + endpoint.weight(), namespaceId, groupName, clusterName, app); + } + + /** + * Returns a {@code WebClient} for accessing to Nacos server. + */ + WebClient nacosWebClient() { + return webClient; + } + + /** + * Returns the {@link URI} of Nacos uri. + */ + public URI uri() { + return webClient.uri(); + } +} diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java new file mode 100644 index 00000000000..dc99b759cbf --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java @@ -0,0 +1,77 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.internal.nacos; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.regex.Pattern; + +import com.linecorp.armeria.common.annotation.Nullable; +import com.linecorp.armeria.common.nacos.NacosConfigSetters; + +public final class NacosClientBuilder implements NacosConfigSetters { + public static final String DEFAULT_NACOS_API_VERSION = "v2"; + private static final Pattern NACOS_API_VERSION_PATTERN = Pattern.compile("^v[0-9][-._a-zA-Z0-9]*$"); + private final URI nacosUri; + + private String nacosApiVersion = DEFAULT_NACOS_API_VERSION; + + @Nullable + private String username; + + @Nullable + private String password; + + NacosClientBuilder(URI nacosUri) { + this.nacosUri = requireNonNull(nacosUri, "nacosUri"); + } + + @Override + public NacosConfigSetters nacosApiVersion(String nacosApiVersion) { + this.nacosApiVersion = requireNonNull(nacosApiVersion, "nacosApiVersion"); + checkArgument(NACOS_API_VERSION_PATTERN.matcher(nacosApiVersion).matches(), + "nacosApiVersion: %s (expected: a version string that starts with 'v', e.g. 'v1')", + nacosApiVersion); + + return this; + } + + @Override + public NacosClientBuilder authorization(String username, String password) { + requireNonNull(username, "username"); + requireNonNull(password, "password"); + + this.username = username; + this.password = password; + + return this; + } + + public NacosClient build() { + final URI uri; + try { + uri = new URI(nacosUri.getScheme(), null, nacosUri.getHost(), nacosUri.getPort(), + "/nacos", null, null); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + + return new NacosClient(uri, nacosApiVersion, username, password); + } +} diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java new file mode 100644 index 00000000000..b54729348f0 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java @@ -0,0 +1,83 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.internal.nacos; + +import com.linecorp.armeria.common.QueryParams; +import com.linecorp.armeria.common.QueryParamsBuilder; +import com.linecorp.armeria.common.annotation.Nullable; + +/** + * Utility methods related to Nacos clients. + */ +final class NacosClientUtil { + private static final String NAMESPACE_ID_PARAM = "namespaceId"; + + private static final String GROUP_NAME_PARAM = "groupName"; + + private static final String SERVICE_NAME_PARAM = "serviceName"; + + private static final String CLUSTER_NAME_PARAM = "clusterName"; + + private static final String HEALTHY_ONLY_PARAM = "healthyOnly"; + + private static final String APP_PARAM = "app"; + + private static final String IP_PARAM = "ip"; + + private static final String PORT_PARAM = "port"; + + private static final String WEIGHT_PARAM = "weight"; + + /** + * Encodes common Nacos API parameters as {@code QueryParams}. + */ + static QueryParams queryParams(@Nullable String namespaceId, @Nullable String groupName, + @Nullable String serviceName, @Nullable String clusterName, + @Nullable Boolean healthyOnly, @Nullable String app, + @Nullable String ip, @Nullable Integer port, @Nullable Integer weight) { + final QueryParamsBuilder paramsBuilder = QueryParams.builder(); + if (namespaceId != null) { + paramsBuilder.add(NAMESPACE_ID_PARAM, namespaceId); + } + if (groupName != null) { + paramsBuilder.add(GROUP_NAME_PARAM, groupName); + } + if (serviceName != null) { + paramsBuilder.add(SERVICE_NAME_PARAM, serviceName); + } + if (clusterName != null) { + paramsBuilder.add(CLUSTER_NAME_PARAM, clusterName); + } + if (healthyOnly != null) { + paramsBuilder.add(HEALTHY_ONLY_PARAM, healthyOnly.toString()); + } + if (app != null) { + paramsBuilder.add(APP_PARAM, app); + } + if (ip != null) { + paramsBuilder.add(IP_PARAM, ip); + } + if (port != null) { + paramsBuilder.add(PORT_PARAM, port.toString()); + } + if (weight != null) { + paramsBuilder.add(WEIGHT_PARAM, weight.toString()); + } + return paramsBuilder.build(); + } + + private NacosClientUtil() {} +} diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java new file mode 100644 index 00000000000..e9f0207037c --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java @@ -0,0 +1,222 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.internal.nacos; + +import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.util.Objects.requireNonNull; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; + +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.HttpEntity; +import com.linecorp.armeria.common.QueryParams; +import com.linecorp.armeria.common.annotation.Nullable; + +/** + * A Nacos client that is responsible for + * Nacos Open-Api - Query instances. + */ +final class QueryInstancesClient { + + static QueryInstancesClient of(NacosClient nacosClient, @Nullable LoginClient loginClient, + String nacosApiVersion) { + return new QueryInstancesClient(nacosClient, loginClient, nacosApiVersion); + } + + private final WebClient webClient; + + @Nullable + private final LoginClient loginClient; + + private final String nacosApiVersion; + + QueryInstancesClient(NacosClient nacosClient, @Nullable LoginClient loginClient, + String nacosApiVersion) { + webClient = nacosClient.nacosWebClient(); + this.loginClient = loginClient; + this.nacosApiVersion = nacosApiVersion; + } + + CompletableFuture> endpoints(String serviceName, @Nullable String namespaceId, + @Nullable String groupName, @Nullable String clusterName, + @Nullable Boolean healthyOnly, @Nullable String app) { + requireNonNull(serviceName, "serviceName"); + return queryInstances(serviceName, namespaceId, groupName, clusterName, healthyOnly, app) + .thenApply(response -> response.data.hosts.stream() + .map(QueryInstancesClient::toEndpoint) + .filter(Objects::nonNull) + .collect(toImmutableList())); + } + + CompletableFuture queryInstances(String serviceName, @Nullable String namespaceId, + @Nullable String groupName, @Nullable String clusterName, + @Nullable Boolean healthyOnly, @Nullable String app) { + requireNonNull(serviceName, "serviceName"); + final StringBuilder path = new StringBuilder("/").append(nacosApiVersion).append("/ns/instance/list?"); + final QueryParams params = NacosClientUtil.queryParams(namespaceId, groupName, serviceName, clusterName, + healthyOnly, app, null, null, null); + path.append(params.toQueryString()); + if (loginClient == null) { + return webClient.prepare() + .get(path.toString()) + .asJson(QueryInstancesResponse.class) + .as(HttpEntity::content) + .execute(); + } else { + return loginClient.login().thenCompose(accessToken -> + webClient.prepare() + .get(path.append("&accessToken=").append(accessToken).toString()) + .asJson(QueryInstancesResponse.class) + .as(HttpEntity::content) + .execute() + ); + } + } + + @Nullable + private static Endpoint toEndpoint(Host host) { + if (host.enabled != null && !host.enabled) { + return null; + } else if (host.weight != null && host.weight.intValue() >= 0) { + return Endpoint.of(host.ip, host.port).withWeight(host.weight.intValue()); + } else { + return Endpoint.of(host.ip, host.port); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static final class QueryInstancesResponse { + Data data; + + @JsonCreator + QueryInstancesResponse(@JsonProperty("data") Data data) { + this.data = data; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static final class Data { + List hosts; + + @JsonCreator + Data(@JsonProperty("hosts") List hosts) { + this.hosts = hosts; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static final class Host { + @Nullable + String instanceId; + + String ip; + + Integer port; + + @Nullable + Double weight; + + @Nullable + Boolean healthy; + + @Nullable + Boolean enabled; + + @Nullable + Boolean ephemeral; + + @Nullable + String clusterName; + + @Nullable + String serviceName; + + @Nullable + Map metadata; + + @Nullable + Integer instanceHeartBeatInterval; + + @Nullable + String instanceIdGenerator; + + @Nullable + Integer instanceHeartBeatTimeOut; + + @Nullable + Integer ipDeleteTimeout; + + @JsonCreator + Host(@JsonProperty("instanceId") @Nullable String instanceId, @JsonProperty("ip") String ip, + @JsonProperty("port") Integer port, @JsonProperty("weight") @Nullable Double weight, + @JsonProperty("healthy") @Nullable Boolean healthy, + @JsonProperty("enabled") @Nullable Boolean enabled, + @JsonProperty("ephemeral") @Nullable Boolean ephemeral, + @JsonProperty("clusterName") @Nullable String clusterName, + @JsonProperty("serviceName") @Nullable String serviceName, + @JsonProperty("metadata") @Nullable Map metadata, + @JsonProperty("instanceHeartBeatInterval") @Nullable Integer instanceHeartBeatInterval, + @JsonProperty("instanceIdGenerator") @Nullable String instanceIdGenerator, + @JsonProperty("instanceHeartBeatTimeOut") @Nullable Integer instanceHeartBeatTimeOut, + @JsonProperty("ipDeleteTimeout") @Nullable Integer ipDeleteTimeout + ) { + this.instanceId = instanceId; + this.ip = ip; + this.port = port; + this.weight = weight; + this.healthy = healthy; + this.enabled = enabled; + this.ephemeral = ephemeral; + this.clusterName = clusterName; + this.serviceName = serviceName; + this.metadata = metadata; + this.instanceHeartBeatInterval = instanceHeartBeatInterval; + this.instanceIdGenerator = instanceIdGenerator; + this.instanceHeartBeatTimeOut = instanceHeartBeatTimeOut; + this.ipDeleteTimeout = ipDeleteTimeout; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .omitNullValues() + .add("instanceId", instanceId) + .add("ip", ip) + .add("port", port) + .add("weight", weight) + .add("healthy", healthy) + .add("enabled", enabled) + .add("ephemeral", ephemeral) + .add("clusterName", clusterName) + .add("serviceName", serviceName) + .add("metaData", metadata) + .add("instanceHeartBeatInterval", instanceHeartBeatInterval) + .add("instanceIdGenerator", instanceIdGenerator) + .add("instanceHeartBeatTimeOut", instanceHeartBeatTimeOut) + .add("ipDeleteTimeout", ipDeleteTimeout) + .toString(); + } + } +} diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java new file mode 100644 index 00000000000..87c80ae2775 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java @@ -0,0 +1,117 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.internal.nacos; + +import static java.util.Objects.requireNonNull; + +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.MediaType; +import com.linecorp.armeria.common.annotation.Nullable; + +/** + * A Nacos client that is responsible for + * Nacos Open-Api - Register instance. + */ +final class RegisterInstanceClient { + + static RegisterInstanceClient of(NacosClient nacosClient, @Nullable LoginClient loginClient, + String nacosApiVersion) { + return new RegisterInstanceClient(nacosClient, loginClient, nacosApiVersion); + } + + private final WebClient webClient; + + @Nullable + private final LoginClient loginClient; + + private final String instanceApiPath; + + RegisterInstanceClient(NacosClient nacosClient, @Nullable LoginClient loginClient, + String nacosApiVersion) { + webClient = nacosClient.nacosWebClient(); + this.loginClient = loginClient; + instanceApiPath = new StringBuilder("/").append(nacosApiVersion).append("/ns/instance").toString(); + } + + /** + * Registers a service into the Nacos. + */ + HttpResponse register(String serviceName, String ip, int port, int weight, @Nullable String namespaceId, + @Nullable String groupName, @Nullable String clusterName, @Nullable String app) { + requireNonNull(serviceName, "serviceName"); + requireNonNull(ip, "ip"); + + final String params = NacosClientUtil.queryParams(namespaceId, groupName, serviceName, clusterName, + null, app, ip, port, weight) + .toQueryString(); + + if (loginClient == null) { + return webClient.prepare() + .post(instanceApiPath) + .content(MediaType.FORM_DATA, params) + .execute(); + } else { + return HttpResponse.of( + loginClient.login() + .thenApply(accessToken -> { + final String paramsWithToken = new StringBuilder(params) + .append("&accessToken=") + .append(accessToken) + .toString(); + return webClient.prepare() + .post(instanceApiPath) + .content(MediaType.FORM_DATA, paramsWithToken) + .execute(); + }) + ); + } + } + + /** + * De-registers a service from the Nacos. + */ + HttpResponse deregister(String serviceName, String ip, int port, int weight, @Nullable String namespaceId, + @Nullable String groupName, @Nullable String clusterName, @Nullable String app) { + requireNonNull(serviceName, "serviceName"); + requireNonNull(ip, "ip"); + + final String params = NacosClientUtil.queryParams(namespaceId, groupName, serviceName, clusterName, + null, app, ip, port, weight) + .toQueryString(); + + if (loginClient == null) { + return webClient.prepare() + .delete(instanceApiPath) + .content(MediaType.FORM_DATA, params) + .execute(); + } else { + return HttpResponse.of( + loginClient.login() + .thenApply(accessToken -> { + final String paramsWithToken = new StringBuilder(params) + .append("&accessToken=") + .append(accessToken) + .toString(); + return webClient.prepare() + .delete(instanceApiPath) + .content(MediaType.FORM_DATA, paramsWithToken) + .execute(); + }) + ); + } + } +} diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/package-info.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/package-info.java new file mode 100644 index 00000000000..57066b91104 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/** + * Various classes used internally. Anything in this package can be changed or removed at any time. + */ +@NonNullByDefault +package com.linecorp.armeria.internal.nacos; + +import com.linecorp.armeria.common.annotation.NonNullByDefault; diff --git a/settings.gradle b/settings.gradle index 7d9a15d58c0..5458a0c9519 100644 --- a/settings.gradle +++ b/settings.gradle @@ -175,6 +175,7 @@ includeWithFlags ':zookeeper3', 'java', 'publish', 'rel includeWithFlags ':saml', 'java', 'publish', 'relocate', 'native' includeWithFlags ':bucket4j', 'java', 'publish', 'relocate', 'native' includeWithFlags ':consul', 'java', 'publish', 'relocate', 'native' +includeWithFlags ':nacos', 'java', 'publish', 'relocate', 'native' // Published Javadoc-only projects includeWithFlags ':javadoc', 'java', 'publish', 'no_aggregation' From a76d4fcd17c25d5bd0dcc7006f8b102939debf15 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Thu, 25 Jan 2024 02:35:41 +0900 Subject: [PATCH 02/25] Add NacosUpdatingListener --- .../server/nacos/NacosUpdatingListener.java | 162 ++++++++++++++++++ .../nacos/NacosUpdatingListenerBuilder.java | 145 ++++++++++++++++ .../armeria/server/nacos/package-info.java | 25 +++ 3 files changed, 332 insertions(+) create mode 100644 nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java create mode 100644 nacos/src/main/java/com/linecorp/armeria/server/nacos/package-info.java diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java new file mode 100644 index 00000000000..ca416ad18f1 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java @@ -0,0 +1,162 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.server.nacos; + +import static java.util.Objects.requireNonNull; + +import java.net.Inet4Address; +import java.net.URI; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.common.annotation.Nullable; +import com.linecorp.armeria.common.util.SystemInfo; +import com.linecorp.armeria.internal.nacos.NacosClient; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.ServerListener; +import com.linecorp.armeria.server.ServerListenerAdapter; +import com.linecorp.armeria.server.ServerPort; + +/** + * A {@link ServerListener} which registers the current {@link Server} to + * Nacos. + */ +public class NacosUpdatingListener extends ServerListenerAdapter { + + private static final Logger logger = LoggerFactory.getLogger(NacosUpdatingListener.class); + + /** + * Returns a newly-created {@link NacosUpdatingListenerBuilder} with the specified {@code nacosUri} + * and {@code serviceName} to build {@link NacosUpdatingListener}. + * + * @param nacosUri the URI of Nacos API service + * @param serviceName the service name which is registered into Nacos. + */ + public static NacosUpdatingListenerBuilder builder(URI nacosUri, String serviceName) { + return new NacosUpdatingListenerBuilder(nacosUri, serviceName); + } + + private final NacosClient nacosClient; + + private final String serviceName; + + @Nullable + private final Endpoint endpoint; + + @Nullable + private final String namespaceId; + + @Nullable + private final String groupName; + + @Nullable + private final String clusterName; + + @Nullable + private final String app; + + private volatile boolean isRegistered; + + NacosUpdatingListener(NacosClient nacosClient, String serviceName, @Nullable Endpoint endpoint, + @Nullable String namespaceId, @Nullable String groupName, + @Nullable String clusterName, @Nullable String app) { + this.nacosClient = requireNonNull(nacosClient, "nacosClient"); + this.serviceName = requireNonNull(serviceName, "serviceName"); + this.endpoint = endpoint; + this.namespaceId = namespaceId; + this.groupName = groupName; + this.clusterName = clusterName; + this.app = app; + } + + @Override + public void serverStarted(Server server) throws Exception { + final Endpoint endpoint = getEndpoint(server); + nacosClient.register(serviceName, endpoint, namespaceId, groupName, clusterName, app) + .aggregate() + .handle((res, cause) -> { + if (cause != null) { + logger.warn("Failed to register {}:{} to Nacos: {}", + endpoint.host(), endpoint.port(), nacosClient.uri(), cause); + return null; + } + + if (res.status() != HttpStatus.OK) { + logger.warn("Failed to register {}:{} to Nacos: {} (status: {}, content: {})", + endpoint.host(), endpoint.port(), nacosClient.uri(), res.status(), + res.contentUtf8()); + return null; + } + + logger.info("Registered {}:{} to Nacos: {}", + endpoint.host(), endpoint.port(), nacosClient.uri()); + isRegistered = true; + return null; + }); + } + + private Endpoint getEndpoint(Server server) { + if (endpoint != null) { + if (endpoint.hasPort()) { + warnIfInactivePort(server, endpoint.port()); + } + return endpoint; + } + return defaultEndpoint(server); + } + + private static Endpoint defaultEndpoint(Server server) { + final ServerPort serverPort = server.activePort(); + assert serverPort != null; + + final Inet4Address inet4Address = SystemInfo.defaultNonLoopbackIpV4Address(); + final String host = inet4Address != null ? inet4Address.getHostAddress() : server.defaultHostname(); + return Endpoint.of(host, serverPort.localAddress().getPort()); + } + + private static void warnIfInactivePort(Server server, int port) { + for (ServerPort serverPort : server.activePorts().values()) { + if (serverPort.localAddress().getPort() == port) { + return; + } + } + logger.warn("The specified port number {} does not exist. (expected one of activePorts: {})", + port, server.activePorts()); + } + + @Override + public void serverStopping(Server server) { + final Endpoint endpoint = getEndpoint(server); + if (isRegistered) { + nacosClient.deregister(serviceName, endpoint, namespaceId, groupName, clusterName, app) + .aggregate() + .handle((res, cause) -> { + if (cause != null) { + logger.warn("Failed to deregister {}:{} from Nacos: {}", + endpoint.ipAddr(), endpoint.port(), nacosClient.uri(), cause); + } else if (res.status() != HttpStatus.OK) { + logger.warn("Failed to deregister {}:{} from Nacos: {}. (status: {}, content: {})", + endpoint.ipAddr(), endpoint.port(), nacosClient.uri(), res.status(), + res.contentUtf8()); + } + return null; + }); + } + } +} diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java new file mode 100644 index 00000000000..fdecbf52043 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java @@ -0,0 +1,145 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.server.nacos; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +import java.net.URI; + +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.common.annotation.Nullable; +import com.linecorp.armeria.common.annotation.UnstableApi; +import com.linecorp.armeria.common.nacos.NacosConfigSetters; +import com.linecorp.armeria.internal.nacos.NacosClient; +import com.linecorp.armeria.internal.nacos.NacosClientBuilder; +import com.linecorp.armeria.server.Server; + +/** + * Builds a new {@link NacosUpdatingListener}, which registers the server to Nacos. + *

Examples

+ *
{@code
+ * NacosUpdatingListener listener = NacosUpdatingListener.builder(nacosUri, "myService")
+ *                                                         .build();
+ * ServerBuilder sb = Server.builder();
+ * sb.serverListener(listener);
+ * }
+ */ +@UnstableApi +public final class NacosUpdatingListenerBuilder implements NacosConfigSetters { + + private final String serviceName; + + @Nullable + private Endpoint endpoint; + + @Nullable + private String namespaceId; + + @Nullable + private String groupName; + + @Nullable + private String clusterName; + + @Nullable + private String app; + + private final NacosClientBuilder nacosClientBuilder; + + /** + * Creates a {@link NacosUpdatingListenerBuilder} with a service name. + * + * @param nacosUri the URI of Nacos API service + * @param serviceName the service name to register + */ + NacosUpdatingListenerBuilder(URI nacosUri, String serviceName) { + this.serviceName = requireNonNull(serviceName, "serviceName"); + checkArgument(!this.serviceName.isEmpty(), "serviceName can't be empty"); + nacosClientBuilder = NacosClient.builder(nacosUri); + } + + /** + * Sets the {@link Endpoint} to register. If not set, the current host name is used by default. + * + * @param endpoint the {@link Endpoint} to register + */ + public NacosUpdatingListenerBuilder endpoint(Endpoint endpoint) { + this.endpoint = requireNonNull(endpoint, "endpoint"); + return this; + } + + /** + * Sets the namespace ID to register the instance. + * + * @param namespaceId the namespace ID to register. + */ + public NacosUpdatingListenerBuilder namespaceId(String namespaceId) { + this.namespaceId = requireNonNull(namespaceId, "namespaceId"); + return this; + } + + /** + * Sets the group name of the instance. + * + * @param groupName the group name of the instance. + */ + public NacosUpdatingListenerBuilder groupName(String groupName) { + this.groupName = requireNonNull(groupName, "groupName"); + return this; + } + + /** + * Sets the cluster name of the instance. + * + * @param clusterName the cluster name of the instance. + */ + public NacosUpdatingListenerBuilder clusterName(String clusterName) { + this.clusterName = requireNonNull(clusterName, "clusterName"); + return this; + } + + /** + * Sets the app name of the instance. + * + * @param app app name of the instance. + */ + public NacosUpdatingListenerBuilder app(String app) { + this.app = requireNonNull(app, "app"); + return this; + } + + @Override + public NacosUpdatingListenerBuilder nacosApiVersion(String nacosApiVersion) { + nacosClientBuilder.nacosApiVersion(nacosApiVersion); + return this; + } + + @Override + public NacosUpdatingListenerBuilder authorization(String username, String password) { + nacosClientBuilder.authorization(username, password); + return this; + } + + /** + * Returns a newly-created {@link NacosUpdatingListener} that registers the {@link Server} to + * Nacos when the {@link Server} starts. + */ + public NacosUpdatingListener build() { + return new NacosUpdatingListener(nacosClientBuilder.build(), serviceName, endpoint, namespaceId, + groupName, clusterName, app); + } +} diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/package-info.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/package-info.java new file mode 100644 index 00000000000..c4885b056c5 --- /dev/null +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/** + * Automatic service registration and discovery with Nacos. + **/ +@NonNullByDefault +@UnstableApi +package com.linecorp.armeria.server.nacos; + +import com.linecorp.armeria.common.annotation.NonNullByDefault; +import com.linecorp.armeria.common.annotation.UnstableApi; From 4caabafc2da0a3ee68e4602a68c8a8001a7c3246 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Thu, 25 Jan 2024 04:13:21 +0900 Subject: [PATCH 03/25] Test codes --- nacos/build.gradle.kts | 4 +- .../nacos/NacosEndpointGroupBuilderTest.java | 45 ++++ .../client/nacos/NacosEndpointGroupTest.java | 204 ++++++++++++++++++ .../nacos/NacosClientBuilderTest.java | 45 ++++ .../armeria/internal/nacos/NacosTestBase.java | 151 +++++++++++++ .../nacos/NacosUpdatingListenerTest.java | 137 ++++++++++++ 6 files changed, 584 insertions(+), 2 deletions(-) create mode 100644 nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java create mode 100644 nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java create mode 100644 nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java create mode 100644 nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java create mode 100644 nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java diff --git a/nacos/build.gradle.kts b/nacos/build.gradle.kts index 7a445eafc48..aae19538681 100644 --- a/nacos/build.gradle.kts +++ b/nacos/build.gradle.kts @@ -1,4 +1,4 @@ dependencies { - // Caffeine implementation(libs.caffeine) -} \ No newline at end of file + testImplementation(libs.testcontainers.junit.jupiter) +} diff --git a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java new file mode 100644 index 00000000000..7c08011b77e --- /dev/null +++ b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java @@ -0,0 +1,45 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.client.nacos; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; + +import org.junit.jupiter.api.Test; + +import com.linecorp.armeria.common.Flags; + +class NacosEndpointGroupBuilderTest { + + @Test + void selectionTimeoutDefault() { + try (NacosEndpointGroup group = NacosEndpointGroup.of(URI.create("http://127.0.0.1/node"), + "testService")) { + assertThat(group.selectionTimeoutMillis()).isEqualTo(Flags.defaultResponseTimeoutMillis()); + } + } + + @Test + void selectionTimeoutCustom() { + try (NacosEndpointGroup group = + NacosEndpointGroup.builder(URI.create("http://127.0.0.1/node"), "testService") + .selectionTimeoutMillis(4000) + .build()) { + assertThat(group.selectionTimeoutMillis()).isEqualTo(4000); + } + } +} diff --git a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java new file mode 100644 index 00000000000..d7e01d3fd09 --- /dev/null +++ b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java @@ -0,0 +1,204 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.client.nacos; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.awaitility.Awaitility.await; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.internal.nacos.NacosTestBase; +import com.linecorp.armeria.internal.testing.GenerateNativeImageTrace; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.ServerListener; +import com.linecorp.armeria.server.nacos.NacosUpdatingListener; + +@GenerateNativeImageTrace +class NacosEndpointGroupTest extends NacosTestBase { + + private static final List servers = new ArrayList<>(); + private static volatile List sampleEndpoints; + + private static final String DEFAULT_CLUSTER_NAME = "c1"; + + @BeforeAll + static void startServers() { + await().pollInSameThread() + .pollInterval(Duration.ofSeconds(1)) + .untilAsserted(() -> assertThatCode(() -> { + final List endpoints = newSampleEndpoints(); + servers.clear(); + for (Endpoint endpoint : endpoints) { + final Server server = Server.builder() + .http(endpoint.port()) + .service("/", new EchoService()) + .build(); + final ServerListener listener = + NacosUpdatingListener + .builder(nacosUri(), serviceName) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .clusterName(DEFAULT_CLUSTER_NAME) + .build(); + server.addListener(listener); + server.start().join(); + servers.add(server); + } + sampleEndpoints = endpoints; + }).doesNotThrowAnyException()); + } + + @AfterAll + static void stopServers() { + servers.forEach(Server::close); + servers.clear(); + } + + @Test + void testNacosEndpointGroupWithClient() { + try (NacosEndpointGroup endpointGroup = + NacosEndpointGroup.builder(nacosUri(), serviceName) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .registryFetchInterval(Duration.ofSeconds(1)) + .build()) { + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints); + }); + + // stop a server + servers.get(0).stop().join(); + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + assertThat(endpointGroup.endpoints()).hasSize(sampleEndpoints.size() - 1); + }); + + // restart the server + await().pollInSameThread().pollInterval(Duration.ofSeconds(1)).untilAsserted(() -> { + // The port bound to the server could be stolen while stopping the server. + assertThatCode(servers.get(0).start()::join).doesNotThrowAnyException(); + }); + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints); + }); + } + } + + @Test + void testNacosEndpointGroupWithUrl() { + try (NacosEndpointGroup endpointGroup = + NacosEndpointGroup.builder(nacosUri(), serviceName) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .registryFetchInterval(Duration.ofSeconds(1)) + .build()) { + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints); + }); + + // stop a server + servers.get(0).stop().join(); + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + assertThat(endpointGroup.endpoints()).hasSize(sampleEndpoints.size() - 1); + }); + + // restart the server + await().pollInSameThread().pollInterval(Duration.ofSeconds(1)).untilAsserted(() -> { + // The port bound to the server could be stolen while stopping the server. + assertThatCode(servers.get(0).start()::join).doesNotThrowAnyException(); + }); + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints); + }); + } + } + + @Test + void testSelectStrategy() { + try (NacosEndpointGroup endpointGroup = + NacosEndpointGroup.builder(nacosUri(), serviceName) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .registryFetchInterval(Duration.ofSeconds(1)) + .build()) { + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(endpointGroup.selectNow(null)) + .isNotEqualTo(endpointGroup.selectNow(null))); + } + } + + @Test + void testNacosEndpointGroupWithClusterName() { + final NacosEndpointGroupBuilder builder = + NacosEndpointGroup.builder(nacosUri(), serviceName) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .registryFetchInterval(Duration.ofSeconds(1)); + // default cluster name + try (NacosEndpointGroup endpointGroup = builder.clusterName(DEFAULT_CLUSTER_NAME).build()) { + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints)); + } + // non-existent cluster name + try (NacosEndpointGroup endpointGroup = builder.clusterName("c2").build()) { + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).isEmpty()); + } + } + + @Test + void testNacosEndpointGroupWithNamespaceId() { + final NacosEndpointGroupBuilder builder = + NacosEndpointGroup.builder(nacosUri(), serviceName) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .registryFetchInterval(Duration.ofSeconds(1)); + // default namespace id + try (NacosEndpointGroup endpointGroup = builder.namespaceId("public").build()) { + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints)); + } + // non-existent namespace id + try (NacosEndpointGroup endpointGroup = builder.namespaceId("private").build()) { + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).isEmpty()); + } + } + + @Test + void testNacosEndpointGroupWithGroupName() { + final NacosEndpointGroupBuilder builder = + NacosEndpointGroup.builder(nacosUri(), serviceName) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .registryFetchInterval(Duration.ofSeconds(1)); + try (NacosEndpointGroup endpointGroup = builder.build()) { + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints)); + } + try (NacosEndpointGroup endpointGroup = builder.groupName("not-default-group").build()) { + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).isEmpty()); + } + } +} diff --git a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java new file mode 100644 index 00000000000..a1f9c5643c2 --- /dev/null +++ b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java @@ -0,0 +1,45 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.internal.nacos; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.net.URI; + +import org.junit.jupiter.api.Test; + +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.HttpStatus; + +class NacosClientBuilderTest extends NacosTestBase { + + @Test + void gets403WhenNoToken() throws Exception { + final HttpStatus status = WebClient.of(nacosUri()) + .blocking() + .get("/nacos/v1/ns/service/list?pageNo=0&pageSize=10") + .status(); + assertThat(status).isEqualTo(HttpStatus.FORBIDDEN); + } + + @Test + void nacosApiVersionCanNotStartsWithSlash() { + assertThrows(IllegalArgumentException.class, () -> + NacosClient.builder(URI.create("http://localhost:8500")).nacosApiVersion("/v1")); + NacosClient.builder(URI.create("http://localhost:8500")).nacosApiVersion("v1"); + } +} diff --git a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java new file mode 100644 index 00000000000..21884adcd83 --- /dev/null +++ b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java @@ -0,0 +1,151 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.internal.nacos; + +import static com.google.common.base.Preconditions.checkState; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.URI; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +import org.junit.jupiter.api.BeforeAll; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import com.google.common.collect.ImmutableList; + +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.common.AggregatedHttpRequest; +import com.linecorp.armeria.common.HttpRequest; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.common.ResponseHeaders; +import com.linecorp.armeria.common.annotation.Nullable; +import com.linecorp.armeria.common.util.CompletionActions; +import com.linecorp.armeria.internal.testing.FlakyTest; +import com.linecorp.armeria.server.AbstractHttpService; +import com.linecorp.armeria.server.ServiceRequestContext; + +/** + * A helper class for testing with Nacos. + */ +@FlakyTest +@Testcontainers(disabledWithoutDocker = true) +public abstract class NacosTestBase { + protected static final String serviceName = "testService"; + protected static final String NACOS_AUTH_TOKEN = "armeriaarmeriaarmeriaarmeriaarmeriaarmeriaarmeriaarmeria"; + protected static final String NACOS_AUTH_SECRET = "nacos"; + + protected static List newSampleEndpoints() { + final int[] ports = unusedPorts(3); + return ImmutableList.of(Endpoint.of("host.docker.internal", ports[0]).withWeight(2), + Endpoint.of("host.docker.internal", ports[1]).withWeight(4), + Endpoint.of("host.docker.internal", ports[2]).withWeight(2)); + } + + @Container + static final GenericContainer nacosContainer = + new GenericContainer(DockerImageName.parse("nacos/nacos-server:v2.3.0-slim")) + .withExposedPorts(8848) + .withEnv("MODE", "standalone") + .withEnv("NACOS_AUTH_ENABLE", "true") + .withEnv("NACOS_AUTH_TOKEN", NACOS_AUTH_TOKEN) + .withEnv("NACOS_AUTH_IDENTITY_KEY", NACOS_AUTH_SECRET) + .withEnv("NACOS_AUTH_IDENTITY_VALUE", NACOS_AUTH_SECRET); + + @Nullable + private static URI nacosUri; + + protected NacosTestBase() {} + + @Nullable + private static NacosClient nacosClient; + + @BeforeAll + static void start() throws Throwable { + // Initialize Nacos Client + nacosUri = URI.create( + "http://" + nacosContainer.getHost() + ':' + nacosContainer.getMappedPort(8848)); + + nacosClient = NacosClient.builder(nacosUri) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .build(); + } + + protected static NacosClient client() { + if (nacosClient == null) { + throw new IllegalStateException("nacos client has not initialized"); + } + return nacosClient; + } + + protected static URI nacosUri() { + checkState(nacosUri != null, "nacosUri has not initialized."); + return nacosUri; + } + + protected static int[] unusedPorts(int numPorts) { + final int[] ports = new int[numPorts]; + final Random random = ThreadLocalRandom.current(); + for (int i = 0; i < numPorts; i++) { + for (;;) { + final int candidatePort = random.nextInt(64512) + 1024; + try (ServerSocket ss = new ServerSocket()) { + ss.bind(new InetSocketAddress("127.0.0.1", candidatePort)); + ports[i] = candidatePort; + break; + } catch (IOException e) { + // Port in use or unable to bind. + continue; + } + } + } + + return ports; + } + + public static class EchoService extends AbstractHttpService { + private volatile HttpStatus responseStatus = HttpStatus.OK; + + @Override + protected final HttpResponse doHead(ServiceRequestContext ctx, HttpRequest req) { + return HttpResponse.of(req.aggregate() + .thenApply(aReq -> HttpResponse.of(HttpStatus.OK)) + .exceptionally(CompletionActions::log)); + } + + @Override + protected final HttpResponse doPost(ServiceRequestContext ctx, HttpRequest req) { + return HttpResponse.of(req.aggregate() + .thenApply(this::echo) + .exceptionally(CompletionActions::log)); + } + + protected HttpResponse echo(AggregatedHttpRequest aReq) { + final HttpStatus httpStatus = HttpStatus.valueOf(aReq.contentUtf8()); + if (httpStatus != HttpStatus.UNKNOWN) { + responseStatus = httpStatus; + } + return HttpResponse.of(ResponseHeaders.of(responseStatus), aReq.content()); + } + } +} diff --git a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java new file mode 100644 index 00000000000..e24d99ce410 --- /dev/null +++ b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java @@ -0,0 +1,137 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.linecorp.armeria.server.nacos; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.awaitility.Awaitility.await; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.internal.nacos.NacosTestBase; +import com.linecorp.armeria.internal.testing.GenerateNativeImageTrace; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.ServerListener; + +@GenerateNativeImageTrace +class NacosUpdatingListenerTest extends NacosTestBase { + + private static final List servers = new ArrayList<>(); + private static volatile List sampleEndpoints; + + @BeforeAll + static void startServers() throws JsonProcessingException { + + await().pollInSameThread().pollInterval(Duration.ofSeconds(1)).untilAsserted(() -> { + assertThatCode(() -> { + final List endpoints = newSampleEndpoints(); + servers.clear(); + for (Endpoint endpoint : endpoints) { + final Server server = Server.builder() + .http(endpoint.port()) + .service("/echo", new EchoService()) + .build(); + final ServerListener listener = + NacosUpdatingListener + .builder(nacosUri(), serviceName) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .endpoint(endpoint) + .build(); + server.addListener(listener); + server.start().join(); + servers.add(server); + } + sampleEndpoints = endpoints; + }).doesNotThrowAnyException(); + }); + } + + @AfterAll + static void stopServers() throws Exception { + servers.forEach(Server::close); + servers.clear(); + } + + @Test + void testBuild() { + assertThat(NacosUpdatingListener.builder(nacosUri(), serviceName) + .build()).isNotNull(); + assertThat(NacosUpdatingListener.builder(nacosUri(), serviceName) + .build()).isNotNull(); + } + + @Test + void testEndpointsCountOfListeningServiceWithAServerStopAndStart() { + // Checks sample endpoints created when initialized. + await().untilAsserted(() -> { + assertThat(client().endpoints(serviceName, null, null, null, null, null) + .join()).hasSameSizeAs(sampleEndpoints); + }); + + // When we close one server then the listener deregister it automatically from nacos. + servers.get(0).stop().join(); + + await().untilAsserted(() -> { + final List results = client() + .endpoints(serviceName, null, null, null, null, null).join(); + assertThat(results).hasSize(sampleEndpoints.size() - 1); + }); + + // Endpoints increased after service restart. + servers.get(0).start().join(); + + await().untilAsserted(() -> { + assertThat(client().endpoints(serviceName, null, null, null, null, null) + .join()).hasSameSizeAs(sampleEndpoints); + }); + } + + @Test + void testThatGroupNameIsSpecified() { + final int port = unusedPorts(1)[0]; + final Endpoint endpoint = Endpoint.of("host.docker.internal", port).withWeight(1); + + final Server server = Server.builder() + .http(port) + .service("/echo", new EchoService()) + .build(); + final ServerListener listener = + NacosUpdatingListener.builder(nacosUri(), "testThatGroupNameIsSpecified") + .nacosApiVersion("v1") + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .endpoint(endpoint) + .groupName("groupName") + .build(); + server.addListener(listener); + server.start().join(); + await().untilAsserted(() -> { + assertThat(client().endpoints("testThatGroupNameIsSpecified", + null, "groupName", null, + null, null).join()) + .hasSize(1); + }); + server.stop(); + } +} From 3af6f6c4aeb44e73ebac85d071ef8f6c1ed89d3c Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sat, 27 Jan 2024 16:59:30 +0900 Subject: [PATCH 04/25] Modified NacosClientUtil::queryParams to return QueryParamsBuilder instead of QueryParams, making it easier to append additional parameters (e.g., accessToken). --- .../internal/nacos/NacosClientUtil.java | 13 ++++---- .../internal/nacos/QueryInstancesClient.java | 9 ++--- .../nacos/RegisterInstanceClient.java | 33 ++++++++++--------- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java index b54729348f0..1f171e8193a 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java @@ -42,12 +42,13 @@ final class NacosClientUtil { private static final String WEIGHT_PARAM = "weight"; /** - * Encodes common Nacos API parameters as {@code QueryParams}. + * Encodes common Nacos API parameters as {@code QueryParamsBuilder}. */ - static QueryParams queryParams(@Nullable String namespaceId, @Nullable String groupName, - @Nullable String serviceName, @Nullable String clusterName, - @Nullable Boolean healthyOnly, @Nullable String app, - @Nullable String ip, @Nullable Integer port, @Nullable Integer weight) { + static QueryParamsBuilder queryParamsBuilder(@Nullable String namespaceId, @Nullable String groupName, + @Nullable String serviceName, @Nullable String clusterName, + @Nullable Boolean healthyOnly, @Nullable String app, + @Nullable String ip, @Nullable Integer port, + @Nullable Integer weight) { final QueryParamsBuilder paramsBuilder = QueryParams.builder(); if (namespaceId != null) { paramsBuilder.add(NAMESPACE_ID_PARAM, namespaceId); @@ -76,7 +77,7 @@ static QueryParams queryParams(@Nullable String namespaceId, @Nullable String gr if (weight != null) { paramsBuilder.add(WEIGHT_PARAM, weight.toString()); } - return paramsBuilder.build(); + return paramsBuilder; } private NacosClientUtil() {} diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java index e9f0207037c..d4ff2b275df 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java @@ -31,7 +31,7 @@ import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.common.HttpEntity; -import com.linecorp.armeria.common.QueryParams; +import com.linecorp.armeria.common.QueryParamsBuilder; import com.linecorp.armeria.common.annotation.Nullable; /** @@ -75,9 +75,10 @@ CompletableFuture queryInstances(String serviceName, @Nu @Nullable Boolean healthyOnly, @Nullable String app) { requireNonNull(serviceName, "serviceName"); final StringBuilder path = new StringBuilder("/").append(nacosApiVersion).append("/ns/instance/list?"); - final QueryParams params = NacosClientUtil.queryParams(namespaceId, groupName, serviceName, clusterName, - healthyOnly, app, null, null, null); - path.append(params.toQueryString()); + final QueryParamsBuilder paramsBuilder = NacosClientUtil + .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, healthyOnly, app, null, + null, null); + path.append(paramsBuilder.build().toQueryString()); if (loginClient == null) { return webClient.prepare() .get(path.toString()) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java index 87c80ae2775..a85fc50dfee 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java @@ -20,6 +20,7 @@ import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.common.HttpResponse; import com.linecorp.armeria.common.MediaType; +import com.linecorp.armeria.common.QueryParamsBuilder; import com.linecorp.armeria.common.annotation.Nullable; /** @@ -55,23 +56,23 @@ HttpResponse register(String serviceName, String ip, int port, int weight, @Null requireNonNull(serviceName, "serviceName"); requireNonNull(ip, "ip"); - final String params = NacosClientUtil.queryParams(namespaceId, groupName, serviceName, clusterName, - null, app, ip, port, weight) - .toQueryString(); + final QueryParamsBuilder paramsBuilder = NacosClientUtil + .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, null, app, + ip, port, weight); if (loginClient == null) { return webClient.prepare() .post(instanceApiPath) - .content(MediaType.FORM_DATA, params) + .content(MediaType.FORM_DATA, paramsBuilder.toQueryString()) .execute(); } else { return HttpResponse.of( loginClient.login() .thenApply(accessToken -> { - final String paramsWithToken = new StringBuilder(params) - .append("&accessToken=") - .append(accessToken) - .toString(); + final String paramsWithToken = paramsBuilder + .add("accessToken", accessToken) + .build() + .toQueryString(); return webClient.prepare() .post(instanceApiPath) .content(MediaType.FORM_DATA, paramsWithToken) @@ -89,23 +90,23 @@ HttpResponse deregister(String serviceName, String ip, int port, int weight, @Nu requireNonNull(serviceName, "serviceName"); requireNonNull(ip, "ip"); - final String params = NacosClientUtil.queryParams(namespaceId, groupName, serviceName, clusterName, - null, app, ip, port, weight) - .toQueryString(); + final QueryParamsBuilder paramsBuilder = NacosClientUtil + .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, null, app, + ip, port, weight); if (loginClient == null) { return webClient.prepare() .delete(instanceApiPath) - .content(MediaType.FORM_DATA, params) + .content(MediaType.FORM_DATA, paramsBuilder.build().toQueryString()) .execute(); } else { return HttpResponse.of( loginClient.login() .thenApply(accessToken -> { - final String paramsWithToken = new StringBuilder(params) - .append("&accessToken=") - .append(accessToken) - .toString(); + final String paramsWithToken = paramsBuilder + .add("accessToken", accessToken) + .build() + .toQueryString(); return webClient.prepare() .delete(instanceApiPath) .content(MediaType.FORM_DATA, paramsWithToken) From 95303729ef660eb8d0a47a2888d6cf3b7d57a1a4 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Fri, 9 Feb 2024 23:05:28 +0900 Subject: [PATCH 05/25] Make properties of the classes that define data rendering private and immutable. --- .../armeria/internal/nacos/LoginClient.java | 6 ++-- .../internal/nacos/QueryInstancesClient.java | 32 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index 424429cbd44..e56c95a421f 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -102,12 +102,12 @@ private CompletableFuture loginInternal() { @JsonIgnoreProperties(ignoreUnknown = true) private static final class LoginResult { - String accessToken; + private final String accessToken; - Integer tokenTtl; + private final Integer tokenTtl; @Nullable - Boolean globalAdmin; + private final Boolean globalAdmin; @JsonCreator LoginResult(@JsonProperty("accessToken") String accessToken, @JsonProperty("tokenTtl") Integer tokenTtl, diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java index d4ff2b275df..bcc56ebf6fc 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java @@ -109,7 +109,7 @@ private static Endpoint toEndpoint(Host host) { @JsonIgnoreProperties(ignoreUnknown = true) private static final class QueryInstancesResponse { - Data data; + private final Data data; @JsonCreator QueryInstancesResponse(@JsonProperty("data") Data data) { @@ -119,7 +119,7 @@ private static final class QueryInstancesResponse { @JsonIgnoreProperties(ignoreUnknown = true) private static final class Data { - List hosts; + private final List hosts; @JsonCreator Data(@JsonProperty("hosts") List hosts) { @@ -130,44 +130,44 @@ private static final class Data { @JsonIgnoreProperties(ignoreUnknown = true) private static final class Host { @Nullable - String instanceId; + private final String instanceId; - String ip; + private final String ip; - Integer port; + private final Integer port; @Nullable - Double weight; + private final Double weight; @Nullable - Boolean healthy; + private final Boolean healthy; @Nullable - Boolean enabled; + private final Boolean enabled; @Nullable - Boolean ephemeral; + private final Boolean ephemeral; @Nullable - String clusterName; + private final String clusterName; @Nullable - String serviceName; + private final String serviceName; @Nullable - Map metadata; + private final Map metadata; @Nullable - Integer instanceHeartBeatInterval; + private final Integer instanceHeartBeatInterval; @Nullable - String instanceIdGenerator; + private final String instanceIdGenerator; @Nullable - Integer instanceHeartBeatTimeOut; + private final Integer instanceHeartBeatTimeOut; @Nullable - Integer ipDeleteTimeout; + private final Integer ipDeleteTimeout; @JsonCreator Host(@JsonProperty("instanceId") @Nullable String instanceId, @JsonProperty("ip") String ip, From ffc64d7b7fda9038a01083d0fc836c95b188f84a Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sat, 10 Feb 2024 20:18:34 +0900 Subject: [PATCH 06/25] Make LoginClient an implementation of SimpleDecoratingHttpClient. Hide login-related context from other clients as much as possible. --- .../armeria/internal/nacos/LoginClient.java | 52 ++++++++++++---- .../armeria/internal/nacos/NacosClient.java | 13 ++-- .../internal/nacos/QueryInstancesClient.java | 33 +++-------- .../nacos/RegisterInstanceClient.java | 59 ++++--------------- 4 files changed, 64 insertions(+), 93 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index e56c95a421f..7d84fd05f47 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -18,7 +18,13 @@ import static java.util.Objects.requireNonNull; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import com.linecorp.armeria.client.ClientRequestContext; +import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.SimpleDecoratingHttpClient; +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.*; import org.checkerframework.checker.nullness.qual.NonNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -29,20 +35,16 @@ import com.github.benmanes.caffeine.cache.Expiry; import com.google.common.base.MoreObjects; -import com.linecorp.armeria.client.WebClient; -import com.linecorp.armeria.common.HttpEntity; -import com.linecorp.armeria.common.MediaType; -import com.linecorp.armeria.common.QueryParams; import com.linecorp.armeria.common.annotation.Nullable; /** * A Nacos client that is responsible for * Nacos Authentication. */ -final class LoginClient { - - static LoginClient of(NacosClient nacosClient, String username, String password) { - return new LoginClient(nacosClient, username, password); +final class LoginClient extends SimpleDecoratingHttpClient { + public static Function newDecorator(WebClient webClient, + String username, String password) { + return delegate -> new LoginClient(delegate, webClient, username, password); } private static final String NACOS_ACCESS_TOKEN_CACHE_KEY = "NACOS_ACCESS_TOKEN_CACHE_KEY"; @@ -67,7 +69,15 @@ public long expireAfterRead(@NonNull String key, @NonNull LoginResult loginResul return currentDuration; } }) - .buildAsync((key, executor) -> loginInternal()); + .buildAsync((key, executor) -> { + try { + return loginInternal(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + private final HttpClient delegate; private final WebClient webClient; @@ -75,15 +85,16 @@ public long expireAfterRead(@NonNull String key, @NonNull LoginResult loginResul private final String password; - LoginClient(NacosClient nacosClient, String username, String password) { - requireNonNull(nacosClient, "nacosClient"); - webClient = nacosClient.nacosWebClient(); + LoginClient(HttpClient delegate, WebClient webClient, String username, String password) { + super(delegate); + this.delegate = requireNonNull(delegate, "delegate"); + this.webClient = requireNonNull(webClient, "webClient"); this.username = requireNonNull(username, "username"); this.password = requireNonNull(password, "password"); } - public CompletableFuture login() { + private CompletableFuture login() { return tokenCache.get(NACOS_ACCESS_TOKEN_CACHE_KEY) .thenApply(loginResult -> loginResult.accessToken); } @@ -100,6 +111,21 @@ private CompletableFuture loginInternal() { .execute(); } + @Override + public HttpResponse execute(ClientRequestContext ctx, HttpRequest req) { + CompletableFuture future = login().thenApply(accessToken -> { + try { + return delegate.execute(ctx, req.mapHeaders(headers -> headers.toBuilder() + .set(HttpHeaderNames.AUTHORIZATION, "Bearer " + accessToken) + .build())); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + return HttpResponse.of(future); + } + @JsonIgnoreProperties(ignoreUnknown = true) private static final class LoginResult { private final String accessToken; diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java index e587d7b4136..58ae35de9ea 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java @@ -51,17 +51,14 @@ public static NacosClientBuilder builder(URI nacosUri) { final WebClientBuilder builder = WebClient.builder(uri) .decorator(retryingClientDecorator); - webClient = builder.build(); - - final LoginClient loginClient; if (username != null && password != null) { - loginClient = LoginClient.of(this, username, password); - } else { - loginClient = null; + builder.decorator(LoginClient.newDecorator(builder.build(), username, password)); } - queryInstancesClient = QueryInstancesClient.of(this, loginClient, nacosApiVersion); - registerInstanceClient = RegisterInstanceClient.of(this, loginClient, nacosApiVersion); + webClient = builder.build(); + + queryInstancesClient = QueryInstancesClient.of(this, nacosApiVersion); + registerInstanceClient = RegisterInstanceClient.of(this, nacosApiVersion); } public CompletableFuture> endpoints(String serviceName, @Nullable String namespaceId, diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java index bcc56ebf6fc..a44cde14935 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java @@ -40,22 +40,16 @@ */ final class QueryInstancesClient { - static QueryInstancesClient of(NacosClient nacosClient, @Nullable LoginClient loginClient, - String nacosApiVersion) { - return new QueryInstancesClient(nacosClient, loginClient, nacosApiVersion); + static QueryInstancesClient of(NacosClient nacosClient, String nacosApiVersion) { + return new QueryInstancesClient(nacosClient, nacosApiVersion); } private final WebClient webClient; - @Nullable - private final LoginClient loginClient; - private final String nacosApiVersion; - QueryInstancesClient(NacosClient nacosClient, @Nullable LoginClient loginClient, - String nacosApiVersion) { + QueryInstancesClient(NacosClient nacosClient, String nacosApiVersion) { webClient = nacosClient.nacosWebClient(); - this.loginClient = loginClient; this.nacosApiVersion = nacosApiVersion; } @@ -79,21 +73,12 @@ CompletableFuture queryInstances(String serviceName, @Nu .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, healthyOnly, app, null, null, null); path.append(paramsBuilder.build().toQueryString()); - if (loginClient == null) { - return webClient.prepare() - .get(path.toString()) - .asJson(QueryInstancesResponse.class) - .as(HttpEntity::content) - .execute(); - } else { - return loginClient.login().thenCompose(accessToken -> - webClient.prepare() - .get(path.append("&accessToken=").append(accessToken).toString()) - .asJson(QueryInstancesResponse.class) - .as(HttpEntity::content) - .execute() - ); - } + + return webClient.prepare() + .get(path.toString()) + .asJson(QueryInstancesResponse.class) + .as(HttpEntity::content) + .execute(); } @Nullable diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java index a85fc50dfee..db6e83114ff 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java @@ -29,22 +29,17 @@ */ final class RegisterInstanceClient { - static RegisterInstanceClient of(NacosClient nacosClient, @Nullable LoginClient loginClient, + static RegisterInstanceClient of(NacosClient nacosClient, String nacosApiVersion) { - return new RegisterInstanceClient(nacosClient, loginClient, nacosApiVersion); + return new RegisterInstanceClient(nacosClient, nacosApiVersion); } private final WebClient webClient; - @Nullable - private final LoginClient loginClient; - private final String instanceApiPath; - RegisterInstanceClient(NacosClient nacosClient, @Nullable LoginClient loginClient, - String nacosApiVersion) { + RegisterInstanceClient(NacosClient nacosClient, String nacosApiVersion) { webClient = nacosClient.nacosWebClient(); - this.loginClient = loginClient; instanceApiPath = new StringBuilder("/").append(nacosApiVersion).append("/ns/instance").toString(); } @@ -60,26 +55,10 @@ HttpResponse register(String serviceName, String ip, int port, int weight, @Null .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, null, app, ip, port, weight); - if (loginClient == null) { - return webClient.prepare() - .post(instanceApiPath) - .content(MediaType.FORM_DATA, paramsBuilder.toQueryString()) - .execute(); - } else { - return HttpResponse.of( - loginClient.login() - .thenApply(accessToken -> { - final String paramsWithToken = paramsBuilder - .add("accessToken", accessToken) - .build() - .toQueryString(); - return webClient.prepare() - .post(instanceApiPath) - .content(MediaType.FORM_DATA, paramsWithToken) - .execute(); - }) - ); - } + return webClient.prepare() + .post(instanceApiPath) + .content(MediaType.FORM_DATA, paramsBuilder.toQueryString()) + .execute(); } /** @@ -94,25 +73,9 @@ HttpResponse deregister(String serviceName, String ip, int port, int weight, @Nu .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, null, app, ip, port, weight); - if (loginClient == null) { - return webClient.prepare() - .delete(instanceApiPath) - .content(MediaType.FORM_DATA, paramsBuilder.build().toQueryString()) - .execute(); - } else { - return HttpResponse.of( - loginClient.login() - .thenApply(accessToken -> { - final String paramsWithToken = paramsBuilder - .add("accessToken", accessToken) - .build() - .toQueryString(); - return webClient.prepare() - .delete(instanceApiPath) - .content(MediaType.FORM_DATA, paramsWithToken) - .execute(); - }) - ); - } + return webClient.prepare() + .delete(instanceApiPath) + .content(MediaType.FORM_DATA, paramsBuilder.build().toQueryString()) + .execute(); } } From 68e1137a97fce8abc45a36886698a9dc838ef7b1 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sat, 10 Feb 2024 20:31:47 +0900 Subject: [PATCH 07/25] LoginClient : Cache and reuse QueryStrings that were unnecessarily repeatedly produced. --- .../armeria/internal/nacos/LoginClient.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index 7d84fd05f47..0674acba52c 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -81,17 +81,17 @@ public long expireAfterRead(@NonNull String key, @NonNull LoginResult loginResul private final WebClient webClient; - private final String username; - - private final String password; + private final String queryParamsForLogin; LoginClient(HttpClient delegate, WebClient webClient, String username, String password) { super(delegate); this.delegate = requireNonNull(delegate, "delegate"); this.webClient = requireNonNull(webClient, "webClient"); - this.username = requireNonNull(username, "username"); - this.password = requireNonNull(password, "password"); + this.queryParamsForLogin = QueryParams.builder() + .add("username", requireNonNull(username, "username")) + .add("password", requireNonNull(password, "password")) + .toQueryString(); } private CompletableFuture login() { @@ -101,11 +101,7 @@ private CompletableFuture login() { private CompletableFuture loginInternal() { return webClient.prepare().post("/v1/auth/login") - .content(MediaType.FORM_DATA, - QueryParams.builder() - .add("username", username) - .add("password", password) - .toQueryString()) + .content(MediaType.FORM_DATA, queryParamsForLogin) .asJson(LoginResult.class) .as(HttpEntity::content) .execute(); From 0f718694cf09ce8f51861a426ce90186d52b5525 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sat, 10 Feb 2024 22:36:39 +0900 Subject: [PATCH 08/25] QueryInstancesClient : Cache and reuse request path that were unnecessarily repeatedly produced. --- .../client/nacos/NacosEndpointGroup.java | 45 ++-------------- .../nacos/NacosEndpointGroupBuilder.java | 36 +++---------- .../armeria/internal/nacos/NacosClient.java | 18 +++---- .../internal/nacos/NacosClientBuilder.java | 53 ++++++++++++++++++- .../internal/nacos/QueryInstancesClient.java | 42 +++++++-------- .../nacos/NacosUpdatingListenerBuilder.java | 2 +- .../nacos/NacosClientBuilderTest.java | 6 ++- .../armeria/internal/nacos/NacosTestBase.java | 25 ++++----- .../nacos/NacosUpdatingListenerTest.java | 12 ++--- 9 files changed, 117 insertions(+), 122 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java index a321b121a23..37ae1dc685a 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java @@ -70,41 +70,17 @@ public static NacosEndpointGroupBuilder builder(URI nacosUri, String serviceName private final NacosClient nacosClient; - private final String serviceName; - private final long registryFetchIntervalMillis; - @Nullable - private final String namespaceId; - - @Nullable - private final String groupName; - - @Nullable - private final String clusterName; - - @Nullable - private final String app; - - private final boolean useHealthyEndpoints; - @Nullable private volatile ScheduledFuture scheduledFuture; NacosEndpointGroup(EndpointSelectionStrategy selectionStrategy, boolean allowEmptyEndpoints, - long selectionTimeoutMillis, NacosClient nacosClient, String serviceName, - long registryFetchIntervalMillis, @Nullable String namespaceId, - @Nullable String groupName, @Nullable String clusterName, - @Nullable String app, boolean useHealthyEndpoints) { + long selectionTimeoutMillis, NacosClient nacosClient, + long registryFetchIntervalMillis) { super(selectionStrategy, allowEmptyEndpoints, selectionTimeoutMillis); this.nacosClient = requireNonNull(nacosClient, "nacosClient"); - this.serviceName = requireNonNull(serviceName, "serviceName"); this.registryFetchIntervalMillis = registryFetchIntervalMillis; - this.namespaceId = namespaceId; - this.groupName = groupName; - this.clusterName = clusterName; - this.app = app; - this.useHealthyEndpoints = useHealthyEndpoints; update(); } @@ -118,8 +94,7 @@ private void update() { final EventLoop eventLoop; try (ClientRequestContextCaptor captor = Clients.newContextCaptor()) { - response = nacosClient.endpoints(serviceName, namespaceId, groupName, clusterName, - useHealthyEndpoints, app); + response = nacosClient.endpoints(); eventLoop = captor.get().eventLoop().withoutContext(); } @@ -128,14 +103,12 @@ private void update() { return null; } if (cause != null) { - logger.warn("Unexpected exception while fetching the registry from: {}" + - " (serviceName: {})", nacosClient.uri(), serviceName, cause); + logger.warn("Unexpected exception while fetching the registry from: {}", nacosClient.uri(), cause); } else { setEndpoints(endpoints); } - scheduledFuture = eventLoop.schedule(this::update, - registryFetchIntervalMillis, TimeUnit.MILLISECONDS); + scheduledFuture = eventLoop.schedule(this::update, registryFetchIntervalMillis, TimeUnit.MILLISECONDS); return null; }); } @@ -153,15 +126,7 @@ protected void doCloseAsync(CompletableFuture future) { public String toString() { return MoreObjects.toStringHelper(this) .omitNullValues() - .add("serviceName", serviceName) .add("registryFetchIntervalMillis", registryFetchIntervalMillis) - .add("namespaceId", namespaceId) - .add("groupName", groupName) - .add("clusterName", clusterName) - .add("app", app) - .add("useHealthyEndpoints", useHealthyEndpoints) - .add("clusterName", clusterName) - .add("serviceName", serviceName) .toString(); } } diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java index 179cca3a3cb..ab689a5977b 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java @@ -25,7 +25,6 @@ import com.linecorp.armeria.client.endpoint.AbstractDynamicEndpointGroupBuilder; import com.linecorp.armeria.client.endpoint.EndpointSelectionStrategy; import com.linecorp.armeria.common.Flags; -import com.linecorp.armeria.common.annotation.Nullable; import com.linecorp.armeria.internal.nacos.NacosClient; import com.linecorp.armeria.internal.nacos.NacosClientBuilder; @@ -45,30 +44,13 @@ public class NacosEndpointGroupBuilder extends AbstractDynamicEndpointGroupBuild private EndpointSelectionStrategy selectionStrategy = EndpointSelectionStrategy.weightedRoundRobin(); - private final String serviceName; - private long registryFetchIntervalMillis = DEFAULT_CHECK_INTERVAL_MILLIS; - @Nullable - private String namespaceId; - - @Nullable - private String groupName; - - @Nullable - private String clusterName; - - @Nullable - private String app; - - private boolean useHealthyEndpoints; - private final NacosClientBuilder nacosClientBuilder; NacosEndpointGroupBuilder(URI nacosUri, String serviceName) { super(Flags.defaultResponseTimeoutMillis()); - this.serviceName = requireNonNull(serviceName, "serviceName"); - nacosClientBuilder = NacosClient.builder(nacosUri); + nacosClientBuilder = NacosClient.builder(nacosUri, requireNonNull(serviceName, "serviceName")); } /** @@ -85,7 +67,7 @@ public NacosEndpointGroupBuilder selectionStrategy(EndpointSelectionStrategy sel * that match the specified 'namespaceId' value. */ public NacosEndpointGroupBuilder namespaceId(String namespaceId) { - this.namespaceId = requireNonNull(namespaceId, "namespaceId"); + nacosClientBuilder.namespaceId(namespaceId); return this; } @@ -95,7 +77,7 @@ public NacosEndpointGroupBuilder namespaceId(String namespaceId) { * that match the specified 'groupName' value. */ public NacosEndpointGroupBuilder groupName(String groupName) { - this.groupName = requireNonNull(groupName); + nacosClientBuilder.groupName(groupName); return this; } @@ -105,7 +87,7 @@ public NacosEndpointGroupBuilder groupName(String groupName) { * that match the specified 'clusterName' value. */ public NacosEndpointGroupBuilder clusterName(String clusterName) { - this.clusterName = requireNonNull(clusterName); + nacosClientBuilder.clusterName(clusterName); return this; } @@ -115,7 +97,7 @@ public NacosEndpointGroupBuilder clusterName(String clusterName) { * to query only instances that match the specified 'app' value. */ public NacosEndpointGroupBuilder app(String app) { - this.app = requireNonNull(app); + nacosClientBuilder.app(app); return this; } @@ -125,7 +107,7 @@ public NacosEndpointGroupBuilder app(String app) { * If not set, false is used by default. */ public NacosEndpointGroupBuilder useHealthyEndpoints(boolean useHealthyEndpoints) { - this.useHealthyEndpoints = useHealthyEndpoints; + nacosClientBuilder.healthyOnly(useHealthyEndpoints); return this; } @@ -170,10 +152,8 @@ public NacosEndpointGroupBuilder authorization(String username, String password) * Returns a newly-created {@link NacosEndpointGroup}. */ public NacosEndpointGroup build() { - return new NacosEndpointGroup(selectionStrategy, shouldAllowEmptyEndpoints(), - selectionTimeoutMillis(), nacosClientBuilder.build(), - serviceName, registryFetchIntervalMillis, namespaceId, - groupName, clusterName, app, useHealthyEndpoints); + return new NacosEndpointGroup(selectionStrategy, shouldAllowEmptyEndpoints(), selectionTimeoutMillis(), + nacosClientBuilder.build(), registryFetchIntervalMillis); } @Override diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java index 58ae35de9ea..22380ff8f3d 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java @@ -37,8 +37,8 @@ public final class NacosClient { .maxTotalAttempts(3) .build()); - public static NacosClientBuilder builder(URI nacosUri) { - return new NacosClientBuilder(nacosUri); + public static NacosClientBuilder builder(URI nacosUri, String serviceName) { + return new NacosClientBuilder(nacosUri, serviceName); } private final WebClient webClient; @@ -47,7 +47,9 @@ public static NacosClientBuilder builder(URI nacosUri) { private final RegisterInstanceClient registerInstanceClient; - NacosClient(URI uri, String nacosApiVersion, @Nullable String username, @Nullable String password) { + NacosClient(URI uri, String nacosApiVersion, @Nullable String username, @Nullable String password, + String serviceName, @Nullable String namespaceId, @Nullable String groupName, + @Nullable String clusterName, @Nullable Boolean healthyOnly, @Nullable String app) { final WebClientBuilder builder = WebClient.builder(uri) .decorator(retryingClientDecorator); @@ -57,15 +59,13 @@ public static NacosClientBuilder builder(URI nacosUri) { webClient = builder.build(); - queryInstancesClient = QueryInstancesClient.of(this, nacosApiVersion); + queryInstancesClient = QueryInstancesClient.of(this, nacosApiVersion, serviceName, namespaceId, + groupName, clusterName, healthyOnly, app); registerInstanceClient = RegisterInstanceClient.of(this, nacosApiVersion); } - public CompletableFuture> endpoints(String serviceName, @Nullable String namespaceId, - @Nullable String groupName, @Nullable String clusterName, - @Nullable Boolean healthyOnly, @Nullable String app) { - return queryInstancesClient.endpoints(serviceName, namespaceId, groupName, - clusterName, healthyOnly, app); + public CompletableFuture> endpoints() { + return queryInstancesClient.endpoints(); } /** diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java index dc99b759cbf..a9293a22cf0 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java @@ -38,8 +38,26 @@ public final class NacosClientBuilder implements NacosConfigSetters { @Nullable private String password; - NacosClientBuilder(URI nacosUri) { + final private String serviceName; + + @Nullable + private String namespaceId; + + @Nullable + private String groupName; + + @Nullable + private String clusterName; + + @Nullable + private Boolean healthyOnly; + + @Nullable + private String app; + + NacosClientBuilder(URI nacosUri, String serviceName) { this.nacosUri = requireNonNull(nacosUri, "nacosUri"); + this.serviceName = requireNonNull(serviceName, "serviceName"); } @Override @@ -63,6 +81,36 @@ public NacosClientBuilder authorization(String username, String password) { return this; } + public NacosClientBuilder namespaceId(String namespaceId) { + this.namespaceId = requireNonNull(namespaceId); + + return this; + } + + public NacosClientBuilder groupName(String groupName) { + this.groupName = requireNonNull(groupName); + + return this; + } + + public NacosClientBuilder clusterName(String clusterName) { + this.clusterName = requireNonNull(clusterName); + + return this; + } + + public NacosClientBuilder healthyOnly(Boolean healthyOnly) { + this.healthyOnly = requireNonNull(healthyOnly); + + return this; + } + + public NacosClientBuilder app(String app) { + this.app = requireNonNull(app); + + return this; + } + public NacosClient build() { final URI uri; try { @@ -72,6 +120,7 @@ public NacosClient build() { throw new IllegalArgumentException(e); } - return new NacosClient(uri, nacosApiVersion, username, password); + return new NacosClient(uri, nacosApiVersion, username, password, serviceName, namespaceId, + groupName, clusterName, healthyOnly, app); } } diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java index a44cde14935..cccda153637 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java @@ -40,42 +40,42 @@ */ final class QueryInstancesClient { - static QueryInstancesClient of(NacosClient nacosClient, String nacosApiVersion) { - return new QueryInstancesClient(nacosClient, nacosApiVersion); + static QueryInstancesClient of(NacosClient nacosClient, String nacosApiVersion, String serviceName, + @Nullable String namespaceId, @Nullable String groupName, + @Nullable String clusterName, @Nullable Boolean healthyOnly, @Nullable String app) { + return new QueryInstancesClient(nacosClient, nacosApiVersion, serviceName, namespaceId, groupName, clusterName, + healthyOnly, app); } private final WebClient webClient; - private final String nacosApiVersion; + private final String pathForQuery; - QueryInstancesClient(NacosClient nacosClient, String nacosApiVersion) { + QueryInstancesClient(NacosClient nacosClient, String nacosApiVersion, String serviceName, + @Nullable String namespaceId, @Nullable String groupName, + @Nullable String clusterName, @Nullable Boolean healthyOnly, @Nullable String app) { webClient = nacosClient.nacosWebClient(); - this.nacosApiVersion = nacosApiVersion; + + final StringBuilder pathBuilder = new StringBuilder("/").append(nacosApiVersion).append("/ns/instance/list?"); + final QueryParamsBuilder paramsBuilder = NacosClientUtil + .queryParamsBuilder(namespaceId, groupName, requireNonNull(serviceName, "serviceName"), + clusterName, healthyOnly, app, null, null, null); + pathBuilder.append(paramsBuilder.build().toQueryString()); + + this.pathForQuery = pathBuilder.toString(); } - CompletableFuture> endpoints(String serviceName, @Nullable String namespaceId, - @Nullable String groupName, @Nullable String clusterName, - @Nullable Boolean healthyOnly, @Nullable String app) { - requireNonNull(serviceName, "serviceName"); - return queryInstances(serviceName, namespaceId, groupName, clusterName, healthyOnly, app) + CompletableFuture> endpoints() { + return queryInstances() .thenApply(response -> response.data.hosts.stream() .map(QueryInstancesClient::toEndpoint) .filter(Objects::nonNull) .collect(toImmutableList())); } - CompletableFuture queryInstances(String serviceName, @Nullable String namespaceId, - @Nullable String groupName, @Nullable String clusterName, - @Nullable Boolean healthyOnly, @Nullable String app) { - requireNonNull(serviceName, "serviceName"); - final StringBuilder path = new StringBuilder("/").append(nacosApiVersion).append("/ns/instance/list?"); - final QueryParamsBuilder paramsBuilder = NacosClientUtil - .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, healthyOnly, app, null, - null, null); - path.append(paramsBuilder.build().toQueryString()); - + CompletableFuture queryInstances() { return webClient.prepare() - .get(path.toString()) + .get(pathForQuery) .asJson(QueryInstancesResponse.class) .as(HttpEntity::content) .execute(); diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java index fdecbf52043..0258cdabdc3 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java @@ -69,7 +69,7 @@ public final class NacosUpdatingListenerBuilder implements NacosConfigSetters { NacosUpdatingListenerBuilder(URI nacosUri, String serviceName) { this.serviceName = requireNonNull(serviceName, "serviceName"); checkArgument(!this.serviceName.isEmpty(), "serviceName can't be empty"); - nacosClientBuilder = NacosClient.builder(nacosUri); + nacosClientBuilder = NacosClient.builder(nacosUri, serviceName); } /** diff --git a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java index a1f9c5643c2..4a41bade121 100644 --- a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java @@ -16,6 +16,7 @@ package com.linecorp.armeria.internal.nacos; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; import java.net.URI; @@ -39,7 +40,8 @@ void gets403WhenNoToken() throws Exception { @Test void nacosApiVersionCanNotStartsWithSlash() { assertThrows(IllegalArgumentException.class, () -> - NacosClient.builder(URI.create("http://localhost:8500")).nacosApiVersion("/v1")); - NacosClient.builder(URI.create("http://localhost:8500")).nacosApiVersion("v1"); + NacosClient.builder(URI.create("http://localhost:8500"), serviceName).nacosApiVersion("/v1")); + assertDoesNotThrow(() -> + NacosClient.builder(URI.create("http://localhost:8500"), serviceName).nacosApiVersion("v1")); } } diff --git a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java index 21884adcd83..a6c285be683 100644 --- a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java +++ b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java @@ -77,25 +77,26 @@ protected static List newSampleEndpoints() { protected NacosTestBase() {} - @Nullable - private static NacosClient nacosClient; - @BeforeAll - static void start() throws Throwable { + static void start() { // Initialize Nacos Client nacosUri = URI.create( "http://" + nacosContainer.getHost() + ':' + nacosContainer.getMappedPort(8848)); - - nacosClient = NacosClient.builder(nacosUri) - .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) - .build(); } - protected static NacosClient client() { - if (nacosClient == null) { - throw new IllegalStateException("nacos client has not initialized"); + protected static NacosClient client(@Nullable String serviceName, @Nullable String groupName) { + final NacosClientBuilder builder; + if (serviceName != null) { + builder = NacosClient.builder(nacosUri, serviceName); + } else { + builder = NacosClient.builder(nacosUri, NacosTestBase.serviceName); } - return nacosClient; + + if (groupName != null) { + builder.groupName(groupName); + } + return builder.authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .build(); } protected static URI nacosUri() { diff --git a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java index e24d99ce410..dd06997670c 100644 --- a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java @@ -86,7 +86,7 @@ void testBuild() { void testEndpointsCountOfListeningServiceWithAServerStopAndStart() { // Checks sample endpoints created when initialized. await().untilAsserted(() -> { - assertThat(client().endpoints(serviceName, null, null, null, null, null) + assertThat(client(null, null).endpoints() .join()).hasSameSizeAs(sampleEndpoints); }); @@ -94,8 +94,8 @@ void testEndpointsCountOfListeningServiceWithAServerStopAndStart() { servers.get(0).stop().join(); await().untilAsserted(() -> { - final List results = client() - .endpoints(serviceName, null, null, null, null, null).join(); + final List results = client(null, null) + .endpoints().join(); assertThat(results).hasSize(sampleEndpoints.size() - 1); }); @@ -103,7 +103,7 @@ void testEndpointsCountOfListeningServiceWithAServerStopAndStart() { servers.get(0).start().join(); await().untilAsserted(() -> { - assertThat(client().endpoints(serviceName, null, null, null, null, null) + assertThat(client(null, null).endpoints() .join()).hasSameSizeAs(sampleEndpoints); }); } @@ -127,9 +127,7 @@ void testThatGroupNameIsSpecified() { server.addListener(listener); server.start().join(); await().untilAsserted(() -> { - assertThat(client().endpoints("testThatGroupNameIsSpecified", - null, "groupName", null, - null, null).join()) + assertThat(client("testThatGroupNameIsSpecified", "groupName").endpoints().join()) .hasSize(1); }); server.stop(); From 46abd6dfb5d23525c835c7f37a56a75d4fd04e14 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sat, 10 Feb 2024 23:16:08 +0900 Subject: [PATCH 09/25] Reduce the parameters of the RegisterInstanceClient APIS, by moving all immutable parameters to properties. --- .../armeria/internal/nacos/NacosClient.java | 27 +++------- .../nacos/RegisterInstanceClient.java | 50 ++++++++++++------- .../server/nacos/NacosUpdatingListener.java | 29 ++--------- .../nacos/NacosUpdatingListenerBuilder.java | 23 ++------- 4 files changed, 48 insertions(+), 81 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java index 22380ff8f3d..cd0fabdbbfe 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java @@ -61,7 +61,8 @@ public static NacosClientBuilder builder(URI nacosUri, String serviceName) { queryInstancesClient = QueryInstancesClient.of(this, nacosApiVersion, serviceName, namespaceId, groupName, clusterName, healthyOnly, app); - registerInstanceClient = RegisterInstanceClient.of(this, nacosApiVersion); + registerInstanceClient = RegisterInstanceClient.of(this, nacosApiVersion, serviceName, namespaceId, + groupName, clusterName, app); } public CompletableFuture> endpoints() { @@ -71,37 +72,21 @@ public CompletableFuture> endpoints() { /** * Registers a instance to Nacos with service name. * - * @param serviceName a service name that identifying a service. * @param endpoint an endpoint of service to register - * @param namespaceId the namespace ID of the service instance. - * @param groupName the group name of the service. - * @param clusterName the cluster name of the service. - * @param app the application name associated with the service. * @return a {@link HttpResponse} indicating the result of the registration operation. */ - public HttpResponse register(String serviceName, Endpoint endpoint, @Nullable String namespaceId, - @Nullable String groupName, @Nullable String clusterName, - @Nullable String app) { - return registerInstanceClient.register(serviceName, endpoint.host(), endpoint.port(), endpoint.weight(), - namespaceId, groupName, clusterName, app); + public HttpResponse register(Endpoint endpoint) { + return registerInstanceClient.register(endpoint.host(), endpoint.port(), endpoint.weight()); } /** * De-registers a instance to Nacos with service name. * - * @param serviceName a service name that identifying a service. * @param endpoint an endpoint of service to register - * @param namespaceId the namespace ID of the service instance. - * @param groupName the group name of the service. - * @param clusterName the cluster name of the service. - * @param app the application name associated with the service. * @return a {@link HttpResponse} indicating the result of the de-registration operation. */ - public HttpResponse deregister(String serviceName, Endpoint endpoint, @Nullable String namespaceId, - @Nullable String groupName, @Nullable String clusterName, - @Nullable String app) { - return registerInstanceClient.deregister(serviceName, endpoint.host(), endpoint.port(), - endpoint.weight(), namespaceId, groupName, clusterName, app); + public HttpResponse deregister(Endpoint endpoint) { + return registerInstanceClient.deregister(endpoint.host(), endpoint.port(), endpoint.weight()); } /** diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java index db6e83114ff..fa2f1c22d20 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java @@ -29,49 +29,65 @@ */ final class RegisterInstanceClient { - static RegisterInstanceClient of(NacosClient nacosClient, - String nacosApiVersion) { - return new RegisterInstanceClient(nacosClient, nacosApiVersion); + static RegisterInstanceClient of(NacosClient nacosClient, String nacosApiVersion, String serviceName, + @Nullable String namespaceId, @Nullable String groupName, + @Nullable String clusterName, @Nullable String app) { + return new RegisterInstanceClient(nacosClient, nacosApiVersion, serviceName, namespaceId, groupName, + clusterName, app); } private final WebClient webClient; private final String instanceApiPath; - RegisterInstanceClient(NacosClient nacosClient, String nacosApiVersion) { + private final String serviceName; + + @Nullable + private final String namespaceId; + + @Nullable + private final String groupName; + + @Nullable + private final String clusterName; + + @Nullable + private final String app; + + RegisterInstanceClient(NacosClient nacosClient, String nacosApiVersion, String serviceName, + @Nullable String namespaceId, @Nullable String groupName, + @Nullable String clusterName, @Nullable String app) { webClient = nacosClient.nacosWebClient(); instanceApiPath = new StringBuilder("/").append(nacosApiVersion).append("/ns/instance").toString(); + + this.serviceName = requireNonNull(serviceName, "serviceName"); + this.namespaceId = namespaceId; + this.groupName = groupName; + this.clusterName = clusterName; + this.app = app; } /** * Registers a service into the Nacos. */ - HttpResponse register(String serviceName, String ip, int port, int weight, @Nullable String namespaceId, - @Nullable String groupName, @Nullable String clusterName, @Nullable String app) { - requireNonNull(serviceName, "serviceName"); - requireNonNull(ip, "ip"); - + HttpResponse register(String ip, int port, int weight) { final QueryParamsBuilder paramsBuilder = NacosClientUtil .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, null, app, - ip, port, weight); + requireNonNull(ip, "ip"), port, weight); return webClient.prepare() .post(instanceApiPath) - .content(MediaType.FORM_DATA, paramsBuilder.toQueryString()) + .content(MediaType.FORM_DATA, paramsBuilder.build().toQueryString()) .execute(); } /** * De-registers a service from the Nacos. */ - HttpResponse deregister(String serviceName, String ip, int port, int weight, @Nullable String namespaceId, - @Nullable String groupName, @Nullable String clusterName, @Nullable String app) { - requireNonNull(serviceName, "serviceName"); - requireNonNull(ip, "ip"); - + HttpResponse deregister(String ip, int port, int weight) { final QueryParamsBuilder paramsBuilder = NacosClientUtil .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, null, app, - ip, port, weight); + requireNonNull(ip, "ip"), port, weight); return webClient.prepare() .delete(instanceApiPath) diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java index ca416ad18f1..47dbf947282 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java @@ -54,41 +54,20 @@ public static NacosUpdatingListenerBuilder builder(URI nacosUri, String serviceN private final NacosClient nacosClient; - private final String serviceName; - @Nullable private final Endpoint endpoint; - @Nullable - private final String namespaceId; - - @Nullable - private final String groupName; - - @Nullable - private final String clusterName; - - @Nullable - private final String app; - private volatile boolean isRegistered; - NacosUpdatingListener(NacosClient nacosClient, String serviceName, @Nullable Endpoint endpoint, - @Nullable String namespaceId, @Nullable String groupName, - @Nullable String clusterName, @Nullable String app) { + NacosUpdatingListener(NacosClient nacosClient, @Nullable Endpoint endpoint) { this.nacosClient = requireNonNull(nacosClient, "nacosClient"); - this.serviceName = requireNonNull(serviceName, "serviceName"); this.endpoint = endpoint; - this.namespaceId = namespaceId; - this.groupName = groupName; - this.clusterName = clusterName; - this.app = app; } @Override - public void serverStarted(Server server) throws Exception { + public void serverStarted(Server server) { final Endpoint endpoint = getEndpoint(server); - nacosClient.register(serviceName, endpoint, namespaceId, groupName, clusterName, app) + nacosClient.register(endpoint) .aggregate() .handle((res, cause) -> { if (cause != null) { @@ -144,7 +123,7 @@ private static void warnIfInactivePort(Server server, int port) { public void serverStopping(Server server) { final Endpoint endpoint = getEndpoint(server); if (isRegistered) { - nacosClient.deregister(serviceName, endpoint, namespaceId, groupName, clusterName, app) + nacosClient.deregister(endpoint) .aggregate() .handle((res, cause) -> { if (cause != null) { diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java index 0258cdabdc3..3eb85fd56b9 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java @@ -46,18 +46,6 @@ public final class NacosUpdatingListenerBuilder implements NacosConfigSetters { @Nullable private Endpoint endpoint; - @Nullable - private String namespaceId; - - @Nullable - private String groupName; - - @Nullable - private String clusterName; - - @Nullable - private String app; - private final NacosClientBuilder nacosClientBuilder; /** @@ -88,7 +76,7 @@ public NacosUpdatingListenerBuilder endpoint(Endpoint endpoint) { * @param namespaceId the namespace ID to register. */ public NacosUpdatingListenerBuilder namespaceId(String namespaceId) { - this.namespaceId = requireNonNull(namespaceId, "namespaceId"); + nacosClientBuilder.namespaceId(namespaceId); return this; } @@ -98,7 +86,7 @@ public NacosUpdatingListenerBuilder namespaceId(String namespaceId) { * @param groupName the group name of the instance. */ public NacosUpdatingListenerBuilder groupName(String groupName) { - this.groupName = requireNonNull(groupName, "groupName"); + nacosClientBuilder.groupName(groupName); return this; } @@ -108,7 +96,7 @@ public NacosUpdatingListenerBuilder groupName(String groupName) { * @param clusterName the cluster name of the instance. */ public NacosUpdatingListenerBuilder clusterName(String clusterName) { - this.clusterName = requireNonNull(clusterName, "clusterName"); + nacosClientBuilder.clusterName(clusterName); return this; } @@ -118,7 +106,7 @@ public NacosUpdatingListenerBuilder clusterName(String clusterName) { * @param app app name of the instance. */ public NacosUpdatingListenerBuilder app(String app) { - this.app = requireNonNull(app, "app"); + nacosClientBuilder.app(app); return this; } @@ -139,7 +127,6 @@ public NacosUpdatingListenerBuilder authorization(String username, String passwo * Nacos when the {@link Server} starts. */ public NacosUpdatingListener build() { - return new NacosUpdatingListener(nacosClientBuilder.build(), serviceName, endpoint, namespaceId, - groupName, clusterName, app); + return new NacosUpdatingListener(nacosClientBuilder.build(), endpoint); } } From adb40fc2ecaf6ce73f982ea0afc473a04e47b0ae Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sat, 10 Feb 2024 23:20:03 +0900 Subject: [PATCH 10/25] Revert "Modified NacosClientUtil::queryParams to return QueryParamsBuilder instead" --- .../armeria/internal/nacos/NacosClientUtil.java | 12 ++++++------ .../internal/nacos/QueryInstancesClient.java | 8 ++++---- .../internal/nacos/RegisterInstanceClient.java | 14 +++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java index 1f171e8193a..bd4a2f38b08 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java @@ -44,11 +44,11 @@ final class NacosClientUtil { /** * Encodes common Nacos API parameters as {@code QueryParamsBuilder}. */ - static QueryParamsBuilder queryParamsBuilder(@Nullable String namespaceId, @Nullable String groupName, - @Nullable String serviceName, @Nullable String clusterName, - @Nullable Boolean healthyOnly, @Nullable String app, - @Nullable String ip, @Nullable Integer port, - @Nullable Integer weight) { + static QueryParams queryParams(@Nullable String namespaceId, @Nullable String groupName, + @Nullable String serviceName, @Nullable String clusterName, + @Nullable Boolean healthyOnly, @Nullable String app, + @Nullable String ip, @Nullable Integer port, + @Nullable Integer weight) { final QueryParamsBuilder paramsBuilder = QueryParams.builder(); if (namespaceId != null) { paramsBuilder.add(NAMESPACE_ID_PARAM, namespaceId); @@ -77,7 +77,7 @@ static QueryParamsBuilder queryParamsBuilder(@Nullable String namespaceId, @Null if (weight != null) { paramsBuilder.add(WEIGHT_PARAM, weight.toString()); } - return paramsBuilder; + return paramsBuilder.build(); } private NacosClientUtil() {} diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java index cccda153637..b048bab49f4 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java @@ -31,7 +31,7 @@ import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.common.HttpEntity; -import com.linecorp.armeria.common.QueryParamsBuilder; +import com.linecorp.armeria.common.QueryParams; import com.linecorp.armeria.common.annotation.Nullable; /** @@ -57,10 +57,10 @@ static QueryInstancesClient of(NacosClient nacosClient, String nacosApiVersion, webClient = nacosClient.nacosWebClient(); final StringBuilder pathBuilder = new StringBuilder("/").append(nacosApiVersion).append("/ns/instance/list?"); - final QueryParamsBuilder paramsBuilder = NacosClientUtil - .queryParamsBuilder(namespaceId, groupName, requireNonNull(serviceName, "serviceName"), + final QueryParams params = NacosClientUtil + .queryParams(namespaceId, groupName, requireNonNull(serviceName, "serviceName"), clusterName, healthyOnly, app, null, null, null); - pathBuilder.append(paramsBuilder.build().toQueryString()); + pathBuilder.append(params.toQueryString()); this.pathForQuery = pathBuilder.toString(); } diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java index fa2f1c22d20..0a29d27d0c7 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java @@ -20,7 +20,7 @@ import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.common.HttpResponse; import com.linecorp.armeria.common.MediaType; -import com.linecorp.armeria.common.QueryParamsBuilder; +import com.linecorp.armeria.common.QueryParams; import com.linecorp.armeria.common.annotation.Nullable; /** @@ -71,13 +71,13 @@ static RegisterInstanceClient of(NacosClient nacosClient, String nacosApiVersion * Registers a service into the Nacos. */ HttpResponse register(String ip, int port, int weight) { - final QueryParamsBuilder paramsBuilder = NacosClientUtil - .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, null, app, + final QueryParams params = NacosClientUtil + .queryParams(namespaceId, groupName, serviceName, clusterName, null, app, requireNonNull(ip, "ip"), port, weight); return webClient.prepare() .post(instanceApiPath) - .content(MediaType.FORM_DATA, paramsBuilder.build().toQueryString()) + .content(MediaType.FORM_DATA, params.toQueryString()) .execute(); } @@ -85,13 +85,13 @@ HttpResponse register(String ip, int port, int weight) { * De-registers a service from the Nacos. */ HttpResponse deregister(String ip, int port, int weight) { - final QueryParamsBuilder paramsBuilder = NacosClientUtil - .queryParamsBuilder(namespaceId, groupName, serviceName, clusterName, null, app, + final QueryParams params = NacosClientUtil + .queryParams(namespaceId, groupName, serviceName, clusterName, null, app, requireNonNull(ip, "ip"), port, weight); return webClient.prepare() .delete(instanceApiPath) - .content(MediaType.FORM_DATA, paramsBuilder.build().toQueryString()) + .content(MediaType.FORM_DATA, params.toQueryString()) .execute(); } } From 0120fd78ab312962d09a0dbf8224d1d291366c15 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sun, 11 Feb 2024 14:19:47 +0900 Subject: [PATCH 11/25] To reuse access-token, instead of the Caffeine cache, use own implementation. --- nacos/build.gradle.kts | 1 - .../client/nacos/NacosEndpointGroup.java | 6 +- .../armeria/internal/nacos/LoginClient.java | 96 ++++++++++--------- .../internal/nacos/NacosClientBuilder.java | 2 +- .../internal/nacos/QueryInstancesClient.java | 11 ++- .../nacos/NacosUpdatingListenerTest.java | 6 +- 6 files changed, 63 insertions(+), 59 deletions(-) diff --git a/nacos/build.gradle.kts b/nacos/build.gradle.kts index aae19538681..84411a8250d 100644 --- a/nacos/build.gradle.kts +++ b/nacos/build.gradle.kts @@ -1,4 +1,3 @@ dependencies { - implementation(libs.caffeine) testImplementation(libs.testcontainers.junit.jupiter) } diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java index 37ae1dc685a..ecedb5647f3 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java @@ -103,12 +103,14 @@ private void update() { return null; } if (cause != null) { - logger.warn("Unexpected exception while fetching the registry from: {}", nacosClient.uri(), cause); + logger.warn("Unexpected exception while fetching the registry from: {}", + nacosClient.uri(), cause); } else { setEndpoints(endpoints); } - scheduledFuture = eventLoop.schedule(this::update, registryFetchIntervalMillis, TimeUnit.MILLISECONDS); + scheduledFuture = eventLoop.schedule(this::update, + registryFetchIntervalMillis, TimeUnit.MILLISECONDS); return null; }); } diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index 0674acba52c..951f134d902 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -19,23 +19,25 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Function; - -import com.linecorp.armeria.client.ClientRequestContext; -import com.linecorp.armeria.client.HttpClient; -import com.linecorp.armeria.client.SimpleDecoratingHttpClient; -import com.linecorp.armeria.client.WebClient; -import com.linecorp.armeria.common.*; -import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.function.Supplier; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.github.benmanes.caffeine.cache.AsyncLoadingCache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Expiry; import com.google.common.base.MoreObjects; +import com.linecorp.armeria.client.ClientRequestContext; +import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.SimpleDecoratingHttpClient; +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.HttpEntity; +import com.linecorp.armeria.common.HttpHeaderNames; +import com.linecorp.armeria.common.HttpRequest; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.MediaType; +import com.linecorp.armeria.common.QueryParams; import com.linecorp.armeria.common.annotation.Nullable; +import com.linecorp.armeria.common.util.UnmodifiableFuture; /** * A Nacos client that is responsible for @@ -47,42 +49,14 @@ public static Function newDecorator(WebClient w return delegate -> new LoginClient(delegate, webClient, username, password); } - private static final String NACOS_ACCESS_TOKEN_CACHE_KEY = "NACOS_ACCESS_TOKEN_CACHE_KEY"; - private final AsyncLoadingCache tokenCache = Caffeine.newBuilder() - .maximumSize(1) - .expireAfter(new Expiry() { - @Override - public long expireAfterCreate(@NonNull String key, @NonNull LoginResult loginResult, - long currentTime) { - return loginResult.tokenTtl.longValue(); - } - - @Override - public long expireAfterUpdate(@NonNull String key, @NonNull LoginResult loginResult, - long currentTime, long currentDuration) { - return loginResult.tokenTtl.longValue(); - } - - @Override - public long expireAfterRead(@NonNull String key, @NonNull LoginResult loginResult, - long currentTime, long currentDuration) { - return currentDuration; - } - }) - .buildAsync((key, executor) -> { - try { - return loginInternal(); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - private final HttpClient delegate; private final WebClient webClient; private final String queryParamsForLogin; + private final CachedLoginResult cachedLoginResult = new CachedLoginResult(); + LoginClient(HttpClient delegate, WebClient webClient, String username, String password) { super(delegate); @@ -94,12 +68,7 @@ public long expireAfterRead(@NonNull String key, @NonNull LoginResult loginResul .toQueryString(); } - private CompletableFuture login() { - return tokenCache.get(NACOS_ACCESS_TOKEN_CACHE_KEY) - .thenApply(loginResult -> loginResult.accessToken); - } - - private CompletableFuture loginInternal() { + private CompletableFuture login() { return webClient.prepare().post("/v1/auth/login") .content(MediaType.FORM_DATA, queryParamsForLogin) .asJson(LoginResult.class) @@ -109,7 +78,7 @@ private CompletableFuture loginInternal() { @Override public HttpResponse execute(ClientRequestContext ctx, HttpRequest req) { - CompletableFuture future = login().thenApply(accessToken -> { + final CompletableFuture future = cachedLoginResult.get().thenApply(accessToken -> { try { return delegate.execute(ctx, req.mapHeaders(headers -> headers.toBuilder() .set(HttpHeaderNames.AUTHORIZATION, "Bearer " + accessToken) @@ -122,6 +91,39 @@ public HttpResponse execute(ClientRequestContext ctx, HttpRequest req) { return HttpResponse.of(future); } + private final class CachedLoginResult { + @Nullable + private volatile CacheEntry cacheEntry; + + CachedLoginResult() { } + + CompletableFuture get() { + final CacheEntry cacheEntry = this.cacheEntry; + if (cacheEntry != null && !cacheEntry.isExpired()) { + return UnmodifiableFuture.completedFuture(cacheEntry.loginResult.accessToken); + } + + return login().thenApply(loginResult -> { + this.cacheEntry = new CacheEntry(loginResult); + return loginResult.accessToken; + }); + } + + private class CacheEntry { + private final LoginResult loginResult; + private final long expirationTimeMillis; + + CacheEntry(LoginResult loginResult) { + this.loginResult = loginResult; + this.expirationTimeMillis = System.currentTimeMillis() + loginResult.tokenTtl * 1000; + } + + boolean isExpired() { + return System.currentTimeMillis() >= expirationTimeMillis; + } + } + } + @JsonIgnoreProperties(ignoreUnknown = true) private static final class LoginResult { private final String accessToken; diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java index a9293a22cf0..eebc614577e 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java @@ -38,7 +38,7 @@ public final class NacosClientBuilder implements NacosConfigSetters { @Nullable private String password; - final private String serviceName; + private final String serviceName; @Nullable private String namespaceId; diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java index b048bab49f4..38b7ce16f98 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java @@ -42,9 +42,10 @@ final class QueryInstancesClient { static QueryInstancesClient of(NacosClient nacosClient, String nacosApiVersion, String serviceName, @Nullable String namespaceId, @Nullable String groupName, - @Nullable String clusterName, @Nullable Boolean healthyOnly, @Nullable String app) { - return new QueryInstancesClient(nacosClient, nacosApiVersion, serviceName, namespaceId, groupName, clusterName, - healthyOnly, app); + @Nullable String clusterName, @Nullable Boolean healthyOnly, + @Nullable String app) { + return new QueryInstancesClient(nacosClient, nacosApiVersion, serviceName, namespaceId, groupName, + clusterName, healthyOnly, app); } private final WebClient webClient; @@ -56,7 +57,9 @@ static QueryInstancesClient of(NacosClient nacosClient, String nacosApiVersion, @Nullable String clusterName, @Nullable Boolean healthyOnly, @Nullable String app) { webClient = nacosClient.nacosWebClient(); - final StringBuilder pathBuilder = new StringBuilder("/").append(nacosApiVersion).append("/ns/instance/list?"); + final StringBuilder pathBuilder = new StringBuilder("/") + .append(nacosApiVersion) + .append("/ns/instance/list?"); final QueryParams params = NacosClientUtil .queryParams(namespaceId, groupName, requireNonNull(serviceName, "serviceName"), clusterName, healthyOnly, app, null, null, null); diff --git a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java index dd06997670c..4ca8229c198 100644 --- a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java @@ -27,8 +27,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import com.fasterxml.jackson.core.JsonProcessingException; - import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.internal.nacos.NacosTestBase; import com.linecorp.armeria.internal.testing.GenerateNativeImageTrace; @@ -42,7 +40,7 @@ class NacosUpdatingListenerTest extends NacosTestBase { private static volatile List sampleEndpoints; @BeforeAll - static void startServers() throws JsonProcessingException { + static void startServers() { await().pollInSameThread().pollInterval(Duration.ofSeconds(1)).untilAsserted(() -> { assertThatCode(() -> { @@ -69,7 +67,7 @@ static void startServers() throws JsonProcessingException { } @AfterAll - static void stopServers() throws Exception { + static void stopServers() { servers.forEach(Server::close); servers.clear(); } From a0d78953f276e88001b9c2c58b8bffa5a9a7cf1a Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sun, 16 Jun 2024 14:16:56 +0900 Subject: [PATCH 12/25] Revert "To reuse access-token, instead of the Caffeine cache, use own implementation." This reverts commit 0120fd78ab312962d09a0dbf8224d1d291366c15. excluding linting style. --- nacos/build.gradle.kts | 1 + .../armeria/internal/nacos/LoginClient.java | 96 +++++++++---------- .../nacos/NacosUpdatingListenerTest.java | 6 +- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/nacos/build.gradle.kts b/nacos/build.gradle.kts index 84411a8250d..aae19538681 100644 --- a/nacos/build.gradle.kts +++ b/nacos/build.gradle.kts @@ -1,3 +1,4 @@ dependencies { + implementation(libs.caffeine) testImplementation(libs.testcontainers.junit.jupiter) } diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index 951f134d902..0674acba52c 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -19,25 +19,23 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Function; -import java.util.function.Supplier; + +import com.linecorp.armeria.client.ClientRequestContext; +import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.SimpleDecoratingHttpClient; +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.*; +import org.checkerframework.checker.nullness.qual.NonNull; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.benmanes.caffeine.cache.AsyncLoadingCache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.Expiry; import com.google.common.base.MoreObjects; -import com.linecorp.armeria.client.ClientRequestContext; -import com.linecorp.armeria.client.HttpClient; -import com.linecorp.armeria.client.SimpleDecoratingHttpClient; -import com.linecorp.armeria.client.WebClient; -import com.linecorp.armeria.common.HttpEntity; -import com.linecorp.armeria.common.HttpHeaderNames; -import com.linecorp.armeria.common.HttpRequest; -import com.linecorp.armeria.common.HttpResponse; -import com.linecorp.armeria.common.MediaType; -import com.linecorp.armeria.common.QueryParams; import com.linecorp.armeria.common.annotation.Nullable; -import com.linecorp.armeria.common.util.UnmodifiableFuture; /** * A Nacos client that is responsible for @@ -49,14 +47,42 @@ public static Function newDecorator(WebClient w return delegate -> new LoginClient(delegate, webClient, username, password); } + private static final String NACOS_ACCESS_TOKEN_CACHE_KEY = "NACOS_ACCESS_TOKEN_CACHE_KEY"; + private final AsyncLoadingCache tokenCache = Caffeine.newBuilder() + .maximumSize(1) + .expireAfter(new Expiry() { + @Override + public long expireAfterCreate(@NonNull String key, @NonNull LoginResult loginResult, + long currentTime) { + return loginResult.tokenTtl.longValue(); + } + + @Override + public long expireAfterUpdate(@NonNull String key, @NonNull LoginResult loginResult, + long currentTime, long currentDuration) { + return loginResult.tokenTtl.longValue(); + } + + @Override + public long expireAfterRead(@NonNull String key, @NonNull LoginResult loginResult, + long currentTime, long currentDuration) { + return currentDuration; + } + }) + .buildAsync((key, executor) -> { + try { + return loginInternal(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + private final HttpClient delegate; private final WebClient webClient; private final String queryParamsForLogin; - private final CachedLoginResult cachedLoginResult = new CachedLoginResult(); - LoginClient(HttpClient delegate, WebClient webClient, String username, String password) { super(delegate); @@ -68,7 +94,12 @@ public static Function newDecorator(WebClient w .toQueryString(); } - private CompletableFuture login() { + private CompletableFuture login() { + return tokenCache.get(NACOS_ACCESS_TOKEN_CACHE_KEY) + .thenApply(loginResult -> loginResult.accessToken); + } + + private CompletableFuture loginInternal() { return webClient.prepare().post("/v1/auth/login") .content(MediaType.FORM_DATA, queryParamsForLogin) .asJson(LoginResult.class) @@ -78,7 +109,7 @@ private CompletableFuture login() { @Override public HttpResponse execute(ClientRequestContext ctx, HttpRequest req) { - final CompletableFuture future = cachedLoginResult.get().thenApply(accessToken -> { + CompletableFuture future = login().thenApply(accessToken -> { try { return delegate.execute(ctx, req.mapHeaders(headers -> headers.toBuilder() .set(HttpHeaderNames.AUTHORIZATION, "Bearer " + accessToken) @@ -91,39 +122,6 @@ public HttpResponse execute(ClientRequestContext ctx, HttpRequest req) { return HttpResponse.of(future); } - private final class CachedLoginResult { - @Nullable - private volatile CacheEntry cacheEntry; - - CachedLoginResult() { } - - CompletableFuture get() { - final CacheEntry cacheEntry = this.cacheEntry; - if (cacheEntry != null && !cacheEntry.isExpired()) { - return UnmodifiableFuture.completedFuture(cacheEntry.loginResult.accessToken); - } - - return login().thenApply(loginResult -> { - this.cacheEntry = new CacheEntry(loginResult); - return loginResult.accessToken; - }); - } - - private class CacheEntry { - private final LoginResult loginResult; - private final long expirationTimeMillis; - - CacheEntry(LoginResult loginResult) { - this.loginResult = loginResult; - this.expirationTimeMillis = System.currentTimeMillis() + loginResult.tokenTtl * 1000; - } - - boolean isExpired() { - return System.currentTimeMillis() >= expirationTimeMillis; - } - } - } - @JsonIgnoreProperties(ignoreUnknown = true) private static final class LoginResult { private final String accessToken; diff --git a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java index 4ca8229c198..dd06997670c 100644 --- a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java @@ -27,6 +27,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.core.JsonProcessingException; + import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.internal.nacos.NacosTestBase; import com.linecorp.armeria.internal.testing.GenerateNativeImageTrace; @@ -40,7 +42,7 @@ class NacosUpdatingListenerTest extends NacosTestBase { private static volatile List sampleEndpoints; @BeforeAll - static void startServers() { + static void startServers() throws JsonProcessingException { await().pollInSameThread().pollInterval(Duration.ofSeconds(1)).untilAsserted(() -> { assertThatCode(() -> { @@ -67,7 +69,7 @@ static void startServers() { } @AfterAll - static void stopServers() { + static void stopServers() throws Exception { servers.forEach(Server::close); servers.clear(); } From a42707416da664fbcc41fbad726276ff858b4370 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sun, 16 Jun 2024 14:54:37 +0900 Subject: [PATCH 13/25] Lint code. (omitted 'final', meaningless lambda expression, unnecessary 'this.') --- .../armeria/internal/nacos/LoginClient.java | 4 +- .../internal/nacos/QueryInstancesClient.java | 2 +- .../client/nacos/NacosEndpointGroupTest.java | 26 +++----- .../nacos/NacosUpdatingListenerTest.java | 63 +++++++++---------- 4 files changed, 39 insertions(+), 56 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index 0674acba52c..574d3f983f3 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -88,7 +88,7 @@ public long expireAfterRead(@NonNull String key, @NonNull LoginResult loginResul this.delegate = requireNonNull(delegate, "delegate"); this.webClient = requireNonNull(webClient, "webClient"); - this.queryParamsForLogin = QueryParams.builder() + queryParamsForLogin = QueryParams.builder() .add("username", requireNonNull(username, "username")) .add("password", requireNonNull(password, "password")) .toQueryString(); @@ -109,7 +109,7 @@ private CompletableFuture loginInternal() { @Override public HttpResponse execute(ClientRequestContext ctx, HttpRequest req) { - CompletableFuture future = login().thenApply(accessToken -> { + final CompletableFuture future = login().thenApply(accessToken -> { try { return delegate.execute(ctx, req.mapHeaders(headers -> headers.toBuilder() .set(HttpHeaderNames.AUTHORIZATION, "Bearer " + accessToken) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java index 38b7ce16f98..a9c42ba0c74 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java @@ -65,7 +65,7 @@ static QueryInstancesClient of(NacosClient nacosClient, String nacosApiVersion, clusterName, healthyOnly, app, null, null, null); pathBuilder.append(params.toQueryString()); - this.pathForQuery = pathBuilder.toString(); + pathForQuery = pathBuilder.toString(); } CompletableFuture> endpoints() { diff --git a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java index d7e01d3fd09..32c76bed207 100644 --- a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java @@ -83,16 +83,13 @@ void testNacosEndpointGroupWithClient() { .registryFetchInterval(Duration.ofSeconds(1)) .build()) { await().atMost(5, TimeUnit.SECONDS) - .untilAsserted(() -> { - assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints); - }); + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints)); // stop a server servers.get(0).stop().join(); await().atMost(5, TimeUnit.SECONDS) - .untilAsserted(() -> { - assertThat(endpointGroup.endpoints()).hasSize(sampleEndpoints.size() - 1); - }); + .untilAsserted(() -> assertThat(endpointGroup.endpoints()) + .hasSize(sampleEndpoints.size() - 1)); // restart the server await().pollInSameThread().pollInterval(Duration.ofSeconds(1)).untilAsserted(() -> { @@ -100,9 +97,7 @@ void testNacosEndpointGroupWithClient() { assertThatCode(servers.get(0).start()::join).doesNotThrowAnyException(); }); await().atMost(5, TimeUnit.SECONDS) - .untilAsserted(() -> { - assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints); - }); + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints)); } } @@ -114,16 +109,13 @@ void testNacosEndpointGroupWithUrl() { .registryFetchInterval(Duration.ofSeconds(1)) .build()) { await().atMost(5, TimeUnit.SECONDS) - .untilAsserted(() -> { - assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints); - }); + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints)); // stop a server servers.get(0).stop().join(); await().atMost(5, TimeUnit.SECONDS) - .untilAsserted(() -> { - assertThat(endpointGroup.endpoints()).hasSize(sampleEndpoints.size() - 1); - }); + .untilAsserted(() -> assertThat(endpointGroup.endpoints()) + .hasSize(sampleEndpoints.size() - 1)); // restart the server await().pollInSameThread().pollInterval(Duration.ofSeconds(1)).untilAsserted(() -> { @@ -131,9 +123,7 @@ void testNacosEndpointGroupWithUrl() { assertThatCode(servers.get(0).start()::join).doesNotThrowAnyException(); }); await().atMost(5, TimeUnit.SECONDS) - .untilAsserted(() -> { - assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints); - }); + .untilAsserted(() -> assertThat(endpointGroup.endpoints()).hasSameSizeAs(sampleEndpoints)); } } diff --git a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java index dd06997670c..ac3c969c64a 100644 --- a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java @@ -43,29 +43,28 @@ class NacosUpdatingListenerTest extends NacosTestBase { @BeforeAll static void startServers() throws JsonProcessingException { - - await().pollInSameThread().pollInterval(Duration.ofSeconds(1)).untilAsserted(() -> { - assertThatCode(() -> { - final List endpoints = newSampleEndpoints(); - servers.clear(); - for (Endpoint endpoint : endpoints) { - final Server server = Server.builder() - .http(endpoint.port()) - .service("/echo", new EchoService()) - .build(); - final ServerListener listener = - NacosUpdatingListener - .builder(nacosUri(), serviceName) - .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) - .endpoint(endpoint) - .build(); - server.addListener(listener); - server.start().join(); - servers.add(server); - } - sampleEndpoints = endpoints; - }).doesNotThrowAnyException(); - }); + await().pollInSameThread() + .pollInterval(Duration.ofSeconds(1)) + .untilAsserted(() -> assertThatCode(() -> { + final List endpoints = newSampleEndpoints(); + servers.clear(); + for (Endpoint endpoint : endpoints) { + final Server server = Server.builder() + .http(endpoint.port()) + .service("/echo", new EchoService()) + .build(); + final ServerListener listener = + NacosUpdatingListener + .builder(nacosUri(), serviceName) + .authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) + .endpoint(endpoint) + .build(); + server.addListener(listener); + server.start().join(); + servers.add(server); + } + sampleEndpoints = endpoints; + }).doesNotThrowAnyException()); } @AfterAll @@ -85,10 +84,8 @@ void testBuild() { @Test void testEndpointsCountOfListeningServiceWithAServerStopAndStart() { // Checks sample endpoints created when initialized. - await().untilAsserted(() -> { - assertThat(client(null, null).endpoints() - .join()).hasSameSizeAs(sampleEndpoints); - }); + await().untilAsserted(() -> assertThat(client(null, null).endpoints() + .join()).hasSameSizeAs(sampleEndpoints)); // When we close one server then the listener deregister it automatically from nacos. servers.get(0).stop().join(); @@ -102,10 +99,8 @@ void testEndpointsCountOfListeningServiceWithAServerStopAndStart() { // Endpoints increased after service restart. servers.get(0).start().join(); - await().untilAsserted(() -> { - assertThat(client(null, null).endpoints() - .join()).hasSameSizeAs(sampleEndpoints); - }); + await().untilAsserted(() -> assertThat(client(null, null).endpoints() + .join()).hasSameSizeAs(sampleEndpoints)); } @Test @@ -126,10 +121,8 @@ void testThatGroupNameIsSpecified() { .build(); server.addListener(listener); server.start().join(); - await().untilAsserted(() -> { - assertThat(client("testThatGroupNameIsSpecified", "groupName").endpoints().join()) - .hasSize(1); - }); + await().untilAsserted(() -> assertThat(client("testThatGroupNameIsSpecified", "groupName") + .endpoints().join()).hasSize(1)); server.stop(); } } From 625535a9c7e066d9afff6d2b85c65598fc89986c Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sun, 16 Jun 2024 14:58:04 +0900 Subject: [PATCH 14/25] Lint code. (Remove meaningless property and use parameter directly) --- .../armeria/server/nacos/NacosUpdatingListenerBuilder.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java index 3eb85fd56b9..f687815dc8a 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java @@ -40,9 +40,6 @@ */ @UnstableApi public final class NacosUpdatingListenerBuilder implements NacosConfigSetters { - - private final String serviceName; - @Nullable private Endpoint endpoint; @@ -55,8 +52,8 @@ public final class NacosUpdatingListenerBuilder implements NacosConfigSetters { * @param serviceName the service name to register */ NacosUpdatingListenerBuilder(URI nacosUri, String serviceName) { - this.serviceName = requireNonNull(serviceName, "serviceName"); - checkArgument(!this.serviceName.isEmpty(), "serviceName can't be empty"); + requireNonNull(serviceName, "serviceName"); + checkArgument(!serviceName.isEmpty(), "serviceName can't be empty"); nacosClientBuilder = NacosClient.builder(nacosUri, serviceName); } From ed1d5bab39966434342096a97635e8e79a6ad67f Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sun, 16 Jun 2024 15:10:41 +0900 Subject: [PATCH 15/25] Lint code. (Optimize imports, according to :nacos:checkStyleMain) --- .../armeria/internal/nacos/LoginClient.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index 574d3f983f3..416bba5ee02 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -20,11 +20,6 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Function; -import com.linecorp.armeria.client.ClientRequestContext; -import com.linecorp.armeria.client.HttpClient; -import com.linecorp.armeria.client.SimpleDecoratingHttpClient; -import com.linecorp.armeria.client.WebClient; -import com.linecorp.armeria.common.*; import org.checkerframework.checker.nullness.qual.NonNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -35,6 +30,16 @@ import com.github.benmanes.caffeine.cache.Expiry; import com.google.common.base.MoreObjects; +import com.linecorp.armeria.client.ClientRequestContext; +import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.SimpleDecoratingHttpClient; +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.HttpEntity; +import com.linecorp.armeria.common.HttpHeaderNames; +import com.linecorp.armeria.common.HttpRequest; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.MediaType; +import com.linecorp.armeria.common.QueryParams; import com.linecorp.armeria.common.annotation.Nullable; /** From f94cf66065ab14c06e5623ca979145fe9fe8680a Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sun, 16 Jun 2024 15:25:40 +0900 Subject: [PATCH 16/25] Copyright header: Line Corporation -> LY Corporation --- .../linecorp/armeria/client/nacos/NacosEndpointGroup.java | 6 +++--- .../armeria/client/nacos/NacosEndpointGroupBuilder.java | 6 +++--- .../com/linecorp/armeria/client/nacos/package-info.java | 6 +++--- .../linecorp/armeria/common/nacos/NacosConfigSetters.java | 6 +++--- .../com/linecorp/armeria/common/nacos/package-info.java | 6 +++--- .../com/linecorp/armeria/internal/nacos/LoginClient.java | 6 +++--- .../com/linecorp/armeria/internal/nacos/NacosClient.java | 6 +++--- .../linecorp/armeria/internal/nacos/NacosClientBuilder.java | 6 +++--- .../linecorp/armeria/internal/nacos/NacosClientUtil.java | 6 +++--- .../armeria/internal/nacos/QueryInstancesClient.java | 6 +++--- .../armeria/internal/nacos/RegisterInstanceClient.java | 6 +++--- .../com/linecorp/armeria/internal/nacos/package-info.java | 6 +++--- .../armeria/server/nacos/NacosUpdatingListener.java | 6 +++--- .../armeria/server/nacos/NacosUpdatingListenerBuilder.java | 6 +++--- .../com/linecorp/armeria/server/nacos/package-info.java | 6 +++--- .../armeria/client/nacos/NacosEndpointGroupBuilderTest.java | 6 +++--- .../armeria/client/nacos/NacosEndpointGroupTest.java | 6 +++--- .../armeria/internal/nacos/NacosClientBuilderTest.java | 6 +++--- .../com/linecorp/armeria/internal/nacos/NacosTestBase.java | 6 +++--- .../armeria/server/nacos/NacosUpdatingListenerTest.java | 6 +++--- 20 files changed, 60 insertions(+), 60 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java index ecedb5647f3..41ce8337fad 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java index ab689a5977b..05d51054436 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/package-info.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/package-info.java index dda63589679..da9a43a38f7 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/package-info.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/package-info.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java b/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java index 432d1adcf11..9a850729bb2 100644 --- a/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java +++ b/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/common/nacos/package-info.java b/nacos/src/main/java/com/linecorp/armeria/common/nacos/package-info.java index 6ff5ddbc7c9..28027bc31fd 100644 --- a/nacos/src/main/java/com/linecorp/armeria/common/nacos/package-info.java +++ b/nacos/src/main/java/com/linecorp/armeria/common/nacos/package-info.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index 416bba5ee02..0982f698cae 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java index cd0fabdbbfe..8cbae1cc598 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java index eebc614577e..1897fdb6fc3 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java index bd4a2f38b08..39c7c98142e 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientUtil.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java index a9c42ba0c74..0e7c813e121 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/QueryInstancesClient.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java index 0a29d27d0c7..ed1445dd71e 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/package-info.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/package-info.java index 57066b91104..600661d3d7e 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/package-info.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/package-info.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java index 47dbf947282..5a3be3ff3ec 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java index f687815dc8a..da865e64d9b 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/package-info.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/package-info.java index c4885b056c5..0651abf2f9c 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/package-info.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/package-info.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java index 7c08011b77e..ce88f1842ee 100644 --- a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java index 32c76bed207..cdd120ff972 100644 --- a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java index 4a41bade121..4466874b316 100644 --- a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java index a6c285be683..4682df90ea8 100644 --- a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java +++ b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * diff --git a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java index ac3c969c64a..22f649d1ec7 100644 --- a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java @@ -1,7 +1,7 @@ /* - * Copyright 2024 LINE Corporation - * - * LINE Corporation licenses this file to you under the Apache License, + * Copyright 2024 LY Corporation + + * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * From c10ebc8d9dbbe1d3421c203a573e5b764fb6a4aa Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sun, 16 Jun 2024 15:41:02 +0900 Subject: [PATCH 17/25] TODO comment about AsyncLoadingCache --- .../java/com/linecorp/armeria/internal/nacos/LoginClient.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index 0982f698cae..52d56675a41 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -53,6 +53,8 @@ public static Function newDecorator(WebClient w } private static final String NACOS_ACCESS_TOKEN_CACHE_KEY = "NACOS_ACCESS_TOKEN_CACHE_KEY"; + + // TODO: Replace the caffeine AsyncLoadingCache with internally implemented AsyncLoader, if #5590 merged. private final AsyncLoadingCache tokenCache = Caffeine.newBuilder() .maximumSize(1) .expireAfter(new Expiry() { From 5fe5ba28a2b716797c59c533d6c02c8a2ef9d40e Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Wed, 19 Jun 2024 13:59:17 +0900 Subject: [PATCH 18/25] Hide NacosEndpointGroup as package private. --- .../com/linecorp/armeria/client/nacos/NacosEndpointGroup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java index 41ce8337fad..06129d3c246 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java @@ -44,7 +44,7 @@ * using Nacos's HTTP Open API * and updates the {@link Endpoint}s periodically. */ -public class NacosEndpointGroup extends DynamicEndpointGroup { +class NacosEndpointGroup extends DynamicEndpointGroup { private static final Logger logger = LoggerFactory.getLogger(NacosEndpointGroup.class); /** From 03150f28886e469e49b7c52eb8ff6ffe79bfcf7e Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Wed, 19 Jun 2024 14:56:54 +0900 Subject: [PATCH 19/25] Apply AbstractDynamicEndpointGroupBuilder's changes(SELF type parameter) --- .../armeria/client/nacos/NacosEndpointGroupBuilder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java index 05d51054436..a7e7d165d92 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java @@ -38,7 +38,7 @@ * sb.serverListener(listener); * } */ -public class NacosEndpointGroupBuilder extends AbstractDynamicEndpointGroupBuilder { +public final class NacosEndpointGroupBuilder extends AbstractDynamicEndpointGroupBuilder { private static final long DEFAULT_CHECK_INTERVAL_MILLIS = 30_000; @@ -158,7 +158,7 @@ public NacosEndpointGroup build() { @Override public NacosEndpointGroupBuilder allowEmptyEndpoints(boolean allowEmptyEndpoints) { - return (NacosEndpointGroupBuilder) super.allowEmptyEndpoints(allowEmptyEndpoints); + return super.allowEmptyEndpoints(allowEmptyEndpoints); } /** @@ -168,7 +168,7 @@ public NacosEndpointGroupBuilder allowEmptyEndpoints(boolean allowEmptyEndpoints */ @Override public NacosEndpointGroupBuilder selectionTimeout(Duration selectionTimeout) { - return (NacosEndpointGroupBuilder) super.selectionTimeout(selectionTimeout); + return super.selectionTimeout(selectionTimeout); } /** @@ -178,6 +178,6 @@ public NacosEndpointGroupBuilder selectionTimeout(Duration selectionTimeout) { */ @Override public NacosEndpointGroupBuilder selectionTimeoutMillis(long selectionTimeoutMillis) { - return (NacosEndpointGroupBuilder) super.selectionTimeoutMillis(selectionTimeoutMillis); + return super.selectionTimeoutMillis(selectionTimeoutMillis); } } From 88f6235207868a3a87339fc397a69a76263070bd Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Wed, 19 Jun 2024 15:00:30 +0900 Subject: [PATCH 20/25] Use SELF type parameter for NacosConfigSetters. --- .../linecorp/armeria/common/nacos/NacosConfigSetters.java | 6 +++--- .../linecorp/armeria/internal/nacos/NacosClientBuilder.java | 4 ++-- .../armeria/server/nacos/NacosUpdatingListenerBuilder.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java b/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java index 9a850729bb2..97d68680078 100644 --- a/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java +++ b/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java @@ -22,13 +22,13 @@ * Sets properties for building a Nacos client. */ @UnstableApi -public interface NacosConfigSetters { +public interface NacosConfigSetters> { /** * Sets the specified Nacos's API version. * @param nacosApiVersion the version of Nacos API service, default: {@value * NacosClientBuilder#DEFAULT_NACOS_API_VERSION} */ - NacosConfigSetters nacosApiVersion(String nacosApiVersion); + SELF nacosApiVersion(String nacosApiVersion); /** * Sets the username and password pair for Nacos's API. @@ -39,5 +39,5 @@ public interface NacosConfigSetters { * @param username the username for access Nacos API, default: {@code null} * @param password the password for access Nacos API, default: {@code null} */ - NacosConfigSetters authorization(String username, String password); + SELF authorization(String username, String password); } diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java index 1897fdb6fc3..fff2a369d94 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java @@ -25,7 +25,7 @@ import com.linecorp.armeria.common.annotation.Nullable; import com.linecorp.armeria.common.nacos.NacosConfigSetters; -public final class NacosClientBuilder implements NacosConfigSetters { +public final class NacosClientBuilder implements NacosConfigSetters { public static final String DEFAULT_NACOS_API_VERSION = "v2"; private static final Pattern NACOS_API_VERSION_PATTERN = Pattern.compile("^v[0-9][-._a-zA-Z0-9]*$"); private final URI nacosUri; @@ -61,7 +61,7 @@ public final class NacosClientBuilder implements NacosConfigSetters { } @Override - public NacosConfigSetters nacosApiVersion(String nacosApiVersion) { + public NacosClientBuilder nacosApiVersion(String nacosApiVersion) { this.nacosApiVersion = requireNonNull(nacosApiVersion, "nacosApiVersion"); checkArgument(NACOS_API_VERSION_PATTERN.matcher(nacosApiVersion).matches(), "nacosApiVersion: %s (expected: a version string that starts with 'v', e.g. 'v1')", diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java index da865e64d9b..c91ae1f192b 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java @@ -39,7 +39,7 @@ * } */ @UnstableApi -public final class NacosUpdatingListenerBuilder implements NacosConfigSetters { +public final class NacosUpdatingListenerBuilder implements NacosConfigSetters { @Nullable private Endpoint endpoint; From 0a715cdeb3a0a7c17161b7eab7092df6056f2815 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Fri, 28 Jun 2024 16:10:09 +0900 Subject: [PATCH 21/25] Lint code: Run auto-indent. --- .../client/nacos/NacosEndpointGroup.java | 6 +- .../armeria/internal/nacos/LoginClient.java | 117 ++++++++++-------- .../armeria/internal/nacos/NacosClient.java | 10 +- .../internal/nacos/NacosClientBuilder.java | 6 +- .../internal/nacos/NacosClientUtil.java | 4 +- .../internal/nacos/QueryInstancesClient.java | 79 ++++++------ .../nacos/RegisterInstanceClient.java | 44 +++---- .../server/nacos/NacosUpdatingListener.java | 97 ++++++++------- .../nacos/NacosUpdatingListenerBuilder.java | 3 +- .../nacos/NacosEndpointGroupBuilderTest.java | 6 +- .../client/nacos/NacosEndpointGroupTest.java | 3 +- .../nacos/NacosClientBuilderTest.java | 4 +- .../armeria/internal/nacos/NacosTestBase.java | 20 ++- .../nacos/NacosUpdatingListenerTest.java | 8 +- 14 files changed, 207 insertions(+), 200 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java index 06129d3c246..34eb0a1d02e 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java @@ -127,8 +127,8 @@ protected void doCloseAsync(CompletableFuture future) { @Override public String toString() { return MoreObjects.toStringHelper(this) - .omitNullValues() - .add("registryFetchIntervalMillis", registryFetchIntervalMillis) - .toString(); + .omitNullValues() + .add("registryFetchIntervalMillis", registryFetchIntervalMillis) + .toString(); } } diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java index 52d56675a41..5f9cd34e9fc 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/LoginClient.java @@ -47,80 +47,97 @@ * Nacos Authentication. */ final class LoginClient extends SimpleDecoratingHttpClient { + private static final String NACOS_ACCESS_TOKEN_CACHE_KEY = "NACOS_ACCESS_TOKEN_CACHE_KEY"; + public static Function newDecorator(WebClient webClient, String username, String password) { return delegate -> new LoginClient(delegate, webClient, username, password); } - private static final String NACOS_ACCESS_TOKEN_CACHE_KEY = "NACOS_ACCESS_TOKEN_CACHE_KEY"; - - // TODO: Replace the caffeine AsyncLoadingCache with internally implemented AsyncLoader, if #5590 merged. - private final AsyncLoadingCache tokenCache = Caffeine.newBuilder() - .maximumSize(1) - .expireAfter(new Expiry() { - @Override - public long expireAfterCreate(@NonNull String key, @NonNull LoginResult loginResult, - long currentTime) { - return loginResult.tokenTtl.longValue(); - } - - @Override - public long expireAfterUpdate(@NonNull String key, @NonNull LoginResult loginResult, - long currentTime, long currentDuration) { - return loginResult.tokenTtl.longValue(); - } - - @Override - public long expireAfterRead(@NonNull String key, @NonNull LoginResult loginResult, - long currentTime, long currentDuration) { - return currentDuration; - } - }) - .buildAsync((key, executor) -> { - try { - return loginInternal(); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - private final HttpClient delegate; - private final WebClient webClient; - private final String queryParamsForLogin; + // TODO: Replace the caffeine AsyncLoadingCache with internally implemented AsyncLoader, if #5590 merged. + private final AsyncLoadingCache tokenCache = + Caffeine.newBuilder() + .maximumSize(1) + .expireAfter( + new Expiry() { + @Override + public long expireAfterCreate( + @NonNull + String key, + @NonNull + LoginResult loginResult, + long currentTime) { + return loginResult.tokenTtl.longValue(); + } + + @Override + public long expireAfterUpdate( + @NonNull + String key, + @NonNull + LoginResult loginResult, + long currentTime, + long currentDuration) { + return loginResult.tokenTtl.longValue(); + } + + @Override + public long expireAfterRead( + @NonNull + String key, + @NonNull + LoginResult loginResult, + long currentTime, + long currentDuration) { + return currentDuration; + } + }) + .buildAsync((key, executor) -> { + try { + return loginInternal(); + } catch (Exception e) { + throw new RuntimeException( + e); + } + }); + LoginClient(HttpClient delegate, WebClient webClient, String username, String password) { super(delegate); this.delegate = requireNonNull(delegate, "delegate"); this.webClient = requireNonNull(webClient, "webClient"); queryParamsForLogin = QueryParams.builder() - .add("username", requireNonNull(username, "username")) - .add("password", requireNonNull(password, "password")) - .toQueryString(); + .add("username", requireNonNull(username, "username")) + .add("password", requireNonNull(password, "password")) + .toQueryString(); } private CompletableFuture login() { return tokenCache.get(NACOS_ACCESS_TOKEN_CACHE_KEY) - .thenApply(loginResult -> loginResult.accessToken); + .thenApply(loginResult -> loginResult.accessToken); } private CompletableFuture loginInternal() { return webClient.prepare().post("/v1/auth/login") - .content(MediaType.FORM_DATA, queryParamsForLogin) - .asJson(LoginResult.class) - .as(HttpEntity::content) - .execute(); + .content(MediaType.FORM_DATA, queryParamsForLogin) + .asJson(LoginResult.class) + .as(HttpEntity::content) + .execute(); } @Override public HttpResponse execute(ClientRequestContext ctx, HttpRequest req) { final CompletableFuture future = login().thenApply(accessToken -> { try { - return delegate.execute(ctx, req.mapHeaders(headers -> headers.toBuilder() - .set(HttpHeaderNames.AUTHORIZATION, "Bearer " + accessToken) - .build())); + return delegate.execute(ctx, + req.mapHeaders(headers -> headers.toBuilder() + .set(HttpHeaderNames.AUTHORIZATION, + "Bearer " + accessToken) + .build())); } catch (Exception e) { throw new RuntimeException(e); } @@ -149,11 +166,11 @@ private static final class LoginResult { @Override public String toString() { return MoreObjects.toStringHelper(this) - .omitNullValues() - .add("accessToken", accessToken) - .add("tokenTtl", tokenTtl) - .add("globalAdmin", globalAdmin) - .toString(); + .omitNullValues() + .add("accessToken", accessToken) + .add("tokenTtl", tokenTtl) + .add("globalAdmin", globalAdmin) + .toString(); } } } diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java index 8cbae1cc598..341c7687d25 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClient.java @@ -34,8 +34,8 @@ public final class NacosClient { private static final Function retryingClientDecorator = RetryingClient.newDecorator(RetryConfig.builder(RetryRule.onServerErrorStatus()) - .maxTotalAttempts(3) - .build()); + .maxTotalAttempts(3) + .build()); public static NacosClientBuilder builder(URI nacosUri, String serviceName) { return new NacosClientBuilder(nacosUri, serviceName); @@ -51,7 +51,7 @@ public static NacosClientBuilder builder(URI nacosUri, String serviceName) { String serviceName, @Nullable String namespaceId, @Nullable String groupName, @Nullable String clusterName, @Nullable Boolean healthyOnly, @Nullable String app) { final WebClientBuilder builder = WebClient.builder(uri) - .decorator(retryingClientDecorator); + .decorator(retryingClientDecorator); if (username != null && password != null) { builder.decorator(LoginClient.newDecorator(builder.build(), username, password)); @@ -60,9 +60,9 @@ public static NacosClientBuilder builder(URI nacosUri, String serviceName) { webClient = builder.build(); queryInstancesClient = QueryInstancesClient.of(this, nacosApiVersion, serviceName, namespaceId, - groupName, clusterName, healthyOnly, app); + groupName, clusterName, healthyOnly, app); registerInstanceClient = RegisterInstanceClient.of(this, nacosApiVersion, serviceName, namespaceId, - groupName, clusterName, app); + groupName, clusterName, app); } public CompletableFuture> endpoints() { diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java index fff2a369d94..cbfdc5328cc 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java @@ -28,7 +28,9 @@ public final class NacosClientBuilder implements NacosConfigSetters { public static final String DEFAULT_NACOS_API_VERSION = "v2"; private static final Pattern NACOS_API_VERSION_PATTERN = Pattern.compile("^v[0-9][-._a-zA-Z0-9]*$"); + private final URI nacosUri; + private final String serviceName; private String nacosApiVersion = DEFAULT_NACOS_API_VERSION; @@ -38,8 +40,6 @@ public final class NacosClientBuilder implements NacosConfigSetters> endpoints() { - return queryInstances() - .thenApply(response -> response.data.hosts.stream() - .map(QueryInstancesClient::toEndpoint) - .filter(Objects::nonNull) - .collect(toImmutableList())); - } - - CompletableFuture queryInstances() { - return webClient.prepare() - .get(pathForQuery) - .asJson(QueryInstancesResponse.class) - .as(HttpEntity::content) - .execute(); + static QueryInstancesClient of(NacosClient nacosClient, String nacosApiVersion, String serviceName, + @Nullable String namespaceId, @Nullable String groupName, + @Nullable String clusterName, @Nullable Boolean healthyOnly, + @Nullable String app) { + return new QueryInstancesClient(nacosClient, nacosApiVersion, serviceName, namespaceId, groupName, + clusterName, healthyOnly, app); } @Nullable @@ -95,6 +78,22 @@ private static Endpoint toEndpoint(Host host) { } } + CompletableFuture> endpoints() { + return queryInstances() + .thenApply(response -> response.data.hosts.stream() + .map(QueryInstancesClient::toEndpoint) + .filter(Objects::nonNull) + .collect(toImmutableList())); + } + + CompletableFuture queryInstances() { + return webClient.prepare() + .get(pathForQuery) + .asJson(QueryInstancesResponse.class) + .as(HttpEntity::content) + .execute(); + } + @JsonIgnoreProperties(ignoreUnknown = true) private static final class QueryInstancesResponse { private final Data data; @@ -190,22 +189,22 @@ private static final class Host { @Override public String toString() { return MoreObjects.toStringHelper(this) - .omitNullValues() - .add("instanceId", instanceId) - .add("ip", ip) - .add("port", port) - .add("weight", weight) - .add("healthy", healthy) - .add("enabled", enabled) - .add("ephemeral", ephemeral) - .add("clusterName", clusterName) - .add("serviceName", serviceName) - .add("metaData", metadata) - .add("instanceHeartBeatInterval", instanceHeartBeatInterval) - .add("instanceIdGenerator", instanceIdGenerator) - .add("instanceHeartBeatTimeOut", instanceHeartBeatTimeOut) - .add("ipDeleteTimeout", ipDeleteTimeout) - .toString(); + .omitNullValues() + .add("instanceId", instanceId) + .add("ip", ip) + .add("port", port) + .add("weight", weight) + .add("healthy", healthy) + .add("enabled", enabled) + .add("ephemeral", ephemeral) + .add("clusterName", clusterName) + .add("serviceName", serviceName) + .add("metaData", metadata) + .add("instanceHeartBeatInterval", instanceHeartBeatInterval) + .add("instanceIdGenerator", instanceIdGenerator) + .add("instanceHeartBeatTimeOut", instanceHeartBeatTimeOut) + .add("ipDeleteTimeout", ipDeleteTimeout) + .toString(); } } } diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java index ed1445dd71e..3525ff71224 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/RegisterInstanceClient.java @@ -29,17 +29,8 @@ */ final class RegisterInstanceClient { - static RegisterInstanceClient of(NacosClient nacosClient, String nacosApiVersion, String serviceName, - @Nullable String namespaceId, @Nullable String groupName, - @Nullable String clusterName, @Nullable String app) { - return new RegisterInstanceClient(nacosClient, nacosApiVersion, serviceName, namespaceId, groupName, - clusterName, app); - } - private final WebClient webClient; - private final String instanceApiPath; - private final String serviceName; @Nullable @@ -67,31 +58,34 @@ static RegisterInstanceClient of(NacosClient nacosClient, String nacosApiVersion this.app = app; } + static RegisterInstanceClient of(NacosClient nacosClient, String nacosApiVersion, String serviceName, + @Nullable String namespaceId, @Nullable String groupName, + @Nullable String clusterName, @Nullable String app) { + return new RegisterInstanceClient(nacosClient, nacosApiVersion, serviceName, namespaceId, groupName, + clusterName, app); + } + /** * Registers a service into the Nacos. */ HttpResponse register(String ip, int port, int weight) { - final QueryParams params = NacosClientUtil - .queryParams(namespaceId, groupName, serviceName, clusterName, null, app, - requireNonNull(ip, "ip"), port, weight); - - return webClient.prepare() - .post(instanceApiPath) - .content(MediaType.FORM_DATA, params.toQueryString()) - .execute(); + final QueryParams params = NacosClientUtil.queryParams(namespaceId, groupName, serviceName, clusterName, + null, app, requireNonNull(ip, "ip"), port, + weight); + + return webClient.prepare().post(instanceApiPath).content(MediaType.FORM_DATA, params.toQueryString()) + .execute(); } /** * De-registers a service from the Nacos. */ HttpResponse deregister(String ip, int port, int weight) { - final QueryParams params = NacosClientUtil - .queryParams(namespaceId, groupName, serviceName, clusterName, null, app, - requireNonNull(ip, "ip"), port, weight); - - return webClient.prepare() - .delete(instanceApiPath) - .content(MediaType.FORM_DATA, params.toQueryString()) - .execute(); + final QueryParams params = NacosClientUtil.queryParams(namespaceId, groupName, serviceName, clusterName, + null, app, requireNonNull(ip, "ip"), port, + weight); + + return webClient.prepare().delete(instanceApiPath).content(MediaType.FORM_DATA, params.toQueryString()) + .execute(); } } diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java index 5a3be3ff3ec..f401af7f293 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListener.java @@ -64,42 +64,6 @@ public static NacosUpdatingListenerBuilder builder(URI nacosUri, String serviceN this.endpoint = endpoint; } - @Override - public void serverStarted(Server server) { - final Endpoint endpoint = getEndpoint(server); - nacosClient.register(endpoint) - .aggregate() - .handle((res, cause) -> { - if (cause != null) { - logger.warn("Failed to register {}:{} to Nacos: {}", - endpoint.host(), endpoint.port(), nacosClient.uri(), cause); - return null; - } - - if (res.status() != HttpStatus.OK) { - logger.warn("Failed to register {}:{} to Nacos: {} (status: {}, content: {})", - endpoint.host(), endpoint.port(), nacosClient.uri(), res.status(), - res.contentUtf8()); - return null; - } - - logger.info("Registered {}:{} to Nacos: {}", - endpoint.host(), endpoint.port(), nacosClient.uri()); - isRegistered = true; - return null; - }); - } - - private Endpoint getEndpoint(Server server) { - if (endpoint != null) { - if (endpoint.hasPort()) { - warnIfInactivePort(server, endpoint.port()); - } - return endpoint; - } - return defaultEndpoint(server); - } - private static Endpoint defaultEndpoint(Server server) { final ServerPort serverPort = server.activePort(); assert serverPort != null; @@ -119,23 +83,60 @@ private static void warnIfInactivePort(Server server, int port) { port, server.activePorts()); } + @Override + public void serverStarted(Server server) { + final Endpoint endpoint = getEndpoint(server); + nacosClient.register(endpoint) + .aggregate() + .handle((res, cause) -> { + if (cause != null) { + logger.warn("Failed to register {}:{} to Nacos: {}", + endpoint.host(), endpoint.port(), nacosClient.uri(), cause); + return null; + } + + if (res.status() != HttpStatus.OK) { + logger.warn("Failed to register {}:{} to Nacos: {} (status: {}, content: {})", + endpoint.host(), endpoint.port(), nacosClient.uri(), res.status(), + res.contentUtf8()); + return null; + } + + logger.info("Registered {}:{} to Nacos: {}", + endpoint.host(), endpoint.port(), nacosClient.uri()); + isRegistered = true; + return null; + }); + } + + private Endpoint getEndpoint(Server server) { + if (endpoint != null) { + if (endpoint.hasPort()) { + warnIfInactivePort(server, endpoint.port()); + } + return endpoint; + } + return defaultEndpoint(server); + } + @Override public void serverStopping(Server server) { final Endpoint endpoint = getEndpoint(server); if (isRegistered) { nacosClient.deregister(endpoint) - .aggregate() - .handle((res, cause) -> { - if (cause != null) { - logger.warn("Failed to deregister {}:{} from Nacos: {}", - endpoint.ipAddr(), endpoint.port(), nacosClient.uri(), cause); - } else if (res.status() != HttpStatus.OK) { - logger.warn("Failed to deregister {}:{} from Nacos: {}. (status: {}, content: {})", - endpoint.ipAddr(), endpoint.port(), nacosClient.uri(), res.status(), - res.contentUtf8()); - } - return null; - }); + .aggregate() + .handle((res, cause) -> { + if (cause != null) { + logger.warn("Failed to deregister {}:{} from Nacos: {}", + endpoint.ipAddr(), endpoint.port(), nacosClient.uri(), cause); + } else if (res.status() != HttpStatus.OK) { + logger.warn( + "Failed to deregister {}:{} from Nacos: {}. (status: {}, content: {})", + endpoint.ipAddr(), endpoint.port(), nacosClient.uri(), res.status(), + res.contentUtf8()); + } + return null; + }); } } } diff --git a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java index c91ae1f192b..9df93c384bb 100644 --- a/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerBuilder.java @@ -40,11 +40,10 @@ */ @UnstableApi public final class NacosUpdatingListenerBuilder implements NacosConfigSetters { + private final NacosClientBuilder nacosClientBuilder; @Nullable private Endpoint endpoint; - private final NacosClientBuilder nacosClientBuilder; - /** * Creates a {@link NacosUpdatingListenerBuilder} with a service name. * diff --git a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java index ce88f1842ee..8dafb9f56c6 100644 --- a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilderTest.java @@ -28,7 +28,7 @@ class NacosEndpointGroupBuilderTest { @Test void selectionTimeoutDefault() { try (NacosEndpointGroup group = NacosEndpointGroup.of(URI.create("http://127.0.0.1/node"), - "testService")) { + "testService")) { assertThat(group.selectionTimeoutMillis()).isEqualTo(Flags.defaultResponseTimeoutMillis()); } } @@ -37,8 +37,8 @@ void selectionTimeoutDefault() { void selectionTimeoutCustom() { try (NacosEndpointGroup group = NacosEndpointGroup.builder(URI.create("http://127.0.0.1/node"), "testService") - .selectionTimeoutMillis(4000) - .build()) { + .selectionTimeoutMillis(4000) + .build()) { assertThat(group.selectionTimeoutMillis()).isEqualTo(4000); } } diff --git a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java index cdd120ff972..570dd0950ae 100644 --- a/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupTest.java @@ -39,9 +39,8 @@ class NacosEndpointGroupTest extends NacosTestBase { private static final List servers = new ArrayList<>(); - private static volatile List sampleEndpoints; - private static final String DEFAULT_CLUSTER_NAME = "c1"; + private static volatile List sampleEndpoints; @BeforeAll static void startServers() { diff --git a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java index 4466874b316..0d14213ad41 100644 --- a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosClientBuilderTest.java @@ -41,7 +41,7 @@ void gets403WhenNoToken() throws Exception { void nacosApiVersionCanNotStartsWithSlash() { assertThrows(IllegalArgumentException.class, () -> NacosClient.builder(URI.create("http://localhost:8500"), serviceName).nacosApiVersion("/v1")); - assertDoesNotThrow(() -> - NacosClient.builder(URI.create("http://localhost:8500"), serviceName).nacosApiVersion("v1")); + assertDoesNotThrow(() -> NacosClient.builder(URI.create("http://localhost:8500"), serviceName) + .nacosApiVersion("v1")); } } diff --git a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java index 4682df90ea8..71c9418b95c 100644 --- a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java +++ b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java @@ -54,14 +54,6 @@ public abstract class NacosTestBase { protected static final String serviceName = "testService"; protected static final String NACOS_AUTH_TOKEN = "armeriaarmeriaarmeriaarmeriaarmeriaarmeriaarmeriaarmeria"; protected static final String NACOS_AUTH_SECRET = "nacos"; - - protected static List newSampleEndpoints() { - final int[] ports = unusedPorts(3); - return ImmutableList.of(Endpoint.of("host.docker.internal", ports[0]).withWeight(2), - Endpoint.of("host.docker.internal", ports[1]).withWeight(4), - Endpoint.of("host.docker.internal", ports[2]).withWeight(2)); - } - @Container static final GenericContainer nacosContainer = new GenericContainer(DockerImageName.parse("nacos/nacos-server:v2.3.0-slim")) @@ -71,12 +63,18 @@ protected static List newSampleEndpoints() { .withEnv("NACOS_AUTH_TOKEN", NACOS_AUTH_TOKEN) .withEnv("NACOS_AUTH_IDENTITY_KEY", NACOS_AUTH_SECRET) .withEnv("NACOS_AUTH_IDENTITY_VALUE", NACOS_AUTH_SECRET); - @Nullable private static URI nacosUri; protected NacosTestBase() {} + protected static List newSampleEndpoints() { + final int[] ports = unusedPorts(3); + return ImmutableList.of(Endpoint.of("host.docker.internal", ports[0]).withWeight(2), + Endpoint.of("host.docker.internal", ports[1]).withWeight(4), + Endpoint.of("host.docker.internal", ports[2]).withWeight(2)); + } + @BeforeAll static void start() { // Initialize Nacos Client @@ -96,7 +94,7 @@ protected static NacosClient client(@Nullable String serviceName, @Nullable Stri builder.groupName(groupName); } return builder.authorization(NACOS_AUTH_SECRET, NACOS_AUTH_SECRET) - .build(); + .build(); } protected static URI nacosUri() { @@ -108,7 +106,7 @@ protected static int[] unusedPorts(int numPorts) { final int[] ports = new int[numPorts]; final Random random = ThreadLocalRandom.current(); for (int i = 0; i < numPorts; i++) { - for (;;) { + for (; ; ) { final int candidatePort = random.nextInt(64512) + 1024; try (ServerSocket ss = new ServerSocket()) { ss.bind(new InetSocketAddress("127.0.0.1", candidatePort)); diff --git a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java index 22f649d1ec7..bb5b558ffca 100644 --- a/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java +++ b/nacos/src/test/java/com/linecorp/armeria/server/nacos/NacosUpdatingListenerTest.java @@ -76,16 +76,16 @@ static void stopServers() throws Exception { @Test void testBuild() { assertThat(NacosUpdatingListener.builder(nacosUri(), serviceName) - .build()).isNotNull(); + .build()).isNotNull(); assertThat(NacosUpdatingListener.builder(nacosUri(), serviceName) - .build()).isNotNull(); + .build()).isNotNull(); } @Test void testEndpointsCountOfListeningServiceWithAServerStopAndStart() { // Checks sample endpoints created when initialized. await().untilAsserted(() -> assertThat(client(null, null).endpoints() - .join()).hasSameSizeAs(sampleEndpoints)); + .join()).hasSameSizeAs(sampleEndpoints)); // When we close one server then the listener deregister it automatically from nacos. servers.get(0).stop().join(); @@ -100,7 +100,7 @@ void testEndpointsCountOfListeningServiceWithAServerStopAndStart() { servers.get(0).start().join(); await().untilAsserted(() -> assertThat(client(null, null).endpoints() - .join()).hasSameSizeAs(sampleEndpoints)); + .join()).hasSameSizeAs(sampleEndpoints)); } @Test From 7476edec045c6d78925d0a32f82317f9301c367b Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Fri, 28 Jun 2024 16:45:36 +0900 Subject: [PATCH 22/25] NacosEndpointGroup : Batch the tasks related to scheduledFuture into a single eventLoop, instead of using volatile. --- .../client/nacos/NacosEndpointGroup.java | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java index 34eb0a1d02e..417c490ed1c 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroup.java @@ -18,7 +18,6 @@ import static java.util.Objects.requireNonNull; import java.net.URI; -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -28,16 +27,15 @@ import com.google.common.base.MoreObjects; -import com.linecorp.armeria.client.ClientRequestContextCaptor; -import com.linecorp.armeria.client.Clients; import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.endpoint.DynamicEndpointGroup; import com.linecorp.armeria.client.endpoint.EndpointGroup; import com.linecorp.armeria.client.endpoint.EndpointSelectionStrategy; +import com.linecorp.armeria.common.CommonPools; import com.linecorp.armeria.common.annotation.Nullable; import com.linecorp.armeria.internal.nacos.NacosClient; -import io.netty.channel.EventLoop; +import io.netty.util.concurrent.EventExecutor; /** * A Nacos-based {@link EndpointGroup} implementation that retrieves the list of {@link Endpoint} from Nacos @@ -72,8 +70,10 @@ public static NacosEndpointGroupBuilder builder(URI nacosUri, String serviceName private final long registryFetchIntervalMillis; + private final EventExecutor eventLoop; + @Nullable - private volatile ScheduledFuture scheduledFuture; + private ScheduledFuture scheduledFuture; NacosEndpointGroup(EndpointSelectionStrategy selectionStrategy, boolean allowEmptyEndpoints, long selectionTimeoutMillis, NacosClient nacosClient, @@ -81,6 +81,7 @@ public static NacosEndpointGroupBuilder builder(URI nacosUri, String serviceName super(selectionStrategy, allowEmptyEndpoints, selectionTimeoutMillis); this.nacosClient = requireNonNull(nacosClient, "nacosClient"); this.registryFetchIntervalMillis = registryFetchIntervalMillis; + eventLoop = CommonPools.workerGroup().next(); update(); } @@ -90,37 +91,36 @@ private void update() { return; } - final CompletableFuture> response; - final EventLoop eventLoop; - - try (ClientRequestContextCaptor captor = Clients.newContextCaptor()) { - response = nacosClient.endpoints(); - eventLoop = captor.get().eventLoop().withoutContext(); - } - - response.handle((endpoints, cause) -> { - if (isClosing()) { - return null; - } - if (cause != null) { - logger.warn("Unexpected exception while fetching the registry from: {}", - nacosClient.uri(), cause); - } else { - setEndpoints(endpoints); - } - - scheduledFuture = eventLoop.schedule(this::update, - registryFetchIntervalMillis, TimeUnit.MILLISECONDS); - return null; - }); + nacosClient.endpoints() + .handleAsync((endpoints, cause) -> { + if (isClosing()) { + return null; + } + + if (cause != null) { + logger.warn("Unexpected exception while fetching the registry from: {}", + nacosClient.uri(), cause); + } else { + setEndpoints(endpoints); + } + + scheduledFuture = eventLoop.schedule(this::update, registryFetchIntervalMillis, + TimeUnit.MILLISECONDS); + return null; + }, eventLoop); } @Override protected void doCloseAsync(CompletableFuture future) { - final ScheduledFuture scheduledFuture = this.scheduledFuture; + if (!eventLoop.inEventLoop()) { + eventLoop.execute(() -> doCloseAsync(future)); + return; + } + if (scheduledFuture != null) { scheduledFuture.cancel(true); } + future.complete(null); } From 4e291322b15b4ce8fb4e9e9aa4b87899dfccdc0c Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Fri, 28 Jun 2024 18:34:36 +0900 Subject: [PATCH 23/25] lint code. --- .../java/com/linecorp/armeria/internal/nacos/NacosTestBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java index 71c9418b95c..3e771bbf8f9 100644 --- a/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java +++ b/nacos/src/test/java/com/linecorp/armeria/internal/nacos/NacosTestBase.java @@ -106,7 +106,7 @@ protected static int[] unusedPorts(int numPorts) { final int[] ports = new int[numPorts]; final Random random = ThreadLocalRandom.current(); for (int i = 0; i < numPorts; i++) { - for (; ; ) { + for (;;) { final int candidatePort = random.nextInt(64512) + 1024; try (ServerSocket ss = new ServerSocket()) { ss.bind(new InetSocketAddress("127.0.0.1", candidatePort)); From 110ee22cf63304172c8e9c61b717b8a17db02148 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Fri, 28 Jun 2024 19:02:30 +0900 Subject: [PATCH 24/25] Remove override functions of NacosEndpointGroupBuilder (Use default implementations of AbstractDynamicEndpointGroupBuilder) --- .../nacos/NacosEndpointGroupBuilder.java | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java index a7e7d165d92..b87cbe25205 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java @@ -21,7 +21,6 @@ import java.net.URI; import java.time.Duration; -import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.endpoint.AbstractDynamicEndpointGroupBuilder; import com.linecorp.armeria.client.endpoint.EndpointSelectionStrategy; import com.linecorp.armeria.common.Flags; @@ -38,7 +37,8 @@ * sb.serverListener(listener); * } */ -public final class NacosEndpointGroupBuilder extends AbstractDynamicEndpointGroupBuilder { +public final class NacosEndpointGroupBuilder + extends AbstractDynamicEndpointGroupBuilder { private static final long DEFAULT_CHECK_INTERVAL_MILLIS = 30_000; @@ -155,29 +155,4 @@ public NacosEndpointGroup build() { return new NacosEndpointGroup(selectionStrategy, shouldAllowEmptyEndpoints(), selectionTimeoutMillis(), nacosClientBuilder.build(), registryFetchIntervalMillis); } - - @Override - public NacosEndpointGroupBuilder allowEmptyEndpoints(boolean allowEmptyEndpoints) { - return super.allowEmptyEndpoints(allowEmptyEndpoints); - } - - /** - * Sets the timeout to wait until a successful {@link Endpoint} selection. - * {@link Duration#ZERO} disables the timeout. - * If unspecified, {@link Flags#defaultResponseTimeoutMillis()} is used by default. - */ - @Override - public NacosEndpointGroupBuilder selectionTimeout(Duration selectionTimeout) { - return super.selectionTimeout(selectionTimeout); - } - - /** - * Sets the timeout to wait until a successful {@link Endpoint} selection. - * {@code 0} disables the timeout. - * If unspecified, {@link Flags#defaultResponseTimeoutMillis()} is used by default. - */ - @Override - public NacosEndpointGroupBuilder selectionTimeoutMillis(long selectionTimeoutMillis) { - return super.selectionTimeoutMillis(selectionTimeoutMillis); - } } From 0b988bad523fcd2300e8fd437e594eaee6683638 Mon Sep 17 00:00:00 2001 From: KonaEspresso94 Date: Sun, 30 Jun 2024 14:56:44 +0900 Subject: [PATCH 25/25] Methods: namespaceId, groupName, clusterName, and app also can be NacosConfigSetters's member. NacosEndpointGroupBuilder is implementation of NacosConfigSetters. --- .../nacos/NacosEndpointGroupBuilder.java | 67 +++++++------------ .../common/nacos/NacosConfigSetters.java | 36 +++++++++- .../internal/nacos/NacosClientBuilder.java | 50 +++++++------- .../nacos/NacosUpdatingListenerBuilder.java | 26 ++----- 4 files changed, 88 insertions(+), 91 deletions(-) diff --git a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java index b87cbe25205..41f8a9136ab 100644 --- a/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/client/nacos/NacosEndpointGroupBuilder.java @@ -24,6 +24,7 @@ import com.linecorp.armeria.client.endpoint.AbstractDynamicEndpointGroupBuilder; import com.linecorp.armeria.client.endpoint.EndpointSelectionStrategy; import com.linecorp.armeria.common.Flags; +import com.linecorp.armeria.common.nacos.NacosConfigSetters; import com.linecorp.armeria.internal.nacos.NacosClient; import com.linecorp.armeria.internal.nacos.NacosClientBuilder; @@ -38,16 +39,14 @@ * } */ public final class NacosEndpointGroupBuilder - extends AbstractDynamicEndpointGroupBuilder { + extends AbstractDynamicEndpointGroupBuilder + implements NacosConfigSetters { private static final long DEFAULT_CHECK_INTERVAL_MILLIS = 30_000; - + private final NacosClientBuilder nacosClientBuilder; private EndpointSelectionStrategy selectionStrategy = EndpointSelectionStrategy.weightedRoundRobin(); - private long registryFetchIntervalMillis = DEFAULT_CHECK_INTERVAL_MILLIS; - private final NacosClientBuilder nacosClientBuilder; - NacosEndpointGroupBuilder(URI nacosUri, String serviceName) { super(Flags.defaultResponseTimeoutMillis()); nacosClientBuilder = NacosClient.builder(nacosUri, requireNonNull(serviceName, "serviceName")); @@ -61,46 +60,42 @@ public NacosEndpointGroupBuilder selectionStrategy(EndpointSelectionStrategy sel return this; } - /** - * Sets the 'namespaceId' parameter used to filter instances in a Nacos query. - * This method configures the NacosEndpointGroup to query only instances - * that match the specified 'namespaceId' value. - */ + @Override public NacosEndpointGroupBuilder namespaceId(String namespaceId) { nacosClientBuilder.namespaceId(namespaceId); return this; } - /** - * Sets the 'groupName' parameter used to filter instances in a Nacos query. - * This method configures the NacosEndpointGroup to query only instances - * that match the specified 'groupName' value. - */ + @Override public NacosEndpointGroupBuilder groupName(String groupName) { nacosClientBuilder.groupName(groupName); return this; } - /** - * Sets the 'clusterName' parameter used to filter instances in a Nacos query. - * This method configures the NacosEndpointGroup to query only instances - * that match the specified 'clusterName' value. - */ + @Override public NacosEndpointGroupBuilder clusterName(String clusterName) { nacosClientBuilder.clusterName(clusterName); return this; } - /** - * Sets the 'app' parameter used to filter instances in a Nacos query. - * This method configures the NacosEndpointGroup - * to query only instances that match the specified 'app' value. - */ + @Override public NacosEndpointGroupBuilder app(String app) { nacosClientBuilder.app(app); return this; } + @Override + public NacosEndpointGroupBuilder nacosApiVersion(String nacosApiVersion) { + nacosClientBuilder.nacosApiVersion(nacosApiVersion); + return this; + } + + @Override + public NacosEndpointGroupBuilder authorization(String username, String password) { + nacosClientBuilder.authorization(username, password); + return this; + } + /** * Sets the healthy to retrieve only healthy instances from Nacos. * Make sure that your target endpoints are health-checked by Nacos before enabling this feature. @@ -118,8 +113,8 @@ public NacosEndpointGroupBuilder useHealthyEndpoints(boolean useHealthyEndpoints public NacosEndpointGroupBuilder registryFetchInterval(Duration registryFetchInterval) { requireNonNull(registryFetchInterval, "registryFetchInterval"); checkArgument(!registryFetchInterval.isZero() && !registryFetchInterval.isNegative(), - "registryFetchInterval: %s (expected: > 0)", - registryFetchInterval); + "registryFetchInterval: %s (expected: > 0)", + registryFetchInterval); return registryFetchIntervalMillis(registryFetchInterval.toMillis()); } @@ -129,30 +124,16 @@ public NacosEndpointGroupBuilder registryFetchInterval(Duration registryFetchInt */ public NacosEndpointGroupBuilder registryFetchIntervalMillis(long registryFetchIntervalMillis) { checkArgument(registryFetchIntervalMillis > 0, "registryFetchIntervalMillis: %s (expected: > 0)", - registryFetchIntervalMillis); + registryFetchIntervalMillis); this.registryFetchIntervalMillis = registryFetchIntervalMillis; return this; } - /** - * Sets the username and password pair for Nacos's API. - * Please refer to the - * Nacos Authentication Document - * for more details. - * - * @param username the username for access Nacos API, default: {@code null} - * @param password the password for access Nacos API, default: {@code null} - */ - public NacosEndpointGroupBuilder authorization(String username, String password) { - nacosClientBuilder.authorization(username, password); - return this; - } - /** * Returns a newly-created {@link NacosEndpointGroup}. */ public NacosEndpointGroup build() { return new NacosEndpointGroup(selectionStrategy, shouldAllowEmptyEndpoints(), selectionTimeoutMillis(), - nacosClientBuilder.build(), registryFetchIntervalMillis); + nacosClientBuilder.build(), registryFetchIntervalMillis); } } diff --git a/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java b/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java index 97d68680078..282d24f78a5 100644 --- a/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java +++ b/nacos/src/main/java/com/linecorp/armeria/common/nacos/NacosConfigSetters.java @@ -15,18 +15,50 @@ */ package com.linecorp.armeria.common.nacos; +import java.util.regex.Pattern; + import com.linecorp.armeria.common.annotation.UnstableApi; -import com.linecorp.armeria.internal.nacos.NacosClientBuilder; /** * Sets properties for building a Nacos client. */ @UnstableApi public interface NacosConfigSetters> { + String DEFAULT_NACOS_API_VERSION = "v2"; + Pattern NACOS_API_VERSION_PATTERN = Pattern.compile("^v[0-9][-._a-zA-Z0-9]*$"); + + /** + * Sets the namespace ID to query or register instances. + * + * @param namespaceId the namespace ID. + */ + SELF namespaceId(String namespaceId); + + /** + * Sets the group name to query or register instances. + * + * @param groupName the group name. + */ + SELF groupName(String groupName); + + /** + * Sets the cluster name to query or register instances. + * + * @param clusterName the cluster name. + */ + SELF clusterName(String clusterName); + + /** + * Sets the app name to query or register instances. + * + * @param app app name. + */ + SELF app(String app); + /** * Sets the specified Nacos's API version. * @param nacosApiVersion the version of Nacos API service, default: {@value - * NacosClientBuilder#DEFAULT_NACOS_API_VERSION} + * NacosConfigSetters#DEFAULT_NACOS_API_VERSION} */ SELF nacosApiVersion(String nacosApiVersion); diff --git a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java index cbfdc5328cc..8a624eecdac 100644 --- a/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java +++ b/nacos/src/main/java/com/linecorp/armeria/internal/nacos/NacosClientBuilder.java @@ -20,15 +20,11 @@ import java.net.URI; import java.net.URISyntaxException; -import java.util.regex.Pattern; import com.linecorp.armeria.common.annotation.Nullable; import com.linecorp.armeria.common.nacos.NacosConfigSetters; public final class NacosClientBuilder implements NacosConfigSetters { - public static final String DEFAULT_NACOS_API_VERSION = "v2"; - private static final Pattern NACOS_API_VERSION_PATTERN = Pattern.compile("^v[0-9][-._a-zA-Z0-9]*$"); - private final URI nacosUri; private final String serviceName; @@ -61,52 +57,56 @@ public final class NacosClientBuilder implements NacosConfigSettersExamples *
{@code
  * NacosUpdatingListener listener = NacosUpdatingListener.builder(nacosUri, "myService")
- *                                                         .build();
+ *                                                       .build();
  * ServerBuilder sb = Server.builder();
  * sb.serverListener(listener);
  * }
@@ -66,41 +66,25 @@ public NacosUpdatingListenerBuilder endpoint(Endpoint endpoint) { return this; } - /** - * Sets the namespace ID to register the instance. - * - * @param namespaceId the namespace ID to register. - */ + @Override public NacosUpdatingListenerBuilder namespaceId(String namespaceId) { nacosClientBuilder.namespaceId(namespaceId); return this; } - /** - * Sets the group name of the instance. - * - * @param groupName the group name of the instance. - */ + @Override public NacosUpdatingListenerBuilder groupName(String groupName) { nacosClientBuilder.groupName(groupName); return this; } - /** - * Sets the cluster name of the instance. - * - * @param clusterName the cluster name of the instance. - */ + @Override public NacosUpdatingListenerBuilder clusterName(String clusterName) { nacosClientBuilder.clusterName(clusterName); return this; } - /** - * Sets the app name of the instance. - * - * @param app app name of the instance. - */ + @Override public NacosUpdatingListenerBuilder app(String app) { nacosClientBuilder.app(app); return this;