正文
Klook是总部位于香港的旅游科技公司,成立四年,在亚洲旅游创业领域成为一匹黑马,为全球上千万用户提供折扣门票、旅游特色活动的预订服务。Klook在技术的不断探索和创新,是这家公司国际化、规模型业务的实现根基。今天由Klook的后端技术总监熊传亮,为大家分享Klook在Go语言的应用实践。
1.Go 在Klook的应用情况
2.如何用Go实施新架构
3.架构面临的挑战和技术的演进
4.一点探索和思考
1.
Go 在Klook的应用情况
Klook用Go很早,2015年就开始尝试用Go做项目开发,我们面向全球的:网站、App、Open API等平台都是Go在后面支撑,所有的内部系统,风控,智能推荐,微服务工具链等等也是用Go,可以说Klook的后端用Go在做几乎所有的事情,后端技术栈都是基于Go。
当然,Klook最初也不是用Go,Klook最初的架构是这样子
这是一个非常经典的后端架构,Klook最初的业务服务都是基于这个经典架构的Java单体应用,这套架构的问题在于,依Klook当时的发展速度,在可以预计的时间范围内,这套系统很快会不足以支撑公司业务的增长,很快会面临一个技术架构的升级问题。但启用一个新的技术架构,就需要思考一个问题,怎么在保证业务发展的情况下,去升级和落地新的架构?
常见的顾虑
我认为你要升级一套架构或者落地到新架构,有几个点你可能要考虑清楚:
如何证明新架构或新语言更有优势?
对新架构或新语言,团队是否有足够强的技术问题解决能力?
如何解决旧系统的迁移问题?
业务需求与技术改造的冲突怎么解决?
招不到人怎么办?其它语言开发者转Go之类快么?
如果没有想清楚,升级架构我认为大家可以再斟酌一下。
2.如何落地新架构?
2.1 你需要一些成功案例
具体怎么开始一套新的架构,从哪里着手?从Klook的经验来说,你要开始一套新的架构,你可能需要一些成功案例做背书,Klook最开始做了两个服务
1.香港WeChat
钱包
,客路产品后台服务 (证明新语言是可靠的)
首先是微信钱包的服务,这是当时和腾讯的微信团队合作的一个项目。如果大家打开香港的微信钱包,微信钱包里面购买门票或者旅游类产品,就是Klook提供的服务,而这个服务是用Go写的。
2.Go定时任务服务替换QuartZ(证明能解决旧系统迁移问题)
另外一个项目是定时任务迁移项目,这个项目的背景是这样子的,当时准备多实例部署Tomcat,定时任务会面临重复触发的问题。我们的CTO Bernie建议,可不可以趁这个机会,一次性的把定时任务从Java中抽出来,就花了一点点时间做了这样的服务。这套服务的架构其实非常简单。
2.2 一次定时任务的触发过程
首先把所有的定时任务封装成一个一个独立接口,然后通过Go写的定时任务调度服务,根据定时任务配置定时触发接口,触发成功会有触发成功的响应给调度服务。当这个接口相关业务逻辑处理完毕以后,会把业务逻辑的处理结果再反馈给定时任务的调度服务。
另外,还有一个WEB界面管理和监控定时任务。这套东西从技术的角度并没有很复杂,编码也只花了一点点时间。我认为它做得比较好的地方是它把定时任务变成了接口,任务和调度分离得很干净,也更灵活,符合单一原则。通过这个服务,解决了定时任务迁移问题。 后续,我们用Go做了很多的微服务,增加了很多的定时任务,但是这套代码从写完一直用到了现在都没怎么动过。
有了这两个项目做背书以后,其实这个时候Klook才开始真正的认真思考怎么用Go做微服务。我们知道,Go这几年比较火,如果大家有想要在公司推广Go,可以借鉴一下这个思路,让大家建立起对Go语言的信心。
要上一个新的架构用Go做微服务,可能要做好下面几点
1. 做好日志采集与报警
2. 基础库积累
3. 业务边界划分
4. 统一的技术规范
5. 代码评审很关键
前面分享有强调过做好日志采集与报警的重要性,大家考虑是一样的,日志采集与报警是很重要的,对后端来说,当出现产线问题时,在某些情况下,日志查询甚至是唯一的手段。
另外,做好内部架构需要有一个好的,不断在更新的基础库。另外一个就是业务边界的划分,这个非常重要,因为你的业务边界不在早期划分清楚,随着公司的发展,公司业务和微服务会越来越多,如果不划分清楚,之后就会变成一团乱麻。而你在早期就把业务边界划分清楚,可能后续的代价就不会那么大。我认为再怎么发展,它的大的业务分类是不会有变化的。
剩下的是统一的技术规范和代码评审,这两个东西,我认为对开发来说最大的好处是,它是非常好的代码最佳实践的推广渠道。而好的、最佳的代码实践,它无形之中可以帮开发避免掉很多的坑。这对于服务的肯定性和代码的稳定性是非常有好处的,如果大家的团队里面还没有代码评审机制,我觉得大家真的可以在团队内部尝试一下,它对你的代码质量和服务稳定性提高都是非常有帮助的。
2.3 服务稳定三件套
过载保护 + Go:recover() + 进程守护(supervisord)
如果一个服务要稳定,推荐大家注意这几点:
1、服务的过载保护,如果没有做服务过载保护,当它要处理的量超过服务最大承受能力的时候,它有可能把服务冲垮,后面的服务也会跟着有可能冲垮,甚至你的DB都有可能被冲垮。所以服务的性能测试很重要,你要知道你服务的处理极限在什么地方,再设一个相对比较保守的值。这样你的服务就不会雪崩,连累到其他的兄弟服务。
2、大家知道,GO里面有一个很好用的东西是指针,有指针就有可能会出现空指针,有空指针就有可能会Panic。当Panic了,服务就危险了。所以说你写代码的时候,一定要有recover的机制兜底。这样,就算出现了异常,你的服务也不会挂掉。当然,万一如果你兜底失败,你的服务仍然挂掉了,这时,你需要一个服务守护的工具,服务挂掉,把你的服务自动拉起,保证你的服务是可靠的,可用的。
3、有过载保护保证服务不会冲垮,有recover机制兜底防止服务挂掉,另外还有一个进程守护,保证就算服务挂了,仍会自动拉起。有了这三样东西做保证,我认为,服务的基本稳定性还是有保障的。当然,如果业务逻辑错了就不在这个范围内了。
2.4后端微服务化
Klook的架构微服务化后,当时的架构是这样的:
根据当时的业务边界划分情况,拆分成一个个的服务组,然后,这些服务的服务注册和服务发现,配置中心等,都是基于Consul,fabio做负载均衡路由。然后我们有自己的监控中心,链路追踪,及我们自己的服务治理平台。 当然,在这个架构下面也还有其他很多东西在支撑。
我认为这些应当都是大家非常熟悉的开源项目,至少大家都认识这些东西。这些构成了整个Klook微服务的基石。
随着微服务改造的深入,出现了很多、很多的,微服务治理相关的痛点。 比如,因为引入了这么多的组件或者第三方服务,你会发现,当要做相关事情的时候,经常要在几个窗口或者几个服务间来回切换,经常会有屏幕老是不够用的感觉。另外你的服务越来越多,服务的部署和快速回滚会是很大的麻烦,你服务越多,比如APP发一个大版本,那可能是十几个到几百个服务实例,你很辛苦的一个个部署完了,结果,当测试进行产线回归后,测试告诉你说,很不幸命中了不知在哪里的一个bug,需要紧急回滚,这时候会很惨,要手忙脚乱的回滚,这会是一个问题。
另外就是配置参数的问题,在微服务有很多的参数,假如说批量更改一个参数,比如帐号或者密码,你首先要知道产线那么多服务,有多少服务用到这个参数,怎么去批量更改,你是一个、一个服务更改它吗?这也是一个问题。
总之,服务化后越到后面越发现维护是个很大的负担。对于这些服务治理上存在的的问题,Klook给出的解决方案是这样子的:
我们自己做了一个微服务的治理平台,大家可以看到这个界面上有产线服务的运行情况是怎么样的,它也可以单独针对某个实例进行操作。然后可以在这里面进行批量部署和快速回滚,可以在这个系统上做参数的查询和批量的整理,对一些WAF、流控或者其他的规则,也可以在这个页面进行快速的设置和分发。在这个界面上,基本上已经把微服务相关的痛点在上面解决掉很大部份。至少有了这个界面,我们只要借助鼠标就能完成相关的操作,不用在各种组件中来回切换。
反正我个人感觉,有这个界面以后,很大的提高了工作效率,然后减少了很多的心智负担,我不需要再记那么多的命令和那么多的脚本参数,还有另一个好处是大大减少 了忙中出错的机率,因为有时候往往产线出现紧急情况也是很慌张,一慌张就容易出错。这个比你手动换参数要稳定一点。
当然,这个平台的背后需要对Klook的基础架构做很多的改造,并且需要有很强的技术整合能力才能做这个平台。另外,用一个相对简单的界面去解决这么复杂的微服务治理问题,我个人认为这是符合Go的少即是多的理念的。当Klook的微服务治理平台,特别是上面的界面比较稳定以后,我当时认为Klook的微服务改造进入相对成熟的时期。
但其实并不是,挑战是无处不在的。
3. 新挑战与架构演进
1.全球化扩张对服务的可靠性、安全性和可用性均提出更高的要求
2.系统复杂度在不断的提升,而产品质量要求却越来越高
3.现有业务迭代频率越来越快,同时还在不断出现新业务
4.招人速度赶不上业务发展速度
公司一直在高速发展,会面临各种各样的挑战,其中,最大的挑战是来自全球化,因为公司一直在拓展新的国家、新的目的地,这会面临很多的困难与要求。我不知道大家对全球化的理解是怎么样的,我个人对全球化的理解是这样的,大家可以想象这样一个场景,我想出去玩,说走就走,直接去了香港迪士尼,或者日本的大阪环球影城,或者到了迪拜的某个景点,直接掏出手机通过Klook的APP购买凭证,不用排队,直接扫码入场。不管身处哪个国家,在哪个景点,用户体验是一致的,这就是我所认为的全球化。
而要达到这种用户体验,要面对的情况是,他们可能处于不同的时区、不同的语言、不同的货币、不同的景点、不同的支付方式等等,全球化是件很有挑战和非常有意思的一件事情。回到服务的本身,全球化对服务的可靠性、稳定性都提出了很高的要求,如果服务不稳定,客户就没法进去,只能排队买票,这种用户体验是很差的,所以说全球化的扩张对服务的可靠性越来越高,这是个很重要的问题。
同时,因为公司一直在扩张,不可避免的面对的场景和业务变得越来越复杂,业务压力很大。而同时,有一个很大的问题是人手非常紧张,后端非常缺人,不止是后端,整个技术团队都缺人,甚至整个Klook的所有部门感觉都在缺人,这可能是一个处于高速发展期的公司都会碰的问题,人才的需求。
3.1后端面临的其它问题
1.太多重武器,既消耗机器资源,部署和运维也相当复杂
2.一些开源组件的Bug,与频繁更新的跟进令人头痛
与此同时,后端还有一些其他的问题。大家可以看到前面我们引入了非常、非常多的开源组件或者第三方的服务。我们有那么多的服务,这些组件和服务其实是需要人来配置和维护的。而且搭建这些开源组建的东西是需要资源的,这方面需要花很多的人力、物力。另外就是bug的问题,当然不只是开源软件,其他商业的也会有,对于很多的bug。
当命中后,你去官方查解决方案,往往给的结果是,新的版本已经解决这个问题,或者下一个版本会解决这个问题......其它还要面对很多组件版本的更新频繁且兼容性很差等问题。
这两个都是很热门的项目,特别是Consul,可以看到有很多人参与,有很多人去让它变得更好,这是很好的事情。但大家可能也要注意一下,它已经解决了1900多个issue,还有300多个在处理中。这300多个里面就可能说的是某些bug,我们就命中过其中一些bug。当时命中bug,去查解决方案,里面告诉我说新的版本已经修复了bug,问题是当时产线已经命中了bug,怎么可能没有经过完整测试就直接升级产线?整个微服务的基石就是它,所有的服务注册、服务发现,配置中心都是基于它。
所以我们重新对整个架构的底层进行梳理和思考,引入了那么多的开源组件,是不是真的有这个需要?它的成本到底有多高,给我们带来的好处有多高,对服务稳定性的伤害有多大?对资源的占用是否合理? 最后,我们的结论是需要云化,我们要多使用云服务。云服务在大部份情况下,能提供更好的保障。
3.2微服务云化
微服务化架构
在新的架构里,大家可以看到很多的开源组件或者第三方服务不见了,简洁了很多。
这个架构你可以看到分三个部分,云服务、开源组件加第三方服务以及自研的工具链,就是一些自研的如源代码生成工具,自动化平台和命令行工具之类。这三块构成了目前的后台架构。那为什么中间还剩下一些开源组件之类?首先是有一些功能是云服务商不提供的,另外就是有些项目的质量要远远超过云服务商提供的服务,云服务商提供的服务不是每个都那么好的,这个东西就看你怎么权衡。
大家可以想一想,这种把后端服务全面拥抱亚马逊或者Google云或者其他云服务商以后,会带来什么样的好处或者思路上的转变?
3.3日志处理与报警-ELK Stack
这里我有准备一个例子,一个经典的ELK Stack,相信在场很多人都非常熟悉。日志上报、采集都可以用它来解决,非常好用,我们的开发也非常喜欢用它,Klook早期都一直用它。它本身没有什么问题,有问题的是公司的日志增长速度太快,最初是一台ELK的服务器,很快开发就说查询太慢,查不到资料,然后很快变成两台、变成四台,为什么不一次到位变成四台或者八台?作为创业公司还是要有些成本考量的。
所以,我就开始反思,这个做法可能有问题。在扩ELK这个事情上,花费了太多的精力,但是它的效果并不好,往往你升级以后能稳定一段时间,但是很快又不够用。我们又没有太多的人手在这上面做性能优化,我们都是直接升级。整个评估下来,这个效果真的不是很好。
如果,我们基于云服务的思路去看这个解决方案,我们最后的结论是这样的。
3.4日志与归档数据处理方案
这是我们梳理过后的日志解决方案(PPT),大家可以先看看蓝色的线条。目前的方案是,服务的日志直接用protobuf协议发到nats队列,然后数据上报服务将其上报到aws s3,及对异常触发报警。当我们的开发或业务需要查询相关日志的时候,可以通过我们的数据查询平台拿出数据。
这样带来的好处是显而易见的,首先4台、8台的ELK集群和挂载的日志存储盘都可以扔掉,不再需要人维护。S3是只要你有数据就能存得下,Athena不会因为你的日志量太大影响查询速度,它的查询速度是稳定的。另外现在服务的日志不需要落盘,而以前ELK是日志写到本地磁盘,然后Filebeat通过配置,监控指定日志目录进行上报,这意味着你的日志要在本地写磁盘,有爆盘的风险,且存在延迟。另外一个问题是我们服务用的日志包是glog,它为了提高性能,会缓存30秒再批量写入日志,而我们的报警依赖于日志,报警就会有30秒的延迟。因为PB协议和Nats的速度很快,日志报警会变得更及时。
同时,这样的做法可以带来更多的可能性,首先上报服务,直接完成了日志的归档。 另外还有其他类似的东西也可以采用这种方案上报到AWS S3。 对于数据查询平台,还可以在上面做很多事情,比如说以前有开发反映,一个Request日志只能一个个服务查,但是做微服务,一个请求后面有很多服务,你查问题的时候需要进行聚合,而现在这个可以根据你的相关日志查询的集合快速定位问题,另外,以前有些数据查询因为数据量或查询条件问题会查得比较慢的,在这个界面上可以做相关的限制或者优化。它还有一个好处是支持SQL语句,通过SQL去组合查询日志。