前言

本文接上篇文章《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系列之网关(Gateway)应用篇

SpringCloud系列之集成Dubbo应用篇

SpringCloud系列之集成分布式事务Seata应用篇

SpringCloud系列之Nacos应用篇

SpringCloud系列之Nacos+Dubbo应用篇

项目源码

SpringCloud系列之Nacos+Dubbo+Seata应用篇的更多相关文章

  1. SpringCloud系列之Nacos+Dubbo应用篇

    目录 前言 项目版本 项目说明 项目结构 集成Dubbo2.6.x 支付模块 用户模块 集成Dubbo2.7.x 支付模块 用户模块 测试验证 参考资料 系列文章 前言 本文在前篇文章<Spri ...

  2. SpringCloud系列之Nacos应用篇

    前言 原先项目是以SpringConfig作为项目配置中心组件,Eureka作为服务注册发现组件,基本上就是SpringCloud全家桶,Eureka已经停更,所以前期调研可替换方案,主流替换方案有C ...

  3. SpringCloud系列之集成Dubbo应用篇

    目录 前言 项目版本 项目说明 集成Dubbo 2.6.x 新项目模块 老项目模块 集成Dubbo 2.7.x 新项目模块 老项目模块 参考资料 系列文章 前言 SpringCloud系列开篇文章就说 ...

  4. SpringCloud系列之集成分布式事务Seata应用篇

    目录 前言 项目版本 项目说明 Seata服务端部署 Seata客户端集成 cloud-web module-order module-cart module-goods module-wallet ...

  5. SpringCloud系列之网关(Gateway)应用篇

    @ 目录 前言 项目版本 网关访问 鉴权配置 限流配置 前言 由于项目采用了微服务架构,业务功能都在相应各自的模块中,每个业务模块都是以独立的项目运行着,对外提供各自的服务接口,如没有类似网关之类组件 ...

  6. SpringCloud系列——Bus 消息总线

    前言 SpringCloud Bus使用轻量级消息代理将分布式系统的节点连接起来.然后可以使用此代理广播状态更改(例如配置更改)或其他管理指令.本文结合RabbitMQ+GitHub的Webhook实 ...

  7. Nacos系列:Nacos的三种部署模式

    三种部署模式 Nacos支持三种部署模式 1.单机模式:可用于测试和单机使用,生产环境切忌使用单机模式(满足不了高可用) 2.集群模式:可用于生产环境,确保高可用 3.多集群模式:可用于多数据中心场景 ...

  8. SpringCloud系列-整合Hystrix的两种方式

    Hystrix [hɪst'rɪks],中文含义是豪猪,因其背上长满棘刺,从而拥有了自我保护的能力.本文所说的Hystrix是Netflix开源的一款容错框架,同样具有自我保护能力. 本文目录 一.H ...

  9. SpringBoot系列之集成Dubbo的方式

    SpringBoot系列之集成Dubbo的方式 本博客介绍Springboot框架集成Dubbo实现微服务的3种常用方式,对于Dubbo知识不是很熟悉的,请先学习我上一篇博客:SpringBoot系列 ...

随机推荐

  1. 【深度学习】:一门入门3D计算机视觉

    一.导论 目前深度学习已经在2D计算机视觉领域取得了非凡的成果,比如使用一张图像进行目标检测,语义分割,对视频当中的物体进行目标跟踪等任务都有非常不错的效果.传统的3D计算机视觉则是基于纯立体几何来实 ...

  2. ubuntu 绝望事件

    @ubuntu.com hi!大家好,早上发生了很有意思的事情 显示器分辨率(x2) 系统 2560x1440 Ubuntu 20.04.1 LTS 上面的表格是现在的环境 开机进入锁屏页面,正常显示 ...

  3. Webpack开发指南

    前言 成为一个全栈工程师,前端是必不可少的,这位前端构建工具webpack是一门必修的技术. 在学习webpack之前,先熟悉一下npm工具:https://www.runoob.com/nodejs ...

  4. C++炮台实验

    炮台实验 蒜头君在玩一个战争模拟游戏,他有高度为 1,2,3,... ,n的炮台各一个,他需要把这 n个炮台从左往右排成一行,并且炮口都朝向右边. 在这个游戏中,所有炮台发射的炮弹会摧毁前方所有高度比 ...

  5. 第六篇 Scrum冲刺博客

    一.会议图片 二.项目进展 成员 已完成情况 今日任务 冯荣新 购物车列表,购物车工具栏 博客撰写 陈泽佳 静态结构 自定义图片组件,提交功能 徐伟浩 协助前端获取数据 协助前端获取数据 谢佳余 未完 ...

  6. 求X值问题

    这,其实是一道数学题,难就难在要把数学模型用编程语言实现,其中的规律如果看不出来就比较鸡肋,这类题可以算是智商题,做这类题千万不能紧张,血的教训. 题目描述 已知有整数x,x + 100为一个平方数. ...

  7. java面向对象(封装,继承,多态,抽象,接口的定义和实现)

    1.封装 在面向对象程式设计方法中,封装(英语:Encapsulation)是指,一种将抽象性函式接口的实作细节部份包装.隐藏起来的方法. 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定 ...

  8. 前端框架之vue初步学习

    Vue.js介绍: Vue.js是一个构建数据驱动的web界面的渐进式框架.Vue.js的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件.它不仅易于上手,还便于与第三方库或既有项目整合 ...

  9. 基于postman的api自动化测试实践

    测试的好处 每个人都同意测试很重要,但并不是所有人都会去做.每当你添加新的代码,测试可以保证你的api按照预期运行.通过postman,你可以为所有api编写和运行测试脚本. postman中的测试 ...

  10. 23种设计模式 - 对象创建(FactoryMethod - AbstractFactory - Prototype - Builder)

    其他设计模式 23种设计模式(C++) 每一种都有对应理解的相关代码示例 → Git原码 ⌨ 对象创建 通过"对象创建" 模式绕开new,来避免对象创建(new)过程中所导致的紧耦 ...