Django 를 개발할 때, background job(Async Task) 을 처리하면서 처음으로 Rabbitmq + Celery 를 처음 접했다. 웹에서 긴 동작 처리를 하거나 주기적인 반복동작, 정해진 시간에 일을 처리하기 위해서 여러 방법이 있다. 

웹에서 긴작업이 문제가 되는 경우는 사용자가 인내력 없이 다른 페이지도 이동하는 경우도 있겠지만 인내력이 있다고 해도 Nginx 나 Apache 그리고 uwsgi 같은 시스템들이 Flask 가 Djaong 의 특정 request 의 동작이 길면 뭔가 잘못되었다고 생각해서 client 에  timeout 를 발생시키고 해당 프로세스를 죽이기 때문에 문제가 발생할 수 있다. 

 정해진 시간에 처리하기 위한 가장 쉬운 방법은 Cron(ko.wikipedia.org/wiki/Cron) 을 이용하는 것이다.  일반적으로 정해진 시간에 특정동작은 DB 접근도 필요한 경우가 많아 Flask 에서 이용한 DB 모델(일반적으로 sqlalchemy 로 정의된 모델)구조를 이용할 수 있어야 한다. 이 때 Flask Cli Command 를 이용해서 마치 하나의 console 프로그램 처럼 실행할 수 있다. 그리고 일반적으로 virtualenv 환경에서 동작해야 하고, 여러 환경변수로 전달해야 하고, 로그도 남겨야 한다. 그러다 보면 아래 형태가 된다. 

 bash -c 'FLASK_APP=/usr/share/nginx/flask/main.py FLASK_CONFIG=testing SERVER_NAME="test.labstoo.com" /opt/py_envs/web/bin/python -m flask dailyjob >> /var/log/crontab/dailyjob.log 2>&1 '


Background job 을 하기 위해서는 일반적으로 Dajngo 쪽에서는  Rabbitmq + Celery , Redis + Celery 를 선택하는 것 같다. 이 job 을 동작하기 위해서는 중요한 것이 Background Job 만 처리하는 woker 라는 프로그램을 따로 만들어야 하는 것이다. 일반적으로 이 프로그램도 Django 구조를 사용하게 할 수는 있다. 

Flask 계열에서는 일반적으로 RQ(python-rq.org/) 계열의 종류를 많이 사용하는 것 같다. 이 것도 Redis + Worker 가 필요하다. 

Worker 구조를 갖는 경우 모두 한 서버에서 구현한다고 하면 Nginx(또는 Apache) + uwsgi(또는 gunicorn ) + Postgresql (또는 Mysql, MongoDB 같은 DB) + Redis (또는 Rabbitmq ) + Worker(Celery 를 이용하거나 RQ를 이용하거나) 의 선택을 하게 된다. 이 경우 Redis 의 경우 Cache 서비스 때문에 이용한다고 치더라도 Worker 까지 사용하는 건은 웬지 배보다 배꼽이 큰 것 같이 느껴진다. 
 물론 넉넉한 회사의 경우 이런 서비스 들에 대해서 모두 이중화를 하겠지만 스타트업에 다니면서 단 하나의 서버만을 쓰는 입장에서는 뭔가 리소스 낭비로 생각되기도 하다. (물론 이런 경우 서비스가 죽는다면 다시 서버를 켜면 된다. 결제 시스템 같은 것이 붙은 것도 아니기  이런 선택을 할 수 있다.)

이런 때 사용할 수 있는 시스템이 apscheduler( github.com/agronholm/apscheduler flask 의 경우 github.com/viniciuschiele/flask-apscheduler, django 의 경우 github.com/jcass77/django-apscheduler ) 이다. 따로 Thread 를 만들어 동작하는 방식이다. 이렇기 때문에 Django 나 Flask 가 죽으면 동작하지 않는다. 그리고 프로세스마다 Thread 가 생성되기 때문에 잘 처리해줘야 한다. (uwsgi 의 경우 lazy 옵션을 사용하는 경우가 특히 이렇다. 내 경우 가 이런 형태라 사용을 포기했다. ) 그리고 기본적으로 이중화 처리가 좀 어렵다. 그래서 사용하더라도 하나의 서버만 사용하는 경우에 적합하다.


