diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerFeignClientAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerFeignClientAutoConfiguration.java index 91096e1e6..05c20d052 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerFeignClientAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerFeignClientAutoConfiguration.java @@ -18,12 +18,15 @@ package com.tencent.cloud.polaris.circuitbreaker.config; import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisCircuitBreakerNameResolver; +import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignCircuitBreakerTargeter; import feign.Feign; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.cloud.openfeign.CircuitBreakerNameResolver; import org.springframework.cloud.openfeign.FeignClientFactoryBean; +import org.springframework.cloud.openfeign.Targeter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -43,4 +46,9 @@ public class PolarisCircuitBreakerFeignClientAutoConfiguration { return new PolarisCircuitBreakerNameResolver(); } + @Bean + public Targeter polarisFeignCircuitBreakerTargeter(CircuitBreakerFactory circuitBreakerFactory, CircuitBreakerNameResolver circuitBreakerNameResolver) { + return new PolarisFeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerNameResolver); + } + } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerRestTemplateAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerRestTemplateAutoConfiguration.java index a9efc3d6e..5c030bb89 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerRestTemplateAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerRestTemplateAutoConfiguration.java @@ -4,18 +4,17 @@ import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreak import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) @ConditionalOnPolarisCircuitBreakerEnabled +@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") @AutoConfigureAfter(PolarisCircuitBreakerAutoConfiguration.class) public class PolarisCircuitBreakerRestTemplateAutoConfiguration { @Bean - @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") public static PolarisCircuitBreakerRestTemplateBeanPostProcessor polarisCircuitBreakerRestTemplateBeanPostProcessor( ApplicationContext applicationContext) { return new PolarisCircuitBreakerRestTemplateBeanPostProcessor(applicationContext); diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFallbackFactory.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFallbackFactory.java new file mode 100644 index 000000000..9eb3e0c67 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFallbackFactory.java @@ -0,0 +1,80 @@ +package com.tencent.cloud.polaris.circuitbreaker.feign; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; +import feign.Request; +import feign.RequestTemplate; +import feign.Response; +import feign.codec.Decoder; + +import org.springframework.cloud.openfeign.FallbackFactory; + +public class PolarisCircuitBreakerFallbackFactory implements FallbackFactory { + + private final Decoder decoder; + + public PolarisCircuitBreakerFallbackFactory(Decoder decoder) { + this.decoder = decoder; + } + + @Override + public Object create(Throwable t) { + return new DefaultFallback(t, decoder); + } + + public class DefaultFallback { + + private final Throwable t; + + private final Decoder decoder; + + public DefaultFallback(Throwable t, Decoder decoder) { + this.t = t; + this.decoder = decoder; + } + + public Object fallback(Method method) { + if (t instanceof CallAbortedException) { + CircuitBreakerStatus.FallbackInfo fallbackInfo = ((CallAbortedException) t).getFallbackInfo(); + if (fallbackInfo != null) { + Response.Builder responseBuilder = Response.builder() + .status(fallbackInfo.getCode()); + if (fallbackInfo.getHeaders() != null) { + Map> headers = new HashMap<>(); + fallbackInfo.getHeaders().forEach((k, v) -> { + if (headers.containsKey(k)) { + headers.get(k).add(v); + } + else { + headers.put(k, new HashSet<>(Collections.singleton(v))); + } + }); + responseBuilder.headers(headers); + } + if (fallbackInfo.getBody() != null) { + responseBuilder.body(fallbackInfo.getBody(), StandardCharsets.UTF_8); + } + // Feign Response need a nonnull Request, which is not important in fallback response, so we create a fake one + Request request = Request.create(Request.HttpMethod.GET, "/", new HashMap<>(), Request.Body.empty(), new RequestTemplate()); + responseBuilder.request(request); + try (Response response = responseBuilder.build()) { + return decoder.decode(response, method.getGenericReturnType()); + } + catch (IOException e) { + throw new IllegalStateException(e); + } + } + } + throw new IllegalStateException(t); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreaker.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreaker.java new file mode 100644 index 000000000..36f438234 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreaker.java @@ -0,0 +1,75 @@ +package com.tencent.cloud.polaris.circuitbreaker.feign; + +import feign.Feign; +import feign.Target; + +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.cloud.openfeign.CircuitBreakerNameResolver; +import org.springframework.cloud.openfeign.FallbackFactory; + +public class PolarisFeignCircuitBreaker { + + private PolarisFeignCircuitBreaker() { + throw new IllegalStateException("Don't instantiate a utility class"); + } + + /** + * @return builder for Feign CircuitBreaker integration + */ + public static PolarisFeignCircuitBreaker.Builder builder(Feign.Builder delegateBuilder) { + return new PolarisFeignCircuitBreaker.Builder(delegateBuilder); + } + + /** + * Builder for Feign CircuitBreaker integration. + */ + public static final class Builder extends Feign.Builder { + + private final Feign.Builder delegateBuilder; + + public Builder(Feign.Builder delegateBuilder) { + this.delegateBuilder = delegateBuilder; + } + + private CircuitBreakerFactory circuitBreakerFactory; + + private String feignClientName; + + private CircuitBreakerNameResolver circuitBreakerNameResolver; + + public PolarisFeignCircuitBreaker.Builder circuitBreakerFactory(CircuitBreakerFactory circuitBreakerFactory) { + this.circuitBreakerFactory = circuitBreakerFactory; + return this; + } + + public PolarisFeignCircuitBreaker.Builder feignClientName(String feignClientName) { + this.feignClientName = feignClientName; + return this; + } + + public PolarisFeignCircuitBreaker.Builder circuitBreakerNameResolver(CircuitBreakerNameResolver circuitBreakerNameResolver) { + this.circuitBreakerNameResolver = circuitBreakerNameResolver; + return this; + } + + public T target(Target target, T fallback) { + return build(fallback != null ? new FallbackFactory.Default(fallback) : null).newInstance(target); + } + + public T target(Target target, FallbackFactory fallbackFactory) { + return build(fallbackFactory).newInstance(target); + } + + @Override + public T target(Target target) { + return build(null).newInstance(target); + } + + public Feign build(final FallbackFactory nullableFallbackFactory) { + delegateBuilder.invocationHandlerFactory((target, dispatch) -> new PolarisFeignCircuitBreakerInvocationHandler( + circuitBreakerFactory, feignClientName, target, dispatch, nullableFallbackFactory, circuitBreakerNameResolver, this.decoder)); + return delegateBuilder.build(); + } + + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerInvocationHandler.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerInvocationHandler.java new file mode 100644 index 000000000..0541693b2 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerInvocationHandler.java @@ -0,0 +1,178 @@ +package com.tencent.cloud.polaris.circuitbreaker.feign; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +import feign.InvocationHandlerFactory; +import feign.Target; +import feign.codec.Decoder; + +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException; +import org.springframework.cloud.openfeign.CircuitBreakerNameResolver; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +import static feign.Util.checkNotNull; + +public class PolarisFeignCircuitBreakerInvocationHandler implements InvocationHandler { + + private final CircuitBreakerFactory factory; + + private final String feignClientName; + + private final Target target; + + private final Map dispatch; + + private final FallbackFactory nullableFallbackFactory; + + private final Map fallbackMethodMap; + + private final CircuitBreakerNameResolver circuitBreakerNameResolver; + + private final Decoder decoder; + + public PolarisFeignCircuitBreakerInvocationHandler(CircuitBreakerFactory factory, String feignClientName, Target target, + Map dispatch, FallbackFactory nullableFallbackFactory, + CircuitBreakerNameResolver circuitBreakerNameResolver, Decoder decoder) { + this.factory = factory; + this.feignClientName = feignClientName; + this.target = checkNotNull(target, "target"); + this.dispatch = checkNotNull(dispatch, "dispatch"); + this.fallbackMethodMap = toFallbackMethod(dispatch); + this.nullableFallbackFactory = nullableFallbackFactory; + this.circuitBreakerNameResolver = circuitBreakerNameResolver; + this.decoder = decoder; + } + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + // early exit if the invoked method is from java.lang.Object + // code is the same as ReflectiveFeign.FeignInvocationHandler + if ("equals".equals(method.getName())) { + try { + Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null; + return equals(otherHandler); + } + catch (IllegalArgumentException e) { + return false; + } + } + else if ("hashCode".equals(method.getName())) { + return hashCode(); + } + else if ("toString".equals(method.getName())) { + return toString(); + } + + String circuitName = circuitBreakerNameResolver.resolveCircuitBreakerName(feignClientName, target, method); + CircuitBreaker circuitBreaker = factory.create(circuitName); + Supplier supplier = asSupplier(method, args); + Function fallbackFunction; + if (this.nullableFallbackFactory != null) { + fallbackFunction = throwable -> { + Object fallback = this.nullableFallbackFactory.create(throwable); + try { + return this.fallbackMethodMap.get(method).invoke(fallback, args); + } + catch (Exception exception) { + unwrapAndRethrow(exception); + } + return null; + }; + } + else { + fallbackFunction = throwable -> { + PolarisCircuitBreakerFallbackFactory.DefaultFallback fallback = + (PolarisCircuitBreakerFallbackFactory.DefaultFallback) new PolarisCircuitBreakerFallbackFactory(this.decoder).create(throwable); + return fallback.fallback(method); + }; + } + return circuitBreaker.run(supplier, fallbackFunction); + } + + private void unwrapAndRethrow(Exception exception) { + if (exception instanceof InvocationTargetException || exception instanceof NoFallbackAvailableException) { + Throwable underlyingException = exception.getCause(); + if (underlyingException instanceof RuntimeException) { + throw (RuntimeException) underlyingException; + } + if (underlyingException != null) { + throw new IllegalStateException(underlyingException); + } + throw new IllegalStateException(exception); + } + } + + private Supplier asSupplier(final Method method, final Object[] args) { + final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + final Thread caller = Thread.currentThread(); + return () -> { + boolean isAsync = caller != Thread.currentThread(); + try { + if (isAsync) { + RequestContextHolder.setRequestAttributes(requestAttributes); + } + return dispatch.get(method).invoke(args); + } + catch (RuntimeException throwable) { + throw throwable; + } + catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + finally { + if (isAsync) { + RequestContextHolder.resetRequestAttributes(); + } + } + }; + } + + /** + * If the method param of {@link InvocationHandler#invoke(Object, Method, Object[])} + * is not accessible, i.e in a package-private interface, the fallback call will cause + * of access restrictions. But methods in dispatch are copied methods. So setting + * access to dispatch method doesn't take effect to the method in + * InvocationHandler.invoke. Use map to store a copy of method to invoke the fallback + * to bypass this and reducing the count of reflection calls. + * @return cached methods map for fallback invoking + */ + static Map toFallbackMethod(Map dispatch) { + Map result = new LinkedHashMap<>(); + for (Method method : dispatch.keySet()) { + method.setAccessible(true); + result.put(method, method); + } + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PolarisFeignCircuitBreakerInvocationHandler) { + PolarisFeignCircuitBreakerInvocationHandler other = (PolarisFeignCircuitBreakerInvocationHandler) obj; + return this.target.equals(other.target); + } + return false; + } + + @Override + public int hashCode() { + return this.target.hashCode(); + } + + @Override + public String toString() { + return this.target.toString(); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerTargeter.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerTargeter.java new file mode 100644 index 000000000..238f37bfb --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerTargeter.java @@ -0,0 +1,82 @@ +package com.tencent.cloud.polaris.circuitbreaker.feign; + +import feign.Feign; +import feign.Target; + +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.cloud.openfeign.CircuitBreakerNameResolver; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.cloud.openfeign.FeignCircuitBreaker; +import org.springframework.cloud.openfeign.FeignClientFactoryBean; +import org.springframework.cloud.openfeign.FeignContext; +import org.springframework.cloud.openfeign.Targeter; +import org.springframework.util.StringUtils; + +public class PolarisFeignCircuitBreakerTargeter implements Targeter { + + private final CircuitBreakerFactory circuitBreakerFactory; + + private final CircuitBreakerNameResolver circuitBreakerNameResolver; + + public PolarisFeignCircuitBreakerTargeter(CircuitBreakerFactory circuitBreakerFactory, CircuitBreakerNameResolver circuitBreakerNameResolver) { + this.circuitBreakerFactory = circuitBreakerFactory; + this.circuitBreakerNameResolver = circuitBreakerNameResolver; + } + + @Override + public T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, + Target.HardCodedTarget target) { + if (!(feign instanceof FeignCircuitBreaker.Builder)) { + return feign.target(target); + } + PolarisFeignCircuitBreaker.Builder builder = PolarisFeignCircuitBreaker.builder(feign); + String name = !StringUtils.hasText(factory.getContextId()) ? factory.getName() : factory.getContextId(); + Class fallback = factory.getFallback(); + if (fallback != void.class) { + return targetWithFallback(name, context, target, builder, fallback); + } + Class fallbackFactory = factory.getFallbackFactory(); + if (fallbackFactory != void.class) { + return targetWithFallbackFactory(name, context, target, builder, fallbackFactory); + } + return builder(name, builder).target(target); + } + + private T targetWithFallbackFactory(String feignClientName, FeignContext context, + Target.HardCodedTarget target, PolarisFeignCircuitBreaker.Builder builder, Class fallbackFactoryClass) { + FallbackFactory fallbackFactory = (FallbackFactory) getFromContext("fallbackFactory", + feignClientName, context, fallbackFactoryClass, FallbackFactory.class); + return builder(feignClientName, builder).target(target, fallbackFactory); + } + + private T targetWithFallback(String feignClientName, FeignContext context, Target.HardCodedTarget target, + PolarisFeignCircuitBreaker.Builder builder, Class fallback) { + T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type()); + return builder(feignClientName, builder).target(target, fallbackInstance); + } + + private T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context, + Class beanType, Class targetType) { + Object fallbackInstance = context.getInstance(feignClientName, beanType); + if (fallbackInstance == null) { + throw new IllegalStateException( + String.format("No " + fallbackMechanism + " instance of type %s found for feign client %s", + beanType, feignClientName)); + } + + if (!targetType.isAssignableFrom(beanType)) { + throw new IllegalStateException(String.format("Incompatible " + fallbackMechanism + + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s", + beanType, targetType, feignClientName)); + } + return (T) fallbackInstance; + } + + private PolarisFeignCircuitBreaker.Builder builder(String feignClientName, PolarisFeignCircuitBreaker.Builder builder) { + return builder + .circuitBreakerFactory(circuitBreakerFactory) + .feignClientName(feignClientName) + .circuitBreakerNameResolver(circuitBreakerNameResolver); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderB.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderB.java index c26879242..36730edce 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderB.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderB.java @@ -26,8 +26,7 @@ import org.springframework.web.bind.annotation.GetMapping; * * @author sean yu */ -@Primary -@FeignClient(name = "polaris-circuitbreaker-callee-service") +@FeignClient(name = "polaris-circuitbreaker-callee-service", contextId = "use-polaris-fallback") public interface ProviderB { /** diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBFallback.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBFallback.java index 22d46f6cb..419b18cfc 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBFallback.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBFallback.java @@ -1,34 +1,34 @@ -///* -// * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. -// * -// * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. -// * -// * Licensed under the BSD 3-Clause License (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * https://opensource.org/licenses/BSD-3-Clause -// * -// * 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 com.tencent.cloud.polaris.circuitbreaker.feign.example; -// -//import org.springframework.stereotype.Component; -// -///** -// * Circuit breaker example callee fallback. -// * -// * @author sean yu -// */ -//@Component -//public class ProviderBFallback implements ProviderB { -// -// @Override -// public String info() { -// return "fallback: trigger the refuse for service b"; -// } -//} +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.cloud.polaris.circuitbreaker.feign.example; + +import org.springframework.stereotype.Component; + +/** + * Circuit breaker example callee fallback. + * + * @author sean yu + */ +@Component +public class ProviderBFallback implements ProviderBWithFallback { + + @Override + public String info() { + return "fallback: trigger the refuse for service b"; + } +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBWithFallback.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBWithFallback.java new file mode 100644 index 000000000..de83f3226 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBWithFallback.java @@ -0,0 +1,17 @@ +package com.tencent.cloud.polaris.circuitbreaker.feign.example; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; + +@FeignClient(name = "polaris-circuitbreaker-callee-service", contextId = "use-code-fallback", fallback = ProviderBFallback.class) +public interface ProviderBWithFallback { + + /** + * Get info of service B. + * + * @return info of service B + */ + @GetMapping("/example/service/b/info") + String info(); + +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAController.java index 5c7108716..45e49d26a 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAController.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAController.java @@ -35,12 +35,24 @@ public class ServiceAController { @Autowired private ProviderB polarisServiceB; + @Autowired + private ProviderBWithFallback providerBWithFallback; + + /** + * Get info of Service B by Feign. + * @return info of Service B + */ + @GetMapping("/getBServiceInfo/fallbackFromLocalCode") + public String getBServiceInfoFallbackFromLocalCode() { + return providerBWithFallback.info(); + } + /** * Get info of Service B by Feign. * @return info of Service B */ - @GetMapping("/getBServiceInfo") - public String getBServiceInfo() { + @GetMapping("/getBServiceInfo/fallbackFromPolaris") + public String getBServiceInfoFallbackFromPolaris() { return polarisServiceB.info(); } diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/CustomPolarisCircuitBreakerFallback.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/CustomPolarisCircuitBreakerFallback.java index a845cf3e4..e12035190 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/CustomPolarisCircuitBreakerFallback.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/CustomPolarisCircuitBreakerFallback.java @@ -1,6 +1,8 @@ package com.tencent.cloud.polaris.circuitbreaker.resttemplate.example; +import java.util.HashMap; + import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerFallback; import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse; @@ -12,6 +14,9 @@ public class CustomPolarisCircuitBreakerFallback implements PolarisCircuitBreake public PolarisCircuitBreakerHttpResponse fallback() { return new PolarisCircuitBreakerHttpResponse( 200, + new HashMap(){{ + put("Content-Type", "application/json"); + }}, "{\"msg\": \"this is a fallback class\"}"); } } diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAController.java index 39fcc5d35..63dd3dcb4 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAController.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAController.java @@ -62,13 +62,13 @@ public class ServiceAController { } @GetMapping("/getBServiceInfo/fallback") - public String getBServiceInfoFallback() { - return fallbackRestTemplate.getForObject("/example/service/b/info", String.class); + public ResponseEntity getBServiceInfoFallback() { + return fallbackRestTemplate.getForEntity("/example/service/b/info", String.class); } @GetMapping("/getBServiceInfo/fallbackClass") - public String getBServiceInfoFallbackClass() { - return fallbackClassRestTemplate.getForObject("/example/service/b/info", String.class); + public ResponseEntity getBServiceInfoFallbackClass() { + return fallbackClassRestTemplate.getForEntity("/example/service/b/info", String.class); } }