diff --git a/README.md b/README.md
index e70186be..14287f9d 100644
--- a/README.md
+++ b/README.md
@@ -7,52 +7,11 @@
-#### 多版本控制
-* [spring-cloud-bamboo](spring-cloud-bamboo/README.md)
-* spring-cloud-start-multi-version
-* [spring-cloud-mult-version-samples](spring-cloud-mult-version-samples/README.md)
-
-
-
-#### 灰度发布
-* [spring-cloud-gray-core](spring-cloud-gray-core/README.md)
-* spring-cloud-gray-client
-* spring-cloud-gray-server
-* spring-cloud-start-gray
-* spring-cloud-start-gray-server
-* [spring-cloud-gray-samples](spring-cloud-gray-samples/README.md)
-* [spring-cloud-gray-zookeeper-samples](spring-cloud-gray-zookeeper-samples/README.md)
-
-#### maven 依赖
-jar包已经上传到maven中央库,可以通过maven直接从中央库下载
-```xml
-
-
- cn.springcloud.gray
- spring-cloud-starter-multi-version
- {version}
-
-
-
-
- cn.springcloud.gray
- spring-cloud-starter-gray
- {version}
-
-
-
-
- cn.springcloud.gray
- spring-cloud-starter-gray-server
- {version}
-
-```
-
#### 不足
-gray目前只有灰度管理的基本功能, 像数据持久化,高可用,推送灰度调整消息等, 都没有实现。 也没有界面化, 仅仅只有接口列表。
+没有界面化, 仅仅只有接口列表。
#### 扩展思考
-gray目前仅仅只支持spring cloud eureka, 但是在spring cloud中,eureka只是做为其中一个注册中心, 如果要做spring cloud的灰度管理, 就还需要兼容其他的注册中心, 比如zookeeper, consul等。
+gray目前仅仅只支持spring cloud eureka, 但是在spring cloud中,eureka只是做为其中一个注册中心, 如果要做spring cloud的灰度管理, 就还需要兼容其他的注册中心, 比如zookeeper, consul, nacos等。
diff --git a/pom.xml b/pom.xml
index 69cd894a..c8d3982d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,30 +4,29 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- cn.springcloud.gray
- spring-cloud-gray
- pom
- 1.1.0
org.springframework.boot
spring-boot-starter-parent
1.5.4.RELEASE
+ cn.springcloud.gray
+ spring-cloud-gray
+ pom
+ 2.0.0
spring-cloud-gray-dependencies
- spring-cloud-bamboo
- spring-cloud-starter-multi-version
- spring-cloud-mult-version-samples
-
spring-cloud-gray-core
spring-cloud-gray-server
spring-cloud-gray-client
- spring-cloud-starter-gray
+ spring-cloud-starter-gray-client
spring-cloud-starter-gray-server
spring-cloud-gray-samples
- spring-cloud-gray-zookeeper-samples
+ spring-cloud-gray-client-netflix
+ spring-cloud-gray-utils
+ spring-cloud-gray-starter-dependencies
+ spring-cloud-gray-webui
@@ -56,11 +55,15 @@
1.8
1.8
1.8
- Dalston.SR5
- 2.7.0
-
-
+ Edgware.SR5
+ 2.9.2
+ 1.18.8
+ 1.7.26
+ 3.5
+ 27.0.1-jre
+ 1.1.0.Final
+
@@ -73,13 +76,13 @@
import
-
- cn.springcloud.gray
- spring-cloud-gray-dependencies
- ${project.version}
- pom
- import
-
+
+
+
+
+
+
+
@@ -93,6 +96,44 @@
+
+
+ cn.springcloud.gray
+ spring-cloud-gray-core
+ ${project.version}
+
+
+ cn.springcloud.gray
+ spring-cloud-gray-client
+ ${project.version}
+
+
+ cn.springcloud.gray
+ spring-cloud-gray-client-netflix
+ ${project.version}
+
+
+ cn.springcloud.gray
+ spring-cloud-gray-utils
+ ${project.version}
+
+
+ cn.springcloud.gray
+ spring-cloud-starter-gray-client
+ ${project.version}
+
+
+ cn.springcloud.gray
+ spring-cloud-gray-server
+ ${project.version}
+
+
+ cn.springcloud.gray
+ spring-cloud-starter-gray-server
+ ${project.version}
+
+
+
io.springfox
@@ -109,12 +150,84 @@
springfox-bean-validators
${springfox.version}
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ commons-io
+ commons-io
+ 2.4
+
+
+
+ org.mapstruct
+ mapstruct-jdk8
+ ${mapstruct.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+ provided
+
+
+ spring-boot:run
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.6.0
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
+
diff --git a/spring-cloud-bamboo/README.md b/spring-cloud-bamboo/README.md
deleted file mode 100644
index 5d310c87..00000000
--- a/spring-cloud-bamboo/README.md
+++ /dev/null
@@ -1,334 +0,0 @@
-## 多版本控制
-
-该项目是在spring-cloud-ribbon的基础上进行扩展,以实现接口的多个版本的调用及负载均衡,支持feign方式和断路器(spring-cloud-hystrix)。
-
-
-##### 场景
-服务A部署了两个实例 serivceA-1,serviceA-2, spring cloud ribbon默认是轮询的方式将请求分别转到两个实例上。如果由于业务原因,服务需要从1.0升级到2.0。
-
-场景1:将所有服务实例平缓的过度到2.0。
-场景2:2.0的服务实例需要兼容1.0的服务接口。
-
-
-
-##### 思路
-在spring cloud微服务体系中,服务的请求来源无外乎两个方面:
-来源1:外部请求通过网关(zuul)转发而来。
-来源2:内部服务之间的调用请求。
-不论网关转发过来的请求,还是内部服务调用过来的请求,都需要ribbon做负载均衡,所以可以扩展ribbon的负载均衡策略从而实现不同版本的请求转发到不同的服务实例上。
-
-
-
-网关的转发过程是:zuul > hystrix > ribbon
-内部服务调用的过程有两种:
-RestTemplate > hystrix > ribbon
-Feign > hystrix > ribbon
-
-而其中hystrix有一个线程池隔离的能力,会创建另一个线程去请求服务,拥有更好的控制并发访问量、以及服务降级等能力,但是会出现一个问题,就是线程变量(ThreadLocal)的传递问题,这可以通过com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableDefault对象解决。
-
-
-
-
-##### 代码设计
-虽然整个项目实现起来代码量不少, 但是在接口设计上, 却只有三个简单的接口负责数据传递,路由的逻辑依然是封装在实现了IRule接口的实现类中(后面分析)。
-
-
-
-* BambooRibbonConnectionPoint
-这个接口是负责将bamboo跟ribbon连接起来的,将请求的信息, 以及根据业务需要添加的一些路由信息,和获取请求接口的目标版本,还有触发执行LoadBanceRequestTrigger等,都是由该接口的实现类DefaultRibbonConnectionPoint负责实现。
-```java
-public class DefaultRibbonConnectionPoint implements BambooRibbonConnectionPoint, ApplicationContextAware {
- ...
- @Override
- public void executeConnectPoint(ConnectPointContext connectPointContext) {
- ConnectPointContext.contextLocal.set(connectPointContext);
- BambooRequest bambooRequest = connectPointContext.getBambooRequest();
- String requestVersion = versionExtractor.extractVersion(bambooRequest);
- BambooRequestContext.initRequestContext(bambooRequest, requestVersion);
- executeBeforeReuqestTrigger();
- }
-
- @Override
- public void shutdownconnectPoint() {
- try {
- executeAfterReuqestTrigger();
- } catch (Exception e) {
- ConnectPointContext.getContextLocal().setExcption(e);
- } finally {
- curRequestTriggers.remove();
- ConnectPointContext.contextLocal.remove();
- BambooRequestContext.shutdownRequestContext();
- }
- }
- ...
-}
-```
-
-
-* RequestVersionExtractor
-这个接口负责获取请求需要访问的目标接口的版本。比如有些接口版本是放在路径上,如:/v1/api/test/get。也有放在uri参数中:/api/test/get?v=1。也有可能放到header中,所以在bamboo抽象出来一个接口, 具体的实现由开发者根据业务去实现。
-
-
-* LoadBalanceRequestTrigger
-Ribbon请求的触发器,在ribbon请求发起时, 会被执行。这个接口有三个方法,分别是判断是否需要执行的方法(shouldExecute),以及请求之前执行(before)和请求完成之后执行(after),如果出现异常,after方法依然会被执行。
-
-
-##### 代码实现
-上面三个接口只是简单的实现了获取请求的目标版本、触发ribbon请求的触发器,以及将信息向下一步传递。在这一段中,将介绍如何与zuul、feign、RestTemplate以及ribbon和hystrix衔接起来。
-
-* RestTemplate衔接
-ClientHttpRequestInterceptor是RestTemplate的拦截器接口,可以通过这个接口添加bamboo的逻辑, 从而将RestTemplate和bamboo衔接起来。
-BambooClientHttpRequestIntercptor是ClientHttpRequestInterceptor接口的实现类,它加入了bamboo的逻辑。
-```java
-/**
- * 用于@LoadBalance 标记的 RestTemplate,主要作用是用来获取request的相关信息,为后面的路由提供数据基础。
- */
-public class BambooClientHttpRequestIntercptor implements ClientHttpRequestInterceptor {
- @Override
- public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
-
- URI uri = request.getURI();
- BambooRequest bambooRequest = BambooRequest.builder()
- .serviceId(uri.getHost())
- .uri(uri.getPath())
- .ip(RequestIpKeeper.getRequestIp())
- .addMultiHeaders(request.getHeaders())
- .addMultiParams(WebUtils.getQueryParams(uri.getQuery()))
- .build();
-
- ConnectPointContext connectPointContext = ConnectPointContext.builder().bambooRequest(bambooRequest).build();
- try {
- BambooAppContext.getBambooRibbonConnectionPoint().executeConnectPoint(connectPointContext);
- return execution.execute(request, body);
- } finally {
- BambooAppContext.getBambooRibbonConnectionPoint().shutdownconnectPoint();
- }
- }
-}
-```
-
-* Feign衔接
-BambooFeignClient类实现了feign.Client接口, 该类是一个代理类,主要的Feign的调用逻辑依然由被代理的类去执行,在该类中添加了bamboo的逻辑,从而将Feign和bamboo衔接起来。
-```java
-/**
- * 主要作用是用来获取request的相关信息,为后面的路由提供数据基础。
- */
-public class BambooFeignClient implements Client {
-
- private Client delegate;
-
-
- public BambooFeignClient(Client delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public Response execute(Request request, Request.Options options) throws IOException {
- URI uri = URI.create(request.url());
- BambooRequest.Builder builder = BambooRequest.builder()
- .serviceId(uri.getHost())
- .uri(uri.getPath())
- .ip(RequestIpKeeper.getRequestIp())
- .addMultiParams(WebUtils.getQueryParams(uri.getQuery()));
-
- request.headers().entrySet().forEach(entry ->{
- for (String v : entry.getValue()) {
- builder.addHeader(entry.getKey(), v);
- }
- });
-
- ConnectPointContext connectPointContext = ConnectPointContext.builder().bambooRequest(builder.build()).build();
-
- try {
- BambooAppContext.getBambooRibbonConnectionPoint().executeConnectPoint(connectPointContext);
- return delegate.execute(request, options);
- }finally {
- BambooAppContext.getBambooRibbonConnectionPoint().shutdownconnectPoint();
- }
- }
-}
-```
-
-
-* Zuul衔接
-实现两个ZuulFilter接口,分别是pre和post类型,将bamboo的逻辑加入其中。Pre类型的ZuulFilter获取请求信息,并执行LoadBalanceRequestTrigger#before方法。Post类型的ZuulFilter执行LoadBalanceRequestTrigger#after方法,并清除存在ThradLocal中的相关信息。
-```java
-/**
- * 主要作用是用来获取request的相关信息,为后面的路由提供数据基础。
- */
-public class BambooPreZuulFilter extends ZuulFilter {
- @Override
- public String filterType() {
- return FilterConstants.PRE_TYPE;
- }
-
- @Override
- public int filterOrder() {
- return 10000;
- }
-
- @Override
- public boolean shouldFilter() {
- return true;
- }
-
- @Override
- public Object run() {
- RequestContext context = RequestContext.getCurrentContext();
- BambooRequest.Builder builder = BambooRequest.builder()
- .serviceId((String)context.get(FilterConstants.SERVICE_ID_KEY))
- .uri((String)context.get(FilterConstants.REQUEST_URI_KEY))
- .ip(context.getZuulRequestHeaders().get(FilterConstants.X_FORWARDED_FOR_HEADER.toLowerCase()))
- .addMultiParams(context.getRequestQueryParams())
- .addHeaders(context.getZuulRequestHeaders())
- .addHeaders(context.getOriginResponseHeaders().stream().collect(Collectors.toMap(Pair::first, Pair::second)));
- context.getOriginResponseHeaders().forEach(pair-> builder.addHeader(pair.first(), pair.second()));
-
- ConnectPointContext connectPointContext = ConnectPointContext.builder().bambooRequest(builder.build()).build();
-
- BambooAppContext.getBambooRibbonConnectionPoint().executeConnectPoint(connectPointContext);
- return null;
- }
-
-}
-```
-```java
-/**
- * 做一些善后工作。比如删除BambooRequestContext在ThreadLocal中的信息。
- */
-public class BambooPostZuulFilter extends ZuulFilter {
- @Override
- public String filterType() {
- return FilterConstants.POST_TYPE;
- }
-
- @Override
- public int filterOrder() {
- return 0;
- }
-
- @Override
- public boolean shouldFilter() {
- return true;
- }
-
- @Override
- public Object run() {
-// BambooRequestContext.shutdownRequestContext();
- BambooAppContext.getBambooRibbonConnectionPoint().shutdownconnectPoint();
- return null;
- }
-}
-```
-
-* Hystrix衔接
-Hystrix实现降级、断路器等功能,但是在使用线程池隔离时,ThreadLocal存储的信息如何传递下去呢?使用HystrixRequestVariableDefault可以解决这个问题。可以查看com.netflix.hystrix.strategy.concurrency包下的HystrixContexSchedulerAction、HystrixContextCallable、HystrixContextRunnable,它们都有一段相同功能的代码
-```java
-public class HystrixContextRunnable implements Runnable {
-
- private final Callable actual;
- private final HystrixRequestContext parentThreadState;
-
- //...
-
- @Override
- public void run() {
- HystrixRequestContext existingState = HystrixRequestContext.getContextForCurrentThread();
- try {
- // set the state of this thread to that of its parent
- HystrixRequestContext.setContextOnCurrentThread(parentThreadState);
- // execute actual Callable with the state of the parent
- try {
- actual.call();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- } finally {
- // restore this thread back to its original state
- HystrixRequestContext.setContextOnCurrentThread(existingState);
- }
- }
-
-}
-```
-parentThreadState也是一个HystrixRequestContext对象,它是在hystrix创建线程之前的,也就是处理http请求的线程的HystrixRequestContext对象,我们一般也是维护这个对象。在使用线程池隔离时,hystrix会将parentThreadState中的信息复到到新线程中,实现跨线程的数据传递,从而在后面的逻辑中可以获取到parentThreadState中维护的信息,包括ribbon的路由信息。在bamboo中,将一步骤的逻辑放到BambooRequestContext中,将BambooRequestContext实例本身传递下去。
-```java
-public class BambooRequestContext {
-
- private static final Logger log = LoggerFactory.getLogger(BambooRequestContext.class);
-
- private static final HystrixRequestVariableDefault CURRENT_CONTEXT = new HystrixRequestVariableDefault();
-
-
- private final String apiVersion;
- private final BambooRequest bambooRequest;
- private Map params;
-
-
- private BambooRequestContext(BambooRequest bambooRequest, String apiVersion) {
- params = new HashMap<>();
- this.apiVersion = apiVersion;
- this.bambooRequest = bambooRequest;
- }
-
-
- public static BambooRequestContext currentRequestCentxt() {
- return CURRENT_CONTEXT.get();
- }
-
- public static void initRequestContext(BambooRequest bambooRequest, String apiVersion) {
- if (!HystrixRequestContext.isCurrentThreadInitialized()) {
- HystrixRequestContext.initializeContext();
- }
- CURRENT_CONTEXT.set(new BambooRequestContext(bambooRequest, apiVersion));
- }
-
- public static void shutdownRequestContext() {
- if (HystrixRequestContext.isCurrentThreadInitialized()) {
- HystrixRequestContext.getContextForCurrentThread().shutdown();
- }
- }
-
- //忽略setter/getter
-}
-```
-
-* Ribbon 路由规则
-Bamboo中的BambooZoneAvoidanceRule继承了ZoneAvoidanceRule,所以它会有ZvoidanceRule的一切特性,在此基础上,还加入了版本过滤的逻辑,这个逻辑主要是由BambooApiVersionPredicate实现。从BambooRequestContext中获取请求的接口的版本,如果有该没有获取到版本,就返回true;如果有获取到版本,就获取服务实例的metadata中的version信息,并进行匹配校验,返回结果。
-```java
-public class BambooApiVersionPredicate extends AbstractServerPredicate {
-
-
- public BambooApiVersionPredicate(BambooZoneAvoidanceRule rule) {
- super(rule);
- }
-
- @Override
- public boolean apply(PredicateKey input) {
- BambooLoadBalancerKey loadBalancerKey = getBambooLoadBalancerKey(input);
- if (loadBalancerKey != null && !StringUtils.isEmpty(loadBalancerKey.getApiVersion())) {
- Map serverMetadata = ((BambooZoneAvoidanceRule) this.rule)
- .getServerMetadata(loadBalancerKey.getServiceId(), input.getServer());
- String versions = serverMetadata.get("versions");
- return matchVersion(versions, loadBalancerKey.getApiVersion());
- }
- return true;
- }
-
- private BambooLoadBalancerKey getBambooLoadBalancerKey(PredicateKey input) {
- if(BambooRequestContext.currentRequestCentxt()!=null){
- BambooRequestContext bambooRequestContext = BambooRequestContext.currentRequestCentxt();
- String apiVersion = bambooRequestContext.getApiVersion();
- if(!StringUtils.isEmpty(apiVersion)){
- return BambooLoadBalancerKey.builder().apiVersion(apiVersion)
- .serviceId(bambooRequestContext.getServiceId()).build();
- }
- }
- return null;
- }
- //...
-}
-```
-
-##### 使用说明
-多版本控制 --> [spring-cloud-mult-version-samples](../spring-cloud-mult-version-samples/README.md)
\ No newline at end of file
diff --git a/spring-cloud-bamboo/pom.xml b/spring-cloud-bamboo/pom.xml
deleted file mode 100644
index a731a9c0..00000000
--- a/spring-cloud-bamboo/pom.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
- spring-cloud-gray
- cn.springcloud.gray
- 1.1.0
-
- 4.0.0
-
- spring-cloud-bamboo
-
-
- jar
-
-
-
-
- org.springframework.boot
- spring-boot-starter-web
- provided
-
-
- org.springframework.boot
- spring-boot-starter-test
- provided
-
-
- org.springframework.cloud
- spring-cloud-starter-eureka
- provided
-
-
- org.springframework.cloud
- spring-cloud-starter-feign
- provided
-
-
- org.springframework.cloud
- spring-cloud-starter-zuul
- provided
-
-
-
- org.springframework.cloud
- spring-cloud-netflix-core
- provided
-
-
-
-
-
-
\ No newline at end of file
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooAppContext.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooAppContext.java
deleted file mode 100644
index 376fe6fe..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooAppContext.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package cn.springcloud.bamboo;
-
-
-import cn.springcloud.bamboo.ribbon.EurekaServerExtractor;
-
-public class BambooAppContext {
-
- private static BambooRibbonConnectionPoint defaultConnectionPoint;
- private static EurekaServerExtractor eurekaServerExtractor;
- private static String localIp;
-
- public static BambooRibbonConnectionPoint getBambooRibbonConnectionPoint(){
- return defaultConnectionPoint;
- }
-
-
- static void setDefaultConnectionPoint(BambooRibbonConnectionPoint connectionPoint){
- BambooAppContext.defaultConnectionPoint = connectionPoint;
- }
-
- public static EurekaServerExtractor getEurekaServerExtractor() {
- return eurekaServerExtractor;
- }
-
- static void setEurekaServerExtractor(EurekaServerExtractor eurekaServerExtractor) {
- BambooAppContext.eurekaServerExtractor = eurekaServerExtractor;
- }
-
- public static String getLocalIp() {
- return localIp;
- }
-
- static void setLocalIp(String localIp) {
- BambooAppContext.localIp = localIp;
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooConstants.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooConstants.java
deleted file mode 100644
index 382b1e6f..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooConstants.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package cn.springcloud.bamboo;
-
-public class BambooConstants {
-
-
- /**
- * 初始化Bean的顺序标准值
- */
- public static final int INITIALIZING_ORDER = 100000;
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooInitializingBean.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooInitializingBean.java
deleted file mode 100644
index 578be146..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooInitializingBean.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package cn.springcloud.bamboo;
-
-import cn.springcloud.bamboo.ribbon.EurekaServerExtractor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-
-/**
- * Bamboo相关依赖的初始化工作
- */
-public class BambooInitializingBean implements InitializingBean, ApplicationContextAware {
-
- private static final Logger log = LoggerFactory.getLogger(BambooInitializingBean.class);
-
-
- private ApplicationContext ctx;
-
- @Override
- public void afterPropertiesSet() {
- BambooAppContext.setDefaultConnectionPoint(ctx.getBean(BambooRibbonConnectionPoint.class));
- BambooAppContext.setEurekaServerExtractor(ctx.getBean(EurekaServerExtractor.class));
- setLocalIp();
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.ctx = applicationContext;
- }
-
-
- /**
- * 设置本机ip
- */
- private void setLocalIp(){
- try {
- BambooAppContext.setLocalIp(InetAddress.getLocalHost().getHostAddress());
- } catch (UnknownHostException e) {
- log.error("[IpHelper-getIpAddr] IpHelper error.", e);
- }
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooRequest.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooRequest.java
deleted file mode 100644
index 246103a2..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooRequest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package cn.springcloud.bamboo;
-
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-
-import java.util.List;
-import java.util.Map;
-
-public class BambooRequest {
- private final String uri;
- private final String serviceId;
- private final String ip;
- private final MultiValueMap params;
- private final MultiValueMap headers;
- private final RequestBody requestBody;
-
-
- private BambooRequest(
- String uri, String serviceId, String ip, MultiValueMap params,
- MultiValueMap headers, RequestBody requestBody) {
- this.uri = uri;
- this.serviceId = serviceId;
- this.params = params;
- this.headers = headers;
- this.ip = ip;
- this.requestBody = requestBody;
-
- }
-
-
- public static Builder builder(){
- return new Builder();
- }
-
- public static class Builder{
- private String uri;
- private String serviceId;
- private String ip;
- private MultiValueMap params = new LinkedMultiValueMap<>();
- private MultiValueMap headers = new LinkedMultiValueMap<>();
- private RequestBody requestBody;
- private Builder(){
-
- }
-
-
-
- public Builder ip(String ip){
- this.ip = ip;
- return this;
- }
-
- public Builder uri(String uri){
- this.uri = uri;
- return this;
- }
-
- public Builder serviceId(String serviceId){
- this.serviceId = serviceId;
- return this;
- }
-
- public Builder params(MultiValueMap params){
- if(params!=null) {
- this.params = params;
- }
- return this;
- }
-
- public Builder addParams(Map params){
- if(params!=null) {
- this.params.setAll(params);
- }
- return this;
- }
-
- public Builder addParameter(String key, String value){
- this.params.add(key, value);
- return this;
- }
-
- public Builder addMultiParams(Map> params){
- if(params!=null) {
- this.params.putAll(params);
- }
- return this;
- }
-
- public Builder headers(MultiValueMap headers){
- if(headers!=null) {
- this.headers = headers;
- }
- return this;
- }
-
- public Builder addHeaders(Map headers){
- if(headers!=null) {
- this.headers.setAll(headers);
- }
- return this;
- }
-
- public Builder addMultiHeaders(Map> headers){
- if(headers!=null) {
- this.headers.putAll(headers);
- }
- return this;
- }
-
- public Builder addHeader(String key, String value){
- this.headers.add(key, value);
- return this;
- }
-
- public Builder requestBody(RequestBody requestBody){
- this.requestBody = requestBody;
- return this;
- }
-
- public Builder requestBody(byte[] body){
- this.requestBody = new BytesRequestBody(body);
- return this;
- }
-
-
- public BambooRequest build() {
- return new BambooRequest(uri, serviceId, ip, params, headers, requestBody);
- }
- }
-
-
-
-
-
- public String getUri() {
- return uri;
- }
-
- public String getServiceId() {
- return serviceId;
- }
-
- public MultiValueMap getParams() {
- return params;
- }
-
- public MultiValueMap getHeaders() {
- return headers;
- }
-
-
- public String getIp() {
- return ip;
- }
-
- public RequestBody getRequestBody() {
- return requestBody;
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooRequestContext.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooRequestContext.java
deleted file mode 100644
index f10ac3b7..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooRequestContext.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package cn.springcloud.bamboo;
-
-import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
-import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableDefault;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class BambooRequestContext {
-
- private static final Logger log = LoggerFactory.getLogger(BambooRequestContext.class);
-
- private static final HystrixRequestVariableDefault CURRENT_CONTEXT = new HystrixRequestVariableDefault();
-
-
- private final String apiVersion;
- private final BambooRequest bambooRequest;
- private Map params;
-
-
- private BambooRequestContext(BambooRequest bambooRequest, String apiVersion) {
- params = new HashMap<>();
- this.apiVersion = apiVersion;
- this.bambooRequest = bambooRequest;
- }
-
-
- public static BambooRequestContext currentRequestCentxt() {
- return CURRENT_CONTEXT.get();
- }
-
- public static void initRequestContext(BambooRequest bambooRequest, String apiVersion) {
- if (!HystrixRequestContext.isCurrentThreadInitialized()) {
- HystrixRequestContext.initializeContext();
- }
- CURRENT_CONTEXT.set(new BambooRequestContext(bambooRequest, apiVersion));
- }
-
- public static void shutdownRequestContext() {
- if (HystrixRequestContext.isCurrentThreadInitialized()) {
- HystrixRequestContext.getContextForCurrentThread().shutdown();
- }
- }
-
-
- public String getApiVersion() {
- return apiVersion;
- }
-
- public String getServiceId() {
- return bambooRequest.getServiceId();
- }
-
-
- public void addParameter(String key, Object value){
- params.put(key, value);
- }
-
- public Object getParameter(String key){
- return params.get(key);
- }
-
-
- public String getStrParameter(String key){
- return (String) params.get(key);
- }
-
- public Integer getIntegerParameter(String key){
- return (Integer) params.get(key);
- }
-
-
- public Long getLongParameter(String key){
- return (Long) params.get(key);
- }
-
- public Boolean getBooleanParameter(String key){
- return (Boolean) params.get(key);
- }
-
- public BambooRequest getBambooRequest() {
- return bambooRequest;
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooRibbonConnectionPoint.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooRibbonConnectionPoint.java
deleted file mode 100644
index 2178eedd..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BambooRibbonConnectionPoint.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package cn.springcloud.bamboo;
-
-
-/**
- * 个接口是负责将bamboo跟ribbon连接起来的,将请求的信息, 以及根据业务需要添加的一些路由信息,和获取请求接口的目标版本,
- * 还有触发执行LoadBanceRequestTrigger等,都是由该接口的实现类DefaultRibbonConnectionPoint负责实现。
- */
-public interface BambooRibbonConnectionPoint {
-
-
- void executeConnectPoint(ConnectPointContext connectPointContext);
-
-
- void shutdownconnectPoint();
-
-
-// void executeBeforeReuqestTrigger();
-
-
-// void executeAfterReuqestTrigger();
-
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BytesRequestBody.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BytesRequestBody.java
deleted file mode 100644
index 600bda07..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/BytesRequestBody.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package cn.springcloud.bamboo;
-
-import java.io.UnsupportedEncodingException;
-
-public class BytesRequestBody implements RequestBody{
-
- private static final String DEFAULT_CHARSET = "UTF-8";
-
- private byte[] body;
-
- public BytesRequestBody(byte[] body) {
- this.body = body;
- }
-
- @Override
- public byte[] getBody() {
- return body;
- }
-
- @Override
- public String getBodyString() {
- return getBodyString(DEFAULT_CHARSET);
- }
-
- @Override
- public String getBodyString(String charset) {
- try {
- return new String(body, charset);
- }catch (UnsupportedEncodingException e){
- throw new RuntimeException(e.getMessage(), e);
- }
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ConnectPointContext.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ConnectPointContext.java
deleted file mode 100644
index 0f70ef1a..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ConnectPointContext.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package cn.springcloud.bamboo;
-
-public class ConnectPointContext {
-
- static final ThreadLocal contextLocal = new ThreadLocal<>();
-
-
- private BambooRequest bambooRequest;
- private Throwable excption;
-
- private ConnectPointContext(BambooRequest bambooRequest) {
- this.bambooRequest = bambooRequest;
- }
-
- public BambooRequest getBambooRequest() {
- return bambooRequest;
- }
-
- void setBambooRequest(BambooRequest bambooRequest) {
- this.bambooRequest = bambooRequest;
- }
-
-
- public Throwable getExcption() {
- return excption;
- }
-
- void setExcption(Throwable excption) {
- this.excption = excption;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public static class Builder {
-
- private BambooRequest bambooRequest;
-
- private Builder() {
-
- }
-
-
- public Builder bambooRequest(BambooRequest bambooRequest) {
- this.bambooRequest = bambooRequest;
- return this;
- }
-
- public ConnectPointContext build() {
- return new ConnectPointContext(bambooRequest);
- }
- }
-
-
- public static ConnectPointContext getContextLocal() {
- return contextLocal.get();
- }
-
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/DefaultRibbonConnectionPoint.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/DefaultRibbonConnectionPoint.java
deleted file mode 100644
index 2712a19f..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/DefaultRibbonConnectionPoint.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package cn.springcloud.bamboo;
-
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-public class DefaultRibbonConnectionPoint implements BambooRibbonConnectionPoint, ApplicationContextAware {
-
- private RequestVersionExtractor versionExtractor;
- private ApplicationContext ctx;
- private static ThreadLocal> curRequestTriggers = new ThreadLocal();
- private List requestTriggerList;
-
- public DefaultRibbonConnectionPoint(RequestVersionExtractor versionExtractor) {
- this(versionExtractor, null);
- }
-
- public DefaultRibbonConnectionPoint(RequestVersionExtractor versionExtractor, List requestTriggerList) {
- this.versionExtractor = versionExtractor;
- this.requestTriggerList = requestTriggerList;
- }
-
- @Override
- public void executeConnectPoint(ConnectPointContext connectPointContext) {
- ConnectPointContext.contextLocal.set(connectPointContext);
- BambooRequest bambooRequest = connectPointContext.getBambooRequest();
- String requestVersion = versionExtractor.extractVersion(bambooRequest);
- BambooRequestContext.initRequestContext(bambooRequest, requestVersion);
- executeBeforeReuqestTrigger();
- }
-
- @Override
- public void shutdownconnectPoint() {
- try {
- executeAfterReuqestTrigger();
- } catch (Exception e) {
- ConnectPointContext.getContextLocal().setExcption(e);
- } finally {
- curRequestTriggers.remove();
- ConnectPointContext.contextLocal.remove();
- BambooRequestContext.shutdownRequestContext();
- }
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.ctx = applicationContext;
- }
-
- private List chooseRequestTrigger() {
- if (curRequestTriggers.get() != null) {
- return curRequestTriggers.get();
- }
-
- Collection triggers;
-
- if (requestTriggerList != null) {
- triggers = requestTriggerList;
- } else {
- triggers = ctx.getBeansOfType(LoadBalanceRequestTrigger.class).values();
- }
-
- List requestTriggers = new ArrayList<>();
- triggers.forEach(trigger -> {
- if (trigger.shouldExecute()) {
- requestTriggers.add(trigger);
- }
- });
- curRequestTriggers.set(requestTriggers);
- return requestTriggers;
- }
-
-
- protected void executeBeforeReuqestTrigger() {
- ConnectPointContext connectPointContext = ConnectPointContext.getContextLocal();
- List requestTriggers = chooseRequestTrigger();
- if (requestTriggers != null && !requestTriggers.isEmpty()) {
- requestTriggers.forEach(trigger -> trigger.before(connectPointContext));
- }
- }
-
-
- protected void executeAfterReuqestTrigger() {
- ConnectPointContext connectPointContext = ConnectPointContext.getContextLocal();
- List requestTriggers = chooseRequestTrigger();
- if (requestTriggers != null && !requestTriggers.isEmpty()) {
- requestTriggers.forEach(trigger -> trigger.after(connectPointContext));
- }
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/LoadBalanceRequestTrigger.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/LoadBalanceRequestTrigger.java
deleted file mode 100644
index 00e2c318..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/LoadBalanceRequestTrigger.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package cn.springcloud.bamboo;
-
-/**
- * Ribbon请求的触发器,在ribbon请求发起时, 会被执行
- */
-public interface LoadBalanceRequestTrigger {
-
-
- /**
- * 判断是否需要执行的方法
- *
- * @return boolean
- */
- boolean shouldExecute();
-
-
- /**
- * 请求之前执行
- *
- * @param connectPointContext 连接点上下文
- */
- void before(ConnectPointContext connectPointContext);
-
-
- /**
- * 请求完成之后执行
- * 如果出现异常,该方法依然会被执行
- *
- * @param connectPointContext 连接点上下文
- */
- void after(ConnectPointContext connectPointContext);
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/RequestBody.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/RequestBody.java
deleted file mode 100644
index 0e928b9a..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/RequestBody.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package cn.springcloud.bamboo;
-
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * 请求的body
- * 如读取 {@link HttpServletRequest#getInputStream()}获取得到的内容
- */
-public interface RequestBody {
-
-
- /**
- * 返回byte数组类型的request body
- * @return 请求体内容
- */
- byte[] getBody();
-
-
- /**
- * 返回字符串类型的request body
- * @return 请求体内容
- */
- String getBodyString();
-
-
- /**
- * 将byte数组类型的request body转换成字符串类型的request body并返回
- * @param charset 字符串编码
- * @return 请求体内容
- */
- String getBodyString(String charset);
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/RequestVersionExtractor.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/RequestVersionExtractor.java
deleted file mode 100644
index 0653ac47..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/RequestVersionExtractor.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package cn.springcloud.bamboo;
-
-
-/**
- * 这个接口负责获取请求需要访问的目标接口的版本。
- * 比如有些接口版本是放在路径上,如:/v1/api/test/get。也有放在uri参数中:/api/test/get?v=1。
- * 也有可能放到header中,所以在bamboo抽象出来一个接口, 具体的实现由开发者根据业务去实现。
- */
-public interface RequestVersionExtractor {
-
- String extractVersion(BambooRequest bambooRequest);
-
-
- /**
- * 默认从querystring中获取:/api/test?version=1
- */
- class Default implements RequestVersionExtractor {
- private static final String VERSION = "version";
-
- @Override
- public String extractVersion(BambooRequest bambooRequest) {
- return bambooRequest.getParams().getFirst(VERSION);
- }
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/BambooAutoConfiguration.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/BambooAutoConfiguration.java
deleted file mode 100644
index 93e78fdb..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/BambooAutoConfiguration.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package cn.springcloud.bamboo.autoconfig;
-
-import cn.springcloud.bamboo.*;
-import cn.springcloud.bamboo.autoconfig.properties.BambooProperties;
-import cn.springcloud.bamboo.feign.config.BambooFeignConfiguration;
-import cn.springcloud.bamboo.ribbon.BambooClientHttpRequestIntercptor;
-import cn.springcloud.bamboo.ribbon.EurekaServerExtractor;
-import cn.springcloud.bamboo.zuul.config.BambooZuulConfiguration;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.cloud.client.loadbalancer.LoadBalanced;
-import org.springframework.cloud.netflix.ribbon.RibbonClients;
-import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
-import org.springframework.core.annotation.Order;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Created by saleson on 2017/11/9.
- */
-@Configuration
-@EnableConfigurationProperties({BambooProperties.class})
-@AutoConfigureBefore({BambooFeignConfiguration.class, BambooZuulConfiguration.class})
-@Import(BambooWebConfiguration.class)
-//@RibbonClients(defaultConfiguration = {BambooExtConfigration.class})
-@RibbonClients(defaultConfiguration = BambooRibbonClientsConfiguration.class)
-public class BambooAutoConfiguration {
-
-
- public static class UnUseBambooIRule {
-
- }
-
-
-// @Autowired(required = false)
-// private IClientConfig config;
-
- @Autowired
- private SpringClientFactory springClientFactory;
- @Autowired
- private BambooProperties bambooProperties;
-
-
- @Bean
- @LoadBalanced
- public RestTemplate restTemplate() {
- RestTemplate restTemplate = new RestTemplate();
- restTemplate.getInterceptors().add(new BambooClientHttpRequestIntercptor(bambooProperties));
- return restTemplate;
- }
-
- @Bean
- @ConditionalOnMissingBean
- public EurekaServerExtractor eurekaServerExtractor() {
- return new EurekaServerExtractor(springClientFactory);
- }
-
-
-// @Bean
-// @ConditionalOnMissingBean(value = {BambooAutoConfiguration.UnUseBambooIRule.class})
-// public IRule ribbonRule() {
-// BambooZoneAvoidanceRule rule = new BambooZoneAvoidanceRule();
-// rule.initWithNiwsConfig(config);
-// return rule;
-// }
-
- @Bean
- @ConditionalOnMissingBean
- public RequestVersionExtractor requestVersionExtractor() {
- return new RequestVersionExtractor.Default();
- }
-
-
- @Bean
- @ConditionalOnMissingBean
- public BambooRibbonConnectionPoint bambooRibbonConnectionPoint(
- RequestVersionExtractor requestVersionExtractor,
- @Autowired(required = false) List requestTriggerList) {
- if (requestTriggerList != null) {
- requestTriggerList = Collections.EMPTY_LIST;
- }
- return new DefaultRibbonConnectionPoint(requestVersionExtractor, requestTriggerList);
- }
-
- @Bean
- @Order(value = BambooConstants.INITIALIZING_ORDER)
- public BambooInitializingBean bambooInitializingBean() {
- return new BambooInitializingBean();
- }
-
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/BambooRibbonClientsConfiguration.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/BambooRibbonClientsConfiguration.java
deleted file mode 100644
index 53f14528..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/BambooRibbonClientsConfiguration.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package cn.springcloud.bamboo.autoconfig;
-
-
-import cn.springcloud.bamboo.ribbon.loadbalancer.BambooZoneAvoidanceRule;
-import com.netflix.client.config.IClientConfig;
-import com.netflix.loadbalancer.IRule;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class BambooRibbonClientsConfiguration {
-
- @Autowired(required = false)
- private IClientConfig config;
-
- @Bean
- @ConditionalOnMissingBean(value = {BambooAutoConfiguration.UnUseBambooIRule.class})
- public IRule ribbonRule() {
- BambooZoneAvoidanceRule rule = new BambooZoneAvoidanceRule();
- rule.initWithNiwsConfig(config);
- return rule;
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/BambooWebConfiguration.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/BambooWebConfiguration.java
deleted file mode 100644
index d013807e..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/BambooWebConfiguration.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package cn.springcloud.bamboo.autoconfig;
-
-import cn.springcloud.bamboo.web.IpKeepInterceptor;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
-
-@Configuration
-public class BambooWebConfiguration extends WebMvcConfigurerAdapter {
-
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(new IpKeepInterceptor());
- super.addInterceptors(registry);
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/properties/BambooProperties.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/properties/BambooProperties.java
deleted file mode 100644
index 08bf2927..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/autoconfig/properties/BambooProperties.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package cn.springcloud.bamboo.autoconfig.properties;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-@ConfigurationProperties("multiversion")
-public class BambooProperties {
-
- private BambooRequest bambooRequest = new BambooRequest();
-
- public BambooRequest getBambooRequest() {
- return bambooRequest;
- }
-
- public void setBambooRequest(BambooRequest bambooRequest) {
- this.bambooRequest = bambooRequest;
- }
-
- public static class BambooRequest {
-
- private boolean loadBody = false;
-
- /**
- * 是否读取并加载请求的body数据
- *
- * @return
- */
- public boolean isLoadBody() {
- return loadBody;
- }
-
- public void setLoadBody(boolean loadBody) {
- this.loadBody = loadBody;
- }
- }
-
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/feign/BambooFeignClient.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/feign/BambooFeignClient.java
deleted file mode 100644
index 9659a7c4..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/feign/BambooFeignClient.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package cn.springcloud.bamboo.feign;
-
-import cn.springcloud.bamboo.BambooAppContext;
-import cn.springcloud.bamboo.BambooRequest;
-import cn.springcloud.bamboo.ConnectPointContext;
-import cn.springcloud.bamboo.autoconfig.properties.BambooProperties;
-import cn.springcloud.bamboo.utils.WebUtils;
-import cn.springcloud.bamboo.web.RequestIpKeeper;
-import feign.Client;
-import feign.Request;
-import feign.Response;
-
-import java.io.IOException;
-import java.net.URI;
-
-/**
- * 主要作用是用来获取request的相关信息,为后面的路由提供数据基础。
- */
-public class BambooFeignClient implements Client {
-
- private Client delegate;
- private BambooProperties bambooProperties;
-
- public BambooFeignClient(BambooProperties bambooProperties, Client delegate) {
- this.delegate = delegate;
- this.bambooProperties = bambooProperties;
- }
-
- @Override
- public Response execute(Request request, Request.Options options) throws IOException {
- URI uri = URI.create(request.url());
- BambooRequest.Builder builder = BambooRequest.builder()
- .serviceId(uri.getHost())
- .uri(uri.getPath())
- .ip(RequestIpKeeper.getRequestIp())
- .addMultiParams(WebUtils.getQueryParams(uri.getQuery()));
- if(bambooProperties.getBambooRequest().isLoadBody()){
- builder.requestBody(request.body());
- }
-
-
- request.headers().entrySet().forEach(entry ->{
- for (String v : entry.getValue()) {
- builder.addHeader(entry.getKey(), v);
- }
- });
-
- ConnectPointContext connectPointContext = ConnectPointContext.builder().bambooRequest(builder.build()).build();
-
- try {
- BambooAppContext.getBambooRibbonConnectionPoint().executeConnectPoint(connectPointContext);
- return delegate.execute(request, options);
- }finally {
- BambooAppContext.getBambooRibbonConnectionPoint().shutdownconnectPoint();
- }
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/feign/config/BambooFeignClientsConfiguration.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/feign/config/BambooFeignClientsConfiguration.java
deleted file mode 100644
index fd591974..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/feign/config/BambooFeignClientsConfiguration.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package cn.springcloud.bamboo.feign.config;
-
-import cn.springcloud.bamboo.autoconfig.properties.BambooProperties;
-import cn.springcloud.bamboo.feign.BambooFeignClient;
-import feign.Client;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class BambooFeignClientsConfiguration {
-
-
- @Autowired
- private Client feignClient;
- @Autowired
- private BambooProperties bambooProperties;
-
- @Bean
- public Client bambooFeignClient() {
- return new BambooFeignClient(bambooProperties, feignClient);
- }
-
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/feign/config/BambooFeignConfiguration.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/feign/config/BambooFeignConfiguration.java
deleted file mode 100644
index c2a23416..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/feign/config/BambooFeignConfiguration.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package cn.springcloud.bamboo.feign.config;
-
-import com.netflix.loadbalancer.ILoadBalancer;
-import feign.Feign;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.cloud.netflix.feign.EnableFeignClients;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Created by saleson on 2017/11/9.
- */
-@ConditionalOnClass(value = {ILoadBalancer.class, Feign.class})
-@Configuration
-@EnableFeignClients(defaultConfiguration = {BambooFeignClientsConfiguration.class})
-public class BambooFeignConfiguration {
-
-
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/BambooClientHttpRequestIntercptor.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/BambooClientHttpRequestIntercptor.java
deleted file mode 100644
index 1c9cc1a1..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/BambooClientHttpRequestIntercptor.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package cn.springcloud.bamboo.ribbon;
-
-import cn.springcloud.bamboo.BambooAppContext;
-import cn.springcloud.bamboo.BambooRequest;
-import cn.springcloud.bamboo.ConnectPointContext;
-import cn.springcloud.bamboo.autoconfig.properties.BambooProperties;
-import cn.springcloud.bamboo.utils.WebUtils;
-import cn.springcloud.bamboo.web.RequestIpKeeper;
-import org.springframework.http.HttpRequest;
-import org.springframework.http.client.ClientHttpRequestExecution;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
-import org.springframework.http.client.ClientHttpResponse;
-
-import java.io.IOException;
-import java.net.URI;
-
-
-/**
- * 用于@LoadBalance 标记的 RestTemplate,主要作用是用来获取request的相关信息,为后面的路由提供数据基础。
- */
-public class BambooClientHttpRequestIntercptor implements ClientHttpRequestInterceptor {
-
- private BambooProperties bambooProperties;
-
- public BambooClientHttpRequestIntercptor(BambooProperties bambooProperties) {
- this.bambooProperties = bambooProperties;
- }
-
- @Override
- public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
-
- URI uri = request.getURI();
- BambooRequest.Builder bambooReqBuilder = BambooRequest.builder()
- .serviceId(uri.getHost())
- .uri(uri.getPath())
- .ip(RequestIpKeeper.getRequestIp())
- .addMultiHeaders(request.getHeaders())
- .addMultiParams(WebUtils.getQueryParams(uri.getQuery()));
-
- if(bambooProperties.getBambooRequest().isLoadBody()) {
- bambooReqBuilder.requestBody(body);
- }
-
- BambooRequest bambooRequest = bambooReqBuilder.build();
-
- ConnectPointContext connectPointContext = ConnectPointContext.builder().bambooRequest(bambooRequest).build();
- try {
- BambooAppContext.getBambooRibbonConnectionPoint().executeConnectPoint(connectPointContext);
- return execution.execute(request, body);
- } finally {
- BambooAppContext.getBambooRibbonConnectionPoint().shutdownconnectPoint();
- }
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/EurekaServerExtractor.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/EurekaServerExtractor.java
deleted file mode 100644
index e76f3420..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/EurekaServerExtractor.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package cn.springcloud.bamboo.ribbon;
-
-import com.netflix.loadbalancer.Server;
-import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
-import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
-import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
-
-import java.util.Map;
-
-/**
- * 获取服务实例的相关信息
- */
-public class EurekaServerExtractor {
-
-
- private SpringClientFactory clientFactory;
-
- public EurekaServerExtractor(SpringClientFactory clientFactory) {
- this.clientFactory = clientFactory;
- }
-
- public ServerIntrospector serverIntrospector(String serviceId) {
- ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,
- ServerIntrospector.class);
- if (serverIntrospector == null) {
- serverIntrospector = new DefaultServerIntrospector();
- }
- return serverIntrospector;
- }
-
- /**
- * 获取实例的metadata信息
- *
- * @param serviceId 服务id
- * @param server ribbon服务器(服务实例)
- * @return 服务实例的metadata信息
- */
- public Map getServerMetadata(String serviceId, Server server) {
- return serverIntrospector(serviceId).getMetadata(server);
- }
-
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/loadbalancer/BambooApiVersionPredicate.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/loadbalancer/BambooApiVersionPredicate.java
deleted file mode 100644
index bc226f47..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/loadbalancer/BambooApiVersionPredicate.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package cn.springcloud.bamboo.ribbon.loadbalancer;
-
-import cn.springcloud.bamboo.BambooRequestContext;
-import com.netflix.loadbalancer.AbstractServerPredicate;
-import com.netflix.loadbalancer.PredicateKey;
-import org.apache.commons.lang.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.Map;
-
-/**
- * Created by saleson on 2017/11/10.
- */
-public class BambooApiVersionPredicate extends AbstractServerPredicate {
-
-
- public BambooApiVersionPredicate(BambooZoneAvoidanceRule rule) {
- super(rule);
- }
-
- @Override
- public boolean apply(PredicateKey input) {
- BambooLoadBalancerKey loadBalancerKey = getBambooLoadBalancerKey(input);
- if (loadBalancerKey != null && !StringUtils.isEmpty(loadBalancerKey.getApiVersion())) {
- Map serverMetadata = ((BambooZoneAvoidanceRule) this.rule)
- .getServerMetadata(loadBalancerKey.getServiceId(), input.getServer());
- String versions = serverMetadata.get("versions");
- return matchVersion(versions, loadBalancerKey.getApiVersion());
- }
- return true;
- }
-
- private BambooLoadBalancerKey getBambooLoadBalancerKey(PredicateKey input) {
- /*if (input.getLoadBalancerKey() != null && input.getLoadBalancerKey() instanceof BambooLoadBalancerKey) {
- return (BambooLoadBalancerKey) input.getLoadBalancerKey();
- } else if (BambooRequestContext.instance() != null) {
- return BambooRequestContext.instance().getLoadBalancerKey();
- } else */if(BambooRequestContext.currentRequestCentxt()!=null){
- BambooRequestContext bambooRequestContext = BambooRequestContext.currentRequestCentxt();
- String apiVersion = bambooRequestContext.getApiVersion();
- if(!StringUtils.isEmpty(apiVersion)){
- return BambooLoadBalancerKey.builder().apiVersion(apiVersion)
- .serviceId(bambooRequestContext.getServiceId()).build();
- }
- }
- return null;
- }
-
- /**
- * 匹配api version
- * @param serverVersions
- * @param apiVersion
- * @return
- */
- private boolean matchVersion(String serverVersions, String apiVersion) {
- if (StringUtils.isEmpty(serverVersions)) {
- return false;
- }
- String[] versions = StringUtils.split(serverVersions, ",");
- return ArrayUtils.contains(versions, apiVersion);
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/loadbalancer/BambooLoadBalancerKey.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/loadbalancer/BambooLoadBalancerKey.java
deleted file mode 100644
index 05296998..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/loadbalancer/BambooLoadBalancerKey.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package cn.springcloud.bamboo.ribbon.loadbalancer;
-
-/**
- * Created by saleson on 2017/11/9.
- */
-public class BambooLoadBalancerKey {
-
-
- private String serviceId;
- private String apiVersion;
-
-
- private BambooLoadBalancerKey() {
-
- }
-
-
- public String getApiVersion() {
- return apiVersion;
- }
-
- public String getServiceId() {
- return serviceId;
- }
-
- private void setApiVersion(String apiVersion) {
- this.apiVersion = apiVersion;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
-
- public static class Builder {
-
- private BambooLoadBalancerKey build = new BambooLoadBalancerKey();
-
- public Builder apiVersion(String apiVersion) {
- build.apiVersion = apiVersion;
- return this;
- }
-
- public Builder serviceId(String serviceId) {
- build.serviceId = serviceId;
- return this;
- }
-
- public BambooLoadBalancerKey build() {
- return build;
- }
-
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/loadbalancer/BambooZoneAvoidanceRule.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/loadbalancer/BambooZoneAvoidanceRule.java
deleted file mode 100644
index 15bb835d..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/ribbon/loadbalancer/BambooZoneAvoidanceRule.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package cn.springcloud.bamboo.ribbon.loadbalancer;
-
-import cn.springcloud.bamboo.BambooAppContext;
-import com.netflix.loadbalancer.AbstractServerPredicate;
-import com.netflix.loadbalancer.CompositePredicate;
-import com.netflix.loadbalancer.Server;
-import com.netflix.loadbalancer.ZoneAvoidanceRule;
-
-import java.util.Map;
-
-/**
- * Created by saleson on 2017/11/9.
- */
-public class BambooZoneAvoidanceRule extends ZoneAvoidanceRule {
-
- protected CompositePredicate bambooCompositePredicate;
-
- public BambooZoneAvoidanceRule() {
- super();
- BambooApiVersionPredicate apiVersionPredicate = new BambooApiVersionPredicate(this);
- bambooCompositePredicate = CompositePredicate.withPredicates(super.getPredicate(),
- apiVersionPredicate).build();
- }
-
- @Override
- public AbstractServerPredicate getPredicate() {
- return bambooCompositePredicate;
- }
-
-
- public Map getServerMetadata(String serviceId, Server server) {
- return BambooAppContext.getEurekaServerExtractor().getServerMetadata(serviceId, server);
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/web/IpKeepInterceptor.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/web/IpKeepInterceptor.java
deleted file mode 100644
index ef37e113..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/web/IpKeepInterceptor.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package cn.springcloud.bamboo.web;
-
-import cn.springcloud.bamboo.utils.WebUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-
-/**
- * 获取请求的真实ip,并保存到当前线程中。
- * @see RequestIpKeeper
- */
-public class IpKeepInterceptor extends HandlerInterceptorAdapter {
-
- private static final Logger log = LoggerFactory.getLogger(IpKeepInterceptor.class);
-
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- //获取ip
- String ip = WebUtils.getIpAddr(request);
- //保存
- RequestIpKeeper.instance().setIp(ip);
- return super.preHandle(request, response, handler);
- }
-
-
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- //清除ThreadLocal
- RequestIpKeeper.instance().clear();
- super.afterCompletion(request, response, handler, ex);
- }
-
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/web/RequestIpKeeper.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/web/RequestIpKeeper.java
deleted file mode 100644
index fad61f48..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/web/RequestIpKeeper.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package cn.springcloud.bamboo.web;
-
-import cn.springcloud.bamboo.BambooAppContext;
-import org.apache.commons.lang3.StringUtils;
-
-public final class RequestIpKeeper {
-
- private static final ThreadLocal ipLocal = new ThreadLocal<>();
-
-
- private static RequestIpKeeper INSTANCE = new RequestIpKeeper();
-
- private RequestIpKeeper() {
-
- }
-
- public static RequestIpKeeper instance() {
- return INSTANCE;
- }
-
-
- void setIp(String ip) {
- ipLocal.set(ip);
- }
-
-
- public String getIp() {
- return ipLocal.get();
- }
-
-
- public void clear() {
- ipLocal.remove();
- }
-
-
- public static String getRequestIp() {
- String ip = instance().getIp();
- if (StringUtils.isEmpty(ip)) {
- ip = BambooAppContext.getLocalIp();
- }
- return ip;
- }
-
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/zuul/config/BambooZuulConfiguration.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/zuul/config/BambooZuulConfiguration.java
deleted file mode 100644
index 6ca5827b..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/zuul/config/BambooZuulConfiguration.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package cn.springcloud.bamboo.zuul.config;
-
-import cn.springcloud.bamboo.autoconfig.properties.BambooProperties;
-import cn.springcloud.bamboo.zuul.filter.BambooPostZuulFilter;
-import cn.springcloud.bamboo.zuul.filter.BambooPreZuulFilter;
-import com.netflix.zuul.http.ZuulServlet;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@ConditionalOnClass(value = ZuulServlet.class)
-public class BambooZuulConfiguration {
- @Autowired
- private BambooProperties bambooProperties;
-
- @Bean
- public BambooPreZuulFilter bambooPreZuulFilter(){
- return new BambooPreZuulFilter(bambooProperties);
- }
-
- @Bean
- public BambooPostZuulFilter bambooPostZuulFilter(){
- return new BambooPostZuulFilter();
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/zuul/filter/BambooPostZuulFilter.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/zuul/filter/BambooPostZuulFilter.java
deleted file mode 100644
index 06f1bfbe..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/zuul/filter/BambooPostZuulFilter.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package cn.springcloud.bamboo.zuul.filter;
-
-import cn.springcloud.bamboo.BambooAppContext;
-import com.netflix.zuul.ZuulFilter;
-import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
-
-/**
- * 做一些善后工作。比如删除BambooRequestContext在ThreadLocal中的信息。
- */
-public class BambooPostZuulFilter extends ZuulFilter {
- @Override
- public String filterType() {
- return FilterConstants.POST_TYPE;
- }
-
- @Override
- public int filterOrder() {
- return 0;
- }
-
- @Override
- public boolean shouldFilter() {
- return true;
- }
-
- @Override
- public Object run() {
-// BambooRequestContext.shutdownRequestContext();
- BambooAppContext.getBambooRibbonConnectionPoint().shutdownconnectPoint();
- return null;
- }
-}
diff --git a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/zuul/filter/BambooPreZuulFilter.java b/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/zuul/filter/BambooPreZuulFilter.java
deleted file mode 100755
index 819f2091..00000000
--- a/spring-cloud-bamboo/src/main/java/cn/springcloud/bamboo/zuul/filter/BambooPreZuulFilter.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package cn.springcloud.bamboo.zuul.filter;
-
-import cn.springcloud.bamboo.BambooAppContext;
-import cn.springcloud.bamboo.BambooRequest;
-import cn.springcloud.bamboo.ConnectPointContext;
-import cn.springcloud.bamboo.autoconfig.properties.BambooProperties;
-import com.netflix.util.Pair;
-import com.netflix.zuul.ZuulFilter;
-import com.netflix.zuul.context.RequestContext;
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
-
-import javax.servlet.http.HttpServletRequest;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.Enumeration;
-import java.util.stream.Collectors;
-
-/**
- * 主要作用是用来获取request的相关信息,为后面的路由提供数据基础。
- */
-public class BambooPreZuulFilter extends ZuulFilter {
-
- private static final Logger log = LoggerFactory.getLogger(BambooPreZuulFilter.class);
-
- private BambooProperties bambooProperties;
-
- public BambooPreZuulFilter(BambooProperties bambooProperties) {
- this.bambooProperties = bambooProperties;
- }
-
- @Override
- public String filterType() {
- return FilterConstants.PRE_TYPE;
- }
-
- @Override
- public int filterOrder() {
- return 10000;
- }
-
- @Override
- public boolean shouldFilter() {
- return true;
- }
-
- @Override
- public Object run() {
- RequestContext context = RequestContext.getCurrentContext();
- BambooRequest.Builder builder = BambooRequest.builder()
- .serviceId((String)context.get(FilterConstants.SERVICE_ID_KEY))
- .uri((String)context.get(FilterConstants.REQUEST_URI_KEY))
- .ip(context.getZuulRequestHeaders().get(FilterConstants.X_FORWARDED_FOR_HEADER.toLowerCase()))
- .addMultiParams(context.getRequestQueryParams())
- .addHeaders(context.getZuulRequestHeaders());
-
- // add http server request header
- HttpServletRequest servletRequest = context.getRequest();
- Enumeration headerNames = servletRequest.getHeaderNames();
- while(headerNames.hasMoreElements()){
- String headerName = headerNames.nextElement();
- builder.addHeader(headerName, servletRequest.getHeader(headerName));
- }
-
- if(bambooProperties.getBambooRequest().isLoadBody()) {
- try {
- BufferedReader reader = new BufferedReader(new InputStreamReader(context.getRequest().getInputStream()));
- byte[] reqBody = IOUtils.toByteArray(reader);
- builder.requestBody(reqBody);
- } catch (IOException e) {
- String errorMsg = "获取request body出现异常";
- log.error(errorMsg, e);
- throw new RuntimeException(errorMsg, e);
- }
- }
-
- ConnectPointContext connectPointContext = ConnectPointContext.builder().bambooRequest(builder.build()).build();
-
- BambooAppContext.getBambooRibbonConnectionPoint().executeConnectPoint(connectPointContext);
- return null;
- }
-
-}
diff --git a/spring-cloud-gray-client-netflix/pom.xml b/spring-cloud-gray-client-netflix/pom.xml
new file mode 100644
index 00000000..97a17ae4
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/pom.xml
@@ -0,0 +1,101 @@
+
+
+
+ spring-cloud-gray
+ cn.springcloud.gray
+ 2.0.0
+
+ 4.0.0
+
+ spring-cloud-gray-client-netflix
+
+
+
+ cn.springcloud.gray
+ spring-cloud-gray-utils
+
+
+ cn.springcloud.gray
+ spring-cloud-gray-core
+
+
+ cn.springcloud.gray
+ spring-cloud-gray-client
+
+
+
+ org.springframework
+ spring-web
+
+
+ io.github.openfeign
+ feign-hystrix
+
+
+ io.github.openfeign
+ feign-core
+
+
+ org.springframework.cloud
+ spring-cloud-netflix-core
+
+
+ org.springframework.cloud
+ spring-cloud-context
+
+
+
+ org.springframework.cloud
+ spring-cloud-netflix-eureka-client
+
+
+ com.netflix.eureka
+ eureka-client
+
+
+ com.netflix.ribbon
+ ribbon-eureka
+
+
+ com.netflix.ribbon
+ ribbon-loadbalancer
+
+
+ com.netflix.ribbon
+ ribbon-core
+
+
+ com.netflix.zuul
+ zuul-core
+
+
+ org.projectlombok
+ lombok
+
+
+ javax.servlet
+ javax.servlet-api
+
+
+ org.slf4j
+ slf4j-api
+
+
+ commons-io
+ commons-io
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ com.google.guava
+ guava
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/EurekaServerExplainer.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/EurekaServerExplainer.java
new file mode 100644
index 00000000..5ffd5462
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/EurekaServerExplainer.java
@@ -0,0 +1,50 @@
+package cn.springcloud.gray.client.netflix;
+
+import cn.springcloud.gray.servernode.ServerExplainer;
+import cn.springcloud.gray.servernode.ServerSpec;
+import com.netflix.loadbalancer.Server;
+import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
+import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
+import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
+
+import java.util.Map;
+
+public class EurekaServerExplainer implements ServerExplainer {
+
+ private SpringClientFactory springClientFactory;
+
+ public EurekaServerExplainer(SpringClientFactory springClientFactory) {
+ this.springClientFactory = springClientFactory;
+ }
+
+ @Override
+ public ServerSpec apply(Server server) {
+ Map metadata = getServerMetadata(server.getMetaInfo().getServiceIdForDiscovery(), server);
+ return ServerSpec.builder().instanceId(server.getMetaInfo().getInstanceId())
+ .serviceId(server.getMetaInfo().getServiceIdForDiscovery())
+ .metadatas(metadata).build();
+ }
+
+ public ServerIntrospector serverIntrospector(String serviceId) {
+ if (springClientFactory == null) {
+ return new DefaultServerIntrospector();
+ }
+ ServerIntrospector serverIntrospector = this.springClientFactory.getInstance(serviceId,
+ ServerIntrospector.class);
+ if (serverIntrospector == null) {
+ serverIntrospector = new DefaultServerIntrospector();
+ }
+ return serverIntrospector;
+ }
+
+ /**
+ * 获取实例的metadata信息
+ *
+ * @param serviceId 服务id
+ * @param server ribbon服务器(服务实例)
+ * @return 服务实例的metadata信息
+ */
+ public Map getServerMetadata(String serviceId, Server server) {
+ return serverIntrospector(serviceId).getMetadata(server);
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/GrayClientHolder.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/GrayClientHolder.java
new file mode 100644
index 00000000..3635a545
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/GrayClientHolder.java
@@ -0,0 +1,37 @@
+package cn.springcloud.gray.client.netflix;
+
+import cn.springcloud.gray.GrayManager;
+import cn.springcloud.gray.servernode.ServerExplainer;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import com.netflix.loadbalancer.Server;
+
+public class GrayClientHolder {
+
+ private static GrayManager grayManager;
+ private static RequestLocalStorage requestLocalStorage;
+ private static ServerExplainer serverExplainer;
+
+ public static GrayManager getGrayManager() {
+ return grayManager;
+ }
+
+ public static void setGrayManager(GrayManager grayManager) {
+ GrayClientHolder.grayManager = grayManager;
+ }
+
+ public static RequestLocalStorage getRequestLocalStorage() {
+ return requestLocalStorage;
+ }
+
+ public static void setRequestLocalStorage(RequestLocalStorage requestLocalStorage) {
+ GrayClientHolder.requestLocalStorage = requestLocalStorage;
+ }
+
+ public static ServerExplainer getServerExplainer() {
+ return serverExplainer;
+ }
+
+ public static void setServerExplainer(ServerExplainer serverExplainer) {
+ GrayClientHolder.serverExplainer = serverExplainer;
+ }
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientEurekaAutoConfiguration.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/configuration/GrayClientEurekaAutoConfiguration.java
similarity index 56%
rename from spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientEurekaAutoConfiguration.java
rename to spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/configuration/GrayClientEurekaAutoConfiguration.java
index 200f7080..1d74e9ff 100644
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientEurekaAutoConfiguration.java
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/configuration/GrayClientEurekaAutoConfiguration.java
@@ -1,11 +1,13 @@
-package cn.springcloud.gray.client.config;
+package cn.springcloud.gray.client.netflix.configuration;
import cn.springcloud.gray.InstanceLocalInfo;
+import cn.springcloud.gray.client.netflix.EurekaServerExplainer;
import com.netflix.discovery.EurekaClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
+import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -17,15 +19,29 @@
@ConditionalOnBean(EurekaClient.class)
public class GrayClientEurekaAutoConfiguration {
+
+ @Autowired
+ private SpringClientFactory springClientFactory;
+
@Bean
@ConditionalOnMissingBean
public InstanceLocalInfo instanceLocalInfo(@Autowired EurekaRegistration registration) {
String instanceId = registration.getInstanceConfig().getInstanceId();
- InstanceLocalInfo localInfo = new InstanceLocalInfo();
- localInfo.setInstanceId(instanceId);
- localInfo.setServiceId(registration.getServiceId());
- localInfo.setGray(false);
- return localInfo;
+ return InstanceLocalInfo.builder()
+ .instanceId(instanceId)
+ .serviceId(registration.getServiceId())
+ .host(registration.getHost())
+ .port(registration.getPort())
+ .build();
}
+
+
+ @Bean
+ @ConditionalOnMissingBean
+ public EurekaServerExplainer eurekaServerExplainer() {
+ return new EurekaServerExplainer(springClientFactory);
+ }
+
+
}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/configuration/HystrixGrayAutoConfiguration.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/configuration/HystrixGrayAutoConfiguration.java
new file mode 100644
index 00000000..a39727c9
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/configuration/HystrixGrayAutoConfiguration.java
@@ -0,0 +1,61 @@
+package cn.springcloud.gray.client.netflix.configuration;
+
+import cn.springcloud.gray.GrayManager;
+import cn.springcloud.gray.client.netflix.hystrix.HystrixRequestLocalStorage;
+import cn.springcloud.gray.request.GrayHttpTrackInfo;
+import cn.springcloud.gray.request.GrayInfoTracker;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import cn.springcloud.gray.web.GrayTrackFilter;
+import com.netflix.hystrix.HystrixCommand;
+import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
+import feign.hystrix.HystrixFeign;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.List;
+
+@Configuration
+@ConditionalOnClass({HystrixCommand.class, HystrixFeign.class})
+public class HystrixGrayAutoConfiguration {
+
+
+ @Autowired
+ private GrayManager grayManager;
+
+
+ @Bean
+ public RequestLocalStorage requestLocalStorage() {
+ return new HystrixRequestLocalStorage();
+ }
+
+
+ @Bean
+ public GrayTrackFilter grayTrackFilter(
+ RequestLocalStorage requestLocalStorage,
+ List> trackors) {
+ return new GrayTrackFilter(requestLocalStorage, trackors) {
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ if (!HystrixRequestContext.isCurrentThreadInitialized()) {
+ HystrixRequestContext.initializeContext();
+ }
+ try {
+ super.doFilter(request, response, chain);
+ } finally {
+ if (HystrixRequestContext.isCurrentThreadInitialized()) {
+ HystrixRequestContext.getContextForCurrentThread().shutdown();
+ }
+ }
+ }
+ };
+ }
+
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/configuration/NetflixGrayAutoConfiguration.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/configuration/NetflixGrayAutoConfiguration.java
new file mode 100644
index 00000000..e0e29d80
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/configuration/NetflixGrayAutoConfiguration.java
@@ -0,0 +1,31 @@
+package cn.springcloud.gray.client.netflix.configuration;
+
+import cn.springcloud.gray.GrayManager;
+import cn.springcloud.gray.client.netflix.connectionpoint.DefaultRibbonConnectionPoint;
+import cn.springcloud.gray.client.netflix.connectionpoint.RibbonConnectionPoint;
+import cn.springcloud.gray.client.netflix.ribbon.configuration.GrayRibbonClientsConfiguration;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.cloud.netflix.ribbon.RibbonClients;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@RibbonClients(defaultConfiguration = GrayRibbonClientsConfiguration.class)
+public class NetflixGrayAutoConfiguration {
+
+
+ @Autowired
+ private GrayManager grayManager;
+ @Autowired
+ private RequestLocalStorage requestLocalStorage;
+
+
+ @Bean
+ @ConditionalOnMissingBean
+ public RibbonConnectionPoint ribbonConnectionPoint() {
+ return new DefaultRibbonConnectionPoint(grayManager, requestLocalStorage);
+ }
+
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/connectionpoint/ConnectPointContext.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/connectionpoint/ConnectPointContext.java
new file mode 100644
index 00000000..1c046810
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/connectionpoint/ConnectPointContext.java
@@ -0,0 +1,35 @@
+package cn.springcloud.gray.client.netflix.connectionpoint;
+
+import cn.springcloud.gray.request.GrayRequest;
+import lombok.*;
+
+
+@Builder
+@Getter
+@AllArgsConstructor
+@NoArgsConstructor
+public class ConnectPointContext {
+
+ private static final ThreadLocal contextLocal = new ThreadLocal<>();
+
+
+ private GrayRequest grayRequest;
+ @Setter
+ private Throwable throwable;
+
+ private String interceptroType;
+
+
+ static void setContextLocal(ConnectPointContext cxt) {
+ contextLocal.set(cxt);
+ }
+
+ static void removeContextLocal() {
+ contextLocal.remove();
+ }
+
+
+ public static ConnectPointContext getContextLocal() {
+ return contextLocal.get();
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/connectionpoint/DefaultRibbonConnectionPoint.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/connectionpoint/DefaultRibbonConnectionPoint.java
new file mode 100644
index 00000000..df4c4ead
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/connectionpoint/DefaultRibbonConnectionPoint.java
@@ -0,0 +1,60 @@
+package cn.springcloud.gray.client.netflix.connectionpoint;
+
+import cn.springcloud.gray.GrayManager;
+import cn.springcloud.gray.RequestInterceptor;
+import cn.springcloud.gray.request.GrayRequest;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class DefaultRibbonConnectionPoint implements RibbonConnectionPoint {
+
+ private GrayManager grayManager;
+ private RequestLocalStorage requestLocalStorage;
+
+ public DefaultRibbonConnectionPoint(GrayManager grayManager, RequestLocalStorage requestLocalStorage) {
+ this.grayManager = grayManager;
+ this.requestLocalStorage = requestLocalStorage;
+ }
+
+ @Override
+ public void executeConnectPoint(ConnectPointContext connectPointContext) {
+ ConnectPointContext.setContextLocal(connectPointContext);
+ GrayRequest grayRequest = connectPointContext.getGrayRequest();
+ grayRequest.setGrayTrackInfo(requestLocalStorage.getGrayTrackInfo());
+ requestLocalStorage.setGrayRequest(grayRequest);
+
+ List interceptors = grayManager.getRequeestInterceptors(connectPointContext.getInterceptroType());
+ interceptors.forEach(interceptor -> {
+ if (interceptor.shouldIntercept()) {
+ if (!interceptor.pre(grayRequest)) {
+ return;
+ }
+ }
+ });
+
+ }
+
+ @Override
+ public void shutdownconnectPoint(ConnectPointContext connectPointContext) {
+ List interceptors = grayManager.getRequeestInterceptors(connectPointContext.getInterceptroType());
+ interceptors.forEach(interceptor -> {
+ if (interceptor.shouldIntercept()) {
+ if (!interceptor.after(connectPointContext.getGrayRequest())) {
+ return;
+ }
+ }
+ });
+ ConnectPointContext.removeContextLocal();
+ requestLocalStorage.removeGrayTrackInfo();
+ requestLocalStorage.removeGrayRequest();
+
+ }
+
+
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/connectionpoint/RibbonConnectionPoint.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/connectionpoint/RibbonConnectionPoint.java
new file mode 100644
index 00000000..46cd21ee
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/connectionpoint/RibbonConnectionPoint.java
@@ -0,0 +1,32 @@
+package cn.springcloud.gray.client.netflix.connectionpoint;
+
+
+import java.io.IOException;
+
+public interface RibbonConnectionPoint {
+
+
+ default T execute(ConnectPointContext connectPointContext, Supplier supplier) throws IOException {
+ try {
+ executeConnectPoint(connectPointContext);
+ return supplier.get();
+ } catch (Exception e) {
+ connectPointContext.setThrowable(e);
+ throw e;
+ } finally {
+ shutdownconnectPoint(connectPointContext);
+ }
+ }
+
+
+ void executeConnectPoint(ConnectPointContext connectPointContext);
+
+
+ void shutdownconnectPoint(ConnectPointContext connectPointContext);
+
+
+ public interface Supplier {
+ T get() throws IOException;
+ }
+
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/constants/GrayNetflixClientConstants.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/constants/GrayNetflixClientConstants.java
new file mode 100644
index 00000000..450077f8
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/constants/GrayNetflixClientConstants.java
@@ -0,0 +1,9 @@
+package cn.springcloud.gray.client.netflix.constants;
+
+public class GrayNetflixClientConstants {
+
+ public static final String INTERCEPTRO_TYPE_ALL = "all";
+ public static final String INTERCEPTRO_TYPE_FEIGN = "feign";
+ public static final String INTERCEPTRO_TYPE_ZUUL = "zuul";
+ public static final String INTERCEPTRO_TYPE_RESTTEMPLATE = "rest";
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/FeignRequestInterceptor.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/FeignRequestInterceptor.java
new file mode 100644
index 00000000..3f45cd2c
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/FeignRequestInterceptor.java
@@ -0,0 +1,64 @@
+package cn.springcloud.gray.client.netflix.feign;
+
+import cn.springcloud.gray.RequestInterceptor;
+import cn.springcloud.gray.client.netflix.constants.GrayNetflixClientConstants;
+import cn.springcloud.gray.request.GrayHttpTrackInfo;
+import cn.springcloud.gray.request.GrayRequest;
+import cn.springcloud.gray.request.GrayTrackInfo;
+import feign.Request;
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+
+public class FeignRequestInterceptor implements RequestInterceptor {
+ @Override
+ public String interceptroType() {
+ return GrayNetflixClientConstants.INTERCEPTRO_TYPE_FEIGN;
+ }
+
+ @Override
+ public boolean shouldIntercept() {
+ return true;
+ }
+
+ @Override
+ public boolean pre(GrayRequest request) {
+ Request feignRequest = (Request) (request.getAttribute(GrayFeignClient.GRAY_REQUEST_ATTRIBUTE_NAME_FEIGN_REQUEST));
+ GrayHttpTrackInfo grayTrack = (GrayHttpTrackInfo) request.getGrayTrackInfo();
+ if (grayTrack != null) {
+ if (StringUtils.isNotEmpty(grayTrack.getUri())) {
+ feignRequest.headers().put(GrayHttpTrackInfo.GRAY_TRACK_URI, Arrays.asList(grayTrack.getUri()));
+ }
+ if (StringUtils.isNotEmpty(grayTrack.getTraceIp())) {
+ feignRequest.headers().put(GrayHttpTrackInfo.GRAY_TRACK_TRACE_IP, Arrays.asList(grayTrack.getTraceIp()));
+ }
+ if (StringUtils.isNotEmpty(grayTrack.getMethod())) {
+ feignRequest.headers().put(GrayHttpTrackInfo.GRAY_TRACK_METHOD, Arrays.asList(grayTrack.getMethod()));
+ }
+ if (grayTrack.getParameters() != null && !grayTrack.getParameters().isEmpty()) {
+ grayTrack.getParameters().entrySet().forEach(entry -> {
+ String name = new StringBuffer().append(GrayHttpTrackInfo.GRAY_TRACK_PARAMETER_PREFIX)
+ .append(GrayTrackInfo.GRAY_TRACK_SEPARATE)
+ .append(entry.getKey()).toString();
+ feignRequest.headers().put(name, entry.getValue());
+ });
+ }
+ if (grayTrack.getHeaders() != null && !grayTrack.getHeaders().isEmpty()) {
+ grayTrack.getHeaders().entrySet().forEach(entry -> {
+ String name = new StringBuffer().append(GrayHttpTrackInfo.GRAY_TRACK_HEADER_PREFIX)
+ .append(GrayTrackInfo.GRAY_TRACK_SEPARATE)
+ .append(entry.getKey()).toString();
+ feignRequest.headers().put(name, entry.getValue());
+ });
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean after(GrayRequest request) {
+ return true;
+ }
+
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/GrayFeignClient.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/GrayFeignClient.java
new file mode 100644
index 00000000..7107f1a9
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/GrayFeignClient.java
@@ -0,0 +1,75 @@
+package cn.springcloud.gray.client.netflix.feign;
+
+import cn.springcloud.gray.client.config.properties.GrayRequestProperties;
+import cn.springcloud.gray.client.config.properties.GrayTrackProperties;
+import cn.springcloud.gray.client.netflix.connectionpoint.RibbonConnectionPoint;
+import cn.springcloud.gray.client.netflix.connectionpoint.ConnectPointContext;
+import cn.springcloud.gray.client.netflix.constants.GrayNetflixClientConstants;
+import cn.springcloud.gray.request.GrayHttpRequest;
+import cn.springcloud.gray.request.GrayRequest;
+import cn.springcloud.gray.utils.WebUtils;
+import feign.Client;
+import feign.Request;
+import feign.Response;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * 主要作用是用来获取request的相关信息,为后面的路由提供数据基础。
+ */
+public class GrayFeignClient implements Client {
+
+
+ public static final String GRAY_REQUEST_ATTRIBUTE_NAME_FEIGN_REQUEST = "feign.request";
+ public static final String GRAY_REQUEST_ATTRIBUTE_NAME_FEIGN_REQUEST_OPTIONS = "feign.request.options";
+
+
+ private GrayRequestProperties grayRequestProperties;
+ private GrayTrackProperties grayTrackProperties;
+ private RibbonConnectionPoint ribbonConnectionPoint;
+ private Client delegate;
+
+ public GrayFeignClient(
+ Client delegate, RibbonConnectionPoint ribbonConnectionPoint, GrayRequestProperties grayRequestProperties) {
+ this.delegate = delegate;
+ this.grayRequestProperties = grayRequestProperties;
+ this.ribbonConnectionPoint = ribbonConnectionPoint;
+ }
+
+ @Override
+ public Response execute(Request request, Request.Options options) throws IOException {
+ URI uri = URI.create(request.url());
+
+
+ GrayHttpRequest grayRequest = new GrayHttpRequest();
+ grayRequest.setUri(uri);
+ grayRequest.setServiceId(uri.getHost());
+ grayRequest.setParameters(WebUtils.getQueryParams(uri.getQuery()));
+ grayRequest.addHeaders(request.headers());
+ grayRequest.setMethod(request.method());
+
+ if (grayRequestProperties.isLoadBody()) {
+ grayRequest.setBody(request.body());
+ }
+
+ grayRequest.setAttribute(GRAY_REQUEST_ATTRIBUTE_NAME_FEIGN_REQUEST, request);
+ grayRequest.setAttribute(GRAY_REQUEST_ATTRIBUTE_NAME_FEIGN_REQUEST_OPTIONS, options);
+ ConnectPointContext connectPointContext = ConnectPointContext.builder()
+ .interceptroType(GrayNetflixClientConstants.INTERCEPTRO_TYPE_FEIGN)
+ .grayRequest(grayRequest).build();
+ return ribbonConnectionPoint.execute(connectPointContext, () -> delegate.execute(request, options));
+
+// try {
+// ribbonConnectionPoint.executeConnectPoint(connectPointContext);
+// return delegate.execute(request, options);
+// } catch (Exception e) {
+// connectPointContext.setThrowable(e);
+// throw e;
+// } finally {
+// ribbonConnectionPoint.shutdownconnectPoint(connectPointContext);
+// }
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/GrayTrackFeignRequestInterceptor.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/GrayTrackFeignRequestInterceptor.java
new file mode 100644
index 00000000..a7c03f90
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/GrayTrackFeignRequestInterceptor.java
@@ -0,0 +1,49 @@
+package cn.springcloud.gray.client.netflix.feign;
+
+import cn.springcloud.gray.request.*;
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+import org.apache.commons.lang3.StringUtils;
+
+
+public class GrayTrackFeignRequestInterceptor implements RequestInterceptor {
+
+ private RequestLocalStorage requestLocalStorage;
+
+
+ public GrayTrackFeignRequestInterceptor(RequestLocalStorage requestLocalStorage) {
+ this.requestLocalStorage = requestLocalStorage;
+ }
+
+ @Override
+ public void apply(RequestTemplate template) {
+ GrayHttpTrackInfo grayTrack = (GrayHttpTrackInfo) requestLocalStorage.getGrayTrackInfo();
+ if (grayTrack != null) {
+ if (StringUtils.isNotEmpty(grayTrack.getUri())) {
+ template.header(GrayHttpTrackInfo.GRAY_TRACK_URI, grayTrack.getUri());
+ }
+ if (StringUtils.isNotEmpty(grayTrack.getTraceIp())) {
+ template.header(GrayHttpTrackInfo.GRAY_TRACK_TRACE_IP, grayTrack.getTraceIp());
+ }
+ if (StringUtils.isNotEmpty(grayTrack.getMethod())) {
+ template.header(GrayHttpTrackInfo.GRAY_TRACK_METHOD, grayTrack.getMethod());
+ }
+ if (grayTrack.getParameters() != null && !grayTrack.getParameters().isEmpty()) {
+ grayTrack.getParameters().entrySet().forEach(entry -> {
+ String name = new StringBuffer().append(GrayHttpTrackInfo.GRAY_TRACK_PARAMETER_PREFIX)
+ .append(GrayTrackInfo.GRAY_TRACK_SEPARATE)
+ .append(entry.getKey()).toString();
+ template.header(name, entry.getValue());
+ });
+ }
+ if (grayTrack.getHeaders() != null && !grayTrack.getHeaders().isEmpty()) {
+ grayTrack.getHeaders().entrySet().forEach(entry -> {
+ String name = new StringBuffer().append(GrayHttpTrackInfo.GRAY_TRACK_HEADER_PREFIX)
+ .append(GrayTrackInfo.GRAY_TRACK_SEPARATE)
+ .append(entry.getKey()).toString();
+ template.header(name, entry.getValue());
+ });
+ }
+ }
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/configuration/GrayFeignAutoConfiguration.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/configuration/GrayFeignAutoConfiguration.java
new file mode 100644
index 00000000..19d84092
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/configuration/GrayFeignAutoConfiguration.java
@@ -0,0 +1,47 @@
+package cn.springcloud.gray.client.netflix.feign.configuration;
+
+import cn.springcloud.gray.client.netflix.configuration.HystrixGrayAutoConfiguration;
+import cn.springcloud.gray.client.netflix.feign.GrayTrackFeignRequestInterceptor;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import com.netflix.loadbalancer.ILoadBalancer;
+import feign.Feign;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.netflix.feign.EnableFeignClients;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Created by saleson on 2017/11/9.
+ */
+@ConditionalOnClass(value = {ILoadBalancer.class, Feign.class})
+@Configuration
+@EnableFeignClients(defaultConfiguration = {GrayFeignClientsConfiguration.class})
+public class GrayFeignAutoConfiguration {
+
+
+ @Configuration
+ @ConditionalOnProperty(value = "gray.request.track.enabled", matchIfMissing = true)
+ public static class GrayTrackFeignConfiguration {
+
+
+// @Bean
+// public FeignRequestInterceptor feignRequestInterceptor() {
+// return new FeignRequestInterceptor();
+// }
+
+ @Bean
+ public GrayTrackFeignRequestInterceptor grayTrackFeignRequestInterceptor(RequestLocalStorage requestLocalStorage) {
+ return new GrayTrackFeignRequestInterceptor(requestLocalStorage);
+ }
+
+ }
+
+
+ @Configuration
+ @ConditionalOnProperty(value = "feign.hystrix.enabled")
+ public static class HystrixConfiguration extends HystrixGrayAutoConfiguration {
+
+ }
+
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/configuration/GrayFeignClientsConfiguration.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/configuration/GrayFeignClientsConfiguration.java
new file mode 100644
index 00000000..ccec4ad5
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/feign/configuration/GrayFeignClientsConfiguration.java
@@ -0,0 +1,27 @@
+package cn.springcloud.gray.client.netflix.feign.configuration;
+
+import cn.springcloud.gray.client.config.properties.GrayRequestProperties;
+import cn.springcloud.gray.client.netflix.connectionpoint.RibbonConnectionPoint;
+import cn.springcloud.gray.client.netflix.feign.GrayFeignClient;
+import feign.Client;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class GrayFeignClientsConfiguration {
+
+
+ @Autowired
+ private Client feignClient;
+ @Autowired
+ private RibbonConnectionPoint ribbonConnectionPoint;
+ @Autowired
+ private GrayRequestProperties grayRequestProperties;
+
+ @Bean
+ public Client getFeignClient() {
+ return new GrayFeignClient(feignClient, ribbonConnectionPoint, grayRequestProperties);
+ }
+
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/hystrix/HystrixRequestLocalStorage.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/hystrix/HystrixRequestLocalStorage.java
new file mode 100644
index 00000000..8f53a633
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/hystrix/HystrixRequestLocalStorage.java
@@ -0,0 +1,44 @@
+package cn.springcloud.gray.client.netflix.hystrix;
+
+import cn.springcloud.gray.request.GrayRequest;
+import cn.springcloud.gray.request.GrayTrackInfo;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableDefault;
+
+public class HystrixRequestLocalStorage implements RequestLocalStorage {
+
+
+ private static final HystrixRequestVariableDefault grayTrackInfoLocal = new HystrixRequestVariableDefault();
+ private static final HystrixRequestVariableDefault rrayRequestLocal = new HystrixRequestVariableDefault();
+
+
+ @Override
+ public void setGrayTrackInfo(GrayTrackInfo grayTrackInfo) {
+ grayTrackInfoLocal.set(grayTrackInfo);
+ }
+
+ @Override
+ public void removeGrayTrackInfo() {
+ grayTrackInfoLocal.remove();
+ }
+
+ @Override
+ public GrayTrackInfo getGrayTrackInfo() {
+ return grayTrackInfoLocal.get();
+ }
+
+ @Override
+ public void setGrayRequest(GrayRequest grayRequest) {
+ rrayRequestLocal.set(grayRequest);
+ }
+
+ @Override
+ public void removeGrayRequest() {
+ rrayRequestLocal.remove();
+ }
+
+ @Override
+ public GrayRequest getGrayRequest() {
+ return rrayRequestLocal.get();
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/resttemplate/GrayClientHttpRequestIntercptor.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/resttemplate/GrayClientHttpRequestIntercptor.java
new file mode 100644
index 00000000..22c231c1
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/resttemplate/GrayClientHttpRequestIntercptor.java
@@ -0,0 +1,66 @@
+package cn.springcloud.gray.client.netflix.resttemplate;
+
+import cn.springcloud.gray.client.config.properties.GrayRequestProperties;
+import cn.springcloud.gray.client.netflix.connectionpoint.ConnectPointContext;
+import cn.springcloud.gray.client.netflix.connectionpoint.RibbonConnectionPoint;
+import cn.springcloud.gray.client.netflix.constants.GrayNetflixClientConstants;
+import cn.springcloud.gray.request.GrayHttpRequest;
+import cn.springcloud.gray.request.GrayRequest;
+import cn.springcloud.gray.utils.WebUtils;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+
+import java.io.IOException;
+import java.net.URI;
+
+
+/**
+ * 用于@LoadBalance 标记的 RestTemplate,主要作用是用来获取request的相关信息,为后面的路由提供数据基础。
+ */
+public class GrayClientHttpRequestIntercptor implements ClientHttpRequestInterceptor {
+
+ public static final String GRAY_REQUEST_ATTRIBUTE_RESTTEMPLATE_REQUEST = "restTemplate.request";
+
+ private GrayRequestProperties grayRequestProperties;
+ private RibbonConnectionPoint ribbonConnectionPoint;
+
+ public GrayClientHttpRequestIntercptor(
+ GrayRequestProperties grayRequestProperties, RibbonConnectionPoint ribbonConnectionPoint) {
+ this.grayRequestProperties = grayRequestProperties;
+ this.ribbonConnectionPoint = ribbonConnectionPoint;
+ }
+
+ @Override
+ public ClientHttpResponse intercept(
+ HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
+ URI uri = request.getURI();
+ GrayHttpRequest grayRequest = new GrayHttpRequest();
+ grayRequest.setUri(uri);
+ grayRequest.setServiceId(uri.getHost());
+ grayRequest.setParameters(WebUtils.getQueryParams(uri.getQuery()));
+ if (grayRequestProperties.isLoadBody()) {
+ grayRequest.setBody(body);
+ }
+ grayRequest.setMethod(request.getMethod().name());
+ grayRequest.addHeaders(request.getHeaders());
+
+ grayRequest.setAttribute(GRAY_REQUEST_ATTRIBUTE_RESTTEMPLATE_REQUEST, request);
+ ConnectPointContext connectPointContext = ConnectPointContext.builder()
+ .interceptroType(GrayNetflixClientConstants.INTERCEPTRO_TYPE_RESTTEMPLATE)
+ .grayRequest(grayRequest).build();
+
+ return ribbonConnectionPoint.execute(connectPointContext, () -> execution.execute(request, body));
+
+// try {
+// ribbonConnectionPoint.executeConnectPoint(connectPointContext);
+// return execution.execute(request, body);
+// } catch (Exception e) {
+// connectPointContext.setThrowable(e);
+// throw e;
+// } finally {
+// ribbonConnectionPoint.shutdownconnectPoint(connectPointContext);
+// }
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/resttemplate/RestTemplateRequestInterceptor.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/resttemplate/RestTemplateRequestInterceptor.java
new file mode 100644
index 00000000..e712ebef
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/resttemplate/RestTemplateRequestInterceptor.java
@@ -0,0 +1,65 @@
+package cn.springcloud.gray.client.netflix.resttemplate;
+
+import cn.springcloud.gray.RequestInterceptor;
+import cn.springcloud.gray.client.netflix.constants.GrayNetflixClientConstants;
+import cn.springcloud.gray.request.GrayHttpTrackInfo;
+import cn.springcloud.gray.request.GrayRequest;
+import cn.springcloud.gray.request.GrayTrackInfo;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpRequest;
+
+import java.util.Arrays;
+
+public class RestTemplateRequestInterceptor implements RequestInterceptor {
+ @Override
+ public String interceptroType() {
+ return GrayNetflixClientConstants.INTERCEPTRO_TYPE_RESTTEMPLATE;
+ }
+
+ @Override
+ public boolean shouldIntercept() {
+ return true;
+ }
+
+ @Override
+ public boolean pre(GrayRequest request) {
+ GrayHttpTrackInfo grayTrack = (GrayHttpTrackInfo) request.getGrayTrackInfo();
+ if (grayTrack != null) {
+ HttpRequest httpRequest = (HttpRequest) request.getAttribute(
+ GrayClientHttpRequestIntercptor.GRAY_REQUEST_ATTRIBUTE_RESTTEMPLATE_REQUEST);
+ HttpHeaders httpHeaders = httpRequest.getHeaders();
+ if (StringUtils.isNotEmpty(grayTrack.getUri())) {
+ httpHeaders.add(GrayHttpTrackInfo.GRAY_TRACK_URI, grayTrack.getUri());
+ }
+ if (StringUtils.isNotEmpty(grayTrack.getTraceIp())) {
+ httpHeaders.add(GrayHttpTrackInfo.GRAY_TRACK_TRACE_IP, grayTrack.getTraceIp());
+ }
+ if (StringUtils.isNotEmpty(grayTrack.getMethod())) {
+ httpHeaders.add(GrayHttpTrackInfo.GRAY_TRACK_METHOD, grayTrack.getMethod());
+ }
+ if (grayTrack.getParameters() != null && !grayTrack.getParameters().isEmpty()) {
+ grayTrack.getParameters().entrySet().forEach(entry -> {
+ String name = new StringBuffer().append(GrayHttpTrackInfo.GRAY_TRACK_PARAMETER_PREFIX)
+ .append(GrayTrackInfo.GRAY_TRACK_SEPARATE)
+ .append(entry.getKey()).toString();
+ httpHeaders.put(name, entry.getValue());
+ });
+ }
+ if (grayTrack.getHeaders() != null && !grayTrack.getHeaders().isEmpty()) {
+ grayTrack.getHeaders().entrySet().forEach(entry -> {
+ String name = new StringBuffer().append(GrayHttpTrackInfo.GRAY_TRACK_HEADER_PREFIX)
+ .append(GrayTrackInfo.GRAY_TRACK_SEPARATE)
+ .append(entry.getKey()).toString();
+ httpHeaders.put(name, entry.getValue());
+ });
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean after(GrayRequest request) {
+ return true;
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/resttemplate/configuration/GrayRestTemplateAutoConfiguration.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/resttemplate/configuration/GrayRestTemplateAutoConfiguration.java
new file mode 100644
index 00000000..2138843f
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/resttemplate/configuration/GrayRestTemplateAutoConfiguration.java
@@ -0,0 +1,58 @@
+package cn.springcloud.gray.client.netflix.resttemplate.configuration;
+
+import cn.springcloud.gray.client.config.properties.GrayRequestProperties;
+import cn.springcloud.gray.client.netflix.connectionpoint.RibbonConnectionPoint;
+import cn.springcloud.gray.client.netflix.resttemplate.GrayClientHttpRequestIntercptor;
+import cn.springcloud.gray.client.netflix.resttemplate.RestTemplateRequestInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.List;
+
+@Configuration
+@ConditionalOnClass(value = RestTemplate.class)
+public class GrayRestTemplateAutoConfiguration {
+
+ @Autowired
+ private GrayRequestProperties grayRequestProperties;
+ @Autowired
+ private RibbonConnectionPoint ribbonConnectionPoint;
+
+
+ @Bean
+ @LoadBalanced
+ @ConditionalOnMissingBean
+ public RestTemplate restTemplate() {
+ RestTemplate restTemplate = new RestTemplate();
+// restTemplate.getInterceptors().add(grayClientHttpRequestIntercptor);
+ return restTemplate;
+ }
+
+
+ @Bean
+ public GrayClientHttpRequestIntercptor grayClientHttpRequestIntercptor(@LoadBalanced List restTemplates) {
+ GrayClientHttpRequestIntercptor intercptor = new GrayClientHttpRequestIntercptor(
+ grayRequestProperties, ribbonConnectionPoint);
+ restTemplates.forEach(restTemplate -> restTemplate.getInterceptors().add(intercptor));
+ return intercptor;
+ }
+
+
+ @Configuration
+ @ConditionalOnProperty(value = "gray.request.track.enabled", matchIfMissing = true)
+ public static class GrayTrackRestTemplateConfiguration {
+
+
+ @Bean
+ public RestTemplateRequestInterceptor restTemplateRequestInterceptor() {
+ return new RestTemplateRequestInterceptor();
+ }
+
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/ribbon/GrayDecisionPredicate.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/ribbon/GrayDecisionPredicate.java
new file mode 100644
index 00000000..ff319fcb
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/ribbon/GrayDecisionPredicate.java
@@ -0,0 +1,45 @@
+package cn.springcloud.gray.client.netflix.ribbon;
+
+import cn.springcloud.gray.decision.GrayDecision;
+import cn.springcloud.gray.decision.GrayDecisionInputArgs;
+import cn.springcloud.gray.servernode.ServerSpec;
+import cn.springcloud.gray.request.GrayRequest;
+import com.netflix.loadbalancer.AbstractServerPredicate;
+import com.netflix.loadbalancer.PredicateKey;
+import com.netflix.loadbalancer.Server;
+
+import java.util.List;
+
+public class GrayDecisionPredicate extends AbstractServerPredicate {
+
+ public GrayDecisionPredicate(GrayLoadBalanceRule rule) {
+ super(rule);
+ }
+
+ @Override
+ public boolean apply(PredicateKey input) {
+
+ GrayLoadBalanceRule grayRule = getIRule();
+ GrayRequest grayRequest = grayRule.getRequestLocalStorage().getGrayRequest();
+
+ Server server = input.getServer();
+ String serviceId = grayRequest.getServiceId();
+ String instanceId = server.getMetaInfo().getInstanceId();
+ List grayDecisions = grayRule.getGrayManager().getGrayDecision(serviceId, instanceId);
+
+ ServerSpec serverSpec = grayRule.getServerExplainer().apply(server);
+
+ for (GrayDecision grayDecision : grayDecisions) {
+ if (grayDecision.test(GrayDecisionInputArgs.builder().grayRequest(grayRequest).server(serverSpec).build())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ protected GrayLoadBalanceRule getIRule() {
+ return (GrayLoadBalanceRule) this.rule;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/ribbon/GrayLoadBalanceRule.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/ribbon/GrayLoadBalanceRule.java
new file mode 100644
index 00000000..aa3b84b0
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/ribbon/GrayLoadBalanceRule.java
@@ -0,0 +1,125 @@
+package cn.springcloud.gray.client.netflix.ribbon;
+
+import cn.springcloud.gray.GrayManager;
+import cn.springcloud.gray.client.netflix.GrayClientHolder;
+import cn.springcloud.gray.model.GrayService;
+import cn.springcloud.gray.servernode.ServerExplainer;
+import cn.springcloud.gray.request.GrayRequest;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import com.google.common.base.Optional;
+import com.netflix.client.config.IClientConfig;
+import com.netflix.loadbalancer.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 灰度发布的负载规则
+ */
+@Slf4j
+public class GrayLoadBalanceRule extends ZoneAvoidanceRule {
+
+ protected CompositePredicate grayCompositePredicate;
+ protected GrayManager grayManager;
+ protected RequestLocalStorage requestLocalStorage;
+ protected ServerExplainer serverExplainer;
+
+
+ public GrayLoadBalanceRule() {
+ this(GrayClientHolder.getGrayManager(), GrayClientHolder.getRequestLocalStorage(),
+ GrayClientHolder.getServerExplainer());
+ }
+
+ public GrayLoadBalanceRule(GrayManager grayManager, RequestLocalStorage requestLocalStorage,
+ ServerExplainer serverExplainer) {
+ super();
+ this.grayManager = grayManager;
+ this.requestLocalStorage = requestLocalStorage;
+ this.serverExplainer = serverExplainer;
+ init();
+ }
+
+// public GrayLoadBalanceRule() {
+// super();
+// init();
+// }
+
+ protected void init() {
+ GrayDecisionPredicate grayPredicate = new GrayDecisionPredicate(this);
+ grayCompositePredicate = CompositePredicate.withPredicates(super.getPredicate(),
+ grayPredicate).build();
+ }
+
+
+ @Override
+ public Server choose(Object key) {
+ ILoadBalancer lb = getLoadBalancer();
+ GrayRequest grayRequest = requestLocalStorage.getGrayRequest();
+ String serviceId = grayRequest.getServiceId();
+ if (grayManager.hasGray(serviceId)) {
+ GrayService grayService = grayManager.getGrayService(serviceId);
+ List servers = lb.getAllServers();
+ List grayServers = new ArrayList<>(grayService.getGrayInstances().size());
+ List normalServers = new ArrayList<>(servers.size() - grayService.getGrayInstances().size());
+
+ for (Server server : servers) {
+// ServerSpec serverSpec = serverExplainer.apply(server);
+ if (grayService.getGrayInstance(server.getMetaInfo().getInstanceId()) != null) {
+ grayServers.add(server);
+ } else {
+ normalServers.add(server);
+ }
+ }
+ Optional server = grayCompositePredicate.chooseRoundRobinAfterFiltering(grayServers, key);
+ if (server.isPresent()) {
+ return expect(server.get());
+ } else {
+ return expect(choose(super.getPredicate(), normalServers, key));
+ }
+
+ } else {
+ return expect(super.choose(key));
+ }
+ }
+
+ @Override
+ public void initWithNiwsConfig(IClientConfig clientConfig) {
+ super.initWithNiwsConfig(clientConfig);
+ }
+
+ @Override
+ public void setLoadBalancer(ILoadBalancer lb) {
+ super.setLoadBalancer(lb);
+ }
+
+ private Server choose(AbstractServerPredicate serverPredicate, List servers, Object key) {
+ Optional server = serverPredicate.chooseRoundRobinAfterFiltering(servers, key);
+ if (server.isPresent()) {
+ return server.get();
+ } else {
+ return null;
+ }
+ }
+
+
+ public GrayManager getGrayManager() {
+ return grayManager;
+ }
+
+ public RequestLocalStorage getRequestLocalStorage() {
+ return requestLocalStorage;
+ }
+
+ public ServerExplainer getServerExplainer() {
+ return serverExplainer;
+ }
+
+ private Server expect(Server server) {
+ if (server != null) {
+ log.debug("找到server:{}", server.getId());
+ }
+ return server;
+ }
+}
+
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/ribbon/configuration/GrayRibbonClientsConfiguration.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/ribbon/configuration/GrayRibbonClientsConfiguration.java
new file mode 100644
index 00000000..3d7d07d2
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/ribbon/configuration/GrayRibbonClientsConfiguration.java
@@ -0,0 +1,40 @@
+package cn.springcloud.gray.client.netflix.ribbon.configuration;
+
+import cn.springcloud.gray.GrayManager;
+import cn.springcloud.gray.client.netflix.GrayClientHolder;
+import cn.springcloud.gray.client.netflix.ribbon.GrayLoadBalanceRule;
+import cn.springcloud.gray.servernode.ServerExplainer;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import com.netflix.client.config.IClientConfig;
+import com.netflix.loadbalancer.IRule;
+import com.netflix.loadbalancer.Server;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class GrayRibbonClientsConfiguration implements InitializingBean {
+
+ @Autowired
+ private GrayManager grayManager;
+ @Autowired
+ private RequestLocalStorage requestLocalStorage;
+ @Autowired
+ private ServerExplainer serverExplainer;
+
+ @Bean
+ public IRule ribbonRule(@Autowired(required = false) IClientConfig config) {
+ GrayLoadBalanceRule rule = new GrayLoadBalanceRule(grayManager, requestLocalStorage, serverExplainer);
+ rule.initWithNiwsConfig(config);
+ return rule;
+ }
+
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ GrayClientHolder.setGrayManager(grayManager);
+ GrayClientHolder.setRequestLocalStorage(requestLocalStorage);
+ GrayClientHolder.setServerExplainer(serverExplainer);
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/GrayPostZuulFilter.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/GrayPostZuulFilter.java
new file mode 100644
index 00000000..2aa50fa3
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/GrayPostZuulFilter.java
@@ -0,0 +1,41 @@
+package cn.springcloud.gray.client.netflix.zuul;
+
+import cn.springcloud.gray.client.netflix.connectionpoint.ConnectPointContext;
+import cn.springcloud.gray.client.netflix.connectionpoint.RibbonConnectionPoint;
+import com.netflix.zuul.ZuulFilter;
+import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
+
+/**
+ * 做一些善后工作。比如删除BambooRequestContext在ThreadLocal中的信息。
+ */
+public class GrayPostZuulFilter extends ZuulFilter {
+
+
+ private RibbonConnectionPoint ribbonConnectionPoint;
+
+
+ public GrayPostZuulFilter(RibbonConnectionPoint ribbonConnectionPoint) {
+ this.ribbonConnectionPoint = ribbonConnectionPoint;
+ }
+
+ @Override
+ public String filterType() {
+ return FilterConstants.POST_TYPE;
+ }
+
+ @Override
+ public int filterOrder() {
+ return 0;
+ }
+
+ @Override
+ public boolean shouldFilter() {
+ return true;
+ }
+
+ @Override
+ public Object run() {
+ ribbonConnectionPoint.shutdownconnectPoint(ConnectPointContext.getContextLocal());
+ return null;
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/GrayPreZuulFilter.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/GrayPreZuulFilter.java
new file mode 100755
index 00000000..caff7caf
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/GrayPreZuulFilter.java
@@ -0,0 +1,107 @@
+package cn.springcloud.gray.client.netflix.zuul;
+
+import cn.springcloud.gray.client.config.properties.GrayRequestProperties;
+import cn.springcloud.gray.client.netflix.connectionpoint.ConnectPointContext;
+import cn.springcloud.gray.client.netflix.connectionpoint.RibbonConnectionPoint;
+import cn.springcloud.gray.client.netflix.constants.GrayNetflixClientConstants;
+import cn.springcloud.gray.request.GrayHttpRequest;
+import cn.springcloud.gray.request.GrayRequest;
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.util.Enumeration;
+
+/**
+ * 主要作用是用来获取request的相关信息,为后面的路由提供数据基础。
+ */
+public class GrayPreZuulFilter extends ZuulFilter {
+
+ private static final Logger log = LoggerFactory.getLogger(GrayPreZuulFilter.class);
+
+ public static final String GRAY_REQUEST_ATTRIBUTE_NAME_ZUUL_REQUEST = "zuul.request";
+ public static final String GRAY_REQUEST_ATTRIBUTE_NAME_ZUUL_REQUEST_CONTEXT = "zuul.requestContext";
+
+ private GrayRequestProperties grayRequestProperties;
+ private RibbonConnectionPoint ribbonConnectionPoint;
+
+ public GrayPreZuulFilter(GrayRequestProperties grayRequestProperties, RibbonConnectionPoint ribbonConnectionPoint) {
+ this.grayRequestProperties = grayRequestProperties;
+ this.ribbonConnectionPoint = ribbonConnectionPoint;
+ }
+
+ @Override
+ public String filterType() {
+ return FilterConstants.PRE_TYPE;
+ }
+
+ @Override
+ public int filterOrder() {
+ return 10000;
+ }
+
+ @Override
+ public boolean shouldFilter() {
+ return true;
+ }
+
+ @Override
+ public Object run() {
+ RequestContext context = RequestContext.getCurrentContext();
+ HttpServletRequest servletRequest = context.getRequest();
+
+ GrayHttpRequest grayRequest = new GrayHttpRequest();
+ URI uri = URI.create((String) context.get(FilterConstants.REQUEST_URI_KEY));
+ grayRequest.setUri(uri);
+ grayRequest.setServiceId((String) context.get(FilterConstants.SERVICE_ID_KEY));
+ grayRequest.addParameters(context.getRequestQueryParams());
+ if (grayRequestProperties.isLoadBody()) {
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(context.getRequest().getInputStream()));
+ byte[] reqBody = IOUtils.toByteArray(reader);
+ grayRequest.setBody(reqBody);
+ } catch (IOException e) {
+ String errorMsg = "获取request body出现异常";
+ log.error(errorMsg, e);
+ }
+ }
+
+ grayRequest.setMethod(servletRequest.getMethod());
+ grayRequest.setHeaders(getHeaders(context));
+ grayRequest.setAttribute(GRAY_REQUEST_ATTRIBUTE_NAME_ZUUL_REQUEST, servletRequest);
+ grayRequest.setAttribute(GRAY_REQUEST_ATTRIBUTE_NAME_ZUUL_REQUEST_CONTEXT, context);
+ //context.getZuulRequestHeaders().get(FilterConstants.X_FORWARDED_FOR_HEADER.toLowerCase())
+
+ ConnectPointContext connectPointContext = ConnectPointContext.builder()
+ .interceptroType(GrayNetflixClientConstants.INTERCEPTRO_TYPE_ZUUL)
+ .grayRequest(grayRequest).build();
+ ribbonConnectionPoint.executeConnectPoint(connectPointContext);
+ return null;
+ }
+
+
+ private MultiValueMap getHeaders(RequestContext context) {
+ MultiValueMap headers = new LinkedMultiValueMap<>();
+ context.getZuulRequestHeaders().entrySet().forEach(entry -> {
+ headers.add(entry.getKey(), entry.getValue());
+ });
+ HttpServletRequest servletRequest = context.getRequest();
+ Enumeration headerNames = servletRequest.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String headerName = headerNames.nextElement();
+ headers.add(headerName, servletRequest.getHeader(headerName));
+ }
+ return headers;
+ }
+
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/ZuulRequestInterceptor.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/ZuulRequestInterceptor.java
new file mode 100644
index 00000000..2e43668f
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/ZuulRequestInterceptor.java
@@ -0,0 +1,68 @@
+package cn.springcloud.gray.client.netflix.zuul;
+
+import cn.springcloud.gray.RequestInterceptor;
+import cn.springcloud.gray.client.netflix.constants.GrayNetflixClientConstants;
+import cn.springcloud.gray.request.GrayHttpTrackInfo;
+import cn.springcloud.gray.request.GrayRequest;
+import cn.springcloud.gray.request.GrayTrackInfo;
+import com.netflix.zuul.context.RequestContext;
+import org.apache.commons.lang3.StringUtils;
+
+public class ZuulRequestInterceptor implements RequestInterceptor {
+ @Override
+ public String interceptroType() {
+ return GrayNetflixClientConstants.INTERCEPTRO_TYPE_ZUUL;
+ }
+
+ @Override
+ public boolean shouldIntercept() {
+ return true;
+ }
+
+ @Override
+ public boolean pre(GrayRequest request) {
+ GrayHttpTrackInfo grayTrack = (GrayHttpTrackInfo) request.getGrayTrackInfo();
+ if (grayTrack != null) {
+ RequestContext context = (RequestContext) request.getAttribute(
+ GrayPreZuulFilter.GRAY_REQUEST_ATTRIBUTE_NAME_ZUUL_REQUEST_CONTEXT);
+ if (StringUtils.isNotEmpty(grayTrack.getUri())) {
+ context.addOriginResponseHeader(GrayHttpTrackInfo.GRAY_TRACK_URI, grayTrack.getUri());
+ }
+ if (StringUtils.isNotEmpty(grayTrack.getTraceIp())) {
+ context.addOriginResponseHeader(GrayHttpTrackInfo.GRAY_TRACK_TRACE_IP, grayTrack.getTraceIp());
+ }
+ if (StringUtils.isNotEmpty(grayTrack.getMethod())) {
+ context.addOriginResponseHeader(GrayHttpTrackInfo.GRAY_TRACK_METHOD, grayTrack.getMethod());
+ }
+ if (grayTrack.getParameters() != null && !grayTrack.getParameters().isEmpty()) {
+ grayTrack.getParameters().entrySet().forEach(entry -> {
+ String name = new StringBuffer().append(GrayHttpTrackInfo.GRAY_TRACK_PARAMETER_PREFIX)
+ .append(GrayTrackInfo.GRAY_TRACK_SEPARATE)
+ .append(entry.getKey()).toString();
+ context.addOriginResponseHeader(GrayHttpTrackInfo.GRAY_TRACK_METHOD, grayTrack.getMethod());
+
+ entry.getValue().forEach(v -> {
+ context.addOriginResponseHeader(name, v);
+ });
+ });
+ }
+ if (grayTrack.getHeaders() != null && !grayTrack.getHeaders().isEmpty()) {
+ grayTrack.getHeaders().entrySet().forEach(entry -> {
+ String name = new StringBuffer().append(GrayHttpTrackInfo.GRAY_TRACK_HEADER_PREFIX)
+ .append(GrayTrackInfo.GRAY_TRACK_SEPARATE)
+ .append(entry.getKey()).toString();
+ entry.getValue().forEach(v -> {
+ context.addOriginResponseHeader(name, v);
+ });
+ });
+ }
+
+ }
+ return true;
+ }
+
+ @Override
+ public boolean after(GrayRequest request) {
+ return true;
+ }
+}
diff --git a/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/configuration/GrayZuulAutoConfiguration.java b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/configuration/GrayZuulAutoConfiguration.java
new file mode 100644
index 00000000..66ccb1a7
--- /dev/null
+++ b/spring-cloud-gray-client-netflix/src/main/java/cn/springcloud/gray/client/netflix/zuul/configuration/GrayZuulAutoConfiguration.java
@@ -0,0 +1,55 @@
+package cn.springcloud.gray.client.netflix.zuul.configuration;
+
+import cn.springcloud.gray.client.config.properties.GrayRequestProperties;
+import cn.springcloud.gray.client.netflix.configuration.HystrixGrayAutoConfiguration;
+import cn.springcloud.gray.client.netflix.connectionpoint.RibbonConnectionPoint;
+import cn.springcloud.gray.client.netflix.zuul.GrayPostZuulFilter;
+import cn.springcloud.gray.client.netflix.zuul.GrayPreZuulFilter;
+import cn.springcloud.gray.client.netflix.zuul.ZuulRequestInterceptor;
+import com.netflix.zuul.http.ZuulServlet;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConditionalOnClass(value = ZuulServlet.class)
+public class GrayZuulAutoConfiguration {
+
+ @Autowired
+ private GrayRequestProperties grayRequestProperties;
+ @Autowired
+ private RibbonConnectionPoint ribbonConnectionPoint;
+
+ @Bean
+ public GrayPreZuulFilter grayPreZuulFilter() {
+ return new GrayPreZuulFilter(grayRequestProperties, ribbonConnectionPoint);
+ }
+
+ @Bean
+ public GrayPostZuulFilter grayPostZuulFilter() {
+ return new GrayPostZuulFilter(ribbonConnectionPoint);
+ }
+
+
+ @Configuration
+ @ConditionalOnProperty(value = "gray.request.track.enabled", matchIfMissing = true)
+ public static class GrayTrackZuulConfiguration {
+
+
+ @Bean
+ public ZuulRequestInterceptor zuulRequestInterceptor() {
+ return new ZuulRequestInterceptor();
+ }
+
+ }
+
+
+ @Configuration
+ @ConditionalOnProperty(value = "zuul.ribbonIsolationStrategy", havingValue = "THREAD")
+ public static class HystrixConfiguration extends HystrixGrayAutoConfiguration {
+
+ }
+
+}
diff --git a/spring-cloud-gray-client/pom.xml b/spring-cloud-gray-client/pom.xml
index 78b6e46b..ad4f1fb1 100644
--- a/spring-cloud-gray-client/pom.xml
+++ b/spring-cloud-gray-client/pom.xml
@@ -5,60 +5,72 @@
spring-cloud-gray
cn.springcloud.gray
- 1.1.0
+ 2.0.0
4.0.0
spring-cloud-gray-client
-
- cn.springcloud.gray
- spring-cloud-bamboo
+ org.springframework
+ spring-web
- cn.springcloud.gray
- spring-cloud-gray-core
+ org.springframework
+ spring-webmvc
-
org.springframework.boot
- spring-boot-starter-web
+ spring-boot-autoconfigure
provided
- org.springframework.boot
- spring-boot-starter-test
- provided
+ cn.springcloud.gray
+ spring-cloud-gray-core
- org.springframework.cloud
- spring-cloud-starter-eureka
- provided
+ cn.springcloud.gray
+ spring-cloud-gray-utils
- org.springframework.cloud
- spring-cloud-starter-zookeeper-discovery
- provided
+ org.projectlombok
+ lombok
- org.springframework.cloud
- spring-cloud-starter-feign
- provided
+ javax.servlet
+ javax.servlet-api
+
+
+ org.slf4j
+ slf4j-api
+
+
+ commons-collections
+ commons-collections
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ cn.springcloud.gray
+ spring-cloud-gray-utils
+ 2.0.0
+ compile
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
-
-
-
-
-
-
-
-
-
-
+
+ org.springframework.cloud
+ spring-cloud-stream
+
+
\ No newline at end of file
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/AbstractCommunicableGrayManager.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/AbstractCommunicableGrayManager.java
new file mode 100644
index 00000000..15e63bd8
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/AbstractCommunicableGrayManager.java
@@ -0,0 +1,43 @@
+package cn.springcloud.gray;
+
+import cn.springcloud.gray.communication.HttpInformationClient;
+import cn.springcloud.gray.communication.InformationClient;
+import cn.springcloud.gray.communication.RetryableInformationClient;
+import cn.springcloud.gray.decision.GrayDecisionFactoryKeeper;
+
+import java.util.List;
+
+public abstract class AbstractCommunicableGrayManager extends SimpleGrayManager implements CommunicableGrayManager {
+
+ private GrayClientConfig grayClientConfig;
+ private InformationClient informationClient;
+
+ public AbstractCommunicableGrayManager(GrayClientConfig grayClientConfig, GrayDecisionFactoryKeeper grayDecisionFactoryKeeper, List requestInterceptors) {
+ super(grayDecisionFactoryKeeper, requestInterceptors);
+ this.grayClientConfig = grayClientConfig;
+ createInformationClient();
+ }
+
+ public GrayClientConfig getGrayClientConfig() {
+ return grayClientConfig;
+ }
+
+ @Override
+ public InformationClient getGrayInformationClient() {
+ return informationClient;
+ }
+
+ protected void createInformationClient() {
+
+ GrayClientConfig clientConfig = getGrayClientConfig();
+ InformationClient httpClient = new HttpInformationClient(clientConfig.getServerUrl());
+ if (clientConfig.isRetryable()) {
+ informationClient = new RetryableInformationClient(Math.max(3, clientConfig.getRetryNumberOfRetries()), httpClient);
+ } else {
+ informationClient = httpClient;
+ }
+
+ }
+
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/AbstractGrayManager.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/AbstractGrayManager.java
index 83a51254..c14e5e3e 100644
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/AbstractGrayManager.java
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/AbstractGrayManager.java
@@ -1,14 +1,20 @@
package cn.springcloud.gray;
-import cn.springcloud.gray.client.GrayClientAppContext;
-import cn.springcloud.gray.core.*;
+import cn.springcloud.gray.decision.GrayDecision;
import cn.springcloud.gray.decision.MultiGrayDecision;
+import cn.springcloud.gray.decision.factory.GrayDecisionFactory;
+import cn.springcloud.gray.decision.GrayDecisionFactoryKeeper;
+import cn.springcloud.gray.model.DecisionDefinition;
+import cn.springcloud.gray.model.GrayInstance;
+import cn.springcloud.gray.model.PolicyDefinition;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.ListUtils;
+import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.OrderComparator;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
/**
@@ -17,89 +23,89 @@
public abstract class AbstractGrayManager implements GrayManager {
private static final Logger log = LoggerFactory.getLogger(AbstractGrayManager.class);
- protected GrayDecisionFactory decisionFactory;
- protected InformationClient client;
-
- public AbstractGrayManager(InformationClient client, GrayDecisionFactory decisionFactory) {
- this.decisionFactory = decisionFactory;
- this.client = client;
- }
+ private GrayDecisionFactoryKeeper grayDecisionFactoryKeeper;
+ private Map> requestInterceptors = new HashMap<>();
- @Override
- public boolean isOpen(String serviceId) {
- GrayService grayService = grayService(serviceId);
- return grayService != null
- && grayService.isOpenGray();
+ public AbstractGrayManager(
+ GrayDecisionFactoryKeeper grayDecisionFactoryKeeper, List requestInterceptors) {
+ initRequestInterceptors(requestInterceptors);
+ this.grayDecisionFactoryKeeper = grayDecisionFactoryKeeper;
}
- @Override
- public List listGrayService() {
- return client.listGrayService();
- }
@Override
- public GrayService grayService(String serviceId) {
- return client.grayService(serviceId);
+ public List getRequeestInterceptors(String interceptroType) {
+ List list = requestInterceptors.get(interceptroType);
+ if (list == null) {
+ return ListUtils.EMPTY_LIST;
+ }
+ return list;
}
- @Override
- public GrayInstance grayInstance(String serviceId, String instanceId) {
- return client.grayInstance(serviceId, instanceId);
- }
@Override
- public List grayDecision(GrayInstance instance) {
- return grayDecision(instance.getServiceId(), instance.getInstanceId());
- }
+ public List getGrayDecision(GrayInstance instance) {
+ List policyDefinitions = instance.getPolicyDefinitions();
+ List grayDecisions = new ArrayList<>(policyDefinitions.size());
- @Override
- public List grayDecision(String serviceId, String instanceId) {
- GrayInstance grayInstance = grayInstance(serviceId, instanceId);
- if (grayInstance == null || !grayInstance.isOpenGray()
- || grayInstance.getGrayPolicyGroups() == null
- || grayInstance.getGrayPolicyGroups().isEmpty()) {
- return Collections.emptyList();
- }
- List policyGroups = grayInstance.getGrayPolicyGroups();
- List decisions = new ArrayList<>(policyGroups.size());
- for (GrayPolicyGroup policyGroup : policyGroups) {
- if (!policyGroup.isEnable()) {
+ for (PolicyDefinition policyDefinition : policyDefinitions) {
+ if (CollectionUtils.isEmpty(policyDefinition.getList())) {
continue;
}
- GrayDecision grayDecision = toGrayDecision(policyGroup);
- if (grayDecision != GrayDecision.refuse()) {
- decisions.add(grayDecision);
+ GrayDecision decision = createGrayDecision(policyDefinition);
+ if (decision != null) {
+ grayDecisions.add(decision);
}
}
- return decisions;
+
+ return grayDecisions;
}
@Override
- public void serviceDownline() {
- InstanceLocalInfo localInfo = GrayClientAppContext.getInstanceLocalInfo();
- if (localInfo.isGray()) {
- log.debug("灰度服务下线...");
- client.serviceDownline();
- log.debug("灰度服务下线完成");
- }
- serviceShutdown();
+ public List getGrayDecision(String serviceId, String instanceId) {
+ return getGrayDecision(getGrayInstance(serviceId, instanceId));
}
- protected abstract void serviceShutdown();
+ private GrayDecision createGrayDecision(PolicyDefinition policyDefinition) {
+ MultiGrayDecision decision = new MultiGrayDecision(GrayDecision.allow());
+ for (DecisionDefinition decisionDefinition : policyDefinition.getList()) {
+ decision = decision.and(grayDecisionFactoryKeeper.getGrayDecision(decisionDefinition));
+ }
+ return decision;
+ }
- private GrayDecision toGrayDecision(GrayPolicyGroup policyGroup) {
- List policies = policyGroup.getList();
- if (policies == null || policies.isEmpty()) {
- return GrayDecision.refuse();
+ private void initRequestInterceptors(List requestInterceptors) {
+ if (requestInterceptors == null || requestInterceptors.isEmpty()) {
+ return;
}
- MultiGrayDecision decision = new MultiGrayDecision(GrayDecision.allow());
- policies.forEach(policy -> decision.and(decisionFactory.getDecision(policy)));
- return decision;
+ List all = new ArrayList<>();
+ for (RequestInterceptor interceptor : requestInterceptors) {
+ if (StringUtils.equals(interceptor.interceptroType(), "all")) {
+ all.add(interceptor);
+ } else {
+ List interceptors = this.requestInterceptors.get(interceptor.interceptroType());
+ if (interceptors == null) {
+ interceptors = new ArrayList<>();
+ this.requestInterceptors.put(interceptor.interceptroType(), interceptors);
+ }
+ interceptors.add(interceptor);
+ }
+ }
+ putTypeAllTo(all);
}
+ private void putTypeAllTo(List all) {
+ if (all.isEmpty()) {
+ return;
+ }
+ requestInterceptors.values().forEach(list -> {
+ list.addAll(all);
+ OrderComparator.sort(list);
+ });
+ }
}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/BaseGrayManager.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/BaseGrayManager.java
deleted file mode 100644
index 31d16dcb..00000000
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/BaseGrayManager.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package cn.springcloud.gray;
-
-import cn.springcloud.gray.client.GrayClientAppContext;
-import cn.springcloud.gray.client.GrayClientConfig;
-import cn.springcloud.gray.client.GrayOptionalArgs;
-import cn.springcloud.gray.core.GrayInstance;
-import cn.springcloud.gray.core.GrayService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-
-/**
- * 在AbstractGrayManager基础上进行了扩展,将灰度列表缓存起来,定时从灰度服务端更新灰度列表。
- */
-public class BaseGrayManager extends AbstractGrayManager {
- private static final Logger log = LoggerFactory.getLogger(BaseGrayManager.class);
- private Map grayServiceMap;
- private Timer updateTimer = new Timer("Gray-UpdateTimer", true);
- private GrayClientConfig clientConfig;
-
- public BaseGrayManager(GrayOptionalArgs grayOptionalArgs) {
- super(grayOptionalArgs.getInformationClient(), grayOptionalArgs.getDecisionFactory());
- clientConfig = grayOptionalArgs.getGrayClientConfig();
- grayServiceMap = new ConcurrentHashMap<>();
- }
-
-
- @Override
- public void openForWork() {
- if (clientConfig.isGrayEnroll()) {
- grayEnroll();
- }
- log.info("拉取灰度列表");
- doUpdate();
- updateTimer.schedule(new UpdateTask(),
- clientConfig.getServiceUpdateIntervalTimerInMs(),
- clientConfig.getServiceUpdateIntervalTimerInMs());
- }
-
- @Override
- public List listGrayService() {
- if (grayServiceMap == null) {
- List grayServices = super.listGrayService();
- if (grayServices == null) {
- return null;
-
- }
- updateGrayServices(grayServices);
- }
- return new ArrayList<>(grayServiceMap.values());
- }
-
-
- @Override
- public GrayService grayService(String serviceId) {
- if (grayServiceMap == null) {
- return super.grayService(serviceId);
- }
- return grayServiceMap.get(serviceId);
- }
-
- @Override
- public GrayInstance grayInstance(String serviceId, String instanceId) {
- if (grayServiceMap == null) {
- return super.grayInstance(serviceId, instanceId);
- }
- GrayService grayService = grayService(serviceId);
- if (grayService != null) {
- return grayService.getGrayInstance(instanceId);
- }
- return null;
- }
-
- @Override
- protected void serviceShutdown() {
- updateTimer.cancel();
- }
-
- @Override
- public void updateGrayServices(Collection grayServices) {
- if (grayServices == null) {
- return;
- }
-
- Map grayMap = new HashMap<>();
- grayServices.forEach(grayService -> grayMap.put(grayService.getServiceId(), grayService));
- grayServiceMap = new ConcurrentHashMap(grayMap);
- checkLocalGray();
- }
-
-
- private void doUpdate() {
- try {
- log.debug("更新灰度服务列表...");
- updateGrayServices(client.listGrayService());
- } catch (Exception e) {
- log.error("更新灰度服务列表失败", e);
- }
- }
-
-
- private void grayEnroll() {
- Thread t = new Thread(() -> {
-
- try {
- Thread.sleep(clientConfig.grayEnrollDealyTimeInMs());
- } catch (InterruptedException e) {
- }
- log.info("灰度注册自身实例...");
- InstanceLocalInfo localInfo = GrayClientAppContext.getInstanceLocalInfo();
- try {
- client.addGrayInstance(localInfo.getServiceId(), localInfo.getInstanceId());
- localInfo.setGray(true);
- } catch (Exception e) {
- log.error("自身实例灰度注册失败", e);
- }
- }, "GrayEnroll");
- t.start();
- }
-
- private void checkLocalGray() {
- InstanceLocalInfo localInfo = GrayClientAppContext.getInstanceLocalInfo();
- GrayService grayService = grayServiceMap.get(localInfo.getServiceId());
- localInfo.setGray(grayService != null && grayService.getGrayInstance(localInfo.getInstanceId()) != null);
- }
-
- class UpdateTask extends TimerTask {
-
- @Override
- public void run() {
- doUpdate();
- }
- }
-
-
-}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/CommunicableGrayManager.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/CommunicableGrayManager.java
new file mode 100644
index 00000000..ddab3697
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/CommunicableGrayManager.java
@@ -0,0 +1,9 @@
+package cn.springcloud.gray;
+
+import cn.springcloud.gray.communication.InformationClient;
+
+public interface CommunicableGrayManager extends GrayManager {
+
+ InformationClient getGrayInformationClient();
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/DefaultGrayManager.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/DefaultGrayManager.java
index 2fa44fe9..06ad5e1b 100644
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/DefaultGrayManager.java
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/DefaultGrayManager.java
@@ -1,12 +1,61 @@
package cn.springcloud.gray;
-import cn.springcloud.gray.client.GrayOptionalArgs;
+import cn.springcloud.gray.decision.GrayDecisionFactoryKeeper;
+import cn.springcloud.gray.model.GrayInstance;
+import cn.springcloud.gray.model.GrayService;
+import lombok.extern.slf4j.Slf4j;
-public class DefaultGrayManager extends BaseGrayManager {
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
- public DefaultGrayManager(GrayOptionalArgs grayOptionalArgs) {
- super(grayOptionalArgs);
+@Slf4j
+public class DefaultGrayManager extends AbstractCommunicableGrayManager {
+
+ private Timer updateTimer = new Timer("Gray-Update-Timer", true);
+
+ public DefaultGrayManager(GrayClientConfig grayClientConfig, GrayDecisionFactoryKeeper grayDecisionFactoryKeeper,
+ List requestInterceptors) {
+ super(grayClientConfig, grayDecisionFactoryKeeper, requestInterceptors);
+ openForWork();
+ }
+
+
+ public void openForWork() {
+ log.info("拉取灰度列表");
+ doUpdate();
+ updateTimer.schedule(new UpdateTask(),
+ getGrayClientConfig().getServiceUpdateIntervalTimerInMs(),
+ getGrayClientConfig().getServiceUpdateIntervalTimerInMs());
+ }
+
+
+ private void doUpdate() {
+ try {
+ log.debug("更新灰度服务列表...");
+
+ List grayInstances = getGrayInformationClient().allGrayInstances();
+ Map grayServices = new ConcurrentHashMap<>();
+ grayInstances.forEach(instance -> {
+ updateGrayInstance(grayServices, instance);
+ });
+ this.grayServices = grayServices;
+ } catch (Exception e) {
+ log.error("更新灰度服务列表失败", e);
+ }
}
+
+ class UpdateTask extends TimerTask {
+
+ @Override
+ public void run() {
+ doUpdate();
+ }
+ }
+
+
}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientConfig.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/GrayClientConfig.java
similarity index 88%
rename from spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientConfig.java
rename to spring-cloud-gray-client/src/main/java/cn/springcloud/gray/GrayClientConfig.java
index 98041f81..6a2bb4c6 100644
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientConfig.java
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/GrayClientConfig.java
@@ -1,7 +1,14 @@
-package cn.springcloud.gray.client;
+package cn.springcloud.gray;
public interface GrayClientConfig {
+ /**
+ * 运行类型:web
+ *
+ * @return
+ */
+ String runenv();
+
/**
* 启动时是否灰度注册
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/GrayManager.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/GrayManager.java
new file mode 100644
index 00000000..5372325f
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/GrayManager.java
@@ -0,0 +1,47 @@
+package cn.springcloud.gray;
+
+import cn.springcloud.gray.decision.GrayDecision;
+import cn.springcloud.gray.model.GrayInstance;
+import cn.springcloud.gray.model.GrayService;
+
+import java.util.Collection;
+import java.util.List;
+
+
+/**
+ * 灰度客户端管理器,维护灰度列表,维护自身灰度状态,创建灰度决策对象。
+ * 抽象实现类AbstractGrayManager实现了基础的获取灰度列表, 创建灰度决策对象的能力。
+ * BaseGrayManger在其基础上进行了扩展,将灰度列表缓存起来,定时从灰度服务端更新灰度列表。
+ */
+public interface GrayManager {
+
+
+ /**
+ * 判断指定的服务ID是否有灰度实例
+ *
+ * @param serviceId 服务ID
+ * @return has gray instance if true
+ */
+ boolean hasGray(String serviceId);
+
+ Collection allGrayServices();
+
+ GrayService getGrayService(String serviceId);
+
+ GrayInstance getGrayInstance(String serviceId, String instanceId);
+
+ List getGrayDecision(GrayInstance instance);
+
+ List getGrayDecision(String serviceId, String instanceId);
+
+ void updateGrayInstance(GrayInstance instance);
+
+ void closeGray(GrayInstance instance);
+
+ void closeGray(String serviceId, String instanceId);
+
+ List getRequeestInterceptors(String interceptroType);
+
+
+ void shutdown();
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/HttpInformationClient.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/HttpInformationClient.java
deleted file mode 100644
index d79e9c92..00000000
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/HttpInformationClient.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package cn.springcloud.gray;
-
-import cn.springcloud.gray.client.GrayClientAppContext;
-import cn.springcloud.gray.core.GrayInstance;
-import cn.springcloud.gray.core.GrayService;
-import cn.springcloud.gray.core.InformationClient;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.core.ParameterizedTypeReference;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class HttpInformationClient implements InformationClient {
- private static final Logger log = LoggerFactory.getLogger(HttpInformationClient.class);
- private final String baseUrl;
- private RestTemplate rest;
-
- public HttpInformationClient(String baseUrl, RestTemplate rest) {
- this.baseUrl = baseUrl;
- this.rest = rest;
- }
-
- @Override
- public List listGrayService() {
- String url = this.baseUrl + "/gray/services/enable";
- ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() {
- };
-
- try {
- ResponseEntity> responseEntity = rest.exchange(url, HttpMethod.GET, null, typeRef);
- return responseEntity.getBody();
- } catch (RuntimeException e) {
- log.error("获取灰度服务列表失败", e);
- throw e;
- }
- }
-
- @Override
- public GrayService grayService(String serviceId) {
- String url = this.baseUrl + "/gray/services/{serviceId}";
- Map params = new HashMap<>();
- params.put("serviceId", serviceId);
-
- try {
- ResponseEntity responseEntity = rest.getForEntity(url, GrayService.class, params);
- return responseEntity.getBody();
- } catch (RuntimeException e) {
- log.error("获取灰度服务失败", e);
- throw e;
- }
- }
-
- @Override
- public GrayInstance grayInstance(String serviceId, String instanceId) {
- String url = this.baseUrl + "/gray/services/{serviceId}/instance/?instanceId={instanceId}";
-
- Map params = new HashMap<>();
- params.put("serviceId", serviceId);
- params.put("instanceId", instanceId);
- try {
- ResponseEntity responseEntity = rest.getForEntity(url, GrayInstance.class, params);
- return responseEntity.getBody();
- } catch (RuntimeException e) {
- log.error("获取灰度服务实例失败", e);
- throw e;
- }
- }
-
- @Override
- public void addGrayInstance(String serviceId, String instanceId) {
- GrayInstance grayInstance = new GrayInstance();
- grayInstance.setInstanceId(instanceId);
- grayInstance.setServiceId(serviceId);
-
- String url = this.baseUrl + "/gray/services/{serviceId}/instance";
- try {
- rest.postForEntity(url, grayInstance, null, serviceId);
- } catch (RuntimeException e) {
- log.error("灰度服务实例下线失败", e);
- throw e;
- }
- }
-
- @Override
- public void serviceDownline() {
- InstanceLocalInfo localInfo = GrayClientAppContext.getInstanceLocalInfo();
- if (!localInfo.isGray()) {
- return;
- }
-
- serviceDownline(localInfo.getServiceId(), localInfo.getInstanceId());
- }
-
- @Override
- public void serviceDownline(String serviceId, String instanceId) {
- String url = this.baseUrl + "/gray/services/{serviceId}/instance/?instanceId={instanceId}";
- Map params = new HashMap<>();
- params.put("serviceId", serviceId);
- try {
- params.put("instanceId", instanceId);
- rest.delete(url, params);
- } catch (Exception e) {
- log.error("灰度服务实例下线失败", e);
- throw e;
- }
- }
-}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/InstanceLocalInfo.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/InstanceLocalInfo.java
index 0bde8920..f079fcdf 100644
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/InstanceLocalInfo.java
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/InstanceLocalInfo.java
@@ -1,31 +1,18 @@
package cn.springcloud.gray;
+import lombok.*;
+
+
+@Getter
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
public class InstanceLocalInfo {
private String serviceId;
private String instanceId;
+ private String host;
+ private int port;
+ @Setter
private boolean isGray;
- public String getServiceId() {
- return serviceId;
- }
-
- public void setServiceId(String serviceId) {
- this.serviceId = serviceId;
- }
-
- public String getInstanceId() {
- return instanceId;
- }
-
- public void setInstanceId(String instanceId) {
- this.instanceId = instanceId;
- }
-
- public boolean isGray() {
- return isGray;
- }
-
- public void setGray(boolean gray) {
- isGray = gray;
- }
}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/RequestInterceptor.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/RequestInterceptor.java
new file mode 100644
index 00000000..4230b5a8
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/RequestInterceptor.java
@@ -0,0 +1,25 @@
+package cn.springcloud.gray;
+
+
+import cn.springcloud.gray.request.GrayRequest;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+
+/**
+ *
+ */
+public interface RequestInterceptor extends Ordered {
+
+ String interceptroType();
+
+ boolean shouldIntercept();
+
+ boolean pre(GrayRequest request);
+
+ boolean after(GrayRequest request);
+
+ default int getOrder() {
+ return Ordered.LOWEST_PRECEDENCE;
+ }
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/SimpleGrayManager.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/SimpleGrayManager.java
new file mode 100644
index 00000000..8bad6258
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/SimpleGrayManager.java
@@ -0,0 +1,100 @@
+package cn.springcloud.gray;
+
+import cn.springcloud.gray.decision.GrayDecisionFactoryKeeper;
+import cn.springcloud.gray.model.GrayInstance;
+import cn.springcloud.gray.model.GrayService;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Slf4j
+public class SimpleGrayManager extends AbstractGrayManager {
+
+
+ protected Map grayServices = new ConcurrentHashMap<>();
+
+ public SimpleGrayManager(GrayDecisionFactoryKeeper grayDecisionFactoryKeeper, List requestInterceptors) {
+ super(grayDecisionFactoryKeeper, requestInterceptors);
+ }
+
+
+ @Override
+ public boolean hasGray(String serviceId) {
+ GrayService grayService = grayServices.get(serviceId);
+ return grayService != null && !grayService.getGrayInstances().isEmpty();
+ }
+
+ @Override
+ public Collection allGrayServices() {
+ return grayServices.values();
+ }
+
+ @Override
+ public GrayService getGrayService(String serviceId) {
+ return grayServices.get(serviceId);
+ }
+
+ @Override
+ public GrayInstance getGrayInstance(String serviceId, String instanceId) {
+ GrayService service = getGrayService(serviceId);
+ return service != null ? service.getGrayInstance(instanceId) : null;
+ }
+
+
+ @Override
+ public void updateGrayInstance(GrayInstance instance) {
+ if (instance == null || !instance.isGray()) {
+ return;
+ }
+ updateGrayInstance(grayServices, instance);
+ }
+
+ protected void updateGrayInstance(Map grayServices, GrayInstance instance) {
+ GrayService service = getGrayService(instance.getServiceId());
+ if (service == null) {
+ synchronized (this) {
+ service = getGrayService(instance.getServiceId());
+ if (service == null) {
+ service = new GrayService();
+ service.setServiceId(instance.getServiceId());
+ grayServices.put(service.getServiceId(), service);
+ }
+ }
+ } else if (grayServices != this.grayServices) {
+ grayServices.put(service.getServiceId(), service);
+ }
+ log.debug("添加灰度实例, serviceId:{}, instanceId:{}", instance.getServiceId(), instance.getInstanceId());
+ service.setGrayInstance(instance);
+ }
+
+ @Override
+ public void closeGray(GrayInstance instance) {
+ GrayService service = getGrayService(instance.getServiceId());
+ if (service == null) {
+ log.debug("没有找到灰度服务:{}, 所以无需删除实例:{} 的灰度状态", instance.getServiceId(), instance.getInstanceId());
+ return;
+ }
+ log.debug("关闭实例的在灰度状态, serviceId:{}, instanceId:{}", instance.getServiceId(), instance.getInstanceId());
+ service.removeGrayInstance(instance.getInstanceId());
+ }
+
+ @Override
+ public void closeGray(String serviceId, String instanceId) {
+ GrayService service = getGrayService(serviceId);
+ if (service == null) {
+ log.debug("没有找到灰度服务:{}, 所以无需删除实例:{} 的灰度状态", serviceId, instanceId);
+ return;
+ }
+ log.debug("关闭实例的在灰度状态, serviceId:{}, instanceId:{}", serviceId, instanceId);
+ service.removeGrayInstance(instanceId);
+ }
+
+
+ @Override
+ public void shutdown() {
+
+ }
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientAppContext.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientAppContext.java
deleted file mode 100644
index fda98c44..00000000
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientAppContext.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package cn.springcloud.gray.client;
-
-import cn.springcloud.gray.InstanceLocalInfo;
-import cn.springcloud.gray.client.config.properties.GrayClientProperties;
-import cn.springcloud.gray.core.GrayManager;
-
-public class GrayClientAppContext {
- private static GrayManager grayManager;
- private static InstanceLocalInfo instanceLocalInfo;
- private static GrayClientProperties grayClientProperties;
-
-
- public static GrayManager getGrayManager() {
- return grayManager;
- }
-
- static void setGrayManager(GrayManager grayManager) {
- GrayClientAppContext.grayManager = grayManager;
- }
-
-
- public static InstanceLocalInfo getInstanceLocalInfo() {
- return instanceLocalInfo;
- }
-
- static void setInstanceLocalInfo(InstanceLocalInfo instanceLocalInfo) {
- GrayClientAppContext.instanceLocalInfo = instanceLocalInfo;
- }
-
- public static GrayClientProperties getGrayClientProperties() {
- return grayClientProperties;
- }
-
- static void setGrayClientProperties(GrayClientProperties grayClientProperties) {
- GrayClientAppContext.grayClientProperties = grayClientProperties;
- }
-}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientInitializingBean.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientInitializingBean.java
deleted file mode 100644
index 33488fb9..00000000
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientInitializingBean.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package cn.springcloud.gray.client;
-
-import cn.springcloud.gray.InstanceLocalInfo;
-import cn.springcloud.gray.client.config.properties.GrayClientProperties;
-import cn.springcloud.gray.core.GrayManager;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-
-import javax.annotation.PreDestroy;
-
-public class GrayClientInitializingBean implements InitializingBean, ApplicationContextAware {
- private ApplicationContext cxt;
-
- @Override
- public void afterPropertiesSet() throws Exception {
- GrayClientAppContext.setGrayManager(cxt.getBean(GrayManager.class));
- GrayClientAppContext.setInstanceLocalInfo(cxt.getBean(InstanceLocalInfo.class));
- GrayClientAppContext.setGrayClientProperties(cxt.getBean(GrayClientProperties.class));
-
- startForWork();
-
-// registrShutdownFunc();
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.cxt = applicationContext;
- }
-
-// private void registrShutdownFunc(){
-// Runtime.getRuntime().addShutdownHook(new Thread(()->{
-// shutdown();
-// }));
-// }
-
- @PreDestroy
- public void shutdown() {
- GrayClientAppContext.getGrayManager().serviceDownline();
- }
-
- private void startForWork() {
- GrayClientAppContext.getGrayManager().openForWork();
- }
-}
\ No newline at end of file
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientInitializingDestroyBean.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientInitializingDestroyBean.java
new file mode 100644
index 00000000..1f18174f
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayClientInitializingDestroyBean.java
@@ -0,0 +1,63 @@
+package cn.springcloud.gray.client;
+
+import cn.springcloud.gray.CommunicableGrayManager;
+import cn.springcloud.gray.GrayClientConfig;
+import cn.springcloud.gray.InstanceLocalInfo;
+import cn.springcloud.gray.model.GrayInstance;
+import cn.springcloud.gray.model.GrayStatus;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.InitializingBean;
+
+
+@Slf4j
+public class GrayClientInitializingDestroyBean implements InitializingBean {
+
+ private CommunicableGrayManager grayManager;
+ private InstanceLocalInfo instanceLocalInfo;
+ private GrayClientConfig clientConfig;
+
+ public GrayClientInitializingDestroyBean(
+ CommunicableGrayManager grayManager, GrayClientConfig clientConfig, InstanceLocalInfo instanceLocalInfo) {
+ this.grayManager = grayManager;
+ this.clientConfig = clientConfig;
+ this.instanceLocalInfo = instanceLocalInfo;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (clientConfig.isGrayEnroll()) {
+ if (clientConfig.grayEnrollDealyTimeInMs() > 0) {
+ Thread t = new Thread(() -> {
+ try {
+ Thread.sleep(clientConfig.grayEnrollDealyTimeInMs());
+ } catch (InterruptedException e) {
+ }
+ log.info("灰度注册自身实例...");
+ grayRegister();
+ }, "GrayEnroll");
+ t.start();
+ } else {
+ grayRegister();
+ }
+ }
+ }
+
+ public void shutdown() {
+ if (instanceLocalInfo.isGray()) {
+ grayManager.getGrayInformationClient().serviceDownline(
+ instanceLocalInfo.getInstanceId());
+ }
+ }
+
+ private void grayRegister() {
+ GrayInstance grayInstance = new GrayInstance();
+ grayInstance.setHost(instanceLocalInfo.getHost());
+ grayInstance.setGrayStatus(GrayStatus.OPEN);
+ grayInstance.setInstanceId(instanceLocalInfo.getInstanceId());
+ grayInstance.setServiceId(instanceLocalInfo.getServiceId());
+ grayInstance.setPort(instanceLocalInfo.getPort());
+
+ grayManager.getGrayInformationClient().addGrayInstance(grayInstance);
+ instanceLocalInfo.setGray(true);
+ }
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayOptionalArgs.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayOptionalArgs.java
deleted file mode 100644
index d36221ef..00000000
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/GrayOptionalArgs.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package cn.springcloud.gray.client;
-
-import cn.springcloud.gray.core.GrayDecisionFactory;
-import cn.springcloud.gray.core.InformationClient;
-
-public class GrayOptionalArgs {
-
- private GrayClientConfig grayClientConfig;
- private InformationClient informationClient;
- private GrayDecisionFactory decisionFactory;
-
-
- public GrayClientConfig getGrayClientConfig() {
- return grayClientConfig;
- }
-
-
- public GrayDecisionFactory getDecisionFactory() {
- return decisionFactory;
- }
-
-
- public void setGrayClientConfig(GrayClientConfig grayClientConfig) {
- this.grayClientConfig = grayClientConfig;
- }
-
-
- public void setDecisionFactory(GrayDecisionFactory decisionFactory) {
- this.decisionFactory = decisionFactory;
- }
-
-
- public InformationClient getInformationClient() {
- return informationClient;
- }
-
- public void setInformationClient(InformationClient informationClient) {
- this.informationClient = informationClient;
- }
-}
-
-
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientAutoConfiguration.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientAutoConfiguration.java
index b3bab564..df498074 100644
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientAutoConfiguration.java
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientAutoConfiguration.java
@@ -1,87 +1,53 @@
package cn.springcloud.gray.client.config;
-import cn.springcloud.bamboo.BambooConstants;
-import cn.springcloud.bamboo.autoconfig.BambooAutoConfiguration;
-import cn.springcloud.gray.DefaultGrayManager;
-import cn.springcloud.gray.HttpInformationClient;
-import cn.springcloud.gray.RetryableInformationClient;
-import cn.springcloud.gray.client.GrayClientInitializingBean;
-import cn.springcloud.gray.client.GrayOptionalArgs;
+import cn.springcloud.gray.*;
+import cn.springcloud.gray.client.GrayClientInitializingDestroyBean;
import cn.springcloud.gray.client.config.properties.GrayClientProperties;
-import cn.springcloud.gray.core.GrayDecisionFactory;
-import cn.springcloud.gray.core.GrayManager;
-import cn.springcloud.gray.core.InformationClient;
-import cn.springcloud.gray.decision.DefaultGrayDecisionFactory;
+import cn.springcloud.gray.client.config.properties.GrayRequestProperties;
+import cn.springcloud.gray.decision.GrayDecisionFactoryKeeper;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import cn.springcloud.gray.request.ThreadLocalRequestStorage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.core.annotation.Order;
-import org.springframework.web.client.RestTemplate;
+import org.springframework.context.annotation.Import;
+
+import java.util.List;
@Configuration
-@EnableConfigurationProperties({GrayClientProperties.class})
+@EnableConfigurationProperties({GrayClientProperties.class, GrayRequestProperties.class})
@ConditionalOnBean(GrayClientMarkerConfiguration.GrayClientMarker.class)
-@RibbonClients(defaultConfiguration = GrayRibbonClientsConfiguration.class)
+@Import({GrayDecisionFactoryConfiguration.class, GrayTrackConfiguration.class})
public class GrayClientAutoConfiguration {
- @Bean
- public BambooAutoConfiguration.UnUseBambooIRule unUseBambooIRule() {
- return new BambooAutoConfiguration.UnUseBambooIRule();
- }
-
-
-// @Bean
-// public IRule ribbonRule(@Autowired(required = false) IClientConfig config) {
-// GrayLoadBalanceRule rule = new GrayLoadBalanceRule();
-// rule.initWithNiwsConfig(config);
-// return rule;
-// }
+ @Autowired
+ private GrayClientProperties grayClientProperties;
- @Bean
- @Order(value = BambooConstants.INITIALIZING_ORDER + 1)
- public GrayClientInitializingBean grayClientInitializingBean() {
- return new GrayClientInitializingBean();
- }
@Bean
@ConditionalOnMissingBean
- public GrayDecisionFactory grayDecisionFactory() {
- return new DefaultGrayDecisionFactory();
+ public GrayManager grayManager(GrayDecisionFactoryKeeper grayDecisionFactoryKeeper,
+ @Autowired(required = false) List requestInterceptors) {
+ return new DefaultGrayManager(grayClientProperties, grayDecisionFactoryKeeper, requestInterceptors);
}
- @Configuration
- @ConditionalOnProperty(prefix = "gray.client", value = "information-client", havingValue = "http", matchIfMissing
- = true)
- public static class HttpGrayManagerClientConfiguration {
- @Autowired
- private GrayClientProperties grayClientProperties;
-
- @Bean
- public InformationClient informationClient() {
- InformationClient client = new HttpInformationClient(grayClientProperties.getServerUrl(), new
- RestTemplate());
- if (!grayClientProperties.isRetryable()) {
- return client;
- }
- return new RetryableInformationClient(grayClientProperties.getRetryNumberOfRetries(), client);
- }
+ @Bean
+ @ConditionalOnBean({CommunicableGrayManager.class, InstanceLocalInfo.class})
+ public GrayClientInitializingDestroyBean grayClientInitializingDestroyBean(
+ CommunicableGrayManager grayManager, InstanceLocalInfo instanceLocalInfo) {
+ return new GrayClientInitializingDestroyBean(grayManager, grayClientProperties, instanceLocalInfo);
+ }
- @Bean
- public GrayManager grayManager(InformationClient informationClient, GrayDecisionFactory grayDecisionFactory) {
- GrayOptionalArgs args = new GrayOptionalArgs();
- args.setDecisionFactory(grayDecisionFactory);
- args.setGrayClientConfig(grayClientProperties);
- args.setInformationClient(informationClient);
- return new DefaultGrayManager(args);
- }
+ @Bean
+ @ConditionalOnMissingBean
+ public RequestLocalStorage requestLocalStorage() {
+ return new ThreadLocalRequestStorage();
}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientMarkerConfiguration.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientMarkerConfiguration.java
index 75f5449f..917a7379 100644
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientMarkerConfiguration.java
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientMarkerConfiguration.java
@@ -12,6 +12,6 @@ public GrayClientMarker grayClientMarker() {
return new GrayClientMarker();
}
- class GrayClientMarker {
+ public class GrayClientMarker {
}
}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientZookeeperAutoConfiguration.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientZookeeperAutoConfiguration.java
deleted file mode 100644
index b3e29e9d..00000000
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayClientZookeeperAutoConfiguration.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package cn.springcloud.gray.client.config;
-
-import cn.springcloud.gray.InstanceLocalInfo;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.cloud.client.serviceregistry.Registration;
-import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties;
-import org.springframework.cloud.zookeeper.serviceregistry.ZookeeperRegistration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import javax.naming.ConfigurationException;
-
-/**
- * @Author: duozl
- * @Date: 2018/6/5 18:18
- */
-@Configuration
-@ConditionalOnBean(ZookeeperRegistration.class)
-public class GrayClientZookeeperAutoConfiguration {
- private static final String METADATA_KEY_INSTANCE_ID = "instanceId";
-
- @Bean
- @ConditionalOnMissingBean
- public InstanceLocalInfo instanceLocalInfo(@Autowired Registration registration,
- @Autowired ZookeeperDiscoveryProperties properties)
- throws ConfigurationException {
- String instanceId;
- if (properties.getMetadata().containsKey(METADATA_KEY_INSTANCE_ID)) {
- instanceId = properties.getMetadata().get(METADATA_KEY_INSTANCE_ID);
- } else {
- throw new ConfigurationException("Unable to find config spring.cloud.zookeeper.discovery.metadata" +
- ".instanceId!");
- }
-
- InstanceLocalInfo localInfo = new InstanceLocalInfo();
- localInfo.setInstanceId(instanceId);
- localInfo.setServiceId(registration.getServiceId());
- localInfo.setGray(false);
- return localInfo;
- }
-}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayDecisionFactoryConfiguration.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayDecisionFactoryConfiguration.java
new file mode 100644
index 00000000..56068301
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayDecisionFactoryConfiguration.java
@@ -0,0 +1,56 @@
+package cn.springcloud.gray.client.config;
+
+import cn.springcloud.gray.decision.DefaultGrayDecisionFactoryKeeper;
+import cn.springcloud.gray.decision.GrayDecisionFactoryKeeper;
+import cn.springcloud.gray.decision.factory.*;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.convert.support.DefaultConversionService;
+import org.springframework.validation.Validator;
+
+import java.util.List;
+
+@Configuration
+public class GrayDecisionFactoryConfiguration {
+
+
+ @Configuration
+ public static class WebGrayDecisionFactoryConfiguration {
+
+ @Bean
+ public HttpHeaderGrayDecisionFactory httpHeaderGrayDecisionFactory() {
+ return new HttpHeaderGrayDecisionFactory();
+ }
+
+ @Bean
+ public HttpMethodGrayDecisionFactory httpMethodGrayDecisionFactory() {
+ return new HttpMethodGrayDecisionFactory();
+ }
+
+ @Bean
+ public HttpParameterGrayDecisionFactory httpParameterGrayDecisionFactory() {
+ return new HttpParameterGrayDecisionFactory();
+ }
+
+ @Bean
+ public TraceIpGrayDecisionFactory traceIpGrayDecisionFactory() {
+ return new TraceIpGrayDecisionFactory();
+ }
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public GrayDecisionFactoryKeeper grayDecisionFactoryKeeper(
+ List conversionServices, Validator validator, List decisionFactories) {
+ if (CollectionUtils.isNotEmpty(conversionServices)) {
+ return new DefaultGrayDecisionFactoryKeeper(conversionServices.get(0), validator, decisionFactories);
+ }
+ return new DefaultGrayDecisionFactoryKeeper(DefaultConversionService.getSharedInstance(), validator, decisionFactories);
+
+ }
+
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayEventAutoConfiguration.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayEventAutoConfiguration.java
new file mode 100644
index 00000000..b22a63cb
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayEventAutoConfiguration.java
@@ -0,0 +1,39 @@
+package cn.springcloud.gray.client.config;
+
+import cn.springcloud.gray.CommunicableGrayManager;
+import cn.springcloud.gray.event.DefaultGrayEventListener;
+import cn.springcloud.gray.event.GrayEventListener;
+import cn.springcloud.gray.event.stream.StreamInput;
+import cn.springcloud.gray.event.stream.StreamMessageListener;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.stream.annotation.EnableBinding;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class GrayEventAutoConfiguration {
+
+
+ @Bean
+ @ConditionalOnMissingBean
+ public GrayEventListener grayEventListener(CommunicableGrayManager grayManager) {
+ return new DefaultGrayEventListener(grayManager);
+ }
+
+
+ @Configuration
+ @ConditionalOnClass(EnableBinding.class)
+ @EnableBinding({StreamInput.class})
+ @ConditionalOnProperty(value = "spring.cloud.stream.bindings.GrayEventInput.destination")
+ public static class StreamEventConfiguration {
+
+ @Bean
+ public StreamMessageListener streamMessageListener(GrayEventListener grayEventListener) {
+ return new StreamMessageListener(grayEventListener);
+ }
+
+ }
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayLoadAutoConfigration.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayLoadAutoConfigration.java
new file mode 100644
index 00000000..3da51f44
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayLoadAutoConfigration.java
@@ -0,0 +1,36 @@
+package cn.springcloud.gray.client.config;
+
+import cn.springcloud.gray.GrayManager;
+import cn.springcloud.gray.client.config.properties.GrayLoadProperties;
+import cn.springcloud.gray.model.GrayStatus;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConditionalOnProperty(value = "gray.load.enabled", havingValue = "true")
+@EnableConfigurationProperties({GrayLoadProperties.class})
+public class GrayLoadAutoConfigration {
+
+ @Autowired
+ private GrayLoadProperties grayLoadProperties;
+ @Autowired
+ private GrayManager grayManager;
+
+
+ @Bean
+ public InitializingBean loadGrayInfoInitializing() {
+ return () -> {
+ grayLoadProperties.getGrayInstances().forEach(instance -> {
+ if (instance.getGrayStatus() == null) {
+ instance.setGrayStatus(GrayStatus.OPEN);
+ }
+ grayManager.updateGrayInstance(instance);
+ });
+ };
+ }
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayRibbonClientsConfiguration.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayRibbonClientsConfiguration.java
deleted file mode 100644
index 6ea1798f..00000000
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayRibbonClientsConfiguration.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package cn.springcloud.gray.client.config;
-
-import cn.springcloud.gray.ribbon.GrayLoadBalanceRule;
-import com.netflix.client.config.IClientConfig;
-import com.netflix.loadbalancer.IRule;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class GrayRibbonClientsConfiguration {
-
-
- @Bean
- public IRule ribbonRule(@Autowired(required = false) IClientConfig config) {
- GrayLoadBalanceRule rule = new GrayLoadBalanceRule();
- rule.initWithNiwsConfig(config);
- return rule;
- }
-}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayTrackConfiguration.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayTrackConfiguration.java
new file mode 100644
index 00000000..d2d86046
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/GrayTrackConfiguration.java
@@ -0,0 +1,137 @@
+package cn.springcloud.gray.client.config;
+
+
+import cn.springcloud.gray.client.config.properties.GrayTrackProperties;
+import cn.springcloud.gray.request.GrayHttpTrackInfo;
+import cn.springcloud.gray.request.GrayInfoTracker;
+import cn.springcloud.gray.request.RequestLocalStorage;
+import cn.springcloud.gray.web.GrayTrackFilter;
+import cn.springcloud.gray.web.GrayTrackRequestInterceptor;
+import cn.springcloud.gray.web.tracker.*;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@Configuration
+@ConditionalOnProperty(value = "gray.request.track.enabled", matchIfMissing = true)
+@EnableConfigurationProperties(GrayTrackProperties.class)
+public class GrayTrackConfiguration {
+
+
+ @ConditionalOnProperty(value = "gray.client.runenv", havingValue = "web", matchIfMissing = true)
+ @Configuration
+ public static class GrayClientWebConfiguration extends WebMvcConfigurerAdapter {
+
+ @Autowired
+ private GrayTrackProperties grayTrackProperties;
+
+ @Autowired
+ private List> trackors;
+
+ @Autowired
+ private RequestLocalStorage requestLocalStorage;
+
+// @Bean
+// @ConditionalOnMissingBean
+// public GrayTrackInterceptor grayTrackInterceptor() {
+// return new GrayTrackInterceptor(requestLocalStorage, trackors);
+// }
+
+// @Override
+// public void addInterceptors(InterceptorRegistry registry) {
+// InterceptorRegistration grayTrackRegistor = registry.addInterceptor(grayTrackInterceptor());
+// GrayTrackProperties.Web webProperties = grayTrackProperties.getWeb();
+//
+// for (String pattern : webProperties.getPathPatterns()) {
+// grayTrackRegistor.addPathPatterns(pattern);
+// }
+// for (String pattern : webProperties.getExcludePathPatterns()) {
+// grayTrackRegistor.excludePathPatterns(pattern);
+// }
+// }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public GrayTrackFilter grayTrackFilter() {
+ return new GrayTrackFilter(requestLocalStorage, trackors);
+ }
+
+
+ @Bean
+ public FilterRegistrationBean companyUrlFilterRegister(GrayTrackFilter filter) {
+ GrayTrackProperties.Web webProperties = grayTrackProperties.getWeb();
+ FilterRegistrationBean registration = new FilterRegistrationBean();
+ //注入过滤器
+ registration.setFilter(filter);
+ //拦截规则
+ for (String pattern : webProperties.getPathPatterns()) {
+ registration.addUrlPatterns(pattern);
+ }
+ //过滤器名称
+ registration.setName("GrayTrackFilter");
+ //过滤器顺序
+ registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
+ return registration;
+ }
+
+
+ @Bean
+ public HttpReceiveGrayTracker httpReceiveGrayTracker() {
+ return new HttpReceiveGrayTracker();
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "gray.request.track.web.need.headers")
+ public HttpHeaderGrayTracker httpHeaderGrayTracker() {
+ String header = grayTrackProperties.getWeb().getNeed().get(GrayTrackProperties.Web.NEED_HEADERS);
+ String[] headers = StringUtils.isEmpty(header) ? new String[]{} : header.split(",");
+ return new HttpHeaderGrayTracker(headers);
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "gray.request.track.web.need.method", havingValue = "enable")
+ public HttpMethodGrayTracker httpMethodGrayTracker() {
+ return new HttpMethodGrayTracker();
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "gray.request.track.web.need.uri", havingValue = "enable")
+ public HttpURIGrayTracker httpURIGrayTracker() {
+ return new HttpURIGrayTracker();
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "gray.request.track.web.need.ip", havingValue = "enable")
+ public HttpIPGrayTracker httpIPGrayTracker() {
+ return new HttpIPGrayTracker();
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "gray.request.track.web.need.parameters", havingValue = "enable")
+ public HttpParameterGrayTracker httpParameterGrayTracker() {
+ String name = grayTrackProperties.getWeb().getNeed().get(GrayTrackProperties.Web.NEED_PARAMETERS);
+ String[] names = StringUtils.isEmpty(name) ? new String[]{} : name.split(",");
+ return new HttpParameterGrayTracker(names);
+ }
+
+
+ @Bean
+ public GrayTrackRequestInterceptor grayTrackRequestInterceptor() {
+ return new GrayTrackRequestInterceptor(grayTrackProperties);
+ }
+
+ }
+
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayClientProperties.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayClientProperties.java
index 6a03ac86..1885b0fa 100644
--- a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayClientProperties.java
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayClientProperties.java
@@ -1,12 +1,13 @@
package cn.springcloud.gray.client.config.properties;
-import cn.springcloud.gray.RetryableInformationClient;
-import cn.springcloud.gray.client.GrayClientConfig;
+import cn.springcloud.gray.communication.RetryableInformationClient;
+import cn.springcloud.gray.GrayClientConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("gray.client")
public class GrayClientProperties implements GrayClientConfig {
+ private String runenv = "web";
private int serviceUpdateIntervalTimerInMs = 60000;
@@ -28,6 +29,11 @@ public void setInformationClient(String informationClient) {
this.informationClient = informationClient;
}
+ @Override
+ public String runenv() {
+ return runenv;
+ }
+
@Override
public boolean isGrayEnroll() {
return instance.isGrayEnroll();
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayLoadProperties.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayLoadProperties.java
new file mode 100644
index 00000000..7a5a2485
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayLoadProperties.java
@@ -0,0 +1,19 @@
+package cn.springcloud.gray.client.config.properties;
+
+import cn.springcloud.gray.model.GrayInstance;
+import cn.springcloud.gray.model.GrayStatus;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@ConfigurationProperties("gray.load")
+@Setter
+@Getter
+public class GrayLoadProperties {
+ private boolean enabled = false;
+ private List grayInstances = new ArrayList<>();
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayRequestProperties.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayRequestProperties.java
new file mode 100644
index 00000000..aeac6acd
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayRequestProperties.java
@@ -0,0 +1,14 @@
+package cn.springcloud.gray.client.config.properties;
+
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@Setter
+@Getter
+@ConfigurationProperties(prefix = "gray.request")
+public class GrayRequestProperties {
+ private boolean loadBody = false;
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayTrackProperties.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayTrackProperties.java
new file mode 100644
index 00000000..cadad69b
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/client/config/properties/GrayTrackProperties.java
@@ -0,0 +1,35 @@
+package cn.springcloud.gray.client.config.properties;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+@Setter
+@Getter
+@ConfigurationProperties(prefix = "gray.request.track")
+public class GrayTrackProperties {
+ private boolean enabled = true;
+ private String trackType = "web";
+ private Web web = new Web();
+
+ @Setter
+ @Getter
+ public static class Web {
+
+ public static final String NEED_URI = "uri";
+ public static final String NEED_IP = "ip";
+ public static final String NEED_METHOD = "method";
+ public static final String NEED_HEADERS = "headers";
+ public static final String NEED_PARAMETERS = "parameters";
+
+ private String[] pathPatterns = new String[]{"/*"};
+ private String[] excludePathPatterns = new String[]{};
+
+ private Map need = new HashMap<>();
+ }
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/communication/HttpInformationClient.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/communication/HttpInformationClient.java
new file mode 100644
index 00000000..4d1cd12d
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/communication/HttpInformationClient.java
@@ -0,0 +1,81 @@
+package cn.springcloud.gray.communication;
+
+import cn.springcloud.gray.model.GrayInstance;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class HttpInformationClient implements InformationClient {
+ private static final Logger log = LoggerFactory.getLogger(HttpInformationClient.class);
+ private final String baseUrl;
+ private RestTemplate rest;
+
+ public HttpInformationClient(String baseUrl) {
+ this(baseUrl, new RestTemplate());
+ }
+
+ public HttpInformationClient(String baseUrl, RestTemplate rest) {
+ this.baseUrl = baseUrl;
+ this.rest = rest;
+ }
+
+ @Override
+ public List allGrayInstances() {
+ String url = this.baseUrl + "/gray/instances/enable";
+ ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() {
+ };
+
+ try {
+ ResponseEntity> responseEntity = rest.exchange(url, HttpMethod.GET, null, typeRef);
+ return responseEntity.getBody();
+ } catch (RuntimeException e) {
+ log.error("获取灰度服务列表失败", e);
+ throw e;
+ }
+ }
+
+
+ @Override
+ public void addGrayInstance(GrayInstance grayInstance) {
+ String url = this.baseUrl + "/gray/instance/";
+ try {
+ rest.postForEntity(url, grayInstance, null);
+ } catch (RuntimeException e) {
+ log.error("灰度服务实例下线失败", e);
+ throw e;
+ }
+ }
+
+ @Override
+ public GrayInstance getGrayInstance(String serviceId, String instanceId) {
+ String url = this.baseUrl + "/gray/instance?serviceId={serviceId}&instanceId={instanceId}";
+ try {
+ ResponseEntity responseEntity =
+ rest.getForEntity(url, GrayInstance.class, serviceId, instanceId);
+ return responseEntity.getBody();
+ } catch (RuntimeException e) {
+ log.error("获取灰度实例", e);
+ throw e;
+ }
+ }
+
+ @Override
+ public void serviceDownline(String instanceId) {
+ String url = this.baseUrl + "/gray/instance/{id}/switchStatus?switch=0";
+ Map params = new HashMap<>();
+ params.put("instanceId", instanceId);
+ try {
+ rest.delete(url, params);
+ } catch (Exception e) {
+ log.error("灰度服务实例下线失败", e);
+ throw e;
+ }
+ }
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/communication/InformationClient.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/communication/InformationClient.java
new file mode 100644
index 00000000..e39febda
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/communication/InformationClient.java
@@ -0,0 +1,47 @@
+package cn.springcloud.gray.communication;
+
+import cn.springcloud.gray.model.GrayService;
+import cn.springcloud.gray.model.GrayInstance;
+
+import java.util.List;
+
+
+/**
+ * 该接口主要是负责和灰度服务端进行通信,获取灰度列表,编辑灰度实例等能力。其实现类HttpInformationClient默认使用http方式访问灰度服务端。
+ * 子类InformationClientDecorator是一个适配器类,RetryableInformationClient继承了InformationClientDecorator类,实现了重试的功能。
+ */
+public interface InformationClient {
+
+
+ /**
+ * 返回在灰度中注册的所有灰度实例(不包括非灰度实例)
+ *
+ * @return 所有的灰度实例
+ */
+ List allGrayInstances();
+
+ /**
+ * 注册灰度实例
+ *
+ * @param grayInstance 服务实例id
+ */
+ void addGrayInstance(GrayInstance grayInstance);
+
+ /**
+ * 获取灰度实例的信息
+ *
+ * @param serviceId 服务id
+ * @param instanceId 实例id
+ * @return
+ */
+ GrayInstance getGrayInstance(String serviceId, String instanceId);
+
+
+ /**
+ * 灰度实例下线
+ *
+ * @param instanceId 实例id
+ */
+ void serviceDownline(String instanceId);
+
+}
diff --git a/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/communication/InformationClientDecorator.java b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/communication/InformationClientDecorator.java
new file mode 100644
index 00000000..4e8e8992
--- /dev/null
+++ b/spring-cloud-gray-client/src/main/java/cn/springcloud/gray/communication/InformationClientDecorator.java
@@ -0,0 +1,95 @@
+package cn.springcloud.gray.communication;
+
+import cn.springcloud.gray.model.GrayInstance;
+
+import java.util.List;
+
+
+/**
+ * InformationClientDecorator是一个适配器类
+ */
+public abstract class InformationClientDecorator implements InformationClient {
+
+
+ public enum RequestType {
+ AddGrayInstance,
+ ServiceDownline,
+ AllGrayInstances,
+ GetGrayInstance
+ }
+
+
+ public interface RequestExecutor {
+ R execute(InformationClient delegate);
+
+ RequestType getRequestType();
+ }
+
+
+ protected abstract R execute(RequestExecutor requestExecutor);
+
+ @Override
+ public GrayInstance getGrayInstance(String serviceId, String instanceId) {
+ return execute(new RequestExecutor() {
+ @Override
+ public GrayInstance execute(InformationClient delegate) {
+ return delegate.getGrayInstance(serviceId, instanceId);
+ }
+
+ @Override
+ public RequestType getRequestType() {
+ return RequestType.GetGrayInstance;
+ }
+ });
+ }
+
+ @Override
+ public List allGrayInstances() {
+ return execute(new RequestExecutor>() {
+ @Override
+ public List execute(InformationClient delegate) {
+ return delegate.allGrayInstances();
+ }
+
+ @Override
+ public RequestType getRequestType() {
+ return RequestType.AllGrayInstances;
+ }
+ });
+ }
+
+
+ @Override
+ public void addGrayInstance(GrayInstance grayInstance) {
+
+ execute(new RequestExecutor