// 에드센스


1. 왜 RabbitMQ를 사용하는가?

RabbitMQ는 *AMQP를 따르는 오픈소스 메세지 브로커이다.

RabbitMQ는 데이터를 잠시 보관하고 나중에 비동기적으로 처리하고 싶을 경우 사용하는 일종의 데이터 저장소이다.

 

*AMQP: Advanced Message Queing Protocol의 약자로, 흔히 알고 있는 MQ의 오픈소스에 기반한 표준 프로토콜

 

실생활에서 예를 들어보자

스타벅스에서 손님들이 줄을 서서 커피를 주문한다.

 

1. 메시지 큐X

첫번째 손님이 커피를 주문하면 첫번째 손님의 커피가 완성될때까지 두번째 손님은 계속 줄을 서서 대기해야한다.

 

2. 메시지 큐O

첫번째 손님은 커피를 주문하고 자리로 간다. 두번째, 세번째 손님들도 주문서만 던져놓고 자리로 간다. 바리스타는 쌓여가는 주문서들을 보며 순서대로 커피를 만든다. 커피가 만들어지면 손님들이 받아간다.

 

이때 손님(프로듀서)들이 바리스타(컨슈머)에게 던져놓는 주문서가 메시지가 되고 주문서가 쌓여가는 곳이 메시지 큐가 된다.

 

이런 구조는 커피를 비동기적으로 만들기에 효율적이며,

주문서는 바리스타에게 전달될때 까지 잠시 저장되기에 바리스타가 까먹거나 하는 주문 누락이 발생하지 않는다.

 

 

 

정리하자면

  • 메시지를 많은 사용자에게 전달해야할 때
  • 요청에 대한 처리시간이 길어 해당 요청을 다른 API에 위임하고 빠른 응답처리가 필요할 때
  • 애플리케이션 간 결합도를 낮춰야 할 때

RabbitMQ를 사용한다

 

 

 

 


2. RabbitMQ는 어떻게 이루어져있는가?

[프로듀서 → 브로커(익스체인지+큐) → 컨슈머]

의 구조로 메시지를 전달해주는 메시징 서버

RabbitMQ는 다음과 같이 구성된다.

  1. Producer: 메시지를 보내는 놈
  2. Exchange: 메시지를 알맞은 큐에 전달해주는 놈
  3. Queue: 메시지를 차곡차곡 쌓아두는 놈
  4. Consumer: 메시지를 받는 놈
 

 

위 그럼처럼 Producer는 Queue에 직접 메시지를 전달하는 것이 아니다.

[프로듀서 → 익스체인지 → 큐 → 컨슈머]의 절차를 밟는다.

Exchange에서 알맞은 Queue로 메시지를 분배한다.(Exchange들과 Queue들은 바인딩되어있다)

무슨 기준으로 분배하느냐?

Exchange Type에 따라 다르다.

 

Exchange Type 4가지

Direct

메시지에 포함된 Routing Key를 기반으로 특정 Queue에 메시지를 하나씩 전달한다.

 

 

Fanout

Routing Key에 상관 없이 연결돼있는 모든 Queue에 동일한 메시지를 전달한다.

라우팅키를 평가할 필요가 없기때문에 성능적인 이점이 있다.

 

Topic

라우팅키 전체가 일치하거나 일부 패턴과 일치하는 모든 Queue로 메시지가 전달된다.

Topic Exchange 에서 사용하는 binding key 는 점(.)으로 구분된 단어를 조합해서 정의한다.

* 와 #을 이용해 와일드 카드를 표현할 수 있으며, * 는 단어 하나 일치 # 는 0 또는 1개 이상의 단어 일치를 의미한다.

 

다음과 같이 binding key 를 정의한 경우에 메시지의 routing key 가 quick.orange.rabbit 또는 lazy.orange.elephant 이면, Q1, Q2 둘 다 전달된다. lazy.pink.rabbit 는 binding key 2개와 일치 하더라도 1번만 전달된다.

quick.brown.fox, quick.orange.male.rabbit 는 일치하는 binding key 가 없기 때문에 무시된다.

 

Header

메시지 속성 중 headers 테이블을 사용해 특정한 규칙의 라우팅을 처리한다.

  • x-match = any 일 경우 헤더 테이블 값 중 하나가 연결된 값 중 하나와 일치하면 메시지 전달
  • x-match = all 일 경우 모든 값이 일치해야 메시지를 전달한다.

 

 

