728x90

ZeroMQ란

ZeroMQ는 Zero Message Queue의 약자로, 고성능 경량 메시징 라이브러리

주로 메시지 큐, 분산 컴퓨팅, 백그라운드 작업 등의 작업을 수행하는 데 사용됨

ZeroMQ는 소켓을 사용하여 메시지를 전송하고, 여러 패턴 (pub-sub, req-rep 등)을 지원하여 유연성을 제공

파이썬에서는 pyzmq라이브러리를 통해 ZeroMQ를 사용할 수 있어 파이썬 애플리케이션에서 효율적이고 확장 가능한 분산 시스템을 구축할 수 있음

ZeroMQ의 장점

  • 가벼움
    ZeroMQ는 경량 메시지 패싱 라이브러리이므로, 추가적인 오버헤드 없이 빠르고 효율적인 통신 제공
  • 다양한 패턴 지원
    ZeroMQ는 다양한 통신 패턴을 지원하여 다양한 분산 시스템 아키텍처 구축 가능
    PUB-SUB, REQ-REP, PUSH-PULL 등의 패턴을 사용하여 쉽게 메시지 큐, 분산 작업 구현 가능
  • 다양한 언어 지원
    ZeroMQ는 C, C++, Python, Java, Go, Ruby 등 다양한 프로그래밍 언어에서 사용할 수 있어 다양한 환경에서의 통합 및 상호 운용성을 향상함
  • 유연성
    ZeroMQ는 유연한 구성 및 사용법을 제공하여 다양한 설정 옵션을 통해 사용자가 필요에 맞게 구성할 수 있음
  • 분산 시스템 구축 용이성
    ZeroMQ를 사용하면 복잡한 분산 시스템을 비교적 쉽게 구축할 수 있음
    다양한 컴포넌트 간의 통신을 표준화된 패턴을 사용하여 관리하기 용이함
  • 확장성
    ZeroMQ는 고성능 및 확장 가능한 아키텍처를 지원하므로, 대규모 시스템에서도 잘 동작함
  • 안정성
    ZeroMQ는 오랜 기간 동안 널리 사용되어 왔으며, 안정성이 검증됨
    또한 풍부한 커뮤니티 및 문서 지원을 통해 문제 발생 시 해결할 수 있음

ZeroMQ 사용 예시

Publisher와 Subscriber 두 개의 코드를 작성해야 함

ZeroMQ의 Publisher-Subscriber 모델에 대한 내용은 여기를 참고

 

1. 설치

pip install pyzmq

 

2. Publisher 코드 작성

import zmq
import time

context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:5555")

while True:
    message = "Update: " + str(time.time())
    socket.send_string(message)
    time.sleep(1)

 

3. Subscriber 코드 작성

import zmq

context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect("tcp://localhost:5555")
socket.subscribe(b"")

while True:
    message = socket.recv_string()
    print("Received message:", message)

 

4. 실행

Publisher를 먼저 실행하고, Subscriber를 실행

발행자가 주기적으로 메시지를 발행하고, 구독자가 이를 구독하여 수신하는 것을 확인할 수 있음

참고

pyzmq 공식 문서 : https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/pyzmq.html

728x90

'Python' 카테고리의 다른 글

[작성중] Python 문서화 (feat. sphinx)  (0) 2024.04.12
Python 문서화 (feat. PEP 257)  (0) 2024.04.11
asyncio를 이용한 Python 비동기 프로그래밍  (0) 2024.04.10
Python websocket  (0) 2024.04.08
Python 루틴, 서브루틴, 코루틴  (1) 2024.04.04
728x90

동기 vs 비동기

일반적인 동기식 프로그래밍에서는 작업이 순차적으로 실행되며, 한 작업이 완료될 때까지 다음 작업을 기다림

반면에 비동기식 프로그래밍에서는 작업을 병렬로 실행하고, 작업이 완료될 때까지 기다리지 않고 다른 작업을 계속할 수 있음

비동기 프로그래밍은 주로 입출력이나 네트워크 작업과 같이 시간이 오래 걸리는 작업을 수행할 때 유용함

이를 통해 여러 작업을 동시에 처리할 수 있고, 시스템 자원을 효율적으로 활용할 수 있음

asyncio

asyncio는 파이썬의 비동기 프로그래밍을 지원하는 라이브러리로, 이벤트 기반의 비동기 프로그램을 작성할 때 유용함

대규모의 동시성 작업을 처리하고, 네트워크와 파일 입출력을 효율적으로 다룰 수 있게 해 줌

asyncio의 핵심은 코루틴이벤트루프라는 개념

이벤트 루프는 비동기 작업을 관리하고 실행하는 역할이고, 코루틴은 비동기 함수로 중단된 지점에서 실행을 일시 중단하고 다른 작업을 수행한 뒤 재개할 수 있게 함

asyncio에서의 코루틴

코루틴은 async def 키워드를 사용하여 정의됨

asyncio에서는 await 키워드를 사용하여 코루틴을 호출하고 실행 

import asyncio

async def example_coroutine():
    print("Coroutine started")
    await asyncio.sleep(1)
    print("Coroutine finished")

async def main():
    await example_coroutine()

asyncio.run(main())

asyncio에서의 이벤트 루프

asyncio에서 이벤트 루프는 비동기 작업을 관리하고 실행하는 핵심 요소

