如何更好运用Go语言 造就数千万月活的互联网产品

mob604756f0bbf4 · · 1155 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

熊传亮:Klook的后端技术总监

前言

大家下午好!这次我给大家带来的分享是 Go 在客路的应用实践。我将从以下几个方面作分享:

一、Go In KLOOK

二、不同阶段的架构回顾

三、面临的新挑战

四、 一点探索和思考

Go In KLOOK

首先给大家介绍一下客路,目前主要业务是在海外,是一个全球目的地旅游体验预订平台,当地遍布全世界 250 个热门国家,提供 8万+ 服务的预定,包括折扣景点门票、一日游、当地交通等等。每月有 2000 多万全球用户使用 KLOOK 预订旅游活动,客路的国际化也得到了资本的认可,累计获得 3 亿美元融资,成为了目的地旅游全球融资额最高的公司。下面是客路 APP,大家可以看一下。客路近期也在大量招募超百位的技术人才,在这里给自己提前打个广告,欢迎大家去拉勾看看。

如何更好运用Go语言 造就数千万月活的互联网产品

客路团队是一个相对比较国际化的团队,目前来自于 25 个国家及地区,大家可以看到客路办公室遍布全球,客路做的是全球化的生意,什么是全球化?如果有一天你想去迪士尼去玩,你带上手机直接去,到了场地打开客路 APP,直接购买扫码入场就可以,不需要考虑其它,无论你身处全球的哪个地方,所有用户体验是一致的,这是客路一直在做的事情,这就是全球化。

如何更好运用Go语言 造就数千万月活的互联网产品

GO 在客路的应用有很多,包括 Go in KLOOK APP,还有内部运行的APP,几乎整个客路后端都是用 Go 开发,用 Go 在做客路几乎所有系统。之所以说几乎,还有一些东西是 Go 不能做的,Go 不能做的东西怎么办呢?当然就用 CGo 啦,这个其实是一样的。

不同阶段的架构回顾

对于客路在不同阶段所使用的架构,我在这做一些回顾分享给大家,希望大家能有所收获。客路最初和很多初创公司一样,是基于 SSH 的单体应用,但客路发展其实非常快,在当时那个时间点就可以看到,以当时的发展趋势,Java 很快将不能支撑,我们决定必须要重构我们的技术架构,做这个决定真的很难。

最初期的思考

我们也做了很多考量,比如,当时团队大部分是 Java 的,那换 Go 语言的成本怎么样?所以最初用 Go 试做了很多小系统,发现 Go 语言效率很高,且学习成本很低,通过一步步的积累后,我们开始大量的通过 Go 来实现我们新的基于微服务的后台架构。

如何更好运用Go语言 造就数千万月活的互联网产品

这上面列了一些我们当时有考虑到的一些事项。如写业务是否够快?旧的系统怎么办?旧的系统难不难?碰到难题,团队能不能 Hold 住? 我们当时通过一些案例和验证,发现这些团队完全可以 Hold 住的, Go 写业务也是飞快。

同时还会有一些问题:需求多不多?人员是否紧张?人员跟团队技术有冲突的时候怎么办? 我们当时定了一个原则,如果这方面有冲突,一定是以业务为优先的原则。

关键先用起来,当真正用起来后,开发效率有提高,业务写得快了之后,上面的所有疑问都不再是问题。

最初期的落地

具体落地现在看起来,其实当时有几个点做得比较好,保证了新架构的有效落地。 首先是思想上统一,要制定一个好的规范。如用什么样的原则划分微服务,这些规范要统一;然后是工程规范,就是说你的代码仓库怎么摆,具体到每个用户仓库里面,每个模块文件用什么统一规范去做,早期这些事项规范的越早,对后面的帮助越大;其次,公司业务变化非常快,当时很难定义一套特别具体的细则来做为业务边界划分规则,但大的划分原则是必须统一的,大家要在统一的频道、统一的思路上面去做事和沟通。没有这个,当微服务多了之后,要重新梳理密密麻麻的各种服务,代价非常的大,所以统一思想非常关键;最后,要有一个可用的基础库,大家注意这个库的只要可用,够用就可以了。早期,尤其是团队人力资源紧张的情况,基础库其实不需要特别强大,太过强大全面,意味着会变得复杂度很高,团队可能会很难驾驭,而且也很有可能团队等不起,你只要好用、足够可控就可以了。

如何更好运用Go语言 造就数千万月活的互联网产品

这里要再强调一下,在早期有一份比较好的代码规范是非常必要的,这也是代码评审推行的必要条件之一,代码评审其实是一个相互学习的过程,通过相互检查、沟通交流,大家的技术水平和业务能力才有可能提高的更快。同时,一个好的代码规范,能无形之中帮研发避开很多已踩过的坑,对提高代码质量,能起到非常重要的作用. 总的来说因为有这些东西作为基础,我们客路早期微服务实施的算比较顺利。

