如何在Pylons应用中连接Cassandra?
我创建了一个新的Pylons项目,想用Cassandra作为我的数据库服务器。我打算使用Pycassa来支持cassandra 0.7beta。可惜的是,我不知道在哪里建立连接,以便在我的应用程序中使用。
我的目标是:
- 在应用程序启动时创建一个连接池
- 每次请求时从连接池中获取一个连接,并让它在我的控制器和库中可用(在请求的上下文中)。最好是“懒惰地”获取连接,也就是说,只有在需要的时候才获取
- 如果一个连接被使用过,在请求处理完后释放它
另外,有什么重要的事情我需要知道吗?当我看到一些评论说“使用QueuePool时要小心,特别是当use_threadlocal=True并且启用了重试时。可能需要同步,以防止在另一个线程使用时连接发生变化。”这到底是什么意思呢?
谢谢。
--
皮埃尔
2 个回答
好的。
我工作了一点,学到了很多,也找到了一个可能的解决办法。
创建连接池
创建连接池的最佳位置似乎是在 app_globals.py 文件中,这个文件基本上是一个容器,可以在“应用程序的整个生命周期”中访问的对象。实际上,这正是我想要的连接池。
我在文件末尾添加了我的初始化代码,这段代码从 pylons 配置文件中获取设置:
"""Creating an instance of the Pycassa Pool"""
kwargs = {}
# Parsing servers
if 'cassandra.servers' in config['app_conf']:
servers = config['app_conf']['cassandra.servers'].split(',')
if len(servers):
kwargs['server_list'] = servers
# Parsing timeout
if 'cassandra.timeout' in config['app_conf']:
try:
kwargs['timeout'] = float(config['app_conf']['cassandra.timeout'])
except:
pass
# Finally creating the pool
self.cass_pool = pycassa.QueuePool(keyspace='Keyspace1', **kwargs)
我本可以做得更好,比如把它放到一个函数里,或者支持更多参数(比如连接池大小等等)。我会这么做的。
在每次请求时获取连接
好吧。似乎有一个简单的方法:在 base.py
文件中,添加类似 c.conn = g.cass_pool.get()
的代码,在调用 WSGIController
之前,之后再加上 c.conn.return_to_pool()
。这个方法简单且有效。但这样即使控制器不需要,也会从连接池中获取连接。我需要深入研究一下。
创建连接管理器
我有个简单的想法,就是创建一个类,在每次请求时在 base.py
文件中实例化,这个类会在需要时自动从连接池中获取连接(并在之后释放它)。这是一个非常简单的类:
class LocalManager:
'''Requests a connection from a Pycassa Pool when needed, and releases it at the end of the object's life'''
def __init__(self, pool):
'''Class constructor'''
assert isinstance(pool, Pool)
self._pool = pool
self._conn = None
def get(self):
'''Grabs a connection from the pool if not already done, and returns it'''
if self._conn is None:
self._conn = self._pool.get()
return self._conn
def __getattr__(self, key):
'''It's cooler to write "c.conn" than "c.get()" in the code, isn't it?'''
if key == 'conn':
return self.get()
else:
return self.__dict__[key]
def __del__(self):
'''Releases the connection, if needed'''
if not self._conn is None:
self._conn.return_to_pool()
我只需在 base.py
中调用 WSGIController 之前添加 c.cass = CassandraLocalManager(g.cass_pool)
,之后再加上 del(c.cass)
,就完成了。
而且它有效:
conn = c.cass.conn
cf = pycassa.ColumnFamily(conn, 'TestCF')
print cf.get('foo')
\o/
我不知道这是否是最好的方法。如果不是,请告诉我 =)
另外,我仍然不太明白 Pycassa 源代码中的“同步”部分。如果在我的情况下需要这个,我该怎么做才能避免问题。
谢谢。
好吧,我又多做了一些工作。其实,使用连接管理器可能不是个好主意,因为这应该是模板的上下文。此外,为每个线程打开一个连接其实没什么大不了的。每个请求打开一个连接才会有问题。
最后,我在应用的全局变量里用了pycassa.connect_thread_local()
,就这样搞定了。