应对突发流量,限流是好手段,但还有其它手段,可最大限度保障业务无损。
1 为何分组
若在接口上再加一个分组维度去管理,不就让接口复杂了?
实则不然,比如无汽车年代,道路很简单,就一条,行人、洋车都在上边走。那随着汽车普及,道路越来越宽,有高速、辅路、人行道。交通网的建设与完善不仅提高出行效率,还更好保障行人安全。
RPC治理也一样。假设你是一个服务提供方应用的负责人,早期业务量不大,应用之间的调用关系简单,请求量不大,应用有足够能力扛日常所有流量。无需花太多时间治理调用请求过来的流量,通常选择最简单的方法,把服务实例统一管理,把所有请求都用一个共享“大池子”处理。类似“简单道路时期”,服务调用方跟服务提供方之间的调用拓扑,无隔离调用拓扑:
后期你接口的调用方越来越多,流量也多了。突然其中一个调用方流量激增,让你整个集群瞬间高负载,影响到其它调用方,导致它们的整体可用率下降,就得想法保证应用稳定。
2 问题本质是啥?
早期为管理方便,把接口都放到同一分组下,所有服务实例是以一个整体对外提供能力。
但业务膨胀,这种粗暴管理模式不再适用,就好比“有了汽车,交通网也得抓紧建设”,让人车分流。
道路上的人和车类比我们应用的调用方,我们可尝试把应用提供方这个大池子,划分出不同规格小池子,再分配给不同调用方,而不同小池子之间的隔离带,就是RPC里的分组,可实现流量隔离。
3 分组实现方案
RPC里咋实现分组?
既然是要求不同调用方拿到池子内容不同,回想服务发现,因为RPC流程里,能影响调用方获取服务节点的逻辑就在于它。
服务调用方通过接口名去注册中心找到所有的服务节点,完成服务发现。那换到这里的话,这样做其实并不合适,因为这样调用方会拿到所有的服务节点。因此为了实现分组隔离逻辑,我们需要重新改造下服务发现的逻辑,调用方去获取服务节点的时候除了要带着接口名,还需要另外加一个分组参数,相应的服务提供方在注册的时候也要带上分组参数。
通过改造后的分组逻辑,我们可以把服务提供方所有的实例分成若干组,每一个分组可以提供给单个或者多个不同的调用方来调用。那怎么分组好呢,有没有统一的标准?
这分组没有标准,我自己总结个规则参考,按应用重要级别划分。
非核心应用,不要跟核心应用分在同组,核心应用间应隔离,重要原则:核心应用不能受影响。如提供给下单过程中用的商品信息接口,要独立出一个单独分组,避免受其它调用方污染!
有了分组,服务调用方跟服务提供方之间的调用拓扑变成,分组调用拓扑:
通过分组,隔离调用方的流量,避免因为一个调用方出现流量激增而影响其它调用方的可用率。对服务提供方,这种方式是日常治理服务过程中的常用手段,这种分组流量隔离,对调用方应用有啥影响呢?
4 如何实现高可用?
分组隔离后,单个调用方在发RPC请求时,可选择的服务节点数相比没有分组前减少了,那对单个调用方,出错概率就升高了。如一个集中交换机设备坏了,而这调用方的所有服务节点都在这交换机下面,对服务调用方,它的请求怎么也到达不了服务提供方了,导致调用方业务受损。
有没有高可用方案?正常必须让车在车道行驶,人在人行道上。但当人行道或车道出现抢修,条件允许情况下,一般允许对方借道行驶一段时间,直到道路完全恢复。
RPC里咋实现“借道”?
调用方应用服务发现时,要带上:
- 对应接口名
- 特定分组名
所以对调用方来说,它是拿不到其它分组的服务节点的,那这样的话,调用方就无法建立连接。
因此问题核心变成:caller要拿到其它分组的服务节点,但又不能拿到所有的服务节点,否则分组就无意义了。最简单方案:允许caller配置多个分组。但若如此,这些节点对caller就都一样了,caller可随意选择获取到的所有节点去发请求,就又失去分组隔离意义,且还没实现“借道”效果。
所以还要把配置的分组,区分主次分组,只有主分组上的节点都不可用,才选择次分组节点。只要主分组里的节点恢复正常,须把流量都切换到主节点,整个切换过程对应用层透明,从而一定程度保障caller应用的高可用。
5 总结
RPC里可通过分组,人为给不同caller划分出不同小集群,实现caller的流量隔离,保障核心业务不受非核心业务干扰。考虑问题不能顾此失彼,不能因新加功能而影响原系统稳定性。
不仅可通过分组把Provider划分成不同规模小集群,还可利用分组,完成一个接口多种实现。
一般为方便管理服务,推荐每个接口的功能尽量唯一。但特殊场景下,两个接口也会完全一样,只是具体实现稍不同,就可以在Provider应用里同时暴露两个相同接口,但只是接口分组不一样而已。
6 FAQ
开发人员在开发过程中可能要启动自身的应用,而测试人员为验证功能,会在测试环境中部署同样应用。若开发、测试人员用的接口分组名刚好一样,就会干扰其它正在联调的调用方进行功能验证,影响整体工作效率,咋办?
- 直连?服务发现就失去意义了!
- 环境不同,注册中心不同!
我司目前使用的不同的注册中心,即注册中心部署了3份:pro、pre、test,其实test和dev用的同一注册中心,因为注册中心内部也有环境区分,服务在往注册中心注册时,需说明自己的环境。所以目前服务间的调用: pro走生产网关prod.gateway.com(网关同步注册中心信息) 环境参数默认prod pre走生产网关pre.gateway.com(网关同步注册中心信息) 环境参数默认pre test和dev走 test.gateway.com(网关同步注册中心信息),test需要环境的参数,test就是tets,dev就是dev
是的,环境的硬的隔离最省心。
K8S的namespace是个环境隔离的好解决方案,放在rpc觉得有点重。流量隔离可能更多是做业务做SET,同时也是容灾考虑,因为只是服务提供方做流量隔离,但服务提供方依赖的服务没有做流量隔离意义不大,同样会因为流量突增引发异常。这样若都做流量隔离,其实也就是SET化架构。所以不太明白rpc分组功能究竟使用场景是啥,调用链一长,分组就很难维护了,还是说分组就是为了SET化功能考虑?
不是很明白了。开发这边开发环境容器化,我们云主机克隆过来或者直接拉镜像都行,最后修改好配置。就相当于和开发隔离开来了。开发这边每天的更新打包好,我们也拿过来打包升级就行了。
关键在于配置是否存在开发测试冲突。
分组确实好用,而且还可控制是否同机房调用,如果一个机房有问题也可以切到别的机房,专门的分组也可以用来灵活做别的事情,如直接线上联调。 针对有条件进行环境隔离的是不存在的,针对没这个条件的,可以通过类似分组的方式进行服务分组隔离,这个也做不到,哪知道约定使用方式或时间区间了,当然,最好能环境隔离,同时有你进行数据的同步控制,这样既不互相影响又能测试类似的场景。
请问老师如果在限流阈值配置中加入应用分组的设置,和本文方案是不是也有相同的效果?
可以把限流融入到分组。
多环境的服务,我们原来采用的是修改route模块,根据调用端IP提前建立私有圈子,如果本地服务启动了就调用自己本地的。这类似于命名空间机制。
按业务类型分组,各业务服务包含对应业务所有接口。 将对应业务中所有的接口进行分级,按级别进行分组。所有分组信息保存在限流服务中,并对各级别配置可用的临时级别,用于流量突增时临时借用,或直接对分组进行扩容。 调用方携带自身级别从限流服务中获取对应接口信息,限流服务在对应级别服务达到饱和时,考虑临时使用其他级别,并在流量降低时,停止使用临时级别。
分组跟限流不冲突,两个纬度。
1.测试环境和开发环境一般是隔离的,不会出现上述情况。问题可以改成:同个开发环境的小伙伴,如何保证自己测试时请求到的是自己的机器?
2.在分组后面追加变量,通过识别环境参数,做到每个人的分组都不一样,这就能做到隔离。但遗憾的是,这将复用不到一些部署在开发环境的已有应用。自己本地得跑全套。
开发之间隔离可以通过直连的方式
这种问题会有发生,但都是做法不规范造成,一般通过环境区分,如开发有专门dev环境,测试有专属的QA环境,生产的有专属的prod环境。而且如果有交叉使用的情况的话就要互相知会到,没必要通过技术解决。
约定的东西很难口口相传。
同个服务集群的分组还是说不同服务的分组:
- 若是同一个服务分组,既然同个服一务有核心、非核心接口,是否继续拆分服务才合适?
- 若是不同服务,分组只是为了标识不同的应用而已吧?
同一个接口分不同组,分组的目的就是为了隔离不同调用方。
服务本身增加环境属性,注册信息中增加环境信息,服务发现时默认发现同环境的服务,类似于再增加个分组级别?是的,可以利用自动部署来自动加。
若没有用rpc,而是走http调用的微服务怎么分组?有在网关做分组的吗?可通过不同vip来隔离。