Skip to content

Commit 1377f02

Browse files
committed
readme
1 parent f82859a commit 1377f02

File tree

3 files changed

+2
-465
lines changed

3 files changed

+2
-465
lines changed

README.md

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,52 +7,11 @@
77

88

99

10-
#### 多版本控制
11-
* [spring-cloud-bamboo](spring-cloud-bamboo/README.md)
12-
* spring-cloud-start-multi-version
13-
* [spring-cloud-mult-version-samples](spring-cloud-mult-version-samples/README.md)
14-
15-
16-
17-
#### 灰度发布
18-
* [spring-cloud-gray-core](spring-cloud-gray-core/README.md)
19-
* spring-cloud-gray-client
20-
* spring-cloud-gray-server
21-
* spring-cloud-start-gray
22-
* spring-cloud-start-gray-server
23-
* [spring-cloud-gray-samples](spring-cloud-gray-samples/README.md)
24-
* [spring-cloud-gray-zookeeper-samples](spring-cloud-gray-zookeeper-samples/README.md)
25-
26-
#### maven 依赖
27-
jar包已经上传到maven中央库,可以通过maven直接从中央库下载
28-
```xml
29-
<!-- 版本控制 -->
30-
<dependency>
31-
<groupId>cn.springcloud.gray</groupId>
32-
<artifactId>spring-cloud-starter-multi-version</artifactId>
33-
<version>{version}</version>
34-
</dependency>
35-
36-
<!-- gray-client -->
37-
<dependency>
38-
<groupId>cn.springcloud.gray</groupId>
39-
<artifactId>spring-cloud-starter-gray</artifactId>
40-
<version>{version}</version>
41-
</dependency>
42-
43-
<!-- gray-server -->
44-
<dependency>
45-
<groupId>cn.springcloud.gray</groupId>
46-
<artifactId>spring-cloud-starter-gray-server</artifactId>
47-
<version>{version}</version>
48-
</dependency>
49-
```
50-
5110

5211
#### 不足
53-
gray目前只有灰度管理的基本功能, 像数据持久化,高可用,推送灰度调整消息等, 都没有实现。 也没有界面化, 仅仅只有接口列表。
12+
没有界面化, 仅仅只有接口列表。
5413

5514

5615
#### 扩展思考
57-
gray目前仅仅只支持spring cloud eureka, 但是在spring cloud中,eureka只是做为其中一个注册中心, 如果要做spring cloud的灰度管理, 就还需要兼容其他的注册中心, 比如zookeeper, consul等
16+
gray目前仅仅只支持spring cloud eureka, 但是在spring cloud中,eureka只是做为其中一个注册中心, 如果要做spring cloud的灰度管理, 就还需要兼容其他的注册中心, 比如zookeeper, consul, nacos等
5817

spring-cloud-gray-core/README.md

