본문 바로가기
대동단결 Python

키움서버의 요청간격을 자동체크

by 즐거운 지니 2021. 5. 12.
반응형

키움 OPEN API에서는 클라이언트의 너무 빈번한 요청을 방지하기 위하여 요청간격 0.2초가 설정되어 있다. 어떤 클라이언트에서 요청 후 동일 클라이언트에서 요청하기 위해서는 0.2초 후에 가능하다. 요청간격 이전의 요청은 무시된다.

  • 서버에 뭔가를 요청하는 작업을 요청작업 이라 하자.
  • 전체 프로그램이 돌아가는 사이 다양한 형태의 다수의 요청작업이 시간에 관계없이 나타난다.
  • 한꺼번에 많은 요청작업이 생겨도 이를 0.2초의 요청간격을 두고 순차적으로 진행해야 한다.
  • 요청작업이 100개이면 모든 요청작업이 완료되기 까지 20초의 의 대기시간이 필요하다. 그 시간 동안 프로그램이 다른 작업을 할 수 없게된다.
  • 프로그램은 요청작업들이 각각 요청간격을 두고 순차적으로 진행하도록 설정해두고 다른 일을 할 수 있도록 하자.
from threading import Thread, Lock
from time import localtime, strftime, time, sleep


class 요청간격:
    """
    키움서버가 API 클라이언트로 부터 받는 작업요청의 시간 간격이 0.2초이다. 이를 지키려면 작업요청 전에
    요청간격.체크()
    를 실행해야 한다.
    """
    시작시간 = 0
    lock = Lock()

    @staticmethod
    def 체크():
        """
        키움서버에 요청한 이전 시간을 확인 한 후, 필요한 만큼의 시간을 기다림.
        :return:
        """
        with 요청간격.lock:
            지금 = time()
            기다림 = (0.2 - (지금 - 요청간격.시작시간))
            if 기다림 > 0: sleep(기다림)
            요청간격.시작시간 = time()


def worker(num):
    ts = time()
    print(f'worker {num} start at {ts}')
    요청간격.체크()
    te = time()
    print(f'worker {num} complete at {te} during {te-ts}')


for i in range(3):
    worker(i)
print('모든 워커가 일을 마칠 때 까지 대기해야 함.')
sleep(1)
print('... 1초후 다른 작업을 시작합니다.')
for i in range(3):
    Thread(target=worker, args=(i,)).start()
print('Thread 워커는?')
C:\ProgramData\Anaconda3-32\envs\python3.9\python.exe D:/work/projects/donkiwoom/donTest_요청간격.py
worker 0 start at 1620784693.2134843
worker 0 complete at 1620784693.2134843 during 0.0
worker 1 start at 1620784693.2134843
worker 1 complete at 1620784693.426966 during 0.21348166465759277
worker 2 start at 1620784693.426966
worker 2 complete at 1620784693.630073 during 0.20310711860656738
모든 워커가 일을 마칠 때 까지 대기해야 함.
... 1초후 다른 작업을 시작합니다.
worker 0 start at 1620784694.6335075
worker 0 complete at 1620784694.6335075 during 0.0
worker 1 start at 1620784694.6348495
worker 2 start at 1620784694.6356997
Thread 워커는?
worker 1 complete at 1620784694.834912 during 0.20006251335144043
worker 2 complete at 1620784695.0386844 during 0.402984619140625

