超3亿活跃用户的多活架构,数据同步与流量调度怎么做? - 架构 - dbaplus社群:围绕Data、Blockchain、AiOps的企业级专业社群。技术大咖、原创干货,每天精品原创文章推送,每周线上技术分享,每月线下技术沙龙。

标签: | 发表时间:2021-04-01 14:05 | 作者:
出处:https://dbaplus.cn

一、多活业务架构

 

1、OPPO多活架构原则 

 

第一,主线多活。

 

多活成本比较高的,双活是两倍,三活可能成本会低一些,但三活的难度更大。因此没有办法对所有业务进行多活,只能对主线做多活。

 

第二,是保障多数用户。

 

举个例子,系统有个充值的功能,充值功能本身是强一致的,完全不能允许任何的延迟或者是副本的读。

 

但是多活切换之后,只有少数用户在切换的前几分钟有充值的,这部分用户余额可能没有通过过去,只需要对这部分用户进行服务降级,其他绝大多数用户是可以使用完整的服务的。

 

第三,数据分类,应用不同的CAP模型。

 

CAP定理不是针对的业务功能,比如说账号、支付、登录,CAP定理是对数据的要求。一个功能可能用到多个数据,数据本身的一致性、可用性、延迟的容忍是不一样的。

 

所以需要对业务功能用到的数据进行分类,比如余额数据、流水数据、日志数据、个人资料数据……我们对每个数据进行一致性、可用性的需求分析,一致性要求很强,这个数据就选用同城高可用的数据库服务。这个数据一致性要求不高、允许延迟,就可以选择异地高可用的数据库服务。

 

所以这个业务来说不是整体使用一个CAP模型,在业务内部,因为不同的数据分类,使用了不同的模型,因此业务有时候存在部分降级的情况。

 

第四,平台业务SDK化。

 

 

OPPO的业务比较多,比如浏览器、软件商店、广告务、音乐、视频等非常多的业务,这些业务都用了平台化的服务,比如评论系统、消息系统,还有账号鉴权的系统等等。

 

OPPO公司的机房比较多,主要的就有好几个机房,我们的上层业务是分布在不同的机房里面去,这对平台业务来说就比较麻烦,上层业务可能只需要做双活就行了,而平台业务可能就要做七活、甚至八活,而且七、八个机房都要有读和写,难度就非常大。

 

为解决这个问题,我们提出平台业务进行SDK化思路,把这种平台型业务,拆分成独立的域名,从SDK开始拆分,这样我们平台业务只需要单独做多活就行了,不需要在每个机房都提供读写的能力。

 

第五,数据最终一致。

 

第六,我们的记录日志、流水,避免修改、计数操作。

 

2、同城多活业务架构 

 

 

上图是典型同城多活的业务架构,应用层是完全无状态的,随便打流量。四层采用DPDK技术开发,七层包括Nginx和API网关两个组件,Nginx只用来做SSL卸载、WAF防火墙,其他功能都是API网关来提供。

 

数据层以主备为主,写流量只会写到Master节点,但是读的流量可以访问slave节点,但是也不一定,看业务本身数据一致性要求,如果要求非常强一致的,我们的读也只会指向Master节点。

 

需要注意,我们把Nginx和API网关都放到同一个容器中,两只之间采用进程间通信。这样的好处是,我们扩容的时候,我们可以将整个七层同步去扩容,而不会存在某一层组件容量不足的情况。

 

另外就是注册中心,我们没有使用 k8s本身的一个注册功能,而是自己基于数据库,实现了AP模型的注册中心,保证注册中心的跨机房高可用。同时注册中心兼容Consul协议,从而更好的融入开源生态。多个k8s集群的实例,都会注册到统一的注册中心里面去。这个注册的动作,是由发布平台完成的,好处是应用发布的时候,发布平台可以提前摘掉流量,避免重启影响服务的成功率。

 

3、异地多活业务架构——单元化 

 

 

异地多活,比较典型的架构是单元化,就是将用户进行分片,将不同的用户分片放到不同的机房里面去,这样可以做到一个完全的扩展,随着用户规模的增加,我们可以很容易去扩展机房的数量,这些都可以持续的去增加的,包括每个机房的容量也可能不一样。比如有的机房大,有的机房小,我们可以调整每个机房存放的单元数量。

 

这里确实实现了多活,每个机房都有流量,每个机房也是读写,是完全的多活,但是单元高可用的问题如何解决,单元的归属机房故障了,如果把这个单元转移另一个机房继续提供服务。

 

4、异地双活业务架构 

 

 

上图是我们使用较多的异地双活架构,首先我们将用户按照地域维度进行了一个单元划分,比如说按照地域将用户划分为七个大区单元。

 

注意这个单元划分是用户首次访问服务的时候进行的,然后客户端就保存了单元号,就不会产生变化了,所以用户出差,换到因为另外一个地域里面去,它所属的单元号我们是不会变化的,还是访问单元归属的机房,这个时候可能就不是访问最优的机房。

 

