Published on

Socket 서버 구현 1 (Master & Worker Cluster 관리)

1. pm2 를 통한 Master & Worker Process 생성

  • exec_mode는 cluster로 설정한다. cluster모드로 설정하면, cluster 모듈을 사용하여 워커를 생성하였을 때처럼 서버의 포트를 공유하고, Round Robin 방식으로 로드밸런싱을 pm2에서 관리 해준다.

  • out_file과 error_file을 /dev/null 로 설정하여 자동 로깅을 diable한다.

pm2.start(
  {
    name: 'Master',
    script: 'master.js',
    args: ['master'],
    exec_mode: 'cluster',
    instances: 1,
    out_file: '/dev/null',
    error_file: '/dev/null',
  },
  function (err, app) {
    if (err) {
      console.log('master start error!');
      return;
    }
    var i,
      len = 20,
      workerId;

    for (i = 0; i < len; i += 1) {
      workerId = '' + (i + 1);
      pm2.start(
        {
          name: 'Worker',
          script: 'worker.js',
          args: [workerId],
          exec_mode: 'cluster',
          instances: 1,
          out_file: '/dev/null',
          error_file: '/dev/null',
        },
        function (err, app) {}
      );
    }
  }
);

2. Master & Worker 간의 Communication

  • IPC(Inter Process Communication)로 process간에 통신이 가능하다.

  • pm2.list를 통해 pm2에서 관리하는 master process와 자신(worker process)의 pm_id를 찾는다. (pm2를 통한 IPC에 사용)

  • pm2.sendDataToProcessId를 통해 process끼리 통신을 한다.

  • Worker가 Master에 Message를 보낼 때 해당 메시지에 대한 hashKey를 생성하고, hashKey를 Key로 가지는 콜백을 캐시해놓는다. 이후 Master로부터 응답이 왔을때 hashKey를 통해 해당 메시지에 대한 콜백을 찾아서 실행한다.

var getPID = function (callback) {
  pm2.list(function (err, list) {
    var i,
      len = list.length;

    for (i = 0; i < len; i += 1) {
      if (list[i].name === 'Master') {
        masterPID = list[i].pm2_env.pm_id;
      } else if (process.pid === list[i].pid) {
        processPID = list[i].pm2_env.pm_id;
        processNumber = list[i].pm2_env.args[0];
      }
    }

    if (masterPID === null || processPID === null) {
      callback(new Error('Fail to Get masterPID or processPID'), null);
    } else {
      callback(null);
    }
  });
};

pm2.sendDataToProcessId(
  masterPID,
  {
    type: 'process:msg',
    data: {
      processId: processPID,
      msgKey: keyValue,
      type: message.type,
      data: message.data,
    },
    topic: 'message',
  },
  function (err, res) {
    if (err) {
      var regex = /unknown/g;
      if (regex.exec(err)) {
        setTimeout(function () {
          process.exit(0);
        }, 3000);
      } else {
        targetCallback = replyMessageCallbackList[keyValue];
        if (targetCallback) {
          targetCallback('error');
        }
        delete replyMessageCallbackList[keyValue];
        callback(new Error('Message Send Error'), null);
      }
    } else {
      callback(null);
    }
  }
);

3. Mater 또는 Worker 가 죽는 경우 처리

  • Master 또는 Worker Process가 죽는 경우, pm2에 의해 재실행된다.

  • Master가 죽는경우, Worker들은 Master에게 메시지를 보내는데 실패한다. 실패하면, Worker도 스스로 Process를 죽이고 재시작한다.

  • Worker가 죽어서 다시 재실행될때, Master에게 'restart' 메시지를 보낸다. Master는 'restart' 메시지를 받으면 Master에서 관리하는 해당 Worker Process의 Data를 초기화한다.