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

前言

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

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

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

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,也可以流量切回,产生的影响也控制在较小的范围内。这样的系统在面对故障时,具有更强的容错能力和抗故障能力,才能确保系统整体运行的稳定性和可用性。

作者:全渠道生态 伍悦

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows深度学习环境----Cuda version 10.2 pytorch3d version 0.3.0

Requirements Python version 3.8.5Pytorch version: pytorch1.6.0 torchvision0.8.2 torchaudio0.7.0 cudatoolkit10.2.89pytorch3d version 0.3.0Cuda version 10.2 感觉readme文件里的不适配,跟pytorch官网不同 以前的 PyTorch 版本 |PyTorch的 # CUDA 10.2 c…

面板数据回归模型(二)房价的影响因素分析

1.数据来源 本文选择我国出一线城市房价均值、新一线城市房价均值、二线城市房价均值、货币供应量和利率。选取2002-2018年的数据,共17组数据,由于数据的自然对数变换不改变原有的协整关系,并能使其趋势线性化,消除时间序列中存在的异方差现象,所以对所有数据取其自然对数…

程序员简历程序员简历.pdf

你们在制作简历时,是不是基本只关注两件事:简历模板,还有基本信息的填写。 当你再次坐下来更新你的简历时,可能会发现自己不自觉地选择了那个“看起来最好看的模板”,填写基本信息,却没有深入思考如何使简历…

聊一下Redis实现分布式锁的8大坑

前两篇文章都在讲 Redis 的 5 大常用数据类型,以及典型的 10 大应用场景。 那么今天就来看看 Redis 实现分布式锁。 聊一聊Redis实现分布式锁的8大坑 Redis中5大常见数据类型用法 工作中Redis用的最多的10种场景 在分布式系统中,保证资源的互斥访问是…

新火种AI|拳打百度,脚踢阿里...令国产大模型危机感爆棚的kimi强势上线!

作者:小岩 编辑:冰冰 Sora大火后,生成式AI的高光时候终于轮到了国产大模型。2024年3月,Kimi智能助手在市场上掀起了一股狂热的热潮。 Kimi是由时下大火的AI初创公司——北京月之暗面科技有限公司所推出的一款智能助手&#xff…

自动化测试框架 - Unittest 学习笔记速查

文章目录 UnitTest 简介UnitTest 核心UnitTest 原理UnitTest 断言函数TestCase(用例)基本用法执行结果 TestFixture(夹具)方法级夹具类级夹具模块级夹具 TestSuite(套件)TestLoader(加载器&…

文件/目录的权限和归属

一.文件/目录的权限和归属的简要分析与概括 1.程序访问文件时的权限,取决于此程序的发起者 * 进程的发起者,同文件的属主:则应用文件属主权限 *进程的发起者,属于文件属组;则应用文件属组权限 *应用文件“其它”权…

【云贝教育】Oracle 19c OCP 083题库解析(71)

本文为云贝教育郭一军(微信:guoyJoe)原创,请尊重知识产权,转发请注明出处,不接受任何抄袭、演绎和未经注明出处的转载。 71、Which two are true about an Oracle gold image-based installation in Oracle…

指尖论文靠谱不 #学习方法#媒体#职场发展

指尖论文是当前市场上一款非常实用的论文写作、查重、降重工具。许多学生、研究人员和教师都已经开始使用它,因为它相当靠谱、方便、易用且高效。 首先,指尖论文的查重功能非常强大,能够及时准确地检测出文本中的相似内容,帮助用户…

源清天木生物科技带您抢先体验2024国际生物发酵展

参展企业介绍 优质的种质资源是生物产业的核心,也是农业的核心! 高效的选育优质的种质资源,是生物产业和农业最重要工作之一。 天木生物,致力于高效生物育种技术开发和特色生物育种装备开 发,并依托自主研发的技术和装…

揭秘强大的文件同步利器Rsycn

目录 引言 一、Rsycn基础介绍 (一)基本概念 (二)特性 (三)同步方式 (四)服务备份角色 (五)命令工具 (六)配置格式 &#xff…

[C++][C++11][六] -- [线程库]

目录 1.thread类的简单介绍2.线程对象的构造方法1.无参构造2.带参构造3.移动构造4.注意 3.thread提供的成员函数4.获取线程id5.线程函数的参数问题1.指针2.借助std::ref函数3.借助lambda表达式 6.join和detach1.join()2.detach() 7.[mutex](http://在C11中,Mutex总共…

OAuth2.0客户端和服务端Java实现

oauth2 引言 读了《设计模式之美》和《凤凰架构》架构安全篇之后,决定写一个OAuth2.0的认证流程的Demo,也算是一个阶段性的总结,具体原理实现见《凤凰架构》(架构安全设计篇)。 涉及到的源码可以从https://github.com/WeiXiao-Hyy/oauth2获…

智慧农场物联网系统:重塑农业的未来

随着科技的进步,物联网技术正在逐渐改变我们的生活。在农业领域,物联网系统也正在发挥着越来越重要的作用,为智慧农场的发展提供了新的可能。本文将深入探讨智慧农场物联网系统的优势、应用场景、技术实现以及未来发展趋势。 一、智慧农场物…

Java Web这一路走来

大部分Java应用都是Web或网络应用,MVC框架在Java框架中有着举足轻重的地位,一开始的Web应用并不现在这样子的,一步一步走来,每一步都经历了无数的血和泪的教训,以史为镜可以知兴替。 1. 草莽时代 早期的Java服务端技…

只有线上出了bug,老板们才知道测试的价值?

有同学说,测试没价值,我们测试团队刚被拆散了。 也有同学说,公司不重视测试,我觉得我们就是测试得太好了。哪天线上出个bug,老板们就知道测试的价值了。 还有人给测试同学规划职业发展路径,就是不做测试&…

蓝桥杯算法题:练功

【问题描述】 小明每天都要练功,练功中的重要一项是梅花桩。 小明练功的梅花桩排列成 n 行 m 列,相邻两行的距离为 1,相邻两列的距离也为 1。 小明站在第 1 行第 1 列上,他要走到第 n 行第 m 列上。小明已经练了一段时间&#xff…

【IC前端虚拟项目】spyglass lint环境组织与lint清理

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 和上个虚拟项目的lint清理环节一样,关于spyglass的lint清理功能与流程还是大家通过各种资料去学习下就好啦。和之前不同的事,这次的虚拟项目里我把流程封装为Makefile,…

中老年人购买需求不断上升,下一个电商新风口又会在哪里?

大家好,我是电商花花。 电商互联网时代,不再是年轻人的刚需。 中老年消费市场与日俱增,据统计,我国中老年人口已近10亿,占比巨大,且呈上升趋势。 50、60后的中老年人入网率已然在加快提升,相…

提示工程中的10个设计模式

我们可以将提示词定义为向大型语言模型(Large Language Model,LLM)提供的一个查询或一组指令,这些指令随后使模型能够维持一定程度的自定义或增强,以改进其功能并影响其输出。我们可以通过提供细节、规则和指导来引出更有针对性的输出&#x…