Sharding-JDBC分布式事务详解与实战

在这里插入图片描述

🚀 ShardingSphere 🚀

🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀
🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨
🌲 作者简介:硕风和炜,CSDN-Java领域优质创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享💎💎💎
🌲 恭喜你发现一枚宝藏博主,赶快收入囊中吧🌻
🌲 人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步?🎯🎯

🚀 ShardingSphere 🚀

在这里插入图片描述
在这里插入图片描述

🍔 目录

    • 🍀 一.Sharding-JDBC分布式事务详解
      • 🥦 1.1 本地事务 & 事务四大特性
      • 🥦 1.2 分布式事务
      • 🥦 1.3 LOCAL vs XA vs BASE
    • 🍀 二.Sharding-JDBC分布式事务实战
      • 🥦 2.1 数据环境准备
      • 🥦 2.2 创建SpringBoot程序 - 导入依赖
      • 🥦 2.3 创建SpringBoot程序 - 实体类
      • 🥦 2.4 创建SpringBoot程序 - 数据库Mapper接口 & 主启动类
      • 🥦 2.5 创建SpringBoot程序 - 数据加密配置文件
    • 🍀 三.Sharding-JDBC分布式事务实战测试
      • 🥦 3.1 编写测试接口 - 不做任何事务处理
      • 🥦 3.2 编写测试接口 - 添加本地事务处理注解
      • 🥦 3.3 编写测试接口 - 添加分布式事务XA
    • 🍀 四.总结
    • 💬 五.共勉

🍀 一.Sharding-JDBC分布式事务详解

🥦 1.1 本地事务 & 事务四大特性

数据库事务需要满足 ACID(原子性、一致性、隔离性、持久性)四个特性:

  • 原子性(Atomicity)指事务作为整体来执行,要么全部执行,要么全不执行;
  • 一致性(Consistency)指事务应确保数据从一个一致的状态转变为另一个一致的状态;
  • 隔离性(Isolation)指多个事务并发执行时,一个事务的执行不应影响其他事务的执行;
  • 持久性(Durability)指已提交的事务修改数据会被持久保存。

  在单一数据节点中,事务仅限于对单一数据库资源的访问控制,称之为本地事务。 几乎所有的成熟的关系型数据库都提供了对本地事务的原生支持。 但是在基于微服务的分布式应用环境下,越来越多的应用场景要求对多个服务的访问及其相对应的多个数据库资源能纳入到同一个事务当中,分布式事务应运而生。

  关系型数据库虽然对本地事务提供了完美的 ACID 原生支持。 但在分布式的场景下,它却成为系统性能的桎梏。 如何让数据库在分布式场景下满足 ACID 的特性或找寻相应的替代方案,是分布式事务的重点工作。

Sharding-JDBC分布式事务详解官方指导手册

🥦 1.2 分布式事务

  ShardingSphere 对外提供 begin/commit/rollback 传统事务接口,通过 LOCAL,XA,BASE 三种模式提供了分布式事务的能力。

  • LOCAL 事务:LOCAL 模式基于 ShardingSphere 代理的数据库 begin/commit/rolllback 的接口实现, 对于一条逻辑 SQL,ShardingSphere 通过 begin 指令在每个被代理的数据库开启事务,并执行实际 SQL,并执行 commit/rollback。 由于每个数据节点各自管理自己的事务,它们之间没有协调以及通信的能力,也并不互相知晓其他数据节点事务的成功与否。 在性能方面无任何损耗,但在强一致性以及最终一致性方面不能够保证。
  • XA 事务:采用的是 X/OPEN 组织所定义的 DTP 模型 所抽象的 AP(应用程序), TM(事务管理器)和 RM(资源管理器) 概念来保证分布式事务的强一致性。 其中 TM 与 RM 间采用 XA 的协议进行双向通信,通过两阶段提交实现。 与传统的本地事务相比,XA 事务增加了准备阶段,数据库除了被动接受提交指令外,还可以反向通知调用方事务是否可以被提交。 TM 可以收集所有分支事务的准备结果,并于最后进行原子提交,以保证事务的强一致性。
  • 柔性事务:如果将实现了 ACID 的事务要素的事务称为刚性事务的话,那么基于 BASE 事务要素的事务则称为柔性事务。 BASE 是基本可用、柔性状态和最终一致性这三个要素的缩写。
    • 基本可用(Basically Available)保证分布式事务参与方不一定同时在线;
    • 柔性状态(Soft state)则允许系统状态更新有一定的延时,这个延时对客户来说不一定能够察觉;
    • 最终一致性(Eventually consistent)通常是通过消息传递的方式保证系统的最终一致性。

