From 8abfdff4756b88a8a2a62ceeadf3d7f3ec879485 Mon Sep 17 00:00:00 2001 From: chenzhida Date: Sat, 12 Oct 2024 18:21:09 +0800 Subject: [PATCH 01/27] =?UTF-8?q?=E6=B7=BB=E5=8A=A0etcd=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E5=8F=91=E7=8E=B0=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dependencies/bom/pom.xml | 5 + pom.xml | 2 + service-registry/pom.xml | 1 + service-registry/registry-etcd/pom.xml | 52 +++++ .../registry/zookeeper/EtcdConfiguration.java | 40 ++++ .../registry/zookeeper/EtcdConst.java | 30 +++ .../registry/zookeeper/EtcdDiscovery.java | 146 ++++++++++++++ .../zookeeper/EtcdDiscoveryInstance.java | 36 ++++ .../registry/zookeeper/EtcdInstance.java | 190 ++++++++++++++++++ .../registry/zookeeper/EtcdRegistration.java | 189 +++++++++++++++++ .../zookeeper/EtcdRegistrationInstance.java | 36 ++++ .../zookeeper/EtcdRegistryProperties.java | 99 +++++++++ .../registry/zookeeper/MuteExceptionUtil.java | 44 ++++ ...ot.autoconfigure.AutoConfiguration.imports | 18 ++ test_consumer/pom.xml | 68 +++++++ .../servicecomb/ConsumerApplication.java | 33 +++ .../servicecomb/ConsumerController.java | 20 ++ .../apache/servicecomb/ConsumerService.java | 13 ++ .../apache/servicecomb/ProviderService.java | 13 ++ .../ProviderServiceConfiguration.java | 13 ++ .../src/main/resources/application.yml | 36 ++++ test_provider/pom.xml | 68 +++++++ .../servicecomb/ProviderApplication.java | 33 +++ .../servicecomb/ProviderController.java | 15 ++ .../apache/servicecomb/ProviderService.java | 13 ++ .../src/main/resources/application.yml | 36 ++++ 26 files changed, 1249 insertions(+) create mode 100644 service-registry/registry-etcd/pom.xml create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConfiguration.java create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConst.java create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscovery.java create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscoveryInstance.java create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdInstance.java create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistration.java create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistrationInstance.java create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistryProperties.java create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/MuteExceptionUtil.java create mode 100644 service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 test_consumer/pom.xml create mode 100644 test_consumer/src/main/java/org/apache/servicecomb/ConsumerApplication.java create mode 100644 test_consumer/src/main/java/org/apache/servicecomb/ConsumerController.java create mode 100644 test_consumer/src/main/java/org/apache/servicecomb/ConsumerService.java create mode 100644 test_consumer/src/main/java/org/apache/servicecomb/ProviderService.java create mode 100644 test_consumer/src/main/java/org/apache/servicecomb/ProviderServiceConfiguration.java create mode 100644 test_consumer/src/main/resources/application.yml create mode 100644 test_provider/pom.xml create mode 100644 test_provider/src/main/java/org/apache/servicecomb/ProviderApplication.java create mode 100644 test_provider/src/main/java/org/apache/servicecomb/ProviderController.java create mode 100644 test_provider/src/main/java/org/apache/servicecomb/ProviderService.java create mode 100644 test_provider/src/main/resources/application.yml diff --git a/dependencies/bom/pom.xml b/dependencies/bom/pom.xml index 068a29ca27f..4a9be76e00b 100644 --- a/dependencies/bom/pom.xml +++ b/dependencies/bom/pom.xml @@ -278,6 +278,11 @@ registry-zookeeper ${project.version} + + org.apache.servicecomb + registry-etcd + ${project.version} + org.apache.servicecomb diff --git a/pom.xml b/pom.xml index 28e42707f21..4489aa69f14 100644 --- a/pom.xml +++ b/pom.xml @@ -154,6 +154,8 @@ clients governance huawei-cloud + test_provider + test_consumer diff --git a/service-registry/pom.xml b/service-registry/pom.xml index 0fb0f347586..213f0b0f2d6 100644 --- a/service-registry/pom.xml +++ b/service-registry/pom.xml @@ -37,5 +37,6 @@ registry-zero-config registry-nacos registry-zookeeper + registry-etcd diff --git a/service-registry/registry-etcd/pom.xml b/service-registry/registry-etcd/pom.xml new file mode 100644 index 00000000000..1beac21bcbe --- /dev/null +++ b/service-registry/registry-etcd/pom.xml @@ -0,0 +1,52 @@ + + + + + + org.apache.servicecomb + service-registry-parent + 3.3.0-SNAPSHOT + + 4.0.0 + + registry-etcd + Java Chassis::Service Registry::Etcd + + + + io.etcd + jetcd-core + 0.8.3 + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + + \ No newline at end of file diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConfiguration.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConfiguration.java new file mode 100644 index 00000000000..43c33075dac --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/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.zookeeper; + +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 zookeeperRegistryProperties() { + return new EtcdRegistryProperties(); + } + + @Bean + public EtcdDiscovery zookeeperDiscovery() { + return new EtcdDiscovery(); + } + + @Bean + public EtcdRegistration zookeeperRegistration() { + return new EtcdRegistration(); + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConst.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConst.java new file mode 100644 index 00000000000..6fc6b6b0205 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/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.zookeeper; + +public class EtcdConst { + + public static final String ETCD_REGISTRY_NAME = "etcd-registry"; + + public static final String ETCD_DISCOVERY_ROOT = "/servicecomb/etcd/%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/zookeeper/EtcdDiscovery.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscovery.java new file mode 100644 index 00000000000..33a4b45b8ee --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscovery.java @@ -0,0 +1,146 @@ +/* + * 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.zookeeper; + +import com.google.common.collect.Lists; +import io.etcd.jetcd.*; +import io.etcd.jetcd.kv.GetResponse; +import io.etcd.jetcd.options.GetOption; +import io.etcd.jetcd.options.WatchOption; +import io.etcd.jetcd.watch.WatchEvent; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +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 org.springframework.stereotype.Service; + +import java.nio.charset.Charset; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +public class EtcdDiscovery implements Discovery { + + private Environment environment; + + private String basePath; + + private EtcdRegistryProperties etcdRegistryProperties; + + private Client client; + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDiscovery.class); + + private InstanceChangedListener instanceChangedListener; + + @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 findServiceInstances(String application, String serviceName) { + + KV kvClient = client.getKVClient(); + String path = basePath + "/" + application; + GetResponse response = MuteExceptionUtil.executeCompletableFuture( + kvClient.get(ByteSequence.from(path, Charset.defaultCharset()), + GetOption.builder().build())); + Watch watchClient = client.getWatchClient(); + watchClient.watch(ByteSequence.from(path, Charset.defaultCharset()), + WatchOption.newBuilder().build(), + resp -> { + List keyValueList = resp.getEvents().stream().map(WatchEvent::getKeyValue).toList(); + instanceChangedListener.onInstanceChanged(name(), application, serviceName, convertServiceInstanceList(keyValueList)); + } + ); + + return convertServiceInstanceList(response.getKvs()); + } + + private List convertServiceInstanceList(List keyValueList) { + + List list = Lists.newArrayListWithExpectedSize(keyValueList.size()); + for (KeyValue keyValue : keyValueList) { + String valueJson = new String(keyValue.getValue().getBytes()); + EtcdDiscoveryInstance etcdDiscoveryInstance = JsonUtils.convertValue(valueJson, EtcdDiscoveryInstance.class); + list.add(etcdDiscoveryInstance); + } + return list; + } + +// todo + @Override + public List findServices(String application) { + + return List.of(); + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener 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() { + client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) + .build(); + } + + @Override + public void destroy() { + if (client != null) { + client.close(); + } + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscoveryInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscoveryInstance.java new file mode 100644 index 00000000000..2744ac82196 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscoveryInstance.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.zookeeper; + +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/zookeeper/EtcdInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdInstance.java new file mode 100644 index 00000000000..513a9b644a9 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdInstance.java @@ -0,0 +1,190 @@ +/* + * 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.zookeeper; + +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 endpoints = new ArrayList<>(); + + private Map schemas = new HashMap<>(); + + private Map 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 endpoints) { + this.endpoints = endpoints; + } + + public void setSchemas(Map schemas) { + this.schemas = schemas; + } + + public void setProperties(Map 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 getProperties() { + return properties; + } + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public List 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; + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistration.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistration.java new file mode 100644 index 00000000000..48f1d797801 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistration.java @@ -0,0 +1,189 @@ +/* + * 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.zookeeper; + +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; +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 java.nio.charset.Charset; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class EtcdRegistration implements Registration { + + 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) { + 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 false; + } + + @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) + "/" + registrationId.getInstanceId(); + register(ByteSequence.from(keyPath , Charset.defaultCharset()), + ByteSequence.from(Objects.requireNonNull(MuteExceptionUtil.executeFunction(JsonUtils::writeValueAsString, etcdInstance)), Charset.defaultCharset())); + } + + public void register(ByteSequence key, ByteSequence value) { + + Lease leaseClient = client.getLeaseClient(); + leaseId = MuteExceptionUtil.executeCompletableFuture(leaseClient.grant(60)).getID(); + KV kvClient = client.getKVClient(); + + PutOption putOption = PutOption.builder().withLeaseId(leaseId).build(); + CompletableFuture putResponse = kvClient.put(key, value, putOption); + putResponse.thenRun(() -> { + executorService = Executors.newSingleThreadScheduledExecutor(); + executorService.scheduleAtFixedRate(() -> { + MuteExceptionUtil.executeFunction(leaseClient::keepAliveOnce, leaseId); + }, 0, 5, TimeUnit.SECONDS); + }); + } + + public void unregister() { + // 关闭定时任务 + executorService.shutdownNow(); + // 撤销租约,自动删除临时节点 + 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(); + } + } +} \ No newline at end of file diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistrationInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistrationInstance.java new file mode 100644 index 00000000000..32e49c906c3 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/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.zookeeper; + +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/zookeeper/EtcdRegistryProperties.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistryProperties.java new file mode 100644 index 00000000000..9efda2c8069 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/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.zookeeper; + +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/zookeeper/MuteExceptionUtil.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/MuteExceptionUtil.java new file mode 100644 index 00000000000..bc443b46f57 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/MuteExceptionUtil.java @@ -0,0 +1,44 @@ +package org.apache.servicecomb.registry.zookeeper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class MuteExceptionUtil { + + interface FunctionWithException { + R apply(T t) throws Exception; + } + + private static final Logger LOGGER = LoggerFactory.getLogger(MuteExceptionUtil.class); + + public static R executeFunction(FunctionWithException function, T t) { + try { + return function.apply(t); + } catch (Exception e) { + LOGGER.error("execute Function failure..."); + return null; + } + } + + public static T executeSupplier(Supplier supplier) { + try { + return supplier.get(); + } catch (Exception e) { + LOGGER.error("execute Supplier failure..."); + return null; + } + } + + public static T executeCompletableFuture(CompletableFuture completableFuture) { + try { + return completableFuture.get(); + } catch (Exception e) { + LOGGER.error("execute CompletableFuture failure..."); + return null; + } + } + +} 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 00000000000..d3b128cf90d --- /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.zookeeper.EtcdConfiguration diff --git a/test_consumer/pom.xml b/test_consumer/pom.xml new file mode 100644 index 00000000000..569e1b7be0d --- /dev/null +++ b/test_consumer/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + org.apache.servicecomb + java-chassis + 3.3.0-SNAPSHOT + + + test_consumer + + + 17 + 17 + UTF-8 + + 3.3.0-SNAPSHOT + + + + + + org.apache.servicecomb + java-chassis-dependencies + ${java-chassis-dependencies.version} + pom + import + + + + + + + + + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + com.google.protobuf + protobuf-java + 3.25.3 + runtime + + + org.apache.servicecomb + registry-etcd + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + + + \ No newline at end of file diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ConsumerApplication.java b/test_consumer/src/main/java/org/apache/servicecomb/ConsumerApplication.java new file mode 100644 index 00000000000..9c9f6e3135c --- /dev/null +++ b/test_consumer/src/main/java/org/apache/servicecomb/ConsumerApplication.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; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ConsumerApplication { + public static void main(String[] args) { + try { + new SpringApplicationBuilder(ConsumerApplication.class).web(WebApplicationType.NONE).run(args); + } catch (Throwable e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ConsumerController.java b/test_consumer/src/main/java/org/apache/servicecomb/ConsumerController.java new file mode 100644 index 00000000000..c99e27884c2 --- /dev/null +++ b/test_consumer/src/main/java/org/apache/servicecomb/ConsumerController.java @@ -0,0 +1,20 @@ +package org.apache.servicecomb; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.annotation.Autowired; + +@RestSchema(schemaId = "ConsumerController", schemaInterface = ConsumerService.class) +public class ConsumerController implements ConsumerService { + + private ProviderService providerService; + + @Autowired + public void setProviderService(ProviderService providerService) { + this.providerService = providerService; + } + + @Override + public String sayHello(String name) { + return providerService.sayHello(name); + } +} \ No newline at end of file diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ConsumerService.java b/test_consumer/src/main/java/org/apache/servicecomb/ConsumerService.java new file mode 100644 index 00000000000..092772915c4 --- /dev/null +++ b/test_consumer/src/main/java/org/apache/servicecomb/ConsumerService.java @@ -0,0 +1,13 @@ + +package org.apache.servicecomb; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RequestMapping(path = "/consumer") +public interface ConsumerService { + + @GetMapping("/sayHello") + String sayHello(@RequestParam("name") String name); +} \ No newline at end of file diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ProviderService.java b/test_consumer/src/main/java/org/apache/servicecomb/ProviderService.java new file mode 100644 index 00000000000..20c191392e3 --- /dev/null +++ b/test_consumer/src/main/java/org/apache/servicecomb/ProviderService.java @@ -0,0 +1,13 @@ + +package org.apache.servicecomb; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RequestMapping(path = "/provider") +public interface ProviderService { + + @GetMapping("/sayHello") + String sayHello(@RequestParam("name") String name); +} \ No newline at end of file diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ProviderServiceConfiguration.java b/test_consumer/src/main/java/org/apache/servicecomb/ProviderServiceConfiguration.java new file mode 100644 index 00000000000..fc8393445c3 --- /dev/null +++ b/test_consumer/src/main/java/org/apache/servicecomb/ProviderServiceConfiguration.java @@ -0,0 +1,13 @@ +package org.apache.servicecomb; + +import org.apache.servicecomb.provider.pojo.Invoker; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ProviderServiceConfiguration { + @Bean + public ProviderService providerService() { + return Invoker.createProxy("provider", "ProviderController", ProviderService.class); + } +} \ No newline at end of file diff --git a/test_consumer/src/main/resources/application.yml b/test_consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..a3f45994acf --- /dev/null +++ b/test_consumer/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: basic-application + name: consumer + version: 0.0.1 + registry: + etcd: + enabled: true + connectString: http://127.0.0.1:2379 +# zk: +# enabled: true +# connectString: 127.0.0.1:2181 +# config: +# zk: +# enabled: true +# connectString: 127.0.0.1:2181 + rest: + address: 0.0.0.0:9092 + diff --git a/test_provider/pom.xml b/test_provider/pom.xml new file mode 100644 index 00000000000..c8b1d05e0d4 --- /dev/null +++ b/test_provider/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + org.apache.servicecomb + java-chassis + 3.3.0-SNAPSHOT + + + test_provider + + + 17 + 17 + UTF-8 + + 3.3.0-SNAPSHOT + + + + + + org.apache.servicecomb + java-chassis-dependencies + ${java-chassis-dependencies.version} + pom + import + + + + + + + org.apache.servicecomb + solution-basic + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + com.google.protobuf + protobuf-java + 3.25.3 + runtime + + + org.apache.servicecomb + registry-etcd + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + + + \ No newline at end of file diff --git a/test_provider/src/main/java/org/apache/servicecomb/ProviderApplication.java b/test_provider/src/main/java/org/apache/servicecomb/ProviderApplication.java new file mode 100644 index 00000000000..247885cb3a1 --- /dev/null +++ b/test_provider/src/main/java/org/apache/servicecomb/ProviderApplication.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; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ProviderApplication { + public static void main(String[] args) { + try { + new SpringApplicationBuilder(ProviderApplication.class).web(WebApplicationType.NONE).run(args); + } catch (Throwable e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/test_provider/src/main/java/org/apache/servicecomb/ProviderController.java b/test_provider/src/main/java/org/apache/servicecomb/ProviderController.java new file mode 100644 index 00000000000..08b39a61094 --- /dev/null +++ b/test_provider/src/main/java/org/apache/servicecomb/ProviderController.java @@ -0,0 +1,15 @@ +package org.apache.servicecomb; + +import org.apache.servicecomb.config.DynamicProperties; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.annotation.Autowired; + +@RestSchema(schemaId = "ProviderController", schemaInterface = ProviderService.class) +public class ProviderController implements ProviderService { + + @Override + public String sayHello(String name) { + return "Hello " + name; + } + +} \ No newline at end of file diff --git a/test_provider/src/main/java/org/apache/servicecomb/ProviderService.java b/test_provider/src/main/java/org/apache/servicecomb/ProviderService.java new file mode 100644 index 00000000000..20c191392e3 --- /dev/null +++ b/test_provider/src/main/java/org/apache/servicecomb/ProviderService.java @@ -0,0 +1,13 @@ + +package org.apache.servicecomb; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RequestMapping(path = "/provider") +public interface ProviderService { + + @GetMapping("/sayHello") + String sayHello(@RequestParam("name") String name); +} \ No newline at end of file diff --git a/test_provider/src/main/resources/application.yml b/test_provider/src/main/resources/application.yml new file mode 100644 index 00000000000..5000bc7883f --- /dev/null +++ b/test_provider/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: basic-application + name: provider + version: 0.0.1 + registry: + etcd: + enabled: true + connectString: http://127.0.0.1:2379 +# zk: +# enabled: true +# connectString: 127.0.0.1:2181 +# config: +# zk: +# enabled: true +# connectString: 127.0.0.1:2181 + rest: + address: 0.0.0.0:9091 + From ee26b4c1e92f9e5f095f070d78f24deef1b3f01e Mon Sep 17 00:00:00 2001 From: chenzhida <184120172@qq.com> Date: Mon, 14 Oct 2024 19:43:51 +0800 Subject: [PATCH 02/27] feat(registry-etcd,demo-etcd): implement the basic discovery function through etcd service registration, and add a demo to test etcd - Implement the method of finding all instances of EtcdDiscovery - Implement the registered instance information of EtcdRegistration to etcd - Implement silent exception information printing, see MuteExceptionUtil for details --- demo/demo-etcd/README.md | 6 + demo/demo-etcd/consumer/pom.xml | 88 ++++++++ .../samples/ClientWebsocketController.java | 54 +++++ .../samples/ConsumerController.java | 42 ++++ .../ConsumerHeaderParamWithListSchema.java | 55 +++++ .../ConsumerReactiveStreamController.java | 84 +++++++ .../samples/EtcdConsumerApplication.java | 12 +- .../servicecomb/samples/ProviderService.java | 24 ++ .../src/main/resources/application.yml | 27 ++- .../consumer/src/main/resources/log4j2.xml | 41 ++++ demo/demo-etcd/gateway/pom.xml | 85 +++++++ .../samples/GatewayApplication.java | 12 +- .../src/main/resources/application.yml | 53 +++++ .../gateway/src/main/resources/log4j2.xml | 41 ++++ demo/demo-etcd/pom.xml | 61 +++++ demo/demo-etcd/provider/pom.xml | 101 +++++++++ .../samples/EtcdProviderApplication.java | 33 +++ .../samples/HeaderParamWithListSchema.java | 51 +++++ .../samples/ProviderController.java | 52 +++++ .../samples/ReactiveStreamController.java | 78 +++++++ .../samples/WebsocketController.java | 67 ++++++ .../src/main/resources/application.yml | 47 ++++ .../provider/src/main/resources/log4j2.xml | 41 ++++ demo/demo-etcd/test-client/pom.xml | 209 ++++++++++++++++++ .../apache/servicecomb/samples/Config.java | 22 ++ .../samples/HeaderParamWithListSchemaIT.java | 98 ++++++++ .../servicecomb/samples/HelloWorldIT.java | 68 ++++++ .../servicecomb/samples/ReactiveStreamIT.java | 119 ++++++++++ .../samples/TestClientApplication.java | 49 ++++ .../samples/ThirdSvcConfiguration.java | 125 +++++++++++ .../servicecomb/samples/WebsocketIT.java | 57 +++++ .../src/main/resources/application.yml | 19 +- .../test-client/src/main/resources/log4j2.xml | 41 ++++ .../servicecomb/samples/ZookeeperIT.java | 53 +++++ demo/pom.xml | 2 +- pom.xml | 2 - service-registry/registry-etcd/pom.xml | 1 + .../EtcdConfiguration.java | 2 +- .../{zookeeper => etcd}/EtcdConst.java | 2 +- .../{zookeeper => etcd}/EtcdDiscovery.java | 33 ++- .../EtcdDiscoveryInstance.java | 3 +- .../{zookeeper => etcd}/EtcdInstance.java | 20 +- .../{zookeeper => etcd}/EtcdRegistration.java | 13 +- .../EtcdRegistrationInstance.java | 2 +- .../EtcdRegistryProperties.java | 2 +- .../registry/etcd/MuteExceptionUtil.java | 87 ++++++++ .../registry/zookeeper/MuteExceptionUtil.java | 44 ---- ...ot.autoconfigure.AutoConfiguration.imports | 2 +- test_consumer/pom.xml | 68 ------ .../servicecomb/ConsumerController.java | 20 -- .../apache/servicecomb/ConsumerService.java | 13 -- .../apache/servicecomb/ProviderService.java | 13 -- .../ProviderServiceConfiguration.java | 13 -- test_provider/pom.xml | 68 ------ .../servicecomb/ProviderController.java | 15 -- .../apache/servicecomb/ProviderService.java | 13 -- 56 files changed, 2121 insertions(+), 332 deletions(-) create mode 100644 demo/demo-etcd/README.md create mode 100644 demo/demo-etcd/consumer/pom.xml create mode 100644 demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java create mode 100644 demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java create mode 100644 demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java create mode 100644 demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java rename test_consumer/src/main/java/org/apache/servicecomb/ConsumerApplication.java => demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java (78%) create mode 100644 demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java rename {test_consumer => demo/demo-etcd/consumer}/src/main/resources/application.yml (80%) create mode 100644 demo/demo-etcd/consumer/src/main/resources/log4j2.xml create mode 100644 demo/demo-etcd/gateway/pom.xml rename test_provider/src/main/java/org/apache/servicecomb/ProviderApplication.java => demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java (79%) create mode 100644 demo/demo-etcd/gateway/src/main/resources/application.yml create mode 100644 demo/demo-etcd/gateway/src/main/resources/log4j2.xml create mode 100644 demo/demo-etcd/pom.xml create mode 100644 demo/demo-etcd/provider/pom.xml create mode 100644 demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java create mode 100644 demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java create mode 100644 demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java create mode 100644 demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java create mode 100644 demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java create mode 100644 demo/demo-etcd/provider/src/main/resources/application.yml create mode 100644 demo/demo-etcd/provider/src/main/resources/log4j2.xml create mode 100644 demo/demo-etcd/test-client/pom.xml create mode 100644 demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java create mode 100644 demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java create mode 100644 demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java create mode 100644 demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java create mode 100644 demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java create mode 100644 demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java create mode 100644 demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java rename {test_provider => demo/demo-etcd/test-client}/src/main/resources/application.yml (76%) create mode 100644 demo/demo-etcd/test-client/src/main/resources/log4j2.xml create mode 100644 demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java rename service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/{zookeeper => etcd}/EtcdConfiguration.java (96%) rename service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/{zookeeper => etcd}/EtcdConst.java (95%) rename service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/{zookeeper => etcd}/EtcdDiscovery.java (77%) rename service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/{zookeeper => etcd}/EtcdDiscoveryInstance.java (96%) rename service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/{zookeeper => etcd}/EtcdInstance.java (86%) rename service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/{zookeeper => etcd}/EtcdRegistration.java (90%) rename service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/{zookeeper => etcd}/EtcdRegistrationInstance.java (96%) rename service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/{zookeeper => etcd}/EtcdRegistryProperties.java (98%) create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/MuteExceptionUtil.java delete mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/MuteExceptionUtil.java delete mode 100644 test_consumer/pom.xml delete mode 100644 test_consumer/src/main/java/org/apache/servicecomb/ConsumerController.java delete mode 100644 test_consumer/src/main/java/org/apache/servicecomb/ConsumerService.java delete mode 100644 test_consumer/src/main/java/org/apache/servicecomb/ProviderService.java delete mode 100644 test_consumer/src/main/java/org/apache/servicecomb/ProviderServiceConfiguration.java delete mode 100644 test_provider/pom.xml delete mode 100644 test_provider/src/main/java/org/apache/servicecomb/ProviderController.java delete mode 100644 test_provider/src/main/java/org/apache/servicecomb/ProviderService.java diff --git a/demo/demo-etcd/README.md b/demo/demo-etcd/README.md new file mode 100644 index 00000000000..8e0978c8d7d --- /dev/null +++ b/demo/demo-etcd/README.md @@ -0,0 +1,6 @@ +# Notice + +This integration tests is designed for Zookeeper 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 00000000000..44fb2fccc7f --- /dev/null +++ b/demo/demo-etcd/consumer/pom.xml @@ -0,0 +1,88 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.3.0-SNAPSHOT + + + etcd-consumer + Java Chassis::Demo::Etcd::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + com.google.protobuf + protobuf-java + 3.25.3 + runtime + + + org.apache.servicecomb + registry-etcd + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + io.fabric8 + docker-maven-plugin + + + org.commonjava.maven.plugins + directory-maven-plugin + + + com.github.odavid.maven.plugins + mixin-maven-plugin + + + + org.apache.servicecomb.demo + docker-build-config + ${project.version} + + + + + + + + + \ No newline at end of file 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 00000000000..76fb2fb17db --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java @@ -0,0 +1,54 @@ +/* + * 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 -> { + System.out.println("send message " + m); + serverWebsocket.writeTextMessage(m); + }); + serverWebsocket.textMessageHandler(m -> { + System.out.println("receive message " + 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 00000000000..e5dd86d9d64 --- /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 00000000000..f0e894d6745 --- /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 headerList) { + return provider.headerListDefault(headerList); + } + + @Override + public String headerListCSV(List headerList) { + return provider.headerListCSV(headerList); + } + + @Override + public String headerListMULTI(List headerList) { + return provider.headerListMULTI(headerList); + } + + @Override + public String headerListSSV(List headerList) { + return provider.headerListSSV(headerList); + } + + @Override + public String headerListPIPES(List 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 00000000000..aa169801cf7 --- /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 sseString(); + + Publisher 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 sseString() { + return controller.sseString(); + } + + @GetMapping("/sseModel") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseModel() { + return controller.sseModel(); + } +} diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ConsumerApplication.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java similarity index 78% rename from test_consumer/src/main/java/org/apache/servicecomb/ConsumerApplication.java rename to demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java index 9c9f6e3135c..2101e6512f8 100644 --- a/test_consumer/src/main/java/org/apache/servicecomb/ConsumerApplication.java +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java @@ -15,19 +15,19 @@ * limitations under the License. */ -package org.apache.servicecomb; +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 ConsumerApplication { - public static void main(String[] args) { +public class EtcdConsumerApplication { + public static void main(String[] args) throws Exception { try { - new SpringApplicationBuilder(ConsumerApplication.class).web(WebApplicationType.NONE).run(args); - } catch (Throwable e) { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(EtcdConsumerApplication.class).run(args); + } catch (Exception e) { e.printStackTrace(); } } -} \ No newline at end of file +} 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 00000000000..fe71314c9f4 --- /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/test_consumer/src/main/resources/application.yml b/demo/demo-etcd/consumer/src/main/resources/application.yml similarity index 80% rename from test_consumer/src/main/resources/application.yml rename to demo/demo-etcd/consumer/src/main/resources/application.yml index a3f45994acf..b89835a883c 100644 --- a/test_consumer/src/main/resources/application.yml +++ b/demo/demo-etcd/consumer/src/main/resources/application.yml @@ -17,20 +17,23 @@ ## --------------------------------------------------------------------------- servicecomb: service: - application: basic-application - name: consumer + application: demo-etcd version: 0.0.1 + name: consumer + properties: + group: red registry: - etcd: + zk: enabled: true - connectString: http://127.0.0.1:2379 -# zk: -# enabled: true -# connectString: 127.0.0.1:2181 -# config: -# zk: -# enabled: true -# connectString: 127.0.0.1:2181 + connectString: 127.0.0.1:2181 + rest: - address: 0.0.0.0:9092 + address: 0.0.0.0:9092?websocketEnabled=true + server: + websocket-prefix: /ws + + highway: + address: 0.0.0.0:7092 + + 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 00000000000..6e6586771bb --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/demo-etcd/gateway/pom.xml b/demo/demo-etcd/gateway/pom.xml new file mode 100644 index 00000000000..4174a08e308 --- /dev/null +++ b/demo/demo-etcd/gateway/pom.xml @@ -0,0 +1,85 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.3.0-SNAPSHOT + + + etcd-gateway + Java Chassis::Demo::Etcd::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + org.apache.servicecomb + registry-etcd + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + io.fabric8 + docker-maven-plugin + + + org.commonjava.maven.plugins + directory-maven-plugin + + + com.github.odavid.maven.plugins + mixin-maven-plugin + + + + org.apache.servicecomb.demo + docker-build-config + ${project.version} + + + + + + + + + \ No newline at end of file diff --git a/test_provider/src/main/java/org/apache/servicecomb/ProviderApplication.java b/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java similarity index 79% rename from test_provider/src/main/java/org/apache/servicecomb/ProviderApplication.java rename to demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java index 247885cb3a1..7d58caafd9d 100644 --- a/test_provider/src/main/java/org/apache/servicecomb/ProviderApplication.java +++ b/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -15,19 +15,19 @@ * limitations under the License. */ -package org.apache.servicecomb; +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 ProviderApplication { - public static void main(String[] args) { +public class GatewayApplication { + public static void main(String[] args) throws Exception { try { - new SpringApplicationBuilder(ProviderApplication.class).web(WebApplicationType.NONE).run(args); - } catch (Throwable e) { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); + } catch (Exception e) { e.printStackTrace(); } } -} \ No newline at end of file +} 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 00000000000..f361091d81b --- /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: + zk: + enabled: true + connectString: 127.0.0.1:2181 + + 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 00000000000..6e6586771bb --- /dev/null +++ b/demo/demo-etcd/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/demo-etcd/pom.xml b/demo/demo-etcd/pom.xml new file mode 100644 index 00000000000..0cb08ff220c --- /dev/null +++ b/demo/demo-etcd/pom.xml @@ -0,0 +1,61 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-parent + 3.3.0-SNAPSHOT + + demo-etcd + Java Chassis::Demo::Etcd + pom + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + solution-basic + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + + + provider + consumer + gateway + test-client + + + \ No newline at end of file diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml new file mode 100644 index 00000000000..19a3ecb2abb --- /dev/null +++ b/demo/demo-etcd/provider/pom.xml @@ -0,0 +1,101 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.3.0-SNAPSHOT + + + etcd-provider + Java Chassis::Demo::Etcd::PROVIDER + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + com.google.protobuf + protobuf-java + 3.25.3 + runtime + + + org.apache.servicecomb + registry-etcd + + + + + + + + io.reactivex.rxjava3 + rxjava + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + io.fabric8 + docker-maven-plugin + + + org.commonjava.maven.plugins + directory-maven-plugin + + + com.github.odavid.maven.plugins + mixin-maven-plugin + + + + org.apache.servicecomb.demo + docker-build-config + ${project.version} + + + + + + + + + \ No newline at end of file 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 00000000000..388b962dc61 --- /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 00000000000..8a773f91c72 --- /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 headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListCSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListMULTI(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListSSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListPIPES(List 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 00000000000..8e6388d35e4 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.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.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 " + 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 00000000000..8108d15fd46 --- /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 sseString() { + return Flowable.fromArray("a", "b", "c"); + } + + @GetMapping("/sseModel") + public Publisher 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 00000000000..5f4f9719ca8 --- /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 00000000000..9ef35f3e345 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/resources/application.yml @@ -0,0 +1,47 @@ +# +## --------------------------------------------------------------------------- +## 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: + zk: + connectString: 127.0.0.1:2181 + config: + zk: + connectString: 127.0.0.1:2181 + instance-tag: config-demo + + rest: + address: 0.0.0.0:9094?websocketEnabled=true + server: + websocket-prefix: /ws + + highway: + address: 0.0.0.0:7094 + + cors: + enabled: true + origin: "*" + allowCredentials: false + allowedMethod: "*" + maxAge: 3600 \ No newline at end of file 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 00000000000..6e6586771bb --- /dev/null +++ b/demo/demo-etcd/provider/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml new file mode 100644 index 00000000000..985df6836a2 --- /dev/null +++ b/demo/demo-etcd/test-client/pom.xml @@ -0,0 +1,209 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.3.0-SNAPSHOT + + + etcd-test-client + Java Chassis::Demo::Etcd::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-local + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + zookeeper:3.8.3 + zookeeper + + alias + + binding to port + + + 2181 + + + + + + zookeeper.port:2181 + + + + + etcd-provider:${project.version} + etcd-provider + + alias + + + -Dservicecomb.registry.zk.connectString=zookeeper:2181 -Dservicecomb.config.zk.connectString=zookeeper:2181 + + /maven/maven/etcd-provider-${project.version}.jar + + + zookeeper:zookeeper + + + ServiceComb is ready + + + 9094 + + + + + + 9094:9094 + + + + + etcd-consumer:${project.version} + etcd-consumer + + alias + + + -Dservicecomb.registry.zk.connectString=zookeeper:2181 + + /maven/maven/etcd-consumer-${project.version}.jar + + + zookeeper:zookeeper + + + ServiceComb is ready + + + 9092 + + + + + + 9092:9092 + + + + + etcd-gateway:${project.version} + etcd-gateway + + alias + + + -Dservicecomb.registry.zk.connectString=zookeeper:2181 + + /maven/maven/etcd-gateway-${project.version}.jar + + + zookeeper:zookeeper + + + ServiceComb is ready + + + 9090 + + + + + + 9090:9090 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + + io.fabric8 + docker-maven-plugin + + + com.github.odavid.maven.plugins + mixin-maven-plugin + + + + org.apache.servicecomb.demo + docker-run-config + ${project.version} + + + + + + + + + \ No newline at end of file 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 00000000000..2e9105cdd4a --- /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 00000000000..1b129acc2c6 --- /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 headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity 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 headers = new HttpHeaders(); + headers.add("headerList", "a|b|c"); + HttpEntity 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 headers = new HttpHeaders(); + headers.add("headerList", "a b c"); + HttpEntity 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 headers = new HttpHeaders(); + headers.add("headerList", "a,b,c"); + HttpEntity 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 headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity 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 00000000000..97e883fb45f --- /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 headers = new HttpHeaders(); + headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); + HttpEntity entity = new HttpEntity(headers); + ResponseEntity 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 00000000000..488a6cf712a --- /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 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 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 00000000000..26a2a491bbe --- /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 00000000000..3ee5db8c346 --- /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 sseString(); + + @GetMapping("/sseModel") + Publisher 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 00000000000..ea7a7f45b15 --- /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/test_provider/src/main/resources/application.yml b/demo/demo-etcd/test-client/src/main/resources/application.yml similarity index 76% rename from test_provider/src/main/resources/application.yml rename to demo/demo-etcd/test-client/src/main/resources/application.yml index 5000bc7883f..396bebfd853 100644 --- a/test_provider/src/main/resources/application.yml +++ b/demo/demo-etcd/test-client/src/main/resources/application.yml @@ -17,20 +17,9 @@ ## --------------------------------------------------------------------------- servicecomb: service: - application: basic-application - name: provider + application: demo-etcd + name: test-client version: 0.0.1 - registry: - etcd: - enabled: true - connectString: http://127.0.0.1:2379 -# zk: -# enabled: true -# connectString: 127.0.0.1:2181 -# config: -# zk: -# enabled: true -# connectString: 127.0.0.1:2181 - rest: - address: 0.0.0.0:9091 + 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 00000000000..6e6586771bb --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java new file mode 100644 index 00000000000..549cfe2a300 --- /dev/null +++ b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.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 ZookeeperIT { + private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperIT.class); + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + 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 bfdd595efaa..c878c5b1883 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -56,7 +56,7 @@ demo-cse-v1 demo-cse-v2 demo-nacos - demo-zookeeper + demo-etcd diff --git a/pom.xml b/pom.xml index 4489aa69f14..28e42707f21 100644 --- a/pom.xml +++ b/pom.xml @@ -154,8 +154,6 @@ clients governance huawei-cloud - test_provider - test_consumer diff --git a/service-registry/registry-etcd/pom.xml b/service-registry/registry-etcd/pom.xml index 1beac21bcbe..599d1892eff 100644 --- a/service-registry/registry-etcd/pom.xml +++ b/service-registry/registry-etcd/pom.xml @@ -30,6 +30,7 @@ Java Chassis::Service Registry::Etcd + io.etcd jetcd-core diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConfiguration.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java similarity index 96% rename from service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConfiguration.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java index 43c33075dac..27684536c8b 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConfiguration.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.registry.zookeeper; +package org.apache.servicecomb.registry.etcd; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConst.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java similarity index 95% rename from service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConst.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java index 6fc6b6b0205..1cb0a22a8e1 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdConst.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.registry.zookeeper; +package org.apache.servicecomb.registry.etcd; public class EtcdConst { diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscovery.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java similarity index 77% rename from service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscovery.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java index 33a4b45b8ee..2394d549522 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscovery.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.registry.zookeeper; +package org.apache.servicecomb.registry.etcd; import com.google.common.collect.Lists; import io.etcd.jetcd.*; @@ -30,12 +30,11 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; -import org.springframework.stereotype.Service; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; +import java.util.concurrent.CompletableFuture; public class EtcdDiscovery implements Discovery { @@ -77,21 +76,28 @@ public boolean enabled(String application, String serviceName) { @Override public List findServiceInstances(String application, String serviceName) { - KV kvClient = client.getKVClient(); - String path = basePath + "/" + application; - GetResponse response = MuteExceptionUtil.executeCompletableFuture( - kvClient.get(ByteSequence.from(path, Charset.defaultCharset()), - GetOption.builder().build())); + String prefixPath = basePath + "/" + application; Watch watchClient = client.getWatchClient(); - watchClient.watch(ByteSequence.from(path, Charset.defaultCharset()), + watchClient.watch(ByteSequence.from(prefixPath, Charset.defaultCharset()), WatchOption.newBuilder().build(), resp -> { List keyValueList = resp.getEvents().stream().map(WatchEvent::getKeyValue).toList(); instanceChangedListener.onInstanceChanged(name(), application, serviceName, convertServiceInstanceList(keyValueList)); } ); + List endpointKv = getValuesByPrefix(prefixPath); + return convertServiceInstanceList(endpointKv); + } + + public List getValuesByPrefix(String prefix) { - return convertServiceInstanceList(response.getKvs()); + CompletableFuture getFuture = client.getKVClient().get( + ByteSequence.from(prefix, StandardCharsets.UTF_8), + GetOption.newBuilder().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 convertServiceInstanceList(List keyValueList) { @@ -99,7 +105,10 @@ private List convertServiceInstanceList(List ke List list = Lists.newArrayListWithExpectedSize(keyValueList.size()); for (KeyValue keyValue : keyValueList) { String valueJson = new String(keyValue.getValue().getBytes()); - EtcdDiscoveryInstance etcdDiscoveryInstance = JsonUtils.convertValue(valueJson, EtcdDiscoveryInstance.class); + + 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); list.add(etcdDiscoveryInstance); } return list; diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscoveryInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java similarity index 96% rename from service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscoveryInstance.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java index 2744ac82196..85f5ade0142 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdDiscoveryInstance.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java @@ -14,12 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.registry.zookeeper; +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); } diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java similarity index 86% rename from service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdInstance.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java index 513a9b644a9..f23dc95d78f 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdInstance.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.registry.zookeeper; +package org.apache.servicecomb.registry.etcd; import java.util.ArrayList; import java.util.HashMap; @@ -187,4 +187,22 @@ public String getInstanceId() { 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/zookeeper/EtcdRegistration.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java similarity index 90% rename from service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistration.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java index 48f1d797801..f419883681d 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistration.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.registry.zookeeper; +package org.apache.servicecomb.registry.etcd; import io.etcd.jetcd.ByteSequence; import io.etcd.jetcd.Client; @@ -150,14 +150,18 @@ public void run() { client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) .build(); keyPath = basePath + "/" + BootStrapProperties.readApplication(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(Objects.requireNonNull(MuteExceptionUtil.executeFunction(JsonUtils::writeValueAsString, etcdInstance)), Charset.defaultCharset())); + ByteSequence.from(valueJson, Charset.defaultCharset())); } public void register(ByteSequence key, ByteSequence value) { Lease leaseClient = client.getLeaseClient(); - leaseId = MuteExceptionUtil.executeCompletableFuture(leaseClient.grant(60)).getID(); + 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(); @@ -165,7 +169,8 @@ public void register(ByteSequence key, ByteSequence value) { putResponse.thenRun(() -> { executorService = Executors.newSingleThreadScheduledExecutor(); executorService.scheduleAtFixedRate(() -> { - MuteExceptionUtil.executeFunction(leaseClient::keepAliveOnce, leaseId); + MuteExceptionUtil.builder().withLog("reRegister, {}, {}", keyPath, etcdInstance) + .executeFunction(leaseClient::keepAliveOnce, leaseId); }, 0, 5, TimeUnit.SECONDS); }); } diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistrationInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java similarity index 96% rename from service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistrationInstance.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java index 32e49c906c3..444fe867089 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistrationInstance.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.registry.zookeeper; +package org.apache.servicecomb.registry.etcd; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.RegistrationInstance; diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistryProperties.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java similarity index 98% rename from service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistryProperties.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java index 9efda2c8069..c4d61094bfc 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/EtcdRegistryProperties.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.registry.zookeeper; +package org.apache.servicecomb.registry.etcd; public class EtcdRegistryProperties { private boolean enabled = true; 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 00000000000..cd03b7ab6de --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/MuteExceptionUtil.java @@ -0,0 +1,87 @@ +package org.apache.servicecomb.registry.etcd; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class MuteExceptionUtil { + + // 定义两个函数式接口 + interface FunctionWithException { + R apply(T t) throws Exception; + } + + interface FunctionWithDoubleParam { + R apply(T1 t1, T2 t2) throws Exception; + } + + private static final Logger LOGGER = LoggerFactory.getLogger(MuteExceptionUtil.class); + + // Builder类,用来自定义日志信息 + 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 R executeFunction(FunctionWithException function, T t) { + try { + return function.apply(t); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Function failure..."), e); + return null; + } + } + + // 执行带异常处理的Supplier + public T executeSupplier(Supplier supplier) { + try { + return supplier.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Supplier failure..."), e); + return null; + } + } + + // 执行带异常处理的CompletableFuture + public T executeCompletableFuture(CompletableFuture completableFuture) { + try { + return completableFuture.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute CompletableFuture failure..."), e); + return null; + } + } + + // 执行带两个参数的Function + public R executeFunctionWithDoubleParam(FunctionWithDoubleParam function, T1 t1, T2 t2) { + try { + return function.apply(t1, t2); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute FunctionWithDoubleParam failure..."), e); + return null; + } + } + } + + // 提供静态方法来创建 Builder + public static MuteExceptionUtilBuilder builder() { + return new MuteExceptionUtilBuilder(); + } + +} \ No newline at end of file diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/MuteExceptionUtil.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/MuteExceptionUtil.java deleted file mode 100644 index bc443b46f57..00000000000 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/zookeeper/MuteExceptionUtil.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.apache.servicecomb.registry.zookeeper; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; - -public class MuteExceptionUtil { - - interface FunctionWithException { - R apply(T t) throws Exception; - } - - private static final Logger LOGGER = LoggerFactory.getLogger(MuteExceptionUtil.class); - - public static R executeFunction(FunctionWithException function, T t) { - try { - return function.apply(t); - } catch (Exception e) { - LOGGER.error("execute Function failure..."); - return null; - } - } - - public static T executeSupplier(Supplier supplier) { - try { - return supplier.get(); - } catch (Exception e) { - LOGGER.error("execute Supplier failure..."); - return null; - } - } - - public static T executeCompletableFuture(CompletableFuture completableFuture) { - try { - return completableFuture.get(); - } catch (Exception e) { - LOGGER.error("execute CompletableFuture failure..."); - return null; - } - } - -} 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 index d3b128cf90d..999ffb6b04b 100644 --- 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 @@ -15,4 +15,4 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -org.apache.servicecomb.registry.zookeeper.EtcdConfiguration +org.apache.servicecomb.registry.etcd.EtcdConfiguration diff --git a/test_consumer/pom.xml b/test_consumer/pom.xml deleted file mode 100644 index 569e1b7be0d..00000000000 --- a/test_consumer/pom.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - 4.0.0 - - org.apache.servicecomb - java-chassis - 3.3.0-SNAPSHOT - - - test_consumer - - - 17 - 17 - UTF-8 - - 3.3.0-SNAPSHOT - - - - - - org.apache.servicecomb - java-chassis-dependencies - ${java-chassis-dependencies.version} - pom - import - - - - - - - - - - - org.apache.logging.log4j - log4j-slf4j-impl - - - org.apache.logging.log4j - log4j-api - - - org.apache.logging.log4j - log4j-core - - - com.google.protobuf - protobuf-java - 3.25.3 - runtime - - - org.apache.servicecomb - registry-etcd - - - org.apache.servicecomb - java-chassis-spring-boot-starter-standalone - - - - - \ No newline at end of file diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ConsumerController.java b/test_consumer/src/main/java/org/apache/servicecomb/ConsumerController.java deleted file mode 100644 index c99e27884c2..00000000000 --- a/test_consumer/src/main/java/org/apache/servicecomb/ConsumerController.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.apache.servicecomb; - -import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.springframework.beans.factory.annotation.Autowired; - -@RestSchema(schemaId = "ConsumerController", schemaInterface = ConsumerService.class) -public class ConsumerController implements ConsumerService { - - private ProviderService providerService; - - @Autowired - public void setProviderService(ProviderService providerService) { - this.providerService = providerService; - } - - @Override - public String sayHello(String name) { - return providerService.sayHello(name); - } -} \ No newline at end of file diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ConsumerService.java b/test_consumer/src/main/java/org/apache/servicecomb/ConsumerService.java deleted file mode 100644 index 092772915c4..00000000000 --- a/test_consumer/src/main/java/org/apache/servicecomb/ConsumerService.java +++ /dev/null @@ -1,13 +0,0 @@ - -package org.apache.servicecomb; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; - -@RequestMapping(path = "/consumer") -public interface ConsumerService { - - @GetMapping("/sayHello") - String sayHello(@RequestParam("name") String name); -} \ No newline at end of file diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ProviderService.java b/test_consumer/src/main/java/org/apache/servicecomb/ProviderService.java deleted file mode 100644 index 20c191392e3..00000000000 --- a/test_consumer/src/main/java/org/apache/servicecomb/ProviderService.java +++ /dev/null @@ -1,13 +0,0 @@ - -package org.apache.servicecomb; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; - -@RequestMapping(path = "/provider") -public interface ProviderService { - - @GetMapping("/sayHello") - String sayHello(@RequestParam("name") String name); -} \ No newline at end of file diff --git a/test_consumer/src/main/java/org/apache/servicecomb/ProviderServiceConfiguration.java b/test_consumer/src/main/java/org/apache/servicecomb/ProviderServiceConfiguration.java deleted file mode 100644 index fc8393445c3..00000000000 --- a/test_consumer/src/main/java/org/apache/servicecomb/ProviderServiceConfiguration.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.apache.servicecomb; - -import org.apache.servicecomb.provider.pojo.Invoker; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class ProviderServiceConfiguration { - @Bean - public ProviderService providerService() { - return Invoker.createProxy("provider", "ProviderController", ProviderService.class); - } -} \ No newline at end of file diff --git a/test_provider/pom.xml b/test_provider/pom.xml deleted file mode 100644 index c8b1d05e0d4..00000000000 --- a/test_provider/pom.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - 4.0.0 - - org.apache.servicecomb - java-chassis - 3.3.0-SNAPSHOT - - - test_provider - - - 17 - 17 - UTF-8 - - 3.3.0-SNAPSHOT - - - - - - org.apache.servicecomb - java-chassis-dependencies - ${java-chassis-dependencies.version} - pom - import - - - - - - - org.apache.servicecomb - solution-basic - - - org.apache.logging.log4j - log4j-slf4j-impl - - - org.apache.logging.log4j - log4j-api - - - org.apache.logging.log4j - log4j-core - - - com.google.protobuf - protobuf-java - 3.25.3 - runtime - - - org.apache.servicecomb - registry-etcd - - - org.apache.servicecomb - java-chassis-spring-boot-starter-standalone - - - - - \ No newline at end of file diff --git a/test_provider/src/main/java/org/apache/servicecomb/ProviderController.java b/test_provider/src/main/java/org/apache/servicecomb/ProviderController.java deleted file mode 100644 index 08b39a61094..00000000000 --- a/test_provider/src/main/java/org/apache/servicecomb/ProviderController.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.apache.servicecomb; - -import org.apache.servicecomb.config.DynamicProperties; -import org.apache.servicecomb.provider.rest.common.RestSchema; -import org.springframework.beans.factory.annotation.Autowired; - -@RestSchema(schemaId = "ProviderController", schemaInterface = ProviderService.class) -public class ProviderController implements ProviderService { - - @Override - public String sayHello(String name) { - return "Hello " + name; - } - -} \ No newline at end of file diff --git a/test_provider/src/main/java/org/apache/servicecomb/ProviderService.java b/test_provider/src/main/java/org/apache/servicecomb/ProviderService.java deleted file mode 100644 index 20c191392e3..00000000000 --- a/test_provider/src/main/java/org/apache/servicecomb/ProviderService.java +++ /dev/null @@ -1,13 +0,0 @@ - -package org.apache.servicecomb; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; - -@RequestMapping(path = "/provider") -public interface ProviderService { - - @GetMapping("/sayHello") - String sayHello(@RequestParam("name") String name); -} \ No newline at end of file From 93e4cc8dba18f2a0890a8f57a400939f8f3a9e32 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Fri, 18 Oct 2024 20:02:23 +0800 Subject: [PATCH 03/27] fix(registry-etcd,demo-etcd): complete etcd registry function - Complete the registration of etcd by the service consumer, and then the consumer can make API calls to the service provider and pass all test cases. --- demo/demo-etcd/README.md | 2 +- .../src/main/resources/application.yml | 10 +- demo/demo-etcd/gateway/pom.xml | 6 + .../src/main/resources/application.yml | 4 +- .../src/main/resources/application.yml | 22 +- .../samples/{ZookeeperIT.java => EtcdIT.java} | 6 +- demo/pom.xml | 1 + .../registry/etcd/EtcdDiscovery.java | 23 +- .../registry/etcd/EtcdRegistration.java | 303 +++++++++--------- 9 files changed, 197 insertions(+), 180 deletions(-) rename demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/{ZookeeperIT.java => EtcdIT.java} (91%) diff --git a/demo/demo-etcd/README.md b/demo/demo-etcd/README.md index 8e0978c8d7d..d25539da93b 100644 --- a/demo/demo-etcd/README.md +++ b/demo/demo-etcd/README.md @@ -1,6 +1,6 @@ # Notice -This integration tests is designed for Zookeeper registry and configuration. And extra test cases include: +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/src/main/resources/application.yml b/demo/demo-etcd/consumer/src/main/resources/application.yml index b89835a883c..be5721fc385 100644 --- a/demo/demo-etcd/consumer/src/main/resources/application.yml +++ b/demo/demo-etcd/consumer/src/main/resources/application.yml @@ -23,17 +23,17 @@ servicecomb: properties: group: red registry: - zk: - enabled: true - connectString: 127.0.0.1:2181 + etcd: +# enabled: true + connectString: http://127.0.0.1:2379 rest: address: 0.0.0.0:9092?websocketEnabled=true server: websocket-prefix: /ws - highway: - address: 0.0.0.0:7092 +# highway: +# address: 0.0.0.0:7092 diff --git a/demo/demo-etcd/gateway/pom.xml b/demo/demo-etcd/gateway/pom.xml index 4174a08e308..50958f4eb1d 100644 --- a/demo/demo-etcd/gateway/pom.xml +++ b/demo/demo-etcd/gateway/pom.xml @@ -38,6 +38,12 @@ org.apache.servicecomb edge-core + + com.google.protobuf + protobuf-java + 3.25.3 + runtime + org.apache.servicecomb registry-etcd diff --git a/demo/demo-etcd/gateway/src/main/resources/application.yml b/demo/demo-etcd/gateway/src/main/resources/application.yml index f361091d81b..f526eb3de70 100644 --- a/demo/demo-etcd/gateway/src/main/resources/application.yml +++ b/demo/demo-etcd/gateway/src/main/resources/application.yml @@ -21,9 +21,9 @@ servicecomb: version: 0.0.1 name: gateway registry: - zk: + etcd: enabled: true - connectString: 127.0.0.1:2181 + connectString: http://127.0.0.1:2379 rest: address: 0.0.0.0:9090?websocketEnabled=true diff --git a/demo/demo-etcd/provider/src/main/resources/application.yml b/demo/demo-etcd/provider/src/main/resources/application.yml index 9ef35f3e345..e155612e522 100644 --- a/demo/demo-etcd/provider/src/main/resources/application.yml +++ b/demo/demo-etcd/provider/src/main/resources/application.yml @@ -24,24 +24,28 @@ servicecomb: properties: group: green registry: - zk: - connectString: 127.0.0.1:2181 - config: - zk: - connectString: 127.0.0.1:2181 - instance-tag: config-demo + etcd: + connectString: http://127.0.0.1:2379 +# config: +# zk: +# connectString: 127.0.0.1:2181 +# instance-tag: config-demo rest: address: 0.0.0.0:9094?websocketEnabled=true server: websocket-prefix: /ws - highway: - address: 0.0.0.0:7094 +# highway: +# address: 0.0.0.0:7094 cors: enabled: true origin: "*" allowCredentials: false allowedMethod: "*" - maxAge: 3600 \ No newline at end of file + maxAge: 3600 + +key1: 1 +key2: 3 +key3: 5 diff --git a/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java similarity index 91% rename from demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java rename to demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java index 549cfe2a300..833feff87c0 100644 --- a/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java +++ b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java @@ -29,8 +29,8 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(classes = TestClientApplication.class) -public class ZookeeperIT { - private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperIT.class); +public class EtcdIT { + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdIT.class); @BeforeEach public void setUp() { @@ -38,7 +38,7 @@ public void setUp() { } @Test - public void clientGetsNoError() throws Exception { + public void clientGetsNoError() { try { TestClientApplication.run(); } catch (Exception e) { diff --git a/demo/pom.xml b/demo/pom.xml index c878c5b1883..fa88e1526bb 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -57,6 +57,7 @@ demo-cse-v2 demo-nacos demo-etcd + demo-zookeeper 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 index 2394d549522..23e7afba13e 100644 --- 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 @@ -17,7 +17,10 @@ package org.apache.servicecomb.registry.etcd; import com.google.common.collect.Lists; -import io.etcd.jetcd.*; +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; @@ -26,8 +29,6 @@ import org.apache.servicecomb.config.BootStrapProperties; 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; @@ -35,6 +36,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; public class EtcdDiscovery implements Discovery { @@ -46,8 +48,6 @@ public class EtcdDiscovery implements Discovery { private Client client; - private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDiscovery.class); - private InstanceChangedListener instanceChangedListener; @Autowired @@ -76,10 +76,10 @@ public boolean enabled(String application, String serviceName) { @Override public List findServiceInstances(String application, String serviceName) { - String prefixPath = basePath + "/" + application; + String prefixPath = basePath + "/" + application + "/" + serviceName; Watch watchClient = client.getWatchClient(); watchClient.watch(ByteSequence.from(prefixPath, Charset.defaultCharset()), - WatchOption.newBuilder().build(), + WatchOption.builder().build(), resp -> { List keyValueList = resp.getEvents().stream().map(WatchEvent::getKeyValue).toList(); instanceChangedListener.onInstanceChanged(name(), application, serviceName, convertServiceInstanceList(keyValueList)); @@ -93,7 +93,7 @@ public List getValuesByPrefix(String prefix) { CompletableFuture getFuture = client.getKVClient().get( ByteSequence.from(prefix, StandardCharsets.UTF_8), - GetOption.newBuilder().withPrefix(ByteSequence.from(prefix, StandardCharsets.UTF_8)).build() + GetOption.builder().withPrefix(ByteSequence.from(prefix, StandardCharsets.UTF_8)).build() ); GetResponse response = MuteExceptionUtil.builder().withLog("get kv by prefix error") .executeCompletableFuture(getFuture); @@ -114,11 +114,14 @@ private List convertServiceInstanceList(List ke return list; } -// todo @Override public List findServices(String application) { - return List.of(); + String prefixPath = basePath + "/" + application; + List endpointKv = getValuesByPrefix(prefixPath); + return endpointKv.stream() + .map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) // 转换 ByteSequence 为 String + .collect(Collectors.toList()); } @Override 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 index f419883681d..1b08f3877cb 100644 --- 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 @@ -34,7 +34,6 @@ import org.springframework.core.env.Environment; import java.nio.charset.Charset; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -42,153 +41,157 @@ public class EtcdRegistration implements Registration { - 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) { - 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 false; - } - - @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) + "/" + 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 = 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); - }); - } - - public void unregister() { - // 关闭定时任务 - executorService.shutdownNow(); - // 撤销租约,自动删除临时节点 - 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(); - } - } + 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 = 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); + }); + } + + public 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(); + } + } } \ No newline at end of file From c31c0006b2942c4c4e42b4324afa5ff750a7cdf2 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Tue, 22 Oct 2024 11:35:15 +0800 Subject: [PATCH 04/27] fix(registry-etcd,demo-etcd): fix pr probleam - add code style - add linelint style - run spotbugs - run ratcheck - update docker config --- demo/demo-etcd/consumer/pom.xml | 2 +- .../samples/ClientWebsocketController.java | 2 - .../src/main/resources/application.yml | 5 +- .../consumer/src/main/resources/log4j2.xml | 2 +- demo/demo-etcd/gateway/pom.xml | 2 +- .../gateway/src/main/resources/log4j2.xml | 2 +- demo/demo-etcd/pom.xml | 2 +- demo/demo-etcd/provider/pom.xml | 10 +- .../samples/ProviderController.java | 34 +- .../src/main/resources/application.yml | 12 +- .../provider/src/main/resources/log4j2.xml | 2 +- demo/demo-etcd/test-client/pom.xml | 23 +- .../test-client/src/main/resources/log4j2.xml | 2 +- dependencies/default/pom.xml | 7 + service-registry/registry-etcd/pom.xml | 1 - .../registry/etcd/EtcdConfiguration.java | 6 +- .../registry/etcd/EtcdDiscovery.java | 235 +++++++------ .../registry/etcd/EtcdInstance.java | 26 +- .../registry/etcd/EtcdRegistration.java | 328 +++++++++--------- .../registry/etcd/MuteExceptionUtil.java | 151 ++++---- 20 files changed, 439 insertions(+), 415 deletions(-) diff --git a/demo/demo-etcd/consumer/pom.xml b/demo/demo-etcd/consumer/pom.xml index 44fb2fccc7f..cbccb498a4c 100644 --- a/demo/demo-etcd/consumer/pom.xml +++ b/demo/demo-etcd/consumer/pom.xml @@ -85,4 +85,4 @@ - \ No newline at end of file + 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 index 76fb2fb17db..f71738a9ed6 100644 --- 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 @@ -43,11 +43,9 @@ public void websocket(ServerWebSocket serverWebsocket) { WebSocket providerWebSocket = providerService.websocket(); providerWebSocket.closeHandler(v -> serverWebsocket.close()); providerWebSocket.textMessageHandler(m -> { - System.out.println("send message " + m); serverWebsocket.writeTextMessage(m); }); serverWebsocket.textMessageHandler(m -> { - System.out.println("receive message " + m); providerWebSocket.writeTextMessage(m); }); } diff --git a/demo/demo-etcd/consumer/src/main/resources/application.yml b/demo/demo-etcd/consumer/src/main/resources/application.yml index be5721fc385..1c686e5544f 100644 --- a/demo/demo-etcd/consumer/src/main/resources/application.yml +++ b/demo/demo-etcd/consumer/src/main/resources/application.yml @@ -24,7 +24,7 @@ servicecomb: group: red registry: etcd: -# enabled: true + # enabled: true connectString: http://127.0.0.1:2379 rest: @@ -34,6 +34,3 @@ servicecomb: # highway: # address: 0.0.0.0:7092 - - - diff --git a/demo/demo-etcd/consumer/src/main/resources/log4j2.xml b/demo/demo-etcd/consumer/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-etcd/consumer/src/main/resources/log4j2.xml +++ b/demo/demo-etcd/consumer/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-etcd/gateway/pom.xml b/demo/demo-etcd/gateway/pom.xml index 50958f4eb1d..b2eb1e51df2 100644 --- a/demo/demo-etcd/gateway/pom.xml +++ b/demo/demo-etcd/gateway/pom.xml @@ -88,4 +88,4 @@ - \ No newline at end of file + diff --git a/demo/demo-etcd/gateway/src/main/resources/log4j2.xml b/demo/demo-etcd/gateway/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-etcd/gateway/src/main/resources/log4j2.xml +++ b/demo/demo-etcd/gateway/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-etcd/pom.xml b/demo/demo-etcd/pom.xml index 0cb08ff220c..bda7b06fe8c 100644 --- a/demo/demo-etcd/pom.xml +++ b/demo/demo-etcd/pom.xml @@ -58,4 +58,4 @@ test-client - \ No newline at end of file + diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml index 19a3ecb2abb..9a4dbbb0006 100644 --- a/demo/demo-etcd/provider/pom.xml +++ b/demo/demo-etcd/provider/pom.xml @@ -48,10 +48,10 @@ org.apache.servicecomb registry-etcd - - - - + + + + io.reactivex.rxjava3 @@ -98,4 +98,4 @@ - \ No newline at end of file + 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 index 8e6388d35e4..e4828bb9ba9 100644 --- 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 @@ -28,25 +28,25 @@ @RestSchema(schemaId = "ProviderController") @RequestMapping(path = "/") public class ProviderController implements InitializingBean { - private Environment environment; + private Environment environment; - @Autowired - public void setEnvironment(Environment environment) { - this.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 " + name; - } + // a very simple service to echo the request parameter + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return "Hello " + name; + } - @GetMapping("/getConfig") - public String getConfig(@RequestParam("key") String key) { - return environment.getProperty(key); - } + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return environment.getProperty(key); + } - @Override - public void afterPropertiesSet() throws Exception { - } + @Override + public void afterPropertiesSet() throws Exception { + } } diff --git a/demo/demo-etcd/provider/src/main/resources/application.yml b/demo/demo-etcd/provider/src/main/resources/application.yml index e155612e522..55dabdb7040 100644 --- a/demo/demo-etcd/provider/src/main/resources/application.yml +++ b/demo/demo-etcd/provider/src/main/resources/application.yml @@ -26,18 +26,18 @@ servicecomb: registry: etcd: connectString: http://127.0.0.1:2379 -# config: -# zk: -# connectString: 127.0.0.1:2181 -# instance-tag: config-demo + # config: + # zk: + # connectString: 127.0.0.1:2181 + # instance-tag: config-demo rest: address: 0.0.0.0:9094?websocketEnabled=true server: websocket-prefix: /ws -# highway: -# address: 0.0.0.0:7094 + # highway: + # address: 0.0.0.0:7094 cors: enabled: true diff --git a/demo/demo-etcd/provider/src/main/resources/log4j2.xml b/demo/demo-etcd/provider/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-etcd/provider/src/main/resources/log4j2.xml +++ b/demo/demo-etcd/provider/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml index 985df6836a2..288d4dcd87a 100644 --- a/demo/demo-etcd/test-client/pom.xml +++ b/demo/demo-etcd/test-client/pom.xml @@ -60,21 +60,21 @@ - zookeeper:3.8.3 - zookeeper + etcd:3.5.9 + etcd alias binding to port - 2181 + 2379 - zookeeper.port:2181 + etcd.port:2379 @@ -85,12 +85,13 @@ alias - -Dservicecomb.registry.zk.connectString=zookeeper:2181 -Dservicecomb.config.zk.connectString=zookeeper:2181 + -Dservicecomb.registry.etcd.connectString=http://etcd:2379 + -Dservicecomb.config.etcd.connectString=http://etcd:2379 /maven/maven/etcd-provider-${project.version}.jar - zookeeper:zookeeper + etcd:etcd ServiceComb is ready @@ -113,12 +114,12 @@ alias - -Dservicecomb.registry.zk.connectString=zookeeper:2181 + -Dservicecomb.registry.etcd.connectString=http://etcd:2379 /maven/maven/etcd-consumer-${project.version}.jar - zookeeper:zookeeper + etcd:etcd ServiceComb is ready @@ -141,12 +142,12 @@ alias - -Dservicecomb.registry.zk.connectString=zookeeper:2181 + -Dservicecomb.registry.etcd.connectString=http://etcd:2379 /maven/maven/etcd-gateway-${project.version}.jar - zookeeper:zookeeper + etcd:etcd ServiceComb is ready @@ -206,4 +207,4 @@ - \ No newline at end of file + diff --git a/demo/demo-etcd/test-client/src/main/resources/log4j2.xml b/demo/demo-etcd/test-client/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-etcd/test-client/src/main/resources/log4j2.xml +++ b/demo/demo-etcd/test-client/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/dependencies/default/pom.xml b/dependencies/default/pom.xml index 6a86ad500f8..71cac93458f 100644 --- a/dependencies/default/pom.xml +++ b/dependencies/default/pom.xml @@ -91,6 +91,7 @@ 4.5.10 3.4.1 3.4.0 + 0.8.3 ${basedir}/../.. @@ -542,6 +543,12 @@ pom import + + + io.etcd + jetcd-core + ${jetcd-core.version} + diff --git a/service-registry/registry-etcd/pom.xml b/service-registry/registry-etcd/pom.xml index 599d1892eff..52681b60784 100644 --- a/service-registry/registry-etcd/pom.xml +++ b/service-registry/registry-etcd/pom.xml @@ -34,7 +34,6 @@ io.etcd jetcd-core - 0.8.3 org.apache.servicecomb 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 index 27684536c8b..a926c7bbc11 100644 --- 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 @@ -24,17 +24,17 @@ public class EtcdConfiguration { @Bean @ConfigurationProperties(prefix = EtcdConst.ETCD_REGISTRY_PREFIX) - public EtcdRegistryProperties zookeeperRegistryProperties() { + public EtcdRegistryProperties etcdRegistryProperties() { return new EtcdRegistryProperties(); } @Bean - public EtcdDiscovery zookeeperDiscovery() { + public EtcdDiscovery etcdDiscovery() { return new EtcdDiscovery(); } @Bean - public EtcdRegistration zookeeperRegistration() { + public EtcdRegistration etcdRegistration() { return new EtcdRegistration(); } } 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 index 23e7afba13e..b3ce9f94bdb 100644 --- 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 @@ -16,7 +16,23 @@ */ package org.apache.servicecomb.registry.etcd; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.List; +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.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; @@ -25,134 +41,131 @@ import io.etcd.jetcd.options.GetOption; import io.etcd.jetcd.options.WatchOption; import io.etcd.jetcd.watch.WatchEvent; -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.config.BootStrapProperties; -import org.apache.servicecomb.foundation.common.utils.JsonUtils; -import org.apache.servicecomb.registry.api.Discovery; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; public class EtcdDiscovery implements Discovery { - private Environment environment; - - private String basePath; - - private EtcdRegistryProperties etcdRegistryProperties; - - private Client client; - - private InstanceChangedListener instanceChangedListener; - - @Autowired - @SuppressWarnings("unused") - public void setEnvironment(Environment environment) { - this.environment = environment; - } - - @Autowired - @SuppressWarnings("unused") - public void setEtcdRegistryProperties(EtcdRegistryProperties etcdRegistryProperties) { - this.etcdRegistryProperties = etcdRegistryProperties; - } + private Environment environment; - @Override - public String name() { - return EtcdConst.ETCD_REGISTRY_NAME; - } + private String basePath; - @Override - public boolean enabled(String application, String serviceName) { - return environment.getProperty(String.format(EtcdConst.ETCD_DISCOVERY_ENABLED, application, serviceName), - boolean.class, true); - } + private EtcdRegistryProperties etcdRegistryProperties; - @Override - public List findServiceInstances(String application, String serviceName) { - - String prefixPath = basePath + "/" + application + "/" + serviceName; - Watch watchClient = client.getWatchClient(); - watchClient.watch(ByteSequence.from(prefixPath, Charset.defaultCharset()), - WatchOption.builder().build(), - resp -> { - List keyValueList = resp.getEvents().stream().map(WatchEvent::getKeyValue).toList(); - instanceChangedListener.onInstanceChanged(name(), application, serviceName, convertServiceInstanceList(keyValueList)); - } - ); - List endpointKv = getValuesByPrefix(prefixPath); - return convertServiceInstanceList(endpointKv); - } + private Client client; - public List getValuesByPrefix(String prefix) { + private InstanceChangedListener instanceChangedListener; - CompletableFuture 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 static volatile boolean isWatch = false; - private List convertServiceInstanceList(List keyValueList) { + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDiscovery.class); - List list = Lists.newArrayListWithExpectedSize(keyValueList.size()); - for (KeyValue keyValue : keyValueList) { - String valueJson = new String(keyValue.getValue().getBytes()); + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } - 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); - list.add(etcdDiscoveryInstance); - } - return list; - } + @Autowired + @SuppressWarnings("unused") + public void setEtcdRegistryProperties(EtcdRegistryProperties etcdRegistryProperties) { + this.etcdRegistryProperties = etcdRegistryProperties; + } - @Override - public List findServices(String application) { + @Override + public String name() { + return EtcdConst.ETCD_REGISTRY_NAME; + } - String prefixPath = basePath + "/" + application; - List endpointKv = getValuesByPrefix(prefixPath); - return endpointKv.stream() - .map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) // 转换 ByteSequence 为 String - .collect(Collectors.toList()); - } + @Override + public boolean enabled(String application, String serviceName) { + return environment.getProperty(String.format(EtcdConst.ETCD_DISCOVERY_ENABLED, application, serviceName), + boolean.class, true); + } - @Override - public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { - this.instanceChangedListener = instanceChangedListener; - } + @Override + public List findServiceInstances(String application, String serviceName) { - @Override - public boolean enabled() { - return etcdRegistryProperties.isEnabled(); + String prefixPath = basePath + "/" + application + "/" + serviceName; + if (!isWatch) { + Watch watchClient = client.getWatchClient(); + try { + watchClient.watch(ByteSequence.from(prefixPath, Charset.defaultCharset()), WatchOption.builder().build(), + resp -> { + List keyValueList = resp.getEvents().stream().map(WatchEvent::getKeyValue).toList(); + instanceChangedListener.onInstanceChanged(name(), application, serviceName, + convertServiceInstanceList(keyValueList)); + }); + isWatch = true; + } catch (Exception e) { + LOGGER.error("add watch failure", e); + } } - - @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); + List endpointKv = getValuesByPrefix(prefixPath); + return convertServiceInstanceList(endpointKv); + } + + public List getValuesByPrefix(String prefix) { + + CompletableFuture 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 convertServiceInstanceList(List keyValueList) { + + List list = Lists.newArrayListWithExpectedSize(keyValueList.size()); + for (KeyValue keyValue : keyValueList) { + 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); + list.add(etcdDiscoveryInstance); } - - @Override - public void run() { - client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) - .build(); + return list; + } + + @Override + public List findServices(String application) { + + String prefixPath = basePath + "/" + application; + List endpointKv = getValuesByPrefix(prefixPath); + return endpointKv.stream().map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) // 转换 ByteSequence 为 String + .collect(Collectors.toList()); + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener 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; } - - @Override - public void destroy() { - if (client != null) { - client.close(); - } + basePath = String.format(EtcdConst.ETCD_DISCOVERY_ROOT, env); + } + + @Override + public void run() { + client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()).build(); + } + + @Override + public void destroy() { + if (client != null) { + client.close(); } + } } 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 index f23dc95d78f..19fbd8cadf6 100644 --- 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 @@ -191,18 +191,18 @@ public String getServiceId() { @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 + - '}'; + "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 index 1b08f3877cb..0fae8b94dc9 100644 --- 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 @@ -16,12 +16,12 @@ */ package org.apache.servicecomb.registry.etcd; -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; +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; @@ -33,165 +33,167 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; -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 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 { - private EtcdInstance etcdInstance; - - private Environment environment; - - private String basePath; + private EtcdInstance etcdInstance; - 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; - } + private Environment environment; + + private String basePath; - @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 = 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); - }); - } - - public 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(); - } - } -} \ No newline at end of file + 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 = 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/MuteExceptionUtil.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/MuteExceptionUtil.java index cd03b7ab6de..21260bfaefe 100644 --- 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 @@ -1,87 +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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class MuteExceptionUtil { - // 定义两个函数式接口 - interface FunctionWithException { - R apply(T t) throws Exception; + interface FunctionWithException { + R apply(T t) throws Exception; + } + + interface FunctionWithDoubleParam { + 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 R executeFunction(FunctionWithException function, T t) { + try { + return function.apply(t); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Function failure..."), customMessageParams, e); + return null; + } } - interface FunctionWithDoubleParam { - R apply(T1 t1, T2 t2) throws Exception; + public T executeSupplier(Supplier supplier) { + try { + return supplier.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Supplier failure..."), customMessageParams, e); + return null; + } } - private static final Logger LOGGER = LoggerFactory.getLogger(MuteExceptionUtil.class); - - // Builder类,用来自定义日志信息 - 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 R executeFunction(FunctionWithException function, T t) { - try { - return function.apply(t); - } catch (Exception e) { - LOGGER.error(getLogMessage("execute Function failure..."), e); - return null; - } - } - - // 执行带异常处理的Supplier - public T executeSupplier(Supplier supplier) { - try { - return supplier.get(); - } catch (Exception e) { - LOGGER.error(getLogMessage("execute Supplier failure..."), e); - return null; - } - } - - // 执行带异常处理的CompletableFuture - public T executeCompletableFuture(CompletableFuture completableFuture) { - try { - return completableFuture.get(); - } catch (Exception e) { - LOGGER.error(getLogMessage("execute CompletableFuture failure..."), e); - return null; - } - } - - // 执行带两个参数的Function - public R executeFunctionWithDoubleParam(FunctionWithDoubleParam function, T1 t1, T2 t2) { - try { - return function.apply(t1, t2); - } catch (Exception e) { - LOGGER.error(getLogMessage("execute FunctionWithDoubleParam failure..."), e); - return null; - } - } + public T executeCompletableFuture(CompletableFuture completableFuture) { + try { + return completableFuture.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute CompletableFuture failure..."),customMessageParams, e); + return null; + } } - // 提供静态方法来创建 Builder - public static MuteExceptionUtilBuilder builder() { - return new MuteExceptionUtilBuilder(); + public R executeFunctionWithDoubleParam(FunctionWithDoubleParam function, T1 t1, T2 t2) { + try { + return function.apply(t1, t2); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute FunctionWithDoubleParam failure..."),customMessageParams, e); + return null; + } } + } -} \ No newline at end of file + public static MuteExceptionUtilBuilder builder() { + return new MuteExceptionUtilBuilder(); + } +} From cf55f2728e77f3c6e068657166dc1aa1b54d3c6b Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Wed, 23 Oct 2024 18:25:13 +0800 Subject: [PATCH 05/27] refactor(registry-etcd): Refactor the EtcdDiscovery class and update related components - Refactor the findServiceInstances method and use SingletonManager to manage watchers - Optimize the output format of exception information in log records - Update the Etcd image version in the test environment - Removed redundant information in README.md - Added SingletonManager class for managing singleton objects --- demo/demo-etcd/README.md | 3 +- demo/demo-etcd/test-client/pom.xml | 3 +- service-registry/registry-etcd/pom.xml | 2 +- .../registry/etcd/EtcdDiscovery.java | 8 ++-- .../registry/etcd/MuteExceptionUtil.java | 6 +-- .../registry/etcd/SingletonManager.java | 47 +++++++++++++++++++ 6 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/SingletonManager.java diff --git a/demo/demo-etcd/README.md b/demo/demo-etcd/README.md index d25539da93b..046bec6c968 100644 --- a/demo/demo-etcd/README.md +++ b/demo/demo-etcd/README.md @@ -2,5 +2,4 @@ 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. - +* Test cases related to SpringMVC annotations that demo-springmvc can not cover. diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml index 288d4dcd87a..461df7e4acf 100644 --- a/demo/demo-etcd/test-client/pom.xml +++ b/demo/demo-etcd/test-client/pom.xml @@ -60,7 +60,8 @@ - etcd:3.5.9 + + bitnami/etcd:latest etcd alias diff --git a/service-registry/registry-etcd/pom.xml b/service-registry/registry-etcd/pom.xml index 52681b60784..cf48811d342 100644 --- a/service-registry/registry-etcd/pom.xml +++ b/service-registry/registry-etcd/pom.xml @@ -49,4 +49,4 @@ - \ No newline at end of file + 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 index b3ce9f94bdb..0537ebf6150 100644 --- 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 @@ -85,7 +85,7 @@ public boolean enabled(String application, String serviceName) { public List findServiceInstances(String application, String serviceName) { String prefixPath = basePath + "/" + application + "/" + serviceName; - if (!isWatch) { + SingletonManager.getInstance().getSingleton(prefixPath, serName -> { Watch watchClient = client.getWatchClient(); try { watchClient.watch(ByteSequence.from(prefixPath, Charset.defaultCharset()), WatchOption.builder().build(), @@ -94,11 +94,11 @@ public List findServiceInstances(String application, Stri instanceChangedListener.onInstanceChanged(name(), application, serviceName, convertServiceInstanceList(keyValueList)); }); - isWatch = true; } catch (Exception e) { LOGGER.error("add watch failure", e); } - } + return watchClient; + }); List endpointKv = getValuesByPrefix(prefixPath); return convertServiceInstanceList(endpointKv); } @@ -110,7 +110,7 @@ public List getValuesByPrefix(String prefix) { 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(); // 返回所有匹配前缀的键值对 + return response.getKvs(); } private List convertServiceInstanceList(List keyValueList) { 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 index 21260bfaefe..83041cacfbc 100644 --- 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 @@ -55,7 +55,7 @@ public R executeFunction(FunctionWithException function, T t) { try { return function.apply(t); } catch (Exception e) { - LOGGER.error(getLogMessage("execute Function failure..."), customMessageParams, e); + LOGGER.error(getLogMessage("execute Function failure..."), customMessageParams, e); return null; } } @@ -73,7 +73,7 @@ public T executeCompletableFuture(CompletableFuture completableFuture) { try { return completableFuture.get(); } catch (Exception e) { - LOGGER.error(getLogMessage("execute CompletableFuture failure..."),customMessageParams, e); + LOGGER.error(getLogMessage("execute CompletableFuture failure..."), customMessageParams, e); return null; } } @@ -82,7 +82,7 @@ public R executeFunctionWithDoubleParam(FunctionWithDoubleParam singletons = new ConcurrentHashMapEx<>(); + + private SingletonManager() { + } + + public static synchronized SingletonManager getInstance() { + if (instance == null) { + instance = new SingletonManager(); + } + return instance; + } + + public T getSingleton(String key, Function mappingFunction) { + return (T) singletons.computeIfAbsent(key, mappingFunction); + } + + public void destroy() { + singletons.clear(); + instance = null; + } +} From fae1f3b92710ffb791ab9deb893299c73bdc83a5 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Fri, 25 Oct 2024 15:50:29 +0800 Subject: [PATCH 06/27] fix(registry-etcd): Support Etcd authentication and optimize the instance discovery mechanism - Add authentication information configuration in EtcdDiscovery - Implement remote-based observers to monitor changes in service instances - Use SingletonManager to store and manage instance information - Update Docker Compose configuration to allow unauthenticated access - Modify the sayHello method in ProviderController to return the service address --- .../src/main/resources/application.yml | 1 + demo/demo-etcd/provider/pom.xml | 4 - .../samples/ProviderController.java | 2 +- demo/demo-etcd/test-client/pom.xml | 6 +- .../registry/etcd/EtcdDiscovery.java | 74 ++++++++++++++----- .../registry/etcd/SingletonManager.java | 10 ++- 6 files changed, 70 insertions(+), 27 deletions(-) diff --git a/demo/demo-etcd/consumer/src/main/resources/application.yml b/demo/demo-etcd/consumer/src/main/resources/application.yml index 1c686e5544f..2bb97c757c7 100644 --- a/demo/demo-etcd/consumer/src/main/resources/application.yml +++ b/demo/demo-etcd/consumer/src/main/resources/application.yml @@ -25,6 +25,7 @@ servicecomb: registry: etcd: # enabled: true +# authenticationInfo: chenzhida:chenzhida connectString: http://127.0.0.1:2379 rest: diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml index 9a4dbbb0006..c1413de54ce 100644 --- a/demo/demo-etcd/provider/pom.xml +++ b/demo/demo-etcd/provider/pom.xml @@ -48,10 +48,6 @@ org.apache.servicecomb registry-etcd - - - - io.reactivex.rxjava3 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 index e4828bb9ba9..5a6a7e3e7ab 100644 --- 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 @@ -38,7 +38,7 @@ public void setEnvironment(Environment environment) { // a very simple service to echo the request parameter @GetMapping("/sayHello") public String sayHello(@RequestParam("name") String name) { - return "Hello " + name; + return "Hello " + environment.getProperty("servicecomb.rest.address"); } @GetMapping("/getConfig") diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml index 461df7e4acf..0c050adcb85 100644 --- a/demo/demo-etcd/test-client/pom.xml +++ b/demo/demo-etcd/test-client/pom.xml @@ -60,13 +60,12 @@ - bitnami/etcd:latest etcd alias - binding to port + etcdserver 2379 @@ -77,6 +76,9 @@ etcd.port:2379 + + yes + 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 index 0537ebf6150..e0bf72237ce 100644 --- 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 @@ -18,8 +18,11 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -40,7 +43,6 @@ import io.etcd.jetcd.kv.GetResponse; import io.etcd.jetcd.options.GetOption; import io.etcd.jetcd.options.WatchOption; -import io.etcd.jetcd.watch.WatchEvent; public class EtcdDiscovery implements Discovery { @@ -54,8 +56,6 @@ public class EtcdDiscovery implements Discovery { private InstanceChangedListener instanceChangedListener; - private static volatile boolean isWatch = false; - private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDiscovery.class); @Autowired @@ -85,22 +85,43 @@ public boolean enabled(String application, String serviceName) { public List findServiceInstances(String application, String serviceName) { String prefixPath = basePath + "/" + application + "/" + serviceName; - SingletonManager.getInstance().getSingleton(prefixPath, serName -> { + SingletonManager.getInstance().computeIfAbsent(prefixPath, serName -> { Watch watchClient = client.getWatchClient(); try { - watchClient.watch(ByteSequence.from(prefixPath, Charset.defaultCharset()), WatchOption.builder().build(), + ByteSequence prefixByteSeq = ByteSequence.from(prefixPath, Charset.defaultCharset()); + watchClient.watch( + prefixByteSeq, + WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> { - List keyValueList = resp.getEvents().stream().map(WatchEvent::getKeyValue).toList(); - instanceChangedListener.onInstanceChanged(name(), application, serviceName, - convertServiceInstanceList(keyValueList)); - }); + Map instanceMap = SingletonManager.getInstance() + .get("instance:" + prefixPath); + resp.getEvents().forEach(event -> { + String key = event.getKeyValue().getKey().toString(Charset.defaultCharset()); + switch (event.getEventType()) { + case PUT -> { + EtcdDiscoveryInstance newInstance = getEtcdDiscoveryInstance(event.getKeyValue()); + instanceMap.put(key, newInstance); + } + case DELETE -> instanceMap.remove(key); + default -> LOGGER.error("unkonw event"); + } + }); + List discoveryInstanceList = new ArrayList<>(instanceMap.values()); + instanceChangedListener.onInstanceChanged(name(), application, serviceName, discoveryInstanceList); + } + ); } catch (Exception e) { - LOGGER.error("add watch failure", e); + LOGGER.error("Failed to add watch", e); } return watchClient; }); + List endpointKv = getValuesByPrefix(prefixPath); - return convertServiceInstanceList(endpointKv); + List etcdDiscoveryInstances = convertServiceInstanceList(endpointKv); + SingletonManager.getInstance().computeIfAbsent("instance:" + prefixPath, v -> etcdDiscoveryInstances.stream() + .collect( + Collectors.toConcurrentMap(instance -> prefixPath + "/" + instance.getInstanceId(), Function.identity()))); + return etcdDiscoveryInstances; } public List getValuesByPrefix(String prefix) { @@ -117,18 +138,23 @@ private List convertServiceInstanceList(List ke List list = Lists.newArrayListWithExpectedSize(keyValueList.size()); for (KeyValue keyValue : keyValueList) { - 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); + 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 findServices(String application) { @@ -159,13 +185,23 @@ public void init() { @Override public void run() { - client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()).build(); + 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(); + SingletonManager.getInstance().destroy(); } } } diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/SingletonManager.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/SingletonManager.java index 7153ff4db14..f1d2a8b1dda 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/SingletonManager.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/SingletonManager.java @@ -36,10 +36,18 @@ public static synchronized SingletonManager getInstance() { return instance; } - public T getSingleton(String key, Function mappingFunction) { + public T computeIfAbsent(String key, Function mappingFunction) { return (T) singletons.computeIfAbsent(key, mappingFunction); } + public T get(String key) { + return (T) singletons.get(key); + } + + public void remove(String key) { + singletons.remove(key); + } + public void destroy() { singletons.clear(); instance = null; From 36864912730cff047ef48436a3209c3035c4b294 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Fri, 25 Oct 2024 16:03:35 +0800 Subject: [PATCH 07/27] fix(provider): fix the content returned by sayHello interface - Removed environment variable acquisition logic - Modify the return value to the request parameter name --- .../org/apache/servicecomb/samples/ProviderController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index 5a6a7e3e7ab..8fc333cee1d 100644 --- 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 @@ -38,7 +38,8 @@ public void setEnvironment(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 " + environment.getProperty("servicecomb.rest.address"); + return "Hello " + name; } @GetMapping("/getConfig") From 4915a8c49b2c5fc2308b7a6d8a51bfde0738b804 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Mon, 28 Oct 2024 10:54:19 +0800 Subject: [PATCH 08/27] refactor(registry-etcd): Refactor the EtcdDiscovery class -Remove SingletonManager class and use ConcurrentHashMapEx instead - Modify the ETCD_DISCOVERY_ROOT path and unify the registration center path format - Optimize the implementation of findServiceInstances and findServices methods - Add watchMap for storing watch clients --- .../src/main/resources/application.yml | 5 -- .../src/main/resources/application.yml | 7 --- .../servicecomb/registry/etcd/EtcdConst.java | 2 +- .../registry/etcd/EtcdDiscovery.java | 20 +++++-- .../registry/etcd/SingletonManager.java | 55 ------------------- 5 files changed, 15 insertions(+), 74 deletions(-) delete mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/SingletonManager.java diff --git a/demo/demo-etcd/consumer/src/main/resources/application.yml b/demo/demo-etcd/consumer/src/main/resources/application.yml index 2bb97c757c7..a63a165189e 100644 --- a/demo/demo-etcd/consumer/src/main/resources/application.yml +++ b/demo/demo-etcd/consumer/src/main/resources/application.yml @@ -24,14 +24,9 @@ servicecomb: group: red registry: etcd: - # enabled: true -# authenticationInfo: chenzhida:chenzhida connectString: http://127.0.0.1:2379 rest: address: 0.0.0.0:9092?websocketEnabled=true server: websocket-prefix: /ws - -# highway: -# address: 0.0.0.0:7092 diff --git a/demo/demo-etcd/provider/src/main/resources/application.yml b/demo/demo-etcd/provider/src/main/resources/application.yml index 55dabdb7040..75b6dee9ea6 100644 --- a/demo/demo-etcd/provider/src/main/resources/application.yml +++ b/demo/demo-etcd/provider/src/main/resources/application.yml @@ -26,19 +26,12 @@ servicecomb: registry: etcd: connectString: http://127.0.0.1:2379 - # config: - # zk: - # connectString: 127.0.0.1:2181 - # instance-tag: config-demo rest: address: 0.0.0.0:9094?websocketEnabled=true server: websocket-prefix: /ws - # highway: - # address: 0.0.0.0:7094 - cors: enabled: true origin: "*" 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 index 1cb0a22a8e1..03b28f0d2ae 100644 --- 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 @@ -20,7 +20,7 @@ public class EtcdConst { public static final String ETCD_REGISTRY_NAME = "etcd-registry"; - public static final String ETCD_DISCOVERY_ROOT = "/servicecomb/etcd/%s"; + public static final String ETCD_DISCOVERY_ROOT = "/servicecomb/registry/%s"; public static final String ETCD_REGISTRY_PREFIX = "servicecomb.registry.etcd"; 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 index e0bf72237ce..10cf2a245f0 100644 --- 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 @@ -27,6 +27,7 @@ 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; @@ -46,6 +47,8 @@ public class EtcdDiscovery implements Discovery { + private final String basePrefix = "instance:"; + private Environment environment; private String basePath; @@ -58,6 +61,10 @@ public class EtcdDiscovery implements Discovery { private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDiscovery.class); + private Map> etcdInstanceMap = new ConcurrentHashMapEx<>(); + + private Map watchMap = new ConcurrentHashMapEx<>(); + @Autowired @SuppressWarnings("unused") public void setEnvironment(Environment environment) { @@ -85,7 +92,7 @@ public boolean enabled(String application, String serviceName) { public List findServiceInstances(String application, String serviceName) { String prefixPath = basePath + "/" + application + "/" + serviceName; - SingletonManager.getInstance().computeIfAbsent(prefixPath, serName -> { + watchMap.computeIfAbsent(prefixPath, serName -> { Watch watchClient = client.getWatchClient(); try { ByteSequence prefixByteSeq = ByteSequence.from(prefixPath, Charset.defaultCharset()); @@ -93,8 +100,8 @@ public List findServiceInstances(String application, Stri prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> { - Map instanceMap = SingletonManager.getInstance() - .get("instance:" + prefixPath); + Map instanceMap = etcdInstanceMap + .get(basePrefix + prefixPath); resp.getEvents().forEach(event -> { String key = event.getKeyValue().getKey().toString(Charset.defaultCharset()); switch (event.getEventType()) { @@ -118,7 +125,7 @@ public List findServiceInstances(String application, Stri List endpointKv = getValuesByPrefix(prefixPath); List etcdDiscoveryInstances = convertServiceInstanceList(endpointKv); - SingletonManager.getInstance().computeIfAbsent("instance:" + prefixPath, v -> etcdDiscoveryInstances.stream() + etcdInstanceMap.computeIfAbsent(basePrefix + prefixPath, v -> etcdDiscoveryInstances.stream() .collect( Collectors.toConcurrentMap(instance -> prefixPath + "/" + instance.getInstanceId(), Function.identity()))); return etcdDiscoveryInstances; @@ -160,7 +167,7 @@ public List findServices(String application) { String prefixPath = basePath + "/" + application; List endpointKv = getValuesByPrefix(prefixPath); - return endpointKv.stream().map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) // 转换 ByteSequence 为 String + return endpointKv.stream().map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) .collect(Collectors.toList()); } @@ -201,7 +208,8 @@ public void run() { public void destroy() { if (client != null) { client.close(); - SingletonManager.getInstance().destroy(); + etcdInstanceMap = null; + watchMap = null; } } } diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/SingletonManager.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/SingletonManager.java deleted file mode 100644 index f1d2a8b1dda..00000000000 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/SingletonManager.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.registry.etcd; - -import java.util.function.Function; - -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; - -public class SingletonManager { - - private static SingletonManager instance; - - private ConcurrentHashMapEx singletons = new ConcurrentHashMapEx<>(); - - private SingletonManager() { - } - - public static synchronized SingletonManager getInstance() { - if (instance == null) { - instance = new SingletonManager(); - } - return instance; - } - - public T computeIfAbsent(String key, Function mappingFunction) { - return (T) singletons.computeIfAbsent(key, mappingFunction); - } - - public T get(String key) { - return (T) singletons.get(key); - } - - public void remove(String key) { - singletons.remove(key); - } - - public void destroy() { - singletons.clear(); - instance = null; - } -} From cc6c8008f50c0cd98262a4145418ea1ca302b910 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Mon, 28 Oct 2024 20:52:16 +0800 Subject: [PATCH 09/27] refactor(registry-etcd): Refactor the EtcdDiscovery class and update related dependencies - Refactored the EtcdDiscovery class and removed unnecessary variables and methods - Optimized the acquisition and monitoring logic of service instances - Updated protobuf version to 3.25.3 - Removed unnecessary protobuf-java dependency in demo project --- demo/demo-etcd/consumer/pom.xml | 6 -- demo/demo-etcd/provider/pom.xml | 6 -- dependencies/default/pom.xml | 4 +- .../registry/etcd/EtcdDiscovery.java | 62 +++++++------------ 4 files changed, 24 insertions(+), 54 deletions(-) diff --git a/demo/demo-etcd/consumer/pom.xml b/demo/demo-etcd/consumer/pom.xml index cbccb498a4c..c066d3e58b3 100644 --- a/demo/demo-etcd/consumer/pom.xml +++ b/demo/demo-etcd/consumer/pom.xml @@ -34,12 +34,6 @@ org.apache.servicecomb java-chassis-spring-boot-starter-standalone - - com.google.protobuf - protobuf-java - 3.25.3 - runtime - org.apache.servicecomb registry-etcd diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml index c1413de54ce..3648c524c3a 100644 --- a/demo/demo-etcd/provider/pom.xml +++ b/demo/demo-etcd/provider/pom.xml @@ -38,12 +38,6 @@ org.apache.servicecomb java-chassis-spring-boot-starter-standalone - - com.google.protobuf - protobuf-java - 3.25.3 - runtime - org.apache.servicecomb registry-etcd diff --git a/dependencies/default/pom.xml b/dependencies/default/pom.xml index d9cf1966f0f..ebf232377cb 100644 --- a/dependencies/default/pom.xml +++ b/dependencies/default/pom.xml @@ -74,7 +74,7 @@ 4.1.113.Final 4.12.0 0.16.0 - 3.23.4 + 3.25.3 1.8.0 2.2.27 1.0.4 @@ -190,7 +190,7 @@ com.google.protobuf protobuf-java ${protobuf.version} - test + 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 index 10cf2a245f0..3f588826986 100644 --- 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 @@ -18,11 +18,9 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.function.Function; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -47,8 +45,6 @@ public class EtcdDiscovery implements Discovery { - private final String basePrefix = "instance:"; - private Environment environment; private String basePath; @@ -61,10 +57,9 @@ public class EtcdDiscovery implements Discovery { private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDiscovery.class); - private Map> etcdInstanceMap = new ConcurrentHashMapEx<>(); - private Map watchMap = new ConcurrentHashMapEx<>(); + @Autowired @SuppressWarnings("unused") public void setEnvironment(Environment environment) { @@ -96,27 +91,8 @@ public List findServiceInstances(String application, Stri Watch watchClient = client.getWatchClient(); try { ByteSequence prefixByteSeq = ByteSequence.from(prefixPath, Charset.defaultCharset()); - watchClient.watch( - prefixByteSeq, - WatchOption.builder().withPrefix(prefixByteSeq).build(), - resp -> { - Map instanceMap = etcdInstanceMap - .get(basePrefix + prefixPath); - resp.getEvents().forEach(event -> { - String key = event.getKeyValue().getKey().toString(Charset.defaultCharset()); - switch (event.getEventType()) { - case PUT -> { - EtcdDiscoveryInstance newInstance = getEtcdDiscoveryInstance(event.getKeyValue()); - instanceMap.put(key, newInstance); - } - case DELETE -> instanceMap.remove(key); - default -> LOGGER.error("unkonw event"); - } - }); - List discoveryInstanceList = new ArrayList<>(instanceMap.values()); - instanceChangedListener.onInstanceChanged(name(), application, serviceName, discoveryInstanceList); - } - ); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> watchNode(application, serviceName, prefixPath)); } catch (Exception e) { LOGGER.error("Failed to add watch", e); } @@ -124,14 +100,24 @@ public List findServiceInstances(String application, Stri }); List endpointKv = getValuesByPrefix(prefixPath); - List etcdDiscoveryInstances = convertServiceInstanceList(endpointKv); - etcdInstanceMap.computeIfAbsent(basePrefix + prefixPath, v -> etcdDiscoveryInstances.stream() - .collect( - Collectors.toConcurrentMap(instance -> prefixPath + "/" + instance.getInstanceId(), Function.identity()))); - return etcdDiscoveryInstances; + return convertServiceInstanceList(endpointKv); + } + + private void watchNode(String application, String serviceName, String prefixPath) { + + CompletableFuture getFuture = client.getKVClient() + .get(ByteSequence.from(prefixPath, StandardCharsets.UTF_8), + GetOption.builder().withPrefix(ByteSequence.from(prefixPath, StandardCharsets.UTF_8)).build()); + getFuture.thenAcceptAsync(response -> { + List discoveryInstanceList = convertServiceInstanceList(response.getKvs()); + instanceChangedListener.onInstanceChanged(name(), application, serviceName, discoveryInstanceList); + }).exceptionally(e -> { + LOGGER.error("watchNode error", e); + return null; + }); } - public List getValuesByPrefix(String prefix) { + private List getValuesByPrefix(String prefix) { CompletableFuture getFuture = client.getKVClient() .get(ByteSequence.from(prefix, StandardCharsets.UTF_8), @@ -167,8 +153,7 @@ public List findServices(String application) { String prefixPath = basePath + "/" + application; List endpointKv = getValuesByPrefix(prefixPath); - return endpointKv.stream().map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) - .collect(Collectors.toList()); + return endpointKv.stream().map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)).collect(Collectors.toList()); } @Override @@ -196,11 +181,9 @@ public void run() { this.client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()).build(); } else { String[] authInfo = etcdRegistryProperties.getAuthenticationInfo().split(":"); - this.client = Client.builder() - .endpoints(etcdRegistryProperties.getConnectString()) + this.client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) .user(ByteSequence.from(authInfo[0], Charset.defaultCharset())) - .password(ByteSequence.from(authInfo[1], Charset.defaultCharset())) - .build(); + .password(ByteSequence.from(authInfo[1], Charset.defaultCharset())).build(); } } @@ -208,7 +191,6 @@ public void run() { public void destroy() { if (client != null) { client.close(); - etcdInstanceMap = null; watchMap = null; } } From 0cdcbca1f12d12d836bfb98f8ca3bc9ccdc08ed8 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Tue, 29 Oct 2024 10:24:37 +0800 Subject: [PATCH 10/27] restore runnable version --- demo/demo-etcd/consumer/pom.xml | 6 ++++++ demo/demo-etcd/provider/pom.xml | 6 ++++++ dependencies/default/pom.xml | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/demo/demo-etcd/consumer/pom.xml b/demo/demo-etcd/consumer/pom.xml index c066d3e58b3..cbccb498a4c 100644 --- a/demo/demo-etcd/consumer/pom.xml +++ b/demo/demo-etcd/consumer/pom.xml @@ -34,6 +34,12 @@ org.apache.servicecomb java-chassis-spring-boot-starter-standalone + + com.google.protobuf + protobuf-java + 3.25.3 + runtime + org.apache.servicecomb registry-etcd diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml index 3648c524c3a..c1413de54ce 100644 --- a/demo/demo-etcd/provider/pom.xml +++ b/demo/demo-etcd/provider/pom.xml @@ -38,6 +38,12 @@ org.apache.servicecomb java-chassis-spring-boot-starter-standalone + + com.google.protobuf + protobuf-java + 3.25.3 + runtime + org.apache.servicecomb registry-etcd diff --git a/dependencies/default/pom.xml b/dependencies/default/pom.xml index ebf232377cb..d9cf1966f0f 100644 --- a/dependencies/default/pom.xml +++ b/dependencies/default/pom.xml @@ -74,7 +74,7 @@ 4.1.113.Final 4.12.0 0.16.0 - 3.25.3 + 3.23.4 1.8.0 2.2.27 1.0.4 @@ -190,7 +190,7 @@ com.google.protobuf protobuf-java ${protobuf.version} - + test From 2c06dc986485f71b19aa39a28d06f4888a3651b5 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Fri, 8 Nov 2024 11:19:07 +0800 Subject: [PATCH 11/27] feat(config): Add etcd configuration support - Added EtcdClient class to implement etcd client function - Add EtcdConfig class to handle etcd configuration parameters - Implement the EtcdDynamicPropertiesSource class to support dynamic configuration -Added MuteExceptionUtil tool class to handle exceptions - Update application.yml file to add etcd configuration - Modify the pom.xml file to add etcd related dependencies --- demo/demo-etcd/provider/pom.xml | 16 + .../src/main/resources/application.yml | 3 + demo/demo-etcd/test-client/pom.xml | 10 + .../servicecomb/samples/EtcdConfigIT.java | 100 +++++++ .../src/main/resources/application.yml | 11 + .../apache/servicecomb/samples/EtcdIT.java | 2 +- dependencies/bom/pom.xml | 5 + dynamic-config/config-etcd/pom.xml | 44 +++ .../servicecomb/config/etcd/EtcdClient.java | 277 ++++++++++++++++++ .../servicecomb/config/etcd/EtcdConfig.java | 65 ++++ .../etcd/EtcdDynamicPropertiesSource.java | 73 +++++ .../config/etcd/MuteExceptionUtil.java | 94 ++++++ ...servicecomb.config.DynamicPropertiesSource | 18 ++ dynamic-config/pom.xml | 1 + 14 files changed, 718 insertions(+), 1 deletion(-) create mode 100644 demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java create mode 100644 dynamic-config/config-etcd/pom.xml create mode 100644 dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java create mode 100644 dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java create mode 100644 dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java create mode 100644 dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/MuteExceptionUtil.java create mode 100644 dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml index c1413de54ce..244e40592dc 100644 --- a/demo/demo-etcd/provider/pom.xml +++ b/demo/demo-etcd/provider/pom.xml @@ -47,6 +47,22 @@ org.apache.servicecomb registry-etcd + + + commons-logging + commons-logging + + + + + org.apache.servicecomb + config-etcd + + + commons-logging + commons-logging + + diff --git a/demo/demo-etcd/provider/src/main/resources/application.yml b/demo/demo-etcd/provider/src/main/resources/application.yml index 75b6dee9ea6..9729755d671 100644 --- a/demo/demo-etcd/provider/src/main/resources/application.yml +++ b/demo/demo-etcd/provider/src/main/resources/application.yml @@ -26,6 +26,9 @@ servicecomb: registry: etcd: connectString: http://127.0.0.1:2379 + config: + etcd: + connectString: http://127.0.0.1:2379 rest: address: 0.0.0.0:9094?websocketEnabled=true diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml index 0c050adcb85..a47ce119863 100644 --- a/demo/demo-etcd/test-client/pom.xml +++ b/demo/demo-etcd/test-client/pom.xml @@ -46,6 +46,16 @@ org.apache.servicecomb registry-local + + io.etcd + jetcd-core + + + com.google.protobuf + protobuf-java + 3.25.3 + runtime + diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java new file mode 100644 index 00000000000..a9337943ee5 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -0,0 +1,100 @@ +/* + * 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.nio.charset.StandardCharsets; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +import io.etcd.jetcd.Client; + +@Component +public class EtcdConfigIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + + testEnvironment(); + testApplication(); + testService(); + testVersion(); + testTag(); + } + + private void testEnvironment() { + + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env1"); + testGetConfig("test1", "env1"); + } + + private void testApplication() { + + putValue("/servicecomb/config/environment/production/demo-etcd/application.properties", + "test2=applition2"); + testGetConfig("test2", "applition2"); + } + + private void testService() { + + putValue("/servicecomb/config/environment/production/demo-etcd/test-client/application.properties", + "test3=service3"); + testGetConfig("test3", "service3"); + } + + private void testVersion() { + + putValue("/servicecomb/config/environment/production/demo-etcd/test-client/0.0.1/application.properties", + "test4=version4"); + testGetConfig("test4", "version4"); + } + + private void testTag() { + + putValue("/servicecomb/config/environment/production/demo-etcd/test-client/0.0.1/tag1/application.properties", + "test5=tag5"); + testGetConfig("test5", "tag5"); + } + + + public void putValue(String key, String value) { + try (Client client = Client.builder().endpoints("http://localhost:2379").build()) { + + client.getKVClient().put( + io.etcd.jetcd.ByteSequence.from(key, StandardCharsets.UTF_8), + io.etcd.jetcd.ByteSequence.from(value, StandardCharsets.UTF_8) + ).get(); + + System.out.println("Value set successfully"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void testGetConfig(String key, String expectValue) { + + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=" + key, String.class); + TestMgr.check(expectValue, result); + } +} diff --git a/demo/demo-etcd/test-client/src/main/resources/application.yml b/demo/demo-etcd/test-client/src/main/resources/application.yml index 396bebfd853..89299054c3e 100644 --- a/demo/demo-etcd/test-client/src/main/resources/application.yml +++ b/demo/demo-etcd/test-client/src/main/resources/application.yml @@ -23,3 +23,14 @@ servicecomb: rest: address: 0.0.0.0:9097 # should be same with server.port to use web container + + config: + etcd: + instance-tag: tag1 + +test2: applition +test3: service +test4: version +test5: tag + + 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 index 833feff87c0..a5602fe2830 100644 --- 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 @@ -30,7 +30,7 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(classes = TestClientApplication.class) public class EtcdIT { - private static final Logger LOGGER = LoggerFactory.getLogger(EtcdIT.class); + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdConfigIT.class); @BeforeEach public void setUp() { diff --git a/dependencies/bom/pom.xml b/dependencies/bom/pom.xml index f2218783fb2..16b2dea9845 100644 --- a/dependencies/bom/pom.xml +++ b/dependencies/bom/pom.xml @@ -283,6 +283,11 @@ registry-etcd ${project.version} + + org.apache.servicecomb + config-etcd + ${project.version} + org.apache.servicecomb diff --git a/dynamic-config/config-etcd/pom.xml b/dynamic-config/config-etcd/pom.xml new file mode 100644 index 00000000000..a1c35d9872f --- /dev/null +++ b/dynamic-config/config-etcd/pom.xml @@ -0,0 +1,44 @@ + + + + + + + dynamic-config + org.apache.servicecomb + 3.3.0-SNAPSHOT + + 4.0.0 + config-etcd + Java Chassis::Dynamic Config::Zookeeper + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-vertx + + + io.etcd + jetcd-core + + + diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java new file mode 100644 index 00000000000..e3018c2a441 --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java @@ -0,0 +1,277 @@ +/* + * 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.config.etcd; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.etcd.EtcdDynamicPropertiesSource.UpdateHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ByteArrayResource; + +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 EtcdClient { + + public class GetDataRunable implements Runnable { + + private Map dataMap; + + private EtcdClient etcdClient; + + private String path; + + public GetDataRunable(Map dataMap, EtcdClient etcdClient, String path) { + this.dataMap = dataMap; + this.etcdClient = etcdClient; + this.path = path; + } + + @Override + public void run() { + try { + dataMap.clear(); + dataMap.putAll(etcdClient.parseData(path)); + refreshConfigItems(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdClient.class); + + public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; + + public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; + + public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; + + public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; + + public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; + + private final UpdateHandler updateHandler; + + private final EtcdConfig etcdConfig; + + private final Environment environment; + + private final Object lock = new Object(); + + private Map environmentData = new HashMap<>(); + + private Map applicationData = new HashMap<>(); + + private Map serviceData = new HashMap<>(); + + private Map versionData = new HashMap<>(); + + private Map tagData = new HashMap<>(); + + private Map allLast = new HashMap<>(); + + private Client client; + + public EtcdClient(UpdateHandler updateHandler, Environment environment) { + this.updateHandler = updateHandler; + this.etcdConfig = new EtcdConfig(environment); + this.environment = environment; + } + + public void getClient() { + if (StringUtils.isEmpty(etcdConfig.getAuthInfo())) { + this.client = Client.builder().endpoints(etcdConfig.getConnectString()).build(); + } else { + String[] authInfo = etcdConfig.getAuthInfo().split(":"); + this.client = Client.builder().endpoints(etcdConfig.getConnectString()) + .user(ByteSequence.from(authInfo[0], Charset.defaultCharset())) + .password(ByteSequence.from(authInfo[1], Charset.defaultCharset())).build(); + } + } + + public void refreshEtcdConfig() throws Exception { + + getClient(); + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = EtcdConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + addEnvironmentConfig(env); + addApplicationConfig(env); + addServiceConfig(env); + addVersionConfig(env); + addTagConfig(env); + + refreshConfigItems(); + } + + private void addTagConfig(String env) throws Exception { + if (StringUtils.isEmpty(etcdConfig.getInstanceTag())) { + return; + } + String path = String.format(PATH_TAG, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment), + etcdConfig.getInstanceTag()); + + ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> { + new Thread(new GetDataRunable(tagData, this, path)).start(); + }); + this.tagData = parseData(path); + } + + private void addVersionConfig(String env) throws Exception { + String path = String.format(PATH_VERSION, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment)); + + ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunable(versionData, this, path)).start()); + this.versionData = parseData(path); + } + + private void addServiceConfig(String env) throws Exception { + String path = String.format(PATH_SERVICE, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment)); + + ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunable(serviceData, this, path)).start()); + this.serviceData = parseData(path); + } + + private void addApplicationConfig(String env) throws Exception { + String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); + + ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunable(applicationData, this, path)).start()); + this.applicationData = parseData(path); + } + + private void addEnvironmentConfig(String env) throws Exception { + String path = String.format(PATH_ENVIRONMENT, env); + + ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunable(environmentData, this, path)).start()); + this.environmentData = parseData(path); + } + + public Map parseData(String path) throws Exception { + + List endpointKv = getValuesByPrefix(path); + return getValues(path, endpointKv); + } + + private Map getValues(String path, List endpointKv) { + Map values = new HashMap<>(); + for (KeyValue keyValue : endpointKv) { + String key = new String(keyValue.getKey().getBytes(), StandardCharsets.UTF_8); + String value = new String(keyValue.getValue().getBytes(), StandardCharsets.UTF_8); + if (key.equals(path)) { + continue; + } + if (key.endsWith(".yaml") || key.endsWith(".yml")) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(value.getBytes(StandardCharsets.UTF_8))); + values.putAll(toMap(yamlFactory.getObject())); + } else if (key.endsWith(".properties")) { + Properties properties = new Properties(); + try { + properties.load(new StringReader(value)); + } catch (IOException e) { + LOGGER.error("load error"); + } + values.putAll(toMap(properties)); + } else { + values.put(key, value); + } + } + return values; + } + + private List getValuesByPrefix(String prefix) { + + CompletableFuture 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 void refreshConfigItems() { + synchronized (lock) { + Map all = new HashMap<>(); + all.putAll(environmentData); + all.putAll(applicationData); + all.putAll(serviceData); + all.putAll(versionData); + all.putAll(tagData); + updateHandler.handle(all, allLast); + this.allLast = all; + } + } + + @SuppressWarnings("unchecked") + private Map toMap(Properties properties) { + if (properties == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + Enumeration keys = (Enumeration) properties.propertyNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + Object value = properties.getProperty(key); + result.put(key, value); + } + return result; + } +} diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java new file mode 100644 index 00000000000..57713eed069 --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java @@ -0,0 +1,65 @@ +/* + * 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.config.etcd; + +import org.springframework.core.env.Environment; + +public class EtcdConfig { + public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; + + public static final String PROPERTY_CONNECT_STRING = "servicecomb.config.etcd.connect-string"; + + public static final String PROPERTY_SESSION_TIMEOUT = "servicecomb.config.etcd.session-timeout-millis"; + + public static final String PROPERTY_CONNECTION_TIMEOUT = "servicecomb.config.etcd.connection-timeout-mills"; + + public static final String PROPERTY_AUTH_SCHEMA = "servicecomb.config.etcd.authentication-schema"; + + public static final String PROPERTY_AUTH_INFO = "servicecomb.config.etcd.authentication-info"; + + public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.etcd.instance-tag"; + + private final Environment environment; + + public EtcdConfig(Environment environment) { + this.environment = environment; + } + + public String getConnectString() { + return environment.getProperty(PROPERTY_CONNECT_STRING, "127.0.0.1:2181"); + } + + public int getSessionTimeoutMillis() { + return environment.getProperty(PROPERTY_SESSION_TIMEOUT, int.class, 60000); + } + + public int getConnectionTimeoutMillis() { + return environment.getProperty(PROPERTY_CONNECTION_TIMEOUT, int.class, 1000); + } + + public String getAuthSchema() { + return environment.getProperty(PROPERTY_AUTH_SCHEMA); + } + + public String getAuthInfo() { + return environment.getProperty(PROPERTY_AUTH_INFO); + } + + public String getInstanceTag() { + return environment.getProperty(PROPERTY_INSTANCE_TAG); + } +} diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java new file mode 100644 index 00000000000..64defb82569 --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java @@ -0,0 +1,73 @@ +/* + * 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.config.etcd; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +public class EtcdDynamicPropertiesSource implements DynamicPropertiesSource { + public static final String SOURCE_NAME = "etcd"; + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDynamicPropertiesSource.class); + + private final Map valueCache = new ConcurrentHashMap<>(); + + public EtcdDynamicPropertiesSource() { + } + + private final UpdateHandler updateHandler = new UpdateHandler(); + + private void init(Environment environment) { + EtcdClient etcdClient = new EtcdClient(updateHandler, environment); + try { + etcdClient.refreshEtcdConfig(); + } catch (Exception e) { + throw new IllegalStateException("Set up zookeeper config failed.", e); + } + } + + public class UpdateHandler { + public void handle(Map current, Map last) { + ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + valueCache.putAll(event.getAdded()); + valueCache.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> valueCache.remove(k)); + EventManager.post(event); + } + } + + @Override + public PropertySource create(Environment environment) { + init(environment); + return new MapPropertySource(SOURCE_NAME, valueCache); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/MuteExceptionUtil.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/MuteExceptionUtil.java new file mode 100644 index 00000000000..3d6880249db --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/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.config.etcd; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MuteExceptionUtil { + + interface FunctionWithException { + R apply(T t) throws Exception; + } + + interface FunctionWithDoubleParam { + 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 R executeFunction(FunctionWithException function, T t) { + try { + return function.apply(t); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Function failure..."), customMessageParams, e); + return null; + } + } + + public T executeSupplier(Supplier supplier) { + try { + return supplier.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Supplier failure..."), customMessageParams, e); + return null; + } + } + + public T executeCompletableFuture(CompletableFuture completableFuture) { + try { + return completableFuture.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute CompletableFuture failure..."), customMessageParams, e); + return null; + } + } + + public R executeFunctionWithDoubleParam(FunctionWithDoubleParam 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/dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..d10124395fa --- /dev/null +++ b/dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -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.config.etcd.EtcdDynamicPropertiesSource diff --git a/dynamic-config/pom.xml b/dynamic-config/pom.xml index 4189d8817f0..540381370f9 100644 --- a/dynamic-config/pom.xml +++ b/dynamic-config/pom.xml @@ -37,6 +37,7 @@ config-nacos config-kie config-zookeeper + config-etcd From 19d612f7f28686aa063a55d3980571ad1735bd0b Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Mon, 11 Nov 2024 20:46:13 +0800 Subject: [PATCH 12/27] Removed redundant annotations in MuteExceptionUtil Removed redundant annotations on the executeFunction method of the MuteExceptionUtil class in the config-etcd and registry-etcd modules. --- .../org/apache/servicecomb/config/etcd/MuteExceptionUtil.java | 1 - .../org/apache/servicecomb/registry/etcd/MuteExceptionUtil.java | 1 - 2 files changed, 2 deletions(-) diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/MuteExceptionUtil.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/MuteExceptionUtil.java index 3d6880249db..bc0e237b01f 100644 --- a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/MuteExceptionUtil.java +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/MuteExceptionUtil.java @@ -50,7 +50,6 @@ private String getLogMessage(String defaultMessage) { return logMessage != null ? logMessage : defaultMessage; } - // 执行带异常处理的Function public R executeFunction(FunctionWithException function, T t) { try { return function.apply(t); 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 index 83041cacfbc..eea59100450 100644 --- 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 @@ -50,7 +50,6 @@ private String getLogMessage(String defaultMessage) { return logMessage != null ? logMessage : defaultMessage; } - // 执行带异常处理的Function public R executeFunction(FunctionWithException function, T t) { try { return function.apply(t); From ce033dc9024dff5fc085a0307e96b2576e593846 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Mon, 11 Nov 2024 20:55:37 +0800 Subject: [PATCH 13/27] refactor:delete unused {} --- .../java/org/apache/servicecomb/config/etcd/EtcdClient.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java index e3018c2a441..5f5a630fd56 100644 --- a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java @@ -153,9 +153,7 @@ private void addTagConfig(String env) throws Exception { ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), - resp -> { - new Thread(new GetDataRunable(tagData, this, path)).start(); - }); + resp -> new Thread(new GetDataRunable(tagData, this, path)).start()); this.tagData = parseData(path); } From e7c39700060877697566c50a20dbe11240d34324 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Mon, 11 Nov 2024 20:59:51 +0800 Subject: [PATCH 14/27] refactor:restore protobuf version --- demo/demo-etcd/consumer/pom.xml | 2 +- demo/demo-etcd/gateway/pom.xml | 2 +- demo/demo-etcd/provider/pom.xml | 2 +- demo/demo-etcd/test-client/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/demo/demo-etcd/consumer/pom.xml b/demo/demo-etcd/consumer/pom.xml index cbccb498a4c..5f79529d65c 100644 --- a/demo/demo-etcd/consumer/pom.xml +++ b/demo/demo-etcd/consumer/pom.xml @@ -37,7 +37,7 @@ com.google.protobuf protobuf-java - 3.25.3 + 3.25.5 runtime diff --git a/demo/demo-etcd/gateway/pom.xml b/demo/demo-etcd/gateway/pom.xml index b2eb1e51df2..9bed75de856 100644 --- a/demo/demo-etcd/gateway/pom.xml +++ b/demo/demo-etcd/gateway/pom.xml @@ -41,7 +41,7 @@ com.google.protobuf protobuf-java - 3.25.3 + 3.25.5 runtime diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml index 244e40592dc..83585c6b26b 100644 --- a/demo/demo-etcd/provider/pom.xml +++ b/demo/demo-etcd/provider/pom.xml @@ -41,7 +41,7 @@ com.google.protobuf protobuf-java - 3.25.3 + 3.25.5 runtime diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml index a47ce119863..a16754c448d 100644 --- a/demo/demo-etcd/test-client/pom.xml +++ b/demo/demo-etcd/test-client/pom.xml @@ -53,7 +53,7 @@ com.google.protobuf protobuf-java - 3.25.3 + 3.25.5 runtime From 6ce7fc3a566e0893e0ddf69b3593ab7bee878eef Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Tue, 12 Nov 2024 10:59:01 +0800 Subject: [PATCH 15/27] refactor:support arm architecter --- demo/docker-build-config/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/docker-build-config/pom.xml b/demo/docker-build-config/pom.xml index 746be574983..597a65aee17 100644 --- a/demo/docker-build-config/pom.xml +++ b/demo/docker-build-config/pom.xml @@ -42,7 +42,7 @@ ${project.artifactId}:${project.version} ${project.artifactId} - openjdk:17-alpine + openjdk:17-slim 7070 8080 From ff0fe3cf58b54a5d1972ab4284c26ed107f166b5 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Tue, 12 Nov 2024 11:22:46 +0800 Subject: [PATCH 16/27] build(docker): restore openjdk:17-alpine --- demo/docker-build-config/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/docker-build-config/pom.xml b/demo/docker-build-config/pom.xml index 597a65aee17..746be574983 100644 --- a/demo/docker-build-config/pom.xml +++ b/demo/docker-build-config/pom.xml @@ -42,7 +42,7 @@ ${project.artifactId}:${project.version} ${project.artifactId} - openjdk:17-slim + openjdk:17-alpine 7070 8080 From b4f8c5686fae1a8da304a94ce6699f346d4d585f Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Tue, 12 Nov 2024 14:58:08 +0800 Subject: [PATCH 17/27] test(etcd): add log and remove test --- demo/demo-etcd/test-client/pom.xml | 8 +++++++- .../java/org/apache/servicecomb/samples/EtcdConfigIT.java | 8 ++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml index a16754c448d..60e58f2fc49 100644 --- a/demo/demo-etcd/test-client/pom.xml +++ b/demo/demo-etcd/test-client/pom.xml @@ -45,6 +45,12 @@ org.apache.servicecomb registry-local + + + commons-logging + commons-logging + + io.etcd @@ -70,7 +76,7 @@ - bitnami/etcd:latest + bitnami/etcd:3.5.16 etcd alias diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java index a9337943ee5..94d27fd23e0 100644 --- a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -21,16 +21,20 @@ import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; import io.etcd.jetcd.Client; -@Component +//@Component public class EtcdConfigIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdConfigIT.class); + @Override public void testRestTransport() throws Exception { @@ -85,7 +89,7 @@ public void putValue(String key, String value) { io.etcd.jetcd.ByteSequence.from(value, StandardCharsets.UTF_8) ).get(); - System.out.println("Value set successfully"); + LOGGER.info("Value set successfully"); } catch (Exception e) { e.printStackTrace(); } From 39e9873ba72b79ffee7850057aa2e288b90f6b5b Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Tue, 12 Nov 2024 15:15:11 +0800 Subject: [PATCH 18/27] test(etcd): test whether location is etcd --- .../org/apache/servicecomb/samples/EtcdConfigIT.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java index 94d27fd23e0..0a06c42c498 100644 --- a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -27,9 +27,10 @@ import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; +import io.etcd.jetcd.ByteSequence; import io.etcd.jetcd.Client; -//@Component +@Component public class EtcdConfigIT implements CategorizedTestCase { RestOperations template = new RestTemplate(); @@ -82,11 +83,11 @@ private void testTag() { public void putValue(String key, String value) { - try (Client client = Client.builder().endpoints("http://localhost:2379").build()) { + try (Client client = Client.builder().endpoints("http://etcd:2379").build()) { client.getKVClient().put( - io.etcd.jetcd.ByteSequence.from(key, StandardCharsets.UTF_8), - io.etcd.jetcd.ByteSequence.from(value, StandardCharsets.UTF_8) + ByteSequence.from(key, StandardCharsets.UTF_8), + ByteSequence.from(value, StandardCharsets.UTF_8) ).get(); LOGGER.info("Value set successfully"); From af2cda27f42fac584223a5081dbb390ec335ce2c Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Tue, 12 Nov 2024 15:35:47 +0800 Subject: [PATCH 19/27] test(etcd): test --- .../org/apache/servicecomb/samples/EtcdConfigIT.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java index 0a06c42c498..5992fcdb59f 100644 --- a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -39,11 +39,12 @@ public class EtcdConfigIT implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { - testEnvironment(); - testApplication(); - testService(); - testVersion(); - testTag(); + LOGGER.error("test..."); +// testEnvironment(); +// testApplication(); +// testService(); +// testVersion(); +// testTag(); } private void testEnvironment() { From 003bf612610941acbfc9887c295d39163dedcfec Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Tue, 12 Nov 2024 15:39:12 +0800 Subject: [PATCH 20/27] test(etcd): test --- .../java/org/apache/servicecomb/samples/EtcdConfigIT.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java index 5992fcdb59f..e10b0803cc0 100644 --- a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -39,8 +39,7 @@ public class EtcdConfigIT implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { - LOGGER.error("test..."); -// testEnvironment(); + testEnvironment(); // testApplication(); // testService(); // testVersion(); @@ -51,7 +50,7 @@ private void testEnvironment() { putValue("/servicecomb/config/environment/production/application.properties", "test1=env1"); - testGetConfig("test1", "env1"); +// testGetConfig("test1", "env1"); } private void testApplication() { From cb3912bdf9123849d7d48583b9f2978522d582a0 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Tue, 12 Nov 2024 15:48:04 +0800 Subject: [PATCH 21/27] test(etcd): test --- demo/demo-etcd/test-client/pom.xml | 2 +- .../org/apache/servicecomb/samples/EtcdConfigIT.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml index 60e58f2fc49..60681baf6e4 100644 --- a/demo/demo-etcd/test-client/pom.xml +++ b/demo/demo-etcd/test-client/pom.xml @@ -90,7 +90,7 @@ - etcd.port:2379 + 2379:2379 yes diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java index e10b0803cc0..c0aab74e193 100644 --- a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -40,17 +40,17 @@ public class EtcdConfigIT implements CategorizedTestCase { public void testRestTransport() throws Exception { testEnvironment(); -// testApplication(); -// testService(); -// testVersion(); -// testTag(); + testApplication(); + testService(); + testVersion(); + testTag(); } private void testEnvironment() { putValue("/servicecomb/config/environment/production/application.properties", "test1=env1"); -// testGetConfig("test1", "env1"); + testGetConfig("test1", "env1"); } private void testApplication() { @@ -83,7 +83,7 @@ private void testTag() { public void putValue(String key, String value) { - try (Client client = Client.builder().endpoints("http://etcd:2379").build()) { + try (Client client = Client.builder().endpoints("http://localhost:2379").build()) { client.getKVClient().put( ByteSequence.from(key, StandardCharsets.UTF_8), From cd555284da7dce50872eb0604a000fd875fd3802 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Wed, 13 Nov 2024 10:31:03 +0800 Subject: [PATCH 22/27] test(demo-etcd): Update the test case to verify the source of the configuration item - add a new configuration item test1 in application.yml - Updated test cases to check correct loading of environment, application, service, version and tag configurations - Modify the path of configuration items to adapt to the new hierarchical structure --- .../servicecomb/samples/EtcdConfigIT.java | 18 ++++++++++++++---- .../src/main/resources/application.yml | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java index c0aab74e193..7dc69ebc86d 100644 --- a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -48,6 +48,8 @@ public void testRestTransport() throws Exception { private void testEnvironment() { + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env"); putValue("/servicecomb/config/environment/production/application.properties", "test1=env1"); testGetConfig("test1", "env1"); @@ -55,28 +57,36 @@ private void testEnvironment() { private void testApplication() { - putValue("/servicecomb/config/environment/production/demo-etcd/application.properties", + putValue("/servicecomb/config/application/production/demo-etcd/application.properties", + "test2=applition"); + putValue("/servicecomb/config/application/production/demo-etcd/application.properties", "test2=applition2"); testGetConfig("test2", "applition2"); } private void testService() { - putValue("/servicecomb/config/environment/production/demo-etcd/test-client/application.properties", + putValue("/servicecomb/config/service/production/demo-etcd/test-client/application.properties", + "test3=service"); + putValue("/servicecomb/config/service/production/demo-etcd/test-client/application.properties", "test3=service3"); testGetConfig("test3", "service3"); } private void testVersion() { - putValue("/servicecomb/config/environment/production/demo-etcd/test-client/0.0.1/application.properties", + putValue("/servicecomb/config/version/production/demo-etcd/test-client/application.properties", + "test3=version"); + putValue("/servicecomb/config/version/production/demo-etcd/test-client/0.0.1/application.properties", "test4=version4"); testGetConfig("test4", "version4"); } private void testTag() { - putValue("/servicecomb/config/environment/production/demo-etcd/test-client/0.0.1/tag1/application.properties", + putValue("/servicecomb/config/tag/production/demo-etcd/test-client/0.0.1/tag1/application.properties", + "test5=tag"); + putValue("/servicecomb/config/tag/production/demo-etcd/test-client/0.0.1/tag1/application.properties", "test5=tag5"); testGetConfig("test5", "tag5"); } diff --git a/demo/demo-etcd/test-client/src/main/resources/application.yml b/demo/demo-etcd/test-client/src/main/resources/application.yml index 89299054c3e..4245a4d2c86 100644 --- a/demo/demo-etcd/test-client/src/main/resources/application.yml +++ b/demo/demo-etcd/test-client/src/main/resources/application.yml @@ -28,6 +28,7 @@ servicecomb: etcd: instance-tag: tag1 +test1: env test2: applition test3: service test4: version From e099d8db66a90a43a6f8c68f6bf3f27b0705f018 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Wed, 13 Nov 2024 14:54:37 +0800 Subject: [PATCH 23/27] fix(config-etcd): Fix configuration update exception information - Corrected "zookeeper" in the exception message to "etcd" to ensure that the exception information is consistent with the actual configuration source used - This modification improves the accuracy and readability of the error message and helps to quickly locate and solve the problem --- .../servicecomb/config/etcd/EtcdDynamicPropertiesSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java index 64defb82569..a86de892180 100644 --- a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java @@ -45,7 +45,7 @@ private void init(Environment environment) { try { etcdClient.refreshEtcdConfig(); } catch (Exception e) { - throw new IllegalStateException("Set up zookeeper config failed.", e); + throw new IllegalStateException("Set up etcd config failed.", e); } } From 3120ae061cc9ef5226bf9e718375b8900eb14b21 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Wed, 13 Nov 2024 18:05:19 +0800 Subject: [PATCH 24/27] refactor(registry-etcd): Refactor the EtcdDiscovery class and add asynchronous execution support - Added ConditionWaiter class for asynchronous task execution and result waiting - Modify findServices and getInstances methods to asynchronous execution - Remove redundant code and optimize code structure - Update related examples and configuration files to remove commons-logging dependency --- demo/demo-etcd/provider/pom.xml | 12 ---- demo/demo-etcd/test-client/pom.xml | 6 -- foundations/foundation-common/pom.xml | 6 ++ foundations/foundation-config/pom.xml | 6 ++ .../registry/etcd/ConditionWaiter.java | 56 +++++++++++++++++++ .../registry/etcd/EtcdDiscovery.java | 29 +++++++--- 6 files changed, 89 insertions(+), 26 deletions(-) create mode 100644 service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/ConditionWaiter.java diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml index 83585c6b26b..cc926bda1ce 100644 --- a/demo/demo-etcd/provider/pom.xml +++ b/demo/demo-etcd/provider/pom.xml @@ -47,22 +47,10 @@ org.apache.servicecomb registry-etcd - - - commons-logging - commons-logging - - org.apache.servicecomb config-etcd - - - commons-logging - commons-logging - - diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml index 60681baf6e4..d14d1b1c45f 100644 --- a/demo/demo-etcd/test-client/pom.xml +++ b/demo/demo-etcd/test-client/pom.xml @@ -45,12 +45,6 @@ org.apache.servicecomb registry-local - - - commons-logging - commons-logging - - io.etcd diff --git a/foundations/foundation-common/pom.xml b/foundations/foundation-common/pom.xml index c377ef211a9..3e07a454296 100644 --- a/foundations/foundation-common/pom.xml +++ b/foundations/foundation-common/pom.xml @@ -54,6 +54,12 @@ org.apache.httpcomponents httpclient + + + commons-logging + commons-logging + + jakarta.ws.rs diff --git a/foundations/foundation-config/pom.xml b/foundations/foundation-config/pom.xml index 6a3173f50b4..14629e95a32 100644 --- a/foundations/foundation-config/pom.xml +++ b/foundations/foundation-config/pom.xml @@ -74,6 +74,12 @@ org.apache.httpcomponents httpclient + + + commons-logging + commons-logging + + io.netty diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/ConditionWaiter.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/ConditionWaiter.java new file mode 100644 index 00000000000..5607b43a768 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/ConditionWaiter.java @@ -0,0 +1,56 @@ +package org.apache.servicecomb.registry.etcd; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class ConditionWaiter { + private final AtomicReference dataReference; + private final AtomicBoolean isComplete; + private final long sleepDuration; + private final TimeUnit timeUnit; + private final ExecutorService executorService; + + public ConditionWaiter(T initialData, long sleepDuration, TimeUnit timeUnit) { + this.dataReference = new AtomicReference<>(initialData); + this.isComplete = new AtomicBoolean(false); + this.sleepDuration = sleepDuration; + this.timeUnit = timeUnit; + this.executorService = Executors.newSingleThreadExecutor(); + } + + public T waitForCompletion() { + while (!isComplete.get()) { + SleepUtil.sleep(sleepDuration, timeUnit); + } + return dataReference.get(); + } + + public void setData(T newData) { + dataReference.set(newData); + } + + public void executeTaskAsync(Callable task) { + CompletableFuture.supplyAsync(() -> { + try { + return task.call(); + } catch (Exception e) { + throw new RuntimeException("Task execution failed", e); + } + }, executorService).thenAccept(result -> { + setData(result); + isComplete.set(true); + }); + } + + public static class SleepUtil { + public static void sleep(long duration, TimeUnit timeUnit) { + try { + timeUnit.sleep(duration); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("Thread was interrupted during sleep!"); + } + } + } +} \ No newline at end of file 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 index 3f588826986..a7fcfcbc8e6 100644 --- 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 @@ -18,9 +18,11 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -59,7 +61,6 @@ public class EtcdDiscovery implements Discovery { private Map watchMap = new ConcurrentHashMapEx<>(); - @Autowired @SuppressWarnings("unused") public void setEnvironment(Environment environment) { @@ -99,8 +100,17 @@ public List findServiceInstances(String application, Stri return watchClient; }); - List endpointKv = getValuesByPrefix(prefixPath); - return convertServiceInstanceList(endpointKv); +// async get all instances,because sync is bad way in etcd. + ConditionWaiter> waiter = new ConditionWaiter<>(new ArrayList<>(), 50, + TimeUnit.MILLISECONDS); + waiter.executeTaskAsync(() -> { + CompletableFuture getFuture = client.getKVClient() + .get(ByteSequence.from(prefixPath, StandardCharsets.UTF_8), + GetOption.builder().withPrefix(ByteSequence.from(prefixPath, StandardCharsets.UTF_8)).build()); + GetResponse getResponse = getFuture.get(); + return convertServiceInstanceList(getResponse.getKvs()); + }); + return waiter.waitForCompletion(); } private void watchNode(String application, String serviceName, String prefixPath) { @@ -144,16 +154,19 @@ private static EtcdDiscoveryInstance getEtcdDiscoveryInstance(KeyValue keyValue) .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; + return new EtcdDiscoveryInstance(etcdInstance); } @Override public List findServices(String application) { - String prefixPath = basePath + "/" + application; - List endpointKv = getValuesByPrefix(prefixPath); - return endpointKv.stream().map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)).collect(Collectors.toList()); + ConditionWaiter> waiter = new ConditionWaiter<>(new ArrayList<>(), 50, TimeUnit.MILLISECONDS); + waiter.executeTaskAsync(() -> { + String prefixPath = basePath + "/" + application; + List endpointKv = getValuesByPrefix(prefixPath); + return endpointKv.stream().map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)).collect(Collectors.toList()); + }); + return waiter.waitForCompletion(); } @Override From d09eb573795266c0662f1c94d0097885df79ce92 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Wed, 13 Nov 2024 23:54:09 +0800 Subject: [PATCH 25/27] feat(config-etcd): Add configuration parameters and optimize the EtcdClient class - Added new configuration parameters in application.yml - Optimized the character encoding in the EtcdClient class, using StandardCharsets.UTF_8 instead of Charset.defaultCharset() - Updated test cases and added configuration parameter coverage tests --- .../src/main/resources/application.yml | 8 ++ .../servicecomb/samples/EtcdConfigIT.java | 31 ++++- .../servicecomb/config/etcd/EtcdClient.java | 14 +-- .../registry/etcd/ConditionWaiter.java | 112 +++++++++++------- 4 files changed, 108 insertions(+), 57 deletions(-) diff --git a/demo/demo-etcd/provider/src/main/resources/application.yml b/demo/demo-etcd/provider/src/main/resources/application.yml index 9729755d671..da2051cd77e 100644 --- a/demo/demo-etcd/provider/src/main/resources/application.yml +++ b/demo/demo-etcd/provider/src/main/resources/application.yml @@ -29,6 +29,7 @@ servicecomb: config: etcd: connectString: http://127.0.0.1:2379 + instance-tag: tag1 rest: address: 0.0.0.0:9094?websocketEnabled=true @@ -42,6 +43,13 @@ servicecomb: allowedMethod: "*" maxAge: 3600 + key1: 1 key2: 3 key3: 5 + +test1: env +test2: applition +test3: service +test4: version +test5: tag diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java index 7dc69ebc86d..e6625db6930 100644 --- a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -44,6 +44,25 @@ public void testRestTransport() throws Exception { testService(); testVersion(); testTag(); + testOverride(); + } + + private void testOverride() { + + putValue("/servicecomb/config/environment/production/application2.properties", + "testValue=t1"); + putValue("/servicecomb/config/application/production/demo-etcd/application2.properties", + "testValue=t2"); + testGetConfig("testValue", "t2"); + putValue("/servicecomb/config/service/production/demo-etcd/provider/application2.properties", + "testValue=t3"); + testGetConfig("testValue", "t3"); + putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application2.properties", + "testValue=t4"); + testGetConfig("testValue", "t4"); + putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application2.properties", + "testValue=t5"); + testGetConfig("testValue", "t5"); } private void testEnvironment() { @@ -66,27 +85,27 @@ private void testApplication() { private void testService() { - putValue("/servicecomb/config/service/production/demo-etcd/test-client/application.properties", + putValue("/servicecomb/config/service/production/demo-etcd/provider/application.properties", "test3=service"); - putValue("/servicecomb/config/service/production/demo-etcd/test-client/application.properties", + putValue("/servicecomb/config/service/production/demo-etcd/provider/application.properties", "test3=service3"); testGetConfig("test3", "service3"); } private void testVersion() { - putValue("/servicecomb/config/version/production/demo-etcd/test-client/application.properties", + putValue("/servicecomb/config/version/production/demo-etcd/provider/application.properties", "test3=version"); - putValue("/servicecomb/config/version/production/demo-etcd/test-client/0.0.1/application.properties", + putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application.properties", "test4=version4"); testGetConfig("test4", "version4"); } private void testTag() { - putValue("/servicecomb/config/tag/production/demo-etcd/test-client/0.0.1/tag1/application.properties", + putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application.properties", "test5=tag"); - putValue("/servicecomb/config/tag/production/demo-etcd/test-client/0.0.1/tag1/application.properties", + putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application.properties", "test5=tag5"); testGetConfig("test5", "tag5"); } diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java index 5f5a630fd56..00be2fe94a2 100644 --- a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java @@ -119,8 +119,8 @@ public void getClient() { } else { String[] authInfo = etcdConfig.getAuthInfo().split(":"); this.client = Client.builder().endpoints(etcdConfig.getConnectString()) - .user(ByteSequence.from(authInfo[0], Charset.defaultCharset())) - .password(ByteSequence.from(authInfo[1], Charset.defaultCharset())).build(); + .user(ByteSequence.from(authInfo[0], StandardCharsets.UTF_8)) + .password(ByteSequence.from(authInfo[1], StandardCharsets.UTF_8)).build(); } } @@ -150,7 +150,7 @@ private void addTagConfig(String env) throws Exception { BootStrapProperties.readServiceVersion(environment), etcdConfig.getInstanceTag()); - ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunable(tagData, this, path)).start()); @@ -163,7 +163,7 @@ private void addVersionConfig(String env) throws Exception { BootStrapProperties.readServiceName(environment), BootStrapProperties.readServiceVersion(environment)); - ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunable(versionData, this, path)).start()); @@ -175,7 +175,7 @@ private void addServiceConfig(String env) throws Exception { BootStrapProperties.readApplication(environment), BootStrapProperties.readServiceName(environment)); - ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunable(serviceData, this, path)).start()); @@ -185,7 +185,7 @@ private void addServiceConfig(String env) throws Exception { private void addApplicationConfig(String env) throws Exception { String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); - ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunable(applicationData, this, path)).start()); @@ -195,7 +195,7 @@ private void addApplicationConfig(String env) throws Exception { private void addEnvironmentConfig(String env) throws Exception { String path = String.format(PATH_ENVIRONMENT, env); - ByteSequence prefixByteSeq = ByteSequence.from(path, Charset.defaultCharset()); + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); Watch watchClient = client.getWatchClient(); watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), resp -> new Thread(new GetDataRunable(environmentData, this, path)).start()); diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/ConditionWaiter.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/ConditionWaiter.java index 5607b43a768..f0626f14cd2 100644 --- a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/ConditionWaiter.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/ConditionWaiter.java @@ -1,56 +1,80 @@ +/* + * 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.*; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; public class ConditionWaiter { - private final AtomicReference dataReference; - private final AtomicBoolean isComplete; - private final long sleepDuration; - private final TimeUnit timeUnit; - private final ExecutorService executorService; - - public ConditionWaiter(T initialData, long sleepDuration, TimeUnit timeUnit) { - this.dataReference = new AtomicReference<>(initialData); - this.isComplete = new AtomicBoolean(false); - this.sleepDuration = sleepDuration; - this.timeUnit = timeUnit; - this.executorService = Executors.newSingleThreadExecutor(); - } + private final AtomicReference dataReference; - public T waitForCompletion() { - while (!isComplete.get()) { - SleepUtil.sleep(sleepDuration, timeUnit); - } - return dataReference.get(); - } + private final AtomicBoolean isComplete; - public void setData(T newData) { - dataReference.set(newData); - } + private final long sleepDuration; + + private final TimeUnit timeUnit; - public void executeTaskAsync(Callable task) { - CompletableFuture.supplyAsync(() -> { - try { - return task.call(); - } catch (Exception e) { - throw new RuntimeException("Task execution failed", e); - } - }, executorService).thenAccept(result -> { - setData(result); - isComplete.set(true); - }); + private final ExecutorService executorService; + + public ConditionWaiter(T initialData, long sleepDuration, TimeUnit timeUnit) { + this.dataReference = new AtomicReference<>(initialData); + this.isComplete = new AtomicBoolean(false); + this.sleepDuration = sleepDuration; + this.timeUnit = timeUnit; + this.executorService = Executors.newSingleThreadExecutor(); + } + + public T waitForCompletion() { + while (!isComplete.get()) { + SleepUtil.sleep(sleepDuration, timeUnit); } + return dataReference.get(); + } + + public void setData(T newData) { + dataReference.set(newData); + } + + public void executeTaskAsync(Callable task) { + CompletableFuture.supplyAsync(() -> { + try { + return task.call(); + } catch (Exception e) { + throw new RuntimeException("Task execution failed", e); + } + }, executorService).thenAccept(result -> { + setData(result); + isComplete.set(true); + }); + } - public static class SleepUtil { - public static void sleep(long duration, TimeUnit timeUnit) { - try { - timeUnit.sleep(duration); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - System.out.println("Thread was interrupted during sleep!"); - } - } + public static class SleepUtil { + public static void sleep(long duration, TimeUnit timeUnit) { + try { + timeUnit.sleep(duration); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("Thread was interrupted during sleep!"); + } } -} \ No newline at end of file + } +} From 9ab34426135c0a5b9623c5a4d894f29f5b8b02f8 Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Wed, 13 Nov 2024 23:58:45 +0800 Subject: [PATCH 26/27] refactor(config-etcd): Remove the unused Charset import package. The unused Charset import package in the EtcdClient class in the config-etcd project is removed, which simplifies the code structure and improves the readability and maintainability of the code. --- .../main/java/org/apache/servicecomb/config/etcd/EtcdClient.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java index 00be2fe94a2..f3088cb0c7e 100644 --- a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java @@ -18,7 +18,6 @@ import java.io.IOException; import java.io.StringReader; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Enumeration; From 13c8de002099afe46059e5fc6e1ea667aa4cf8cf Mon Sep 17 00:00:00 2001 From: SweetWuXiaoMei Date: Thu, 14 Nov 2024 11:41:20 +0800 Subject: [PATCH 27/27] fix(service-registry): Fix etcd service version acquisition problem - Update the EtcdConfigIT test case to use specific version numbers instead of general configurations - Modify the EtcdDiscovery class to improve version information extraction logic - Optimize code structure to improve readability and performance --- .../org/apache/servicecomb/samples/EtcdConfigIT.java | 2 +- .../servicecomb/registry/etcd/EtcdDiscovery.java | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java index e6625db6930..75b0c3fdaad 100644 --- a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -94,7 +94,7 @@ private void testService() { private void testVersion() { - putValue("/servicecomb/config/version/production/demo-etcd/provider/application.properties", + putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application.properties", "test3=version"); putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application.properties", "test4=version4"); 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 index a7fcfcbc8e6..407bc1d7d81 100644 --- 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 @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -164,7 +165,15 @@ public List findServices(String application) { waiter.executeTaskAsync(() -> { String prefixPath = basePath + "/" + application; List endpointKv = getValuesByPrefix(prefixPath); - return endpointKv.stream().map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)).collect(Collectors.toList()); + return endpointKv.stream() + .map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) + .map(key -> { + String[] parts = StringUtils.split(key, "/"); + return parts.length > 5 ? parts[4] : null; + }) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); }); return waiter.waitForCompletion(); }