这样的好处是,当一个用户移动的时候,数据访问就不会在两个机房之间跳来跳去,避免双向同步的数据冲突问题,很容易实施。

 

数据层在两个机房,都是完全全量的,两个机房间数据是做双向同步,没有谁是主谁是从的区分,是完全对等的架构。

 

用户流量调度按单元进行,这样可以保证一个用户,他只会访问其中一个机房,不会在南北两个机房之间跳来跳去,就算是用户出差也是如此,按照首次访问服务时的地域来划分的单元。只要我们的调度规则没有变更的情况下,一个用户他永远只会在其中一个机房读写,这样的好处就是,第一个可以避免我们的同步的冲突,第二个好处就是容忍了数据延迟的情况,比如说一个用户他永远是看到北方机房,南北之间数据同步的延迟日常情况下其实是感知不到的。

 

这个架构是非常简单,只需要在客户端网络库里面做一些封装,对用户进行单元划分,按单元进行流量调度就可以了,双向同步比较好实施,延迟、冲突,这些问题都可以避免。

 

除了地域之外,也可以按照账号或者设备来划分单元。按账号或者设备划分单元的好处是,如果按照地域划分单元,在用户删除手机APP这种情况下,APP里面保存的单元号就没有了,下次访问服务的时候就需要重新分配单元号,因为地域可能和之前不同了,就可能分配到不同的单元号,按账号或者设备划分就没有这个问题,重新分配还是原来的单元号。

 

前面说到,南北机房的数据层都是全量的,一般情况下,按地域的划分单元的模式,就算重新分配了单元号,也不影响数据的读写访问。

 

5、异地双活——评论系统案例 

 

 

上图是平台型业务-评论系统异地生活的案例,评论系统从SDK开始,就进行了域名拆分,避免了在业务域名所在机房内部去做跨机房的评论服务调用,影响服务的可用性和性能。

 

如上图所示,我们只对MySQL原始数据层做了南北双机房同步,第二层的评论元数据表,还有第三层的一个Cache,这两层实际上没做同步的。两个机房分别基于MySQL数据独自去重建第二层的元数据表,第三层的Cache,以及重建其他的数据源。

 

这样好处就是,我们只有一个数据源做了南北机房的同步,就可以避免双数据源同步的时候,两个数据源之间会存在同步的进度不一致,从而两个数据源之间的依赖关系出现问题。

 

举个例子,我们上面的评论表、点赞表这一层,最上面这一层做了同步以后,我们中间第二层如果也做了同步,然后第二层同步以后,两个数据可能存在差异,比如说第一层同步快一点,第二层同步得慢一点,同样是南方的用户,他们看到这个数据之间的存在不匹配的问题。

 

因为用户流量调度是按单元进行的,两个机房的数据虽然有差异,有延迟,但是用户感知不到的。一个用户要么看到南方机房,要么看到北方机房,我们评论数量两个机房有差异,点赞数量有差异,回复数都有差异,但是无所谓,用户是感知不到差异的。需要注意的一点,就是当多活切换的时候,用户能感知到一个差异,但日常情况下用户感知不到这个差异。

 

6、异地N活业务架构 

 

 

上图是比较复杂异地N活业务架构。它基本的思路就是对用户进行两级的划分。第一级按照设备和账号划分单元,其中单元里面既有登录的用户,也有未登录的用户。

 

在第二级划分的单元内部,我们再应用异地双活的模式,或者是同城多活的模式,比如说左边单元1,按照地域做第二级的划分,把它划分成南北两个副本,既然是副本,肯定数据是全量的,是异地双活模式,两个副本数据做双向同步,这种模式适用非强一致的业务。

 

那么强一致的业务怎么办呢?比如右边的单元4,跨同城的两个机房,单元内部采用同城多活的模式,就是共享跨机房高可用的数据层,是主备的的。这种模式适合强一致的业务。

 

前面说了单元内部主要两种模式,第一种是异地双活,双向同步,主主模式,读写在本机房,然后做双向同步;第二种是同城多活,主备的模式,跨机房共享主备切换的数据层。除此之外,单元内部还可以选择主从,冷备等模式。

 

7、服务部署 

 

 

上图是服务部署架构。服务部署分为几大部分。

 

第一部分是中心域。

 

中心域主要是部署一些运营管理后台,还有一些爬虫,还有一些非常长尾的应用,但这些业务可能不太重要,也不需要做一个多活。中心域的读写都是在中心机房,然后把数据单向同步到其他单元机房。

 

第二部分是全局域。

 

全局域主要存放非单元分片维度的数据,比如评论、消息等。这些数据不能按统一维度进行拆分,需要全量的访问,放到全局域的数据都是全量的。

 

第三部分是单元域。

 

存放按单元拆分的数据,比如用户订单、收藏、下载记录等。

 

8、服务路由 

 

 

用户会先请求到API网关,API网关根据请求的单元号参数,判断是是否访问错了机房,如果访问错了,就做重定向,或者跨机房转发,用户自己选择的其中一种模式。转发的模式比较依赖于两个机房之间的专线的带宽和稳定性,重定向模式机房之间的带宽要求会低一些,客户端重新发起请求,这两个机房之间的网络专线要求低一些。

 

前面说到用户首次请求的时候,会给客户端分配一个单元号,这个单元号将会存储起来,以后每次业务请求都会带上这个单元号。

 

请求到了单元内部,单元号会做一个全链路的传递,全链路传递是通过调用链来实现的,调用链可以把一些参数做全链路的传递。应用实例打上了单元号的标签,微服务调用方通过单元号对实例进行筛选,防止请求打到其他单元。

 

数据访问层要做一个兜底的操作,可能由于服务路由还是其他的一些原因,不小心访问错了单元,这个数据层有可能访问错,所以数据访问层要做一个兜底,根据传过来单元号,做拒绝或者转发。

 

9、用单元化解决业务扩展性问题 

 

 

单元化不只是可以用来解决多活的问题,也可以用来解决业务扩展性问题。在一个机房内部,如果服务1000万用户,他可能需要10个数据库,服务1亿个用户,需要100个数据库,如果100个数据库让每个应用实例都连上的话,连接数就太多了。

 

可以在一个机房内部也拆分多个单元,每个单元保证1000万、2000万左右的用户,随着用户的增长,我们再将单元数量进行增加就行了,这样就可以保证每一个单元内部的服务规模受控。

 

二、多活数据同步

 

1、MySQL同城多活 

 

 

上图是MySQL同城多活架构,MySQL对外看上去是一个集群,只有一个IP。我们需要解决的问题是:怎么让跨机房的集群看到的是同一个IP?这里就用到了Anycast技术,IP的作用可以理解为域名,我们把一个 VIP用Anycast技术,将它路由到两个机房,或者是三个机房。我们是路由到三个机房,然后就到了机房内部,再通过 ECMP协议将流量再分到多个四层负载均衡节点。

 

通过Anycast第一层路由到不同的机房,第二层的ECMP再路由到基于DPDK技术开发的四层负载均衡节点。这样我们整个的数据库对外看到的VIP就是同一个了,所有机房看到VIP都是同一个。利用Anycast和ECMP两个技术,实现跨AZ共享VIP。

 

然后是数据层,数据层我们现在是一主三从,然后需要2个以上slave同步成功,才能完成最终的成功。

 

MySQL版本需要5.7以上,操作系统内核需要打一个 toa补丁,这样经过四层负载均衡之后,MySQL Server才能拿到真正来源IP。因为我们这边要做一个IP白名单的授权,如果不打补丁,拿到的来源IP就是四层负载均衡的IP,就没法做IP白名单授权了。当然top补丁有一个缺陷,就是只能支持ipv4,这在内网使用问题不大。

 

底层采用了开源的MySQL拓扑管理组件,通过检测我们数据库节点的情况,然后做重新选组做切换,然后通知SLB改变后端指向,流量打到新的master节点,

 

Anycast不是必须的,也可以用域名代替,但是域名有个问题,需要重新接连的时候才会发起解析,所以域名切换的时候可能会切不干净。Anycast做切换是立即生效的,因为这是路由协议的一个变更,马上就能切过去,不存在解析不干净和生效不一致的问题。

 

Anycast除了内网之外,外网也用的比较多,比如说谷歌上负载均衡器,它发布的IP就是Anycast的IP,在公网环境下,在不同的地区路由到不同的一个真实地址,包括我们 DNS Server也是用Anycast去发布的,在不同的区域,路由到就近的IDC,所以Anycast技术应用还比较广泛。

 

2、MySQL异地多活 

 

 

上图是MySQL的异地多活架构,重点在于提升同步的性能,从源库订阅到数据以后,不是直接写目标库,而是先存起来,在目标机房部署中继日志模块。这样的好处是,我们可以在网络上快速的传输过去,中继日志并行去写目标库。

 

这个设计性能提升非常大,OPPO实际业务场景下,这个模式比订阅后直接写目标库提升了几倍。因为引入了中继日志,就存在两阶段提交的问题。比如中继日志写成功,但是中继日志写目标库没有成功。这就存在数据一致性问题,需要用到两阶段提交。

 

还有就是数据压缩和加密,对数据的安全和同步性能也非常重要。

然后是多消费者支持,订阅模块会保存数据,每个订阅方可以维持自己的消费位点,彼此之间没有干扰,从而减少多订阅方同步对 Source DB的压力。

 

3、MySQL订阅——数据最终一致 

 

 

以前面提到的评论系统为例,数据同步只同步MySQL那一层,而其他的数据源Cache、MQ、ES、排序服务等,分别订阅MySQL binlog重新构建。

 

原则上,我们尽量只同步底层的一份MySQL数据,其他数据源订阅MySQL重建。前面说到,MySQL只需要订阅一次,Jins程序自己存储了一份数据到本地文件队列,然后分别重放到Cache、MQ、ES等其他数据源,也可以多次重放数据。

 

如果多数据源分别进行同步的话,多个数据源同步的进度是没法保证协调一致的,必然有的数据源快,有的数据源慢,这有可能导致两个数据源之间的关联关系出现一些程序错误。所以我们尽量只同步一个数据源,再基于MySQL重建其他的数据源,避免进度不一致的问题。

 

4、MySQL数据对比&修复 

 

 

OPPO的业务场景,很多地方都非常依赖底层的 MySQL数据同步,两个机房之间之间到底有没有差异,是蛮重要的。

 

因此我们设计了一个独立的MySQL比对修复工具,就执行上图这样一个SQL语句,通过这个SQL语句,对一段时间之内的所有数据算一个异或的值,通过异或值去比对两个机房之间数据差异,如果比对有差异,我们再缩小比对范围,逐步逼近到差异的记录行,这个语句的执行效率还是蛮高的。

 

但是这个方案有个不足,要求我们数据库里面有一个时间戳的字段,程序会对比前一个周期内的所有记录的异或值,判断两个机房之间数据是否有差异。

 

另外一重要场景就是数据修复,因为业务可能配置错了数据库、应用实例配置生效不一致,再比如A单元数据写到B单元,这个时候需要修复数据,通过这个工具,把两个数据库不一致的数据行整理出来,然后人工做识别或者批量修复。

 

5、Redis多活 

 

 

Redis同城多活的架构如上图所示,我们在Redis Server上面做了一层代理,下层Redis Server没有使用Redis cluster技术,代理将流量进行分片,分发到了不同的Redis Group里面去,每个Group里面就是普通的Redis主从。

 

主从之间采用binlog的同步,因为Redis本身没有binlog,我们把 AOF做了改造,把让它变成binlog的这种格式,这里改造的工作量不大。

 

然后代理也支持两种模式,一种是重定向模式,一种是转发模式。转发模式就是写主读从,它只会把写流量转到了主机房里面去,但是从机房是能读的。重定向模式就不一样,重定向模式是非常更强一致的,读写都只能在主机房。

 

前面反复提到,CAP是针对数据的,是指数据本身的延迟或者差异的容忍度,所以这两种模式都需要支持,有的数据它就是要强一致,一定要到主库里面的去读,但有的数据它允许从库读,允许延迟。

 

异地多活也很简单,异地多活两个机房各部署一个组件去订阅同机房的Redis,订阅Redis的binlog,订阅的数据写到MQ里面去,两个机房分别重放binlog,实现起来并不复杂。

 

最后简单说一下binlog的格式,里面包括了命令、数据产生的机房、递增的序号,还有一个时间戳。还需要注意的一点,Redis持久化RDB也要改造一下,RDB需要包含一个 binlog offset,binlog读取偏移量,需要把它记下来,因为主从颠倒的时候,我们订阅程序要重新从offset开始继续订阅下面的命令。

 

三、GSLB流量调度

 

1、Http DNS 

 

 

最后讲我们的GSLB流量调度,首先是为什么要使用Http DNS。

 

第一个是防劫持。

 

DNS劫持,DNS是多级缓存,部分环节存在解析劫持的情况。

 

DNS黑洞,这个大家可能遇到比较少,什么叫DNS黑洞呢?就是运营商监控到某个域名有恶意的请求,封杀他的时候不小心扩大了封杀的范围,我们已经出现过几次这种情况,有时候某个地区甚至可以把整个cn顶级域名全封杀,这种封杀的范围很大,称之为DNS黑洞。整个2020年已经发生过多次这种情况了,某个地域整个顶级域名都给你封杀掉,大家都解决不了。

 

第二个是快速生效。

 

 

首先是DNS本身的多级缓存,这个时间不受控制,但它可能不是主要问题,更主要的问题是客户端长连接。

 

我们还没上Http DNS之前,业务使用了客户端长连接,需要20分钟甚至一个小时才能大部分流量调度走。主要的原因就在客户端长连接,DNS做了变更以后,只有客户端重新发起连接的时候,它才会发重新发起解析,才拿到新的IP,如果连接没断开,就一直不会转移,所以这部分长连接用户根本就切不走。

 

如果是机房入口网络故障还好,连接天然会断开,如果是因为业务自己的问题,需要把流量切走,这种情况下就会发现根本切不走,所以客户端长连接是比较重要的问题。所以客户端网络库需要处理一下,解析变更的时候,需要主动去关闭连接,但是传统DNS,没有解析变更的通知机制,不发起解析就不知道解析变更了,这里就进入了循环了,需要仔细的思考一下流程。

 

第三个是精准调度。

 

 

传统的DNS解析只能获取到IP这一个参数,首先IP信息不准确,包括运营商归属、地域归属,都不是很准确,国外运营商特别多,情况更严重。现在IPv6也在快速的推广,信息不准确的情况更为严重。其次传统DNS无法做到用户维度、设备维度的解析。

 

 

最后是生效一致性。

 

单元一旦发生调度以后,在单元内的所有用户要同时调走,不能说一部分先调走,一部分后调走,这样数据写入就乱了,需要保证全体用户生效的一致性。

 

2、单元调度 

 

 

下面讲单元调度的主要流程

 

第一步: 划分用户单元

 

划分用户单元主要有三种模式:

 

  • 按设备划分单元;

  • 按账号划分单元;

  • 按地域划分单元。

 

这里有个地方需要注意,我们为了划分单元,客户端肯定要传一些参数,如果按账号划分单元,需要传账号ID;按设备划分单元,需要传设备的IMEI,或者国内Android厂商推行的OpenID;按地域划分单元很简单,直接从IP里面可以获取,不用客户端传递参数。

 

因为隐私合规要求,比如说海外业务,直接传用户的ID或设备信息,是违规的,因为我们这个调度的域名它是一个独立的域名,它不是业务本身的,这个域名很难跟用户解释,即使跟用户签了协议,因为业务主体的不同,可能也不一定包含了这个域名,所以我们做了一个匿名化处理,设计了两个新参数,一个叫ADG(匿名设备分组),一个叫AUG(匿名用户分组)。

 

我们将账号ID和10万取模的值定义为AUG,设备ID与10万取模的值定义为ADG。通过这种方式,把设备和账号分成10万个桶,然后对桶分单元,比如说1~5000桶是单元1,5000~1万桶是单元2。这样我们就不用传真实的设备ID和真实账号。

 

第二步:客户端获取单元号。

 

客户端首次访问业务的时候要分一个单元号,这样就算按地域划分单元,基本上也不会出现变更,只要用户的APP不被删除,我们OPPO手机的好处就是,我们的APP是不怕被删除的,我们的数据不会被清掉;但如果是一个外发的APP,可能就存在APP删除,这个可以考虑用设备或者账号分单元。获取到单元号之后,就永久保存在客户端。

 

第三步:客户端解析域名IP。

 

域名解析的时候会带上单元号的参数,获取这个单元对应的IP列表,然后客户端缓存IP列表。需要注意的一点是缓存机制,建议根据网络环境进行缓存,比如WiFi名称,或者运营商的名称,底层的缓存数据结构就是域名加上网络环境的名称。这样的好处就是,用户网络切换的时候,比如说家里面是WiFi,我们拿的是IP1,我们一出门,网络环境变了,我们取出的缓存IP就是IP2,在每个网络环境都是缓存最优的IP。

 

另外一点需要注意是:我们为每个单元还分配了一个单元域名,这是一个传统DNS域名,主要是降级的时候使用。可以设想一下,如果我们没有为每个单元单独分配一个传统DNS域名,一旦降级的时候就会走到业务的主域名,而传统DNS是不能携带任何参数的,无法做到按单元进行解析,用户流量就全都乱了。

 

所以每个单元分配一个域名的好处就是,降级的时候只要降级到我们这个单元的域名,这样大多数用户解析结果还是准确的,不准确的一部分通过API网关重定向或者内部转发,只要很少用户需要走这个路径,绝大多数用户还是最佳的路径。

 

第四步:客户端重定向。

 

因为调度过程当中还有一部分用户在访问旧的IP,我们是通过API网关,把新机房IP直接告诉客户端,客户端立即用新IP重试,并且异步去刷新解析,如果只是反馈一个状态码,告诉客户端需要重新刷新解析,客户端的总请求时间就会拉得比较长,这就是重定向模式。

 

但除了重定向模式,还有转发模式,但是转发模式比较依赖机房之间的专线带宽和稳定性,如果公司规模不是很大的话,机房之间的专线带宽和稳定性可能赶不上公网,重定向模式可能更适合一些。

 

3、单元调度注意事项 

 

 

数据层联动,举一个用户余额充值的例子,这是非常强一致的,我们可以维护一个数据不一致用户清单,比如说有用户刚刚进行了充值,这个数据还没在各个机房达成一致,机房调度的时候,只是这一部分用户需要进行服务降级,其他用户还可以继续提供完整的服务。

 

4、域名解析刷新时机 

 

 

接下来讲域名解析刷新时机。因为HttpDNS是直连解析的,不像传统DNS有多级的缓存,如果我们还沿用传统DNS的 TTL方式来刷新解析,这个TTL就不能设置的太短,太短了HttpDNS Server的压力非常大。TTL设置过长又不能满足业务快速恢复的要求。

 

所以域名解析的及时刷新依赖另外两种途径,第一种途径是失败。我们请求一个服务,要么连接错误,要么响应内容出现错误,比如说我们响应了500,或者其他我们认可的一个响应值(客户端可以自己定一个规则),我们访问失败的时候,就需要立即去刷新一下域名解析,因为请求失败的时候可能需要做一个机房调度,不管是业务后端出现了问题,或者是连接不上,这种情况都需要做机房调度,需要客户端刷新解析。

 

第二种途径是指令,如果是因为我们带宽不足,做活动,或者其他原因的,需要把流量切走,这时我们可以通过API网关下发指令,下发指令也是随着API网关的正常的业务请求,响应Header带下去,不是单独的通道,也不是通过Push推送。

 

这样我们就可以兜底,要么会请求失败,会立即刷新解析。要么请求成功,响应header就会携带指令。所以用户一定能走到失败和指令其中一条路径。因此我们做了调度变更以后,用户一定会刷新,不再依赖TTL了,过期时间可以设置非常长,这样我们绝大多数请求,都不会发生真正的解析请求。

 

通常情况下,传统DNS有2%~3%的解析失败率,还是挺高的。通过这种方式,我们就可以把解析成功率做到99.5%以上,日常情况下甚至能做到接近100%。

 

5、调度生效一致性 

 

 

下面讲一讲调度生效的一致性,当我们的客户端降级到传统DNS的时候,就会解析到错误的机房,在调用生效过程当中,也会访问到旧的机房,所以我们在API网关会做一个拦截,因为每个请求都带上了单元号,API网关就可以判断这个请求是否请求到了正确的机房,如果请求错了机房,API网关把请求定向单元当前归属的机房。

 

定向用户请求有两种模式,一种是转发模式,API网关直接转发到新机房的业务后端实例。另一种是重定向模式,API网关在响应header携带了重定向指令,以及新机房的IP(避免客户端多一次的请求),客户端立即重试新IP。

 

转发模式需要消耗较多的机房专线带宽,重定向模式的总体时长更高,业务可以自由选择两种模式。

 

解析刷新采用并行跑马的模式,客户端会并行请求两个HttpDNS Server和一个传统DNS,三个请求同时发出去。如果HttpDNS Server请求成功,哪个先到就用哪个,如果两个HttpDNS Server请求都失败,就使用传统DNS解析结果。因为每个单元都分配一个传统域名,所以传统DNS解析结果和HttpDNS解析结果也基本是一致,只有极少数用户会解析错误,API网关重定向一次以后也能纠正过来。

 

6、调度决策大脑 

 

 

调度决策大脑会收集很多路的原始监控数据,比如客户端调用链的数据、外网拨测平台的数据、机房网络监控的数据等等,多路数据汇总到决策大脑里,进行比对分析,得出故障的结论。

 

调度决策大脑一定要依赖多路监控数据源,因为单路数据的质量无法保证,比如可能会出现拨测用例配置错误、网络监控数据丢失等,所以单路数据都是不可信的,需要多路数据源做交叉的比对,过滤抖动、防止误判。

 

调度决策大脑最终会输出一个指令,指令只会告诉你故障类型,比如:机房故障、运营商线路故障、机房之间网络(DCI)故障,或者是容量不足、业务自身出现了问题等。业务自身出现问题,比如业务的数据库故障,也需要切到另外的机房去。

 

决策指令同时发到两个地方,既要发给接入层,也要发给数据层,为什么需要这样呢?

 

假设我们同城两个机房之间,专线出现了故障,两个机房的数据库肯定达不成一致,同步不过去了。这个情况下,假设我们的数据库选主B机房,而接入层保留A机房, A机房的数据库完全写不进去,即使写进去也是错误的,这里我们要保证数据层和接入层两边选择的机房要一致。

 

所以这种专线故障情况下,我们是调度决策大脑来通知,做统一的决策,同时通知接入层、数据层做联动,选择同一机房,这个主机房的选择是事先配置好的,它不是由我们刚刚说的Raft组件来解决的。

 

7、调度效果 

 

 

上图是我们9月份做过的一次机房调度的效果,基本上做到分钟级(实际上是秒级的)的生效,是很陡的一个曲线。

 

四、总结

 

最后,给大家总结一下今天分享的内容:

 

>>>>

Q&A

 

Q1:Http DNS也有缓存的吧?

A1 :对,刚刚提到我们Http DNS缓存时间非常长,缓存了一周的时间,而且缓存的时候是根据环境来缓存的,就是按照 WiFi名称、运营商的名称来做缓存,这样网络切换的时候可以拿到最优的IP。

缓存的时间非常长,是因为域名解析的刷新,是不依赖缓存过期的,如果能请求成功,API网关在响应Header就会带上调度指令,如果请求失败客户端也会主动去刷新解析。因此解析的刷新,是不依赖缓存过期时间的。

 

Q2:同城多活网络是怎么配置的?两个机房使用相同的ip地址,还是不同的?

A2 :对于跨机房高可用的数据库来说,用户看到的是同一个IP,第一层使用Anycast路由到机房,第二层使用ECMP路由到多个四层负载均衡节点,单个四层负载均衡的流量扛不住,四层负载均衡是一个集群,通过ECMP实现流量分发。

多余入口流量来说,前面架构图可以看到,接入层在两个机房从四层、七层都是独立的,接入层有2组出口IP,如果其中一个机房运营商线路出现问题,根据调度决策系统的指令,自动停止该运营商线路的IP解析。

 

Q3:老师能介绍一下多活带来什么业务收益吗?是什么契机促使 OPPO开始做异地多活?

A3 :OPPO业务多活的三个核心诉求是成本、扩展、容灾。

成本是指业务总体技术运营成本,包括基础设施的资源成本、研发成本,还包括业务中断的成本、品牌和口碑的成本;

扩展是因为业务规模过大,一个服务需要调用数百个三方实例、一个数据库被数百个实例连接、一个服务需要连接几十个数据库,这就需要对用户进行分片,缩小业务规模,自然演进到单元化多活的架构;

容灾一方面是极端情况下用户数据可靠性保障的需求,另一方面还是业务过于复杂、处理的链路很长,总有一些意想不到情况的发生,频率还挺高,问题定位到恢复的时间达不到公司RTO的要求。机房内部共享了运营商线路、DNS、SNAT防火墙、负载均衡、K8S集群、注册中心、监控等等资源,而机房之间是相对隔离的环境,同时出问题的概率大为降低。在业务出现无法自动恢复的故障时,先切换机房恢复业务,然后再从容定位问题根因。

 

Q4:随着业务发展启用多个订阅时,如何减少对数据库的压力?

A4 :我们从数据库源库订阅出来以后,先落地到本地文件队列,然后多个订阅方可以维持自己的同步位点,所以对于源数据库来说,只会有一次订阅。

 

Q5:请问同城双活方案MHA manager部署在哪个数据中心?

A5 :我们这里不是MHA,我们用的是一个开源的Raft组件,部署在同城的3个机房,通过Raft组件检测数据库的状态、触发切换。

 

Q6:Http DNS和Local DNS的区别是什么?

A6 :Http DNS走的是HTTP协议,客户端直连解析,没有运营商的多级缓存。Local DNS就是运营商的DNS,成功率低,还有劫持、黑洞等问题,而且这两年黑洞频率是越来越高了,前几年基本上很少出现黑洞情况。传统DNS劫持情况现在好一些了,像移动端的接口劫持相对来说会少一些,H5的劫持多一点。

Http DNS就是依赖HTTP协议做解析,但这个压力会比较大,因为Http DNS没有多级缓存,所有请求都到我们的机房,所以刷新机制的设计就非常关键,前面一个章节详细介绍解析刷新的时机。

HttpDNS还有一个好处,因为是自定义的协议,可以传递其他参数,比如设备信息、账号信息,这样才能够实现按用户单元进行解析、调度。

 

Q7:能否制定统一的用户单元划分规则?

A7 :这个问题比较好,我们最开始也是想这样子的,我们有云服务、广告、信息流、音乐、视频等业务,起初也想整个公司使用一套单元划分规则,这样业务之间可以做到单元内封闭调用,避免跨机房的调用。

 

最终的方案,业务之间没有使用同一套单元划分规则。主要原因是:比如说有个业务他经常会做活动,做活动的时候他需要将一部分用户调度走,如果全公司用一套规则的话,所有业务都要跟着调度走,其他业务是不同意的。所以我们是每个业务自己制定单元划分规则。

 

那这里怎样解决业务之间跨机房调用呢?前面说到了平台型业务SDK化,上层业务之间本身没有强依赖,音乐、软件商店、视频之间本身没有强依赖,他们主要是依赖平台型服务,如账号、评论服务、消息中心、推送服务等。这些平台型业务我们最开始也是提供机房内部API去给其他业务器调用,这就导致我们的平台型服务在每一个机房都要去部署,每个机房都要提供读写功能。所以我们将平台型域名拆分出来,从SDK就开始就和业务域名分开,平台型自己做多活。当然平台型业务无法做到100%的SDK化拆分,平台型服务的部分数据也需要单向同步到各机房,提供本地查询的服务。

 

Q8:Redis日志是哪个开源组件做到的来的?

A8 :Redis binlog是OPPO自己修改的,基于AOF修改,简单说一下binlog的格式,


相关 [用户 架构 数据] 推荐:

基于用户画像大数据的电商防刷架构

- - 快课网
最近1~2年电商行业飞速发展,各种创业公司犹如雨后春笋大量涌现,商家通过各种活动形式的补贴来获取用户、培养用户的消费习惯. 但任何一件事情都具有两面性,高额的补贴、优惠同时了也催生了“羊毛党”. “羊毛党”的行为距离欺诈只有一步之遥,他们的存在严重破环了活动的目的,侵占了活动的资源,使得正常的用户享受不到活动的直接好处.

HBASE数据架构

- - 数据库 - ITeye博客
关系数据库一般用B+树,HBASE用的是LSM树. MYSQL所用类B+树一般深度不超过3层,数据单独存放,在B+树的叶节点存储指向实际数据的指针,叶节点之间也相互关联,类似双向链表. 这种结构的特点是数据更新或写入导致数据页表分散,不利于顺序访问. LSM存储中,各个文件的结构类似于B+树,但是分多个存在内存或磁盘中,更新和写入变成了磁盘的顺序写,只在合并时去掉重复或过时的数据.

日处理 20 亿数据,实时用户行为服务系统架构实践

- - IT瘾-dev
携程实时用户行为服务作为基础服务,目前普遍应用在多个场景中,比如猜你喜欢(携程的推荐系统)、动态广告、用户画像、浏览历史等等. 以猜你喜欢为例,猜你喜欢为应用内用户提供潜在选项,提高成交效率. 旅行是一项综合性的需求,用户往往需要不止一个产品. 作为一站式的旅游服务平台,跨业务线的推荐,特别是实时推荐,能实际满足用户的需求,因此在上游提供打通各业务线之间的用户行为数据有很大的必要性.

再谈数据架构

- - 人月神话的BLOG
本篇为杂谈,主要是想谈下企业架构中数据架构部分的一些关键点. 首先在TOGAF的ADM方法论中将数据架构部分的内容放在了信息系统架构-数据架构部分,这个方式是不合适的. 前面一直强调了企业架构的两条重要线索,一个是流程,一个是数据,这两者都是既涉及到业务架构部分,也涉及到应用架构部分. 在最终架构的分析和分解,业务建模到IT实现的转换过程中,自然就会过渡到应用架构部分的内容.

大数据Lambda架构

- - CSDN博客云计算推荐文章
1 Lambda架构介绍.          Lambda架构划分为三层,分别是批处理层,服务层,和加速层. 最终实现的效果,可以使用下面的表达式来说明. 1.1 批处理层(Batch Layer, Apache Hadoop).          批处理层主用由Hadoop来实现,负责数据的存储和产生任意的视图数据.

大数据架构hadoop

- - CSDN博客云计算推荐文章
摘要:Admaster数据挖掘总监 随着互联网、移动互联网和物联网的发展,谁也无法否认,我们已经切实地迎来了一个海量数据的时代,数据调查公司IDC预计2011年的数据总量将达到1.8万亿GB,对这些海量数据的分析已经成为一个非常重要且紧迫的需求. 随着互联网、移动互联网和物联网的发展,谁也无法否认,我们已经切实地迎来了一个海量数据的时代,数据调查公司IDC预计2011年的数据总量将达到1.8万亿GB,对这些海量数据的分析已经成为一个非常重要且紧迫的需求.

浅析信息系统架构的应用架构与数据架构

- - 企业架构 - ITeye博客
    信息系统架构包括了对于 应用架构和数据架构. 这里不再介绍具体的方法论,而是考虑如何在设计信息系统架构时有效地避免复杂性. 在应用系统层面将通过分层和配置的方式来简化应用系统,从而可以获得简单的架构. 在数据架构层面将通过分层主数据的思想来考虑我们如何来管理主数据. 企业从生产/采购计划开始,到生产/采购管理,以及现场制造的执行.

对数据库架构的再思考

- - 人月神话的BLOG
前面在谈PaaS的时候曾经谈到过共享数据库,私有数据库的问题,在这里再谈谈在多业务系统建设过程中的数据架构模式问题. 首先来看下传统的数据交换解决方案如下图:. 业务场景为单独构建的四个业务系统,在四个业务系统中SID数据为需要跨四个应用交互和共享的数据. 传统的做法则是对四个应用存在的SID库数据进行数据集成和交换,则后续的每一个业务系统中都有全部的共享基础数据,任何一个应用的SID库数据需要通过数据交换和集成同步四份.

谈大数据-架构维度

- - 人月神话的BLOG
本篇作为在构思大数据平台架构时候维度方面的简单点滴思考记录. 前面关于大数据平台架构的核心功能的时候谈到过,基本应该包括数据采集和集成,数据存储,数据处理,数据分析这些核心层面. 我在前面谈大数据平台的时候也谈到过平台不仅仅是云和分布式相关技术的引入,其架构一方面和传统的BI相似,但是更加重要的则是对外部应用涉及到大数据的应用场景的支撑和大数据平台本身的大数据服务能力的开放问题.

典型的大数据架构

- - 数据库 - ITeye博客
“任何数据架构由主要的四个逻辑组件组成:”. “我不认为这是一个大数据架构的蓝图. 但这样一个图能给你一个关于可能包含的组件的大致的想法. 然后对工程师让事情变得简单,你开始在每个等级上添加需求,约束,和服务等级协议(SLAS Service-level agreement). 一旦你有了关于事情该怎么看的某种想法,你开始建立它并发现你将用到的一些组件不能很好的在一起工作,或者根本没有办法达到这些服务等级协议.