落地网关soul(shenyu)过程中的一些实践

1,920 阅读11分钟

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

前言

这款网关属于业务网关类型,目前已经提交apache孵化,并更名为shenyu,原名soul。

本文依然会使用soul这个名字,是因为我在落地这款网关的时候,当时尚未更名,版本是org.dromara:soul:2.2.1,并且我们使用到了现在,因为没有时间升级到新版本,与最新的主版本已经脱离太久,所以本文暂时会使用soul这个老名字。

以下的说明或者其中的一些问题也只是基于这个版本说明,在最新的版本中可能都已经解决,也请勿直接对齐理解。

其中有一部分比较通用的整理下代码提交到社区了,还有不少因为代码写的随意或者是针对我们自己的场景所以没有提交社区。另外一些是一直想着整理下提交到社区,但是感觉实现可能太low,也没时间整理,所以未提交社区。

网关选型

为什么选择soul

我们在对一个开源组件选型的时候,可能会基于很多方面进行考量,比如业务场景、技术储备、组件的功能特性、组件的性能、社区活跃度等等。

我们选择这款网关却是以我们组的现状来决定的。

之前有遇到有同学问我们为什么选择soul,我当时的解释是人力成本,事实上这个确实是我们选择它的一个原因。对于一款业务网关来说,业内的几款开源网关(除了zuul),性能差异性在一些性能对比的文档中,其实可以看出来,应该还没到几个量级的差距吧。毕竟不是流量网关,性能也确实不算我们唯一考量的因素。

我们组比较看重的是它的一个扩展性、比较完备的控制台、灵活的路由规则,因为这些可以减少我们更多的开发时间。

顺便解释下,我们为什么没有选择spring cloud gateway,如果是为了稳定性,它可以算是首选,毕竟依托于spring社区,作为spring cloud的组件,知名度很大的,遗憾的是它并没有一个比较让我们中意的控制台。

当时我们组三个人,除了TL,只有我和另一个研发,网关落地由我负责执行,在极短时间内我是没法快速开发一个控制台出来。

网关选型

网关选型主要由TL负责了,在老大分析了十几款网关后,最终决定了2款,fizz和soul,然后由我来做最后调研,分析选择哪一款。

我当时简单了解了下两款网关,下面是一个对比(在现在看来可能下面的有些说明已经不适用,并且鉴于当时也没花太多时间了解,下面的有些描述不够准确,所以这个对比仅供参考)

特性支持SoulFizz
可视化管理平台
路由规则调用方根据请求url(原始url)路由,支持模糊匹配、正则等。自身提供了相关插件,可以对url的映射作简单调整支持网关节点进行分组,不同的网关节点支持不同的路由规则。根据url路由,支持url别名映射路由。路由类型支持:服务编排、服务发现、反向代理
插件作用域应用级别、接口级别服务(接口)级别
服务信息暴露自动注册(代码浸入)、手工相对烦琐手工维护,服务提供方代码层面无浸入
多语言支持java 支持自动注册,其它语言手工配置无代码浸入,手工注册http接口,语言无关性
支持的协议http\spring cloud\dubbo\tars\soafhttp \spring cloud
自定义插件配置支持,并且相当灵活,自定义插件采用maven依赖方式,代码无耦合支持,但是需要在fizz上开发,代码耦合
服务编排不支持支持的比较完善,但复杂业务存在使用成本
默认插件支持多种:鉴权、熔断、限流、流量白名单仅支持鉴权、流控
接口鉴权签名认证方式,waf插件支持白名单,默认不启用默认MD5签名并支持自定义插件认证方式,且支持应用白名单
用户权限配置不支持,不够完善自带一套相对完备的权限配置模块,支持角色配置、菜单权限、数据权限、人员组织信息等。
是否开源开源控制台是商业授权使用
监控支持prometheus自带监控面板

Fizz相对Soul最引人的地方应该是服务编排部分了,只是它的控制台不够开源,既然作为商用,我是完全不能考虑的,最终选择了Soul。

其它一些因素

我们在选型的时候可能技术储备方面也会作为很重要的一个参考,很多人会更倾向选择自己熟悉的。我这边以前在公司做的微服务组件大多都是自研的,开源组件都是因为要作对标而去了解,在实际项目中不会用的特别多,因此不会因为“感情”作为评判的标准。

另外说下社区活跃度,目前还是很赞的。当时选型落地的时候提交个别bug或pr也都是很快被处理了。相对之前在rocketmq提的一些pr,最慢的跨度达到了快半年才处理,有些特殊场景的bug我都只能先在本地处理,等不到下个版本发布。或许rocketmq相对已经很稳定了,也可能是处理慢的一个原因。

落地实践

在有了基本了解、确认之后就是下面的一些常规操作了:

springCloud插件菜单配置增强

springCloud插件默认是java自动注册创建相关路由规则,在web端手工配置(仅在springCloud插件上配置)类似于divide插件的作用不支持注册中心,想支持注册中心还需要在其它菜单(元数据管理)进行配置,配置略烦琐(当时刚了解这个,也不够熟悉)。

我们公司是java和go语言为主,少量php和python,当时只有java支持自动注册,所以使用上不够友好。

另外,考虑到java项目自动注册需要依赖相关jar包及代码上需要配置注解,存在一定的代码侵入,去给业务同学推广,他们也有些不太好接受自动注册的使用方式。

我们使用的注册中心是nacos,所以在spring cloud插件的注册上进行了适当改造,可以手工快速配置。

我当时为了减少对soul原有代码的侵入,特地使用切面的方式拦截请求,想着后续升级soul到新版本的时候,这部分代码可以直接copy过去,类似于下面这样:

只是没想到后面改的代码越来越多也不好升级了。

nacos版本对齐

刚开始用的时候,nacos版本与我们用nacos版本不一致,然后发现这里nacos版本也比较乱,有多个版本,所以硬生生改成统一版本了。

但是这部分没有提交到社区,因为这部分没验证不敢提,有些nacos版本是被其中一些插件依赖的,我也不太清楚哪里有用到。

只是对我们用到的地方进行测试,保证没有问题即可。对于其它插件依赖的nacos版本,修改之后因为也没时间测,担心有潜在风险就没提交社区,不过后来社区应该都把nacos客户端版本升到2.x了吧。

梳理文档:规则配置、插件定制规范

因为是给业务同学用的,所以需要一个统一的规范,包括创建的选择器、规则格式。但是因为这一版菜单权限及数据权限还不够完善,最终还是没有完全对外开放由他们自己配置,接入项目的时候还是由我来配置使用。

集成业务侧提供的插件

梳理插件使用规范,业务同学根据他们自己的业务定制插件集成进来。

集成插件这部分还是不太方便的,他们开发完,需要我这边重新打包部署,有问题他们修改后,还需要我重新发布,这个流程太繁琐也耽误双方时间。我本来是考虑支持osgi方式动态加载依赖解决的,只是一直没时间搞,搁置到现在,后续很长一段时期估计也没空了。

迁移老网关过滤器作为soul插件

老网关有个认证授权的过滤器,我改造成soul的插件集成进来,老网关依赖的是qconf,硬编码配置,我顺便直接改造支持nacos做负载均衡器动态配置(业务上的一些改造),基于公司的单点登录方式,目前基本是公司大部分项目接入网关采用的统一认证授权的方式了。

集群化部署、提供域名

联系运维的同学基于nginx集群化部署,对外支持公网域名、内网域名。

跨域问题

soul自身支持跨域的,不过当时有个小bug,配置项不生效,这个问题当时提交社区修复了。

日志平台

采集日志到ELK,有针对地日志处理。

开发logging插件

可以打印请求头、请求体、响应头、响应体等信息,主要是有些情况线上排查问题比较方便(尤其是自定义插件对请求头添加相关字段,查看请求响应信息),该插件将代码规范后,把通用逻辑整理出来已提交社区。

后来一直想再打印请求耗时,到现在过了几个月也没花点时间在这上面增加这个功能。


其实到这一步,已经基本可以对外使用了, 也开始陆续接入一些项目了。

下面一些后续的开发及配置是我后来偶尔抽一点时间赶紧搞下,陆陆续续加的。


老网关平滑迁移方案

老网关基于spring cloud gateway做的,对于其中的很多项目不同的路由规则配置方式需要平滑迁移到新网关,所以对soul的上context_path和rewrite插件做了针对我们使用场景的部分微调整,可以平滑将spring cloud gateway的部分(只有我们用到的那几种)配置规则修改为soul的配置方式。

注册中心排除部分节点

这是某次业务侧提的一个需求:有A、B、C3个服务,A调C(A->C),B调C(B->C),假定C在注册中心上注册了5个实例,A调C的某些接口时候,只能打到前3个实例上,B调C的时候,只能打到后3个实例上。

我也想不明白为啥有这种需求:只调用其中部分实例。

所以加的这个功能,注册中心做负载均衡的时候可以排队某些节点,如下:

开发完成之后,我更多是用到这个场景下了:有些nacos节点临时没法下线,又不能把流量打过去,就现在这上面排除掉。

这部分代码之前还想着完善后提交社区,后来到现在过了几个月也没继续完善,我在自己项目实现有点粗糙,功能凑合能用,也不知道社区是否觉得需要这个功能(不一定通用),所以暂时没抽时间把这部分完善一下提交社区。

后续有时间还是要试着提交一下。

监控、告警

没有直接使用社区提供的监控面板,主要是有些指标我不太关心,然后又加了几个指标做了相关监控、告警:

我平常重点关注的指标大概这几个(后来我加的,也没来得及提交社区,等后续工作不忙了,斟酌一下代码完善后再提交社区):

单个网关实例的tps和每个服务的tps:

最近一分钟网关转发异常的服务及异常次数:

最近1小时YGC的频次:

最近1小时FGC的频次:

在性能上我没想好用哪个指标作告警合适,因此目前只用YGC或FGC频繁告警作为性能预警,下面是一个以前告警的示例:

如果网关转发异常也作了个告警(主要是超时 或者我们业务鉴权失败异常,转发失败一直没空作监控和告警)

结语

还有很多涉及我们业务上的地方需要优化和完善,不过因为时间不充裕就还没顾得上,后续有更多的优化再行补充,有好的实现也会尽量抽时间提交社区。