微服务架构下,如何通过弱依赖原则保障系统高可用?

前言

当我初次接触高可用这个概念的时候,对高可用的【少依赖原则】和【弱依赖原则】的边界感模糊,甚至有些“傻傻分不清楚”。这两个原则都关注降低模块之间的依赖关系,但它们之间的确存在某些差异。

那么,「少依赖原则」和「弱依赖原则」它们之间本质的区别究竟是啥?

少依赖原则和弱依赖原则都是旨在提高系统的可靠性和稳定性,但是它们之间的本质区别在于对依赖关系的管理控制

1.少依赖原则(Less Dependency Principle):这一原则强调系统设计阶段的模块独立性,目的是从源头上降低故障传播的风险,通过降低模块之间的耦合度,让各个模块独立完成特定功能,减少不必要的依赖。

2.弱依赖原则(Weak Dependency Principle):弱依赖原则关注的是在系统运行过程中,如何管理和控制模块之间的依赖关系,以便在某个模块出现故障时,其他模块仍能正常运行。通过实现弱依赖,使得系统具备更好的容错能力高可用性

当然,这两个原则并不是绝对的,两者之间有一定的关联性,我们可以根据系统的复杂性和实际需求,灵活地调整模块之间的依赖关系。在实际应用中,少依赖原则和弱依赖原则也可以相互配合,共同提高系统的高可用性。

一、基于弱依赖原则的架构策略

弱依赖原则:一定要依赖的,尽可能弱依赖,越弱越好 事物a强依赖事物b,一旦b出问题时,那么a也会出问题,一损俱损。 所以任何强依赖都要尽可能的转化成弱依赖,可以直接降低出问题的概率。

1、微服务架构

模块拆分:微服务架构将复杂的应用程序拆分为多个独立的、可组合的服务模块。每个服务具有明确的功能边界和职责,相互之间具备松耦合的特点。这样,当某个服务出现故障时,其他服务可以继续独立运行,保证系统的整体可用性。

独立部署:各个模块有自己独立的代码仓库,可以独立进行分组部署和升级,无需其他模块的配合。当某个服务发生故障或需要升级时,不会影响到其他服务的正常运行。特别针对黄金交易链路上的系统,有条件的话尽量考虑提供独立的数据资源(DB、redis),并进行垂直分组部署。

需要注意的是:部署隔离需要充足的部署资源,以及上下游配合,尽量提前做好该工作。当然,模块隔离必须建立在低耦合的基础上进行才有意义。如果组件之间的耦合关系千头万绪、混乱不堪,模块隔离只会让这种混乱雪上加霜。

2、异步通信

异步通信可以认为是在模块隔离基础上的进一步解耦,将物理上已经分割的模块之间的强依赖关系进一步削弱,使故障无法传播扩散,提高系统可用性。异步在架构上的实现手段主要是使用消息队列,当一个模块发生故障时,另一个模块可以继续处理任务,不会受到太大影响。

以我正在做的职能架构升级项目为例,其中创建员工账号的场景:新员工在PC页面提交创建账号的请求后,需要将员工信息进行数据持久化,并发送创建成功的短信和邮件给员工,此外,还需将员工信息同步给人资系统。

如果用微服务同步调用的方式,那么后续操作任何一个故障,都会导致业务处理失败,员工无法创建成功。我们通过使用消息队列的异步架构,新员工创建时发mq后就立即响应「创建成功」,后续的操作通过消费消息来完成,即使某个操作发生故障,后续再补偿也不会影响员工的创建流程。

图1.1 创建员工业务流程图

3、接口抽象

耦合度过高是软件设计的万恶之源,也是造成系统可用性问题的罪魁祸首。一个高度耦合的系统,可谓“牵一发而动全身”,任何微小的改动都可能会引发意想不到的bug和系统崩溃。连最基本的功能维护都已经勉为其难,更不用奢谈什么高可用了。

我们可以通过定义抽象的策略接口,这个抽象接口通常是从多个具有共同特征行为的类中抽象出来的,而具体实现类的指定都交给工厂类去完成,从而实现模块之间的松耦合。这样,当某个模块发生变化时,也不会影响其他模块的正常运行。接口抽象的方式,我将在后文的「实际场景分析」,针对具体案例展开详细的讨论。

