parent
0f35fb4811
commit
9ef157f7c1
@ -0,0 +1,7 @@
|
|||||||
|
package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
|
||||||
|
|
||||||
|
public interface PolarisCircuitBreakerFallback {
|
||||||
|
|
||||||
|
PolarisCircuitBreakerHttpResponse fallback();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.tencent.polaris.api.pojo.CircuitBreakerStatus;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.client.AbstractClientHttpResponse;
|
||||||
|
|
||||||
|
public class PolarisCircuitBreakerHttpResponse extends AbstractClientHttpResponse {
|
||||||
|
|
||||||
|
private final CircuitBreakerStatus.FallbackInfo fallbackInfo;
|
||||||
|
|
||||||
|
public PolarisCircuitBreakerHttpResponse(int code){
|
||||||
|
this(new CircuitBreakerStatus.FallbackInfo(code, null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolarisCircuitBreakerHttpResponse(int code, String body){
|
||||||
|
this(new CircuitBreakerStatus.FallbackInfo(code, null, body));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolarisCircuitBreakerHttpResponse(int code, Map<String, String> headers, String body){
|
||||||
|
this(new CircuitBreakerStatus.FallbackInfo(code, headers, body));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolarisCircuitBreakerHttpResponse(CircuitBreakerStatus.FallbackInfo fallbackInfo){
|
||||||
|
this.fallbackInfo = fallbackInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() throws IOException {
|
||||||
|
return fallbackInfo.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStatusText() throws IOException {
|
||||||
|
HttpStatus status = HttpStatus.resolve(getRawStatusCode());
|
||||||
|
return (status != null ? status.getReasonPhrase() : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getBody() throws IOException {
|
||||||
|
if (fallbackInfo.getBody() != null) {
|
||||||
|
return new ByteArrayInputStream(fallbackInfo.getBody().getBytes());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
if (fallbackInfo.getHeaders() != null) {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
fallbackInfo.getHeaders().forEach(headers::add);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target({ ElementType.METHOD })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface PolarisCircuitBreakerRestTemplate {
|
||||||
|
|
||||||
|
String fallback() default "";
|
||||||
|
|
||||||
|
Class<PolarisCircuitBreakerFallback> fallbackClass() default PolarisCircuitBreakerFallback.class;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
import org.springframework.core.type.StandardMethodMetadata;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
public class PolarisCircuitBreakerRestTemplateBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
|
||||||
|
|
||||||
|
private final ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
private final CircuitBreakerFactory circuitBreakerFactory;
|
||||||
|
|
||||||
|
public PolarisCircuitBreakerRestTemplateBeanPostProcessor(ApplicationContext applicationContext, CircuitBreakerFactory circuitBreakerFactory) {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
this.circuitBreakerFactory = circuitBreakerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConcurrentHashMap<String, PolarisCircuitBreakerRestTemplate> cache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private void checkPolarisCircuitBreakerRestTemplate(PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate,
|
||||||
|
String beanName) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||||
|
if (checkAnnotated(beanDefinition, beanType, beanName)) {
|
||||||
|
PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate;
|
||||||
|
if (beanDefinition.getSource() instanceof StandardMethodMetadata) {
|
||||||
|
polarisCircuitBreakerRestTemplate = ((StandardMethodMetadata) beanDefinition.getSource()).getIntrospectedMethod()
|
||||||
|
.getAnnotation(PolarisCircuitBreakerRestTemplate.class);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
polarisCircuitBreakerRestTemplate = beanDefinition.getResolvedFactoryMethod()
|
||||||
|
.getAnnotation(PolarisCircuitBreakerRestTemplate.class);
|
||||||
|
}
|
||||||
|
checkPolarisCircuitBreakerRestTemplate(polarisCircuitBreakerRestTemplate, beanName);
|
||||||
|
cache.put(beanName, polarisCircuitBreakerRestTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
if (cache.containsKey(beanName)) {
|
||||||
|
// add interceptor for each RestTemplate with @PolarisCircuitBreakerRestTemplate annotation
|
||||||
|
StringBuilder interceptorBeanNamePrefix = new StringBuilder();
|
||||||
|
PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate = cache.get(beanName);
|
||||||
|
interceptorBeanNamePrefix
|
||||||
|
.append(StringUtils.uncapitalize(
|
||||||
|
PolarisCircuitBreakerRestTemplate.class.getSimpleName()))
|
||||||
|
.append("_")
|
||||||
|
.append(polarisCircuitBreakerRestTemplate.fallbackClass().getSimpleName());
|
||||||
|
RestTemplate restTemplate = (RestTemplate) bean;
|
||||||
|
String interceptorBeanName = interceptorBeanNamePrefix + "@" + bean;
|
||||||
|
registerBean(interceptorBeanName, polarisCircuitBreakerRestTemplate, applicationContext, circuitBreakerFactory);
|
||||||
|
PolarisCircuitBreakerRestTemplateInterceptor polarisCircuitBreakerRestTemplateInterceptor = applicationContext
|
||||||
|
.getBean(interceptorBeanName, PolarisCircuitBreakerRestTemplateInterceptor.class);
|
||||||
|
restTemplate.getInterceptors().add(0, polarisCircuitBreakerRestTemplateInterceptor);
|
||||||
|
}
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkAnnotated(RootBeanDefinition beanDefinition,
|
||||||
|
Class<?> beanType, String beanName) {
|
||||||
|
return beanName != null && beanType == RestTemplate.class
|
||||||
|
&& beanDefinition.getSource() instanceof MethodMetadata
|
||||||
|
&& ((MethodMetadata) beanDefinition.getSource())
|
||||||
|
.isAnnotated(PolarisCircuitBreakerRestTemplate.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerBean(String interceptorBeanName,
|
||||||
|
PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate, ApplicationContext applicationContext, CircuitBreakerFactory circuitBreakerFactory) {
|
||||||
|
// register PolarisCircuitBreakerRestTemplateInterceptor bean
|
||||||
|
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
|
||||||
|
.getAutowireCapableBeanFactory();
|
||||||
|
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
|
||||||
|
.genericBeanDefinition(PolarisCircuitBreakerRestTemplateInterceptor.class);
|
||||||
|
beanDefinitionBuilder.addConstructorArgValue(polarisCircuitBreakerRestTemplate);
|
||||||
|
beanDefinitionBuilder.addConstructorArgValue(applicationContext);
|
||||||
|
beanDefinitionBuilder.addConstructorArgValue(circuitBreakerFactory);
|
||||||
|
BeanDefinition interceptorBeanDefinition = beanDefinitionBuilder
|
||||||
|
.getRawBeanDefinition();
|
||||||
|
beanFactory.registerBeanDefinition(interceptorBeanName,
|
||||||
|
interceptorBeanDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import com.tencent.polaris.api.pojo.CircuitBreakerStatus;
|
||||||
|
import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.http.HttpRequest;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpRequestInterceptor {
|
||||||
|
|
||||||
|
private final PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate;
|
||||||
|
|
||||||
|
private final ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
private final CircuitBreakerFactory circuitBreakerFactory;
|
||||||
|
|
||||||
|
public PolarisCircuitBreakerRestTemplateInterceptor(
|
||||||
|
PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate,
|
||||||
|
ApplicationContext applicationContext,
|
||||||
|
CircuitBreakerFactory circuitBreakerFactory
|
||||||
|
) {
|
||||||
|
this.polarisCircuitBreakerRestTemplate = polarisCircuitBreakerRestTemplate;
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
this.circuitBreakerFactory = circuitBreakerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
||||||
|
return circuitBreakerFactory.create(request.getURI().getHost() + "#" + request.getURI().getPath()).run(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
return execution.execute(request, body);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
t -> {
|
||||||
|
if (!StringUtils.isEmpty(polarisCircuitBreakerRestTemplate.fallback())) {
|
||||||
|
CircuitBreakerStatus.FallbackInfo fallbackInfo = new CircuitBreakerStatus.FallbackInfo(200, null, polarisCircuitBreakerRestTemplate.fallback());
|
||||||
|
return new PolarisCircuitBreakerHttpResponse(fallbackInfo);
|
||||||
|
}
|
||||||
|
if (polarisCircuitBreakerRestTemplate.fallbackClass() != null && polarisCircuitBreakerRestTemplate.fallbackClass().getSuperclass() != null) {
|
||||||
|
Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerFallback.class, "fallback");
|
||||||
|
PolarisCircuitBreakerFallback polarisCircuitBreakerFallback = applicationContext.getBean(polarisCircuitBreakerRestTemplate.fallbackClass());
|
||||||
|
return (PolarisCircuitBreakerHttpResponse) ReflectionUtils.invokeMethod(method, polarisCircuitBreakerFallback);
|
||||||
|
}
|
||||||
|
if (t instanceof CallAbortedException) {
|
||||||
|
CircuitBreakerStatus.FallbackInfo fallbackInfo = ((CallAbortedException) t).getFallbackInfo();
|
||||||
|
if (fallbackInfo != null) {
|
||||||
|
return new PolarisCircuitBreakerHttpResponse(fallbackInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(t);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,34 +1,34 @@
|
|||||||
/*
|
///*
|
||||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
|
// * 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.
|
// * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
*
|
// *
|
||||||
* Licensed under the BSD 3-Clause License (the "License");
|
// * Licensed under the BSD 3-Clause License (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
// * you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
// * You may obtain a copy of the License at
|
||||||
*
|
// *
|
||||||
* https://opensource.org/licenses/BSD-3-Clause
|
// * https://opensource.org/licenses/BSD-3-Clause
|
||||||
*
|
// *
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed
|
// * 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
|
// * 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
|
// * CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations under the License.
|
// * specific language governing permissions and limitations under the License.
|
||||||
*/
|
// */
|
||||||
|
//
|
||||||
package com.tencent.cloud.polaris.circuitbreaker.feign.example;
|
//package com.tencent.cloud.polaris.circuitbreaker.feign.example;
|
||||||
|
//
|
||||||
import org.springframework.stereotype.Component;
|
//import org.springframework.stereotype.Component;
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* Circuit breaker example callee fallback.
|
// * Circuit breaker example callee fallback.
|
||||||
*
|
// *
|
||||||
* @author sean yu
|
// * @author sean yu
|
||||||
*/
|
// */
|
||||||
@Component
|
//@Component
|
||||||
public class ProviderBFallback implements ProviderB {
|
//public class ProviderBFallback implements ProviderB {
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public String info() {
|
// public String info() {
|
||||||
return "fallback: trigger the refuse for service b";
|
// return "fallback: trigger the refuse for service b";
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
Loading…
Reference in new issue