事务的坑:
在同一个类里面,编写两个方法,内部调用的时候,会导致事务设置失效。原因是没有用到代理对象的缘故。
解决:
0)、导入 spring-boot-starter-aop 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1)、启动类加注解:
@EnableTransactionManagement(proxyTargetClass = true)
@EnableAspectJAutoProxy(exposeProxy=true)
3)、使用 AopContext.currentProxy() 生成当前类的代理对象,再调用本类方法
ProductServiceIml o = (ProductServiceIml)AopContext.currentProxy ();
o.getFirPageProductVo (1,pageSize);
本地事务
@Transactional(timeout 30)//a事务的所有设置就传播到了和他公用一个事务的方法
public void a(){
b();//和a用一个事务,回滚
c();//新事务(不回滚)
int i=10/0;
}
@Transactional(propagation Propagation.REQUIRED,timeout 2)// 和a共用一个事务,相当于没设置超时时间
public void b(){
}
@Transactional(propagation Propagation.REQUIRES_NEW)
public void c(){
}
使用seata做分布式事务
https://seata.io/zh-cn/docs/overview/what-is-seata.html
TC是事务协调器,记录事务的状态
TM是全局事务,记录事务的范围
RM是资源管理,算是分支事务,需要和TC维持连接,实时告诉TC事务成功还是失败
使用:
https://seata.io/zh-cn/docs/user/quickstart.html
高并发场景不考虑这里面的2PC和TCC方式,着重在 最大努力通知 和 可靠消息【看后面的延时队列】
分布式事务
[file:///E:/中%20Chromedown/java~spring/10.尚硅谷——谷粒商城/谷粒商城课件/高级篇/03、本地事务%26分布式事务.pdf](file:///E:/中 Chromedown/java~spring/10.尚硅谷——谷粒商城/谷粒商城课件/高级篇/03、本地事务%26分布式事务.pdf)
使用AT模式,详情看链接【高并发场景一般不考虑,因为里面加了很多的锁,效率比较低】
- 建表
- 导入依赖【导入依赖之后,在idea的左侧栏外部库,搜一下seata-all,看看是什么版本,然后再下载对应版本服务器】
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
- 下载服务器压缩包
解压之后,里面的config文件夹,有这两个文件
registry.conf: 可以设置注册中心 和 配置中心,默认使用文件
file.conf: 如上面设置配置中心为file,生效。可以设置把 **事务日志文件(用来自动回滚的文件)**设置在文件或者db里面
-
每个微服务的resoures 都要把 registry.conf 和 file.conf 复制进去
-
- 要修改file.conf 的配置 或者 改微服务配置
service{
vgroup_mapping.my_test_tx_group = "default"
改成下面,用微服务名取代{}
vgroup_mapping.{spring.application.name}--fescar-service-group = "default"
}
微服务配置注册到nacos的名字
spring.cloud.alibaba.seata.tx-service-group = {spring.application.name}.servic
配置seata代理数据源【每个要使用分布式事务的微服务都需要配置】
import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
@Configuration
public class MySeataConfig {
@Autowired
DataSourceProperties dataSourceProperties;
@Bean
public DataSource dataSource(DataSourceProperties dataSourceProperties) {
HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
if (StringUtils.hasText(dataSourceProperties.getName())) {
dataSource.setPoolName(dataSourceProperties.getName());
}
return new DataSourceProxy(dataSource);
}
}
- 在开启事务大入口的方法上,配上 @GlobalTransactional
在被远程调用的微服务上,配上 @Transactional
使用延时队列实现 柔性事务,达到最终一致性!
【当程序延时收到消息后,程序进行各种判断,判断是否执行成功,若执行失败,回复数据】
常用下面第一种方式实现延时队列,设置队列的过期时间,然后没有消费者监听,队列里的消息只能等到过期,消息过期后,设置把消息转到死信路由
然后死信路由再把消息转到队列里,消费者就可以延时获取到消息
【不使用第二种是因为,队列里的消息,是按顺序出来,而不是按过期时间长短出来,前面的会把后面的塞住(要是有一个没设过期时间,gg)】
导入依赖还有弄好其他的rabbitMq配置之后,创建配置类,添加队列,交换机,绑定关系到容器。
spring boot会自动将这些东西弄给mq!!!
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
@Configuration
public class MyRabbitMQConfig {
/* 容器中的Queue、Exchange、Binding 会自动创建(在RabbitMQ)不存在的情况下 */
/**
* 死信队列
*
* @return
*/@Bean
public Queue orderDelayQueue() {
/*
Queue(String name, 队列名字
boolean durable, 是否持久化
boolean exclusive, 是否排他
boolean autoDelete, 是否自动删除
Map<String, Object> arguments) 属性
*/
HashMap<String, Object> arguments = new HashMap<>();
arguments.put("x-dead-letter-exchange", "order-event-exchange");
arguments.put("x-dead-letter-routing-key", "order.release.order");
arguments.put("x-message-ttl", 60000); // 消息过期时间 1分钟
Queue queue = new Queue("order.delay.queue", true, false, false, arguments);
return queue;
}
/**
* 普通队列
*
* @return
*/
@Bean
public Queue orderReleaseQueue() {
Queue queue = new Queue("order.release.order.queue", true, false, false);
return queue;
}
/**
* TopicExchange
*
* @return
*/
@Bean
public Exchange orderEventExchange() {
/*
* String name,
* boolean durable,
* boolean autoDelete,
* Map<String, Object> arguments
* */
return new TopicExchange("order-event-exchange", true, false);
}
@Bean
public Binding orderCreateBinding() {
/*
* String destination, 目的地(队列名或者交换机名字)
* DestinationType destinationType, 目的地类型(Queue、Exhcange)
* String exchange,
* String routingKey,
* Map<String, Object> arguments
* */
return new Binding("order.delay.queue",
Binding.DestinationType.QUEUE,
"order-event-exchange",
"order.create.order",
null);
}
@Bean
public Binding orderReleaseBinding() {
return new Binding("order.release.order.queue",
Binding.DestinationType.QUEUE,
"order-event-exchange",
"order.release.order",
null);
}
}
延时队列实现最终一致性的重点是:可靠消息!!