This is an automated email from the ASF dual-hosted git repository.
liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git
The following commit(s) were added to refs/heads/master by this push:
new 19e26102f [SCB-2892]support Etcd as Discovery (#4564)
19e26102f is described below
commit 19e26102f535ff3b0f54047193159c63649fffb2
Author: SweetWuXiaoMei <[email protected]>
AuthorDate: Tue Oct 29 11:23:14 2024 +0800
[SCB-2892]support Etcd as Discovery (#4564)
Co-authored-by: chenzhida <[email protected]>
---
demo/demo-etcd/README.md | 5 +
demo/demo-etcd/consumer/pom.xml | 88 +++++++++
.../samples/ClientWebsocketController.java | 52 +++++
.../servicecomb/samples/ConsumerController.java | 42 ++++
.../samples/ConsumerHeaderParamWithListSchema.java | 55 ++++++
.../samples/ConsumerReactiveStreamController.java | 84 ++++++++
.../samples/EtcdConsumerApplication.java | 33 ++++
.../servicecomb/samples/ProviderService.java | 24 +++
.../consumer/src/main/resources/application.yml | 32 ++++
.../consumer/src/main/resources/log4j2.xml | 41 ++++
demo/demo-etcd/gateway/pom.xml | 91 +++++++++
.../servicecomb/samples/GatewayApplication.java | 33 ++++
.../gateway/src/main/resources/application.yml | 53 +++++
.../gateway/src/main/resources/log4j2.xml | 41 ++++
{service-registry => demo/demo-etcd}/pom.xml | 46 +++--
demo/demo-etcd/provider/pom.xml | 97 ++++++++++
.../samples/EtcdProviderApplication.java | 33 ++++
.../samples/HeaderParamWithListSchema.java | 51 +++++
.../servicecomb/samples/ProviderController.java | 53 +++++
.../samples/ReactiveStreamController.java | 78 ++++++++
.../servicecomb/samples/WebsocketController.java | 67 +++++++
.../provider/src/main/resources/application.yml | 44 +++++
.../provider/src/main/resources/log4j2.xml | 41 ++++
demo/demo-etcd/test-client/pom.xml | 213 +++++++++++++++++++++
.../org/apache/servicecomb/samples/Config.java | 22 +++
.../samples/HeaderParamWithListSchemaIT.java | 98 ++++++++++
.../apache/servicecomb/samples/HelloWorldIT.java | 68 +++++++
.../servicecomb/samples/ReactiveStreamIT.java | 119 ++++++++++++
.../servicecomb/samples/TestClientApplication.java | 49 +++++
.../servicecomb/samples/ThirdSvcConfiguration.java | 125 ++++++++++++
.../apache/servicecomb/samples/WebsocketIT.java | 57 ++++++
.../test-client/src/main/resources/application.yml | 25 +++
.../test-client/src/main/resources/log4j2.xml | 41 ++++
.../org/apache/servicecomb/samples/EtcdIT.java | 53 +++++
demo/pom.xml | 1 +
dependencies/bom/pom.xml | 5 +
dependencies/default/pom.xml | 7 +
service-registry/pom.xml | 1 +
service-registry/{ => registry-etcd}/pom.xml | 39 ++--
.../registry/etcd/EtcdConfiguration.java | 40 ++++
.../servicecomb/registry/etcd/EtcdConst.java | 30 +++
.../servicecomb/registry/etcd/EtcdDiscovery.java | 197 +++++++++++++++++++
.../registry/etcd/EtcdDiscoveryInstance.java | 37 ++++
.../servicecomb/registry/etcd/EtcdInstance.java | 208 ++++++++++++++++++++
.../registry/etcd/EtcdRegistration.java | 199 +++++++++++++++++++
.../registry/etcd/EtcdRegistrationInstance.java | 36 ++++
.../registry/etcd/EtcdRegistryProperties.java | 99 ++++++++++
.../registry/etcd/MuteExceptionUtil.java | 94 +++++++++
...rk.boot.autoconfigure.AutoConfiguration.imports | 18 ++
49 files changed, 3038 insertions(+), 27 deletions(-)
diff --git a/demo/demo-etcd/README.md b/demo/demo-etcd/README.md
new file mode 100644
index 000000000..046bec6c9
--- /dev/null
+++ b/demo/demo-etcd/README.md
@@ -0,0 +1,5 @@
+# Notice
+
+This integration tests is designed for Etcd registry and configuration. And
extra test cases include:
+
+* Test cases related to SpringMVC annotations that demo-springmvc can not
cover.
diff --git a/demo/demo-etcd/consumer/pom.xml b/demo/demo-etcd/consumer/pom.xml
new file mode 100644
index 000000000..cbccb498a
--- /dev/null
+++ b/demo/demo-etcd/consumer/pom.xml
@@ -0,0 +1,88 @@
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>demo-etcd</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>etcd-consumer</artifactId>
+ <name>Java Chassis::Demo::Etcd::CONSUMER</name>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>java-chassis-spring-boot-starter-standalone</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ <version>3.25.3</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>registry-etcd</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>docker</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.commonjava.maven.plugins</groupId>
+ <artifactId>directory-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>com.github.odavid.maven.plugins</groupId>
+ <artifactId>mixin-maven-plugin</artifactId>
+ <configuration>
+ <mixins>
+ <mixin>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>docker-build-config</artifactId>
+ <version>${project.version}</version>
+ </mixin>
+ </mixins>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git
a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java
new file mode 100644
index 000000000..f71738a9e
--- /dev/null
+++
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java
@@ -0,0 +1,52 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.apache.servicecomb.core.CoreConst;
+import org.apache.servicecomb.core.annotation.Transport;
+import org.apache.servicecomb.provider.pojo.RpcReference;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import io.vertx.core.http.ServerWebSocket;
+import io.vertx.core.http.WebSocket;
+
+@RestSchema(schemaId = "ClientWebsocketController")
+@RequestMapping(path = "/ws")
+public class ClientWebsocketController {
+ interface ProviderService {
+ WebSocket websocket();
+ }
+
+ @RpcReference(schemaId = "WebsocketController", microserviceName =
"provider")
+ private ProviderService providerService;
+
+ @PostMapping("/websocket")
+ @Transport(name = CoreConst.WEBSOCKET)
+ public void websocket(ServerWebSocket serverWebsocket) {
+ WebSocket providerWebSocket = providerService.websocket();
+ providerWebSocket.closeHandler(v -> serverWebsocket.close());
+ providerWebSocket.textMessageHandler(m -> {
+ serverWebsocket.writeTextMessage(m);
+ });
+ serverWebsocket.textMessageHandler(m -> {
+ providerWebSocket.writeTextMessage(m);
+ });
+ }
+}
diff --git
a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java
new file mode 100644
index 000000000..e5dd86d9d
--- /dev/null
+++
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java
@@ -0,0 +1,42 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.apache.servicecomb.provider.pojo.RpcReference;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@RestSchema(schemaId = "ConsumerController")
+@RequestMapping(path = "/")
+public class ConsumerController {
+ @RpcReference(schemaId = "ProviderController", microserviceName = "provider")
+ private ProviderService providerService;
+
+ // consumer service which delegate the implementation to provider service.
+ @GetMapping("/sayHello")
+ public String sayHello(@RequestParam("name") String name) {
+ return providerService.sayHello(name);
+ }
+
+ @GetMapping("/getConfig")
+ public String getConfig(@RequestParam("key") String key) {
+ return providerService.getConfig(key);
+ }
+}
diff --git
a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java
new file mode 100644
index 000000000..f0e894d67
--- /dev/null
+++
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java
@@ -0,0 +1,55 @@
+/*
+ * 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.servicecomb.samples;
+
+import java.util.List;
+
+import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc;
+import org.apache.servicecomb.provider.pojo.RpcReference;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+
+@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface =
IHeaderParamWithListSchemaSpringMvc.class)
+public class ConsumerHeaderParamWithListSchema implements
IHeaderParamWithListSchemaSpringMvc {
+ @RpcReference(microserviceName = "provider", schemaId =
"HeaderParamWithListSchema")
+ private IHeaderParamWithListSchemaSpringMvc provider;
+
+ @Override
+ public String headerListDefault(List<String> headerList) {
+ return provider.headerListDefault(headerList);
+ }
+
+ @Override
+ public String headerListCSV(List<String> headerList) {
+ return provider.headerListCSV(headerList);
+ }
+
+ @Override
+ public String headerListMULTI(List<String> headerList) {
+ return provider.headerListMULTI(headerList);
+ }
+
+ @Override
+ public String headerListSSV(List<String> headerList) {
+ return provider.headerListSSV(headerList);
+ }
+
+ @Override
+ public String headerListPIPES(List<String> headerList) {
+ return provider.headerListPIPES(headerList);
+ }
+}
diff --git
a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java
new file mode 100644
index 000000000..aa169801c
--- /dev/null
+++
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java
@@ -0,0 +1,84 @@
+/*
+ * 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.servicecomb.samples;
+
+
+import org.apache.servicecomb.core.CoreConst;
+import org.apache.servicecomb.core.annotation.Transport;
+import org.apache.servicecomb.provider.pojo.RpcReference;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.reactivestreams.Publisher;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@RestSchema(schemaId = "ReactiveStreamController")
+@RequestMapping(path = "/")
+public class ConsumerReactiveStreamController {
+ interface ProviderReactiveStreamController {
+ Publisher<String> sseString();
+
+ Publisher<Model> sseModel();
+ }
+
+ @RpcReference(microserviceName = "provider", schemaId =
"ReactiveStreamController")
+ ProviderReactiveStreamController controller;
+
+ public static class Model {
+ private String name;
+
+ private int age;
+
+ public Model() {
+
+ }
+
+ public Model(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public Model setAge(int age) {
+ this.age = age;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Model setName(String name) {
+ this.name = name;
+ return this;
+ }
+ }
+
+ @GetMapping("/sseString")
+ @Transport(name = CoreConst.RESTFUL)
+ public Publisher<String> sseString() {
+ return controller.sseString();
+ }
+
+ @GetMapping("/sseModel")
+ @Transport(name = CoreConst.RESTFUL)
+ public Publisher<Model> sseModel() {
+ return controller.sseModel();
+ }
+}
diff --git
a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java
new file mode 100644
index 000000000..2101e6512
--- /dev/null
+++
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java
@@ -0,0 +1,33 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.springframework.boot.WebApplicationType;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+
+@SpringBootApplication
+public class EtcdConsumerApplication {
+ public static void main(String[] args) throws Exception {
+ try {
+ new
SpringApplicationBuilder().web(WebApplicationType.NONE).sources(EtcdConsumerApplication.class).run(args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git
a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java
new file mode 100644
index 000000000..fe71314c9
--- /dev/null
+++
b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java
@@ -0,0 +1,24 @@
+/*
+ * 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.servicecomb.samples;
+
+public interface ProviderService {
+ String sayHello(String name);
+
+ String getConfig(String key);
+}
diff --git a/demo/demo-etcd/consumer/src/main/resources/application.yml
b/demo/demo-etcd/consumer/src/main/resources/application.yml
new file mode 100644
index 000000000..a63a16518
--- /dev/null
+++ b/demo/demo-etcd/consumer/src/main/resources/application.yml
@@ -0,0 +1,32 @@
+#
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+servicecomb:
+ service:
+ application: demo-etcd
+ version: 0.0.1
+ name: consumer
+ properties:
+ group: red
+ registry:
+ etcd:
+ connectString: http://127.0.0.1:2379
+
+ rest:
+ address: 0.0.0.0:9092?websocketEnabled=true
+ server:
+ websocket-prefix: /ws
diff --git a/demo/demo-etcd/consumer/src/main/resources/log4j2.xml
b/demo/demo-etcd/consumer/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..c51f7ad50
--- /dev/null
+++ b/demo/demo-etcd/consumer/src/main/resources/log4j2.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!--this is sample configuration, please modify as your wish-->
+<configuration>
+ <Appenders>
+ <!-- can use MarkerFilter to separate logs with trace id
+ <Console name="Console" target="SYSTEM_OUT">
+ <MarkerFilter marker="SERVICECOMB_MARKER" onMatch="DENY"
onMismatch="ACCEPT"/>
+ <PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n"/>
+ </Console>
+ <Console name="Console-Tracing" target="SYSTEM_OUT">
+ <MarkerFilter marker="SERVICECOMB_MARKER" onMismatch="DENY"
onMatch="ACCEPT"/>
+ <PatternLayout pattern="[%d][%t][%p][%c:%L][%X{SERVICECOMB_TRACE_ID}]
%m%n"/>
+ </Console>
+ -->
+ <Console name="Console" target="SYSTEM_OUT">
+ <PatternLayout pattern="[%d][%t][%p][%c:%L][%X{SERVICECOMB_TRACE_ID}]
%m%n"/>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Root level="info">
+ <AppenderRef ref="Console"/>
+ </Root>
+ </Loggers>
+</configuration>
diff --git a/demo/demo-etcd/gateway/pom.xml b/demo/demo-etcd/gateway/pom.xml
new file mode 100644
index 000000000..b2eb1e51d
--- /dev/null
+++ b/demo/demo-etcd/gateway/pom.xml
@@ -0,0 +1,91 @@
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>demo-etcd</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>etcd-gateway</artifactId>
+ <name>Java Chassis::Demo::Etcd::GATEWAY</name>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>java-chassis-spring-boot-starter-standalone</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>edge-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ <version>3.25.3</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>registry-etcd</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>docker</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.commonjava.maven.plugins</groupId>
+ <artifactId>directory-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>com.github.odavid.maven.plugins</groupId>
+ <artifactId>mixin-maven-plugin</artifactId>
+ <configuration>
+ <mixins>
+ <mixin>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>docker-build-config</artifactId>
+ <version>${project.version}</version>
+ </mixin>
+ </mixins>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git
a/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java
b/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java
new file mode 100644
index 000000000..7d58caafd
--- /dev/null
+++
b/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java
@@ -0,0 +1,33 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.springframework.boot.WebApplicationType;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+
+@SpringBootApplication
+public class GatewayApplication {
+ public static void main(String[] args) throws Exception {
+ try {
+ new
SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/demo/demo-etcd/gateway/src/main/resources/application.yml
b/demo/demo-etcd/gateway/src/main/resources/application.yml
new file mode 100644
index 000000000..f526eb3de
--- /dev/null
+++ b/demo/demo-etcd/gateway/src/main/resources/application.yml
@@ -0,0 +1,53 @@
+#
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+servicecomb:
+ service:
+ application: demo-etcd
+ version: 0.0.1
+ name: gateway
+ registry:
+ etcd:
+ enabled: true
+ connectString: http://127.0.0.1:2379
+
+ rest:
+ address: 0.0.0.0:9090?websocketEnabled=true
+ server:
+ websocket-prefix: /ws
+
+ http:
+ dispatcher:
+ edge:
+ default:
+ enabled: false
+ url:
+ enabled: true
+ pattern: /(.*)
+ mappings:
+ consumer:
+ prefixSegmentCount: 0
+ path: "/.*"
+ microserviceName: consumer
+ versionRule: 0.0.0+
+ websocket:
+ mappings:
+ consumer:
+ prefixSegmentCount: 0
+ path: "/ws/.*"
+ microserviceName: consumer
+ versionRule: 0.0.0+
diff --git a/demo/demo-etcd/gateway/src/main/resources/log4j2.xml
b/demo/demo-etcd/gateway/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..c51f7ad50
--- /dev/null
+++ b/demo/demo-etcd/gateway/src/main/resources/log4j2.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!--this is sample configuration, please modify as your wish-->
+<configuration>
+ <Appenders>
+ <!-- can use MarkerFilter to separate logs with trace id
+ <Console name="Console" target="SYSTEM_OUT">
+ <MarkerFilter marker="SERVICECOMB_MARKER" onMatch="DENY"
onMismatch="ACCEPT"/>
+ <PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n"/>
+ </Console>
+ <Console name="Console-Tracing" target="SYSTEM_OUT">
+ <MarkerFilter marker="SERVICECOMB_MARKER" onMismatch="DENY"
onMatch="ACCEPT"/>
+ <PatternLayout pattern="[%d][%t][%p][%c:%L][%X{SERVICECOMB_TRACE_ID}]
%m%n"/>
+ </Console>
+ -->
+ <Console name="Console" target="SYSTEM_OUT">
+ <PatternLayout pattern="[%d][%t][%p][%c:%L][%X{SERVICECOMB_TRACE_ID}]
%m%n"/>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Root level="info">
+ <AppenderRef ref="Console"/>
+ </Root>
+ </Loggers>
+</configuration>
diff --git a/service-registry/pom.xml b/demo/demo-etcd/pom.xml
similarity index 55%
copy from service-registry/pom.xml
copy to demo/demo-etcd/pom.xml
index 0fb0f3475..bda7b06fe 100644
--- a/service-registry/pom.xml
+++ b/demo/demo-etcd/pom.xml
@@ -18,24 +18,44 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
<parent>
- <groupId>org.apache.servicecomb</groupId>
- <artifactId>java-chassis-parent</artifactId>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>demo-parent</artifactId>
<version>3.3.0-SNAPSHOT</version>
- <relativePath>../parents/default</relativePath>
</parent>
- <modelVersion>4.0.0</modelVersion>
-
- <artifactId>service-registry-parent</artifactId>
- <name>Java Chassis::Service Registry</name>
+ <artifactId>demo-etcd</artifactId>
+ <name>Java Chassis::Demo::Etcd</name>
<packaging>pom</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>demo-schema</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>solution-basic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-slf4j-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ </dependencies>
<modules>
- <module>registry-local</module>
- <module>registry-service-center</module>
- <module>registry-lightweight</module>
- <module>registry-zero-config</module>
- <module>registry-nacos</module>
- <module>registry-zookeeper</module>
+ <module>provider</module>
+ <module>consumer</module>
+ <module>gateway</module>
+ <module>test-client</module>
</modules>
+
</project>
diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml
new file mode 100644
index 000000000..c1413de54
--- /dev/null
+++ b/demo/demo-etcd/provider/pom.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>demo-etcd</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>etcd-provider</artifactId>
+ <name>Java Chassis::Demo::Etcd::PROVIDER</name>
+ <packaging>jar</packaging>
+
+ <properties>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>java-chassis-spring-boot-starter-standalone</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ <version>3.25.3</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>registry-etcd</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>io.reactivex.rxjava3</groupId>
+ <artifactId>rxjava</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>docker</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.commonjava.maven.plugins</groupId>
+ <artifactId>directory-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>com.github.odavid.maven.plugins</groupId>
+ <artifactId>mixin-maven-plugin</artifactId>
+ <configuration>
+ <mixins>
+ <mixin>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>docker-build-config</artifactId>
+ <version>${project.version}</version>
+ </mixin>
+ </mixins>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git
a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java
new file mode 100644
index 000000000..388b962dc
--- /dev/null
+++
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java
@@ -0,0 +1,33 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.springframework.boot.WebApplicationType;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+
+@SpringBootApplication
+public class EtcdProviderApplication {
+ public static void main(String[] args) throws Exception {
+ try {
+ new
SpringApplicationBuilder().web(WebApplicationType.NONE).sources(EtcdProviderApplication.class).run(args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git
a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java
new file mode 100644
index 000000000..8a773f91c
--- /dev/null
+++
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java
@@ -0,0 +1,51 @@
+/*
+ * 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.servicecomb.samples;
+
+import java.util.List;
+
+import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+
+@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface =
IHeaderParamWithListSchemaSpringMvc.class)
+public class HeaderParamWithListSchema implements
IHeaderParamWithListSchemaSpringMvc {
+ @Override
+ public String headerListDefault(List<String> headerList) {
+ return headerList == null ? "null" : headerList.size() + ":" + headerList;
+ }
+
+ @Override
+ public String headerListCSV(List<String> headerList) {
+ return headerList == null ? "null" : headerList.size() + ":" + headerList;
+ }
+
+ @Override
+ public String headerListMULTI(List<String> headerList) {
+ return headerList == null ? "null" : headerList.size() + ":" + headerList;
+ }
+
+ @Override
+ public String headerListSSV(List<String> headerList) {
+ return headerList == null ? "null" : headerList.size() + ":" + headerList;
+ }
+
+ @Override
+ public String headerListPIPES(List<String> headerList) {
+ return headerList == null ? "null" : headerList.size() + ":" + headerList;
+ }
+}
diff --git
a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java
new file mode 100644
index 000000000..8fc333cee
--- /dev/null
+++
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java
@@ -0,0 +1,53 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@RestSchema(schemaId = "ProviderController")
+@RequestMapping(path = "/")
+public class ProviderController implements InitializingBean {
+ private Environment environment;
+
+ @Autowired
+ public void setEnvironment(Environment environment) {
+ this.environment = environment;
+ }
+
+ // a very simple service to echo the request parameter
+ @GetMapping("/sayHello")
+ public String sayHello(@RequestParam("name") String name) {
+// return "Hello " + environment.getProperty("servicecomb.rest.address");
+ return "Hello " + name;
+ }
+
+ @GetMapping("/getConfig")
+ public String getConfig(@RequestParam("key") String key) {
+ return environment.getProperty(key);
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ }
+}
diff --git
a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java
new file mode 100644
index 000000000..8108d15fd
--- /dev/null
+++
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java
@@ -0,0 +1,78 @@
+/*
+ * 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.servicecomb.samples;
+
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.servicecomb.core.CoreConst;
+import org.apache.servicecomb.core.annotation.Transport;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.reactivestreams.Publisher;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import io.reactivex.rxjava3.core.Flowable;
+
+@RestSchema(schemaId = "ReactiveStreamController")
+@RequestMapping(path = "/")
+@Transport(name = CoreConst.RESTFUL)
+public class ReactiveStreamController {
+ public static class Model {
+ private String name;
+
+ private int age;
+
+ public Model() {
+
+ }
+
+ public Model(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public Model setAge(int age) {
+ this.age = age;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Model setName(String name) {
+ this.name = name;
+ return this;
+ }
+ }
+
+ @GetMapping("/sseString")
+ public Publisher<String> sseString() {
+ return Flowable.fromArray("a", "b", "c");
+ }
+
+ @GetMapping("/sseModel")
+ public Publisher<Model> sseModel() {
+ return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS)
+ .map(item -> new Model("jack", item.intValue()));
+ }
+}
diff --git
a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java
new file mode 100644
index 000000000..5f4f9719c
--- /dev/null
+++
b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java
@@ -0,0 +1,67 @@
+/*
+ * 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.servicecomb.samples;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.servicecomb.core.CoreConst;
+import org.apache.servicecomb.core.annotation.Transport;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import io.vertx.core.http.ServerWebSocket;
+
+@RestSchema(schemaId = "WebsocketController")
+@RequestMapping(path = "/ws")
+public class WebsocketController {
+ @PostMapping("/websocket")
+ @Transport(name = CoreConst.WEBSOCKET)
+ public void websocket(ServerWebSocket serverWebsocket) {
+ // Client may have not registered message handler, and messages sent may
get lost.
+ // So we sleep for a while to send message.
+ AtomicInteger receiveCount = new AtomicInteger(0);
+ serverWebsocket.textMessageHandler(s -> {
+ receiveCount.getAndIncrement();
+ });
+ serverWebsocket.closeHandler((v) -> System.out.println("closed"));
+
+ new Thread(() -> {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ serverWebsocket.writeTextMessage("hello", r -> {
+ });
+
+ for (int i = 0; i < 5; i++) {
+ serverWebsocket.writeTextMessage("hello " + i, r -> {
+ });
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ serverWebsocket.writeTextMessage("total " + receiveCount.get());
+ serverWebsocket.close();
+ }).start();
+ }
+}
diff --git a/demo/demo-etcd/provider/src/main/resources/application.yml
b/demo/demo-etcd/provider/src/main/resources/application.yml
new file mode 100644
index 000000000..75b6dee9e
--- /dev/null
+++ b/demo/demo-etcd/provider/src/main/resources/application.yml
@@ -0,0 +1,44 @@
+#
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+# spring boot configurations
+servicecomb:
+ service:
+ application: demo-etcd
+ version: 0.0.1
+ name: provider
+ properties:
+ group: green
+ registry:
+ etcd:
+ connectString: http://127.0.0.1:2379
+
+ rest:
+ address: 0.0.0.0:9094?websocketEnabled=true
+ server:
+ websocket-prefix: /ws
+
+ cors:
+ enabled: true
+ origin: "*"
+ allowCredentials: false
+ allowedMethod: "*"
+ maxAge: 3600
+
+key1: 1
+key2: 3
+key3: 5
diff --git a/demo/demo-etcd/provider/src/main/resources/log4j2.xml
b/demo/demo-etcd/provider/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..c51f7ad50
--- /dev/null
+++ b/demo/demo-etcd/provider/src/main/resources/log4j2.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!--this is sample configuration, please modify as your wish-->
+<configuration>
+ <Appenders>
+ <!-- can use MarkerFilter to separate logs with trace id
+ <Console name="Console" target="SYSTEM_OUT">
+ <MarkerFilter marker="SERVICECOMB_MARKER" onMatch="DENY"
onMismatch="ACCEPT"/>
+ <PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n"/>
+ </Console>
+ <Console name="Console-Tracing" target="SYSTEM_OUT">
+ <MarkerFilter marker="SERVICECOMB_MARKER" onMismatch="DENY"
onMatch="ACCEPT"/>
+ <PatternLayout pattern="[%d][%t][%p][%c:%L][%X{SERVICECOMB_TRACE_ID}]
%m%n"/>
+ </Console>
+ -->
+ <Console name="Console" target="SYSTEM_OUT">
+ <PatternLayout pattern="[%d][%t][%p][%c:%L][%X{SERVICECOMB_TRACE_ID}]
%m%n"/>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Root level="info">
+ <AppenderRef ref="Console"/>
+ </Root>
+ </Loggers>
+</configuration>
diff --git a/demo/demo-etcd/test-client/pom.xml
b/demo/demo-etcd/test-client/pom.xml
new file mode 100644
index 000000000..0c050adcb
--- /dev/null
+++ b/demo/demo-etcd/test-client/pom.xml
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>demo-etcd</artifactId>
+ <version>3.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>etcd-test-client</artifactId>
+ <name>Java Chassis::Demo::Etcd::TEST-CLIENT</name>
+ <packaging>jar</packaging>
+
+ <properties>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>java-chassis-spring-boot-starter-standalone</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>demo-schema</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>registry-local</artifactId>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>docker</id>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <configuration>
+ <images>
+ <image>
+ <name>bitnami/etcd:latest</name>
+ <alias>etcd</alias>
+ <run>
+ <namingStrategy>alias</namingStrategy>
+ <wait>
+ <log>etcdserver</log>
+ <tcp>
+ <ports>
+ <port>2379</port>
+ </ports>
+ </tcp>
+ <time>60000</time>
+ </wait>
+ <ports>
+ <port>etcd.port:2379</port>
+ </ports>
+ <env>
+
<ALLOW_NONE_AUTHENTICATION>yes</ALLOW_NONE_AUTHENTICATION>
+ </env>
+ </run>
+ </image>
+ <image>
+ <name>etcd-provider:${project.version}</name>
+ <alias>etcd-provider</alias>
+ <run>
+ <namingStrategy>alias</namingStrategy>
+ <env>
+ <JAVA_OPTS>
+
-Dservicecomb.registry.etcd.connectString=http://etcd:2379
+
-Dservicecomb.config.etcd.connectString=http://etcd:2379
+ </JAVA_OPTS>
+
<JAR_PATH>/maven/maven/etcd-provider-${project.version}.jar</JAR_PATH>
+ </env>
+ <links>
+ <link>etcd:etcd</link>
+ </links>
+ <wait>
+ <log>ServiceComb is ready</log>
+ <tcp>
+ <ports>
+ <port>9094</port>
+ </ports>
+ </tcp>
+ <time>120000</time>
+ </wait>
+ <ports>
+ <port>9094:9094</port>
+ </ports>
+ </run>
+ </image>
+ <image>
+ <name>etcd-consumer:${project.version}</name>
+ <alias>etcd-consumer</alias>
+ <run>
+ <namingStrategy>alias</namingStrategy>
+ <env>
+ <JAVA_OPTS>
+
-Dservicecomb.registry.etcd.connectString=http://etcd:2379
+ </JAVA_OPTS>
+
<JAR_PATH>/maven/maven/etcd-consumer-${project.version}.jar</JAR_PATH>
+ </env>
+ <links>
+ <link>etcd:etcd</link>
+ </links>
+ <wait>
+ <log>ServiceComb is ready</log>
+ <tcp>
+ <ports>
+ <port>9092</port>
+ </ports>
+ </tcp>
+ <time>120000</time>
+ </wait>
+ <ports>
+ <port>9092:9092</port>
+ </ports>
+ </run>
+ </image>
+ <image>
+ <name>etcd-gateway:${project.version}</name>
+ <alias>etcd-gateway</alias>
+ <run>
+ <namingStrategy>alias</namingStrategy>
+ <env>
+ <JAVA_OPTS>
+
-Dservicecomb.registry.etcd.connectString=http://etcd:2379
+ </JAVA_OPTS>
+
<JAR_PATH>/maven/maven/etcd-gateway-${project.version}.jar</JAR_PATH>
+ </env>
+ <links>
+ <link>etcd:etcd</link>
+ </links>
+ <wait>
+ <log>ServiceComb is ready</log>
+ <tcp>
+ <ports>
+ <port>9090</port>
+ </ports>
+ </tcp>
+ <time>120000</time>
+ </wait>
+ <ports>
+ <port>9090:9090</port>
+ </ports>
+ </run>
+ </image>
+ </images>
+ </configuration>
+ <executions>
+ <execution>
+ <id>start</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>start</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>stop</id>
+ <phase>post-integration-test</phase>
+ <goals>
+ <goal>stop</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>com.github.odavid.maven.plugins</groupId>
+ <artifactId>mixin-maven-plugin</artifactId>
+ <configuration>
+ <mixins>
+ <mixin>
+ <groupId>org.apache.servicecomb.demo</groupId>
+ <artifactId>docker-run-config</artifactId>
+ <version>${project.version}</version>
+ </mixin>
+ </mixins>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git
a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java
new file mode 100644
index 000000000..2e9105cdd
--- /dev/null
+++
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java
@@ -0,0 +1,22 @@
+/*
+ * 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.servicecomb.samples;
+
+public interface Config {
+ String GATEWAY_URL = "http://localhost:9090";
+}
diff --git
a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java
new file mode 100644
index 000000000..1b129acc2
--- /dev/null
+++
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java
@@ -0,0 +1,98 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.apache.servicecomb.demo.CategorizedTestCase;
+import org.apache.servicecomb.demo.TestMgr;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
+
+@Component
+public class HeaderParamWithListSchemaIT implements CategorizedTestCase {
+ RestOperations template = new RestTemplate();
+
+ @Override
+ public void testRestTransport() throws Exception {
+ testHeaderListDefault();
+ testHeaderListMulti();
+ testHeaderListCSV();
+ testHeaderListSSV();
+ testHeaderListPipes();
+ }
+
+ // default to multi
+ private void testHeaderListDefault() {
+ MultiValueMap<String, String> headers = new HttpHeaders();
+ headers.add("headerList", "a");
+ headers.add("headerList", "b");
+ headers.add("headerList", "c");
+ HttpEntity<Void> entity = new HttpEntity<>(headers);
+ String result = template
+ .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault",
HttpMethod.GET, entity, String.class).getBody();
+ TestMgr.check("3:[a, b, c]", result);
+ }
+
+ private void testHeaderListPipes() {
+ MultiValueMap<String, String> headers = new HttpHeaders();
+ headers.add("headerList", "a|b|c");
+ HttpEntity<Void> entity = new HttpEntity<>(headers);
+ String result = template
+ .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES",
HttpMethod.GET, entity, String.class).getBody();
+ TestMgr.check("3:[a, b, c]", result);
+ }
+
+ private void testHeaderListSSV() {
+ MultiValueMap<String, String> headers = new HttpHeaders();
+ headers.add("headerList", "a b c");
+ HttpEntity<Void> entity = new HttpEntity<>(headers);
+ String result = template
+ .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV",
HttpMethod.GET, entity, String.class).getBody();
+ TestMgr.check("3:[a, b, c]", result);
+ }
+
+ private void testHeaderListCSV() {
+ MultiValueMap<String, String> headers = new HttpHeaders();
+ headers.add("headerList", "a,b,c");
+ HttpEntity<Void> entity = new HttpEntity<>(headers);
+ String result = template
+ .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV",
HttpMethod.GET, entity, String.class).getBody();
+ TestMgr.check("3:[a, b, c]", result);
+
+ headers.add("headerList", "a, b, c");
+ entity = new HttpEntity<>(headers);
+ result = template
+ .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV",
HttpMethod.GET, entity, String.class).getBody();
+ TestMgr.check("3:[a, b, c]", result);
+ }
+
+ private void testHeaderListMulti() {
+ MultiValueMap<String, String> headers = new HttpHeaders();
+ headers.add("headerList", "a");
+ headers.add("headerList", "b");
+ headers.add("headerList", "c");
+ HttpEntity<Void> entity = new HttpEntity<>(headers);
+ String result = template
+ .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI",
HttpMethod.GET, entity, String.class).getBody();
+ TestMgr.check("3:[a, b, c]", result);
+ }
+}
diff --git
a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java
new file mode 100644
index 000000000..97e883fb4
--- /dev/null
+++
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java
@@ -0,0 +1,68 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.apache.servicecomb.demo.CategorizedTestCase;
+import org.apache.servicecomb.demo.TestMgr;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
+
+@Component
+public class HelloWorldIT implements CategorizedTestCase {
+ RestOperations template = new RestTemplate();
+
+ @Override
+ public void testRestTransport() throws Exception {
+ testHelloWorld();
+ testGetConfig();
+ }
+
+ private void testGetConfig() {
+ String result = template
+ .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1",
String.class);
+ TestMgr.check("1", result);
+ result = template
+ .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2",
String.class);
+ TestMgr.check("3", result);
+ result = template
+ .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3",
String.class);
+ TestMgr.check("5", result);
+ }
+
+ private void testHelloWorld() {
+ String result = template
+ .getForObject(Config.GATEWAY_URL + "/sayHello?name=World",
String.class);
+ TestMgr.check("Hello World", result);
+
+ // test trace id added
+ MultiValueMap<String, String> headers = new HttpHeaders();
+ headers.add("X-B3-TraceId", "81de2eb7691c2bbb");
+ HttpEntity<Object> entity = new HttpEntity(headers);
+ ResponseEntity<String> response =
+ template.exchange(Config.GATEWAY_URL + "/sayHello?name=World",
HttpMethod.GET, entity, String.class);
+ TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size());
+ TestMgr.check("81de2eb7691c2bbb",
response.getHeaders().getFirst("X-B3-TraceId"));
+ TestMgr.check("Hello World", response.getBody());
+ }
+}
diff --git
a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java
new file mode 100644
index 000000000..488a6cf71
--- /dev/null
+++
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java
@@ -0,0 +1,119 @@
+/*
+ * 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.servicecomb.samples;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.servicecomb.demo.CategorizedTestCase;
+import org.apache.servicecomb.demo.TestMgr;
+import
org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient;
+import
org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model;
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ReactiveStreamIT implements CategorizedTestCase {
+ @Autowired
+ @Qualifier("reactiveStreamProvider")
+ ReactiveStreamClient reactiveStreamProvider;
+
+ @Autowired
+ @Qualifier("reactiveStreamGateway")
+ ReactiveStreamClient reactiveStreamGateway;
+
+ @Override
+ public void testRestTransport() throws Exception {
+ testSseString(reactiveStreamProvider);
+ testSseModel(reactiveStreamProvider);
+ testSseString(reactiveStreamGateway);
+ testSseModel(reactiveStreamGateway);
+ }
+
+ private void testSseModel(ReactiveStreamClient client) throws Exception {
+ Publisher<Model> result = client.sseModel();
+ StringBuilder buffer = new StringBuilder();
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ result.subscribe(new Subscriber<>() {
+ Subscription subscription;
+
+ @Override
+ public void onSubscribe(Subscription s) {
+ subscription = s;
+ subscription.request(1);
+ }
+
+ @Override
+ public void onNext(Model s) {
+ buffer.append(s.getName()).append(s.getAge());
+ subscription.request(1);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ subscription.cancel();
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onComplete() {
+ countDownLatch.countDown();
+ }
+ });
+ countDownLatch.await(10, TimeUnit.SECONDS);
+ TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString());
+ }
+
+ private void testSseString(ReactiveStreamClient client) throws Exception {
+ Publisher<String> result = client.sseString();
+ StringBuilder buffer = new StringBuilder();
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ result.subscribe(new Subscriber<>() {
+ Subscription subscription;
+
+ @Override
+ public void onSubscribe(Subscription s) {
+ subscription = s;
+ subscription.request(1);
+ }
+
+ @Override
+ public void onNext(String s) {
+ buffer.append(s);
+ subscription.request(1);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ subscription.cancel();
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onComplete() {
+ countDownLatch.countDown();
+ }
+ });
+ countDownLatch.await(10, TimeUnit.SECONDS);
+ TestMgr.check("abc", buffer.toString());
+ }
+}
diff --git
a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java
new file mode 100644
index 000000000..26a2a491b
--- /dev/null
+++
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java
@@ -0,0 +1,49 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.apache.servicecomb.demo.CategorizedTestCaseRunner;
+import org.apache.servicecomb.demo.TestMgr;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.WebApplicationType;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+
+@SpringBootApplication
+public class TestClientApplication {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(TestClientApplication.class);
+
+ public static void main(String[] args) throws Exception {
+ try {
+ new
SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args);
+
+ run();
+ } catch (Exception e) {
+ TestMgr.failed("test case run failed", e);
+ LOGGER.error("-------------- test failed -------------");
+ LOGGER.error("", e);
+ LOGGER.error("-------------- test failed -------------");
+ }
+ TestMgr.summary();
+ }
+
+ public static void run() throws Exception {
+ CategorizedTestCaseRunner.runCategorizedTestCase("consumer");
+ }
+}
diff --git
a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java
new file mode 100644
index 000000000..3ee5db8c3
--- /dev/null
+++
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java
@@ -0,0 +1,125 @@
+/*
+ * 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.servicecomb.samples;
+
+import java.util.List;
+
+import org.apache.servicecomb.core.CoreConst;
+import org.apache.servicecomb.core.annotation.Transport;
+import org.apache.servicecomb.localregistry.RegistryBean;
+import org.apache.servicecomb.localregistry.RegistryBean.Instance;
+import org.apache.servicecomb.localregistry.RegistryBean.Instances;
+import org.apache.servicecomb.provider.pojo.Invoker;
+import org.reactivestreams.Publisher;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import io.vertx.core.http.WebSocket;
+
+@Configuration
+public class ThirdSvcConfiguration {
+ @RequestMapping(path = "/ws")
+ public interface WebsocketClient {
+ @PostMapping("/websocket")
+ @Transport(name = CoreConst.WEBSOCKET)
+ WebSocket websocket();
+ }
+
+ @RequestMapping(path = "/")
+ public interface ReactiveStreamClient {
+ class Model {
+ private String name;
+
+ private int age;
+
+ public Model() {
+
+ }
+
+ public Model(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public Model setAge(int age) {
+ this.age = age;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Model setName(String name) {
+ this.name = name;
+ return this;
+ }
+ }
+
+ @GetMapping("/sseString")
+ Publisher<String> sseString();
+
+ @GetMapping("/sseModel")
+ Publisher<Model> sseModel();
+ }
+
+ @Bean
+ public RegistryBean providerServiceBean() {
+ return new RegistryBean()
+ .addSchemaInterface("ReactiveStreamController",
ReactiveStreamClient.class)
+ .setAppId("demo-etcd")
+ .setServiceName("provider")
+ .setVersion("0.0.1")
+ .setInstances(new Instances().setInstances(List.of(
+ new Instance().setEndpoints(List.of("rest://localhost:9094")))));
+ }
+
+ @Bean
+ public RegistryBean gatewayServiceBean() {
+ return new RegistryBean()
+ .addSchemaInterface("ReactiveStreamController",
ReactiveStreamClient.class)
+ .addSchemaInterface("WebsocketController", WebsocketClient.class)
+ .setAppId("demo-etcd")
+ .setServiceName("gateway")
+ .setVersion("0.0.1")
+ .setInstances(new Instances().setInstances(List.of(
+ new
Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true")))));
+ }
+
+ @Bean("reactiveStreamProvider")
+ public ReactiveStreamClient reactiveStreamProvider() {
+ return Invoker.createProxy("provider", "ReactiveStreamController",
ReactiveStreamClient.class);
+ }
+
+ @Bean("reactiveStreamGateway")
+ public ReactiveStreamClient reactiveStreamGateway() {
+ return Invoker.createProxy("gateway", "ReactiveStreamController",
ReactiveStreamClient.class);
+ }
+
+ @Bean
+ public WebsocketClient gatewayWebsocketClient() {
+ return Invoker.createProxy("gateway", "WebsocketController",
WebsocketClient.class);
+ }
+}
diff --git
a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java
new file mode 100644
index 000000000..ea7a7f45b
--- /dev/null
+++
b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java
@@ -0,0 +1,57 @@
+/*
+ * 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.servicecomb.samples;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.servicecomb.demo.CategorizedTestCase;
+import org.apache.servicecomb.demo.TestMgr;
+import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import io.vertx.core.http.WebSocket;
+
+@Component
+public class WebsocketIT implements CategorizedTestCase {
+ @Autowired
+ private WebsocketClient websocketClient;
+
+ @Override
+ public void testRestTransport() throws Exception {
+ StringBuffer sb = new StringBuffer();
+ AtomicBoolean closed = new AtomicBoolean(false);
+ CountDownLatch latch = new CountDownLatch(1);
+
+ WebSocket webSocket = websocketClient.websocket();
+ webSocket.textMessageHandler(s -> {
+ sb.append(s);
+ sb.append(" ");
+ webSocket.writeTextMessage(s);
+ });
+ webSocket.closeHandler(v -> {
+ closed.set(true);
+ latch.countDown();
+ });
+ latch.await(30, TimeUnit.SECONDS);
+ TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello
4 total 6 ");
+ TestMgr.check(closed.get(), true);
+ }
+}
diff --git a/demo/demo-etcd/test-client/src/main/resources/application.yml
b/demo/demo-etcd/test-client/src/main/resources/application.yml
new file mode 100644
index 000000000..396bebfd8
--- /dev/null
+++ b/demo/demo-etcd/test-client/src/main/resources/application.yml
@@ -0,0 +1,25 @@
+#
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+servicecomb:
+ service:
+ application: demo-etcd
+ name: test-client
+ version: 0.0.1
+
+ rest:
+ address: 0.0.0.0:9097 # should be same with server.port to use web
container
diff --git a/demo/demo-etcd/test-client/src/main/resources/log4j2.xml
b/demo/demo-etcd/test-client/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..c51f7ad50
--- /dev/null
+++ b/demo/demo-etcd/test-client/src/main/resources/log4j2.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!--this is sample configuration, please modify as your wish-->
+<configuration>
+ <Appenders>
+ <!-- can use MarkerFilter to separate logs with trace id
+ <Console name="Console" target="SYSTEM_OUT">
+ <MarkerFilter marker="SERVICECOMB_MARKER" onMatch="DENY"
onMismatch="ACCEPT"/>
+ <PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n"/>
+ </Console>
+ <Console name="Console-Tracing" target="SYSTEM_OUT">
+ <MarkerFilter marker="SERVICECOMB_MARKER" onMismatch="DENY"
onMatch="ACCEPT"/>
+ <PatternLayout pattern="[%d][%t][%p][%c:%L][%X{SERVICECOMB_TRACE_ID}]
%m%n"/>
+ </Console>
+ -->
+ <Console name="Console" target="SYSTEM_OUT">
+ <PatternLayout pattern="[%d][%t][%p][%c:%L][%X{SERVICECOMB_TRACE_ID}]
%m%n"/>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Root level="info">
+ <AppenderRef ref="Console"/>
+ </Root>
+ </Loggers>
+</configuration>
diff --git
a/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java
b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java
new file mode 100644
index 000000000..833feff87
--- /dev/null
+++
b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java
@@ -0,0 +1,53 @@
+/*
+ * 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.servicecomb.samples;
+
+import org.apache.servicecomb.demo.TestMgr;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(classes = TestClientApplication.class)
+public class EtcdIT {
+ private static final Logger LOGGER = LoggerFactory.getLogger(EtcdIT.class);
+
+ @BeforeEach
+ public void setUp() {
+ TestMgr.errors().clear();
+ }
+
+ @Test
+ public void clientGetsNoError() {
+ try {
+ TestClientApplication.run();
+ } catch (Exception e) {
+ TestMgr.failed("test case run failed", e);
+ LOGGER.error("-------------- test failed -------------");
+ LOGGER.error("", e);
+ LOGGER.error("-------------- test failed -------------");
+ }
+ TestMgr.summary();
+ Assertions.assertTrue(TestMgr.errors().isEmpty());
+ }
+}
diff --git a/demo/pom.xml b/demo/pom.xml
index bfdd595ef..fa88e1526 100644
--- a/demo/pom.xml
+++ b/demo/pom.xml
@@ -56,6 +56,7 @@
<module>demo-cse-v1</module>
<module>demo-cse-v2</module>
<module>demo-nacos</module>
+ <module>demo-etcd</module>
<module>demo-zookeeper</module>
</modules>
diff --git a/dependencies/bom/pom.xml b/dependencies/bom/pom.xml
index e417ae4cf..f2218783f 100644
--- a/dependencies/bom/pom.xml
+++ b/dependencies/bom/pom.xml
@@ -278,6 +278,11 @@
<artifactId>registry-zookeeper</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>registry-etcd</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- ServiceComb: solutions -->
<dependency>
<groupId>org.apache.servicecomb</groupId>
diff --git a/dependencies/default/pom.xml b/dependencies/default/pom.xml
index 573afe7f7..d9cf1966f 100644
--- a/dependencies/default/pom.xml
+++ b/dependencies/default/pom.xml
@@ -91,6 +91,7 @@
<vertx.version>4.5.10</vertx.version>
<zipkin.version>3.4.1</zipkin.version>
<zipkin-reporter.version>3.4.0</zipkin-reporter.version>
+ <jetcd-core.version>0.8.3</jetcd-core.version>
<!-- Base dir of main -->
<main.basedir>${basedir}/../..</main.basedir>
</properties>
@@ -542,6 +543,12 @@
<type>pom</type>
<scope>import</scope>
</dependency>
+
+ <dependency>
+ <groupId>io.etcd</groupId>
+ <artifactId>jetcd-core</artifactId>
+ <version>${jetcd-core.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
</project>
diff --git a/service-registry/pom.xml b/service-registry/pom.xml
index 0fb0f3475..213f0b0f2 100644
--- a/service-registry/pom.xml
+++ b/service-registry/pom.xml
@@ -37,5 +37,6 @@
<module>registry-zero-config</module>
<module>registry-nacos</module>
<module>registry-zookeeper</module>
+ <module>registry-etcd</module>
</modules>
</project>
diff --git a/service-registry/pom.xml b/service-registry/registry-etcd/pom.xml
similarity index 58%
copy from service-registry/pom.xml
copy to service-registry/registry-etcd/pom.xml
index 0fb0f3475..cf48811d3 100644
--- a/service-registry/pom.xml
+++ b/service-registry/registry-etcd/pom.xml
@@ -16,26 +16,37 @@
~ limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.servicecomb</groupId>
- <artifactId>java-chassis-parent</artifactId>
+ <artifactId>service-registry-parent</artifactId>
<version>3.3.0-SNAPSHOT</version>
- <relativePath>../parents/default</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
- <artifactId>service-registry-parent</artifactId>
- <name>Java Chassis::Service Registry</name>
- <packaging>pom</packaging>
+ <artifactId>registry-etcd</artifactId>
+ <name>Java Chassis::Service Registry::Etcd</name>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>io.etcd</groupId>
+ <artifactId>jetcd-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>foundation-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>foundation-registry</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>java-chassis-core</artifactId>
+ </dependency>
+ </dependencies>
- <modules>
- <module>registry-local</module>
- <module>registry-service-center</module>
- <module>registry-lightweight</module>
- <module>registry-zero-config</module>
- <module>registry-nacos</module>
- <module>registry-zookeeper</module>
- </modules>
</project>
diff --git
a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java
new file mode 100644
index 000000000..a926c7bbc
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * 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.servicecomb.registry.etcd;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class EtcdConfiguration {
+ @Bean
+ @ConfigurationProperties(prefix = EtcdConst.ETCD_REGISTRY_PREFIX)
+ public EtcdRegistryProperties etcdRegistryProperties() {
+ return new EtcdRegistryProperties();
+ }
+
+ @Bean
+ public EtcdDiscovery etcdDiscovery() {
+ return new EtcdDiscovery();
+ }
+
+ @Bean
+ public EtcdRegistration etcdRegistration() {
+ return new EtcdRegistration();
+ }
+}
diff --git
a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java
new file mode 100644
index 000000000..03b28f0d2
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java
@@ -0,0 +1,30 @@
+/*
+ * 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.servicecomb.registry.etcd;
+
+public class EtcdConst {
+
+ public static final String ETCD_REGISTRY_NAME = "etcd-registry";
+
+ public static final String ETCD_DISCOVERY_ROOT = "/servicecomb/registry/%s";
+
+ public static final String ETCD_REGISTRY_PREFIX =
"servicecomb.registry.etcd";
+
+ public static final String ETCD_DISCOVERY_ENABLED = ETCD_REGISTRY_PREFIX +
".%s.%s.enabled";
+
+ public static final String ETCD_DEFAULT_ENVIRONMENT = "production";
+}
diff --git
a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java
new file mode 100644
index 000000000..3f5888269
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java
@@ -0,0 +1,197 @@
+/*
+ * 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.servicecomb.registry.etcd;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.servicecomb.config.BootStrapProperties;
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.apache.servicecomb.foundation.common.utils.JsonUtils;
+import org.apache.servicecomb.registry.api.Discovery;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+
+import com.google.common.collect.Lists;
+
+import io.etcd.jetcd.ByteSequence;
+import io.etcd.jetcd.Client;
+import io.etcd.jetcd.KeyValue;
+import io.etcd.jetcd.Watch;
+import io.etcd.jetcd.kv.GetResponse;
+import io.etcd.jetcd.options.GetOption;
+import io.etcd.jetcd.options.WatchOption;
+
+public class EtcdDiscovery implements Discovery<EtcdDiscoveryInstance> {
+
+ private Environment environment;
+
+ private String basePath;
+
+ private EtcdRegistryProperties etcdRegistryProperties;
+
+ private Client client;
+
+ private InstanceChangedListener<EtcdDiscoveryInstance>
instanceChangedListener;
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(EtcdDiscovery.class);
+
+ private Map<String, Watch> watchMap = new ConcurrentHashMapEx<>();
+
+
+ @Autowired
+ @SuppressWarnings("unused")
+ public void setEnvironment(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Autowired
+ @SuppressWarnings("unused")
+ public void setEtcdRegistryProperties(EtcdRegistryProperties
etcdRegistryProperties) {
+ this.etcdRegistryProperties = etcdRegistryProperties;
+ }
+
+ @Override
+ public String name() {
+ return EtcdConst.ETCD_REGISTRY_NAME;
+ }
+
+ @Override
+ public boolean enabled(String application, String serviceName) {
+ return
environment.getProperty(String.format(EtcdConst.ETCD_DISCOVERY_ENABLED,
application, serviceName),
+ boolean.class, true);
+ }
+
+ @Override
+ public List<EtcdDiscoveryInstance> findServiceInstances(String application,
String serviceName) {
+
+ String prefixPath = basePath + "/" + application + "/" + serviceName;
+ watchMap.computeIfAbsent(prefixPath, serName -> {
+ Watch watchClient = client.getWatchClient();
+ try {
+ ByteSequence prefixByteSeq = ByteSequence.from(prefixPath,
Charset.defaultCharset());
+ watchClient.watch(prefixByteSeq,
WatchOption.builder().withPrefix(prefixByteSeq).build(),
+ resp -> watchNode(application, serviceName, prefixPath));
+ } catch (Exception e) {
+ LOGGER.error("Failed to add watch", e);
+ }
+ return watchClient;
+ });
+
+ List<KeyValue> endpointKv = getValuesByPrefix(prefixPath);
+ return convertServiceInstanceList(endpointKv);
+ }
+
+ private void watchNode(String application, String serviceName, String
prefixPath) {
+
+ CompletableFuture<GetResponse> getFuture = client.getKVClient()
+ .get(ByteSequence.from(prefixPath, StandardCharsets.UTF_8),
+ GetOption.builder().withPrefix(ByteSequence.from(prefixPath,
StandardCharsets.UTF_8)).build());
+ getFuture.thenAcceptAsync(response -> {
+ List<EtcdDiscoveryInstance> discoveryInstanceList =
convertServiceInstanceList(response.getKvs());
+ instanceChangedListener.onInstanceChanged(name(), application,
serviceName, discoveryInstanceList);
+ }).exceptionally(e -> {
+ LOGGER.error("watchNode error", e);
+ return null;
+ });
+ }
+
+ private List<KeyValue> getValuesByPrefix(String prefix) {
+
+ CompletableFuture<GetResponse> getFuture = client.getKVClient()
+ .get(ByteSequence.from(prefix, StandardCharsets.UTF_8),
+ GetOption.builder().withPrefix(ByteSequence.from(prefix,
StandardCharsets.UTF_8)).build());
+ GetResponse response = MuteExceptionUtil.builder().withLog("get kv by
prefix error")
+ .executeCompletableFuture(getFuture);
+ return response.getKvs();
+ }
+
+ private List<EtcdDiscoveryInstance>
convertServiceInstanceList(List<KeyValue> keyValueList) {
+
+ List<EtcdDiscoveryInstance> list =
Lists.newArrayListWithExpectedSize(keyValueList.size());
+ for (KeyValue keyValue : keyValueList) {
+ EtcdDiscoveryInstance etcdDiscoveryInstance =
getEtcdDiscoveryInstance(keyValue);
+ list.add(etcdDiscoveryInstance);
+ }
+ return list;
+ }
+
+ private static EtcdDiscoveryInstance getEtcdDiscoveryInstance(KeyValue
keyValue) {
+ String valueJson = new String(keyValue.getValue().getBytes(),
Charset.defaultCharset());
+
+ EtcdInstance etcdInstance = MuteExceptionUtil.builder()
+ .withLog("convert json value to obj from etcd failure, {}", valueJson)
+ .executeFunctionWithDoubleParam(JsonUtils::readValue,
valueJson.getBytes(StandardCharsets.UTF_8),
+ EtcdInstance.class);
+ EtcdDiscoveryInstance etcdDiscoveryInstance = new
EtcdDiscoveryInstance(etcdInstance);
+ return etcdDiscoveryInstance;
+ }
+
+ @Override
+ public List<String> findServices(String application) {
+
+ String prefixPath = basePath + "/" + application;
+ List<KeyValue> endpointKv = getValuesByPrefix(prefixPath);
+ return endpointKv.stream().map(kv ->
kv.getKey().toString(StandardCharsets.UTF_8)).collect(Collectors.toList());
+ }
+
+ @Override
+ public void
setInstanceChangedListener(InstanceChangedListener<EtcdDiscoveryInstance>
instanceChangedListener) {
+ this.instanceChangedListener = instanceChangedListener;
+ }
+
+ @Override
+ public boolean enabled() {
+ return etcdRegistryProperties.isEnabled();
+ }
+
+ @Override
+ public void init() {
+ String env = BootStrapProperties.readServiceEnvironment(environment);
+ if (StringUtils.isEmpty(env)) {
+ env = EtcdConst.ETCD_DEFAULT_ENVIRONMENT;
+ }
+ basePath = String.format(EtcdConst.ETCD_DISCOVERY_ROOT, env);
+ }
+
+ @Override
+ public void run() {
+ if (StringUtils.isEmpty(etcdRegistryProperties.getAuthenticationInfo())) {
+ this.client =
Client.builder().endpoints(etcdRegistryProperties.getConnectString()).build();
+ } else {
+ String[] authInfo =
etcdRegistryProperties.getAuthenticationInfo().split(":");
+ this.client =
Client.builder().endpoints(etcdRegistryProperties.getConnectString())
+ .user(ByteSequence.from(authInfo[0], Charset.defaultCharset()))
+ .password(ByteSequence.from(authInfo[1],
Charset.defaultCharset())).build();
+ }
+ }
+
+ @Override
+ public void destroy() {
+ if (client != null) {
+ client.close();
+ watchMap = null;
+ }
+ }
+}
diff --git
a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java
new file mode 100644
index 000000000..85f5ade01
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java
@@ -0,0 +1,37 @@
+/*
+ * 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.servicecomb.registry.etcd;
+
+import org.apache.servicecomb.registry.api.DiscoveryInstance;
+import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus;
+
+public class EtcdDiscoveryInstance extends EtcdInstance implements
DiscoveryInstance {
+
+ public EtcdDiscoveryInstance(EtcdInstance other) {
+ super(other);
+ }
+
+ @Override
+ public MicroserviceInstanceStatus getStatus() {
+ return MicroserviceInstanceStatus.UP;
+ }
+
+ @Override
+ public String getRegistryName() {
+ return EtcdConst.ETCD_REGISTRY_NAME;
+ }
+}
diff --git
a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java
new file mode 100644
index 000000000..19fbd8cad
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java
@@ -0,0 +1,208 @@
+/*
+ * 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.servicecomb.registry.etcd;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.registry.api.DataCenterInfo;
+import org.apache.servicecomb.registry.api.MicroserviceInstance;
+
+public class EtcdInstance implements MicroserviceInstance {
+ private String serviceId;
+
+ private String instanceId;
+
+ private String environment;
+
+ private String application;
+
+ private String serviceName;
+
+ private String alias;
+
+ private String version;
+
+ private String description;
+
+ private DataCenterInfo dataCenterInfo;
+
+ private List<String> endpoints = new ArrayList<>();
+
+ private Map<String, String> schemas = new HashMap<>();
+
+ private Map<String, String> properties = new HashMap<>();
+
+ public EtcdInstance() {
+
+ }
+
+ public EtcdInstance(EtcdInstance other) {
+ this.serviceId = other.serviceId;
+ this.instanceId = other.instanceId;
+ this.environment = other.environment;
+ this.application = other.application;
+ this.serviceName = other.serviceName;
+ this.alias = other.alias;
+ this.version = other.version;
+ this.description = other.description;
+ this.dataCenterInfo = other.dataCenterInfo;
+ this.endpoints = other.endpoints;
+ this.schemas = other.schemas;
+ this.properties = other.properties;
+ }
+
+ public void setServiceId(String serviceId) {
+ this.serviceId = serviceId;
+ }
+
+ public void setInstanceId(String instanceId) {
+ this.instanceId = instanceId;
+ }
+
+ public void setEnvironment(String environment) {
+ this.environment = environment;
+ }
+
+ public void setApplication(String application) {
+ this.application = application;
+ }
+
+ public void setServiceName(String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void setDataCenterInfo(DataCenterInfo dataCenterInfo) {
+ this.dataCenterInfo = dataCenterInfo;
+ }
+
+ public void setEndpoints(List<String> endpoints) {
+ this.endpoints = endpoints;
+ }
+
+ public void setSchemas(Map<String, String> schemas) {
+ this.schemas = schemas;
+ }
+
+ public void setProperties(Map<String, String> properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public String getEnvironment() {
+ return this.environment;
+ }
+
+ @Override
+ public String getApplication() {
+ return this.application;
+ }
+
+ @Override
+ public String getServiceName() {
+ return this.serviceName;
+ }
+
+ @Override
+ public String getAlias() {
+ return alias;
+ }
+
+ @Override
+ public String getVersion() {
+ return version;
+ }
+
+ @Override
+ public DataCenterInfo getDataCenterInfo() {
+ return dataCenterInfo;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ @Override
+ public Map<String, String> getSchemas() {
+ return schemas;
+ }
+
+ @Override
+ public List<String> getEndpoints() {
+ return endpoints;
+ }
+
+ public void addSchema(String schemaId, String content) {
+ this.schemas.put(schemaId, content);
+ }
+
+ public void addEndpoint(String endpoint) {
+ this.endpoints.add(endpoint);
+ }
+
+ public void addProperty(String key, String value) {
+ this.properties.put(key, value);
+ }
+
+ @Override
+ public String getInstanceId() {
+ return instanceId;
+ }
+
+ @Override
+ public String getServiceId() {
+ return serviceId;
+ }
+
+ @Override
+ public String toString() {
+ return "EtcdInstance{" +
+ "serviceId='" + serviceId + '\'' +
+ ", instanceId='" + instanceId + '\'' +
+ ", environment='" + environment + '\'' +
+ ", application='" + application + '\'' +
+ ", serviceName='" + serviceName + '\'' +
+ ", alias='" + alias + '\'' +
+ ", version='" + version + '\'' +
+ ", description='" + description + '\'' +
+ ", dataCenterInfo=" + dataCenterInfo +
+ ", endpoints=" + endpoints +
+ ", schemas=" + schemas +
+ ", properties=" + properties +
+ '}';
+ }
+}
diff --git
a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java
new file mode 100644
index 000000000..0fae8b94d
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java
@@ -0,0 +1,199 @@
+/*
+ * 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.servicecomb.registry.etcd;
+
+import java.nio.charset.Charset;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.servicecomb.config.BootStrapProperties;
+import org.apache.servicecomb.config.DataCenterProperties;
+import org.apache.servicecomb.foundation.common.utils.JsonUtils;
+import org.apache.servicecomb.registry.RegistrationId;
+import org.apache.servicecomb.registry.api.DataCenterInfo;
+import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus;
+import org.apache.servicecomb.registry.api.Registration;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+
+import io.etcd.jetcd.ByteSequence;
+import io.etcd.jetcd.Client;
+import io.etcd.jetcd.KV;
+import io.etcd.jetcd.Lease;
+import io.etcd.jetcd.kv.PutResponse;
+import io.etcd.jetcd.options.PutOption;
+
+public class EtcdRegistration implements
Registration<EtcdRegistrationInstance> {
+
+ private EtcdInstance etcdInstance;
+
+ private Environment environment;
+
+ private String basePath;
+
+ private RegistrationId registrationId;
+
+ private DataCenterProperties dataCenterProperties;
+
+ private EtcdRegistryProperties etcdRegistryProperties;
+
+ private Client client;
+
+ private ScheduledExecutorService executorService;
+
+ private String keyPath;
+
+ private Long leaseId;
+
+ @Autowired
+ @SuppressWarnings("unused")
+ public void setEnvironment(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Autowired
+ @SuppressWarnings("unused")
+ public void setEtcdRegistryProperties(EtcdRegistryProperties
etcdRegistryProperties) {
+ this.etcdRegistryProperties = etcdRegistryProperties;
+ }
+
+ @Autowired
+ public void setRegistrationId(RegistrationId registrationId) {
+ this.registrationId = registrationId;
+ }
+
+ @Autowired
+ @SuppressWarnings("unused")
+ public void setDataCenterProperties(DataCenterProperties
dataCenterProperties) {
+ this.dataCenterProperties = dataCenterProperties;
+ }
+
+ @Override
+ public String name() {
+ return EtcdConst.ETCD_REGISTRY_NAME;
+ }
+
+ @Override
+ public EtcdRegistrationInstance getMicroserviceInstance() {
+ return new EtcdRegistrationInstance(etcdInstance);
+ }
+
+ @Override
+ public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus
status) {
+ return true;
+ }
+
+ @Override
+ public void addSchema(String schemaId, String content) {
+ if (etcdRegistryProperties.isEnableSwaggerRegistration()) {
+ etcdInstance.addSchema(schemaId, content);
+ }
+ }
+
+ @Override
+ public void addEndpoint(String endpoint) {
+ etcdInstance.addEndpoint(endpoint);
+ }
+
+ @Override
+ public void addProperty(String key, String value) {
+ etcdInstance.addProperty(key, value);
+ }
+
+ @Override
+ public boolean enabled() {
+ return etcdRegistryProperties.isEnabled();
+ }
+
+ @Override
+ public void init() {
+ String env = BootStrapProperties.readServiceEnvironment(environment);
+ if (StringUtils.isEmpty(env)) {
+ env = EtcdConst.ETCD_DEFAULT_ENVIRONMENT;
+ }
+ basePath = String.format(EtcdConst.ETCD_DISCOVERY_ROOT, env);
+ etcdInstance = new EtcdInstance();
+ etcdInstance.setInstanceId(registrationId.getInstanceId());
+ etcdInstance.setEnvironment(env);
+
etcdInstance.setApplication(BootStrapProperties.readApplication(environment));
+
etcdInstance.setServiceName(BootStrapProperties.readServiceName(environment));
+ etcdInstance.setAlias(BootStrapProperties.readServiceAlias(environment));
+
etcdInstance.setDescription(BootStrapProperties.readServiceDescription(environment));
+ if (StringUtils.isNotEmpty(dataCenterProperties.getName())) {
+ DataCenterInfo dataCenterInfo = new DataCenterInfo();
+ dataCenterInfo.setName(dataCenterProperties.getName());
+ dataCenterInfo.setRegion(dataCenterProperties.getRegion());
+ dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone());
+ etcdInstance.setDataCenterInfo(dataCenterInfo);
+ }
+
etcdInstance.setProperties(BootStrapProperties.readServiceProperties(environment));
+
etcdInstance.setVersion(BootStrapProperties.readServiceVersion(environment));
+ }
+
+ @Override
+ public void run() {
+ client =
Client.builder().endpoints(etcdRegistryProperties.getConnectString())
+ .build();
+ keyPath = basePath + "/" +
+ BootStrapProperties.readApplication(environment) + "/" +
+ BootStrapProperties.readServiceName(environment) + "/" +
+ registrationId.getInstanceId();
+
+ String valueJson = MuteExceptionUtil.builder().withLog("to json, key:{},
value:{}", keyPath, etcdInstance)
+ .executeFunction(JsonUtils::writeValueAsString, etcdInstance);
+ register(ByteSequence.from(keyPath, Charset.defaultCharset()),
+ ByteSequence.from(valueJson, Charset.defaultCharset()));
+ }
+
+ public void register(ByteSequence key, ByteSequence value) {
+
+ Lease leaseClient = client.getLeaseClient();
+ leaseId = MuteExceptionUtil.builder().withLog("get lease id, key:{},
value:{}", keyPath, etcdInstance)
+ .executeCompletableFuture(leaseClient.grant(60)).getID();
+ KV kvClient = client.getKVClient();
+
+ PutOption putOption = PutOption.builder().withLeaseId(leaseId).build();
+ CompletableFuture<PutResponse> putResponse = kvClient.put(key, value,
putOption);
+ putResponse.thenRun(() -> {
+ executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.scheduleAtFixedRate(
+ () -> MuteExceptionUtil.builder().withLog("reRegister, {}, {}",
keyPath, etcdInstance)
+ .executeFunction(leaseClient::keepAliveOnce, leaseId), 0, 5,
TimeUnit.SECONDS);
+ });
+ }
+
+ private void unregister() {
+ // close job
+ executorService.shutdownNow();
+
+ // close etcd node
+ Lease leaseClient = client.getLeaseClient();
+ leaseClient.revoke(leaseId);
+ client.getKVClient().delete(ByteSequence.from(keyPath,
Charset.defaultCharset()));
+ }
+
+ @Override
+ public void destroy() {
+ if (client != null) {
+ unregister();
+ client.close();
+ }
+ }
+}
diff --git
a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java
new file mode 100644
index 000000000..444fe8670
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java
@@ -0,0 +1,36 @@
+/*
+ * 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.servicecomb.registry.etcd;
+
+import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus;
+import org.apache.servicecomb.registry.api.RegistrationInstance;
+
+public class EtcdRegistrationInstance extends EtcdInstance implements
RegistrationInstance {
+ public EtcdRegistrationInstance(EtcdInstance instance) {
+ super(instance);
+ }
+
+ @Override
+ public MicroserviceInstanceStatus getInitialStatus() {
+ return MicroserviceInstanceStatus.STARTING;
+ }
+
+ @Override
+ public MicroserviceInstanceStatus getReadyStatus() {
+ return MicroserviceInstanceStatus.UP;
+ }
+}
diff --git
a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java
new file mode 100644
index 000000000..c4d61094b
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java
@@ -0,0 +1,99 @@
+/*
+ * 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.servicecomb.registry.etcd;
+
+public class EtcdRegistryProperties {
+ private boolean enabled = true;
+
+ private boolean ephemeral = true;
+
+ private String connectString = "http://127.0.0.1:2379";
+
+ private String authenticationSchema;
+
+ private String authenticationInfo;
+
+ private int connectionTimeoutMillis = 1000;
+
+ private int sessionTimeoutMillis = 60000;
+
+ private boolean enableSwaggerRegistration = false;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEphemeral() {
+ return ephemeral;
+ }
+
+ public void setEphemeral(boolean ephemeral) {
+ this.ephemeral = ephemeral;
+ }
+
+ public String getConnectString() {
+ return connectString;
+ }
+
+ public void setConnectString(String connectString) {
+ this.connectString = connectString;
+ }
+
+ public int getConnectionTimeoutMillis() {
+ return connectionTimeoutMillis;
+ }
+
+ public void setConnectionTimeoutMillis(int connectionTimeoutMillis) {
+ this.connectionTimeoutMillis = connectionTimeoutMillis;
+ }
+
+ public int getSessionTimeoutMillis() {
+ return sessionTimeoutMillis;
+ }
+
+ public void setSessionTimeoutMillis(int sessionTimeoutMillis) {
+ this.sessionTimeoutMillis = sessionTimeoutMillis;
+ }
+
+ public boolean isEnableSwaggerRegistration() {
+ return enableSwaggerRegistration;
+ }
+
+ public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) {
+ this.enableSwaggerRegistration = enableSwaggerRegistration;
+ }
+
+ public String getAuthenticationSchema() {
+ return authenticationSchema;
+ }
+
+ public void setAuthenticationSchema(String authenticationSchema) {
+ this.authenticationSchema = authenticationSchema;
+ }
+
+ public String getAuthenticationInfo() {
+ return authenticationInfo;
+ }
+
+ public void setAuthenticationInfo(String authenticationInfo) {
+ this.authenticationInfo = authenticationInfo;
+ }
+}
diff --git
a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/MuteExceptionUtil.java
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/MuteExceptionUtil.java
new file mode 100644
index 000000000..83041cacf
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/MuteExceptionUtil.java
@@ -0,0 +1,94 @@
+/*
+ * 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.servicecomb.registry.etcd;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MuteExceptionUtil {
+
+ interface FunctionWithException<T, R> {
+ R apply(T t) throws Exception;
+ }
+
+ interface FunctionWithDoubleParam<T1, T2, R> {
+ R apply(T1 t1, T2 t2) throws Exception;
+ }
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(MuteExceptionUtil.class);
+
+ public static class MuteExceptionUtilBuilder {
+
+ private String logMessage;
+
+ private Object[] customMessageParams;
+
+ public MuteExceptionUtilBuilder withLog(String message, Object... params) {
+ this.logMessage = message;
+ this.customMessageParams = params;
+ return this;
+ }
+
+ private String getLogMessage(String defaultMessage) {
+ return logMessage != null ? logMessage : defaultMessage;
+ }
+
+ // 执行带异常处理的Function
+ public <T, R> R executeFunction(FunctionWithException<T, R> function, T t)
{
+ try {
+ return function.apply(t);
+ } catch (Exception e) {
+ LOGGER.error(getLogMessage("execute Function failure..."),
customMessageParams, e);
+ return null;
+ }
+ }
+
+ public <T> T executeSupplier(Supplier<T> supplier) {
+ try {
+ return supplier.get();
+ } catch (Exception e) {
+ LOGGER.error(getLogMessage("execute Supplier failure..."),
customMessageParams, e);
+ return null;
+ }
+ }
+
+ public <T> T executeCompletableFuture(CompletableFuture<T>
completableFuture) {
+ try {
+ return completableFuture.get();
+ } catch (Exception e) {
+ LOGGER.error(getLogMessage("execute CompletableFuture failure..."),
customMessageParams, e);
+ return null;
+ }
+ }
+
+ public <T1, T2, R> R
executeFunctionWithDoubleParam(FunctionWithDoubleParam<T1, T2, R> function, T1
t1, T2 t2) {
+ try {
+ return function.apply(t1, t2);
+ } catch (Exception e) {
+ LOGGER.error(getLogMessage("execute FunctionWithDoubleParam
failure..."), customMessageParams, e);
+ return null;
+ }
+ }
+ }
+
+ public static MuteExceptionUtilBuilder builder() {
+ return new MuteExceptionUtilBuilder();
+ }
+}
diff --git
a/service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
b/service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..999ffb6b0
--- /dev/null
+++
b/service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+org.apache.servicecomb.registry.etcd.EtcdConfiguration