当前位置: 首页 > news >正文

【总结】实际业务场景中锁、事务、异常如何考虑使用?

文章目录

  • 锁处理
    • 目的:
    • 考虑锁控制思路:
    • 生命周期接口并发控制解决方案:
    • 测试锁是否生效:
      • 模拟多线程并发场景的2种方式:
  • 事务处理
    • 目的:
    • 考虑事务控制思路:
    • 解决方案:
  • 总结

锁处理

目的:

为了避免接口被并发访问,导致业务数据校验(是否正式已存在、是否审批中)被迫通过,导致创建多个流程单据去新增业务数据,造成正式业务数据重复;

*代表采用应用的方案。

应用分布式锁

考虑锁控制思路:

1.分析并发场景(相同应用、不同应用访问)、给出解决方案

如:客户上市接口

并发访问场景:

  1. 同一外部应用系统,同一个用户创建一个客户,连续快速点击2次(2个线程同时访问同一接口,数据已在审批中代码校验被迫通过)
    解决办法:clientID 外部应用标识

  2. 同一外部应用系统,不同用户同时创建同一个客户(2个线程同时访问同一接口,数据已在审批中代码校验被迫通过)

    解决办法:clientID 外部应用标识

  3. 不同外部应用,同时创建相同客户标识的客户(2个线程同时访问同一接口,数据已在审批中代码校验被迫通过)

    解决办法:客户唯一识别标识

2.取用最适配所有场景方案

客户上市最终方案:通过客户唯一识别标识来加锁处理(身份证号+纳税人识别号)

即能控制并发场景1、2,又能控制并发场景3

生命周期接口并发控制解决方案:

1. 客户上市接口并发控制:
方案1:(粗粒度)clientId控制同一外部应用并发访问,缺点:不能控制不同应用并发创建同一个客户时场景
方案2*:(细粒度)不同外部应用,同一个客户不能并发创建(同一个客户识别标识:身份证号 + 纳税人识别号)

2. 客户变更(基础变更、客户调整)接口并发控制:
方案1:(粗粒度)clientId控制同一外部应用并发访问,缺点:不能控制不同应用并发更新同一个客户时场景
方案2*:(细粒度)同一个客户不能并发进行基础信息变更、客户调整,缺点:牺牲了同一个客户实际场景中是可以同时进行基础变更、客户调整操作,造成的结果就是A客户变更、B客户调整,AB同时发起,线程快的会先进入方法,后进入的被锁住,失败访问,需要等待1-2s后进行访问

3. 客户扩充、冻结解冻、地址变更接口并发控制:
方案1*:(粗粒度)clientId控制外部应用并发访问,缺点:不能控制不同应用并发扩充、冻结解冻同一个客户时场景,回调处理新增数据时,先去校验扩充插入条目是否存在,弥补了并发导致重复扩充同一业务范围条目这种场景
方案2:(细粒度)由于是批量客户处理,无法通过客户编码等业务字段来限制并发访问

测试锁是否生效:

// 这里是对外部应用加锁,控制同一应用并发访问改接口方法
@Lock4j(keys = {"#request.clientId"})
@Override
public ServiceResponse testLock(ExtendsCustomerBusinessScopeTaskDto request) {System.out.println("开始处理: " + request.getClientId() + " - " + LocalDateTime.now());try {// 模拟处理时间Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("处理完成: " + request.getClientId() + " - " + LocalDateTime.now());return ServiceResponse.success();
}/**
预期结果1:如果加锁无效,开始处理和结束处理之间间隔2s,但同一clientid调用时间不联系,几乎是同时进行的,类似这样:开始处理: client001 - 2024-07-13T14:31:58.451154700处理完成: client001 - 2024-07-13T14:32:00.4599192002024-07-13 14:32:00.549  INFO 21864 ---[tag 客户端ID:client001-]开始处理: client001 - 2024-07-13T14:32:00.550816300处理完成: client001 - 2024-07-13T14:32:02.5556544002024-07-13 14:32:02.636  INFO 21864 ---[tag 客户端ID:client001-]开始处理: client001 - 2024-07-13T14:32:02.636094400处理完成: client001 - 2024-07-13T14:32:04.6392284002024-07-13 14:32:04.700  INFO 21864 ---[tag 客户端ID:client001-]开始处理: client001 - 2024-07-13T14:32:04.701772900处理完成: client001 - 2024-07-13T14:32:06.7171383002024-07-13 14:32:06.799  INFO 21864 ---[tag 客户端ID:client001-]开始处理: client001 - 2024-07-13T14:32:06.799141500处理完成: client001 - 2024-07-13T14:32:08.803225预期结果2:如果加锁有效,开始处理和结束处理之间间隔2s,且同一clientid调用时间是连续的,类似这样开始处理: client001 - 2024-07-13T14:23:48.889049800处理完成: client001 - 2024-07-13T14:23:50.8946381002024-07-13 14:23:50.984  INFO 16064 --- [tag 客户端ID:client001-]开始处理: client001 - 2024-07-13T14:23:50.992565800处理完成: client001 - 2024-07-13T14:23:52.9942138002024-07-13 14:23:53.088  INFO 16064 --- [tag 客户端ID:client001-]开始处理: client001 - 2024-07-13T14:23:53.095387900处理完成: client001 - 2024-07-13T14:23:55.0962475002024-07-13 14:23:55.170  INFO 16064 --- [tag 客户端ID:client001-]开始处理: client001 - 2024-07-13T14:23:55.178764600处理完成: client001 - 2024-07-13T14:23:57.1841779002024-07-13 14:23:57.269  INFO 16064 --- [tag 客户端ID:client001-]开始处理: client001 - 2024-07-13T14:23:57.335996600处理完成: client001 - 2024-07-13T14:23:59.341987300实际测试结果:预期结果2,加锁有效
*/

模拟多线程并发场景的2种方式:

方式1:postman多线程并发测试

image-20240713143615934

方式2:代码对任务多线程同时调用

// 应用启动main方法
@EnableOpenApi("matrix-mdm")
@EnableMatrixFeignClients
@EnableDiscoveryClient
@EnableMatrixResourceServer
@SpringBootApplication
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);// lock4j分布式锁控制方法并发有效前提:线程必须通过Spring管理的bean来访问方法,否则加锁无效TnCustomerManagementService tnCustomerManagementServiceImpl = (TnCustomerManagementService) SpringReflectUtils.getBean("tnCustomerManagementServiceImpl");ExtendsCustomerBusinessScopeTaskDto request = new ExtendsCustomerBusinessScopeTaskDto();request.setClientId("client001");Runnable task = () -> tnCustomerManagementServiceImpl.customerBusinessScopeExtendsTask(request);new Thread(task).start();new Thread(task).start();new Thread(task).start();new Thread(task).start();new Thread(task).start();}
}// spring管理的bean的内部方// 请求超时时间设置为30s, 多线程并发时,第一个线程拿到锁,执行中,第二个线程获取不到锁,直接报错,reqire fail, 想要看到完整5个线程并发控制后,串行打印出梳理时间信息,就需要放大点请求超时时间 >= 5个并发线程处理时间总和 @Lock4j(keys = {"#request.clientId"}, acquireTimeout = 30000L)@Overridepublic ServiceResponse customerBusinessScopeExtendsTask(ExtendsCustomerBusinessScopeTaskDto request) {System.out.println("Thread " + Thread.currentThread().getName() + "开始处理: " + request.getClientId() + " - " + LocalDateTime.now());try {// 模拟处理时间Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread " + Thread.currentThread().getName() + "处理完成: " + request.getClientId() + " - " + LocalDateTime.now());return ServiceResponse.success();}

事务处理

目的:

为了避免多条数据库SQL操作中间异常时,数据已被插入正式表,但是业务已经执行不下去,再次执行时,就会发生数据已存在的代码校验死锁卡住。

考虑事务控制思路:

  1. 多个SQL操作要么都发生,要么都不发生
  2. 不数据回滚导致业务重复校验死锁、业务不能操作下去

解决方案:

异常回滚处理场景:

  1. 扩充、地址新增:

    插入正式表新条目时,根据数据条目唯一标识校验是否存在(弥补客户扩充是批量扩充接口,导致锁控制粒度只能控制在 相同应用 clientId 中,不同 clientId 并发扩充同一个业务范围数据场景控制不住),

    如果存在,(原因:可能历史数据已存在、锁未并发控制不同ClientId导致审批中数据代码校验失效),直接抛出异常,将之前可能执行的新增、更新操作回滚,

    这时候操作人员看到报错,去检查处理异常已存在数据后(检查记录被创建原因,处理删除数据),再次点击时业务正常进行下去

  2. 扩充、地址新增、变更:

    执行多条SQL操作方法时,中间出现字段过长等导致插入失败时,抛出异常,事务回滚,返回操作人员看到报错,去检查处理异常(放大字段)后,继续执行下去

总结

锁控制、事务控制、异常处理,必须分析场景,再采用最优处理方案

  1. sychronized 同步锁在分布式系统中不生效,需要用到分布式锁

  2. 加锁时,需要考虑锁的粒度。

    判断条件:如果接口访问场景并发量大,考虑细粒度锁控制;如果接口访问场景并发量小,考虑粗粒度锁控制;

    粗粒度锁:如访问接口的外部应用

    细粒度锁:如具体接口业务功能数据判定(客户上市:身份证号、纳税人识别号来锁定唯一客户,控制类似重复点击、同一时刻创建同一客户并发访问接口,导致同一客户被创建审批数据)

  3. 事务控制,需要考虑到多SQL操作异常、业务数据重复异常时等,进行回滚操作;异常信息暴漏到前端,方便开发运维;


若有错误,烦请评论指正!!!

相关文章:

【总结】实际业务场景中锁、事务、异常如何考虑使用?

文章目录 锁处理目的:考虑锁控制思路:生命周期接口并发控制解决方案:测试锁是否生效:模拟多线程并发场景的2种方式: 事务处理目的:考虑事务控制思路:解决方案: 总结 锁处理 目的&am…...

Pytorch使用Dataset加载数据

1、前言: 在阅读之前,需要配置好对应pytorch版本。 对于一般学习,使用cpu版本的即可。参考教程点我 导入pytorch包,使用如下命令即可。 import torch # 注意虽然叫pytorch,但是在引用时是引用torch2、神经网络获取…...

【nginx】nginx的优点

目录 一、高性能1.1 高并发处理1.2 低内存消耗1.3 快速响应 二、高扩展性2.1 模块化设计2.2 动态模块扩展 三、高可靠性3.1 核心框架稳定3.2 进程管理3.3 负载均衡与健康检查3.4 热部署 四、功能丰富4.1 反向代理4.2 HTTP缓存4.3 安全功能 五、易于配置和管理5.1 配置文件简单5…...

K8S ingress 初体验 - ingress-ngnix 的安装与使用

准备环境 先把 google 的vm 跑起来… gatemanMoreFine-S500:~/projects/coding/k8s-s/service-case/cloud-user$ kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane,master 124d v1.23.6 k8s-no…...

qt 获取父控件

在 Qt 中,你可以通过调用 QWidget 的 parentWidget() 方法来获取一个控件的父控件。这个方法会返回一个指向父控件的指针,如果该控件没有父控件,则返回 nullptr。 以下是一个简单的示例,展示了如何获取一个按钮的父控件&#xff…...

flask基础配置详情

前言 一个简单的应用 app Flask(__name__) app.route("/") def hello_world():return "<p>Hello,World!"运行Flask应用 #flask命令运行flask --app hello run#使用Python命令进行运行python -m flask # 作为一个捷径&#xff0c;如果文件名为 app…...

单相整流-TI视频课笔记

目录 1、单相半波整流 1.1、单相半波----电容滤波---超轻负载 1.2、单相半波----电容滤波---轻负载 1.3、单相半波----电容滤波---重负载 2、全波整流 2.1、全波整流的仿真 2.2、半波与全波滤波的对比 3、全桥整流电路 3.1、全波和全桥整流对比 3.2、半波全波和全桥…...

用GPT 4o提高效率

**GPT-4o可以通过提高编程效率、优化工作流程、增强文档管理和知识分享等多方面帮助用户提升工作效率**。具体如下&#xff1a; 1. **代码生成与优化** - **快速原型开发**&#xff1a;程序员可以通过向GPT-4o描述需求或功能来生成初步的代码框架或关键函数&#xff0c;从而节省…...

20240711每日消息队列-------------MQ消息的积压的折磨

目标 解决MQ消息的积压 背景 菜馆系统----------- 系统读取消息&#xff0c;处理业务逻辑&#xff0c;持久化订单和菜品数据&#xff0c;然后将其显示在菜品管理客户端上。 最初我们的用户基数很小&#xff0c;上线后的一段时间内&#xff0c;MQ消息通信还算顺利。 随着用户…...

推荐一个比 Jenkins 使用更简单的项目构建和部署工具

