@@ -15,6 +15,30 @@ spring-cloud-gray-webui提供操作界面。
15
15
** 钉钉交流群**
16
16
<img src =" ./doc/discuss-group/C-Group-DingDing.png " width =" 25% " />
17
17
18
+ ## SpringCloudGray能做什么
19
+ 1 . 实现金丝雀测试
20
+
21
+ 先发布1台实例,用于测试验证,指定测试的流量进入这台实例,其它流量依然进入其它正常的实例。优势在于发布成本小,快速测试,并且不影响正常用户体验影响,即使测试不通过,也只需回滚这一台实例,用户无感知。
22
+
23
+ 2 . 实现灰度放量
24
+
25
+ 通过金丝雀测试后,可以逐渐放量到新的版本上。例如,根据userId或者ip放5%的流量到其中一台灰度实例上,观察一天没异常,可调整放入20%的流量,如果一台实例扛不住,可再发一台或多台实例。将发布产生的风险保持在可控范围内。
26
+
27
+ 3 . 切断实例流量
28
+
29
+ 当线上出现问题,可将某台实例的流量切断,保留现场,设置指定的请求进入实例,在线调试并且不影响其它用户。
30
+
31
+ 4 . 数据透传
32
+
33
+ 借助灰度追踪的能力,在网关处记录用户请求的最初的数据,可以将之透到整个请求的调用链。
34
+
35
+ 5 . 借助“破窗”能力,实例蓝绿发布
36
+
37
+ 首次上灰度时,会存在两种环境,一种是已经依赖了灰度客户端的环境,另一种是已经稳定运行的一段时间的当前环境。假如微服务的负载均衡是由ribbon实现,那么当前环境会请求路由到实例状态为UP的实例上,而依赖了灰度客户端的环境,则可以通过"破窗"能力,跟灰度路由结合,可以将匹配灰度策略的请求路由到实例状态为STARTING的实例上,不匹配灰度策略的请求路由到实例状态为UP的实例上。
38
+ 
39
+
40
+
41
+
18
42
## Demo
19
43
[ 点击查看] ( ./spring-cloud-gray-samples )
20
44
@@ -34,6 +58,101 @@ A.1.1.0 | Edgware.SR6 | 1.5.22.RELEASE
34
58
B.0.0.1 | Finchley.SR4 | 2.0.9.RELEASE
35
59
C.0.0.1-SNAPHOST | Greenwich.SR2 | 2.1.7.RELEASE
36
60
61
+
62
+
63
+ ## 设计思想
64
+ 在微服务架构中,接口的调用通常是服务消费方按照某种负载均衡策略去选择服务实例;但这无法满足线上更特殊化的一些路由逻辑,比如根据一次请求携带的请求头中的信息路由到某一个服务实例上。Spring Cloud Gray正是为此而创建。<br />
65
+ 在Spring Cloud Gray中定义了几个角色灰度客户端(gray-client)、灰度管控端(gray-server)、注册中心。<br />
66
+ ![ ] ( doc/img/gray.png )
67
+
68
+ ** 注册中心**
69
+ 负责服务的注册和发现。
70
+
71
+ ** 灰度客户端**
72
+ 灰度的客户端是指依赖了spring-cloud-gray-client的服务,一般是指服务消费方。
73
+
74
+ ** 灰度管控端**
75
+ 负责灰度信息的管理、持久化等维护工作。
76
+
77
+ 灰度客户端会从灰度管控端拉取一份灰度信息的清单,并在内存中维护这份清单信息,清单中包含服务,服务实例,灰度策略,灰度追踪字段等。
78
+ 当请求达到网关时,网关就会在灰度追踪中将需要透传的信息记录下来,并将传递给转发的服务实例,后面的接口调用也会按照同样的逻辑将追踪信息透传下去,从而保证所有一个请求在微服务调用链中的灰度路由。<br />
79
+ 如下图所示:
80
+
81
+ ![ ] ( doc/img/gray-all.png )
82
+
83
+
84
+ ## 管控端的功能
85
+ 1 . 用户管理
86
+
87
+ 可添加用户,禁用用户,重置密码等。
88
+ 
89
+ 2 . 服务列表
90
+ 
91
+ 3 . 权限控制
92
+
93
+ 灰度的权限控制是以服务为对象的,拥有服务的权限,就可以操作服务的所有灰度信息。在服务的权限控制中,分为两种角色,owner和管理者,owner拥有最大的权限,管理者除了不能删除owner的权限,其它权限同owner一样。
94
+ 
95
+ 
96
+ 4 . 灰度实例管理
97
+
98
+ 列出服务的灰度实例列表
99
+ 
100
+ 5 . 在线实例列表
101
+
102
+ 列出指定服务在注册中心注册的实例,点击【Add】按钮,可快速添加为实例实例
103
+ 
104
+
105
+ 6 . 编辑灰度策略
106
+
107
+ 从实例列表点击【策略】按钮进入灰度策略列表,可在策略列表中添加灰度策略和灰度决策。
108
+
109
+ 实例的灰度策略,包含可多个灰度决策。
110
+ 策略是从灰度实例列表进入。 一个实例可以有多个灰度策略,策略与策略之间是"或"的关系。就是说,一个请求只要 满足实例的任间一个灰度略,即表明请求被路由到该实例上。
111
+
112
+ 决策是灰度中进行比对的最小项。它定义一种规则,对请求进行比对,返回 true/false。 当请求到来时,灰度调用端可以根据灰度实例的灰度决策,进行对比,以判断灰度实例是否可以受理该请求。 多个决策是"与"的关系。
113
+ 
114
+ 
115
+
116
+
117
+ 7 . 编辑灰度追踪
118
+
119
+ 从服务列表点击【追踪】按钮进入
120
+ 
121
+
122
+ 8 . 改变实例状态
123
+
124
+ 可在实例列表中,通过【实例状态】按钮修改实例状态。提前是实例得依赖了灰度客户端的jar包,并且uri没有设置前缀
125
+ 
126
+
127
+ 9 . 操作审记
128
+
129
+ 所有的POST,PUT,DELETE操作都会被记录下来,可能通过操作记录查询,用于事后审计。
130
+ 
131
+
132
+ 查询维度包括:
133
+
134
+ * StartTime - EndTime
135
+ 记录时间
136
+
137
+ * Operator Id
138
+ 操作人Id
139
+
140
+ * ApiRes Code
141
+ 接口返回的ApiRes Code
142
+
143
+ * operate State
144
+ 操作的结果
145
+
146
+ * Request Hander
147
+ spring mvc 的接口(Controller/Handler)的类名和方法
148
+
149
+ * URI
150
+ 接口的uri
151
+
152
+ * IP
153
+ http请求(操作人)的ip
154
+
155
+
37
156
## 工程模块
38
157
功能模块
39
158
@@ -72,26 +191,6 @@ spring-cloud-gray-gateway-sample | spring-cloud-gateway网关示例(B版及以
72
191
73
192
74
193
75
- ## 设计
76
- 在微服务架构中,接口的调用通常是服务消费方按照某种负载均衡策略去选择服务实例;但这无法满足线上更特殊化的一些路由逻辑,比如根据一次请求携带的请求头中的信息路由到某一个服务实例上。Spring Cloud Gray正是为此而创建。<br />
77
- 在Spring Cloud Gray中定义了几个角色灰度客户端(gray-client)、灰度管控端(gray-server)、注册中心。<br />
78
- ![ Role] ( ./doc/img/gray.png )
79
-
80
- ** 注册中心**
81
- 负责服务的注册和发现。
82
-
83
- ** 灰度客户端**
84
- 灰度的客户端是指依赖了spring-cloud-gray-client的服务,一般是指服务消费方。
85
-
86
- ** 灰度管控端**
87
- 负责灰度信息的管理、持久化等维护工作。
88
-
89
- 灰度客户端会从灰度管控端拉取一份灰度信息的清单,并在内存中维护这份清单信息,清单中包含服务,服务实例,灰度策略,灰度追踪字段等。
90
- 当请求达到网关时,网关就会在灰度追踪中将需要透传的信息记录下来,并将传递给转发的服务实例,后面的接口调用也会按照同样的逻辑将追踪信息透传下去,从而保证所有一个请求在微服务调用链中的灰度路由。<br />
91
- 如下图所示:
92
-
93
- ![ ] ( ./doc/img/gray-all.png )
94
-
95
194
## 灰度决策
96
195
灰度决策是灰度路由的关键,灰度决策由工厂类创建,工厂类的抽象接口是cn.springcloud.gray.decision.factory.GrayDecisionFactory。<br >
97
196
目前已有的灰度决策有:
@@ -107,6 +206,48 @@ TraceIpGray | TraceIpGrayDecisionFactory | 根据灰度追踪记录的请求ip
107
206
TrackAttribute | TrackAttributeGrayDecisionFactory | 根据灰度追踪记录的属性值进行判断
108
207
FlowRateGray | FlowRateGrayDecisionFactory | 按百分比放量进行判断
109
208
209
+ ### 自定义灰度决策实现
210
+ 如果上面这些决策还不能满足需求,那么可以扩展` cn.springcloud.gray.decision.factory.GrayDecisionFactory ` ,实现自定义的逻辑,发布到spring 容器中即可。如:
211
+ ``` java
212
+ import cn.springcloud.gray.decision.GrayDecision ;
213
+ import cn.springcloud.gray.decision.factory.AbstractGrayDecisionFactory ;
214
+ import cn.springcloud.gray.request.GrayHttpTrackInfo ;
215
+ import lombok.Getter ;
216
+ import lombok.Setter ;
217
+ import org.apache.commons.lang3.StringUtils ;
218
+ import org.springframework.stereotype.Component ;
219
+
220
+ @Component
221
+ public class VersionGrayDecisionFactory extends AbstractGrayDecisionFactory<VersionGrayDecisionFactory .Config > {
222
+
223
+ public VersionGrayDecisionFactory () {
224
+ super (VersionGrayDecisionFactory . Config . class);
225
+ }
226
+
227
+ @Override
228
+ public GrayDecision apply (Config configBean ) {
229
+ return args - > {
230
+ GrayHttpTrackInfo grayRequest = (GrayHttpTrackInfo ) args. getGrayRequest(). getGrayTrackInfo();
231
+ int version = StringUtils . defaultIfNull(grayRequest. getAttribute(USER_ID_PARAM_NAME ), " 0" );
232
+ if (StringUtils . equal(configBean. getCompareMode(), " LT" )){
233
+ return configBean. getVersion() > version;
234
+ }else if (StringUtils . equal(configBean. getCompareMode(), " GT" )){
235
+ return configBean. getVersion() < version;
236
+ }else {
237
+ return configBean. getVersion() == version;
238
+ }
239
+ };
240
+ }
241
+
242
+ @Setter
243
+ @Getter
244
+ public static class Config {
245
+ private String compareMode;
246
+ private int varsion;
247
+ }
248
+ }
249
+ ```
250
+
110
251
## 灰度追踪
111
252
灰度追踪记录的逻辑是由cn.springcloud.gray.request.GrayInfoTracker的实现类实现。<br />
112
253
目前已有的灰度追踪有:
@@ -120,3 +261,41 @@ HttpMethod | HttpMethodGrayInfoTracker | 获取http请求的请求方法并记
120
261
HttpParameter | HttpParameterGrayInfoTracker | 获取http请求的url参数并记录到灰度追踪的parameter中
121
262
HttpURI | HttpURIGrayInfoTracker | 获取http请求的URI并记录到灰度追踪中
122
263
264
+ ### 自定义灰度追踪实现
265
+ 如果上面这些决策还不能满足需求,那么可以扩展` cn.springcloud.gray.request.GrayInfoTracker ` ,实现自定义的逻辑,发布到spring 容器中即可。如:
266
+ ``` java
267
+ import cn.springcloud.gray.request.GrayHttpTrackInfo ;
268
+ import cn.springcloud.gray.request.TrackArgs ;
269
+ import cn.springcloud.gray.web.tracker.HttpGrayInfoTracker ;
270
+ import lombok.extern.slf4j.Slf4j ;
271
+ import org.springframework.security.core.Authentication ;
272
+ import org.springframework.security.core.context.SecurityContext ;
273
+ import org.springframework.security.core.context.SecurityContextHolder ;
274
+ import org.springframework.security.core.userdetails.UserDetails ;
275
+ import org.springframework.stereotype.Component ;
276
+ import javax.servlet.http.HttpServletRequest ;
277
+
278
+ @Slf4j
279
+ @Component
280
+ public class UserIdGrayInfoTracker implements HttpGrayInfoTracker {
281
+
282
+ @Override
283
+ public void call (TrackArgs<GrayHttpTrackInfo , HttpServletRequest > args ) {
284
+ SecurityContext securityContext = SecurityContextHolder . getContext();
285
+ Authentication authentication = securityContext. getAuthentication();
286
+ String userId = null ;
287
+ if (authentication. getPrincipal() instanceof UserDetails ) {
288
+ UserDetails springSecurityUser = (UserDetails ) authentication. getPrincipal();
289
+ userId = springSecurityUser. getUsername();
290
+ } else if (authentication. getPrincipal() instanceof String ) {
291
+ userId = (String ) authentication. getPrincipal();
292
+ }
293
+ args. getTrackInfo(). setAttribute(" userId" , userId);
294
+ }
295
+ }
296
+ ```
297
+
298
+ ## 项目扩展
299
+ 项目已经实现了灰度的内核,如果要与其它的注册中心或者负载均衡中间件集成,只需实现相应的plugin即可,spring cloud gray已经提供了eureka、ribbon、feign、zuul以及spring cloud gateway和spring cloud stream的plugin,只需添加相应的plugin依赖即可。
300
+
301
+
0 commit comments