Upgrade etcd cluster coordinator and dynamic configuration to v3.x (#7215)
This commit is contained in:
parent
0efc733901
commit
5dd56a5ca4
|
|
@ -72,6 +72,7 @@ Release Notes.
|
|||
* Support session expired threshold configurable.
|
||||
* Fix InfluxDB storage-plugin Metrics#multiGet issue.
|
||||
* Replace zuul proxy with spring cloud gateway 2.x. in webapp module.
|
||||
* Upgrade etcd cluster coordinator and dynamic configuration to v3.x.
|
||||
|
||||
#### UI
|
||||
|
||||
|
|
|
|||
|
|
@ -320,11 +320,9 @@ The text of each license is the standard Apache 2.0 license.
|
|||
proto files from lyft/protoc-gen-validate: https://github.com/lyft/protoc-gen-validate Apache 2.0
|
||||
proto files from gogo/googleapis: https://github.com/gogo/googleapis Apache 2.0
|
||||
flatbuffers files from istio/proxy: https://github.com/istio/proxy Apache 2.0
|
||||
json-flatter 0.6.0: https://github.com/wnameless/json-flattener Apache 2.0
|
||||
Apache: commons-text 1.4: https://github.com/apache/commons-text Apache 2.0
|
||||
sundrio 0.9.2: https://github.com/sundrio/sundrio Apache 2.0
|
||||
Ctripcorp: apollo 1.8.0: https://github.com/ctripcorp/apollo Apache 2.0
|
||||
etcd4j 2.18.0: https://github.com/jurmous/etcd4j Apache 2.0
|
||||
javaassist 3.25.0-GA: https://github.com/jboss-javassist/javassist Apache 2.0
|
||||
jackson-module-afterburner 2.12.2: https://github.com/FasterXML/jackson-modules-base, Apache 2.0
|
||||
perfmark-api 0.19.0: https://github.com/perfmark/perfmark, Apache 2.0
|
||||
|
|
@ -343,6 +341,8 @@ The text of each license is the standard Apache 2.0 license.
|
|||
okio 1.17.2: https://github.com/square/okio Apache 2.0
|
||||
caffeine 2.6.2: https://github.com/ben-manes/caffeine Apache 2.0
|
||||
simpleclient_httpserver from prometheus https://github.com/prometheus/client_java Apache 2.0
|
||||
jetcd 0.5.3, https://github.com/etcd-io/jetcd, Apache 2.0
|
||||
failasfe 2.3.4, https://github.com/jhalterman/failsafe, Apache 2.0
|
||||
|
||||
========================================================================
|
||||
MIT licenses
|
||||
|
|
|
|||
|
|
@ -896,3 +896,17 @@ This distribution has a binary dependency on jersey, which is available under th
|
|||
License. The source code of jersey can be found at https://github.com/jersey/jersey/.
|
||||
|
||||
========================================================================
|
||||
|
||||
------
|
||||
|
||||
===========================================================================
|
||||
jetcd Notice
|
||||
===========================================================================
|
||||
|
||||
CoreOS Project
|
||||
Copyright 2018 CoreOS, Inc
|
||||
|
||||
This product includes software developed at CoreOS, Inc.
|
||||
(http://www.coreos.com/).
|
||||
|
||||
===========================================================================
|
||||
|
|
|
|||
|
|
@ -89,12 +89,20 @@ The following settings are provided to set the host and port manually, based on
|
|||
|
||||
|
||||
## Etcd
|
||||
Set the **cluster/selector** to **etcd** in the yml to enable it.
|
||||
Set the **cluster/selector** to **etcd** in the yml to enable it. The Etcd client has upgraded to v3 protocol and changed to the CoreOS official library. **Since 8.7.0, only the v3 protocol is supported for Etcd.**
|
||||
|
||||
```yaml
|
||||
cluster:
|
||||
selector: ${SW_CLUSTER:etcd}
|
||||
# other configurations
|
||||
etcd:
|
||||
# etcd cluster nodes, example: 10.0.0.1:2379,10.0.0.2:2379,10.0.0.3:2379
|
||||
endpoints: ${SW_CLUSTER_ETCD_ENDPOINTS:localhost:2379}
|
||||
namespace: ${SW_CLUSTER_ETCD_NAMESPACE:/skywalking}
|
||||
serviceName: ${SW_SCLUSTER_ETCD_ERVICE_NAME:"SkyWalking_OAP_Cluster"}
|
||||
authentication: ${SW_CLUSTER_ETCD_AUTHENTICATION:false}
|
||||
user: ${SW_SCLUSTER_ETCD_USER:}
|
||||
password: ${SW_SCLUSTER_ETCD_PASSWORD:}
|
||||
```
|
||||
|
||||
Same as the Zookeeper coordinator,
|
||||
|
|
|
|||
|
|
@ -64,11 +64,12 @@ core|default|role|Option values, `Mixed/Receiver/Aggregator`. **Receiver** mode
|
|||
| - | - | aclToken| ALC Token of Consul. Empty string means `without ALC token`.| SW_CLUSTER_CONSUL_ACLTOKEN | - |
|
||||
| - | - | internalComHost| The hostname registered in the Consul for the internal communication of OAP cluster.| - | -|
|
||||
| - | - | internalComPort| The port registered in the Consul for the internal communication of OAP cluster.| - | -1|
|
||||
| - | etcd| serviceName| Service name used for SkyWalking cluster. |SW_SERVICE_NAME|SkyWalking_OAP_Cluster|
|
||||
| - | - | hostPort| hosts and ports used of etcd cluster.| SW_CLUSTER_ETCD_HOST_PORT|localhost:2379|
|
||||
| - | - | isSSL| Open SSL for the connection between SkyWalking and etcd cluster.| - | - |
|
||||
| - | - | internalComHost| The hostname registered in the etcd for the internal communication of OAP cluster.| - | -|
|
||||
| - | - | internalComPort| The port registered in the etcd for the internal communication of OAP cluster.| - | -1|
|
||||
| - | etcd| serviceName| Service name used for SkyWalking cluster. |SW_CLUSTER_ETCD_SERVICE_NAME|SkyWalking_OAP_Cluster|
|
||||
| - | - | endpoints| hosts and ports used of etcd cluster.| SW_CLUSTER_ETCD_ENDPOINTS|localhost:2379|
|
||||
| - | - | namespace | Namespace used for SkyWalking cluster. |SW_CLUSTER_ETCD_NAMESPACE | /skywalking |
|
||||
| - | - | authentication | Whether has authentication. | SW_CLUSTER_ETCD_AUTHENTICATION | false |
|
||||
| - | - | user | Etcd auth username | SW_CLUSTER_ETCD_USER | |
|
||||
| - | - | password | Etcd auth password | SW_CLUSTER_ETCD_PASSWORD | |
|
||||
| - | Nacos| serviceName| Service name used for SkyWalking cluster. |SW_SERVICE_NAME|SkyWalking_OAP_Cluster|
|
||||
| - | - | hostPort| hosts and ports used of Nacos cluster.| SW_CLUSTER_NACOS_HOST_PORT|localhost:8848|
|
||||
| - | - | namespace| Namespace used by SkyWalking node coordination.| SW_CLUSTER_NACOS_NAMESPACE|public|
|
||||
|
|
@ -264,9 +265,11 @@ core|default|role|Option values, `Mixed/Receiver/Aggregator`. **Receiver** mode
|
|||
| - | - | baseSleepTimeMs|The period of Zookeeper client between two retries. Unit is ms.|SW_CONFIG_ZK_BASE_SLEEP_TIME_MS|1000|
|
||||
| - | - | maxRetries| The max retry time of re-trying.|SW_CONFIG_ZK_MAX_RETRIES|3|
|
||||
| - | - | period | The period of data sync. Unit is second. | SW_CONFIG_ZK_PERIOD | 60 |
|
||||
| - | etcd| clusterName| Service name used for SkyWalking cluster. |SW_CONFIG_ETCD_CLUSTER_NAME|default|
|
||||
| - | - | serverAddr| hosts and ports used of etcd cluster.| SW_CONFIG_ETCD_SERVER_ADDR|localhost:2379|
|
||||
| - | - | group |Additional prefix of the configuration key| SW_CONFIG_ETCD_GROUP | skywalking|
|
||||
| - | etcd| endpoints | hosts and ports used of etcd cluster(If there are multiple, separate them with commas). | SW_CONFIG_ETCD_ENDPOINTS | localhost:2379 |
|
||||
| - | - | namespace | Namespace used for SkyWalking cluster. |SW_CONFIG_ETCD_NAMESPACE | /skywalking |
|
||||
| - | - | authentication | Whether has authentication. | SW_CONFIG_ETCD_AUTHENTICATION | false |
|
||||
| - | - | user | Etcd auth username | SW_CONFIG_ETCD_USER | |
|
||||
| - | - | password | Etcd auth password | SW_CONFIG_ETCD_PASSWORD | |
|
||||
| - | - | period | The period of data sync. Unit is second. | SW_CONFIG_ZK_PERIOD | 60
|
||||
| - | consul | hostPort| hosts and ports used of Consul cluster.| SW_CONFIG_CONSUL_HOST_AND_PORTS|localhost:8500|
|
||||
| - | - | aclToken| ALC Token of Consul. Empty string means `without ALC token`.| SW_CONFIG_CONSUL_ACL_TOKEN | - |
|
||||
|
|
|
|||
|
|
@ -70,11 +70,15 @@ configuration:
|
|||
selector: ${SW_CONFIGURATION:etcd}
|
||||
etcd:
|
||||
period: ${SW_CONFIG_ETCD_PERIOD:60} # Unit seconds, sync period. Default fetch every 60 seconds.
|
||||
group: ${SW_CONFIG_ETCD_GROUP:skywalking}
|
||||
serverAddr: ${SW_CONFIG_ETCD_SERVER_ADDR:localhost:2379}
|
||||
clusterName: ${SW_CONFIG_ETCD_CLUSTER_NAME:default}
|
||||
endpoints: ${SW_CONFIG_ETCD_ENDPOINTS:localhost:2379}
|
||||
namespace: ${SW_CONFIG_ETCD_NAMESPACE:/skywalking}
|
||||
authentication: ${SW_CONFIG_ETCD_AUTHENTICATION:false}
|
||||
user: ${SW_CONFIG_ETCD_USER:}
|
||||
password: ${SW_CONFIG_ETCD_password:}
|
||||
```
|
||||
|
||||
**NOTICE**, only the v3 protocol is supported since 8.7.0.
|
||||
|
||||
## Dynamic Configuration Consul Implementation
|
||||
|
||||
[Consul](https://github.com/rickfast/consul-client) is also supported as DCC(Dynamic Configuration Center), to use it, please configure as follows:
|
||||
|
|
|
|||
|
|
@ -73,10 +73,19 @@
|
|||
<commons-beanutils.version>1.9.4</commons-beanutils.version>
|
||||
<flatbuffers-java.version>1.12.0</flatbuffers-java.version>
|
||||
<postgresql.version>42.2.18</postgresql.version>
|
||||
<jetcd.version>0.5.3</jetcd.version>
|
||||
<testcontainers.version>1.15.3</testcontainers.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-bom</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<scope>import</scope>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
|
|
@ -200,6 +209,16 @@
|
|||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http2</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler-proxy</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
|
|
@ -252,6 +271,11 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-grpclb</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
|
|
@ -329,65 +353,37 @@
|
|||
<artifactId>nacos-client</artifactId>
|
||||
<version>${nacos.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mousio</groupId>
|
||||
<artifactId>etcd4j</artifactId>
|
||||
<groupId>io.etcd</groupId>
|
||||
<artifactId>jetcd-core</artifactId>
|
||||
<version>${jetcd.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>netty-codec-dns</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-core</artifactId>
|
||||
</exclusion>
|
||||
|
||||
<exclusion>
|
||||
<artifactId>netty-codec-dns</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty</artifactId>
|
||||
</exclusion>
|
||||
|
||||
<exclusion>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
</exclusion>
|
||||
|
||||
<exclusion>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
</exclusion>
|
||||
|
||||
<exclusion>
|
||||
<artifactId>netty-resolver-dns</artifactId>
|
||||
<groupId>io.netty</groupId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-grpclb</artifactId>
|
||||
</exclusion>
|
||||
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-afterburner</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<version>${etcd4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-resolver-dns</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-dns</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
@ -496,6 +492,13 @@
|
|||
<artifactId>postgresql</artifactId>
|
||||
<version>${postgresql.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>${testcontainers.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@
|
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<maven-docker-plugin.version>0.30.0</maven-docker-plugin.version>
|
||||
<etcd.version>v3.2.3</etcd.version>
|
||||
<zookeeper.image.version>3.5</zookeeper.image.version>
|
||||
<kafka-clients.version>2.4.1</kafka-clients.version>
|
||||
<spring-kafka-test.version>2.4.6.RELEASE</spring-kafka-test.version>
|
||||
|
|
|
|||
|
|
@ -38,9 +38,13 @@ cluster:
|
|||
hostPort: ${SW_CLUSTER_CONSUL_HOST_PORT:localhost:8500}
|
||||
aclToken: ${SW_CLUSTER_CONSUL_ACLTOKEN:""}
|
||||
etcd:
|
||||
serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}
|
||||
# etcd cluster nodes, example: 10.0.0.1:2379,10.0.0.2:2379,10.0.0.3:2379
|
||||
hostPort: ${SW_CLUSTER_ETCD_HOST_PORT:localhost:2379}
|
||||
endpoints: ${SW_CLUSTER_ETCD_ENDPOINTS:localhost:2379}
|
||||
namespace: ${SW_CLUSTER_ETCD_NAMESPACE:/skywalking}
|
||||
serviceName: ${SW_SCLUSTER_ETCD_ERVICE_NAME:"SkyWalking_OAP_Cluster"}
|
||||
authentication: ${SW_CLUSTER_ETCD_AUTHENTICATION:false}
|
||||
user: ${SW_SCLUSTER_ETCD_USER:}
|
||||
password: ${SW_SCLUSTER_ETCD_PASSWORD:}
|
||||
nacos:
|
||||
serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}
|
||||
hostPort: ${SW_CLUSTER_NACOS_HOST_PORT:localhost:8848}
|
||||
|
|
@ -448,9 +452,11 @@ configuration:
|
|||
maxRetries: ${SW_CONFIG_ZK_MAX_RETRIES:3} # max number of times to retry
|
||||
etcd:
|
||||
period: ${SW_CONFIG_ETCD_PERIOD:60} # Unit seconds, sync period. Default fetch every 60 seconds.
|
||||
group: ${SW_CONFIG_ETCD_GROUP:skywalking}
|
||||
serverAddr: ${SW_CONFIG_ETCD_SERVER_ADDR:localhost:2379}
|
||||
clusterName: ${SW_CONFIG_ETCD_CLUSTER_NAME:default}
|
||||
endpoints: ${SW_CONFIG_ETCD_ENDPOINTS:localhost:2379}
|
||||
namespace: ${SW_CONFIG_ETCD_NAMESPACE:/skywalking}
|
||||
authentication: ${SW_CONFIG_ETCD_AUTHENTICATION:false}
|
||||
user: ${SW_CONFIG_ETCD_USER:}
|
||||
password: ${SW_CONFIG_ETCD_password:}
|
||||
consul:
|
||||
# Consul host and ports, separated by comma, e.g. 1.2.3.4:8500,2.3.4.5:8500
|
||||
hostAndPorts: ${SW_CONFIG_CONSUL_HOST_AND_PORTS:1.2.3.4:8500}
|
||||
|
|
|
|||
|
|
@ -37,35 +37,54 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-dns</artifactId>
|
||||
<groupId>io.etcd</groupId>
|
||||
<artifactId>jetcd-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
<artifactId>netty-codec-http2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<artifactId>netty-handler-proxy</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-resolver-dns</artifactId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mousio</groupId>
|
||||
<artifactId>etcd4j</artifactId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-afterburner</artifactId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-grpclb</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
|
@ -73,70 +92,9 @@
|
|||
<id>CI-with-IT</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<sourceMode>all</sourceMode>
|
||||
<logDate>default</logDate>
|
||||
<verbose>true</verbose>
|
||||
<imagePullPolicy>IfNotPresent</imagePullPolicy>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>start</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<images>
|
||||
<image>
|
||||
<name>quayio/coreos-etcd:${etcd.version}</name>
|
||||
<alias>etcd-client-integration-test</alias>
|
||||
<run>
|
||||
<ports>
|
||||
<port>+etcd.host:etcd.port:2379</port>
|
||||
</ports>
|
||||
<wait>
|
||||
<time>20000</time>
|
||||
</wait>
|
||||
<entrypoint>
|
||||
<!-- exec form -->
|
||||
<exec>
|
||||
<arg>/usr/local/bin/etcd</arg>
|
||||
<arg>--advertise-client-urls=http://0.0.0.0:2379</arg>
|
||||
<arg>--listen-client-urls=http://0.0.0.0:2379</arg>
|
||||
</exec>
|
||||
</entrypoint>
|
||||
</run>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>remove-it-etcd</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<etcd.host>
|
||||
${etcd.host}
|
||||
</etcd.host>
|
||||
<etcd.port>
|
||||
${etcd.port}
|
||||
</etcd.port>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
|
|
|||
|
|
@ -18,25 +18,38 @@
|
|||
|
||||
package org.apache.skywalking.oap.server.cluster.plugin.etcd;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import com.google.common.base.Strings;
|
||||
import java.util.Arrays;
|
||||
import lombok.Data;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
|
||||
|
||||
@Data
|
||||
public class ClusterModuleEtcdConfig extends ModuleConfig {
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private String serviceName;
|
||||
@Setter
|
||||
@Getter
|
||||
private String hostPort;
|
||||
@Setter
|
||||
@Getter
|
||||
private boolean isSSL;
|
||||
@Setter
|
||||
@Getter
|
||||
private String endpoints;
|
||||
private String namespace;
|
||||
|
||||
private String authority;
|
||||
|
||||
private boolean authentication;
|
||||
private String user;
|
||||
private String password;
|
||||
|
||||
private String internalComHost;
|
||||
@Setter
|
||||
@Getter
|
||||
private int internalComPort = -1;
|
||||
|
||||
public String getNamespace() {
|
||||
if (Strings.isNullOrEmpty(namespace)) {
|
||||
return null;
|
||||
}
|
||||
if (!namespace.endsWith("/")) {
|
||||
return namespace + "/";
|
||||
}
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public String[] getEndpointArray() {
|
||||
return Arrays.stream(endpoints.split("\\s*,\\s*")).toArray(String[]::new);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,6 @@
|
|||
|
||||
package org.apache.skywalking.oap.server.cluster.plugin.etcd;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import mousio.etcd4j.EtcdClient;
|
||||
import org.apache.skywalking.oap.server.core.CoreModule;
|
||||
import org.apache.skywalking.oap.server.core.cluster.ClusterModule;
|
||||
import org.apache.skywalking.oap.server.core.cluster.ClusterNodesQuery;
|
||||
|
|
@ -38,10 +35,7 @@ public class ClusterModuleEtcdProvider extends ModuleProvider {
|
|||
|
||||
private final ClusterModuleEtcdConfig config;
|
||||
|
||||
private EtcdClient client;
|
||||
|
||||
public ClusterModuleEtcdProvider() {
|
||||
super();
|
||||
this.config = new ClusterModuleEtcdConfig();
|
||||
}
|
||||
|
||||
|
|
@ -62,12 +56,13 @@ public class ClusterModuleEtcdProvider extends ModuleProvider {
|
|||
|
||||
@Override
|
||||
public void prepare() throws ServiceNotProvidedException, ModuleStartException {
|
||||
List<URI> uris = EtcdUtils.parse(config);
|
||||
//TODO check isSSL
|
||||
client = new EtcdClient(uris.toArray(new URI[] {}));
|
||||
EtcdCoordinator coordinator = new EtcdCoordinator(getManager(), config, client);
|
||||
this.registerServiceImplementation(ClusterRegister.class, coordinator);
|
||||
this.registerServiceImplementation(ClusterNodesQuery.class, coordinator);
|
||||
try {
|
||||
EtcdCoordinator coordinator = new EtcdCoordinator(getManager(), config);
|
||||
this.registerServiceImplementation(ClusterRegister.class, coordinator);
|
||||
this.registerServiceImplementation(ClusterNodesQuery.class, coordinator);
|
||||
} catch (Exception e) {
|
||||
throw new ModuleStartException("Failed to start ETCD coordinator.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -20,15 +20,21 @@ package org.apache.skywalking.oap.server.cluster.plugin.etcd;
|
|||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gson.Gson;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.Client;
|
||||
import io.etcd.jetcd.ClientBuilder;
|
||||
import io.etcd.jetcd.KV;
|
||||
import io.etcd.jetcd.Lease;
|
||||
import io.etcd.jetcd.kv.GetResponse;
|
||||
import io.etcd.jetcd.lease.LeaseKeepAliveResponse;
|
||||
import io.etcd.jetcd.options.GetOption;
|
||||
import io.etcd.jetcd.options.PutOption;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import mousio.etcd4j.EtcdClient;
|
||||
import mousio.etcd4j.promises.EtcdResponsePromise;
|
||||
import mousio.etcd4j.responses.EtcdKeysResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.skywalking.apm.util.StringUtil;
|
||||
import org.apache.skywalking.oap.server.core.cluster.ClusterHealthStatus;
|
||||
import org.apache.skywalking.oap.server.core.cluster.ClusterNodesQuery;
|
||||
import org.apache.skywalking.oap.server.core.cluster.ClusterRegister;
|
||||
|
|
@ -37,30 +43,48 @@ import org.apache.skywalking.oap.server.core.cluster.RemoteInstance;
|
|||
import org.apache.skywalking.oap.server.core.cluster.ServiceRegisterException;
|
||||
import org.apache.skywalking.oap.server.core.remote.client.Address;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleDefineHolder;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
|
||||
import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
|
||||
import org.apache.skywalking.oap.server.telemetry.api.HealthCheckMetrics;
|
||||
import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator;
|
||||
import org.apache.skywalking.oap.server.telemetry.api.MetricsTag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Slf4j
|
||||
public class EtcdCoordinator implements ClusterRegister, ClusterNodesQuery {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EtcdCoordinator.class);
|
||||
private static final Integer KEY_TTL = 45;
|
||||
|
||||
private static final Gson GSON = new Gson().newBuilder().create();
|
||||
private final ModuleDefineHolder manager;
|
||||
private final ClusterModuleEtcdConfig config;
|
||||
private final EtcdClient client;
|
||||
private final String serviceName;
|
||||
private final ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
|
||||
private volatile Address selfAddress;
|
||||
private HealthCheckMetrics healthChecker;
|
||||
|
||||
public EtcdCoordinator(final ModuleDefineHolder manager, final ClusterModuleEtcdConfig config, final EtcdClient client) {
|
||||
private final Client client;
|
||||
private final String serviceName;
|
||||
private final ByteSequence serviceNameBS;
|
||||
|
||||
public EtcdCoordinator(final ModuleDefineHolder manager,
|
||||
final ClusterModuleEtcdConfig config) throws ModuleStartException {
|
||||
if (Strings.isNullOrEmpty(config.getServiceName())) {
|
||||
throw new ModuleStartException("ServiceName cannot be empty.");
|
||||
}
|
||||
this.manager = manager;
|
||||
this.config = config;
|
||||
this.client = client;
|
||||
this.serviceName = config.getServiceName();
|
||||
if (!config.getServiceName().endsWith("/")) {
|
||||
serviceName = config.getServiceName() + "/";
|
||||
} else {
|
||||
serviceName = config.getServiceName();
|
||||
}
|
||||
this.serviceNameBS = ByteSequence.from(serviceName, Charset.defaultCharset());
|
||||
ClientBuilder builder = Client.builder()
|
||||
.endpoints(config.getEndpointArray())
|
||||
.authority(config.getAuthority());
|
||||
if (StringUtil.isNotEmpty(config.getNamespace())) {
|
||||
builder.namespace(ByteSequence.from(config.getNamespace(), Charset.defaultCharset()));
|
||||
}
|
||||
if (config.isAuthentication()) {
|
||||
builder.user(ByteSequence.from(config.getUser(), Charset.defaultCharset()))
|
||||
.password(ByteSequence.from(config.getPassword(), Charset.defaultCharset()));
|
||||
}
|
||||
this.client = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -68,20 +92,25 @@ public class EtcdCoordinator implements ClusterRegister, ClusterNodesQuery {
|
|||
List<RemoteInstance> remoteInstances = new ArrayList<>();
|
||||
try {
|
||||
initHealthChecker();
|
||||
EtcdKeysResponse response = client.get(serviceName + "/").send().get();
|
||||
List<EtcdKeysResponse.EtcdNode> nodes = response.getNode().getNodes();
|
||||
|
||||
Gson gson = new Gson();
|
||||
if (nodes != null) {
|
||||
nodes.forEach(node -> {
|
||||
EtcdEndpoint endpoint = gson.fromJson(node.getValue(), EtcdEndpoint.class);
|
||||
Address address = new Address(endpoint.getHost(), endpoint.getPort(), true);
|
||||
if (!address.equals(selfAddress)) {
|
||||
address.setSelf(false);
|
||||
}
|
||||
remoteInstances.add(new RemoteInstance(address));
|
||||
});
|
||||
}
|
||||
final KV kvClient = client.getKVClient();
|
||||
final GetResponse response = kvClient.get(
|
||||
serviceNameBS,
|
||||
GetOption.newBuilder().withPrefix(serviceNameBS).build()
|
||||
).get();
|
||||
|
||||
response.getKvs().forEach(kv -> {
|
||||
EtcdEndpoint endpoint = GSON.fromJson(
|
||||
kv.getValue().toString(Charset.defaultCharset()),
|
||||
EtcdEndpoint.class
|
||||
);
|
||||
Address address = new Address(endpoint.getHost(), endpoint.getPort(), false);
|
||||
if (address.equals(selfAddress)) {
|
||||
address.setSelf(true);
|
||||
}
|
||||
remoteInstances.add(new RemoteInstance(address));
|
||||
});
|
||||
|
||||
ClusterHealthStatus healthStatus = OAPNodeChecker.isHealth(remoteInstances);
|
||||
if (healthStatus.isHealth()) {
|
||||
this.healthChecker.health();
|
||||
|
|
@ -97,54 +126,63 @@ public class EtcdCoordinator implements ClusterRegister, ClusterNodesQuery {
|
|||
|
||||
@Override
|
||||
public void registerRemote(RemoteInstance remoteInstance) throws ServiceRegisterException {
|
||||
|
||||
if (needUsingInternalAddr()) {
|
||||
remoteInstance = new RemoteInstance(new Address(config.getInternalComHost(), config.getInternalComPort(), true));
|
||||
remoteInstance = new RemoteInstance(
|
||||
new Address(config.getInternalComHost(), config.getInternalComPort(), true));
|
||||
}
|
||||
|
||||
this.selfAddress = remoteInstance.getAddress();
|
||||
|
||||
EtcdEndpoint endpoint = new EtcdEndpoint.Builder().serviceName(serviceName)
|
||||
.host(selfAddress.getHost())
|
||||
.port(selfAddress.getPort())
|
||||
.build();
|
||||
final EtcdEndpoint endpoint = new EtcdEndpoint.Builder().serviceName(serviceName)
|
||||
.host(selfAddress.getHost())
|
||||
.port(selfAddress.getPort())
|
||||
.build();
|
||||
try {
|
||||
initHealthChecker();
|
||||
client.putDir(serviceName).send();
|
||||
String key = buildKey(serviceName, selfAddress, remoteInstance);
|
||||
String json = new Gson().toJson(endpoint);
|
||||
EtcdResponsePromise<EtcdKeysResponse> promise = client.put(key, json).ttl(KEY_TTL).send();
|
||||
//check register.
|
||||
promise.get();
|
||||
renew(client, key, json);
|
||||
|
||||
final Lease leaseClient = client.getLeaseClient();
|
||||
final long leaseID = leaseClient.grant(30L).get().getID();
|
||||
|
||||
ByteSequence instance = ByteSequence.from(GSON.toJson(endpoint), Charset.defaultCharset());
|
||||
client.getKVClient()
|
||||
.put(
|
||||
buildKey(serviceName, selfAddress, remoteInstance),
|
||||
instance,
|
||||
PutOption.newBuilder().withLeaseId(leaseID).build()
|
||||
)
|
||||
.get();
|
||||
healthChecker.health();
|
||||
|
||||
client.getLeaseClient().keepAlive(leaseID, new StreamObserver<LeaseKeepAliveResponse>() {
|
||||
@Override
|
||||
public void onNext(final LeaseKeepAliveResponse response) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Refresh lease id = {}, ttl = {}", response.getID(), response.getTTL());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Throwable throwable) {
|
||||
log.error("Failed to keep alive in Etcd coordinator", throwable);
|
||||
healthChecker.unHealth(throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
healthChecker.unHealth(e);
|
||||
throw new ServiceRegisterException(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void renew(EtcdClient client, String key, String json) {
|
||||
service.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
client.refresh(key, KEY_TTL).send().get();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
client.put(key, json).ttl(KEY_TTL).send().get();
|
||||
} catch (Exception ee) {
|
||||
LOGGER.error(ee.getMessage(), ee);
|
||||
}
|
||||
}
|
||||
}, 5 * 1000, 30 * 1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private String buildKey(String serviceName, Address address, RemoteInstance instance) {
|
||||
return new StringBuilder(serviceName).append("/")
|
||||
.append(address.getHost())
|
||||
.append("_")
|
||||
.append(instance.hashCode())
|
||||
.toString();
|
||||
private static ByteSequence buildKey(String serviceName, Address address, RemoteInstance instance) {
|
||||
String key = new StringBuilder(serviceName).append(address.getHost())
|
||||
.append("_")
|
||||
.append(instance.hashCode())
|
||||
.toString();
|
||||
return ByteSequence.from(key, Charset.defaultCharset());
|
||||
}
|
||||
|
||||
private boolean needUsingInternalAddr() {
|
||||
|
|
@ -153,8 +191,11 @@ public class EtcdCoordinator implements ClusterRegister, ClusterNodesQuery {
|
|||
|
||||
private void initHealthChecker() {
|
||||
if (healthChecker == null) {
|
||||
MetricsCreator metricCreator = manager.find(TelemetryModule.NAME).provider().getService(MetricsCreator.class);
|
||||
healthChecker = metricCreator.createHealthCheckerGauge("cluster_etcd", MetricsTag.EMPTY_KEY, MetricsTag.EMPTY_VALUE);
|
||||
MetricsCreator metricCreator = manager.find(TelemetryModule.NAME)
|
||||
.provider()
|
||||
.getService(MetricsCreator.class);
|
||||
healthChecker = metricCreator.createHealthCheckerGauge(
|
||||
"cluster_etcd", MetricsTag.EMPTY_KEY, MetricsTag.EMPTY_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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
|
||||
*
|
||||
* http://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 org.apache.skywalking.oap.server.cluster.plugin.etcd;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
|
||||
import org.apache.skywalking.oap.server.library.util.Address;
|
||||
import org.apache.skywalking.oap.server.library.util.ConnectStringParseException;
|
||||
import org.apache.skywalking.oap.server.library.util.ConnectUtils;
|
||||
|
||||
public class EtcdUtils {
|
||||
|
||||
public EtcdUtils() {
|
||||
}
|
||||
|
||||
public static List<URI> parse(ClusterModuleEtcdConfig config) throws ModuleStartException {
|
||||
List<URI> uris = new ArrayList<>();
|
||||
try {
|
||||
List<Address> addressList = ConnectUtils.parse(config.getHostPort());
|
||||
for (Address address : addressList) {
|
||||
uris.add(URI.create(new StringBuilder("http://").append(address.getHost())
|
||||
.append(":")
|
||||
.append(address.getPort())
|
||||
.toString()));
|
||||
}
|
||||
} catch (ConnectStringParseException e) {
|
||||
throw new ModuleStartException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return uris;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,63 +18,68 @@
|
|||
|
||||
package org.apache.skywalking.oap.server.cluster.plugin.etcd;
|
||||
|
||||
import java.net.URI;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.Client;
|
||||
import io.etcd.jetcd.kv.GetResponse;
|
||||
import io.etcd.jetcd.options.GetOption;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import mousio.etcd4j.EtcdClient;
|
||||
import mousio.etcd4j.responses.EtcdKeysResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.skywalking.oap.server.core.cluster.RemoteInstance;
|
||||
import org.apache.skywalking.oap.server.core.remote.client.Address;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleDefineHolder;
|
||||
import org.apache.skywalking.oap.server.telemetry.api.HealthCheckMetrics;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@Slf4j
|
||||
public class ITClusterEtcdPluginTest {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ITClusterEtcdPluginTest.class);
|
||||
|
||||
private ClusterModuleEtcdConfig etcdConfig;
|
||||
|
||||
private EtcdClient client;
|
||||
private Client client;
|
||||
|
||||
private HealthCheckMetrics healthChecker = mock(HealthCheckMetrics.class);
|
||||
|
||||
private EtcdCoordinator coordinator;
|
||||
|
||||
private Address remoteAddress = new Address("10.0.0.1", 1000, false);
|
||||
private Address selfRemoteAddress = new Address("10.0.0.2", 1001, true);
|
||||
|
||||
private Address internalAddress = new Address("10.0.0.3", 1002, false);
|
||||
private final Address remoteAddress = new Address("10.0.0.1", 1000, false);
|
||||
private final Address selfRemoteAddress = new Address("10.0.0.2", 1001, true);
|
||||
private final Address internalAddress = new Address("10.0.0.3", 1002, false);
|
||||
|
||||
private static final String SERVICE_NAME = "my-service";
|
||||
|
||||
@ClassRule
|
||||
public static final GenericContainer CONTAINER =
|
||||
new GenericContainer(DockerImageName.parse("bitnami/etcd:3.5.0"))
|
||||
.waitingFor(Wait.forLogMessage(".*etcd setup finished!.*", 1))
|
||||
.withEnv(Collections.singletonMap("ALLOW_NONE_AUTHENTICATION", "yes"));
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
String etcdHost = System.getProperty("etcd.host");
|
||||
String port = System.getProperty("etcd.port");
|
||||
String baseUrl = "http://" + etcdHost + ":" + port;
|
||||
LOGGER.info("etcd baseURL: {}", baseUrl);
|
||||
String baseUrl = "http://127.0.0.1:" + CONTAINER.getMappedPort(2379);
|
||||
etcdConfig = new ClusterModuleEtcdConfig();
|
||||
etcdConfig.setServiceName(SERVICE_NAME);
|
||||
client = new EtcdClient(URI.create(baseUrl));
|
||||
doNothing().when(healthChecker).health();
|
||||
ModuleDefineHolder manager = mock(ModuleDefineHolder.class);
|
||||
coordinator = new EtcdCoordinator(manager, etcdConfig, client);
|
||||
Whitebox.setInternalState(coordinator, "healthChecker", healthChecker);
|
||||
}
|
||||
etcdConfig.setEndpoints(baseUrl);
|
||||
etcdConfig.setNamespace("skywalking/");
|
||||
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
client.close();
|
||||
etcdConfig.setServiceName(SERVICE_NAME);
|
||||
doNothing().when(healthChecker).health();
|
||||
|
||||
ModuleDefineHolder manager = mock(ModuleDefineHolder.class);
|
||||
coordinator = new EtcdCoordinator(manager, etcdConfig);
|
||||
|
||||
client = Whitebox.getInternalState(coordinator, "client");
|
||||
Whitebox.setInternalState(coordinator, "healthChecker", healthChecker);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -128,12 +133,20 @@ public class ITClusterEtcdPluginTest {
|
|||
}
|
||||
|
||||
private void clear() throws Throwable {
|
||||
EtcdKeysResponse response = client.get(SERVICE_NAME + "/").send().get();
|
||||
List<EtcdKeysResponse.EtcdNode> nodes = response.getNode().getNodes();
|
||||
ByteSequence prefix = ByteSequence.from(SERVICE_NAME + "/", Charset.defaultCharset());
|
||||
GetResponse response = client.getKVClient()
|
||||
.get(
|
||||
ByteSequence.EMPTY,
|
||||
GetOption.newBuilder().withPrefix(prefix).build()
|
||||
).get();
|
||||
|
||||
for (EtcdKeysResponse.EtcdNode node : nodes) {
|
||||
client.delete(node.getKey()).send().get();
|
||||
}
|
||||
response.getKvs().forEach(e -> {
|
||||
try {
|
||||
client.getKVClient().delete(e.getKey()).get();
|
||||
} catch (Exception exp) {
|
||||
log.error("", exp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void verifyRegistration(Address remoteAddress, EtcdEndpoint endpoint) {
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@
|
|||
|
||||
package org.apache.skywalking.oap.server.cluster.plugin.etcd;
|
||||
|
||||
import io.etcd.jetcd.Client;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import mousio.etcd4j.EtcdClient;
|
||||
import org.apache.skywalking.apm.util.StringUtil;
|
||||
import org.apache.skywalking.oap.server.core.cluster.ClusterNodesQuery;
|
||||
import org.apache.skywalking.oap.server.core.cluster.ClusterRegister;
|
||||
|
|
@ -34,9 +34,13 @@ import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator;
|
|||
import org.apache.skywalking.oap.server.telemetry.none.MetricsCreatorNoop;
|
||||
import org.apache.skywalking.oap.server.telemetry.none.NoneTelemetryProvider;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
@ -45,21 +49,20 @@ import static org.mockito.Mockito.mock;
|
|||
|
||||
public class ITClusterModuleEtcdProviderFunctionalTest {
|
||||
|
||||
private String etcdAddress;
|
||||
private ModuleManager moduleManager = mock(ModuleManager.class);
|
||||
private NoneTelemetryProvider telemetryProvider = mock(NoneTelemetryProvider.class);
|
||||
private static String ENDPOINTS;
|
||||
private static NoneTelemetryProvider TELEMETRY_PROVIDER = mock(NoneTelemetryProvider.class);
|
||||
|
||||
@ClassRule
|
||||
public static final GenericContainer CONTAINER =
|
||||
new GenericContainer(DockerImageName.parse("bitnami/etcd:3.5.0"))
|
||||
.waitingFor(Wait.forLogMessage(".*etcd setup finished!.*", 1))
|
||||
.withEnv(Collections.singletonMap("ALLOW_NONE_AUTHENTICATION", "yes"));
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
Mockito.when(telemetryProvider.getService(MetricsCreator.class))
|
||||
.thenReturn(new MetricsCreatorNoop());
|
||||
TelemetryModule telemetryModule = Mockito.spy(TelemetryModule.class);
|
||||
Whitebox.setInternalState(telemetryModule, "loadedProvider", telemetryProvider);
|
||||
Mockito.when(moduleManager.find(TelemetryModule.NAME)).thenReturn(telemetryModule);
|
||||
String etcdHost = System.getProperty("etcd.host");
|
||||
String port = System.getProperty("etcd.port");
|
||||
assertTrue(!StringUtil.isEmpty(etcdHost) && !StringUtil.isEmpty(port));
|
||||
etcdAddress = etcdHost + ":" + port;
|
||||
public void setup() {
|
||||
Mockito.when(TELEMETRY_PROVIDER.getService(MetricsCreator.class))
|
||||
.thenReturn(new MetricsCreatorNoop());
|
||||
ENDPOINTS = "http://127.0.0.1:" + CONTAINER.getMappedPort(2379);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -160,7 +163,7 @@ public class ITClusterModuleEtcdProviderFunctionalTest {
|
|||
validateServiceInstance(addressB, addressA, remoteInstancesOfB);
|
||||
|
||||
// unregister A
|
||||
EtcdClient client = Whitebox.getInternalState(providerA, "client");
|
||||
Client client = Whitebox.getInternalState(getClusterRegister(providerA), "client");
|
||||
client.close();
|
||||
|
||||
// only B
|
||||
|
|
@ -176,12 +179,12 @@ public class ITClusterModuleEtcdProviderFunctionalTest {
|
|||
}
|
||||
|
||||
private ClusterModuleEtcdProvider createProvider(String serviceName, String internalComHost,
|
||||
int internalComPort) throws ModuleStartException {
|
||||
int internalComPort) throws ModuleStartException {
|
||||
ClusterModuleEtcdProvider provider = new ClusterModuleEtcdProvider();
|
||||
|
||||
ClusterModuleEtcdConfig config = (ClusterModuleEtcdConfig) provider.createConfigBeanIfAbsent();
|
||||
|
||||
config.setHostPort(etcdAddress);
|
||||
config.setEndpoints(ENDPOINTS);
|
||||
config.setServiceName(serviceName);
|
||||
|
||||
if (!StringUtil.isEmpty(internalComHost)) {
|
||||
|
|
@ -191,7 +194,12 @@ public class ITClusterModuleEtcdProviderFunctionalTest {
|
|||
if (internalComPort > 0) {
|
||||
config.setInternalComPort(internalComPort);
|
||||
}
|
||||
provider.setManager(moduleManager);
|
||||
TelemetryModule telemetryModule = Mockito.spy(TelemetryModule.class);
|
||||
Whitebox.setInternalState(telemetryModule, "loadedProvider", TELEMETRY_PROVIDER);
|
||||
ModuleManager manager = mock(ModuleManager.class);
|
||||
Mockito.when(manager.find(TelemetryModule.NAME)).thenReturn(telemetryModule);
|
||||
|
||||
provider.setManager(manager);
|
||||
provider.prepare();
|
||||
provider.start();
|
||||
provider.notifyAfterCompleted();
|
||||
|
|
@ -211,7 +219,7 @@ public class ITClusterModuleEtcdProviderFunctionalTest {
|
|||
}
|
||||
|
||||
private List<RemoteInstance> queryRemoteNodes(ModuleProvider provider, int goals,
|
||||
int cyclic) throws InterruptedException {
|
||||
int cyclic) throws InterruptedException {
|
||||
do {
|
||||
List<RemoteInstance> instances = getClusterNodesQuery(provider).queryRemoteNodes();
|
||||
if (instances.size() == goals) {
|
||||
|
|
@ -241,4 +249,5 @@ public class ITClusterModuleEtcdProviderFunctionalTest {
|
|||
assertTrue(selfExist);
|
||||
assertTrue(otherExist);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,39 +34,54 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-dns</artifactId>
|
||||
<groupId>io.etcd</groupId>
|
||||
<artifactId>jetcd-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
<artifactId>netty-codec-http2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<artifactId>netty-handler-proxy</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-resolver-dns</artifactId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mousio</groupId>
|
||||
<artifactId>etcd4j</artifactId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-afterburner</artifactId>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-grpclb</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
@ -87,93 +102,9 @@
|
|||
<id>CI-with-IT</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<sourceMode>all</sourceMode>
|
||||
<logDate>default</logDate>
|
||||
<verbose>true</verbose>
|
||||
<imagePullPolicy>IfNotPresent</imagePullPolicy>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>start</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<images>
|
||||
<image>
|
||||
<name>quayio/coreos-etcd:${etcd.version}</name>
|
||||
<alias>etcd-client-integration-test</alias>
|
||||
<run>
|
||||
<ports>
|
||||
<port>etcd.port:2379</port>
|
||||
</ports>
|
||||
<wait>
|
||||
<time>5000</time>
|
||||
</wait>
|
||||
<entrypoint>
|
||||
<!-- exec form -->
|
||||
<exec>
|
||||
<arg>/usr/local/bin/etcd</arg>
|
||||
<arg>--advertise-client-urls=http://0.0.0.0:2379</arg>
|
||||
<arg>--listen-client-urls=http://0.0.0.0:2379</arg>
|
||||
</exec>
|
||||
</entrypoint>
|
||||
</run>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>remove-it-etcd</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.gmaven</groupId>
|
||||
<artifactId>gmaven-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-default-properties</id>
|
||||
<phase>initialize</phase>
|
||||
<goals>
|
||||
<goal>execute</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<providerSelection>2.0</providerSelection>
|
||||
<source>
|
||||
project.properties.setProperty('etcd.host', 'localhost')
|
||||
|
||||
log.info("Etcd host is " + project.properties['etcd.host'])
|
||||
</source>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<etcd.host>
|
||||
${etcd.host}
|
||||
</etcd.host>
|
||||
<etcd.port>
|
||||
${etcd.port}
|
||||
</etcd.port>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
|
|
|||
|
|
@ -13,144 +13,65 @@
|
|||
* 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 org.apache.skywalking.oap.server.configuration.etcd;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.Client;
|
||||
import io.etcd.jetcd.ClientBuilder;
|
||||
import io.etcd.jetcd.KV;
|
||||
import io.etcd.jetcd.kv.GetResponse;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import mousio.client.promises.ResponsePromise;
|
||||
import mousio.etcd4j.EtcdClient;
|
||||
import mousio.etcd4j.promises.EtcdResponsePromise;
|
||||
import mousio.etcd4j.responses.EtcdErrorCode;
|
||||
import mousio.etcd4j.responses.EtcdException;
|
||||
import mousio.etcd4j.responses.EtcdKeysResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.skywalking.apm.util.StringUtil;
|
||||
import org.apache.skywalking.oap.server.configuration.api.ConfigTable;
|
||||
import org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Slf4j
|
||||
public class EtcdConfigWatcherRegister extends ConfigWatcherRegister {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EtcdConfigWatcherRegister.class);
|
||||
private final KV client;
|
||||
|
||||
/**
|
||||
* server settings for Etcd configuration
|
||||
*/
|
||||
private EtcdServerSettings settings;
|
||||
public EtcdConfigWatcherRegister(EtcdServerSettings setting) {
|
||||
super(setting.getPeriod());
|
||||
ClientBuilder builder = Client.builder()
|
||||
.authority(setting.getAuthority())
|
||||
.endpoints(setting.getEndpointArray());
|
||||
|
||||
/**
|
||||
* etcd client.
|
||||
*/
|
||||
private final EtcdClient client;
|
||||
|
||||
private final Map<String, ResponsePromise.IsSimplePromiseResponseHandler> listenersByKey;
|
||||
|
||||
private final Map<String, Optional<String>> configItemKeyedByName;
|
||||
|
||||
private final Map<String, EtcdResponsePromise<EtcdKeysResponse>> responsePromiseByKey;
|
||||
|
||||
public EtcdConfigWatcherRegister(EtcdServerSettings settings) {
|
||||
super(settings.getPeriod());
|
||||
this.settings = settings;
|
||||
this.configItemKeyedByName = new ConcurrentHashMap<>();
|
||||
this.client = new EtcdClient(EtcdUtils.parse(settings).toArray(new URI[] {}));
|
||||
this.listenersByKey = new ConcurrentHashMap<>();
|
||||
responsePromiseByKey = new ConcurrentHashMap<>();
|
||||
if (StringUtil.isNotEmpty(setting.getNamespace())) {
|
||||
builder.namespace(ByteSequence.from(setting.getNamespace(), Charset.defaultCharset()));
|
||||
}
|
||||
if (setting.isAuthentication()) {
|
||||
builder.user(ByteSequence.from(setting.getUser(), Charset.defaultCharset()))
|
||||
.password(ByteSequence.from(setting.getPassword(), Charset.defaultCharset()));
|
||||
}
|
||||
client = builder.build().getKVClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ConfigTable> readConfig(Set<String> keys) {
|
||||
removeUninterestedKeys(keys);
|
||||
registerKeyListeners(keys);
|
||||
final ConfigTable table = new ConfigTable();
|
||||
public Optional<ConfigTable> readConfig(final Set<String> keys) {
|
||||
ConfigTable table = new ConfigTable();
|
||||
keys.forEach(e -> {
|
||||
try {
|
||||
GetResponse response = client.get(ByteSequence.from(e, Charset.defaultCharset())).get();
|
||||
|
||||
for (Map.Entry<String, Optional<String>> entry : configItemKeyedByName.entrySet()) {
|
||||
final String key = entry.getKey();
|
||||
final Optional<String> value = entry.getValue();
|
||||
|
||||
if (value.isPresent()) {
|
||||
table.add(new ConfigTable.ConfigItem(key, value.get()));
|
||||
} else {
|
||||
table.add(new ConfigTable.ConfigItem(key, null));
|
||||
if (0 == response.getCount()) {
|
||||
table.add(new ConfigTable.ConfigItem(e, null));
|
||||
} else {
|
||||
response.getKvs().forEach(kv -> table.add(new ConfigTable.ConfigItem(
|
||||
kv.getKey().toString(Charset.defaultCharset()),
|
||||
kv.getValue().toString(Charset.defaultCharset())
|
||||
)
|
||||
));
|
||||
}
|
||||
} catch (Exception exp) {
|
||||
throw new EtcdConfigException("Failed to read configuration", exp);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
return Optional.of(table);
|
||||
}
|
||||
|
||||
private void registerKeyListeners(final Set<String> keys) {
|
||||
for (final String key : keys) {
|
||||
String dataId = "/" + settings.getGroup() + "/" + key;
|
||||
if (listenersByKey.containsKey(dataId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
listenersByKey.putIfAbsent(dataId, p -> {
|
||||
onDataValueChanged(p, dataId);
|
||||
});
|
||||
|
||||
try {
|
||||
EtcdResponsePromise<EtcdKeysResponse> responsePromise = client.get(dataId).waitForChange().send();
|
||||
responsePromise.addListener(listenersByKey.get(dataId));
|
||||
responsePromiseByKey.putIfAbsent(dataId, responsePromise);
|
||||
|
||||
// the key is newly added, read the config for the first time
|
||||
EtcdResponsePromise<EtcdKeysResponse> promise = client.get(dataId).send();
|
||||
onDataValueChanged(promise, dataId);
|
||||
} catch (Exception e) {
|
||||
throw new EtcdConfigException("wait for etcd value change fail", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeUninterestedKeys(final Set<String> interestedKeys) {
|
||||
final Set<String> uninterestedKeys = new HashSet<>(listenersByKey.keySet());
|
||||
uninterestedKeys.removeAll(interestedKeys);
|
||||
|
||||
uninterestedKeys.forEach(k -> {
|
||||
final ResponsePromise.IsSimplePromiseResponseHandler listener = listenersByKey.remove(k);
|
||||
if (listener != null) {
|
||||
responsePromiseByKey.remove(k).removeListener(listener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onDataValueChanged(ResponsePromise<EtcdKeysResponse> promise, String dataId) {
|
||||
String key = getRealKey(dataId, settings.getGroup());
|
||||
try {
|
||||
EtcdKeysResponse.EtcdNode node = promise.get().getNode();
|
||||
String value = node.getValue();
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Etcd config changed: {}: {}", key, node.getValue());
|
||||
}
|
||||
|
||||
configItemKeyedByName.put(key, Optional.ofNullable(value));
|
||||
} catch (Exception e) {
|
||||
if (e instanceof EtcdException) {
|
||||
if (EtcdErrorCode.KeyNotFound == ((EtcdException) e).errorCode) {
|
||||
configItemKeyedByName.put(key, Optional.empty());
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new EtcdConfigException("wait for value changed fail", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get real key in etcd cluster which is removed "/${group}" from the key retrive from etcd.
|
||||
*/
|
||||
private String getRealKey(String key, String group) {
|
||||
int index = key.indexOf(group);
|
||||
if (index <= 0) {
|
||||
throw new RuntimeException("the group doesn't match");
|
||||
}
|
||||
String realKey = key.substring(index + group.length() + 1);
|
||||
return realKey;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,40 +13,29 @@
|
|||
* 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 org.apache.skywalking.oap.server.configuration.etcd;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.skywalking.apm.util.StringUtil;
|
||||
import org.apache.skywalking.oap.server.configuration.api.AbstractConfigurationProvider;
|
||||
import org.apache.skywalking.oap.server.configuration.api.ConfigWatcherRegister;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Get Configuration from etcd.
|
||||
*/
|
||||
@Slf4j
|
||||
public class EtcdConfigurationProvider extends AbstractConfigurationProvider {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EtcdConfigurationProvider.class);
|
||||
|
||||
private EtcdServerSettings settings;
|
||||
private final EtcdServerSettings settings;
|
||||
|
||||
public EtcdConfigurationProvider() {
|
||||
settings = new EtcdServerSettings();
|
||||
this.settings = new EtcdServerSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigWatcherRegister initConfigReader() throws ModuleStartException {
|
||||
LOGGER.info("settings: {}", settings);
|
||||
if (Strings.isNullOrEmpty(settings.getServerAddr())) {
|
||||
throw new ModuleStartException("Etcd serverAddr cannot be null or empty.");
|
||||
}
|
||||
if (Strings.isNullOrEmpty(settings.getGroup())) {
|
||||
throw new ModuleStartException("Etcd group cannot be null or empty.");
|
||||
if (StringUtil.isEmpty(settings.getEndpoints())) {
|
||||
throw new ModuleStartException("Etcd endpoints cannot be null or empty.");
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -13,38 +13,41 @@
|
|||
* 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 org.apache.skywalking.oap.server.configuration.etcd;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import com.google.common.base.Strings;
|
||||
import java.util.Arrays;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
|
||||
|
||||
/**
|
||||
* entity wrapps the etcd cluster configuration.
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EtcdServerSettings extends ModuleConfig {
|
||||
private int period;
|
||||
private String endpoints;
|
||||
private String namespace;
|
||||
private String authority;
|
||||
private String user;
|
||||
private String password;
|
||||
|
||||
private String clusterName = "default";
|
||||
/**
|
||||
* etcd cluster address, like "10.10.10.1:2379, 10.10.10.2:2379,10.10.10.3.2379".
|
||||
*/
|
||||
private String serverAddr;
|
||||
private boolean authentication;
|
||||
|
||||
/**
|
||||
* directory for configuration
|
||||
*/
|
||||
private String group;
|
||||
|
||||
/**
|
||||
* sec for interval refresh config data.
|
||||
*/
|
||||
private int period = 60;
|
||||
public String getNamespace() {
|
||||
if (Strings.isNullOrEmpty(namespace)) {
|
||||
return null;
|
||||
}
|
||||
if (!namespace.endsWith("/")) {
|
||||
return namespace + "/";
|
||||
}
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public String[] getEndpointArray() {
|
||||
return Arrays.stream(endpoints.split("\\s*,\\s*")).toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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
|
||||
*
|
||||
* http://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 org.apache.skywalking.oap.server.configuration.etcd;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import org.apache.skywalking.oap.server.library.util.Address;
|
||||
import org.apache.skywalking.oap.server.library.util.ConnectUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* a util for etcd serverAddr parse.
|
||||
*/
|
||||
public class EtcdUtils {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EtcdUtils.class);
|
||||
|
||||
public EtcdUtils() {
|
||||
}
|
||||
|
||||
public static List<URI> parse(EtcdServerSettings settings) {
|
||||
List<URI> uris = new ArrayList<>();
|
||||
try {
|
||||
LOGGER.info("etcd settings is {}", settings);
|
||||
List<Address> addressList = ConnectUtils.parse(settings.getServerAddr());
|
||||
for (Address address : addressList) {
|
||||
uris.add(new URI("http", null, address.getHost(), address.getPort(), null, null, null));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new EtcdConfigException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return uris;
|
||||
}
|
||||
|
||||
public static List<URI> parseProp(Properties properties) {
|
||||
List<URI> uris = new ArrayList<>();
|
||||
try {
|
||||
LOGGER.info("etcd server addr is {}", properties);
|
||||
List<Address> addressList = ConnectUtils.parse(properties.getProperty("serverAddr"));
|
||||
for (Address address : addressList) {
|
||||
uris.add(new URI("http", null, address.getHost(), address.getPort(), null, null, null));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new EtcdConfigException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return uris;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ public class EtcdConfigurationTestProvider extends ModuleProvider {
|
|||
private volatile String testValue;
|
||||
|
||||
@Override
|
||||
public void notify(ConfigChangeWatcher.ConfigChangeEvent value) {
|
||||
public void notify(ConfigChangeEvent value) {
|
||||
LOGGER.info("ConfigChangeWatcher.ConfigChangeEvent: {}", value);
|
||||
if (EventType.DELETE.equals(value.getEventType())) {
|
||||
testValue = null;
|
||||
|
|
|
|||
|
|
@ -18,99 +18,114 @@
|
|||
|
||||
package org.apache.skywalking.oap.server.configuration.etcd;
|
||||
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.Client;
|
||||
import io.etcd.jetcd.KV;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.Reader;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import mousio.etcd4j.EtcdClient;
|
||||
import mousio.etcd4j.promises.EtcdResponsePromise;
|
||||
import mousio.etcd4j.responses.EtcdKeysResponse;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.skywalking.apm.util.PropertyPlaceholderHelper;
|
||||
import org.apache.skywalking.oap.server.library.module.ApplicationConfiguration;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleConfigException;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleManager;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleNotFoundException;
|
||||
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
|
||||
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
|
||||
import org.apache.skywalking.oap.server.library.util.ResourceUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@Slf4j
|
||||
public class ITEtcdConfigurationTest {
|
||||
@ClassRule
|
||||
public static final GenericContainer CONTAINER =
|
||||
new GenericContainer(DockerImageName.parse("bitnami/etcd:3.5.0"))
|
||||
.waitingFor(Wait.forLogMessage(".*etcd setup finished!.*", 1))
|
||||
.withEnv(Collections.singletonMap("ALLOW_NONE_AUTHENTICATION", "yes"));
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ITEtcdConfigurationTest.class);
|
||||
private static EtcdConfigurationTestProvider PROVIDER;
|
||||
|
||||
private final Yaml yaml = new Yaml();
|
||||
private static final String TEST_VALUE = "value";
|
||||
|
||||
private EtcdServerSettings settings;
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws FileNotFoundException, ModuleConfigException, ModuleNotFoundException, ModuleStartException {
|
||||
System.setProperty("etcd.endpoint", "http://127.0.0.1:" + CONTAINER.getMappedPort(2379));
|
||||
|
||||
private EtcdConfigurationTestProvider provider;
|
||||
|
||||
private EtcdClient client;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final ApplicationConfiguration applicationConfiguration = new ApplicationConfiguration();
|
||||
loadConfig(applicationConfiguration);
|
||||
|
||||
final ModuleManager moduleManager = new ModuleManager();
|
||||
moduleManager.init(applicationConfiguration);
|
||||
|
||||
final String etcdHost = System.getProperty("etcd.host");
|
||||
final String etcdPort = System.getProperty("etcd.port");
|
||||
LOGGER.info("etcdHost: {}, etcdPort: {}", etcdHost, etcdPort);
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("serverAddr", etcdHost + ":" + etcdPort);
|
||||
PROVIDER = (EtcdConfigurationTestProvider) moduleManager.find(EtcdConfigurationTestModule.NAME).provider();
|
||||
|
||||
List<URI> uris = EtcdUtils.parseProp(properties);
|
||||
client = new EtcdClient(uris.toArray(new URI[] {}));
|
||||
|
||||
provider = (EtcdConfigurationTestProvider) moduleManager.find(EtcdConfigurationTestModule.NAME).provider();
|
||||
|
||||
assertNotNull(provider);
|
||||
assertNotNull(PROVIDER);
|
||||
}
|
||||
|
||||
@Test(timeout = 20000)
|
||||
public void shouldReadUpdated() throws Exception {
|
||||
assertNull(provider.watcher.value());
|
||||
assertNull(PROVIDER.watcher.value());
|
||||
|
||||
assertTrue(publishConfig("test-module.default.testKey", "skywalking", "500"));
|
||||
KV client = Client.builder()
|
||||
.endpoints("http://localhost:" + CONTAINER.getMappedPort(2379))
|
||||
.namespace(ByteSequence.from("/skywalking/", Charset.defaultCharset()))
|
||||
.build()
|
||||
.getKVClient();
|
||||
|
||||
for (String v = provider.watcher.value(); v == null; v = provider.watcher.value()) {
|
||||
LOGGER.info("value is : {}", provider.watcher.value());
|
||||
client.put(
|
||||
ByteSequence.from("test-module.default.testKey", Charset.defaultCharset()),
|
||||
ByteSequence.from(TEST_VALUE, Charset.defaultCharset())
|
||||
).get();
|
||||
|
||||
for (String v = PROVIDER.watcher.value(); v == null; v = PROVIDER.watcher.value()) {
|
||||
log.info("value is : {}", PROVIDER.watcher.value());
|
||||
TimeUnit.MILLISECONDS.sleep(200L);
|
||||
}
|
||||
|
||||
assertEquals("500", provider.watcher.value());
|
||||
assertEquals(TEST_VALUE, PROVIDER.watcher.value());
|
||||
|
||||
assertTrue(removeConfig("test-module.default.testKey", "skywalking"));
|
||||
client.delete(ByteSequence.from("test-module.default.testKey", Charset.defaultCharset())).get();
|
||||
|
||||
for (String v = provider.watcher.value(); v != null; v = provider.watcher.value()) {
|
||||
for (String v = PROVIDER.watcher.value(); v != null; v = PROVIDER.watcher.value()) {
|
||||
TimeUnit.MILLISECONDS.sleep(200L);
|
||||
}
|
||||
|
||||
assertNull(provider.watcher.value());
|
||||
assertNull(PROVIDER.watcher.value());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void loadConfig(ApplicationConfiguration configuration) throws FileNotFoundException {
|
||||
private static void loadConfig(ApplicationConfiguration configuration) throws FileNotFoundException {
|
||||
final Yaml yaml = new Yaml();
|
||||
|
||||
Reader applicationReader = ResourceUtils.read("application.yml");
|
||||
Map<String, Map<String, Map<String, ?>>> moduleConfig = yaml.loadAs(applicationReader, Map.class);
|
||||
if (CollectionUtils.isNotEmpty(moduleConfig)) {
|
||||
moduleConfig.forEach((moduleName, providerConfig) -> {
|
||||
if (providerConfig.size() > 0) {
|
||||
ApplicationConfiguration.ModuleConfiguration moduleConfiguration = configuration.addModule(moduleName);
|
||||
ApplicationConfiguration.ModuleConfiguration moduleConfiguration = configuration.addModule(
|
||||
moduleName);
|
||||
providerConfig.forEach((name, propertiesConfig) -> {
|
||||
Properties properties = new Properties();
|
||||
if (propertiesConfig != null) {
|
||||
propertiesConfig.forEach((key, value) -> {
|
||||
properties.put(key, value);
|
||||
final Object replaceValue = yaml.load(PropertyPlaceholderHelper.INSTANCE.replacePlaceholders(value + "", properties));
|
||||
final Object replaceValue = yaml.load(
|
||||
PropertyPlaceholderHelper.INSTANCE.replacePlaceholders(value + "", properties));
|
||||
if (replaceValue != null) {
|
||||
properties.replace(key, replaceValue);
|
||||
}
|
||||
|
|
@ -123,24 +138,8 @@ public class ITEtcdConfigurationTest {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean publishConfig(String key, String group, String value) {
|
||||
try {
|
||||
client.putDir(group).send().get();
|
||||
EtcdResponsePromise<EtcdKeysResponse> promise = client.put(generateKey(key, group), value).send();
|
||||
promise.get();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
@AfterClass
|
||||
public static void teardown() {
|
||||
CONTAINER.close();
|
||||
}
|
||||
|
||||
private boolean removeConfig(String key, String group) throws Exception {
|
||||
client.delete(generateKey(key, group)).send().get();
|
||||
return true;
|
||||
}
|
||||
|
||||
private String generateKey(String key, String group) {
|
||||
return new StringBuilder("/").append(group).append("/").append(key).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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
|
||||
*
|
||||
* http://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 org.apache.skywalking.oap.server.configuration.etcd;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestEtcdUtils {
|
||||
|
||||
private EtcdServerSettings settings;
|
||||
|
||||
private Properties properties;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
settings = new EtcdServerSettings();
|
||||
settings.setServerAddr("localhost:2379");
|
||||
properties = new Properties();
|
||||
properties.setProperty("serverAddr", "localhost:2379");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse() {
|
||||
List<URI> list = EtcdUtils.parse(settings);
|
||||
Assert.assertEquals(1, list.size());
|
||||
URI uri = list.get(0);
|
||||
Assert.assertEquals("http", uri.getScheme());
|
||||
Assert.assertEquals("localhost", uri.getHost());
|
||||
Assert.assertEquals(2379, uri.getPort());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProp() {
|
||||
List<URI> list = EtcdUtils.parseProp(properties);
|
||||
Assert.assertEquals(1, list.size());
|
||||
URI uri = list.get(0);
|
||||
Assert.assertEquals("http", uri.getScheme());
|
||||
Assert.assertEquals("localhost", uri.getHost());
|
||||
Assert.assertEquals(2379, uri.getPort());
|
||||
}
|
||||
}
|
||||
|
|
@ -22,12 +22,8 @@ test-module:
|
|||
configuration:
|
||||
etcd:
|
||||
# Etcd Server Host
|
||||
serverAddr: ${etcd.host}:${etcd.port}
|
||||
# Etcd Server Port
|
||||
port: ${etcd.port}
|
||||
endpoints: ${etcd.endpoint}
|
||||
# Etcd Configuration Group
|
||||
group: 'skywalking'
|
||||
namespace: /skywalking/
|
||||
# Unit seconds, sync period. Default fetch every 60 seconds.
|
||||
period: 1
|
||||
# the name of current cluster, set the name if you want to upstream system known.
|
||||
clusterName: "default"
|
||||
|
|
|
|||
|
|
@ -47,6 +47,18 @@
|
|||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler-proxy</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
|
|
|
|||
|
|
@ -59,5 +59,21 @@
|
|||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ elasticsearch-rest-high-level-client-7.10.2.jar
|
|||
elasticsearch-secure-sm-7.10.2.jar
|
||||
elasticsearch-x-content-7.10.2.jar
|
||||
error_prone_annotations-2.3.2.jar
|
||||
etcd4j-2.18.0.jar
|
||||
failsafe-2.3.4.jar
|
||||
failureaccess-1.0.1.jar
|
||||
flatbuffers-java-1.12.0.jar
|
||||
freemarker-2.3.28.jar
|
||||
|
|
@ -51,6 +51,7 @@ groovy-3.0.3.jar
|
|||
grpc-api-1.32.1.jar
|
||||
grpc-context-1.32.1.jar
|
||||
grpc-core-1.32.1.jar
|
||||
grpc-grpclb-1.32.1.jar
|
||||
grpc-netty-1.32.1.jar
|
||||
grpc-protobuf-1.32.1.jar
|
||||
grpc-protobuf-lite-1.32.1.jar
|
||||
|
|
@ -81,6 +82,9 @@ javassist-3.25.0-GA.jar
|
|||
javax.inject-1.jar
|
||||
javax.servlet-api-3.1.0.jar
|
||||
jcl-over-slf4j-1.7.30.jar
|
||||
jetcd-common-0.5.3.jar
|
||||
jetcd-core-0.5.3.jar
|
||||
jetcd-resolver-0.5.3.jar
|
||||
jetty-http-9.4.40.v20210413.jar
|
||||
jetty-io-9.4.40.v20210413.jar
|
||||
jetty-security-9.4.40.v20210413.jar
|
||||
|
|
@ -92,7 +96,6 @@ jna-5.5.0.jar
|
|||
joda-time-2.10.5.jar
|
||||
jopt-simple-4.6.jar
|
||||
jose4j-0.7.6.jar
|
||||
json-flattener-0.6.0.jar
|
||||
jsr305-3.0.2.jar
|
||||
kafka-clients-2.4.1.jar
|
||||
kotlin-reflect-1.1.1.jar
|
||||
|
|
@ -120,7 +123,6 @@ lucene-spatial3d-8.7.0.jar
|
|||
lucene-suggest-8.7.0.jar
|
||||
lz4-java-1.6.0.jar
|
||||
mapper-extras-client-7.10.2.jar
|
||||
minimal-json-0.9.5.jar
|
||||
moshi-1.5.0.jar
|
||||
msgpack-core-0.8.16.jar
|
||||
mvel2-2.4.8.Final.jar
|
||||
|
|
@ -129,19 +131,17 @@ nacos-client-1.4.2.jar
|
|||
nacos-common-1.4.2.jar
|
||||
netty-buffer-4.1.65.Final.jar
|
||||
netty-codec-4.1.65.Final.jar
|
||||
netty-codec-dns-4.1.65.Final.jar
|
||||
netty-codec-http-4.1.65.Final.jar
|
||||
netty-codec-http2-4.1.51.Final.jar
|
||||
netty-codec-socks-4.1.51.Final.jar
|
||||
netty-codec-http2-4.1.65.Final.jar
|
||||
netty-codec-socks-4.1.65.Final.jar
|
||||
netty-common-4.1.65.Final.jar
|
||||
netty-handler-4.1.65.Final.jar
|
||||
netty-handler-proxy-4.1.51.Final.jar
|
||||
netty-handler-proxy-4.1.65.Final.jar
|
||||
netty-resolver-4.1.65.Final.jar
|
||||
netty-resolver-dns-4.1.65.Final.jar
|
||||
netty-tcnative-boringssl-static-2.0.39.Final.jar
|
||||
netty-transport-4.1.65.Final.jar
|
||||
netty-transport-native-epoll-4.1.45.Final.jar
|
||||
netty-transport-native-unix-common-4.1.45.Final.jar
|
||||
netty-transport-native-epoll-4.1.65.Final.jar
|
||||
netty-transport-native-unix-common-4.1.65.Final.jar
|
||||
okhttp-3.14.9.jar
|
||||
okio-1.17.2.jar
|
||||
parent-join-client-7.10.2.jar
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ elasticsearch-rest-high-level-client-6.3.2.jar
|
|||
elasticsearch-secure-sm-6.3.2.jar
|
||||
elasticsearch-x-content-6.3.2.jar
|
||||
error_prone_annotations-2.3.2.jar
|
||||
etcd4j-2.18.0.jar
|
||||
failsafe-2.3.4.jar
|
||||
failureaccess-1.0.1.jar
|
||||
flatbuffers-java-1.12.0.jar
|
||||
freemarker-2.3.28.jar
|
||||
|
|
@ -49,6 +49,7 @@ groovy-3.0.3.jar
|
|||
grpc-api-1.32.1.jar
|
||||
grpc-context-1.32.1.jar
|
||||
grpc-core-1.32.1.jar
|
||||
grpc-grpclb-1.32.1.jar
|
||||
grpc-netty-1.32.1.jar
|
||||
grpc-protobuf-1.32.1.jar
|
||||
grpc-protobuf-lite-1.32.1.jar
|
||||
|
|
@ -79,6 +80,9 @@ javassist-3.25.0-GA.jar
|
|||
javax.inject-1.jar
|
||||
javax.servlet-api-3.1.0.jar
|
||||
jcl-over-slf4j-1.7.30.jar
|
||||
jetcd-common-0.5.3.jar
|
||||
jetcd-core-0.5.3.jar
|
||||
jetcd-resolver-0.5.3.jar
|
||||
jetty-http-9.4.40.v20210413.jar
|
||||
jetty-io-9.4.40.v20210413.jar
|
||||
jetty-security-9.4.40.v20210413.jar
|
||||
|
|
@ -90,7 +94,6 @@ jna-4.5.1.jar
|
|||
joda-time-2.10.5.jar
|
||||
jopt-simple-4.6.jar
|
||||
jose4j-0.7.6.jar
|
||||
json-flattener-0.6.0.jar
|
||||
jsr305-3.0.2.jar
|
||||
kafka-clients-2.4.1.jar
|
||||
kotlin-reflect-1.1.1.jar
|
||||
|
|
@ -117,7 +120,6 @@ lucene-spatial-extras-7.3.1.jar
|
|||
lucene-spatial3d-7.3.1.jar
|
||||
lucene-suggest-7.3.1.jar
|
||||
lz4-java-1.6.0.jar
|
||||
minimal-json-0.9.5.jar
|
||||
moshi-1.5.0.jar
|
||||
msgpack-core-0.8.16.jar
|
||||
mvel2-2.4.8.Final.jar
|
||||
|
|
@ -126,19 +128,17 @@ nacos-client-1.4.2.jar
|
|||
nacos-common-1.4.2.jar
|
||||
netty-buffer-4.1.65.Final.jar
|
||||
netty-codec-4.1.65.Final.jar
|
||||
netty-codec-dns-4.1.65.Final.jar
|
||||
netty-codec-http-4.1.65.Final.jar
|
||||
netty-codec-http2-4.1.51.Final.jar
|
||||
netty-codec-socks-4.1.51.Final.jar
|
||||
netty-codec-http2-4.1.65.Final.jar
|
||||
netty-codec-socks-4.1.65.Final.jar
|
||||
netty-common-4.1.65.Final.jar
|
||||
netty-handler-4.1.65.Final.jar
|
||||
netty-handler-proxy-4.1.51.Final.jar
|
||||
netty-handler-proxy-4.1.65.Final.jar
|
||||
netty-resolver-4.1.65.Final.jar
|
||||
netty-resolver-dns-4.1.65.Final.jar
|
||||
netty-tcnative-boringssl-static-2.0.39.Final.jar
|
||||
netty-transport-4.1.65.Final.jar
|
||||
netty-transport-native-epoll-4.1.45.Final.jar
|
||||
netty-transport-native-unix-common-4.1.45.Final.jar
|
||||
netty-transport-native-epoll-4.1.65.Final.jar
|
||||
netty-transport-native-unix-common-4.1.65.Final.jar
|
||||
okhttp-3.14.9.jar
|
||||
okio-1.17.2.jar
|
||||
parent-join-client-6.3.2.jar
|
||||
|
|
|
|||
Loading…
Reference in New Issue