达达集团高可用系统架构进化之路

2021-03-12 16:44:29 浏览数 (1)

嘉宾 | 魏智博 整理 | 李忠良

在时间、资源等多重阻力之下,中小企业技术领导人,如何带领团队搭建最高性价比的系统架构?在刚过去的 QCon 全球软件开发大会上,达达集团首席架构师魏智博就此发表了主题分享。他从达达集团遇到的挑战困境入手,详述了达达集团是如何解决中间件宕机、内部流量以及监控问题。以下为其演讲整理。

达达是一个年轻的创业公司,在整个创业过程中,遇到了各种各样的问题,解决这些问题常常会受到人力资源以及时间的限制,如何带领团队搭建最高性价比的系统架构是我着重分享的内容。所以副标题是——小厂的经济适用性方案。

我们团队做方案,一直秉承一个原则——如何获取最高的性价比,所以我分享的各种方案也许不是最先进的,也许不是功能最完善的,但是一定是在当时环境下,性价比最高的。

今天的分享共有三部分。首先是 2017 年达达集团遇到的困难与挑战,其次是我们采取的解决方案,最后是达达集团未来的一些迭代以及优化。

困难与挑战

达达高峰时期接近日千万的单量级别,数据库大概在万级的 TPS ,大概有 150 个微服务,1000 多个虚拟机节点,业务跑在单云双机房上。

达达的技术栈非常简单,整个的服务治理直接把 consul 拿来,没有做任何的修改就直接使用;

公司的监控系统是基于一套 Log 的分析系统,我们将 Log 分析系统拿来,经过清洗之后,塞到 TSDB 或 ES 里;

技术栈是 Java 和 Python 共存的状态;

中间的 RPC 协议是最原生的 HTTP JSON 等等。

以上是公司背景,我们来看看都有哪些问题?

第一类是中间件宕机问题,这个问题最让我们苦恼。任何一个数据源宕机诸如数据库、Redis、消息队列等问题,至少需要 10 分钟才能恢复过来。当时确实束手无策,除此之外,冷备资源及其数据同步也浪费了大量的资源。

第二类是内部流量问题。首先是内部流量无法限流问题,由于某些 Bug 或参与策略,内部相对核心的服务直接被核心较低的服务杀死;

其次就是冷热分离问题,订单系统承载了离线以及在线的业务需求,运营人员一次提取几万个单子,对系统的损耗是非常大的,这些请求虽然没那么重要,但是却挤压了重要业务的性能。

除去内部流量以及冷热分离之外,压测问题也比较大,达达的流量高峰与电商几乎同步,每年的 618、双 11 大促是流量的最高峰,在这些大促之前需要进行很多的压测,但是由于没有合适的工具,我们只能提前购买一批大批机器,重新部署一个完全网络隔离的环境,把中间件、数据库以及所有的服务重新搭建一遍,非常耗时耗力,性价比非常低。

最后一部分是在线测试问题,达达的每个核心服务有一群人在修改,每个人修改出来的版本不同,但这些不同版本的服务无法同时测试。大家想要测试,要么排队,要么抢资源,测试的效率非常低。

第三类是监控系统的问题,当时的系统是基于 Log 的监控系统,出现问题之后很难找到根源节点,任何一个事故发生,所有的系统都在报警,等大家找到问题所在,十多分钟已经过去,大大地延误了我们的战机。其次是业务逻辑的 Bug 得依靠客服反馈,基于 log 的一套监控系统,是无法显示不报错 Bug 的,只能依靠客户反馈,这让我们的技术同学很难堪。

从发现问题入手

解决问题的前提是发现问题,如果问题都无法发现的话,解决问题就无从谈起,想要解决这些流量治理或高可用的问题,我们希望把监控系统重新开发一次。

我们整理当时监控的所有需求,将监控系统定义了三个层次,针对每个不同的层次开发了一套额外的监控系统。

第一套监控系统——集群级别的监控系统,这套监控系统可以看到集群内部运行的所有的微服务。这是一个鸟瞰图监控系统,而且当进入监控系统的任何一个圈里,你还可以看到整条调用链。

这套监控系统有两个先进之处,首先它可以让我们了解系统的健康状态。其次它解决了「发生了事故,却无法找到根源的问题」,这整套的可视化的调用链,可以很轻易地知道报错的节点。

第二套是时序业务和系统监控。基于 Log 的这套监控系统,其实存在很多问题。首先 Log 系统只能分析 Access Log 或 Air Log 之类的,所以 Log 收集上来的信息数据远远不够 。其次系统扩展性非常差,如果我们想要埋点,Log 系统是很难做到,所以我们把整套的 Log 系统推翻掉,重新开发了一遍。

