[Tornado] 파이톤을 통해 I/O가 차단되지 않는 웹 응용 프로그램 구축

14406 단어 Pythontornado

개시하다


파이톤의 웹 프레임워크Tornado를 소개합니다.
죄송하지만, 뎅고나 플래시보다 인지도가 낮고(대략) 지금도 적극적으로 FW를 업데이트하고 있는데 잠재력이 높은 FW라고 생각해요.

Tornado란?


파이톤에서 비차단 입출력을 제공할 수 있는 웹 프레임워크를 말합니다.
닫히지 않은 입출력은 node를 나타냅니다.제 생각에는 js입니다.
Curl과 mysql 연결 등 외부 통신을 할 때 CPU를 계속 잡을 때까지 기다리지 말고 끊임없이 다른 방식으로 다음 처리를 하는 특성을 고려할 수 있다.
모르는 사람이 있으면 참고하세요이 일대.
노드야.파이톤을 통해 js 호환 동작을 실현할 수 있습니다!이거 대박이다!
"비폐쇄적인 I/O로 실현하고 싶어요. 하지만 node.js보다 파이톤을 더 좋아해요."이럴 땐 꼭 채용하세요!

I/O를 차단하지 않는 용도


그렇다면 실제로 I/O가 차단되지 않는 이점은 무엇일까요? 예를 들어 처리량이 많은 API를 들 수 있습니다.
구체적으로 지금 유행(?)를 참고하십시오.방문량이 많아 스스로 논리성이 강하다기보다는 마이크로 서비스의 교접적 입장이라는 API 같은 것이 성능을 잘 발휘할 수 있지 않을까 싶다.
아니면 폐쇄적이지 않은 I/O의 단점을 잘 생각하지 못하기 때문에 솔직히 어디서든 사용할 수 있다고 생각합니다.

사용법


그럼 저는 Tornado에서 간단한 설치 방법을 소개하고 싶습니다.
이번에는 다음과 같은 환경에서 실시한다.
python
tornado
OS
3.6
5.1.1
Linux
실시례에서 이용async/await했기 때문에pythn3.나는 조건이 5 이상이라고 생각한다.

Tornado 설치


pip 명령 하나만 있으면 됩니다.(이것은 pip를 사용하는 전제이다.)
pip install tornado
설치를 확인하려면 아래에서 확인하십시오.(버전은 현재 최신 버전으로 앞으로 업데이트되면 변경됩니다.)
pip freeze | grep tornado
tornado==5.1.1 

hello world


Users Guide에도 약정hello world이 기재되어 있다.
PaaS에서 이동하기 위해 설치 포트만 변경되었습니다.
app.py
import os
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(os.environ.get("PORT", 8080))
    tornado.ioloop.IOLoop.current().start()
이걸 시작해서 방문해 보세요.
python app.py

나는 이런 느낌으로 방문할 수 있다고 생각한다.
하지만 이렇게 되면 블로킹 같은 게 아니라 서버를 가동한 것일 뿐...

비동기 HTTP 통신


그럼 이번에는 비동기적으로 외부 통신을 하는 반을 만들어 보자.
Qiita에 투고 중이라서(?)예제에서는 Qita API에 액세스합니다.
적절한 카탈로그를 끊고 거기서 핸들러 클래스(MVC로 불리는 컨트롤러)를 만듭니다.
midir routes
vim routes/access.py
access.py
import tornado.web
from tornado.httpclient import AsyncHTTPClient

class AsyncHandler(tornado.web.RequestHandler):
    # Getでアクセスした際、Handlerクラスの`get`メソッドが呼び出される。
    # Post等も同様。リクエストメソッドを意識せず処理したい場合は`prepare`メソッドを実装するとそこにルーティングされる。
    async def get(self):
        body = await self.getRate()
        self.set_header("Content-type", "application/json")
        self.write(body)

    async def getRate(self):
        http_client = AsyncHTTPClient()
        try:
            response =  await http_client.fetch("https://qiita.com/api/v2/items?page=1")
            return response.body
        except Exception as e:
            print("Error: %s" % e)

외부 통신 시에는 Tornado에서 제공한AsyncHTTPClient을 사용합니다.
일반적으로 파이톤의 요청에 사용된requests 등은 모두 동시 실행된 것으로 토너도(Tornado)의 특성을 활용할 수 없다.
그리고 핸들 r급 앱을 제작했다.py에 불러옵니다.
app.py
import os
import tornado.ioloop
import tornado.web
+from routes.access import AsyncHandler

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
+       (r"/async", AsyncHandler), # タプルの先頭にURL、次にHnadlerクラスを指定して、ルーティングできる。
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(os.environ.get("PORT", 8888))
    tornado.ioloop.IOLoop.current().start()