이벤트 루프는 이벤트를 감지하고 처리하는 무한 루프로, 비동기 작업을 실행하고 완료될 때까지 대기하는 역할을 함

이를 통해 여러 비동기 작업을 동시에 실행하고, 작업이 완료될 때까지 기다리지 않고 다른 작업을 수행할 수 있음

시간이 오래 걸리는 입출력 작업을 비동기적으로 처리하여 프로그램의 성능을 향상하는 데 유용함

 

asyncio에서는 asyncio.get_event_loop 함수를 사용하여 이벤트 루프를 얻고, asyncio.run 메서드를 사용하여 비동기 작업을 실행함

import asyncio
import aiofiles

async def write_to_file(file_name, content):
    print(f"Writing to file: {file_name}")
    async with aiofiles.open(file_name, 'w') as file:
        await file.write(content)
        print(f"Content successfully written to {file_name}")

async def main():
    file_contents = [
        ("file1.txt", "Hello, World!"),
        ("file2.txt", "Async I/O is awesome!"),
        ("file3.txt", "Python is powerful!")
    ]

    # 비동기 파일 쓰기 작업 등록
    tasks = [write_to_file(file_name, content) for file_name, content in file_contents]

    # 비동기 작업 실행
    await asyncio.gather(*tasks)

# 이벤트 루프를 생성하여 main 함수 실행
asyncio.run(main())
더보기

실행결과

Writing to file: file1.txt
Writing to file: file2.txt
Writing to file: file3.txt
Content successfully written to file1.txt
Content successfully written to file2.txt
Content successfully written to file3.txt

asyncio에서의 동시성 제어

동시성 제어는 여러 작업이 동시에 실행될 때 발생하는 문제를 관리하고 해결하는 것

경쟁 조건과 교착 상태 같은 문제를 방지하여 프로그램의 안정성을 보장함

asyncio에서는 Lock, Semaphore, Queue 등의 동시성 제어 도구를 이용하여 여러 작업 간의 상호작용을 조절하고, 동기화를 유지할 수 있음

 

Lock를 이용한 동시성 제어 예제 코드

import asyncio

async def worker(lock, task_name):
    print(f"{task_name} is trying to acquire the lock")
    async with lock:
        print(f"{task_name} has acquired the lock")
        await asyncio.sleep(1)
        print(f"{task_name} is releasing the lock")

async def main():
    # 생성된 Lock 객체
    lock = asyncio.Lock()
    # 두 개의 작업이 동시에 실행되지만, 하나의 작업만이 lock을 확보하도록 하여 동시성 제어
    await asyncio.gather(
        worker(lock, "Task 1"),
        worker(lock, "Task 2")
    )

asyncio.run(main())
더보기

실행결과

Task 1 is trying to acquire the lock
Task 1 has acquired the lock        
Task 2 is trying to acquire the lock
Task 1 is releasing the lock
Task 2 has acquired the lock
Task 2 is releasing the lock

 

Semaphore를 이용한 동시성 제어 예제 코드

import asyncio

async def worker(semaphore, task_name):
    async with semaphore:
        print(f"{task_name} is entering the semaphore")
        await asyncio.sleep(1)
        print(f"{task_name} is leaving the semaphore")

async def main():
    # Semaphore 객체 생성
    semaphore = asyncio.Semaphore(2)  # 최대 2개의 작업을 동시에 실행
    # Semaphore를 공유하는 세 개의 작업
    await asyncio.gather(
        worker(semaphore, "Task 1"),
        worker(semaphore, "Task 2"),
        worker(semaphore, "Task 3")
    )

asyncio.run(main())
더보기

실행결과

Task 1 is entering the semaphore
Task 2 is entering the semaphore
Task 1 is leaving the semaphore
Task 2 is leaving the semaphore
Task 3 is entering the semaphore
Task 3 is leaving the semaphore

 

Queue를 이용한 동시성 제어 예제 코드

import asyncio

async def producer(queue):
    for i in range(5):
        print(f"Producing {i}")
        await queue.put(i)
        await asyncio.sleep(1)

async def consumer(queue, consumer_name):
    while True:
        item = await queue.get()
        print(f"{consumer_name} consumed {item}")
        queue.task_done()

async def main():
    # 크기가 3인 Queue 생성
    queue = asyncio.Queue(maxsize=3) # 최대 3개의 작업을 동시에 실행

    # Producer와 Consumer 생성
    producer_task = asyncio.create_task(producer(queue))
    consumer_task1 = asyncio.create_task(consumer(queue, "Consumer 1"))
    consumer_task2 = asyncio.create_task(consumer(queue, "Consumer 2"))

    # Producer가 종료될 때까지 기다림
    await producer_task
    # Consumer가 모든 작업을 처리할 때까지 기다림
    await queue.join()

    # Consumer들에게 종료를 알림
    consumer_task1.cancel()
    consumer_task2.cancel()

asyncio.run(main())
더보기

실행결과

Producing 0
Consumer 1 consumed 0
Producing 1
Consumer 1 consumed 1
Producing 2
Consumer 2 consumed 2
Producing 3
Consumer 1 consumed 3
Producing 4
Consumer 2 consumed 4

728x90

'Python' 카테고리의 다른 글

Python 문서화 (feat. PEP 257)  (0) 2024.04.11
Python ZeroMQ  (0) 2024.04.11
Python websocket  (0) 2024.04.08
Python 루틴, 서브루틴, 코루틴  (1) 2024.04.04
Python multiprocessing  (0) 2024.04.04
728x90

