使用RabbitMQ的可靠消息传递

使用RabbitMQ的可靠消息传递,rabbitmq,amqp,Rabbitmq,Amqp,我有一个通过RabbitMQ发送AMQP消息的应用程序。消息发送在http请求时触发。最近,我注意到一些消息似乎丢失了(如“从未传递”)。我还注意到,由服务器管理的频道列表正在稳步增加。我纠正的第一件事是在不再需要频道后关闭频道。然而,我仍然不确定我的代码是否正确构造以确保交付。下面是代码的两部分;第一个是管理连接的单例的一部分(不是每次调用都重新创建),第二个是发送代码。如有任何建议/指导,将不胜感激 @Service public class PersistentConnection {

我有一个通过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();
}
请尝试以下代码:

@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()
方法,请阅读:

等参

布尔值isOpen()确定组件当前是否打开。 如果我们当前正在关闭,将返回false。检查此方法 应仅供参考,因为比赛条件-状态 你可以在电话后换衣服。相反,只是执行并尝试捕获 ShutdowSignalException和IOException返回:当组件 是开放的,否则是虚假的

编辑**

问题1:

你要找的是医管局的客户

默认情况下,RabbitMQ java客户端不支持此功能,因为版本3.3.0仅支持重新连接,读取:

…允许基于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

我建议大家多读一些有关医管局的资料,例如:

  • 这是给客户的:
  • (我从来没用过)
使用rabbitmq创建可靠的系统并不复杂,但您应该了解一些基本概念


无论如何。。让我知道

能否从更多线程调用
getConnection()
?如果是,则代码不是线程安全的。http请求最终会调用getConnection()调用。所以我想它可以。我想通过让它成为一个单身汉可以解决这个问题。改进代码的最佳方法是什么?非常感谢。我将在今晚完成这项工作,明天加载代码并更新你。我刚刚开始看这个,我有几个问题。首先,如果RabbitMQ服务器重新启动会发生什么情况(例如)-我假设连接将失败,并且在服务器启动时不会自动重新连接?有没有办法自动重新连接?另外,我认为如果connection==null,send方法可能会抛出一个异常-这有意义吗?这非常有用。我已经加载了新代码,它似乎工作得很好。我还通读了参考资料,找到了你的书:)。我不认为我的申请真的需要莱拉,但我会记住它的未来。再次感谢您。@skyman如果可靠性有问题,请查看Lyra。试图推出自己的HA客户端是一件很难的事情,你要么没有涵盖所有可能的场景,要么最终实现了类似Lyra:)@Jonathan谢谢你的评论。我似乎仍然有一些小问题——看看莱拉