在我工作的項目裡面使用了mongodb.自然也用到了pymongo.但是它都是在大片的函數裡面使用類似於這樣的方式
| 代碼如下 |
複製代碼 |
import db def test(): ... db.test.find_one() ... |
但是問題是在使用完都沒有關閉串連,這樣多台伺服器串連我這台mongodb伺服器,在業務高峰期就會佔滿我的串連, 我當時總結造成這個原因的問題有以下三種:
上面說的用完db不關閉串連而是等著db逾時
注意上面的import,其實在import檔案的時候資料庫連接就已經產生了,沒有在需要的時候才建立, 佔滿我連線應用程式其實有很多沒有用,浪費了
nginx、uwsgi,celery等應用配置的問題,造成過多的執行個體,其實根本無益
我今天寫的一個封裝pymongo和關閉資料庫連接的裝飾器
| 代碼如下 |
複製代碼 |
#/usr/bin/env python # coding=utf-8 """ 1. 封裝資料庫操作(INSERT,FIND,UPDATE) 2. 函數執行完MONGODB操作後關閉資料庫連接 """ from functools import wraps from pymongo.database import Database try: from pymongo import MongoClient except ImportError: # 好像2.4之前的pymongo都沒有MongoClient,現在官網已經把Connection拋棄了 import warnings warnings.warn("Strongly recommend upgrading to the latest version pymongo version," "Connection is DEPRECATED: Please use mongo_client instead.") from pymongo import Connection as MongoClient class Mongo(object): '''封裝資料庫操作''' def __init__(self, host='localhost', port=27017, database='test', max_pool_size=10, timeout=10): self.host = host self.port = port self.max_pool_size = max_pool_size self.timeout = timeout self.database = database @property def connect(self): # 我這裡是為了使用類似"db.集合.操作"的操作的時候才會產生資料庫連接,其實pymongo已經實現了進程池,也可以把這個db放在__init__裡面, # 比如我把db關掉有其他的資料庫調用串連又會產生,並且不影響使用.我這裡只是想每次執行資料庫產生一個串連用完關掉-自己控制自己的 return MongoClientself.host, self.port, max_pool_size=self.max_pool_size, connectTimeoutMS=60 * 60 * self.timeout) def __getitem__(self, collection): # 為了相容db[集合].操作的用法 return self.__getattr__(collection) def __getattr__(self, collection_or_func): db = self.connect[self.database] if collection_or_func in Database.__dict__: # 當調用的是db的方法就直接返回 return getattr(db, collection_or_func) # 否則委派給Collection return Collection(db, collection_or_func) class Collection(object): def __init__(self, db, collection): self.collection = getattr(db, collection) def __getattr__(self, operation): # 我這個封裝只是為了攔截一部分操作,不符合的就直接raise屬性錯誤 control_type = ['disconnect', 'insert', 'update', 'find', 'find_one'] if operation in control_type: return getattr(self.collection, operation) raise AttributeError(operation) def close_db(dbs=['db']): ''' 關閉mongodb資料庫連接 db : 在執行函數裡面使用的db的名字(大部分是db,也會有s_db) Usage:: >>>s_db = Mongo() >>>@close_db(['s_db']) ...: def test(): ...: print s_db.test.insert({'a': 1, 'b': 2}) ...: ''' def _deco(func): @wraps(func) def _call(*args, **kwargs): result = func(*args, **kwargs) for db in dbs: try: func.func_globals[db].connection.disconnect() except KeyError: pass return result return _call return _deco |
PS: 在我測試的時候發現,使用Mongo()類產生的db,操作完會自動關閉串連了…
怎麼樣給一個很大的檔案每個函數都加上面的這個裝飾器?
項目每個指令碼的代碼都很長,函數也很多,並且每個函數裡面使用的db的名字都不同,比如有一些一些風格:
| 代碼如下 |
複製代碼 |
db.test.find_one() s_db.test.insert(dict(test='test')) ... |
每個函數加一個裝飾器,好費勁,就想能不能自動分辨檔案中的函數然後給他們自動加裝飾器,然後就有以下的一個做好的指令碼:
| 代碼如下 |
複製代碼 |
#coding=utf-8 from functools import wraps import copy import types def wrap(func): @wraps(func) def _call(*args, **kwargs): result = func(*args, **kwargs) print 'wrap you' return result return _call def test(): print 'test' def test2(): print 'test3' glocal_dict = copy.copy(globals()) func_list = [[k, v] for k, v in glocal_dict.iteritems() if not k.startswith('__')] for func_name, func in func_list: if func_name in ['wraps', 'copy', 'wrap', 'types']: continue if types.FunctionType == type(func): globals()[func_name]= wrap(func) |
這樣當你調用的時候自動就有了裝飾器:
| 代碼如下 |
複製代碼 |
>>> from test import test >>> test() test wrap you >>> |