Publisher-Subscriber Pattern

Publisher-Subscriber Pattern Publisher-Subscriber Pattern(게시자-구독자 패턴)은 소프트웨어 아키텍처에서 중요한 디자인 패턴 중 하나로, 분산 시스템에서 비동기 통신을 구현하는 데 널리 사용된다. 이 패턴은 메시지를 보내는 발행자(Publisher)와 메시지를 받는 구독자(Subscriber) 사이의 느슨한 결합(Loose Coupling)을 제공하는 메시징 패턴으로, 발행자는 메시지를 특정 주제(Topic)나 채널로 발행하고, 해당 주제를 구독하는 모든 구독자들이 그 메시지를 받게 된다. 이 패턴은 컴포넌트 간의 느슨한 결합을 제공하여 확장성과 유연성을 높이는 데 기여한다. https://learn.microsoft.com/en-us/azure/architecture/patterns/publisher-subscriber 기본 개념 Publisher-Subscriber 패턴의 핵심 개념은 다음과 같다: ...

September 27, 2024 · 3 min · Me

Retry Pattern

Retry Pattern Retry Pattern은 분산 시스템이나 마이크로서비스 아키텍처에서 일시적인 오류(Transient Failure)를 처리하기 위한 핵심 설계 패턴이다. 네트워크 불안정, 일시적인 서비스 중단 등 일시적인 실패 상황에서 시스템의 복원력(Resilience)을 강화하는 데 목적을 둔다. 이 패턴은 분산 시스템의 안정성을 높이는 필수 도구이지만, 남용할 경우 역효과를 낼 수 있으므로 신중한 정책 수립이 필요하다. Retry Pattern의 핵심 개념 작동 원리 실패한 작업 자동 재시도: API 호출, 데이터베이스 접근 등 실패 가능성이 있는 작업을 정의된 정책에 따라 재시도한다. 일시적 오류 감지: 네트워크 타임아웃, HTTP 5xx 에러, 데이터베이스 연결 실패 등 일시적인 오류만 대상으로 한다. 주요 구성 요소 ...

September 27, 2024 · 3 min · Me

Master-Slave

