【第二战】又谈网关Zuul

Zuul 很有意思,作为网关服务,它将充当内部服务的代理,负责与外部通信。但仅仅是与外部服务通信需要它嘛?不对,从设计上来讲,它能将具有相同访问特征的系列服务归类处理。

zuul 架构

不仅仅是外部访问,服务簇之间的通信也可以依赖代理 zuul。但这样多增加一跳,就会增加相应的网络开销。

客户端不可能直接访问内部服务总代理,如果直接访问,这个代理没法实现集群,所以,一般会从 Nginx ,再到 zuul,这样 zuul 部署为集群就可以实现高可用了。

本文会简单谈谈几个思考的点,你会发现这些组件真的很有趣,没有想象那么复杂;

  1. 增加 zuul,究竟能带来什么?
  2. 负载均衡有多重要?
  3. 使用 zuul 的一些注意事项
    • routes 的 path 配置的顺序问题
    • httpclient 的指定
    • 大文件上传问题
    • 管理 zuul 应用程序的端点
    • 超时问题

增加 zuul,究竟能带来什么?

想想如何管理服务的 CORS认证。看起来,每个服务都需要去做这件事,如果更好的话,可能会打成相应的 jar 包,然后依赖。这看起来也可行,CORS 和 认证通过 web 容器的拦截器很方便就能做到。CORS 在 spring mvc 中可以通过如下配置做到:

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/path-1/**")
                    .allowedOrigins("https://allowed-origin.com")
                    .allowedMethods("GET", "POST");
        }
    };
}

zuul 还提供路由,如果服务自己实现路由,那就没有意义,这更多的是想在一个更高的层次去做统一路径访问管理。

增加 zuul 这一层次,能够统一解决这些每个服务都不得不面对的问题,对于部署为集群的服务还支持负载均衡,这很重要!如果不部署这一层,对外接口的服务将直接利用 nginx 来实现负载均衡,,仅仅维护这些服务路径就是很难忍受的。并且在代码层面能做的事比较少,一些共性不得不泛滥在每个服务里。我们常常所说的面向对象,对于共性,都是可抽取的,重复是不能容忍的!


负载均衡有多重要?

为什么需要负载均衡?因为部署为集群,服务提供者提供了多个可访问地址,调用者需要通过负载均衡调用。部署为集群是为实现高可用,增加服务的冗余性,避免单点故障!

所以,你会发现,在微服务架构中,如果按层次来看,下一层要实现高可用,那么上一层就需要支持负载均衡,这是避免不了的,哪怕是最简单的顺序访问,它也算!

微服务强调高可用,集群是实现高可用的最佳方式,而访问集群又需要调用者实现负载均衡,所以,它所处的地位自然重要,但很多情况下,客户端仅仅只使用了简单的顺序访问(轮询),并未实现常用的负载均衡策略。

常用的负载均衡策略是不是也可以抽象出来呢?spring cloud 就抽象了负载均衡客户端(LoadBalancerClient)。所以,我想负载均衡策略也是可以的,但由于负载均衡策略本身不算太复杂,可能也就没花那么多时间去思考如何抽象它。

微服务主键中涉及负载均衡的是组件是 ribbon,spring cloud 微服务生态中,需要负载均衡组的件也大多依赖它,那它自然可以看做负载均衡抽象的实现。那谈到负载均衡策略的抽象呢?ribbon 的负载均衡策略最终是执行 http 请求,真正的抽象应该独立于这个之外,dubbo 的负载均衡策略又是基于 rpc 的。如果我们有这样一个负载均衡策略的抽象,那么,dubbo 以及 ribbon 都能很好的使用它!

感觉这会是一个很有趣的想法,但谈论总是很简单,展示代码才是最重要的!先暂时记于此。


使用 zuul 的一些注意事项

这一节更多的是对阅读官网文档的一个总结;

  • routes 的 path 配置的顺序问题

    在路由器配置中,需要注意顺序,如果在上一个路径规则中匹配到,则不会继续往后匹配,所以会选择将兜底的匹配放在最后。这与 Java 的 catch 异常差不多,兜底的会放在最后,匹配肯定能正常结束。还需要注意的是需要使用 yaml 文件作为配置文件,properties 文件获取路由配置是无序的。

  • httpclient 的指定

    现在默认使用的 http client 是 Apache HTTP Client,替代了已不推荐使用的 Ribbon RestClient。但想要使用 RestClient 或者 okhttp3.OkHttpClient ,可以设置:

    ribbon.restclient.enable=true 或者 ribbon.okhttp.enable=true

    如果想要自定义,提供 CloseableHttpClient 或者 OkHttpClient 类型的 bean 即可。

  • 大文件上传问题

    小文件上传可以走代理路径,但对于大文件,官网推荐使用 “/zuul/*” 来做,这会跳过 zuul 中的 DispatcherServlet ,直接由 ZuulServlet 处理。

    “/zuul/*” 可以由 zuul.servletPath 修改掉;

  • 管理 zuul 应用程序的端点

    zuul 有 /routes/filters 端点,一个用于获取路由,一个用于获取过滤器;zuul 会为从服务注册中心获取到的服务添加默认路由(这可以通过配置修改这种行为),@EnableZuulProxy 会启用一些默认的 filter,这些信息都可以从上面这两个端点获取到。

    @EnableZuulServer 使用的默认 filters 和 @EnableZuulProxy 注解不同,这些端点需要通过 management.endpoints.web.exposure.include 暴露。

  • 超时问题

    Zuul 的超时配置在 ZuulPropertiesHost 内部类中,maxTotalConnectionsmaxPerRouteConnections 等这些容易理解的配置就不做特殊说明。这里主要困惑的是 socketTimeoutMillisconnectTimeoutMills 这两个配置,因为 ribbonhystrix

    也有对应的配置,那我们应该如何处理这些配置值呢?

    分析

    zuul 中,这两个值是用于 httpclient 的:

    • connectTimeoutMills 连接建立时间;
    • socketTimeoutMillis 等待数据的时间;

    ribbon 当中也是一样,hystrix 有所不同,它是用在 HystrixCommand 上,所以整个包装层次应该是 hystrix 》ribbon,不提及 zuul,是因为 zuul 使用了 ribbon 之后,它的超时配置并不会使用,所以整个超时配置时间配置应该确保上层大于下层,并且需要考虑重试,也就是说:

    ​ hystrix 超时时间 > ribbon 重试次数 * (ribbon 连接超时 + ribbon socket超时)


总结:

前端时间学习了 zuul 的使用,这次结合整个环境,思考了之前忽略的一些问题,但还是不能脱离俗套,这可能与个人的知识点积累的太浅有关!少年加油,不负年华。

这篇文章是 【第一战】微服务何不康康我 spring cloud 一文的后续,上篇博文中提到的源码 JWeb 依然在更新,新增对外开放的文件服务。

下一篇准备写写 ribbon,记录下一些思考,考虑从源码以及它所处的层次去分析,做一次思想上的升华!


我与风来


认认真真学习,做思想的产出者,而不是文字的搬运工
错误之处,还望指出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章