接着上篇说,我们微服务中用到的nepxion discovery主要采用了三种灰度释出方式,一种是web图形化界面释出,二是zuul过滤器灰度释出,三是业务引数策略灰度释出。下面将重点介绍三种方式的实现。
一、web图形化界面灰度释出
因为我们专案用到了eureka注册中心,所以选择web图形化界面灰度释出比较合适。
1) 首先需要建立一个discovery控制台工程console, 埠为2222,控制台工程负责web图形化界面请求的处理,执行console工程。
2) 下载discovery ui,地址:https://github.com/Nepxion/DiscoveryUI,执行discovery UI,埠为8090
3)浏览器中输入localhost:8090,即可开启控制台,如下
登陆页面
服务拓扑图
注意:全链路灰度释出需要在“配置中心”下才可用。灰度释出配置中心,负责储存全链路灰度释出规则,并将规则推送到各个微服务中。而配置中心可用nacos,redis等,Discovery 中提供了相应配置中心的外挂包。
二、zuul闸道器过滤器灰度释出
通过闸道器过滤器传递Http Header的方式传递全链路灰度路由规则。下面程式码只适用于Zuul和Spring Cloud Gateway闸道器,Service微服务不需要加该方式。
/ 适用于A/B Testing或者更根据某业务引数决定灰度路由路径。可以结合配置中心分别配置A/B两条路径,可以动态改变并通知
// 当Header中传来的使用者为张三,执行一条路由路径;为李四,执行另一条路由路径
public class MyRouteFilter extends DefaultZuulStrategyRouteFilter {
private static final String DEFAULT_A_ROUTE_VERSION = "{"discovery-gray-service-a":"1.0", "discovery-gray-service-b":"1.1"}";
private static final String DEFAULT_B_ROUTE_VERSION = "{"discovery-gray-service-a":"1.1", "discovery-gray-service-b":"1.0"}";
@Value("${a.route.version:" + DEFAULT_A_ROUTE_VERSION + "}")
private String aRouteVersion;
@Value("${b.route.version:" + DEFAULT_B_ROUTE_VERSION + "}")
private String bRouteVersion;
@Override
public String getRouteVersion() {
String user = strategyContextHolder.getHeader("user");
if (StringUtils.equals(user, "zhangsan")) {
return aRouteVersion;
} else if (StringUtils.equals(user, "lisi")) {
return bRouteVersion;
}
return super.getRouteVersion();
}
}
三、业务引数在策略类中自定义灰度路由规则
通过策略方式自定义灰度路由规则。下面程式码既适用于Zuul和Spring Cloud Gateway闸道器,也适用于Service微服务,同时全链路中闸道器和服务都必须加该方式
// 实现了组合策略,版本路由策略+区域路由策略+IP和埠路由策略+自定义策略
public class DiscoveryGrayEnabledStrategy extends AbstractDiscoveryEnabledStrategy {
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryGrayEnabledStrategy.class);
@Override
public boolean apply(Server server) {
// 对Rest呼叫传来的Header引数(例如:mobile)做策略
String mobile = strategyContextHolder.getHeader("mobile");
String serviceId = pluginAdapter.getServerServiceId(server);
String version = pluginAdapter.getServerMetadata(server).get(DiscoveryConstant.VERSION);
LOG.info("负载均衡使用者定制触发:mobile={}, serviceId={}, version={}", mobile, serviceId, version);
if (StringUtils.isNotEmpty(mobile)) {
// 手机号以移动138开头,路由到1.0版本的服务上
if (mobile.startsWith("138") && StringUtils.equals(version, "1.0")) {
return true;
// 手机号以联通133开头,路由到2.0版本的服务上
} else if (mobile.startsWith("133") && StringUtils.equals(version, "1.1")) {
return true;
} else {
// 其它情况,直接拒绝请求
return false;
}
}
return true;
}
}
上面说了具体灰度规则释出方式,那究竟怎么定义灰度规则呢??
规则是基于XML或者Json为配置方式,储存于本地档案或者远端配置中心,可以通过远端配置中心修改的方式达到规则动态化。其核心程式码参考discovery-plugin-framework以及它的扩充套件、discovery-plugin-config-center以及它的扩充套件和discovery-plugin-admin-center等,规则示例
XML示例(Json示例见discovery-springcloud-example-service下的rule.json)
黑/白名单的IP地址注册的过滤规则
微服务启动的时候,禁止指定的IP地址注册到服务注册发现中心。支援黑/白名单,白名单表示只允许指定IP地址字首注册,黑名单表示不允许指定IP地址字首注册。规则如何使用,见示例说明
全域性过滤,指注册到服务注册发现中心的所有微服务,只有IP地址包含在全域性过滤字段的字首中,都允许注册(对于白名单而言),或者不允许注册(对于黑名单而言)区域性过滤,指专门针对某个微服务而言,那么真正的过滤条件是全域性过滤+区域性过滤结合在一起最大注册数的限制的过滤规则
微服务启动的时候,一旦微服务丛集下注册的例项数目已经达到上限(可配置),将禁止后续的微服务进行注册。规则如何使用,见示例说明
全域性配置值,只下面配置所有的微服务丛集,最多能注册多少个区域性配置值,指专门针对某个微服务而言,那么该值如存在,全域性配置值失效黑/白名单的IP地址发现的过滤规则
微服务启动的时候,禁止指定的IP地址被服务发现。它使用的方式和“黑/白名单的IP地址注册的过滤规则”一致
版本访问的灰度释出规则
1. 标准配置,举例如下
表示消费端1.0版本,允许访问提供端1.0和1.1版本
2. 版本值不配置,举例如下
表示消费端任何版本,允许访问提供端1.0和1.1版本
表示消费端1.0版本,允许访问提供端任何版本
表示消费端任何版本,允许访问提供端任何版本
3. 版本值空字串,举例如下
表示消费端任何版本,允许访问提供端1.0和1.1版本
表示消费端1.0版本,允许访问提供端任何版本
表示消费端任何版本,允许访问提供端任何版本
4. 版本对应关系未定义,预设消费端任何版本,允许访问提供端任何版本
特殊情况处理,在使用上需要极力避免该情况发生
1. 消费端的application.properties未定义版本号,则该消费端可以访问提供端任何版本
2. 提供端的application.properties未定义版本号,当消费端在xml里不做任何版本配置,才可以访问该提供端
版本权重的灰度释出规则
1. 标准配置,举例如下
表示消费端访问提供端的时候,提供端的1.0版本提供90%的权重流量,1.1版本提供10%的权重流量
表示所有消费端访问提供端的时候,提供端的1.0版本提供90%的权重流量,1.1版本提供10%的权重流量
2. 区域性配置,即指定consumer-service-name,专门为该消费端配置权重。全域性配置,即不指定consumer-service-name,为所有消费端配置相同情形的权重。当局部配置和全域性配置同时存在的时候,以区域性配置优先
3. 尽量为线上所有版本都赋予权重值
全域性版本权重的灰度释出规则
1. 标准配置,举例如下
表示版本为1.0的服务提供85%的权重流量,版本为1.1的服务提供15%的权重流量
2. 全域性版本权重可以切换整条呼叫链的权重配比
3. 尽量为线上所有版本都赋予权重值
区域权重的灰度释出规则
1. 标准配置,举例如下
表示消费端访问提供端的时候,区域为dev的服务提供85%的权重流量,区域为qa的服务提供15%的权重流量
表示所有消费端访问提供端的时候,区域为dev的服务提供85%的权重流量,区域为qa的服务提供15%的权重流量
2. 区域性配置,即指定consumer-service-name,专门为该消费端配置权重。全域性配置,即不指定consumer-service-name,为所有消费端配置相同情形的权重。当局部配置和全域性配置同时存在的时候,以区域性配置优先
3. 尽量为线上所有版本都赋予权重值
全域性区域权重的灰度释出规则
1. 标准配置,举例如下
表示区域为dev的服务提供85%的权重流量,区域为qa的服务提供15%的权重流量
2. 全域性区域权重可以切换整条呼叫链的权重配比
3. 尽量为线上所有区域都赋予权重值
闸道器端全链路路由策略的灰度释出规则
1. 标准配置,举例如下
{"discovery-springcloud-example-a":"1.0", "discovery-springcloud-example-b":"1.0", "discovery-springcloud-example-c":"1.0;1.2"}
{"discovery-springcloud-example-a":"qa;dev", "discovery-springcloud-example-b":"dev", "discovery-springcloud-example-c":"qa"}
{"discovery-springcloud-example-a":"192.168.43.101:1100", "discovery-springcloud-example-b":"192.168.43.101:1201", "discovery-springcloud-example-c":"192.168.43.101:1300"}
{"discovery-springcloud-example-a":"1.0=90;1.1=10", "discovery-springcloud-example-b":"1.0=90;1.1=10", "discovery-springcloud-example-c":"1.0=90;1.1=10"}
{"discovery-springcloud-example-a":"dev=85;qa=15", "discovery-springcloud-example-b":"dev=85;qa=15", "discovery-springcloud-example-c":"dev=85;qa=15"}
2. 用法和基于Http Header头部传路由引数一致。前置是通过前端或者闸道器传入,后者是配置在配置档案里。让两者全部启用的时候,以前端或者闸道器传入Header方式优先
注意 路由策略的入口有三个(以{"discovery-springcloud-example-a":"1.0", "discovery-springcloud-example-b":"1.0", "discovery-springcloud-example-c":"1.0;1.2"})为例:
从外界传入(例如:Postman),在Header上加入n-d-version={"discovery-springcloud-example-a":"1.0", "discovery-springcloud-example-b":"1.0", "discovery-springcloud-example-c":"1.0;1.2"}在闸道器Zuul或者Spring Cloud Gateway的Filter中指定闸道器端全链路路由策略的灰度释出规则,在配置中心或者本地rule.xml配置其作用的优先级为外界传入>闸道器Filter指定>配置中心或者本地rule.xml配置
您可以根据自己需求,自由定义灰度释出规则,灵活实现微服务的灰度释出。
源代码位置:https://github.com/Nepxion/Discovery