Labs 导读

微服务是一种架构理念而不是具体的框架项目,许多编程语言都可以实现,但有的语言对微服务开发有天生的优势,Golang便是之一。Golang本身十分轻便,运行效率极高,同时对并发编程有着原生的支持,从而能更好的利用多核处理器。这里介绍一种成熟的Golang分布式微服务框架—go-zero。

单位:中移系统集成有限公司
● ●

在传统软件开发中,整个应用的代码都组织在一个单一的代码库,一般会有以下拆分代码的方式:一是按照特征做代码拆分,如MVC模式;二是按照功能做拆分,在更大的项目中将代码封装在处理不同业务的包中,包内部再做拆分。

无论怎么拆分,最终二者的代码都会集中在一个库中进行开发的管理,而微服务则是上述第二种拆分方法的拓展,其将代码按功能拆分为好几个包,每个都是可独立运行的单一代码库,其区别如下:

传统代码库与微服务代码库区别

微服务相较于传统开发来说大大降低了开发维护复杂度且提高了业务拓展性。在传统单一代码库的应用中,模块之间是紧耦合且边界模糊的,随着产品的深入迭代,代码的开发和维护将变得更加复杂,潜在的bug和漏洞也不可避免的会越来越多,而将整个应用的代码按功能对应拆分为小且独立的微代码服务库,模块耦合度减小、各模块间边界进一步清晰。在传统项目开发中,可能会有一部分代码会在多个模块中频繁地被用到,这种复用性很高的模块常常被抽离出来作为公共代码库使用,当此功能要拓展功能时,单一代码库的规模只增不减,整个应用还需重新部署。而在微服务框架中,高复用模块可作为单个服务独立出来,可独立运行、测试和部署。

微服务是一种架构理念而不是具体的框架项目,许多编程语言都可以实现,但有的语言对微服务开发有天生的优势,Golang便是之一。Golang本身十分轻便,运行效率极高,同时对并发编程有着原生的支持,从而能更好的利用多核处理器。这里介绍一种成熟的golang分布式微服务框架—go-zero。

  • 轻松获得支撑千万日活服务的稳定性;
  • 内建级联超时控制、限流、自适应熔断、自适应降载等微服务治理能力,无需配置和额外代码;
  • 微服务治理中间件可无缝集成到其他现有框架使用;
  • 极简API描述一键生成各端代码;
  • 自动校验客户端请求参数合法性;
  • 拥有大量微服务治理和并发工具包,社区生态优秀。

go-zero架构图

● ●

熔断在微服务中服务间非常常见,比如评论服务依赖审核服务而审核服务又依赖反垃圾服务,当评论服务调用审核服务时,审核服务又调用反垃圾服务,这时如果反垃圾服务宕机超时会导致审核服务一直等待,评论服务又在一直调用审核服务,这样就会导致请求大量堆积导致所有服务宕机。

由此可见,在整个调用链中,中间某个环节出现异常会引起上游调用服务出现一系列问题,甚至导致整个调用链的服务都宕机。因此一个服务作为调用方调用另一个服务时,为了防止被调用服务出现问题进而导致调用服务出现问题,所有调用服务器需要进行自我保护,而常用的保护手段就是熔断

评论-审核-反垃圾服务调用图

熔断器原理:熔断机制参考了日常生活中的保险丝的保护机制,电路超负荷运行时,保险丝会自动断开,从而保证电路中的电器不受损害。服务中的熔断机制指发起服务器调用时候,如果被调用方法返回的错误率超过阈值,那么将不会真正发起请求而是在调用方法时直接返回错误。

在熔断器模式下,服务调用方为每一个调用服务维护一个状态机,在此状态机中有三个状态:

1.关闭(Closed):在此状态下,我们需要一个计数器来记录调用失败的次数和总的请求次数,如果在某个时间窗口内,失败的失败率达到预设的阈值,则切换到断开状态,此时开启一个超时时间,当到达该时间则切换到半关闭状态,该超时时间是给了系统一次机会来修正导致调用失败的错误,以回到正常的工作状态。在关闭状态下,调用错误是基于时间的,在特定的时间间隔内会重置,这能够防止偶然错误导致熔断器进入断开状态。

2. 打开(Open):在该状态下,发起请求时会立即返回错误,一般会启动一个超时计时器,当计时器超时后,状态切换到半打开状态,也可以设置一个定时器,定期的探测服务是否恢复。

3. 半打开(Half-Open):在该状态下,允许应用程序一定数量的请求发往被调用服务,如果这些调用正常,那么可以认为被调用服务已经恢复正常,此时熔断器切换到关闭状态,同时需要重置计数。如果这部分仍有调用失败的情况,则认为被调用方仍然没有恢复,熔断器会切换到打开状态,然后重置计数器,半打开状态能够有效防止正在恢复中的服务被突然大量请求再次打垮。

熔断器状态集

● ●

在微服务开发中,api网管扮演对外提供restful api的角色,而api的数据往往以来其他服务,复杂的api更是会依赖多个甚至数十个服务,虽然单个被依赖服务的耗时一般较低但如果多个服务串行依赖的话那么整个api的耗时将会大大增加。

那么通过什么手段来优化呢?我们首先想到的是通过并发来的方式来处理依赖,这样就能降低整个依赖的耗时,Go基础库中为我们提供了 WaitGroup 工具用来进行并发控制,但实际业务场景中多个依赖如果有一个出错我们期望能立即返回而不是等所有依赖都执行完再返回结果,而且WaitGroup中对变量的赋值往往需要加锁,每个依赖函数都需要添加Add和Done对于新手来说比较容易出错。

基于以上的背景,go-zero框架中为我们提供了并发处理工具MapReduce,MapReduce是Google提出的一个软件架构,用于大规模数据集的并行运算,go-zero中的MapReduce工具正是借鉴了这种架构思想,go-zero框架中的MapReduce工具主要用来对批量数据进行并发的处理,以此来提升服务的性能。

MapReduce原理图

参考文献

[1]go-zero官方文档:https://go-zero.dev/