zuul网关实现灰度发布

本文使用zuul网关实现灰度发布,包括了网关到服务、服务到服务的灰度。

服务部署可分为三种方式

1)蓝绿发布

  蓝绿发布是通过冗余的方式来解决部署问题,生产环境为绿色配置,冗余的服务为蓝色配置。在部署服务时,首先在冗余服务器上部署最新代码,由部分用户使用,

若使用没有问题,则通过负载均衡将所有用户请求转发到冗余服务器中,即冗余的服务转变为生产环境服务。优点是无需停机部署,服务回滚方便。缺点耗费服务器资源。

2)滚动发布

  滚动发布指每次只部署一个或多个服务,直到服务部署完成为止。优点:用户无感知,平滑过渡;相比蓝绿发布节省服务器资源。缺点:部署复杂,且时间长;遇到

问题回滚比较复杂。

3)灰度发布

  只升级部分服务,让少量用户访问新部署的服务,其他用户使用老服务,用户反馈无误后,整个集群部署,将用户迁移到新服务上来。优点:在灰度时即可发现问题及

时处理,保证系统稳定性;如果出现问题,影响范围小;用户无感知,过渡平滑。

灰度发布实现步骤:

1)定义规则:哪些用户可以访问灰度环境,比如按百分比(10%的用户可以访问灰度),或让固定用户先体验灰度环境;

2)利用网关实现路由策略,即网关到服务的路由;

3)服务与服务之间的调用使用ribbon实现灰度规则。

代码实现

代码使用zuul网关实现,项目包括了zuul、im、search三个服务,im服务调用search服务,具体实现如下。

1.引入maven依赖,关键依赖

  <dependency>
            <groupId>io.jmnarloch</groupId>
            <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
            <version>2.1.0</version>
 </dependency>        
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
            <version>1.4.6.RELEASE</version>
 </dependency>

2.application.properties配置文件进行配置

server.port=9090

spring.application.name=zuul
#注册中心
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.instance.prefer-ip-address=true
eureka.client.registerWithEureka=true 
#zuul网关路由 前缀
zuul.routes.prefix=/zuul
zuul.routes.im.path=/im/**
#im代表自定义服务
zuul.routes.im.service-id=im
#false不会截取  true截取前缀
zuul.routes.im.stripPrefix=true

#http://localhost:9090/zuul/im/index

3.GrayFilter过滤器实现网关到服务的灰度规则

@Component
public class GrayFilter extends ZuulFilter {
    private static final String GRAY = "gray";
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        //是否开启过滤
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //实现灰度逻辑
        //前端在请求头中携带灰度标识字段
        //服务注册时加入metadata数据,代表该服务节点为灰度节点
        //首先从头部中获取标识
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String header = request.getHeader("gray_header");
        //将灰度的请求转发到meataData中forward为1的服务
        if(StringUtils.equals(header,GRAY)){
            RibbonFilterContextHolder.getCurrentContext().add("forward","1");
        }else {
            RibbonFilterContextHolder.getCurrentContext().add("forward","2");
        }
        return null;
    }
}

4.im、search服务启动时注册metadata到注册中心

eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.client.enabled=true
#eureka.instance.hostname=localhost
#eureka.instance.instance-id=im
#灰度端口
server.port=8081
#生产端口
#server.port=8082
spring.application.name=im
#灰度发布需要metadata
#灰度为1
eureka.instance.metadata-map.forward=1

 

eureka.client.service-url.defaultZone=http://localhost:8761/eureka
server.port=8089
spring.application.name=search

#灰度为1
eureka.instance.metadata-map.forward=1

5.实现服务到服务的灰度规则

/**
 *  定义服务间灰度调用规则
 */
@Component
public class GrayRule extends AbstractLoadBalancerRule {

    private static final String GRAY = "gray";
    private static final String GRAY_HEADER = "forward";
    private static final String GRAY_VALUE = "1";
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object o) {
        ILoadBalancer loadBalancer = getLoadBalancer();
        return this.choose(loadBalancer);
    }

    private Server choose(ILoadBalancer lb){
        Server server = null;
        if (server==null){
       //获取请求头中的参数,具体实现在gitee Map<String, String> stringStringMap = GrayRibbonParamater.get(); String grayParmater = null; if(stringStringMap!=null){ grayParmater = stringStringMap.get("gray_header"); } //获得可到达的服务 List<Server> reachableServers = lb.getReachableServers(); for (Server reachableServer : reachableServers) { //获取服务的metadata Map<String, String> metadata = ((DiscoveryEnabledServer) reachableServer).getInstanceInfo().getMetadata(); if(StringUtils.equals(metadata.get(GRAY_HEADER),GRAY_VALUE)&&StringUtils.equals(grayParmater,GRAY)){ return reachableServer; } } } return server; } }

  

  

  

  

  

 

上一篇:K8s部署MongoDB使用cephfs做数据持久化


下一篇:kubernetes网络介绍