Redis pub/sub для асинхронного ответа/ожидания ответа на Nodejs

У меня есть два приложения NodeJs Express, связанные с двумя разными базами данных, для удобства назовем первое приложение A и второе B, оба связаны с одним и тем же экземпляром Redis с помощью Ioredis.

A не может напрямую получить доступ к базе данных B, ему необходимо выполнить HTTP-запрос, чтобы получить данные из базы данных B.

В конечной точке приложения А мне нужно проверить значение из базы данных приложения Б, это конечная точка с очень высоким трафиком и сотнями запросов в минуту, я не могу просто выполнить http-запрос для получения данных из приложения Б, потеря производительности. будет слишком высоким.

Решение, которое я нашел, — это redis Pub/Sub, я могу зарегистрироваться с двух каналов в приложении A и B.

Если требуемое значение отсутствует в Redis, я буду использовать два канала:

Один канал для отправки сообщений из канала приложения A в приложение B для сохранения объектов из базы данных в Redis.

Второй канал для отправки сообщений из приложения B в приложение A, чтобы сохранить и вернуть объект, полученный из базы данных.

Мой вопрос: есть ли способ в вызове первого канала приложения A ожидать ответного ответа от приложения B через второй канал?

Может ли что-то подобное работать?

Базовый POC

  async function main() {

    await redis.subscribe("get-company", (err, count) => {
      if (err) console.error(err.message);
    });

  
    redis.on("message", (channel, message) => {
      console.info(`Received first message from ${channel} channel.`);
      redis.publish('send-company', 'test')
      console.info(message);
    });
    main2()
  }

  async function main2() {
    await redis2.subscribe("get-company", (err, count) => {
      if (err) console.error(err.message);
    });

    redis2.publish('get-company', 'test').then(async (data)=>{
      console.info('first send completed');
      redis2.on("message", (channel, message) => {
        console.info(`Received response from ${channel} channel.`);
        console.info(message);
        redis2.unsubscribe();
      })
    })
  }

main()
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
96
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете сделать что-то вроде ниже. Обратите внимание, что это базовый макет. Пожалуйста, обновите логику в соответствии с вашими потребностями.

Первым шагом было бы разделить два приложения для ясности.

В приложении А:

  1. Будем подписываться на канал send-company
  2. Мы опубликуем конкретное сообщение на канале get-company внутри обещания.
  3. Мы подождем и разрешим сообщение, как только ответ будет получен в канале компании-отправителя внутри обещания.
// import necessary modules as per your need
// import necessary modules as per your need
const Redis = require('ioredis');
const redisAppASub = new Redis();
const redisAppAPub = new Redis();

async function appA(){

    await redisAppASub.subscribe("send-company", (err, count) => {
        if (err) console.error(err?.message);
    });

    redisAppASub.on("message", async (channel, message) => {
        if (channel === "send-company") {
            console.info(message);

            // handle the received message here

            // unsubscribe if required
            // redisAppA.unsubscribe();
        }
    });

    // Possible solution for the query ["My question is, is there a way in the application A first channel call to await a return response from application B through the second channel ?"]

    async function requestDataFromAppB(){
        return new Promise((resolve, reject) => {
            redisAppAPub.publish("get-company", "requestDBData", (err) => {
                if (err) return reject(err);

                console.info("request sent to Application B");
            });

            // Temporary subscription to listen and resolve the response
            redisAppASub.once("message", (channel, message) => {
                if (channel == "send-company"){
                    resolve(message)
                }
            });
        });
    }

    // call the requestDataFromAppB and get the response
    const response = await requestDataFromAppB();        
    console.info(response, " <<--------- response received from Application B");               
}

// call as per your need
appA().catch(err => console.error(`Application A error: ${err?.message}`));

В приложении Б:

// import necessary modules as per your need
const Redis = require('ioredis');
const redisAppBSub = new Redis();
const redisAppBPub = new Redis();

async function appB(){
    console.info("Application B is starting...");

    await redisAppBSub.subscribe("get-company", (err, count) => {
        if (err) console.error(err?.message);
    });

    redisAppBSub.on("message", async (channel, message) => {
        if (channel == "get-company" && message == "requestDBData"){
        const dbData = await fetchDataFromDatabase();
        console.info(dbData, ` <<----- dbData`);

        redisAppBPub.publish("send-company", dbData, (err) => {
            if (err) console.info(err?.message)
            console.info("response sent to Application A")
        });
        }
    });

    async function fetchDataFromDatabase() {
        // Your data fetching logic here

        // Simulate fetching data with a delay using a Promise
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve(JSON.stringify({data: "data from database"}));
            }, 5000);
        });
    }
}

// call as per your need
appB().catch(err => console.error(`Application B error: ${err?.message}`));

Не за что. Я обновил код и добавил два соединения Redis. Один для подписки, а другой для публикации, так как у меня возникали ошибки. Поэтому, пожалуйста, имейте это в виду и проверяйте тоже со своей стороны. Приятного кодирования :)

Khaled Hasan 11.06.2024 10:25

Другие вопросы по теме