[ https://issues.apache.org/jira/browse/SCB-760?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16549062#comment-16549062 ]
ASF GitHub Bot commented on SCB-760: ------------------------------------ liubao68 closed pull request #821: [SCB-760]provide a way to invoke service with full path URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/821 This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): diff --git a/archetypes/README.md b/archetypes/README.md index 687a6f4ca..84663b553 100644 --- a/archetypes/README.md +++ b/archetypes/README.md @@ -48,5 +48,5 @@ In console Interactive mode, input your GroupId, ArtifactId and Version of new p *Notice: We will publish these archetypes to maven center repository since 1.0.0-m2, if you would like to use an archetype from an unreleased version, must use `archetypeRepository` option in the version 2.4 of archetype-plugin in order to set maven repository to apache snapshot groups: * ```bash -mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate -DarchetypeGroupId=org.apache.servicecomb.archetypes -DarchetypeArtifactId=business-service-jaxrs-archetype -DarchetypeVersion=1.0.0-m2-SNAPSHOT -DarchetypeRepository=https://repository.apache.org/content/groups/snapshots-group +mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate -DarchetypeGroupId=org.apache.servicecomb.archetypes -DarchetypeArtifactId=business-service-jaxrs-archetype -DarchetypeVersion=1.0.0-SNAPSHOT -DarchetypeRepository=https://repository.apache.org/content/groups/snapshots-group ``` \ No newline at end of file diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringmvcClient.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringmvcClient.java index cc93248b5..beab26a74 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringmvcClient.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringmvcClient.java @@ -30,6 +30,7 @@ import org.apache.servicecomb.foundation.common.utils.Log4jUtils; import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.apache.servicecomb.provider.springmvc.reference.UrlWithProviderPrefixClientHttpRequestFactory; import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -47,6 +48,8 @@ public class SpringmvcClient { private static RestTemplate templateUrlWithServiceName = new CseRestTemplate(); + private static RestTemplate templateUrlWithProviderPrefix = new CseRestTemplate(); + private static RestTemplate restTemplate; private static Controller controller; @@ -65,6 +68,7 @@ public static void run() { templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); restTemplate = RestTemplateBuilder.create(); + templateUrlWithProviderPrefix.setRequestFactory(new UrlWithProviderPrefixClientHttpRequestFactory("/pojo/rest")); controller = BeanUtils.getBean("controller"); String prefix = "cse://springmvc"; @@ -80,6 +84,7 @@ public static void run() { CodeFirstRestTemplateSpringmvc codeFirstClient = BeanUtils.getContext().getBean(CodeFirstRestTemplateSpringmvc.class); codeFirstClient.testCodeFirst(restTemplate, "springmvc", "/codeFirstSpringmvc/"); + codeFirstClient.testCodeFirst(templateUrlWithProviderPrefix, "springmvc", "/pojo/rest/codeFirstSpringmvc/"); String microserviceName = "springmvc"; for (String transport : DemoConst.transports) { @@ -115,11 +120,13 @@ public static void run() { TestMgr.check(true, metrics.size() > 0); TestMgr.check(true, metrics.get( - "servicecomb.invocation(operation=springmvc.codeFirst.saySomething,role=PRODUCER,stage=total,statistic=count,status=200,transport=highway)") >= 0); + "servicecomb.invocation(operation=springmvc.codeFirst.saySomething,role=PRODUCER,stage=total,statistic=count,status=200,transport=highway)") + >= 0); //prometheus integration test try { - String content = restTemplate.getForObject("cse://springmvc/codeFirstSpringmvc/prometheusForTest", String.class); + String content = restTemplate + .getForObject("cse://springmvc/codeFirstSpringmvc/prometheusForTest", String.class); TestMgr.check(true, content.contains("servicecomb_invocation{operation=\"springmvc.codeFirst.addDate")); TestMgr.check(true, content.contains("servicecomb_invocation{operation=\"springmvc.codeFirst.sayHello")); diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java index d913dc999..ca91ef617 100644 --- a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/config/ArchaiusUtils.java @@ -18,6 +18,8 @@ package org.apache.servicecomb.foundation.test.scaffolding.config; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.concurrent.ConcurrentHashMap; import org.springframework.util.ReflectionUtils; @@ -40,23 +42,34 @@ private static final Field FIELD_DYNAMIC_PROPERTY_SUPPORTIMPL = ReflectionUtils.findField(DynamicProperty.class, "dynamicPropertySupportImpl"); + private static final Field FIELD_DYNAMIC_PROPERTY_ALL_PROPS = ReflectionUtils + .findField(DynamicProperty.class, "ALL_PROPS"); + + private static Method updatePropertyMethod = + ReflectionUtils.findMethod(DynamicProperty.class, "updateProperty", String.class, Object.class); + static { FIELD_INSTANCE.setAccessible(true); FIELD_CUSTOM_CONFIGURATION_INSTALLED.setAccessible(true); FIELD_CONFIG.setAccessible(true); FIELD_INITIALIZED_WITH_DEFAULT_CONFIG.setAccessible(true); FIELD_DYNAMIC_PROPERTY_SUPPORTIMPL.setAccessible(true); + FIELD_DYNAMIC_PROPERTY_ALL_PROPS.setAccessible(true); + updatePropertyMethod.setAccessible(true); } private ArchaiusUtils() { } + @SuppressWarnings("unchecked") public static void resetConfig() { ReflectionUtils.setField(FIELD_INSTANCE, null, null); ReflectionUtils.setField(FIELD_CUSTOM_CONFIGURATION_INSTALLED, null, false); ReflectionUtils.setField(FIELD_CONFIG, null, null); ReflectionUtils.setField(FIELD_INITIALIZED_WITH_DEFAULT_CONFIG, null, false); ReflectionUtils.setField(FIELD_DYNAMIC_PROPERTY_SUPPORTIMPL, null, null); + ((ConcurrentHashMap<String, DynamicProperty>) ReflectionUtils.getField(FIELD_DYNAMIC_PROPERTY_ALL_PROPS, null)) + .clear(); } public static void setProperty(String key, Object value) { @@ -67,4 +80,13 @@ public static void setProperty(String key, Object value) { .getBackingConfigurationSource(); config.getConfiguration(0).addProperty(key, value); } + + /** + * difference with setProperty is that, updateProperty value can be null + * @param key + * @param value + */ + public static void updateProperty(String key, Object value) { + ReflectionUtils.invokeMethod(updatePropertyMethod, null, key, value); + } } diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java index 5f7f2821f..44b86b267 100644 --- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java +++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java @@ -422,12 +422,12 @@ public static OperationMeta getMockOperationMeta(String microserviceName, String } public static void setConfig(String key, int value) { - Utils.updateProperty(key, value); + ArchaiusUtils.setProperty(key, value); } public static void setConfigWithDefaultPrefix(String key, int value) { String configKey = Config.CONSUMER_LIMIT_KEY_PREFIX + key; - Utils.updateProperty(configKey, value); + ArchaiusUtils.setProperty(configKey, value); } public static void clearState(QpsControllerManager qpsControllerManager) { diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java index 540d3031c..15d1e29b1 100644 --- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java +++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConfig.java @@ -17,7 +17,10 @@ package org.apache.servicecomb.qps; +import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.junit.AfterClass; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; /** @@ -25,20 +28,30 @@ * */ public class TestConfig { + @BeforeClass + public static void classSetup() { + ArchaiusUtils.resetConfig(); + } + + @AfterClass + public static void classTeardown() { + ArchaiusUtils.resetConfig(); + } + @Test public void testEnabled() { Assert.assertEquals(true, Config.INSTANCE.isProviderEnabled()); - Utils.updateProperty(Config.PROVIDER_ENABLED, false); + ArchaiusUtils.updateProperty(Config.PROVIDER_ENABLED, false); Assert.assertEquals(false, Config.INSTANCE.isProviderEnabled()); - Utils.updateProperty(Config.PROVIDER_ENABLED, null); + ArchaiusUtils.updateProperty(Config.PROVIDER_ENABLED, null); Assert.assertEquals(true, Config.INSTANCE.isProviderEnabled()); Assert.assertEquals(true, Config.INSTANCE.isConsumerEnabled()); - Utils.updateProperty(Config.CONSUMER_ENABLED, false); + ArchaiusUtils.updateProperty(Config.CONSUMER_ENABLED, false); Assert.assertEquals(false, Config.INSTANCE.isConsumerEnabled()); - Utils.updateProperty(Config.CONSUMER_ENABLED, null); + ArchaiusUtils.updateProperty(Config.CONSUMER_ENABLED, null); Assert.assertEquals(true, Config.INSTANCE.isConsumerEnabled()); } } diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java index d14e6a25a..f671eeab0 100644 --- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java +++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java @@ -56,7 +56,7 @@ public void setUP() { ArchaiusUtils.resetConfig(); QpsControllerManagerTest.clearState(ProviderQpsFlowControlHandler.qpsControllerMgr); - Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + "test", 1); + ArchaiusUtils.setProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + "test", 1); } @@ -86,7 +86,7 @@ public void testGlobalQpsControl(final @Injectable Invocation invocation, ProviderQpsFlowControlHandler gHandler = new ProviderQpsFlowControlHandler(); gHandler.handle(invocation, asyncResp); - Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_GLOBAL, 3); + ArchaiusUtils.setProperty(Config.PROVIDER_LIMIT_KEY_GLOBAL, 3); expectedException.expect(RuntimeException.class); expectedException.expectMessage("test error"); diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithProviderPrefixClientHttpRequestFactory.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithProviderPrefixClientHttpRequestFactory.java new file mode 100644 index 000000000..6c7ba67d3 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithProviderPrefixClientHttpRequestFactory.java @@ -0,0 +1,56 @@ +/* + * 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.provider.springmvc.reference; + +import java.io.IOException; +import java.net.URI; + +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpRequestFactory; + +/** + * When deploying in a container, like tomcat, users want to invoke service with full path, including container context + * root and servlet path. + */ +public class UrlWithProviderPrefixClientHttpRequestFactory implements ClientHttpRequestFactory { + static class UrlWithProviderPrefixClientHttpRequest extends CseClientHttpRequest { + private String prefix; + + public UrlWithProviderPrefixClientHttpRequest(URI uri, HttpMethod httpMethod, String prefix) { + super(uri, httpMethod); + this.prefix = prefix; + } + + @Override + protected String findUriPath(URI uri) { + return uri.getRawPath().substring(prefix.length()); + } + } + + private String prefix; + + public UrlWithProviderPrefixClientHttpRequestFactory(String prefix) { + this.prefix = prefix; + } + + @Override + public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { + return new UrlWithProviderPrefixClientHttpRequest(uri, httpMethod, prefix); + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithProviderPrefixClientHttpRequestFactory.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithProviderPrefixClientHttpRequestFactory.java new file mode 100644 index 000000000..c66684188 --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithProviderPrefixClientHttpRequestFactory.java @@ -0,0 +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.provider.springmvc.reference; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; +import org.apache.servicecomb.provider.springmvc.reference.UrlWithProviderPrefixClientHttpRequestFactory.UrlWithProviderPrefixClientHttpRequest; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.http.HttpMethod; + +import mockit.Deencapsulation; +import mockit.Expectations; +import mockit.Mocked; + +public class TestUrlWithProviderPrefixClientHttpRequestFactory { + UrlWithProviderPrefixClientHttpRequestFactory factory = new UrlWithProviderPrefixClientHttpRequestFactory("/a/b/c"); + + URI uri = URI.create("cse://ms/a/b/c/v1/path"); + + @Test + public void findUriPath() throws IOException { + UrlWithProviderPrefixClientHttpRequest request = + (UrlWithProviderPrefixClientHttpRequest) factory.createRequest(uri, HttpMethod.GET); + + Assert.assertEquals("/v1/path", request.findUriPath(uri)); + } + + @Test + public void invoke_checkPath(@Mocked Invocation invocation, @Mocked RequestMeta requestMeta) { + Map<String, String> handlerContext = new HashMap<>(); + UrlWithProviderPrefixClientHttpRequest request = new UrlWithProviderPrefixClientHttpRequest(uri, HttpMethod.GET, + "/a/b/c") { + @Override + protected Response doInvoke(Invocation invocation) { + return Response.ok(null); + } + }; + + new Expectations(InvocationFactory.class) { + { + invocation.getHandlerContext(); + result = handlerContext; + InvocationFactory.forConsumer((ReferenceConfig) any, (OperationMeta) any, (Object[]) any); + result = invocation; + } + }; + + Deencapsulation.setField(request, "requestMeta", requestMeta); + Deencapsulation.setField(request, "path", request.findUriPath(uri)); + + Deencapsulation.invoke(request, "invoke", new Object[] {new Object[] {}}); + + Assert.assertEquals("/v1/path?null", handlerContext.get(RestConst.REST_CLIENT_REQUEST_PATH)); + } +} diff --git a/samples/bmi/build.gradle b/samples/bmi/build.gradle index 3dfb8b530..3b7365892 100644 --- a/samples/bmi/build.gradle +++ b/samples/bmi/build.gradle @@ -19,7 +19,7 @@ allprojects { apply plugin: 'maven' group = 'org.apache.servicecomb.samples' - version = '1.0.0-m2-SNAPSHOT' + version = '1.0.0-SNAPSHOT' } buildscript { diff --git a/samples/bmi/calculator/build.gradle b/samples/bmi/calculator/build.gradle index 16223d9a0..71c00197b 100644 --- a/samples/bmi/calculator/build.gradle +++ b/samples/bmi/calculator/build.gradle @@ -44,6 +44,6 @@ apply plugin: 'io.spring.dependency-management' dependencyManagement { imports { - mavenBom 'org.apache.servicecomb:java-chassis-dependencies:1.0.0-m2-SNAPSHOT' + mavenBom 'org.apache.servicecomb:java-chassis-dependencies:1.0.0-SNAPSHOT' } } diff --git a/samples/bmi/webapp/build.gradle b/samples/bmi/webapp/build.gradle index c8973f6b8..1624d9cf5 100644 --- a/samples/bmi/webapp/build.gradle +++ b/samples/bmi/webapp/build.gradle @@ -44,6 +44,6 @@ apply plugin: 'io.spring.dependency-management' dependencyManagement { imports { - mavenBom 'org.apache.servicecomb:java-chassis-dependencies:1.0.0-m2-SNAPSHOT' + mavenBom 'org.apache.servicecomb:java-chassis-dependencies:1.0.0-SNAPSHOT' } } diff --git a/samples/codefirst-sample/build.gradle b/samples/codefirst-sample/build.gradle index cfff44a76..67a7af595 100644 --- a/samples/codefirst-sample/build.gradle +++ b/samples/codefirst-sample/build.gradle @@ -19,7 +19,7 @@ allprojects { apply plugin: 'maven' group = 'org.apache.servicecomb.samples' - version = '1.0.0-m2-SNAPSHOT' + version = '1.0.0-SNAPSHOT' } subprojects { diff --git a/samples/codefirst-sample/codefirst-consumer/build.gradle b/samples/codefirst-sample/codefirst-consumer/build.gradle index 060ad092e..90d4ddc0f 100644 --- a/samples/codefirst-sample/codefirst-consumer/build.gradle +++ b/samples/codefirst-sample/codefirst-consumer/build.gradle @@ -21,7 +21,7 @@ dependencies { compile group: 'org.apache.servicecomb', name: 'provider-pojo' compile group: 'org.apache.servicecomb', name: 'transport-highway' compile group: 'org.apache.servicecomb', name: 'transport-rest-vertx' - compile group: 'org.apache.servicecomb.samples', name: 'common-schema', version: '1.0.0-m2-SNAPSHOT' + compile group: 'org.apache.servicecomb.samples', name: 'common-schema', version: '1.0.0-SNAPSHOT' compile group: 'org.slf4j', name: 'slf4j-log4j12' } @@ -46,6 +46,6 @@ apply plugin: 'io.spring.dependency-management' dependencyManagement { imports { - mavenBom 'org.apache.servicecomb:java-chassis-dependencies:1.0.0-m2-SNAPSHOT' + mavenBom 'org.apache.servicecomb:java-chassis-dependencies:1.0.0-SNAPSHOT' } -} \ No newline at end of file +} diff --git a/samples/codefirst-sample/codefirst-provider/build.gradle b/samples/codefirst-sample/codefirst-provider/build.gradle index bb0b845f3..9c85d0717 100644 --- a/samples/codefirst-sample/codefirst-provider/build.gradle +++ b/samples/codefirst-sample/codefirst-provider/build.gradle @@ -22,7 +22,7 @@ dependencies { compile group: 'org.apache.servicecomb', name: 'provider-springmvc' compile group: 'org.apache.servicecomb', name: 'transport-highway' compile group: 'org.apache.servicecomb', name: 'transport-rest-vertx' - compile group: 'org.apache.servicecomb.samples', name: 'common-schema', version: '1.0.0-m2-SNAPSHOT' + compile group: 'org.apache.servicecomb.samples', name: 'common-schema', version: '1.0.0-SNAPSHOT' compile group: 'org.slf4j', name: 'slf4j-log4j12' } @@ -47,6 +47,6 @@ apply plugin: 'io.spring.dependency-management' dependencyManagement { imports { - mavenBom 'org.apache.servicecomb:java-chassis-dependencies:1.0.0-m2-SNAPSHOT' + mavenBom 'org.apache.servicecomb:java-chassis-dependencies:1.0.0-SNAPSHOT' } -} \ No newline at end of file +} diff --git a/samples/trust-sample/customer/pom.xml b/samples/trust-sample/customer/pom.xml index 8aa383915..fdabbffd0 100644 --- a/samples/trust-sample/customer/pom.xml +++ b/samples/trust-sample/customer/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>org.apache.servicecomb.samples</groupId> <artifactId>trust-sample</artifactId> - <version>1.0.0-m2-SNAPSHOT</version> + <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>customer</artifactId> <dependencies> diff --git a/samples/trust-sample/hacker/pom.xml b/samples/trust-sample/hacker/pom.xml index 91c7a3116..8d85ae7ac 100644 --- a/samples/trust-sample/hacker/pom.xml +++ b/samples/trust-sample/hacker/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>org.apache.servicecomb.samples</groupId> <artifactId>trust-sample</artifactId> - <version>1.0.0-m2-SNAPSHOT</version> + <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>hacker</artifactId> <dependencies> diff --git a/samples/trust-sample/pom.xml b/samples/trust-sample/pom.xml index d688a54c8..c7790312f 100644 --- a/samples/trust-sample/pom.xml +++ b/samples/trust-sample/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>org.apache.servicecomb.samples</groupId> <artifactId>samples</artifactId> - <version>1.0.0-m2-SNAPSHOT</version> + <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>trust-sample</artifactId> <packaging>pom</packaging> diff --git a/samples/trust-sample/store/pom.xml b/samples/trust-sample/store/pom.xml index 07e9893f3..24fc1ea82 100644 --- a/samples/trust-sample/store/pom.xml +++ b/samples/trust-sample/store/pom.xml @@ -21,7 +21,7 @@ <parent> <groupId>org.apache.servicecomb.samples</groupId> <artifactId>trust-sample</artifactId> - <version>1.0.0-m2-SNAPSHOT</version> + <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>store</artifactId> <dependencies> diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java index 877b886f8..196cd24d3 100644 --- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java @@ -171,8 +171,11 @@ public String getMicroserviceId(String appId, String microserviceName, String st @Override public String registerMicroservice(Microservice microservice) { - String serviceId = - microservice.getServiceId() == null ? UUID.randomUUID().toString() : microservice.getServiceId(); + String serviceId = microservice.getServiceId(); + if (serviceId == null) { + serviceId = UUID.randomUUID().toString(); + microservice.setServiceId(serviceId); + } microserviceIdMap.put(serviceId, microservice); microserviceInstanceMap.computeIfAbsent(serviceId, k -> new ConcurrentHashMap<>()); diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/AppManager.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/AppManager.java index 8093ceb5e..7cc45e221 100644 --- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/AppManager.java +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/AppManager.java @@ -43,6 +43,10 @@ public MicroserviceVersionFactory getMicroserviceVersionFactory() { return microserviceVersionFactory; } + public Map<String, MicroserviceManager> getApps() { + return apps; + } + public void setMicroserviceVersionFactory(MicroserviceVersionFactory microserviceVersionFactory) { this.microserviceVersionFactory = microserviceVersionFactory; } diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceManager.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceManager.java index a7bd4ae3d..2d16d0d95 100644 --- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceManager.java +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceManager.java @@ -45,6 +45,10 @@ public MicroserviceManager(AppManager appManager, String appId) { appManager.getEventBus().register(this); } + public Map<String, MicroserviceVersions> getVersionsByName() { + return versionsByName; + } + public MicroserviceVersions getOrCreateMicroserviceVersions(String microserviceName) { MicroserviceVersions microserviceVersions = versionsByName.computeIfAbsent(microserviceName, name -> { MicroserviceVersions instance = new MicroserviceVersions(appManager, appId, microserviceName); diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java index 169328eca..f867f6ae7 100644 --- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.serviceregistry.consumer; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -48,8 +49,15 @@ private String microserviceName; + // revision and pulledInstances directly equals to SC's response private String revision = null; + private List<MicroserviceInstance> pulledInstances; + + // instances not always equals to pulledInstances + // in the future: + // pulledInstances means all instance + // instances means available instance private List<MicroserviceInstance> instances; // key is service id @@ -101,6 +109,14 @@ public String getMicroserviceName() { return (T) versions.get(serviceId); } + public String getRevision() { + return revision; + } + + public List<MicroserviceInstance> getPulledInstances() { + return pulledInstances; + } + public void submitPull() { pendingPullCount.incrementAndGet(); @@ -127,7 +143,9 @@ public void pullInstances() { if (!microserviceInstances.isNeedRefresh()) { return; } - List<MicroserviceInstance> pulledInstances = microserviceInstances.getInstancesResponse().getInstances(); + + pulledInstances = microserviceInstances.getInstancesResponse().getInstances(); + pulledInstances.sort(Comparator.comparing(MicroserviceInstance::getInstanceId)); String rev = microserviceInstances.getRevision(); safeSetInstances(pulledInstances, rev); diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/Status.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/Status.java new file mode 100644 index 000000000..47aedd4fd --- /dev/null +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/Status.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.serviceregistry.diagnosis; + +public enum Status { + NORMAL, + ABNORMAL, + UNKNOWN +} diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheCheckTask.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheCheckTask.java new file mode 100644 index 000000000..b6dd6155c --- /dev/null +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheCheckTask.java @@ -0,0 +1,139 @@ +/* + * 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.serviceregistry.diagnosis.instance; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.serviceregistry.consumer.AppManager; +import org.apache.servicecomb.serviceregistry.registry.RemoteServiceRegistry; +import org.apache.servicecomb.serviceregistry.registry.ServiceRegistryTaskInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.EventBus; +import com.netflix.config.DynamicIntProperty; +import com.netflix.config.DynamicPropertyFactory; +import com.netflix.config.DynamicStringProperty; + +import io.vertx.core.json.Json; + +public class InstanceCacheCheckTask implements ServiceRegistryTaskInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger(InstanceCacheCheckTask.class); + + private static final int DEFAULT_DIAGNOSE_INSTANCE_CACHE_INTERVAL_IN_HOUR = 24; + + private static final String CONFIG_PREFIX = "servicecomb.service.registry.instance.diagnose."; + + public static final String MANUAL = CONFIG_PREFIX + "manual"; + + public static final String AUTO_INTERVAL = CONFIG_PREFIX + "interval"; + + // auto task + private ScheduledFuture<?> scheduledFuture; + + private AppManager appManager; + + private ScheduledThreadPoolExecutor taskPool; + + private EventBus eventBus; + + private DynamicIntProperty autoCheckIntervalProperty; + + private DynamicStringProperty manualCheckProperty; + + private TimeUnit timeUnit = TimeUnit.HOURS; + + // make test easier + public void setTimeUnit(TimeUnit timeUnit) { + this.timeUnit = timeUnit; + } + + public void setAppManager(AppManager appManager) { + this.appManager = appManager; + } + + public void setTaskPool(ScheduledThreadPoolExecutor taskPool) { + this.taskPool = taskPool; + } + + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + } + + public DynamicStringProperty getManualCheckProperty() { + return manualCheckProperty; + } + + public DynamicIntProperty getAutoCheckIntervalProperty() { + return autoCheckIntervalProperty; + } + + @Override + public void init(RemoteServiceRegistry remoteServiceRegistry) { + appManager = remoteServiceRegistry.getAppManager(); + taskPool = remoteServiceRegistry.getTaskPool(); + eventBus = remoteServiceRegistry.getEventBus(); + + init(); + } + + protected void init() { + startAutoTask(); + registerManualTask(); + } + + private void registerManualTask() { + // if manual config item changed, then run task once + manualCheckProperty = DynamicPropertyFactory.getInstance().getStringProperty(MANUAL, null, this::runTask); + } + + protected void startAutoTask() { + autoCheckIntervalProperty = DynamicPropertyFactory.getInstance().getIntProperty(AUTO_INTERVAL, + DEFAULT_DIAGNOSE_INSTANCE_CACHE_INTERVAL_IN_HOUR, + this::doStartAutoTask); + doStartAutoTask(); + } + + private void doStartAutoTask() { + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + scheduledFuture = null; + } + + int interval = autoCheckIntervalProperty.get(); + if (interval <= 0) { + LOGGER.info("disable instance cache check task, interval={}.", interval); + return; + } + + scheduledFuture = taskPool.scheduleAtFixedRate(this::runTask, interval, interval, timeUnit); + } + + protected void runTask() { + try { + InstanceCacheChecker checker = new InstanceCacheChecker(appManager); + InstanceCacheSummary instanceCacheSummary = checker.check(); + eventBus.post(instanceCacheSummary); + + LOGGER.info("check instance cache, result={}.", Json.encode(instanceCacheSummary)); + } catch (Throwable e) { + LOGGER.error("failed check instance cache..", e); + } + } +} diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheChecker.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheChecker.java new file mode 100644 index 000000000..8e0e1d7a7 --- /dev/null +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheChecker.java @@ -0,0 +1,140 @@ +/* + * 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.serviceregistry.diagnosis.instance; + +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.apache.servicecomb.serviceregistry.RegistryUtils; +import org.apache.servicecomb.serviceregistry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.serviceregistry.client.http.MicroserviceInstances; +import org.apache.servicecomb.serviceregistry.consumer.AppManager; +import org.apache.servicecomb.serviceregistry.consumer.MicroserviceManager; +import org.apache.servicecomb.serviceregistry.consumer.MicroserviceVersions; +import org.apache.servicecomb.serviceregistry.definition.DefinitionConst; +import org.apache.servicecomb.serviceregistry.diagnosis.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.json.Json; + +public class InstanceCacheChecker { + private static final Logger LOGGER = LoggerFactory.getLogger(InstanceCacheChecker.class); + + private AppManager appManager; + + private Set<Status> statuses = new HashSet<>(); + + private InstanceCacheSummary instanceCacheSummary = new InstanceCacheSummary(); + + public InstanceCacheChecker(AppManager appManager) { + this.appManager = appManager; + } + + public InstanceCacheSummary check() { + instanceCacheSummary.setAppId(RegistryUtils.getMicroservice().getAppId()); + instanceCacheSummary.setMicroserviceName(RegistryUtils.getMicroservice().getServiceName()); + instanceCacheSummary.setTimestamp(System.currentTimeMillis()); + + for (MicroserviceManager microserviceManager : appManager.getApps().values()) { + for (MicroserviceVersions microserviceVersions : microserviceManager.getVersionsByName().values()) { + InstanceCacheResult instanceCacheResult = check(microserviceVersions); + addInstanceCacheResult(instanceCacheResult); + } + } + + generateStatus(); + + return instanceCacheSummary; + } + + private void addInstanceCacheResult(InstanceCacheResult instanceCacheResult) { + statuses.add(instanceCacheResult.getStatus()); + instanceCacheSummary.getProducers().add(instanceCacheResult); + } + + protected InstanceCacheResult check(MicroserviceVersions microserviceVersions) { + InstanceCacheResult instanceCacheResult = new InstanceCacheResult(); + instanceCacheResult.setAppId(microserviceVersions.getAppId()); + instanceCacheResult.setMicroserviceName(microserviceVersions.getMicroserviceName()); + + MicroserviceInstances microserviceInstances = RegistryUtils + .findServiceInstances(microserviceVersions.getAppId(), + microserviceVersions.getMicroserviceName(), + DefinitionConst.VERSION_RULE_ALL, + null); + if (microserviceInstances == null) { + instanceCacheResult.setStatus(Status.UNKNOWN); + instanceCacheResult.setDetail("failed to find instances from service center"); + return instanceCacheResult; + } + if (microserviceInstances.isMicroserviceNotExist()) { + // no problem, will be deleted from MicroserviceManager in next pull + instanceCacheResult.setStatus(Status.UNKNOWN); + instanceCacheResult.setDetail("microservice is not exist anymore, will be deleted from memory in next pull"); + return instanceCacheResult; + } + + if (!Objects.equals(microserviceInstances.getRevision(), microserviceVersions.getRevision())) { + // maybe not pull, wait for next pull we get the same revision + instanceCacheResult.setStatus(Status.UNKNOWN); + instanceCacheResult.setDetail(String.format( + "revision is different, will be synchronized in next pull. local revision=%s, remote revision=%s", + microserviceVersions.getRevision(), microserviceInstances.getRevision())); + return instanceCacheResult; + } + + // compare all instances + List<MicroserviceInstance> remoteInstances = microserviceInstances.getInstancesResponse().getInstances(); +// if (RandomUtils.nextInt(0, 2) == 0) { +// MicroserviceInstance microserviceInstance = new MicroserviceInstance(); +// microserviceInstance.setInstanceId("abc"); +// remoteInstances.add(microserviceInstance); +// } + remoteInstances.sort(Comparator.comparing(MicroserviceInstance::getInstanceId)); + String local = Json.encode(microserviceVersions.getPulledInstances()); + String remote = Json.encode(remoteInstances); + if (local.equals(remote)) { + instanceCacheResult.setStatus(Status.NORMAL); + return instanceCacheResult; + } + + LOGGER.error("instance cache not match. appId={}, microservice={}.\n" + + "local cache: {}\n" + + "remote cache: {}", + microserviceVersions.getAppId(), + microserviceVersions.getMicroserviceName(), + local, + remote); + instanceCacheResult.setStatus(Status.ABNORMAL); + instanceCacheResult.setDetail("instance cache not match"); + return instanceCacheResult; + } + + protected void generateStatus() { + if (statuses.contains(Status.ABNORMAL)) { + instanceCacheSummary.setStatus(Status.ABNORMAL); + } else if (statuses.contains(Status.UNKNOWN)) { + instanceCacheSummary.setStatus(Status.UNKNOWN); + } else { + instanceCacheSummary.setStatus(Status.NORMAL); + } + } +} diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheResult.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheResult.java new file mode 100644 index 000000000..a5ae3de63 --- /dev/null +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheResult.java @@ -0,0 +1,62 @@ +/* + * 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.serviceregistry.diagnosis.instance; + +import org.apache.servicecomb.serviceregistry.diagnosis.Status; + +public class InstanceCacheResult { + // producer appId and microserviceName + private String appId; + + private String microserviceName; + + private Status status; + + private String detail; + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getMicroserviceName() { + return microserviceName; + } + + public void setMicroserviceName(String microserviceName) { + this.microserviceName = microserviceName; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } +} diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheSummary.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheSummary.java new file mode 100644 index 000000000..9a02cf8fc --- /dev/null +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/InstanceCacheSummary.java @@ -0,0 +1,79 @@ +/* + * 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.serviceregistry.diagnosis.instance; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.serviceregistry.diagnosis.Status; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +public class InstanceCacheSummary { + // consumer appId and microserviceName + @JsonIgnore + private String appId; + + @JsonIgnore + private String microserviceName; + + private Status status; + + private List<InstanceCacheResult> producers = new ArrayList<>(); + + private long timestamp; + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getMicroserviceName() { + return microserviceName; + } + + public void setMicroserviceName(String microserviceName) { + this.microserviceName = microserviceName; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public List<InstanceCacheResult> getProducers() { + return producers; + } + + public void setProducers(List<InstanceCacheResult> producers) { + this.producers = producers; + } +} diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java index e6efa79a6..8eb4fb261 100644 --- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java @@ -16,9 +16,11 @@ */ package org.apache.servicecomb.serviceregistry.registry; +import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.serviceregistry.client.ServiceRegistryClient; import org.apache.servicecomb.serviceregistry.client.http.ServiceRegistryClientImpl; import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig; @@ -38,6 +40,9 @@ private ScheduledThreadPoolExecutor taskPool; + private List<ServiceRegistryTaskInitializer> taskInitializers = SPIServiceUtils + .getOrLoadSortedService(ServiceRegistryTaskInitializer.class); + public RemoteServiceRegistry(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, MicroserviceDefinition microserviceDefinition) { super(eventBus, serviceRegistryConfig, microserviceDefinition); @@ -77,6 +82,10 @@ public void run() { serviceRegistryConfig.getInstancePullInterval(), serviceRegistryConfig.getInstancePullInterval(), TimeUnit.SECONDS); + + for (ServiceRegistryTaskInitializer initializer : taskInitializers) { + initializer.init(this); + } } @Subscribe @@ -91,8 +100,7 @@ public void onMicroserviceRegistryTask(MicroserviceRegisterTask event) { } } - // for testing - ScheduledThreadPoolExecutor getTaskPool() { + public ScheduledThreadPoolExecutor getTaskPool() { return this.taskPool; } } diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/Utils.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/ServiceRegistryTaskInitializer.java similarity index 60% rename from handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/Utils.java rename to service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/ServiceRegistryTaskInitializer.java index e4ceb5308..21cd84258 100644 --- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/Utils.java +++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/ServiceRegistryTaskInitializer.java @@ -15,23 +15,12 @@ * limitations under the License. */ -package org.apache.servicecomb.qps; +package org.apache.servicecomb.serviceregistry.registry; -import java.lang.reflect.Method; - -import org.springframework.util.ReflectionUtils; - -import com.netflix.config.DynamicProperty; - -public class Utils { - private static Method updatePropertyMethod = - ReflectionUtils.findMethod(DynamicProperty.class, "updateProperty", String.class, Object.class); - - static { - updatePropertyMethod.setAccessible(true); +public interface ServiceRegistryTaskInitializer { + default int getOrder() { + return 0; } - public static void updateProperty(String key, Object value) { - ReflectionUtils.invokeMethod(updatePropertyMethod, null, key, value); - } + void init(RemoteServiceRegistry remoteServiceRegistry); } diff --git a/service-registry/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.registry.ServiceRegistryTaskInitializer b/service-registry/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.registry.ServiceRegistryTaskInitializer new file mode 100644 index 000000000..761ee02f5 --- /dev/null +++ b/service-registry/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.registry.ServiceRegistryTaskInitializer @@ -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.serviceregistry.diagnosis.instance.InstanceCacheCheckTask diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java index 8bd0a2d23..9614048ca 100644 --- a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java +++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java @@ -73,6 +73,7 @@ @Before public void setUp() throws Exception { + ArchaiusUtils.resetConfig(); microserviceInstances = new MicroserviceInstances(); findInstancesResponse = new FindInstancesResponse(); } diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheCheckTask.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheCheckTask.java new file mode 100644 index 000000000..862cf6fad --- /dev/null +++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheCheckTask.java @@ -0,0 +1,151 @@ +/* + * 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.serviceregistry.diagnosis.instance; + +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.xml.ws.Holder; + +import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; +import org.apache.servicecomb.serviceregistry.consumer.AppManager; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +import io.vertx.core.json.Json; +import mockit.Deencapsulation; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestInstanceCacheCheckTask { + @Mocked + AppManager appManager; + + ScheduledThreadPoolExecutor taskPool = new ScheduledThreadPoolExecutor(2, + task -> new Thread(task, "Service Center Task test thread"), + (task, executor) -> System.out.println("Too many pending tasks, reject " + task.getClass().getName())); + + EventBus eventBus = new EventBus(); + + InstanceCacheCheckTask task = new InstanceCacheCheckTask(); + + InstanceCacheSummary result; + + @Before + public void setUp() { + task.setAppManager(appManager); + task.setTaskPool(taskPool); + task.setEventBus(eventBus); + task.setTimeUnit(TimeUnit.MILLISECONDS); + + new MockUp<InstanceCacheChecker>() { + @Mock + InstanceCacheSummary check() { + return new InstanceCacheSummary(); + } + }; + } + + @After + public void tearDown() throws Exception { + ArchaiusUtils.resetConfig(); + taskPool.shutdownNow(); + } + + @Test + public void manualTask() throws InterruptedException { + + ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, 0); + CountDownLatch latch = new CountDownLatch(1); + eventBus.register(new Object() { + @Subscribe + public void onChecked(InstanceCacheSummary instanceCacheSummary) { + result = instanceCacheSummary; + latch.countDown(); + } + }); + task.init(); + + ArchaiusUtils.setProperty(InstanceCacheCheckTask.MANUAL, UUID.randomUUID().toString()); + latch.await(); + + Assert.assertEquals("{\"status\":null,\"producers\":[],\"timestamp\":0}", Json.encode(result)); + } + + @Test + public void autoTask_normal() throws InterruptedException { + ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, 1); + CountDownLatch latch = new CountDownLatch(1); + eventBus.register(new Object() { + @Subscribe + public void onChecked(InstanceCacheSummary instanceCacheSummary) { + result = instanceCacheSummary; + ((ScheduledFuture<?>) Deencapsulation.getField(task, "scheduledFuture")).cancel(false); + latch.countDown(); + } + }); + task.init(); + + latch.await(); + Assert.assertNotNull(Deencapsulation.getField(task, "scheduledFuture")); + Assert.assertEquals("{\"status\":null,\"producers\":[],\"timestamp\":0}", Json.encode(result)); + } + + @Test + public void autoTask_clearOldTask() { + Holder<Boolean> cancelResult = new Holder<>(); + ScheduledFuture<?> scheduledFuture = new MockUp<ScheduledFuture>() { + @Mock + boolean cancel(boolean mayInterruptIfRunning) { + cancelResult.value = true; + return true; + } + }.getMockInstance(); + + ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, 0); + Deencapsulation.setField(task, "scheduledFuture", scheduledFuture); + task.init(); + + Assert.assertNull(Deencapsulation.getField(task, "scheduledFuture")); + Assert.assertTrue(cancelResult.value); + } + + @Test + public void autoTask_invalidIntervalZero() { + ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, 0); + task.init(); + + Assert.assertNull(Deencapsulation.getField(task, "scheduledFuture")); + } + + @Test + public void autoTask_invalidIntervalLessThanZero() { + ArchaiusUtils.setProperty(InstanceCacheCheckTask.AUTO_INTERVAL, -1); + task.init(); + + Assert.assertNull(Deencapsulation.getField(task, "scheduledFuture")); + } +} diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheChecker.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheChecker.java new file mode 100644 index 000000000..7ffd600fe --- /dev/null +++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/diagnosis/instance/TestInstanceCacheChecker.java @@ -0,0 +1,244 @@ +/* + * 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.serviceregistry.diagnosis.instance; + +import java.util.ArrayList; + +import javax.xml.ws.Holder; + +import org.apache.servicecomb.serviceregistry.RegistryUtils; +import org.apache.servicecomb.serviceregistry.ServiceRegistry; +import org.apache.servicecomb.serviceregistry.api.registry.Microservice; +import org.apache.servicecomb.serviceregistry.api.registry.MicroserviceInstance; +import org.apache.servicecomb.serviceregistry.api.response.FindInstancesResponse; +import org.apache.servicecomb.serviceregistry.client.http.MicroserviceInstances; +import org.apache.servicecomb.serviceregistry.definition.DefinitionConst; +import org.apache.servicecomb.serviceregistry.diagnosis.Status; +import org.apache.servicecomb.serviceregistry.registry.ServiceRegistryFactory; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.vertx.core.json.Json; +import mockit.Mock; +import mockit.MockUp; + +public class TestInstanceCacheChecker { + ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); + + InstanceCacheChecker checker; + + InstanceCacheSummary expectedSummary = new InstanceCacheSummary(); + + String appId = "appId"; + + String microserviceName = "msName"; + + @Before + public void setUp() throws Exception { + serviceRegistry.init(); + RegistryUtils.setServiceRegistry(serviceRegistry); + + checker = new InstanceCacheChecker(serviceRegistry.getAppManager()); + expectedSummary.setStatus(Status.NORMAL); + expectedSummary.setTimestamp(1); + + new MockUp<System>() { + @Mock + long currentTimeMillis() { + return 1L; + } + }; + } + + @After + public void tearDown() throws Exception { + RegistryUtils.setServiceRegistry(null); + } + + @Test + public void check_appManager_empty() { + InstanceCacheSummary instanceCacheSummary = checker.check(); + + Assert.assertEquals(Json.encode(expectedSummary), Json.encode(instanceCacheSummary)); + } + + @Test + public void check_microserviceManager_empty() { + appId = "notExist"; + serviceRegistry.getAppManager().getOrCreateMicroserviceVersions(appId, microserviceName); + InstanceCacheSummary instanceCacheSummary = checker.check(); + + InstanceCacheResult instanceCacheResult = new InstanceCacheResult(); + instanceCacheResult.setAppId(appId); + instanceCacheResult.setMicroserviceName(microserviceName); + instanceCacheResult.setStatus(Status.NORMAL); + + Assert.assertEquals(Json.encode(expectedSummary), Json.encode(instanceCacheSummary)); + } + + protected Holder<MicroserviceInstances> createFindServiceInstancesResult() { + MicroserviceInstances microserviceInstances = new MicroserviceInstances(); + microserviceInstances.setNeedRefresh(true); + microserviceInstances.setRevision("first"); + FindInstancesResponse findInstancesResponse = new FindInstancesResponse(); + findInstancesResponse.setInstances(new ArrayList<>()); + microserviceInstances.setInstancesResponse(findInstancesResponse); + + Holder<MicroserviceInstances> findHolder = new Holder<>(); + findHolder.value = microserviceInstances; + return findHolder; + } + + protected void registerMicroservice(String appId, String microserviceName) { + Microservice microservice = new Microservice(); + microservice.setAppId(appId); + microservice.setServiceName(microserviceName); + microservice.setVersion("1.0.0"); + + serviceRegistry.getServiceRegistryClient().registerMicroservice(microservice); + } + + @Test + public void check_findInstances_failed() { + Holder<MicroserviceInstances> findHolder = createFindServiceInstancesResult(); + + new MockUp<RegistryUtils>() { + @Mock + MicroserviceInstances findServiceInstances(String appId, String serviceName, + String versionRule, String revision) { + return findHolder.value; + } + }; + + registerMicroservice(appId, microserviceName); + + serviceRegistry.getAppManager() + .getOrCreateMicroserviceVersionRule(appId, microserviceName, DefinitionConst.VERSION_RULE_ALL); + + findHolder.value = null; + InstanceCacheSummary instanceCacheSummary = checker.check(); + + InstanceCacheResult instanceCacheResult = new InstanceCacheResult(); + instanceCacheResult.setAppId(appId); + instanceCacheResult.setMicroserviceName(microserviceName); + instanceCacheResult.setStatus(Status.UNKNOWN); + instanceCacheResult.setDetail("failed to find instances from service center"); + expectedSummary.getProducers().add(instanceCacheResult); + expectedSummary.setStatus(Status.UNKNOWN); + + Assert.assertEquals(Json.encode(expectedSummary), Json.encode(instanceCacheSummary)); + } + + @Test + public void check_findInstances_serviceNotExist() { + Holder<MicroserviceInstances> findHolder = createFindServiceInstancesResult(); + + new MockUp<RegistryUtils>() { + @Mock + MicroserviceInstances findServiceInstances(String appId, String serviceName, + String versionRule, String revision) { + return findHolder.value; + } + }; + + registerMicroservice(appId, microserviceName); + + serviceRegistry.getAppManager() + .getOrCreateMicroserviceVersionRule(appId, microserviceName, DefinitionConst.VERSION_RULE_ALL); + + findHolder.value.setMicroserviceNotExist(true); + InstanceCacheSummary instanceCacheSummary = checker.check(); + + InstanceCacheResult instanceCacheResult = new InstanceCacheResult(); + instanceCacheResult.setAppId(appId); + instanceCacheResult.setMicroserviceName(microserviceName); + instanceCacheResult.setStatus(Status.UNKNOWN); + instanceCacheResult.setDetail("microservice is not exist anymore, will be deleted from memory in next pull"); + expectedSummary.getProducers().add(instanceCacheResult); + expectedSummary.setStatus(Status.UNKNOWN); + + Assert.assertEquals(Json.encode(expectedSummary), Json.encode(instanceCacheSummary)); + } + + @Test + public void check_findInstances_revisionNotMatch() { + Holder<MicroserviceInstances> findHolder = createFindServiceInstancesResult(); + + new MockUp<RegistryUtils>() { + @Mock + MicroserviceInstances findServiceInstances(String appId, String serviceName, + String versionRule, String revision) { + return findHolder.value; + } + }; + + registerMicroservice(appId, microserviceName); + + serviceRegistry.getAppManager() + .getOrCreateMicroserviceVersionRule(appId, microserviceName, DefinitionConst.VERSION_RULE_ALL); + + findHolder.value.setRevision("second"); + InstanceCacheSummary instanceCacheSummary = checker.check(); + + InstanceCacheResult instanceCacheResult = new InstanceCacheResult(); + instanceCacheResult.setAppId(appId); + instanceCacheResult.setMicroserviceName(microserviceName); + instanceCacheResult.setStatus(Status.UNKNOWN); + instanceCacheResult.setDetail( + "revision is different, will be synchronized in next pull. local revision=first, remote revision=second"); + expectedSummary.getProducers().add(instanceCacheResult); + expectedSummary.setStatus(Status.UNKNOWN); + + Assert.assertEquals(Json.encode(expectedSummary), Json.encode(instanceCacheSummary)); + } + + @Test + public void check_findInstances_cacheNotMatch() { + Holder<MicroserviceInstances> findHolder = createFindServiceInstancesResult(); + + new MockUp<RegistryUtils>() { + @Mock + MicroserviceInstances findServiceInstances(String appId, String serviceName, + String versionRule, String revision) { + return findHolder.value; + } + }; + + registerMicroservice(appId, microserviceName); + + serviceRegistry.getAppManager() + .getOrCreateMicroserviceVersionRule(appId, microserviceName, DefinitionConst.VERSION_RULE_ALL); + + Holder<MicroserviceInstances> newFindHolder = createFindServiceInstancesResult(); + newFindHolder.value.getInstancesResponse().getInstances().add(new MicroserviceInstance()); + findHolder.value = newFindHolder.value; + InstanceCacheSummary instanceCacheSummary = checker.check(); + + InstanceCacheResult instanceCacheResult = new InstanceCacheResult(); + instanceCacheResult.setAppId(appId); + instanceCacheResult.setMicroserviceName(microserviceName); + instanceCacheResult.setStatus(Status.ABNORMAL); + instanceCacheResult.setDetail( + "instance cache not match"); + expectedSummary.getProducers().add(instanceCacheResult); + expectedSummary.setStatus(Status.ABNORMAL); + + Assert.assertEquals(Json.encode(expectedSummary), Json.encode(instanceCacheSummary)); + } +} diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java index d4d2b5d15..3b01fe885 100644 --- a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java +++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java @@ -100,4 +100,20 @@ public void testSchema() { String content = serviceRegistry.getServiceRegistryClient().getSchema(microservice.getServiceId(), "s1"); Assert.assertEquals("s1-content", content); } + + @Test + public void registerMicroservice() { + ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); + serviceRegistry.init(); + serviceRegistry.run(); + + Microservice microservice = new Microservice(); + microservice.setAppId("appId"); + microservice.setServiceName("msName"); + + String serviceId = serviceRegistry.getServiceRegistryClient().registerMicroservice(microservice); + Microservice remoteMicroservice = serviceRegistry.getRemoteMicroservice(serviceId); + + Assert.assertEquals(serviceId, remoteMicroservice.getServiceId()); + } } diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestRemoteServiceRegistry.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestRemoteServiceRegistry.java index d46ef932f..73483fac1 100644 --- a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestRemoteServiceRegistry.java +++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/registry/TestRemoteServiceRegistry.java @@ -17,12 +17,15 @@ package org.apache.servicecomb.serviceregistry.registry; import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.foundation.common.net.IpPort; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.serviceregistry.RegistryUtils; import org.apache.servicecomb.serviceregistry.ServiceRegistry; import org.apache.servicecomb.serviceregistry.client.LocalServiceRegistryClientImpl; @@ -65,12 +68,20 @@ protected ServiceRegistryClient createServiceRegistryClient() { @Test public void testLifeCycle(@Injectable ServiceRegistryConfig config, @Injectable MicroserviceDefinition definition, - @Injectable ServiceRegistry registry) { + @Injectable ServiceRegistry registry) throws InterruptedException { ArrayList<IpPort> ipPortList = new ArrayList<>(); ipPortList.add(new IpPort("127.0.0.1", 9980)); ipPortList.add(new IpPort("127.0.0.1", 9981)); - new Expectations() { + CountDownLatch latch = new CountDownLatch(1); + ServiceRegistryTaskInitializer initializer = new MockUp<ServiceRegistryTaskInitializer>() { + @Mock + void init(RemoteServiceRegistry remoteServiceRegistry) { + latch.countDown(); + } + }.getMockInstance(); + + new Expectations(SPIServiceUtils.class) { { definition.getConfiguration(); result = ConfigUtil.createLocalConfig(); @@ -86,6 +97,8 @@ public void testLifeCycle(@Injectable ServiceRegistryConfig config, @Injectable result = 30; config.isWatch(); result = false; + SPIServiceUtils.getOrLoadSortedService(ServiceRegistryTaskInitializer.class); + result = Arrays.asList(initializer); } }; @@ -95,6 +108,10 @@ public void testLifeCycle(@Injectable ServiceRegistryConfig config, @Injectable RemoteServiceRegistry remote = new TestingRemoteServiceRegistry(bus, config, definition); remote.init(); remote.run(); + + // should not block + latch.await(); + Assert.assertTrue(2 <= remote.getTaskPool().getTaskCount()); // includes complete tasks bus.post(new ShutdownEvent()); @@ -102,7 +119,6 @@ public void testLifeCycle(@Injectable ServiceRegistryConfig config, @Injectable remote.getTaskPool().schedule(new Runnable() { @Override public void run() { - // TODO Auto-generated method stub } }, 0, TimeUnit.SECONDS); ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org > provide a way to invoke service with full path > ---------------------------------------------- > > Key: SCB-760 > URL: https://issues.apache.org/jira/browse/SCB-760 > Project: Apache ServiceComb > Issue Type: New Feature > Reporter: liubao > Assignee: liubao > Priority: Major > > When deploying in a container, like tomcat, users want to invoke service > with full path, including container context > root and servlet path. -- This message was sent by Atlassian JIRA (v7.6.3#76005)