springcloud家族有很多不错的组件,其中Netflix开源的微服务网关zuul也被整合到了其中。Zuul 担任着网关的角色,对发送到服务端的请求进行一些预处理,比如安全验证、动态路由、负载分配等。
Zuul 的核心是 Filters,根据执行时期分为以下几类:
– PRE:这种过滤器在请求被路由之前调用
– ROUTING:这种过滤器将请求路由到微服务
– POST:这种过滤器在路由到微服务以后执行
– ERROR:在其他阶段发生错误时执行该过滤器
springcloud和zuul的相关知识网上一搜索,资料一大堆,今天我们不讲这些技术理论,主要讲一次使用springcloud动态网关技术zuul提高研发效率的实战经验。
有两个项目A、B,A、B项目都是我们这个项目组进行维护和研发。A项目比较成熟一直运行良好,其中A项目中有一个子系统C(提供的服务主要是根据用户提供的一些数据进行审核,审核通过后会给用户一些奖金),C这个子系统单独提供HTTP接口,并且这个子系统大概有几十个API,逻辑也是比较复杂。
A项目大部分的工程都比较老旧(使用的spring版本还是3.0或3.2),所幸运的是C这个子系统研发稍晚,使用的是springboot,这跟B项目使用springboot的技术相似。A、B项目的各个模块除了网关层是接收http请求外,各个模块之间是使用dubbo来进行数据交互。
某一天,B项目的产品经理突然提出要在B项目中集成C子系统的所有功能(所有业务逻辑与A系统完全一致)。所有的研发人员都懵逼咯,首先A、B两个系统属于不同的公司主体(用户体系不同);其次网关层使用的技术也不完全相同(包括开源组件、基础组件、实现方案都不一样);再次页面的表现形式也不同(除了业务逻辑外);最后要求负责审核的部门不管是来自A还是B的都是一波人(后台管理是同一套,如果相同的数据在A和B都出现,只会有一条审核通过)。
要想让B项目内实现C子系统的所有功能,大家给出了如下方案:
- 完全独立部署一套C子系统到B,比如原来的是Ca,新部署的是Cb,底层使用相同的数据存储,只有一个管理端。这种方案看似可行,但要改Cb中的鉴权、cookie、session等相关的东西,并且Cb还要依赖B系统中的用户模块,这种方案太过于定制,如果将来其他系统也要依赖C子系统,C子系统将变得臃肿和难堪。
- C子系统把原来的所有Http接口抽象成dubbo接口,由B项目的网关层调用并提供新的http接口给前端。这些方案可行,但增加了C子系统的开发量,相同的功能有一套http接口,有一套dubbo接口,并且也增加了B项目的网关层的开发量(向下对接dubbo接口,向上提供http接口),以后的维护升级比较淡腾。
- 打通A、B系统的用户,保证B系统的用户在A系统中都有一个映射,所有鉴权都交给A系统,访问C系统完全走的是A系统的逻辑,B系统类似于第三方登录的一个概念。这种做法也可行,但这就导致了B系统对A系统的依赖(耦合),未来两个业务线的发展有太多不可控的地方。
上面的3种方案都能达到产品经理的要求,第一种不优雅、第二种开发量大、第三种会导致不相关的两个主题业务存在耦合。
有没有比上面更好的方案呢?
现在的C子系统,出现了A、B两个主体会使用的场景,说明C系统的业务比较有价值,将来还可能开放给D、E、F。如果使用上面的方案,维护成本都比较高或者对A形成较强的依赖。
由于B项目网关层和C子系统网关层都是用的是springboot,因为之前的项目中有动态网关的经验,我想到了zuul,做法就是在B项目和C子系统的网关层中集成zuul。登录鉴权等一切操作交给各个业务线如B的网关层,C只做业务逻辑处理。
当请求到达B系统网关层,B系统的网关层在zuul的pre阶段增加一个Filte比如PreAuthFilter用于处理登录授权,对于登录用户,添加如下的请求头(附加说明,zuul添加请求头默认会转换为小写,如果请求头中有下划线”_”好像无效):
RequestContext ctx = RequestContext.getCurrentContext();
ctx.addZuulRequestHeader("这里是请求头的key", "这里是请求头的加密过的值");
C系统增加一个登录检测的selvlet拦截器LoginCheckFilter,收到B系统转发过来的http请求后拿到上面添加的请求头:
String authHeader = request.getHeader("这里是请求头的key");
对authHeader解密后判断是否已经登录授权,如果已经登录授权,则对c系统的本地session进行设置登录属性:
session.setAttribute("isUserLogin", true);
这样C系统的所有业务逻辑完全不变,只需要记录数据的来源是A系统还是B系统即可(这不管是哪一种方案都少不了的)。
这样一来我们使用zuul实现了动态网关转发和鉴权,将来B系统还可以动态的接入其他业务,也可以随时下线掉类似C系统的业务。
使用zuul网关可以参考springboot2.0.1.RELEASE结合springcloud使用eureka和zuul实现动态网关。