[코딩몰라여] steem-python으로 포스트와 댓글 실시간으로 읽어오기 기초 ~속도개선~ 편

By @maanya4/25/2018kr-dev

front3.png

아직도 알아야할게 있다니! 뭔지 모르겠지만 예외처리도 잘 따라했는데?

# 스팀잇에서 실시간 처리란?


  스팀잇에서 실시간이란 블록이 생성되는 주기, 3초에 해당합니다. 스팀잇에서 실시간 봇이란 최근 블록을 읽어서 필요한 행동을 다음 블록이 생성되기 전에 끝마치는 절차를 계속해서 반복하는 프로그램이라고 표현할 수 있습니다. 주어진 일을 3초마다 해내지 못하는 봇은 불완전합니다. 점점 일이 밀리게 될테니까요.

불완전한 봇에는 어떤 것이 있나요? 이런 봇이 있습니다.

result03.PNG

  실시간편 기초에서 계속 보여드렸던, kr 태그가 붙은 포스트와 댓글을 보여주는 프로그램입니다. 아주 간단한 프로그램이죠? 실행해보신 분들은 눈치 채셨을지 모르겠지만 이 봇은 불완전한 봇으로써 아주 지옥의 성능을 갖고 있습니다.

실행한 직후에는 현재 시간으로부터 약 1분 전의 글을 읽어오지만 처리 속도가 떨어져 10분만 지나도 2~3분이 밀리게 됩니다. 실행해둔 시간이 길어질 수록 '실시간' 과는 거리가 멀어집니다.

속도개선 편에선 이에 대해 자세히 살펴보고자 합니다.

gomen.jpg
저도 일상글 쓰고싶어요! 미안해요! 문과 친구들!

# Blockchain.py에 숨겨진, 잠드는 시간


07.PNG

의도하지 않았는데 부제목이 흑기사님이 쓰신 것 같다.

  Blockchain 클래스의 stream 함수에는 프로그램이 잠드는 시간이 포함되어있습니다. blockchain.py를 살펴보면 곳곳에서 time.sleep 이라는 코드를 통해 잠드는 부분이 있는 것을 확인하실 수 있습니다.

이는 실시간 스트림을 하면서 생성되지 않은 블록을 프로그램이 접근하지 않도록 여유 시간을 준 것이라고 판단합니다.

그렇다고 blockchain.py 에서 time.sleep을 전부 삭제하는 것은 안정성을 고려했을 때 별로 좋지 못한 선택이 됩니다. 그래서 Blockchain 클래스의 stream 함수를 대신하는 함수를 새로 만들기로 했습니다.

계속해서 작성되는 포스트와 댓글을 감시하려면 프로그램이 줄곧 블록을 읽어와야하는 것은 사실입니다. 하지만 모든 기록이 발생한 순간에 읽어야만 하는 것은 아닙니다.

왜냐면, 우리는 3초 단위의 세상에 살고 있으니까요. 그리고 그 단위를 블록이라고 부르지요. 그럼 블록이 생성될 때마다 읽어야할까요? 아니요. 딱히 그렇지만도 않을 것입니다. 생성될 때마다 읽을 수 있다면 좋지만, 딱히 그럴 필요는 없죠. 우리가 느끼기에 '충분히 빠르다.' 라고 생각할 정도의 속도만 가지고 있으면 된다고 생각했습니다.

lazy.gif

  블록 5개씩 읽어들이는 프로그램을 만들었다고 가정해볼게요. 블록이 5개 생성되는데엔 15초가 걸리니 5개의 블록을 읽어들이고, 원하는 기능에 따라 처리하는 과정을 15초 내에 전부 해내기만 하면 해당 프로그램은 안정성을 가지게 됩니다. 이렇게 계산을 미뤄두고 한 번에 처리하는 것은 느긋한 계산법(Lazy evaluation)에 포함되는 개념입니다.

스팀잇에서 일어나는 일에 대한 반응이 15초 안에 일어난다면 충분히 빠르다고 볼 수 있겠죠? 예를 들면 가이드독을 부르는 댓글을 달고나서 15초 후에 나타난다해도 우리는 충분히 빠르다고 생각합니다.

이렇게 굳이 계산을 미뤄뒀다 한 번에 처리하는 이유는 프로그램에서 느린 행위 중에 손꼽히는게 입출력(I/O)이기 때문에 마음의 준비를 하고 한 번에 처리하는게 더 빠르기 때문입니다. 설거지를 조금씩 하는 것보다 한 번에 하는게 더 빠른 것과 마찬가지죠. 세제도 적게 쓸 수 있습니다. :D

@tpdns90321 님의 이 포스트를 참고해서 b.stream을 대신할 함수를 아래와 같이 만들었습니다.

  • 블록 내용을 JSON으로 가져온다.
  • 블록을 가져올 땐 gevent 모듈을 이용하여 빠르게 가져온다.
  • 블록 내용에서 코멘트만 간추려내는 것도 다중 프로세싱(Multiprossing)으로 구성하여 속도를 높인다.

get_block 함수 내에 FalseTrue로 바꾸면 보상(Reward) 관련 활동만 가져올 수도 있습니다.

01.PNG

02.PNG

