最终,为什么选择go-kit

转载请附上原文地址:http://www.jianshu.com/p/0c34a75569b1,谢谢!

前言

工作这些年,先后经历过两家公司,有参与过php语言框架的开发和主导过go语言技术栈的落地工作,在此过程中有一些感悟和总结。我想以之前我主导的go语言技术栈为线索,来陈述当时遇到的一些问题,以及分析问题和解决问题的思路。主要目的是想陈述go技术体系在团队中落地的过程,分析我们在各个阶段中,遇到的一些问题,并將分析问题的思路和解决问题的方法记录下来,以便让后来的同学了解go语言在团队的演进过程,吸取相关的经验,以便在今后的系统设计和开发上少走弯路。

在系统不断演进的过程中,有时候对框架的选型很随意,认为能满足现在功能就行,没有对其功能扩展性和性能进行考量,导致随着业务的发展,发现当时选型有误,但想转又很难。那么现在,我就来谈谈,我们是如何抉择这些事情的。

我们为什么要由php转向go

最初,大约是在2015年时,平台内所有的业务系统均是由php语言构成的,上线没多久,平台的流量开始爆发性增长,并发量越来越大,晚高峰每秒请求由最初的几千QPS到现在的几十万QPS。当时,最快最有效的优化手段无外乎加机器和增加php-fpm的数量,但是,受限于php本身的网络模型,终究不适合这种高并发,大流量的场景。面对这种棘手的问题,再加上当时人手有限,业务任务重等因素,于是找其它部门借了一批写lua的外援,帮忙把一些逻辑简单且访问量大的接口,换成了lua,暂时扛过了晚高峰,因此,有相当长的一段时间,大部分项目是php+lua共存的一个状态。但由于lua本身的一些局限性,不太适合做一些复杂的业务逻辑,所以最终,在业务有强烈的需求的前提下,同时伴随着技术发展的潮流,于2015年底,我们开始选择转向go语言。

我们怎样由php转向go

由于之前团队全部都是php栈,在go方面的积累并不多,所以在php转向go的过程中,面临了在转型过程中都会遇到的问题:

1 用什么框架;

2 在业务任务重,人员极其匮乏的情况下如何將php项目重构成go。

用什么框架

之前团队有人仿造内部php框架开发过一个golang框架,有人提议将其直接拿过来用,有人说找个开源的如beego,gin,martini等这类流行的框架。我个人当时不太赞同使用自研的框架,主要有以下几点原因:1 文档少,漏洞多;2 需要投入人力去开发和维护,在当时人力极其紧缺的情况下是不现实的。另外,当时社区流行的框架也比较多,但是最终也没有选择那些流行的框架,主要是出于以下考虑:时间短,任务重,没有精力去辨别各个框架的优劣,适用场景以及性能如何。万一冒然使用一个还没有深入了解的框架,线上出问题咋办!尤其在当时系统频繁出问题,顶着各种压力的情况下。

虽说,我们无法在短时间内选一个合适的框架,但是能够确定的是:我们的需求是什么?

1 只做高性能的HTTP 接口;

2 需要完整的单元测试体系;

3 可扩展,组件化;

基于以上三点,可以发现,go语言自带的特性就可以满足这些需求。于是,我们开始决定裸写。

此外,还有一个裸写的原因就是:没想好将来想要什么!当然,每个团队的背景不一样,业务场景也不同,在人力和时间充裕的情况下,还是需要选择一个合适的框架比较好。

裸写不是乱写

裸写不是乱写。众说周知,用框架的其中一个好处就是保证团队代码风格的一致性,当然,目前市面上除了beego外的大多数框架,在代码风格上也并没有做约束。为了保证团队go代码的规范性和一致性,按照经典的分层架构和过往的经验,我们制定了一套go编程模版,由上向下:Router层,Service层,Dao层,还有贯穿这三层的Entity层,架构图如图1所示。其中,Router层负责处理与http handler逻辑,请求参数以及response格式相关的处理工作;Service层处理业务逻辑;Dao层处理数据访问逻辑;Entity层负责实体定义相关的逻辑,并贯穿Router,Service,Dao这三层。层与层之间不直接进行耦合,高层模块不直接依赖与低层模块,它们都依赖于所定义的抽象接口。

Booch曾经说过:“所有结构良好的面向对象构架都具有清晰的层次定义,每个层次通过一个定义良好的,受控的接口向外提供了一组内聚的服务”。

除此之外,我们还维护了一套常用的公共组件库,如:日志库,各种数据库driver等。

图1 分层构架

如何重构

当我们制定好编程模版后,我们就开始进行项目重构工作。由于,业务任务重,人手少,所以,重构的基本方向就是:根据业务需求,结合接口重要性进行重构。只有这样,才能保证在业务需求不停的情况下,进行系统重构。所以,在此期间,有相当长的一段时间是处于php+go进行混合编程,混合部署的状态,如图2所示,重构完的接口,通过nginx代理到新接口,这种状态一直持续了一年。采取混合编程的思路在重构初期,可能会遇到一些问题,比如:同一段业务逻辑,需要用go写一遍,用php写一遍,无疑增加了一定的工作量,当然这也是避免不了的。

