Python 3.x 博士后、sqlalchemy和多重处理

Python 3.x 博士后、sqlalchemy和多重处理,python-3.x,postgresql,sqlalchemy,multiprocessing,Python 3.x,Postgresql,Sqlalchemy,Multiprocessing,我对python多处理比较陌生,并且正在努力解决与此主题相关的许多问题。我的最新问题是来自多处理、sqlalchemy和postgres的组合。有了这个组合,我有时会得到一个 sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) SSL error: decryption failed or bad record mac 经过研究,我在文档中发现了以下提示: “这很关键 当使用连接池时,扩展到使用 通过create_Engi

我对python多处理比较陌生,并且正在努力解决与此主题相关的许多问题。我的最新问题是来自多处理、sqlalchemy和postgres的组合。有了这个组合,我有时会得到一个

 sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) SSL error: decryption failed or bad record mac
经过研究,我在文档中发现了以下提示:

“这很关键 当使用连接池时,扩展到使用 通过create_Engine()创建的引擎,池连接 未共享到分叉进程。TCP连接表示为 文件描述符,通常跨流程边界工作, 这意味着这将导致对上的文件描述符的并发访问 代表两个或多个完全独立的Python解释器状态

有两种方法可以解决这个问题

第一个是,在子进程中创建一个新引擎,或者 在现有引擎上,在子引擎之前调用Engine.dispose() 进程使用任何连接。这将删除所有现有连接 池中的连接,以便它创建所有新的连接。”

这是:

“问题最终是uwsgi的分叉

当使用主进程处理多个进程时,uwsgi 在主进程中初始化应用程序,然后复制 应用程序转移到每个辅助进程。问题是如果打开 数据库连接在初始化应用程序时 多个进程共享同一连接,这会导致错误 上面。”

我的解释是,当使用多处理时,我必须确保每个进程都使用一个新引擎。 在我的子进程中,只有一个类可以读写postgres db,因此我决定在该类中定义一个slqalchemy引擎:

class WS_DB_Booker():
    def __init__(self):

        engine_inside_class = create_engine(botpak.bas.dontgitp.bot_engine_string)
        Base_inside_class = declarative_base()
        Base_inside_class.metadata.create_all(engine_inside_class)
        session_factory_inside_class = sessionmaker(bind=engine_inside_class)
        self.DBSession_inside_class = scoped_session(session_factory_inside_class)

    def example_method_to_read_from_db(self):
        try:
            sql_alc_session = self.DBSession_inside_class()
            sql_alc_session.query(and_so_on....

这在第一次试验中效果良好,没有任何问题。但我不确定这是在类中定义引擎的正确方法,还是会导致任何问题?

如何分叉进程或哪个组件进行分叉实际上是不可理解的

您需要确保的是在分叉之后实例化
WS\u DB\u Broker

如果使用错误的方法(在fork之前实例化),则
引擎可能已经在其
池中引用了一些
dbapi
连接。请参阅SQLAlchemy

为了使您的错误更加明显,您可以执行以下操作:


我在分叉之后实例化这个类。我的问题更关心的是在类中定义引擎。如果您可以确保在程序的整个生命周期中只实例化一次类,那么就可以了。否则,你必须以某种方式确保你做到了。
import os

class WS_DB_Booker():
    def __init__(self):
        # Remember the process id from the time of instantiation. If the
        # interpreter is forked then the output of `os.getpid()` will change.
        self._pid = os.getpid()

        engine_inside_class = create_engine(botpak.bas.dontgitp.bot_engine_string)
        Base_inside_class = declarative_base()
        Base_inside_class.metadata.create_all(engine_inside_class)
        session_factory_inside_class = sessionmaker(bind=engine_inside_class)
        self._session = scoped_session(session_factory_inside_class)

    def get_session():
        if self._pid != os.getpid():
             raise RuntimeError("Forked after instantiating! Please fix!")
        return self._session()

    def example_method_to_read_from_db(self):
        try:
            sql_alc_session = self.get_session()  
            #                      ^^^^^^^^^^^^^
            # this may throw RuntimeError when used incorrectly, thus saving you
            # from your own mistake.
            sql_alc_session.query(and_so_on....