序
Zuul 很有意思,作为网关服务,它将充当内部服务的代理,负责与外部通信。但仅仅是与外部服务通信需要它嘛?不对,从设计上来讲,它能将具有相同访问特征的系列服务归类处理。
不仅仅是外部访问,服务簇之间的通信也可以依赖代理 zuul。但这样多增加一跳,就会增加相应的网络开销。
客户端不可能直接访问内部服务总代理,如果直接访问,这个代理没法实现集群,所以,一般会从 Nginx ,再到 zuul,这样 zuul 部署为集群就可以实现高可用了。
本文会简单谈谈几个思考的点,你会发现这些组件真的很有趣,没有想象那么复杂;
- 增加 zuul,究竟能带来什么?
- 负载均衡有多重要?
- 使用 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 的超时配置在
ZuulProperties
的Host
内部类中,maxTotalConnections
和maxPerRouteConnections
等这些容易理解的配置就不做特殊说明。这里主要困惑的是socketTimeoutMillis
和connectTimeoutMills
这两个配置,因为ribbon
和hystrix
也有对应的配置,那我们应该如何处理这些配置值呢?
分析:
zuul 中,这两个值是用于
httpclient
的:connectTimeoutMills
连接建立时间;socketTimeoutMillis
等待数据的时间;
ribbon 当中也是一样,hystrix 有所不同,它是用在
HystrixCommand
上,所以整个包装层次应该是 hystrix 》ribbon,不提及 zuul,是因为 zuul 使用了 ribbon 之后,它的超时配置并不会使用,所以整个超时配置时间配置应该确保上层大于下层,并且需要考虑重试,也就是说: hystrix 超时时间 > ribbon 重试次数 * (ribbon 连接超时 + ribbon socket超时)
总结:
前端时间学习了 zuul 的使用,这次结合整个环境,思考了之前忽略的一些问题,但还是不能脱离俗套,这可能与个人的知识点积累的太浅有关!少年加油,不负年华。
这篇文章是 【第一战】微服务何不康康我 spring cloud 一文的后续,上篇博文中提到的源码 JWeb 依然在更新,新增对外开放的文件服务。
下一篇准备写写 ribbon,记录下一些思考,考虑从源码以及它所处的层次去分析,做一次思想上的升华!
我与风来
认认真真学习,做思想的产出者,而不是文字的搬运工
错误之处,还望指出