如何将两个或多个Spring上下文与RabbitMQ同步?广播消息/事件?

时间:2013-03-29 12:18:52

标签: spring events rabbitmq spring-integration

我目前正在评估如何管理多个Spring应用程序上下文(即Tomcat集群)之间的事件同步的一些想法。

在该用例中,每个上下文都可以生成事件X(无论是 Spring上下文事件还是自制事件),必须将其广播到所有其他上下文实例。这个事件只在它的“生命周期”中有效,这意味着我不想以任何方式坚持它(因为它们很多,并且它们的状态在几分钟之后就会过时)。

我的想法是使用已经存在的RabbitMQ实例。但是,标准的生产者/消费者模式将不适合,因为该事件应该向所有消费者广播。每个消费者都是生产者......就像聊天室一样。

问题1:RabbitMQ(+ Spring Integration)可以实现吗?如何构建这样的广播消息设置?

问题2:这是否可行?有没有人有更好的解决方案/想法?

用例:每个Web应用程序上下文都可以生成“user x invites user y”等事件,这些事件应该通过Websocket或EventSource或其他方式尽快传输到用户的浏览器。因为这是(已经)运行请求,所以动作地点(服务器1)可能不是消费地点(服务器2)。

解决方案的主要目标是:

  1. (相对)快速且可扩展。
  2. Fire&忘记。如果消息已发送,请将其销毁。如果消息无关紧要,请忘掉它。没有坚持。在RabbitMQ中,可以用TTL制作。
  3. 邮件正在针对模型进行序列化,即与杰克逊或类似的东西。
  4. 不基于(活动)上下文/节点的数量进行手动配置。如果我要添加一个额外的上下文(Web服务器,后端进程,什么),我不想修改同步设置。
  5. 更新1

    在阅读了Gary Russell之后,我已经玩了一点点。

        <beans profile="rabbit">
        <rabbit:connection-factory id="connectionFactory" channel-cache-size="10" host="${rabbitmq.host}"
                                   port="${rabbitmq.port}" username="${rabbitmq.username}"
                                   password="${rabbitmq.password}" virtual-host="${rabbitmq.virtualhost}"/>
        <rabbit:admin connection-factory="connectionFactory"/>
    
        <rabbit:queue id="eventQueue" name="${rabbitmq.queue.springevents}" auto-delete="false" durable="true"></rabbit:queue>
    
        <bean id="amqpTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate">
            <property name="connectionFactory" ref="connectionFactory"/>
            <property name="exchange" value="${rabbitmq.springevents.exchange.fanout}"/>
            <property name="replyTimeout" value="${rabbitmq.replyTimeout}"/>
        </bean>
    
        <!-- Receiving -->
        <int-amqp:inbound-channel-adapter connection-factory="connectionFactory" channel="mqEventInChannelJson"
                                          queue-names="${rabbitmq.queue.springevents}"/>
    
        <!-- Sending -->
        <int-amqp:outbound-channel-adapter channel="mqEventOutChannelJson" amqp-template="amqpTemplate" routing-key=""
                                           exchange-name="${rabbitmq.springevents.exchange.fanout}" />
    </beans>
    

    频道mqEventOutChannelJson - &gt; Rabbit Exchange(amqp.fanout) - &gt;

    如果我使用此配置,多个并行启动将跳过事件,因为所有正在运行的进程都在同一队列上运行( rabbitmq.queue.springevents )。是否有可能创建自定义队列名称而不为每个节点提供不同的配置?

    我已经使用单独的Virtualhost和交换 amqp.fanout 测试了它。与特定扇出交换相同。

    更新2

    为了确保每个消费者拥有自己的队列,我为每个消费者创建了一个唯一的应用程序ID。

    bean 应用程序本身创建了一个唯一标识符:

    @Component("application")
    public class Application {
    
    private String id;
    
    @PostConstruct
    public void initialize() {
        id = "app" + Math.round(1000 * Math.random());
    }
    
    public String getId() {
        return id;
    }
    

    }

    鉴于此,我可以动态创建一个在公共交换机上注册的唯一队列。无需外部配置步骤。

        <util:property-path id="applicationId" path="application.id" />
        <rabbit:queue id="eventQueue" name="${rabbitmq.queue.springevents}_#{applicationId}" auto-delete="true" durable="true" exclusive="true">
            <rabbit:queue-arguments>
                <!-- Attention if you want to declare mixed value types: https://jira.springsource.org/browse/AMQP-198 -->
                <entry key="x-message-ttl">
                    <value type="java.lang.Long">${rabbitmq.queue.ttl}</value>
                </entry>
            </rabbit:queue-arguments>
        </rabbit:queue>
    

1 个答案:

答案 0 :(得分:1)

您可以使用RabbitMQ topic exchangefanout exchange并让每个使用者绑定一个队列。完全由Spring AMQP和Spring Integration支持。