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.
|
||||
*
|
||||
* 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 ProviderB {
|
||||
//
|
||||
// @Override
|
||||
// public String info() {
|
||||
// return "fallback: trigger the refuse for service b";
|
||||
// }
|
||||
//}
|
||||
|
Loading…
Reference in new issue