Commit 804ddb6c by Nepxion

服务端使用Hystrix做线程模式的服务隔离时,实现服务灰度路由的功能

parent 56373d73
......@@ -27,7 +27,7 @@ public class DefaultDiscoveryEnabledAdapter extends AbstractDiscoveryEnabledAdap
@Override
protected String getVersionValue(Server server) {
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRequestAttributes();
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRestAttributes();
if (attributes == null) {
String serviceId = server.getMetaInfo().getAppName().toLowerCase();
......@@ -41,7 +41,7 @@ public class DefaultDiscoveryEnabledAdapter extends AbstractDiscoveryEnabledAdap
@Override
protected String getRegionValue(Server server) {
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRequestAttributes();
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRestAttributes();
if (attributes == null) {
String serviceId = server.getMetaInfo().getAppName().toLowerCase();
......
......@@ -41,7 +41,7 @@ public class FeignStrategyInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRequestAttributes();
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRestAttributes();
if (attributes == null) {
return;
}
......
......@@ -44,7 +44,7 @@ public class RestTemplateStrategyInterceptor implements ClientHttpRequestInterce
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRequestAttributes();
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRestAttributes();
if (attributes == null) {
return execution.execute(request, body);
}
......
......@@ -17,7 +17,7 @@ import com.nepxion.matrix.proxy.aop.DefaultAutoScanProxy;
import com.nepxion.matrix.proxy.mode.ProxyMode;
import com.nepxion.matrix.proxy.mode.ScanMode;
public class ServiceStrategyAutoScanProxy extends DefaultAutoScanProxy {
public class RpcStrategyAutoScanProxy extends DefaultAutoScanProxy {
private static final long serialVersionUID = 8436914718400274011L;
private String[] commonInterceptorNames;
......@@ -25,14 +25,14 @@ public class ServiceStrategyAutoScanProxy extends DefaultAutoScanProxy {
@SuppressWarnings("rawtypes")
private Class[] methodAnnotations;
public ServiceStrategyAutoScanProxy(String scanPackages) {
public RpcStrategyAutoScanProxy(String scanPackages) {
super(scanPackages, ProxyMode.BY_CLASS_ANNOTATION_ONLY, ScanMode.FOR_CLASS_ANNOTATION_ONLY);
}
@Override
protected String[] getCommonInterceptorNames() {
if (commonInterceptorNames == null) {
commonInterceptorNames = new String[] { "serviceStrategyInterceptor" };
commonInterceptorNames = new String[] { "rpcStrategyInterceptor" };
}
return commonInterceptorNames;
......
......@@ -18,11 +18,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.nepxion.discovery.plugin.strategy.service.constant.ServiceStrategyConstant;
import com.nepxion.discovery.plugin.strategy.service.context.ServiceStrategyContext;
import com.nepxion.discovery.plugin.strategy.service.context.RpcStrategyContext;
import com.nepxion.matrix.proxy.aop.AbstractInterceptor;
public class ServiceStrategyInterceptor extends AbstractInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(ServiceStrategyInterceptor.class);
public class RpcStrategyInterceptor extends AbstractInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(RpcStrategyInterceptor.class);
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
......@@ -46,19 +46,19 @@ public class ServiceStrategyInterceptor extends AbstractInterceptor {
}
}
ServiceStrategyContext context = ServiceStrategyContext.getCurrentContext();
RpcStrategyContext context = RpcStrategyContext.getCurrentContext();
context.add(ServiceStrategyConstant.CLASS, proxiedClass);
context.add(ServiceStrategyConstant.METHOD, methodName);
context.add(ServiceStrategyConstant.PARAMETER_MAP, parameterMap);
LOG.debug("Service strategy context is set with {}", context);
LOG.debug("Rpc strategy context is set with {}", context);
try {
return invocation.proceed();
} finally {
ServiceStrategyContext.clearCurrentContext();
RpcStrategyContext.clearCurrentContext();
LOG.debug("Service strategy context is cleared");
LOG.debug("Rpc strategy context is cleared");
}
}
}
\ No newline at end of file
......@@ -25,10 +25,12 @@ import com.nepxion.discovery.plugin.strategy.constant.StrategyConstant;
import com.nepxion.discovery.plugin.strategy.service.adapter.DefaultDiscoveryEnabledAdapter;
import com.nepxion.discovery.plugin.strategy.service.aop.FeignStrategyInterceptor;
import com.nepxion.discovery.plugin.strategy.service.aop.RestTemplateStrategyInterceptor;
import com.nepxion.discovery.plugin.strategy.service.aop.ServiceStrategyAutoScanProxy;
import com.nepxion.discovery.plugin.strategy.service.aop.ServiceStrategyInterceptor;
import com.nepxion.discovery.plugin.strategy.service.aop.RpcStrategyAutoScanProxy;
import com.nepxion.discovery.plugin.strategy.service.aop.RpcStrategyInterceptor;
import com.nepxion.discovery.plugin.strategy.service.constant.ServiceStrategyConstant;
import com.nepxion.discovery.plugin.strategy.service.context.ServiceStrategyContextHolder;
import com.nepxion.discovery.plugin.strategy.service.wrapper.DefaultCallableWrapper;
import com.nepxion.discovery.plugin.strategy.wrapper.CallableWrapper;
@Configuration
@AutoConfigureBefore(RibbonClientConfiguration.class)
......@@ -39,7 +41,7 @@ public class ServiceStrategyAutoConfiguration {
@Bean
@ConditionalOnProperty(value = ServiceStrategyConstant.SPRING_APPLICATION_STRATEGY_SCAN_PACKAGES, matchIfMissing = false)
public ServiceStrategyAutoScanProxy serviceStrategyAutoScanProxy() {
public RpcStrategyAutoScanProxy rpcStrategyAutoScanProxy() {
String scanPackages = environment.getProperty(ServiceStrategyConstant.SPRING_APPLICATION_STRATEGY_SCAN_PACKAGES);
if (StringUtils.isEmpty(scanPackages)) {
throw new DiscoveryException(ServiceStrategyConstant.SPRING_APPLICATION_STRATEGY_SCAN_PACKAGES + "'s value can't be empty, remove it if useless");
......@@ -49,12 +51,12 @@ public class ServiceStrategyAutoConfiguration {
throw new DiscoveryException("It can't scan packages for '" + ServiceStrategyConstant.EXCLUSION_SCAN_PACKAGES + "', please check '" + ServiceStrategyConstant.SPRING_APPLICATION_STRATEGY_SCAN_PACKAGES + "'");
}
return new ServiceStrategyAutoScanProxy(scanPackages);
return new RpcStrategyAutoScanProxy(scanPackages);
}
@Bean
@ConditionalOnProperty(value = ServiceStrategyConstant.SPRING_APPLICATION_STRATEGY_SCAN_PACKAGES, matchIfMissing = false)
public ServiceStrategyInterceptor serviceStrategyInterceptor() {
public RpcStrategyInterceptor rpcStrategyInterceptor() {
String scanPackages = environment.getProperty(ServiceStrategyConstant.SPRING_APPLICATION_STRATEGY_SCAN_PACKAGES);
if (StringUtils.isEmpty(scanPackages)) {
throw new DiscoveryException(ServiceStrategyConstant.SPRING_APPLICATION_STRATEGY_SCAN_PACKAGES + " can't be empty, remove it if useless");
......@@ -64,7 +66,7 @@ public class ServiceStrategyAutoConfiguration {
throw new DiscoveryException("It can't scan packages for '" + ServiceStrategyConstant.EXCLUSION_SCAN_PACKAGES + "', please check '" + ServiceStrategyConstant.SPRING_APPLICATION_STRATEGY_SCAN_PACKAGES + "'");
}
return new ServiceStrategyInterceptor();
return new RpcStrategyInterceptor();
}
@Bean
......@@ -99,4 +101,10 @@ public class ServiceStrategyAutoConfiguration {
public ServiceStrategyContextHolder serviceStrategyContextHolder() {
return new ServiceStrategyContextHolder();
}
@Bean
@ConditionalOnProperty(value = StrategyConstant.SPRING_APPLICATION_STRATEGY_HYSTRIX_THREADLOCAL_SUPPORTED, matchIfMissing = false)
public CallableWrapper callableWrapper() {
return new DefaultCallableWrapper();
}
}
\ No newline at end of file
package com.nepxion.discovery.plugin.strategy.service.context;
/**
* <p>Title: Nepxion Discovery</p>
* <p>Description: Nepxion Discovery</p>
* <p>Copyright: Copyright (c) 2017-2050</p>
* <p>Company: Nepxion</p>
* @author Haojun Ren
* @version 1.0
*/
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.web.context.request.ServletRequestAttributes;
public class RestStrategyContext {
private static final ThreadLocal<RestStrategyContext> THREAD_LOCAL = new InheritableThreadLocal<RestStrategyContext>() {
@Override
protected RestStrategyContext initialValue() {
return new RestStrategyContext();
}
};
public static RestStrategyContext getCurrentContext() {
return THREAD_LOCAL.get();
}
public static void clearCurrentContext() {
THREAD_LOCAL.remove();
}
private ServletRequestAttributes requestAttributes;
public ServletRequestAttributes getRequestAttributes() {
return requestAttributes;
}
public void setRequestAttributes(ServletRequestAttributes requestAttributes) {
this.requestAttributes = requestAttributes;
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(Object object) {
return EqualsBuilder.reflectionEquals(this, object);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}
\ No newline at end of file
......@@ -18,15 +18,15 @@ import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class ServiceStrategyContext {
private static final ThreadLocal<ServiceStrategyContext> THREAD_LOCAL = new InheritableThreadLocal<ServiceStrategyContext>() {
public class RpcStrategyContext {
private static final ThreadLocal<RpcStrategyContext> THREAD_LOCAL = new InheritableThreadLocal<RpcStrategyContext>() {
@Override
protected ServiceStrategyContext initialValue() {
return new ServiceStrategyContext();
protected RpcStrategyContext initialValue() {
return new RpcStrategyContext();
}
};
public static ServiceStrategyContext getCurrentContext() {
public static RpcStrategyContext getCurrentContext() {
return THREAD_LOCAL.get();
}
......@@ -36,7 +36,7 @@ public class ServiceStrategyContext {
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
public ServiceStrategyContext add(String key, Object value) {
public RpcStrategyContext add(String key, Object value) {
attributes.put(key, value);
return this;
......@@ -46,13 +46,13 @@ public class ServiceStrategyContext {
return attributes.get(key);
}
public ServiceStrategyContext remove(String key) {
public RpcStrategyContext remove(String key) {
attributes.remove(key);
return this;
}
public ServiceStrategyContext clear() {
public RpcStrategyContext clear() {
attributes.clear();
return this;
......
package com.nepxion.discovery.plugin.strategy.service.context;
import java.util.Map;
/**
* <p>Title: Nepxion Discovery</p>
* <p>Description: Nepxion Discovery</p>
......@@ -11,6 +9,8 @@ import java.util.Map;
* @version 1.0
*/
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.web.context.request.RequestContextHolder;
......@@ -22,17 +22,16 @@ public class ServiceStrategyContextHolder {
@Autowired
private ConfigurableEnvironment environment;
public ServletRequestAttributes getRequestAttributes() {
public ServletRequestAttributes getRestAttributes() {
Boolean hystrixThreadlocalSupported = environment.getProperty(StrategyConstant.SPRING_APPLICATION_STRATEGY_HYSTRIX_THREADLOCAL_SUPPORTED, Boolean.class, Boolean.FALSE);
if (hystrixThreadlocalSupported) {
// 服务端使用Hystrix做线程模式的服务隔离时,实现服务灰度路由的功能,预留待实现
return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return RestStrategyContext.getCurrentContext().getRequestAttributes();
} else {
return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
}
}
public Map<String, Object> getMethodAttributes() {
return ServiceStrategyContext.getCurrentContext().getAttributes();
public Map<String, Object> getRpcAttributes() {
return RpcStrategyContext.getCurrentContext().getAttributes();
}
}
\ No newline at end of file
package com.nepxion.discovery.plugin.strategy.service.wrapper;
/**
* <p>Title: Nepxion Discovery</p>
* <p>Description: Nepxion Discovery</p>
* <p>Copyright: Copyright (c) 2017-2050</p>
* <p>Company: Nepxion</p>
* @author Haojun Ren
* @author Hao Huang
* @version 1.0
*/
import java.util.concurrent.Callable;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.nepxion.discovery.plugin.strategy.service.context.RestStrategyContext;
import com.nepxion.discovery.plugin.strategy.wrapper.CallableWrapper;
public class DefaultCallableWrapper implements CallableWrapper {
@Override
public <T> Callable<T> wrapCallable(Callable<T> delegate) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return new Callable<T>() {
@Override
public T call() throws Exception {
try {
RestStrategyContext.getCurrentContext().setRequestAttributes(requestAttributes);
return delegate.call();
} finally {
RestStrategyContext.clearCurrentContext();
}
}
};
}
}
\ No newline at end of file
......@@ -82,6 +82,12 @@
<artifactId>discovery-plugin-strategy-starter-service</artifactId>
</dependency>
<!-- 当服务用Hystrix做线程隔离的时候,才需要导入下面的包 -->
<!-- <dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-strategy-starter-hystrix</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
......
......@@ -32,6 +32,7 @@ import com.nepxion.discovery.plugin.strategy.service.aop.RestTemplateStrategyInt
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
// @EnableCircuitBreaker
public class DiscoveryApplicationA1 {
public static void main(String[] args) {
System.setProperty("spring.profiles.active", "a1");
......
......@@ -27,6 +27,7 @@ public class AFeignImpl extends AbstractFeignImpl implements AFeign {
private BFeign bFeign;
@Override
// @HystrixCommand(fallbackMethod = "fallback", commandProperties = { @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD") })
public String invoke(@RequestBody String value) {
value = doInvoke(value);
value = bFeign.invoke(value);
......@@ -35,4 +36,12 @@ public class AFeignImpl extends AbstractFeignImpl implements AFeign {
return value;
}
/*public String fallback(String value, Throwable e) {
if (e != null) {
LOG.error("Fallback error", e);
}
return "Fallback by Hystrix";
}*/
}
\ No newline at end of file
......@@ -44,7 +44,7 @@ public class MyDiscoveryEnabledStrategy implements DiscoveryEnabledStrategy {
// 根据Rest调用传来的Header参数(例如Token),选取执行调用请求的服务实例
private boolean applyFromHeader(Server server, Map<String, String> metadata) {
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRequestAttributes();
ServletRequestAttributes attributes = serviceStrategyContextHolder.getRestAttributes();
if (attributes == null) {
return true;
}
......@@ -70,7 +70,7 @@ public class MyDiscoveryEnabledStrategy implements DiscoveryEnabledStrategy {
// 根据RPC调用传来的方法参数(例如接口名、方法名、参数名或参数值等),选取执行调用请求的服务实例
@SuppressWarnings("unchecked")
private boolean applyFromMethod(Server server, Map<String, String> metadata) {
Map<String, Object> attributes = serviceStrategyContextHolder.getMethodAttributes();
Map<String, Object> attributes = serviceStrategyContextHolder.getRpcAttributes();
String serviceId = server.getMetaInfo().getAppName().toLowerCase();
String version = metadata.get(DiscoveryConstant.VERSION);
......
......@@ -88,4 +88,6 @@ spring.boot.admin.url=http://localhost:5555
# 用户自定义和编程灰度路由策略的时候,对RPC方法调用拦截的时候,需要指定对业务Controller类的扫描路径,以便传递上下文对象。该项配置只对服务有效,对网关无效。缺失则默认关闭该功能
spring.application.strategy.scan.packages=com.nepxion.discovery.plugin.example.service.feign
# 用户自定义和编程灰度路由策略的时候,对REST调用拦截的时候(支持Feign或者RestTemplate调用),需要把来自外部的指定Header参数传递到服务里,如果多个用“;”分隔,不允许出现空格。该项配置只对服务有效,对网关无效。缺失则默认关闭该功能
spring.application.strategy.request.headers=version;region;token
\ No newline at end of file
spring.application.strategy.request.headers=version;region;token
# 开启服务端实现Hystrix线程隔离模式做服务隔离时,必须把spring.application.strategy.hystrix.threadlocal.supported设置为true,同时要引入discovery-plugin-strategy-starter-hystrix包,否则线程切换时会发生ThreadLocal上下文对象丢失
# spring.application.strategy.hystrix.threadlocal.supported=true
\ No newline at end of file
......@@ -82,11 +82,11 @@
<artifactId>discovery-plugin-strategy-starter-zuul</artifactId>
</dependency>
<!-- Used for zuul hystrix thread isolation -->
<dependency>
<!-- 当Zuul用Hystrix做线程隔离的时候,才需要导入下面的包 -->
<!-- <dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-strategy-starter-hystrix</artifactId>
</dependency>
</dependency> -->
<dependency>
<groupId>de.codecentric</groupId>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment