Metric

Metric Metric는 시스템의 상태와 성능을 수치화하여 측정하는 중요한 관측 도구이다. Metric는 시스템의 상태, 동작, 성능 등을 나타내는 수치화된 측정값이다. 예를 들어, 웹 서버의 응답 시간, CPU 사용률, 메모리 사용량 등이 Metric가 될 수 있다. 장점 효율적인 저장: 숫자 데이터는 저장 공간을 적게 차지한다. 빠른 쿼리: 시계열 데이터베이스를 사용하여 빠른 검색과 분석이 가능하다. 장기 추세 분석: 오랜 기간 동안의 데이터를 저장하고 분석할 수 있다. 시각화 용이성: 그래프나 대시보드로 쉽게 표현할 수 있다. 단점 초기 설정에 시간과 노력이 필요하다 너무 많은 Metric는 오히려 혼란을 줄 수 있다 저장 공간과 처리 리소스가 필요하다 Metric의 중요성 성능 모니터링: 시스템의 전반적인 성능을 지속적으로 모니터링할 수 있다. 문제 감지: 비정상적인 패턴이나 임계값 초과를 빠르게 감지할 수 있다. 용량 계획: 리소스 사용량 추세를 분석하여 미래의 용량을 계획할 수 있다. 최적화: 성능 병목 현상을 식별하고 최적화할 수 있는 기회를 제공한다. Metric의 구성 요소 일반적인 Metric는 다음 요소로 구성된다: ...

September 28, 2024 · 3 min · Me

Trace

Trace Trace는 분산 시스템에서 요청의 흐름을 추적하고 시각화하는 데 사용된다. Trace는 분산 시스템에서 요청이나 트랜잭션이 여러 서비스와 컴포넌트를 통과하는 전체 여정을 기록한 것이다. 각 Trace는 하나 이상의 span으로 구성되며, 첫 번째 span은 root span이라고 한다. Trace의 목적 분산 시스템에서의 요청 흐름 이해 성능 병목 지점 식별 서비스 간 의존성 파악 오류 및 지연의 근본 원인 분석 Trace의 구성 요소 트레이스는 다음과 같은 구성 요소들로 이루어진다: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // 트레이스 시작 Span rootSpan = tracer.spanBuilder("checkout-process") .setSpanKind(SpanKind.SERVER) .startSpan(); try (Scope scope = rootSpan.makeCurrent()) { // 자식 스팬 생성 Span paymentSpan = tracer.spanBuilder("process-payment") .setParent(Context.current().with(rootSpan)) .startSpan(); try { processPayment(); paymentSpan.setStatus(StatusCode.OK); } catch (Exception e) { paymentSpan.setStatus(StatusCode.ERROR, e.getMessage()); throw e; } finally { paymentSpan.end(); } } finally { rootSpan.end(); } 트레이스 구성의 핵심 요소들: ...

September 28, 2024 · 3 min · Me

Log

Log Log는 애플리케이션 실행 시 생성되는 텍스트 기반의 기록이다. 이는 구조화된 형식(예: JSON)이나 비구조화된 텍스트 형식으로 제공될 수 있다. 문제가 발생했을 때 무슨 일이 있었는지 추적할 수 있게 해주며, 시스템의 동작을 이해하는 데 필수적인 정보를 제공한다. 로그 구조를 설계할 때는 다음과 같은 원칙들을 고려해야 한다: 일관성(Consistency): 모든 로그 항목은 동일한 구조와 형식을 따라야 한다. 이는 로그 파싱과 분석을 용이하게 만든다. 검색 가능성(Searchability): 주요 필드들은 쉽게 검색하고 필터링할 수 있는 형태여야 한다. 확장성(Extensibility): 새로운 정보를 추가할 필요가 생겼을 때 기존 구조를 해치지 않고 확장할 수 있어야 한다. 상세도 조절(Verbosity Control): 로그 레벨을 통해 필요한 상세도를 조절할 수 있어야 한다. 로그 구조를 효과적으로 설계하면 다음과 같은 이점을 얻을 수 있다: ...

September 28, 2024 · 4 min · Me

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

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

Access Modifiers

Access Modifiers 1. 주제 분류의 적절성 분석 “Access Modifiers(접근 제어자)” 를 “Computer Science and Engineering > System and Software Architecture > Principles > Programming Paradigms > Object-Oriented Programming(객체지향 프로그래밍)” 에 분류하는 것은 매우 적절합니다. 접근 제어자는 객체지향 프로그래밍 (OOP, Object-Oriented Programming) 의 핵심 원칙인 캡슐화 (encapsulation) 와 정보 은닉 (data hiding) 을 실현하는 주요 수단이기 때문입니다. 클래스, 메서드, 변수 등 구성 요소의 접근 범위를 제어하여 소프트웨어 아키텍처의 구조적 안정성과 보안성을 높이는 데 필수적입니다 [3][5][12]. ...