# 편리함의 댓가를 지독하게 치르게 만드는 Post


  이전까지는 stream = map(Post, b.stream(filter_by=['comment'])) 를 통해, 읽어들인 블록의 내용을 Post 객체로 바꾸어서 처리했습니다. Post 객체를 사용하면 코드의 가독성이 높아지고 각종 편리한 기능을 사용할 수 있다는 장점이 있습니다.

읽어들인 내용이 포스트(본문)인지, 댓글인지 판단하려면 is_main_post, is_comment 함수만 호출하면 바로 알려줍니다. 내용은 ['body']만 붙이면 바로 가져올 수 있습니다. 하지만 그 편리함에는 엄청난 문제가 있었으니, 블록의 내용을 Post 객체로 바꾸기만 해도 어마어마한 시간을 소모한다는 것입니다. 한 블록의 처리가 3초가 넘을 정도로 말이죠.

실험을 위한 메인 함수는 아래와 같습니다.

05.PNG

  1. 프로그램 실행 직후에 가장 최근의 블록 번호를 수집합니다.
  2. 7초 후(2 ~ 3블록 생성)에 가장 최근의 블록 번호를 수집합니다.
  3. 수집한 블록 번호에 해당하는 블록을 처리합니다.
  4. 수집한 블록을 처리하는 동안 새로운 블록이 생성되었을테니 생성된 블록을 처리하는 과정을 반복합니다.

## 1. Post 객체로 변환하지 않는 경우

03.PNG

## 2. Post 객체로 변환하는 경우

04.PNG

## 각 경우에 대한 처리해야할 블록 수 변화

06.PNG

  Post 객체를 사용하지 않는 경우엔 쌓여있는 블록을 처리하는 속도가 빨라 처리해야할 블록의 갯수가 줄어듭니다. 4회 이후로는 처리 속도가 훨씬 빨라서 다음 블록이 생성될 때까지 기다리고 있다가 처리합니다. 그러나 Post 객체를 사용하는 경우에는 별 다른 기능도 없이, Post 객체로 변환하는 것만으로도 처리가 버거워 점점 처리해야할 블록이 늘어납니다.

스팀잇과 관련된 프로그램을 만들 때 Post 객체를 사용하는 횟수를 최소한으로 줄여야합니다. Post 객체를 사용할 땐 정말 필요한지 고민하고 사용합시다. :D

그럼 Post 객체를 사용하지 않고 내용은 어떻게 확인할 수 있나요?
포스트를 보시면 됩니다. 구조가 똑같아요. (찡긋)

# 실시간 기초편은 진짜 끝? 제발... 저도 그러길 바라요.


original.gif

  이번 포스트의 내용은, 저로써는 참 오랫동안 해결하려고 노력했던 것입니다. 더 많은 내용을 안내해드리기 전에 제가 깨달아서, 알려드릴 수 있어서 다행입니다. 마아냐봇(@maanyabot)을 시작으로 실시간 처리를 위한 프로그램을 만들었는데 왠걸, 처리속도가 안나오는겁니다. 점점 처리해야 할 댓글은 많아지고 1시간이 지나면 20분이 밀리고, 2시간이 지나면 40분이 밀리더라고요.

이 문제를 해결하기 위해 제가 취한 긴급 조치는 봇이 30분만 동작하고 처리 못한 댓글들은 다 버리게 하는 것이었습니다. 그리고 봇을 두 개를 실행하는 것이죠. 15분 간격으로. 마아냐봇1 이 9시부터 동작하면, 마아냐봇2 는 9시 15분부터 동작하기 시작합니다.

봇1 은 9시 30분이 되면 그 때 처리할 수 있는 댓글까지만 처리하고(약 20분 치) 나머지는 다 버린 후에 새로 시작해서 9시 30분의 댓글부터 처리하는 것이죠.

봇2 는 9시 45분이 되면 마찬가지로 처리 못한 것은 다 버리고 새로 시작하는 방식으로 처리 속도에 대한 문제를 땜빵했습니다. 그러다보니 봇1과 봇2가 겹치는 시간대가 존재하고, 댓글이 두 개씩 작성하는(...) 현상이 생겼던 것입니다.

오랫동안 저를 괴롭힌 문제를 해결해서 기분이 좋기도 하고 허탈하기도 합니다. 급하게 만드느라 확인을 제대로 못했다고 변명하고 싶지만, 그런 것까지 확인할 정도로 실력이 좋지 않기 때문에... ;ㅂ;// Post를 사용할 때 코드가 더 이뻐지는 것도 맞거든요. 허허.

스캐머들은 계속해서 새로운 링크로 사람들을 낚으려고 하고 있고, 그러한 링크를 봇에 추가할 때마다 봇은 더욱 느려졌고, 약 2주 전부터는 도저히 제 기능을 못하는 상태가 됐습니다. 이제 고쳐줄 수 있어서 기쁩니다. 먼저 손을 들고 나선 이상, 져야 할 책임이 있기 때문이죠. 이 이야기는 코딩몰라여 시리즈와 어울리는 이야기는 아니군요 (...) 여기까지만.

커피가 땅깁니다. 커피 한 잔 마시면서 잠깐 스팀잇 둘러보다가 고치러 갈게요. 즐거운 하루 되세요. :D




이번 편에서 작성된 코드는 아래의 링크에서 다운로드 가능합니다.
https://github.com/maaanya/codingmolayo-steempy
코딩몰라여 시리즈는 아래의 사이트에서 모아볼 수 있습니다.
https://codingmola.herokuapp.com/

26

comments