注意,有些同学在重构的时候容易走到一个误区:一口气把整个项目都重构了,或者说重构大部分内容。从时间成本和系统稳定性上来讲,这种方式风险比较大,不推荐。推荐的思路:一个接口一个接口进行重构。

图2 php+go混合架构

最终,为何想引入go-kit

之前这套东西,基本上可以满足大部分的业务场景,但随着随着业务的发展,请求量越来越大,同时,有些请求的链路也变长了,为了继续保证接口的高并发和低时延特性,团队有少量业务开始尝试GRPC。根据测试,压测一个空接口,GRPC的性能大约是HTTP+JSON的2~3倍,在这里推荐一个压测框架fperf

但是,针对GRPC的使用,不要盲目“求新”。以本人经验,HTTP+JSON的模式基本上可以满足大部分的业务开发场景了,针对小部分对接口时延和并发量要求极高的场景可以考虑使用GRPC。因为,GRPC本身还是不利于调试,且会在一定程度上增加调用方和服务方的耦合性,所以,最后的传输协议和格式建议还是以HTTP+JSON为主,以GRPC为辅。

另外,我们还是需要标准化一些中间件的使用,如回路断流,rate limit等,来保障系统的稳定性。这次的思考,时间比较充分,所以有精力去研究一些新的东西。最后,框架抉择的思路和最初是一样的,就是,明确我们的需求是什么?

1 需要一个同时支持多种传输协议,不论是现在的http,thrift,grpc,还是将来出现的某种新协议,要有良好扩展性的框架;

2 框架本身和业务代码保持一种低耦合的状态;

3 需要一套通用的middleware,使之与http,grpc等传输协议无关。

目前市面上流行的框架都是围绕着http协议而展开的,包括gin,beego等。经调研,我们发现go-kit能够满足我们的需求。 go-kit本身不是一个框架,而是一套微服务工具集。其设计思想跟我们初期go编程模版制定的思想也算是不谋而合——分层设计,组件化,可扩展。go-kit的架构如图3所示,分为三层结构:Transport层,Endpoint层,Service层。Transport层主要负责与传输协议HTTP,GRPC,THRIFT等相关的逻辑,Endpoint层主要负责request/response格式的转换,以及公用拦截器相关的逻辑;Service层则专注于业务逻辑。go-kit除了经典的分层架构外,还在endpoint层提供了很多公用的拦截器,如log,metric,tracing,circuitbreaker,rate-limiter等,来保障业务系统的可用性。它们在设计上有一个共同特点:都是同传输协议无关的。在之前的一些http框架中,这些拦截器同传输协议是紧紧耦合在一起的,如果,此时我需要将某些HTTP接口改造成GRPC协议的接口,那么这些拦截器我还得再基于grpc再实现一遍,设计上存在一定的冗余。因此,借助go-kit这套工具集,我们就能很好的对transport协议,middleware进行扩展,且不会影响到业务本身的设计。

图3 go-kit架构图

怎样將go-kit集成到现有的业务系统中

我们找到了心仪的开源工具后,那么我们怎样以较低的成本将其引入到我们业务系统中呢?之前我们有提到,我们的go模版是分为三层:router,service和dao。而go-kit也分为三层,我们可以根据每层职责的不同进行重新组合,如图4所示,从上到下依次为:transport层,endpoint层,service层,dao层。这样就能很轻易的將go-kit集成进来,当然你如果哪天因为某种原因,不想再继续使用go-kit这套东西,直接將endpoint层和Transport层移除即可。在集成的过程中,需要注意一点:之前的代码中router层不能包含任何业务逻辑,否则就无法集成。

图4 架构的演进

如何高效的使用go-kit

前面有提到,go-kit本身分为三层,针对这点有同学会提出:“每次新建项目,都需要手动写下go-kit的这三层逻辑,有点浪费时间,不够简洁”,这确实是一个共性问题,从go-kit的github的issue中可以发现,也有不少人反馈过类似问题。很庆幸的是,有人给我们铺好了路,详见GoKit CLi,其主要功能如图5所示。

图5 GoKit CLi功能模块

这个工具可以根据我们的需求自动生成service,transport和endpoint模版,以及生成供调用方的使用的client library,节省我们大量的时间,提高我们的生产效率。具体操作步骤,可以参考GoKit CLi的说明,这里不再赘述。

总结

不论是我之前的一篇文章《浅谈互联网业务系统设计》所讲的系统设计,还是这篇文章所陈述的框架选型。我一直在强调的一点就是:需求是什么?如何在满足需求的同时,让框架和系统具有一定的弹性。无外乎使用经典的五大设计原则:单一职责原则,开放封闭原则,依赖倒置原则,接口隔离原则,为你的设计提供坚实的理论基础和方向指引。另外,在做选型的时候不要盲从,别人口中好的东西,不一定适合你,只有明确自身需求后,找到适合自己的才是最好的!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259

推荐阅读更多精彩内容