September 23, 2024 · 38 min · Me

Abstract Classes

Abstract Classes 추상 클래스는 하나 이상의 추상 메서드를 포함하는 클래스이다. 추상 메서드는 선언만 되고 구현되지 않은 메서드를 말한다. 이는 기본적인 구조는 정의하지만 세부적인 구현은 하위 클래스에 맡긴다. 기본 구조: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def calculate_area(self): """도형의 넓이를 계산하는 추상 메서드""" pass @abstractmethod def calculate_perimeter(self): """도형의 둘레를 계산하는 추상 메서드""" pass def get_description(self): """일반 메서드 - 모든 하위 클래스가 공유""" return "이것은 2차원 도형입니다." 주요 특징 인스턴스화 불가: 추상 클래스는 직접 객체를 생성할 수 없다. 상속 목적: 다른 클래스들의 기본 클래스 역할을 한다. 추상 및 구체 메서드 포함: 추상 메서드와 구현된 메서드를 모두 가질 수 있다. 공통 인터페이스 제공: 관련된 클래스들에 대한 공통 인터페이스나 동작을 정의한다. 추상 클래스의 구현과 활용 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Circle(Shape): def __init__(self, radius): self.radius = radius def calculate_area(self): """원의 넓이 계산 구현""" return 3.14 * self.radius * self.radius def calculate_perimeter(self): """원의 둘레 계산 구현""" return 2 * 3.14 * self.radius class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def calculate_area(self): """직사각형의 넓이 계산 구현""" return self.width * self.height def calculate_perimeter(self): """직사각형의 둘레 계산 구현""" return 2 * (self.width + self.height) 사용 목적 계층 구조 생성: 관련 클래스들의 공통 속성과 메서드를 정의한다. 템플릿 메서드 패턴: 알고리즘의 골격을 정의하고 일부 단계를 하위 클래스에서 구현하도록 한다. 프레임워크 개발: API나 프레임워크에서 기본 구조를 정의하는 데 사용된다. 예시 Java에서 추상 클래스 선언 예: 이 예시에서 Shape는 추상 클래스로, draw() 메서드는 추상 메서드이며 setColor() 메서드는 구체적인 구현을 가진다. ...

September 22, 2024 · 2 min · Me

Interfaces

Interfaces 소프트웨어나 애플리케이션에서 인터페이스(Interface)는 두 개의 시스템, 프로그램, 장치 또는 구성 요소 간의 상호 작용을 가능하게 하는 연결점 또는 접점을 의미한다. 인터페이스의 역할 통신 매개체: 서로 다른 시스템이나 구성 요소 간의 통신을 가능하게 한다. 추상화: 복잡한 내부 구현을 숨기고 간단한 사용 방법을 제공한다. 표준화: 상호 작용 방식을 정의하여 일관된 통신을 보장한다. 모듈화: 시스템을 독립적인 부분으로 분리하여 개발과 유지보수를 용이하게 한다. 인터페이스의 주요 기능 데이터 교환: 시스템 간에 정보를 주고받을 수 있게 한다. 기능 접근: 다른 시스템이나 모듈의 기능을 사용할 수 있게 한다. 호환성 보장: 서로 다른 시스템이나 버전 간의 호환성을 제공한다. 사용자 상호작용: 사용자와 시스템 간의 상호작용을 가능하게 한다(사용자 인터페이스의 경우). 오류 처리: 시스템 간 상호작용 중 발생할 수 있는 오류를 관리한다. 프로그래밍 언어에서 인터페이스의 간단한 예시 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 // Java에서의 인터페이스 예시 public interface Vehicle { // 추상 메서드 정의 - 구현하는 클래스에서 반드시 정의해야 함 void start(); void stop(); double getFuelEfficiency(); } // 인터페이스를 구현하는 구체적인 클래스 public class Car implements Vehicle { @Override public void start() { System.out.println("Car started"); } @Override public void stop() { System.out.println("Car stopped"); } @Override public double getFuelEfficiency() { return 15.5; // 리터당 킬로미터 } } 이 예시에서 Vehicle 인터페이스는 모든 차량이 가져야 할 기본적인 메서드를 정의하고, Car 클래스는 이를 구체적으로 구현한다. ...

September 22, 2024 · 2 min · Me