OPPO 虽然是一家手机公司,但是其互联网业务的规模非常庞大,月活用户数超过 3 亿,既有内容业务、分发业务,也有商业广告、金融等业务。整体上,用户的使用非常高频,来自客户端的请求和数据量非常庞大,并且这几年一直在持续高速发展。
随着业务复杂度和并发量迅速增加,OPPO 面临的难题是如何保证互联网业务的高可用?多活架构如何落地,如何根据业务需求持续演进?针对复杂的系统,如何提供可靠的监控方案?......带着这些疑问,InfoQ 记者采访了 OPPO 互联网服务系统后端框架团队负责人罗工。
据悉,他于 2015 年加入 OPPO,有十余年的研发经历,并在高可用架构、PaaS 平台和基础框架研发等方面有丰富的实践经验。罗工负责 OPPO 后端框架体系建设,包括 API 网关、微服务框架、服务网格等,且经历了 OPPO 从千万级用户到亿级用户的增长历程。
据罗工介绍,OPPO 业务多活的三个核心诉求是成本、扩展、容灾。
具体说来,成本是指业务总体技术运营成本,包括基础设施的资源成本、研发成本,还包括业务中断的成本、品牌和口碑成本;
扩展是因为业务规模过大,一个服务需要调用数百个三方实例、一个数据库被数百个实例连接、一个服务需要连接几十个数据库,架构非常复杂。因此,这就需要对用户进行分片,缩小业务规模,自然演进到单元化多活的架构;
容灾,一方面是极端情况下对用户数据可靠性保障的需求,另一方面,因业务过于复杂、处理的链路很长,总会出现一些意外情况,频率还挺高,而问题定位到恢复的时间超过公司 RTO 的要求。机房内部共享运营商线路、DNS、SNAT 防火墙、负载均衡、K8s 集群、注册中心、监控等资源,而机房之间是相对隔离的环境,同时出问题的概率大幅降低。在业务出现无法自动恢复的故障时,先切换机房恢复业务,然后再从容定位问题根因。
OPPO 互联网业务有两大特点:一是业务种类多,没有主线;二是非常高频,请求量大。
先说第一大特点:业务种类多,没有主线,很难制定统一的用户单元划分规则。对平台服务的提供方来说,比如活动、评论、账号、积分、内容中台等,所有机房都需要读写,都需全量的数据。因此,要设计一个 N 主全量数据的架构难度非常大。
以业务中台-评论系统为例,多活架构如下:
1.评论以独立 SDK、独立域名提供服务。避免在业务部署的多个机房的内部调用,否则,评论服务就需要每个机房提供读写服务;
2.记录日志、流水,比如点赞记录、取消点赞记录,避免修改、计数操作;
3.最终一致。通过订阅的方式更新元数据、缓存,避免双写造成的数据不一致,同时在数据错误时更容易修复;
4.按用户单元进行调度,用户只会访问其中一个机房,感知不到两个机房独立更新的元数据的差异。
通过以上措施,把平台业务和产品业务分开,避免机房内部调用。
第二大特点是非常高频,请求量大。罗工表示,“一方面,我们提供的服务本身就是用户使用频率较高的,另一方面,还有一些常驻服务,比如天气、推送通道、软件自更新、手机云服务等。一般的开源技术方案无法支撑集群的规模,比如 1000 个实例的 Redis 集群,并且运行的成本过高,自愈能力和可观测性不足等,因此需要自研或深度的掌控。这对技术上的挑战很大。”
以 Redis 为例,如上图所示,后端搭建多个 Redis Group,Proxy 实现 Redis 3.0 Cluster 协议,将一部分 slot 流量分发给 Redis Group,这样 Redis Group 的规模就能得到控制。
基于 RocksDB 研发日志存储系统,对数据进行压缩,性能和成本大大优于 ElasticSearch Lucene 架构。
据罗工介绍,多活架构分为接入层、服务层、数据层。
接入层包括 HttpDNS、域名调度系统、单元划分服务、四层负载均衡、SSL 卸载、API 网关。其中,四层负载均衡基于 DPDK 开发,功能比较简单。SSL 卸载采用标准的 Nginx,扩展了一些 Prometheus 监控指标。API 网关基于 Netty 网络框架开发,NIO 部分使用了 Netty Native transports,采用微内核+全异步的模型,相比 Zuul/Spring Cloud Gateway 方案有较大的性能优势。
服务层本身是无状态的,扩展了服务路由、同机房优先、机房级熔断等功能,根据单元号/请求染色标记筛选服务提供者实例。
数据层分为同城多活方案、异地多活方案。同城多活即集群跨 AZ 高可用,是一个 CP 的方案。它对于开发者是无感知的,一旦出现故障就会自动切换自愈。罗工透露,这里的切换过程没有采用域名方案,而是修改 SDK 服务发现,以及 Anycast 跨机房路由变更。
异地多活是一个 AP 方案,双向同步,追求最终一致性。
据悉,OPPO 互联网业务种类很多,目前在大量使用同城多活、异地多活的方案。
实际上,OPPO 互联网业务是在近几年快速发展起来的,因此一开始就有同城多活、异地多活,并行发展。
早期,同城多活、异地多活缺乏统一方法论,比如按用户单元进行调度(减少用户感知的不一致、数据同步冲突)、主线多活、保障多数用户、最终一致、数据分类(应用不同的 CAP 模型)、平台业务 SDK 化,结果业务做到了多活,但满足不了 RTO 要求,异常情况不能为大多数用户提供服务,以及上层业务多活,而依赖平台服务需要跨机房调用。
后期,业务多活不只是关注数据层的同步方案,更关注方法论、设计原则的推广,梳理上层业务与平台业务的关系、业务主线、数据分类应用不同的 CAP 模型,以及建设统一的多活决策与观测平台,整个过程更智能化、减少误判、过滤抖动。
最近几年,OPPO 业务多活的研发重点在于开发人员无感,降低业务多活接入成本。
在整个多活业务架构设计中,最大的难题就是没有主线,很难统一单元划分的规则,平台型服务需要在每个机房提供本机房访问、提供全量数据,没有办法应用单元化的思路去解决。
这里,罗工谈到了三种解决思路。
第一种解决思路:提供 2+2 核心机房本地读写能力,南方选择 2 个机房做同城多活,北方选择 2 个机房做同城多活,南北之间双向同步。数据结构设计上,避免使用计数器,避免使用 update,用日志、流水、南北分别订阅 MySQL 重建 ES/Cache/计数器、库存南北分别扣减等方式代替,达成数据的最终一致。2+2 之外的机房调用同城的服务。
第二种解决思路:提供本机房的读能力,而写集中到中心机房。
第三种解决思路:上层业务对平台服务的依赖,一定要做好降级,有应急手段,比如账号登录验证,业务可以自己适当的缓存,也可以在 Token 设计时就具备一些不依赖数据层的降级验证能力。
数据同步方面。如果 ES、MQ、MongDB 本身提供了多机房数据同步的能力,那无需做太多研发工作。Redis 则可以改造 AOF 为 binlog 模式,从而具备订阅同步的能力。
如果 ES、MQ、MongDB、Redis 作为唯一数据存储,用相关组件自身的同步能力;如果可以从 MySQL 数据源重建,则优先使用 MySQL 的同步机制,在各机房分别订阅 MySQL 重建其他组件的数据。其好处是避免了数据存储双写的一致性问题、数据修复比较容易。
对 OPPO 而言,它们自研了 Jins 数据同步框架,用来同步 MySQL 等数据库的 binlog,采取下列措施提升性能:
通过上述手段,南北机房的双向同步对比流行开源软件,在典型场景下基本达到一倍的性能提升,超过 20000 TPS。
据悉,自动化调度分为多个层次,数据层的自动故障切换和自愈,接入层的机房流量自动调度。罗工介绍,在机房流量自动调度方面,首先需要多维度的监控数据,避免单个数据源数据质量问题造成误判,包括客户端调用链或 APM 的数据、外网拨测平台的数据、机房网络设备监控数据、四层负载均衡监控数据、API 网关监控数据。
数据汇总到多活调度平台(青龙),进行综合决策,防止误判,过滤抖动,输出故障类型指令,以及观察调度的效果。
另一个关键即使是调度执行系统,需要客户端网络库、HttpDNS、传统 DNS、API 网关深度联动,保证切换过程生效的及时性、生效一致性。
罗工表示,现在,他们核心业务基本上可以实现分钟级完成机房调度,流量 100%切换到新机房。
目前,他们正在做一些新的尝试,比如调度决策中枢接入更多的数据源,优化算法,提升故障判断的及时性、准确性,有效过滤抖动,减少人工的干预。
他们的同城多活、异地多活以双机房为主,需要 200%的容量。他们正尝试划分更小单元,逐步迁移到三活单元化架构,逼近 150%容量的理想情况。
在数据层,他们继续推动计算存储分离,降低副本数,降低数据层多活的成本。并且,Elasticsearch、Redis、MongDB、MySQL 异地同步都收敛到 MySQL 异地同步相同的传输通道-Jins。异地两个机房部署完全独立的集群,减少机房之间的依赖,提升数据传输性能、数据安全性。
回顾 OPPO 互联网业务的实践历程,罗工认为业务多活架构的成功与一些因素密切相关。在业务分层和数据分类上,CAP 定理约束的是数据,不是业务。同一个业务的不同数据可以采用不同的多活方案,可以共存异地多活、同城多活,重点保障主线功能,保障多数用户。在基础设施层面,尤其是接入层、数据层,要尽量做到对用户透明,降低业务接入的成本。否则,标准、动作不一致,运维管理的成本也很高。
他说:“业务架构设计需要深入掌握基础设施的限制条件,比如 Redis AOF 改造为 binlog 后,部分数据结构不同提供支持,就需要从业务设计上规避。”
领取专属 10元无门槛券
私享最新 技术干货