spring cloud zuul

一 spring cloud Zuul底层架构

spring cloud zuul融合的是netflix 的zuul 1.x版本

1.1 netflix 的1.x的线程模型

zuul1.x线程模型.png

zuul1.x采用的是线程阻塞模型,也就是我们常说的BIO,每来一个请求就会从线程池中分配一个线程去处理。这里导致的问题就是如果每一次请求的耗时很长,i/o操作时间很长,就会导致大量线程被挂起,利用率不仅低,很容易耗尽容器线程池内的线程,造成容器无法接受新的请求。所以对于这种io密集型,大请求来说,是不合适的。他适合那种小请求,cpu密集型,如果每一次请求只需要0.0几秒,一个线程1s钟也可以处理上百次请求,再加上简单,所以对于流量不是特别大,请求时间很短的场景是很实用的。
应用场景

  • cpu密集型任务
  • 简单操作的需求
  • 开发简单的需求
  • 实时请求高的

1.2 zuul 2.x 的线程模型

image.png

可以简单理解为有一个队列专门负责处理用户请求(如果连接量大,可以开个线程组来处理),后端有个队列专门负责处理后台服务调用(如果连接量大,可以开个线程组来处理),中间有个事件环线程(Event Loop Thread),它同时监听前后两个队列上的事件,有事件就触发回调函数处理事件。这种模式下需要的线程比较少,基本上每个CPU核上只需要一个事件环处理线程,前端的连接数可以很多,连接来了只需要进队列,不需要启动线程,事件环线程由事件触发,没有多线程阻塞问题。但是zuul2.x带来的问题就是开发成本大,对于小请求来说他的性能提升不明显。
应用场景

  • io密集的任务
  • 大请求或者大文件
  • 队列的流式数据
  • 超大量的连接

1.3 Zulu 1.x 的架构

image.png

每一次请求都会通过servlet,然后进入各种过滤器,最后再返回给客户端。而各个filters之间不会直接进行通信的。zuul 1.x使用RequestContext来实现各个filters之间共享数据,而RequestContext采用ConcurrentHashMap和ThreadLocal实现线程安全。

1.4 Zuul 1.x requestLifeCycle

image.png

一次请求的生命周期就如上图所示。可见最重要的一环就是各种过滤器。

过滤器类型

  • PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

  • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。

  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

  • ERROR:在其他阶段发生错误时执行该过滤器

    对于我们的业务来说,我们可以结合我们的业务来定制适合自己的过滤器。

    定制自己的过滤器其实也很简单,只继成ZuulFilter,实现

    • String filterType();

    • int filterOrder();

    • boolean shouldFilter();

    • Object run();

      其中
      filterType:返回过滤器的类型。有pre、route、post、error等几种取值,分别对应上文的几种过滤器
      filterOrder:返回一个int值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字。
      shouldFilter:返回一个boolean值来判断该过滤器是否要执行,true表示执行,false表示不执行。可以在此添加一些条件
      run:过滤器的具体逻辑。

      一个对token进行check过滤器的例子

         * @author zhaokai008@ke.com
         * @date 2019-05-11 21:45
         */
        public class TokenFilters {
         private static Logger LOGGER = LoggerFactory.getLogger(TokenFilter.class);
        ​
         @Override
         public String filterType() {
         return PRE_TYPE;
         }
        ​
         @Override
         public int filterOrder() {
         return 0;
         }
        ​
         @Override
         public boolean shouldFilter() {
         //todo 什么时候需要验证token

         return true;
         }
        ​
         @Override
         public Object run() {
        ​
         RequestContext ctx = RequestContext.getCurrentContext();
         HttpServletRequest request = ctx.getRequest();
         String url = request.getRequestURI();
         String token = request.getHeader("token");
         checkToken(url,token);
         return null;
        ​
         }
        ​
         private boolean checkToken(String url, String token) {

         //todo dosomething
         return true;
         }
        ​
        }

