Axon框架使用指南(七):命令调度

       使用明确的命令调度机制具有许多优点。 首先,有一个对象清楚地描述了客户端的意图。 通过记录命令,您可以存储意图数据和相关数据以备将来参考。 命令处理还可以很容易地通过Web服务将命令处理组件公开给远程客户端。测试也变得更容易,您可以通过列出大量事件和命令(请参阅测试)来定义测试脚本,方法是定义开始情况(给定),执行命令(何时)和预期结果(然后)。 最后一个主要优势是,在同步和异步以及本地和分布式命令处理之间切换非常容易;

       这并不意味着使用显式命令对象进行命令分派是实现它的唯一方法。 Axon的目标不是规定特定的工作方式,而是支持您按自己的方式进行操作,同时提供最佳实践作为默认行为。 您仍然可以使用可以调用的服务层来执行命令,该方法只需要启动一个工作单元(请参阅工作单元),并在方法完成时对其执行提交或回滚。

       接下来的部分概述了与使用Axon框架设置Command调度基础架构有关的任务。

命令网关

       Command Gateway是Command调度机制的便捷接口。 虽然您不需要使用网关来分派命令,但通常这是最简单的选择。有两种使用Command Gateway的方法。 首先是使用Axon提供的CommandGateway接口和DefaultCommandGateway实现。 命令网关提供了许多方法,允许您发送命令并同步(具有超时)或异步等待结果。另一种选择也许是最灵活的:您可以使用CommandGatewayFactory将几乎任何接口转换为CommandGateway。这使您可以使用强类型定义应用程序的接口,并声明自己的(可检查)业务异常。 Axon会在运行时自动为该接口生成一个实现。

配置命令网关

        您的自定义网关和Axon提供的自定义网关都必须配置为至少可以访问Command Bus。另外,Command Gateway可以配置RetryScheduler,CommandDispatchInterceptors和CommandCallbacks。

        当命令执行失败时,RetryScheduler能够安排重试。IntervalRetryScheduler是一个实现,它将以设定的时间间隔重试给定的命令,直到成功或完成最大次数的重试。仅当由于RuntimeException导致命令失败时才会调用重试调度程序,checked异常被视为“业务异常”,并且永远不会触发重试。 RetryScheduler的典型用法是在分布式CommandBus上分派命令时。如果某个节点发生故障,则重试调度程序将导致将一个命令分派给能够处理该命令的下一个节点(请参阅分发命令总线)。

     CommandDispatchInterceptor允许在将CommandMessage发送到CommandBus之前对其进行修改,与在CommandBus上配置的CommandDispatchInterceptor相比,只有在通过此网关发送消息时才会调用这些拦截器。

         每次发送命令时将调用CommandCallback。 这允许通过此网关执行一些命令的通用行为,而不管此命令的类型如何。

创建定制的命令网关

扫描二维码关注公众号,回复: 1722429 查看本文章

        Axon允许自定义接口用作命令网关,该接口中声明了每个方法的参数类型、返回类型和声明的异常,使用此网关不仅方便,而且允许您在需要的地方模拟接口,使测试变得容易得多。

以下就是影响CommandGateway行为的参数:

       第一个参数作为期望要发送的实际命令对象。

       使用@MetaDataValue注解的参数将使用标识符从元数据字段取值

       类型MetaData的参数将与CommandMessage上的MetaData合并。 如果它们的key相同,较迟MetaData的参数定义将覆盖较早MetaData的参数定义。

        CommandCallback类型的参数在处理命令后将调用onSuccess或onFailure。

       最后两个参数的类型可以是long(或int)和TimeUnit,在这种情况下,该方法将允许的执行时间是这些参数所指定的,超时后作出反应取决于方法中声明的异常(请参见下文)。请注意,如果方法的其他属性同时在避免调用被阻塞,则永远不会发生超时。

方法的声明返回值也会影响其行为:

       void返回类型将导致方法立即返回,除非方法上还有其他指示要等待,例如超时或声明的异常。

    Future,CompletionStage和CompletableFuture的返回类型将使该方法立即返回。 您可以使用从方法返回的CompletableFuture实例访问命令处理程序的结果,并且方法中声明的异常和超时被忽略。

      任何其他返回类型都会导致方法阻塞,直到结果可用。结果将转换为返回类型(如果类型不匹配,则会导致ClassCastException)

