zoukankan      html  css  js  c++  java
  • 使django与数据库保持长连接

       最近遇到一个很蛋疼的问题,写了一个后台管理系统, 由于是后台管理系统,所以使用频率不是很高,当django程序在闲置一段时间后,再次打开后台系统,就变得很慢,然后又好了。查了很多方面,从模板引擎到请求(request),再到django配置,nginx等等,都没有查出原因。虽然也查过是不是数据库的原因,但都因为查的不够深入,没有查出所以然。

            有一次在处理权限问题时,不断地追朔源代码,由于我使用的是dbroute来控制权限,最后定位到了这个db(具体目录:/usr/local/lib/python2.7/dist-packages/django/db/__init__.py),里面的代码有关于连接的,不看不知道,看了后菊花一紧。我去!原来django每次查询的时候就连一次数据库,而且查询完成之后就立马关掉,真搞不懂作者是怎么想的。每次查询建一次连接不是很耗时间吗?源代码如下:

    1.  
      from django.conf import settings
    2.  
      from django.core import signals
    3.  
      from django.core.exceptions import ImproperlyConfigured
    4.  
      from django.db.utils import (ConnectionHandler, ConnectionRouter,
    5.  
      load_backend, DEFAULT_DB_ALIAS, DatabaseError, IntegrityError)
    6.  
       
    7.  
      __all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError',
    8.  
      'IntegrityError', 'DEFAULT_DB_ALIAS')
    9.  
       
    10.  
       
    11.  
      if settings.DATABASES and DEFAULT_DB_ALIAS not in settings.DATABASES:
    12.  
      raise ImproperlyConfigured("You must define a '%s' database" % DEFAULT_DB_ALIAS)
    13.  
       
    14.  
      connections = ConnectionHandler(settings.DATABASES)
    15.  
       
    16.  
      router = ConnectionRouter(settings.DATABASE_ROUTERS)
    17.  
       
    18.  
      # `connection`, `DatabaseError` and `IntegrityError` are convenient aliases
    19.  
      # for backend bits.
    20.  
       
    21.  
      # DatabaseWrapper.__init__() takes a dictionary, not a settings module, so
    22.  
      # we manually create the dictionary from the settings, passing only the
    23.  
      # settings that the database backends care about. Note that TIME_ZONE is used
    24.  
      # by the PostgreSQL backends.
    25.  
      # We load all these up for backwards compatibility, you should use
    26.  
      # connections['default'] instead.
    27.  
      class DefaultConnectionProxy(object):
    28.  
      """
    29.  
      Proxy for accessing the default DatabaseWrapper object's attributes. If you
    30.  
      need to access the DatabaseWrapper object itself, use
    31.  
      connections[DEFAULT_DB_ALIAS] instead.
    32.  
      """
    33.  
      def __getattr__(self, item):
    34.  
      return getattr(connections[DEFAULT_DB_ALIAS], item)
    35.  
       
    36.  
      def __setattr__(self, name, value):
    37.  
      return setattr(connections[DEFAULT_DB_ALIAS], name, value)
    38.  
       
    39.  
      connection = DefaultConnectionProxy()
    40.  
      backend = load_backend(connection.settings_dict['ENGINE'])
    41.  
       
    42.  
      # Register an event that closes the database connection
    43.  
      # when a Django request is finished.
    44.  
      def close_connection(**kwargs):
    45.  
      # Avoid circular imports
    46.  
      from django.db import transaction
    47.  
      for conn in connections:
    48.  
      # If an error happens here the connection will be left in broken
    49.  
      # state. Once a good db connection is again available, the
    50.  
      # connection state will be cleaned up.
    51.  
      transaction.abort(conn)
    52.  
      connections[conn].close()
    53.  
      signals.request_finished.connect(close_connection)
    54.  
       
    55.  
      # Register an event that resets connection.queries
    56.  
      # when a Django request is started.
    57.  
      def reset_queries(**kwargs):
    58.  
      for conn in connections.all():
    59.  
      conn.queries = []
    60.  
      signals.request_started.connect(reset_queries)
    61.  
       
    62.  
      # Register an event that rolls back the connections
    63.  
      # when a Django request has an exception.
    64.  
      def _rollback_on_exception(**kwargs):
    65.  
      from django.db import transaction
    66.  
      for conn in connections:
    67.  
      try:
    68.  
      transaction.rollback_unless_managed(using=conn)
    69.  
      except DatabaseError:
    70.  
      pass
    71.  
      signals.got_request_exception.connect(_rollback_on_exception)

    发现它是接收到了一个关闭信号后就立马关闭,其实也就是每一次django请求,它就连接一次和查询一次。网上查了下资料,有个人是这样解决的:

    在django项目中的__init__.py文件中加入如下代码来实现:

    1.  
      from django.core import signals
    2.  
      from django.db import close_connection
    3.  
       
    4.  
      # 取消信号关联,实现数据库长连接
    5.  
      signals.request_finished.disconnect(close_connection)

    它采用的原理是最后一行代码相关的Signal对象,其中还有一个disconnect方法,对应实现取消信号关联。

           我照着他的实现来做,发现不行,在闲置几小时后,打开后台依然需要10秒左右。由于我使用的是fastcgi来运行django,可能django在没有动静的时候,fastcgi就把它挂起,所以就还会去重新建立连接。如果使用supervisord来运行,估计不会。

           既然这个方法不行,那就再看一下它的源码,发现backends目录下面有mysql,oracle,postgresql_psycopg2,sqlite3等,于是选择msql进去看一下base.py文件。发现django是直接封装MySQLdb,每创建一个MySQLdb对象其实也就进行了一次连接。能不能像线程池一样,一次性创建多个MySQLdb对象呢?答案是肯定的。sqlalchemy有个pool可以让数据库保持长连接,那就直接把这个文件里的Database改成sqlalchemy的pool。当然,我们不能暴力地修改去修改源码。因为这个模块是用来建立数据库连接的,所以可以独立出来。其实很简单,只需要修改base.py 文件几处就行。实现方法如下:

            把django/db/backends/mysql目录下的文件全部拷贝出来,放在项目的一个libs/mysql下面,然后修改base.py文件。找到

    try:
        import MySQLdb as Database
    except ImportError as e:
        from django.core.exceptions import ImproperlyConfigured
        raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)

    这段代码,在下面添加:

    1.  
      from sqlalchemy import pool
    2.  
      Database = pool.manage(Database)


    基本上就可以了。如果还想再修改,就找到

    self.connection = Database.connect(**kwargs)

    把它注释掉,添加

    1.  
      self.connection = Database.connect(
    2.  
      host=kwargs.get('host', '127.0.0.1'),
    3.  
      port=kwargs.get('port', 3306),
    4.  
      user=kwargs['user'],
    5.  
      db=kwargs['db'],
    6.  
      passwd=kwargs['passwd'],
    7.  
      use_unicode=kwargs['use_unicode'],
    8.  
      charset='utf8'
    9.  
      )


    再修改一下settings.py的数据库配置,把django.db.backends.mysql改为libs.mysql。至此,世界美好了很多。如果精虫上脑,可以移步至:来撸吧,你会发现世界更美好。

  • 相关阅读:
    js 基于可视区域 创建展示区域对应的经纬度二维数组
    主要看思路:区域数据去重 + JavaScript一次性展示几万条数据实例代码
    推荐js库: underscore
    开博缘由 , 可点下看看 http://www.cnblogs.com/jshare
    php--sphinx的使用
    php--validate表单验证实例
    php--validate错误信息提示样式
    php--yii框架中的ajax分页与yii框架自带的分页
    php--jquery操作全选、批量删除、加减行
    php--yii2.0框架的curl
  • 原文地址:https://www.cnblogs.com/ExMan/p/9373884.html
Copyright © 2011-2022 走看看