二 配置详解

  zuul:
  SendErrorFilter:
    error:
      disable: true
  sensitive-headers:
  #ignored-services: '*'
  # 限流
  ratelimit:
    enabled: true
    fallback: true
    repository: in_memory
    # 全局限流
    default-policy-list:
      - limit: 10000 #optional - request number limit per refresh interval window
        quota: 300 #optional - request time limit per refresh interval window (in seconds)
        refresh-interval: 60 #default value (in seconds)
        type: #optional
          - url
    # 分route限流
    policy-list:
      test1:
        - limit: 1 #optional - request number limit per refresh interval window
          quota: 10 #optional - request time limit per refresh interval window (in seconds)
          refresh-interval: 1 #default value (in seconds)
          type: #optional
            - url
  routes:
    test1:
      path: /api/10/search/**
      serviceId: common
      stripPrefix: false
      token: 10
    test2:
      path: /api/10/search/**
      serviceId: on
      stripPrefix: false
      token: 10
    test3:
      path: /api/data/10/**
      serviceId: DAS
      stripPrefix: false
      token: d10
    test4:
      path: /api/data/**
      serviceId: no
      stripPrefix: false
      token: d10
    test5:
      path: /api/search/**
      serviceId: no
      stripPrefix: true
      token: d10
    test6:
      path: /api/search/**
      url: http://host/api/data/**
      stripPrefix: false
      token: d10
    test7:
      path: /*
      url: http://host/api/data/**
      stripPrefix: false
      token: d10

# hystrix
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000

  host:
    max-per-route-connections: 20 #默认20
    max-total-connections: 200  #默认200

ribbon:
  eager-load:
    enabled: true
  ConnectTimeout: 15 #默认1000
  ReadTimeout: 15 #默认2000

2.1 请求路由

zuul.ignored-services: 默认情况下所有Eureka上的服务都会被Zuul自动创建映射关系进行路由,一般情况下需要 将其 置为 * ,表示不需要为所有服务自动创建路规则。这个默认情况下是需要设置为 * ,因为很多时候我们不想让我们的服务都对外暴露

2.1.1 设置路由

有两种方式,URL和serviceId.都是通过匹配到网关的path然后映射到对应的服务(url或者serviceId)上去。其中test5是url式,其余的都是serviceId式。但是要注意的是url方式没有 Hystrix、Ribbon 特性。

2.1.2 存取路由顺序

  • 所有的Route保存在一个map里面,而map的key是path,所有如果配置有两个path一致的话就会有一个被覆盖
  • 而对于yaml文件的来说,存入的顺序是按照先后顺序存入的。
    示例:
    比如上面的配置,首先存入的是test1,但是后来发现最后test2的path和test1一致,最终我们的routes里面包含的route就只有test2而没有test1

2.1.3 读取路由规则

1,使用路由规则匹配请求路径的时候是通过线性遍历的方式,在请求路径获取到第一个匹配的路由规则之后就会返回并结束匹配过程。所以当存在多个匹配的路由规则时,匹配结果完全取决于路由规则的保存顺序。
2, 在yaml存入是按照顺序存入的,左边第一个图当我们访问/api/data/10的时候,因为第一个匹配到的是/api/data/10/**,所以会映射到test3的DAS,而不会映射到test4 的no服务

2.2 限流

  • 分为全局限流(default-policy-list)和分route(policy-list)限流:
  • Limit:单位时间内允许访问的次数
  • Quota:单位时间内允许访问的总时间(统计每次请求的时间综合)
  • refresh-interval:单位时间设置,默认60s
  • Type限流方式:ORIGIN, USER, URL
    policy-list.test1配置的意思是:在一个时间窗口 1s 内,最多允许 12 次访问,或者总请求时间小于 120s

2.3 超时,熔断

  • ribbon. ConnectTimeout,ribbon.ReadTimeout
  • zuul.host.connect-timeout-millis, zuul.host.socket-timeout-millis
  • hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 6000
    单位:ms,区别在于,如果路由方式是serviceId的方式,那么ribbon的生效,如果是url的方式,则zuul.host开头的生效,ribbon 和hystrix谁小,谁生效。Hystrix是熔断,力度可基于全局配置和基于某个服务,但是不能基于route

2.4 线程池大小

  • max-per-route-connections:表示为每一个route分配的最大线程数,只是限制了线程数而不是限流,和qps没多大关系
  • max-total-connections:表示所有route的最大线程数

三 Zuul 使用的坑:

  • stripPrefix:默认为true,需要注意
  • 过动态刷新的配置,对于路由规则的变动,只能新增和修改,不能删除(所以动态删除会刷新不生效的错觉);
  • 新增的规则在匹配顺序上,位于老规则的后(如上面的test7配置生效之后,因为/*已经表示所有的路由,导致后面所有新加的所以路由都会打到test7之上,以后想要再添加其他的路由,是相当于不生效的。)
  • 一定要要将超时时间设置小一点,负责如果一瞬间上来都是2s或者更长的请求,就会导致在这2s内的这些线程得不到释放,就不能处理更多的请求,服务器性能大幅度降低。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容