关于机房监控:看完这篇异地多活的改造我决定和架构师battle一下|得物技术
简述异地多活的概念以及为什么要做异地多活这里就不进行概述了。概念性的很多,像什么同城双活、两地三核心、三地五核心等等概念。如果有对这些容灾架构模式感兴趣的能够浏览下这篇文章进行理解:《浅谈业务级灾备的架构模式》。 浏览本篇文章之前,咱们先明确一下背景,这样大家后续在看的时候就不会产生困惑。 1.1 机房划分得物多活革新一期目前有两个机房,别离是机房A和机房B。文章中大部分图中都会有标识,这就阐明是两个不同的机房。 A机房咱们定义为核心机房,也就是多活上线之前正在应用的机房。如果说到核心机房那指的就是A机房。另一个B机房,在形容的时候可能会说成单元机房,那指的就是B机房。 1.2 单元化单元化简略点咱们间接就能够认为是一个机房,在这个单元内可能实现业务的闭环。比如说用户进入APP,浏览商品,抉择商品确认订单,下单,领取,查看订单信息,这整个流程都在一个单元中可能实现,并且数据也是存储在这个单元外面。 做单元化无非就两个起因,容灾和进步零碎并发能力。然而也得思考机房建设的规模和技术,硬件等投入的老本。具体的就不多讲了,大家大略了解了就行。 2. 革新点理解革新点之前咱们先来看下目前单机房的现状是什么样子,能力更好的帮忙大家去了解为什么要做这些革新。 如上图所示,客户端的申请进来会先到SLB(负载平衡),而后到咱们外部的网关,通过网关再散发到具体的业务服务。业务服务会依赖Redis, Mysql, MQ, Nacos等中间件。 既然做异地多活,那么必然是在不同地区有不同的机房,比方核心机房,单元机房。所以咱们要实现的成果如下图所示: 大家看下面这张图可能会感觉很简略,其实也就是一些罕用的中间件,再多一个机房部署罢了,这有什么难度。如果你这样想我只能说一句:格局小了啊。 2.1 流量调度用户的申请,从客户端收回,这个用户的申请该到哪个机房,这是咱们要革新的第一个点。 没做多活之前,域名会解析到一个机房内,做了多活后,域名会随机解析到不同的机房中。如果依照这种随机的形式是必定有问题的,对于服务的调用是无所谓的,因为没有状态。然而服务外部依赖的存储是有状态的呀。 咱们是电商业务,用户在核心机房下了一个单,而后跳转到订单详情,这个时候申请到了单元机房,底层数据同步有提早,一拜访报个错:订单不存在。 用户当场就懵了,钱都付了,订单没了。 所以针对同一个用户,尽可能在一个机房内实现业务闭环。为了解决流量调度的问题,咱们基于OpenResty二次开发出了DLB流量网关,DLB会对接多活控制中心,可能晓得以后拜访的用户是属于哪个机房,如果用户不属于以后机房,DLB会间接将申请路由到该用户所属机房内的DLB。 如果每次都随机到固定的机房,再通过DLB去校对,必然会存在跨机房申请,耗时加长。所以在这块咱们也是联合客户端做了一些优化,在DLB校对申请后,咱们会将用户对应的机房IP间接通过Header响应给客户端。这样下次申请的时候,客户端就能够间接通过这个IP拜访。 如果用户以后拜访的机房挂了,客户端须要降级成之前的域名拜访形式,通过DNS解析到存活的机房。 2.2 RPC框架当用户的申请达到了单元机房内,实践上后续所有的操作都是在单元机房实现。后面咱们也提到了,用户的申请尽量在一个机房内实现闭环,只是尽量,没有说全副。 这是因为有的业务场景不适宜划分单元,比方库存扣减。所以在咱们的划分外面,有一个机房是核心机房,那些不做多活的业务只会部署在核心机房外面,那么库存扣减的时候就须要跨机房调用。 申请在核心机房,怎么晓得单元机房的服务信息?所以咱们的注册核心(Nacos)要做双向同步,这样能力拿到所有机房的服务信息。 当咱们的注册信息采纳双向复制后,对于核心服务,间接跨机房调用。对于单元服务会存在多个机房的服务信息,如果不进行管制,则会呈现调用其余机房的状况,所以RPC框架要进行革新。 2.2.1 定义路由类型默认路由申请到核心机房,会优先调用核心机房内的服务,如果核心机房无此服务,则调用单元机房的服务,如果单元机房没有此服务则间接报错。 单元路由申请到单元机房,那么阐明此用户的流量规定是在单元机房,接下来所有的RPC调用都只会调用单元机房内的服务,没有服务则报错。 核心路由申请到单元机房,那么间接调用核心机房的服务,核心机房没有服务则报错。申请到核心机房,那么就本机房调用。 2.2.2 业务革新业务方须要对本人的接口(Java interface)进行标记是什么类型,通过@HARoute加在接口下面。标记实现后,在Dubbo接口进行注册的时候,会把路由类型放入到这个接口的元数据外面,在Nacos后盾能够查看。前面通过RPC调用接口外部所有的办法都会依照标记类型进行路由。 如果标记为单元路由,目前咱们外部的标准是办法的第一个参数为小写的long buyerId,RPC在路由的时候会依据这个值判断用户所在的机房。 路由逻辑如下: 2.2.3 革新过程接口复制一份,命名为UnitApi,第一个参数加long buyerId。在新接口的实现外面调用老接口,新旧接口共存。将UnitApi公布上线,此时没有流量。业务方须要降级其余域的API包,将老接口的调用切换为新的UnitApi,此处减少开关管制。上线后,通过开关管制调用走UnitApi,有问题可敞开开关。下线老的API,实现切换。2.2.4 遇到的问题2.2.4.1 其余场景切单元接口除了RPC间接调用的接口,还有一大部分是通过Dubbo泛化过去的,这块在上线后也须要将流量切到UnitApi,等老接口没有申请量之后能力下线。 2.2.4.2 接口分类接口进行分类,之前没有多活的束缚,一个Java interface中的办法可能各种各样,如果当初你的interface为单元路由,那么外面的办法第一个参数都必须加buyerId,其余没有buyerId场景的办法要挪进来。 2.2.4.3 业务层面调整业务层面调整,比方之前查问订单只须要一个订单号,然而当初须要buyerId进行路由,所以接入这个接口的上游都须要调整。 2.3 数据库申请顺利的达到了服务层,接下来要跟数据库打交道了。数据库咱们定义了不同的类型,定义如下: 单元化此库为单元库,会同时在两个机房部署,每个机房都有残缺的数据,数据采纳双向同步。 中心化此库为核心库,只会在核心机房部署。 核心单元化此库为核心单元库,会同时在两个机房部署,核心能够读写,其余机房只能读。核心写数据后单向复制到另一个机房。 2.3.1 代理中间件目前各个业务方用的都是客户端模式的Sharding中间件,每个业务方的版本还不统一。在多活切流的过程中须要对数据库禁写来保障业务数据的准确性,如果没有对立的中间件,这将是一件很麻烦的事件。 所以咱们通过对ShardingSphere进行深度定制,二次开发数据库代理中间件 彩虹桥。各业务方须要接入彩虹桥来替换之前的Sharding形式。在切换过程中,如何保障稳固平滑迁徙,出问题如何疾速复原,咱们也有一套胜利的实际,大家能够看下我之前写的这篇文章《客户端分片到Proxy分片,如丝般顺滑的安稳迁徙》,外面有实现形式。 2.3.2 分布式ID单元化的库,数据层面会做双向同步复制操作。如果间接用表的自增ID则会呈现上面的抵触问题: 这个问题能够通过设置不同机房id有不同的自增步长来解决,但比拟麻烦,后续可能会减少更多的机房。咱们采纳了一种一劳永逸的形式,接入全局惟一的分布式ID来防止主键的抵触。 2.3.2.1 客户端接入目前,接入分布式ID有两种形式,一种是利用内通过基础架构提供的jar包接入,具体逻辑如下: ...