如何实现java生产级RabbitMQ消费者

时间:2014-06-23 09:54:53

标签: java multithreading tomcat rabbitmq

我遇到了RabbitMQ Work Queue实现的问题。我当前在Tomcat中运行它,并且我有以下类不断地对队列中的新任务进行监视。但是在一两天之后,突然它表现得很奇怪,其中对象 DeliveryOK 通过channel.queueDeclare返回(taskQueueName,isDurable,false,false,null); 总是零。 (我在下面的日志中打印出这个"当前的poolSize")。

但是在Rabbit admin( ./ rabbitmqadmin list queues或RabbitMq Admin portal )中,它总是返回一个大于零的数字(比如队列中的1267条消息)。并且它不会减少到零直到我重新启动tomcat,下面的类只能检测到队列中实际上有一些消息。

最初我认为这个类以某种方式被终止,但它能够消耗那些新到达的消息。它不会消耗那些留在队列中的1267条消息。例如,队列中的消息1267在重新启动tomcat之前不会消耗。

从下面的代码中,是因为有缺陷的实现还是有更好的方法来专门为RabbitMQ实现队列使用者?我已经阅读了相关的帖子帖子(Producer/Consumer threads using a Queue),但我不确定它是否有帮助。

此外,下面的此消费者实现是否无法在RunTimeException中存活?

MqConsumer Class:

