2016 年 12 月 17 日,又拍云 Open Talk 有米科技专场在广州举办。此次 Open Talk 邀请了有米科技(834156.SZ)技术团队的4位大咖探讨技术领导力、大数据和用户画像、在线业务扩容的技术选型等话题。以下是有米科技研发经理黄俊炜分享的《基于云计算——在线业务快速扩容的技术选择》。
随着云服务的逐渐普及,快速扩容的实践也逐渐进入到各个企业的生产环境中,快速扩容令系统有更高的可用性,帮助我们节省一定的服务器成本。我以有米科技“用户标签服务”为例子,分享一下我们在线业务快速扩容的经验。
本次分享分4个部分:
- 背景介绍
- 方案分享
- 技术实践
- 经验心得
用户标签服务的背景介绍
用户标签服务简称DMP Fetcher,主要服务对象是DSP广告、SDK广告、数据分析。DSP广告有一个特点是响应的时间比较敏感,有米科技的用户标签服务尽量做到低延迟,还有数据分析的脚本,它的量非常大,脚本启动时间和结束时间对服务提供商是不可知的。
有米科技整个服务是通过Golang开发的,当用户需要查询用户标签的时候,跟我们的后端交互是一个协议,当我们插入了负载均衡的请求逻辑,DMP就会查询相对应的数据库。整个系统中扩展策略是水平扩展,是查询指读类型的服务,但是我们开辟了进程类缓存,比方说数据库里有人更新了一个用户的标签,从A到B,用户查出数据是B,由于一个缓存的存在,可能对客户端来说就是A,对于他们来说这种A到B的数据落后不一致是可以容忍的,有米科技在业务发展的初期凭借数量比较少的几台服务器,就能做到比较高的单机性能。
后来有米科技的业务发生了飞速的发展,业务也变成了忙时流量和闲时流量。上图是有米科技的用户标签一天的请求变化趋势图,可以看到中间是闲时流量,两边是忙时流量,忙时流量是闲时的2到4倍,有一些峰值是闲时的6倍以上。面对这种场景,需要做一些节省成本的需求,我们在一些拓展过程中发现了一些问题:
- 服务搭设在Fetcher上,对于负载均衡器这一层来说,它的单机负载会比较高,网络会比较繁忙,在实际应用当中,8个虚拟CPU只能支撑6万CPU的查询,这样是远远不够的,所以我们在负载均衡器上做了很多水平扩展的动作。
- 扩展下面的节点,会意味着中间需要修改配置,但是因为负载均衡器通常是非常繁忙的,在忙时可能需要8、9分钟,对我们来说非常难以接受。
- 节点在变化,静态的文件里有一个正在动态变化的节点信息,带来了非常大的运维成本。
- 网络相关的问题,因为单机的网络性能是相对有限的,而整个系统当中的一个网络链路比较长,网络传输的消耗比较大,有一定的优化的空间。
快速扩容方案
在有米科技的改进方案当中,一个核心的点是在系统中加入服务发现元素,在内网的机器集群里面搭设了Zookeeper集群,请求分发策略,废弃负载均衡器转发请求逻辑。直接由客户端请求可以缩短网络链路,一定程度减少整个系统中网络传输带来的消耗,管理通知台可以管理请求分发的策略,还可以在管理控制台上定制扩容和缩容的条件。
我们在方案中加入了自己提供的Lib,迁入了我们的Lib之后,就会在Zookeeper集群上有一个节点。如果迁入了Lib可以不断地订阅这个集群上的数据变更,能获知到这些消息。
最后一个改变,是运维工具Ansible,确保每一次操作都操作到比较完整的列表,能避免刚才提到的初期要在一个Ansible的文件里动态变化的节点信息。
技术实践
首先分为三个模块,如果想获取用户标签,可以向连接池里请求一个正确的连接,连接到了Connection Pool会请求分发的策略,返回一个正确的连接给主逻辑,主逻辑可以拿连接跟DMP Feither中,数据传送使用Protobuf的训练方法,这两个可以做到交互协议,减少我们网络的消耗。
在主逻辑加一个有限次数的失败重试,如果主逻辑超时或者报错,就会在Connection Pool下一个连接节点,这个一定是有限次数的。
启动一个独立去监听的数据变更,比如说在Zookeeper集群上有一个请求的策略改变了,这个策略就会推送到订阅,维护连接池,发现DMP Fetcher上当了会都拿出来。如果一个新的DMP Fetcher启动了,会创建一个连接,把这个连接抄到那边,就会修改连接池上面请求分发的一个数据结构,当那个结构做完了更新连接池的操作之后,会把它的一些操作结果的成功与否以及失败的具体原因上到监控系统里面。
DMP的实践
首先是初始化一些协议框架,启动的时候在Zookeeper上面建立一个临时节点,注册一个临时节点之后,还会在Zookeeper集群登记访问方式,比如说在外面通过什么端口、IP能访问到它,除了访问方式之后。
有米科技还在Zookeeper集群上登记了处理能力,有时候在做程序的时候会开N个work,最大可能只能开10个work,把这10个work的指标上到整个的集群上面,退出的时候也会把这个临时节点删掉,最终做到这个临时节点的生命周期跟Zookeeper是一致的。Zookeeper集群把一些状态的数据上报到我们自建的监控上面,在初始化之后会启动,整个内网的集群就会通过Zookeeper集群做出相应的动作。
Manager的控制台
Manager控制台可以掌控一些计算资源,还能管理一些Zookeeper的请求分发策略。
我们扩容的过程:首先在监控系统里读取到的监控指标,比如平均响应时间,当前活跃的work数,就可以在Zookeeper集群里读取到最大的处理能力,也就是最大的work数。结合当前的work数和最大的work数就可以知道当前的情况下是否需要扩容或者是否需要缩容,假如需要扩容了,就调动增加计算节点的数量,计算出一个正确的请求分发的策略,保存在Zookeeper集群上面。
刚才提到的节点的变化和请求分发的策略,都会有Zookeeper集群一个个推到客户端中,并把维护的结果上报到监控系统当中,就可以得知整个过程是否是成功了,假如有失败的,会做出一些策略去补救。
经验心得
首先需要提一下,有米科技在整个系统中用的比较多的是一致性算法,它有两个特点对我们非常有用:
首先是单调性:通过这个一致性的算法之后,被分配到某一个固定的节点上;能否充分利用进程内缓存。- 其次是平衡性:当节点发生增减的时候,在这个环里面受影响的数据是相对有限的;对于快速扩容的情景来说,计算节点是动态变化的,扩展之后有部分的节点的缓存一定是失效的,一致性的宣发可以减少进程缓存失效的影响。
在Array开一个内存,就意味着消耗会非常大,变得非常的慢 ,针对这个对大Fetcher进行了分片的操作,开辟了容量较小的内存缓存,当key过来的时候,首先针对key进行一次Hash,分到某一个小的上面,这样做的效果发现Hash的时候,另外对于缓存的改善原来是进程缓存的时候,Hash分片映射map的数据结构,但是这样的消耗非常大,它虽然变了所有的key,但是不需要辨认指针对应的数据,大大减少了Cache的消耗。除了进程的缓存,也加入了一层公共缓存,主要是在快速扩容的情景下,不可避免的涉及到缓存失效的情况,当进程内的缓存失效之后,那个请求就会到我们的公共缓存,大大减少了数据库串的概率。
在扩容方面,我们建议做到“早扩容”:
确保在系统负荷到达处理能力极限前扩容,比如系统CPU使用率占80%,我们就认为到了一个上限,具体的实践是当系统CPU持续了一分钟到达了75%以上,就果断对系统进行扩容,确保系统的处理能力时进行进行扩容,能有效避免雪崩,同时也能为服务器、Fatcher启动预留时间,降低启动失败的损失。
第二个建议是做到“迟缩容”:
比如说CPU使用率占到20%、30%的时候,我们觉得CPU使用率比较低,会选择缩容,但不是马上缩容,可能整个系统在十分钟或半个小时内都在30%或20%的情况下,再选择做缩容的动作,这样能避免错误判断短暂波谷而过快缩容,同时也能防止缩减过多的计算能力,然后下一个瞬间就往上扩容了,这种对于系统内部过于频繁的改变,能增加系统的稳定性。
第三个建议,对整个系统有一个定期的Benchmark,这样了解到系统的处理能力的变化。两种情况下,我们的扩容策略是不一样的,这对于后续的工作会非常的有好处。
在启动完管理控制台之后,我建议进行一些Health Check,可以对它进行检查,确保查询速度是否在预值里面,同时还可以对服务器进行适当的优化。
下一个是Shutdown操作的改进,针对服务器以及Fetcher实例的特点,定制Graceful Shutdown脚本 。
每个人每个工作的实践可能不一样,有米科技会有自己程序上的特征、特色。比如对于Fetcher来说,可能会放开Zookeeper的链接,Fetcher才真正地缩放。大家可以根据一些历史的扩缩记录,购买适当的预留实例优化;购买预留实例的时候,可以根据服务器变化画出一个,购买相对数量,这对于成本控制会比较友好。
对于Zookeeper集群,内网DNS配置TXT记录,标记Zookeeper集群的地址。可以适当的降低Ticktime,让Client能尽快获知Fetcher实例异常,这样能更快地发现异常,对整个系统发现异常来说是非常好的。
关于 Open Talk
又拍云Open Talk是又拍云在2014年启动的开放式主题分享沙龙,每月一期。
Open Talk秉承了又拍云帮助企业提升发展速度的初衷,组织资深、一线的工程师,用全干货分享的形态,为互联网从业人员呈现先进的IT技术、理念、解决方案,帮助与会者不断提升自身的专业技能,推动企业更快发展。