前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >图解 Unicorn 工作原理

图解 Unicorn 工作原理

作者头像
HelloGitHub
发布2021-05-14 10:50:29
1.2K0
发布2021-05-14 10:50:29
举报
文章被收录于专栏:HelloGitHub

我很早之前,就是通过这篇文章搞懂了 Python Web 应用服务器是个什么鬼,虽然本文讲的是 Ruby 的 Web 应用服务器,但原理是通的所以翻出来推荐给大家,下面是正文。

什么是 Unicorn

Unicorn 是 Ruby Web 应用中的一款应用服务器,提供两个功能:

  • 为 Rack 应用(使用 Ruby 编程语言开发的 Web 服务器和 Web 应用程序之间的模块化接口)提供 HTTP 服务能力
  • 为 Web 应用实现高并发能力

注:Python 也有类似功能的应用服务器:Gunicorn 就是从 Unicorn 移植的 pre-fork worker 模型,推荐 Python 程序员阅读本文。

Unicorn 工作在 Web 的应用层,直接调用后端 Handler 处理请求。

如何工作

提供 HTTP 服务功能:

  • 监听端口,接收 HTTP 请求
  • 解析 HTTP 请求,调用应用处理请求
  • 将处理结果返回

通过多进程提供高并发能力。由于 Ruby 全局解释锁阻碍的同一进程中的线程并行执行,Ruby 应用中的高并发必须通过多进程实现。

线程中如果有 IO 操作时,GIL 会自动释放,所以在线程中有 IO 操作的情况下,同一进程中的多个线程可以实现近似的并行执行。

通过 Master-Workers 进程结构提供服务:

  • 一个 Master,管理 Worker 进程,处理外部信号,不处理请求
  • 多个 Worker 进程,处理实际的请求,彼此独立

此结构和 Nginx 多进程模式一致。Master 作为劳心者,不处理实际的事务,只做顶层调度。Workers 作为劳力者,只处理实际的请求,受制于 Master。

请求处理过程

通过上图总结如下:

  • Master 监听端口,Fork 子进程 (也可以通过 sparn)
  • 子进程通过 select && accept 调用获取连接
  • 子进程读 socket 数据,调用 Rack App,再将结果回写 socket

另外:

  • 通过 kgio 实现非阻塞系统调用
  • 通过 C 扩展实现高速 HTTP 协议解析

IO 模型

由上图可以看出 unicorn 的 IO 模型有如下特点:

  • IO 低效,阻塞(宏观视角,本身使用非阻塞调用)
  • 一个进程同时只能处理一个请求,吞吐量低下
  • 在 socket 读写或数据库查询等等 IO 操作时,进程空闲,资源浪费
  • 无法处理慢 IO,需要前置 Nginx

如何管理进程

Master

  • 杀掉超时的子进程
  • 维持子进程数量

Worker

  • 检测 Master,同生共死,代码
  • 接收 Master 的指令

通信方式:

  • pipe
  • raindrops (共享数据)

如何平滑重启

平滑关键点在于:

  • 通过环境变量传递监听的 socket
  • 设置监听 socket close_on_exec,让操作系统保留监听 socket

unicorn-killer 的代码

代码语言:javascript
复制
def process_client(client)
  super(client) # Unicorn::HttpServer#process_client
  return if @_worker_memory_limit_min == 0 && @_worker_memory_limit_max == 0
  @_worker_process_start ||= Time.now
  @_worker_memory_limit ||= @_worker_memory_limit_min + randomize(@_worker_memory_limit_max - @_worker_memory_limit_min + 1)
  @_worker_check_count += 1
  if @_worker_check_count % @_worker_check_cycle == 0
    rss = GetProcessMem.new.bytes
    logger.info "#{self}: worker (pid: #{Process.pid}) using #{rss} bytes." if @_verbose
    if rss > @_worker_memory_limit
      logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds memory limit (#{rss} bytes > #{@_worker_memory_limit} bytes)"
      Unicorn::WorkerKiller.kill_self(logger, @_worker_process_start) # 关键点
    end
    @_worker_check_count = 0
  end
end

通过 hack process_client 方法,在请求处理完后,检测进程内存消耗等参数,操作阈值,则将自己干掉,Master 会自动起新的 Worker 进程。

- END -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 HelloGitHub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是 Unicorn
  • 如何工作
  • 请求处理过程
  • IO 模型
  • 如何管理进程
  • 如何平滑重启
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档