SpringCloud系列之Nacos+Dubbo+Seata应用篇
前言
本文接上篇文章《SpringCloud系列之Nacos+Dubbo应用篇》继续展开,本次在原先集成Nacos和Dubbo基础上增加分布式事务Seata组件,之前在《SpringCloud系列之集成分布式事务Seata应用篇》中也提及到Seata的集成,只不过原先Seata使用都是基于“file”展开,在本篇中将介绍如何基于nacos进行配置以及Dubbo和Seata的集成验证。
项目版本
spring-boot.version:2.2.5.RELEASE
spring-cloud.version:Hoxton.SR3
nacos.version:1.3.2
dubbo.version:2.6.9/2.7.6
seata.version:1.3.0
关注本文末尾微信公众号,回复“666”获取常用开发工具包,内含常用开发组件(Nacos,Seata等),节省翻墙下载时间。
项目说明
项目模块说明如下:
前端请求接口请求cloud-web模块保留接口,从而调用相应业务微服务模块,执行业务逻辑后响应前端请求。
支付模块对外提供Dubbo服务,其余服务采用Feign进行通讯,用户模块示例中未涉及相关业务调用。
Dubbo 2.6.x 和 Dubbo 2.7.x 除了依赖不一样,其余配置项基本一样,这边就不再单独进行区分说明,详情请查阅本文文末项目源码,有进行区分说明。
Nacos服务
Nacos服务端部署请查阅《SpringCloud系列之Nacos应用篇》
Seata服务
进入Seata配置目录下,编辑registry.conf配置文件,将registry配置项中type配置项配置成"nacos",并配置nacos相关配置,具体如下
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = "public"
cluster = "default"
username = ""
password = ""
}
}
配置完registry配置项后将config配置项也配置成“nacos”,具体如下
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = "public"
group = "SEATA_GROUP"
username = ""
password = ""
}
}
调整完后启动Seata服务即可在Nacos后台看到已注册的服务,如下图
订单模块
其余模块本次未进行任何调整,只在订单模块中增加Dubbo相关配置
部分pom.xml如下,完整信息请查阅本文文末项目源码
pom.xml
<!--nacos discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--nacos config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--dubbo-->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.9</version>
</dependency>
<!--dubbo nacos-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>2.6.7</version>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--nacos-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.31.Final</version>
</dependency>
<!-- seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
application.properties
dubbo.application.name=order-service
# dubbo扫描包路径
dubbo.scan.base-packages=com.chinawu.cloud.order.service
# dubbo协议
dubbo.protocol.name=dubbo
# 随机端口
dubbo.protocol.port=-1
# zookeeper地址
dubbo.registry.address=nacos://127.0.0.1:8848
spring.main.allow-bean-definition-overriding=true
OrderService.java
@RestController
public class OrderService implements OrderFacade {
@Autowired
private TbOrderMapper tbOrderMapper;
@Autowired
private CartFacade cartFacade;
@Autowired
private GoodsFacade goodsFacade;
@Autowired
private WalletFacade walletFacade;
// Dubbo服务消费
@Reference(check = false)
DPayFacade dPayFacade;
/**
* <p >
* 功能:新增订单
* </p>
* @param cartId 购物车ID
* @author wuyubin
* @date 2020年05月22日
* @return
*/
@Override
public void addOrder(Long cartId) {
CartDTO cart = cartFacade.getCartById(cartId);
TbOrder order = new TbOrder();
order.setUserId(cart.getUserId());
order.setGoodsId(cart.getGoodsId());
order.setOrderNo(String.valueOf(System.currentTimeMillis()));
order.setCreateTime(System.currentTimeMillis());
order.setUpdateTime(order.getCreateTime());
order.setIsDeleted(Byte.valueOf("0"));
// 新增订单
tbOrderMapper.insert(order);
// 删除购物车(Feign)
cartFacade.deleteCartById(cartId);
GoodsDTO goods = goodsFacade.getByGoodsId(cart.getGoodsId());
// 扣减库存(Feign)
goodsFacade.substractStock(goods.getId());
// 扣减金额(Feign)
walletFacade.substractMoney(cart.getUserId(),goods.getMoney());
// 记录支付消息(Dubbo调用)
dPayFacade.goToPay("wuyubin");
throw new RuntimeException();
}
}
新增引用支付模块Dubbo服务,dPayFacade.goToPay("wuyubin");
支付模块
支付模块在原先基础上增加Seata依赖,部分pom信息如下
pom.xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
application.properties
# seata配置
seata.enabled=true
#seata.excludes-for-auto-proxying=firstClassNameForExclude,secondClassNameForExclude
seata.application-id=pay-service
# 事务分组名
seata.tx-service-group=pay-service_tx_group
# 默认开启数据源自动代理
seata.enable-auto-data-source-proxy=true
seata.use-jdk-proxy=false
seata.config.type=nacos
seata.registry.type=nacos
seata.registry.nacos.server-addr=127.0.0.1:8848
DPayService.java
@Service
public class DPayService implements DPayFacade {
@Autowired
TbPayMapper tbPayMapper;
@Override
public String goToPay(String userName) {
String xid = RootContext.getXID();
System.out.println("pay.seata.xid:"+xid);
System.out.println("dubbo.method:goToPay request "+userName);
TbPay pay = new TbPay();
pay.setMoney(new BigDecimal("5"));
pay.setStatus(Byte.valueOf("1"));
tbPayMapper.insert(pay);
return "dubbo.method:goToPay result:" + userName + " pay success";
}
}
调整完启动支付模块后,启动过程中发现一行报错日志,信息如下
no available service 'null' found, please make sure registry config correct
其余正常,但心里还是有点担忧,其他服务模块启动后试一下再说
http://localhost:9100/order/add?cartId=1
请求后果然在支付模块报如下错误
Caused by: io.seata.core.exception.RmTransactionException: Runtime
Caused by: io.seata.common.exception.FrameworkException: No available service
看信息应该是缺少服务,后来经过一点点排查后,最终确认是这一行配置项导致,将
seata.config.type=nacos调整至file时并把这行配置项打开即可(用nacos时其余seata配置项都注释掉了)
seata.service.vgroup-mapping.pay-service_tx_group=default
先了解下该行配置项主要作用,原来是靠此配置项用于查询TC服务的。
那是不是只要添加这行配置就可以了呢,重新将seata配置信息调整至“nacos”,根据命名规则尝试创建以下配置项
配置创建成功后,在支付模块后台立马看到有日志输出
2020-09-14 15:58:43.284 ERROR 28292 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager : no available service 'null' found, please make sure registry config correct
2020-09-14 15:58:53.252 ERROR 28292 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : no available service 'null' found, please make sure registry config correct
2020-09-14 15:58:53.285 ERROR 28292 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager : no available service 'null' found, please make sure registry config correct
2020-09-14 15:59:02.853 INFO 28292 --- [-localhost_8848] c.a.n.client.config.impl.ClientWorker : [fixed-localhost_8848] [polling-resp] config changed. dataId=service.vgroupMapping.pay-service_tx_group, group=SEATA_GROUP
2020-09-14 15:59:02.853 INFO 28292 --- [-localhost_8848] c.a.n.client.config.impl.ClientWorker : get changedGroupKeys:[service.vgroupMapping.pay-service_tx_group+SEATA_GROUP]
2020-09-14 15:59:02.857 INFO 28292 --- [-localhost_8848] c.a.n.client.config.impl.ClientWorker : [fixed-localhost_8848] [data-received] dataId=service.vgroupMapping.pay-service_tx_group, group=SEATA_GROUP, tenant=null, md5=c21f969b5f03d33d43e04f8f136e7682, content=default, type=text
2020-09-14 15:59:02.857 INFO 28292 --- [-localhost_8848] c.a.nacos.client.config.impl.CacheData : [fixed-localhost_8848] [notify-context] dataId=service.vgroupMapping.pay-service_tx_group, group=SEATA_GROUP, md5=c21f969b5f03d33d43e04f8f136e7682
2020-09-14 15:59:02.858 INFO 28292 --- [-localhost_8848] c.a.nacos.client.config.impl.CacheData : [fixed-localhost_8848] [notify-ok] dataId=service.vgroupMapping.pay-service_tx_group, group=SEATA_GROUP, md5=c21f969b5f03d33d43e04f8f136e7682, listener=io.seata.config.nacos.NacosConfiguration$NacosListener@37dd83b6
2020-09-14 15:59:02.858 INFO 28292 --- [-localhost_8848] c.a.nacos.client.config.impl.CacheData : [fixed-localhost_8848] [notify-listener] time cost=1ms in ClientWorker, dataId=service.vgroupMapping.pay-service_tx_group, group=SEATA_GROUP, md5=c21f969b5f03d33d43e04f8f136e7682, listener=io.seata.config.nacos.NacosConfiguration$NacosListener@37dd83b6
2020-09-14 15:59:03.253 INFO 28292 --- [eoutChecker_1_1] com.alibaba.nacos.client.naming : new ips(1) service: SEATA_GROUP@@seata-server@@default -> [{"instanceId":"192.168.2.241#8091#default#SEATA_GROUP@@seata-server","ip":"192.168.2.241","port":8091,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"default","serviceName":"SEATA_GROUP@@seata-server","metadata":{},"ipDeleteTimeout":30000,"instanceHeartBeatInterval":5000,"instanceIdGenerator":"simple","instanceHeartBeatTimeOut":15000}]
2020-09-14 15:59:03.254 INFO 28292 --- [eoutChecker_1_1] com.alibaba.nacos.client.naming : current ips:(1) service: SEATA_GROUP@@seata-server@@default -> [{"instanceId":"192.168.2.241#8091#default#SEATA_GROUP@@seata-server","ip":"192.168.2.241","port":8091,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"default","serviceName":"SEATA_GROUP@@seata-server","metadata":{},"ipDeleteTimeout":30000,"instanceHeartBeatInterval":5000,"instanceIdGenerator":"simple","instanceHeartBeatTimeOut":15000}]
2020-09-14 15:59:03.254 INFO 28292 --- [eoutChecker_1_1] com.alibaba.nacos.client.naming : [LISTENER] adding SEATA_GROUP@@seata-server with default to listener map
2020-09-14 15:59:03.256 INFO 28292 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : will connect to 192.168.2.241:8091
2020-09-14 15:59:03.258 INFO 28292 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : NettyPool create channel to transactionRole:TMROLE,address:192.168.2.241:8091,msg:< RegisterTMRequest{applicationId='pay-service', transactionServiceGroup='pay-service_tx_group'} >
2020-09-14 15:59:03.281 INFO 28292 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager : will connect to 192.168.2.241:8091
2020-09-14 15:59:03.281 INFO 28292 --- [eoutChecker_2_1] i.s.c.rpc.netty.RmNettyRemotingClient : RM will register :jdbc:mysql://127.0.0.1:3306/spring-cloud
2020-09-14 15:59:03.283 INFO 28292 --- [eoutChecker_2_1] i.s.core.rpc.netty.NettyPoolableFactory : NettyPool create channel to transactionRole:RMROLE,address:192.168.2.241:8091,msg:< RegisterRMRequest{resourceIds='jdbc:mysql://127.0.0.1:3306/spring-cloud', applicationId='pay-service', transactionServiceGroup='pay-service_tx_group'} >
2020-09-14 15:59:03.305 INFO 28292 --- [eoutChecker_2_1] i.s.c.rpc.netty.RmNettyRemotingClient : register RM success. client version:1.3.0, server version:1.3.0,channel:[id: 0x15ef339d, L:/192.168.2.241:64550 - R:/192.168.2.241:8091]
2020-09-14 15:59:03.305 INFO 28292 --- [eoutChecker_1_1] i.s.c.rpc.netty.TmNettyRemotingClient : register TM success. client version:1.3.0, server version:1.3.0,channel:[id: 0xd4e407be, L:/192.168.2.241:64548 - R:/192.168.2.241:8091]
2020-09-14 15:59:03.313 INFO 28292 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : register success, cost 39 ms, version:1.3.0,role:TMROLE,channel:[id: 0xd4e407be, L:/192.168.2.241:64548 - R:/192.168.2.241:8091]
2020-09-14 15:59:03.313 INFO 28292 --- [eoutChecker_2_1] i.s.core.rpc.netty.NettyPoolableFactory : register success, cost 26 ms, version:1.3.0,role:RMROLE,channel:[id: 0x15ef339d, L:/192.168.2.241:64550 - R:/192.168.2.241:8091]
感觉有戏,再次请求接口,果然不再报错,提示事务回滚成功,其余模块也提示回滚成功,另外数据也正常,至此就可以使用啦。
pay.seata.xid:192.168.2.241:8091:48803259719028736
dubbo.method:goToPay request wuyubin
2020-09-14 16:06:44.006 INFO 28292 --- [ch_RMROLE_1_1_8] i.s.c.r.p.c.RmBranchRollbackProcessor : rm handle branch rollback process:xid=192.168.2.241:8091:48803259719028736,branchId=48803260146847744,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/spring-cloud,applicationData=null
2020-09-14 16:06:44.010 INFO 28292 --- [ch_RMROLE_1_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 192.168.2.241:8091:48803259719028736 48803260146847744 jdbc:mysql://127.0.0.1:3306/spring-cloud
2020-09-14 16:06:44.061 INFO 28292 --- [ch_RMROLE_1_1_8] i.s.r.d.undo.AbstractUndoLogManager : xid 192.168.2.241:8091:48803259719028736 branch 48803260146847744, undo_log deleted with GlobalFinished
2020-09-14 16:06:44.063 INFO 28292 --- [ch_RMROLE_1_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked
参考资料
Seata事务分组专题
源码分析Seata-XID传递 Dubbo篇
系列文章
SpringCloud系列之配置中心(Config)使用说明
SpringCloud系列之服务注册发现(Eureka)应用篇
SpringCloud系列之Nacos+Dubbo+Seata应用篇的更多相关文章
- SpringCloud系列之Nacos+Dubbo应用篇
目录 前言 项目版本 项目说明 项目结构 集成Dubbo2.6.x 支付模块 用户模块 集成Dubbo2.7.x 支付模块 用户模块 测试验证 参考资料 系列文章 前言 本文在前篇文章<Spri ...
- SpringCloud系列之Nacos应用篇
前言 原先项目是以SpringConfig作为项目配置中心组件,Eureka作为服务注册发现组件,基本上就是SpringCloud全家桶,Eureka已经停更,所以前期调研可替换方案,主流替换方案有C ...
- SpringCloud系列之集成Dubbo应用篇
目录 前言 项目版本 项目说明 集成Dubbo 2.6.x 新项目模块 老项目模块 集成Dubbo 2.7.x 新项目模块 老项目模块 参考资料 系列文章 前言 SpringCloud系列开篇文章就说 ...
- SpringCloud系列之集成分布式事务Seata应用篇
目录 前言 项目版本 项目说明 Seata服务端部署 Seata客户端集成 cloud-web module-order module-cart module-goods module-wallet ...
- SpringCloud系列之网关(Gateway)应用篇
@ 目录 前言 项目版本 网关访问 鉴权配置 限流配置 前言 由于项目采用了微服务架构,业务功能都在相应各自的模块中,每个业务模块都是以独立的项目运行着,对外提供各自的服务接口,如没有类似网关之类组件 ...
- SpringCloud系列——Bus 消息总线
前言 SpringCloud Bus使用轻量级消息代理将分布式系统的节点连接起来.然后可以使用此代理广播状态更改(例如配置更改)或其他管理指令.本文结合RabbitMQ+GitHub的Webhook实 ...
- Nacos系列:Nacos的三种部署模式
三种部署模式 Nacos支持三种部署模式 1.单机模式:可用于测试和单机使用,生产环境切忌使用单机模式(满足不了高可用) 2.集群模式:可用于生产环境,确保高可用 3.多集群模式:可用于多数据中心场景 ...
- SpringCloud系列-整合Hystrix的两种方式
Hystrix [hɪst'rɪks],中文含义是豪猪,因其背上长满棘刺,从而拥有了自我保护的能力.本文所说的Hystrix是Netflix开源的一款容错框架,同样具有自我保护能力. 本文目录 一.H ...
- SpringBoot系列之集成Dubbo的方式
SpringBoot系列之集成Dubbo的方式 本博客介绍Springboot框架集成Dubbo实现微服务的3种常用方式,对于Dubbo知识不是很熟悉的,请先学习我上一篇博客:SpringBoot系列 ...
随机推荐
- ElasticSearch实战系列七: Logstash实战使用-图文讲解
前言 在上一篇中我们介绍了Logstash快速入门,本文主要介绍的是ELK日志系统中的Logstash的实战使用.实战使用我打算从以下的几个场景来进行讲解. 时区问题解决方案 在我们使用logstas ...
- JavaScript设计模式之单例模式【惰性单例】
在提高开发水平,往中高级前端工程师中,利用设计模式是必不可少的一条道路.掌握设计模式的思想远远比硬套重要,因为设计模式是一种思想,不局限于开发语言.但实际上由于语言的特性不同,往往在实现的时候会有不少 ...
- Android Studio上传项目到GitHub出错
上传代码到Github出错: 一.github push文件过大(超过50M会有警告,超出100M就会被限制) error: GH001: Large files detected. this exc ...
- Vue源码解析,keep-alive是如何实现缓存的?
前言 在性能优化上,最常见的手段就是缓存.对需要经常访问的资源进行缓存,减少请求或者是初始化的过程,从而降低时间或内存的消耗.Vue 为我们提供了缓存组件 keep-alive,它可用于路由级别或组件 ...
- 强化学习模型实现RL-Adventure
源代码:https://github.com/higgsfield/RL-Adventure 在Pytorch1.4.0上解决bug后的复现版本:https://github.com/lucifer2 ...
- session 机制和 httpsession 详解 (转载)
https://www.cnblogs.com/bjanzhuo/archive/2013/02/27/3575884.html 一.术语session 在我的经验里,session这个词被滥用的程度 ...
- 项目通用配置xml
SqlMapperClient.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE co ...
- failed to resolve org.junit.platform
IDEA提示:failed to resolve org.junit.platform,如下图 方法一:修改Maven镜像 D:\Program Files\apache-maven-3.6.3-pc ...
- js区别对象和数组的三种方法
var arr = {}||[]; 区分arr是数组还是对象 1.arr.constructor ...
- 【Android】AndroidStudio关于EventBus报错解决方法its super classes have no public methods with the @Subscribe
作者:程序员小冰,GitHub主页:https://github.com/QQ986945193 新浪微博:http://weibo.com/mcxiaobing 首先说明,以前我用eventBus的 ...