이렇게 하면 완성된다.
재부팅app.py, 액세스/async이렇게 하면 Qita API의 내용을 얻을 수 있을 것 같습니다.
이렇게 Tornado가 제공하는 프로그램 라이브러리를 이용하여 비막힌 상태에서 외부 통신을 실현할 수 있다.
예?단지 이렇다면 이것이 정말 비동기적인 이동인지 모르겠는가?
아, 네...

비동기 통신의 동작 검증(추가)


살려줘.Tornado 사용법도 별것도 아닌 만큼 관심 없는 사람은 건너뛰세요.
원래는 앱 성능 측정만 하면 비동기 통신 동작 등을 바로 확인할 수 있었는데 어쩔 수 없이 준비 같은 것도 귀찮아서 여기에 설치하는 김에 동작도 확인하고 싶었어요.access.py도 비교적 사용되는 동기화 통신 단점을 만들어 보았다.
access.py
import tornado.web
from tornado.httpclient import AsyncHTTPClient
import requests

class AsyncHandler(tornado.web.RequestHandler):
    async def get(self):
        print("Async Request")
        body = await self.getRate()
        self.write(body)

    async def getRate(self):
        http_client = AsyncHTTPClient()
        try:
            response =  await http_client.fetch("http://localhost:8888/")
            return response.body
        except Exception as e:
            print("Error: %s" % e)

class SyncHandler(tornado.web.RequestHandler):
    def get(self):
        print("Sync Request")
        body = self.getRate()
        self.write(body)

    def getRate(self):
        try:
            response = requests.get("http://localhost:8888/")
            return response.text
        except Exception as e:
            print("Error: %s" % e)

app.py
#- from routes.access import AsyncHandler
+ from routes.access import AsyncHandler, SyncHandler
# 略
        (r"/", MainHandler),
        (r"/async", AsyncHandler), 
+       (r"/sync", SyncHandler), 
    ])

# 略
동기화 통신용 라이브러리에 넣었기 때문에 설치했습니다.
(Tornado가 제공하는 동기화 통신의 HTTP 클라이언트 라이브러리도 있지만 그들이 원하는 대로 작동하지 않아 조사 중...)
pip install requests
여기서부터는 좀 번거로우니 통신용 통신용과 통신할 수 있도록 두 포트에서 애플리케이션을 시작해 I/O가 차단되지 않는 동작을 확인하세요.
export PATH=8888
python app.py

# ターミナルの別タブとか開いて

export PATH=8080
python app.py
액세스localhost:8080를 통해 8080 포트의 응용 프로그램이 8888 포트의 응용 프로그램에 대해 비동기 통신과 동기 통신을 할 때의 두 가지 모델을 검증할 수 있다.
(Qita API에서 localhost로 통신 목적지를 변경하는 것은 Qita를 공격하지 않고 검증을 통해 동시에 액세스하기 위한 것입니다.)
실제로 아파치 벤치 등으로 성능 측정을 하면 비차단 I/O의 특성을 확인할 수 있을 것으로 생각한다.
비동기 통신
ab -n 10 -c 10 localhost:8080/async
# 略
Percentage of the requests served within a certain time (ms)
  50%     34
  66%     34
  75%     34
  80%     34
  90%     35
  95%     35
  98%     35
  99%     35
 100%     35 (longest request)
동기 통신
ab -n 10 -c 10 localhost:8080/sync
Percentage of the requests served within a certain time (ms)
  50%     23
  66%     27
  75%     32
  80%     37
  90%     41
  95%     41
  98%     41
  99%     41
 100%     41 (longest request)
100% 종료 전의 지연을 통해 알 수 있듯이 비동기 처리는 기본적으로 동시에 진행되고 동기 처리는 천천히 응답을 회복했다.
또한 ab를 실행할 때 출력하는 print 문장의 출력에도 다른 쪽에서 오류가 발생할 수 있습니다.나는 동기들이 천천히 수출할 수 있다고 생각한다.

총결산


파이톤에서 비동기 처리를 수행할 수 있는 Tornado를 소개했다.
Tornado 자체가 그렇게 새로운 FW는 아닌데, wiki 초판에 따르면 2009년인 것 같다.
하지만 저는 최근에 이 FW를 만나서 개인적으로 너무 좋아해요. 앞으로 채용 상황이 계속 늘어나면 좋겠어요.
일본어 자료가 많지 않기 때문에 책도 아마존에서 한 권영어 교과서만 검색했어요...
응, 비동기 통신을 하려면 처음부터 노드로 써.그렇게 지도 모른다면 앞으로 토나의 활약이 기대된다. 파이톤의 풍부한 도서관 등을 활용해 높은 물동량을 견딜 수 있을 것이다.
토너도 사용법 등을 기사에 다시 썼으면 좋겠다.

좋은 웹페이지 즐겨찾기