小服务大作用-监控

现在回过头看这个阶段,除了上面说的这些以外,发现当时写的一个小监控服务,在对于新架构的实施也起到了意外的关键作用。这个监控服务当时就花了一点点时间,用了几百行 Go 代码来实现,它起得作用看起来似乎没有多大,就一个告警和简单的日志收集,有一个日志查询页面,但是它实际起到的作用非常大。

作为一个创业公司,获取一个新用户是比较难的。用户的口碑非常关键,产品要是出了问题,会导致用户投诉。 公司 CEG (客服)部门会过来找开发,这时负责的研发如果没有一些快速定位问题的工具或方法,可能会很紧张,因为不知道到底是服务问题?还是系统出问题?还是网络出问题?或者是数据库出问题了? 好不容易有点头绪,还不知道具体这次请求有经过哪些服务,是哪个环节出了问题?

而有了监控服务后,首先我们能通过告警提前发现异常,并在出现异常后,能通过日志查询,快速定位问题。 当时起的作用是立竿见影的。并且在当时情况下,这套东西 Go 这边有,Java 那边没有,这个对比非常之明显。

监控和告警应当是你的架构基石中,不可缺少的一环,它能带来很大的收益,千万不要忽视。

第二阶段

当微服务从几个增加到十几个的时候,一些问题微服务治理相关的问题开始出现。比如 APP 发个版本,会发现要开十几个窗口,执行一堆脚本,来回切换,这是很痛苦的事情。服务配置管理也变得比较困难,还存在很多其它类似问题。所有问题粗看起来好像很容易解决,比如招个运维?但真要去解决时发现其实并不容易,因为有些会涉及到基础技术框架层的东西,光运维是不能完全解决的,另外当时团队没有也没专职运维,好的运维其实也蛮难招的。

还有另外一些问题,比如说服务在线检查以及服务接口的内外隔离之类,也很麻烦。以服务接口隔离为例,虽然我们在微服务方面做了很多分离约定,但后来发现,前端还是可以绕过后端对外服务,直接调用后端内部服务接口。 另外,有些后端服务对于本身所属的 API 接口分类和权责也不清晰。 比如,在实际开发过程中,他可能在服务里面,既支持了对外的接口,又支持对内的接口,还有支持内部运营系统的接口,这个在一个服务里面是三种不同的业务场景,它们使用场景都不一样,这个其实也是很可怕。到这个阶段微服务管理开始有点乱,感觉有点吃力了。不过还好,在后期出现了一个转折,这个转折点出现在哪里呢?

转折点-微服务治理平台

转折点是研发了一个微服务治理平台,当时从有想法到让它跑起来实际上花的时间并不多,它的核心思想是说把所有服务注册、部署、在线状态管理和相关的配置等等聚集在一个地方管理,不需要关心背后的各种关系也不需要切换,一个系统一个页面管理所有事情,当然,这些功能的背后是需要对架构进行一系列的改造才能做,团队也一直在持续对它进行优化和改造。

如何更好运用Go语言 造就数千万月活的互联网产品

微服务探索与发现

随着我们持续的完善发展,引入了非常多的开源组件,大家可以看到各类组件很多,有些大家应当都非常熟悉,如 Consul、fabio 等等。它们都非常好用,但引入后是需要人来维护,而这其实会消耗掉很多的资源,我们很多人力和时间,如果消耗在维护上面,消耗在部署上面,这其实是一种浪费。

如何更好运用Go语言 造就数千万月活的互联网产品

后期问题

另外,后期发现开源组件的搭建和运维上,投入的团队资源比重越来越大,在消耗人力和机器资源的同时,部署和运维也变得太过复杂。同时,一些开源组件的 bug 与频繁更新的跟进也令人头疼。在遭遇过一系列的产线 Bug 后,我们开始思考这些分析,决定大部份组件移到云服务上,我们做了几个方面的改变:

第一,最基础的设施,比如说 Redis、Nginx、NFS 之类,不再自己搭建。这些完全可以使用云服务。

第二,对依赖组件做减法,只留下最精华的开源或自研组件,其它都尽量换为云服务组件。为什么还要留来下一些开源组件呢?首先不是所有的云服务都那么好,也有很糟的云服务组件,有很多非常不错的开源组件做得比云服务好。另外,也有很多组件也没有云服务可替代。

第三,通过自研组件的方式来解决架构的可控问题。自研组件这件事对我们来说,它的功能不需要那么多,但是它要满足未来一段时间的需求。它是自研可控的,出现问题时,我们有对应的解决方案。有些组件的实现,你会发现它非常强大,但是它非常复杂,想完全掌控这个东西,其实是要花费相当大的精力。可控性在有些设计上非常关键,整个架构需要足够可控,尤其架构组件多起来的后,自研必须纳入考量的选择。