WebSocket이란

웹소켓(WebSocket)은 양방향 통신을 제공하는 컴퓨터 통신 프로토콜 중 하나

주로 웹 브라우저와 웹 서버 간의 실시간 데이터 전송을 위해 사용됨

HTTP 프로토콜과 달리 웹소켓은 한 번의 연결로 데이터를 주고받을 수 있어서 더 효율적이며, 실시간성이 요구되는 애플리케이션에 적합함

웹소켓은 다음과 같은 특징을 가지고 있음

  • 양방향 통신: 클라이언트와 서버 간에 양방향으로 데이터를 주고받을 수 있음
  • 실시간성: HTTP와는 달리 웹소켓은 계속 연결을 유지하며, 변경사항이 발생하면 즉시 데이터를 전달할 수 있어서 실시간성이 요구되는 애플리케이션에 적합함
  • 지속적인 연결: 웹 소켓은 지속적인 연결을 유지하여, 한번 연결되면 연결은 수동으로 종료되거나 네트워크 오류가 발생할 때까지 유지됨
  • 간편한 구현: 대부분의 주요 프로그래밍 언어에는 웹소켓을 다루기 위한 라이브러리나 프레임워크가 있어서 쉽게 구현할 수 있음

Python Websockets

Python에서 웹소켓을 다루기 위해서는 보통 websockets 라이브러리를 사용

이 라이브러리를 이용하면 손쉽게 웹소켓 서버와 클라이언트를 구현할 수 있음

 

websockets 라이브러리 설치

pip install websockets

 

웹소켓 서버 작성

import asyncio
import websockets

async def echo(websocket, path):
    async for message in websocket:
        await websocket.send(message)

async def main():
    async with websockets.serve(echo, "localhost", 8765):
        await asyncio.Future()  # Serve forever.

asyncio.run(main())

위 코드는 클라이언트가 보낸 메시지를 그대로 다시 돌려주는 간단한 에코(echo) 서버를 구현한 것

 

웹소켓 클라이언트 작성

import asyncio
import websockets

async def hello():
    uri = "ws://localhost:8765"
    async with websockets.connect(uri) as websocket:
        await websocket.send("Hello, world!")
        response = await websocket.recv()
        print(response)

asyncio.run(hello())

위 코드는 서버에 "Hello, world!"라는 메시지를 보내고, 서버로부터의 응답을 받아 출력하는 웹소켓 클라이언트를 구현한 것

 

실행

먼저 서버를 실행한 다음, 클라이언트를 실행

서버와 클라이언트가 연결되면 클라이언트는 서버에 메시지를 보내고, 서버는 클라이언트로부터 받은 메시지를 다시 돌려줌

웹소켓을 사용하면 위 예제처럼 간단하게 실시간 통신을 구현할 수 있음

필요에 따라 서버와 클라이언트 코드를 수정하여 원하는 기능을 구현 가능함

참고

https://wikidocs.net/218440

728x90

'Python' 카테고리의 다른 글

Python ZeroMQ  (0) 2024.04.11
asyncio를 이용한 Python 비동기 프로그래밍  (0) 2024.04.10
Python 루틴, 서브루틴, 코루틴  (1) 2024.04.04
Python multiprocessing  (0) 2024.04.04
파이썬 사용자 정의 에러  (0) 2024.04.02
728x90

마이크로서비스는 소프트웨어를 작고 독립적인 서비스로 분할하여 개발하고 배포하는 아키텍처

이러한 아키텍처를 지원하기 위해 쿠버네티스(Kubernetes)가 널리 사용되고 있음

쿠버네티스란

쿠버네티스는 컨테이너 오케스트레이션(Orchestration) 도구로, 컨테이너화된 애플리케이션의 배포, 확장 및 관리를 자동화하는 오픈소스 플랫폼

Google에서 개발한 Borg 시스템을 기반으로 하여 구축되었으며, 대규모의 컨테이너화된 애플리케이션을 효율적으로 관리하기 위해 설계됨

쿠버네티스는 컨테이너의 스케줄링, 자원 관리, 네트워킹, 스토리지 관리, 로깅, 모니터링 등 다양한 기능을 제공함

마이크로서비스 아키텍처란

마이크로서비스 아키텍처는 애플리케이션을 작은 서비스 단위로 분해하여 각 서비스가 독립적으로 배포, 업데이트 및 확장될 수 있는 아키텍처

각 서비스는 특정 비즈니스 기능을 담당하며, 서로 통신하여 전체 애플리케이션을 구성함