Master-Slave Pattern 마스터-슬레이브 패턴(Master-Slave Pattern)은 분산 시스템에서 널리 사용되는 소프트웨어 아키텍처 패턴. 이 패턴은 하나의 마스터 컴포넌트와 여러 슬레이브 컴포넌트로 구성되어 있으며, 작업을 효율적으로 분배하고 관리하는 데 사용된다. 주요 구성요소 마스터(Master): 작업 분배와 조정을 담당합니다 슬레이브들의 상태를 관리합니다 작업의 완료 여부를 추적합니다 결과를 취합하고 클라이언트에게 전달합니다 슬레이브(Slave): 마스터로부터 할당받은 작업을 처리합니다 독립적으로 동작합니다 처리 결과를 마스터에게 반환합니다 자신의 상태(사용 가능/처리 중)를 관리합니다 작업(Task): 처리해야 할 작업의 단위입니다 작업에 필요한 데이터와 결과를 포함합니다 고유한 식별자를 가집니다 작동 방식 마스터는 전체 작업을 여러 개의 하위 작업으로 분할합니다. 분할된 작업을 슬레이브들에게 분배합니다. 슬레이브들은 할당받은 작업을 독립적으로 수행합니다. 작업 완료 후, 슬레이브들은 결과를 마스터에게 보고합니다. 마스터는 모든 결과를 취합하여 최종 결과를 생성합니다. 장점 병렬 처리: 여러 슬레이브가 동시에 작업을 수행하여 전체 처리 속도를 향상시킵니다. 확장성: 슬레이브의 수를 늘리거나 줄여 시스템의 처리 능력을 조절할 수 있습니다. 부하 분산: 마스터가 작업을 효율적으로 분배하여 시스템 자원을 최적화할 수 있습니다. fault tolerance: 일부 슬레이브가 실패해도 마스터가 작업을 재분배하여 시스템이 계속 작동할 수 있습니다. 단점 단일 장애점: 마스터 노드가 실패하면 전체 시스템이 중단될 수 있습니다. 복잡성: 여러 노드 간의 통신과 동기화를 관리해야 하므로 시스템이 복잡해질 수 있습니다. 불균형한 작업 크기: 작업의 크기가 불균형할 경우 일부 슬레이브가 과부하될 수 있습니다. 적용 분야 데이터베이스 복제: 마스터 데이터베이스가 쓰기 작업을 처리하고, 슬레이브 데이터베이스들이 읽기 작업을 분산 처리합니다. 분산 컴퓨팅: 대규모 계산 작업을 여러 노드에 분산하여 처리합니다. 데이터 처리: 빅데이터 처리 시스템에서 마스터 노드가 작업을 관리하고 슬레이브 노드들이 실제 데이터 처리를 수행합니다. 임베디드 시스템: 여러 센서나 액추에이터를 제어하는 데 사용됩니다. 구현 예시 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 import threading from abc import ABC, abstractmethod from queue import Queue from typing import List import time import random # 작업을 정의하는 기본 클래스 class Task: def __init__(self, task_id: int, data: List[int]): self.task_id = task_id self.data = data self.result = None # 슬레이브의 추상 클래스 class Slave(ABC): def __init__(self, slave_id: int): self.slave_id = slave_id self.is_busy = False @abstractmethod def process_task(self, task: Task) -> None: pass # 구체적인 슬레이브 구현 - 숫자 배열의 합을 계산 class SumCalculatorSlave(Slave): def process_task(self, task: Task) -> None: print(f"Slave {self.slave_id} starting task {task.task_id}") # 실제 작업 처리를 시뮬레이션하기 위한 지연 time.sleep(random.uniform(0.5, 2.0)) task.result = sum(task.data) print(f"Slave {self.slave_id} completed task {task.task_id}, result: {task.result}") # 마스터 클래스 class Master: def __init__(self, num_slaves: int): # 슬레이브 풀 초기화 self.slaves = [SumCalculatorSlave(i) for i in range(num_slaves)] # 작업 큐 self.task_queue = Queue() # 완료된 작업 저장 self.completed_tasks = {} # 작업 분배를 위한 쓰레드 self.distribution_thread = threading.Thread(target=self._distribute_tasks) self.is_running = True def start(self): """마스터 시작""" print("Master starting…") self.distribution_thread.start() def stop(self): """마스터 종료""" print("Master stopping…") self.is_running = False self.distribution_thread.join() def submit_task(self, task: Task): """새로운 작업 제출""" print(f"Submitting task {task.task_id}") self.task_queue.put(task) def get_result(self, task_id: int) -> int: """작업 결과 조회""" while task_id not in self.completed_tasks: time.sleep(0.1) # 결과가 준비될 때까지 대기 return self.completed_tasks[task_id] def _distribute_tasks(self): """작업 분배 로직""" while self.is_running: try: # 대기 중인 작업이 있는지 확인 task = self.task_queue.get(timeout=1.0) # 사용 가능한 슬레이브 찾기 slave = self._get_available_slave() if slave: # 작업 처리를 위한 새 쓰레드 시작 threading.Thread( target=self._process_task_with_slave, args=(slave, task) ).start() except Queue.Empty: continue def _get_available_slave(self) -> Slave: """사용 가능한 슬레이브 찾기""" for slave in self.slaves: if not slave.is_busy: return slave return None def _process_task_with_slave(self, slave: Slave, task: Task): """슬레이브를 사용하여 작업 처리""" try: slave.is_busy = True slave.process_task(task) self.completed_tasks[task.task_id] = task.result finally: slave.is_busy = False # 사용 예시 def main(): # 3개의 슬레이브로 마스터 생성 master = Master(num_slaves=3) master.start() try: # 여러 작업 제출 tasks = [ Task(1, [1, 2, 3, 4, 5]), Task(2, [10, 20, 30, 40, 50]), Task(3, [100, 200, 300, 400, 500]), Task(4, [1000, 2000, 3000, 4000, 5000]) ] # 작업 제출 for task in tasks: master.submit_task(task) # 결과 수집 for task in tasks: result = master.get_result(task.task_id) print(f"Final result for task {task.task_id}: {result}") # 잠시 대기 후 종료 time.sleep(5) finally: master.stop() if __name__ == "__main__": main() 용어 정리 용어 설명 참고 및 출처

September 27, 2024 · 4 min · Me

Pipe-Filter Pattern

Pipe-Filter Pattern 파이프-필터 패턴(Pipe-Filter Pattern)은 데이터 스트림을 처리하는 시스템에서 사용되는 소프트웨어 아키텍처 패턴. 이 패턴은 복잡한 처리 과정을 독립적인 단계로 나누어 모듈화하고, 이들을 순차적으로 연결하여 데이터를 처리한다. 주요 구성 요소 필터(Filter): 단일 작업을 수행하는 처리 컴포넌트입니다 입력을 받아 처리하고 출력을 생성합니다 독립적으로 동작하며 다른 필터에 대해 알지 못합니다 재사용이 가능하고 조합할 수 있어야 합니다 파이프(Pipe): 필터 간의 데이터 전달을 담당합니다 데이터 버퍼링과 동기화를 처리합니다 필터들을 느슨하게 결합시킵니다 대개 큐나 스트림으로 구현됩니다 파이프라인(Pipeline): ...

September 27, 2024 · 4 min · Me

