Pub/Sub APIs

Pub/Sub 패턴의 기본 개념

Pub/Sub(Publish-Subscribe) 패턴은 메시지 기반 아키텍처의 핵심 패러다임으로, 데이터를 생성하는 발행자(Publisher)와 데이터를 소비하는 구독자(Subscriber) 사이의 느슨한 결합(loose coupling)을 제공한다. 이 패턴에서 발행자는 특정 주제(Topic)에 메시지를 발행하고, 해당 주제를 구독한 모든 구독자는 자동으로 그 메시지를 수신한다.

Pub/Sub 패턴의 가장 중요한 특징은 발행자와 구독자가 서로에 대해 직접적인 지식이 필요 없다는 점이다. 발행자는 단순히 주제에 메시지를 보내고, 시스템이 해당 주제의 모든 구독자에게 메시지를 전달한다. 이러한 분리는 시스템 구성 요소 간의 의존성을 줄이고 확장성을 크게 향상시킨다.

Pub/Sub API의 작동 원리

Pub/Sub API의 일반적인 작동 과정은 다음과 같다:

  1. 주제 생성: 시스템 관리자나 애플리케이션이 메시지를 분류할 주제(Topic)를 생성한다.
  2. 구독 등록: 클라이언트는 관심 있는 주제에 구독을 등록한다. 이때 메시지 필터링 조건이나 메시지 처리 방식 등을 지정할 수 있다.
  3. 메시지 발행: 발행자는 특정 주제에 메시지를 발행한다. 메시지에는 데이터 페이로드와 메타데이터가 포함될 수 있다.
  4. 메시지 라우팅: Pub/Sub 시스템은 발행된 메시지를 해당 주제를 구독한 모든 구독자에게 전달한다.
  5. 메시지 처리: 구독자는 수신한 메시지를 처리하고, 필요에 따라 확인(ack)을 보낸다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// Node.js에서 Google Cloud Pub/Sub 사용 예시
const {PubSub} = require('@google-cloud/pubsub');
const pubsub = new PubSub();

// 주제 생성
async function createTopic(topicName) {
  const [topic] = await pubsub.createTopic(topicName);
  console.log(`주제 ${topic.name}가 생성되었습니다.`);
  return topic;
}

// 구독 생성
async function createSubscription(topicName, subscriptionName) {
  const [subscription] = await pubsub
    .topic(topicName)
    .createSubscription(subscriptionName);
  console.log(`구독 ${subscription.name}이 생성되었습니다.`);
  return subscription;
}

// 메시지 발행
async function publishMessage(topicName, data) {
  const dataBuffer = Buffer.from(JSON.stringify(data));
  const messageId = await pubsub.topic(topicName).publish(dataBuffer);
  console.log(`메시지 ${messageId}가 발행되었습니다.`);
  return messageId;
}

// 메시지 구독 및 처리
function listenForMessages(subscriptionName) {
  const subscription = pubsub.subscription(subscriptionName);
  
  subscription.on('message', message => {
    console.log(`메시지 수신: ${message.id}`);
    console.log(`데이터: ${message.data}`);
    
    // 메시지 처리 후 확인(ack)
    message.ack();
  });
  
  console.log(`${subscriptionName} 구독 대기 중…`);
}