마이크로서비스 아키텍처는 애플리케이션을 유연하고 확장 가능하게 만들며, 서비스 간의 결합도를 낮추어 유지보수성을 향상시킴

  • 마이크로서비스 아키텍처의 핵심 원칙
    • 서비스 간 독립성 : 마이크로서비스는 독립적으로 배포/확장/유지보수될 수 있어야 하며, 다른 서비스의 변경 사항에 대해 영향을 받지 않고 개별적으로 업데이트할 수 있어야 함
    • 단일 책임 원칙 : 각 서비스는 한 가지 기능 또는 책임만 가져야 하며, 이를 위해 작게 유지되어야 함
    • 통신과 관리의 분리 : 서비스 간의 통신은 API를 통해 이루어지며, 각 서비스는 자체적으로 관리되어야 함
  • 마이크로서비스 아키텍처의 이점
    • 유연성 : 각각의 마이크로서비스는 독립적으로 개발, 배포 및 확장될 수 있어 보다 빠르게 개발 가능
    • 확장성 : 서비스 단위로 분할된 애플리케이션은 필요에 따라 개별적으로 확장할 수 있어 사용자의 요구에 맞게 스케일링 가능
    • 유지보수성 : 각 서비스는 독립적으로 관리되므로 하나의 서비스를 업데이트하거나 수정할 때 전체 애플리케이션에 영향을 미치지 않음
    • 장애 격리 : 하나의 마이크로서비스가 실패하더라도 시스템 전체가 영향을 받지 않아 다른 서비스는 계속 정상적으로 작동할 수 있도록 함

쿠버네티스를 통한 마이크로서비스 관리

각 마이크로서비스는 컨테이너로 패키징되어 쿠버네티스 클러스터에서 실행될 수 있음

쿠버네티스는 여러 서비스 간의 통신, 서비스 검색, 스케일링 등을 관리하는 데 도움이 됨

  • 서비스 디스커버리 및 로드 밸런싱 자동 처리
    쿠버네티스는 서비스 디스커버리 및 로드 밸런싱을 위해 서비스를 위한 DNS를 자동으로 설정하고 서비스에 접근할 때 요청을 자동으로 분산시켜 마이크로서비스 간 통신을 용이하게 함
  • 자동화된 배포
    쿠버네티스는 애플리케이션의 배포 및 업데이트를 자동화함
    롤링 업데이트와 같은 기능을 통해 새로운 버전의 애플리케이션을 점진적으로 배포하고, 문제가 발생할 경우 자동으로 롤백할 수 있어
    안정적인 배포 프로세스를 보장함
  • 스케일링
    쿠버네티스는 수평적 스케일링을 지원하여 서비스의 부하에 따라 자동으로 인스턴스 수를 조정할 수 있어 사용량이 변하는 상황에서도 안정적인 성능을 유지할 수 있도록 도와줌

결론

쿠버네티스와 마이크로서비스 아키텍처는 현대적인 소프트웨어 개발과 운영에서 핵심적인 역할을 하고 있음

이러한 기술들은 애플리케이션을 보다 유연하고 확장 가능하게 만들어줄 뿐만 아니라, 개발자들이 개발과 운영에 투입하는 노력을 최소화하고 안정적인 서비스를 제공할 수 있도록 도와줌

728x90
728x90

루틴(Routine)

  • 파이썬에서 루틴은 함수(function)를 가리킴
  • 일반적으로 루틴은 함수의 형태를 가지고 있으며, 코드 블록을 실행하여 특정한 작업을 수행
  • 함수를 호출하면 루틴이 실행되고, 실행이 완료되면 결과를 반환하거나 호출자에게 제어를 반환
def my_routine():
    print("This is a routine")

my_routine()  # 루틴 호출

서브루틴(Subroutine)

  • 파이썬에서 서브루틴은 함수(function)의 하위 개념으로 사용됨
  • 일반적으로 반복되는 코드를 재사용하기 위해 만들어진 함수를 가리킴
  • 다른 함수나 메소드 내에서 호출되어 실행되며, 실행이 완료되면 호출자에게 제어를 반환함
def my_subroutine():
    print("This is a subroutine")

def my_routine():
    print("Before subroutine")
    my_subroutine()  # 서브루틴 호출
    print("After subroutine")

my_routine()  # 루틴 호출

코루틴(Coroutine)

  • 파이썬에서 코루틴은 비동기 프로그래밍을 위해 사용되는 특별한 종류의 함수
  • 코루틴은 실행 중에 일시 중단되고 다시 시작될 수 있는 함수
  • 여러 번 호출될 수 있으며, 실행이 중단된 후 나중에 다시 호출되면 이전 상태를 기억하고 계속 진행함
import asyncio

# 코루틴 함수 정의
async def producer(queue, num):
    for i in range(num):
        print(f"Producing {i}")
        await queue.put(i)  # 데이터를 큐에 넣음
        await asyncio.sleep(0.5)  # 비동기 대기

async def consumer(queue):
    while True:
        data = await queue.get()  # 큐에서 데이터를 가져옴
        print(f"Consuming {data}")
        await asyncio.sleep(1)  # 비동기 대기

async def main():
    queue = asyncio.Queue()  # 비동기 큐 생성

    # producer와 consumer를 비동기적으로 실행
    await asyncio.gather(
        producer(queue, 5),
        consumer(queue)
    )

# 이벤트 루프를 생성하고 main 함수를 실행
asyncio.run(main())

이 코드에서는 async def 키워드를 사용하여 비동기 코루틴인 producer와 consumer 함수를 정의

producer 함수는 주어진 개수의 데이터를 생성하여 큐에 넣음

consumer 함수는 큐에서 데이터를 가져와서 소비함

main 함수에서는 asyncio.gather()를 사용하여 producer와 consumer를 동시에 실행하고, 이를 위해 이벤트 루프에 등록

이 코드를 실행하면 두 코루틴이 비동기적으로 실행되고, 데이터를 주고받으며 동작하는 것을 확인할 수 있음