그리고 오늘 발견한 시스템이 있는데, uwsgi 를 이용하는 경우 uwsgi 자체의 background-job 을 지원하는 경우를 사용해서 처리하는 방법이 있다. 이를 조금 편하게 지원하기 위해 uwsgi_tasks (github.com/Bahus/uwsgi_tasks) 라는 라이브러리를 사용할 수 있다. 결과적으로 uwsgi 가 책임지고 task 를 돌려주는 방식이다. 그리고 서버가 죽었다가 다시 실행하더라도 자동으로 다시 task 를 동작한다. 아직 회사에서 테스트 줄인데, uwsgi 설정파일(ini 파일) 에 설정한 환경변수도 같이 전달되는지는 확인 하지 못했다. 그거 까지 된다면 단일 웹서버 시스템의 경우 Celery 의 대안이 될 수 있을 것 같다.

 

 

 

 예전부터 만든던 프로그램을 rust 로 만들기 위해 rust를 공부하고 있다. 

예전부터 만들고 싶었던 프로그램이란 간단한 형태의 cvs viewer 와 약간의 함수를 지원하는 프로그램인데, 이 프로그램이 처음에는 nodejs 을 이용해서 electron 으로 만들려고 했는데, 성능이 생각대로 나오지 않아서 golang으로 만들려고 했는데, 그것도 내가 원하는 속도가 나오지 않아서 rust 로 만들려고 한다. 아마 이것도 내가 원하는 만큼의 속도가 안나온다면  cuda 를 이용해 가속을 하는 방법밖에 없는 것 같다.

첫 며칠은 그래도 공부할만 하다가 뒤로 갈 수로 많이 어렵다. 소유권까지는 이해 할 것 같은데, 라이프타임이라는 개념이 나오자, 내 머리가 너무 복잡해졌다. rust는 메모리관리가 없는 줄 알았는데, 라이프타임개념은 완전 반자동 메모리 관리 개념이 들어 가는것 같더라.

첫 회사에서 C++ 으로 프로그램을 만들었는데, 그 때 메모리 관리를 addRef 함수와 release 함수로 했다. 이 함수를 호출하면 내부적인 counter 가 +1 또는 -1 이 되었다. 이 값이 0이 되면 delete this; 가 호출되면서 메모리가 스스로 삭제되는 식으로 만들어져있다. 그래서 참조를 할 때 마다 addRef 해주고 참조가 사라진 때, release 함수를 꼭 해줘야 했다. 이게 참조라는 개념이 나도 모르게 되는 경우도 있는지라, 참 어려웠고 메모리 오류가 발생하는 버그도 참 많았다. 

 rust 의 라이프타임을 보자 옛날에 사용했던 addRef, release 함수가 생각난다. 이거 이렿게 까지 복잡하게 메모리를 관리하진 않길 바란다. 

내가 느낌 rust 의 인상은 그래도 C나 C++ 보다는 개발환경이 편리한 것 같다. vscode 가 꽤 편하고 platform 에 따라 코드가 엄청 달라지지 않는 것 같다. 이것만 해도 그런대로 할만한 것 같다. 

 

 전에 kaggle 대회에 첨가한다고 했는데, 오늘 첫 결과를 올렸다. LGBMClassifier 을 이용하는 코드인데, 참가하게 되면서 배운것들이 많다. 회사업무에서 생긴 의문도 해소되었다. 정확도를 측정하는 방법이 여러가지가 있었다. 나는 accuracy 만을 생각했는데, f1 socre 를 이용하는 방법도 있었다. yiunsr.tistory.com/849 에서 모든 계약을 실패로 처리하면 accuracy 는 높아지지만 f1 score 는 낮을 것이다. 이 경우에는 모델의 정확도를 f1 score 로 평가하는게 좋았을 것이다. 

 아직 개선해야 할 방법이 많지만 눈에 보이는 목표가 있고, 경쟁이 있으니 뭔가 더 재미있는 것 같다. 이번 기회에 확실히 level up 해야겠다.