关于django中几个重要的gunicorn worker的配置

一、worker_class

worker_class 是 Gunicorn 的配置参数之一,它指定了工作进程(worker)的类型。不同的 worker_class 提供了不同的并发模型,适合不同类型的应用场景。syncgevent 是两种常见的 worker_class,它们的作用和区别如下:

1. sync(同步 worker)

  • 默认值:如果没有指定 worker_class,Gunicorn 默认为 sync
  • 工作方式:每个 worker 同时只能处理一个请求。这种模式是同步的,意味着每个请求会阻塞 worker,直到请求处理完成。
  • 适用场景
    • CPU 密集型应用:适用于需要进行大量计算且对 I/O 操作要求不高的应用。这类应用通常不需要高并发处理,因而同步 worker 足够。
    • 简单、低并发的应用:适合负载不高,或并发请求较少的简单应用。
  • 优点
    • 实现简单,容易理解。
    • 在 CPU 密集型应用中,sync worker 可以充分利用 CPU 资源,因为它不需要在大量 I/O 等待上浪费 CPU 时间。
  • 缺点
    • I/O 密集型场景表现不佳:如果应用需要频繁的 I/O 操作(如数据库查询、外部 API 调用等),sync worker 在处理这些操作时会阻塞,导致不能充分利用 CPU。
    • 并发处理能力较差:在高并发场景下,sync worker 由于单个 worker 只能处理一个请求,因此并发性能有限。

2. gevent(异步 worker,基于 gevent 库)

  • 工作方式gevent 使用协程来管理并发请求。每个 worker 可以在同一时间处理多个请求。这种模式是异步的,使用 gevent 库的轻量级协程来管理 I/O 任务。
  • 适用场景
    • I/O 密集型应用:非常适合需要处理大量 I/O 操作的应用,如高并发的 Web 服务、实时消息应用等。因为 gevent 的协程模型允许在 I/O 等待时切换到其他任务,从而提高资源利用率。
    • 高并发场景:当需要处理大量并发请求时,gevent worker 可以在不增加物理线程或进程的情况下通过异步 I/O 来提高并发处理能力。
  • 优点
    • 高效的并发处理gevent 的异步 I/O 模型使其能够处理大量并发请求,同时在 I/O 等待期间高效切换。
    • 节省资源:与多进程或多线程模型相比,gevent 协程的内存和 CPU 开销更小,允许更高的并发量。
  • 缺点
    • 需要支持异步 I/O:使用 gevent 需要确保所有 I/O 操作(如数据库驱动、HTTP 客户端等)支持异步 I/O,否则会阻塞整个协程。
    • 复杂性增加:异步编程通常比同步编程更复杂,可能需要开发者对异步 I/O 和协程有更多了解。

如何选择 worker_class

  • CPU 密集型应用(如数据处理、图像处理):选择 sync worker 或者基于线程的 worker(如 gthread)。
  • I/O 密集型应用(如 Web 服务、需要频繁进行数据库查询或 API 调用的应用):选择 geventasync worker,以充分利用异步 I/O 的优势,提高并发性能。

示例配置

  • sync worker 示例

    worker_class = 'sync'
    
  • gevent worker 示例

    worker_class = 'gevent'
    

根据应用的特性和需求,选择合适的 worker_class 可以显著提高服务器的性能和并发处理能力。

二、worker_connections

worker_connections 参数在 Gunicorn 中用于设置每个 worker 允许的最大并发连接数。这个参数主要适用于异步 worker 类型,例如 geventeventlet

worker_connections 参数的作用

  • 最大并发连接数worker_connections 决定了每个异步 worker(如 gevent)可以同时处理的最大连接数量。这些连接可以是客户端请求的连接,也可以是后台与其他服务(如数据库、缓存服务等)的连接。
  • 适用对象worker_connections 只对异步 worker 有效,比如 geventeventlet。对于同步 worker(sync)或线程 worker(gthread),这个参数没有意义。

使用 worker_connections 的场景

  • 高并发 I/O 应用:如果你的应用是 I/O 密集型,并且需要处理大量并发连接,比如 WebSocket 服务、实时消息推送服务、需要与多个外部服务频繁交互的 API 服务器等,设置较高的 worker_connections 可以提升并发性能。
  • 限制资源消耗:在某些情况下,你可能想限制单个 worker 的最大连接数,以防止某个 worker 消耗过多资源,影响其他 worker 的运行。

配置建议

  • 合理的设置:默认情况下,worker_connections 的值为 1000,这对于大多数应用来说已经足够了。根据你的应用特性(主要是 I/O 密集型应用)和服务器硬件资源,你可以调整这个值。
  • 服务器性能:在设置 worker_connections 时,需要考虑服务器的性能(如 CPU、内存等)。较高的 worker_connections 值意味着每个 worker 可以处理更多的并发连接,但也会占用更多的内存和 CPU 资源。