서브루틴과 코루틴의 차이

  • 실행 흐름 제어
    • 서브루틴은 일반적인 함수처럼 호출되어 실행되고, 실행이 완료되면 호출자에게 제어를 반환
      일반적인 함수 호출과 동일한 방식으로 작동
    • 코루틴은 실행 중에 일시 중단되고 다시 시작될 수 있는 함수
      코루틴은 실행 중에 제어를 호출자에게 반환하고 다시 호출될 때 이전 상태를 기억하고 실행을 계속함
  • 동시성과 비동기성
    • 서브루틴은 동기적으로 호출되어 실행되며, 실행이 완료될 때까지 호출자는 대기
    • 코루틴은 비동기적으로 호출되어 실행되며, 실행 중에 다른 작업을 수행하거나 대기할 수 있음
      이를 통해 동시성을 지원하고 비동기 작업을 수행 가능
  • 호출 주체
    • 서브루틴은 일반적으로 호출자가 서브루틴을 호출하고 실행
      호출자는 서브루틴이 실행을 완료할 때까지 기다리고, 결과를 반환받음
    • 코루틴은 보통 이벤트 루프(Event Loop)나 다른 코루틴에 의해 호출되고 실행됨
      코루틴은 다른 코루틴이나 이벤트 루프에게 제어를 넘기고, 나중에 다시 실행됨

정리하면 서브루틴은 일반적인 함수 호출과 동일한 방식으로 실행되며, 실행이 완료될 때까지 호출자는 대기함

반면에  코루틴은 실행 중에 중단 및 재개가 가능한 비동기 함수이며, 동시성을 지원하고 호출자와 제어를 주고받는 동안 다른 작업을 수행할 수 있음

 

728x90

'Python' 카테고리의 다른 글

asyncio를 이용한 Python 비동기 프로그래밍  (0) 2024.04.10
Python websocket  (0) 2024.04.08
Python multiprocessing  (0) 2024.04.04
파이썬 사용자 정의 에러  (0) 2024.04.02
데코레이터  (0) 2024.04.01
728x90

개요

파이썬은 기본적으로 멀티스레딩을 지원하지만, Global Interpreter Lock(GIL)의 영향으로 CPU-bound 작업에서는 실제로 병렬 실행을 보장하지 못함

이에 대한 대안 중 하나가 multiprocessing

multiprocessing 모듈은 파이썬의 표준 라이브러리에 포함되어 있으며, 여러 프로세스를 사용하여 작업을 병렬로 실행할 수 있게 해 줌

multiprocessing은 별도의 프로세스를 생성하며, 각 프로세스는 별도의 메모리 공간을 가지므로 GIL의 영향을 받지 않고  병렬 처리를 지원함

이를 통해 CPU-bound 작업의 성능을 향상할 수 있음

기본 사용법

import multiprocessing

def worker(num):
    """작업을 수행하는 함수"""
    print(f'Worker: {num}')

if __name__ == '__main__':
    # 부모 프로세스 (메인 프로세스)
    # 프로세스 스포닝 (자식 프로세스 생성)
    processes = []
    for i in range(5):
        p = multiprocessing.Process(name="SubProcess", target=worker, args=(i,))
        processes.append(p)
        p.start()

    # 모든 프로세스 종료 대기
    for p in processes:
        p.join()

multiprocessing 모듈에서 프로세스 스포닝은 Process 클래스의 인스턴스를 생성한 후 start( ) 메서드를 호출하면 됨

Process 클래스의 인스턴스를 생성할 때 생성될 자식 프로세스의 이름(name)과 위함 하고자 하는 일(함수, target), 인자(args)를 전달

※ 스포닝 : 부모 프로세스가 운영체제에 요청하여 자식 프로세스를 새로 만들어내는 과정

프로세스 풀 활용

multiprocessing.Pool을 사용하면 풀 내에서 여러 작업을 병렬로 실행 가능

multiprocessing.Pool 객체를 생성할 때 processes 매개변수를 사용하여 동시에 실행할 프로세스의 수를 지정 가능 ( 만약 지정하지 않는다면 CPU 코어 수에 따라 자동으로 설정됨 )

이를 통해 동시에 몇 개의 작업을 처리할지를 유연하게 관리할 수 있음

풀이 가득 차있는 상황에서 새로운 작업이 추가되면 작업은 작업 큐에 대기하며, 만약 작업 큐에 공간이 있으면 풀 내의 프로세스가 새로운 작업을 가져와서 처리함

from multiprocessing import Pool

def worker(num):
    """작업을 수행하는 함수"""
    return f'Worker: {num}'

if __name__ == '__main__':
    with Pool(processes=3) as pool:
        results = pool.map(worker, range(5))
    print(results)

데몬 활용

일반적으로 생성한 경우 부모 프로세스는 자식 프로세스가 종료되지 않으면 종료되지 않음

하지만 multiprocessing 모듈에서 daemon=True 옵션을 사용하면 부모 프로세스가 종료될 때 자식 프로세스도 함께 종료됨

 

