Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Nacos Support #5409

Merged
merged 63 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
785b353
Add NacosClient and NacosEndpointGroup
KonaEspresso94 Jan 21, 2024
a76d4fc
Add NacosUpdatingListener
KonaEspresso94 Jan 24, 2024
4caabaf
Test codes
KonaEspresso94 Jan 24, 2024
3af6f6c
Modified NacosClientUtil::queryParams to return QueryParamsBuilder in…
KonaEspresso94 Jan 27, 2024
d4332aa
Merge branch 'main' into nacos-support
KonaEspresso94 Jan 27, 2024
9530372
Make properties of the classes that define data rendering private and…
KonaEspresso94 Feb 9, 2024
ffc64d7
Make LoginClient an implementation of SimpleDecoratingHttpClient.
KonaEspresso94 Feb 10, 2024
68e1137
LoginClient : Cache and reuse QueryStrings that were unnecessarily re…
KonaEspresso94 Feb 10, 2024
0f71869
QueryInstancesClient : Cache and reuse request path that were unneces…
KonaEspresso94 Feb 10, 2024
46abd6d
Reduce the parameters of the RegisterInstanceClient APIS, by moving a…
KonaEspresso94 Feb 10, 2024
adb40fc
Revert "Modified NacosClientUtil::queryParams to return QueryParamsBu…
KonaEspresso94 Feb 10, 2024
0120fd7
To reuse access-token, instead of the Caffeine cache, use own impleme…
KonaEspresso94 Feb 11, 2024
12b2214
Merge branch 'main' into nacos-support
KonaEspresso94 Feb 11, 2024
23d8913
Merge branch 'main' into nacos-support
KonaEspresso94 May 8, 2024
0d0082d
Merge branch 'main' into nacos-support
KonaEspresso94 Jun 16, 2024
a0d7895
Revert "To reuse access-token, instead of the Caffeine cache, use own…
KonaEspresso94 Jun 16, 2024
a427074
Lint code. (omitted 'final', meaningless lambda expression, unnecessa…
KonaEspresso94 Jun 16, 2024
625535a
Lint code. (Remove meaningless property and use parameter directly)
KonaEspresso94 Jun 16, 2024
ed1d5ba
Lint code. (Optimize imports, according to :nacos:checkStyleMain)
KonaEspresso94 Jun 16, 2024
f94cf66
Copyright header: Line Corporation -> LY Corporation
KonaEspresso94 Jun 16, 2024
c10ebc8
TODO comment about AsyncLoadingCache
KonaEspresso94 Jun 16, 2024
5fe5ba2
Hide NacosEndpointGroup as package private.
KonaEspresso94 Jun 19, 2024
03150f2
Apply AbstractDynamicEndpointGroupBuilder's changes(SELF type parameter)
KonaEspresso94 Jun 19, 2024
88f6235
Use SELF type parameter for NacosConfigSetters.
KonaEspresso94 Jun 19, 2024
0a715cd
Lint code: Run auto-indent.
KonaEspresso94 Jun 28, 2024
7476ede
NacosEndpointGroup : Batch the tasks related to scheduledFuture into …
KonaEspresso94 Jun 28, 2024
4e29132
lint code.
KonaEspresso94 Jun 28, 2024
110ee22
Remove override functions of NacosEndpointGroupBuilder (Use default i…
KonaEspresso94 Jun 28, 2024
0b988ba
Methods: namespaceId, groupName, clusterName, and app also can be Nac…
KonaEspresso94 Jun 30, 2024
8933fcd
Merge branch 'main' into nacos-support
KonaEspresso94 Jul 1, 2024
94568bd
Merge branch 'main' into nacos-support
jrhee17 Jul 11, 2024
6231ee9
Make NacosEndpointGroup public final class, and mark as UnstableApi
KonaEspresso94 Jul 21, 2024
c2cd4e4
mark NacosEndpointGroupBuilder as UnstableApi
KonaEspresso94 Jul 21, 2024
4f983a5
NN by default. remove verbose @NonNull marks.
KonaEspresso94 Jul 21, 2024
1d3f9c6
Hide LoginClient#newDecorator. (public -> private)
KonaEspresso94 Jul 21, 2024
c773424
LoginResult#tokenTtl is non-nullable. Make it primitive type.
KonaEspresso94 Jul 21, 2024
cccc61e
Remove omitNullValues
KonaEspresso94 Jul 21, 2024
4ad84a5
Insert empty line between static and non-static fields.
KonaEspresso94 Jul 21, 2024
e514772
Change DEFAULT_CHECK_INTERVAL_MILLIS to 10sec.
KonaEspresso94 Jul 21, 2024
edc84f2
Pass WebClient directly to QueryInstancesClient and RegisterInstanceC…
KonaEspresso94 Jul 21, 2024
44ac036
NacosClientUtil#queryParams#serviceName is NonNull on all call sites.
KonaEspresso94 Jul 21, 2024
dab94b1
Use Boolean.FALSE.equals, instead of null check && boolean ref.
KonaEspresso94 Jul 21, 2024
597d9f4
Mark NacosUpdatingListener as UnstableApi
KonaEspresso94 Jul 21, 2024
f6141d3
Modify example code of NacosEndpointGroupBuilder javadocs, according …
KonaEspresso94 Jul 21, 2024
0d56428
Remove verbose logic to add path /nacos to nacosUri, and just use res…
KonaEspresso94 Jul 21, 2024
2ffafe0
RetryingClientDecorator should be outside the authentication decorato…
KonaEspresso94 Jul 21, 2024
8c003bc
Null check for Response.
KonaEspresso94 Jul 21, 2024
dc532a0
Add missing ctx.updateRequest(newReq);
KonaEspresso94 Jul 21, 2024
02c412c
Remove verbose javadocs @params.
KonaEspresso94 Jul 21, 2024
dff6e08
Lint Style
KonaEspresso94 Jul 21, 2024
ce4fe89
Use Exceptions.throwUnsafely(e)
KonaEspresso94 Jul 21, 2024
2841315
Delegate registryFetchInterval's registryFetchInterval non zero, non …
KonaEspresso94 Jul 21, 2024
73a2e0f
Move DEFAULT_NACOS_API_VERSION and NACOS_API_VERSION_PATTERN to inter…
KonaEspresso94 Jul 21, 2024
83fdffa
Allows the user to directly control the path of the Nacos URI.
KonaEspresso94 Jul 21, 2024
268023f
Lint Code
KonaEspresso94 Jul 21, 2024
e287a3e
Lint Code
KonaEspresso94 Jul 21, 2024
d7c9dfc
Merge branch 'main' into nacos-support
ikhoon Jul 25, 2024
a34068d
clean up
ikhoon Jul 25, 2024
c690614
Merge branch 'main' into pr-5409-KonaEspresso94-nacos-support
ikhoon Aug 2, 2024
aae35fb
Apply AsyncLoader
ikhoon Aug 2, 2024
e8ce9c5
remove this
ikhoon Aug 2, 2024
7f323be
Merge branch 'main' into nacos-support
jrhee17 Nov 7, 2024
73c1699
Fix
minwoox Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions nacos/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dependencies {
implementation(libs.caffeine)
testImplementation(libs.testcontainers.junit.jupiter)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* 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:
*
* 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.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.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.common.annotation.UnstableApi;
import com.linecorp.armeria.internal.nacos.NacosClient;

import io.netty.util.concurrent.EventExecutor;

/**
* A Nacos-based {@link EndpointGroup} implementation that retrieves the list of {@link Endpoint} from Nacos
* using <a href="https://nacos.io/en-us/docs/v2/guide/user/open-api.html">Nacos's HTTP Open API</a>
* and updates the {@link Endpoint}s periodically.
*/
@UnstableApi
public final class NacosEndpointGroup extends DynamicEndpointGroup {

private static final Logger logger = LoggerFactory.getLogger(NacosEndpointGroup.class);

/**
* Returns a {@link NacosEndpointGroup} with the specified {@code serviceName}.
*/
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, including the path up to but not including API version.
* (example: http://localhost:8848/nacos)
*/
public static NacosEndpointGroupBuilder builder(URI nacosUri, String serviceName) {
return new NacosEndpointGroupBuilder(nacosUri, serviceName);
}

private final NacosClient nacosClient;

private final long registryFetchIntervalMillis;

private final EventExecutor eventLoop;

@Nullable
private ScheduledFuture<?> scheduledFuture;
KonaEspresso94 marked this conversation as resolved.
Show resolved Hide resolved

NacosEndpointGroup(EndpointSelectionStrategy selectionStrategy, boolean allowEmptyEndpoints,
long selectionTimeoutMillis, NacosClient nacosClient,
long registryFetchIntervalMillis) {
super(selectionStrategy, allowEmptyEndpoints, selectionTimeoutMillis);
this.nacosClient = requireNonNull(nacosClient, "nacosClient");
this.registryFetchIntervalMillis = registryFetchIntervalMillis;
eventLoop = CommonPools.workerGroup().next();

update();
}

private void update() {
if (isClosing()) {
return;
}

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) {
if (!eventLoop.inEventLoop()) {
eventLoop.execute(() -> doCloseAsync(future));
return;
}

if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}

future.complete(null);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("registryFetchIntervalMillis", registryFetchIntervalMillis)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* 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:
*
* 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.AbstractDynamicEndpointGroupBuilder;
import com.linecorp.armeria.client.endpoint.EndpointSelectionStrategy;
import com.linecorp.armeria.common.Flags;
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;

/**
* A builder class for {@link NacosEndpointGroup}.
* <h2>Examples</h2>
* <pre>{@code
* NacosEndpointGroup endpointGroup = NacosEndpointGroup.builder(nacosUri, "myService")
* .build();
* WebClient client = WebClient.of(SessionProtocol.HTTPS, endpointGroup);
* }</pre>
*/
@UnstableApi
public final class NacosEndpointGroupBuilder
KonaEspresso94 marked this conversation as resolved.
Show resolved Hide resolved
extends AbstractDynamicEndpointGroupBuilder<NacosEndpointGroupBuilder>
implements NacosConfigSetters<NacosEndpointGroupBuilder> {

private static final long DEFAULT_CHECK_INTERVAL_MILLIS = 10_000;

private final NacosClientBuilder nacosClientBuilder;
private EndpointSelectionStrategy selectionStrategy = EndpointSelectionStrategy.weightedRoundRobin();
private long registryFetchIntervalMillis = DEFAULT_CHECK_INTERVAL_MILLIS;

NacosEndpointGroupBuilder(URI nacosUri, String serviceName) {
super(Flags.defaultResponseTimeoutMillis());
nacosClientBuilder = NacosClient.builder(nacosUri, requireNonNull(serviceName, "serviceName"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we be consistent with how the rnn is done for nacosUri and serviceName? (Either rnn here, or just delegate the rnn to NacosClientBuilder)

}

/**
* Sets the {@link EndpointSelectionStrategy} of the {@link NacosEndpointGroup}.
*/
public NacosEndpointGroupBuilder selectionStrategy(EndpointSelectionStrategy selectionStrategy) {
this.selectionStrategy = requireNonNull(selectionStrategy, "selectionStrategy");
return this;
}

@Override
public NacosEndpointGroupBuilder namespaceId(String namespaceId) {
nacosClientBuilder.namespaceId(namespaceId);
return this;
}

@Override
public NacosEndpointGroupBuilder groupName(String groupName) {
nacosClientBuilder.groupName(groupName);
return this;
}

@Override
public NacosEndpointGroupBuilder clusterName(String clusterName) {
nacosClientBuilder.clusterName(clusterName);
return this;
}

@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.
* If not set, false is used by default.
*/
public NacosEndpointGroupBuilder useHealthyEndpoints(boolean useHealthyEndpoints) {
nacosClientBuilder.healthyOnly(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");
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;
}

/**
* Returns a newly-created {@link NacosEndpointGroup}.
*/
public NacosEndpointGroup build() {
return new NacosEndpointGroup(selectionStrategy, shouldAllowEmptyEndpoints(), selectionTimeoutMillis(),
nacosClientBuilder.build(), registryFetchIntervalMillis);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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:
*
* 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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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:
*
* 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<SELF extends NacosConfigSetters<SELF>> {

/**
* Sets the namespace ID to query or register instances.
*/
SELF namespaceId(String namespaceId);

/**
* Sets the group name to query or register instances.
*/
SELF groupName(String groupName);

/**
* Sets the cluster name to query or register instances.
*/
SELF clusterName(String clusterName);

/**
* Sets the app name to query or register instances.
*/
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}
*/
SELF nacosApiVersion(String nacosApiVersion);

/**
* Sets the username and password pair for Nacos's API.
* Please refer to the
* <a href=https://nacos.io/en-us/docs/v2/guide/user/auth.html>Nacos Authentication Document</a>
* 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}
*/
SELF authorization(String username, String password);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about also adding a method that takes a token?
I think most users will:

  • Issue a token using the username and password.
  • Specify the token to this client.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@minwoox

According to the Nacos documentation, there is no official support for generating access tokens directly. However, it can be added through a customized auth plugin. Since the specific requirements of the custom plugin are unpredictable, it seems preferable to allow users to customize decorator instead of provided LoginClient decorator.

Also, would it be beneficial to provide an implementation for Aliyun(Alibaba Cloud spec?) Auth?

https://nacos.io/en-us/docs/v2/plugin/auth-plugin.html

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know that requires additional plugin. 😓
Let's keep as is and implement the plugin later if there's a demand for that. 😉

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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:
*
* 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;
Loading
Loading