프로그래밍/서버, DBMS

[서버] Node.js Redis Only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context 오류 해결

포도알77 2019. 3. 11. 08:22

 Socket.io를 이용하여 데이터 크롤링 결과를 실시간으로 뿌려주는 기능을 구현하였다. 



 물론 Node.js를 두개로 나누어 웹 서비스용과 데이터 크롤링 및 전송용으로 나눠서 사용했지만, Node.js http request 모듈이 특정 사이트에 가면 timeout이 생기는 문제가 발생했다.(Node.js가 Non-blocking이 아니었나..)



 그래서 서버를 웹 서비스 / 데이터 크롤링 / 데이터 전송 세가지로 나누고, 데이터 크롤링 서버가 데이터를 redis 상에 업데이트 하면 이 정보를 데이터 전송 서버에 전달하도록 구현하는데 redis publish, subscribe를 사용했다. 



 우선 데이터 크롤링에서 얻은 데이터는 실시간 데이터기 때문에 소실되어도 문제가 크게 없어서, redis pub/sub이 적당하였다. 사실 MQ(메시지큐)를 쓰게되면, 오히려 데이터를 체크하고 제거하는게 문제가 될 수도 있었다. (Scale Out을 고려하면 더욱) 



 아무튼 인터넷상에 다양한 예제들을 보면서 대충 구현했는데, 문제가 생겨버렸다.   

Only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context


 오류가 발생했는데, 어제 처음 레디스를 사용해본 것이라 멘탈이 탈탈 털려버렸다. 


 이 오류는 subscribe 하고 있는 redis connection이 저 위의 명령 외의 다른 명령(데이터를 가져온다던지, 입력한다던지)을 수행하면, 현재 커넥션은 subscribe중이라 다른 동작은 할 수 없다는 뜻이었는데, 당연히 커넥션 재활용은 하지 않은 상태. 


 일반적으로 인터넷 예제에서는 pub/sub 커넥션 2개와 데이터 관리용 커넥션 1개, 총 3개를 사용한다. 나도 당연히 이를 참조한거기 때문에 커넥션 3개 이상을 사용했다.(디버깅할때는 3개로 해봤지만) 근데 안되네..  


 진짜 별 삽질을 다해봤는데, 이유를 찾을 수 없었다. 두어시간 날려버리고, 우연치 않게 redis-cli에서 커넥션 리스트를 확인하면서 알 수 있었는데... 결론은 커넥션이 2개밖에 안생겼다. 즉 커넥션 생성이 문제가 있었던 것.  


 왜일까... 우선 내 코드는 모듈화를 위해서 따로 redis-client.js를 작성하고 이를 require 명령으로 import후 사용하는 것이다.

redis-client.js
// redis-client.js
var redis = require('redis');
var redisConfig = require('./redis-client-info');
module.exports = (function(){
  var rds = new redis.createClient(redisConfig.port, redisConfig.host);
  return {
    client : rds
  };
}());


sending-server.js
// 기존 코드
// redis-client.js를 바탕으로 새로 만든 모듈들
var rdb = require('./redis-db');
var crdb = reuqire('./redis-db'); 
var rpub = require('./pub-sub.js');
var rsub = require('./pub-sub.js');


pub-sub.js
module.exports = function(){
  var client = require('./util/redis-client').client; 
  function subscribe(channel){
    client.subscribe(channel);
  }
  function publish(channel, data){
    client.publish(channel, data);
  }
  function ping(channel){
    client.ping(channel);
  }
  function onMsgListener(callback){
    client.on("message", function(channel, message){
      callback(channel,message);
    });
  }
  return {
    subscribe:subscribe,
    publish:publish,
    ping:ping,
    client:client,
    onMsgListener:onMsgListener
  };
}();



 변경한 코드 
 pub-sub.js
var rutils = require('./util/redis-utils');
var redisConfig = require('./util/redis-client-info');
var redis = require('redis');

module.exports = function(){
 var client = new redis.createClient(redisConfig.port, redisConfig.host);
 function subscribe(channel){
 client.subscribe(channel);
 }
 function publish(channel, data){
 client.publish(channel, data);
 }
 function ping(channel){
 client.ping(channel);
 }
 function onMsgListener(callback){
 client.on("message", function(channel, message){
 callback(channel,message);
 });
 }
 return {
 subscribe:subscribe,
 publish:publish,
 ping:ping,
 client:client,
 onMsgListener:onMsgListener
 };
}();


 즉, 그냥 pub-sub 소스코드에서 redis의 createClient를 호출했다. 왜 안되던 것이었을까? 심지어 동일한 JS 모듈을 호출하는게 여러개나 있었는데! 정말 이해하지 못하겠다. 그래서 나는 버그라고 자기 위로를 하고있다.  


 혹시 아시는분 있다면 댓글로 알려주세요.  



페이스북으로 공유카카오톡으로 공유카카오스토리로 공유트위터로 공유URL 복사