分享 Puma 源代码分析 - 概述

ylt · 2015年02月27日 · 最后由 lanzhiheng 回复于 2018年06月05日 · 10549 次阅读
本帖已被管理员设置为精华贴

2015 年春节期间,通读了 Puma 的源代码。阅读 Puma 的源码是一个愉快的技术探索过程,这里把阅读所得分享出来。

Puma 总体架构

什么是 Puma?

Puma 是一个面向 ruby 语言的并发的 Web 服务器(a modern, concurrent web server for ruby)。它的官方网站是puma.io.

什么是 Web 服务器?

一个标准的 Web 服务器遵循 HTTP 协议,接受 HTTP Request 请求,返回 HTTP Response 的响应结果。请求和响应都是字符串流的格式。如图 1 所示:

使用 curl 命令可以很方便的测试和学习 HTTP 协议。比如执行下面的 curl 命令,可以看到 http 的请求和响应。

curl  -v http://z.cn

Http 的请求由 curl 发出,curl 此时就是 Web Client。

GET / HTTP/1.1
User-Agent: curl/7.37.1
Host: z.cn
Accept: */*

然后 z.cn 的 web 服务器收到 HTTP Request 请求,并返回 HTTP Response 的响应结果,如下:

HTTP/1.1 301 Moved Permanently
Date: Thu, 26 Feb 2015 07:25:05 GMT
Location: http://www.amazon.cn/ref=z_cn?tag=zcn0e-23
Content-Length: 250
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://www.amazon.cn/ref=z_cn?tag=zcn0e-23">here</a>.</p>
</body></html>

Web 服务器根据 Http 请求的路径和其他头部信息来决定如何输出 Http 的响应结果。Web 服务器有三种方式处理 Http 请求:

  1. 代理到下一级的 Web 服务器;
  2. 对静态文件请求,直接读取本地文件内容并输出;
  3. 对动态请求,则通过接口调用动态程序。

对于第三种的动态请求,又有很多接口规范,比如 cgi/fastcgi, servlet, rack 等。其中 cgi/fastcgi 是语言无关的,servlet 是特定于 java 语言的,而 rack 是特定于 ruby 语言的。

Ruby/Rack Web 服务器

Rack 的官方网站是rack.github.io。支持 Rack 的 Web 服务器的架构如图 2 所示:

Rack 应用的输入是一个表示环境的 hash,输出是一个数组包括三个元素:

  1. HTTP 响应码
  2. HTTP 响应头的 Hash
  3. HTTP 响应体,该响应体必须支持 each 方法调用

Rack 服务器和 Rack 应用之间通过 ruby 对象交互,Rack 服务器直接调用 Rack 应用;而 Web 服务器和 Web 客户端之间则是通过字符串流交互。

Puma 是一个支持 Ruby/Rack applications 的 Web 服务器。其它支持 Rack 规范的 Web 服务器还有 Unicorn/Thin/Passenger 等。

Puma 的源代码结构

Puma 是一个典型的 Rack 服务器。其总体目录结构与关键源代码文件如下:

├── bin   可执行脚本
│   ├── puma      启动脚本
│   ├── puma-wild
│   └── pumactl   控制脚本
├── docs 文档
├── examples  一些例子文件
├── ext   ruby的原生扩展,实现快速解析HTTP的头部
│   └── puma_http11  
├── lib
│   ├── puma
│   │   ├── app
│   │   │   └── status.rb     响应pumactl的控制请求
│   │   ├── binder.rb         启动并绑定Tcp/Unix端口
│   │   ├── cli.rb            Puma的命令行接口,解析命令行参数
│   │   ├── client.rb         代表Http请求的客户端
│   │   ├── cluster.rb            Puma的集群模式
│   │   ├── configuration.rb  Puma的配置参数
│   │   ├── control_cli.rb     命令行控制脚本的实现代码
│   │   ├── daemon_ext.rb      实现puma进程的daemon化
│   │   ├── events.rb          服务器的事件支持
│   │   ├── null_io.rb            当request没有body时rack.input的值
│   │   ├── rack_default.rb   指定缺省的rack handler
│   │   ├── rack_patch.rb     Patch CommonLogger to use after_reply
│   │   ├── reactor.rb        所有需要监听新数据的client链接,放到reactor的@sockets队列中
│   │   ├── runner.rb     启动Puma Server的基类,Single/Cluster继承它
│   │   ├── server.rb     The HTTP Server itself. Serves out a single Rack app.
│   │   ├── single.rb     Puma的单进程模式
│   │   ├── thread_pool.rb 线程池,其中是所有可执行的worker
│   ├── puma.rb
│   └── rack
│       └── handler
│           └── puma.rb       缺省的puma rack handler实现
├── test  测试代码
└── tools 进程监控脚本:init.d/upstart
    ├── jungle
    │   ├── init.d
    │   └── upstart

本系列后续部分将详细分析 Puma 如何启动、如何监听 tcp 连接,如何解析 http header,如何执行 rack app,如何响应控制命令,如何处理文件上传,如何实现集群模式,如何使用 reactor 模型处理 io 等。

Puma 中关于 Jruby 和 SSL 支持的部分本文档不会涉及。因为笔者自己都是在 MRI ruby 上跑 puma 的,没有用过 jruby 跑 puma,而且 SSL 都是交给 web 接入层比如 nginx 处理的,所以 puma 中这两个功能点的代码就不关心了。

Puma 源代码分析 - 启动流程 Puma 源代码分析 - 单进程模式 Puma 源代码分析 - 集群模式 Puma 源代码分析 - IO 处理 Puma 源代码分析 - http 协议解析 Puma 源代码分析 - 完结篇

#1 楼 @iBachue 写文档比读源码费时间,实际读代码也就三天时间,全部写出来可能要一个星期以上了。

👍 强烈支持

delicious

#2 楼 @ylt LZ,分享一下你是如何来读 Puma 源代码的呗。

11 楼 已删除
12 楼 已删除

#7 楼 @ruby_sky ruby 是动态语言,其源代码的读法和静态语言不一样。我的方法就是按代码执行流程来读,碰到不懂的打断点,然后在断点处调试,调试其实就是在断点处写代码(这是和静态语言的调试很不一样的地方)。碰到函数/变量不知道含义,直接 grep 搜索名称,看整个代码库里函数在哪些地方调用了,变量什么时候赋值、什么时候读取,一般就能知道其用途了。

非常赞,期待下文。

脸脸的用户 只能说这是个很好的 app 只是没有线上推广 貌似

#15 楼 @riskgod 是啊,脸脸还没有线上推广,资金到位后会推的。

ruby 有 puma/unicorn 等多种竞争品,为何 php 没看到类似现象呢? fastcgi, rack 是互相竞争的么?还是不相关的呢?

#17 楼 @chenge fastcgi 是语言无关的一个接口,使用 fastcgi 时 web server 和 script 是两个进程;而 rack 是一个进程内同语言的方法调用。至于 fastcgi 和 php 的关系我也不是太清楚,至少 spawn-fcgi 与 PHP-FPM 就是支持 PHP 的两个 FastCGI 进程管理器。

#18 楼 @ylt 是否可以这样理解,puma 是类似 php-fpm 这样的,而 php 没有太多别的选择。 php 也是用的 fastcgi 吧。还有比 fastcgi 更先进的么?

感谢分享

感谢分享 🍎

#24 楼 @mogodb 没有啊,我这里深圳电信,不使用代理也可以

26 楼 已删除
27 楼 已删除
29 楼 已删除
需要 登录 后方可回复, 如果你还没有账号请 注册新账号