17.Axon框架-消息

消息

1.介绍

在Axon中,组件之间的所有通信都是通过Message接口的对象表示的显式消息完成的

Axon中的核心概念之一是消息传递。组件之间的所有通信都通过消息对象完成。这为这些组件提供了必要的位置透明性,以便在需要时能够扩展和分布这些组件

2.类型

  1. 命令(CommandMessage):用于描述变更应用状态的意图
  2. 事件(EventMessage):是用于描述应用中已发生事情的对象
  3. 查询(QueryMessage):用于描述获取信息或状态的请求

3.组成

  • 负载(payload):消息的内容(如命令、事件的具体内容)
  • 元数据(metadata):以键值对(String→Object)形式存在,描述消息发送的上下文,如跟踪信息、安全上下文等,典型用途为审计、日志记录,不应基于元数据做业务决策
  • 唯一标识符(unique identifier):唯一ID,用于标识消息

4.流转过程

  1. Command被发送至CommandBus
  2. CommandBus将命令分发给对应的CommandHandler
  3. CommandHandler会应用事件,这些事件随后会被发布
  4. 已发布的事件由EventHandler接收并处理,EventHandler可用于更新读模型
  5. 后续可通过发送QueryMessage,查询已更新的读模型以获取所需信息

5.不可变特性

消息是不可变的。这意味着,若要添加新元素,实际上需要创建一个新的消息实例。为了能将两个Java对象实例视为代表同一个概念性消息,每个消息都有一个标识符。修改消息的元数据不会改变这个标识符

6.元数据

介绍

以键值对(String→Object)形式存在,描述消息发送的上下文,如跟踪信息、安全上下文等,典型用途为审计、日志记录,不应基于元数据做业务决策

创建

java 复制代码
MetaData metaData = MetaData.with("myKey", 42)
                            .and("otherKey", "some value");

消息中的元数据

java 复制代码
EventMessage eventMessage = GenericEventMessage
    .asEventMessage("myPayload")
    .withMetaData(Collections.singletonMap("myKey", 42))
    .andMetaData(Collections.singletonMap("otherKey", "some value"));
  1. 创建一个以"myPayload"作为有效载荷的EventMessage
  2. withMetaData将消息中的所有元数据替换为给定的Map。在本例中,java.util.Collections.singletonMap()用于定义单个条目
  3. andMetaData将给定Map中的条目添加到消息的元数据中。具有相同键的现有条目将被覆盖

不可变特性

与常规的不同Map<String,Object>,MetaData在Axon中的是不可变的。状态变更的方法将创建并返回一个新实例,而不是修改现有实例

由于MetaData是不可变的, 的所有变异操作Map都会抛出UnsupportedOperationException

7.消息关联

介绍

在消息传递系统中,将消息分组或关联是常见做法。在Axon Framework中,一个Command消息可能产生一个或多个Event消息,一个Query消息可能产生一个或多个QueryResponse消息。通常,关联通过特定的消息属性(即所谓的关联标识符,correlation identifier)来实现

CorrelationDataProvider

Axon Framework中的消息使用MetaData属性来传输消息的元信息。MetaData对象的类型为Map<String, Object>,会随消息一起传递。要填充在一个工作单元(Unit of Work)中生成的新消息的MetaData,可以使用所谓的CorrelationDataProvider。工作单元负责基于此CorrelationDataProvider填充新消息的MetaData

接口定义如下:

java 复制代码
@FunctionalInterface
public interface CorrelationDataProvider {
    Map<String, ?> correlationDataFor(Message<?> message);
}

常见实现如下:

MessageOriginProvider

介绍

MessageOriginProvider是默认的。它负责在消息之间传递两个值:correlationId和traceId

基本概念
  • correlationId:指向父消息的id
  • traceId:指向消息链的根消息id

当创建新消息时,如果父消息中不存在这两个字段,将使用新消息自身的标识符同时作为这两个值

案例理解

