低成本高可用性异地多活跨云部署方案

在跨云异地多活部署遇到的最大的两个问题 1 是复杂度 2是成本。 一个项目想达到99.9%的高可用性,并且不依赖云厂商还是非常困难。
异地多活有很多实现方法,下面分享一种 较为简单并尽可能的降低成本的方案。以阿里云 腾讯云 华为云,分别 上海 广州 北京 三城。以阿里(上海)和腾讯(广州)为主,华为为辅。
下文中所有的redis所指均为keydb6.x。

# 主要内容

负载均衡 路由层 业务层 缓存层 数据层 定时备份

# 负载均衡和路由层

因为非传统web项目,有客户端,这部分内容集成到客户端。由客户端根据预置规则选择 请求到某一个机房。

# 业务层

要全部改造为无状态服务, 部分业务需要完全兼容华为/阿里/腾讯的函数计算,并在对应的云厂商和自己的机器上都有部署。
部分业务有读写冲突的情况,虽然多活部署,但是使用消息中间件确保只有一个实例处在运行状态,其他实例等待抢占上位。

# 缓存层和数据层

应用层对数据库的读写 都在redis中,少数在pgsql。pgsql的数据只作为分析和统计使用。
其他需要储存的数据采用云厂商的对象储存。(要优化一下)

# redis 使用多个集群

# 核心集群跨云跨机房

读多写少全局写入集群 储存核心数据 至少3主三从分散部署到 三云三城市。
核心数据主要用于储存一些增长缓慢但是读取频繁的数据。例如 用户信息 物联网设备配置信息等。
为了防止频繁跨云读取,应用层需要针对性的做一些改造,针对读写非常频繁的数据,本地机房应该开一个redis实例来缓存核心集群的数据降低核心集群压力。
1、应用层在启动的时候从redis按需要读取数据到本地全局map,并在每次处理请求的时候或者用定时器/消息订阅等方式来判断是否需要重新从redis读取数据。
2、对读取非常频繁的数据,需要在本机机房搭建一个redis实例用来作为核心集群的从库,核心集群的部分数据,降低核心集群压力,并降低核心集群公网通讯开支。
3、应用层在写入数据的是,需要按需判断本地redis同步库是否已经同步成功。

# 机房内部缓存

# 读缓存

上文已经提到,机房内应该增加至少一个redis实例或者集群,用来作为核心集群的从库,作为本地部分数据的读取使用。以降低核心集群的公网通讯开支。
一致性要求非常高的数据,依旧是从核心集群读取。

# 写缓存的实现

一致性要求不高的数据,写入到本地的redis集群,并安需定时同步到核心集群。例如:设备的在线状态和传感器数据(容许1-2秒延迟,故障时容许10-15秒延迟)
需要落地持久化的数据,并容许短时间丢失的数据(设备传感器的历史数据容许丢失3-5分钟的)先写入到本地的keydb 开启flash持久化,并定时同步flash文件和rdb aof文件到 本云的对象储存(因为数据庞大,后文针对此方案做了进一步优化降低开支)。 一致性要求高的数据,直接写入到核心集群。

# redis同步

除了redis cluster之外使用阿里的RedisShake来做单向同步。

# pgsql

# 时序数据/kv持久化储存

# 实现

项目以写为主,读取较少,所以这里直接使用keydb替代redis。同时部署多个集群来作为纯redis 和kv硬盘储存使用。
没有引入时序数据库,是因为数量没有那么庞大,keddb的flash方案可以满足。

# 容灾和数据合并

# 数据合并

因为此项目的这部分数据都是基于设备或用户的,所以同一个设备和同一个数据的历史数据可以合并到同一个key里面 以减少RocksDB(keydb的flash功能基于RocksDB)的小文件数量降低对象储存成本。
通过k8s/swarm 在阿里云和腾讯云分别 分别部署一个实例。两个实例先查询本地要处理的数据量,并协商以数据量较多的为主实例。两个实例分别合并前一天的历史数据到数组/map ,主实例在处理的时候询问从实例是否有同一个设备/用户的数据,如果有数据交付给主实例处理。如果没有从实例自己处理。 阿里云走内网数据持久到oss 腾讯云云走内网数据持久化到cos。另外一台不限制带宽的服务器同步合并两家对象储存数据保持一致。
查询的时候,当日数据从keydb查询,前一日数据从对象储存查询。

# 负载均衡和历史数据的处理

# 设备和app的负载均衡

设备实时数据 接受的应用层 分别部署到阿里云和腾讯云的多台机器上,由客户端先询问服务上的入口应用程序自己应该请求那台服务,并在一定周期内(未断电重启)持续请求这台服务。如果对应的机器负载较大,会通知客户端更换一个别的地址,或者地址连不同的时候客户端也会更换一个地址。(用户的app同样的规则)

# 数据的储存

# 实时数据

因为容许部分数据丢失,所以实时数据由对应的devConn服务直接储存到map,而不写入redis。用户端app在查询的时候,会先根据核心redis的从库中的对应设备的记录获取对应设备的服务器地址,直接查询map.

# 历史数据

实时数据需要及时落盘,容许丢失一小部分,但是不能产生大量碎文件,不然会导致后续储存费用太高。
为了降低io,由devConn自己处理储存问题。储存后如何同步到云储存参考上文。

# 其他

推荐阅读 http://kaito-kidd.com/2021/10/15/what-is-the-multi-site-high-availability-design/

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计