추가적인 사용 패턴들은 다음 블로그에 잘 설명이 되어있더라.

https://hamait.tistory.com/402

 

RabbitMQ 사용패턴들

역주: 메세지큐에 대한 글을 적기 전에 왜 메세지큐냐? 를 먼저 생각해봐야한다. 메세지큐는 그냥 메세지를 전달해주는 서버인건데, 기술자체에 집중할 필요는 나중에 생각해보고 , 처음 생각해

hamait.tistory.com

 

 

 

 


3. Connection과 Channel

Connection

  • RabbitMQ에서 지원하는 모든 프로토콜은 TCP 기반이다.
  • 효율성을 위해 긴 연결을 가정한다. (프로토콜 작업당 새 연결이 열리지 않음.)
  • 하나의 클라이언트 연결은 단일 TCP 연결을 사용한다.
  • 클라이언트가 연결을 성공하려면, RabbitMQ 대상 노드는 특정 프로토콜에 대한 연결을 허용해야 한다.
  • 연결은 오래 지속되어야 하기 때문에 일반적으로 구독을 등록하고, 폴링 대신에 메시지를 전달하여 소비한다.
  • 연결이 더 이상 필요하지 않은 경우, 리소스 절약을 위해 연결을 닫아야 한다. 이를 수행하지 못하는 클라이언트는 리소스의 대상 노드를 고갈시킬 위험이 있다.
  • 운영 체제는 단일 프로세스가 동시에 열 수 있는 TCP 연결(소켓) 수에 대한 제한이 있다. QA 환경에서는 충분한 경우가 있지만, Production 환경에서는 더 높은 제한을 사용하도록 구성해야 할 수도 있다.

 

Channel

  • 단일 TCP 연결을 공유하는 논리적인 개념의 경량 연결로 다중화된다.
  • 클라이언트가 수행하는 모든 프로토콜 작업은 채널에서 발생한다.
  • 채널 안에 연결할 Queue를 선언할 수 있으며, 채널 하나당 하나의 Queue만 선언이 가능하다.
  • 채널은 Connection Context에만 존재하기 때문에 Connection이 닫히면, 연결된 모든 채널도 닫힌다.
  • 클라이언트에서 처리를 위해 멀티 프로세스/스레드를 사용한다면, 프로세스/스레드 별로 새 채널을 열고 공유하지 않는 것이 일반적이다.

 

아무튼.

Connection: Application과 RabbitMQ Broker사이의 물리적인 TCP 연결

Channel: connection내부에 정의된 가상의 연결. queue에서 데이터를 손볼 때 생기는 일종의 통로같은 개념

 

한개의 Connection에 여러개의 Channel,
한개의 Channel에 한개의 Queue

https://hyos-dev-log.tistory.com/8

 

RabbitMQ의 Connection과 Channel

사내에서 진행되는 서비스를 개편하게 되면서, RabbitMQ를 사용하게 되었습니다. RabbitMQ을 사용하고, 운영하면서 Connection과 Channel에 대한 개념이 잡히지 않아서 정리하게 되었습니다. Connection 일반

hyos-dev-log.tistory.com

 

 

 

 


4. 적용하기

NodeJS에 적용하기

1. 초기 설정

amqp.connect함수를 통해서 Rabbitmq에 연결한다.

export async function makeConnection() {
  const connection = await amqp.connect({
    protocol: config.rabbitmq.protocol,
    username: config.rabbitmq.username,
    password: config.rabbitmq.password,
    hostname: config.rabbitmq.hostname,
    port: config.rabbitmq.port,
    vhost: '/',
    heartbeat: 0,
  });
}

 

2.  NodeJS에서 메시지 Subscribe하기

const messageQueueConnectionString = {
  protocol: config.rabbitmq.protocol,
  username: config.rabbitmq.username,
  password: config.rabbitmq.password,
  hostname: config.rabbitmq.hostname,
  port: config.rabbitmq.port,
  vhost: '/',
  heartbeat: 0,
};


export async function listenForResults() {
  try {
    // connect to Rabbit MQ (1)
    const connection = await amqp.connect(messageQueueConnectionString);

    // create a channel and prefetch 1 message at a time (2)
    const myChannel1 = await connection.createChannel();
    await myChannel1.prefetch(1);

    const myChannel2 = await connection.createChannel();
    await myChannel2.prefetch(1);

    // start consuming messages (3)
    console.log('start consuming messages from web service');
    await myController.myConsume1({ connection, myChannel1 });
    await myController.myConsume2({ connection, myChannel2 });
  } catch (e) {
    Raven.captureException(e);
  }
}