精简后

这是精简后的架构,可以看到只保留了 NSQ、slack、NATS 等几个有限的组件,复杂度与维护工作量减轻了非常多,这就是转换思路后对我们带来的影响。

如何更好运用Go语言 造就数千万月活的互联网产品

案例-日志查询系统

我这里还准备了一个这种思路下的实践例子"日志查询"系统。 查询日志一个非常高频的需求。刚开始我们有好几个不同场景不同平台的日志查询系统,其实查询心智负担很重,研发不应当需要那么多的日志查询平台,最好只提供一个统一的查询平台就好。

微服务下,接收和处理一个请求基本都会经过很多的服务实例,对请求的日志,如果可以把请求经过所有微服务所有日志全部串起来,对于定位修复问题,会是很大的效率提升。 另外,我们上新服务或新需求时,研发常常需要实时定位日志,但因为安全的考量,不是所有的开发都直接登录服务器去实时监控服务实时日志,而我们现在的日志系统直接支持在网页上实时对产线服务实例日志在线监控,效果与 "tail -f" 相同,这是个非常受欢迎的功能。

早期的日志查询都是基于 ELK 是很难做这些定制化功能的,当然早期它也非常好用。但后面公司日志涨的实在太快,一直在加服务器, 我们又没有人有足够精力在上面做优化,虽然我相信如果优化的好,也是能撑住的。最后方案是用云服务加自研实现了一个稳定,性能比很高的方案。

如何更好运用Go语言 造就数千万月活的互联网产品

日志查询平台具体是怎么实现的呢?首先,各类日志会上传存储到 Amazon S3,研发可在查询系统,通过背后的 Athena 进行日志查询。 性能有保障且几乎没什么维护成本。其实,前面有一个讲师讲了一个非常好的日志解决方案,我很羡慕唯品会有这么多能力和资源可以在这个事情上一个事情做到极致,但是对于创业团队来说,很难有这种资源或者精力投入到这个事情上面,对比唯品会的方案,使用云服务其实是一个对创业团队来说是一个受益非常高且更实际的方式。

如何更好运用Go语言 造就数千万月活的互联网产品

面临的新挑战

经历过几次架构思路上的转变及一系更迭代后,当时我们觉得至少应付未来一两年是没有什么问题的。其实市场的发展超乎意料,最大挑战是公司融资了,公司完成 2 亿美元的 D 轮融资。这个事情带来的影响:

第一、用户增长的更快,用户量的上涨,带来的请求量也跟着涨得非常快。

第二、有了更大的市场压力。有更大市场压力怎么办?市场团队会拼命提需求,开新的业务线,不停在现有产品上加各种各样的优化,最后结果可能就是所有研发都在拼命去做需求,技术架构升级这块已经没有人有精力了。但从监控和数据上看,系统的迭代是不能停的。我们很艰难的从团队抽出人手开始了新一轮的优化。

最主要的优化精力放在两个方面: 性能 和 安全。

说到性能问题,双十一刚刚过去,相信国内的电商为双十一准备了很久。对于很多电商来说,双十一只需要准备好国内市场就可以了,但是客路不一样,我们做双十一的时候,发现韩国、日本、台湾、香港用户也在过双十一,但最后发现当日最大流量竞然是来自于菲律宾,市场真是给人意外。 另外,客路做的是全球市场,考虑不仅是国内市场还考虑国外市场,比如说国内的双十一,618 大促要准备,国外的黑色星期五也要准备,国外及一些地区的区域性节日也要准备,这些会对系统性能提出更高的要求。

如何更好运用Go语言 造就数千万月活的互联网产品

从 HTTP 转 RPC,是我们为解决性能问题做的一个比较大的决策,上面是当时提出一些需求。 说白了就是,即要有大的性能提升,又要尽量无感知的能让现有的项目切换过去,减少对正常业务需求的影响。

以当时团队的人员情况看,应当是很困难的任务。 但实际情况是,RPC 在很短时间内,超出预期的在团队落地。 这一方面是团队对 RPC 的热情很高,另一方面确实是团队的成长速度出乎意料。可能是一路走来,趟过的坑,让团队的积累会相对足一点,如果直接用特别成熟的框架跳过很多中间过程,团队成员可能也同步缺失了某些收获经验的机会。当然,这个各有利弊。

具体的RPC实现

目前的 RPC 基于 gRPC,融入客路微服务治理:服务注册、服务发现、负载均衡。对于 gRPC,当时我们做过一些对比,它其实是最均衡的一个 RPC,只需要针对它在现有系统上很少量定制化就 OK。然后,为了减少迁移成本,还做些有意思的事,比如,我们做了一个插件,可以直接把 struct 转为 PB 协议。对于已有的 HTTP 服务,通过在 main.go 加二行代码,即可将整个服务转为即支持 HTTP 也支持 RPC 的服务。

