使用spring-websocket和RabbitMQ代理订阅已删除的队列(Queue NOT_FOUND)

时间:2016-01-18 17:10:43

标签: spring websocket rabbitmq stomp spring-websocket

我在Tomcat8上有一个spring-websocket(4.1.6)应用程序,它使用STOMP RabbitMQ(3.4.4)消息代理进行消息传递。当客户端(Chrome 47)启动应用程序时,它会订阅创建持久队列的端点。当此客户端取消订阅端点时,RabbitMQ将在30秒后清除队列,如定制的RabbitMQ策略中所定义。当我尝试重新连接到具有已清理队列的端点时,我在RabbitMQ日志中收到以下异常:“NOT_FOUND - 在vhost中没有队列'position-updates-user9zm_szz9'/'\ n”。我不想使用自动删除队列,因为我有一些重新连接逻辑,以防websocket连接死亡。

通过将以下代码添加到spring-websocket-portfolio github示例中,可以重现此问题。

在index.html的容器div中添加:

stompClient.subscribe("/user/queue/position-updates", function(message) {

在portfolio.js中替换:

positionUpdates = stompClient.subscribe("/user/queue/position-updates", function(message) {

使用:

  self.unsubscribe = function() {
    positionUpdates.unsubscribe();
  }

  self.subscribe = function() {
    positionUpdates = stompClient.subscribe("/user/queue/position-updates", function(message) {
      self.pushNotification("Position update " + message.body);
      self.portfolio().updatePosition(JSON.parse(message.body));
    });
  }

并添加以下内容:

<?php

// configuration
require("../includes/config.php"); 

$rows = CS50::query("SELECT symbol, shares FROM portfolios WHERE user_id = ?", $_SESSION["id"]);
$cash = CS50::query("SELECT cash FROM users WHERE id = ?", $_SESSION["id"]);

$positions = [];
foreach ($rows as $row)
{
    $stock = lookup($row["symbol"]);
    $total = ($stock["price"] * $row["shares"]);

    if ($stock !== false)
    {
        $positions[] = [
            "name" => $stock["name"],
            "price" => $stock["price"],
            "shares" => $row["shares"],
            "symbol" => $row["symbol"],
            "total" => $total,
            "cash" => $cash[0]["cash"]
        ];
    }
}

// render portfolio
render("portfolio.php", ["positions" => $positions, "title" => "Portfolio"]);
?>

现在您可以通过以下方式重现问题:

  1. 启动应用程序
  2. 点击取消订阅
  3. 删除RabbitMQ控制台中的位置更新队列
  4. 点击订阅
  5. 通过chrome devtools和RabbitMQ日志在websocket框架中查找错误消息。

2 个答案:

答案 0 :(得分:1)

  

在websocket连接断开的情况下重新连接逻辑。

  

没有队列&#39; position-updates-user9zm_szz9&#39;在vhost中

完全不同的故事。

我建议您实施&#34;重新订阅&#34;删除队列时的逻辑。

实际上这就是STOMP的工作原理:它为订阅创建auto-deleted(生成的)队列,是的,它在unsubscrire上被删除。

在RabbitMQ STOMP Adapter Manual中查看更多信息。

从另一方面考虑订阅现有的AMQP队列:

  

要解决在STOMP适配器外部创建的现有队列,可以使用/amq/queue/<name>格式的目标。

答案 1 :(得分:0)

问题是,如果被RabbitMQ策略删除,Stomp将不会重新创建队列。我通过在SessionSubscribeEvent被触发时自己创建队列来解决这个问题。

public void onApplicationEvent(AbstractSubProtocolEvent event) {
   if (event instanceof SessionSubscribeEvent) {
      MultiValueMap nativeHeaders = (MultiValueMap)event.getMessage().getHeaders().get("nativeHeaders");
      List destination = (List)nativeHeaders.get("destination");
      String queueName = ((String)destination.get(0)).substring("/queue/".length());

      try {
         Connection connection = connectionFactory.newConnection();
         Channel channel = connection.createChannel();
         channel.queueDeclare(queueName, true, false, false, null);
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}