示例配置

在使用 gevent 时,如果你预计有非常高的并发连接量,可以这样配置:

import multiprocessing

# 绑定的 IP 和端口
bind = '0.0.0.0:8000'

# 使用 gevent 异步 worker
worker_class = 'gevent'

# 设置每个 worker 的最大并发连接数
worker_connections = 10000

# 根据 CPU 核心数设置合适的 worker 数量
workers = multiprocessing.cpu_count() * 2 + 1

注意事项

  • 适度配置:虽然设置 worker_connections = 10000 能够处理更多的并发请求,但请确保你的应用和服务器可以承受这样的负载。过高的并发连接数会导致内存和 CPU 使用激增,可能会影响到服务器的整体性能。
  • 监控和优化:部署后,应持续监控应用的性能和资源使用情况,根据实际情况调整 worker_connections 和其他相关参数,确保服务器稳定运行。

在选择合适的 worker_connections 值时,建议从默认值开始,并根据应用的实际需求和服务器性能逐步调整。

max_requests_jitterkeepalive 是 Gunicorn 配置文件中的两个参数,用于控制 worker 进程的生命周期和连接的保持策略。让我们分别了解这两个参数的作用和如何配置。

三、max_requests与keepalive

1. max_requests_jitter

  • 作用: max_requests_jitter 参数用于设置在 max_requests 的基础上增加一个随机的抖动值。max_requests 是指每个 worker 在处理一定数量的请求后将会重启,以防止内存泄漏或其他资源问题。max_requests_jitter 的引入是为了避免所有 worker 同时重启,从而平滑系统的负载波动。

  • 用法: max_requests_jitter 的值是一个整数,它会被随机添加到 max_requests 上。例如,如果 max_requests 设置为 1000,而 max_requests_jitter 设置为 2,那么每个 worker 在处理了 1000 到 1002(1000 + 随机[0, 2])之间的请求后将会重启。

  • 示例:

    max_requests = 1000
    max_requests_jitter = 2
    

    在这个配置中,每个 worker 将会在处理 1000 到 1002 个请求后重启。这种方式可以避免多个 worker 同时重启而带来的短暂服务不可用。

2. keepalive

  • 作用: keepalive 参数用于设置在连接空闲时,HTTP Keep-Alive 连接保持打开的秒数。这对于减少客户端和服务器之间的连接建立开销(如握手、SSL/TLS 协商等)非常有用,特别是在客户端会频繁发起多个请求的情况下。

  • 用法: keepalive 的值是一个整数,表示空闲连接保持打开的秒数。如果设置为 0,则表示关闭 HTTP Keep-Alive 功能,即每个请求结束后立即关闭连接。

  • 配置建议:

    • 适中值: 对于大多数应用程序,设置 keepalive 为 30 到 60 秒是一个合理的范围。这允许连接在短时间内被复用,从而减少握手开销和延迟。
    • 长时间保持: 如果你的应用有较长时间的操作,或者希望减少连接重建开销,可以设置更长的保持时间,例如 120 秒。
    • 短时间保持: 如果你希望服务器能够更快地释放连接资源,以便处理更多的客户端连接,可以设置一个较短的时间,例如 5 到 10 秒。
  • 示例:

    keepalive = 60
    

    在这个配置中,每个空闲连接在 60 秒后会被关闭。如果客户端在这段时间内发起另一个请求,连接可以复用。

总结

  • max_requests_jitter: 为了避免所有 worker 同时重启,平滑负载波动。
  • keepalive: 用于优化连接复用,减少连接建立开销。

在实际配置中,建议根据应用的负载特征、请求频率、网络环境等因素来调整这两个参数,以达到最佳的性能和稳定性。

四、我的项目中gunicorn_config示例

from imp import reload
import os
from distutils.util import strtobool
BASE_PATH = os.path.abspath(os.path.dirname(__file__))

# 指定应用
wsgi_app ='app:app'


# 是否让进入后台运行, docker内肯定设置False, 因为必须有一直前台运行的进程
daemon = False


#################debugging############
reload = False

#################logging############
# 设置进程文件目录
# pidfile = '/var/run/gunicorn.pid'
pidfile = os.path.join(BASE_PATH, 'gunicorn.pid')
# 设置访问日志输出文件地址, -表示输出到标准输出
accesslog = '-'
# 禁用将访问日志转发到syslog
disable_redirect_access_to_syslog = True
# 日志访问格式
access_log_format = '%(h)s %(l)s %(t)s %(p)s "%(r)s" %(U)s %(s)s %(l)s %(b)s %(l)s %(L)s'
# 设置错误日志访问地址, -表示输出到标准错误
errorlog = '-'
# 设置日志记录水平
loglevel = 'info'
# 是否重定向标准输出和标准错误到errorlog去, 默认是False
# capture_output = False
# 处理日志的日志类,下面是default的值
# logger_class = 'gunicorn.glogging.Logger'
# 配置logger的设置, 是不是这里可以设置日志旋转
# logconfig = None
# 通过字典配置logger,优先级高于logconfig
# logconfig_dict = {}
# 系统日志syslog地址
# syslog_addr = 'udp://localhost:514'
# 是否启动将gunicorn日志发送到syslog,默认False
# syslog = False

