上篇文章中介绍了Zuul核心逻辑以及通过实际例子演示如何编写Zuul的pre、route、post filter,此篇文章将介绍Zuul如何集成Apollo进行动态配置刷新,如何集成CAT完成链路追踪,如何集成Hystrix完成限流、降级。如果对Appolo、Hystrix、CAT不熟悉,可以先浏览重点介绍Apollo、Hystrix、CAT的文章。
Zuul网关与Apollo集成实现动态路由机制
Zuul网关与Apollo集成的Demo例子地址(Demo来源于bobo微课)。Zuul网关原生是可以方便支持Archaius集成的,Netflix Archaius是一个功能强大的配置管理库。它是一个可用于从许多不同来源收集配置属性的框架,提供对配置信息的快速及线程安全访问。故下面的例子中为了简便,这里会引入Archaius的客户端,与Apollo的服务端集成,从而完成Zuul关键配置信息的动态刷新。即通过Archaius定期从Apollo服务端拉取最新的配置信息,提供给Zuul使用,具体如下所示。
Apollo服务端自身就支持通过接口方式获取最新的配置信息,请求的url格式是“{config_server_url}/configfiles/{appId}/{clusterName}/{namespaceName}”。为了引入Archaius客户端,需要在pom.xml文件中引入对应jar包即可。
接着设置环境变量"DEPLOY_CONFIG_URL"的值即可。具体代码如下所示,这里是通过System.setProperty设置环境变量值,实际项目中可以在启动应用时设置环境变量。
因为apollo已经占用了8080端口,为了避免端口冲突,zuul启动配置处修改成9000端口。另外,这里要模拟路由表配置动态加载机制,故还需要启动student服务,zuul网关会调用该服务提供的接口。启动后查看apollo中目前的配置信息,debug设置为false,配置的路由表信息是“student@http://localhost:9700&appolo@http://localhost:9700”,静态文件的配置是“student@http://localhost:9700&hello@http://localhost:9700”,启动Apollo后,apollo上的配置会覆盖静态文件的配置。
此时访问“/api/hello/getStudentDetails/bobo”会返回No Route,如果访问“api/appolo/getStudentDetails/bobo”能返回成功的response body,说明Zuul获取到了Apollo上的配置信息。另外,因为此时设置的debug是false,那么返回的response header也缺少添加的debug信息。Postman调用接口的结果如下所示:
此时修改Apollo上的配置,修改路由表配置以及把debug设置为true,刷新配置后,再次用postman调用服务。可以看到response header中新增了debug的信息,且新的路由信息生效.(备注:Archaius是定期轮询拉取Apollo上的配置信息,所以刷新后需要等一下调用服务才能看到新配置的效果)
另外,为了实现动态刷新路由,除了Archaius定期轮询从Apollo获取最新配置信息外,在pre filter的TestRout.groovy脚本中,还有一段代码,这段代码负责当路由表配置信息修改后,重新构建路由table信息,这样zuul网关在route的filter中才能获取到最新转换出来的forward url。
以上就是Zuul网关与Apollo集成的例子,利用Archaius客户端实现某些配置动态加载的机制,例如动态路由机制,有了动态路由机制,那么就可以实现如蓝绿部署和金丝雀部署等高级部署方式了。另外,上面的zuul demo代码中实际还提供了zuul配置信息查看功能,访问localhost:8077可以查看最终生效的所有配置的值,在apollo中配置的两个值在这里也能查看到。
Zuul与CAT集成完成链路追踪
为了演示与CAT集成的例子,这里需要启动CAT,另外需要启动已经进行埋点的服务。相关信息请查看这里。已经埋点的四个服务的调用关系如下所示:
需要注意一点,因为目前已经启动了apollo,apollo已经占用了8080端口。而CAT服务也会占用8080端口,而且CAT的客户端写死了端口,所以为了方便,这里会停止apollo,重新采用静态配置文件中的配置。即在zuul网关的的InitializeServletListener class中注释掉System.setProperty(Constants.DEPLOY_CONFIG_URL,xxx)。接着启动CAT服务和上面已经埋点的四个服务,修改静态配置文件中路由表信息,增加UI服务的路由信息。配置内容如下所示:
postman上通过zuul网关地址调用接口(http://localhost:9000/s2g_zuul_mobile_war/api/acme/start),因为配置了路由表,故能调用到ui服务,ui服务又调用到back-office服务。调用后在CAT上查看调用链信息,可以看到CAT上记录了从zuul网关到UI服务,UI服务到Back-office,Back-office到Customer和Account的调用链关系。结果如下所示:
Zuul上只是做了路由配置,并没有写任何CAT埋点的代码,为什么能在CAT上生成调用链信息呢?因为UI服务在做埋点时已经添加的Servlet拦截,故当zuul网关调用UI这个服务时,会记录链路信息,UI的日志打印如下所示:
总结而言,zuul与CAT集成,需要在被Zuul直接调用的服务上添加servlet拦截,记录链路信息即可。zuul只需在路由表中添加到服务路由信息,在调用服务时,从zuul网关地址发起调用,即可在CAT上看到包括Zuul在内的链路调用信息。上面介绍了Zuul与CAT集成,接下来看看Zuul如何与Hystrix集成
Zuul与Hystrix集成完成限流降级熔断
Zuul的route filter在执行路由转发的forward方法中,当构建出forward的url后,真正的http请求是被Hystrix Command封装调用的,代码如下所示。前面的信息是从读取Hystrix的配置信息,获取后,new ZuulRequestCommandThreadIsolation来发起真正的http请求调用。
ZuuRequestCommandForThreadIsolation实际是继承自HystrixCommand<HttpResponse>,Command的Run方法中执行forward方法,forward()方法就是发起http请求,httpclient.execute(httpUriRequest, httpContext)
可以看到Zuul和Hystrix集成,只需在发送接口调用的http请求的部分,封装上HystrixCommand即可。上面的代码为了简便只写了run方法中的逻辑,实际项目中还需要编写fallback()中逻辑,即降级处理逻辑。Hystrix相关的配置信息可存放properties静态文件中,或者通过Apollo存放。
实际项目中,因为Zuul作为网关,流量很大,会选择信号量隔离策略,那么可以通过设置信号量最大并发请求数来控制流量大小,即上面的“hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests”。另外,还可以设置超时时间来控制,如果后端服务响应很慢时主动返回超时错误,避免出现因后端服务响应太慢消耗了所有资源,造成雪崩效应。以及就是Zuul与Hystrix集成的过程。