데몬 프로세스를 사용하여 메인 프로세스에 영향을 주지 않으면서 동작시킬 작업들을 구현할 수 있음

  • daemon=True 옵션을 주면 유용한 예시
    • 로그 감시 데몬
      시스템 로그 파일을 지속적으로 모니터링하고 변경사항을 감지하여 중요한 이벤트를 기록하는 데몬
      이 데몬은 백그라운드에서 지속적으로 실행되며, 부모 프로세스가 종료되면 함께 종료되는 것이 좋음
    • 자동 백업 프로세스
      주기적으로 파일 시스템을 스캔하여 중요한 데이터를 백업하는 데몬
      이러한 데몬은 사용자가 시스템에 로그인할 필요 없이 백그라운드에서 실행되며, 부모 프로세스가 종료되면 함께 종료되는 것이 좋음

# Daemon Process
# 메인 프로세스가 종료시 서브 프로세스도 종료됨
import multiprocessing as mp 
import time


def work():
    print("Sub Process start")
    time.sleep(5) 
    print("Sub Process end")


if __name__ == "__main__":
    print("Main Process start")
    proc = mp.Process(name="Sub Process", target=work, daemon=True)
    proc.start()
    print("Main Process end")

서브 프로세스 상태 확인 및 종료

import multiprocessing as mp 
import time

def work():
    while True:
        print("sub process is running") 
        time.sleep(1)


if __name__ == "__main__":
    p = mp.Process(target=work, name="SubProcess")
    print("Status: ", p.is_alive())

    p.start()
    print("Status: ", p.is_alive())

    time.sleep(5)   # 메인 프로세스 3초 대기
    p.kill()        # 서브 프로세스 종료
    print("Status: ", p.is_alive())

    p.join()        # 메인 프로세스는 서브 프로세스가 종료될 때까지 블록됨
    print("Status: ", p.is_alive())

 

결과

Status:  False
Status:  True
sub process is running
sub process is running
sub process is running
sub process is running
sub process is running
Status:  True
Status:  False

 

multiprocessing 사용 시 주의사항

  • 메모리 사용량이 증가할 수 있으므로 대용량 데이터 처리 시 주의 필요
  • 각 프로세스가 독립적인 메모리 공간을 가지므로 데이터를 공유하려면 명시적인 메커니즘(예: 공유 메모리, 큐 등)을 사용해야 함
import multiprocessing as mp

def worker(q):
    q.put(1) 
    q.put(2) 
    q.put(3)

if __name__ == "__main__":
    # multiprocessing.Manager().Queue()로 Queue 생성
    manager = mp.Manager()
    q = manager.Queue() 
    
    # 프로세스 생성
    processes = []
    for _ in range(3):
        p = mp.Process(target=worker, args=(q,))
        processes.append(p)
        p.start()

    # 모든 프로세스가 종료될 때까지 대기
    for p in processes:
        p.join()

    # 멀티프로세스 간에 데이터를 공유하기 위해 Queue를 사용하여 데이터를 가져옴
    results = []
    while not q.empty():
        results.append(q.get())

    print(results)

 

결과

[1, 2, 3, 1, 2, 3, 1, 2, 3]

 

728x90

'Python' 카테고리의 다른 글

Python websocket  (0) 2024.04.08
Python 루틴, 서브루틴, 코루틴  (1) 2024.04.04
파이썬 사용자 정의 에러  (0) 2024.04.02
데코레이터  (0) 2024.04.01
파이썬 매직 메서드  (0) 2024.03.29
728x90

사용자 정의 에러란?

파이썬에서 사용자 정의 에러는 내장된 예외 클래스(Exception 클래스를 상속한 클래스)를 확장하여 새로운 예외 클래스를 정의하는 것

이렇게 정의된 사용자 정의 에러 클래스는 프로그램에서 특정 조건이나 상황에 대한 예외를 일으킬 때 사용

사용자 정의 에러를 정의함으로써 코드의 가독성을 높이고, 예외 처리를 보다 구체적으로 할 수 있음

사용자 정의 에러 구현 방식

  1. 내장 예외 클래스(Exception 클래스를 상속)를 상속하여 새로운 클래스를 생성
  2. 클래스 내부에 필요한 추가적인 메서드나 속성을 정의할 수 있음
    예를 들어, __str__ 메서드를 재정의하여 해당 예외가 발생했을 때 출력될 문자열을 지정할 수 있음
  3. 사용자 정의 에러 클래스를 발생시키기 위해 필요한 조건이나 상황을 프로그램에서 체크하고, 그에 따라 적절한 사용자 정의 에러 객체를 생성하여 raise 키워드를 사용하여 예외를 일으킴

사용자 정의 에러 구현 예시

class UpbitError(Exception):
    """Base 에러 클래스"""
    pass 


class CreateAskError(UpbitError):
    def __str__(self):
        return "주문 요청 정보가 올바르지 않습니다."


class CreateBidError(UpbitError):
    def __str__(self):
        return "주문 요청 정보가 올바르지 않습니다."


class InsufficientFundsAsk(UpbitError):
    def __str__(self):
        return "매수/매도 가능 잔고가 부족합니다."


class InsufficientFundsBid(UpbitError):
    def __str__(self):
        return "매수/매도 가능 잔고가 부족합니다.

예시 코드에서는 UpbitError를 기반으로 여러 사용자 정의 에러 클래스를 정의하고 있음

이 코드에서는 주문 요청 정보가 올바르지 않을 때와 매수/매도 가능 잔고가 부족할 때를 처리하기 위해 각각의 사용자 정의 에러 클래스를 구현하고 있음

위에서 구현한 사용자 정의 클래스로 예외를 발생시키는 예시코드는 아래와 같음

