分布式事务——Dubbo集成hmily使用
1. 背景
在市面上分布式事务框架挺多的,但没有哪一款分布式事务框架像Mybatis、Spring Cloud这样市场占有率高的,因为分布式事务跟业务逻辑是比较贴近的,所以找到一款适合当前业务逻辑、符合技术生态的分布式事务框架就显得尤为重要。
经过收集和了解,决定对seata、servicecomb pack、hmily进行使用对比,一开始使用的是servicecomb pack,但经过实验后,发现如下问题:
- 不支持最新的
Apache Dubbo; - 文档和网上资料比较少;
- 使用不通,报
io.grpc.StatusRuntimeException: UNIMPLEMENTED: Method not found: TccEventService/OnDisconnected,如果有小伙伴有解决过的,麻烦评论或私信下。
然后放弃了servicecomb pack,转向了hmily,先说使用后的结论:
- 分布式事务可以走通;
- 支持多种RPC框架,且紧跟这些RPC框架的更新,比如支持
Apache Dubbo; - 使用简单,且不是
服务端——客户端模式,省去了部署服务端的时间和资源; - 符合当前流行的技术生态。
2. 使用
1. 环境
JDK1.8
Apache Dubbo2.7.8
Spring Boot2.3.7.RELEASE
2. 项目结构
maven主要分为4个模块:
digital-api: Dubbo 接口定义
digital-provider: Dubbo提供者A
digital-order-provider: Dubbo提供者B
digital-consumer: Dubbo消费者
3. 父依赖
parent pom主要管理项目中使用到的包版本
pom.xml
<properties>
<alibaba-spring-cloud.version>2.2.4.RELEASE</alibaba-spring-cloud.version>
<dubbo.version>2.7.8</dubbo.version>
<zookeeper.version>3.4.13</zookeeper.version>
<curator.version>4.0.1</curator.version>
<hmily.version>2.1.1</hmily.version>
<mysql.version>5.1.47</mysql.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${alibaba-spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!-- zookeeper客户端 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
</dependency>
<!-- hmily start -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>hmily-annotation</artifactId>
<version>${hmily.version}</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>hmily-spring-boot-starter-apache-dubbo</artifactId>
<version>${hmily.version}</version>
</dependency>
<!-- hmily end -->
<!-- mysql依赖,用于hmily记录事务日志 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
4. digital-api
pom.xml: 需要用到hmily中的注解
<dependencies>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>hmily-annotation</artifactId>
</dependency>
</dependencies>
定义接口:这里定义两个接口类,用于之后两个提供者实现,从而模拟两个服务提供者的分布式事务场景
/**
* 数字藏品订单接口类
* @author Tarzan写bug
* @date 2022/11/28
*/
public interface DigitalOrderService {
/**
* 铸造
* @param dto
* @return
*/
@Hmily
boolean casting(CastingDTO dto);
}
/**
* 数字藏品接口
* @author Tarzan写bug
* @date 2022/11/28
*/
public interface DigitalService {
/**
* 数字藏品查询
* @return
*/
List<CastingDTO> list();
}
官方文档是要在接口方法上标注@Hmily注解,实验过没有注解也是可以正常跑通分布式事务的,不知道是否是跟后面管理界面有关系。
5. digital-provider
pom.xml
<dependencies>
<dependency>
<groupId>com.mountain</groupId>
<artifactId>digital-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>hmily-spring-boot-starter-apache-dubbo</artifactId>
<exclusions>
<exclusion>
<groupId>org.dromara</groupId>
<artifactId>hmily-repository-mongodb</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
注意:这里排除掉hmily-repository-mongodb是为了解决下面这个报错:
An attempt was made to call a method that does not exist. The attempt was made from the following location:
org.springframework.boot.autoconfigure.mongo.MongoClientFactorySupport.applyUuidRepresentation(MongoClientFactorySupport.java:85)
The following method did not exist:
com.mongodb.MongoClientSettings$Builder.uuidRepresentation(Lorg/bson/UuidRepresentation;)Lcom/mongodb/MongoClientSettings$Builder;
The method's class, com.mongodb.MongoClientSettings$Builder, is available from the following locations:
jar:file:/D:/rts/java-condition-service/apache-maven-3.6.3-bin/rts-maven-repository/org/mongodb/mongo-java-driver/3.8.0/mongo-java-driver-3.8.0.jar!/com/mongodb/MongoClientSettings$Builder.class
The class hierarchy was loaded from the following locations:
com.mongodb.MongoClientSettings.Builder: file:/D:/rts/java-condition-service/apache-maven-3.6.3-bin/rts-maven-repository/org/mongodb/mongo-java-driver/3.8.0/mongo-java-driver-3.8.0.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of com.mongodb.MongoClientSettings$Builder
而且没有用到mongodb进行分布式事务日志记录,所以干脆直接排除依赖。
Dubbo配置
/**
* Dubbo配置
* @author Tarzan写bug
* @since 2022/11/28
*/
@Configuration
public class DubboConfiguration {
@Bean
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("digital-provider");
return applicationConfig;
}
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setId("hmily-digital-provider");
registryConfig.setAddress("zookeeper://localhost:2181");
registryConfig.setClient("curator");
registryConfig.setTimeout(12000000);
return registryConfig;
}
@Bean
public ProviderConfig providerConfig() {
ProviderConfig providerConfig = new ProviderConfig();
providerConfig.setGroup("hmily-dubbo");
providerConfig.setTimeout(12000000);
providerConfig.setVersion("1.0.0");
return providerConfig;
}
@Bean
public ConsumerConfig consumerConfig() {
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setGroup("hmily-dubbo");
consumerConfig.setTimeout(12000000);
consumerConfig.setVersion("1.0.0");
consumerConfig.setCheck(false);
return consumerConfig;
}
}
接口实现
/**
* 数字藏品订单实现类
* @author Tarzan写bug
* @since 2022/11/28
*/
@DubboService(interfaceClass = DigitalOrderService.class)
public class DigitalOrderServiceImpl implements DigitalOrderService {
private static final Logger logger = LoggerFactory.getLogger(DigitalOrderServiceImpl.class);
@DubboReference
private DigitalService digitalService;
@HmilyTCC(confirmMethod = "confirm", cancelMethod = "cancel")
@Override
public boolean casting(CastingDTO dto) {
// int num = 1/0;
digitalService.list();
return false;
}
public boolean confirm(CastingDTO dto) {
logger.info("DigitalOrderServiceImpl confirm()");
return true;
}
public boolean cancel(CastingDTO dto) {
logger.info("DigitalOrderServiceImpl cancel()");
return false;
}
}
在实现方法上标注@HmilyTCC注解,并定义提交方法和回滚方法,这三个方法的参数和返回值都是要一样。
hmily.yml: hmily配置,文件放置在resources目录下,内容如下:
hmily:
server:
configMode: local
appName: digital-provider
config:
appName: digital-provider
serializer: kryo
contextTransmittalMode: threadLocal
scheduledThreadMax: 16
scheduledRecoveryDelay: 60
scheduledCleanDelay: 60
scheduledPhyDeletedDelay: 600
scheduledInitDelay: 30
recoverDelayTime: 60
cleanDelayTime: 180
limit: 200
retryMax: 10
bufferSize: 8192
consumerThreads: 16
asyncRepository: true
autoSql: true
phyDeleted: true
storeDays: 3
repository: mysql
support:
rpc:
annotation: true
repository:
database:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3307/hmily?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
maxActive: 20
minIdle: 10
connectionTimeout: 30000
idleTimeout: 600000
maxLifetime: 1800000
hmily.server.configMode=local表示读取本地文件配置,还可以使用Nacos、Zookeeper等配置中心的配置,所以这也反映了hmily对当前主流的技术生态的支持。这里使用mysql记录事务日志,也可以使用mongodb、redis等存储。
6. digital-order-provider
跟digital-provider一样
7. digital-consumer
普通Dubbo消费者配置,没有hmily依赖
8. hmily sql
当使用mysql记录事务日志时,需要初始化表,sql文件在hmily源码的hmily-repository\hmily-repository-database\hmily-repository-database-mysql\src\main\resources\mysql目录下。
3. 总结
使用hmily后的第一印象就是简单,而且能用。后面将对hmily的原理进行分析。上述项目代码收录于https://gitee.com/ouwenrts/hmily-demo.git.
世界那么大,感谢遇见,未来可期...
欢迎同频共振的那一部分人
作者公众号:Tarzan写bug
淘宝店:提供一元解决Java问题和其他方面的解决方案,欢迎咨询