How to implement network connection pool in python

In web software or service software, program is running in a concurrency situation as multiple thread or process, if create a network connection every time when handle a request, it waste some of performances to establish or close connect. If always use a single connection, we have to face the connection competition situation. So maintain a connection pool is a regular and classic way to solve this problem. I have a web service written in Python with pyramid, it use thrift’s void mode function to report the every request’s statistic and error information to a thrift server, since thrift official client has no client pool support, in order to purchase the extreme performance improvement, I implement a client pool for thrift client.

Basically we create a Pool to maintain all client objects, and create a Client object hold the real thrift client, when this Client object’s reference count is 0, which means it was destroy by python interpreter, recycle the real thrift client to Pool object. It’s really simple and I think I should just show the source code

import threading

from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol

# it's a network error handler decorator, you can find it here:
# https://github.com/vincentwyshan/ZooBus/blob/master/zoobus/thrift/thrifthelper.py
from .helper import client_ensure


class Pool(object):
    def __init__(self, host, port, client_cls, pool_size=30):
        self.pool_size = pool_size
        self.lock = threading.RLock()
        self.host = host
        self.port = port
        self.client_cls = client_cls

        self.pool = []

    def connect(self):
        return Client(self)

    def new_client(self):
        transport = TSocket.TSocket(self.host, self.port)
        # Time out 2 seconds
        transport.setTimeout(1000*2)
        transport = TTransport.TBufferedTransport(transport)

        # Wrap in a protocol
        protocol = TBinaryProtocol.TBinaryProtocol(transport)

        # Create a client to use the protocol encoder
        client = self.client_cls(protocol)

        transport.open()

        return client

    def check_in(self, client):
        # print "check in:", id(client), len(self.pool)
        self.lock.acquire()
        try:
            while len(self.pool) >= self.pool_size:
                self.pool.pop(0)
            self.pool.append(client)
        finally:
            self.lock.release()

    def check_out(self):
        self.lock.acquire()
        try:
            client = None
            if len(self.pool) > 0:
                client = self.pool.pop(0)
            if not client:
                client = self.new_client()
            # print time.time(), "check out:", id(client)
            return client
        finally:
            self.lock.release()


class Client(object):
    def __init__(self, pool):
        self.client = pool.check_out()
        self.pool = pool

        for method in dir(self.client):
            if(not method.startswith('_') and not method.startswith('recv_')
               and not method.startswith('send_')):
                setattr(self, method,
                        client_ensure(getattr(self.client, method)))

    def _new_client(self):
        self.client = self.pool.new_client()

    def close(self):
        self.pool.check_in(self.client)
        self.pool = None

    def __del__(self):
        if hasattr(self, 'pool') and self.pool:
            self.pool.check_in(self.client)

Using this logic, you can implement other network connection pool.

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.