이전 포스팅에서 MSA 를 얘기할 때 잠깐 적기도 했던 내용이지만 분산 어플리케이션을 구성하는데에 메세지를 사용해
시스템 전체에 데이터나 이벤트를 전파하는 것은 주요합니다. 경우에 따라 직접 구현하기도하고, 클라우트 서비스의 도움을 받기도 하고,
간단하게 redis 등을 사용하기도 합니다.
메세징 시스템을 구축할 때 고려할 부분은 아래와 같은 포인트가 있습니다.
- 통신의 방향
- 메세지 목적 및 타이밍
- p2p 방식 또는 브로커를 통한 메세지 전달
- 통신의 방향
통신의 방향은 단방향과 양방향이 있고 쉬운건 단방향이겠지만 양방향이 훨씬 인기가 많고 많이 쓰이는 것 같습니다.
채팅 프로그램같은걸 구현할 때 많이 사용하는 WebSocket 이 단방향 통신의 예시로 볼 수 있습니다.
양방향 통신은 멀리갈 것도 없이 웹서비스 호출을 생각하면 됩니다. 요청을 보내고, 응답을 받는 가장 흔히 볼 수 있는 구조죠.
기본적으로 메세지는 다른 시스템이 갖고 있는 정보를 얻거나, 원격으로 작업 실행 또는 어떠한 이벤트를 전파하기 위해 사용합니다.
메세지는 목적에 따라 아래와 같이 구분할 수 있습니다.
- Command
- Event
- Document
Command, 즉 명령 메세지는 가장 잘 알고 있는 메세지입니다. 메세지를 보냄으로써 수신자에게 어떤 동작이나 작업을 수행하라는
목적을 갖고 있고, RPC 나 RESTful HTTP 호출이 그 예시입니다. 우리는 RESTful HTTP 호출에서 GET, POST, PUT, DELETE 등의 메서드를 사용해 수신자 측에 그에 맞는 동작을 하게끔 명령을 하죠.
Event, 이벤트 메세지는 무언가 일이 발생했을음 알리는데 사용합니다. 일반적으로는 무언가의 정보를 같이 포함하지만, zero-payload 방식을 사용해 id 만 전달하고 부가정보는 별도의 API 를 사용해 조회하는 등의 이벤트를 MSA 에선 사용하기도 합니다.
Document, 도큐먼트 메세지는 데이터 전송을 의미합니다. 그냥 이렇게 보면 Command 와 거의 차이가 없지만, 구별되는 점은
수신자에게 데이터를 어떻게 처리하라는 정보가 포함되어 있지 않습니다. 예를 들면 RESTful HTTP 호출에 대한 응답의 경우엔
응답 데이터만 들어가있을 뿐 이 데이터를 어떻게 하라는 정보는 없으므로 Document 메시지로 볼 수 있습니다.
- 메세지 타이밍
동기식과 비동기식이 있고 상대적으로 동기식이 구현하기 간단한 패턴이지만, 비동기식으로 갈때는 약간 복잡해집니다.
하지만 그럼에도 비동기식의 장점때문에 많이 쓰이죠.
비동기식에선 동기식과 다르게 수신자의 네트워크에 연결할 필요도 없이 그냥 보내고, 보낸 메세지를 수신자가 바로 받거나 지연된 후에
받거나 아예 못받는 경우도 있습니다. 또한 여러 수신자에게 보낼 수도 있죠. 비동기식 메세지 패턴에서 이러한 지연 처리가 가능하기에
너무 메세지가 몰릴 경우나 반드시 전달을 보장해야할 때 유용하게 사용할 수 있습니다.
그리고 이런 것을 가능하게 하기 위해 메세지 큐를 사용하죠. 메세지 큐에 대해선 다른 포스팅에서 좀 더 다뤄보겠습니다.
- p2p 방식 또는 브로커
메세지를 수신자에게 직접 p2p 방식으로 전달하거나, 브로커라는 중앙 시스템을 통해 전달할 수 있습니다.
전에 이미 한번 적긴 했습니다만.. p2p 방식을 사용하려면 수신자의 정보에 대해 잘 알고 있어야 합니다. 주소나 포트 같은 정보를 말이죠.
그리고 수신자 또한 프로토콜과 메세지 형식을 완벽히 알고 있어야 사용할 수 있습니다.
하지만 메세지 브로커를 사용하게 되면, 이런 세부 정보를 알지 못해도 메세지를 전파할 수 있습니다. 자연스레 느슨한 커플링도
얻을 수 있는 장점이죠.
물론 브로커를 사용함으로써 브로커 라는 단일 지점을 사용하게 되기 때문에, 브로커가 장애가 생겼을 경우엔 모든 피어로 메세지를
전달하지 못한다는 단점도 있습니다. 브로커 자체의 확장도 고려해야하고, 아무래도 네트워크 홉이 하나 추가되는 것이기 때문에
p2p 에 비해선 전송 속도에 불리함이 있을 수 있죠. 이런 점만 아니라고 한다면, 메세지 브로커를 사용하는 것은 장점이 많은 패턴입니다.
- Redis pub/sub
publish, subscribe 기능을 사용해 Redis 를 메세지 브로커로 사용할 수 있는 예시를 간단하게 보려고 합니다.
Redis 는 엄밀히 분류하자면 데이터베이스입니다만, 메세지 브로커로 사용할 수 있는 독특한 명령을 지원합니다.
// subscribe.ts
import IORedis from 'ioredis';
const client = new IORedis();
(async () => {
await client.subscribe('test-channel');
client.on('message', (channel, message) => {
console.info(channel, message);
});
})();
간단하게 보려고 대충 짰습니다.. Node.JS 에서 redis client 모듈로는 IORedis 를 사용했고, 구독하고자 하는 채널에 대해 subscribe
명령어로 구독을 우선 합니다. 그리고 이벤트 핸들러를 통해 'message' 라는 이벤트가 발생하면 이벤트가 발생한 채널과, message 를
받을 수 있습니다.
// publish.ts
import IORedis from 'ioredis';
const client = new IORedis();
(async () => {
await client.publish('test-channel', JSON.stringify({ id: 1 }));
})();
이제 이벤트를 publish 해봐야할텐데요. 동일하게 IORedis 를 사용했고 publish 를 사용해 특정 채널에 메세지를 보내는게 전부입니다.
subscribe.ts 를 먼저 실행해서 test-channel 에 subscribe 를 한 상태에서 publish.ts 를 실행해보면, 메시지가 오는걸 확인할 수
있습니다. 메세지는 string 타입만 사용가능하지만, 이 자체로도 충분히 유용하죠.
이러한 pub/sub 패턴은 가장 쉽게 볼 수 있는 단방향 메세지입니다. 코드에서도 볼 수 있듯이, publish 하는 게시자는 누가 메세지를
수신하는지 알 필요가 없습니다. 그냥 보내면 그만이죠. 반대로 메세지가 필요한 쪽에선 채널을 subscribe 하기만 하면 됩니다.
메세지가 필요한 수신자는 자유롭게 늘릴 수 있습니다. 이렇게 Redis 를 메세지 브로커로 사용함으로써 느슨한 커플링 또한 얻었습니다.
팀에서도 간단한 메세징 전파가 필요한 부분에선 Redis pub/sub 으로 쉽게 가져가는 편입니다.
특별히 큰 허들도 없다고 생각되므로 우선 Redis 를 통해 메세지 브로커를 접해보는 것은 좋은 시작일 것 같습니다.
'Backend > Node.js' 카테고리의 다른 글
Use AMQP (w/ RabbitMQ) (0) | 2023.02.11 |
---|---|
Use ZeroMQ (0) | 2023.02.05 |
MSA (0) | 2023.01.14 |
peer-to-peer load balancing (0) | 2023.01.08 |
Node.JS 어플리케이션 확장 (0) | 2023.01.01 |