异常类型对方法的行为有以下影响:

         如果命令处理程序(或拦截器)抛出一个未被声明的异常,它将被包装在一个CommandExecutionException中,这是一个RuntimeException。

        发生超时时,默认行为是从方法返回null。这可以通过声明一个TimeoutException来改变。 如果声明了此异常,则会抛出TimeoutException。

        当线程在等待结果时被中断时,默认行为是返回null。 在这种情况下,被中断的标志被设置在线程上。 通过在方法上声明一个InterruptedException,此行为将改为抛出该异常。 抛出异常时,中断标志被移除,与java规范一致。

         其他运行时异常可以在方法中声明,但除了向API用户作出澄清之外不会有任何效果。

最后,可以使用注释影响方法的行为:

      如参数部分中所指定的,参数上的@MetaDataValue注释将使用元数据值作为该参数的值,注释中的key值表示元数据字段,用于从元数据的字段中取值。

       使用@Timeout注解的方法将需要在指定的等待时间内返回。 如果该方法已经声明了超时参数,则该注释将被忽略。

       使用@Timeout注解的类将导致在该类中声明的所有方法至多等待指定的时间量,除非它们使用它们自己的@Timeout注释或指定超时参数进行注释。

public interface MyGateway {
// fire and forget
void sendCommand(MyPayloadType command);
    // method that attaches meta data and will wait for a result for 10 seconds
    @Timeout(value = 10, unit = TimeUnit.SECONDS)
    ReturnValue sendCommandAndWaitForAResult(MyPayloadType command, @MetaDataValue("userId") String userId);
    // alternative that throws exceptions on timeout
    @Timeout(value = 20, unit = TimeUnit.SECONDS)
    ReturnValue sendCommandAndWaitForAResult(MyPayloadType command) throws TimeoutException, InterruptedException;
    // this method will also wait, caller decides how long
    void sendCommandAndWait(MyPayloadType command, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException;
}
// To configure a gateway:
CommandGatewayFactory factory = new CommandGatewayFactory(commandBus);
// note that the commandBus can be obtained from the `Configuration` object returned o n `configurer.initialize()`.
MyGateway myGateway = factory.createGateway(MyGateway.class);

命令总线

       命令总线是将命令调度到它们各自的命令处理程序的机制。 每个命令总是发送到一个命令处理程序。 如果没有命令处理程序可用于处理分发的命令,则会引发NoHandlerForCommandException异常。 在相同的命令处理类型上配置多个命令处理程序将导致处理程序相互替换, 在这种情况下,只有最后一个命令处理程序生效

调度命令

       CommandBus提供了两种方法来将命令分配给它们各自的处理程序:

      dispatch(commandMessage,callback)和dispatch(commandMessage)。第一个参数是包含实际分派的命令的消息。可选的第二个参数需要一个回调,该命令允许在命令处理完成时通知调度组件。这个回调有两个方法:onSuccess()和onFailure(),分别在命令处理正常返回或者抛出异常时调用。

     调用组件不能假设回调是在调度该命令的同一个线程中执行的。如果调用线程在继续之前需要等待结果,则可以使用FutureCallback。它是Future(在java.concurrent包中定义)和Axon的CommandCallback的一个组合。或者,考虑使用CommandGateway。

       如果应用程序对命令的结果不感兴趣,则可以使用dispatch(commandMessage)方法。

SimpleCommandBus

        顾名思义,SimpleCommandBus是最简单的实现。它可以直接处理调度它们的线程中的命令。处理完命令后,修改后的聚合被保存,生成的事件将在同一个线程中发布。在大多数情况下,例如Web应用程序,此实现将满足您的需求。 SimpleCommandBus是配置API中默认使用的实现。

     与大多数CommandBus实现一样,SimpleCommandBus允许配置拦截器。在命令总线上分派命令时调用CommandDispatchInterceptor。在实际的命令处理程序方法之前调用CommandHandlerInterceptor,这允许您修改或阻止该命令。有关更多信息,请参阅命令拦截器。

        由于所有命令处理都在同一个线程中完成,因此此实现仅限于JVM的边界。 这种实施的表现很好,但并不出色。 要跨越JVM边界,或充分利用CPU周期,请查看其他CommandBus实现。

AsynchronousCommandBus

        顾名思义,AsynchronousCommandBus实现从调度它们的线程异步执行命令。 它使用Executor在不同的Thread上执行实际的处理逻辑。默认情况下,AsynchronousCommandBus使用无限制的缓存线程池。 这意味着一个线程在分派Command时被创建。 已完成处理命令的线程将重新用于新命令。 如果线程60秒后没有处理的命令,线程将被停止。

