前言
在 4 月 27 日举办的 Gopher China 2019 中,来自花椒直播的周洋进行了题为《花椒直播关于创业公司中台的技术思考与实践》的演讲,以下为演讲实录。
No.0
背景介绍
周洋:2009年我作为应届生,在金山软件工作过一年,主要从事的是C和C++的系统开发,简单接触了一些epoll和libevent还有boost asio等,这些工作经历相信也是我早期被Go语言从事网络开发所吸引的原因。2010年我来到新浪,学会了世界最好的语言,两年的时间一直从事PHP开发,即使转型Go语言多年,个人仍然认为,业务场景下,PHP仍旧是快速迭代和开发的很好选择。相信我也是近几年公开演讲中,力挺PHP做业务开发的gopher吧。2012年来到360,因为之前在PHP在新浪做了一部分业务,主要是写手机微博的v3框架封装,如果有手机微博同学,据我所知是踩过我没有填上的坑的。几年以后,听说鸟哥来到微博的时候,手机微博也替换成Yaf等方案,把我原来框架替换掉了。其实框架就是这样,如果不能业务正交的持续跟进,当设计者离开或转移注意力的时候,灵魂也就不在了,这也是我个人倾向于业务团队采用轻量级的框架,只是在统一的规范下嵌入子功能,当新技术和方案对应的库和服务出现后,能够及时更换,而不是在之前的“完美”设计中负重迭代。
2012年我来到360,做整个360集团PUSH长连接服务,这是一个承载亿级别长连接在线的典型高并发场景,2012年Go语言的底层没有这么成熟,很大一部分工作是与Golang的GC做斗争。我们一些案例在Gopher China第一次大会引起了重视,并且通过Asta与GO Team建立了联系。这也是首次有国内大厂使用Golang在生产环境成功构建亿级在线场景。文章发表在Golang的官方blog上,为当时使用Go语言遇到瓶颈的团队提供了参考案例。现在Golang的Runtime和标准库已经足够强大了,但是优化思路万变不离其宗,都是基本的策略,防止竞争,防止内存频繁分配,常驻Goroutine等等。2012年到2016年之后,我相当于从公司系统开发团队调到360项目4部直播业务线,相当于转型到业务开发。业务开发要应对产品需求,跟以前工作模式不一样了。在这个过程当中,系统开发转到业务开发转型,遇到的一些问题,行程的一些积累希望在后面的文章中和大家分享。
今天主题分三部分,第一部分是Go在业务场景下探索中台的诞生,所谓中台概念就是今年热点,实际上已经存在很久了,我想通过花椒服务端的从0到1,从1万DAU(日活跃用户数量)到过百万DAU(日活跃用户数量)迭代中,讲一下中台自然而然行程的一些雏形。
第二部分,是中台服务化的探索,想通过一些Case,来阐述这个问题。做几个中间件就是中台了? 我在创业公司理解这个概念,不只是做系统开发,不只是做开源东西封装解决公司问题。什么是真正有价值的技术产品,能够真正有效提升团队的开发效率,将结合实战阐述。
第三部分,花椒直播的中台团队对业务如何支撑,大型活动,新的产品线。创业公司需要什么样的中台技术和团队能不断拥抱市场热点,这一部分将讲述,中台团队结合服务端,客户端和前端综合的构建可复用和迅速构建的基础能力。
No.1
Go在业务场景下的探索,中台的诞生
PHP 转 Go 的思考
当我承接花椒服务端的时候,大概的架构如上图,单体服务居多。大概有三个部分,分别是运营系统,一部分是经济系统(支付和金钱相关的业务和底层服务),产品系统(承载产品的所有功能)。存在一部分中间层服务,比如说搜索、图床(图片存储与处理)、静床(静态资源和文件的存储和缓存)。只有一个Golang的服务,这个Golang服务还是从ERlang转过来。那一阵Erlang挺热,有淘宝背书,但是迭代这个系统需要招人,一个月时间只招到一个Erlang工程师,那一个Erlang工程师说,“之所以来,是听说你们要用Go”。索性将这个Erlang服务改写成Go了,图中的“弹幕/IM系统”就是第一个使用Go构建的花椒服务。
整体来说,早期的花椒业务以PHP为主。随着业务量级的扩张,肯定有一些单体服务需要进行升级和拆分。工作包括水平拆分(按照不同功能拆模块),垂直拆分(对服务分层,公用的提到底层提供服务),曾经考虑过,重构是否都统一使用Go进行改造呢?Go的引入和使用,在业务场景下有多大的收益呢?是一个架构上需要谨慎的问题。
事实上无论是JAVA还是PHP,都是有一套成熟的系统在运行的。除了语言本身的成熟,还包括周边配套系统的成熟,比如代码持续集成、发布、运维、管理、监控。完全用Golang来重构和替换,带来的收益是什么是需要仔细思考的。有人说是性能,性能我们稍后单独讨论。(和PHP相比,在绝大部分业务场景下收益未必明显)。如果只是用Golang把需要重构和拆分的业务重新写一次。系统中出现php服务和golang服务的混搭,造成服务和配套的异构,是否能够给长期的生产带来效益是需要考虑的。如果只是业务重构,基于业务语言本身来做,个人觉得是代价最小,更容易把注意力集中在解决问题本身,而非语言引入带来的一系列异构。
再来说大家关心的性能问题,很多同行认为使用Golang重构PHP项目,可以带来性能的提升。(google php转golang)。那我们来仔细分析下真实场景下的性能提升收益究竟会有多少呢?
我们用可量化的场景对比Golang和PHP开发特点。并且逐渐追加对比的外部条件,看最后在生产环境中,Golang业务的性能优势会有哪些。
首先最简单的条件,单存的一个裸框架的基准测试。golang本身就是webserver,nginx+php的请求生命周期在传输周转上消耗过多,可以说理想环境golang是有性能优势的。
我们把场景复杂化一些,高并发场景下,同时记录大量日志。这个时候Golang的解决方案的性能优势与nginx+php相比较,会有一定的下滑。当出现大量文件IO的情况下,存在系统调用引起的上下文切换,Linux环境下,文件IO并没有多路复用机制,对golang的runtime在处理文件io引起的应用层协程切换仍旧会拉低整体的吞吐能力。所以在高QPS吞吐,需要记录大量请求日志的前提下,两者性能会被进一步拉近。(当然应用层带缓冲异步刷盘的策略不在讨论之中,都以常见业务场景的优化程度来比较问题)
场景继续复杂化,在存在大量日志操作和对redis和mysql和外部接口的访问。这时候业务数据在内存中的处理消耗极其有限,真正耗费时间是各种各样的IO,大量网络IO的存在,产生各种各样的上下文切换,最终导致CPU很高,负载很高,压测数据在一定程度后趋近一个稳态。即在计算很小的情况下,并发量的大小消耗了cpu,最终导致负载上升。当IO的Round Trip时长在毫秒以上,其实真正决定单机效率的并不是语言本身的效率了。在这种场景下持续压测,golang和php的性能又有进一步的趋近。
再将其他一些条件加入,来考虑两个语言的对比问题。比如代码的发布和上线。PHP大部分情况下只需要推送代码文件即可。Golang的发布形式是二进制文件,需要你引入蓝绿发布或者平滑重启的问题,因此相比较PHP的优势在增加更多比较维度的时候,是值得商榷的。
如果比较PHP和Golang在业务场景的开发速度。个人觉得PHP可能更高。
关于Golang使用场景问题,我觉得回归Golang产生的本源,已经给出了建议。谷歌早期分享的文章非常好,就是为了解决他们内部大型系统的Build时间的问题,解决依赖管理问题,解决C++互相依赖和集成的问题,Golang的诞生是效率和性能的权衡,主要是解决大型系统开发层面的问题。
我了解过360集团业务层的情况,PHP转Golang热情是非常高涨。他们确实有刚需。PHP业务开发到一定程度,会考虑采用框架和扩展的方式来弥补和扩展使用场景。但这些手段并不够友好,希望有其他的工具能更好的解决问题,Golang的学习成本和易用性是一个非常好的补充。使用Golang的业务线越来越多了。我个人建议,如果从0到1贡献项目,直接用Golang写项目没有问题了。但如果是在一个运作良好的体系下,升级迭代,这个事情得想清楚,给公司带来多少收益。
根据上面我们讨论的问题,花椒会在业务中寻找有痛点的场景结合Golang的优势进行重构。比如图中的计数器服务,各个业务场景都有需求,除了缓存操作也需要落库,提供数据统计相关,包括异步的清理内存中的冷数据。以lib的形式提供功能安全性和可维护性都不是最优。因此我们把这个服务拆出来使用Golang进行重构。类似寻找这些痛点服务使用golang进行拆分和重构。重构的项目有一个明显特点就功能单一,迭代周期非常慢,基本不会随着业务需求变化,这样发挥Golang更多的好处,配备完善的单元和系统测试,可以有一个长期稳定的收益。
第二个场景我们找到各个业务线重复的通用服务,比如说各个业务线有自己的队列服务,每个队列服务有自己的一套后台,有自己一套配置管理系统、监控。这些可以统一起来,对所有业务提供一套基础服务。当然,所谓的技术中台并不是统一并搭建开源的服务,而是同时解决痛点,我们在改造和抽象过程中,倾向于提供抽象接口,屏蔽底层的具体细节,方便其他开源方案的引入和升级,减少对业务层的影响,这个后面在第二章节会详细讨论。
这样在项目的水平和垂直拆分过程中,产生了很多新的基础服务,如LBS服务,计数器服务,通用队列服务和分布式Cron服务等等,技术中台在“无意识”的重构中,产生雏形。
小结
综上所述,第一环节小结一下,差不多业务整体迁移Golang最好评估成本和效益,现有业务的微服务过程中寻找能利用Golang场景,coding乐趣不等于效率提升,问题建设和机能栈扩展不等于业务或公司利益的刚需。
另外整个微服务有限分层,底层基础迭代频次低,测试复用和覆盖高,业务减负,系统开发团队增负有限,开发后人员释放,整体人员成本降低。
No.2
花椒直播基于Go的中台服务化策略的探索
花椒直播基于Golang中台服务的探索
我第一部分大概解释了花椒早期一个迭代过程,已经有一个中台雏形在里面了。我觉得大厂中台概念有其复杂性,包括产品侧的中台,战略侧的中台,我列了大场中台vs创业中台我觉得大家听完了我的演讲会有自己的分析和结论了。
大厂中台标准化,增加系统复用,经验复用,减少人员成本,减少同步成本,像大厂既有事业群,还有事业部,还有项目部,整个结构是网状。如果他没有一个中台的概念解决通用能力和技术,从高层来看是有资源浪费的,从底层来看百花齐放会造成很多沟通和协作的鸿沟。
我理解创业中台首先是定制化、一定效率最优,不是搭各种各样的开源解决方案。最小成本使用行业新技术,并且具备对新技术的使用和推广能力。另外一部分核心点是以高效为原则,高度定制。后面这一章围绕高度定制来说。怎么做才能解决业务刚需,而不是直接搭建一套通用的服务。最终目标就是以最小的智力和人力投入,稳定业务和解决业务的瓶颈。
我们通过一些案例,来讲述花椒中台做了哪些工作。如前所述,花椒中台做了很多服务化的东西,尤其是高度定制的服务化需求。但服务化本身,也是依赖一些通用服务的,这些服务也是高度定制的。Gokeeper是花椒的微服务管理中心,其本身完成配置文件管理,服务的启动和关闭管理,服务监控和调优和服务发现4合一的功能。所有的SDK启动以后都是网络配置,直接连到gokeeper可以拿到所有信息,底层基于ETCD做服务发现和存储,这样无论做哪一套新的服务,标准化的接入这一套东西,不需要在业务团队里面部署其他东西。
这一部分我觉得是我们核心亮点,再说详细一点,我们配置文件为了方便避免重复编写数据的解析,组织格式仍旧是INI,但编写格式支持Golang的语法,如上图所示,并可以编译生成sdk需要的golang代码(和PB类似)。并指向使用sdk的工程目录,这样sdk就可以避免再写数据解析,直接使用生成的代码来更新远端的INI配置。
根据业务场景做高度定制通用产品
Gokeeper是最基础的服务,有个4合一的服务后,可以轻装上阵,给业务需求提供更多加速。比如说做总线的业务,业务的需求是除了总线基础功能,还有额外四个需求,否则替换的意义不大。第一是: 公司有流量大的明星过来,几十倍上百倍激增。怎么计算异步任务的扩容,需要总线系统能够给出实时的数据提供预估。二是除了任务的增删查改外,报错信息可以方便的进行可视化。三. 用运维监控要更方便支持一系列定制。第四是 PHP的方式异步消费,得重启才能加载新代码,能不能像发API接口一样,解决PHP消费进程的代码热更新。
这些额外的需求,就是我之前提到的,为业务提供的解决刚需的定制化能力。是创业公司的中台团队需要考虑的存在价值问题。
大概说一下总线的实现,工作模式有点像Apache Storm。首先接入方式对业务友好,接入方式是Redis协议,基本所有的语言的redis sdk都很完善。总线只需要实现redis协议的解析即可,底层可以使用redis也可以使用其他kv存储。同时业务消费的时候,为了解决动态重启的问题,我们让PHP仍旧以cgi的方式进行工作,通过CGI协议,将队列的数据输出给PHP。
针对JAVA和Golang的SDK和PHP不同。直接http调用或者grpc调用即可。整体来说,高度定制如下图所示。
btw实现一套CGI协议还是有很多乐趣在里面,如果大家有兴趣,可以关注我们后续的公众号,再看我们更多的内容。
中台服务最好与云服务亲和
对于总线来说,可视化和操作界面也根据业务需求做了高度定制,比如测试环境队列可以一键发布到线上。定制报警发送给配置修改队列任务的人员等待。
对于流量扩容需求,我们提供实时的消费统计数据,包括平均消费的延迟,最长消费延迟等,服务器负载等对应信息。可以通过后台直接预估出,流量扩张情况下的扩容服务器数量。
对于消费出错的数据,也有对应的出错信息提供给用户。可以通过后台直接查看队列的报错情况。不需要上服务器定位问题。
另一个特色功能是,可以直接在后台模拟数据的发送和消费,实时查看当前这条数据的消费结果,对于测试环境排查问题,非常刚需。
上图是存储监控,可以根据监控数据动态的扩容或者缩容服务。
第二个环节说另一个CASE项目。就是Cron项目,我们参考google的论文实现,可以实现分布式的Cron系统,进行定时任务的分配与管理,故障漂移,避免定时任务的管理失控和单点问题。
整个架构基于ETCD实现,虽然高度定制有一定开发成本,但整个开发周期可以限制在1个月以内。但服务的周期是永久的,可以对业务想法做出响应。
分布式Cron的状态监控如上图所示。所有的任务都有备用节点可供执行。双节点是最低要求。
另外提供出错的显示:
定制需求还包括,支持Cron的匹配值,对应的可视化的东西,互斥任务,任务编排,需要什么场景可以根据场景设计。
除了以上,我们的技术中台团队输出还包括其他一些服务。花椒的ESproxy,通过ESPproxy汇总实时数据,定期刷入ES,并且对查询的数据提供缓存,对ES访问提供流控。
IM系统也做了定制的改造,比如实时流量反馈系统,可以根据当前机房的出口带宽配置,决定对弹幕的限流策略,防止过载。控制的过程中,按照不同类型的消息进行配比,保障花椒的主要功能不受影响。
小结
总结一下这个环节,中台我们做了三个工作,根据业务场景做高度定制通用产品,集成开源产品,提供更简单统一的环境,支撑企业高效能建设。
二中台服务深入了解业务需求,成为连接各个业务抽象具体载体,迭代和完善中台服务的产品。
三中台服务最好与云服务亲和,方便利用云服务提供的SAAS和PASS进行部署与维护。
No.3
中台能力对业务的定制化支撑
中台业务支撑
除了服务化的支撑,中台团队还需要具备集中各端能力突击解决公司的疑难和瓶颈问题。对主要项目和业务提供直接支持。
第一个我们去年做直播答题,这个有很多人玩过,花椒也是7×24小时跟进这个热点,难点就是说针对百万用户直播,怎么保证一百万用户看到所有状态都是同步的。
常规做法有很多,比如各公司都有IM服务,保证消息不乱序,保证消息同步到达。但是IM有问题,如果有100万户要保证不乱序一般是要落地存储的,但是一百万用户瞬时落地是不现实的。常规做法就是PUSH消息,长连接在弱网下会产生一系列问题。不预先落地的推送,会导致跳状态,丢状态。我们会采取推拉结合的方式,还是用PUSH,80%、90%的用户在PUSH没有问题。但是还有用户有问题,可以通过另外一个服务可以补偿,每个消息有版本号,如果版本号持续递增不用补偿,如果不是递增就要补偿。这样保证数据正常。但拉取服务本身需要能支撑超大并发。推拉结合的协议上,要保障客户端尽量对服务端减少冲击。
整体来说,通过提供客户端完备的SDK和服务端推送和拉取同步服务配合,可以很好的解决这个场景出现的问题。
如上所示,拉取服务内部使用广播,所有数据在各个服务器采用全量缓存,并用LRU的方式进行淘汰。
最后一个环节也是花椒今年中台团队做一个工作,目前很多创业公司是需要不停的试错或或者创新。去年直播答题,今年K歌,语言公共房、面对面聊天。每次有新的热点,谁能以最快的速度完成产品迭代谁能占领市场。创业公司的中台团队需要快速构建产品的基础能力。今年花椒中台团队使用Golang构建了一套快速构建系统,可以将新产品的迭代速度提升2周。核心来说就是GRBC加阿里云的Kubernetes,加Flutter技术。快速构建新产品。
GRPC的作用比较简单,开源生态扩展丰富。PB对各个语言都比较友好,可以生成多端的数据接口SDK,同时使用PB插件,还能转成Swagger doc。节省了客户端和服务端的对接成本。
整体目标就是希望在一个云的平台上,把我们之前做的所有中台的服务使用DOKCER和K8S统一迅速构建起来。包括常规的通用能力,比如登录、注册、分享、充值、体现、弹幕聊天、直播点播、连麦、信息流,都能通过配置一键生成。
小结
总结最后一部分,创业公司利用灵活多变的优势,大厂不是具备这个形势,一旦决定一个技术方向,要转和调是内部做。但是我们可以用新技术为开发提供提速。相比较业务团队,创业公司的中台不只是服务提供方和技术支持方,也是创新和创新推广的引擎。
Q & A
提问:你好,我想请问一下你们对于业务有一些模型,比如说像支付中心那一块业务为什么没有用Go写?
周洋:用户中心这一部分其实我们新的是用Go写的,老的并没有完全用Go。对于支付中心,我们对接第三方SDK,我们还有海外版,所以使用PHP最简单了。