1.导读
高德启动Go业务建设曾经有段时间了,次要蕴含Go利用落地,Go中间件建设,云原生三个局部。通过继续的发力,在这些方面获得了不错的停顿。高德Go业务落地过程是如何实现的,遇到过哪些问题,如何解决?本文将为大家介绍相干教训,心愿对感兴趣的同学有所帮忙。
2. 高德为什么要落地Go利用
当初高德内支流的语言还是Java,Java利用最多,机器数非常惊人。而且高德整体业务也在疾速向前奔跑,成本增加的速度十分快。在缩小机器负载方面,Go语言在语言级别对Java语言有相当劣势。缩小机器老本是咱们落地Go利用的第一个思考因素。
其次,Go语言近几年发展势头迅猛,不论是阿里团体外部,还是在高德外部,对应用Go语言的呼声越来愈高。落地Go利用能够很好的验证Go中间件的稳定性。当然咱们能够通过混沌工程等伎俩去验证,但通过生产环境考验才最具备说服力。验证积淀Go语言中间件稳定性是咱们落地Go利用的第二个思考因素。
最初,Go语言作为云原生根底框架应用较多的语言,提前落地Go利用,对后续落地云原生能够缩小不少阻力。高德目前落地的Serverless/Faas规模相当大。落地Go利用的第三个思考因素是为后续云原生落地铺路。
3. 大流量场景Go利用落地
3.1 渲染网关介绍
本文所述中提到的高德渲染网关,是咱们落地的Go利用中业务流量、革新难度、危险,收益均处前列的利用。渲染网关在接入层,占高德总流量的一半,重要性可想而知。
接下来简要介绍下渲染网关承接的业务,不便大家有一些更平面的意识。
渲染网关承接高德手机App、车机、开放平台等起源所有的图面渲染。大家在应用高德时,看到的建筑物、地形图、名称、路线、地铁站、公交站、红绿灯等等所有图面,都是由渲染引擎通过渲染网关透出到端。上面放几张图,不便大家有一些更理性的意识。
下面图一为行前,图二为行中,图三为打车页面,图四为景区手绘图。渲染网关波及业务泛滥,以上仅为举例,其余业务就不在这里贴图了。
3.2 重构难点
做过重构我的项目的同学置信都深有体会,重构我的项目中最大难点有二,一是要保障业务正确性,二是要保障服务稳定性。
对于保障业务正确性,一般来说,重构的服务大多数为老服务,老服务面临的最大问题是历史逻辑简单,人员更迭,文档缺失,这些因素都是重构过程中的“拦路虎”。
渲染网关重构同样如此,它波及高德手机端、车机端、开放平台、打车等各个业务线,所有的历史版本,再加上上述因素,所以保障业务正确性是一件十分艰难的工作。
对于保障服务稳定性,做过网关的同学应该都晓得,网关自身的属性就决定了它并不会有频繁的业务迭代,稳定性是网关的第一诉求。咱们要保障,无论外部环境/依赖是否失常,网关始终能放弃高可用。因为Go版本中间件不足在大流量场景的充沛验证,这一难点须要认真评测,用适合的办法和伎俩,尽可能的在仿真环境里验证各种边界状况,从而保障在生产环境不出问题。
3.3 技术计划
在重构高德渲染网关时,咱们整体技术计划分三大步走:
3.3.1 线上流量比照
如何验证新服务的业务正确性呢?咱们采纳了线上流量比照的形式。
咱们后期做了大量调研,心愿找到一个满足(近)实时,二进制级比照的工具,但惋惜并没有找到一个满足要求的工具。因为渲染业务的非凡属性,渲染网关绝大多数接口返回的是二进制矢量数据,所以现实的工具不仅要能反对惯例数据比照,也要能反对二进制级比照。
二进制级比照的另一个益处是,能够排除字符集差别,不同语言库函数差别。更能保障比照的准确性。有些同学可能会想到打日志,而后离线读取比拟的形式来做比照,这种形式有很多弊病。
首先,流量无奈重放至指定机器。其次,这种应用形式个别为固定语料,语料残缺度不够,不能齐全模仿线上环境。此外,打日志比照带来的字符集和语言库函数差别,会对比拟准确性有较大影响,特地是对于特殊字符(当7层协定为二进制协定时更加显著)。没有现成的称手工具,怎么办?"逢山开路,遇水搭桥"。
咱们自主研发了一款(近)实时流量比照工具,它保障了此次重构的业务正确性,并且还能服务于高德其余业务的重构。其技术细节对TCP/IP波及较多,十分有意思,感兴趣的同学能够间接跳至《流量比照工具(ln)技术细节》一节。
3.3.2 仿真环境压测
做服务的同学置信都深有体会,想让服务保障做到5个9的可用性并不是一件容易的事。实在生产环境中可能会呈现各种状况,咱们要想方法验证各种边界状况下服务的稳定性,能力保障服务高可用。对于重构实现的新服务,更须要一个仿真环境,进行各种状况验证。
构建仿真环境,咱们须要放弃机器基线、内部依赖、内部流量均统一(比方从线上引流)。仿真环境不仅要提供失常态环境的能力,更要能提供异样态环境的能力。
异样态包含断网,网络丢包等等。有句话说的好:20%的代码实现性能,80%的代码来解决各种异常情况。咱们在实践中构建异样态的次要伎俩为混沌工程,通过混沌工程模仿下至操作系统级的异样(如断网,丢包等),上至应用层的异样(如消息中间件积压,JVM办法前后Hook模仿业务异样等等)。
在仿真环境里,同时进行长时间极限压测,语料从线上导流,压测在失常态,异样态均进行,察看服务在一段较长时间内的体现,从而得出服务的稳定性,可用性论断。
观测指标包含根底指标,例如CPU、磁盘利用率、内存利用率、连接数,以及业务指标,例如业务接口成功率、胜利量、总量、TP99。通过这种形式,基本上齐全笼罩了可能呈现各种状况,充分保证了服务稳定性和高可用。
3.3.3 平滑灰度切流
前边讲了如何保障业务正确性和服务稳定性。接下来说说如何保障平滑灰度切流。牢牢恪守阿里公布三准则是平滑灰度切流的“法宝”:可灰度,可监控,可回滚。
在具体实际中,咱们依照如下步骤灰度切流:
a. 原Java集群不动,新申请一套Go集群。批改路由规定,局部白名单用户应用Go集群服务。
b. 一一接口批改路由规定至Go集群,缓缓灰度,期间亲密察看机器姿势,业务日志,监控指标。如有异样一键切回至Java集群。
c. 接口全量切至Go集群后,Java集群/Go集群同时共存一段时间。
d. 逐步下掉Java集群机器。
3.4 次要收益
第一个重要收益:降本提效。高德渲染网关由Java换成Go语言之后,机器数缩小近一半。用原来一半的资源实现了雷同的工作,大大降低了老本,进步了资源利用率,更好反对了业务倒退,大大降低了业务流量快速增长带来的接入层机器增长速度。
第二个重要的收益是:验证了高德与团体单干共建的Go版本中间件的稳定性,肯定水平上欠缺凋敝了团体Go生态。在大流量场景考验过后,高德与团体单干共建的Go版本中间件稳定性失去了相当充沛的验证。
第三个重要的收益是:为网关云原生化铺路。网关Go化只是第一步,Go是云原生基础设施实现应用较多的语言,第一步抹平语言差别,对于网关后续云原生化,好处多多,可升高革新危险和老本。
当然,高德渲染网关重构过程中还有许多十分有用的工具积淀。可为后续业务重构提供关键性保障,比方自研的流量比照工具ln。
4. 技术干货
4.1 流量比照工具(ln)技术细节
先提一个问题,做一款(近)实时流量比照工具须要实现哪些性能?没错,就是流量复制,流量解析,流量重放,流量比对。其实不止这些,在实践中更多是一个流量回归闭环,如下图:
4.1.1 流量复制
为了反对所有的7层协定,流量获取必须从3层或4层开始。有同学会立马想到tcpdump。没错,就是tcpdump。tcpdump出的文件就是实实在在的流量。复制流量这一步曾经有着落了,至于实时,能够两到三个过程错开工夫,时间段首尾相互重叠即可实现实时。
另外,设计此工具的另一个考量点是,对线上机器不能有太重的负载,防止对线上机器产生稳定性影响。此种流量复制形式十分轻量,对线上机器减少的负载十分小,能够忽略不计。
4.1.2 流量上传&流量拉取
流量上传和流量拉取均应用外部文件服务。
4.1.3 流量比照
流量比照为了保障比照的严谨性,排除可能的字符集烦扰/不同库函数实现烦扰,咱们原生反对了二进制流比照。
4.1.4 问题流量本地重放Debug
回归流量时,可能会发现局部流量比对不统一,这时咱们心愿只重放特定流量到指定机器,以便于Debug或其余操作,ln原生反对了此性能。
4.1.5 流量解析
流量解析十分有意思,这种单纯的高兴来自于对网络协议的"把玩"。
理论做法就是如何解析tcpdump文件,拿到tcp payload,还原出http申请。
这里有两个关键点,一是咱们如何从tcpdump文件中拿到tcp payload,二是咱们如何把四层的tcp payload重新聚合成七层的http申请。
4.1.5.1 tcpdump文件格式
先说如何从tcpdump文件拿到tcp payload,如果能晓得tcpdump文件的格局,不就能够晓得tcp payload在哪个地位,长度如何了么?这一趴咱们就来看看tcpdump文件格式。
先看tcpdump文件总览
文件头的格局和长度都是固定的,如下:
咱们能够在读取tcpdump文件后,往后挪动23字节,而后开始解决每个数据包。每个数据包的格局如下:
咱们解决每个数据包,将前边的包头,数据链路头,ip层头,tcp协定头顺次跳过,最终偏移到tcp payload第一个字节地位。其中的更多实现细节(不同层的头字段值的判断,不同长度的判断,大小端的判断,申请数据包与响应数据包如何对应等等)在此不再开展。这里只介绍大体思路,感兴趣的同学能够深挖网络协议。
4.1.5.2 tcp payload还原http申请
这一部分介绍如何将tcp payload还原成http申请(此处http指http1.0/1.1,不含http2),ln工具中的残缺实现是由tcp payload还原出申请及对应的响应,此处为了便于了解,仅解说如何解析http申请。解析出http申请实际上已能够从新别离申请新老服务,比照响应二进制流。
一条tcp连贯,多个payload发送(这里仅做示意,判断丢包重发等诸多状况属于代码细节,在此不再开展)。可能多个payload对应一个http申请;也可能一个payload的前一部分对应一个http申请,后一部分对应另一个http申请。咱们要做的就是把多个payload造成的字节流读入,按http帧的格局,聚合http申请即可。另外,http2的申请不能按这种形式聚合。
4.2 一些go语言最佳实际
4.2.1 sync.pool 实际
因为Go语言和Java语言的内存管理机制不雷同,在内存的申请,开释开销也有差异。
对于Go语言来说,sync.pool是复用内存的一把利器。sync.pool长处有许多,比方缩小内存的申请,缩小了零碎调用,缩小了gc的压力。但事物都有两面性,sync.pool同样如此,咱们在应用sync.pool的时候须要留神,寄存在sync.pool里的对象会在不告诉的状况下被回收掉,所以相似数据库连贯等资源不适宜应用sync.pool。
总之,sync.pool能够复用内存,缩小机器负载,非常适合长期对象。
4.2.2 Golang Byte
Go语言Byte类型为无符号,Java语言Byte类型为有符号,在Java服务迁徙Go服务过程中,Java代码中Byte类型正、负、零的比拟要留神。
4.2.3 Golang字节切片与字符串高效转换
字节切片转字符串
func Bytes2String(b []byte) string { return *(*string)(unsafe.Pointer(&b)) }
字符串转字节切片
func String2Bytes(s string) []byte { x := (*[2]uintptr)(unsafe.Pointer(&s)) h := [3]uintptr{x[0], x[1], x[1]} return *(*[]byte)(unsafe.Pointer(&h)) }
应用此种形式转换,性能很高。起因在于底层无新的内存申请与拷贝。然而不论是字节切片转字符串,还是字符串转字节切片,字节切片中的值更改都会影响字符串的值,使用者要依据业务逻辑判断是否承受,要更准确的把控生命周期。
4.2.4 Golang库函数重写
对于网关来说,耗CPU比拟多的一部分是Hash函数/编解码函数/加解密函数/序列化反序列化函数等。在实践中咱们重写了相干的库函数,在CPU负载上做了大量优化。
想要升高CPU负载,咱们得先晓得CPU是如何工作的,能力晓得如何写代码会更好的升高CPU负载。这里会介绍粗略的CPU工作原理。
放张CPU 流水线工作步骤图
- 指令读取(instruction fetch,IF)
- 指令解码(instruction decode,ID)
- 执行(execute,EXE)
- 内存拜访(memory access,MEM)
- 寄存器回写(register write-back,WB)
次要优化MEM步骤,利用CPU缓存尽可能减少MEM步骤所占时钟周期,从而升高CPU负载。
相似NUMA架构,affinity等升高CPU负载的形式也是同样的思维,尽可能减少Load数据所需的时钟周期。
对于优化Golang库函数来说,能够晋升的点有两个:优化算法自身;优化CPU缓存亲和度。
咱们专一于第二种,拿base64编解码函数举例,传入的Byte切片与返回Byte切片,底层并非为同一数组,同一内存。这两头就波及两块能够额定耗费CPU时钟周期的点,一是内存的申请与开释,二是两块内存别离拜访带来的CPU缓存争用问题(与伪共享不齐全一样)。
如果咱们复用传入的内存呢?即边解码边覆写同一块内存。美好的事件产生了,上边所说的问题不存在了。用更少的时钟周期实现了一样的工作。须要留神的是,因为函数的输出和输入应用同一块内存,对程序开发者来说有更高的编码要求,即对数据在程序中流转的生命周期有更精准的把控力,代码要打磨的很粗疏。
5.将来瞻望
网关的下一步是云原生化,采纳Service Mesh形式实现。这能够解决目前中心化网关的弊病,去中心化能够晋升接入层稳定性,缩小爆炸半径,加强隔离能力,实现更精密粒度的管控。
其次,升高机器老本,依照目前外部压测及业界已有的实际压测论断,Mesh化后老本会进一步缩小,思考到现有RPC框架自身的耗费,老本会进一步缩减。且数据面代理也在一直优化中,后续性能体现会更优异,额定两跳对机器的负载将进一步降落。
再有,网络层能力集大大加强。网关Mesh化,能够带动上游业务Mesh化,最初在整个网络层做一个能力超集。
现有的Service Mesh框架提供的能力能够概括为Connect,Secure,Control,Observe四大局部,其能力是现有网关能力的超集,能够做到之前做不到的事件,最显著的是Observe能力带来的益处,可大大增强全链路服务可观测性,这于对后续发展服务稳定性,全链路故障疾速定位等工作有极大帮忙。
以上要做的事件任重而道远,另外咱们在会做更多云原生的试点和落地,技术同学都分明,从技术选型到技术原型,再到理论业务落地,两头有很长的路要走。但路选对了,就不怕远。
诚招同路人
笔者所在团队求贤若渴,盼有激情的技术小伙伴一起做些乏味的事,各技术栈均可,有志愿的小伙伴请纵情砸简历到邮箱gdtech@alibaba-inc.com,邮件主题为:姓名-技术方向-来自高德技术。
Happy Hacking!