Pub/Sub API의 주요 특징

  1. 비동기 통신
    Pub/Sub 시스템은 본질적으로 비동기적이다. 발행자는 메시지를 발행한 후 구독자의 응답을 기다리지 않고 작업을 계속할 수 있다. 이는 시스템 구성 요소 간의 타이밍 의존성을 제거하여 더 유연한 아키텍처를 가능하게 한다.

  2. 메시지 내구성(Durability)
    대부분의 Pub/Sub 시스템은 메시지의 내구성을 보장한다. 구독자가 일시적으로 오프라인 상태여도 메시지는 저장되어 나중에 전달될 수 있다. 이는 시스템 구성 요소 간의 일시적인 연결 문제를 극복하는 데 도움이 된다.

  3. 확장성(Scalability)
    Pub/Sub 아키텍처는 높은 확장성을 제공한다:

    • 수평적 확장: 발행자와 구독자의 수를 독립적으로 증가시킬 수 있다.
    • 부하 분산: 여러 구독자 간에 메시지 처리 부하를 분산할 수 있다.
    • 탄력성: 트래픽 패턴에 따라 자원을 동적으로 할당할 수 있다.
  4. 필터링과 라우팅
    많은 Pub/Sub 시스템은 메시지 필터링과 고급 라우팅 기능을 제공한다:

    • 주제 기반 필터링: 구독자는 특정 주제만 구독할 수 있다.
    • 콘텐츠 기반 필터링: 메시지 내용에 따라 선택적으로 수신할 수 있다.
    • 와일드카드 구독: 패턴 매칭을 통해 여러 주제를 한 번에 구독할 수 있다.
  5. 메시지 전달 보장
    Pub/Sub 시스템은 다양한 수준의 메시지 전달 보장을 제공한다:

    • At most once: 메시지가 최대 한 번 전달된다(일부 손실 가능).
    • At least once: 메시지가 최소 한 번 전달된다(중복 가능).
    • Exactly once: 메시지가 정확히 한 번 전달된다(이상적이지만 구현이 복잡).

주요 Pub/Sub API 서비스 비교

Apache Kafka

Apache Kafka는 대규모 이벤트 스트리밍 플랫폼으로, 높은 처리량과 내구성을 제공한다.

주요 특징:

  • 로그 기반 아키텍처로 메시지 순서 보장
  • 파티션을 통한 병렬 처리와 확장성
  • 장기간 메시지 저장 지원
  • 강력한 복제 및 내결함성 기능
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Python에서 Kafka 사용 예시
from kafka import KafkaProducer, KafkaConsumer
import json