整套架构比较简单直接,上图绿色的框里的内容都是经过二次开发的开源产品,实质上我们开发了一套 Metric 的收集系统,将 Metric 的收集系统当成一个 SDK 做到了业务系统里,这套收集系统可以收集几乎所有的技术指标,比如说 GVM 的信息、各种错误信息、异常的类型。其次达达开发了 Server,这个 Server 就是一个 Metric 收集器,达达每天大概有几十亿个 Metric 的点会汇报到这里,在 Server 上可以进行数据清理以及数据格式化之类的工作。

团队开发 Server 之后,将这些数据塞到 Kafka,放置到 InfluxDB 里,我们在 Influx 外围包了一层类似于 InfluxDB 集群——它可以做自动的数据路以及各种 HA 的事情。InfluxDB 的展示层,很多人都使用 Grafana, Grafana 可以对接多个数据源,我们对 Grafana 也做了一些二次开发,使其可以支持同比环比的数据展示等等,至此全新业务和系统监控完成了。

最后一套就是 APM,可能每个公司都有不同的选择方案,但是功能差不多,这里我不展开讲了。在这三层的监控系统处理之后,我们才有信心继续解决各种流量以及数据源的问题。

开发流量系统常常受到诸多限制。例如,涉及到业务的时候,整个工程的工期会因为各种原因被拖得很长,但是我们只有两位架构同学,他们需要在 1~2 个季度搞定整套系统。除此以外,达达内部是 Java 与 Python 一起在开发,所以流量系统也需要支持多元环境。这些限制意味着很多的事情无法进行。

比如业界比较通用的这种流量或者数据打标的方案无法进行,因为这套方案需要很多改造,对于我们业务同学不太可能接受。另外一些现成的开源方案,如阿里的 Double 可能也不能用了,因为它不支持 Python,所以后来决定自己进行,如果不能做流量和数据打标,我们就只能做节点打标,节点打标的好处是对业务来说完全透明的,架构和运维可能直接干掉,其他尽量保持不变。

说到节点打标,我们发现真正所有流量相关的需求无非两大类——隔离问题与路由问题。反观现有的一些原数据是不足以做出整套流量治理方案的,所以我们又重新引入了两套新的原数据。

一套原数据是链路,引入链路是为了做隔离。现在达达集团链路的隔离方案,一共三套环境,生产环境、测试回归环境和开发环境,这三套环境之间的网络是完全物理隔离的,但是每个环境之间通过链路来逻辑隔离。以压测为例,可以把线上的机器分割为两套不同的环境,一套是生产默认电路,另一套是专门为压测使用的压测电路。因为有了链路概念,所有工程都能拥有一套沙箱环境或者说沙箱链路,团队之间的测试和开发工作互相不打扰。通过引入了链路这个概念,解决了流量逻辑的问题。

另外我们还需要一个概念解决路由问题——服务分组。

上图是现实应用场景的示例图,服务 B 可以分组,一个分组专门为离线业务调用,慢一些也没有关系,其余的隔离分组为在线的核心服务 A 提供调用。

当引入两套原数据之后,我们就可以解决隔离和路由的问题,但是如何使用原数据?我们又在元数据的基础上开发了一套规则协议,这套规则协议很简单,用 JSON 就可以完成。具备所有必需的原数据以及对源数据操作的规则引擎之后,我们就剩下开发工作。

在发起调用以及发送请求之间,增加两步就可以做到隔离和路由。我们提供了一套 SDK,第一步获取隔离性,获取一些链路的信息,第二步根据 IP 接口等匹配路由,然后直接发起调用。另外因为实现过程非常简单,所以未来只要进行一些扩展,就可以处理各种流量治理方面的问题。

采用这整套系统的效果非常不错,两位同学从设计、开发、测试到最终上线,两个季度搞定。有了这套流量治理的方案之后,压测资源从 250 人日提升到 70 人日,提升了接近 4 倍。

针对之前令我们最难受的数据源节点宕机问题,我们非常希望有一套自动 Recover 的方案。

最早之前是非常简单的直联方式,数据库上 IP 地址都是从配置中心提取出来,如果一旦宕机发生,其实需要经历以下步骤才能把整个系统恢复过来。

首先需要把配置中心里的数据源 IP 地址修改为备用集群的 IP 地址,进行配置下发,最后所有受影响的应用服务都需要重启。因为整个过程中间需要有人工参与,所以 10 分钟已经是最短的恢复时间,但是这对业务来说是不可以被接受的,所以达达决定开发一套快速恢复的系统。

