分布式系统开发实战:微服务架构,实战:基于CQRS微服务通信

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: Axon Framework是一个适用于Java的、基于事件驱动的轻量级CQRS框架,既支持直接持久化Aggregate状态,也支持采用EventSourcing。

实战:基于CQRS微服务通信

Axon Framework是一个适用于Java的、基于事件驱动的轻量级CQRS框架,既支持直接持久化Aggregate状态,也支持采用EventSourcing。

Axon Framework的应用架构如图9-6所示。

网络异常,图片无法展示
|

图9-6 Axon Framework应用架构

本节,我们将基于Axon Framework来实现一个CQRS应用“axon-cqrs”。该应用展示了:“开通银行账户,取钱”这样一个逻辑业务。

配置

配置依赖如下。

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.2.3.RELEASE</spring.version>
<axon.version>3.4.3</axon.version>
<log4j.version>2.13.0</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId><artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-core</artifactId>
<version>${axon.version}</version>
</dependency>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring</artifactId>
<version>${axon.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>

其中,采用了Axon Framework以及日志框架。

Aggregate

BankAccount是DDD中的Aggregate,代表了银行账户。

package com.waylau.axon.cqrs.command.aggregates;
import static org.axonframework.commandhandling.model.AggregateLifecycle.apply;
import static org.slf4j.LoggerFactory.getLogger;
import java.math.BigDecimal;
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.commandhandling.model.AggregateIdentifier;
import org.axonframework.eventhandling.EventHandler;
import org.slf4j.Logger;
import com.waylau.axon.cqrs.command.commands.CreateAccountCommand;
import com.waylau.axon.cqrs.command.commands.WithdrawMoneyCommand;
import com.waylau.axon.cqrs.common.domain.AccountId;
import com.waylau.axon.cqrs.common.events.CreateAccountEvent;
import com.waylau.axon.cqrs.common.events.WithdrawMoneyEvent;
public class BankAccount {
private static final Logger LOGGER = getLogger(BankAccount.class);@AggregateIdentifier
private AccountId accountId;
private String accountName;
private BigDecimal balance;
public BankAccount() {
}
@CommandHandler
public BankAccount(CreateAccountCommand command){
LOGGER.debug("Construct a new BankAccount");
apply(new CreateAccountEvent(command.getAccountId(),
command.getAccountName(),
command.getAmount()));
}
@CommandHandler
public void handle(WithdrawMoneyCommand command){
apply(new WithdrawMoneyEvent(command.getAccountId(),
command.getAmount()));
}
@EventHandler
public void on(CreateAccountEvent event){
this.accountId = event.getAccountId();
this.accountName = event.getAccountName();
this.balance = new BigDecimal(event.getAmount());
LOGGER.info("Account {} is created with balance {}",
accountId,
this.balance);
}
@EventHandler
public void on(WithdrawMoneyEvent event){
BigDecimal result = this.balance.subtract(
new BigDecimal(event.getAmount()));
if(result.compareTo(BigDecimal.ZERO)<0)
LOGGER.error("Cannot withdraw more money than the balance!");
else {
this.balance = result;
LOGGER.info("Withdraw {} from account {}, balance result: {}",
event.getAmount(), accountId, balance);
}
}
}

其中,注解@CommandHandler和@EventHandler代表了对Command和Event的处理。@AggregateIdentifier代表的AccountId,是一个Aggregate的全局唯一的标识符。

AccountId声明如下。

package com.waylau.axon.cqrs.common.domain;
import org.axonframework.common.Assert;
import org.axonframework.common.IdentifierFactory;
import java.io.Serializable;
public class AccountId implements Serializable {private static final long serialVersionUID = 7119961474083133148L;
private final String identifier;
private final int hashCode;
public AccountId() {
this.identifier =
IdentifierFactory.getInstance().generateIdentifier();
this.hashCode = identifier.hashCode();
}
public AccountId(String identifier) {
Assert.notNull(identifier, ()->"Identifier may not be null");
this.identifier = identifier;
this.hashCode = identifier.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AccountId accountId = (AccountId) o;
return identifier.equals(accountId.identifier);
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public String toString() {
return identifier;
}
}

其中:

·实现equal和hashCode方法,因为它会被拿来与其他标识对比。

·实现toString方法,其结果也应该是全局唯一的。

·实现Serializable接口以表明可序列化。

Command

该应用共有两个Command:创建账户和取钱。

CreateAccountCommand代表了创建账户。

package com.waylau.axon.cqrs.command.commands;
import com.waylau.axon.cqrs.common.domain.AccountId;
public class CreateAccountCommand {
private AccountId accountId;
private String accountName;
private long amount;public CreateAccountCommand(AccountId accountId,
String accountName,
long amount) {
this.accountId = accountId;
this.accountName = accountName;
this.amount = amount;
}
public AccountId getAccountId() {
return accountId;
}
public String getAccountName() {
return accountName;
}
public long getAmount() {
return amount;
}
}

WithdrawMoneyCommand代表了取钱。

package com.waylau.axon.cqrs.command.commands;
import org.axonframework.commandhandling.TargetAggregateIdentifier;
import com.waylau.axon.cqrs.common.domain.AccountId;
public class WithdrawMoneyCommand {
@TargetAggregateIdentifier
private AccountId accountId;
private long amount;
public WithdrawMoneyCommand(AccountId accountId, long amount) {
this.accountId = accountId;
this.amount = amount;
}
public AccountId getAccountId() {
return accountId;
}
public long getAmount() {
return amount;
}
}

Event

Event是系统中发生任何改变时产生的事件类,典型的Event就是对Aggregate状态的修改。与Command相对应,会有两个事件。

CreateAccountEvent代表了创建账户的Event。

package com.waylau.axon.cqrs.common.events;import com.waylau.axon.cqrs.common.domain.AccountId;
public class CreateAccountEvent {
private AccountId accountId;
private String accountName;
private long amount;
public CreateAccountEvent(AccountId accountId,
String accountName, long amount) {
this.accountId = accountId;
this.accountName = accountName;
this.amount = amount;
}
public AccountId getAccountId() {
return accountId;
}
public String getAccountName() {
return accountName;
}
public long getAmount() {
return amount;
}
}
WithdrawMoneyEvent代表了取钱的Event。
package com.waylau.axon.cqrs.common.events;
import com.waylau.axon.cqrs.common.domain.AccountId;
public class WithdrawMoneyEvent {
private AccountId accountId;
private long amount;
public WithdrawMoneyEvent(AccountId accountId, long amount) {
this.accountId = accountId;
this.amount = amount;
}
public AccountId getAccountId() {
return accountId;
}
public long getAmount() {
return amount;
}
}

测试

为了方便测试,我们创建一个Application类。

package com.waylau.axon.cqrs;
import static org.slf4j.LoggerFactory.getLogger;
import org.axonframework.config.Configuration;
import org.axonframework.config.DefaultConfigurer;import org.axonframework.eventsourcing.eventstore.inmemory.
InMemoryEventStorageEngine;
import org.slf4j.Logger;
import com.waylau.axon.cqrs.command.aggregates.BankAccount;
import com.waylau.axon.cqrs.command.commands.CreateAccountCommand;
import com.waylau.axon.cqrs.command.commands.WithdrawMoneyCommand;
import com.waylau.axon.cqrs.common.domain.AccountId;
public class Application {
private static final Logger LOGGER = getLogger(Application.class);
public static void main(String args[]) throws InterruptedException{
LOGGER.info("Application is start.");
Configuration config = DefaultConfigurer.defaultConfiguration()
.configureAggregate(BankAccount.class)
.configureEmbeddedEventStore(c -> new InMemoryEventStorageEngine())
.buildConfiguration();
config.start();
AccountId id = new AccountId();
config.commandGateway().send(new CreateAccountCommand(id, "MyAccount",1000));
config.commandGateway().send(new WithdrawMoneyCommand(id, 500));
config.commandGateway().send(new WithdrawMoneyCommand(id, 500));
config.commandGateway().send(new WithdrawMoneyCommand(id, 500));
// 线程先睡5s,等事件处理完
Thread.sleep(5000L);
LOGGER.info("Application is shutdown.");
}
}

在该类中,我们创建了一个账户,并执行了3次取钱动作。由于账户总额为1000,所以,当执行第3次取钱动作时,预期是会因为余额不足而失败。

运行该Application类,能看到以下输出。

22:11:52.432 [main] INFO com.waylau.axon.cqrs.Application - Application is start.
22:11:52.676 [main] INFO com.waylau.axon.cqrs.command.aggregates.BankAccount –
Account cc0653ff-afbd-49f5-b868-38aff296611c is created with balance 1000
22:11:52.719 [main] INFO com.waylau.axon.cqrs.command.aggregates.BankAccount –
Account cc0653ff-afbd-49f5-b868-38aff296611c is created with balance 1000
22:11:52.725 [main] INFO com.waylau.axon.cqrs.command.aggregates.BankAccount –
Withdraw 500 from account cc0653ff-afbd-49f5-b868-38aff296611c, balance result: 500
22:11:52.725 [main] INFO com.waylau.axon.cqrs.command.aggregates.BankAccount –
Account cc0653ff-afbd-49f5-b868-38aff296611c is created with balance 1000
22:11:52.725 [main] INFO com.waylau.axon.cqrs.command.aggregates.BankAccount –
Withdraw 500 from account cc0653ff-afbd-49f5-b868-38aff296611c, balance result: 500
22:11:52.727 [main] INFO com.waylau.axon.cqrs.command.aggregates.BankAccount –
Withdraw 500 from account cc0653ff-afbd-49f5-b868-38aff296611c, balance result: 0
22:11:52.727 [main] INFO com.waylau.axon.cqrs.command.aggregates.BankAccount –
Account cc0653ff-afbd-49f5-b868-38aff296611c is created with balance 1000
22:11:52.728 [main] INFO com.waylau.axon.cqrs.command.aggregates.BankAccount –
Withdraw 500 from account cc0653ff-afbd-49f5-b868-38aff296611c, balance result: 500
22:11:52.728 [main] INFO com.waylau.axon.cqrs.command.aggregates.BankAccount –
Withdraw 500 from account cc0653ff-afbd-49f5-b868-38aff296611c, balance result: 022:11:52.728 [main] ERROR com.waylau.axon.cqrs.command.aggregates.BankAccount - Cannot
withdraw more money than the balance!
22:11:57.729 [main] INFO com.waylau.axon.cqrs.Application - Application is shutdown.

本节示例,可以在axon-cqrs项目下找到。

本章小结

本章介绍了微服务架构的概念及构建微服务常用的技术。同时,也介绍了在微服务中常用的3种通信方式:HTTP、消息、事件驱动。在微服务中,我们可以使用CQRS来降低构建微服务通信的复杂度。

本文给大家讲解的内容是分布式系统开发实战:微服务架构,实战:基于CQRS微服务通信

本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。

相关文章
|
16小时前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【6月更文挑战第30天】Spring Cloud是Java微服务治理明星框架,整合Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(断路器)、Zuul(API网关)和Config Server(配置中心),提供完整服务治理解决方案。通过Eureka实现服务注册与发现,Ribbon进行负载均衡,Hystrix确保服务容错,Config Server集中管理配置,Zuul则作为API入口统一处理请求。理解和使用Spring Cloud是现代Java开发者的关键技能。
12 2
|
1天前
|
存储 负载均衡 云计算
微服务架构中的服务发现与注册机制
在分布式系统设计中,微服务架构因其灵活性和可伸缩性而受到青睐。本文深入探讨了微服务架构下的服务发现与注册机制,通过分析Eureka、Consul和Zookeeper等工具的原理与实践,揭示了这些机制如何优化服务间的通信和故障转移。文章结合最新研究和案例,提供了对微服务架构中关键组件的深刻见解,并讨论了其在不同场景下的应用效果。
|
1天前
|
Kubernetes Java 测试技术
探索微服务架构的演变与实践
【6月更文挑战第28天】在数字化时代,软件架构不断演进以应对复杂多变的业务需求。本文将深入探讨微服务架构从概念到实践的发展过程,分析其设计原则、技术选型及实施策略,并结合作者亲身经验,阐述在微服务转型过程中的挑战与解决之道。
|
1天前
|
Cloud Native 安全 开发者
云原生架构的演进与实践:从微服务到无服务器计算
本文深入探讨了云原生技术的最新进展,特别关注微服务和无服务器计算模型。通过分析相关研究数据和行业案例,文章揭示了云原生架构如何推动现代应用开发,提升运维效率,并实现资源的最优化配置。文中详细讨论了云原生生态系统中的关键组成部分,包括容器化、自动化管理工具和服务网格,以及它们如何共同促进敏捷性和可扩展性。此外,文章还分析了云原生安全策略的重要性,以及如何在保障安全的同时,保持系统的灵活性和高效性。
|
1天前
|
缓存 安全 API
深入理解微服务架构中的API网关模式
本文将探讨API网关在微服务架构中的核心作用和设计考量,通过分析相关研究与实践案例,揭示其在系统性能优化、安全增强及简化客户端交互方面的关键价值。我们将基于最新的行业报告和学术文献,结合具体的使用场景,对API网关的实现策略进行深度剖析,旨在为读者提供一套系统的理解和应用API网关的知识框架。
|
1天前
|
缓存 监控 负载均衡
探索微服务架构中的API网关模式
在现代软件开发领域,微服务架构因其灵活性和可扩展性而备受青睐。本文将深入探讨微服务架构中至关重要的组件——API网关。通过分析API网关的核心功能、设计原则以及实际应用案例,我们旨在揭示其在提高系统性能、增强安全性及简化客户端与服务间通信中的重要作用。结合最新研究和实际开发经验,本文将为读者提供关于如何有效实施API网关的深刻见解。
|
4天前
|
存储 监控 负载均衡
深入理解微服务架构中的服务发现机制
【6月更文挑战第25天】在微服务架构中,服务发现是确保各独立服务组件能够高效、可靠通信的关键环节。本文将探讨服务发现的基本原理、核心组件以及在现代云原生应用中的最佳实践,旨在为读者提供一套系统化理解和实现服务发现机制的指导思路。
|
1天前
|
Kubernetes Cloud Native Serverless
云原生时代的微服务架构演进之路
【6月更文挑战第28天】在数字化转型的大潮中,企业不断寻求更高效、灵活的软件开发与部署方式。云原生技术因此应运而生,它不仅改变了应用的开发模式,也重塑了微服务架构的未来。本文将探讨云原生环境下微服务架构的演进路径,包括容器化、服务网格、无服务器计算等关键技术的应用与挑战,并展望未来微服务架构的发展方向。
|
2天前
|
负载均衡 Java API
使用Spring Cloud构建Java微服务架构
使用Spring Cloud构建Java微服务架构
|
2天前
|
运维 Kubernetes 云计算
云计算时代的运维革新:容器化与微服务架构的融合之道
在云计算技术飞速发展的当下,企业IT运维面临前所未有的挑战与机遇。传统的运维模式已难以满足现代业务对敏捷性、可伸缩性和自动化的需求。本文深入探讨了容器化技术和微服务架构如何共同推动运维领域的革命,通过数据支持和科学分析,揭示了这一融合趋势如何提高运维效率、降低风险并促进创新。

热门文章

最新文章