        或者,一个Executor实例可以配置不同的线程策略。

        请注意,在停止应用程序时应关闭AsynchronousCommandBus,以确保任何等待的线程已正确关闭。 要关闭它,请调用shutdown()方法。 这也将关闭任何已经提供的Executor实例——如果它实现了ExecutorService接口。

DisruptorCommandBus

        SimpleCommandBus具有合理的性能特性,但是,SimpleCommandBus需要被锁定以防止多个线程同时访问相同聚合的事实会导致处理开销和锁的争用。

        DisruptorCommandBus采用不同的方法来执行多线程处理。不是每个线程都处理相同的过程,而是每个线程负责处理一个过程中的一个片段。 DisruptorCommandBus使用Disruptor(http://lmax-exchange.github.io/disruptor/)(一个用于并发编程的小型框架),通过采用不同的多线程方法来获得更好的性能。它不是在调用线程中进行处理任务,而是将任务交给两组线程,每个线程都负责处理一部分任务。第一组线程将执行命令处理程序,更改聚合的状态。第二组将存储事件并将其发布到事件存储。

        虽然DisruptorCommandBus轻松超过SimpleCommandBus4倍,但有一些限制:

  • DisruptorCommandBus仅支持Event Sourced聚合。该命令总线还充当Disruptor处理的聚合的存储库。要获取对存储库的引用,请使用createRepository(AggregateFactory)。
  • 命令只能导致单个聚合实例中的状态更改。
  • 使用缓存时,它只允许给定标识符的单个聚合。这意味着不可能有两个具有相同标识符的不同类型的聚合体。
  • 命令通常不会引起需要回滚工作单元的故障,但是如果发生回滚,DisruptorCommandBus不能保证命令是按照它们分派的顺序处理回滚的。此外,它需要重试一些其他命令,导致不必要的计算。
  • 在创建新的聚合实例时,更新已创建实例的命令可能并不全都按照提供的顺序发生。一旦创建了聚合,所有的命令需要按照它们被调度的顺序执行。所以为了确保顺序,需要在创建命令上使用回调来等待创建的聚合,但这不会超过几毫秒。

要构造一个DisruptorCommandBus实例,您需要一个EventStore。 该组件在存储库和事件存储中进行了说明。或者,您可以提供DisruptorConfiguration实例,该实例允许您调整配置以优化特定环境的性能:

  • Buffer size: 用于注册传入命令的环形缓冲区上的插槽数量。较高的值可能会增加吞吐量,但也会导致较高的延迟。必须始终是2的幂。默认为4096。
  • ProducerType:指示条目是由单个线程还是多个线程生成的。默认为多个。
  • WaitStrategy:当处理器线程(三个线程负责实际处理)时使用的策略需要彼此等待。最佳的WaitStrategy取决于机器中可用的内核数量以及运行的其他进程的数量。如果低延迟至关重要,并且DisruptorCommandBus可能为自己申请内核,则可以使用usySpinWaitStrategy。为了使命令总线声明更少的CPU并允许其他线程进行处理,请使用YieldingWaitStrategy。最后,你可以使用SleepingWaitStrategy和BlockingWaitStrategy允许其他进程公平地分享CPU。如果命令总线预计不是全职处理的,后者是合适的。默认为BlockingWaitStrategy。
  • Executor:设置为DisruptorCommandBus提供线程的执行程序。该执行者必须能够提供至少4个线程。其中3个线程由DisruptorCommandBus的处理组件声明。额外的线程用于调用回调函数,并在检测到Aggregate的状态被破坏的情况下安排重试。默认为一个CachedThreadPool,它提供了一个名为“DisruptorCommandBus”的线程组中的线程。
  • TransactionManager:定义事务管理器,用于确保事件的存储和发布以事务方式执行。
  • InvokerInterceptors:定义将在调用过程中使用的CommandHandlerInterceptor。这是调用实际的CommandHandler方法的过程。
  • PublisherInterceptors:定义要在发布过程中使用的CommandHandlerInterceptor。这是存储和发布生成的事件的过程。
  • RollbackConfiguration:定义应该回滚一个工作单元的例外。缺省为回滚未经检查的异常的配置。
  • RescheduleCommandsOnCorruptState:指示是否已针对已损坏的聚合(例如,因为工作单元已回滚)而执行的命令应重新计划。如果为false,则将调用回调的onFailure()方法。如果为true(默认),则该命令将被重新安排。
  • CoolingDownPeriod:设置等待处理所有命令的秒数。在 cooling down期间,不接受任何新命令,但会处理现有命令,并在必要时重新安排。 cooling down期确保线程可用于重新调度命令和调用回调。默认为1000(1秒)。
  • 缓存:设置已从事件存储中重建的聚合实例的缓存。缓存用于存储disruptor程序未使用的聚合实例。
  • InvokerThreadCount:分配给调用命令处理程序的线程数。一个好的起点是机器内核数量的一半。
  • PublisherThreadCount:用于发布事件的线程数。一个好的起点是内核数量的一半,如果在IO上花费大量时间,可以增加内核数量。
  • SerializerThreadCount:用于预先序列化事件的线程数。这默认为1,但如果没有配置序列化器,则忽略。
  • 序列化器:串行器用来执行序列化。 当一个序列化器被配置时,DisruptorCommandBus将把所有生成的事件包装在一个SerializationAware消息。 有效负载和元数据的序列化形式在发布到事件存储库之前附加。

消息处理拦截器

       消息处理程序拦截器可以在命令处理之前和之后执行操作。拦截器甚至可以完全禁止命令处理,例如出于安全原因。

      拦截器必须实现MessageHandlerInterceptor接口。这个接口声明了一个方法handle,它有三个参数:命令消息,当前的UnitOfWork和一个InterceptorChain。InterceptorChain用于连续调度过程。

      与调度拦截器不同,处理程序拦截器是在命令处理程序的上下文中调用的。这意味着他们可以基于正在处理的消息将关联数据附加到工作单元,然后将该相关数据附加到在该工作单元上下文中创建的消息中。

       处理程序拦截器通常也用于管理处理命令的事务。为此,需要注册一个TransactionManagingInterceptor,它要被配置一个TransactionManager来启动和提交(或回滚)实际的事务

分布式命令总线

       前面描述的CommandBus实现只允许在单个JVM中分派命令消息。 有时候,你需要不同JVM中的多个CommandBuses实例组合为一个Command Bus。 在一个JVM的Command Bus上调度的命令应该无缝地传送到另一个JVM中的Command Handler,同时返回结果。

       这就是DistributedCommandBus的功能。与其他CommandBus实现不同,DistributedCommandBus根本不调用任何处理程序。 它所做的只是在不同JVM上的命令总线实现之间形成一个“桥梁”。 每个JVM上的每个DistributedCommandBus实例称为“段”。

        

        由于分布式命令总线本身是AxonFramework Core模块的一部分,所以您可以在axon-distributed-commandbus-...模块中找到这些组件。 如果您使用Maven,请确保您已设置适当的依赖关系。 groupId和版本与Core模块的相同。

        DistributedCommandBus依赖于两个组件:实现JVM之间通信协议的CommandBusConnector和为每个传入命令选择目标的CommandRouter。该路由器基于路由策略计算得到路由Key,来定义应该路由到分布式命令总线的哪个段。只要网段的数量和配置没有变化。具有相同路由Key的两个命令将始终路由到相同的网段,通常,目标聚合的标识符被用作路由Key。

       Axon提供了两个RoutingStrategy实现:MetaDataRoutingStrategy,它使用命令消息中的元数据属性来查找路由Key;以及AnnotationRoutingStrategy,它使用命令消息有效载荷上的@TargetAggregateIdentifier注释来提取路由Key。显然,你也可以提供你自己的实现。

     默认情况下,如果没有Key可以从命令消息中解析,则RoutingStrategy实现将引发异常。通过在MetaDataRoutingStrategy或AnnotationRoutingStrategy的构造函数中提供UnresolvedRoutingKeyPolicy,可以更改此行为。有三种可能的政策:

  • 错误:这是默认设置,当路由Key不可用时将引发异常
  • RANDOM_KEY:当无法从命令消息解析路由Key时,将返回一个随机值。这实际上意味着这些命令将被路由到命令总线的随机段。
  • STATIC_KEY:将为未解决的路由Key返回静态密钥(“未解析”)。 这实际上意味着只要段的配置没有改变,所有这些命令将被路由到相同的段。

JGroupsConnector

     JGroupsConnector使用JGroups作为基础发现和调度机制。 描述JGroups的功能集超过了本参考指南的范围,所以请参阅JGroups用户指南了解更多详细信息。

      由于JGroups同时处理节点的发现和它们之间的通信,因此JGroupsConnector既充当CommandBusConnector也充当CommandRouter.

       Tips:您可以在axon-distributed-commandbus-jgroups模块中找到DistributedCommandBus的JGroups特定组件。

JGroupsConnector有四个强制配置元素:

  • 第一个是JChannel,它定义了JGroups协议栈。 通常,JChannel是通过引用JGroups配置文件构建的。JGroups附带了许多默认配置,可用作您自己配置的基础。 请记住,IP组播通常不能在云服务中使用,如亚马逊。 TCP Gossip在这种类型的环境中通常是一个良好的开端。
  •  Cluster Name定义每个段应该注册到的群集的名称。 具有相同群集名称的段最终会相互检测并相互调度命令。
  • “localsegment”是命令总线实现,它调度发往本地JVM的命令。 这些命令可能由其他JVM或本地JVM上的实例分派。
  • 最后,Serializer用于在命令消息通过线路发送之前序列化命令消息。

注意:使用缓存时,应在ConsistentHash更改时将其清除,以避免潜在的数据损坏(例如,当命令未指定@TargetAggregateVersion并且有新的聚合快速加入和离开JGroup时,此时被修改的聚合仍旧缓存在别处);

     最后,JGroupsConnector需要实际连接,以便将消息发送到其他段。 为此,请调用connect()方法。

JChannel channel = new JChannel("path/to/channel/config.xml");
CommandBus localSegment = new SimpleCommandBus();
Serializer serializer = new XStreamSerializer();
JGroupsConnector connector = new JGroupsConnector(channel, "myCommandBus", localSegment, serializer);
DistributedCommandBus commandBus = new DistributedCommandBus(connector, connector);
//	on one node: 
commandBus.subscribe(CommandType.class.getName(),handler); 
connector.connect();
//	on another node, with more CPU: 
commandBus.subscribe(CommandType.class.getName(), handler); 
commandBus.subscribe(AnotherCommandType.class.getName(), handler2); commandBus.updateLoadFactor(150); // defaults to 100 
connector.connect();//from now on, just deal with commandBus as if it is local..

        请注意,并不要求所有段都具有用于相同类型命令的命令处理程序。 您可以为不同的命令类型使用不同的段。 分布式命令总线将始终选择一个支持特定类型命令的处理节点来分派命令。

      如果你使用Spring,你可能要考虑使用JGroupsConnectorFactoryBean。 它在ApplicationContext启动时自动连接Connector,并在ApplicationContext关闭时进行适当的断开连接。 此外,它对测试环境使用合理的默认值(但不应将其视为生产就绪),并对配置进行自动装配。

Spring Cloud Connector

       Spring Cloud Connector安装使用SpringCloud描述的服务注册和发现机制来分发命令总线。 因此,您可以自由选择使用哪种Spring Cloud实现来分发您的命令。 示例实现是Eureka Discovery / Eureka服务器组合。

        注意SpringCloudCommandRouter使用特定于SpringCloud的特定ServiceInstance.Metadata字段向系统中的所有节点通知其消息路由信息。 因此选择Spring Cloud的实现必须支持ServiceInstance.Metadata字段的使用,这点非常重要。 如果所需的SpringCloud实现不支持修改ServiceInstance.Metadata(例如Consul),则SpringCloudHttpBackupCommandRouter是一个可行的解决方案。 有关SpringCloudHttpBackupCommandRouter的配置细节,请参阅本章末尾的内容。

       对每个SpringCloud实现进行描述将偏离本参考指南的主题。 因此,我们只引用他们各自的文件获取更多信息。

       Spring Cloud Connector安装程序是SpringCloudCommandRouter和SpringHttpCommandBusConnector的组合,它们分别填充CommandRouter和CommandBusConnector的位置以用于DistributedCommandBus;

        SpringCloudCommandRouter必须通过提供以下内容来创建:

  • DiscoveryClient类型的发现客户端。这可以通过使用@EnableDiscoveryClient注释您的Spring Boot应用程序来提供,应用程序将在您的类路径中查找Spring Cloud实现。
  • 一种类型为RoutingStrategy的路由策略。axon-core模块目前提供了几个实现,但通过功能调用也可以满足。 例如,如果您想根据“聚合标识符”路由命令,则可以使用AnnotationRoutingStrategy并在有效负载上标注用@TargetAggregateIdentifier标识聚合的字段。

SpringCloudCommandRouter的其他可选参数是:

  • 一个Predicate<ServiceInstance>类型的“服务实例过滤器”。 这个断言用于过滤掉DiscoveryClient可能遇到的ServiceInstances,你知道它们不会处理任何命令消息,如果您在Spring Cloud发现服务中设置了多个服务,那么这可能会非常有用,您不需要考虑这些服务进行命令处理。
  • ConsistentHashChangeListener类型的“一致的哈希更改侦听器”。 如果新成员已添加到已知的命令处理程序集中,那么添加一致的哈希更改侦听器为您提供执行特定任务的机会。

SpringHttpCommandBusConnector需要三个参数来创建:

  • CommandBus类型的本地命令总线。 这是将命令分派到本地JVM的命令总线实现。 这些命令可能已经由其他JVM或本地JVM上的实例分派。
  • 一个RestOperations对象,用于执行将CommandMessage发布到另一个实例。
  • 最后是Serializer类型的序列化器。 序列化器用于在命令消息通过线路发送之前序列化。

     DistributedCommandBus的SpringCloud Connector特定组件可以在axon-distributed-commandbus-springcloud模块中找到

     SpringCloudCommandRouter和SpringHttpCommandBusConnector都应该用于创建DistributedCommandsBus。 在SpringJava配置中,它看起来如下所示:

// Simple Spring Boot App providing the `DiscoveryClient` bean 
@EnableDiscoveryClient
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) { 
       SpringApplication.run(MyApplication.class, args);
   }
   // Example function providing a Spring Cloud Connector @Bean
   public CommandRouter springCloudCommandRouter(DiscoveryClient discoveryClient) { 
       return new SpringCloudCommandRouter(discoveryClient, new AnnotationRoutingStrategy());
   }
   @Bean
   public CommandBusConnector springHttpCommandBusConnector(@Qualifier("localSegment")CommandBus localSegment,RestOperations restOperations,Serializer serializer) {
       return new SpringHttpCommandBusConnector(localSegment, restOperations, serializer);
   }
   @Primary // to make sure this CommandBus implementation is used for autowiring 
   @Bean
   public DistributedCommandBus springCloudDistributedCommandBus(CommandRouter comman dRouter,CommandBusConnector commandBusConnector) {
      return new DistributedCommandBus(commandRouter, commandBusConnector);
  }
}
// if you don't use Spring Boot Autoconfiguration, you will need to explicitly define the local segment:
@Bean
@Qualifier("localSegment")
public CommandBus localSegment() {
    return new SimpleCommandBus();
}

        注意:并不要求所有段都具有用于相同类型命令的命令处理程序。 您可以为不同的命令类型使用不同的段。 分布式命令总线将始终选择一个支持该特定类型命令的节点来分派命令。

