2016-03-11 1 views
0

У меня есть приложение Express, которое требует очень низкой скорости ответа ~ < 200ms. Сейчас мы можем получить только этот номер, но это отдельная тема.Любой способ уменьшить количество одновременных запросов для извлечения данных и кеша в nodejs?

Мы планируем извлечь часть данных из базы данных, если они найдены в Redis, возвращают данные, если нет, тогда запустите запрос и сохраните его, чтобы повторить запрос, чтобы следующие запросы могли получить его из Redis.

Я запускаю некоторые тесты и задавался вопросом, есть ли способ уменьшить количество запросов на получение базы данных?

Например, в настоящее время наше приложение имеет 300 рек/с в поле. У нас шесть боксов, работающих на AWS. Если в первый раз эта часть данных недоступна в Redis, может быть около ~ 500 запросов, пытающихся извлечь данные из БД и кеша, что в Redis. Мы пытаемся уменьшить это число. Не уверен, что в Node.js или Redis есть способ справиться с этим.

Вот код, который я тестирую.

client.getAsync('key').then(function (data) { 
    if(data) { 
    console.log(data); // Return this data if found 
    res.send(data); 
    } else { 
    // I'm trying to reduce the number of calls for concurrent requests in this block. 
    console.log('not found'); 
    var dataFromDb = // fetch data from DB 
    client.set('key', dataFromDb); // Fire and forget 
    res.send('not found'); // Return not found right away 
    } 
}); 

И я испытываю вызов с помощью ab

ab -n 20 -c 10 http://localhost:8081/redis 

Это результаты, которые я получил

not found 
not found 
not found 
not found 
not found 
not found 
something 
not found 
something 
something 
something 
something 
something 
something 
something 
something 
something 
something 

В этом примере есть 7 запросов, пытающихся извлечь базу данных с теми же данными и сохранить в Redis.

Мой вопрос в том, есть ли способ уменьшить количество запросов? Поскольку выборка DB довольно медленная на данный момент ~ 900 мс (мы пытаемся ее оптимизировать)

+0

Это зависит. Например, вы можете разогревать свой кеш при его покраснении или уничтожении или при повторной публикации приложения вы можете сделать распределенную блокировку, когда вы извлекаете данные из БД, чтобы разрешить доступ только одному клиенту, и после этого получить его из redis –

+0

Спасибо за ваш ответ. Любой пример, которому я могу следовать? Я не знаком с распределенной блокировкой. – toy

+0

есть алгоритм redlock, предоставляемый командой Redis –

ответ

1

Да, есть. Я сделал то же самое. Здесь я опишу только логику. Метод fetchCache должен вернуть обещание. Также вы сохраняете массив {cacheKey, обещание}. Каждый раз, когда вы отправляете запрос, вы добавляете ключ к этому массиву. Когда в следующий раз вам нужно будет извлечь кеш, сначала проверяете массив, и если ключ найдет это обещание. Повторное вызов fetchCache.

Вот мой код. Он работает, но, вероятно, трудно читать. Должно дать вам общее понимание.

class DictTranslatableRepo { 

    constructor(model) { 
     var self = this; 
     self.title = model + "s Repo"; 

     self.model = models[model]; 
     self.running = {}; 
     self.curItems = {}; 
    } 


    *start() { 
     var self = this; 
     var curItems = yield self.model.findAll(); 
     _.forEach(curItems, function(row) { 
      self.curItems[row.key] = row.value; 
     }); 
    }; 


    *map(from) { 
     var self = this; 

     if (from == "") return ""; 

     if (!_.isUndefined(self.curItems[from])) return self.curItems[from]; 

     if (_.isUndefined(self.running[from])) { 
      self.running[from] = []; 
      return new Promise(function(resolve, reject) { 
       self.running[from].push(resolve); 

       self.job(from, function(err, to) {  // Main job 
        var callbackArr = self.running[from]; 
        delete self.running[from]; 
        _.forEach(callbackArr, function(callback) { 
         callback(to); 
        }); 
       }); 
      }); 
     } else { 
      return new Promise(function(resolve, reject) { 
       self.running[from].push(resolve); 
      }); 
     } 
    }; 


    job(from, callback) { 
     var self = this; 
     var to = "as shown"; 
     co(function*() { 
      try { 
       to = yield translator.translate(from); 
       yield self.model.add({key: from, value: to}); 
       self.curItems[from] = to; 
       callback(null, to); 
      } catch (err) { 
       callback(err); 
       //logger.error("Cant translate entity: " + from); 
      } 
     }).catch(function(err) { 
      // Unhandled Error 
      callback(new Error(err)); 
     }); 
    }; 

} 

Мой map метод ваш метод fetchCache.