Help us understand the problem. What is going on with this article?

Djangoでmechanizeをプーリング

More than 5 years have passed since last update.

前提

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というクラスを追加して、これにプールを管理させます。

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管理クラスと管理対象クラスが相互依存チックなのが気に食わないのですがいまいちスマートな実装が思いつかずこんな形でお茶を濁しています。

もっとスマートなのがあればどなたか教えて頂けますと幸いです。

denzow
OracleDatabase/PostgreSQLのサポートをしていましたが、現在はPythonのWEBエンジニアをしています。DjangoとかScrapyを触っています
http://www.denzow.me/
scouty
「あらゆる事象を必然化し、 世の中のミスマッチをなくす」ことをミッションとし、AIヘッドハンティングサービスを開発・運営するスタートアップ
https://lapras.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away