使用 Gunicorn 进行 Python Web 应用部署的基础配置

Gunicorn(Green Unicorn)是一个用于部署 Python Web 应用的 WSGI HTTP 服务器。它以高效、稳定和易用而著称。在这篇博客中,我们将介绍如何进行基础的 Gunicorn 配置,并展示一个示例配置文件。

安装 Gunicorn

首先,确保你已经安装了 Gunicorn。可以使用 pip 进行安装:

pip install gunicorn

基础配置示例

以下是一个简单的 Gunicorn 配置文件示例 conf.py,用于启动一个 Web 应用:

#conf.py

import gevent.monkey
import multiprocessing

gevent.monkey.patch_all()

bind = '0.0.0.0:5000'
preload_app = True
loglevel = 'error'
logfile = 'log/debug.log'
accesslog = 'log/access.log'
access_log_format = '%(h)s %(t)s %(U)s %(q)s'
errorlog = 'log/error.log'
proc_name = 'vservice'
pidfile = 'log/gunicorn.pid'
daemon = True
workers = multiprocessing.cpu_count() * 2 + 1
threads = multiprocessing.cpu_count() * 2
worker_class = 'gevent'


这个配置文件设置了 Gunicorn 的基本参数,包括绑定地址、日志路径、进程和线程数量等。现在,让我们一行一行地解释这个配置。

  1. import gevent.monkey 和 gevent.monkey.patch_all():引入并启用 gevent monkey patch,以使 Gunicorn 与 gevent 协同工作。
  2. import multiprocessing:引入 multiprocessing 模块,用于获取 CPU 核心数量。
  3. bind = ‘0.0.0.0:8002’:设置 Gunicorn 的绑定地址和端口,允许通过所有网络接口访问,端口为 8002。
  4. preload_app = True:在主进程启动时预加载应用程序,适用于一些需要共享资源或者在启动时执行初始化代码的情况。
  5. loglevel = ‘error’:设置日志级别为 ‘error’,只记录错误信息。
  6. logfile = ‘log/debug.log’:设置日志文件路径。
  7. accesslog = ‘log/access.log’:设置访问日志文件路径。
  8. access_log_format = ‘%(h)s %(t)s %(U)s %(q)s’:设置访问日志的格式。
  9. errorlog = ‘log/error.log’:设置错误日志文件路径。
  10. proc_name = ‘vservice’:设置 Gunicorn 进程的名称。
  11. pidfile = ‘log/gunicorn.pid’:设置 Gunicorn 进程的 PID 文件路径。
  12. daemon = True:将 Gunicorn 进程设置为守护进程,即在后台运行。
  13. workers = multiprocessing.cpu_count() * 2 + 1:设置工作进程的数量,通常为 CPU 核心数的两倍加一。
  14. threads = multiprocessing.cpu_count() * 2:设置每个工作进程的线程数,通常为 CPU 核心数的两倍。
  15. worker_class = ‘gevent’:设置工作进程的类别为 gevent,表示使用 gevent 模块提供的协程支持。

启动 Gunicorn 服务器

使用以下命令启动 Gunicorn 服务器:

gunicorn -c conf.py your_app:app

这里的 your_app:app 是你的应用的模块和应用实例。通过这个简单的配置文件,你可以轻松地启动一个 Gunicorn 服务器,并根据实际需要进行配置。

希望这篇博客对你理解和配置 Gunicorn 服务器有所帮助!如果有任何问题或疑问,请随时留言。

避免Python中的递归调用问题:如何正确使用__getattr__和__setattr__方法

递归调用是指一个函数或方法在执行的过程中,直接或间接地调用了自身,从而形成了无限循环的调用关系,导致程序崩溃或陷入死循环。在 Python 中,由于方法和属性的调用方式不同,很容易出现递归调用的问题,特别是在使用 __setattr__ 和 __getattr__ 等特殊方法时。

递归调用是指一个函数或方法在执行的过程中,直接或间接地调用了自身,从而形成了无限循环的调用关系,导致程序崩溃或陷入死循环。在 Python 中,由于方法和属性的调用方式不同,很容易出现递归调用的问题,特别是在使用 __setattr__ 和 __getattr__ 等特殊方法时。

RecursionError: maximum recursion depth exceeded 

在相关开发的项目中,为了便于数据库和业务的交互,定义了一些Model来处理任务,为了简便完成Model的开发,采用了 __setattr__ 和 __getattr__ ,但却因此产生了递归调用的问题。以下是出错的代码:

class Person(object):
    def __init__(self, *args, **kwargs):
       self.document = {"genre": "XXX", "age": "XXX"}
    def __getattr__(self, name):
        if name in self.document:
            return self.document[name]
        else:
            raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
    def __setattr__(self, name, value):
        # 这里的self.document会触发__getattr__方法
        # 从而在__getattr__里面的self.document又继续
        # 触发__getattr__,从而产生递归调用问题
        if name in self.document: 
            self.document[name] = value
        else:
            super().__setattr__(name, value)
person = Person()
person.age = 18 

为了解决这个问题,我们可以在类中使用 self.__dict__.setdefault() 方法或者在 init 方法中使用 self._document = {} 来初始化实例属性。这样做的好处是,实例属性和类属性的作用域得到了清晰的界定,避免了递归调用的问题。此外,如果要修改实例属性,我们可以直接使用 setattr() 方法,而不是直接赋值,这样也可以避免递归调用的问题。

下面是一个示例代码,演示了如何使用 getattrsetattr 方法,避免递归调用的问题:

class Person(object):
    def __init__(self, *args, **kwargs):
       # 初始化实例下的__dict__
       self.__dict__.setdefault('document', {})
       self.document = {"genre": "XXX", "age": "XXX"}
    def __getattr__(self, name):
        if name in self.document:
            return self.document[name]
        else:
            raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
    def __setattr__(self, name, value):
        if name in self.document: 
            self.document[name] = value
        else:
            super().__setattr__(name, value)

在这个示例代码中,我们使用了 self.__dict__.setdefault() 方法来初始化实例属性。这样,就可以避免递归调用的问题,确保程序能够正确地运行。

另外在类属性这里初始化一个document = {}也能解决问题,但是需要注意类属性和实例属性的作用域,避免产生混淆,导致程序出错。

class Person(object):
    document = {"genre": "XXX", "age": "XXX"}
    def __init__(self, *args, **kwargs):
        pass
    def __getattr__(self, name):
        if name in self.document:
            return self.document[name]
        else:
            raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

    def __setattr__(self, name, value):
        if name in self.document: 
            self.document[name] = value
        else:
            super().__setattr__(name, value)

Python的__getattr__和__setattr__是在开发中很好用的方法,能大大提高开发效率,但是递归调用的问题不能忽视,以上便是针对这个问题的解决方案。