Angularjs 节点amqp+;socketio:如何同步队列订阅/取消订阅?

Angularjs 节点amqp+;socketio:如何同步队列订阅/取消订阅?,angularjs,rabbitmq,amqp,socket.io,node-amqp,Angularjs,Rabbitmq,Amqp,Socket.io,Node Amqp,我有一个angularjs应用程序,服务器端有node和express。 我还有节点amqp和socket.io 我想实现以下行为 该应用程序有一个页面(路线、角度视图),显示包含实时数据的表格 使用socket.io和amqp实时更新数据,以从位于应用程序外部的rabbitMQ服务器流式传输数据 当用户在浏览器上访问此页面/路线时 客户端发出套接字事件“subscribe” 服务器,在套接字事件“subscribe”上, 声明一个兔子队列 将兔子队列绑定到exchange 订阅兔子队列中的消

我有一个angularjs应用程序,服务器端有node和express。 我还有节点amqp和socket.io

我想实现以下行为

该应用程序有一个页面(路线、角度视图),显示包含实时数据的表格 使用socket.io和amqp实时更新数据,以从位于应用程序外部的rabbitMQ服务器流式传输数据

当用户在浏览器上访问此页面/路线时

  • 客户端发出套接字事件“subscribe”
  • 服务器,在套接字事件“subscribe”上,
    • 声明一个兔子队列
    • 将兔子队列绑定到exchange
    • 订阅兔子队列中的消息/数据
    • 发出套接字事件“data”,将数据发送回用户/客户端
  • 当用户离开页面,或者换言之,更改路由时

  • 客户端发出套接字事件“unsubscribe”
  • 服务器,在套接字事件“unsubscribe”上,
    • 从队列中取消订阅
  • 我的问题是:如何确保queue.subscribe和queue.unsubscribe同步? 如果用户执行快速的路线更改顺序:访问/离开/访问/离开/访问/离开 订阅和取消订阅的顺序有时会恢复,服务器会在新订阅完成之前第二次取消订阅上一次订阅。有什么建议吗? 这是我尝试过的,但不起作用:

    客户端:controller.js

    .controller('WatchdogCtrl', function($scope, watchSocket) {
    
        var data = {}
        $scope.data = []
    
        var socket = watchSocket
    
        socket.emit('subscribe', { exchange: 'bus', key: 'mis.service-state' })
        socket.on('data', function(message) {
            // refreshing  data 
            data[message.payload.id] = message.payload;
            var new-values = [];
            angular.forEach(data, function(value, index) {
                this.push(value);
            }, new-values);
    
            $scope.data = new-values
            $scope.$apply()
        });
    
        $scope.$on('$destroy', function (event) {
            // unsubscribe from rabbit queue when leaving 
            socket.emit('unsubscribe')
        });
    })
    
    // set up amqp listener
    var amqp = require('amqp');
    // create rabbitmq connection with amqp
    var rabbitMQ = amqp.createConnection({url: "amqp://my:url"});
    rabbitMQ.on('ready', function() {
        console.log('Connection to rabbitMQ is ready')
    });
    
    // Hook Socket.io into Express
    var io = require('socket.io').listen(server);
    io.set('log level', 2);
    io.of('/watch').on('connection', function(socket) {
        var watchq;
        var defr;
        socket.on('subscribe', function(spec) {
            watchq = rabbitMQ.queue('watch-queue', function(queue) {
                console.log('declare rabbit queue: "' + queue.name +'"');
                console.log('bind queue '+ queue.name + ' to exch=' + spec.exchange + ', key=' + spec.key);
    
                queue.bind(spec.exchange, spec.key)
                defr = queue.subscribe(function(message, headers, deliveryInfo) {
                         socket.emit('data', {
                            key: deliveryInfo.routingKey,
                            payload: JSON.parse(message.data.toString('utf8'))
                         })
                       }).addCallback(function(ok) { 
                           var ctag = ok.consumerTag; 
                           console.log('subscribed to queue: ' + queue.name + ' ctag = ' + ctag)
                       });
    
            })
        })
    
        socket.on('unsubscribe', function() {
            //needs fix: this does not ensure subscribe/unsubscribe synchronization…..
            defr.addCallback(function(ok) {
                console.log('unsubscribe form queue:', watchq.name, ', ctag =', ok.consumerTag)
                watchq.unsubscribe(ok.consumerTag);
            })
        })
    
    });
    
    服务器端:Server.js

    .controller('WatchdogCtrl', function($scope, watchSocket) {
    
        var data = {}
        $scope.data = []
    
        var socket = watchSocket
    
        socket.emit('subscribe', { exchange: 'bus', key: 'mis.service-state' })
        socket.on('data', function(message) {
            // refreshing  data 
            data[message.payload.id] = message.payload;
            var new-values = [];
            angular.forEach(data, function(value, index) {
                this.push(value);
            }, new-values);
    
            $scope.data = new-values
            $scope.$apply()
        });
    
        $scope.$on('$destroy', function (event) {
            // unsubscribe from rabbit queue when leaving 
            socket.emit('unsubscribe')
        });
    })
    
    // set up amqp listener
    var amqp = require('amqp');
    // create rabbitmq connection with amqp
    var rabbitMQ = amqp.createConnection({url: "amqp://my:url"});
    rabbitMQ.on('ready', function() {
        console.log('Connection to rabbitMQ is ready')
    });
    
    // Hook Socket.io into Express
    var io = require('socket.io').listen(server);
    io.set('log level', 2);
    io.of('/watch').on('connection', function(socket) {
        var watchq;
        var defr;
        socket.on('subscribe', function(spec) {
            watchq = rabbitMQ.queue('watch-queue', function(queue) {
                console.log('declare rabbit queue: "' + queue.name +'"');
                console.log('bind queue '+ queue.name + ' to exch=' + spec.exchange + ', key=' + spec.key);
    
                queue.bind(spec.exchange, spec.key)
                defr = queue.subscribe(function(message, headers, deliveryInfo) {
                         socket.emit('data', {
                            key: deliveryInfo.routingKey,
                            payload: JSON.parse(message.data.toString('utf8'))
                         })
                       }).addCallback(function(ok) { 
                           var ctag = ok.consumerTag; 
                           console.log('subscribed to queue: ' + queue.name + ' ctag = ' + ctag)
                       });
    
            })
        })
    
        socket.on('unsubscribe', function() {
            //needs fix: this does not ensure subscribe/unsubscribe synchronization…..
            defr.addCallback(function(ok) {
                console.log('unsubscribe form queue:', watchq.name, ', ctag =', ok.consumerTag)
                watchq.unsubscribe(ok.consumerTag);
            })
        })
    
    });
    
    Server console.log消息:(访问#3和离开#3不同步)

    声明兔子队列:“监视队列”
    将队列监视队列绑定到exch=bus,key=mis.service-state
    
    订阅队列:watch queue ctag=node-amqp-8359-0.6418165327049792/我们的设置与您的非常相似。我们创建一个匿名的、独占的队列,如果未使用该队列,则该队列将有一个过期时间匿名队列获取代理为其生成的唯一名称独占一旦客户端断开连接(一旦通道断开),队列就会被删除队列的过期时间是一个RabbitMQ扩展,但由我们使用的amqplib支持。我相信node-amqp也支持这种扩展

    还为每个套接字创建一个通道(但重复使用相同的连接)。这提供了套接字和匿名队列之间的一对一映射。对该队列的任何绑定都相当于单个套接字的绑定。正因为如此,我们天生就知道什么套接字应该获得什么消息,而不需要任何队列的特殊命名约定或路由密钥检查等

    当套接字关闭时,关闭RabbitMQ通道(同样,不是连接)。不需要特殊的退订活动,尽管我们可能会在以后添加此类活动

    这也意味着,如果同一浏览器在没有任何竞争条件的情况下打开了多个选项卡,那么它们可以有多个队列