社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Django

Django ORM 数据库生命周期

hezhiming • 7 年前 • 543 次点击  

起因

有时在线上会遇到“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
 
543 次点击