go-zero负载均衡实现原理
1. 什么是负载均衡
关于微服务分布式及集群的概念即定义,在业界中这些往往会同时在同一个项目中,而集群在微服务中主要为服务的运行保障高可用。
比如:在当前的项目情况下,我们可能针对用户服务部署两台服务以保障用户服务的高可用。实践在用户服务的Login方法中我们增加对请求信息的打印输出,即输出监听的配置信息以验证是访问的那个服务。
/apps/user/rpc/logic/loginlogic.go
在启动的时候修改/rpc/etc/local/user.yaml的配置
再通过api服务即可确定go-zero请求的时候会在这两个服务中选择一个来完成请求,这里就用到了负载均衡。
负载均衡:如字面意思即对流量负载的均衡处理,当存在多个服务的时候,通过一个特定的方式将流量均衡的分撒到各个服务上这就是负载均衡。
2. 负载均衡的实现方式
负载均衡从实现上分为两大类无状态的负载均衡与有状态的负载均衡,这里的状态是指服务器的运行状态比如【繁忙、停止、空闲等】。
无状态的负载均衡
这种方式几乎是大多数项目采用的方式,对于负载均衡的实现端来说是不关心服务器的状况情况,而是依据自己的算法方式将请求相对均衡的方式发送给对应的服务器。
轮询
算法方式相对简单,只需要根据服务组的顺序按个分配即可,如果分配完了再重头进行分配请求,不用做其他处理。
优点:实现简单、分配公平,适用于服务器性能相近的情况
缺点:无法根据服务器的负载情况进行动态调整,可能导致某些服务器负载过高。
随机
在一组服务列表中,通过随机算法选择一个服务器来处理请求
优点:实现简单、分配公平,适用于服务器性能相近的情况。
缺点:无法根据服务器的负载情况进行动态调整,可能导致某些服务器负载过高。
哈希
哈希的实现方式有多种比如根据ip哈希、url哈希通过哈希算法来确定访问的服务器
优点:可以保证同一请求始终分发给同一台服务器,适用于有状态的应用
缺点:增加或减少服务器时,可能会导致哈希值的变化,导致请求被分发到不同的服务器
权重
权重可以在随机/轮询等机制中应用,会根据权重基于实现算法的基础上优先分配,比如A、B两个服务分别的权重是20、80,则会存在20%的请求走A服务、80%的请求走B服务。
优点:可以根据服务器的性能和负载情况进行动态调整,提高系统的性能和可靠性
缺点:需要手动配置服务器的权重值,不够灵活。
有状态的负载均衡
有状态的负载均衡可以根据服务实例的运行状态、负载均衡情况进行请求的分发,因此与无状态的关键则在于,负载均衡实现机制会需要关注分配处理的服务对象在运行中的状态情况,而状态信息可以是自己记录也可以通过服务器返回来获取。
P2C+EWMA
这是go-zero中默认使用的负载均衡算法P2C,算法的特点是先从一组可用的节点中随机选择两个节点,然后依据权重、服务运行情况如cpu、请求成功数、拥塞度等计算出负载率,然后选择负载率较低的一个节点来完成本次请求,为了避免某些节点的分配不均衡,会在超过一定的实际后强制选择执行一次。同时采用了EWMA指数移动加权平均的算法,表示是一段时间内的均值。
优点:可依据服务情况合理分配请求
缺点:在对算法理解不足的情况实现会比较困难
其他:我们也可以依据服务器状态筛选出较低负载的服务器依据【无状态的负载均衡】提到的算法分配。
3. 分析框架对负载均衡加载和使用的过程
在分析框架的负载均衡是如何加载执行前,仍然先做理论分析。
方式1
我们结合服务发现机制,在服务中记录负载的信息而不记录其他内容,需要使用的时候调用即可,视为是一个工具。
当发起请求后,程序通过服务发现机制去获取到最新的请求服务列表,每次请求都将服务信息至于负载以获取可用的服务。
方式2
方式1的基础上每次请求都需要去进行服务发现,会多一次网络的调度,因此从性能上还可以再优化。优化的思路就是,对发现的服务异步更新维护,同时在每次存在更新服务后就修改负载均衡机制的缓存。即此时每次请求都通过负载均衡机制去获取服务地址。
而grpc就是使用的方式2,在异步维护更新服务之后,会同时修改负载均衡的缓存机制。
4. 源码分析
4.1. 自定义注册
在grpc中balancer和resolver同样是可以通过Register方法进行注册的
/grpc/balancer/balancer.go
因此在grpc中的go-zero/zrpc/internal/balancer/p2c/p2c.go:36代码处就在init中完成了负载均衡机制的注册, 需注意go-zero中是直接使用base.baseBuilder进行注册。
4.2. grcp加载自定义负载机制
根据上一节课的分析流程,我们直接从grpc:clientConn.maybeApplyDefaultServiceConfig方法开始进行分析。
在这个方法中主要的调度链就是为了创建出负载均衡机制,因为go-zero使用的是系统默认的机制,顾最终创建的则就是balancer/base下的baseBuilder对象。
/grpc/balancer/base/base.go
在得到了负载均衡的构建机制后,下一步则就是基于负载均衡机制去加载自定义的机制。
通过ccBalancerWrapper.updateClientConnState开始,在调度链的后可以看到在grpc:prickerWrapper中对自定义的picker进行来初始化加载
/grpc/pricker_wrapper.go
4.3. 请求调度
在分析负载均衡机制前,我们可以先看看在请求调度的时候是如何获取服务对象的地址。
根据上一讲的课程中,已经有了解到请求在grpc内部的调度链,因此我们将直接看pick方法。
/grpc/pricker_wrapper.go
在代码中可以分析出,先调用p.Pick方法获取pickResult对象即要连接的对象属性。实际上就是调用go-zero中的p2c负载均衡的处理算法获取连接并返回。
/go-zero/zrpc/internal/balancer/p2c/p2c.go
5. 总结
从目前的代码分析中可以发现grpc在负载均衡和服务发现机制的原理实现上具有相似的特点,而在程序上均采用了装饰模式进行设计。将自己内部实现好的关键服务机制方法通过接口传递给子类,由子类依据自己的业务处理装饰后再调度自己的方法完成后续功能。
结合对grpc的服务发现和负载均衡机制的原理分析,可以了看出实际上grpc在对两者的加载设计思路上具有相似之处,在设计的整体结构上grpc是以clientConn作为核心对象,要求自定义的机制在完成机制本身初始化后,再根据结果装饰自己的属性参数值。
推荐阅读:
基础设施即代码初探-开发Terraform Provider管理私有云MySQL实例
如何使用whisper+ollama+ffmpeg为视频添加中文字幕
更多技术和产品文章,请关注👆
如果您对哪个产品感兴趣,欢迎留言给我们,我们会定向邀文~
360智汇云是以"汇聚数据价值,助力智能未来"为目标的企业应用开放服务平台,融合360丰富的产品、技术力量,为客户提供平台服务。
目前,智汇云提供数据库、中间件、存储、大数据、人工智能、计算、网络、视联物联与通信等多种产品服务以及一站式解决方案,助力客户降本增效,累计服务业务1000+。
智汇云致力于为各行各业的业务及应用提供强有力的产品、技术服务,帮助企业和业务实现更大的商业价值。
官网:https://zyun.360.cn 或搜索“360智汇云”
客服电话:4000052360
欢迎使用我们的产品!😊