@Service
public class MqConsumer implements Runnable{

private static final Logger logger = LoggerFactory.getLogger(MqConsumer.class);
private final int MAX_ALERT_THRESHOLD = 10000;

@Autowired
private AsynchSystemConnections asynchSystemConnections;
public MqConsumer(){

}

@PostConstruct
private void init() {
    (new Thread(new MqConsumer(asynchSystemConnections))).start();
}

public MqConsumer(AsynchSystemConnections asynchSystemConnections){
    this.asynchSystemConnections = asynchSystemConnections;
}

@Override
public void run() {
    logger.info("Execute Consumer instance...");

    while (true) { // infinite loop until it die due server restart
        boolean toSleep = consume(asynchSystemConnections);

        if (toSleep){
            logger.error("Sleeping for 1 second...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                logger.error("", e);
            }
        }
    }
}
private boolean consume(AsynchSystemConnections asynchSystemConnections) {
    com.rabbitmq.client.Connection mqConnection = null;
    Channel mqChannel = null;
    DatasiftMq dMq = null;

    try {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(asynchSystemConnections.getMqServerHost());

        mqConnection = factory.newConnection();
        mqChannel = mqConnection.createChannel();
        //consumePushInteractionJob method will forward to AsynchTwService.consume(connection, channel, AsynchTwService.PUSH_INTERACTION_QUEUE )
        dMq = asynchSystemConnections.getAsynchService().consumePushInteractionJob(mqConnection, mqChannel);


        int poolSize = asynchSystemConnections.getAsynchService().getPushInteractionQueueSize();
        logger.info("Current poolSize: " + poolSize);

    } catch(NullPointerException e) {
        logger.error("", e);
        if (dMq != null) {

            try {
                logger.error("Removing JSON with" + dMq.getLogHeader(dMq));
                asynchSystemConnections.getAsynchService().ack(mqChannel, dMq.getDelivery());
                logger.error("Removed JSON with" + dMq.getLogHeader(dMq));
            } catch (IOException e1) {
                logger.error("Remove JSON Failed: ", e);
            }
        }
        return true;
    } catch (IOException e) {
        logger.error("Unable to create new MQ Connection from factory.", e);
        return true;
    } catch (InterruptedException e) {
        logger.error("", e);
        return true;
    } catch (ClassNotFoundException e) {
        logger.error("", e);
        return true;
    }  catch (Exception e) {
        logger.error("Big problem, better solve this fast!!", e);
        asynchSystemConnections.getNotificationService().notifySystemException(null, e);
        return true;    
    } finally {

        try {
            asynchSystemConnections.getAsynchService().ack(mqChannel, dMq.getDelivery());
            asynchSystemConnections.getAsynchService().disconnect(mqConnection, mqChannel);
        } catch (IOException e) {
            logger.error("", e);
        }
    }

    return false;
}

AsynchTwService类:

@Service("asynchTwService")
public class AsynchTwService implements AsynchService {
static final String FAVOURITE_COUNT_QUEUE = "favourite_count_queue";
static final String FRIENDS_FOLLOWERS_QUEUE = "friends_followers_queue";
static final String DIRECT_MESSAGE_RECEIVE_QUEUE = "direct_message_receive_queue";
static final String PUSH_INTERACTION_QUEUE = "push_interaction_queue";

private static String mqServerHost;

private static final Logger logger = LoggerFactory.getLogger(AsynchTwService.class);
private static final boolean isDurable = true;
private boolean autoAck = false;

private ConcurrentHashMap<String, Integer> currentQueueSize = new ConcurrentHashMap<String, Integer>();

@Override
public Connection getConnection() throws IOException{
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost(mqServerHost); 

    return factory.newConnection();
}

@Override
public void produce(Connection connection, Channel channel, Object object, String taskQueueName) throws IOException {
    sendToQueue(connection, channel, object, taskQueueName);
}

@Override
public QueueItem consume(Connection connection, Channel channel, String taskQueueName) throws IOException, InterruptedException, ClassNotFoundException{
    Serializer serializer = new Serializer();
    try {
        Delivery delivery = listenFromQueue(connection, channel, taskQueueName);
        Object messageObj = serializer.toObject(delivery.getBody());
        QueueItem queueItem = (QueueItem)messageObj;
        queueItem.setDelivery(delivery);
        return queueItem;
    } catch (InterruptedException e) {
        throw e;
    } catch (ClassNotFoundException e) {
        logger.error("Unable to serialize the message to QueueItem object", e);
        throw e;
    }
}

@Override
public int getQueueSize(String taskQueueName){
    return this.currentQueueSize.get(taskQueueName); 
}

private Delivery listenFromQueue(Connection connection, Channel channel, String taskQueueName) throws IOException, InterruptedException, ClassNotFoundException{
    try {
        DeclareOk  ok = channel.queueDeclare(taskQueueName, isDurable, false, false, null);
        currentQueueSize.put(taskQueueName, ok.getMessageCount());
        logger.info("Queue ("+ taskQueueName + ") has items: " +ok.getMessageCount());
    } catch (IOException e) {
        // TODO Auto-generated catch block
    }

    logger.info(" [*] Consuming "+taskQueueName+" message...");

    QueueingConsumer consumer = new QueueingConsumer(channel);
    try {
        channel.basicConsume(taskQueueName, autoAck, consumer);
    } catch (IOException e) {
        logger.error("", e);
    }

    try {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        return delivery;
    } catch (ShutdownSignalException e) {
        logger.error("Unable to retrieve message from Queue", e);
        throw e;
    } catch (ConsumerCancelledException e) {
        logger.error("Unable to retrieve message from Queue", e);
        throw e;
    } catch (InterruptedException e) {
        logger.error("Unable to retrieve message from Queue", e);
        throw e;
    } 
}



private void sendToQueue(Connection connection, Channel channel, Object object, String taskQueueName) throws IOException{
    //Initialization, create Message Queue broker connection
    try{
        channel.queueDeclare(taskQueueName, isDurable, false, false, null);
    }catch(IOException e) {
        logger.error(e.getMessage());
        logger.error("Error create Message Queue connection for queue name:" + taskQueueName, e);
        throw e;
    }

    //send message to broker
    try {
        long start = System.currentTimeMillis();
        Serializer serializer = new Serializer();
        logger.info("Sending Twitter QueueItem to Message Queue...");

        channel.basicPublish("", taskQueueName, MessageProperties.PERSISTENT_TEXT_PLAIN, 
                serializer.toBytes(object)); 

        logger.info("Queue successfully sent, process took: " + (System.currentTimeMillis()-start)+ "ms");
    } catch (IOException e) {
        logger.error("Error while sending object to queue : " + taskQueueName, e);
        throw e;
    }
}

public static String getMqServerHost() {
    return mqServerHost;
}

public static void setMqServerHost(String mqServerHost) {
    AsynchTwService.mqServerHost = mqServerHost;
}

@Override
public void disconnect(Connection connection, Channel channel) throws IOException{
    try {
        if (channel != null){
            if (channel.isOpen()){
                channel.close();    
            }
        }
        if (connection != null){
            if (connection.isOpen()){
                connection.close(); 
            }
        }
        logger.debug("MQ Channel Disconnected");
    } catch (IOException e) {
        throw e;
    }
}

@Override
public void ack(Channel channel, QueueingConsumer.Delivery delivery) throws IOException {
    // this is made as another method call is to avoid Ack too fast un intentionally
    try {
        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        logger.info("[x] acked" );
    } catch (IOException e) {
        logger.error("Unable Acknowledge Queue Message", e);
        throw e;
    }
}

@Override
public DatasiftMq consumeDatasiftInteraction(Connection connection, Channel channel, 
        String taskQueueName) throws IOException, InterruptedException, ClassNotFoundException {

    Serializer serializer = new Serializer();
    try {
        Delivery delivery = listenFromQueue(connection, channel, taskQueueName);
        Object messageObj = serializer.toObject(delivery.getBody());
        DatasiftMq dto = (DatasiftMq)messageObj;
        dto.setDelivery(delivery);
        return dto;
    } catch (InterruptedException e) {
        throw e;
    } catch (ClassNotFoundException e) {
        logger.error("Unable to serialize the message to DatasiftDTO object", e);
        throw e;
    }
}

@Override
public void reQueue(Channel channel, Delivery delivery) throws IOException {
    try {
        channel.basicNack(delivery.getEnvelope().getDeliveryTag(), false, true);
        logger.info("[x] Nacked" );
    } catch (IOException e) {
        logger.error("Unable Acknowledge Queue Message", e);
        throw e;
    }       
}

}

1 个答案:

答案 0 :(得分:2)

好像你在这里缺少一些基础知识。

取自here以及我的一些代码。 在消费者线程之外设置连接:

//executed once
ConnectionFactory factory = new ConnectionFactory();

factory.setHost("someHost");
factory.setUsername("user");
factory.setPassword("pass");

Connection connection = factory.newConnection();

你在线程中要做的事情:

//Consumer - executed in a Thread
QueueingConsumer consumer = new QueueingConsumer(connection.createChannel());
boolean autoAck = false;
channel.basicConsume("hello", autoAck, consumer);

while (!Thread.current().isInterrupted())) {
  QueueingConsumer.Delivery delivery = consumer.nextDelivery();
  //...      
  channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}

总的来说,我仍然建议您查看它完美集成的spring-amqp库。