(1) connection 획득

(2) connection으로부터 channel을 생성

(3) consume 실행

 

위 1, 2, 3번의 과정은 서버가 최초 실행될때 실행된다.

*Prefetch란, 큐의 메시지를 컨슈머의 메모리에 쌓아놓을 수 있는 최대 메시지의 양

 

https://minholee93.tistory.com/entry/RabbitMQ-Prefetch

 

[RabbitMQ] Prefetch

이번 글에서는 RabbitMQ의 Prefetch에 대해 알아보겠습니다. 1. Prefetch란? Queue의 메세지를 Consumer의 메모리에 쌓아놓을 수 있는 최대 메세지의 양 입니다. 예를 들어 Prefetch가 250일 경우, RabbitMQ는 250..

minholee93.tistory.com

 

 

export function myConsume1({ connection, myChannel1 }) {
  return new Promise((resolve, reject) => {
    myChannel1.consume('q1', async function (msg) { // queue 이름
      console.log('myConsume1');
      
      // (4)
      const msgBody = msg.content.toString();
      const data = JSON.parse(msgBody);

      // (5)
      // 비즈니스로직

      // acknowledge message as received
      await myChannel1.ack(msg);
    });

    // handle connection closed
    connection.on('close', err => {
      return reject(err);
    });

    // handle errors
    connection.on('error', err => {
      return reject(err);
    });
  });
}

 

(4) 메시지 파싱

(5) 원래 수행하고자 했던 로직 실행

ack를 전송하지 않고 소비자가 죽거나 (채널이 닫히거나 연결이 끊어 지거나 TCP 연결이 끊어지는 경우),

RabbitMQ는 메시지가 완전히 처리되지 않았 음을 인식하고 다시 대기한다.

즉 컨슈머(소비자)가 사망했을 경우를 대비

 

3.  NodeJS에서 메시지 Publish하기

export let channel1;

// (1)
export async function makeConnection() {
  const connection = await amqp.connect({
    protocol: config.rabbitmq.protocol,
    username: config.rabbitmq.username,
    password: config.rabbitmq.password,
    hostname: config.rabbitmq.hostname,
    port: config.rabbitmq.port,
    vhost: '/',
    heartbeat: 0,
  });
  
  // (2)
  channel1 = await connection.createConfirmChannel(); // createConfirmChannel는 ack과 nack을 확인한다
}

// (3)
export async function publishToChannel(channel, { exchangeName, routingKey, data }) {
  await channel.publish(exchangeName, routingKey, Buffer.from(JSON.stringify(data), 'utf-8'), { persistent: true });
}

(1) 서버 최초 실행시 makeConnection 메소드를 사용하여 연결한다

(2) channel당 하나의 queue만 생성이 가능하기에 필요한 큐만큼의 채널을 생성하고 전역변수로 export해준다

(3) channel의 publish메소드를 사용하는 함수를 만든다

(1), (2)의 과정은 서버가 최초 실행될때 실행된다.

 

RabbitMQ가 종료되거나 충돌하면 사용자가 알리지 않는 한 대기열과 메시지를 잃게된다. 메시지가 손실되지 않도록하려면 큐와 메시지에 durable과 persistent 설정을 주어야한다.

즉, ack과 달리 RabbitMQ가 사망할 경우를 대비

 

https://skarlsla.tistory.com/13

 

[RABBITMQ] persistent, durable, ack

1. 메시지 수신 자동 확인(ack , noAck) 작업을 수행하는 데 몇 초가 걸릴 수 있습니다. 소비자 중 한 명이 긴 작업을 시작하고 부분적으로 만 수행되어 사망하는 경우 어떻게되는지 궁금 할 수 있습

skarlsla.tistory.com

 

 

 

// (4)
const result = await rabbitmq.publishToChannel(rabbitmq.channel1, {
  exchangeName: 'ex1', // exchange name
  routingKey: 'p1', // bind pattern
  data: { uid: uid },
});

 

(4) 이후 메시지를 publish할 곳에서 export한 channel과 설정값을 publish함수의 인자로 넣어주며 사용한다.

 

 

 

Spring에서 사용하기

1. 초기설정 - application.yml

