前提
DjangoでWEB APIつくって業務改善と称して社内においてまして、一部にmechanizeを使ってイントラログインとイントラの操作をAPI化して楽をしてました。
問題
WEB APIなので基本的にアクセスはステートレスでして、都度mechanizeのインスタンスを作成してイントラ操作をしています。そのため、イントラから見るとAPIへの1リクエストごとに新規ログイン状態でした。近い将来情シスに怒られるので改善したくなります。
対処
mechanizeのインスタンスを毎回作成するのではなく、プーリングすることにしました。
worker = IntraWorker(USER,PASS)
worker.do_something()
IntraWokerはmechanizeを使ったクラスです。__init__
で毎回ログインしています。これを以下にしました。
# get from pool
worker = IntraWorkerPool().get(USER,PASS)
worker.do_something()
# back to pool
worker.close()
IntraWorkerPoolというクラスを追加して、これにプールを管理させます。
from threading import Lock
class IntraWorkerPool(object):
"""
Mechanizeのプーリング管理
"""
# poolされているWorker
# user,passのタプルをキーとする
pooled_workers = {}
def __init__(self):
self.lock = Lock()
def get(self, user, password):
"""
Workerインスタンスをプールから取り出す
"""
ret_aw = None
pool = IntraWorkerPool.pooled_workers
key = (hashed(user), password)
with self.lock:
if key in pool:
# 使用可能なのがなければ作る
if len(pool[key]) == 0:
pool[key].append(IntraWorker(*key, pool_control=self))
else:
pool[key] = []
pool[key].append(IntraWorker(*key, pool_control=self)
# Workerがまだログイン出来る状態かチェック
while (ret_aw is None) or (not ret_aw.is_login()):
ret_aw = pool[key].pop()
return ret_aw
def back_to_pool(self, aw_inst):
"""
poolにインスタンスを戻す
実際はこれを直接コールしない。Worker側のcloseで呼ばせる
"""
key = (hashed(aw_inst.user_no), aw_inst.user_password)
pool = IntraWorkerPool.pooled_workers
with self.lock:
pool.setdefault(key, [])
pool[key].append(aw_inst)
やってることは、クラス変数としてpooled_workersというディクショナリを用意してユーザ名とパスワードのタプルとWorkerインスタンスのリストの管理です。また、リスト操作はスレッドセーフにしないといけないはずなのでthreading.Lock()をつかってシリアライズしています。プールへの返却はback_to_pool
というメソッドを用意し、こちらもLockで保護していますが、実際にはWorkerインスタンス側のCloseメソッド実行時にback_to_poolを実行されるようにしています。
正直、Pool管理クラスと管理対象クラスが相互依存チックなのが気に食わないのですがいまいちスマートな実装が思いつかずこんな形でお茶を濁しています。
もっとスマートなのがあればどなたか教えて頂けますと幸いです。