在 ACID 事务中对隔离性的要求很高,在事务执行过程中,必须将所有的资源锁定。 柔性事务的理念则是通过业务逻辑将互斥锁操作从资源层面上移至业务层面。 通过放宽对强一致性要求,来换取系统吞吐量的提升。

🥦 1.3 LOCAL vs XA vs BASE

  基于 ACID 的强一致性事务基于 BASE 的最终一致性事务都不是银弹,只有在最适合的场景中才能发挥它们的最大长处。 Apache ShardingSphere 集成了 SEATA 作为柔性事务的使用方案。 可通过下表详细对比它们之间的区别,以帮助开发者进行技术选型。
在这里插入图片描述

🍀 二.Sharding-JDBC分布式事务实战

🥦 2.1 数据环境准备

191.168.10.132服务器上创建ljw_position_db0数据库,然后在数据库中创建dept职位表和dept_detail职位描述表 ;
191.168.10.133服务器上创建ljw_position_db1数据库,然后在数据库中创建dept职位表和dept_detail职位描述表 。
在这里插入图片描述

在这里插入图片描述

-- 职位表
CREATE TABLE `dept` (`Id` bigint(11) NOT NULL AUTO_INCREMENT,`name` varchar(256) DEFAULT NULL,`salary` varchar(50) DEFAULT NULL,`city` varchar(256) DEFAULT NULL,PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- 职位描述表
CREATE TABLE `dept_detail` (`Id` bigint(11) NOT NULL AUTO_INCREMENT,`pid` bigint(11) NOT NULL DEFAULT '0',`description` text,PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在这里插入图片描述

🥦 2.2 创建SpringBoot程序 - 导入依赖

引入依赖 (注意: 在这里使用ShardingSphere4.1版本为案例进行分布式事务的实战)

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.1</version></dependency><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>4.1.0</version></dependency><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-core-common</artifactId><version>4.1.0</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.10</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.5.8</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>2.0.5</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>20.0</version><scope>compile</scope></dependency><!-- XA模式--><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-transaction-xa-core</artifactId><version>4.1.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

🥦 2.3 创建SpringBoot程序 - 实体类

与数据库表中对应的实体类

@TableName("dept")
@Data
@ToString
public class Dept{@TableId(type = IdType.AUTO)private Long id;private String name;private String salary;private String city;
}
@TableName("dept_detail")
@Data
@ToString
public class DeptDetail {@TableId(type = IdType.AUTO)private Long id;private Long pid;private String description;}

🥦 2.4 创建SpringBoot程序 - 数据库Mapper接口 & 主启动类

编写对应的数据库持久层UserMapper接口

@Repository
public interface DeptMapper extends BaseMapper<Dept> {}@Repository
public interface DeptDetailMapper extends BaseMapper<DeptDetail> {}

@Repository注解的类需要交给我们的Spring容器进行管理,因此需要我们在主启动类加上扫描接口的注解。

@SpringBootApplication
@MapperScan("com.ljw.mapper")
public class ShardingSphereApplication {public static void main(String[] args) {SpringApplication.run(ShardingSphereApplication.class, args);}
}

🥦 2.5 创建SpringBoot程序 - 数据加密配置文件

配置读写分离相关配置的信息

# 应用名称
spring.application.name=sharding-jdbc-transaction# 打印SQl
spring.shardingsphere.props.sql-show=true# 端口
server.port=8888# 配置数据源
spring.shardingsphere.datasource.names=db0,db1# 数据源1
spring.shardingsphere.datasource.db0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.db0.jdbc-url=jdbc:mysql://192.168.10.132:3306/ljw_position_db0?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.db0.username=root
spring.shardingsphere.datasource.db0.password=root# 数据源2
spring.shardingsphere.datasource.db1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.db1.jdbc-url=jdbc:mysql://192.168.10.133:3306/ljw_position_db1?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.db1.username=root
spring.shardingsphere.datasource.db1.password=root# 分库策略
spring.shardingsphere.sharding.tables.position.database-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.position.database-strategy.inline.algorithm-expression=db$->{id % 2}spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.sharding-column=pid
spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.algorithm-expression=db$->{pid % 2}# 分布式主键生成
spring.shardingsphere.sharding.tables.position.key-generator.column=id
spring.shardingsphere.sharding.tables.position.key-generator.type=SNOWFLAKEspring.shardingsphere.sharding.tables.position_detail.key-generator.column=id
spring.shardingsphere.sharding.tables.position_detail.key-generator.type=SNOWFLAKE

🍀 三.Sharding-JDBC分布式事务实战测试

🥦 3.1 编写测试接口 - 不做任何事务处理

@RestController
@RequestMapping("/dept")
public class DeptController {@Autowiredprivate DeptMapper deptMapper;@Autowiredprivate DeptDetailMapper deptDetailMapper;@RequestMapping("/save")public String saveDept(){for(int i=0;i<6;i++){Dept dept = new Dept();dept.setName("Java高级开发工程师" + i);dept.setSalary("40K");dept.setCity("北京");deptMapper.insert(dept);// 注意 : 模拟出现 BUG -> 然后去数据库中验证数据是否插入!if(i == 4){throw new RuntimeException("模拟出现 BUG -> 然后去数据库中验证数据是否插入!");}DeptDetail deptDetail = new DeptDetail();deptDetail.setPid(dept.getId());deptDetail.setDescription("这是Java高级开发工程师具体的职位描述" + i);deptDetailMapper.insert(deptDetail);}return "Save Successfully!";}
}

启动项目打开浏览器,访问接口http://localhost:8888/dept/save,到数据库中验证数据!

在这里插入图片描述

程序出现异常,检查数据库, 发现数据库的数据插入了,但是数据是不完整。

在这里插入图片描述

🥦 3.2 编写测试接口 - 添加本地事务处理注解

主启动类添加@EnableTransactionManagement注解,开启声明式事务

@EnableTransactionManagement
@SpringBootApplication
@MapperScan("com.ljw.mapper")
public class ShardingSphereApplication {public static void main(String[] args) {SpringApplication.run(ShardingSphereApplication.class, args);}
}

方法上添加@Transactional注解

@RestController
@RequestMapping("/dept")
public class DeptController {@Autowiredprivate DeptMapper deptMapper;@Autowiredprivate DeptDetailMapper deptDetailMapper;@Transactional@RequestMapping("/save")public String saveDept(){for(int i=0;i<6;i++){Dept dept = new Dept();dept.setName("Java高级开发工程师" + i);dept.setSalary("40K");dept.setCity("北京");deptMapper.insert(dept);// 注意 : 模拟出现 BUG -> 然后去数据库中验证数据是否插入!if(i == 4){throw new RuntimeException("模拟出现 BUG -> 然后去数据库中验证数据是否插入!");}DeptDetail deptDetail = new DeptDetail();deptDetail.setPid(dept.getId());deptDetail.setDescription("这是Java高级开发工程师具体的职位描述" + i);deptDetailMapper.insert(deptDetail);}return "Save Successfully!";}
}

启动项目打开浏览器,访问接口http://localhost:8888/dept/save,到数据库中验证数据(项目启动之前要把数据库中原来的数据删除!)!

在这里插入图片描述

程序出现错误,检查数据库, 发现数据库中没有任何数据,说明数据发生了回滚的操作。

在这里插入图片描述

问题来了:
问题1: 为什么会出现回滚操作呢?此时是分布式环境呀? 难道@Transactional注解可以解决分布式事务吗?(@Transactional不能解决分布式事务)

  • Sharding-JDBC中的本地事务在以下两种情况是完全支持的:

    • 支持非跨库事务,比如仅分表、在单库中操作;
    • 支持因逻辑异常导致的跨库事务(这点非常重要),比如上述的操作,跨两个库插入数据,中间存在逻辑的错误哦!
  • 本地事务不支持的情况:

    • 不支持因网络、硬件异常导致的跨库事务;例如:同一事务中,跨两个库更新,更新完毕后、未提交之前,第一个库宕机,则只有第二个库数据提交。

      对于因网络、硬件异常导致的跨库事务无法支持很好理解,在分布式事务中无论是两阶段还是三阶段提交都是直接或者间接满足以下两个条件:

      ​ 1.有一个事务协调者 2.事务日志记录
      本地事务并未满足上述条件,自然是无法支持

问题2:为什么逻辑异常导致的跨库事务能够支持?

  • Sharding-JDBC中的一条SQL会经过改写,拆分成不同数据源的SQL,比如一条select语句,会按照其中分片键拆分成对应数据源的SQL,然后在不同数据源中的执行,最终会提交或者回滚。

🥦 3.3 编写测试接口 - 添加分布式事务XA

导入sharding-transaction-xa-core依赖

<!--XA模式-->
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-transaction-xa-core</artifactId><version>4.1.0</version>
</dependency>

方法上添加@ShardingTransactionType(TransactionType.XA)注解

@RestController
@RequestMapping("/dept")
public class DeptController {@Autowiredprivate DeptMapper deptMapper;@Autowiredprivate DeptDetailMapper deptDetailMapper;@Transactional@ShardingTransactionType(TransactionType.XA)@RequestMapping("/save")public String saveDept(){for(int i=0;i<6;i++){Dept dept = new Dept();dept.setName("Java高级开发工程师" + i);dept.setSalary("40K");dept.setCity("北京");deptMapper.insert(dept);// 注意 : 模拟出现 BUG -> 然后去数据库中验证数据是否插入!if(i == 4){throw new RuntimeException("模拟出现 BUG -> 然后去数据库中验证数据是否插入!");}DeptDetail deptDetail = new DeptDetail();deptDetail.setPid(dept.getId());deptDetail.setDescription("这是Java高级开发工程师具体的职位描述" + i);deptDetailMapper.insert(deptDetail);}return "Save Successfully!";}
}

启动项目打开浏览器,访问接口http://localhost:8888/dept/save,到数据库中验证数据!(项目启动之前记得清除数据!)

在这里插入图片描述

程序出现错误,检查数据库, 发现数据库中没有任何数据,说明数据发生了回滚的操作。

在这里插入图片描述

🍀 四.总结

本篇文章主要讲解了Sharding-JDBC分布式事务详解与实战,实操过程非常重要,大家一定要动手亲自实践一下,必须掌握。下节预告,ShardingProxy实战,大家敬请期待呦!!!。

💬 五.共勉

最后,我想和大家分享一句一直激励我的座右铭,希望可以与大家共勉!

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

K8s卷存储详解(二)

K8s卷存储详解&#xff08;二&#xff09; 持久卷持久卷&#xff08;PV&#xff09;持久卷申领&#xff08;PVC&#xff09;存储类&#xff08;StorageClass&#xff09;存储制备器&#xff08;Provisioner&#xff09;PV和PVC的生命周期持久卷的类型CSI 卷快照CSI 卷克隆 投射…

多租户的低代码平台,Saas开发平台:MateCloud

简介 MateCloud是一款基于Spring Cloud Alibaba的微服务架构。目前已经整合Spring Boot 2.7.0、 Spring Cloud 2021、Spring Cloud Alibaba 2021、Spring Security Oauth2、Feign、Dubbo、JetCache、RocketMQ等&#xff0c;支持多租户的低代码平台&#xff0c;Saas平台开发套件…

Elasticsearch-增删改查数据工作原理

集群 集群的基本概念&#xff1a; 集群&#xff1a;ES 集群由一个或多个 Elasticsearch 节点组成&#xff0c;每个节点配置相同的 cluster.name 即可加入集群&#xff0c;默认值为 “elasticsearch”。节点&#xff1a;一个 Elasticsearch 服务启动实例就是一个节点&#xff…

【Linux】线程同步条件变量

目录 1 线程同步的引入 2 条件变量&线程同步&竞争条件的概念 3 条件变量相关函数 初始化 销毁 等待条件满足 唤醒等待 4 demo代码——理解条件变量&线程同步 5 为什么 pthread_cond_wait 需要互斥量? 6 条件变量使用规范 1 线程同步的引入 例子生活化&…

【多任务编程-线程通信】

进程/线程通信的方式 某些应用程序中&#xff0c;进程/进程和线程/线程之间不可避免的进行通信&#xff0c;进行消息传递&#xff0c;数据共享等 同一进程的线程之间通信方式包括Windows中常用Event, Message等。 不同进程之间的通信可以利用Event, FileMapping(内存共享), W…

24考研数据结构-栈和队列的应用

目录 3.3.1栈在括号匹配中的应用流程图算法代码 3.3.2栈在表达式求值中的应用1. 中缀表达式 (需要界限符)2. 后缀表达式 (逆波兰表达式)中缀表达式转后缀表达式-手算重点&#xff1a;中缀表达式转后缀表达式-机算重点&#xff1a;后缀表达式的计算—机算 3.前缀表达式 (波兰表达…

杭电oj Simple Set Problem 双指针 尺取法 满注释版

&#x1f468;‍&#x1f3eb; 题目地址 输入 3 2 1 6 3 -7 7 10 4 9 -5 -9 2 8 5 4 3 3 8 2 10 8 1 -7 3 1 6 10 1 1 9输出 1 15 0使用快读&#xff0c;避免使用 Arrays.fill() 按需初始化 避免卡常 &#x1f351; 思路 &#x1f37a; AC code import java.io.*; import ja…

机器学习李宏毅学习笔记39

文章目录 前言一、大模型的发展趋势二、KNN LM总结 前言 大模型大资料 大模型的顿悟时刻 一、大模型的发展趋势 随数据量增加&#xff0c;模型可以从量变达到质变&#xff0c;从某一刻开始突然学会东西。 当成为大模型时&#xff0c;分数会从0,0突然变成100&#xff0c;完成“…

【Matplotlib 绘制折线图】

使用 Matplotlib 绘制折线图 在数据可视化中&#xff0c;折线图是一种常见的图表类型&#xff0c;用于展示随着变量的变化&#xff0c;某个指标的趋势或关系。Python 的 Matplotlib 库为我们提供了方便易用的功能来绘制折线图。 绘制折线图 下面的代码展示了如何使用 Matplo…

基于Centos 7虚拟机的磁盘操作(添加磁盘、分区、格式分区、挂载)

目录 一、添加硬盘 二、查看新磁盘 三、磁盘分区 3.1新建分区 3.2 格式分区 3.3 挂载分区 3.4 永久挂载新分区 3.5 取消挂载分区 一、添加硬盘 1.在虚拟机处选择编辑虚拟机设置&#xff0c;然后选择添加 2.选择硬盘&#xff0c;然后选择下一步 3.默认即可&#xff0c;下一步…

【6】toLocaleString、toLocaleDateString、toLocaleTimeString等toLocale系列方法的使用及案例

一、介绍 在当今前端开发的领域里&#xff0c;快速、高效的项目构建工具以及使用最新技术栈是非常关键的。ViteVue3 组合为一体的项目实战示例专栏将带领你深入了解和掌握这一最新的前端开发工具和框架。 作为下一代前端构建工具&#xff0c;Vite 在开发中的启动速度和热重载…

C语言:动态内存管理

文章目录 一、动态内存函数1. malloc2. calloc3. realloc4. free 二、常见的错误1.malloc或calloc开辟的空间未检查2.越界访问3.对非malloc和calloc开辟的空间&#xff0c;用free释放4.对同一块动态内存多次释放5.用free释放动态内存的一部分 三、通讯录(动态版本改写)总结 一、…

uni-app:模态框的实现(弹窗实现)

效果图 代码 标签 <template><view><!-- 按钮用于触发模态框的显示 --><button click"showModal true">显示模态框</button><!-- 模态框组件 --><view class"modal" v-if"showModal"><view cla…

探索APP开发的新趋势:人工智能和大数据的力量

随着5G技术的不断发展&#xff0c;人工智能和大数据将会更加广泛的应用于我们生活和工作中&#xff0c;作为 APP开发公司&#xff0c;应该及时的对新技术进行研发&#xff0c;进而更好的为用户服务。目前 APP开发已经不是传统的软件开发了&#xff0c;而是向移动互联网转型&…

iPhone 开机停留在苹果logo画面(已解决)

一、问题 如下图&#xff0c;开不了机&#xff1a; 标题 二、根因 存储空间满了。 三、解决方法 方法一 用苹果数据线&#xff08;最好是原装&#xff09;连接Mac电脑&#xff0c;在装有 macOS Catalina 10.15 或更高版本的 Mac 上&#xff0c;打开“访达”。在装有 macOS…

六、代理模式

文章目录 一、代理模式1、代理模式的好处和缺点1.1 代理模式理解加深 一、代理模式 为什么要学习代理模式&#xff1f; 代理模式是Spring AOP 以及 Spring MVC 的底层&#xff01;&#xff01;并且还是 JAVA 的23种设计模式之一&#xff01;&#xff01; 代理模式的分类&#…

opencv-27 阈值处理 cv2.threshold()

怎么理解阈值处理? 阈值处理&#xff08;Thresholding&#xff09;是一种常用的图像处理技术&#xff0c;在机器学习和计算机视觉中经常被用于二值化图像或二分类任务。它基于设定一个阈值来将像素值进行分类&#xff0c;将像素值大于或小于阈值的部分分为两个不同的类别&…

从Web2到Web3:区块链技术的未来前景

随着互联网的发展&#xff0c;Web1.0、Web2.0 和 Web3.0 成为了人们口中津津乐道的话题。那么&#xff0c;这三种网络时代究竟有什么区别呢&#xff1f; Web1.0 是一个只读的时代&#xff0c;那个时候&#xff0c;用户只能浏览网页&#xff0c;无法进行互动和创作。Web2.0 则是…

php-golang-jsonrpc2.0 rpc-codec/jsonrpc2和tivoka/tivoka实践

golang代码&#xff1a; package main import ( "context" "net" "net/rpc" "github.com/powerman/rpc-codec/jsonrpc2" ) type App struct{} type Res struct { Code int json:"code" Msg string json:"msg&quo…

系列二、RocketMQ简介

一、概述 RocketMQ是一款阿里巴巴开源的消息中间件。2016年11月28日&#xff0c;阿里巴巴向Apache软件基金会捐赠RabbitMQ&#xff0c;成为Apache孵化项目。2017年9月25日&#xff0c;Apache宣布RocketMQ孵化成为Apache顶级项目&#xff08;TLP&#xff09;&#xff0c;成为国内…