PPT:
演讲文稿:
开场
我叫张无忌,听到这个名字你们就想到了光明顶,还有赵敏、周芷若。百度一下“张无忌”,大多的标题都是说:张无忌为什么选择赵敏?张无忌永远不知道周芷若的一个秘密!我在想,我有这么渣吗?
今天分享的主题是:基于dubbo的自研网关。我在现在公司很大一块工作就是负责技术中台,自然也包括网关。中间我们调用了市场上很多的网关,因为一系列的原因呢,最后我们选择自研网关,期间遇到了很多问题、和一些经验。在这总结、整理做出分享。大概会占据大家20分钟的时间,分享的内容主要可分为四块:
- 为什么要有网关?
- 为什么选择自研?
- 自研网关解决方案
- 遇到的问题
为什么要有网关?
第一个topic,就是为什么要有网关?我们去做一件事情,肯定是有需求去完成它对吧。那我们先了解什么是服务网关,我对网关的理解就是:网关=路由转发 通用功能的抽象。
路由转发好理解,就是接收外界所有的请求,根据一定的规则转发到后端的服务上去。
通用功能的抽象,我们开发一个接口,需要做什么?有参数校验、ip黑白名单、鉴权,涉及到敏感数据的,还可能需要签名、加密,等等。这一系列的功能都属于通用的基础功能。
那回到为什么要有网关的问题上?我们要完成以上功能,通常有三种实现方式:
- 每个应用系统实现一遍
- 写一个公共服务,所有服务有依赖这个服务
- 利用网关实现
第一种,缺点明显,代码冗余,难以维护。第二种相对来说要好很多,但也存在缺点:
- 每个应用依赖公共jar包,增加调用调用链路。
- 需要升级公共服务时,需要各个应用系统依赖新版本的jar,重新打包、编译。
而网关则很好的可以解决这些问题,不会有冗余代码、也不用依赖jar。当然也包括一些其他原因,我列了6点。比方说:刚描述的通用功能抽象,还有可以进行统一的日志监控 分析,还有每当新应用上线,都需要修改nginx配置,这需要运维的参与,多一个人参与进来,则又多了一份操作失误的风险。还有比方说,接口文档也没有统一维护的地方,没人管理。
所以呢。我们开始调研市场的网关。
为什么选择自研?
这个问题,其实很简单,就是别人的不符合自己的size呗~~~那我们来看看到底不符合在哪?
首先我们肯定是调研了很多的网关了,比方说:Kong、Zuul,发现大部分都是restful模式,不符合我们预期。我们后端RPC框架是Dubbo,当然是期望能和Dubbo无缝对接,这是第一个原因。
当然有的人又会说,也有支持Dubbo的啊,比方说Soul啊。这个我们也尝试过一段时间,然后又发现另一个问题,就是感觉配置不好用,总感觉少了点什么。我们期望的配置呢,最好是一眼过去,就能知道这个请求的整个链路,需要执行什么插件、不需要执行什么插件,一目了然。在生产时更快的定位问题。
当然还有运维成本高,需要有一个对源码理解透彻的人。通常情况下,开发人员不太愿意去看别人代码,定位问题也比较麻烦。
网关的需求
- 高可用,网关一宕机,后端服务就都访问不了了,所以高可用一定要保证的。分布式部署,各节点对等。
- 低延迟,耗时少。不能说别人接你的网关后,响应时间增加了几百毫秒。那别人肯定也接受不了的。
- 配置足够简单,这是我们放弃其他网关的原因,所以一定要做好。
- 插件配置要灵活,可插拔,顺序也要可以调整。比方有的接口需要先解密之后,根据解密的内容进行鉴权。有的则是需要先鉴权再解密。
- 多种rpc分发,需要适应各种场景,http、dubbo、cloud。
- 请求指标监控,直接投放至大屏。
- 接入网关要足够简单,大家都很忙,如果需要增加很多工作量,那也是不行的。
自研网关解决方案
我们第一个解决的是,各基础功能的抽象,以责任链的形式,将各插件调用顺序进行连接,每一个插件都可进行插拔。以及每个插件都可以进行个性化的配置,图中不方便体现。比方说签名支持:md5,sha,rsa。这些切换都可以通过插件的个性化进行简单的配置得以实现。
为了清晰体现插件的配置和简单操作,我们采用json 拖拽的方案。我们提供在前端页面,则可以通过拖拽的形式,配置插件链路。同时也支持json的配置,json的话相对于开发者更加亲切友好,开发者只需要关注json的next字段,表明当前插件执行完后的下一个插件。
为了解决低延迟,我们增加API Admin作为配置中心。所有的插件配置在API Admin中完成,网关(Top)的唯一工作就是处理请求。并且所有的插件配置,支持推、拉两种模式。在Top中,所有插件配置保存在内存中,由API Admin对其进行更新,尽最大可能对响应时间进行缩短。我们最终版的网关,经过测试,整个请求经过网关平均耗时仅仅为5ms。
那么我们加上监控 大屏后,第一版的整体架构就是这个样子。回顾一下,请求到达Top,Top从内存中获取插件配置,插件以责任链的形式执行。内存中的配置,由API Admin,采用推、拉的方式进行更新。最后利用日志,进行数据的监控,以及大屏的展示。
上面说是第一版的架构,我们在第一版开发完后,在测试中,发现插件串行调用效率并不高。最后衍生出了新一版的优化架构,则是增加了层级的概念。第一步先对插件进行分层,然后配置层级之间的调用关系。插件执行逻辑也就变成:层与层之间串行调用、层中的插件并行调用。最终10个插件串行调用的时间,优化为近似4个插件串行调用时间。经过测试,层级配置得当,性能能提升1-2倍。
遇到问题
我们主要为了适配dubbo的场景,这里分享的问题,多数属于dubbo。
- 泛化调用,dubbo为了反序列化,在返回值中增加了class字段,导致调用者获取到返回值后,验证签名失败。我们的解决方案是通过fastjson,排除所有的class字段。
- 泛化调用,第一次连接初始化失败后,后续调用一直使用缓存中初始化失败的连接,导致该接口一直访问不了。
- 后端服务接收dubbo请求时,只能用map接收
- 日期传输报错
总结
最后做一个总结,自研网关时,需要注意的一些地方:
- 高可用,支持分布式部署,各节点平等。
- 低延迟、高并发,不依赖本地存储,在内存完成处理和分发。
- 配置足够灵活,接口灵活多变,各个插件之间不要有依赖关系。
- 优化方案,层级概念,层中并行执行,层级串行执行。