(2018-05-30.Python ZeroからOne)8、(Tornado)非同期とWebSockets_1.7.2 Tornado非同期

6849 ワード

7.2 Tornado非同期


epollは主にネットワークIOの同時問題を解決するために用いられるため,Tornadoの非同期プログラミングも主にネットワークIOの非同期,すなわち非同期Web要求に現れる.

1. tornado.httpclient.AsyncHTTPClient


Tornadoは非同期Web要求クライアントtornadoを提供する.httpclient.AsyncHTTPClientは非同期Webリクエストに使用されます.

fetch(request, callback=None)


Webリクエストrequestを実行し、tornadoを非同期で返す.httpclient.HTTPResponse応答.
requestはurlでもtornadoでもよい.httpclient.HTTPRequestオブジェクト.urlの場合、fetchは自分でHTTPRequestオブジェクトを構築します.

HTTPRequest


HTTPリクエストクラス、HTTPRequestのコンストラクション関数は多くのコンストラクションパラメータを受信することができ、最もよく使われるのは以下の通りである.
  • url(string)-アクセスするurl.このパラメータは必ず送信されます.それ以外はオプションのパラメータ
  • です.
  • method(string)-HTTPアクセス方式、例えば「GET」または「POST」、デフォルトはGET方式
  • headers(HTTPHeaders or dict)-追加のHTTPプロトコルヘッダ
  • body–HTTPリクエストのリクエスト体
  • HTTPResponse


    HTTP応答クラス、その共通属性は以下の通りである.
  • code:HTTPステータスコード、例えば200または404
  • reason:ステータスコード記述情報
  • body:応答体文字列
  • error:異常(有無可)
  • 2.テストインタフェース


    新浪IPアドレスライブラリ
    インタフェースの説明
    1.要求インタフェース(GET):
    [http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=ipアドレス文字列
    2.応答情報:
    (json形式の)国、省(自治区又は直轄市)、市(県)、運営者
    3.データのフォーマットを返します.
    {"ret":1,"start":-1,"end":-1,"country":"\u4e2d\u56fd","province":"\u5317\u4eac","city":"\u5317\u4eac","district":"","isp":"","type":"","desc":""}
    
    

    3.コールバック非同期

    class IndexHandler(tornado.web.RequestHandler):
        @tornado.web.asynchronous  #  , 
        def get(self):
            http = tornado.httpclient.AsyncHTTPClient()
            http.fetch("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=14.130.112.24",
                       callback=self.on_response)
    
        def on_response(self, response):
            if response.error:
                self.send_error(500)
            else:
                data = json.loads(response.body)
                if 1 == data["ret"]:
                    self.write(u" :%s  : %s  : %s" % (data["country"], data["province"], data["city"]))
                else:
                    self.write(" IP ")
            self.finish() #  , 
    
    

    tornado.web.asynchronous


    このアクセラレータはコールバック形式の非同期メソッドに使用され、HTTPのメソッド(get、postなど)にのみ使用されるべきである.
    この装飾器は、装飾された方法を非同期にするのではなく、フレームが装飾された方法が非同期であることを示すだけで、方法が戻ったときに応答が完了していない.今回のリクエスト処理は、request handlerがfinishメソッドを呼び出した後にのみ終了し、応答を送信します.
    この装飾器を持たないリクエストはget,postなどのメソッドが戻ったときに自動的に終了リクエスト処理を完了する.

    4.コヒーレント非同期


    前節では自分たちで封入したアクセサリーget_Tornadoに対応するのはtornadoですgen.coroutine.
    class IndexHandler(tornado.web.RequestHandler):
        @tornado.gen.coroutine
        def get(self):
            http = tornado.httpclient.AsyncHTTPClient()
            response = yield http.fetch("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=14.130.112.24")
            if response.error:
                self.send_error(500)
            else:
                data = json.loads(response.body)
                if 1 == data["ret"]:
                    self.write(u" :%s  : %s  : %s" % (data["country"], data["province"], data["city"]))
                else:
                    self.write(" IP ")
    
    

    非同期Webリクエストを個別に表示することもできます.
    class IndexHandler(tornado.web.RequestHandler):
        @tornado.gen.coroutine
        def get(self):
            rep = yield self.get_ip_info("14.130.112.24")
            if 1 == rep["ret"]:
                self.write(u" :%s  : %s  : %s" % (rep["country"], rep["province"], rep["city"]))
            else:
                self.write(" IP ")
    
        @tornado.gen.coroutine
        def get_ip_info(self, ip):
            http = tornado.httpclient.AsyncHTTPClient()
            response = yield http.fetch("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=" + ip)
            if response.error:
                rep = {"ret:0"}
            else:
                rep = json.loads(response.body)
            raise tornado.gen.Return(rep)  #  
    
    

    コードで注意しなければならない点はgetです.ip_infoが値を返す方式では、python 2でyieldを用いたジェネレータは、値を返さないreturnを用いることができるが、return valueを用いることができないため、Tornadoはジェネレータに値を返すための特殊な異常tornadoをカプセル化する.gen.Return、raiseを使用してこの戻り値を返します.

    へいれつきょうてい


    Tornadoは複数の非同期を同時に実行できます.同時非同期はリストまたは辞書を使用できます.以下のようにします.
    class IndexHandler(tornado.web.RequestHandler):
        @tornado.gen.coroutine
        def get(self):
            ips = ["14.130.112.24",
                "15.130.112.24",
                "16.130.112.24",
                "17.130.112.24"]
            rep1, rep2 = yield [self.get_ip_info(ips[0]), self.get_ip_info(ips[1])]
            rep34_dict = yield dict(rep3=self.get_ip_info(ips[2]), rep4=self.get_ip_info(ips[3]))
            self.write_response(ips[0], rep1) 
            self.write_response(ips[1], rep2) 
            self.write_response(ips[2], rep34_dict['rep3']) 
            self.write_response(ips[3], rep34_dict['rep4']) 
    
        def write_response(self, ip, response):
            self.write(ip) 
            self.write(":
    ") if 1 == response["ret"]: self.write(u" :%s : %s : %s
    " % (response["country"], response["province"], response["city"])) else: self.write(" IP
    ") @tornado.gen.coroutine def get_ip_info(self, ip): http = tornado.httpclient.AsyncHTTPClient() response = yield http.fetch("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=" + ip) if response.error: rep = {"ret:1"} else: rep = json.loads(response.body) raise tornado.gen.Return(rep)

    5.データベースに関する非同期の説明


    サイトには基本的にデータベース操作がありますが、Tornadoは単一スレッドです.これは、データベースクエリの戻りが遅すぎると、サーバ全体の応答が詰まってしまうことを意味します.
    データベースクエリーは、実質的にリモートのネットワーク呼び出しでもあります.理想的には、これらの操作も非同期にカプセル化される.しかし、Tornadoはこれに対してサポートを提供していません.
    これは欠陥ではなくTornadoの設計です.
    1つのシステムは、高流量を満たす必要があります.データベース・クエリーの速度の問題を解決する必要があります.
    データベースにクエリーのパフォーマンスの問題がある場合、システム全体がどのように最適化されても、データベースはボトルネックになり、システム全体を遅らせます.
    非同期は本質的にシステムの性能に言及することはできない.余分なネットワーク応答待ちを回避し、スレッドを切り替えるCPUに消費されるだけです.
    データベース・クエリーの応答が遅すぎる場合は、データベースのパフォーマンスの問題を解決する必要があります.データベースのフロントエンドWebアプリケーションを呼び出すのではなく、
    リアルタイムで返されるデータクエリーでは、理想的にはすべてのデータがメモリに含まれていることを確認する必要があります.データベースハードディスクIOは0である必要があります.このようなクエリーは十分に速くできます.データベース・クエリーが十分に速い場合、フロントエンドのWebアプリケーションは、データ・クエリーを非同期にカプセル化する必要はありません.
    コラボレーションを使用しても、非同期プログラムは同期プログラムに対して常に複雑さを向上させる.これらの追加の複雑さを処理する価値があるかどうかを測定する必要があります.
    バックエンドにクエリがあるのが遅すぎて迂回できない場合は、Tornaodの推奨は、これらのクエリをバックエンドパッケージで独立してHTTPインタフェースにカプセル化し、Tornadoに内蔵された非同期HTTPクライアントを使用して呼び出すことです.