当时 Go 的1.4版本已经发布,我曾在 Go 处于1.1版本的时候,开始使用 Go 语言开发后端组件,并且使用 Go 构建过超大流量的后端服务,因此对 Go 语言本身的稳定性比较有信心。再加上头条后端整体服务化的架构改造,所以决定使用 Go 语言构建今日头条后端的微服务架构。
2015年6月,今日头条开始使用 Go 语言重构后端的 Feed 流服务,期间一边重构,一边迭代现有业务,同时还进行服务拆分,直到2016年6月,Feed 流后端服务几乎全部迁移到 Go。由于期间业务增长较快,夹杂服务拆分,因此没有横向对比重构前后的各项指标。但实际上切换到 Go 语言之后,服务整体的稳定性和性能都大幅提高。
微服务架构
对于复杂的服务间调用,我们抽象出五元组的概念:(From, FromCluster, To, ToCluster, Method)。每一个五元组唯一定义了一类的RPC调用。以五元组为单元,我们构建了一整套微服务架构。
我们使用 Go 语言研发了内部的微服务框架 kite,协议上完全兼容 Thrift。以五元组为基础单元,我们在 kite 框架上集成了服务注册和发现,分布式负载均衡,超时和熔断管理,服务降级,Method 级别的指标监控,分布式调用链追踪等功能。目前统一使用 kite 框架开发内部 Go 语言的服务,整体架构支持无限制水平扩展。
关于 kite 框架和微服务架构实现细节后续有机会会专门分享,这里主要分享下我们在使用 Go 构建大规模微服务架构中,Go 语言本身给我们带来了哪些便利以及实践过程中我们取得的经验。内容主要包括并发,性能,监控以及对Go语言使用的一些体会。
并发
Go 作为一门新兴的编程语言,最大特点就在于它是原生支持并发的。和传统基于 OS 线程和进程实现不同,Go 语言的并发是基于用户态的并发,这种并发方式就变得非常轻量,能够轻松运行几万甚至是几十万的并发逻辑。因此使用 Go 开发的服务端应用采用的就是“协程模型”,每一个请求由独立的协程处理完成。
比进程线程模型高出几个数量级的并发能力,而相对基于事件回调的服务端模型,Go 开发思路更加符合人的逻辑处理思维,因此即使使用 Go 开发大型的项目,也很容易维护。
并发模型
Go 的并发属于 CSP 并发模型的一种实现,CSP 并发模型的核心概念是:“不要通过共享内存来通信,而应该通过通信来共享内存”。这在 Go 语言中的实现就是 Goroutine 和 Channel。在1978发表的 CSP 论文中有一段使用 CSP 思路解决问题的描述。
"Problem: To print in ascending order all primes less than 10000. Use an array of processes, SIEVE, in which each process inputs a prime from its predecessor and prints it. The process then inputs an ascending stream of numbers from its predecessor and passes them on to its successor, suppressing any that are multiples of the original prime.
"
分析服务接口功能可以发现,数据解压缩,反序列化这个过程是最频繁的,这也符合性能分析得出来的结论。仔细分析解压缩和反序列化的过程,发现对于反序列化操作而言,需要一个”io.Reader”的接口,而对于解压缩,其本身就实现了”io.Reader“接口。在 Go 语言中,“io.Reader”的接口定义如下: