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

news2024/11/25 0:37:57

文章目录

  • 锁处理
    • 目的:
    • 考虑锁控制思路:
    • 生命周期接口并发控制解决方案:
    • 测试锁是否生效:
      • 模拟多线程并发场景的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.459919200
	2024-07-13 14:32:00.549  INFO 21864 ---[tag 客户端ID:client001-]
	开始处理: client001 - 2024-07-13T14:32:00.550816300
	处理完成: client001 - 2024-07-13T14:32:02.555654400
	2024-07-13 14:32:02.636  INFO 21864 ---[tag 客户端ID:client001-]
	开始处理: client001 - 2024-07-13T14:32:02.636094400
	处理完成: client001 - 2024-07-13T14:32:04.639228400
	2024-07-13 14:32:04.700  INFO 21864 ---[tag 客户端ID:client001-]
	开始处理: client001 - 2024-07-13T14:32:04.701772900
	处理完成: client001 - 2024-07-13T14:32:06.717138300
	2024-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.894638100
	2024-07-13 14:23:50.984  INFO 16064 --- [tag 客户端ID:client001-]
	开始处理: client001 - 2024-07-13T14:23:50.992565800
	处理完成: client001 - 2024-07-13T14:23:52.994213800
	2024-07-13 14:23:53.088  INFO 16064 --- [tag 客户端ID:client001-]
	开始处理: client001 - 2024-07-13T14:23:53.095387900
	处理完成: client001 - 2024-07-13T14:23:55.096247500
	2024-07-13 14:23:55.170  INFO 16064 --- [tag 客户端ID:client001-]
	开始处理: client001 - 2024-07-13T14:23:55.178764600
	处理完成: client001 - 2024-07-13T14:23:57.184177900
	2024-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)
	@Override
	public 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操作异常、业务数据重复异常时等,进行回滚操作;异常信息暴漏到前端,方便开发运维;


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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

集群架构-web服务器(接入负载均衡+数据库+会话保持redis)--15454核心配置详解

紧接着前面的集群架构深化—中小型公司(拓展到大型公司业务)–下面图简单回顾一下之前做的及故障核心知识总结(等后期完全整理后,上传资源希望能帮大家) web集群架构-接入负载均衡部署web02服务器等 web集群-搭建web0…

[C++]一些list,stack和queue选择题和编程题

这时我们学完后的应用 一、选择题 1.下面有关vector和list的区别,描述错误的是( ) A.vector拥有一段连续的内存空间,因此支持随机存取,如果需要高效的随机存取,应该使用vector B.list拥有一段不连续的内存空间,如果需要大量的插入…

JavaScript基础(十四)

函数 很多人一看到这两个字就头大,可能由于多年被数学摧残有不小阴影,放心,我们这里的函数不是那些东西,在编程中我们的函数指的是: 程序的基本单元,是完成特定任务的代码语句块。 我们在写程序时,可能会…

【RAG探索第4讲】KG+RAG丨基于知识图谱优化大型语言模型方法

原文链接:【RAG探索第4讲】KGRAG丨基于生物医学知识图谱优化的大型语言模型提示生成方法 一、现有问题: LLMs在处理特定领域或高度专业化查询时缺乏专业知识,导致回答不够准确和可靠。 LLMs可能会产生事实错误(即幻觉&#xff0…

【整洁单元测试】测试气味Test Smells

背景 "Code smell" 是软件开发中的一个术语,指的是代码中可能表明存在问题的某些迹象或模式。这些迹象本身并不表示代码一定有错误,但它们通常表明代码可能难以理解、维护或扩展。Code smells 可以视为一种警告,提示开发者需要进一…

0基础学python-14:python进阶之面向对象

前言 Python是一门解释型、面向对象以及动态数据类型的高级程序设计语言,今天所要说的就是极为重要的概念,面向对象。 一、面向对象的核心概念: 1.类(Class): 类是对象的抽象描述,是面向对象编…

HarmonyOS开发中几个常见问题

前言 最近开始HarmonyOS应用开发,遇到一些小问题,也算是自己看官网文档没记住的东西,过程中再记录一下。 一、更改应用的名字和图标 对比看下Android工程中是如何更改的,只需要在清单文件AndroidManifest.xml中,更改…

机器学习 | 深入理解激活函数

什么是激活函数? 在人工神经网络中,节点的激活函数定义了该节点或神经元对于给定输入或一组输入的输出。然后,将此输出用作下一个节点的输入,依此类推,直到找到原始问题的所需解决方案。 它将结果值映射到所需的范围…

CSS综合案例(快报模块头部制作)

(大家好,今天我们将继续来学习CSS的相关知识,大家可以在评论区进行互动答疑哦~加油!💕) 目录 一、前述 二、案例分析 1.样例参看 2.拆分分析 三、案例实施 一、前述 案例:快报模块头部制…

微信小程序基本语法

官网 https://developers.weixin.qq.com/miniprogram/dev/framework/ 视频教程:尚硅谷微信小程序开发教程,2024最新微信小程序项目实战! 仿慕尚花坊项目源码:https://gitee.com/abcdfdewrw/flower-workshop 目录 一,初…

热门软件缺陷管理工具2024:专业评测与建议

国内外主流的10款软件缺陷管理工具软件对比:PingCode、Worktile、禅道、Tapd、Teambition、Tower、JIRA、Bugzilla、MantisBT、Trac。 在软件开发过程中,管理缺陷和漏洞常常成为一项挑战,尤其是在项目规模庞大时。选择一个高效的软件缺陷管理…

C#实现自定义标签的设计和打印

背景:最近在进行资产盘点的时候,需要对固定资产设计标签并进行打印。 设计标签:选用的是Fastreport自带的,可拆包忌用的标签设计器;进行标签的模型设计。 软件解压后可直接进行使用。模板的设计基本都是无脑操作,拖拽控件按,放置到固定未知即可;我设计的模板如下: 说…

Vision Pro的增强视觉:企业级Unity插件包实现主摄像头访问

在AR和VR技术的快速发展中,Unity作为跨平台游戏和应用开发的首选引擎,其插件生态的丰富性一直是开发者们关注的焦点。最近,一个专为Vision Pro设计的Unity插件包——EnterpriseCameraAccessPlugin,因其能够通过企业API访问主摄像头的功能,引起了广泛关注。 一、插件背景与…

springboot+vue+mybatis鲜花管理系统+PPT+论文+讲解+售后

随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,鲜花管理系统当然也不能排除在外。鲜花管理系统是以实际运用为开发背景,运用软件工程开发方法,采用SSM技…

小阿轩yx-zookeeper+kafka群集

小阿轩yx-zookeeperkafka群集 消息队列(Message Queue) 是分布式系统中重要的组件 通用的使用场景可以简单地描述为 当不需要立即获得结果,但是并发量又需要进行控制的时候,差不多就是需要使用消息队列的时候。 消息队列 什么是消息队列 消息(Mes…

前端框架入门之Vue _el和data的两种写法 分析MVVM模型

目录 _el与data的两种写法 MVVM模型 _el与data的两种写法 查看vue的实例对象 我们在这边注释掉了el属性 这样的话div容器就绑定不了vue实例 当我们可以在这里写一个定时任务 然后再回头指定 这个mount有挂载的意思 就是把容器对象交给vue实例后 去给他挂载指定的对象 &…

MySQL 进阶(四)【锁】

1、锁 1.1、锁的概述 锁就不需要多介绍了,多个用户访问共享数据资源,如何保证数据并发访问的一致性、有效性是数据库最重要的问题。同时,锁冲突也是影响一个数据库并发性能最重要的因素。 MySQL 中锁的划分有三类: 全局锁&…

敏捷营销在AI智能名片微信小程序中的应用探索

摘要:在数字化转型的浪潮中,企业面临着前所未有的挑战与机遇。AI智能名片微信小程序作为一种创新的营销工具,以其便捷性、智能化和高效性,正逐步成为企业连接客户、推广品牌的新宠。然而,如何在快速变化的市场环境中&a…

Automation Anywhere推出新一代AI+自动化企业系统,助力企业实现10倍商业增长

RPA厂商纷纷进军AI Agent ( AI 代理)领域,陆续推出创新产品。最近,Automation Anywhere宣布推出其新的AI 自动化企业系统,该系统结合AI和自动化技术,以实现指数级的业务成果。 在Imagine 2024大会上首次亮相的这款新产品&#xf…

机器学习中的梯度下降

本文只是简单解释一下梯度下降,其中涉及到的公式并没有展示说明。 1.什么是梯度? 梯度也可以理解为导数。 在一维空间中:梯度就是导数,或者说对于一个线性函数,也就是线的斜率。 2.什么是梯度下降? 梯度是…