インタフェース時間最適化の実践(コラボレーションでのマルチスレッド)

3952 ワード

最近、新しいニーズがあるため、元のインタフェースの時間が長いため、既存のインタフェースを最適化しました.
質問:単一スレッドでネットワークを複数回呼び出すのに時間がかかります
問題の原因は簡単で,従来のインタフェースはI/Oの処理で同時であり,各インタフェースの呼び出しをコプロセッサで処理する.しかしyield fromの使用により,インタフェース関数内部の関数呼び出しが同期実行となる.
@asyncio.coroutine
def update(self,params):
  all_vins = yield from self.vehicle.get_all_vins()
  for vin_data in all_vins:
      #         
      net_data = yield from self.get_data_by_url()
      #      
      yield from self.insert_db()

これは,時間消費が主にネットワーク呼び出しとデータベースへの書き込みによって生じることを示している.データベースの時間消費はしばらく考えない.今yield fromのため、協程の中のタスクを保留して、このタスクが完了した後に次のタスクを実行することができます.ネットワーク呼び出しの時間消費を0.2 sとすると,現在の消費時間は0.2*len(all_vins)となり,時間がかかる.
解決:マルチスレッドの追加
このような問題に対しては,コヒーレントな特性を利用するだけでは解決できない(しばらくは良い方法が見つからない).また,従来のネットワーク呼び出し関数は,コヒーレントなスレッドプールに投げ込まれ,再利用が不便であった.そこで個人はこのネットワーク呼び出しをスレッドプールから取り出し,asycioタグを追加しない.これでthreadingを使用してマルチスレッドを起動できます.コードは次のとおりです.
@asyncio.coroutine
def update(self,params):
  all_vins = yield from self.vehicle.get_all_vins()
  threads = []
  for vin_data in all_vins:
      #         
      t = threading.Thread(target=self.get_data_by_url,arge=())
      t.start()
      threads.append[t]
      #      
      yield from self.insert_db()
  for i in rang(len(all_vins)):
      threads[i].join()

このようにマルチスレッドを利用してネットワークI/Oを同時操作し,実際の時間消費はこれらのスレッドの中で最も長い時間を消費するスレッド時間である.しかし、プロジェクトのプログラムはそんなに簡単ではありません.ネットワーク呼び出しはjsonデータを取得します.このように、上の書き方がforループの中でこのようにしても、データを書き込むときに時間の消費が増加します.これは私がこのforを2つに分解します.
RESULT = []
@asyncio.coroutine
def update(self,params):
  all_vins = yield from self.vehicle.get_all_vins()
  threads = []
  for vin_data in all_vins:
      #         
      t = threading.Thread(target=self.get_data_by_url,arge=())
      t.start()
      threads.append[t]
    
  for i in rang(len(all_vins)):
      threads[i].join()

  for vin_data in all_vins:
      #      
      yield from self.insert_db()

これで上の理想的な効果を達成します.返品の結果は、グローバル変数RESULTに格納されます.実際のテストでは、時間消費は主にデータのライブラリの書き込みに由来し、ネットワークの時間消費はネットワーク呼び出しの時間に減少した.
善後
このように書くと、特定の時間に大量のスレッドが増加し、このインタフェースのアクセス量が多すぎると、サーバの圧力が短時間で急増します.だから一定の時間を費やして最適化しなければなりません.
RESULT = []
@asyncio.coroutine
def update(self,params):
  all_vins = yield from self.vehicle.get_all_vins()
  threads = []
  for_times = all_vins / 200
  remain_time = all_vins % 200
  if for_times == 0:
      threads = []
      for vin_data in all_vins:
          #         
          t = threading.Thread(target=self.get_data_by_url,arge=())
          t.start()
          threads.append[t]
      
      for i in rang(len(all_vins)):
          threads[i].join()
  else:
      for i in for_times:
          threads = []
          for i in rang(0, 200):
              #         
              t = threading.Thread(target=self.get_data_by_url,arge=())
              t.start()
              threads.append[t]
          for i in rang(0,200)
              threads[i].join()
      threads = []
      for i in rang(0,remain_time ):
             #         
              t = threading.Thread(target=self.get_data_by_url,arge=())
              t.start()
              threads.append[t]
      for i in rang(0,remain_time ):
             threads[i].join()

  for vin_data in all_vins:
      #      
      yield from self.insert_db()

スレッド数を最大200に設定すると、過大なスレッド数によるサーバのクラッシュを回避できます.
補足
きょうてい
通常、関数の実行時には単一の入力パラメータのセットが使用されますが、関数は、送信された一連の入力を処理するためのタスクプログラムとして記述することもできます.このような関数をコヒーレントと呼ぶ.(『pythonリファレンスマニュアル』--David M.Beazleyより抜粋)コパスは@asynico.Coroutineは、非同期I/O動作を生成します.これにより、高同時インタフェース呼び出しが処理されます.
マルチスレッド
pythonのマルチスレッドここではthreadingモジュールを処理して実現します.もちろんthread、queueモジュールもあります.もちろんthreadingモジュールはスレッドの処理に対して比較的強い制御を持っている.重要なサブスレッドがプロセスが終了する前に終了することを保証します.上記の例では、関数全体がコヒーレントであるため、threadingはこのスレッドの下で複数のサブスレッドを作成することに相当する.私が必要とする結果は、すべてのデータを取得してから書き込むことであり、すべての戻り結果は不規則な配列に配置され、データの出力は前後を区別する必要はなく、データの混乱の問題を心配する必要はありません.
まとめ
実はこの最適化は簡単ですが、最適化のついでに協程とマルチスレッドを復習します.