シングルトンの実装をしたrequests.Session
クラスを使うことで、毎回同じ識別子のクラスを使い回すことが出来ます。
そうすることで、リクエストを飛ばすたびに毎回ホストとセッションをつなぐ必要がなくなるので、めちゃ便利です。
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class RequestSession():
_has_instance = None
def __new__(cls):
if not cls._has_instance:
cls._has_instance = super(RequestSession, cls).__new__(cls)
cls.session = cls.create_session()
return cls._has_instance
@staticmethod
def create_session():
session = requests.Session()
retries = Retry(total=1)
session.mount("http://", HTTPAdapter(max_retries=retries))
return session
では次にこれがちゃんとシングルトンになっているのかを確認します。
>>> rs = RequestSession()
>>> id(rs)
140271749279264
>>> rs2 = RequestSession()
>>> id(rs2)
140271749279264
識別子の値が一緒なので、同じインスタンスだとわかります。
ネットワークを確認する
ではこれを使用して、ネットワークを確認します。
しかし、Mac上にss
コマンドやwatch
コマンドがないので、Dockerコンテナ上で動かします。
FROM python:3
RUN apt-get -y install iproute2 watch
RUN pip install --upgrade pip
RUN pip install requests
次に、下記コマンドを叩きます。これで TCP の情報をリアルタイムで確認することが出来ます。
docker container run -it <IMAGE ID> bash
watch ss -aot
別窓でインタラクティブモードでpython
を起動します。
docker ps
docker exec -it <CONTAINER ID> bash
python
TCP の状態を確認するために、まずは通常の get を使います
import requests
requests.get("https://google.com")
requests.get("https://google.com")
requests.get("https://yahoo.co.jp")
別窓のssコマンドにはこのように表示されたかと思います。
State Recv-Q Send-Q Local Address:Port Peer Address:Port
TIME-WAIT 0 0 172.17.0.2:..... 172.217.25.196:https
timer:(timewait,49sec,0)
TIME-WAIT 0 0 172.17.0.2:..... 216.58.220.142:https
timer:(timewait,49sec,0)
TIME-WAIT 0 0 172.17.0.2:..... 216.58.220.142:https
timer:(timewait,50sec,0)
TIME-WAIT 0 0 172.17.0.2:..... 216.58.220.142:https
timer:(timewait,50sec,0)
TIME-WAIT 0 0 172.17.0.2:..... 172.217.25.196:https
timer:(timewait,50sec,0)
TIME-WAIT 0 0 172.17.0.2:..... 216.58.220.142:https
timer:(timewait,49sec,0)
TIME-WAIT 0 0 172.17.0.2:..... 172.217.25.196:https
timer:(timewait,49sec,0)
TIME-WAIT 0 0 172.17.0.2:..... 172.217.25.196:https
timer:(timewait,50sec,0)
次はシングルトンで書いたほうでgetしてみます。
rs = RequestSession()
rs.session.get("https://google.com")
rs.session.get("https://google.com")
rs.session.get("https://yahoo.co.jp")
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 172.17.0.2:..... 172.217.25.196:https
ESTAB 0 0 172.17.0.2:..... 216.58.220.142:https
requests.get()
とsession.get()
で、TCPのコネクション数がかなり違うかと思います。
シングルトンで実装することで何が嬉しいのか ()
ローカルから外部サーバーに通信するとき、ルーターのNAPT機能を使用しています。
ただ、リクエストをしすぎてポート番号が枯渇してしまった場合、色々と問題が起きてしまいます。
そのため、ホストとのセッションを維持し続けて通信することで、上記問題は多少増しになるよね!といった感じです。
(上記で説明したように、requests.get()などは毎回セッション繋ぎに行ってます)
このあたりをより詳しく知りたい方はKeep Alive
で調べると面白いと思います。
おまけ
2020年も最後ですね。最後になにかやっときたいな〜〜〜〜けどめんどくさいな〜〜〜と思ってたら社内の人からこれネタにできるやんと言われたので書いた感じです😇
TwitterのTLは今年買ってよかったものとか2020年の振り返りばっかで溢れてますが、逆張り陰キャなのでそういうのは書きません()
今年も私のブログをご覧いただきありがとうございました。来年もよろしくお願いします🥲 みなさん良いお年を!