4、故障切换与容错

设置完善的故障处理机制,包括故障检测、故障切换和故障恢复。当检测到故障时,系统可以快速切换到备用组件或恢复服务,保证系统的可用性。

数据分片:存储数据时,将其分布在多个存储节点上。当某个存储节点发生故障时,数据可以从其他节点恢复,从而提高数据的可用性和容错性。

读写分离:对于接受弱一致性的场景,将读操作分配给从数据库,写操作分配给主数据库,以提高系统的性能和稳定性,并支持主从切换;数据量大时,也可以进行分库分表处理。

兜底降级:当一个系统中的强依赖服务数量较少时,其整体基础稳定性便会越高。对于那些特殊数据依赖较多而逻辑依赖较少的系统,我们可以采取去依赖的架构设计策略。具体来说,就是将依赖服务数据持久化异构到自己的数据库,并通过异步方式进行同步更新维护,从而降低对其他系统的依赖程度,进一步提升系统的稳定性。然而,这种方法也存在一定的弊端:数据冗余可能导致在特定时间窗口内出现数据不一致的情况。

5、松耦合的业务逻辑

将业务逻辑进行解耦,使其相互独立。例如:目前线下店仓系统中专卖店、大商超、超级大店三个业态是公用一套代码,各个业态之间设计上是松耦合的,不同业态扩展点的实现相互隔离,这样当某个业务逻辑出现故障时,其他业务逻辑可以继续运行,降低故障影响范围。

二、实际场景分析

1、case 1:中间件弱依赖

i、消息队列弱依赖

日常开发中经常会遇到分布式事务的场景,同一个事务内涉及「RPC调用、写DB、对外消息发送」等一系列原子操作,可能存在某一个环节请求异常的情况,为了保证事务的最终一致性,需要采用失败重试策略。对一个简单的应用流程来说,抛出异常业务中断回滚即可;但是对于复杂业务流程是不可行的,发生请求异常时,上游应用可能已经执行完毕,尤其是多个异步流程组合一个整体流程的场景,其他前置的流程可能已经执行完毕,无法回滚。

以店仓生产缺货取消单据的场景为例,店仓拣货环节,若商品全部缺货、或者用户选择“缺货取消订单”的发货策略时,此时拣货缺货,会调用下游接口取消订单。经常会出现前置操作(如RPC调用、写DB等)完成后,调用订单取消接口失败或者异常的情况。调用失败会触发UMP报警,需人工干预进行处理。

图2.1 生产缺货取消回写opc流程

为了避免以上问题,保障数据的最终一致性,初期优化采用mq自产自消的方式,进行重试。

图2.2 JMQ解决回调取消接口失败

这样一来,业务系统的稳定性与JMQ中间件的稳定性就强关联了,自然对JMQ的稳定性有较高要求。为了降低对JMQ的强依赖,保证业务的顺利执行,通过技术手段提升用户体验,减轻研发值班人员压力,最终形成了任务重试工具。

其核心思想是将分布式事务拆分成本地事务进行处理,具体实现方式是:将任务落库,保证业务操作表与纯任务表在同一个数据库,通过数据库事务保证业务操作与任务持久化的强一致性。在一定程度上,将业务操作与中间件依赖解耦。

采用回调函数的机制实现调用者和底层驱动的解耦,提升了组件的灵活性,对业务侵入性小。

图2.3 任务重试组件工作流程

ii、数据库弱依赖

第二个涉及中间件弱依赖的场景是数据库弱依赖,在日常开发中,数据库操作异常的情况屡见不鲜,比如网络链路问题、慢SQL导致的性能下降,以及引发的故障等。这些问题往往会导致交易黄金链路在短时间内无法正常工作,给业务带来不小的损失。为了应对这些情况,我们考虑引入灾备机制,以确保在异常情况下仍能维持较高的交易成功率,保障订单履约时效。