Spring Cloud Http Back UpCommand Router

       在内部,SpringCloudCommandRouter使用SpringCloud的ServiceInstance中包含的Metadata映射,在整个分布式Axon环境中传递允许的消息路由信息。如果所需的Spring Cloud实现不允许修改ServiceInstance.Metadata字段(例如Consul),则可以选择实例化SpringCloudHttpBackupCommandRouter而不是SpringCloudCommandRouter。

        顾名思义,SpringCloudHttpBackupCommandRouter是在ServiceInstance.Metadata字段不包含预期的消息路由信息时,提供备份机制,该机制提供一个 HTTP 端点, 从中可以检索消息路由信息,并同时添加功能以查询群集中其他已知节点的端点,然后检索其消息路由信息。因此,备份机制功能是一个Spring Controller,用于在可指定端点接收请求,并使用RestTemplate向可指定端点上的其他节点发送请求。

       要使用SpringCloudHttpBackupCommandRouter而不是SpringCloudCommandRouter,添加以下SpringJava配置(它取代了我们前面例子中的SpringCloudCommandRouter方法):

public class MyApplicationConfiguration { 
@Bean
public CommandRouter springCloudHttpBackupCommandRouter(
      DiscoveryClient discoveryClient,RestTemplate restTemplate,
      @Value("${axon.distributed.spring-cloud.fallback-url}") String messageRoutingInformationEndpoint) {
         return new SpringCloudHttpBackupCommandRouter(discoveryClient, new AnnotationR outingStrategy(), restTemplate, messageRoutingInformationEndpoint);
    }
}



猜你喜欢

转载自blog.csdn.net/wangli13860426642/article/details/80679125