通过之前对Apollo的学习,对Apollo的使用已经有了大概的了解。本篇博文通过与Spring Cloud Zuul作为网关配合,Apollo配置灰度实例来学习灰度发布。本文的核心是以github上的灰度发布开源项目ribbon-discovery-filter-spring-cloud-starter作为基础,通过对其中的过滤规则(Rule)的修改,来适合满足需求实现灰度发布。
实现灰度发布其中关键点在于服务实例的过滤,这里我们希望通过zuul过滤器来判断用户是否设置为灰度用户,灰度用户会使用灰度实例,否则使用正常的实例。一个服务通常会部署多个实例,其中灰度实例的配置需要通过Apollo来完成,但是如何实现不同的用户在不同的服务实例中选择一个服务实例?这里的不同指的是是否灰度。
使用zuul作为服务网关,zuul中又集成了ribbon,使用ribbon实现服务路由和负载均衡。在ribbon中使用rule来过滤服务实例,所以关键在于要实现适合的rule,而在rule中要使用服务实例的配置来判断是否灰度,这里的配置就是服务实例的metadata。
具体的实现原理在本篇博文不做太多介绍,简单来说,就是通过Apollo来配置灰度实例,配置metadata数据,通过ribbon的服务过滤再通过负载均衡来达到灰度发布。
基于开源项目ribbon-discovery-filter-spring-cloud-starter来创建自己的服务过滤rule,从github上将该项目clone到本地,由于该项目是gradle项目,这里我们创建自己的maven项目。同时结合之前的博文《springboot学习(六): 创建自定义的springboot starter》来创建springboot的starter方便之后使用。
创建名为ribbondiscoveryfilter的maven项目,pom.xml文件如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wds</groupId>
<artifactId>ribbon-discovery-filter</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.17.BUILD-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
修改MetadataAwarePredicate的过滤逻辑,在本项目中使用了三个常量:GRAY_TAG表示该服务是否要灰度发布,GRAY_INSTANCE_TAG表示该服务实例是否为灰度实例,GRAY_USER_TAG表示请求的用户是否为灰度用户
@Override
protected boolean apply(DiscoveryEnabledServer server) {
final RibbonFilterContext context = RibbonFilterContextHolder.getCurrentContext();
final String grayUserTag = context.get(Constants.GRAY_USER_TAG);
//判断是否设置了用户的灰度标志 如果没有则表示正常实例
if(grayUserTag == null || "".equals(grayUserTag)){
return true;
}
System.out.println(grayUserTag);
//判断请求的服务是否要做灰度发布
String grayTag = "0";
final Map<String,String> metadata = server.getInstanceInfo().getMetadata();
if(metadata != null && metadata.containsKey(Constants.GRAY_TAG)){
grayTag = metadata.get(Constants.GRAY_TAG);
System.out.println(grayTag);
}
System.out.println(metadata.get(Constants.GRAY_INSTANCE_TAG));
//若不做灰度
if("0".equals(grayTag)){
return true;
}
//若有灰度用户标记且请求的服务有灰度实例 则过滤实例
//根据不同的用户过滤不同的服务实例
if(grayUserTag.equals(metadata.get(Constants.GRAY_INSTANCE_TAG))){
System.out.println(metadata.get(Constants.GRAY_INSTANCE_TAG));
return true;
}
return false;
}
关于starter创建,请看之前的博文,一定要注意spring.factories文件的配置。
通过创建zuul的pre类型过滤器,来对用户进行判断是否设置灰度用户,若需要则以GRAY_USER_TAG为key,value为1表示灰度用户,0或不设置表示正常用户。
在本例中,通过判断ip地址设置灰度用户
public class GrayFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 100;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String ip = request.getRemoteAddr();
System.out.println(ip);
if("ip地址".equals(ip)){
RibbonFilterContextHolder.getCurrentContext().add(Constants.GRAY_USER_TAG,"1");
}
return null;
}
}
服务执行完毕后,清除该请求的RibbonFilterContext
public class GrayCleanFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(GrayCleanFilter.class);
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return 1000;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RibbonFilterContextHolder.clearCurrentContext();
logger.info("================clean the threadLocal!=================");
return null;
}
}
在前面说到服务实例的过滤需要服务的元数据配置参数。在apollo中为服务配置参数,元数据通过eureka.instance.metadata-map.KEY进行设置。之前提到在本例中使用了三个常量,GRAY_USER_INSTANCE是为用户创建标记,其他两个都是为服务实例创建元数据配置。GRAY_TAG表示该服务是否要做灰度发布。0表示没有灰度服务实例,1表示该服务存在灰度服务实例。GRAY_INSTANCE_TAY表示该服务实例是否为灰度实例,0表示不是,1表示为灰度服务实例。
在主版本配置参数
配置灰度参数
配置灰度规则
这里配置灰度规则表示要将哪个服务实例作为灰度实例,以ip为单位进行设置
在zuul中配置了服务请求路由,通过服务名进行请求,使用设置的ip访问服务将得到是灰度版本的值。
本篇博文只是简单介绍了apollo与zuul实现灰度发布的大概流程,在实际的开发中根据实际需要进行配置,创建过滤器,改写Rule等。关于其服务实例的过滤原理,推荐大家可以看看ribbon的源码,了解其服务实例的获取,过滤,负载均衡算法,不同Rule的代码实现。
参考资料:
https://github.com/jmnarloch/ribbon-discovery-filter-spring-cloud-starter
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/sinat_36553913/article/details/83243567
内容来源于网络,如有侵权,请联系作者删除!