はじめに
Pythonのgeventライブラリを「入門Python3」で知ったきっかけに,公式ドキュメントなどを参考にしながら,学んだことを備忘録のようにまとめる.
geventライブラリについて
gevent
ライブラリはイベント駆動で,通常の命令型のコードを書くと,gevent
が手品のように部品をコルーチン(注1)に変換する.
互いに通信して相手がどこにいるのかをつねに把握できるジェネレータのよう動く.
gevent
はsocketなどのpythonの標準オブジェクトの多くを書き換え,ブロックせずにgevent
のメカニズムを使うようにする.
(注1.)コルーチンとは処理を中断や再開する仕組みのこと
特徴として通常の関数のとは異なり、
処理を途中で抜けて任意のタイミングで中断部分から処理を再開する.
イベント駆動プログラミングについて
以下の資料がわかりやすく参考になった.
イベント駆動プログラミングとI/O多重化
https://www.slideshare.net/mizzy/io-18459625
Apache Kafka on Herokuを活用したイベント駆動アーキテクチャの設計と実装
https://www.slideshare.net/DeveloperForceJapan/apache-kafka-on-heroku
What is gevent?
geventはコルーチンベースのpythonネットワーキングライブラリ.
本稿では,geventの以下の機能について,サンプルコードを確かめながら理解を深める.
- グリーンレットに基づく軽量の実行ユニット
- サードパーティのモジュールに対してモンキーパッチを適応
サンプルコード
グリーンレットの機能
import gevent
from gevent import socket
if __name__ == "__main__":
hosts = ['www.google.com','www.yahoo.co.jp','www.qiita.com']
jobs = [gevent.spawn(gevent.socket.gethostbyname,host) for host in hosts]
gevent.joinall(jobs,timeout=5)
# ホスト名のIPアドレスを出力
for job in jobs:
print(job.value)
$ python gevent_test.py
216.58.197.196
182.22.25.252
52.196.217.245
説明
socket
モジュールのgethostbyname()
関数は,ドメイン名に対応するIPアドレスを返す.同期的なので,世界中のネームサーバーを捕まえて.そのアドレスを解決しようと競い合うときに待ちに入る.しかし,geventバージョンを使用すれば,非同期的に実行し複数のサイトを独立で解決できる.
gevent.spawn()
は個々のgevent.socket.gethostbyname(url)を実行するために,**グリーンレット(グリーンスレッド,マイクロスレッド)**を作る.
通常のスレッドとの違いはグリーンレットならブロックしないこと.ブロック処理とは他のスレッドがその資源にアクセスできないようする処理で,排他処理や排他制御ともいう.通常のスレッドをブロックしてしまうようなことが起きても,geventはほかのグリーンレットのどれかを制御を切り替える.
gevent.joinall()
メソッドは派生させたすべてのジョブが終了するのを待つ.
geventのモンキーパッチ機能
geventバージョンのsocketではなく,モンキーパッチング関数を使うことができる.
これらはgevetnバージョンのモジュールを呼び出すのでなく,socket
などの標準モジュールがグリーンレットを使うように書き換える.
Pythonコードには機能するが,Cで書かれたライブラリには適用しない.
モンキーパッチングについて
- 元のコードを変更することなく、動的にコードを拡張/変更する事の総称のこと.(メタプログラミングの一種)
- ライブラリのコードを直接変えたくない時とかに利用される
- テスト系ライブラリでよく利用される
import gevent
from gevent import monkey;monkey.patch_all()
import socket
import time
if __name__ == "__main__":
hosts = ['www.google.com','www.yahoo.co.jp','www.qiita.com']
#gevent.spawn()の引数にはsocket.gethostbynameを指定しているが,ここがモンキーパッチング対象
jobs = [gevent.spawn(socket.gethostbyname,host) for host in hosts]
gevent.joinall(jobs,timeout=5)
gevent.spawn()
for job in jobs:
print(job.value)
説明
genvent_test.py
では,gevent.socket
モジュールを明示的にインポートしていた.
一方,gevent_monkey.py
では,標準ライブラリであるsocket
モジュールを明示的インポートした.monkey.patch_all()
を使用しない場合,import socket
の段階では,gevent版のsocketモジュールではなく,標準ライブラリとしてのsocketモジュールである.
モンキーパッチング関数(サンプルコードではmonkey.patch_all()
)を使用することで,標準ライブラリをgevent版(gevent.socket)に書き換えることができる.
#所感
ここに記述したことは全然実践的でないし,もっと調べていきたい.
知らべてわかったことを追加していく予定.
参考文献
- 入門Python3
- http://www.gevent.org/