如何更好运用Go语言 造就数千万月活的互联网产品

另外是安全问题,做电商的都离不开安全这个话题,基本都会认识到安全的重要性,当然安全涉及到非常多方面,要做很多工作。

比如在语言层面,需要有代码安全规范,让研发知道,怎么写代码最安全。

最近在考量一些源代码的分析工具如 Gosec 来进行加固。而 Go 语言本身虽然是相对比较安全的语言,但留意一下,你会发现,暴露出来的问题还是很恐怖的,大家可以关注下面 PPT 上的 CVE 漏洞。

如何更好运用Go语言 造就数千万月活的互联网产品

安全-基础架构层

在基础框架层,我们用自研的 secutils 安全工具函数库,来替换很多可能存在安全风险操作,比如文件上传,下载之类操作。在 SQL 层面,基于 SQL.Driver 层,我们做了一些封装,会做统一的 SQL 采集,然后在后台通过工具做相关的安全分析。在框架层也自研了一层薄薄的WAF封装,提供给对外服务使用,之所以要做一层 WAF,是为了能在必时时,定制一些自己的安全规则,因为很多业务场景,只有我们自己最清楚。

场景思考

通过上面一系列的性能与安全优化后,我们在总结时发现,到了这个阶段以后,通过这些单纯的技术框架优化,在支撑业务的快速发展方面,作用明显不如之前?从以往的经验看,在进行很多优化后,团队会产生很明显的受益,但这次,好像有点失灵了。

因此,我们列了很多典型的场景,有技术的,也有业务的,来做分析,看最有益的方案是什么?

场景一

多语言处理

早期多语言处理相关的函数 ,是统一放在代码公共库里面的。因为客路做全球化市场,所以会面临很多语言的问题,早期只支持简体中文、繁体中文。英文。但随着市场越来越大,对更多语言支持需求也越来越多,越来越细,甚至最近开始要求支持英文方言。 而微服务有个特点,服务多。每次加个语言很痛苦,别的不说,光全编译一遍都非常花时间。 在这个场景下,如果把多语言相关的函数独立出来成一个单独服务,好处非常大。这让我们认识到,业务发展到一定程度,早期特别好用的功能,说不定反而变为了瓶颈,所以不是基础公共库功能越多,越复杂就好,反而应当适当的给它瘦身,减负,收益更大。

如何更好运用Go语言 造就数千万月活的互联网产品

场景二

第二个场景是同一个业务线在微服务场景下,同时存在 A、B、C 三种不同甚至更多种业务服务,按业务重要性分一定会有核心与非核心,但核心服务并不一定是请求占量大的,而服务所需资源(比如说数据库或缓存等)总体上看是有限的。在一些特殊情况下你会发现出现资源争用,非核心服务请求有可能抢占了更多资源。这个问题在单纯基础代码框架层很难解决,要彻底解决,会需要独立出一层资源调度层,这样才能做到可控。并且还有其它的好处,当你的资源如数据库要做升级或数据迁移时,有这个中间层在,处理手段会灵活动。

业务也是一样,初期一些封装一些通用的业务函数也蛮好用,随着业务发展变化,各种更细化,更定制的需求堆积后,框架层的一些基础业务函数逻辑可能会变得非常复杂,而且因为它可能会被 N 多的服务所引用,任何的改动,包袱很重风险比较高的操作。这时,把这些抽象成服务,以接口的方式提供,反而是值得的,会更灵活,更有优势。

资源的可控,业务层的合理抽象,基本能保证,不管需求怎么增加,怎么变,系统的改变会最可能的小,服务的拓展性会更强,更灵活。

一点探索和思考

有三点探索和思考:

第一,主动变化,在不同阶段,找到最适合的那个架构。这个架构不需要非常强大,但是是最适合你的。基础库到一个阶段的时候,你会发现很多东西是一个体系,不要盲目特别追求特高要求的东西,你要考虑整个团队的消费能力,这个的东西可能非常好,但是需要十几二十人在维护,你的整个团队可能不一定 hold 得住。

第二,要善于“组合”,也要善于“拆分”。针对你的问题要知道怎么利用已有的资源组合利用起来,变成可以落地的解决方案。 选型时不要太过拘泥,开源组件、自研、云服务 哪个在当时合适就用哪个,关键是要解决问题,很多优化可以放到后期去迭代。另外,当系统发展到一定阶段后,一定会在某些地方碰上瓶颈,找到瓶颈,该用新组件的替换的就替换,该拆分独立成新服务的,就应当拆分。

第三,服务稳定最重要。所有的优化不能影响服务的稳定性。

我的分享就到这,谢谢大家!


有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:mob604756f0bbf4

查看原文:如何更好运用Go语言 造就数千万月活的互联网产品

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1155 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传