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();
    }
}
相关推荐
小蒜学长5 小时前
springboot多功能智能手机阅读APP设计与实现(代码+数据库+LW)
java·spring boot·后端·智能手机
追逐时光者6 小时前
精选 4 款开源免费、美观实用的 MAUI UI 组件库,助力轻松构建美观且功能丰富的应用程序!
后端·.net
你的人类朋友7 小时前
【Docker】说说卷挂载与绑定挂载
后端·docker·容器
间彧7 小时前
在高并发场景下,如何平衡QPS和TPS的监控资源消耗?
后端
间彧7 小时前
QPS和TPS的区别,在实际项目中,如何准确测量和监控QPS和TPS?
后端
间彧8 小时前
消息队列(RocketMQ、RabbitMQ、Kafka、ActiveMQ)对比与选型指南
后端·消息队列
brzhang9 小时前
AI Agent 干不好活,不是它笨,告诉你一个残忍的现实,是你给他的工具太难用了
前端·后端·架构
brzhang9 小时前
一文说明白为什么现在 AI Agent 都把重点放在上下文工程(context engineering)上?
前端·后端·架构
Roye_ack9 小时前
【项目实战 Day9】springboot + vue 苍穹外卖系统(用户端订单模块 + 商家端订单管理模块 完结)
java·vue.js·spring boot·后端·mybatis
AAA修煤气灶刘哥10 小时前
面试必问的CAS和ConcurrentHashMap,你搞懂了吗?
后端·面试