# 예시 코드
def create_ask(order_info):
    if not is_valid_order_info(order_info):
        raise CreateAskError()

def create_bid(order_info):
    if not is_valid_order_info(order_info):
        raise CreateBidError()

def execute_trade(trade_type, order_info):
    if trade_type == 'ask':
        try:
            create_ask(order_info)
        except CreateAskError as e:
            print("에러 발생:", e)
    elif trade_type == 'bid':
        try:
            create_bid(order_info)
        except CreateBidError as e:
            print("에러 발생:", e)

def is_valid_order_info(order_info):
    # 주문 요청 정보가 유효한지 확인하는 코드
    return True  # 예시로 항상 유효하다고 가정

# 테스트
order_info = {'amount': 100, 'price': 50}
execute_trade('ask', order_info)

order_info = {'amount': 100, 'price': 50}
execute_trade('bid', order_info)

위 코드에서는 주문 요청 정보가 유효하지 않을 경우와 매수/매도 가능 잔고가 부족할 경우에 각각의 사용자 정의 에러 클래스를 발생시킴

그리고 execute_trade 함수 내에서 이러한 예외를 처리함

참고

https://wikidocs.net/126218

728x90

'Python' 카테고리의 다른 글

Python 루틴, 서브루틴, 코루틴  (1) 2024.04.04
Python multiprocessing  (0) 2024.04.04
데코레이터  (0) 2024.04.01
파이썬 매직 메서드  (0) 2024.03.29
파이썬 공부할 책 모음  (0) 2024.03.28
728x90

데코레이터란? 

데코레이터(Decorator)는 파이썬에서 함수나 클래스의 기능을 간단하게 수정하거나 확장하기 위한 기법

주로 함수를 감싸는 형태로 사용되며, 이를 통해 함수의 동작을 변경하거나 확장 가능

데코레이터 사용 방법

데코레이터는 기능을 추가하고자 하는 함수나 메서드 위에 @데코레이터함수 와 같이 사용함

이렇게 함으로써 데코레이터 함수가 해당 함수를 감싸서 새로운 동작을 추가하거나 수정할 수 있음

데코레이터 사용 예시

deco라는 데코레이터 함수를 정의하여 hello 함수에 hello 프린트 전/후로 * 20개를 출력하는 새로운 동작을 추가함

def deco(fn):
    def deco_hello():
        print("*" * 20)
        fn()
        print("*" * 20)
    return deco_hello
    
@deco
def hello():
    print("hello")
    
hello() # hello 호출

 

호출 결과

********************
hello
********************

참고

https://wikidocs.net/160127

728x90

'Python' 카테고리의 다른 글

Python multiprocessing  (0) 2024.04.04
파이썬 사용자 정의 에러  (0) 2024.04.02
파이썬 매직 메서드  (0) 2024.03.29
파이썬 공부할 책 모음  (0) 2024.03.28
파이썬 Iterable, Iterator, Generator 간단 소개와 활용 예제  (0) 2024.03.21
728x90

Visual Studio Code에서는 기본적으로 패키지 디렉토리를 PYTHONPATH에 자동으로 추가해주지 않음

 

따라서 그냥 파이썬 파일을 실행하면 다음과 같은 에러가 발생함

 

이러한 에러를 방지하기 위해서는 launch.json 파일에 PYTHONPATH 설정을 하면 됨

launch.json은 debug 메뉴를 클릭하여 create a launch.json file > Python Debugger > Python File 을 클릭하여 생성할 수 있음

 

생성된 launch.json아래와 같이 PYTHONPATH 관련 설정 추가

  • "cwd": "${fileDirname}"
    • cwd  설정은 "current working directory"의 약자로, 디버깅 세션을 시작할 때 디버깅이 시작될 디렉토리를 지정함
    • ${fileDirname}은 현재 열려있는 파일의 디렉토리 이름을 나타냄
    • 이 설정은 종합적으로 Python 스크립트를 디버그하고 있는 경우에 해당 스크립트의 디렉토리를 작업 디렉토리로 설정함
  • "env": {"PYTHONPATH": "${workspaceFolder}${pathSeparator}${env:PYTHONPATH}"}
    • env  설정은 환경 변수를 설정
    • 여기서 "PYTHONPATH"는 Python 모듈을 검색하는 데 사용되는 경로를 지정하는 환경 변수
    • ${workspaceFolder}는 현재 작업 중인 Visual Studio Code 프로젝트의 디렉토리를 나타냄
    • ${pathSeparator}는 OS에 따라 파일 경로를 분리하는 데 사용되는 경로 구분자를 나타냄
    • 마지막으로 ${env:PYTHONPATH}는 이미 설정된 PYTHONPATH 환경 변수를 참조함
    • 이 설정은 종합적으로 현재 프로젝트의 경로를 PYTHONPATH에 추가하여 Python 인터프리터가 해당 디렉토리에서 모듈을 찾을 수 있도록 함

 

설정을 추가해 준 뒤에는 다음과 같이 정상적으로 수행되는 것을 확인 할 수 있음

 

728x90

'etc > vscode' 카테고리의 다른 글

vscode 테마 창마다 다르게 설정하는 방법  (0) 2024.05.24
vscode 언어 별로 설정 다르게 하기  (0) 2024.04.25
728x90

매직 메서드란?

매직 메서드는 파이썬의 특수한 메서드로, 던더(double underscore, __)로 시작하고 끝나는 이름을 가지고 있음

