我正在使用以下设置在 lighttpd 后面开发一个 CherryPy FastCGI 服务器,以启用在 CherryPy 控制器中使用 ORM SQLAlchemy 会话。但是,当我对大约 500 个循环的 14 个并发请求运行压力测试时,它开始出现类似AttributeError: '_ThreadData' object has no attribute 'scoped_session_class'
inopen_dbsession()
或AttributeError: 'Request' object has no attribute 'scoped_session_class'
inclose_dbsession()
一段时间后的错误。错误率总共约为 50%。
仅当我在 lighttpd 后面运行服务器时才会发生这种情况,而不是直接通过cherrypy.engine.start()
. 已确认connect()
不会引发异常。
scoped_session
我还尝试分配to的返回值GlobalSession
(就像它在此处所做的那样),但随后它给出了诸如UnboundExceptionError
和其他 SA 级错误之类的错误。(并发:10,循环:1000,错误率:16%。即使直接运行也会发生。)
有一些可能的原因,但我缺乏足够的知识来选择一个。
1. start_thread
FastCGI环境下订阅不可靠吗?似乎open_dbsession()
是在 2 之前调用的connect()
。是否cherrypy.thread_data
由于某种原因被清除?
服务器代码
import sqlalchemy as sa
from sqlalchemy.orm import session_maker, scoped_session
engine = sa.create_engine(dburi, strategy="threadlocal")
GlobalSession = session_maker(bind=engine, transactional=False)
def connect(thread_index):
cherrypy.thread_data.scoped_session_class = scoped_session(GlobalSession)
def open_dbsession():
cherrypy.request.scoped_session_class = cherrypy.thread_data.scoped_session_class
def close_dbsession():
cherrypy.request.scoped_session_class.remove()
cherrypy.tools.dbsession_open = cherrypy.Tool('on_start_resource', open_dbsession)
cherrypy.tools.dbsession_close = cherrypy.Tool('on_end_resource', close_dbsession)
cherrypy.engine.subscribe('start_thread', connect)
lighttpd fastcgi 配置
...
var.server_name = "test"
var.server_root = "/path/to/root"
var.svc_env = "test"
fastcgi.server = (
"/" => (
"cherry.fcgi" => (
"bin-path" => server_root + "/fcgi_" + server_name + ".fcgi",
"bin-environment" => (
"SVC_ENV" => svc_env
),
"bin-copy-environment" => ("PATH", "LC_CTYPE"),
"socket" => "/tmp/cherry_" + server_name + "." + svc_env + ".sock",
"check-local" => "disable",
"disable-time" => 1,
"min-procs" => 1,
"max-procs" => 4,
),
),
)
编辑
- 从原始源代码中恢复了代码示例中缺少的
thread_index
参数(感谢注释) - 澄清错误不会立即发生
- 将条件缩小到 lighttpd