Producer-Consumer Pattern

Producer-Consumer Pattern Producer-Consumer Pattern은 소프트웨어 아키텍처에서 중요한 디자인 패턴 중 하나로, 주로 동시성 프로그래밍과 분산 시스템에서 사용된다. 이 패턴은 데이터를 생성하는 프로듀서(Producer)와 데이터를 소비하는 컨슈머(Consumer) 사이의 작업을 분리하여 효율적인 데이터 처리를 가능하게 한다. https://jenkov.com/tutorials/java-concurrency/producer-consumer.html 주요 구성 요소 프로듀서 (Producer): 데이터나 작업을 생성하는 엔티티. 컨슈머 (Consumer): 프로듀서가 생성한 데이터나 작업을 처리하는 엔티티. 버퍼 (Buffer): 프로듀서와 컨슈머 사이에서 데이터를 임시 저장하는 공유 자원. 주로 큐(Queue)의 형태로 구현된다. 작동 방식 프로듀서는 데이터나 작업을 생성하여 버퍼에 추가한다. 컨슈머는 버퍼에서 데이터나 작업을 가져와 처리한다. 버퍼는 프로듀서와 컨슈머 사이의 중간 저장소 역할을 하며, 동기화를 관리한다. 주요 특징 비동기 처리: 프로듀서와 컨슈머가 독립적으로 작동하여 비동기 처리가 가능하다. 버퍼링: 버퍼를 통해 생산과 소비 속도의 차이를 조절할 수 있다. 병렬 처리: 여러 프로듀서와 컨슈머가 동시에 작업할 수 있어 병렬 처리가 가능하다. 느슨한 결합: 프로듀서와 컨슈머는 서로에 대해 직접적인 의존성이 없다. 장점 처리량 향상: 생산과 소비를 병렬로 수행하여 전체 시스템의 처리량을 높일 수 있다. 유연성: 프로듀서와 컨슈머를 독립적으로 확장할 수 있다. 부하 분산: 버퍼를 통해 작업 부하를 균등하게 분산시킬 수 있다. 피크 부하 관리: 일시적인 부하 증가를 버퍼를 통해 완화할 수 있다. 단점 복잡성: 동기화와 버퍼 관리로 인해 시스템 복잡도가 증가할 수 있다. 메모리 사용: 버퍼가 큰 경우 메모리 사용량이 증가할 수 있다. 지연 가능성: 버퍼가 가득 차거나 비어있을 때 지연이 발생할 수 있다. 적용 사례 작업 큐 시스템: 백그라운드 작업 처리, 이메일 발송 등의 비동기 작업 관리 로그 처리 시스템: 대량의 로그 데이터를 효율적으로 수집하고 분석 스트리밍 데이터 처리: 실시간 데이터 스트림의 처리 및 분석 멀티스레드 애플리케이션: 스레드 간 작업 분배 및 동기화 구현 시 고려사항 동기화 메커니즘: 버퍼 접근 시 적절한 동기화 방법(예: 세마포어, 뮤텍스) 사용 버퍼 크기 조정: 시스템 요구사항에 맞는 적절한 버퍼 크기 설정 예외 처리: 버퍼 오버플로우, 언더플로우 등의 예외 상황 관리 종료 조건: 프로듀서와 컨슈머의 적절한 종료 시점 및 방법 정의 참고 및 출처

September 27, 2024 · 2 min · Me

Event-Bus Pattern

Event-Bus Pattern 소프트웨어 시스템의 컴포넌트 간 통신을 단순화하고 유연성을 높이는 아키텍처 패턴이다. 이 패턴은 발행-구독(Publish-Subscribe) 모델을 기반으로 하며, 컴포넌트 간의 느슨한 결합을 촉진한다. 장점 느슨한 결합: 컴포넌트 간 직접적인 의존성이 줄어들어 시스템의 유연성이 향상된다. 확장성: 새로운 컴포넌트를 쉽게 추가하거나 제거할 수 있어 시스템 확장이 용이한다. 비동기 통신: 이벤트 기반의 비동기 통신으로 시스템의 반응성과 성능이 향상된다. 단순화된 통신: 복잡한 컴포넌트 간 통신 로직을 단순화할 수 있다. 단점 복잡성 증가: 시스템 전체의 흐름을 파악하기 어려울 수 있다. 메모리 사용 증가: 모든 구독자에게 이벤트가 전달되므로 메모리 사용량이 증가할 수 있다. 디버깅의 어려움: 비동기적 특성으로 인해 문제 추적이 어려울 수 있다. 핵심 구성요소 https://medium.com/elixirlabs/event-bus-implementation-s-d2854a9fafd5 Event Bus with multiple subscribers(green arrows) and notifiers(red arrows) ...

September 26, 2024 · 4 min · Me