网关:Zuul

来源:
《微服务架构实战160讲》
https://www.jianshu.com/p/d1e61f9fc13a?utm_source=oschina-app

API网关的基本功能:
在这里插入图片描述

Zuul网关

1 场景

Zuul实现了跨区高可用、防爬防攻击、健康检查、屏蔽坏节点、流量判断等诸多功能

  • API网关

类似单体应用中的过滤器,在请求到达业务服务器之前可以做一些操作

  • 红绿部署

类似配置中心Apollo可以实现的蓝绿部署(流量判断)

  • 开发者测试分支

也是通过流量判断,使部分流量达到测试代码而不是全部

  • 埋点测试

也是通过流量判断,使部分流量达到埋点代码而不是全部

  • 压力测试

也是通过流量判断

  • 调试路由

也是通过流量判断,将代码放到线上来调试

  • 金丝雀测试

也是通过流量判断,部分流量测试金丝雀代码,如果没问题,再全量切换

  • 粘性金丝雀

同金丝雀测试,不过固定了用户,如果第一次到达了金丝雀代码,下一次就依然会到达此处,而不是随机分配

  • 失败注入测试

注入错误,看看业务服务器的容错性

  • 降级测试

网关检测到错误后,能执行降级,将流量打向降级的集群,可以跟失败注入测试结合来测试降级能力

2 架构设计+源码简析(1.0)

Zuul网关的核心是一系列的过滤器,可以对请求或者响应结果做一系列过滤,在zuul中过滤器分为四种类型(Type),每一种Type中可以有多个拦截器,开发人员可自行指定执行顺序(ExecutionOrder),只要满足指定的条件(Criteria),就执行指定的动作(Action):

1 PRE Filters(前置过滤器) :当请求会路由转发到具体后端服务器前执行的过滤器,比如鉴权过滤器,日志过滤器,路由选择过滤器

2 ROUTING Filters (路由过滤器):该过滤器作用是把请求具体转发到后端服务器上,一般是通过Apache HttpClient 或者 Netflix Ribbon把请求发送到具体的后端服务器上

3 POST Filters(后置过滤器):当把请求路由到具体后端服务器后执行的过滤器,可以添加标准http 响应头、收集一些统计数据(比如请求耗时等)、写入请求结果到请求方等。

4 ERROR Filters(错误过滤器):当任何一个其他类型过滤器执行出错时候执行该过滤器

  • 总体架构+原理
    在这里插入图片描述
    开发/运维人员通过管理模块来管理过滤器,可以设置不同的状态,如用于金丝雀测试的状态等,然后存储到FilterPersister中,如数据库,需要做一个表出来存储

FilterPoller会定期扫描FilterPersister(通过ZuulFilterDaoFactory,工厂模式),加载新的过滤器进FilterDirectory,不同类型的过滤器有各自的目录,FileManager组合了这几个目录,使用一个线程扫描FilterDirectory,加载到FilterLoader中,最终送到FilterRunner中

FilterRunner则完成所有请求流程的过滤,是最核心的组件

请求入口:ZuulServlet本质是一个HttpServlet,会拦截所有请求,执行四个过滤器方法,方法内部会将具体实现交给FilterRunner(委派模式),FilterRunner则交给FilterProcessor真正执行对应类型的过滤器(从FilterLoader中取出相应类型的过滤器,使用for循环依次处理,为责任链模式)

过滤器的执行顺序:ZuulServlet#service

public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
    ...
    try {
        preRoute();
    } catch (ZuulException e) {
        error(e);
        postRoute();
        return;
    }
    try {
        route();
    } catch (ZuulException e) {
        error(e);
        postRoute();
        return;
    }
    try {
        postRoute();
    } catch (ZuulException e) {
        error(e);
        return;
    }
    ...
}

RequestContext存储整个请求的一些数据,使得过滤器之间可以共享数据,且线程安全,即每个请求有各自的局部RequestContext,由所有过滤器共享(其实就是一个请求里的数据共享)
RequestContext使用ThreadLocal实现,其本身简单wrap了ConcurrentHashMap,核心源码:

public class RequestContext extends ConcurrentHashMap<String, Object> {

    protected static Class<? extends RequestContext> contextClass = RequestContext.class;
	//使用泛型,这样可以通过继承RequestContext来定制RequestContext
    protected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() {
        @Override
        protected RequestContext initialValue() {
            try {
                return contextClass.newInstance();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
    };
    //方法内部都是通过ConcurrentHashMap的put和get
    public void setRequest(HttpServletRequest request) {
        put("request", request);
    }
    ...
    public static RequestContext getCurrentContext() {
        if (testContext != null) return testContext;

        RequestContext context = threadLocal.get();
        return context;
    }
}
  • 请求处理生命周期
    在这里插入图片描述

3 部署案例

  • 整体架构
    在这里插入图片描述
  • 以无线网关为例
    在这里插入图片描述
  • 如何获取路由表
    在这里插入图片描述
    使用Eureka作为中介,Zuul通过集成Eureka客户端Ribbon就可以自动发现服务的ip+端口了

也可以基于域名或基于Apollo来实现,不过没有Eureka好,因为无法实现自发现,即自动化

4 Zuul2

使用了非阻塞异步的模式(Netty),增加了一些功能点,架构做了一些修改

5 Zuul1最佳实践

  • 使用异步 AsyncServlet 来优化连接数

需要自实现

  • 使用Apollo配置中心集成动态配置

  • Hystrix熔断限流

信号量隔离和线程池隔离两种方式建议使用信号量隔离,如果使用线程池隔离,则每个服务都要配一个线程池,会导致Zuul1上的线程更多,更容易使网关资源被耗尽

  • 对连接池进行管理

  • CAT和Hystrix 监控

  • 过滤器调试技巧

使用java代码调试,完成后再改成groovy去上传

  • 网关不要有业务逻辑

  • 自助路由 (需定制扩展 )

提供一些工具、文档,让前后端开发人员可以自助扩展网关

发布了235 篇原创文章 · 获赞 264 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_41594698/article/details/102958422