Process finished with exit code 0
  • Thread 와 Lock를 이용해야 함.
  • sleep(시간)는 해당 쓰레드를 시간 만큼 쉬게한다.
  • 모든 서버요청작업을 Thread로 감싸고
  • Thread 안에서 서버 요청 전 요청간격.체크()를 부른다.
  • 요청간격.체크()는 꼭 Thread 안에서만 해야 되는 것은 아니지만, Thread 밖에서 체크하면 모든 작업이 그 시간 만큼 대기하게 된다.
  • 요청간격.체크() 는 서버 대기 시간을 확인하고 필요한 대기시간 동안 기다리게 한다.

    소스 설명

    • 요청간격.체크() 메쏘드는 스태틱 매쏘드로 어디서든 실행 할 수 있도록 한다.
    • 각각의 worker들이 요청간격을 체크하기 위해서는 lock을 얻어야 함.
    • lock은 오로지 하나의 쓰레드만이 얻을 수 있음. 다른 쓰레드에서 lock을 얻으려면 lock을 쥐고 있는 쓰레드가 그 lock을 풀어주어야 만 됨. 이를 구현하는 부분이 with 요청간격.lock: 임.
    • 요청간격.시작시간은 worker 가 lock을 획득하고 요청작업을 완료한 시간이 저장됨.
    • worker 가 요청간격.체크()하면 먼저 lock을 얻을 때 까지 기다려야 하고, 일단 lock을 얻으면 요청간격을 충족하는 필요한 대기 시간을 더 기다림.
    • worker(num) 함수는 요청작업을 진행하는 worker. 함수의 시작과 끝의 시간을 출력하도록 함.
    • 처음 3개의 워커는 쓰레드 없이 호출. 세개의 워커는 순차적으로 진행함.
    • 마지막 3개의 워커는 쓰레드로 감싸서 호출. 3개의 쓰레드가 동시에 발생하여 각각 비동기적으로 진행하며 순차적으로 lock을 얻은 다음 대기시간을 기다린 후 종료된다. 그러는 사이 메인 쓰레드(프로그램)은 다른 일을 할 수 있다.

    실행결과

    • 처음 세개의 워커
    for i in range(3):
        worker(i)
        
    -------------------
    worker 0 start at 1620784693.2134843
    worker 0 complete at 1620784693.2134843 during 0.0
    worker 1 start at 1620784693.2134843
    worker 1 complete at 1620784693.426966 during 0.21348166465759277
    worker 2 start at 1620784693.426966
    worker 2 complete at 1620784693.630073 during 0.20310711860656738
    • worker 0은 대기시간 없이 바로 요청작업 완료
    • worker 1은 worker 0 종료된 시점에서 작업 시작. 그 후 약 0.2초의 대기시간 후 작업 완료.
    • worker 2은 worker 1 종료된 시점에서 작업 시작. 그 후 약 0.2초의 대기시간 후 작업 완료.
    • 세 개의 워커들이 작업을 진행하는 동안 메인 쓰레드(프로그램)은 대기
    • 세 개의 워커들의 작업이 완료되고 나서야 마지막 print()문이 실행됨.

     

    • 1초의 시간을 흘려 보냄
    sleep(1)
    print('... 1초후 다른 작업을 시작합니다.')

     

    • 마지막 3개의 워커(쓰레드)
    for i in range(3):
        Thread(target=worker, args=(i,)).start()
    print('Thread 워커는?')
    
    -------------
    worker 0 start at 1620784694.6335075
    worker 0 complete at 1620784694.6335075 during 0.0
    worker 1 start at 1620784694.6348495
    worker 2 start at 1620784694.6356997
    Thread 워커는?
    worker 1 complete at 1620784694.834912 during 0.20006251335144043
    worker 2 complete at 1620784695.0386844 during 0.402984619140625
    • 3개의 워커는 거의 동시에 작업 진입.
    • worker 0 은 대기시간 없이 바로 요청작업 완료
    • 3개의 워커 작업이 진입 된 후 프로그램은 마지막 print()을 실행함.
    • worker 1은 worker 0이 작업 완료후 약 0.2초후 작업 완료. 결국 약 0.2초의 대기 시간이 발생함.
    • worker 2는 worker 1이 작업 완료후 약 0.2초후 작업 완료. 작업 진인 이후 결국 약 0.4초의 대기시간이 발생함.

     

    반응형

    댓글