如何同时运行弹出批处理作业,共享相同的读者和作者实例?

时间:2014-05-22 13:22:20

标签: java spring rabbitmq spring-batch spring-amqp

这就是我现有系统的工作方式。

我使用spring batch编写批处理,它将消息写入队列ASYNCHRONOUSLY。编写器一旦将一定数量的消息发送到队列,就开始侦听LINKED_BLOCKING_QUEUE以获得相同数量的消息。

我有spring amqp监听器,它使用消息并处理它们。处理完毕后,消费者会回复回复队列。有些侦听器会侦听回复队列以检查消息是否成功处理。回复侦听器会检索响应并将其添加到LINKED_BLOCKING_QUEUE,然后由writer获取。一旦编写器获取所有响应完成批处理。如果有异常,则停止批处理。

这是我的工作配置

<beans:bean id="computeListener" class="com.st.symfony.Foundation"
    p:symfony-ref="symfony" p:replyTimeout="${compute.reply.timeout}" />

<rabbit:queue name="${compute.queue}" />
<rabbit:queue name="${compute.reply.queue}" />

<rabbit:direct-exchange name="${compute.exchange}">
    <rabbit:bindings>
        <rabbit:binding queue="${compute.queue}" key="${compute.routing.key}" />
    </rabbit:bindings>
</rabbit:direct-exchange>

<rabbit:listener-container
    connection-factory="rabbitConnectionFactory" concurrency="${compute.listener.concurrency}"
    requeue-rejected="false" prefetch="1">
    <rabbit:listener queues="${compute.queue}" ref="computeListener"
        method="run" />
</rabbit:listener-container>


<beans:beans profile="master">

    <beans:bean id="computeLbq" class="java.util.concurrent.LinkedBlockingQueue" />

    <beans:bean id="computeReplyHandler" p:blockingQueue-ref="computeLbq"
        class="com.st.batch.foundation.ReplyHandler" />

    <rabbit:listener-container
        connection-factory="rabbitConnectionFactory" concurrency="1"
        requeue-rejected="false">
        <rabbit:listener queues="${compute.reply.queue}" ref="computeReplyHandler"
            method="onMessage" />
    </rabbit:listener-container>


    <beans:bean id="computeItemWriter"
        class="com.st.batch.foundation.AmqpAsynchItemWriter"
        p:template-ref="amqpTemplate" p:queue="${compute.queue}"
        p:replyQueue="${compute.reply.queue}" p:exchange="${compute.exchange}"
        p:replyTimeout="${compute.reply.timeout}" p:routingKey="${compute.routing.key}"
        p:blockingQueue-ref="computeLbq"
        p:logFilePath="${spring.tmp.batch.dir}/#{jobParameters[batch_id]}/log.txt"
        p:admin-ref="rabbitmqAdmin" scope="step" />


    <job id="computeJob" restartable="true">
        <step id="computeStep">
            <tasklet transaction-manager="transactionManager">
                <chunk reader="computeFileItemReader" processor="computeItemProcessor"
                    writer="computeItemWriter" commit-interval="${compute.commit.interval}" />
            </tasklet>
        </step>
    </job>      


</beans:beans>

这是我的作家代码,

public class AmqpAsynchRpcItemWriter<T> implements ItemWriter<T> {

    protected String exchange;
    protected String routingKey;
    protected String queue;
    protected String replyQueue;
    protected RabbitTemplate template;
    protected AmqpAdmin admin;
    BlockingQueue<Object> blockingQueue;
    String logFilePath;
    long replyTimeout;


    // Getters and Setters

    @Override
    public void write(List<? extends T> items) throws Exception {

        for (T item : items) {


            Message message = MessageBuilder
                    .withBody(item.toString().getBytes())
                    .setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN)
                    .setReplyTo(this.replyQueue)
                    .setCorrelationId(item.toString().getBytes()).build();

            template.send(this.exchange, this.routingKey, message);
        }

        for (T item : items) {

            Object msg = blockingQueue
                    .poll(this.replyTimeout, TimeUnit.MILLISECONDS);

            if (msg instanceof Exception) {

                admin.purgeQueue(this.queue, true);
                throw (Exception) msg;

            } else if (msg == null) {
                throw new Exception("reply timeout...");
            } 

        }

        System.out.println("All items are processed.. Command completed.  ");

    }

}

听众pojo

public class Foundation {

    Symfony symfony;

    long replyTimeout;

    //Getters Setters

    public Object run(String command) {

        System.out.println("Running:" + command);

        try {
            symfony.run(command, this.replyTimeout);
        } catch (Exception e) {
            return e;
        }

        return "Completed : " + command;
    }
}

这是回复处理程序

public class ReplyHandler {

    BlockingQueue<Object> blockingQueue;

    public void onMessage(Object msgContent) {

        try {

            blockingQueue.put(msgContent);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }
    }

}

现在,问题是,我想同时运行多个具有唯一批次ID的批次,这将为不同批次处理不同的数据(相同类型)。

随着未来批次数量的增加,我不想继续为每个批次添加单独的队列和回复队列。

而且,为了同时处理消息,我有多个侦听器(使用侦听器并发设置)侦听队列。如果我为不同的批次添加不同的队列,则运行的侦听器数量将增加,这可能使服务器过载(CPU /内存使用率变高)。

因此,我不想为我要添加的每种类型的批处理复制相同的基础结构。我想使用相同的基础设施,只有特定批次的编写者应该只获得其响应而不是同时运行的其他批次的响应。

我们是否可以使用相同的项目编写器实例,这些实例使用相同的阻塞队列实例来运行多个并行批处理实例?

2 个答案:

答案 0 :(得分:0)

您可能需要查看JMS Message Selectors

根据文件

  

createConsumer和createDurableSubscriber方法允许您在创建消息使用者时将消息选择器指定为参数。

     

消息使用者只接收其标题和属性与选择器匹配的消息。

答案 1 :(得分:0)

AMQP(RabbitMQ)世界中没有等效的JMS消息选择器表达式。

每个消费者必须拥有自己的队列,并使用交换机使用发件人设置的路由密钥路由到适当的队列。

它并不像你想象的那样繁重;您不必静态配置代理;消费者可以使用RabbitAdmin按需声明/删除交换,队列和绑定。

请参阅Spring AMQP文档中的Configuring the Broker