This is an automated email from the ASF dual-hosted git repository. liubao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git
commit 6de804859c4dd91c9351a71e6bbed0b2aa3f413b Author: liubao <[email protected]> AuthorDate: Mon Jan 4 09:16:57 2021 +0800 [SCB-2116]add provider implementation and refactor some api --- .../foundation/common/utils/BeanUtils.java | 8 ++ .../apache/servicecomb/governance/GovManager.java | 87 -------------- ...Handler.java => AbstractGovernanceHandler.java} | 17 ++- .../governance/handler/BulkheadHandler.java | 16 +-- .../governance/handler/CircuitBreakerHandler.java | 23 +--- .../servicecomb/governance/handler/GovHandler.java | 28 ----- .../governance/handler/RateLimitingHandler.java | 21 +--- .../governance/handler/RetryHandler.java | 21 +--- .../servicecomb/governance/FlowControlTest.java | 13 +- .../governance/GovernancePropertiesTest.java | 29 ++--- handlers/handler-governance/README.md | 3 + handlers/handler-governance/pom.xml | 65 ++++++++++ .../governance/ProviderGovernanceHandler.java | 132 +++++++++++++++++++++ ....servicecomb.registry.discovery.DiscoveryFilter | 17 +++ .../src/main/resources/config/cse.handler.xml | 21 ++++ handlers/pom.xml | 1 + 16 files changed, 301 insertions(+), 201 deletions(-) diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java index fc7606e..4e58615 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java @@ -141,6 +141,14 @@ public final class BeanUtils { return context.getBeansOfType(type); } + public static <T> T getBean(Class<T> type) { + if (context == null) { + // for some test case + return null; + } + return context.getBean(type); + } + /** * Get the implemented class of the given instance * @param bean the instance to get implemented class from diff --git a/governance/src/main/java/org/apache/servicecomb/governance/GovManager.java b/governance/src/main/java/org/apache/servicecomb/governance/GovManager.java deleted file mode 100644 index e3752e3..0000000 --- a/governance/src/main/java/org/apache/servicecomb/governance/GovManager.java +++ /dev/null @@ -1,87 +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.governance; - -import java.util.List; -import java.util.Map; - -import org.apache.servicecomb.governance.handler.GovHandler; -import org.apache.servicecomb.governance.handler.HandlerType; -import org.apache.servicecomb.governance.handler.ext.ClientRecoverPolicy; -import org.apache.servicecomb.governance.handler.ext.ServerRecoverPolicy; -import org.apache.servicecomb.governance.policy.Policy; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import io.github.resilience4j.decorators.Decorators; -import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; -import io.vavr.CheckedFunction0; -import io.vavr.control.Try; - -@Component -public class GovManager { - - @Autowired - Map<String, GovHandler> handlers; - - @Autowired(required = false) - ServerRecoverPolicy<Object> serverRecoverPolicy; - - @Autowired(required = false) - ClientRecoverPolicy<Object> clientRecoverPolicy; - - public Object processServer(List<Policy> policies, CheckedFunction0<Object> supplier) { - DecorateCheckedSupplier<Object> ds = Decorators.ofCheckedSupplier(supplier); - for (Policy policy : policies) { - if (handlers.get(policy.handler()) == null || - handlers.get(policy.handler()).type() == HandlerType.CLIENT) { - continue; - } - ds = handlers.get(policy.handler()).process(ds, policy); - } - - Try<Object> of = Try.of(ds.decorate()); - return of - .recover(throwable -> { - if (serverRecoverPolicy == null) { - throw (RuntimeException) throwable; - } else { - return serverRecoverPolicy.apply(throwable); - } - }).get(); - } - - public Object processClient(List<Policy> policies, CheckedFunction0<Object> supplier) { - DecorateCheckedSupplier<Object> ds = Decorators.ofCheckedSupplier(supplier); - for (Policy policy : policies) { - if (handlers.get(policy.handler()) == null || - handlers.get(policy.handler()).type() == HandlerType.SERVER) { - continue; - } - ds = handlers.get(policy.handler()).process(ds, policy); - } - Try<Object> of = Try.of(ds.decorate()); - return of - .recover(throwable -> { - if (clientRecoverPolicy == null) { - throw (RuntimeException) throwable; - } else { - return clientRecoverPolicy.apply(throwable); - } - }).get(); - } -} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovernanceHandler.java similarity index 80% rename from governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovHandler.java rename to governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovernanceHandler.java index a6f008e..1cb55d1 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovernanceHandler.java @@ -19,28 +19,33 @@ package org.apache.servicecomb.governance.handler; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import com.google.common.eventbus.Subscribe; import org.apache.servicecomb.governance.event.ConfigurationChangedEvent; import org.apache.servicecomb.governance.event.EventManager; -public abstract class AbstractGovHandler<PROCESSOR> implements GovHandler { +import com.google.common.eventbus.Subscribe; + +public abstract class AbstractGovernanceHandler<PROCESSOR, POLICY> { private Map<String, PROCESSOR> map = new ConcurrentHashMap<>(); - protected AbstractGovHandler() { + protected AbstractGovernanceHandler() { EventManager.register(this); } - protected <R> PROCESSOR getActuator(String key, R policy, Function<R, PROCESSOR> func) { + public <R> PROCESSOR getActuator(POLICY policy) { + String key = createKey(policy); PROCESSOR processor = map.get(key); if (processor == null) { - processor = func.apply(policy); + processor = createProcessor(policy); map.put(key, processor); } return processor; } + abstract protected String createKey(POLICY policy); + + abstract protected PROCESSOR createProcessor(POLICY policy); + @Subscribe public void onDynamicConfigurationListener(ConfigurationChangedEvent event) { event.getChangedConfigurations().forEach(v -> map.remove(v)); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/BulkheadHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/BulkheadHandler.java index df99d93..c5b2497 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/BulkheadHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/BulkheadHandler.java @@ -19,32 +19,28 @@ package org.apache.servicecomb.governance.handler; import java.time.Duration; +import org.apache.servicecomb.governance.policy.BulkheadPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import org.apache.servicecomb.governance.policy.BulkheadPolicy; -import org.apache.servicecomb.governance.policy.Policy; - import io.github.resilience4j.bulkhead.Bulkhead; import io.github.resilience4j.bulkhead.BulkheadConfig; import io.github.resilience4j.bulkhead.BulkheadRegistry; import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; @Component("BulkheadHandler") -public class BulkheadHandler extends AbstractGovHandler<Bulkhead> { +public class BulkheadHandler extends AbstractGovernanceHandler<Bulkhead, BulkheadPolicy> { private static final Logger LOGGER = LoggerFactory.getLogger(BulkheadHandler.class); @Override - public <RESULT> DecorateCheckedSupplier<RESULT> process(DecorateCheckedSupplier<RESULT> supplier, Policy policy) { - Bulkhead bulkhead = getActuator("servicecomb.bulkhead." + policy.name(), (BulkheadPolicy) policy, - this::getBulkhead); - return supplier.withBulkhead(bulkhead); + protected String createKey(BulkheadPolicy policy) { + return "servicecomb.bulkhead." + policy.name(); } @Override - public HandlerType type() { - return HandlerType.SERVER; + protected Bulkhead createProcessor(BulkheadPolicy policy) { + return getBulkhead(policy); } private Bulkhead getBulkhead(BulkheadPolicy policy) { diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java index 91dfdf9..d041edc 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java @@ -18,40 +18,29 @@ package org.apache.servicecomb.governance.handler; import java.time.Duration; +import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; -import org.apache.servicecomb.governance.policy.Policy; - import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; -import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; @Component("CircuitBreakerHandler") -public class CircuitBreakerHandler extends AbstractGovHandler<CircuitBreaker> { +public class CircuitBreakerHandler extends AbstractGovernanceHandler<CircuitBreaker, CircuitBreakerPolicy> { private static final Logger LOGGER = LoggerFactory.getLogger(CircuitBreakerHandler.class); @Override - public <RESULT> DecorateCheckedSupplier<RESULT> process(DecorateCheckedSupplier<RESULT> supplier, Policy policy) { - CircuitBreaker circuitBreaker = getActuator("servicecomb.circuitBreaker." + policy.name(), - (CircuitBreakerPolicy) policy, this::getCircuitBreaker); - return supplier.withCircuitBreaker(circuitBreaker); + protected String createKey(CircuitBreakerPolicy policy) { + return "servicecomb.circuitBreaker." + policy.name(); } @Override - public HandlerType type() { - return HandlerType.SERVER; + protected CircuitBreaker createProcessor(CircuitBreakerPolicy policy) { + return getCircuitBreaker(policy); } - /** - * todo: recordExceptions - * - * @param policy - * @return - */ private CircuitBreaker getCircuitBreaker(CircuitBreakerPolicy policy) { LOGGER.info("applying new policy: {}", policy.toString()); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/GovHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/GovHandler.java deleted file mode 100644 index 81a9611..0000000 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/GovHandler.java +++ /dev/null @@ -1,28 +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.governance.handler; - -import org.apache.servicecomb.governance.policy.Policy; - -import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; - -public interface GovHandler { - - <RESULT> DecorateCheckedSupplier<RESULT> process(DecorateCheckedSupplier<RESULT> supplier, Policy policy); - - HandlerType type(); -} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/RateLimitingHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/RateLimitingHandler.java index 82a271a..dcdb5d8 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/RateLimitingHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/RateLimitingHandler.java @@ -18,38 +18,29 @@ package org.apache.servicecomb.governance.handler; import java.time.Duration; +import org.apache.servicecomb.governance.policy.RateLimitingPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import org.apache.servicecomb.governance.policy.Policy; -import org.apache.servicecomb.governance.policy.RateLimitingPolicy; - -import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RateLimiterConfig; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; @Component("RateLimitingHandler") -public class RateLimitingHandler extends AbstractGovHandler<RateLimiter> { +public class RateLimitingHandler extends AbstractGovernanceHandler<RateLimiter, RateLimitingPolicy> { private static final Logger LOGGER = LoggerFactory.getLogger(RateLimitingHandler.class); @Override - public <RESULT> DecorateCheckedSupplier<RESULT> process(DecorateCheckedSupplier<RESULT> supplier, Policy policy) { - RateLimiter rateLimiter = getActuator("servicecomb.rateLimiting." + policy.name(), (RateLimitingPolicy) policy, - this::getRateLimiter); - return supplier.withRateLimiter(rateLimiter); + protected String createKey(RateLimitingPolicy policy) { + return "servicecomb.rateLimiting." + policy.name(); } @Override - public HandlerType type() { - return HandlerType.SERVER; + protected RateLimiter createProcessor(RateLimitingPolicy policy) { + return getRateLimiter(policy); } - /** - * @param policy - * @return - */ private RateLimiter getRateLimiter(RateLimitingPolicy policy) { LOGGER.info("applying new policy: {}", policy.toString()); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java index 86b902d..068bb55 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java @@ -22,42 +22,33 @@ import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; -import org.apache.servicecomb.governance.policy.Policy; +import org.apache.servicecomb.governance.handler.ext.RetryExtension; import org.apache.servicecomb.governance.policy.RetryPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.apache.servicecomb.governance.handler.ext.RetryExtension; - -import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryConfig; import io.github.resilience4j.retry.RetryRegistry; @Component("RetryHandler") -public class RetryHandler extends AbstractGovHandler<Retry> { +public class RetryHandler extends AbstractGovernanceHandler<Retry, RetryPolicy> { private static final Logger LOGGER = LoggerFactory.getLogger(RetryHandler.class); @Autowired private RetryExtension retryExtension; - /** - * @param supplier - * @param policy - * @return - */ @Override - public <RESULT> DecorateCheckedSupplier<RESULT> process(DecorateCheckedSupplier<RESULT> supplier, Policy policy) { - Retry retry = getActuator("servicecomb.retry." + policy.name(), (RetryPolicy) policy, this::getRetry); - return supplier.withRetry(retry); + protected String createKey(RetryPolicy policy) { + return "servicecomb.retry." + policy.name(); } @Override - public HandlerType type() { - return HandlerType.CLIENT; + protected Retry createProcessor(RetryPolicy policy) { + return getRetry(policy); } private Retry getRetry(RetryPolicy retryPolicy) { diff --git a/governance/src/test/java/org/apache/servicecomb/governance/FlowControlTest.java b/governance/src/test/java/org/apache/servicecomb/governance/FlowControlTest.java index 8aeddba..e16dafa 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/FlowControlTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/FlowControlTest.java @@ -23,7 +23,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.apache.servicecomb.governance.handler.RateLimitingHandler; import org.apache.servicecomb.governance.marker.GovHttpRequest; -import org.apache.servicecomb.governance.policy.Policy; +import org.apache.servicecomb.governance.policy.RateLimitingPolicy; import org.apache.servicecomb.governance.properties.RateLimitProperties; import org.junit.Assert; import org.junit.Test; @@ -35,6 +35,7 @@ import org.springframework.test.context.junit4.SpringRunner; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; +import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RequestNotPermitted; @RunWith(SpringRunner.class) @@ -57,11 +58,12 @@ public class FlowControlTest { GovHttpRequest request = new GovHttpRequest("testService", "1.0"); request.setUri("/hello"); - Policy policy = matchersManager.match(request, rateLimitProperties.getParsedEntity()); + RateLimitingPolicy policy = matchersManager.match(request, rateLimitProperties.getParsedEntity()); Assert.assertNotNull(policy); - DecorateCheckedSupplier<Object> dcs = rateLimitingHandler.process(ds, policy); + RateLimiter rateLimiter = rateLimitingHandler.getActuator(policy); + ds.withRateLimiter(rateLimiter); - Assert.assertEquals("test", dcs.get()); + Assert.assertEquals("test", ds.get()); // flow control CountDownLatch cd = new CountDownLatch(10); @@ -71,8 +73,7 @@ public class FlowControlTest { new Thread() { public void run() { try { - DecorateCheckedSupplier<Object> dcs = rateLimitingHandler.process(ds, policy); - Object result = dcs.get(); + Object result = ds.get(); if (!"test".equals(result)) { notExpected.set(true); } diff --git a/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java index 560bf05..971a09d 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java @@ -23,19 +23,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.EnumerablePropertySource; -import org.springframework.core.env.Environment; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - import org.apache.servicecomb.governance.event.ConfigurationChangedEvent; import org.apache.servicecomb.governance.event.EventManager; import org.apache.servicecomb.governance.marker.Matcher; @@ -51,6 +38,18 @@ import org.apache.servicecomb.governance.properties.GovernanceProperties; import org.apache.servicecomb.governance.properties.MatchProperties; import org.apache.servicecomb.governance.properties.RateLimitProperties; import org.apache.servicecomb.governance.properties.RetryProperties; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigFileApplicationContextInitializer.class) @@ -79,10 +78,6 @@ public class GovernancePropertiesTest { private Map<String, Object> dynamicValues = new HashMap<>(); - public GovernancePropertiesTest() { - System.out.print(1); - } - @Before public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; diff --git a/handlers/handler-governance/README.md b/handlers/handler-governance/README.md new file mode 100644 index 0000000..3a9db2f --- /dev/null +++ b/handlers/handler-governance/README.md @@ -0,0 +1,3 @@ +Enable governance features by handler. + +Governance can be enabled in any different process layer, enable it in handler is most portable way. \ No newline at end of file diff --git a/handlers/handler-governance/pom.xml b/handlers/handler-governance/pom.xml new file mode 100644 index 0000000..26aa4c6 --- /dev/null +++ b/handlers/handler-governance/pom.xml @@ -0,0 +1,65 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.servicecomb</groupId> + <artifactId>handlers</artifactId> + <version>2.1.5-SNAPSHOT</version> + </parent> + + <artifactId>handler-governance</artifactId> + <name>Java Chassis::Handlers::Governance</name> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.servicecomb</groupId> + <artifactId>java-chassis-core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.servicecomb</groupId> + <artifactId>servicecomb-governance</artifactId> + </dependency> + <dependency> + <groupId>org.apache.servicecomb</groupId> + <artifactId>registry-local</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.servicecomb</groupId> + <artifactId>foundation-test-scaffolding</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + +</project> diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderGovernanceHandler.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderGovernanceHandler.java new file mode 100644 index 0000000..06d0c50 --- /dev/null +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderGovernanceHandler.java @@ -0,0 +1,132 @@ +/* + * 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.handler.governance; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; + +import org.apache.servicecomb.core.Handler; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.governance.MatchersManager; +import org.apache.servicecomb.governance.handler.BulkheadHandler; +import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; +import org.apache.servicecomb.governance.handler.RateLimitingHandler; +import org.apache.servicecomb.governance.marker.GovHttpRequest; +import org.apache.servicecomb.governance.policy.BulkheadPolicy; +import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; +import org.apache.servicecomb.governance.policy.RateLimitingPolicy; +import org.apache.servicecomb.governance.properties.BulkheadProperties; +import org.apache.servicecomb.governance.properties.CircuitBreakerProperties; +import org.apache.servicecomb.governance.properties.RateLimitProperties; +import org.apache.servicecomb.registry.RegistrationManager; +import org.apache.servicecomb.swagger.invocation.AsyncResponse; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.resilience4j.bulkhead.BulkheadFullException; +import io.github.resilience4j.circuitbreaker.CallNotPermittedException; +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; +import io.github.resilience4j.ratelimiter.RequestNotPermitted; + +public class ProviderGovernanceHandler implements Handler { + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderGovernanceHandler.class); + + private MatchersManager matchersManager = BeanUtils.getBean(MatchersManager.class); + + private RateLimitingHandler rateLimitingHandler = BeanUtils.getBean(RateLimitingHandler.class); + + private RateLimitProperties rateLimitProperties = BeanUtils.getBean(RateLimitProperties.class); + + private CircuitBreakerHandler circuitBreakerHandler = BeanUtils.getBean(CircuitBreakerHandler.class); + + private CircuitBreakerProperties circuitBreakerProperties = BeanUtils.getBean(CircuitBreakerProperties.class); + + private BulkheadHandler bulkheadHandler = BeanUtils.getBean(BulkheadHandler.class); + + private BulkheadProperties bulkheadProperties = BeanUtils.getBean(BulkheadProperties.class); + + @Override + public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { + + Supplier<CompletionStage<Response>> next = createBusinessCompletionStageSupplier(invocation); + DecorateCompletionStage<Response> dcs = Decorators.ofCompletionStage(next); + GovHttpRequest request = createGovHttpRequest(invocation); + + RateLimitingPolicy rateLimitingPolicy = matchersManager.match(request, rateLimitProperties.getParsedEntity()); + dcs.withRateLimiter(rateLimitingHandler.getActuator(rateLimitingPolicy)); + CircuitBreakerPolicy circuitBreakerPolicy = matchersManager + .match(request, circuitBreakerProperties.getParsedEntity()); + dcs.withCircuitBreaker(circuitBreakerHandler.getActuator(circuitBreakerPolicy)); + BulkheadPolicy bulkheadPolicy = matchersManager.match(request, bulkheadProperties.getParsedEntity()); + dcs.withBulkhead(bulkheadHandler.getActuator(bulkheadPolicy)); + + dcs.get().whenComplete((r, e) -> { + if (e == null) { + asyncResp.complete(r); + return; + } + + if (e instanceof RequestNotPermitted) { + asyncResp.complete( + Response.failResp(new InvocationException(429, "rate limited.", new CommonExceptionData("rate limited.")))); + LOGGER.warn("the request is rate limit by policy : {}", e.getMessage()); + } else if (e instanceof CallNotPermittedException) { + asyncResp.complete( + Response.failResp(new InvocationException(429, "circuitBreaker is open.", + new CommonExceptionData("circuitBreaker is open.")))); + LOGGER.warn("circuitBreaker is open by policy : {}", e.getMessage()); + } else if (e instanceof BulkheadFullException) { + asyncResp.complete( + Response.failResp(new InvocationException(429, "bulkhead is full and does not permit further calls.", + new CommonExceptionData("bulkhead is full and does not permit further calls.")))); + LOGGER.warn("bulkhead is full and does not permit further calls by policy : {}", e.getMessage()); + } else { + asyncResp.complete(Response.createProducerFail(e)); + } + }); + } + + private Supplier<CompletionStage<Response>> createBusinessCompletionStageSupplier(Invocation invocation) { + return () -> { + CompletableFuture<Response> result = new CompletableFuture<>(); + try { + invocation.next(response -> { + result.complete(response); + }); + } catch (Exception e) { + result.completeExceptionally(e); + } + return result; + }; + } + + private GovHttpRequest createGovHttpRequest(Invocation invocation) { + GovHttpRequest request = new GovHttpRequest(RegistrationManager.INSTANCE.getMicroservice().getServiceName(), + RegistrationManager.INSTANCE.getMicroservice().getVersion()); + request.setUri(invocation.getSchemaId() + "." + invocation.getOperationName()); + request.setMethod(invocation.getOperationMeta().getHttpMethod()); + request.setHeaders(invocation.getContext()); + return request; + } +} diff --git a/handlers/handler-governance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter b/handlers/handler-governance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter new file mode 100644 index 0000000..6222972 --- /dev/null +++ b/handlers/handler-governance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter @@ -0,0 +1,17 @@ +# +# 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. +# + diff --git a/handlers/handler-governance/src/main/resources/config/cse.handler.xml b/handlers/handler-governance/src/main/resources/config/cse.handler.xml new file mode 100644 index 0000000..f99f63d --- /dev/null +++ b/handlers/handler-governance/src/main/resources/config/cse.handler.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<config> + <handler id="governance-provider" + class="org.apache.servicecomb.handler.governance.ProviderGovernanceHandler"/> +</config> diff --git a/handlers/pom.xml b/handlers/pom.xml index 2d5bb2f..c5cea16 100644 --- a/handlers/pom.xml +++ b/handlers/pom.xml @@ -39,6 +39,7 @@ <module>handler-fault-injection</module> <module>handler-publickey-auth</module> <module>handler-router</module> + <module>handler-governance</module> </modules> </project>
