使用senecajs实施扇出策略

时间:2017-06-04 23:59:15

标签: node.js node-amqp seneca

我正在使用senecajs开发一个node.js社交网络应用程序,并且需要实现一个生产者可以向多个消费者发送相同消息的场景。我发现了一篇文章,似乎说明了使用senecajs来解决这个问题的示例代码。问题在于我试图将此翻译为我的场景,这是示例,来自本文(https://github.com/senecajs/seneca-amqp-transport/issues/27):

我有2个客户端发布事件给2个听众。

客户端:

.client({
type: 'amqp',
pin: 'incomingMessage:*',
url: process.env.AMQP_URL,
exchange: {
  name: process.env.NODE_ENV + ':events',
  type: 'fanout'
}
});

监听器:

.listen({
type: 'amqp',
pin:  'incomingMessage:*', //maybe useless
url:  process.env.AMQP_URL,
name: process.env.NODE_ENV + ':service1',
exchange: {
  name: process.env.NODE_ENV + ':events',
  type: 'fanout'
}
});

.listen({
type: 'amqp',
pin:  'incomingMessage:*', //maybe useless?
url:  process.env.AMQP_URL,
name: process.env.NODE_ENV + ':service2',
exchange: {
  name: process.env.NODE_ENV + ':events',
  type: 'fanout'
}
});

有一些项目令人困惑:

  1. 对于客户端设置,似乎该名称最终将成为“development:events”或“production:events”。我在这个想法中是否正确?

  2. 对于交换对象之外的侦听器的名称字段,此字段的用途是什么?

  3. 当我调用add方法时,我需要传入一个映射到侦听器收到消息时所进行的函数调用的名称,我是否会将“incomingMessage:*”传递给add调用?

  4. 此代码是否实际上有效地提供了使用senecajs的扇出功能?

1 个答案:

答案 0 :(得分:1)

我测试了样品。 (在GitHub上为作者提供的信用)

1,通常NODE_ENV环境变量是指开发,生产,升级等。 它可以是任何东西(比如“apple”),但诀窍是这个“name”属性指的是“queue”名称,它必须对每个监听器/用户都是唯一的。

2,就像我上面提到的,这个“name”属性(在exchange对象之外)是队列的名称。 每个侦听器都必须是唯一的。每个侦听器都有自己的队列,该队列绑定到交换机。

3,它可以是任何东西。但这也很棘手。

对于将“发布”到扇出交换的客户端,它需要在选项中具有与被调用操作模式相同的引脚。例如,如果客户端将调用:

seneca.act('event:orderReceived', optionalPayload)

那么引脚必须是:

{
    pin: 'event:orderReceived',
    ...
}

请参阅Seneca API docs了解模式的工作原理。

对于侦听器,在这种情况下(对于RPC,这不是真的),它们不必匹配引脚。当你调用seneca.add函数时,只有模式必须匹配(有点奇怪)。例如:

seneca.add('event:orderReceived', function (msg, respond) {
    // some logic
})

pin属性可以是:

{
     pin: 'something:different'
}

4,对于最后一个问题答案是否正确,它可以提供发布/订阅功能。我测试了它。

请注意,如果在“发布”到交换时没有将回调传递给seneca.act,那么它不会等待任何响应。例如:

seneca.act('event:orderReceived', { some: "payload" })

另请注意,如果您不需要响应,则必须在侦听器服务中使用null为第一个(错误)参数调用响应回调,否则它将超时。请参阅以下示例。

这是我的代码版本:

<强>发布商

const seneca = require('seneca')
const si = seneca()

si
    .use('seneca-amqp-transport')

    .client({
        type: 'amqp',
        pin: 'event:orderReceived',
        url: 'amqp://localhost:5672',
        exchange: {
            name: 'order-service',
            type: 'fanout'
        }
    })
    .ready( function() {
        si.log.info('order-service application is running...')
        si.act('event:orderReceived', {})
    })

process.on('SIGTERM', () => {
    si.log.info('Got SIGTERM. Graceful shutdown start')
    si.act('role:seneca,cmd:close')
})

订阅者1

const seneca = require('seneca')
const si = seneca()

si
    .use('seneca-amqp-transport')

    .add('event:orderReceived', function(msg, respond) {
        si.log.info(msg)
        respond(null)
    })

    .listen({
        type: 'amqp',
        pin:  'some:pin1',
        url:  'amqp://localhost:5672',
        name: 'delivery-service-queue',
        exchange: {
            name: 'order-service',
            type: 'fanout'
        }
    })

    .ready( function() {
        si.log.info('delivery-service application is running...')
    })

process.on('SIGTERM', () => {
    si.log.info('Got SIGTERM. Graceful shutdown start')
    si.act('role:seneca,cmd:close')
})

订阅者2

const seneca = require('seneca')
const si = seneca()

si
    .use('seneca-amqp-transport')

    .add('event:orderReceived', function(msg, respond) {
        si.log.info(msg)
        respond(null)
    })

    .listen({
        type: 'amqp',
        pin:  'some:pin2',
        url:  'amqp://localhost:5672',
        name: 'financial-service-queue',
        exchange: {
            name: 'order-service',
            type: 'fanout'
        }
    })

    .ready( function() {
        si.log.info('financial-service application is running...')
    })

process.on('SIGTERM', () => {
    si.log.info('Got SIGTERM. Graceful shutdown start')
    si.act('role:seneca,cmd:close')
})

我不确定这是一种正确的方法,但似乎有效。