특정한 문법이나 동작을 제공하거나 클래스의 특정한 동작을 변경하거나 확장하는 데 사용됨

매직메서드의 종류

일반적으로 사용되는 매직 메서드

  • __init__
    • 이 메서드는 클래스의 인스턴스가 생성될 때 호출되는 초기화 메서드로, 주로 객체의 속성을 초기화하고 초기 상태를 설정하는 데 사용
  • __repr__ 및 __str__
    • 객체의 문자열 표현을 반환
    • __repr__: 객체의 "official" 문자열 표현을 반환하는 메서드로, 객체를 재생성할 수 있는 문자열을 반환 (개발자용으로 사용됨)
    • __str__: 객체의 "informal" 문자열 표현을 반환하는 메서드로, 사용자에게 보여주기 위한 문자열을 반환
  • __len__
    • 객체의 길이를 반환 len() 함수가 호출될 때 사용됨
    • 리스트, 튜플, 문자열 등과 같은 내장된 시퀀스 형식들은 모두 __len__ 메서드를 구현하여 길이 반환 가능
  • __getitem__ 및 __setitem__
    • 이 두 메서드는 객체의 인덱싱 및 슬라이싱을 지원
    • __getitem__: 인덱싱 및 슬라이싱을 통해 요소를 가져오는 메서드
    • __setitem__: 인덱싱을 통해 요소에 값을 할당하는 메서드
  • __iter__ 및 __next__
    • 두 메서드는 객체의 이터레이션을 지원
    • __iter__: 이터레이터를 반환하는 메서드로, 이터레이터 프로토콜을 지원하여 객체를 반복할 수 있게 함
    • __next__: 다음 값을 반환하는 메서드로, 이터레이터의 다음 요소를 반환

연산자 오버로딩을 위한 매직 메서드

  • __add__(self, other)
    • 덧셈 연산자(+)를 오버로딩
    • 객체와 다른 객체를 더한 결과를 반환
  • __sub__(self, other)
    • 뺄셈 연산자(-)를 오버로딩
    • 객체에서 다른 객체를 뺀 결과를 반환
  • __mul__(self, other)
    • 곱셈 연산자(*)를 오버로딩
    • 객체와 다른 객체를 곱한 결과를 반환
  • __truediv__(self, other)
    • 나눗셈 연산자(/)를 오버로딩
    • 객체를 다른 객체로 나눈 결과를 반환
  • __floordiv__(self, other)
    • 나눗셈 연산자의 몫(//)를 오버로딩
    • 객체를 다른 객체로 나눈 몫을 반환
  • __mod__(self, other)
    • 나머지 연산자(%)를 오버로딩
    • 객체를 다른 객체로 나눈 나머지를 반환
  • __pow__(self, other[, modulo])
    • 거듭제곱 연산자(**)를 오버로딩
    • 객체를 다른 객체의 거듭제곱으로 계산한 결과를 반환
  • __eq__(self, other)
    • 동등성 비교 연산자(==)를 오버로딩
    • 객체가 다른 객체와 동일한지를 확인하는 데 사용
  • __lt__(self, other), __gt__(self, other), __le__(self, other), __ge__(self, other)
    • 대소 비교 연산자(<, >, <=, >=)를 오버로딩
    • 객체를 다른 객체와 비교하여 대소 관계를 확인하는 데 사용

컨텍스트 관리를 위한 매직 메서드

  • __enter__(self)
    • 컨텍스트가 시작될 때 호출되는 메서드
    • 이 메서드는 with 블록의 시작에서 호출되며, 컨텍스트 진입 시 수행해야 하는 작업을 정의
    • 일반적으로 컨텍스트 진입 시 초기화 작업이나 리소스를 할당하는 작업을 수행
    • 이 메서드는 with 문에 의해 생성된 컨텍스트 매니저를 반환
  • __exit__(self, exc_type, exc_value, traceback)
    • 컨텍스트가 종료될 때 호출되는 메서드
    • 이 메서드는 with 블록에서 예외가 발생했더라도 항상 호출
    • 컨텍스트 종료 시 정리 작업을 수행하거나 예외 처리를 위한 로직을 구현 가능
    • 만약 예외가 발생했을 경우에는 exc_type, exc_value, traceback 파라미터에 예외 정보가 전달됨

매직 메서드를 사용한 커스텀 데이터 구조 및 동작 정의 예시

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    # 벡터의 덧셈 연산을 정의
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    # 벡터의 뺄셈 연산을 정의
    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)

    # 벡터의 스칼라 곱셈 연산을 정의
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)

    # 벡터의 크기(길이)를 반환하는 메서드를 정의
    def __abs__(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

    # 벡터의 문자열 표현을 정의
    def __str__(self):
        return f"({self.x}, {self.y})"

# 사용 예시
v1 = Vector(1, 2)
v2 = Vector(3, 4)

print("v1 =", v1)
print("v2 =", v2)
print("v1 + v2 =", v1 + v2)  # 벡터 덧셈
print("v1 - v2 =", v1 - v2)  # 벡터 뺄셈
print("v1 * 2 =", v1 * 2)    # 스칼라 곱셈
print("|v1| =", abs(v1))     # 벡터 크기(길이)

참고

매직메서드소개 : https://wikidocs.net/83755

728x90

+ Recent posts