# 프로듀서 설정
producer = KafkaProducer(
    bootstrap_servers=['localhost:9092'],
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

# 메시지 발행
producer.send('my-topic', {'key': 'value'})
producer.flush()

# 컨슈머 설정
consumer = KafkaConsumer(
    'my-topic',
    bootstrap_servers=['localhost:9092'],
    auto_offset_reset='earliest',
    enable_auto_commit=True,
    group_id='my-group',
    value_deserializer=lambda x: json.loads(x.decode('utf-8'))
)

# 메시지 소비
for message in consumer:
    print(f"Topic: {message.topic}, Partition: {message.partition}")
    print(f"Key: {message.key}, Value: {message.value}")

Google Cloud Pub/Sub

Google Cloud의 완전 관리형 메시징 서비스로, 글로벌 규모의 이벤트 스트리밍을 제공한다.

주요 특징:

  • 글로벌 메시지 전달과 지역 간 복제
  • 동적인 확장성과 로드 밸런싱
  • 높은 내구성과 가용성(99.99% SLA)
  • 인증 및 권한 부여를 위한 IAM 통합

AWS SNS/SQS

Amazon Simple Notification Service(SNS)와 Simple Queue Service(SQS)는 함께 사용하여 Pub/Sub 패턴을 구현한다.

주요 특징:

  • SNS는 푸시 기반 배포를 제공
  • SQS는 메시지 버퍼링과 폴링 기반 소비를 지원
  • AWS 서비스와의 원활한 통합
  • 메시지 필터링 및 중복 제거 기능
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Node.js에서 AWS SNS/SQS 사용 예시
const AWS = require('aws-sdk');
AWS.config.update({region: 'us-west-2'});

const sns = new AWS.SNS();
const sqs = new AWS.SQS();

// SNS 주제에 메시지 발행
async function publishToSNS(topicArn, message) {
  const params = {
    Message: JSON.stringify(message),
    TopicArn: topicArn
  };
  
  try {
    const result = await sns.publish(params).promise();
    console.log(`메시지가 발행되었습니다. MessageID: ${result.MessageId}`);
    return result;
  } catch (err) {
    console.error('SNS 발행 오류:', err);
    throw err;
  }
}

// SQS 대기열에서 메시지 수신
async function receiveFromSQS(queueUrl) {
  const params = {
    QueueUrl: queueUrl,
    MaxNumberOfMessages: 10,
    WaitTimeSeconds: 10
  };
  
  try {
    const data = await sqs.receiveMessage(params).promise();
    if (data.Messages) {
      return data.Messages;
    }
    return [];
  } catch (err) {
    console.error('SQS 수신 오류:', err);
    throw err;
  }
}

RabbitMQ

RabbitMQ는 AMQP(Advanced Message Queuing Protocol)를 구현한 메시지 브로커로, 다양한 메시징 패턴을 지원한다.

주요 특징:

  • 유연한 라우팅과 교환(Exchange) 유형
  • 다양한 프로그래밍 언어에 대한 클라이언트 라이브러리
  • 플러그인을 통한 확장성
  • 클러스터링 및 고가용성 구성

Redis Pub/Sub

Redis의 Pub/Sub 기능은 간단하지만 효율적인 메시징 솔루션을 제공한다.

주요 특징:

  • 메모리 내 작동으로 매우 빠른 성능
  • 간단한 구현과 사용 방법
  • 패턴 매칭 기반 구독 지원
  • 기존 Redis 인프라를 활용할 수 있음
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Python에서 Redis Pub/Sub 사용 예시
import redis
import threading
import time
import json

r = redis.Redis(host='localhost', port=6379, db=0)

# 발행자 함수
def publisher():
    while True:
        message = {
            'timestamp': time.time(),
            'data': 'Hello, World!'
        }
        r.publish('my-channel', json.dumps(message))
        print(f"메시지 발행: {message}")
        time.sleep(1)

# 구독자 함수
def subscriber():
    pubsub = r.pubsub()
    pubsub.subscribe('my-channel')
    
    for message in pubsub.listen():
        if message['type'] == 'message':
            data = json.loads(message['data'])
            print(f"메시지 수신: {data}")

# 스레드 시작
sub_thread = threading.Thread(target=subscriber)
pub_thread = threading.Thread(target=publisher)

sub_thread.start()
pub_thread.start()

Pub/Sub API의 주요 활용 사례

  1. 마이크로서비스 통신
    마이크로서비스 아키텍처에서 서비스 간 이벤트 기반 통신을 구현하는 데 Pub/Sub API가 널리 사용된다. 이는 서비스 간의 느슨한 결합을 제공하여 전체 시스템의 유연성과 확장성을 높인다.

  2. 실시간 데이터 파이프라인
    데이터 수집, 변환, 처리를 위한 실시간 데이터 파이프라인에서 Pub/Sub API가 중요한 역할을 한다. IoT 디바이스에서 발생한 데이터를 수집하고 분석 시스템으로 전달하는 데 활용될 수 있다.

  3. 알림 및 이벤트 시스템
    사용자 알림, 시스템 이벤트, 모니터링 알림 등을 분배하는 데 Pub/Sub 패턴이 효과적이다. 이벤트가 발생하면 관심 있는 모든 시스템이나 사용자에게 즉시 알림을 전송할 수 있다.

  4. 분산 시스템 동기화
    분산 시스템에서 상태 변경을 동기화하거나 캐시 무효화를 관리하는 데 Pub/Sub 메커니즘이 유용하다. 한 시스템에서 변경이 발생하면 관련 시스템에 자동으로 알림이 전달된다.

  5. 실시간 분석 및 대시보드
    실시간 데이터 분석 및 시각화를 위해 Pub/Sub API를 사용하여 데이터를 스트리밍하고 처리할 수 있다. 이는 비즈니스 인텔리전스 및 모니터링 대시보드에 활용된다.

Pub/Sub API 설계 시 고려사항

  1. 메시지 형식 및 스키마
    메시지의 구조와 형식을 신중하게 설계해야 한다. 스키마 정의 및 버전 관리를 통해 시스템 진화에 따른 호환성 문제를 방지할 수 있다.
    권장 사항:

    • Avro, Protobuf 등의 스키마 정의 언어 사용
    • 스키마 버전 관리 및 변경 전략 수립
    • 메시지 크기 최적화를 위한 직렬화 방식 선택
  2. 메시지 전달 보장 수준
    애플리케이션 요구사항에 따라 적절한 메시지 전달 보장 수준을 선택해야 한다.
    권장 사항:

    • 중요한 데이터의 경우 “at least once” 또는 “exactly once” 전달 사용
    • 중복 메시지 처리를 위한 멱등성(idempotency) 구현
    • 전달 실패 시 재시도 전략 및 데드 레터 큐(DLQ) 설정
  3. 확장성 및 성능
    시스템 규모와 부하 증가에 대비한 확장성 계획이 필요하다.
    권장 사항:

    • 파티션 또는 샤딩을 통한 수평적 확장 지원
    • 메시지 배치 처리를 통한 처리량 최적화
    • 부하 테스트 및 성능 모니터링 구현
  4. 오류 처리 및 복원력
    네트워크 문제, 서비스 장애 등 다양한 오류 상황에 대한 견고한 처리 방안이 필요하다.
    권장 사항:

    • 장애 발생 시 메시지 지속성 보장
    • 자동 재연결 및 재시도 메커니즘 구현
    • 서킷 브레이커 패턴 적용으로 시스템 보호
  5. 보안
    메시지의 기밀성, 무결성, 인증 등 보안 요구사항을 고려해야 한다.
    권장 사항:

    • 전송 중 암호화(TLS/SSL) 적용
    • 주제 및 구독에 대한 접근 제어
    • 필요시 메시지 페이로드 암호화

Pub/Sub APIs와 다른 통신 패턴 비교

특성Pub/SubRequest-ResponseEvent StreamingMessage Queue
통신 모드비동기, 다대다동기, 일대일비동기, 일대다비동기, 일대일
결합도매우 낮음높음낮음중간
확장성매우 높음제한적높음중간
메시지 흐름브로드캐스트/멀티캐스트직접 통신스트림 기반점대점
응답 처리응답 없음명시적 응답응답 없음응답 선택적
주요 사용 사례이벤트 알림, 상태 변경API 호출, 조회로그, 센서 데이터작업 배포, 부하 분산
구현 예시Kafka, SNS, Cloud Pub/SubREST, gRPCKafka Streams, KinesisSQS, RabbitMQ

최신 트렌드 및 미래 전망

  1. 서버리스 Pub/Sub
    서버리스 아키텍처와 Pub/Sub의 결합이 증가하고 있다. AWS Lambda, Google Cloud Functions, Azure Functions 등과 같은 서버리스 컴퓨팅 서비스를 Pub/Sub 이벤트로 트리거하여 이벤트 기반 아키텍처를 구현할 수 있다.

  2. 엣지 컴퓨팅과의 통합
    IoT 및 엣지 컴퓨팅 환경에서의 Pub/Sub 적용이 늘고 있다. 제한된 리소스를 가진 디바이스에서도 효율적으로 작동할 수 있는 경량 Pub/Sub 프로토콜(예: MQTT)의 사용이 증가하고 있다.

  3. 실시간 데이터 분석 통합
    Pub/Sub 시스템과 실시간 데이터 처리 프레임워크(Apache Flink, Spark Streaming 등)의 통합이 강화되고 있다. 이는 스트리밍 데이터에 대한 실시간 분석 및 인사이트 도출을 가능하게 한다.

  4. 멀티 클라우드 Pub/Sub 전략
    여러 클라우드 제공업체 간에 동작하는 통합 Pub/Sub 솔루션에 대한 수요가 증가하고 있다. 하이브리드 및 멀티 클라우드 아키텍처에서 일관된 메시징 인프라를 제공하는 솔루션이 발전하고 있다.

  5. 이벤트 메시 아키텍처
    단일 중앙 집중식 브로커를 넘어, 분산된 이벤트 메시(Event Mesh) 아키텍처가 등장하고 있다. 이는 여러 지역과 환경에 걸쳐 이벤트를 동적으로 라우팅하고 관리할 수 있는 유연한 인프라를 제공한다.


용어 정리

용어설명

참고 및 출처