spring:
  rabbitmq:
    host: somethingsomethingaddress.mq.region.amazonaws.com
    port: 5671
    username: myname
    password: mypassword
    ssl:
      enabled: true
  listener:
    simple:
      acknowledge-mode: manual

 

2. Spring에서 메시지 Subscribe하기

@RabbitListener(queues = ["sample.queue"])
public fun receiveMessage (message : Message){
    System.out.println("수신받은 메시지는 $message");
}

 

 

조금 더 자세한 설정을 하려면 어노테이션에 옵션을 추가해주면 된다.

@RabbitListener(bindings = [QueueBinding(
    value = Queue(value = "q1"),
    exchange = Exchange(value = "ex1"),
)])
fun receiveMessage (message : Message){
    System.out.println("수신받은 메시지는 $message");
}

 

https://cheese10yun.github.io/spring-rabbitmq/

 

Rabbit MQ 기초 사용법 - Yun Blog | 기술 블로그

Rabbit MQ 기초 사용법 - Yun Blog | 기술 블로그

cheese10yun.github.io

https://syaku.tistory.com/?page=7 

 

개발자 샤쿠 @syaku

Full Stack Web Developer.

syaku.tistory.com

 

 

3. Spring에서 메시지 Publish하기

// (1)
val rabbitTemplate: RabbitTemplate            

// (2)
val dto = mqDto(uid, req.amount)
rabbitTemplate.convertAndSend("ex1", "p2", objectMapper.writeValueAsString(dto)); // (exchangeName, routingKey, value)

(1) 생성자 주입

(2) 보낼 메시지를 dto로 포장해서 objectMapper.writeValueAsString 로 직렬화해서 전송한다.

 

 

 

 


5. 그 외 읽어보면 좋을 RabbitMQ 관련 이모저모

1. RabbitMQ에서 최적을 성능을 뽑는 팁?

https://kamang-it.tistory.com/627

 

[AMQP][RabbitMQ]RabbitMQ그리고 Work Queue의 사용법 - (3)

참고 [AMQP][RabbitMQ]RabbitMQ를 사용하는 이유와 설치방법 - (1) [AMQP][RabbitMQ]RabbitMQ아주 기초적이게 사용하기 - Java(feat.Hello World!) - (2) https://www.rabbitmq.com/tutorials/tutorial-two-java...

kamang-it.tistory.com

 

 

2. Spring boot Exchange와 Queue 자동 생성 이해

스프링 부트가 구동될때 exchange와 queue 설정에 따라 자동으로 생성되지 않는 다.

자동 생성 시점은 메시지가 발행이 될때와 메시지를 구독하기 위해 RabbitMQ 서버에 연결될때 생성된다.

아래 설정은 자동 생성 여부를 설정하기 위한 옵션이고 기본 값은 true 이다.

spring.rabbitmq.dynamic=true

https://syaku.tistory.com/?page=7 

 

개발자 샤쿠 @syaku

Full Stack Web Developer.

syaku.tistory.com

 

 

3. kafka vs RabbitMQ

앞선 내용들을 통해 각각의 정의 및 프로세스에 대해 면밀히(?) 살펴봤다.
위 내용을 통해서도 kafkaRabbitMQ의 차이에 대해 어느정도 이해할 수 있겠지만 본 글의 주제가 주제인만큼 다시 한번 간단히 정리해보도록 하겠다.

  1. kafka는 pub/sub 방식 / RabbitMQ는 메시지 브로커 방식
    kafka의 pub/sub방식은 생산자 중심적인 설계로 구성. 생성자가 원하는 각 메시지를 게시할 수 있도록 하는 메시지 배포 패턴으로 진행
    RabbitMQ의메시지브로커방식은 브로커 중심적인 설계로 구성. 지정된 수신인에게 메시지를 확인, 라우팅, 저장 및 배달하는 역할을 수행하며 보장되는 메시지 전달에 초점
  2. 전달된 메시지에 대한 휘발성
    RabbitMQ는 queue에 저장되어 있던 메시지에 대해 Event Consumer가져가게 되면 queue에서 해당 메시지를 삭제한다.
    하지만, kafka는 생성자로부터 메시지가 들어오면 해당 메시지를 topic으로 분류하고 이를 event streamer에 저장한다. 그 후, 수신인이 특정 topic에 대한 메시지를 가져가더라도 event streamer는 해당 topic을 계속 유지하기 때문에 특정 상황이 발생하더라도 재생이 가능하다.
  3. 용도의 차이
    kafka는 클러스터를 통해 병렬처리가 주요 차별점인 만큼 방대한 양의 데이터를 처리할 때, 장점이 부각된다.
    RabbitMQ는 데이터 처리보단 Manage UI를 제공하는 만큼 관리적인 측면이나, 다양한 기능 구현을 위한 서비스를 구축할 때, 장점이 부각된다.

 

 