Lines changed: 0 additions & 280 deletions
Original file line numberDiff line numberDiff line change
@@ -1,280 +0,0 @@
1-
## 灰度发布
2-
灰度发布是在多版本控制的基础上进一步扩展实现出来的项目 -> fm-cloud-graybunny,抽象出灰度服务、灰度服务实例、灰度策略、灰度决策等。支持A/B test, 金丝雀 test。 灰度策略可以从request ip, request patameter, request header等方面进行去创建,也可以根据bamboo的LoadBalanceRequestTrigger结合graybuanny的接口去扩展灰度策略和灰度决策。
3-
4-
##### 场景
5-
6-
有两个服务,共四个服务实例,分别是ServiceA-1, ServiceA-2, ServiceB-1。其中ServiceA-2是灰度实例。
7-
场景1:所有请求头usertype:old,ip:10.217.***.***的请求或者请求头usertype:test, url 参数action:create的请求,都会被转发到的灰度服务ServiceA-2 。
8-
场景2:ServiceA-2通过一段时间的观察,判定运行稳定,开始ServiceA-2删除灰度标记,开始和ServiceA-1一样会加入正常的负载均衡规则当中。
9-
场景3:服务ServiceB发布新版本,ServiceB-2需要灰度注册,注册成功后所有的请求不能转发到ServiceB-2, 在为ServiceB-2设置灰度策略后,符合策略的请求才会被转发到ServiceB-2上。
10-
11-
12-
##### 思路
13-
14-
从上面的场景分析,可以归纳出两个对象:服务实例和调用请求;服务实例的灰度管理是基础,调用请求时如何决策路由,都是根据服务实例的灰度策略去判断的。既然有灰度管理这个概念,那么从功能上分,就会有client-server之分,所以又可以从gray-client和gray-server去分析。接下来将一步一步去分析这四个方面。
15-
16-
* 灰度实例
17-
18-
![灰度实例](../doc/img/u-case-service-instance.png)
19-
20-
实例注册:服务实例添加到灰度管理中。
21-
实例下线:服务实例下线,从灰度管理中删除。
22-
灰度开关:调整服务实例的灰度状态,有启用、禁用两个状态,禁用的实例不纳入灰度列表中。
23-
灰度策略:请求是否可以被转发到该服务实例的条件,只有通过,请求才有可能会被转发到该实例上。
24-
25-
* 调用请求
26-
27-
![调用请求](../doc/img/u-case-call-request.png)
28-
29-
灰度决策:根据请求的信息去匹配灰度服务实例的灰度策略,如果匹配上,会将服务实例加入到通过列表中。如果都没有匹配上,就按bamboo的路由规则去筛选非灰度的服务实例进行转发。
30-
31-
32-
* 灰度客户端
33-
34-
调用请求的服务消费者,和提供服务的服务提供者都可以是灰度客户端,因为微服务中,大多服务实例既是服务提供者,同时也是服务消费者。
35-
36-
![灰度客户端](../doc/img/u-case-gray-client.png)
37-
38-
灰度服务注册:服务实例在启动时,就会向灰度服务端发起请求,将实例自身的灰度开关打开。
39-
灰度服务下线:在服务实例下线前,会触发钩子,向灰度服务端发起请求将实例自身从灰度列表中删除。
40-
接收灰度实例调整消息:接收由灰度服务端推送过来的灰度列表更新消息比如新增灰度实例,删除灰度实例等,维护缓存在实例上的灰度列表。
41-
定时拉取灰度列表:定时从灰度服务端拉取最新的灰度列表,维护实例自身缓存的灰度列表。
42-
43-
* 灰度服务端
44-
45-
灰度服务端负表维护灰度列表,可以新增、删除、编辑灰度信息。
46-
47-
![灰度服务端](../doc/img/u-case-gray-server.png)
48-
49-
编辑灰度实例:新增灰度实例,删除灰度实例,修改实例灰度状态。
50-
编辑灰度策略:新增实例灰度策略,删除实例灰度策略,修改灰度策略状态。
51-
推送灰度服务调整消息:向灰度客户端推送灰度列表变动消息,比如新增灰度实例,删除灰度实例,修改实例灰度状态等。
52-
定时检查服务实例是否下线:定时检查灰度服务实例是否下线,下线的的实例将从灰度列表中删除。
53-
54-
55-
56-
##### 代码设计
57-
根据上面的思路,设计以下对象和接口。共6个接口,4个模型对象。
58-
59-
![灰度代码设计](../doc/img/cd-gray.png)
60-
61-
对象:
62-
* GrayService: 灰度服务
63-
* GrayInstance: 灰度实例,有状态属性
64-
* GrayPolicyGroup: 灰度策略组,有状态属性
65-
* GrayPolicy: 灰度策略
66-
67-
接口:
68-
* GrayManager:
69-
灰度客户端管理器,维护灰度列表,维护自身灰度状态,创建灰度决策对象。抽象实现类AbstractGrayManager实现了基础的获取灰度列表, 创建灰度决策对象的能力。BaseGrayManger在期基础上进行了扩展,将灰度列表缓存起来,定时从灰度服务端更新灰度列表。
70-
71-
* InformationClient:
72-
该接口主要是负责和灰度服务端进行通信,获取灰度列表,编辑灰度实例等能力。其实现类HttpInformationClient默认使用http方式访问灰度服务端。
73-
子类InformationClientDecorator是一个适配器类,RetryableInformationClient继承了InformationClientDecorator类,实现了重试的功能。
74-
75-
* GrayDecision:
76-
该接口是灰度决策,用来判断请求是否匹配灰度策略。实现了ip匹配、request parameter匹配、request header匹配、BambooRequestContext中的参数匹配器以及合并匹配等多个匹配能力。
77-
78-
* GrayDecisionFactory:
79-
灰度决策的工厂类,其默认实现类支持上述几种灰度决策的创建。
80-
81-
* GrayServiceManager:
82-
灰度服务管理类,属于服务端的类。主要是编辑服务实例,编辑灰度策略,以及维护最新的灰度列表。
83-
84-
* GrayBunnyServerEvictor:
85-
如果灰度服务实例下线后, 由于意外情况,没有向灰度服务端发送删除请求, 服务端会每隔一段时间调用该接口的方法,检查灰度列表中的实例是否下线,如果实例已下线,就将其从灰度列表中删除。
86-
87-
88-
##### 代码实现
89-
将模型抽象成接口和对象设计出来之后,实现思路就清晰了。
90-
91-
* 灰度路由:
92-
灰度路由是客户端必须要实现的能力,gray是在bamboo的基础上扩展的,所以gray的路由规则对象GrayLoadBalanceRule继承了BambooZoneAvoidanceRule,
93-
逻辑是这样的:
94-
1、 判断目标服务是否有灰度实例。
95-
2.1、 如果没有, 执行父类逻辑。结束。
96-
2.2、 有灰度实例,先将灰度实例和非灰度实例筛选出来。
97-
3、 挑选灰度实例, 筛选调用请求匹配上灰度实例的策略。
98-
4.1、 如果没有匹配的灰度实例, 将非灰度实例列表传递过去执行父类的筛选逻辑。结束。
99-
4.2、 如果有匹配的灰度实例, 从其中按轮询的方式挑选出一个实例。结束。
100-
```java
101-
/**
102-
* 灰度发布的负载规则
103-
*/
104-
public class GrayLoadBalanceRule extends BambooZoneAvoidanceRule {
105-
106-
protected CompositePredicate grayCompositePredicate;
107-
108-
public GrayLoadBalanceRule() {
109-
super();
110-
GrayDecisionPredicate apiVersionPredicate = new GrayDecisionPredicate(this);
111-
grayCompositePredicate = CompositePredicate.withPredicates(super.getPredicate(),
112-
apiVersionPredicate).build();
113-
}
114-
115-
116-
@Override
117-
public Server choose(Object key) {
118-
ILoadBalancer lb = getLoadBalancer();
119-
BambooRequestContext requestContext = BambooRequestContext.currentRequestCentxt();
120-
if (requestContext != null && getGrayManager().isOpen(requestContext.getServiceId())) {
121-
GrayService grayService = getGrayManager().grayService(requestContext.getServiceId());
122-
List<Server> servers = lb.getAllServers();
123-
List<Server> grayServers = new ArrayList<>(grayService.getGrayInstances().size());
124-
List<Server> normalServers = new ArrayList<>(servers.size() - grayService.getGrayInstances().size());
125-
for (Server server : servers) {
126-
DiscoveryEnabledServer disServer = (DiscoveryEnabledServer) server;
127-
if (grayService.getGrayInstance(disServer.getInstanceInfo().getInstanceId()) != null) {
128-
grayServers.add(server);
129-
} else {
130-
normalServers.add(server);
131-
}
132-
}
133-
134-
Optional<Server> server = grayCompositePredicate.chooseRoundRobinAfterFiltering(grayServers, key);
135-
if (server.isPresent()) {
136-
return server.get();
137-
} else {
138-
return choose(super.getPredicate(), normalServers, key);
139-
}
140-
}
141-
return super.choose(key);
142-
}
143-
144-
145-
private Server choose(AbstractServerPredicate serverPredicate, List<Server> servers, Object key) {
146-
Optional<Server> server = serverPredicate.chooseRoundRobinAfterFiltering(servers, key);
147-
if (server.isPresent()) {
148-
return server.get();
149-
} else {
150-
return null;
151-
}
152-
}
153-
154-
155-
public GrayManager getGrayManager() {
156-
return GrayClientAppContext.getGrayManager();
157-
}
158-
}
159-
160-
```
161-
162-
灰度决策的执行代码在GrayDecisionPredicate中
163-
164-
```java
165-
public class GrayDecisionPredicate extends AbstractServerPredicate {
166-
167-
public GrayDecisionPredicate(GrayLoadBalanceRule rule) {
168-
super(rule);
169-
}
170-
171-
@Override
172-
public boolean apply(PredicateKey input) {
173-
BambooRequestContext bambooRequestContext = BambooRequestContext.currentRequestCentxt();
174-
if (bambooRequestContext == null || bambooRequestContext.getBambooRequest() == null) {
175-
return false;
176-
}
177-
DiscoveryEnabledServer server = (DiscoveryEnabledServer) input.getServer();
178-
BambooRequest bambooRequest = bambooRequestContext.getBambooRequest();
179-
List<GrayDecision> grayDecisions =
180-
getIRule().getGrayManager().grayDecision(bambooRequest.getServiceId(), server.getInstanceInfo().getInstanceId());
181-
for (GrayDecision grayDecision : grayDecisions) {
182-
if (grayDecision.test(bambooRequest)) {
183-
return true;
184-
}
185-
}
186-
return false;
187-
}
188-
189-
190-
protected GrayLoadBalanceRule getIRule() {
191-
return (GrayLoadBalanceRule) this.rule;
192-
}
193-
}
194-
```
195-
196-
* 灰度管理:
197-
灰度管理是灰度服务端的功能,主要是维护灰度列表。其实现类DefaultGrayServiceManager有一个Map, 用来维护GrayService,key是service id。并且每隔一段时间就调用EurekaGrayServerEvictor,检查列表中的实例是否下线,将下线的服务从灰度列表中删除。
198-
199-
```java
200-
public class DefaultGrayServiceManager implements GrayServiceManager {
201-
202-
203-
private Map<String, GrayService> grayServiceMap = new ConcurrentHashMap<>();
204-
private Timer evictionTimer = new Timer("Gray-EvictionTimer", true);
205-
206-
//...
207-
208-
@Override
209-
public void openForWork() {
210-
evictionTimer.schedule(new EvictionTask(),
211-
serverConfig.getEvictionIntervalTimerInMs(),
212-
serverConfig.getEvictionIntervalTimerInMs());
213-
}
214-
215-
@Override
216-
public void shutdown() {
217-
evictionTimer.cancel();
218-
}
219-
220-
221-
protected void evict() {
222-
GrayServerContext.getGrayServerEvictor().evict(this);
223-
}
224-
225-
226-
class EvictionTask extends TimerTask {
227-
228-
@Override
229-
public void run() {
230-
evict();
231-
}
232-
}
233-
234-
}
235-
236-
```
237-
238-
EurekaGrayServerEvictor是依赖EurekaClient来检查服务实例是否下线。
239-
240-
```java
241-
public class EurekaGrayServerEvictor implements GrayServerEvictor {
242-
243-
private EurekaClient eurekaClient;
244-
245-
246-
public EurekaGrayServerEvictor(EurekaClient eurekaClient) {
247-
this.eurekaClient = eurekaClient;
248-
}
249-
250-
@Override
251-
public void evict(GrayServiceManager serviceManager) {
252-
Collection<GrayService> grayServices = serviceManager.allGrayService();
253-
grayServices.forEach(grayService -> {
254-
grayService.getGrayInstances().forEach(grayInstance -> {
255-
evict(serviceManager, grayInstance);
256-
});
257-
});
258-
259-
}
260-
261-
262-
private void evict(GrayServiceManager serviceManager, GrayInstance grayInstance) {
263-
if (isDownline(grayInstance)) {
264-
serviceManager.deleteGrayInstance(grayInstance.getServiceId(), grayInstance.getInstanceId());
265-
}
266-
}
267-
268-
269-
private boolean isDownline(GrayInstance grayInstance) {
270-
Application app = eurekaClient.getApplication(grayInstance.getServiceId());
271-
return app == null || app.getByInstanceId(grayInstance.getInstanceId()) == null;
272-
}
273-
274-
}
275-
```
276-
277-
278-
279-
##### 使用说明
280-
灰度发布 --> [spring-cloud-gray-samples](../spring-cloud-gray-samples/README.md)

0 commit comments

Comments
 (0)