Apollo学习(四):创建灰度配置并与zuul协作实现灰度发布

x33g5p2x  于2021-12-20 转载在 其他  
字(5.6k)|赞(0)|评价(0)|浏览(374)

说明

通过之前对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的服务过滤再通过负载均衡来达到灰度发布。

1.创建ribbon-discovery-filter

基于开源项目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文件的配置。

2.创建zuul过滤器

通过创建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;
    }
}

3.Apollo配置灰度实例

在前面说到服务实例的过滤需要服务的元数据配置参数。在apollo中为服务配置参数,元数据通过eureka.instance.metadata-map.KEY进行设置。之前提到在本例中使用了三个常量,GRAY_USER_INSTANCE是为用户创建标记,其他两个都是为服务实例创建元数据配置。GRAY_TAG表示该服务是否要做灰度发布。0表示没有灰度服务实例,1表示该服务存在灰度服务实例。GRAY_INSTANCE_TAY表示该服务实例是否为灰度实例,0表示不是,1表示为灰度服务实例。
在主版本配置参数

配置灰度参数

配置灰度规则
这里配置灰度规则表示要将哪个服务实例作为灰度实例,以ip为单位进行设置

4.通过zuul访问服务

在zuul中配置了服务请求路由,通过服务名进行请求,使用设置的ip访问服务将得到是灰度版本的值。

最后

本篇博文只是简单介绍了apollo与zuul实现灰度发布的大概流程,在实际的开发中根据实际需要进行配置,创建过滤器,改写Rule等。关于其服务实例的过滤原理,推荐大家可以看看ribbon的源码,了解其服务实例的获取,过滤,负载均衡算法,不同Rule的代码实现。

参考资料:
https://github.com/jmnarloch/ribbon-discovery-filter-spring-cloud-starter

相关文章