https://velog.io/@cho876/%EC%B9%B4%ED%94%84%EC%B9%B4kafka-vs-RabbitMQ

 

카프카(kafka) vs RabbitMQ

오늘은 kafka와 RabbitMQ의 차이에 대해 알아보도록 하겠다. 1. 이해 1-1. 메시지 큐(MessageQuque : MQ) kafka와 rabbitMQ를 이해하기 위해선 우선 메시지 큐에 대한 이해가 선제적으로 필요하다. >메시지 큐(Mes

velog.io

  • 둘다 쩌는 솔루션이다.  -  RabbitMQ 가 좀더 성숙하다. (Written 12 Sep, 2012)
  • 철학은 좀 다른데, 기본적으로 RabbitMQ 는 브로커 중심적이며, 생산자와 소비자간의 보장되는 메세지 전달에 촛점을 맞추었다.  
  • 반면 Kafka 는 생산자 중심적이며, 엄청난 이벤트 데이터을 파티셔닝하는데 기반을 둔다. 배치 소비자를 지원하며, 온라인, 오프라인에 저 지연율(Low latency)을 보장하며 메세지를 전달해준다. 
  • RabbitMQ 는 브로커상에서 전달 상태를 확인하기위한 메세지 표식을 사용한다. 카프카는 그런 메세지 표식이 없으며 컨슈머가 전달(배달) 상태를 기억하는것을 기대한다.
  • 둘다 클러스터간의 상태를 관리하기위해 Zookeeper 를 사용한다. 
  •  RabbitMQ 는 커다란 크기의 데이터를 위해 디자인되지 않았으며 만약 컨슈머가 매우 느리다면 실패할것이다.그러나 post 2.0 에  RabbitMQ  는 느린 배치 컨슈머를 핸들링 되는게 요청되어졌다. 
  • Kafka 은 오직 토픽같은 exchanges 를 사용한다. RabbitMQ 는 다양한 exchanges 를 사용한다.
  • Kafka 는 파티션들 안에서 메세지 순서를 제공하며, 파티션들간에 엄격한 순서를 가진다. 카프카 컨슈머들은 충분히 스마트해야하며 , 그들 스스로 파티션간의 순서를 해결(resolve) 해야한다.
  • Kafka 는 디스크상에서 메세지를 저장하고 데이타 손실을 막기위해 클러스터로 그들을 복제한다. 각각의 성능에 큰 문제없이 브로커는 테라바이트를 핸들링할수있다. Kafka 는 쓰기에 초당  200k 메세지를 , 읽는데는 3M 메세지를  제공되도록 테스트되었다.

https://hamait.tistory.com/403?category=138704 

 

카프카(Kafka) vs RabbitMQ

생산자 (Sender) 테스트 결과 이유 카프카 생산자는 브로커로 부터의 ack 를  기다리지 않고 메세지를 보낸다.브로커가 핸들링 할수있는 만큼 빠르게 메세지를 마구 보낸다. 카프카는 좀더 효율

hamait.tistory.com

 

 

 

 

 

참고:

더보기

https://sg-choi.tistory.com/406

 

[RabbitMQ] RabbitMQ란?

RabbitMQ란? RabbitMQ는 AMQP를 따르는 오픈소스 메세지 브로커 AMQP는 클라이언트가 메시지 미들웨어 브로커와 통신할 수 있게 해주는 메세징 프로토콜 주요 개념으로 Producer, Exchange, Binding, Queue, Consu..

sg-choi.tistory.com

https://jin2rang.tistory.com/entry/RabbitMQ%EB%9E%80

 

RabbitMQ란?

RabbitMQ란? 서버간 메세지를 전달해주는 오픈소스 메세지 브로커이다. A → B에게 또는 A → B,C,D,E,F ... 등 메세지를 보내려고 할 때 RabbitMQ가 이 메세지를 받아서 전달 해주는 것으로 이해하면 된다

jin2rang.tistory.com

https://velog.io/@cckn/%EB%B2%88%EC%97%AD-RabbitMQ-%EB%B0%8F-Node.js%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%EC%84%9C%EB%B9%84%EC%8A%A4

 

(번역) RabbitMQ 및 Node.js를 사용한 비동기 마이크로 서비스

메세지와 이벤트를 통한 확장성과 내결함성.이 글은 Asynchronous Microservices with RabbitMQ and Node.js를 번역한 글입니다

velog.io

https://daily-coding-diary.tistory.com/13

 

[Node.js] RabbitMQ

목표 : RabbitMQ를 이용하여, 데이터를 임시로 저장하고 확인해봅시다. RabbitMQ는 디비 또는 데이터 처리에 문제가 생겼을때 데이터를 임시로 처리해줄때 많이 사용합니다. 예를 들면, 채팅방에 한

daily-coding-diary.tistory.com

https://www.joinc.co.kr/w/man/12/%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90/Message#sid_1

 

스타벅스로 살펴보는 Message 아키텍처

소프트웨어 시나리오

www.joinc.co.kr

 

무중단 배포에 대해 알아보자.


1. 무중단 배포란?

말 그대로 애플리케이션의 중단 없이 배포를 하는 것을 말한다. 

애플리케이션은 언제 중단될까?

 

 

v1 서비스가 실행 중일 때 v2 버전을 다운로드 받는다.
v1 서비스를 종료 시키는 시점부터 v2를 시작하기 전까지 애플리케이션은 중단된다.
이렇게 서비스가 중단되는 시간을 다운타임(downtime)이라고 한다.

 

이런 문제를 해결하기위해 무중단 배포라는 개념이 나왔고 크게 3가지 방법이 있다.

 

 

 

 

 


2. 무중단 배포 기법

1. Rolling 배포

롤링 배포는 사용 중인 인스턴스 내에서 새 버전을 점진적으로 교체하는 것으로 무중단 배포의 가장 기본적인 방식이다.

서비스 중인 인스턴스 하나를 로드밸런서에서 라우팅하지 않도록 한 뒤, 새 버전을 적용하여 다시 라우팅하도록 한다.

이를 반복하여 모든 인스턴스에 새 버전의 애플리케이션을 배포한다.

 

위 움짤을 보면

1. 4개의 기존 버전(v0.0.1)이 있다.

2. 그 중에 2개의 라우팅을 끊고 업데이트 버전(v0.0.2)로 업그레이드 한다.

3. 업데이트 버전이 배포가 되면 다시 라우팅을 시작한다.

4. 나머지 2개 인스턴스의 라우팅을 끊고 업데이트 버전을 배포한다.

5. 나머지 2개의 인스턴스의 업데이트가 끝나면 다시 라우팅을 연결한다.

 

장점

  • 인스턴스마다 차례로 배포를 진행하기에 상황에 따라 손쉽게 롤백이 가능하다.
  • 추가적인 인스턴스를 늘리지 않아도 된다.
  • 간편한 관리

단점

  • 새 버전을 배포할때 인스턴스의 수가 감소하기 때문에 사용중인 인스턴스에 트래픽이 몰릴 수 있다. 즉, 서비스 처리 용량을 고려해야 한다. 위 움짤에서 2번과정, 4번과정에서 4개의 인스턴스에서 감당하던 트래픽이 2개의 인스턴스로 몰리는 경우이다.
  • 배포가 진행될때 구버전과 신버전이 공존하기에 호환성 문제가 발생할 수 있다. 위 움짤에서 3번과정에서 업데이트 된 버전2개, 안된버전 2개가 공존하므로 사용자들은 균일한 서비스를 받지 못한다.

 

 

 

 

2. Blue Green 배포

블루는 구버전, 그린은 신버전을 의미한다.

운영중인 구버전과 동일하게 신버전의 인스턴스를 구성한 후 로드밸런서를 통해 모든 트래픽을 한번에 신버전 쪽으로 전환하는 방식이다.

 

장점.

  • 구버전의 인스턴스가 그대로 남아있어서 손쉬운 롤백이 가능하다.
  • 구버전의 환경을 다음 배포에 재사용할 수 있다.
  • 운영환경에 영향을 주지 않고 새 버전 테스트 가능.

 

단점.

  • 시스템 자원이 두배로 필요하다.
  • 새로운 환경에 대한 테스트가 전제되어야 한다.

 

 

 

 

3. 카나리 배포

옛날 광부들이 유독 가스에 민감한 카나리아 새를 이용해 가스 누출 위험을 감지했던 것에서 유래한 것으로 잠재적 문제 상황을 미리 발견하기 위한 방식이다.

신버전을 소수의 유저들에게만 배포를 해보고 문제가 없는것을 확인해가며 점차 많은 유저들에게 배포하는 기법이다.

블루그린 배포와 유사하지만 블루그린처럼 트래픽을 한번에 확 바꾸는 것이 아니라 단계적으로 전환하기에 부정적 영향을 최소화하고 상황에 따라 트래픽 양을 조절하며 롤백할 수 있다.

 

장점.

  • 문제 상황을 빠르게 감지
  • A/B 테스트로 활용 가능

 

단점.

  • 네트워크 트래픽 제어 부담.

 

*A/B 테스트란? 

대조군과 실험군으로 나누어서 특정한 UI나 알고리즘의 효과를 비교하는 방법론

 

 

 

 


3. 정리

1. Rolling 배포

  • 인스턴스를 늘리지 않고,
  • 하나씩 새로운 버전으로 늘리는 기법.
  • 사용중인 인스턴스에 트래픽이 몰리는 문제가 있다.
  • 버전간 호환성 문제가 생기는 순간이 있다.

 

 

 

2. Blue Green 배포

배포 전
배포 후

 

  • 구버전과 같은 환경으로 신버전을 미리 준비.
  • 로드밸런서의 라우팅을 한번에 확! 전환시킴.
  • 구버전의 환경을 재활용하거나 롤백하기 쉬움.
  • 단, 시스템 자원이 두배로 든다.

 

 

 

3. 카나리 배포

  • 소수만 사용하는 환경에 신버전을 배포하고 문제를 관찰한다.
  • 문제가 없으면 신버전으로의 트래픽을 단계적으로 늘린다.
  • 블루그린 배포와 유사. 하지만 블루그린은 한번에 전환, 이건 단계적으로 전환.
  • 문제를 빠르게 발견할 수 있다.

 

 

 

 

 

 

참고:

  1. Denia-Dev 2022.06.20 22:08 신고

    덕분에 좋은 내용 잘 보고 갑니다
    감사합니다.

출처: https://kr.freepik.com/free-vector/web-hosting-server-rack-isometric-icon-of-database-and-data-center-blockchain-digital-technology_4102271.htm

인터넷을 돌아다니다 사용자가 증가하면 서버를 어떻게 확장시킬지에 관한 글이 올라와있는것을 확인했다.

그냥 읽고만 넘어가기엔 너무 정리가 잘 돼있기에 내용을 다시 훑어보며 포스팅으로 녹여보고자 한다.

 

출처는 맨 밑에 접은글에 표기하였다.


1. 대규모 트래픽이 됐다.

사용하던 서비스가 대박이 나서 늘어난 트래픽을 서버가 견딜 수 없어한다. 이때 어떻게 해야할까?

간단하게 두 가지 방법을 통해 우리의 서버를 견고히 할 수 있을것 이다.

  • 더 성능 좋은 서버로 업그레이드
  • 서버를 여러대 추가로 설치

성능 좋은 서버로 업그레이드하는 방법이 Scale Up이고,

서버의 개수를 늘리는 방법이 Scale Out이다.

 

 

Scale Up

서버의 CPU나 메모리 등을 추가하여 서버 자체의 성능을 향상시키는 방법.

 

장점

  • 서버의 장비만 추가 or 교체하면 되기에 구축과 설계가 간단.
  • 별도의 컨트롤러나 네트워크 인프라 등 비용이 발생하지 않는다.
  • 추가적인 별도의 서버가 생기는 것이 아니기에 데이터 정합성 문제가 없다.

 

단점

  • 부품 업그레이드로는 성능 향상의 한계가 있다.
  • 하나의 서버에서 모든 트래픽을 감당하기에 장애극복 기능이 떨어진다.
  • 매번 장비 업그레이드를 할 때 마다 교체 비용이 든다.

 

 

 

 

Scale Out

서버 자체의 성능 향상이 아닌 서버의 대수를 늘리는 방법.

 

장점

  • 확장에 유연하다.
  • 특정 서버에 트래픽이 몰리는 것을 분산하여 부하를 막을 수 있다.
  • 하나의 서버에 문제가 생겨도 다른 서버가 있으므로 가용성을 높일 수 있다.

 

단점

  • 설계와 구현이 복잡하고 이에 따른 관리 비용도 증가한다.
  • 데이터 불일치 문제가 발생한다.

 

이 데이터 불일치 문제가 오늘 해결할 핵심 문제다.

서버1에서 로그인 처리를 했다고 이걸 서버2에서 알고있을까?

Scale Out 방식에서는 데이터 정합성에 대한 문제를 고민해야한다.

 

 

 

 

 


2. 다중 서버 환경에서 Session 관리

Scale Out을 통해 서버를 늘리면 다음 그림과 같은 상태가 된다.

 

각 서버마다 세션 저장소를 각자 보유하고 있고 이를 서로 공유하지 않기에 데이터 정합성 문제가 발생한다.

 

 

개선1. Sticky Session

 

Sticky Session 방식은 사용자가 한번 사용했던 서버를 이후 요청시 계속 사용하는 것이다.

즉 로드밸런서에서 User1이 WAS1에서 세션을 생성했다는 사실을 알게되면 User1의 요청은 이후부터 WAS1로만 보내지는 것이다. 

User1은 동일한 서버만 사용하게 되기에 정합성 문제가 해결되긴 한다. 

 

하지만,

 

고정된 세션을 사용한다는 것은 특정 서버에 트래픽이 몰릴 수 있다는 것을 의미한다. Scale Out을 한 의미가 없어진다.

 

 

 

 

 

개선2. Session Clustering

한마디로 표현하면, 여러 서버들을 동기화 하는 것이다.

Tomcat 9.0 Document를 보면 톰캣이 세션 클러스터링을 구현하는 방법으로 DeltaManager를 사용하여 all-to-all 세션 복제 방식을 제안한다.

 

이렇게 한 서버의 상태를 다른 세션 저장소에서 복제하여 반영하기에 어떤 서버로 로그인 요청을 보내도 데이터 정합성 문제는 발생하지 않게된다.

 

하지만 이 방법도 문제가 있어보인다. 

 

세션 저장소에 데이터가 입력될 때 마다 모든 서버에 똑같이 반영해 줘야하기에 서버 수에 비례하여 네트워크 트래픽이 증가한다. 따라서 너무 많은 서버를 가진 상태에서는 오히려 비효율적이다.

 

 

Tomcat에서는 이를 또 개선하기 위해 BackupManager를 활용한 Primary Secondary 세션 복제 방식을 제시한다.

 

얘도 간단하게 말하자면, Primary 서버에서 다른 서버로 모든 정보를 복제하는 것이 아닌 Key만 복제하게 하고 이를 통해 세션 전체를 복제하는 것보다 메모리 사용면에서 개선을 한 것이다.

 

 

 

 


3. 출제자의 의도

그래서 어떤 방법을 쓰라는 것일까? 정답은 물론 없지만 사람들이 "흔히" 쓰는 방법은,

기존 서버들이 가지고 있던 로컬 세션 저장소를 사용하는 것이 아닌 제 3의 저장소를 사용하는 것이다.

 

이렇게 하면

  • Sticky Session처럼 트래픽이 몰리는 현상이 일어나지 않고,
  • 서버 하나에 장애가 발생해도 다른 서버로 대체 가능하여 가용성을 확보할 수 있고,
  • 제 3의 통일된 스토리지에 세션을 저장하기에 데이터 정합성을 해결할 수 있다.

 

 

이때 어떤 세션 스토리지가 적당할지 요구사항을 정리해보자면,

  • Read/Write가 빈번하게 이루어지기에 Disk 기반 데이터베이스는 느린 IO속도로 인해 부적합.
  • In-Memory 데이터베이스를 사용한다면 메모리에서 Read/Write를 하기에 속도면에서 적합.
  • 데이터베이스 전원이 끊기면 데이터가 사라지는 In-Memory 데이터베이스이지만 세션들이 사라지는 것은 다시 로그인만 하면 되기에 치명적이지 않다.

즉, In-Memory 데이터베이스를 제 3의 세션 스토리지로 활용하는 것이 좋다.

이에는 Redis와 Memcached가 있는데 Redis는 이후에 따로 포스팅 할 예정이므로 이정도만 알고 넘어가자.

 

 

 

4. JWT 연전연승?

JWT를 처음 공부할 때 이런 상황(서버가 다수 존재할 때)에서 유용하게 쓰인다고 들었는데 정말 그럴 것 같다.

 

 

 

 

 

 

참고:

더보기

+ Recent posts