Как отправить данные с абонента Redis на экспресс-маршрут - PullRequest
0 голосов
/ 20 декабря 2018

У меня есть клиент redis pubsub, в котором издатель находится в одном файле, а подписчик - в другом, и он отлично работает.

У меня есть 2 контроллера: домашний контроллер, который обрабатывает маршрут '/', и контроллер данных, который обрабатывает '/data 'route

Внутри моего подписчика redis я хочу обновить состояние переменной, которую я постоянно получаю от издателя

Как отправить это состояние обоим контроллерам, когда они выполняют запрос

Я делал

app.get('/', (req, res) => {
    c = redis.createClient()
    c.on("message", (channel, message) => {
    // Send data here
    })
})

Это не выглядит хорошей идеей, это создание нового КЛИЕНТА для каждого запроса к конечной точке '/'

Я хочу бытьумеет делать

// home controller file
app.get('/', (req, res) => {
    res.json(state)
})
// data controller file
app.get('/data', (req, res) => {
    res.json(state)
})

Как реализовать это состояние

1 Ответ

0 голосов
/ 02 июня 2019

Проведя некоторое исследование, я решил использовать нодальный модуль событий Node для решения этой проблемы.В этом примере вместо * node_redis используется ioredis , но принцип тот же.

Сначала я создаю три клиента Redis.Один для обычной работы с БД, издатель и подписчик

/* redis.js */

const Redis = require('ioredis');

const redis = new Redis();
const publisher = new Redis();
const subscriber = new Redis();

// redis is the defaut export
// publisher and subscriber are "named" exports

const client = (module.exports = redis);
client.publisher = publisher;
client.subscriber = subscriber;

Затем мы создаем узел EventEmitter, который будет генерировать событие каждый раз, когда подписчик получает сообщение от канала в redis.

/* emitter.js */

const EventEmitter = require('events');
const { subscriber } = require('./redis');

const eventEmitter = new EventEmitter();

subscriber.subscribe('my-channel', err => {
  if (err) { return console.log('Unable to subscribe to my-event channel') };
  console.log('Subscription to my-event channel successful');
});

subscriber.on('message', (channel, message) => {
  eventEmitter.emit('my-event', message);
});

module.exports = eventEmitter;

Здесь у нас есть два маршрута.Первый обрабатывает запрос PUT, который устанавливает поле в redis, а затем публикует сообщение на канал с ключом хэша, который был обновлен.Второй маршрут обрабатывает GET-запрос, который остается открытым (например, в качестве EventSource для соединения SSE).Он прослушивает событие от эмиттера, а затем отправляет данные обновленного ключа из redis

/* route.js*/

const express = require('express');
const redis = require('./redis');
const { publisher } = require('./redis');
const { eventEmitter } = require('./emitter');

const router = express.Router();

router.put('/content', async (req, res) => {
  const { key, field, content } = req.body;
  try {
    await redis.hset(key, field, content);
    res.sendStatus(200);
    return publisher.publish('my-channel', key);

  } catch(err) {
     res.status(500).send(err.message);
  }  
});

router.get('/content-stream', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    Connection: 'keep-alive'
  });

  res.write('\n');

  const handleEvent = async key => {
     try {
      const query = await redis.hgetall(key);
      res.write(`data: ${JSON.stringify(query)}\n\n`);
    } catch(err) {
      console.log('Something went wrong');
    }
  }

  eventEmitter.addListener('my-event', handleEvent);
  req.on('close', eventEmitter.removeListener('my-event', handleEvent));

module.exports = router;

Это эффективно позволит вам избежать создания новых клиентов redis при каждом подключении.Возможно, есть лучшие способы сделать это, но это то, что сработало для меня.

...