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