使用RabbitMQ实现可靠的消息传递

时间:2014-10-02 03:22:12

标签: rabbitmq amqp

我有一个通过RabbitMQ发送AMQP消息的应用程序。在http请求上触发消息发送。最近我注意到有些消息似乎丢失了(因为从未发送过)。我还注意到服务器管理的频道列表正在稳步增加。我纠正的第一件事就是在不再需要频道后关闭频道。但是,我仍然不确定我的代码是否正确构造以确保交付。下面是两段代码;第一个是管理连接的单例的一部分(不会在每次调用时重新创建),第二个是发送代码。任何建议/指导将不胜感激。

@Service
public class PersistentConnection {
    private static Connection myConnection = null;
    private Boolean blocked = false;

    @Autowired ApplicationConfiguration applicationConfiguration;
    @Autowired ConfigurationService configurationService;

    @PostConstruct
    private void init() {
    }

    @PreDestroy
    private void destroy() {
        try {
            myConnection.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Connection getConnection( ) {
        if (myConnection == null) {
            start();
        }
        else if (!myConnection.isOpen()) {
            log.warn("AMQP Connection closed.  Attempting to start.");
            start();
        }
        return myConnection;
    }


    private void start() {
        log.debug("Building AMQP Connection");

        ConnectionFactory factory = new ConnectionFactory();
        String ipAddress = applicationConfiguration.getAMQPHost();
        String password = applicationConfiguration.getAMQPUser();
        String user = applicationConfiguration.getAMQPPassword();
        String virtualHost = applicationConfiguration.getAMQPVirtualHost();
        String port = applicationConfiguration.getAMQPPort();

        try {
            factory.setUsername(user);
            factory.setPassword(password);
            factory.setVirtualHost(virtualHost);
            factory.setPort(Integer.parseInt(port));
            factory.setHost(ipAddress);
            myConnection = factory.newConnection();
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        myConnection.addBlockedListener(new BlockedListener() {
            public void handleBlocked(String reason) throws IOException {
                // Connection is now blocked
                blocked = true;
            }

            public void handleUnblocked() throws IOException {
                // Connection is now unblocked
                blocked = false;
            }
        });
    }

    public Boolean isBlocked() {
        return blocked;
    }
}

/*
 * Sends ADT message to AMQP server.
 */
private void send(String routingKey, String message) throws Exception { 
    String exchange = applicationConfiguration.getAMQPExchange();  
    String exchangeType = applicationConfiguration.getAMQPExchangeType();

    Connection connection = myConnection.getConnection();
    Channel channel = connection.createChannel();
    channel.exchangeDeclare(exchange, exchangeType);
    channel.basicPublish(exchange, routingKey, null, message.getBytes());

    // Close the channel if it is no longer needed in this thread
    channel.close();
}

1 个答案:

答案 0 :(得分:2)

试试这段代码:

@Service
public class PersistentConnection {
    private Connection myConnection = null;
    private Boolean blocked = false;

    @Autowired ApplicationConfiguration applicationConfiguration;
    @Autowired ConfigurationService configurationService;

    @PostConstruct
    private void init() {
      start(); /// In this way you can initthe connection and you are sure it is called only one time.
    }

    @PreDestroy
    private void destroy() {
        try {
            myConnection.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Connection getConnection( ) {
        return myConnection;
    }


    private void start() {
        log.debug("Building AMQP Connection");

        ConnectionFactory factory = new ConnectionFactory();
        String ipAddress = applicationConfiguration.getAMQPHost();
        String password = applicationConfiguration.getAMQPUser();
        String user = applicationConfiguration.getAMQPPassword();
        String virtualHost = applicationConfiguration.getAMQPVirtualHost();
        String port = applicationConfiguration.getAMQPPort();

        try {
            factory.setUsername(user);
            factory.setPassword(password);
            factory.setVirtualHost(virtualHost);
            factory.setPort(Integer.parseInt(port));
            factory.setHost(ipAddress);
            myConnection = factory.newConnection();
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        myConnection.addBlockedListener(new BlockedListener() {
            public void handleBlocked(String reason) throws IOException {
                // Connection is now blocked
                blocked = true;
            }

            public void handleUnblocked() throws IOException {
                // Connection is now unblocked
                blocked = false;
            }
        });
    }

    public Boolean isBlocked() {
        return blocked;
    }
}

/*
 * Sends ADT message to AMQP server.
 */
private void send(String routingKey, String message) throws Exception { 
    String exchange = applicationConfiguration.getAMQPExchange();  
    String exchangeType = applicationConfiguration.getAMQPExchangeType();

    Connection connection = myConnection.getConnection();
    if (connection!=null){
    Channel channel = connection.createChannel();
    try{
    channel.exchangeDeclare(exchange, exchangeType);
    channel.basicPublish(exchange, routingKey, null, message.getBytes());
    } finally{
      // Close the channel if it is no longer needed in this thread
       channel.close();
   }

}     }

这可能就足够了,你在系统启动时与rabbitmq有联系。

如果你是一个懒惰的单身人士,代码就有点不同了。

我建议不要使用isOpen()方法,请阅读here

  

ISOPEN

     

boolean isOpen()确定组件当前是否打开。   如果我们正在关闭,将返回false。检查此方法   应该只是为了获取信息,因为竞争条件 - 国家   电话结束后可以改变。而只是执行并尝试捕获   ShutdownSignalException和IOException返回:组件时为true   是公开的,否则是假的

修改**

问题1:

您在寻找HA客户端。

RabbitMQ java客户端默认不支持此功能,因为版本3.3.0仅支持重新连接,请阅读this

  

...允许基于Java的客户端在网络后自动重新连接   失败。如果你想确定你的消息,你必须创建一个   强大的客户能够抵抗所有失败。

通常你应该考虑失败,例如: 如果在发布消息期间出现错误会发生什么?

在您的情况下,您只是丢失了消息,您应该手动重新排队消息。

问题2:

我不知道您的代码,但connection == null不应该发生,因为首先调用此过程:

@PostConstruct
    private void init() {
      start(); /// In this way you can initthe connection and you are sure it is called only one time.
    }

无论如何你可以提出异常,问题是: 我该怎么处理我试图发送的消息?

见问题1

我想建议阅读有关HA的更多信息,例如:

使用rabbitmq创建一个可靠的系统并不复杂,但您应该了解一些基本概念。

无论如何..让我知道!