当我们在开发系统的时候,我们有很多的选择。最简单的方法是既然 MySQL 是单机的,我们能否使用一些自带 HA 功能的数据来替代 MySQL,典型的例子是 TiDB 以及 Redis;Redis 其实也一样,我们之前 Redis 只是用 Proxy 模式,但是我们能否用 Redis Cluster 来替代;最后是云厂商提供的一些高可用方案。

调研之后,这三项选择都有一些问题,首先我们业务对主从延迟、响应时间要求挺高,TiDB 的性能无法满足我们的业务需求。

其次 Redis Cluster 确实可以做,但是从 Redis Proxy 模式切换到 Redis Cluster 的模式,切换成本非常高,所以我们决定做,但是我们无法等待切换完成,第一是耗时太长,第二即使它做完,我们真正数据库的问题还是没有解决掉。

最后是云厂商高可用方案,这个问题就更多了,首先这些方案不支持定制,不够灵活。其次使用云厂商的方案,数据迁移的成本也是非常高。

这三条路都行不通,达达还是决定自己研究。我们希望有一套方案——它可以让任何的节点可以自行恢复,并且能覆盖的 MySQL 和 Redis 以及其他数据源的通用方案,当然还得具备数据源治理的功能。

数据源或者中间件宕机自动恢复,总体分解出来只有两点:第一,只需要在故障的时候能把它检测出来,监测出来之后触发一个切换。第二,只要应用服务能侦测到切换的 Event,它能重新建立连接就可以。

故障监测的过程,一个比较原生的想法是做一套探针,这套探针分布在集群里的各个地方,可以实时地去监测所有数据源的健康状况,但是如果使用探针系统,有很多的细节问题是需要考虑的。例如如何在保证探针监测准确性的同时,又能保证它的敏感性?

后来我们发现在流量治理时候使用的 Consul 系统,其实就是这样的一套探测系统。Consul 原生自带的高效协议是一个点对点的侦测协议,直接利用 Consul 将数据源当成无状态的服务纳入到 Consul 管理中就可以搞定。

后来我们又对 Consul 进行了一些二次开发。事实证明,这套方案是完全没有问题的,成本非常低,不需要自己写探针,不需要考虑细节的技术问题,仅仅使用 Consul 的原生能力就可以,但是我们做了一件很多人都不会做的事——把有状态的服务纳入到无状态的节点管理中。

当健康检查之后,第二步只需要兴起 Watcher 服务,这个 Watcher 服务只需要实时监听数据源的健康状态。

如果任何一个数据源的节点,Consel 判定它是不健康的状态,触发切换就可以搞定了。紧接着兴起一个 Watcher 服务监听数据源状态,在故障的时候进行切换,最后就是应用服务,让应用服务能感知到切换就可以了。

综上所述,我们开发了一套架在连接层的数据源中间件,它是不接触 GDBC 或者 Redis 协议的,所以这套数据中间件的接入不需要业务改动任何已有的东西,业务端只需要从 Consel 里面监听依赖的数据源节点的信息,如果信息一旦发生变化,业务端只需要关闭之前的连接,重新建立一套连接就可以。到此为止,高可用或者说数据源的故障自动恢复方案已经完成。这套方案的效果非常好——在 30 秒到一分钟之内,任何的节点宕机都能自动地切换过来。

未来的迭代优化

2017 年的大部分各种问题都已经被解决,但是还有些问题没有解决。例如达达仍然需要准备很多的冷备资源,中间的数据同步管道,成本上可能也不是最优的。

我们想尝试使用一些自带 HA 功能的系统来替代现有的系统,其实 MySQL 官网也提供了 MGR 的解决方案,但是因为中间切换时间成本的问题,这套方案还在调研以及运行过程中,但是如果这套方案其实就跟 Redis Cluster 一样,它自己可以管理集群,对我们来说,这将会节省掉近 50% 的机器。

其次是消息队列,目前内部消息队列是两套系统,在线服务运行在 RabbitMQ,离线的 Log 之类的大数据服务运行在 Kafka 上,这两套消息队列系统并存的时候,管理成本非常高,并且 RabbitMQ 本身会存在瓶颈,比如它有一个比较明显的性能上限,以及它自带的 HA 对机器资源的占用非常高。

我们想要把两套消息中间件合二为一,完全统一到 Pulsar 里面,Pulsar 可以既支持这种在线的队列消息,又能支持这种 pop sum 模式,而且本身不管从性能上以及运维成本上都是非常有意思的。

0 人点赞