三个项目,彼此使用seata自带的httpclient来完成相互调用,三个项目分别是:seata_user、seata_msg、seata_online,对应三个数据库。其中seata_online是调用入口,分别调用seata_user、seata_msg,其中当参数等于5的时候,会抛出异常,3个数据库均回滚事务;参数不等于5的时候,3个数据库正常保存数据。
三个项目引入seata的版本如下
io.seata
seata-spring-boot-starter
1.6.1
io.seata
seata-core
1.6.1
com.alibaba.nacos
nacos-client
2.2.0
一、seata_user
1、application.yml
server:
port: 40023
undertow:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io-threads: 4
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
# 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
worker-threads: 200
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分
buffer-size: 1024
# 是否分配的直接内存
direct-buffers: true
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
config-location: classpath:mapper/config/sqlMapConfig.xml
spring:
servlet:
multipart:
enabled: true
max-file-size: 100MB
max-request-size: 100MB
application:
name: seata_user
datasource:
dynamic:
primary: user #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
user:
url: jdbc:mysql://127.0.0.1:3306/business_platform_demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
max-active: 100
max-wait: 1000
management:
endpoints:
web:
exposure:
include: '*'
swagger:
title: app端
scanpackages: com.cn
logging:
level:
com.cn: info
servlet:
multipart:
enabled: true
max-file-size: 100MB
max-request-size: 100MB
# seata配置
seata:
enabled: true
# Seata 应用编号,默认为 ${spring.application.name}
application-id: ${spring.application.name}
# Seata 事务组编号,用于 TC 集群名
tx-service-group: ${spring.application.name}-group
# 开启自动代理
enable-auto-data-source-proxy: true
# 服务配置项
service:
# 虚拟组和分组的映射
vgroup-mapping:
gulimall-order-group: default
grouplist:
default: 192.168.3.21:8091
config:
type: nacos
nacos:
serverAddr: 192.168.3.133:8848
group: SHOP_GROUP
namespace: 53e24698-1032-41cc-966f-129931d10ec9
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.3.133:8848
namespace: 53e24698-1032-41cc-966f-129931d10ec9
group: SHOP_GROUP
2. 在nacos上新建如下配置:service.vgroupMapping.seata_user-group
3. 新建mapper
@Mapper
public interface IUserMapper extends BaseMapper {
}
4. 新建service
@Service
@Slf4j
public class UserService {
@Autowired
private IUserMapper userMapper;
@Transactional
public void saveTmp(PortalUserEntity info) {
info.setId(SnowflakeIdWorkerUtil.getId());
info.setAddTime(DateUtil.getTime("yyyy-MM-dd HH:mm:ss"));
this.userMapper.insert(info);
}
}
5. 新建controller
@RestController
@RequestMapping(value = "mobile/demo")
@Api(tags = "Demo")
public class DemoController {
@Autowired
private UserService userService;
@ApiOperation(value = "saveUser")
@PostMapping(value = "saveUser")
public ResultMsg saveUser(
@RequestBody PortalUserEntity user
) {
this.userService.saveTmp(user);
return ResultMsg.builder();
}
}
6. 启动,启动后的地址及端口:http://127.0.0.1:40023/mobile/demo/saveUser
二、seata_msg
1、application.yml
server:
port: 40024
undertow:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io-threads: 4
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
# 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
worker-threads: 200
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分
buffer-size: 1024
# 是否分配的直接内存
direct-buffers: true
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
config-location: classpath:mapper/config/sqlMapConfig.xml
spring:
servlet:
multipart:
enabled: true
max-file-size: 100MB
max-request-size: 100MB
application:
name: seata_msg
datasource:
dynamic:
primary: company #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
company:
url: jdbc:mysql://127.0.0.1:3306/business_company_demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
seata: true
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
max-active: 100
max-wait: 1000
management:
endpoints:
web:
exposure:
include: '*'
swagger:
title: seata_user
scanpackages: com.cn
logging:
level:
com.cn: info
servlet:
multipart:
enabled: true
max-file-size: 100MB
max-request-size: 100MB
# seata配置
seata:
enabled: true
# Seata 应用编号,默认为 ${spring.application.name}
application-id: ${spring.application.name}
# Seata 事务组编号,用于 TC 集群名
tx-service-group: ${spring.application.name}-group
# 开启自动代理
enable-auto-data-source-proxy: true
# 服务配置项
service:
# 虚拟组和分组的映射
vgroup-mapping:
gulimall-order-group: default
grouplist:
default: 192.168.3.21:8091
config:
type: nacos
nacos:
serverAddr: 192.168.3.133:8848
group: SHOP_GROUP
namespace: 53e24698-1032-41cc-966f-129931d10ec9
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.3.133:8848
namespace: 53e24698-1032-41cc-966f-129931d10ec9
group: SHOP_GROUP
2、在nacos上加入以下配置:service.vgroupMapping.seata_msg-group
3、新建mapper
@Mapper
public interface IMsgMapper extends BaseMapper {
}
4、新建service
@Service
@Slf4j
public class MsgService {
@Autowired
private IMsgMapper msgMapper;
@Transactional
public void saveTmp(MsgEntity info) {
info.setId(SnowflakeIdWorkerUtil.getId());
info.setCreateTime(DateUtil.getTime("yyyy-MM-dd HH:mm:ss"));
this.msgMapper.insert(info);
}
}
5、新建controller
@RestController
@RequestMapping(value = "mobile/demo")
public class Demo2Controller {
@Autowired
private MsgService msgService;
@ApiOperation(value = "saveMsg")
@PostMapping(value = "saveMsg")
public ResultMsg saveMsg(
@RequestBody MsgEntity msg
) {
this.msgService.saveTmp(msg);
return ResultMsg.builder();
}
}
6、启动后的访问地址:http://127.0.0.1:40024/mobile/demo/saveMsg
三、seata_online
1、application.yml
server:
port: 40025
undertow:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io-threads: 4
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
# 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
worker-threads: 200
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分
buffer-size: 1024
# 是否分配的直接内存
direct-buffers: true
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
config-location: classpath:mapper/config/sqlMapConfig.xml
spring:
servlet:
multipart:
enabled: true
max-file-size: 100MB
max-request-size: 100MB
application:
name: seata_online
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/business_portal_demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
seata: true
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
max-active: 100
max-wait: 1000
management:
endpoints:
web:
exposure:
include: '*'
swagger:
title: seata_user
scanpackages: com.cn
logging:
level:
com.cn: info
servlet:
multipart:
enabled: true
max-file-size: 100MB
max-request-size: 100MB
# seata配置
seata:
enabled: true
# Seata 应用编号,默认为 ${spring.application.name}
application-id: ${spring.application.name}
# Seata 事务组编号,用于 TC 集群名
tx-service-group: ${spring.application.name}-group
# 开启自动代理
enable-auto-data-source-proxy: true
# 服务配置项
service:
# 虚拟组和分组的映射
vgroup-mapping:
gulimall-order-group: default
grouplist:
default: 192.168.3.21:8091
config:
type: nacos
nacos:
serverAddr: 192.168.3.133:8848
group: SHOP_GROUP
namespace: 53e24698-1032-41cc-966f-129931d10ec9
registry:
type: nacos
nacos:
application: seata-server
server-addr: 192.168.3.133:8848
namespace: 53e24698-1032-41cc-966f-129931d10ec9
group: SHOP_GROUP
2、在nacos上加入以下配置:service.vgroupMapping.seata_online-group
3、新建mapper
@Mapper
public interface IOnmessageMapper extends BaseMapper {
}
4、新建service
@Service
@Slf4j
public class OnMessageService {
@Autowired
private IOnmessageMapper onmessageMapper;
@Transactional
public void add(OnmessageEdit info) {
OnlineMessageEntity msg = new OnlineMessageEntity();
BeanUtils.copyProperties(info, msg);
msg.setId(SnowflakeIdWorkerUtil.getId());
msg.setCreateDate(DateUtil.getTime("yyyy-MM-dd HH:mm:ss"));
if (StringUtils.isEmpty(msg.getImgs())) {
msg.setImgs("");
}
this.onmessageMapper.insert(msg);
}
}
import com.alibaba.fastjson.JSONObject;
import com.cn.exception.MyException;
import com.cn.msg.ResultMsg;
import com.cn.util.JsonUtil;
import com.cn.web.portal.vo.OnmessageEdit;
import io.seata.integration.http.DefaultHttpExecutor;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
@Service
public class DemoService {
@Autowired
private OnMessageService onMessageService;
private void saveUser() throws IOException {
// 参数拼接
JSONObject params = new JSONObject().fluentPut("title", "我爱我的祖国")
.fluentPut("content", "通知们好,我爱我的祖国");
// 执行调用
HttpResponse response = DefaultHttpExecutor.getInstance().executePost("http://127.0.0.1:40024", "/mobile/demo/saveMsg",
params, HttpResponse.class);
// 解析结果
ResultMsg result = JsonUtil.fromJson(EntityUtils.toString(response.getEntity()), ResultMsg.class);
if (!result.isSuccess()) {
throw new MyException(result.getMsg());
}
}
private void saveMsg() throws IOException {
// 参数拼接
JSONObject params = new JSONObject().fluentPut("phone", "13909384351")
.fluentPut("userName", "cn_yang").fluentPut("userType", "0").fluentPut("userStatus", 0);
// 执行调用
HttpResponse response = DefaultHttpExecutor.getInstance().executePost("http://127.0.0.1:40023", "/mobile/demo/saveUser",
params, HttpResponse.class);
ResultMsg result = JsonUtil.fromJson(EntityUtils.toString(response.getEntity()), ResultMsg.class);
if (!result.isSuccess()) {
throw new MyException(result.getMsg());
}
}
@GlobalTransactional
public void save0(Long id) throws IOException {
saveUser();
saveMsg();
OnmessageEdit lineMsg = new OnmessageEdit();
lineMsg.setTitle("标题").setContent("alsdkfja").setMsgType("0");
onMessageService.add(lineMsg);
if (id == 5) {
throw new MyException("尝试跑出异常");
}
}
}
以上DemoService中,注意一下4个依赖
import io.seata.integration.http.DefaultHttpExecutor; // seata自带的httpclient执行器
import io.seata.spring.annotation.GlobalTransactional; // 全局事务
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
5、新建controller
@RestController
@RequestMapping(value = "mobile/demo")
public class Demo2Controller {
@Autowired
private DemoService demoService;
@ApiOperation(value = "save2")
@GetMapping(value = "save2")
public ResultMsg save2(
@ApiParam(name = "id") @RequestParam Long id
) {
try {
this.demoService.save0(id);
} catch (IOException e) {
e.printStackTrace();
}
return ResultMsg.builder();
}
}
6、启动后的访问地址:http://127.0.0.1:40025/mobile/demo/save2?id=3 当id=5的时候,抛出异常,事务回滚,三个库里面的数据均恢复原样;当id不等于5的时候,正常插入数据。