diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java index 577a16e2082..b38a8aa0b50 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java @@ -74,16 +74,20 @@ private void onOpenAPIChanged(String application, String serviceName) { executor.execute(() -> { synchronized (referenceConfigsLocks.computeIfAbsent(parser.getAppId(), key -> new ConcurrentHashMapEx<>()) .computeIfAbsent(parser.getMicroserviceName(), key -> new Object())) { - MicroserviceReferenceConfig temp = referenceConfigs.get(parser.getAppId()) - .get(parser.getMicroserviceName()); - if (temp != null) { + try { + MicroserviceReferenceConfig temp = referenceConfigs.get(parser.getAppId()) + .get(parser.getMicroserviceName()); + if (temp != null) { + result.complete(temp); + return; + } + temp = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), + parser.getMicroserviceName()); + referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), temp); result.complete(temp); - return; + } catch (Exception e) { + result.completeExceptionally(e); } - temp = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), - parser.getMicroserviceName()); - referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), temp); - result.complete(temp); } }); return result; diff --git a/demo/demo-edge/README.md b/demo/demo-edge/README.md new file mode 100644 index 00000000000..f2d478bcb6f --- /dev/null +++ b/demo/demo-edge/README.md @@ -0,0 +1,20 @@ +# About edge service API compatibility + +* Edge service use the latest version of the microservice meta. e.g. For business 1.0.0, 1.1.0, 2.0.0 have the following APIs: + + * 1.0.0: /business/v1/add + * 1.1.0: /business/v1/add, /business/v1/dec + * 2.0.0: /business/v2/add, /business/v2/dec + + If users invoke /business/v1/add, edge service will give NOT FOUND, because 2.0.0 microservice meta do not have this API. Even using router to route all /business/v1/* requests to 1.1.0, path locating happens before load balance. + +* It's very important to keep your API compatibility cross versions if these versions need work together. e.g. + + * 1.0.0: /business/v1/add + * 1.1.0: /business/v1/add, /business/v1/dec + * 2.0.0: /business/v1/add, /business/v1/dec, /business/v2/add, /business/v2/dec + + Together with router, /business/v1/add will go correctly to 1.0.0 or 1.1.0, and /business/v2/add will go correctly to 2.0.0. Without router, /business/v2/add may route to 1.0.0 or 1.1.0 and NOT FOUND is reported. + + + diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java index 942133ece03..fde945e50bf 100644 --- a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java @@ -17,39 +17,18 @@ package org.apache.servicecomb.demo.edge.business; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.FileUtils; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; -import org.apache.servicecomb.demo.edge.model.DependTypeA; -import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; -import org.apache.servicecomb.demo.edge.model.User; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; - -@RestSchema(schemaId = "news-v2") -@RequestMapping(path = "/business/v2") +@RestSchema(schemaId = "news-v1") +@RequestMapping(path = "/business/v1") public class Impl { private Environment environment; @@ -58,12 +37,6 @@ public void setEnvironment(Environment environment) { this.environment = environment; } - File tempDir = new File("target/downloadTemp"); - - public Impl() throws IOException { - FileUtils.forceMkdir(tempDir); - } - @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { AppClientDataRsp response = new AppClientDataRsp(); @@ -81,45 +54,4 @@ public ResultWithInstance add(int x, int y) { public ResultWithInstance dec(int x, int y) { return ResultWithInstance.create(x - y, environment); } - - @GetMapping(path = "/download") - @ApiResponses({ - @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = File.class)), description = ""), - }) - public ResponseEntity download() throws IOException { - return ResponseEntity - .ok() - .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=download.txt") - .body(new ByteArrayInputStream("download".getBytes(StandardCharsets.UTF_8))); - } - - protected File createBigFile() throws IOException { - File file = new File(tempDir, "bigFile.txt"); - file.delete(); - RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); - randomAccessFile.setLength(10 * 1024 * 1024); - randomAccessFile.close(); - return file; - } - - @GetMapping(path = "/bigFile") - public File bigFile() throws IOException { - return createBigFile(); - } - - @PostMapping(path = "recursiveSelf") - public RecursiveSelfType recursiveSelf(@RequestBody RecursiveSelfType value) { - return value; - } - - @PostMapping(path = "dependType") - public DependTypeA dependType(@RequestBody DependTypeA value) { - return value; - } - - @PostMapping(path = "encrypt") - public User encrypt(@RequestBody User value) { - return value; - } } diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.java new file mode 100644 index 00000000000..0e5eabf290f --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.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.demo.edge.business; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; +import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; +import org.apache.servicecomb.demo.edge.model.DependTypeA; +import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; +import org.apache.servicecomb.demo.edge.model.ResultWithInstance; +import org.apache.servicecomb.demo.edge.model.User; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + +@RestSchema(schemaId = "news-v2") +@RequestMapping(path = "/business/v2") +public class ImplV2 { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + File tempDir = new File("target/downloadTemp"); + + public ImplV2() throws IOException { + FileUtils.forceMkdir(tempDir); + } + + @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) + public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { + AppClientDataRsp response = new AppClientDataRsp(); + String rsp = "result from 2.0.0"; + response.setRsp(rsp); + return response; + } + + @RequestMapping(path = "/add", method = RequestMethod.GET) + public ResultWithInstance add(int x, int y) { + return ResultWithInstance.create(x + y, environment); + } + + @RequestMapping(path = "/dec", method = RequestMethod.GET) + public ResultWithInstance dec(int x, int y) { + return ResultWithInstance.create(x - y, environment); + } + + @GetMapping(path = "/download") + @ApiResponses({ + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = File.class)), description = ""), + }) + public ResponseEntity download() throws IOException { + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=download.txt") + .body(new ByteArrayInputStream("download".getBytes(StandardCharsets.UTF_8))); + } + + protected File createBigFile() throws IOException { + File file = new File(tempDir, "bigFile.txt"); + file.delete(); + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); + randomAccessFile.setLength(10 * 1024 * 1024); + randomAccessFile.close(); + return file; + } + + @GetMapping(path = "/bigFile") + public File bigFile() throws IOException { + return createBigFile(); + } + + @PostMapping(path = "recursiveSelf") + public RecursiveSelfType recursiveSelf(@RequestBody RecursiveSelfType value) { + return value; + } + + @PostMapping(path = "dependType") + public DependTypeA dependType(@RequestBody DependTypeA value) { + return value; + } + + @PostMapping(path = "encrypt") + public User encrypt(@RequestBody User value) { + return value; + } +} diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java index 9fea0a2663b..ab10a7c3713 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java @@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.DependTypeA; import org.apache.servicecomb.demo.edge.model.DependTypeB; @@ -42,6 +43,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -56,9 +58,9 @@ public class Consumer { String edgePrefix; -// List addV1Result = new ArrayList<>(); + List addV1Result = new ArrayList<>(); -// List decV1Result = new ArrayList<>(); + List decV1Result = new ArrayList<>(); List addV2Result = new ArrayList<>(); @@ -94,14 +96,14 @@ public void run(String prefix) { testDownload(); testDownloadBigFile(); testErrorCode(); - // TODO: we use router to test this feature. -// invoke("/v1/add", 2, 1, addV1Result); -// invoke("/v1/add", 3, 1, addV1Result); -// invoke("/v1/add", 4, 1, addV1Result); -// invoke("/v1/add", 5, 1, addV1Result); -// -// invoke("/v1/dec", 2, 1, decV1Result); -// invoke("/v1/dec", 3, 1, decV1Result); + + invoke("/v1/add", 2, 1, addV1Result); + invoke("/v1/add", 3, 1, addV1Result); + invoke("/v1/add", 4, 1, addV1Result); + invoke("/v1/add", 5, 1, addV1Result); + + invoke("/v1/dec", 2, 1, decV1Result); + invoke("/v1/dec", 3, 1, decV1Result); invoke("/v2/add", 2, 1, addV2Result); invoke("/v2/add", 3, 1, addV2Result); @@ -109,14 +111,13 @@ public void run(String prefix) { invoke("/v2/dec", 2, 1, decV2Result); invoke("/v2/dec", 3, 1, decV2Result); - // TODO: we use router to test this feature. -// printResults("v1/add", addV1Result); -// printResults("v1/dec", decV1Result); + printResults("v1/add", addV1Result); + printResults("v1/dec", decV1Result); printResults("v2/add", addV2Result); printResults("v2/dec", decV2Result); -// checkResult("v1/add", addV1Result, "1.0.0", "1.1.0"); -// checkResult("v1/dec", decV1Result, "1.1.0"); + checkResult("v1/add", addV1Result, "1.0.0", "1.1.0"); + checkResult("v1/dec", decV1Result, "1.1.0"); checkResult("v2/add", addV2Result, "2.0.0"); checkResult("v2/dec", decV2Result, "2.0.0"); } @@ -282,21 +283,20 @@ private URIEndpointObject prepareEdge(String prefix) { } protected void invokeBusiness(String urlPrefix, ChannelRequestBase request) { - // since 3.0.0, do not support this feature. - // after 3.0.0, the client will load schema once after startup and will never change, and there - // isn't version rule concept. - - // TODO: we use router to test this feature. -// for (int i = 0; i < 3; i++) { -// String url = urlPrefix + "/channel/news/subscribe"; -// -// HttpHeaders headers = new HttpHeaders(); -// headers.setContentType(MediaType.APPLICATION_JSON); -// -// HttpEntity entity = new HttpEntity<>(request, headers); -// -// ResponseEntity response = template.postForEntity(url, entity, AppClientDataRsp.class); -// Assert.isTrue(response.getBody().getRsp().equals("result from 1.1.0"), response.getBody().getRsp()); -// } + List result = new ArrayList<>(6); + for (int i = 0; i < 6; i++) { + String url = urlPrefix + "/channel/news/subscribe"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(request, headers); + + ResponseEntity response = template.postForEntity(url, entity, AppClientDataRsp.class); + result.add(response.getBody().getRsp()); + } + Assert.isTrue(result.contains("result from 2.0.0"), "invokeBusiness not balance"); + Assert.isTrue(result.contains("result from 1.1.0"), "invokeBusiness not balance"); + Assert.isTrue(result.contains("result from 1.0.0"), "invokeBusiness not balance"); } } diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java index f360912be2e..ae462eb6fd1 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java @@ -42,8 +42,9 @@ public static void runTest() throws Exception { new Consumer().run("rest"); System.out.println("Running url dispatcher."); new Consumer().run("url"); - System.out.println("Running http dispatcher."); - new Consumer().run("http"); +// Common Http Dispatcher do not have OperationMeta, can not use router. +// System.out.println("Running http dispatcher."); +// new Consumer().run("http"); System.out.println("All test case finished."); diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java index 613ba8a0274..53434ac69ca 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java @@ -31,6 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestOperations; @@ -52,7 +53,8 @@ public void testRestTransport() throws Exception { // edge service do not support retry // testEdgeServiceRetry(); - testEdgeServiceInstanceIsolation(); + testEdgeServiceInstanceIsolation(); // may isolate instance for 5 seconds. + Thread.sleep(6000); // ensure isolation is open for new requests testEdgeServiceInstanceBulkhead(); } @@ -98,6 +100,7 @@ private void testEdgeServiceInstanceIsolation() throws Exception { String url = edgePrefix + "/business/v2/testEdgeServiceInstanceIsolation"; CountDownLatch latch = new CountDownLatch(100); + AtomicBoolean expectedFailed404 = new AtomicBoolean(false); AtomicBoolean expectedFailed502 = new AtomicBoolean(false); AtomicBoolean expectedFailed503 = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); @@ -113,15 +116,24 @@ public void run() { notExpectedFailed.set(true); } } catch (Exception e) { - if (!(e instanceof HttpServerErrorException)) { - notExpectedFailed.set(true); - } else { + if (e instanceof HttpClientErrorException) { + // isolate 2.0.0 and other instance will give 404 + if (((HttpClientErrorException) e).getStatusCode().value() == 404) { + expectedFailed404.set(true); + } else { + notExpectedFailed.set(true); + } + } else if (e instanceof HttpServerErrorException) { + // instance isolated and return 503 if (((HttpServerErrorException) e).getStatusCode().value() == 503) { expectedFailed503.set(true); } + // provider throw 502 exception to trigger instance isolation if (((HttpServerErrorException) e).getStatusCode().value() == 502) { expectedFailed502.set(true); } + } else { + notExpectedFailed.set(true); } } latch.countDown(); @@ -132,6 +144,7 @@ public void run() { } latch.await(20, TimeUnit.SECONDS); + TestMgr.check(true, expectedFailed404.get()); TestMgr.check(true, expectedFailed502.get()); TestMgr.check(true, expectedFailed503.get()); TestMgr.check(false, notExpectedFailed.get()); diff --git a/demo/demo-edge/consumer/src/main/resources/microservice.yaml b/demo/demo-edge/consumer/src/main/resources/microservice.yaml index 893ace0961c..953b1e567e4 100644 --- a/demo/demo-edge/consumer/src/main/resources/microservice.yaml +++ b/demo/demo-edge/consumer/src/main/resources/microservice.yaml @@ -29,17 +29,3 @@ servicecomb: chain: Consumer: default: loadbalance - -# TODO: use router to replace version-rule -# routeRule: -# business: | -# - precedence: 2 -# match: -# headers: -# x-url: -# prefix: "/business/v1" -# route: -# - weight: 100 -# tags: -# version: 1.1.0 - diff --git a/demo/demo-edge/edge-service/src/main/resources/microservice.yaml b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml index 251b32ca010..01cd888b2aa 100644 --- a/demo/demo-edge/edge-service/src/main/resources/microservice.yaml +++ b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml @@ -57,14 +57,10 @@ servicecomb: prefixSegmentCount: 1 path: "/url/business/v1/.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 1.0.0-2.0.0 businessV2: prefixSegmentCount: 1 path: "/url/business/v2/.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 2.0.0-3.0.0 http: enabled: true mappings: @@ -72,22 +68,16 @@ servicecomb: prefixSegmentCount: 1 path: "/http/business/v2/.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 2.0.0 businessV1: prefixSegmentCount: 1 path: "/http/business/v1/add.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 1.0.0-1.2.0 businessV1_1: prefixSegmentCount: 1 path: "/http/business/v1/dec.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 1.1.0 -# 服务治理配置 + # 服务治理配置 matchGroup: testEdgeServiceRetry: | matches: @@ -101,11 +91,11 @@ servicecomb: matches: - apiPath: exact: "/business/v2/testEdgeServiceInstanceBulkhead" -# retry not supported now -# retry: -# testEdgeServiceRetry: | -# maxAttempts: 2 -# retryOnSame: 0 + # retry not supported now + # retry: + # testEdgeServiceRetry: | + # maxAttempts: 2 + # retryOnSame: 0 instanceIsolation: testEdgeServiceInstanceIsolation: | minimumNumberOfCalls: 10 @@ -114,9 +104,35 @@ servicecomb: failureRateThreshold: 50 slowCallRateThreshold: 100 slowCallDurationThreshold: 3000 - waitDurationInOpenState: 10000 + waitDurationInOpenState: 5000 permittedNumberOfCallsInHalfOpenState: 10 instanceBulkhead: testEdgeServiceInstanceBulkhead: | maxConcurrentCalls: 1 maxWaitDuration: 1 + + # enable router for edge service + router: + type: router + routeRule: + business: | + - precedence: 2 + match: + apiPath: + prefix: "/business/v2" + route: + - weight: 100 + tags: + version: 2.0.0 + - precedence: 1 + match: + apiPath: + prefix: "/business/v1/dec" + route: + - weight: 50 + tags: + version: 1.1.0 + - weight: 50 + tags: + version: 2.0.0 + diff --git a/demo/docker-run-config-edge/pom.xml b/demo/docker-run-config-edge/pom.xml index 58314f9d7fb..a5d903dc311 100644 --- a/demo/docker-run-config-edge/pom.xml +++ b/demo/docker-run-config-edge/pom.xml @@ -86,8 +86,6 @@ - - business-2-0-0:${project.version} business-2-0-0 diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java index 2a454e5d11a..239b38efd00 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.governance.marker; +import java.util.Collections; import java.util.Map; import org.springframework.util.LinkedCaseInsensitiveMap; @@ -26,7 +27,7 @@ public class GovernanceRequest implements GovernanceRequestExtractor { * For provider: headers indicates the request headers to me. * For consumer: headers indicates the request headers to the target. */ - private Map headers; + private Map headers = Collections.emptyMap(); /** * api path with this request, maybe null. For REST, e.g. /foo/bar; For RPC, e.g. MySchema.sayHello diff --git a/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java b/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java index 4b11c5481a3..0e12cc753e1 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java +++ b/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.router; +import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.match.RouterRuleMatcher; import org.springframework.context.annotation.Bean; @@ -30,8 +31,8 @@ public RouterRuleCache routerRuleCache(Environment environment) { } @Bean - public RouterRuleMatcher routerRuleMatcher(RouterRuleCache routerRuleCache) { - return new RouterRuleMatcher(routerRuleCache); + public RouterRuleMatcher routerRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { + return new RouterRuleMatcher(routerRuleCache, requestProcessor); } @Bean diff --git a/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java b/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java index f544dd71c35..ab742e8613d 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java +++ b/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.router; import java.util.List; -import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.apache.servicecomb.router.match.RouterRuleMatcher; @@ -42,7 +42,7 @@ public RouterFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerR } public List getFilteredListOfServers(List list, - String targetServiceName, Map headers, RouterDistributor distributor) { + String targetServiceName, GovernanceRequestExtractor extractor, RouterDistributor distributor) { if (CollectionUtils.isEmpty(list)) { return list; } @@ -55,7 +55,7 @@ public List getFilteredListOfServers(List list, return list; } // 2.match rule - PolicyRuleItem invokeRule = routerRuleMatcher.match(targetServiceName, headers); + PolicyRuleItem invokeRule = routerRuleMatcher.match(targetServiceName, extractor); if (invokeRule == null) { LOGGER.debug("route management match rule failed"); diff --git a/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java b/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java index a7fcb0f2811..dd2ba78d7d3 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java +++ b/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java @@ -16,22 +16,25 @@ */ package org.apache.servicecomb.router.match; -import java.util.Map; - +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.model.PolicyRuleItem; public class RouterRuleMatcher { private final RouterRuleCache routerRuleCache; - public RouterRuleMatcher(RouterRuleCache routerRuleCache) { + private final RequestProcessor requestProcessor; + + public RouterRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { this.routerRuleCache = routerRuleCache; + this.requestProcessor = requestProcessor; } - public PolicyRuleItem match(String serviceName, Map invokeHeader) { + public PolicyRuleItem match(String serviceName, GovernanceRequestExtractor request) { for (PolicyRuleItem rule : routerRuleCache.getServiceInfoCacheMap().get(serviceName) .getAllrule()) { - if (rule.getMatch() == null || rule.getMatch().match(invokeHeader)) { + if (rule.getMatch() == null || requestProcessor.match(request, rule.getMatch())) { return rule; } } diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/Matcher.java b/governance/src/main/java/org/apache/servicecomb/router/model/Matcher.java deleted file mode 100644 index 7db815dd267..00000000000 --- a/governance/src/main/java/org/apache/servicecomb/router/model/Matcher.java +++ /dev/null @@ -1,93 +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.router.model; - -import java.util.Map; -import org.springframework.util.CollectionUtils; - -/** - * @Author GuoYl123 - * @Date 2019/10/17 - **/ -public class Matcher { - - private String source; - - private Map sourceTags; - - private Map headers; - - private String refer; - - public Matcher() { - } - - public boolean match(Map realHeaders) { - if (CollectionUtils.isEmpty(headers)) { - return true; - } - for (Map.Entry entry : headers.entrySet()) { - if (!realHeaders.containsKey(entry.getKey()) || !entry.getValue() - .match(realHeaders.get(entry.getKey()))) { - return false; - } - } - return true; - } - - public String getSource() { - return source; - } - - public void setSource(String source) { - this.source = source; - } - - public Map getSourceTags() { - return sourceTags; - } - - public void setSourceTags(Map sourceTags) { - this.sourceTags = sourceTags; - } - - public Map getHeaders() { - return headers; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public String getRefer() { - return refer; - } - - public void setRefer(String refer) { - this.refer = refer; - } - - @Override - public String toString() { - return "Matcher{" + - "source='" + source + '\'' + - ", sourceTags=" + sourceTags + - ", headers=" + headers + - ", refer='" + refer + '\'' + - '}'; - } -} diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java b/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java index 1006abfec1b..769b07bdcd5 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java @@ -18,6 +18,7 @@ import java.util.List; +import org.apache.servicecomb.governance.marker.Matcher; import org.apache.servicecomb.router.exception.RouterIllegalParamException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +36,6 @@ public class PolicyRuleItem implements Comparable { private Matcher match; - // any match private List route; private Integer total; diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java index c3901878bdd..02eff797822 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java @@ -26,6 +26,8 @@ import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.Before; @@ -124,9 +126,11 @@ private void postConfigurationChangedEvent() { @Test public void testMatchPrecedenceHigher() { + GovernanceRequest governanceRequest = new GovernanceRequest(); Map headers = new HashMap<>(); headers.put("userId", "01"); headers.put("appId", "01"); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -136,7 +140,7 @@ public void testMatchPrecedenceHigher() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); } @@ -164,6 +168,8 @@ public void testCaseSensitiveNotMatch() { Map headers = new HashMap<>(); headers.put("userId", "User01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -173,7 +179,7 @@ public void testCaseSensitiveNotMatch() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); Assertions.assertEquals("2.0", resultServerList.get(0).getVersion()); @@ -207,6 +213,8 @@ public void testNoneMatch() { Map headers = new HashMap<>(); headers.put("userId", "User01"); headers.put("appId", "App01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -216,7 +224,7 @@ public void testNoneMatch() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(2, resultServerList.size()); } @@ -248,6 +256,8 @@ public void testOneMatchButNoInstance() { Map headers = new HashMap<>(); headers.put("userId", "user01"); headers.put("appId", "app02"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -257,13 +267,13 @@ public void testOneMatchButNoInstance() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(2, resultServerList.size()); } - private List mainFilter(List serverList, Map headers) { + private List mainFilter(List serverList, GovernanceRequestExtractor requestExtractor) { return routerFilter - .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, headers, + .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, requestExtractor, testDistributor); } } diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java index 47228c7b82b..6ded0c21147 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java @@ -18,7 +18,6 @@ package org.apache.servicecomb.router; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -27,6 +26,8 @@ import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.Before; @@ -51,10 +52,9 @@ public class RouterDistributorDynamicConfigTest { + " match: #匹配策略\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 01\n" + + " exact: \"01\"\n" + " route: #路由规则\n" + " - weight: 50\n" + " tags:\n" @@ -63,10 +63,9 @@ public class RouterDistributorDynamicConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 02\n" + + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -75,10 +74,9 @@ public class RouterDistributorDynamicConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 03\n" + + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -143,7 +141,7 @@ public String[] getPropertyNames() { @Test public void testHeaderIsEmpty() { List list = getMockList(); - List serverList = mainFilter(list, Collections.emptyMap()); + List serverList = mainFilter(list, new GovernanceRequest()); Assertions.assertEquals(2, serverList.size()); } @@ -152,9 +150,11 @@ public void testVersionNotMatch() { Map headerMap = new HashMap<>(); headerMap.put("userId", "02"); headerMap.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headerMap); List list = getMockList(); list.remove(1); - List serverList = mainFilter(list, headerMap); + List serverList = mainFilter(list, governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("01", serverList.get(0).getId()); } @@ -164,7 +164,9 @@ public void testVersionMatch() { Map headers = new HashMap<>(); headers.put("userId", "01"); headers.put("appId", "01"); - List serverList = mainFilter(getMockList(), headers); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + List serverList = mainFilter(getMockList(), governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("02", serverList.get(0).getId()); } @@ -174,7 +176,9 @@ public void testMatchPrecedenceLower() { Map headers = new HashMap<>(); headers.put("userId", "02"); headers.put("appId", "01"); - List serverList = mainFilter(getMockList(), headers); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + List serverList = mainFilter(getMockList(), governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("01", serverList.get(0).getId()); } @@ -184,6 +188,8 @@ public void testMatchPrecedenceHigher() { Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -194,7 +200,7 @@ public void testMatchPrecedenceHigher() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("02", resultServerList.get(0).getId()); } @@ -210,9 +216,10 @@ private List getMockList() { return serverList; } - private List mainFilter(List serverList, Map headers) { + private List mainFilter(List serverList, + GovernanceRequestExtractor governanceRequestExtractor) { return routerFilter - .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, headers, + .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, governanceRequestExtractor, testDistributor); } } diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java index 19f0f47f35f..63af6e2a5b5 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.router; +import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.Test; import org.junit.jupiter.api.Assertions; @@ -52,8 +53,11 @@ public void setRouterDistributor(RouterDistributor routerDistributor public void testDistribute() { List list = initServiceList(); HashMap header = new HashMap<>(); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName()); @@ -63,7 +67,7 @@ public void testDistribute() { for (int i = 0; i < 10; i++) { List serverList = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java index 763e2eeca09..43edbb041dc 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.List; +import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.Test; import org.junit.jupiter.api.Assertions; @@ -52,8 +53,10 @@ public void setRouterDistributor(RouterDistributor routerDistributor public void testDistribute() { List list = initServiceList(); HashMap header = new HashMap<>(); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); List listOfServers = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName()); @@ -63,7 +66,7 @@ public void testDistribute() { for (int i = 0; i < 10; i++) { List serverList = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java index 587e7fc7d58..489f1a6e59f 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java @@ -26,6 +26,8 @@ import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.junit.Before; @@ -52,10 +54,9 @@ public class RouterDistributorGlobalConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 02\n" + + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -64,10 +65,9 @@ public class RouterDistributorGlobalConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 03\n" + + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -129,6 +129,8 @@ public void testUseGlobalRouteRule() { Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -139,7 +141,7 @@ public void testUseGlobalRouteRule() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); } @@ -151,10 +153,9 @@ public void testUseProviderRouteRule() { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 03\n" + + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -163,10 +164,9 @@ public void testUseProviderRouteRule() { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 02\n" + + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -177,6 +177,8 @@ public void testUseProviderRouteRule() { Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -187,14 +189,15 @@ public void testUseProviderRouteRule() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("02", resultServerList.get(0).getId()); } - private List mainFilter(List serverList, Map headers) { + private List mainFilter(List serverList, + GovernanceRequestExtractor governanceRequestExtractor) { return routerFilter - .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, headers, + .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, governanceRequestExtractor, testDistributor); } diff --git a/governance/src/test/resources/META-INF/spring/bean.xml b/governance/src/test/resources/META-INF/spring/bean.xml index f9382441770..5ff6c6e05cc 100644 --- a/governance/src/test/resources/META-INF/spring/bean.xml +++ b/governance/src/test/resources/META-INF/spring/bean.xml @@ -23,4 +23,5 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> + diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterAddHeaderFilter.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterAddHeaderFilter.java deleted file mode 100644 index f99556dfb1a..00000000000 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterAddHeaderFilter.java +++ /dev/null @@ -1,111 +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.router.custom; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.AbstractFilter; -import org.apache.servicecomb.core.filter.EdgeFilter; -import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProviderFilter; -import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; -import org.apache.servicecomb.foundation.common.utils.JsonUtils; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.JsonProcessingException; - -public class RouterAddHeaderFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { - - private static final Logger LOGGER = LoggerFactory.getLogger(RouterAddHeaderFilter.class); - - private static final String SERVICECOMB_ROUTER_HEADER = "servicecomb.router.header"; - - private List allHeader = new ArrayList<>(); - - public RouterAddHeaderFilter() { - loadHeaders(); - } - - /** - * read config and get Header - */ - private void loadHeaders() { - addAllHeaders(LegacyPropertyFactory.getStringProperty(SERVICECOMB_ROUTER_HEADER)); - } - - private void addAllHeaders(String str) { - if (StringUtils.isEmpty(str)) { - return; - } - try { - allHeader = Arrays.asList(str.split(",")); - } catch (Exception e) { - LOGGER.error("route management Serialization failed: {}", e.getMessage()); - } - } - - private Map getHeaderMap(HttpServletRequestEx httpServletRequestEx) { - Map headerMap = new HashMap<>(); - allHeader.forEach(headerKey -> { - String val = httpServletRequestEx.getHeader(headerKey); - if (!StringUtils.isEmpty(val)) { - headerMap.put(headerKey, httpServletRequestEx.getHeader(headerKey)); - } - }); - return headerMap; - } - - @Override - public int getOrder() { - return ProviderFilter.PROVIDER_SCHEDULE_FILTER_ORDER - 1970; - } - - @Override - public String getName() { - return "router-add-header"; - } - - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - if (!StringUtils.isEmpty(invocation.getContext(RouterServerListFilter.ROUTER_HEADER))) { - return nextNode.onFilter(invocation); - } - - if (allHeader.isEmpty()) { - return nextNode.onFilter(invocation); - } - - Map headerMap = getHeaderMap(invocation.getRequestEx()); - try { - invocation.addContext(RouterServerListFilter.ROUTER_HEADER, - JsonUtils.OBJ_MAPPER.writeValueAsString(headerMap)); - } catch (JsonProcessingException e) { - LOGGER.error("canary context serialization failed"); - } - return nextNode.onFilter(invocation); - } -} diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterHeaderFilterExt.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterHeaderFilterExt.java deleted file mode 100644 index 7951850f54a..00000000000 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterHeaderFilterExt.java +++ /dev/null @@ -1,35 +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.router.custom; - -import java.util.Map; - -/** - * allow users to filter not necessary headers in request. - **/ -public interface RouterHeaderFilterExt { - - default int getOrder() { - return 0; - } - - default boolean enabled() { - return true; - } - - Map doFilter(Map invokeHeader); -} diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java index 3ebe527b760..fdae82e1445 100644 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java +++ b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java @@ -16,35 +16,23 @@ */ package org.apache.servicecomb.router.custom; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.foundation.common.utils.JsonUtils; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.loadbalance.ServerListFilterExt; import org.apache.servicecomb.loadbalance.ServiceCombServer; import org.apache.servicecomb.router.RouterFilter; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; public class RouterServerListFilter implements ServerListFilterExt { - private static final Logger LOGGER = LoggerFactory.getLogger(RouterServerListFilter.class); - private static final String ENABLE = "servicecomb.router.type"; private static final String TYPE_ROUTER = "router"; - public static final String ROUTER_HEADER = "X-RouterContext"; - @SuppressWarnings("unchecked") private final RouterDistributor routerDistributor = BeanUtils .getBean(RouterDistributor.class); @@ -61,42 +49,7 @@ public boolean enabled() { public List getFilteredListOfServers(List list, Invocation invocation) { String targetServiceName = invocation.getMicroserviceName(); - Map headers = addHeaders(invocation); - headers = filterHeaders(headers); - return routerFilter - .getFilteredListOfServers(list, targetServiceName, headers, - routerDistributor); - } - - private Map filterHeaders(Map headers) { - List filters = SPIServiceUtils - .getOrLoadSortedService(RouterHeaderFilterExt.class); - for (RouterHeaderFilterExt filterExt : filters) { - if (filterExt.enabled()) { - headers = filterExt.doFilter(headers); - } - } - return headers; - } - - private Map addHeaders(Invocation invocation) { - Map headers = new HashMap<>(); - if (invocation.getContext(ROUTER_HEADER) != null) { - Map canaryContext = null; - try { - canaryContext = JsonUtils.OBJ_MAPPER - .readValue(invocation.getContext(ROUTER_HEADER), - new TypeReference>() { - }); - } catch (JsonProcessingException e) { - LOGGER.error("canary context serialization failed"); - } - if (canaryContext != null) { - headers.putAll(canaryContext); - } - } - invocation.getInvocationArguments().forEach((k, v) -> headers.put(k, v == null ? null : v.toString())); - headers.putAll(invocation.getContext()); - return headers; + return routerFilter.getFilteredListOfServers(list, targetServiceName, + MatchType.createGovHttpRequest(invocation), routerDistributor); } } diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java index 7d66001c118..5e98b714b4a 100644 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java +++ b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java @@ -32,9 +32,4 @@ public class ServiceCombRouterConfiguration { public ServiceCombRouterDistributor serviceCombRouterDistributor() { return new ServiceCombRouterDistributor(); } - - @Bean - public RouterAddHeaderFilter routerAddHeaderFilter() { - return new RouterAddHeaderFilter(); - } }