거래소로 부터 수신한 데이타를 처리하는 프로세스를 개발한다.
처리프로세스
| 데이타 | 처리프로세스 | 기능 |
|---|---|---|
| 배치/기타 | sise_job_grp1 | 데이타를 가공해서 postgres,redis에 저장 |
| 체결 | sise_job_a3 | postgres에 적재 / 마스타는 redis에 업데이트 |
| 호가 | sise_job_b6 | redis에 업데이트 |
| 장운영 | sise_job_grp4 | redis에 업데이트 |
배치(마스타)/기타 데이타를 처리하는 sise_job_grp1를 개발
배치/기타 데이타중 가장 기본이 되고 가장 먼저 수신되는 증권종목정보(A0)를 처리한다.
처음으로 log를 처리하기 위한 작업이다. k8s에서는 콘솔출력(stdout, stderr)을 사용할 수 있어서 로그파일을 만들어서 작업할 필요가 많이 없어졌다. initApLogSt()기능은 파라메타에 따라 여러 기능을 제공한다.
로그파일로 출력, 메세지큐나 kafka로 보내는 방법, 콘솔로 출력하는 방법이 있는데 여기서 사용하는 방법은 log_mode = 9 콘솔출력으로 설정한다.
다음으로는 UDP수신 프로세스에서 데이타를 받은 방법을 설정한다. MSG_BROKER_TYPE=zmq로 ZeroMQ로 수신 한다. ZeroMQ로 사용하는 방법은 테스트를 해본 결과 ZeroMQ가 가장 효율적이라고 판단해서다.
DB는 db_type에 따라 MySQL, PostgreSQL 둘중 하나로 설정을 한다. initApSqlSt()에서 db 연결을 한다.
Redis 연결을 한다. 메인과 스레드에서 사용하기 때문에 메인용, 스레드용 2개의 연결을 생성한다.
메인에서 데이타를 수신해서 데이타 가공후에 db처리하는 스레드와 redis처리하는 스레드에 데이타를 송신하고 스레드에서 수신하는 채널을 생성한다.
타파스의 시세처리 프로세스의 구성은
| 함수 | 기능 |
|---|---|
| init() | log,메세지브로커,db,redis 등을 생성 |
| dispose() | init()에서 생성된 자원을 해제 |
| requestProc() | 시세데이타를 수신 받아서 처리하는 함수 |
| main() | 메인함수 |
크게 4개의 함수로 이루어져 있다.
위에서 살펴본 초기함수들을 init()안에 기술한다. 전체 소스는 아래와 같다.
_init()내 주요한 함수는 아래와 같다.
| 함수 | Lib | 기능 |
|---|---|---|
| initApLogSt | aplib | 로그를 처리하기 위한 함수 |
| initApMsgBroker | aplib | 메세지 브로커를 생성한다. 지원하는 메세지 브로커는 kafka, rabbitmq이다. |
| initApZmqSt | aplib | ZeroMQ를 사용하기 위한 함수 |
| initApSqlSt | aplib | 사용할 db를 연결한다. 지원하는 db는 mysql, postgresql이다. |
| initApRedisSt | aplib | 사용할 redis를 연결한다. |
| initChannelSt | pflib | 스레드와의 통신을 위해서 사용 |
| 이함수는 별도로 자세히 기술하기로 한다. |
모든 프로그램에서 _init()함수 사용후 반드시 _dispose()함수를 반드시 호출해야 한다. aplib/pflib의 라이브러리, 타파스 모든 프로세스는 init()으로 생성해서 dispose()로 해제하게 설계 되었다.
_init()함수내에서 메모리를 할당했거나, 메세지브로커, db에 연결을 했다면 _dispose()함수에서는 메모리를 해제, 연결을 끊는 처리를 한다.
지금까지 개발을 할때 별도의 환경파일을 만들어서 설정을 하곤 했다. 그런데 k8s환경에서는 그다지 효율적이지 못할것으로 판단했다.
그래서 결정한 방법이 k8s의 configmap을 사용한다. 아래는 k8s에서 sise_job_grp1에서 사용하는 configmap이다. _init()함수내에서 getenv()로 참조하면 된다.
_init()함수에서 사용하는 환경변수는 configmap에 정의하면 된다.
UDP데이타 수신(클라이언트) -----> 데이타처리(서버:sise_job_grp1)에서 통신은 ZeroMQ를 사용한다.
MSG_BROKER_TYPE : "zmq"으로 ZeroMQ로 사용하고 ZMQ_SERVER로 설정했다. 메세지브로커로 사용하고 싶으면 MSG_BROKER_TYPE : "kafka", "rabbitmq"로 설정하면 된다.
ZeroMQ를 사용하므로 UDP데이타를 수신하는 쪽에서 먼저 sise_job_grp1에 연결을 하고 UDP 데이타를 받아서 sise_job_grp1에 송신한다.
그래서 UDP데이타 수신 프로세스 보다 sise_job_grp1이 먼저 실행되어 수신프로세스에서 sise_job_grp1으로 연결을 해야 한다.
이제 _init()함수에서 사용하는 변수와 main()함수를 보자
메인함수에서 스레드와 통신하기 위해 생성한 채널(_gPgChannel, _gRedisChannel)을 스레드 생성시 파라메타로 넘겨준다.
sise_job_grp1에서 사용하는 모든 자원은 static으로 외부변수로 선언했다. main()에서 _init()함수가 성공적이라면 db처리 스레드와 redis처리 스레드를 생성하고 메세지브로커나 ZeroMQ로 부터 데이타가 들어올때 까지 대기한다.
데이타가 들어오면 requestProc()함수가 호출된다. 메세지브로커 사용시 수신 받을 함수(requestProc)를 넘겨준다.
ZeroMQ 사용시 수신 받을 함수(requestProc)를 넘겨준다.
데이타를 처리하는 모든 로직은 requestProc()함수에서 처리하면 된다.
위의 소스는 requestProc()함수에서 일단 배치(A0)처리만 한다. 이후 필요한 시세데이타 처리를 채워나가면 된다.(위의 데이타 형식은 같은 UDP포트로 수신된다.)

위의 구성도에서 데이타가공 부분을 담당하는것이 make_sise_a0()함수이다.
수신된 배치데이타를 가공하고자 할때 이함수에서 하면 된다. 여기서는 수신된 종목명은 euc-kr형식이므로 이를 utf-8로 변환한다. 수신데이타의 isin값이 999999999999이면 마지막 데이타를 알려주는 패킷으로 처리하지 않는다. 다만 종목배치를 전체 수신후 처리할 작업이 있으면 처리한다.
체결데이타를 적재할때 필요한 db의 일련번호를 초기화하고, 영업일정보를 설정한다.
make_sise_a0()함수에서 한글종목명, 영문종목명를 변환해 주면 시세데이타와 가공된 데이타를 (받은시세데이타 + utf-8의 한글종목명 + utf-8의 영문종목명) 형식으로 만들어서 db처리, redis처리를 하는 스레드에 채널을 통해 데이타를 전송한다.
이렇게 구성한 이유는 데이타처리를 스레드로 분산해서 데이타처리를 좀 더 빠르게 하기 위함이다.
메인에서 스레드 생성시 넘겨받은 채널로 채널로 데이타가 들어올때를 기다리다가 데이타가 들어오면 처리한다.
pg_thread()에서 PostgreSQL에 종목배치 시세데이타와 utf-8로 변환한 종목명을 저장한다.
타파스 시스템에서 데이타 처리는 주로 Redis로 처리하게 설계되었다.
redis_thread()에서는 5가지 형태로 데이타를 생성한다. 추후 또 다른 서비스로 인해 추가가 될 수도 있다.
종목배치(A0)데이타는 가장 먼저 들어오는 종목에 대한 마스타이다.
1) isin코드를 키로 종목마스타를 생성한다. 종목마스타에서 해당종목을 구별하기 위한 코드로 isin코드가 있다.
2) 단축코드(short_code)키로 isin코드를 데이타로 생성한다. 주식정보에서 사용자가 사용하는 코드가 단축코드다. 타파스 시스템에서는 isin코드를 키로 사용하기에 클라이언트에서 단축코드로 요청을 하면 이 단축코드로 isin코드를 구하기 위해서 필요하다. --> 단축코드 --> isin코드 --> 해당 데이타 조회
3) 체결마스타를 생성한다. 종목마스타의 마지막을 알리는 데이타는 isin코드(12자리)에 999999999999가 들어와서 끝을 알려준다. 이때 체결마스타, 호가마스타를 미리 생성한다.
4) 호가마스타를 생성한다. 호가데이타가 들어올때 업데이트를 해서 항상 최신의 호가정보를 가진다.
5) 분봉마스타를 생성한다. 체결데이타가 들어올때 1분봉의 데이타를 업데이트하고 1분이 지날때 마다 1분봉을 적재할때 필요한 데이타이다.
댓글
댓글 쓰기