前言
Grab是东南亚最大的出行平台,业务覆盖东南亚7个国家39个城市,APP下载量高达3600000次。随着业务量的持续增长,为了解决系统性能瓶颈,打造一套高可用的系统架构,Grab 从最开始的 rails、nodejs 到完全转向 Go。除了全部的后台服务,每天支撑亿万级数据处理的流式数据系统也是基于 Go 打造的。本文来自 Grab 高级工程师高超在 Gopher China 2017大会上的精彩分享,以下是对他演讲内容的整理。
Grab是一家东南亚出行平台,致力于解决东南亚人民的出行问题。Grab 的主要市场是东南亚,我们也没有进军中国市场的打算,我们和滴滴是战略合作伙伴的关系。Grab 成立于2011年,目前在东南亚7个国家都有运营,覆盖了39 个城市。目前有710000位司机,3600000次 APP下载。我们还是一个非常年轻的创业公司,目前在在新加坡、北京、西雅图、越南和印尼都有研发中心。我本人目前是在西雅图研发中心工作,是 Grab 大数据研发团队的 Tech Lead。
Grab 从前的技术栈
Grab 现在的技术栈
随着业务量持续增长,以前那一套框架渐渐承载不了我们的业务规模,于是我们做了一个很大的转型。现在的技术栈是这样的,如图2。我们依然在亚马逊云上运行我们的服务,在大数据和机器学习领域也做了很多方面的开发。其中最醒目的也是跟大会最相关的,就是最右边的 Go。现在Grab后台服务都是用 Go 打造的。
为什么会转向Go?
首先是因为当时我们的服务有非常大的性能压力,经过了一段比较黑暗的时期。当时的服务一天会坏好几次,于是我们就思考往哪个方向转型能实现比较高性能的整体架构,最后就选择了Go。原因有几点,首先Go的语言规范非常简洁,上手很轻松,我们现有的程序员很快就能转向Go的开发,学习的曲线不是很陡峭,同时Go的生产效率也非常高。Go拥有非常完善的工具链,它有自己的测试框架,这些都能够帮助你写出很规范的代码。Go还拥有非常方便的部署流程,相比之前需要部署一大堆东西,Go打包好以后部署非常方便。同时,Go的性能也非常好,从实际的经验来看,用Go以后弹性云机器数量骤减90%,响应延迟平均能降低80%。
Grab用Go做什么?
我们用Go写了很多东西,像我刚才提到的全部后台服务目前都是用Go来打造的。现在大概是有50多个微服务,这个数量还在不停的上升。我们现在工程师Gophers数量有300个,今年的目标是扩招到800个。我们用Go打造的流式数据系统,大家如果对打车应用服务比较熟悉的话,会知道它会有很多实时信息需要处理,这些信息是支撑你做调度和决策的东西,流式的数据系统就在做这件事情,这也是完全用Go写的。还有 API Gateway、RPC & RESTful framework等。同时我们还自己写了ORM,我们为什么自己写ORM,大家知道做后台服务一个很通常的瓶颈,就是数据系统,我们想通过自己打造的ORM来约束数据库访问的模式,禁止JOIN和Transaction的使用,并且在服务与数据库中间加了一层数据访问层。这样服务的业务逻辑就不需要关心具体数据库技术的选择和转型,这样的一个架构在我们迁移到微服务框架的过程中给了我们很大的自由度。
Grab的Go实践
接下来跟大家分享一下我们用Go两年半积累的一些经验。重要的有这么几点:首先怎么样管理你的代码,第二就是讲讲在微服务领域现在比较火的话题Distributed Tracing,然后是Go的测试,怎么样管理你的代码质量,最后一个是我们在Go里面写过的Bugs和遇到的问题。
代码管理
另外一个好处是依赖管理非常简单,如果有公有库在不同的Repo里面你要更新它,需要部署不同的依赖管理。在一个代码组织方式下,你可以随时打包进公有库。同时代码的更改可以做到原子化,比方说我们一开始写Go的时候,没有什么经验,很多最佳的实践也不熟悉,写了一段时间之后发现一个模式非常好用,我们想把很多公有库函数加上这个代码,我们先要改公有库,再改服务A的代码,用户代码也要改,服务B的代码改完之后要改B的代码,这样无论更改还是什么都会造成很大的问题,但是我们代码都放在一个Repo里面。这样的方式,就可以支撑很大规模的重构,代码库的更新。同时团队之间也可以更好的合作,因为沟通的障碍可以被消除。还有更灵活的团队界限,大家可能都经历过一个服务移动到另外一个服务,如果你放在一个Repo里面就是一个文件夹的更改。代码透明度自然而然就最大化了,假如说你调度团队下面有十几个服务,这十个服务都放在调度团队文件夹里面,代码的归属权就会非常清晰。
这就是我们管理代码的方式。这个方式有这么多好处,当然也有它的弊端。它的弊端其中之一就是所有市面上的CI系统没有为这样的代码组织方式做优化,所以你在用这些系统的时候,效率会非常低。这也是为什么我们花时间打造了自己的CI,总而言之这样一个方式有自己的好处,但你要有足够强大的工具链支撑才能让好处凸显出来。
Distributed Tracing
图 3 是今年2月份我们系统之间服务间的关系,可以看到杂乱无章。在一个创业公司架构的演变中,从单体应用到大规模微服务架构的时候,都会产生出这么一个很杂乱的图来。之前在单体应用里面,不同的功能就是函数,函数变成了服务,每个服务加入越来越多的依赖。在你遇到问题的时候,如何快速发现及诊断服务存在的问题,这就是Distributed Tracing可以帮助我们的。有如下几个场景:一个请求耗时三秒才能完成,如何诊断何处耗时最多;如何定位Single Point of Failure;如何定位并发现循环依赖关系;如何定位Fan IN,Fan Out。这都是Distributed Tracing可以帮助我们解决的问题。
图5是我们系统里面的真实图,这是一个请求司机评分,在他进入之后会调用另外一个后端服务,那个服务会调用司机的评分,他的数据存在 mysql,耗时两毫秒。有了这么一个图,你想诊断你的性能信息或者系统的瓶颈,就可以帮你很快的缩小范围诊断。而且这个力度,是你可以自己控制的,我们这边只做到这几个,当中可能还有一些计算当天时间之类的函数,你都可以加上去。同时,你还可以根据服务间的协议做一些分析,比如服务A和B之间的协议,服务B必须在100毫秒之内返回请求,服务B实际花了120毫秒,根据这个图就可以到服务B的维护者里面,为什么这个慢,可以做到密度的监控。
这里还介绍一下 Open Tracing,它不是一个框架而是一个库,只要做成这个库都可以以同样的方式实现聚合。你不需要做任何事情,你只需要开启 Open Tracing 开关,这个东西非常好,掌握标准就等于掌握全世界。比较理想的情况下,我们用的数据库这些都可以实现,这样我们每一个服务每一个环节都可以做到非常透明的监控。
Go Testing
我们在Grab最常写的测试包括两种,第一种是单元测试,另外一个是端到端的测试。
讲到单元测试就引出另外一个话题,我们做单元测试的时候,刚开始写Go年少无知引出很多其他不好的习惯,做出不好的实践。其中之一如图 7 这个东西,数据库是一个全局的单例。我测试的时候不想写数据库,首先CI数据库本身不稳定,我不知道是数据库出错还是我程序出错。同样全局的单例,我们也写了很多把函数定义成变量,在另外一个地方测试的时候直接替换函数的定量,你的代码结构会变得非常难看,这是一个比较不好的实践。
经过我们的不断努力学习,我们现在有了一些稍微好的实践,如图 8。在接口里面约束数据库有两种行为,把数据库作为一个依赖,注入到Server里面。这样的好处是,在每个测试的时候,我可以实际化不同的Server,这样就可以互相之间不影响,代码写起来就会轻松很多,整个架构就会清晰简洁很多。
代码质量管理
我们讲了很多测试的东西,测试是为了发现代码里面的问题。为什么会有这些问题,归根结底就是代码写的不好,所有这些议题中最重要的一个,怎么样去管理你的代码质量。我们的观点是Code Review非常重要,但是它的重要性经常会被忽视。我相信没有一家公司会把Code Review 列为KPI。当你同事代码写的不好你怎么提醒他,你看到一段比较乱的代码,我会想这个说的太重会不会影响同事关系,说的不清晰又会影响质量。所以Code Review的重要性,经常会被小事忽略。
,你还会有一个冻结期,会有一个通知。
另外一个CI跑过程中做的事情是跑代码覆盖率。大家可以看到,这套东西就是我们用Go写的集成在CI里面,红色字体是代码覆盖率没有符合我们标准的,当覆盖率没有达到统一标准的时候,你的Diff也是会被拒绝的。通过这些工具,我们希望把工程师对代码的心态调整好一些,因为工程师程序员最重要的就是代码,你代码都写不好的话,谈架构都是白谈的。我们想通过一系列的东西,提高工程师的编码严谨态度。
我们曾经遇到的和Go相关的问题
我们还有一个问题是DNS Resolution。AWS ELB请求没有被均衡的分布在不同的机器上。这个RFC是定义你服务程序受到DNS发过来IP地址的时候,怎么样做一个选择来返回你的应用,Go一开始用C写的,在C的库里面,如果看到你对外接口里面只有IPv4的时候,它不会对列表进行排序,它会尊重DNS排序选择返回你的应用程序,但是Go的库只实现了子集,这样导致的结果Go每次都会把DNS返回的结果以同样的规则进行排序。可想而知,在同一个子网里面收到的IP地址从列表里面第一个选,在列表中间第一个IP自然而然就会收到更多的请求。我们作为一个Go社区的良好用户,第一时间得到的回复,这个会在1.9里面修复。
讲了这么多,我们现在还在用Go做什么呢?如图我们在做数据的处理,整合数以亿计的乘客,司机以及交通的信息,在做函数式计算以及机器学习平台。
Asta、郝林、张鑫、费良宏等大家联袂推荐 Go 领域图书,四大优势:
1.引导读者了解云原生理念的产生、应用场景、优势
2.集现今诸多热点技术之大成
3.掌握Go语言助理云开发完美实现的方法
4.流程完整,示例具体详细