该方案的核心思路是:在DB操作出现故障的时间段内,通过其他存储介质(如redis)临时存储数据。然后,通过MQ异步补偿还原DB操作,从而保障数据的最终一致性。

图2.4 数据灾备方案

这个方案显著提升了我们应对数据库操作异常的处理能力,确保了黄金交易链路的平稳运行。通过实施灾备策略,我们在确保数据最终一致性的同时,也有效减小了故障对业务的影响。

2、case 2:依赖倒置解耦业务逻辑

图2.5 定义抽象接口进行解耦

i、背景

代码层面的依赖优化案例,是基于依赖倒置的设计原则,将业务模块进行解耦。在需求迭代的过程中,我们经常为了图方便,将具体类直接依赖于具体类,也就是所谓的高层模块依赖于低层模块。但是这样是极其不利于扩展的,随着新功能的不断追加,系统的功能会越来越臃肿,核心功能也会越来越模糊,这种情况下,系统的高可用性会受到影响。

请看下面这个案例:历史代码中,执行发货单取消逻辑十分复杂,不同类型、不同来源的单据在不同生产环节取消处理逻辑存在差异,这里的doCandel方法就是高层模块,而调用取消接口和发送取消消息是低层模块,这是一个典型的高层模块依赖于低层模块的编码形式。

ii、优化前的实现方式

以下一个代码片段是系统中的历史代码负债,耦合性强,可读性和可扩展性差。

图2.6 历史代码

iii、优化后的实现方式

弱依赖原则强调应该尽量让模块之间的依赖关系变得弱化。这意味着模块之间的相互作用应该尽量简单,避免复杂的依赖关系。我们优化的核心思想是采用 工厂模式+模板模式 去抽象接口,实现不同环节单据取消后续处理逻辑差异。

a.首先,我们通过定义一个抽象的策略类AbstractDoCancelNodeStrategy,将取消单据后的核心流程进行拆解,最终将拆解的四个步骤定义为四个抽象方法。

图2.7 抽象策略类定义

b.然后,我们创建了4个具体实现策略类,分别用于处理不同环节取消单据的逻辑。主要是提供相同行为的不同实现,业务上可以根据不同条件选择进入不同的实现类。

图2.8 不同策略实现

c.其次,创建获取不同生产环节取消单据的策略工厂:采用启动时加载策略的方式,在项目启动时,把接口的实现类的实例放在Map里,系统运行过程中,可以通过取消节点对应的key,找到这个实现类的标识,进行相应的逻辑处理。

图2.9 策略工厂

d.这样一来,高层模块可以依赖于这个策略类,而不是具体的策略实现。这样,当业务需求发生变更,需要对新的单据类型进行取消消息广播,只需实现一个新的处理策略类,而无需修改高层模块的代码。

图2.10 高层模块代码

这样优化后,相当于把高层模块和具体的RPC调用的底层模块逻辑进行了间接解耦。并且提供了对开闭原则的完美支持,可以在不修改主流程代码的情况下,灵活增加新算法。总之,以抽象为基准比以细节为基准搭建起来的架构要稳定得多,因此咱们在日常开发中,要多尝试面相接口编程,采用先顶层设计再细节的去设计代码结构。

三、强弱依赖治理

服务依赖是决定系统复杂度的一个重要因素,随着业务的不断迭代,服务依赖可能会变得越来越复杂,导致系统难以维护和扩展。在没有明确强弱依赖的前提下,我们很难进行熔断、降级、限流的相关操作,也不能有效的对系统进行相关优化改造、持续推进系统稳定性提升。因此,服务依赖治理变得至关重要。我们需要定期检查我们的依赖模型是否合理,识别不合理的依赖将其合理化。具体的治理流程包括:

1、依赖标记:通过人工梳理代码的形式,对系统核心链路上的所有依赖进行梳理,分析并标注依赖关系及强弱。

2、强弱依赖验证:采用混沌工程等方式模拟链路故障,核心思路就是不断给系统“找麻烦”来验证系统能力,模拟某个依赖服务出现故障的场景,从而验证人工标注的有效性。

3、依赖治理:依赖治理的目标体现在如下几个方面:

•筛选出那些不是真的强依赖的部分,将其转换为弱依赖,实现强依赖最少化。

•对强依赖进行解耦合,建立核心链路的降级预案,并对预案持续保活。

•对弱依赖做合理的异常捕获逻辑,配置合理的超时、熔断以及限流。针对具体的业务场景,以场景为最小单位,编写止损可控的兜底逻辑,并配置相应的动态切换开关,当异常发生时,可一键切换至兜底逻辑。

•弱依赖支持平滑停用,支持突发场景下舍军保帅。

四、结语

当然,除了通过以上措施去构建遵循弱依赖原则的高可用系统,还有一些高可用的架构方案:

比如,在架构设计时,我们还需要考虑到不同层级的异常监控(业务层、应用层、中间件层、基础层),数据采集包含日志、埋点、链路追踪等,数据告警通过电话、短信、邮件、京me等方式通知到值班人员。通过建立完善的监控体系,实时收集系统运行状态,并进行预警。这样,当系统出现潜在故障时,可以及时发现并采取措施进行修复。

此外,对于改造量比较大的新业务上线后,可以通过ducc控制灰度切流的方式,降低软件编码错误带来的影响。观察没有问题,再全量切流,保证即使程序有Bug,也可以流量切回,产生的影响也控制在较小的范围内。这样的系统在面对故障时,具有更强的容错能力和抗故障能力,才能确保系统整体运行的稳定性和可用性。

作者:全渠道生态 伍悦

来源:京东零售技术 转载请注明来源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/301503.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

15.队列集

1.简介 在使用队列进行任务之间的“沟通交流”时,一个队列只允许任务间传递的消息为同一种数据类型,如果需要在任务间传递不同数据类型的消息时,那么就可以使用队列集。FreeRTOS提供的队列集功能可以对多个队列进行“监听”,只要…

Unity类银河恶魔城学习记录12-4 p126 Item Tooltip源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili UI.cs using System.Collections; using System.Collections.Generic; usi…

xilinx AXI CAN驱动开发

CAN收发方案有很多,常见的解决方案通过是采用CAN收发芯片,例如最常用的SJA1000,xilinx直接将CAN协议栈用纯逻辑实现,AXI CAN是其中一种; 通过这种方式硬件上只需外接一个PHY芯片即可 上图加了一个电平转换芯片 软件设计方面&…

Scala大数据开发

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl Scala简述 在此,简要介绍 Scala 的基本信息和情况。 Scala释义 Scala 源自于英语单词scalable,表示可伸缩的、可扩展的含义。 Scala作者 Scala编…

【考研数学】张宇《1000题》刷不动,做不下来怎么办❓

学长肯定是用着效果不错才给你推荐的,但是习题册有很多,各自有不同的风格,1000题适不适合你的情况是你要考虑的点。 选书还是要结合自身的情况,如果当前用着不错的话,继续完全没有问题,核心就是要从自身的…

IT外包服务:企业数据资产化加速利器

随着数字化时代的兴起,数据成为企业最为重要的资源之一。数据驱动创新对于企业的竞争力和可持续发展至关重要。在这一进程中,IT外包服务发挥着关键作用,加速企业数据资产化进程,为企业提供了重要支持。 首先,IT外包服务…

【学习心得】Python中的queue模块使用

一、Queue模块的知识点思维导图 二、Queue模块常用函数介绍 queue模块是内置的,不需要安装直接导入就可以了。 (1)创建一个Queue对象 import queue# 创建一个队列实例 q queue.Queue(maxsize20) # 可选参数,默认为无限大&am…

基于springboot实现教师人事档案管理系统项目【项目源码+论文说明】

基于springboot实现IT技术交流和分享平台系统演示 摘要 我国科学技术的不断发展,计算机的应用日渐成熟,其强大的功能给人们留下深刻的印象,它已经应用到了人类社会的各个层次的领域,发挥着重要的不可替换的作用。信息管理作为计算…

【chrome扩展】简 Tab (SimpTab)‘每日一句名言’样式

