Py学习  »  Django

Django ORM 数据库生命周期

hezhiming • 6 年前 • 474 次点击  

起因

有时在线上会遇到“MySQL Server gone away”的错误,但是是以一定概率出现的,今天又遇上了,不得不逼迫自己彻底去解决这个事情。老实说,一般遇到这种看不懂的错误,心里还是挺懵的。一来这种错误一般暴露自己底层知识理解不够透彻;二来是框架毕竟封装了太多,让人看得见森林却看不见树木,比如 Django 这种封装很完善的框架。要命的是第一种,基础知识不是一下子就能补回来的,又不是调 API,「读几遍文档就能撸起袖子 work」。

一开始,我既不知道从何下手,也不知道如何组织关键词,只得用上老套路,把错误信息粘贴到搜索引擎里,一遍一遍地搜索,同时不断微调关键词。

在阅读了一些资料后,大概明白了一些原理,以及顺利地提出了自己满意的解法。

Django 如何做的

这个问题归根到底是「如何管理数据库连接的生命周期」的问题。

一个数据库连接,一头系着 client (即 App,这里我们简化为 Django ),一头系着 server (MySQL Server),其状态是由这两者加上其他因素(姑且先排除)决定的。client 这端自然好控制,自始至终都在我们的控制范围之内,然而 server 那端就不好控制了,加上 my.cnf 各种奇奇怪怪的配置,皆可影响 server 的行为,所以 server 在我们的控制范围之外。

在 Web App 中,数据库连接的生命周期一般和 HTTP Request 的生命周期一致,即:在 Request 开始时,初始化好连接(一般从 Connection Pool 中取一个);在 Request 结束后,将连接释放( 一般是还回到 Pool 中 )。但这是比较理想的情况,「连接到底是不是可用,到底是不是好的」这个问题没有解决。

Django 的做法是:利用其信号机制(Django 中的一种解耦手段),在 Request 开始和结束的时候,将不可用的连接清理掉,从而确保整个 Request 周期内,所取到的连接都是没问题的。至于如何判断一个连接的可用与不可用,方法是多种多样的,比如 MySQL,可以 ping 下 server,看连接是否正常。

Diango的处理代码如下:

django\db\__init__.py

# 其实是一个 callback
def close_old_connections(**kwargs):
    for conn in connections.all():
        conn.close_if_unusable_or_obsolete()


# 信号注册
# 可以看到, 主要核心逻辑在于 close_old_connections
signals.request_started.connect(close_old_connections)
signals.request_finished.connect(close_old_connections)

针对我的需求,我的做法

我们的项目重度使用 Django,简化的逻辑如下

Web Server( 对外提供 REST API )                     Thrift Server( 对内部提供RPC API )
 ............................
           ORM Model
           DB

其中,Web Server 这块自然不用多说,各种需要操心的细节,Django 都替我们管理好了。主要是 Thrift Server 这里,它也是直接使用 Django 的 ORM Model,所以也需要实现一套「连接管理的信号注册」。

根据前面的说明,我们只需要实现 Thrift Request 开始和结束时的信号注册即可,即:如法炮制,调用 close_old_connections 。但我们有一个针对 Thrift handler 的装饰器,我发现只需要在那里面调用 close_old_connections 就可以了。

大致逻辑如下

def decorator(method):
    def _wrapper(self, *args):
        try:
            # 开始时,清理无效连接
            close_old_connections()
            
            method_result = method(self, *args)
        exception Exception as e:
            raise ThriftServerException(e)
        else:
            return method_result
        finally:
            # 结束后,清理无效连接 (似乎必要性不大 ? )
            close_old_connections()
            
    return _wrapper

Thrift Hanlder 使用:

class Handler(Iface):
    @decorator
    def interface1(self, request):
        xxx

启示

框架一般封装了太多东西,抽象出来的层级数目也很多,一般如果需要实现自己非常个性化的需求,就比较难受了,需要深入到框架的源码中去,才能有效解决问题。所以正如厨师需要熟悉他的工具一样,开发者也需要熟悉他依赖的框架,用 Django 就要熟悉 Django,用 Spring 就要熟悉 Spring 。


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/m9QgQdj1iv
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/6879
 
474 次点击