最近发现了一个比 Jenkins 使用更简单的项目构建和部署工具&#xff0c;完全可以满足个人以及一些小企业的需求&#xff0c;分享一下。 项目介绍 Jpom 是一款 Java 开发的简单轻量的低侵入式在线构建、自动部署、日常运维、项目监控软件。 日常开发中&#xff0c;Jpom 可以解…...

java 在pdf中根据关键字位置插入图片(公章、签名等)

java 在pdf中根据关键字位置插入图片&#xff08;公章、签名等&#xff09; 1.使用依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.1.12</version><type>pom</type>…...

施耐德EOCR系列电机保护器全面升级后無端子型

一、施耐德数码型产品升级背景 施耐德电气作为一家全球领先的能源管理和自动化解决方案提供商&#xff0c;其产品线包括各种电动机保护器等数码型产品。随着技术的不断发展和市场需求的变化&#xff0c;施耐德会对其产品进行定期升级和优化。在升级过程中&#xff0c;产品的设…...

27.数码管的驱动,使用74HC595移位寄存器芯片

PS&#xff1a;升腾A7pro系列FPGA没有数码管外设&#xff0c;因此以AC620FPGA为例展开实验。 &#xff08;1&#xff09;共阳极数码管和共阴极数码管示意图&#xff1a; AC620中的数码管属于共阳极数码管&#xff0c;段选端口(dp,g,f,e,d,c,b,a)低电平即可点亮led。人眼的视觉…...

TCP/IP 原理、实现方式与优缺点

TCP/IP&#xff08;传输控制协议/网际协议&#xff09; 是互联网的核心协议套件&#xff0c;主要用于在不同计算机之间进行通信。它包括多个层次的协议&#xff0c;每层协议负责不同的功能。TCP/IP 的四个层次模型如下&#xff1a; 网络接口层&#xff1a;负责在特定的物理网络…...

利率债与信用债的区别及其与债券型基金的关系

利率债与信用债的定义及其区别 定义 利率债&#xff1a; 定义&#xff1a;利率债是指由主权或类主权主体&#xff08;如中华人民共和国财政部、国家开发银行等&#xff09;发行的债券。这些债券通常被认为没有信用风险&#xff0c;因为它们由国家信用背书。特点&#xff1a;由…...

linux下解压命令

在Linux下&#xff0c;解压缩文件通常涉及多种命令&#xff0c;具体取决于文件的压缩格式。以下是一些常用的解压缩命令&#xff1a; tar.gz / .tgz 如果文件扩展名为 .tar.gz 或 .tgz&#xff0c;你可以使用 tar 命令来解压缩&#xff1a; tar -xzf filename.tar.gz这里的 -x …...

Vulnhub靶场DC-3-2练习

目录 0x00 准备0x01 主机信息收集0x02 站点信息收集0x03 漏洞查找与利用1. joomla漏洞查找2. SQL注入漏洞3. 破解hash4. 上传一句话木马5. 蚁剑连接shell6. 反弹shell7. 提权 0x04 总结 0x00 准备 下载链接&#xff1a;https://download.vulnhub.com/dc/DC-3-2.zip 介绍&#…...

Swift入门笔记

Swift入门笔记 简单值控制流函数和闭包对象和类枚举和结构体并发协议和扩展错误处理泛型 简单值 // 声明变量 var myVariable 42 myVariable 50// 声明常量 let myConstant 42// 声明类型 let implicitInteger 70 let implicitDouble 70.0 let explicitDouble: Double 7…...

【提交ACM出版 | EIScopus检索稳定 | 高录用】第五届大数据与社会科学国际学术会议(ICBDSS 2024,8月16-18)

第五届大数据与社会科学国际学术会议&#xff08;ICBDSS 2024&#xff09;将于2024年08月16-18日在中国-上海隆重举行。 ICBDSS会议在各专家教授的支持下&#xff0c;去年已成功举办了四届会议。为了让更多的学者有机会参与会议分享交流经验。本次会议主要围绕“大数据”、“社…...

Postman与世界相连:集成第三方服务的全面指南

&#x1f50c; Postman与世界相连&#xff1a;集成第三方服务的全面指南 Postman不仅是API开发和测试的强大工具&#xff0c;还支持与多种第三方服务的集成&#xff0c;从而扩展其功能&#xff0c;提高开发和测试的效率。本文将深入探讨如何在Postman中集成第三方服务&#xf…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...