背景:最初参考“每日诗词”发现总是那几句,可以更换API接口完成“每日一句名言” 声明:本人不会ajax及ccs样式,非专业人士,借助CHATGPT代码生成完成。请友善交流。 每一句名言API: "https://api.xygeng.cn/open…

libVLC 提取视频帧

在前面的文章中,我们使用libvlc_media_player_set_hwnd设置了视频的显示的窗口。 libvlc_media_player_set_hwnd(vlc_mediaPlayer, (void *)ui.widgetShow->winId()); 如果我们想要提取每一帧数据,将数据保存到本地,该如何操作呢&#x…

OPC UA遇见chatGPT

最近opc 基金会将召开一个会议,主题是”OPC UA meets IT“。由此可见,工业自动化行业也开始研究和评估chatGPT带来的影响了。 本文谈谈本人对OPC UA 与chatGPT结合的初步实验和思考。 构建OPC UA 信息模型 chatGPT 的确非常强大了,使用自然…

用户登录时md5加密源码解析

首先,在登录的时候,将页面提交的密码password加密处理,即password DigestUtils.md5DigestAsHex(password.getBytes()); 接着按ctrl鼠标左键,进入md5DigestAsHex函数中进行查看: 可以发现,md5DigestAsHex函…

Mysql底层原理六:InnoDB 数据页结构

1.行格式 1.1 Compact行格式 1.1.1 示意图 1.1.2 准备一下 1)建表 mysql> CREATE TABLE record_format_demo (-> c1 VARCHAR(10),-> c2 VARCHAR(10) NOT NULL,-> c3 CHAR(10),-> c4 VARCHAR(10)-> ) CHARSETascii ROW_FORMATCOM…

积木-蓝桥每日真题

0积木 - 蓝桥云课 (lanqiao.cn) 题目描述 小明用积木搭了一个城堡。 为了方便,小明在搭的时候用的是一样大小的正方体积木,搭在了一个n行m列的方格图上,每个积木正好占据方格图的一个小方格。 当然,小明的城堡并不是平面的&#x…

Discord注册教程:Discord刚注册就被封怎么办?附申诉教程!

Discord如今在海外社交媒体平台中迅速崛起,许多社交媒体营销人员也纷纷利用其社群特性进行推广,Discord注册也就成为社媒营销人员必经之路。然而,很多人注册Discord账号时常常会想:“在国内使用Discord会封号吗?”事实…

个推助力小米汽车APP实现智能用户触达,打造智能出行新体验

4月3日,小米SU7首批交付仪式在北京亦庄的小米汽车工厂总装车间举行,全国28城交付中心也同步开启首批交付。随着小米SU7系列汽车的正式发售和交付,小米汽车APP迎来了用户体量的爆发式增长。 小米汽车APP是小米汽车官方推出的手机应用&#xff…

数据库面试题

文章目录 事务未提交和提交事务的4大特征事务的隔离级别并发事务的问题MVVCundo log 和 redo log记录的隐藏字段readview(读视图) 事务未提交和提交 事务未提交时数据存在于数据库系统的缓存中,而在事务提交后,数据才会被写入到磁…

多叉树题目:子树中标签相同的结点数

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题:子树中标签相同的结点数 出处:1519. 子树中标签相同的结点数 难度 5 级 题目描述 要求 给你一个树(即一个连通的无向无环图…

【Canvas技法】图解绘制圆弧的重要函数 arc(x,y,r,startAngle,endAngle,clockWise)

【一图释疑】 【绘制上图用代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>Html5/Canvas中绘制圆弧的重要函数 arc(x,y,r,startA…

虹科Pico汽车示波器 | 免拆诊断案例 | 2019款别克GL8豪华商务车前照灯水平调节故障

一、故障现象 一辆2019款别克GL8豪华商务车&#xff0c;搭载LTG发动机&#xff0c;累计行驶里程约为10.7万km。车主反映&#xff0c;车辆行驶过程中组合仪表提示前照灯水平调节故障。 二、故障诊断 接车后试车&#xff0c;起动发动机&#xff0c;组合仪表上提示“前照灯水平…