举个例子,如果你处理一个Command消息,而该Command消息又发布了一个Event消息,那么Event消息的MetaData将基于以下内容填充:

  • Command消息的id作为correlationId
  • Command消息的MetaData中若存在traceId,则使用该值;否则,使用Command消息的id作为traceId

SimpleCorrelationDataProvider

介绍

SimpleCorrelationDataProvider是无条件地将指定key的值从一个消息复制到另一个消息的元数据中。要实现这一点,必须调用SimpleCorrelationDataProvider的构造函数,并传入应被复制的key的列表

代码
java 复制代码
public class Configuration {

    public CorrelationDataProvider customCorrelationDataProvider() {
        return new SimpleCorrelationDataProvider("myId", "myId2");
    }
}

MultiCorrelationDataProvider

介绍

MultiCorrelationDataProvider能够组合多个CorrelationDataProvider的效果。要实现这一点,必须调用MultiCorrelationDataProvider的构造函数,并传入一个提供器列表

代码
java 复制代码
public class Configuration {

    public CorrelationDataProvider customCorrelationDataProviders() {
        return new MultiCorrelationDataProvider<CommandMessage<?>>(
            Arrays.asList(
                new SimpleCorrelationDataProvider("someKey"),
                new MessageOriginProvider()
            )
        );
    }
}

自定义

介绍

如果预定义的提供器不能满足你的需求,你始终可以实现自己的CorrelationDataProvider。该类必须实现CorrelationDataProvider接口

使用步骤
  1. 定义一个类实现CorrelationDataProvider接口
  2. 在配置中注册
实现
java 复制代码
public class AuthCorrelationDataProvider implements CorrelationDataProvider {

    private final Function<String, String> usernameProvider;

    public AuthCorrelationDataProvider(Function<String, String> userProvider) {
        this.usernameProvider = userProvider;
    }

    @Override
    public Map<String, ?> correlationDataFor(Message<?> message) {
        Map<String, Object> correlationData = new HashMap<>();
        if (message instanceof CommandMessage<?>) {
            if (message.getMetaData().containsKey("authorization")) {
                String token = (String) message.getMetaData().get("authorization");
                correlationData.put("username", usernameProvider.apply(token));
            }
        }
        return correlationData;
    }
}
在配置中注册

原生API注册代码:

java 复制代码
public class Configuration {

    public void configuring() {
        Configurer configurer = DefaultConfigurer
            .defaultConfiguration()
            .configureCorrelationDataProviders(config -> Arrays.asList(
               new SimpleCorrelationDataProvider("someKey"),
               new MessageOriginProvider()
            ));
    }
}

SpringBoot注册代码:

java 复制代码
@Configuration
public class CorrelationDataProviderConfiguration {

    // 配置单个 CorrelationDataProvider 会自动覆盖默认的 MessageOriginProvider
    @Bean
    public CorrelationDataProvider someKeyCorrelationProvider() {
        return new SimpleCorrelationDataProvider("someKey");
    }

    @Bean
    public CorrelationDataProvider messageOriginProvider() {
        return new MessageOriginProvider();
    }
}
相关推荐
AlexZhao1893 分钟前
.NET中联合类型的实现(OneOf框架核心机理讲解)
后端·.net
cxyxiaokui00126 分钟前
论如何优雅地让AI“闭嘴”:深入SpringAI的流式停止与记忆难题
java·后端
bobz96529 分钟前
关于 “涌现” 的最初的定义
后端
Warren9835 分钟前
Spring Boot 整合网易163邮箱发送邮件实现找回密码功能
数据库·vue.js·spring boot·redis·后端·python·spring
秦禹辰1 小时前
本地Docker部署开源Web相册图库Piwigo与在线远程访问实战方案
开发语言·后端·golang
一乐小哥1 小时前
五分钟就能搭好的socks5为啥我装了一个小时😭 进来看小丑
linux·后端
HyggeBest1 小时前
Golang 并发原语 Sync Pool
后端·go
Java水解1 小时前
【RabbitMq C++】消息队列组件
后端·rabbitmq
灵魂猎手1 小时前
10. Mybatis XML配置到SQL的转换之旅
java·后端·源码