# statsd是一款数据采集程序, StatsD系统包括三部分:客户端(client)、服务器(server)和后端(backend)
# 如果设置了,就是把一些数据上送上去, 后面好分析
# statsd_host = None
# 也是关于statsd的设置
# dogstatsd_tags = ''
# 发出statsd度量时使用的前缀
# statsd_prefix = ''

#################server socket############
# 监听内网端口
bind = '0.0.0.0:8888'
# 最大等待连接的客户端数量
backlog = 2048

#################worker processes############
# worker数量. 实际就是进程数量
if os.environ.get('ENV', 'local') == 'local':
    workers = int(os.environ.get('WORKERS', 2))
else:
    workers = int(os.environ.get('WORKERS', 8))
# 工作模式协程
# worker_class = 'sync'
worker_class = 'gevent'
# 设置线程数量,这个参数只在worker类型是Gthread时有效
# 如果使用了sync类型worker,又设置threads大于1,则会使用gthread替代sync
# threads = 1
# 最大并发客户端数量, 这个设置仅影响Eventlet and Gevent worker类型
worker_connections = 10000
# 一个worker在处理一定数量的request后可以让他自动重启, 这是一种帮助限制内存泄漏损害的简单方法. 默认是0, 禁用这个功能
# max_requests = 0
# 这是为了错开worker重启以避免所有worker同时重启,导致服务不可用
max_requests_jitter = 2
# gunicorn 给子进程的执行时间,如果超过这个限制就会被父进程kill掉,然后重启一个新的。 
# 比如处理一个请求30秒内worker不能进行返回结果,gunicorn就认定这个request超时,终止worker继续执行,向客户端返回出错的信息,
# 用于避免系统出现异常时,所有worker都被占住,无法处理更多的正常request,导致整个系统无法访问
# 所以可以检查一下为什么这个request会耗那么久的时间(超过30秒),如果是正常的话,可以适当调高gunicorn的超时限制或者使用异步的worker,
# 如果是系统处理速度遇到瓶颈,那可能要从数据库,缓存,算法等各个方面来提升速度。
timeout = 200 #这个参数只对woker是sync方式起作用
# 优雅关闭,指接收到重启信号, 工作进程不是立马被杀死重启,而是等待一定时间处理可能正在处理的任务,然后才重启
# graceful_timeout = 150
keepalive = 60

#################server Hooks############
# def on_starting(server):
#     '''
#     这个是主进程启动时会被调用
#     '''
#     pass

# def on_reload(server):
#     '''
#     Called to recycle workers during a reload via SIGHUP.
#     '''
#     pass

# def when_ready(sever):
#     '''
#     当gunicorn准备就绪, 坐等请求过来时调用
#     '''
#     pass

# def pre_fork(sever, worker):
#     '''
#     创建子进程之前调用, 有几个子进程就应该被调用几次
#     '''
#     pass

# def post_fork(sever, worker):
#     '''
#     子进程创建成功了被调用,有几个成功就被调用几次
#     '''
#     pass

# def post_worker_init(worker):
#     '''
#     worker进程初始化完成application
#     '''
#     pass

# def worker_int(worker):
#     '''
#     接收到SIGINT or SIGQUIT信号时,工作进程退出调用
#     '''
#     pass

# def worker_abort(worker):
#     '''
#     接收到SIGABRT信号时被调用,通常发生timeout时,处理请求耗费时间超过了设置的timeout值,
#     这里应该主动上报错误,定位为什么处理那么长时间
#     '''
#     pass

# def pre_exec(server):
#     '''
#     一个新的master进程forked时调用
#     '''
#     pass

# def pre_request(worker, req):
#     '''
#     在worker开始处理请求前调用
#     '''
#     pass
    

# def post_request(worker, req, environ, resp):
#     '''
#     处理完成请求时调用
#     '''
#     pass

# def child_exit(sever, worker):
#     '''
#     Called just after a worker has been exited, in the master process.
#     '''
#     pass

# def worker_exit(server, worker):
#     '''
#     工作进程退出时调用
#     '''
#     pass

# def nworkers_changed(server, new_value, old_value):
#     '''
#     workers的数量发生改变时被调用
#     '''
#     pass

# def on_exit(server):
#     '''
#     gunicorn退出时调用
#     '''
#     pass
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