微服务Zuul网关进行灰度发布

微服务中,新版服务上线的时候,为了保证不出什么问题,可以将少量的请求转发到新的服务上,然后其他的请求还是转发到旧的服务上去,等线上的新服务测试通过以后,就可以重新平均分配请求。这种功能就称为灰度发布。

 

要完成灰度发布,要做的就是修改ribbon的负载均衡策略,通过一些特定的标识,比如我们针对某个接口路径/gray/publish/test。将10%的请求转发到新的服务上,将90%的请求转发到旧的服务上,诸如此类,我们可以制定各种规则进行灰度测试。

 

在微服务中,我们可以通过eureka的metamata进行自定义元数据,然后来修改ribbon的负载均衡策略。

 

在此,可以先通过代码进行简单地灰度发布,在实际应用中,可以通过数据库配置进行灰度灵活发布。

 

首先,我们如果要部署新版的服务user-server,我们用不同的端口启动这个服务,并配置不同的自定义元数据,配置如下:

server:
  port: 8181
#Eureka注册配置
eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
  instance:
    metadata-map:
      forward: 1
server:
  port: 8182
#Eureka注册配置
eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
  instance:
    metadata-map:
      forward: 2

我们启动了8181和8182两个服务,并分别定义了元数据forward: 1forward: 2,然后,开始修改zuul网关,首先导入修改ribbon负载均衡策略的依赖

<!-- 修改Zuul的负载均衡策略,也就是进行灰度发布使用的 -->
        <dependency>
            <groupId>io.jmnarloch</groupId>
            <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

然后,开始修改ribbon的负载均衡策略

package com.dkjk.gateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import io.jmnarloch.spring.cloud.ribbon.support.RibbonFilterContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.*;

/**
 * @author: qjc
 * @createTime: 2020/7/29
 * @Description: 接口安全验证过滤器
 */
@Component
@Slf4j
public class ValidFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        // 进行跨域请求的时候,并且请求头中有额外参数,比如token,客户端会先发送一个OPTIONS请求来探测后续需要发起的跨域POST请求是否安全可接受
        // 所以这个请求就不需要拦截,下面是处理方式
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            log.info("OPTIONS请求不做拦截操作");
            return false;
        }
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String requestURI = request.getRequestURI();
        if (requestURI.contains("/gray/publish/test")){
            int send = (int) (Math.random() * 100);
            if (send >= 0 && send < 10) {
                //也就是百分之10的请求转发到forward=1的服务上去
                RibbonFilterContextHolder.getCurrentContext().add("forward", "1");//这句话就代表将请求路由到metadata-map里forward为1的那个服务
            } else {
                //百分之90的请求转发到forward=2的服务上去
                RibbonFilterContextHolder.getCurrentContext().add("forward", "2");//这句话就代表将请求路由到metadata-map里forward为2的那个服务
            }
        }
        return null;
    }
}

接下来就可以测试接口/gray/publish/test的转发情况了,结果就是10%的请求转发到了8181上(即forward为1的服务上),90%的请求转发到了8182(即forward为2的服务上)

PS:在生产上使用的时候,可以通过创建数据库表来动态配置负载均衡策略,比如创建一张数据表:

CREATE TABLE `gray_release_config` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `server_name` varchar(255) DEFAULT NULL, //服务名
   `path` varchar(255) DEFAULT NULL,//需要进行灰度发布的接口路径
     `percent` int(11) DEFAULT NULL,//负载均衡策略,百分之percent的请求转发到forward上
   `forward` int(11) DEFAULT NULL,//自定义元数据值
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8

然后每隔一段时间同步配置表中的信息,也就是写个定时任务。

 

posted @ 2020-07-29 15:08  劈天造陆  阅读(2466)  评论(0编辑  收藏  举报