<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>ylt (袁乐天)</title>
    <link>https://ruby-china.org/ylt</link>
    <description>追求内心的直觉</description>
    <language>en-us</language>
    <item>
      <title>写了一个 Libra 的客户端库</title>
      <description>&lt;p&gt;Libra 是 facebook 推出的一种数字货币，和实际的法币挂钩，在我看来是‘真实’的货币，应该可以算是 M0。最近几个月尝试给 Libra 项目写一个 ruby 的客户端，最终形成了两个开源项目&lt;a href="https://github.com/yuan-xy/canoser-ruby" rel="nofollow" target="_blank" title=""&gt;'casoer-ruby'&lt;/a&gt;和&lt;a href="https://github.com/yuan-xy/libra_client_ruby" rel="nofollow" target="_blank" title=""&gt;'libra_client_ruby'&lt;/a&gt;：&lt;/p&gt;
&lt;h3 id="casoer-ruby : LCS的ruby实现"&gt;casoer-ruby : LCS 的 ruby 实现&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/yuan-xy/canoser-ruby" rel="nofollow" target="_blank" title=""&gt;A Ruby implementation of the LCS(Libra Canonical Serialization)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LCS(Libra Canonical Serialization) 是 Libra 中采用的规范序列化协议。它可确保内存里的数据结构在序列化的时候保证字节一致性。它适用于双方想要有效比较他们独立维护的数据结构。在共识协议中，独立验证者需要就他们独立计算的状态达成一致。共识双方比较的是序列化数据的加密散列。要实现这一点，在计算时，相同数据结构的序列化必须相同。而独立验证器可能由不同的语言编写，有不同的实现代码，但是都遵循同一个规范。&lt;/p&gt;
&lt;h3 id="libra_client_ruby : 访问Libra网络的Ruby语言API"&gt;libra_client_ruby : 访问 Libra 网络的 Ruby 语言 API&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/yuan-xy/libra_client_ruby" rel="nofollow" target="_blank" title=""&gt;Cient APIs  for the Libra network&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Libra 网络采用的是 protobuf+grpc 的接口协议， 'libra_client_ruby'主要是对该协议的封装。安装方式很简单，标准的 gem 命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;libra_client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要访问 Libra 网络，参考下面的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'libra_client'&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Libra&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:testnet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_account_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"000000000000000000000000000000000000000000000000000000000a550c18"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balance&lt;/span&gt;           &lt;span class="c1"&gt;#the balance of the account&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sequence_number&lt;/span&gt;   &lt;span class="c1"&gt;#the sequence_number of the account&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在写这个库的过程中，碰到了一个难题。Ruby 目前不支持 SHA3 系列散列算法，包括 SHA3_224, SHA3_256, SHA3_384 and SHA3_512。但是 Libra 中使用的 Hash 算法是 SHA3_256。
我尝试自己实现了 Openssl 中 SHA3 系列函数的 Ruby 移植，但是&lt;a href="https://github.com/ruby/openssl/pull/271" rel="nofollow" target="_blank" title=""&gt;pull request&lt;/a&gt;没有通过，最后放弃了。&lt;/p&gt;
&lt;h3 id="Libra客户端库的Python实现"&gt;Libra 客户端库的 Python 实现&lt;/h3&gt;
&lt;p&gt;由于 python 和 ruby 语法类似，且支持 SHA3_256，所以我尝试用 python 重写了一遍上面的两个库。而且 python 的版本支持完整的客户端功能，比如交易和钱包。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/yuan-xy/canoser-python" rel="nofollow" target="_blank" title=""&gt;A Python implementation of the LCS(Libra Canonical Serialization)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/yuan-xy/libra-client" rel="nofollow" target="_blank" title=""&gt;Python Language APIs  for the Libra network&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;等 ruby 支持了 SHA3, 也许考虑升级一些 ruby 的客户端。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Sat, 19 Oct 2019 10:28:26 +0800</pubDate>
      <link>https://ruby-china.org/topics/39170</link>
      <guid>https://ruby-china.org/topics/39170</guid>
    </item>
    <item>
      <title>开源一个超酷的 Restful API 自动生成器：Kaola</title>
      <description>&lt;p&gt;项目的地址是： &lt;a href="https://github.com/yuanxinyu/kaola" rel="nofollow" target="_blank"&gt;https://github.com/yuanxinyu/kaola&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="0. 什么是Kaola"&gt;0. 什么是 Kaola&lt;/h2&gt;
&lt;p&gt;Kaola（考拉，英文 koala）是一个全自动的 restful api 代码自动生成系统。给定一个数据库，只需要配置好数据库连接，koala 可以通过扫描数据库自动生成全套的 restful api 的后端代码。通过预先约定好的接口调用规范，前端就可以直接开发应用系统了。&lt;/p&gt;
&lt;h2 id="1. 为什么开发Kaola"&gt;1. 为什么开发 Kaola&lt;/h2&gt;
&lt;p&gt;类似 koala 的代码生成系统之前也有很多，比如 rails 自带的 scaffold 功能，以及更完善的 Active Admin／Rails Admin 等。但是这些系统都有两点不符合要求：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;这些系统都是一套完整的系统，从后端到页面都一次性生成。实际的情况是，后端的接口比较通用，而界面和操作流程往往需要深度的定制。修改这些系统自动生成的页面是很困难的。&lt;/li&gt;
&lt;li&gt;这些系统往往都是只支持单表 CRUD 操作，对多表之间的关联操作支持不够。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以我开发了这套 koala 系统，它的主要特点是：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;只提供后端的 restful api 接口，前端的代码还是需要每个应用自己编写，采用前后端分离架构；&lt;/li&gt;
&lt;li&gt;接口不仅支持常规的单表 CRUD 操作，还自动支持多表关联（一对多、多对多、自引用的树形结构），支持复杂的查询（分页、排序、计数、索引）、支持批量操作、以及拥有一套透明高效的缓存体系。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然，还有一点很重要的，就是定义了一套 restful 的基于约定的接口协议。基于这套约定的协议，前端程序员不需要后端提供繁琐的不一致接口文档，可以轻松上手开始使用这套接口。&lt;/p&gt;
&lt;h2 id="2. Kaola的接口协议约定"&gt;2. Kaola 的接口协议约定&lt;/h2&gt;
&lt;p&gt;Kaola 生成的 Api 接口是基于 http 的 web 接口，URL 的命名基本沿用 rails 框架的命名约定，其中的表名都是复数形式。基本的 CRUD 接口的约定如下：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th style="text-align:left;"&gt;操作&lt;/th&gt;
&lt;th style="text-align:left;"&gt;HTTP Method&lt;/th&gt;
&lt;th style="text-align:left;"&gt;URI&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;获取列表数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;GET&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名 (.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;添加新数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;POST&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名 (.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;修改已有数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;PUT&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名/:id(.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;查看已有数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;GET&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名/:id(.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;删除已有数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;DELETE&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名/:id(.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;这只是对单表资源的 CRUD 操作，koala 针对下列情况也定义了一套的 Restful 规范：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;查询／分页／排序的支持。标准 Restful 接口只有一个列表的接口，对查询相关的功能没有约定，koala 自行扩展了一套约定。&lt;/li&gt;
&lt;li&gt;批量操作的支持。Restful 接口默认只支持单个资源的操作，而实际的业务场景中经常需要有批量操作的需求，比如商品的批量上架、数据的批量删除等。&lt;/li&gt;
&lt;li&gt;有关联的数据表的支持。Restful 只支持单个资源的 CRUD，而实际业务中经常有主子表的级联保存，关联表的查询等。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="查询的约定"&gt;查询的约定&lt;/h3&gt;
&lt;p&gt;Kaola 的查询参数一一对应到数据库中的字段，格式是通过把 json 格式的查询参数扁平化得来的。比如下面的查询条件，&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "s" : {
        gender : 'm',
        "like" : {
            name : 'b'
        }
    },
    "order" : "id asc"
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它的含义是查找所有 gender 等于'm'并且 name 包含'b'的记录，按'id asc'排序，扁平化以后就是：&lt;/p&gt;

&lt;p&gt;s[gender]=m&amp;amp;s[like[name]]=b&amp;amp;order=id asc&lt;/p&gt;

&lt;p&gt;如果表的名字是‘users’，那么请求‘users.json?s[gender]=m&amp;amp;s[like[name]]=b&amp;amp;order=id+asc’就可以得到所有符合条件的 json 格式的数据。&lt;/p&gt;

&lt;p&gt;针对查询／分页／排序／批量操作／关联表操作／导入导出等的具体的约定（前端开发者需要细看）可以参考：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/yuanxinyu/kaola/blob/master/doc/Api.md" rel="nofollow" target="_blank" title=""&gt;kaola api 协议规范&lt;/a&gt;；&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="3. 开始使用Kaola"&gt;3. 开始使用 Kaola&lt;/h2&gt;
&lt;p&gt;Kaola 是基于 ruby on rails 开发的，主要在 Mac 和 Linux 操作系统下完成开发
，数据库使用的是 mysql。如果你的操作系统是 windows，或者数据库不是 mysql，大体上是兼容的，但可能会碰到问题。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;安装 ruby2.2 以上版本，如何安装可参考&lt;a href="https://www.ruby-lang.org/en/documentation/installation/" rel="nofollow" target="_blank" title=""&gt;这个链接&lt;/a&gt;。安装完成 ruby 以后，在命令行运行”gem install bundle”以安装 bundle；&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;下载 koala 的代码，在项目根目录运行“bundle install”安装依赖的第三方库；&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置数据库连接，具体参考&lt;a href="doc/%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6.md" title=""&gt;数据库配置&lt;/a&gt;；&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在项目的根目录运行“./autogen.sh”，自动生成所有的后端 api 代码；&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;启动 api 服务器，在开发环境下就是运行“rails server”&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;然后打开浏览器访问&lt;a href="http://localhost:3000/index2.html" rel="nofollow" target="_blank" title=""&gt;这个链接&lt;/a&gt;就可以看到生成的所有接口了。在开发环境下，koala 除了 api 接口，也提供完整的 CRUD 的 html 页面（其实就是 rails 默认的 scaffold 生成的页面）。在发布环境下，只有接口调用可以访问，基本就是以".json"结尾的 url 访问。&lt;/p&gt;
&lt;h2 id="4. 实现原理"&gt;4. 实现原理&lt;/h2&gt;
&lt;p&gt;扫描数据库实现所有单表的 CRUD 功能接口，这个不难实现。比较难的是怎么处理数据关联关系。数据关联关系主要有三种：一对多，一对一，多对多。Kaola 在实现关联关系的时候，分两个阶段实现的。&lt;/p&gt;
&lt;h3 id="识别一对多关系"&gt;识别一对多关系&lt;/h3&gt;
&lt;p&gt;第一个阶段，在 2016 年项目最初开发的时候，只支持一对多关系。一对一关系是一对多关系的特例，而多对多关系则可以表示为两个一对多关系，所以只支持一对多关系也不算严重的缺陷。&lt;/p&gt;

&lt;p&gt;那么如何自动发现数据库所有的一对多关系呢，主要通过三种方法：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;通过数据库的外键。如果 A 表有一个外键指向 B 表，那么 B 表和 A 表就是一对多关系。&lt;/li&gt;
&lt;li&gt;通过命名约定。外键的命名约定采用 rails 默认的约定，外键的名字都是“表名单数_id”。同时对 rails 的约定有扩展，如果一张表里有多个关联到另外一张表的外键，命名规则是“前缀_表名单数_id”。所有符合命名约定的表，即使没有设置数据库层的外键，也自动建立一对多关系。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;3.通过配置文件。有些遗留的数据库即没有配置外键，也不符合命名约定，那么在配置文件 custom_fk.txt 文件中配置好外键关系也可以。&lt;/p&gt;

&lt;p&gt;关联关系是双向的，对于两个表 table1s 和 table2s，如果 table1 有一个字段 table2_id，那么 table1 是多方，table2 是一方，用 rails 来描述就是：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Table1 belongs_to table2
Table2 has_many  table1s
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="识别多对多关系"&gt;识别多对多关系&lt;/h3&gt;
&lt;p&gt;第二个阶段是 2017 年，kaola 开发完成接近一年的时候，有个项目组提出要多对多关系的支持，在压力下想明白了多对多关系。首先，rails 支持两种多对多关系：直接式的“has_and_belongs_to_many”和间接式的“has_many through”, 对应的例子见下图：&lt;/p&gt;

&lt;p&gt;&lt;img src="http://guides.rubyonrails.org/images/habtm.png" title="" alt="has_and_belongs_to_many"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://guides.rubyonrails.org/images/has_many_through.png" title="" alt="has_many through"&gt;&lt;/p&gt;

&lt;p&gt;Rails 已经不建议使用直接式的多对多关系&lt;a href="http://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many" rel="nofollow" target="_blank" title=""&gt;参考&lt;/a&gt;，Kaola 也不支持这种方式。下面就是自动发现多对多关联的最关键一步：所有包含两个及以上外键（也包括命名约定／配置文件定义的外键）的表自动形成多对多关系。一张表有 2/3/4 个外键，分别会定义 1/2/6 个多对多关系，也就是给定 n 个外键，生产组合 C(2,n) 个多对多关系。&lt;/p&gt;

&lt;p&gt;除了常规的三种关系，还有一种特殊的自引用关系：树形结构。树形结构最常见的例子有组织结构、产品类别等。为了存储树形结构，要求给定的表有一个指向自己的外键。外键值为空的节点是树的根节点。树形结构在 kaola 中被定义为一个指向自己的一对多关系。&lt;/p&gt;
&lt;h3 id="关系的增删改"&gt;关系的增删改&lt;/h3&gt;
&lt;p&gt;识别了这些常见的数据关联关系后，接下来的任务是如何支持对这些关系的增删改查操作。目前对关系数据的增删改还只支持一对多关系，不支持多对多关系。
由于 kaola 采用 json 格式提交数据，所以新增和修改有关系的数据是比较容易的，json 格式很容易支持用嵌套结构来表达一对多关系。比如单个新增的话，提交的是一个 hash 对象&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "表名单数": {id:id, key:value,...}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么对于一次性提交主子表的数据，格式就是&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "主表单数": {id:id, key:value,...},
    "子表复数": [
        {id:id, key:value,...},
        {id:id, key:value,...}
    ],
    其它子表...
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="关系的查询"&gt;关系的查询&lt;/h3&gt;
&lt;p&gt;下一步是如何定义和实现关联关系的查询。Kaola 使用了面向对象语言的“.”操作符来表达主子表的关系，从而可以达到比 sql 语句更简单自然的查询表达式。比如有两张表：公司表 companies 和仓库表 warehouses，一个公司可以有 多个仓库，那么下面的查询&lt;/p&gt;

&lt;p&gt;warehouses.json?s[company.name]='公司 A'&lt;/p&gt;

&lt;p&gt;表示查询所有的 name 为‘公司 A’的公司所有的仓库。对应的 Sql 查询是：&lt;/p&gt;

&lt;p&gt;select * from warehouses join companies on warehouses.company_id = companies.id where companies.name='公司 A'&lt;/p&gt;

&lt;p&gt;关联表的查询支持所有单表查询的功能，包括等于／Like／日期／数值范围／枚举查询。
此外，针对一对多关系，还支持两种特殊的查询：Exists 查询（给定主表是否有子表数据）和树形结构查询（给定数据节点的所有深层嵌套子节点）。&lt;/p&gt;

&lt;p&gt;具体的技术实现细节可以参考：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/yuanxinyu/kaola/blob/master/doc/Tech.md" rel="nofollow" target="_blank" title=""&gt;kaola 技术实现&lt;/a&gt;；&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="5.  案例"&gt;5.  案例&lt;/h2&gt;
&lt;p&gt;Kaola 主要的使用场合是内部 IT 系统的后端，比如各类管理后台、各类信息管理系统（CRM／SCM／ERP／HIS）等。&lt;/p&gt;
&lt;h3 id="单语言案例：供应链系统"&gt;单语言案例：供应链系统&lt;/h3&gt;
&lt;p&gt;Kaola 最初的场景是用在一个供应链系统中。这个系统有几百张表，表间的关联复杂。应用 Kaola 后，整个后端代码量大大减少，所有增删改查的 api 都是自动生成的。最后只有三个文件，几百行代码是需要为供应链的逻辑定制的。这三个文件分别处理各类订单流水号生成逻辑、库存计算逻辑、订单流转逻辑。这些逻辑都是通过 rails 的数据库钩子实现的，不修改自动生成的代码，所以维护也很方便。比如下面就是序列号生成逻辑／订单流转逻辑的模版代码。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ActiveRecord::Base
  before_validation :gen_seq

  def gen_seq
    #序列号生成逻辑
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ActiveRecord::Base
  before_update :order_process

  def order_process
    #订单流转逻辑
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你的团队后端有 ruby 程序员，这是推荐使用 kaola 的方式，增删改查的 api 用 kaola 自动生成，其它的功能通过修改 kaola 的代码实现。&lt;/p&gt;
&lt;h3 id="混合语言后端案例"&gt;混合语言后端案例&lt;/h3&gt;
&lt;p&gt;如果你的团队后端没有 ruby 程序员，也可以使用 kaola 来帮助减少后端的开发工作量。通过采用前后端分离架构，前端开发其实不关心后端的 api 是用什么语言实现的。&lt;/p&gt;

&lt;p&gt;我们在一个健康干预系统项目中采用了混合语言后端的案例。这个项目组都是 java 后端程序员，通过 kaola 来帮助 java 程序员完成增删改查的 api，而健康干预计划／健康报告生成等功能则是独立部署的 java 后端实现的。&lt;/p&gt;
&lt;h2 id="6. 杂项"&gt;6. 杂项&lt;/h2&gt;&lt;h3 id="发布"&gt;发布&lt;/h3&gt;
&lt;p&gt;由于 kaola 提供 api 接口能力太广泛，不能直接暴露在网络上。所以在发布的时候，需要部署在一个 api 网关后面，比如 Netflix/zuul 这样的网关。&lt;/p&gt;
&lt;h3 id="License"&gt;License&lt;/h3&gt;
&lt;p&gt;Kaola 采用 MIT License， &lt;a href="https://opensource.org/licenses/MIT" rel="nofollow" target="_blank"&gt;https://opensource.org/licenses/MIT&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="为何取名Kaola"&gt;为何取名 Kaola&lt;/h3&gt;
&lt;p&gt;俗话说，懒惰是程序员的美德，能够让计算机自动完成的事情，就不要重复劳动了。取名 Kaola 是希望这套系统让程序员可以像考拉一样悠闲，同时 restful 也有宁静的含义，和考拉的形象比较匹配。&lt;/p&gt;
&lt;h3 id="去年的技术分享"&gt;去年的技术分享&lt;/h3&gt;
&lt;p&gt;2016 年在高可用架构社区做的一次技术分享：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&amp;amp;mid=2653548079&amp;amp;idx=1&amp;amp;sn=2377b625db58b2ea93c3ef2d87e4c395&amp;amp;chksm=813a7fb7b64df6a1cd6d1da7ebcc18f958939b7d1226555552dcc098fa64dca9441bc054e567&amp;amp;mpshare=1&amp;amp;scene=1&amp;amp;srcid=0614sXGSoNt6Ui7prVEKq9Ds&amp;amp;key=c32c17d7706c6e263803a518919c45991df530ede512160f78cb7ecd9fcadafb6fed879cdbef2291a467656ef7fe682f7d005c2c6ef1da2a86f4b47ab54c99d576f588f735bbbd40c48e8038f2b3a6a9&amp;amp;ascene=0&amp;amp;uin=MTc3MzI3NTcyMA%3D%3D&amp;amp;devicetype=iMac+MacBook8%2C1+OSX+OSX+10.12.5+build(16F73)&amp;amp;version=12020810&amp;amp;nettype=WIFI&amp;amp;fontScale=100&amp;amp;pass_ticket=kGb2Xo4rtE5Ub9eaFIV%2BhqY9nrNwXuFOvY%2FafkI9B8p9LvU5vtqtOalHZ8EaB4je" rel="nofollow" target="_blank" title=""&gt;kaola 2016 高可用架构社区技术分享&lt;/a&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/yuanxinyu/kaola/blob/master/doc/share%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB.md" title=""&gt;kaola 2016 技术分享&lt;/a&gt;；&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>ylt</author>
      <pubDate>Fri, 21 Jul 2017 00:53:20 +0800</pubDate>
      <link>https://ruby-china.org/topics/33568</link>
      <guid>https://ruby-china.org/topics/33568</guid>
    </item>
    <item>
      <title>一次无后端的供应链系统开发实践 (上篇)： 前后端分离的 Restful 接口设计</title>
      <description>&lt;p&gt;本人在去年底加入了一家医疗保险行业的创业公司‘亿保健康’，公司的技术是基于 Java 体系的。而这次的分享是今年做的一个新的供应链项目。当时项目组缺后端 java 程序员，所以我用 ruby 写了一个自动化的后端。本文分享供应链项目采用的后端自动化技术，该技术极大提升了项目的开发效率。&lt;/p&gt;
&lt;h2 id="1. 缘起"&gt;1. 缘起&lt;/h2&gt;
&lt;p&gt;我们在 2016 年初上线了一个网上药房：&lt;a href="http://www.laobai.com" rel="nofollow" target="_blank" title=""&gt;老白网 laobai.com&lt;/a&gt;。
半年多的时间，老白网的官网销售额在全国自营的网上药房里已经排名前 10 了。电商的后端需要有一套供应链管理系统，但是由于药品的特殊性，药品采购／仓储／物流等需要符合 GSP 规范，导致我们目前外购并同时使用两套供应链系统，一套通用版满足基本的功能需求，一套主要是药品的 GSP 审核的需要，两个系统之间还需要数据交换。此外，这些外购的系统也无法满足我们自己的一些定制化开发的需求。所以就迫切需要自己开发一个满足 GSP 规范的药品行业供应链系统。&lt;/p&gt;

&lt;p&gt;外购的第三方的供应链系统有 1000 多张表，功能上的复杂度主要在数据表多且有关联，对性能／并发上到是没有太多的要求。因为可以参考第三方的数据库结构设计，也可以参考第三方的界面设计，所以数据库／前端都没有太多的不确定性。
当时这个项目的团队情况是有一个靠谱的 DBA，有几个前端开发，但是缺少后端程序员。功能很多，需要开发的人力缺口很大，正是在这种情况下催生了本文提到的技术方案。方案的主要考虑是：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;数据库设计完成后，前端要求可以直接开始开发，不能等后端接口。&lt;/li&gt;
&lt;li&gt;后端的开发工作量要尽可能少，多用代码生成技术，因为没有专职的后端程序员。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="2. REST的优点与不完备性"&gt;2. REST 的优点与不完备性&lt;/h2&gt;
&lt;p&gt;关于前后端之间的接口规范，主要有三种风格：RPC vs REST vs GraphQL。
RPC 风格的接口规范是最传统的，但是不太合适基于 Web 的系统，所以本方案中不予考虑。
GraphQL 调研了一下，它能够满足我们的第一个需求，前端可以定义接口，对后端接口依赖少。但是无法满足第二点，采用 GraphQL 的后端开发工作量很大，测试也麻烦，而且 GraphQL 太新，整个团队都没有使用的经验。
REST 有很多可用的代码生成系统可参考，比如 rails scaffold ／ django admin，不需要从零开始。所以综合考虑还是采用 REST 风格。&lt;/p&gt;

&lt;p&gt;按照 Fielding 博士的说法，REST 只适用于应用软件的架构，不包括操作系统、网络软件和一些仅仅为得到系统支持而使用网络的架构风格 (例如，进程 控制风格)。应用软件代表的是一个系统的“理解业务”(business-aware) 的那部分功能。&lt;/p&gt;

&lt;p&gt;按照 REST 架构设计的 Web API 一般被成为 Restful API。
REST 在数据库类应用中使用广泛。针对数据库中的一张表，Restful 的 api 通过约定来定义好 CRUD 的全部接口 api，不需要前后端之间沟通接口的设计，而且各种语言都有针对单表的 CRUD 后端自动代码生成，能够减少后端的开发工作量。约定大于配置是一个很好的软件工程实践，能够大大减少软件开发的复杂性。下面就是一个 restful 的 api 约定：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th style="text-align:left;"&gt;操作&lt;/th&gt;
&lt;th style="text-align:left;"&gt;HTTP Method&lt;/th&gt;
&lt;th style="text-align:left;"&gt;URI&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;获取列表数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;GET&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名 (.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;添加新数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;POST&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名 (.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;修改已有数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;PUT&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名/:id(.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;查看已有数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;GET&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名/:id(.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;删除已有数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;DELETE&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名/:id(.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;很多知名的软件和框架都采用了类似这套的约定，比如 rails 开发框架／ElasticSearch 搜索等。&lt;/p&gt;

&lt;p&gt;当然针对实际的软件开发需求，REST 的规范还是太简单了。标准的 Restful 接口只有四个动作，CRUD。一个最常见的扩展（或者说是误用），就是使用更多的动作。因为常见的后端开发框架是 controller+action 的模式，一个 controller 对应一个资源，里面配置多个 action 对应前端用户的多个动作。典型的，比如一个帖子，点赞／取消赞是两个动作。然后随着业务的发展，还有锁帖／解锁操作，回复帖子／查看所有 0 回复帖子等等需要。于是就有了下面的这种 url 设计：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /topics/follow
POST /topics/unfollow
POST /topics/lock
POST /topics/unlock
POST /topics/reply
GET /topics/no_reply
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;慢慢的，接口越来越复杂，离原来的 Restful 风格越来越远。当然，有人觉得这种风格也不错。而 DHH 的观点是这种风格 url 需要改造为 Restful 的风格，比如对于点赞的场景，可以认为有一个资源是 topics/follows，然后这个资源有添加和删除两个操作。
关于 DHH 对这种风格的讨论，可参考&lt;a href="http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/" rel="nofollow" target="_blank" title=""&gt;这个链接&lt;/a&gt;，
这里是&lt;a href="http://mp.weixin.qq.com/s?__biz=MzAxNDEyMDI5NA==&amp;amp;mid=453464461&amp;amp;idx=1&amp;amp;sn=57341bf83cef600efb930a134f9ca636" rel="nofollow" target="_blank" title=""&gt;中文翻译版&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;今年的 rubychina 大会上也有一场针对类似问题的分享，可以参考 [这个链接](&lt;a href="https://speakerdeck.com/mechiland/reconsider-rest-chong-gou-jian-da-xing-railsying-yong-de-fang-shi" rel="nofollow" target="_blank"&gt;https://speakerdeck.com/mechiland/reconsider-rest-chong-gou-jian-da-xing-railsying-yong-de-fang-shi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这还只是对单表资源的 CRUD 操作，我们碰到的 Restful 规范主要是缺失下面的一些部分：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;查询／分页／排序的支持。Restful 接口只有一个列表的接口，对查询相关的功能没有约定。&lt;/li&gt;
&lt;li&gt;批量操作的支持。Restful 接口默认只支持单个资源的操作，而实际的业务场景中经常需要有批量操作的需求，比如商品的批量上架、数据的批量删除等。&lt;/li&gt;
&lt;li&gt;有关联的数据表的支持。Restful 只支持单个资源的 CRUD，而实际业务中经常有主子表的级联保存，关联表的查询等。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以本方案主要考虑两点：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;扩展 REST，针对上述三种场景约定好接口规范。目的是让前端程序员只需要知道接口约定，就可以针对所有的数据库表进行接口开发。无需提供详细的接口说明文档。&lt;/li&gt;
&lt;li&gt;如何通过自动代码生成的方式实现这些规范，以减少后端开发的工作量。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="3. Restful扩展：单表批量操作（添加／更新／删除）"&gt;3. Restful 扩展：单表批量操作（添加／更新／删除）&lt;/h2&gt;&lt;h3 id="3.1 批量新增接口"&gt;3.1 批量新增接口&lt;/h3&gt;
&lt;p&gt;REST 规范没有约定如何实现批量操作，也没有说明提交参数和返回值的格式。实践中，elasticsearch 提供了批量操作的入口/_bulk，统一处理所有的批量操作，可以在一次请求里完成索引／更新／删除等多个操作。本方案对批量操作的要求更严格，只支持单表数据的一种操作。对于批量新增接口，我们重用 Restful 的新增接口，只是提交的数据格式不一样。&lt;/p&gt;

&lt;p&gt;单个新增的话，提交的是一个单个的 hash 对象&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "表名单数": {id:id, field:value,...}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;批量新增的话，提交的里层数据是一个数组&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "表名复数": [{id:id1, field:value,...},{id:id2}...]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而且约定在所有的输入输出中，如果是集合对象，名字采用复数形式；如果是单个对象，名字采用单数形式。当然，还有一个约定是所有的主键的字段名都是“id”。请求中的 field 直接对应数据库的列名。如果 id 不传，则由数据库提供自动生成的主键。&lt;/p&gt;
&lt;h3 id="3.2  批量修改接口"&gt;3.2  批量修改接口&lt;/h3&gt;
&lt;p&gt;对于如何定义批量修改接口，有点左右为难。首先，无法按照批量新增的模式重用修改接口，因为 Restful 的单条数据修改接口“PUT   /表名/:id”和单个 id 绑定了。如果严格参考 DHH 的做法，批量修改也需要抽象成一个资源，类似于点赞，而不应该增加一个动作。&lt;/p&gt;

&lt;p&gt;但是我对这个做法不习惯，批量修改我认为还是和 CRUD 一个性质的。目前还是决定增加了一个动作 batch_update，也是整个约定里唯一增加的动作。批量修改接口的 url 地址是“/表名/batch_update.json”，提交的 json 数据格式和批量新增接口一致。以后也有可能统一提供一个类似 elasticsearch 的 bulk 接口。&lt;/p&gt;
&lt;h3 id="3.3 批量删除接口"&gt;3.3 批量删除接口&lt;/h3&gt;
&lt;p&gt;批量删除接口也没有按照 DHH 的说法抽象成资源，而是重用了 Restful 的删除接口，只是 id 的格式不一样。批量删除接口，一次传入多个 id，id 之间以英文逗号“,”分割。&lt;/p&gt;

&lt;p&gt;所以针对批量操作，本方案增加了两个约定如下：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th style="text-align:left;"&gt;操作&lt;/th&gt;
&lt;th style="text-align:left;"&gt;HTTP Method&lt;/th&gt;
&lt;th style="text-align:left;"&gt;URI&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;批量修改数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;POST&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名/batch_update(.:format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:left;"&gt;批量删除数据&lt;/td&gt;
&lt;td style="text-align:left;"&gt;DELETE&lt;/td&gt;
&lt;td style="text-align:left;"&gt;/表名/:id&lt;a href=".:format" title=""&gt;,:id&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;h2 id="4. Restful扩展：单表查询"&gt;4. Restful 扩展：单表查询&lt;/h2&gt;&lt;h2 id="4.1 查询格式"&gt;4.1 查询格式&lt;/h2&gt;
&lt;p&gt;单表的查询，uri 直接重用 rest 规范，但是要约定好查询的参数的传递规范。我们定义了下面这些查询的请求格式&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s[field]=value
s[like[field]]=value
s[date[field]]=value
s[range[field]]=value
s[in[field]]=value
s[cmp[field,field]]=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分别代表精确查询／like 字符串模糊查询／date 日期范围查询／range 范围查询／in 枚举查询／cmp 比较查询。这些查询基本满足了 OLTP 业务的常见需求，报表统计类需求有专门的报表系统。&lt;/p&gt;

&lt;p&gt;如果有多个查询条件，条件之间是逻辑与的关系。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s[field1]=value1&amp;amp;s[like[field2]]=value2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查询的 field 直接对应到数据库的字段。如果 field 有逗号“,”，则表示同时查询多个字段，其中一个满足条件即可，也就是 OR 查询。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"/warehouses.json?s[like[company,address]]=测试"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面这个查询的意思是查找所有 company 包含‘测试’或者 address 包含‘测试’的所有仓库。&lt;/p&gt;

&lt;p&gt;针对 date/range/in 查询，支持 value 中包含逗号“,”。以 range 查询为例，“1,5”代表范围是 1 到 5，",5"代表小于等于 5，"3,"代表大于等于 3。例如：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"/warehouses.json?s[range[id]]=1,5"
"/warehouses.json?s[range[id]]=,5"
"/warehouses.json?s[range[id]]=3,"
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="4.2  分页/排序/Count计数"&gt;4.2  分页/排序/Count 计数&lt;/h3&gt;
&lt;p&gt;分页参数 page／per，排序参数 order，计数参数 count 之间都是可以自由组合的。例如：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"/warehouses.json?page=1"
"/warehouses.json?page=1&amp;amp;per=100"
"/warehouses.json?page=1&amp;amp;order=id+desc"
"/warehouses.json?page=1&amp;amp;per=100&amp;amp;count=1"
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="5. Restful扩展：关联表支持"&gt;5. Restful 扩展：关联表支持&lt;/h2&gt;&lt;h3 id="5.1 外键查询的支持"&gt;5.1 外键查询的支持&lt;/h3&gt;
&lt;p&gt;上一节提到的单表查询规范，其中的 field 字段，增加对特殊符号"."的支持，用于外键查询。这样 Field 可以包含三种类型：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;单个key;
多字段的key，格式："key1,key2,..."
外键的key，格式：“key1.key2”。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中，多字段的 key 的格式表示多个字段的 or 查询，上文已经提及。外键查询要求被查询的表有对应的外键字段。比如仓库属于公司，那么 warehouses 表有一个外键 company_id，company 有 id、name 字段，那么可以有下面的查询：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"warehouses.json?s[company.name]=测试公司"
"warehouses.json?s[range[company.id]]=1,5"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这两个查询的含义是显而易见的。&lt;/p&gt;
&lt;h3 id="5.2 关联表查看"&gt;5.2 关联表查看&lt;/h3&gt;
&lt;p&gt;本系统支持在查看一条数据时，自动带出关联的父表的数据。同时也支持带出给定的几个关联子表数据：传递参数 many=表 1[,表 2]，比如：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET warehouses/1.json?many=stores
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看编号为 1 的仓库的基本信息，同时给出这个仓库下面的所有库位的信息。这个查询要求 stores 表有一个外键指向 warehouses 表。&lt;/p&gt;
&lt;h3 id="5.3 子表的级联新增和删除"&gt;5.3 子表的级联新增和删除&lt;/h3&gt;&lt;h4 id="主子表的级联保存"&gt;主子表的级联保存&lt;/h4&gt;
&lt;p&gt;有很多业务场景需要支持在一个事务里保存主表和关联的多个子表的数据。级联保存的接口和批量保存的接口类似，只是提交的参数有区别。主子表新增的话，提交的数据格式如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "主表单数": {id:id, field:value,...} ,
    "子表复数": [{id:id1, field:value,...},{id:id2,}...]
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="6. 小结"&gt;6. 小结&lt;/h2&gt;
&lt;p&gt;这篇文章主要介绍了项目采用的一种扩展的 restful 接口协议，基于该协议，前端程序员在知道数据库结构的情况下就可以开始项目开发了。由于供应链项目是一类成熟的项目，可以且必须参考已有系统的数据库设计（因为要保证旧系统的数据可以方便的迁移到新系统），所以数据库设计实际上是提前完成的。这和通常的 rails 项目不太相同，常见的 rails 项目数据库结构是在代码中定义的，随着开发的进行不断完善的，而不是提前给定的。&lt;/p&gt;

&lt;p&gt;本文的下篇则会介绍到代码生成技术。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Wed, 07 Dec 2016 11:47:23 +0800</pubDate>
      <link>https://ruby-china.org/topics/31822</link>
      <guid>https://ruby-china.org/topics/31822</guid>
    </item>
    <item>
      <title>Puma 源代码分析 － 完结篇</title>
      <description>&lt;h2 id="puma源代码分析之完结篇"&gt;puma 源代码分析之完结篇&lt;/h2&gt;
&lt;p&gt;这个系列的文章从 puma 的总体结构、启动流程开始分析起，到单进程／集群模式分析、IO 处理、http 协议解析，终于到了最后的完结篇。最后这部分主要讨论两个问题：1.Puma 是如何执行 rack app 来处理客户端请求的；2.运行时 puma server 如何接受和处理命令行工具 pumactl 发过来的请求。&lt;/p&gt;
&lt;h3 id="处理客户端请求"&gt;处理客户端请求&lt;/h3&gt;
&lt;p&gt;Puma 是一个支持 rack 接口的 web 服务器，本节分析 puma 的 rack 接口部分，看看 puma 是如何执行 rack app 并输出 http 响应的。客户端请求的处理由 Server 类的 handle_request 方法完成。其中，req 代表客户端连接，lines 是输出缓冲区。本方法调用 rack app 并把响应写回给客户端。本方法有三种返回值：true/false/:async，分别代表启用 keep_alive, 不启用 keep_alive 和使用 socket-hijacking。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;
  &lt;span class="c1"&gt;# Given the request +env+ from +client+ and a partial request body&lt;/span&gt;
  &lt;span class="c1"&gt;# in +body+, finish reading the body if there is one and invoke&lt;/span&gt;
  &lt;span class="c1"&gt;# the rack app. Then construct the response and write it back to&lt;/span&gt;
  &lt;span class="c1"&gt;# +client+&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# +cl+ is the previously fetched Content-Length header if there&lt;/span&gt;
  &lt;span class="c1"&gt;# was one. This is an optimization to keep from having to look&lt;/span&gt;
  &lt;span class="c1"&gt;# it up again.&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;

    &lt;span class="n"&gt;normalize_env&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;   &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;添加遵循Rack规范的一些env值&lt;/span&gt;

    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;PUMA_SOCKET&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;HIJACK_P&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;HIJACK&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;
    &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;REQUEST_METHOD&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;HEAD&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;RACK_INPUT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;RACK_URL_SCHEME&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;HTTPS_KEY&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="no"&gt;HTTPS&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;HTTP&lt;/span&gt;

    &lt;span class="c1"&gt;# A rack extension. If the app writes #call'ables to this&lt;/span&gt;
    &lt;span class="c1"&gt;# array, we will invoke them when the request is done.&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="n"&gt;after_reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;RACK_AFTER_REPLY&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@app.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;这里调用了rack&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ss"&gt;:async&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hijacked&lt;/span&gt;

        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
          &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;res_body&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"async response must have empty headers and body"&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;

          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ss"&gt;:async&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="vi"&gt;@events.unknown_error&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Rack app"&lt;/span&gt;

        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lowlevel_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;content_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
      &lt;span class="n"&gt;no_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res_body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kind_of?&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;res_body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;content_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;res_body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;cork_socket&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;

      &lt;span class="n"&gt;line_ending&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;LINE_END&lt;/span&gt;
      &lt;span class="n"&gt;colon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;COLON&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;HTTP_VERSION&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;HTTP_11&lt;/span&gt;
        &lt;span class="n"&gt;allow_chunked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;keep_alive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;HTTP_CONNECTION&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;CLOSE&lt;/span&gt;
        &lt;span class="n"&gt;include_keepalive_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;

        &lt;span class="c1"&gt;# An optimization. The most common response is 200, so we can&lt;/span&gt;
        &lt;span class="c1"&gt;# reply with the proper 200 status without having to compute&lt;/span&gt;
        &lt;span class="c1"&gt;# the response header.&lt;/span&gt;
        &lt;span class="c1"&gt;#&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
          &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;HTTP_11_200&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt; &lt;span class="s2"&gt;"HTTP/1.1 "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="n"&gt;fetch_status_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;line_ending&lt;/span&gt;

          &lt;span class="n"&gt;no_body&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;STATUS_WITH_NO_ENTITY_BODY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="o"&gt;......&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;response_hijack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;

      &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;CONTENT_LENGTH2&lt;/span&gt;
          &lt;span class="n"&gt;content_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt;
          &lt;span class="k"&gt;next&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;TRANSFER_ENCODING&lt;/span&gt;
          &lt;span class="n"&gt;allow_chunked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
          &lt;span class="n"&gt;content_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;HIJACK&lt;/span&gt;
          &lt;span class="n"&gt;response_hijack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt;
          &lt;span class="k"&gt;next&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NEWLINE&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line_ending&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line_ending&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;no_body&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content_length&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;204&lt;/span&gt;
          &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt; &lt;span class="no"&gt;CONTENT_LENGTH_S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line_ending&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;line_ending&lt;/span&gt;
        &lt;span class="n"&gt;fast_write&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;keep_alive&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;include_keepalive_header&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;CONNECTION_KEEP_ALIVE&lt;/span&gt;
      &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;keep_alive&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;CONNECTION_CLOSE&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;response_hijack&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content_length&lt;/span&gt;
          &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt; &lt;span class="no"&gt;CONTENT_LENGTH_S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line_ending&lt;/span&gt;
          &lt;span class="n"&gt;chunked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;allow_chunked&lt;/span&gt;
          &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;TRANSFER_ENCODING_CHUNKED&lt;/span&gt;
          &lt;span class="n"&gt;chunked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;line_ending&lt;/span&gt;

      &lt;span class="n"&gt;fast_write&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response_hijack&lt;/span&gt;
        &lt;span class="n"&gt;response_hijack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ss"&gt;:async&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="n"&gt;res_body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunked&lt;/span&gt;
            &lt;span class="n"&gt;fast_write&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;fast_write&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line_ending&lt;/span&gt;
            &lt;span class="n"&gt;fast_write&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;
            &lt;span class="n"&gt;fast_write&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line_ending&lt;/span&gt;
          &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;fast_write&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;

          &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunked&lt;/span&gt;
          &lt;span class="n"&gt;fast_write&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CLOSE_CHUNKED&lt;/span&gt;
          &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;SystemCallError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;IOError&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ConnectionError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Connection error detected during write"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;ensure&lt;/span&gt;
      &lt;span class="n"&gt;uncork_socket&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;

      &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
      &lt;span class="n"&gt;res_body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res_body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt; &lt;span class="ss"&gt;:close&lt;/span&gt;

      &lt;span class="n"&gt;after_reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;keep_alive&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;整个方法很长，但是逻辑不复杂。主要就是根据 rack app 的调用情况和 keep_alive/chunked/hijiack 等 http 选项，输出 http 响应的状态码／响应头部／响应体。其中最核心的执行 rack app 的代码只有一行：&lt;code&gt;status, headers, res_body = @app.call(env)&lt;/code&gt;。此外，normalize_env 方法用于增加和修正一些 Rack 的 env 变量，代码如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Given a Hash +env+ for the request read from +client+, add&lt;/span&gt;
&lt;span class="c1"&gt;# and fixup keys to comply with Rack's env guidelines.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;HTTP_HOST&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;colon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;":"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;SERVER_NAME&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colon&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;SERVER_PORT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;colon&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;SERVER_NAME&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;
      &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;SERVER_PORT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;default_server_port&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;SERVER_NAME&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;LOCALHOST&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;SERVER_PORT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;default_server_port&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;REQUEST_PATH&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# it might be a dumbass full host request header&lt;/span&gt;
    &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;REQUEST_URI&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;REQUEST_PATH&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;

    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"No REQUEST PATH"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;REQUEST_PATH&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;PATH_INFO&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;REQUEST_PATH&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# From http://www.ietf.org/rfc/rfc3875 :&lt;/span&gt;
  &lt;span class="c1"&gt;# "Script authors should be aware that the REMOTE_ADDR and&lt;/span&gt;
  &lt;span class="c1"&gt;# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)&lt;/span&gt;
  &lt;span class="c1"&gt;# may not identify the ultimate source of the request.&lt;/span&gt;
  &lt;span class="c1"&gt;# They identify the client for the immediate request to the&lt;/span&gt;
  &lt;span class="c1"&gt;# server; that client may be a proxy, gateway, or other&lt;/span&gt;
  &lt;span class="c1"&gt;# intermediary acting on behalf of the actual source client."&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;

  &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;REMOTE_ADDR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peeraddr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;

    &lt;span class="c1"&gt;# Set unix socket addrs to localhost&lt;/span&gt;
    &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;

    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;REMOTE_ADDR&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后看看处理客户端请求的循环的 process_client 方法。这个方法的核心是调用 handle_request 方法，然后根据返回值决定下一步的处理。如果 handle_request 返回 false，也就是没有 keep_alive，那么本方法执行完立刻关闭客户端连接；如果返回 true，那么就是 keep_alive，就会循环调用 handle_request，此时会重用同一个客户端连接处理多个 http 请求；如果 handle_request 返回 async，那么就是 socket hijack 模式，此时由 rack app 来直接处理 socket 连接，puma 也不关闭客户端 socket。关于 hijacking 的说明可以参考这篇文章&lt;a href="http://old.blog.phusion.nl/2013/01/23/the-new-rack-socket-hijacking-api/" rel="nofollow" target="_blank" title=""&gt;rack socket hijacking&lt;/a&gt;。Socket hijack 模式可以用来实现 WebSocket 功能。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Given a connection on +client+, handle the incoming requests.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# This method support HTTP Keep-Alive so it may, depending on if the client&lt;/span&gt;
&lt;span class="c1"&gt;# indicates that it supports keep alive, wait for another request before&lt;/span&gt;
&lt;span class="c1"&gt;# returning.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;close_socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;handle_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:async&lt;/span&gt;
        &lt;span class="n"&gt;close_socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@queue_requests&lt;/span&gt;
        &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;

        &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;close_socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
          &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_timeout&lt;/span&gt; &lt;span class="vi"&gt;@persistent_timeout&lt;/span&gt;
          &lt;span class="vi"&gt;@reactor.add&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# The client disconnected while we were reading data&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ConnectionError&lt;/span&gt;
    &lt;span class="c1"&gt;# Swallow them. The ensure tries to close +client+ down&lt;/span&gt;

  &lt;span class="c1"&gt;# The client doesn't know HTTP well&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;HttpParserError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_400&lt;/span&gt;

    &lt;span class="vi"&gt;@events.parse_error&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;

  &lt;span class="c1"&gt;# Server error&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_500&lt;/span&gt;

    &lt;span class="vi"&gt;@events.unknown_error&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Read"&lt;/span&gt;

  &lt;span class="k"&gt;ensure&lt;/span&gt;
    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;

    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;close_socket&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SystemCallError&lt;/span&gt;
      &lt;span class="c1"&gt;# Already closed&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="vi"&gt;@events.unknown_error&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Client"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此把 puma 处理 rack app 的流程总体上过了一遍。&lt;/p&gt;
&lt;h3 id="pumactl如何控制puma的运行状态"&gt;pumactl 如何控制 puma 的运行状态&lt;/h3&gt;
&lt;p&gt;Pumactl 是一个命令行工具，用来控制 puma 的运行状态。Pumactl 可以通过两种手段来控制 puma，一个是 pid，另一个是 control_url。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/usr/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puma/control_cli'&lt;/span&gt;
&lt;span class="n"&gt;cli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ControlCLI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;
&lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControlCLI&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"start"&lt;/span&gt;
    &lt;span class="n"&gt;prepare_configuration&lt;/span&gt;
    &lt;span class="vi"&gt;@options.has_key&lt;/span&gt;&lt;span class="p"&gt;?(&lt;/span&gt;&lt;span class="ss"&gt;:control_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;send_request&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;send_signal&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们先看通过 pid 控制 puma 的运行，这个方式简单一些，但是功能也受限。从代码中可以看到，通过 pid 控制 puma 其实使用的是标准的 unix 信号机制。但是仅使用进程 pid 无法支持 stats 和 reload-worker-directory 命令。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_signal&lt;/span&gt;
  &lt;span class="o"&gt;......&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"restart"&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kill&lt;/span&gt; &lt;span class="s2"&gt;"SIGUSR2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;

  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"halt"&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kill&lt;/span&gt; &lt;span class="s2"&gt;"QUIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;

  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"stop"&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kill&lt;/span&gt; &lt;span class="s2"&gt;"SIGTERM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;

  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"stats"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Stats not available via pid only"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;

  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"reload-worker-directory"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"reload-worker-directory not available via pid only"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;

  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"phased-restart"&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kill&lt;/span&gt; &lt;span class="s2"&gt;"SIGUSR1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;

  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="s2"&gt;"Puma is started"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="s2"&gt;"Command &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; sent success"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要使用 control_url 来控制 puma，必须在启动 puma 的时候提供 control 相关的参数，比如：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;puma &lt;span class="nt"&gt;--control&lt;/span&gt; tcp://127.0.0.1:9293 &lt;span class="nt"&gt;--control-token&lt;/span&gt; foo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后，在启动 puma 的时候，会根据配置的 control 参数启动一个单独的 puma server，并执行一个单独的 rack app。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Runner&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_control&lt;/span&gt;
     &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:control_url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
     &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puma/app/status'&lt;/span&gt;
     &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;
     &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;App&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="vi"&gt;@cli&lt;/span&gt;    &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;响应control请求时执行的rack&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:control_auth_token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
       &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auth_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:none&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;

     &lt;span class="n"&gt;control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@cli.events&lt;/span&gt;  &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;启动一个独立的puma&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;并执行内置的app&lt;/span&gt;
     &lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
     &lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

     &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheme&lt;/span&gt;
     &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
       &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"* Starting control server on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
       &lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_tcp_listener&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;
     &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"unix"&lt;/span&gt;
       &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"* Starting control server on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
       &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

       &lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_unix_listener&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
     &lt;span class="k"&gt;else&lt;/span&gt;
       &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"Invalid control URI: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;

     &lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;
     &lt;span class="vi"&gt;@control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;control&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从代码中可以看出，如果使用 control_url 机制，会启动一个 puma server，并执行 Puma::App::Status 这个 app。也就是说，control_url 机制下的 puma 进程会监听两个不同的端口，一个执行启动者提供的 rack app，另一个执行自己内部的 Status 这个 app。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Status&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rack_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Invalid auth token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'text/plain'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'PATH_INFO'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;/\/stop$/&lt;/span&gt;
      &lt;span class="vi"&gt;@cli.stop&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rack_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OK_STATUS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;/\/halt$/&lt;/span&gt;
      &lt;span class="vi"&gt;@cli.halt&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rack_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OK_STATUS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;/\/restart$/&lt;/span&gt;
      &lt;span class="vi"&gt;@cli.restart&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rack_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OK_STATUS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;/\/phased-restart$/&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="vi"&gt;@cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;phased_restart&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rack_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'{ "error": "phased restart not available" }'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rack_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OK_STATUS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;/\/reload-worker-directory$/&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="vi"&gt;@cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload_worker_directory&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rack_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'{ "error": "reload_worker_directory not available" }'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rack_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OK_STATUS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;/\/stats$/&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rack_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@cli.stats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;rack_response&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unsupported action"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'text/plain'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，Status 是一个标准的 rack app 实现，提供一个 call 方法，接受一个 env 参数并返回 rack_response。下面看一下 stats 的实现：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Runner&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stats&lt;/span&gt;
     &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@server.backlog&lt;/span&gt;
     &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@server.running&lt;/span&gt;
     &lt;span class="sx"&gt;%Q!{ "backlog": &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt;, "running": &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt; }!&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backlog&lt;/span&gt;
     &lt;span class="vi"&gt;@thread_pool&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="vi"&gt;@thread_pool.backlog&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;running&lt;/span&gt;
     &lt;span class="vi"&gt;@thread_pool&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="vi"&gt;@thread_pool.spawned&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ThreadPool&lt;/span&gt;  
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backlog&lt;/span&gt;
   &lt;span class="vi"&gt;@mutex.synchronize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="vi"&gt;@todo.size&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，backlog 是线程池中等待执行的任务的个数，running 指当前线程池中的线程数量。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Wed, 25 Mar 2015 23:33:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/24837</link>
      <guid>https://ruby-china.org/topics/24837</guid>
    </item>
    <item>
      <title>Puma 源代码分析 － HTTP 协议解析</title>
      <description>&lt;h2 id="puma的http协议解析"&gt;puma 的 http 协议解析&lt;/h2&gt;&lt;h3 id="总览"&gt;总览&lt;/h3&gt;
&lt;p&gt;对一个 web 服务器来说，http 协议解析模块是性能关键的部分。为了性能考虑，puma 的 http 协议解析器是用 C 语言写 ruby 扩展实现的。这部分代码原来是 Zed A. Shaw 为 Mongrel 所写，后来被移植到了 Unicorn、Thin 和 Puma。&lt;/p&gt;

&lt;p&gt;上一节分析 socket 的时候已经看到，当客户端连接的 socket 有数据可读的时候，解析 http 协议部分的代码是&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puma/puma_http11'&lt;/span&gt;

&lt;span class="vi"&gt;@parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HttpParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="vi"&gt;@parsed_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@parser.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@parsed_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@parser.finished&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里 parser 相关的方法就是 C 语言实现的。其涉及到的关键文件有：http11_parser.rl, http11_parser.h, http11_parser.c, puma_http11.c。其中 http11_parser.c 其实是由 http11_parser.rl 生成的。这里使用到了一个工具 Ragel，网址&lt;a href="http://www.colm.net/open-source/ragel/" rel="nofollow" target="_blank" title=""&gt;ragel&lt;/a&gt;。它是一个类似 lex 的正则表达式分析器生成工具。在 puma 中使用了 ragel 的格式描述 http 协议。这一章的代码可能是最难懂的，因为涉及到了 ragel/c/ruby 扩展等很多知识点。&lt;/p&gt;
&lt;h3 id="http协议解析的接口"&gt;http 协议解析的接口&lt;/h3&gt;
&lt;p&gt;Http 协议解析的关键部分是理解 puma_parser 结构，这是 ragel／c／ruby 之间进行数据交换的关键结构。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;               &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;ragel&lt;/span&gt;&lt;span class="err"&gt;解析的当前状态&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;body_start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="err"&gt;开始&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;content_len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="err"&gt;长度&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;nread&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="err"&gt;＃已读&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;field_start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;字段开始&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;field_len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;字段长度&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;query_start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="err"&gt;开始&lt;/span&gt;

  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="err"&gt;对象&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="err"&gt;的&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="err"&gt;表示&lt;/span&gt;
  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="err"&gt;对象&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="err"&gt;的&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="err"&gt;表示&lt;/span&gt;

  &lt;span class="n"&gt;field_cb&lt;/span&gt; &lt;span class="n"&gt;http_field&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="err"&gt;＃解析时碰到&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;的&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="err"&gt;时的回调&lt;/span&gt;
  &lt;span class="n"&gt;element_cb&lt;/span&gt; &lt;span class="n"&gt;request_method&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="err"&gt;＃解析时碰到&lt;/span&gt;&lt;span class="n"&gt;request_method&lt;/span&gt;&lt;span class="err"&gt;时的回调&lt;/span&gt;
  &lt;span class="n"&gt;element_cb&lt;/span&gt; &lt;span class="n"&gt;request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;element_cb&lt;/span&gt; &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;element_cb&lt;/span&gt; &lt;span class="n"&gt;request_path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;element_cb&lt;/span&gt; &lt;span class="n"&gt;query_string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;element_cb&lt;/span&gt; &lt;span class="n"&gt;http_version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;element_cb&lt;/span&gt; &lt;span class="n"&gt;header_done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;BUFFER_LEN&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="nf"&gt;void&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;element_cb&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="nf"&gt;void&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;field_cb&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;flen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;vlen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该结构首先被 ragel 使用，其中的 cs 是 ragel 预定义的一个变量，代表当前状态。当 ragel 分析输入时，碰到符合的规则会执行对应的动作（action）。动作中会给 field_start／field_len 之类的变量赋值，也会调用 http_field 等回调。而这些回调则是 ruby 的 c 扩展在其 init 方法中注入的。&lt;/p&gt;
&lt;h3 id="http协议的ragel描述"&gt;http 协议的 ragel 描述&lt;/h3&gt;
&lt;p&gt;先看看 ragel 描述的 http 协议的部分代码：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%%{

  machine puma_parser_common;

  CRLF = "\r\n";

＃ character types
  CTL = (cntrl | 127);
  safe = ("$" | "-" | "_" | ".");
  extra = ("!" | "*" | "'" | "(" | ")" | ",");
  reserved = (";" | "/" | "?" | ":" | "@" | "&amp;amp;" | "=" | "+");
  unsafe = (CTL | " " | "\"" | "#" | "%" | "&amp;lt;" | "&amp;gt;");
  national = any -- (alpha | digit | reserved | extra | safe | unsafe);
  unreserved = (alpha | digit | safe | extra | national);
  escape = ("%" xdigit xdigit);
  uchar = (unreserved | escape);
  pchar = (uchar | ":" | "@" | "&amp;amp;" | "=" | "+");
  tspecials = ("(" | ")" | "&amp;lt;" | "&amp;gt;" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");

＃ elements
  token = (ascii -- (CTL | tspecials));

＃ URI schemes and absolute paths
  scheme = ( alpha | digit | "+" | "-" | "." )* ;
  absolute_uri = (scheme ":" (uchar | reserved )*);

  path = ( pchar+ ( "/" pchar* )* ) ;
  query = ( uchar | reserved )* %query_string ;
  param = ( pchar | "/" )* ;
  params = ( param ( ";" param )* ) ;
  rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?;
  absolute_path = ( "/"+ rel_path );

  Request_URI = ( "*" | absolute_uri | absolute_path ) &amp;gt;mark %request_uri;
  Fragment = ( uchar | reserved )* &amp;gt;mark %fragment;
  Method = ( upper | digit | safe ){1,20} &amp;gt;mark %request_method;

  http_number = ( digit+ "." digit+ ) ;
  HTTP_Version = ( "HTTP/" http_number ) &amp;gt;mark %http_version ;
  Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;

  field_name = ( token -- ":" )+ &amp;gt;start_field $snake_upcase_field %write_field;

  field_value = any* &amp;gt;start_value %write_value;

  message_header = field_name ":" " "* field_value :&amp;gt; CRLF;

  Request = Request_Line ( message_header )* ( CRLF @done );

main := Request;

}%%

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中的 machine 代表一个有限状态机器，它使用 ragel 定制化的正则表达式来表述，然后包含一些 action 的描述。以&lt;code&gt;field_name = ( token -- ":" )+ &amp;gt;start_field $snake_upcase_field %write_field;&lt;/code&gt;为例，这一行的意思是 field_name 是任意的 token 但是不包含":"，这里的--就是 ragel 自定义的操作符，表示“Strong Difference”。这一行后面的三个字段则是 ragel 的用户动作（User Action）。其中&amp;gt;是进入动作（Enter Action）, $是全部转换动作 (All Transition Action), %是离开动作（Leave Action）。所以这个规则匹配的开始会执行 start_field，中间每个字符都会执行 snake_upcase_field，匹配完成执行 write_field。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  machine puma_parser;
  action start_field { MARK(field_start, fpc); }
  action snake_upcase_field { snake_upcase_char((char *)fpc); }
  action write_field { 
    parser-&amp;gt;field_len = LEN(field_start, fpc);
  }

  action start_value { MARK(mark, fpc); }
  action write_value {
    parser-&amp;gt;http_field(parser, PTR_TO(field_start), parser-&amp;gt;field_len, PTR_TO(mark), LEN(mark, fpc));
  }
  action request_method { 
    parser-&amp;gt;request_method(parser, PTR_TO(mark), LEN(mark, fpc));
  }

  include puma_parser_common "http11_parser_common.rl";

＃define LEN(AT, FPC) (FPC - buffer - parser-&amp;gt;AT)
＃define MARK(M,FPC) (parser-&amp;gt;M = (FPC) - buffer)  

static void snake_upcase_char(char *c)
{
    if (*c &amp;gt;= 'a' &amp;amp;&amp;amp; *c &amp;lt;= 'z')
      *c &amp;amp;= ~0x20;
    else if (*c == '-')
      *c = '_';
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 ragel 的 action 里执行的是一些 c 函数。可以看到前面提到的 start_field／write_field 动作其实都是设置了 puma_parser 这个结构里一些字段，而 request_method 动作则会执行 puma_parser 里设置好的回调，这里的代码并不关心回调函数是什么。代码中的 fpc 是 ragel 预定义的变量，是指向当前字符的指针。&lt;/p&gt;
&lt;h3 id="http协议的解析"&gt;http 协议的解析&lt;/h3&gt;
&lt;p&gt;Http 协议的解析需要实现的接口函数如下：&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_finish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_has_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_is_finished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="n"&gt;puma_parser_nread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nread&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先是初始化函数，这个函数很简单，主要就是初始化 puma_parser 结构体。其中的&lt;code&gt;%% write init;&lt;/code&gt;是 ragel 提供的初始化语句，在生成的 c 代码中会被替换为 ragel 的初始化代码。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;%%&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;body_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;content_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;field_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;field_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实现 http 协议解析的代码也很简单，真正的工作都是由 ragel 包办了，也就是&lt;code&gt;%% write exec&lt;/code&gt;这一行。其中的 p/pe/cs 则都是 ragel 要求的预定义变量，分别代表"data pointer", "data end pointer", "current state"。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;pe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="o"&gt;%%&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;puma_parser_has_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nread&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nread&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后是判断解析是否完成，是否有错误等的实现代码，也都比较简单。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_finish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser_has_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser_is_finished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_has_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;puma_parser_error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;puma_parser_is_finished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;puma_parser_first_final&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="ruby对象的c接口"&gt;ruby 对象的 c 接口&lt;/h3&gt;
&lt;p&gt;最后看看如何在 ruby 中使用上面提供的 c 语言的 http 解析代码。关于如何编写 ruby 的 c 语言扩展，可以看看这个系列的&lt;a href="http://clalance.blogspot.com/2011/01/writing-ruby-extensions-in-c-part-1.html" rel="nofollow" target="_blank" title=""&gt;文章&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;首先来看看入口函数，它首先定义了模块 Puma 和类 HttpParser，然后定义了一些 ruby 中的全局的变量如 request_method 等，然后给 HttpParser 对象定义了一些方法如 new/execute/finish 等。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Init_puma_http11&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;mPuma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_define_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Puma"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;cHttpParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_define_class_under&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mPuma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"HttpParser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_cObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;DEF_GLOBAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"REQUEST_METHOD"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;DEF_GLOBAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"REQUEST_URI"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;DEF_GLOBAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"FRAGMENT"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;DEF_GLOBAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"QUERY_STRING"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;DEF_GLOBAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"HTTP_VERSION"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;DEF_GLOBAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"REQUEST_PATH"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;eHttpParserError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_define_class_under&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mPuma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"HttpParserError"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rb_eIOError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_global_variable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;eHttpParserError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;rb_define_alloc_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cHttpParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_alloc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cHttpParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"initialize"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_init&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cHttpParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"reset"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_reset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cHttpParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"finish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_finish&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cHttpParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"execute"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_execute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cHttpParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_has_error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cHttpParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"finished?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_is_finished&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cHttpParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"nread"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_nread&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cHttpParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;init_common_fields&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="n"&gt;Init_io_buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mPuma&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Init_mini_ssl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mPuma&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="nf"&gt;DEF_GLOBAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="n"&gt;global_&lt;/span&gt;&lt;span class="err"&gt;##&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_str_new2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;rb_global_variable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;global_&lt;/span&gt;&lt;span class="err"&gt;##&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们看看 HttpParser 的初始化实现，也就是在执行 ruby 代码&lt;code&gt;HttpParser.new&lt;/code&gt;时，底层到底发生了什么。可以看到，当执行 new 的时候，先给 puma_parser 结构体分配了内存，然后设置了一些回调函数。前面提高的 ragel 的动作执行的时候，会执行这里设置的回调。ALLOC_N 是 ruby 提供的内存分配函数，相比于 malloc 直接分配，ALLOC_N 的功能更强，比如在内存不够的时候它会尝试先执行一次垃圾收集然后再分配内存。ALLOC_N 分配出去的内存必须使用 xfree 回收。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="nf"&gt;HttpParser_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ALLOC_N&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;TRACE&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;http_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http_field&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request_method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request_method&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request_uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;fragment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request_path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;query_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;http_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http_version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;header_done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;header_done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;puma_parser_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Data_Wrap_Struct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_mark&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpParser_free&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后看看实际执行 http 协议解析的函数，这里的关键部分还是调用前面提到的 puma_parser_execute 函数，其它代码都是为了提供给 ruby 层使用而增加的封装。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="nf"&gt;HttpParser_execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;req_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;dlen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;DATA_GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FIX2INT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;dptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_extract_chars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;dlen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;dlen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;rb_free_chars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dptr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rb_raise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eHttpParserError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Requested start is after data buffer end."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;puma_parser_execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dlen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;rb_free_chars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dptr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;VALIDATE_MAX_LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser_nread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;HEADER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser_has_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;rb_raise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eHttpParserError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Invalid HTTP format, parsing fails."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;INT2FIX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser_nread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="nf"&gt;HttpParser_is_finished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;puma_parser&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;DATA_GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;puma_parser_is_finished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Qtrue&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Qfalse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后看看一些 ragel 回调的实现。函数 http_field 就是把解析出来的 field 和 value 对设置到 ruby 的 hash 中。前面看到的 ragel 代码&lt;code&gt;action write_value {parser-&amp;gt;http_field(......)}&lt;/code&gt;会调用这里的 c 代码。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;http_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;flen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;vlen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;VALIDATE_MAX_LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FIELD_NAME&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;VALIDATE_MAX_LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vlen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FIELD_VALUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_str_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vlen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_common_field_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/*
     * We got a strange header that we don't have a memoized value for.
     * Fallback to creating a new string to use as a hash key.
     */&lt;/span&gt;

    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;new_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HTTP_PREFIX_LEN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;flen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;BUFFER_LEN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTP_PREFIX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTP_PREFIX_LEN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;HTTP_PREFIX_LEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_str_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;rb_hash_aset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其它一些 ragel 回调则都很简单，比如函数 request_method 则设置了 ruby 中 global_request_method 的值。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;request_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_str_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_hash_aset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;global_request_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;request_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;VALIDATE_MAX_LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REQUEST_PATH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_str_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_hash_aset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;global_request_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;query_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;puma_parser&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;VALIDATE_MAX_LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QUERY_STRING&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_str_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_hash_aset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;global_query_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 ruby 中通过调用 HttpParser.execute 解析输入的字符流，通过 parser.finished？判断是否解析完成，如果 ragel 的状态是 puma_parser_first_final，那么代表解析完成。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Sun, 15 Mar 2015 00:44:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/24654</link>
      <guid>https://ruby-china.org/topics/24654</guid>
    </item>
    <item>
      <title>Puma 源代码分析 － IO 处理</title>
      <description>&lt;h2 id="puma的IO处理分析"&gt;puma 的 IO 处理分析&lt;/h2&gt;&lt;h3 id="Puma的IO总体架构"&gt;Puma 的 IO 总体架构&lt;/h3&gt;
&lt;p&gt;总体来看，puma 的 io 处理有三个循环：1. Server 里处理 Socket 连接建立的循环；2. Reactor 里处理连接就绪的循环；3. 线程池里处理就绪任务的循环。&lt;/p&gt;

&lt;p&gt;第一步：
所有的网络服务器，接入部分都是一个建立连接的循环。Puma 在接受到客户端的连接请求后，就初始化一个 Client 对象，并将其加入线程池。&lt;/p&gt;

&lt;p&gt;第二步：
所有建立的连接，如果还没有就绪，就加入到 Reactor 里等待其就绪。如果 Reactor 里有一个连接就绪，那么就把这个连接加入到线程池。&lt;/p&gt;

&lt;p&gt;第三步：
线程池不断从任务队列中取出任务，执行它。如果因为 io 还未就绪的原因导致任务无法执行，就把这个任务（Client 对象）再次加入到 Reactor 里。&lt;/p&gt;

&lt;p&gt;这三个循环中，前面两个都是通过 pipe 接受外部控制的，第三个循环通过改变其&lt;a href="/shutdown" class="user-mention" title="@shutdown"&gt;&lt;i&gt;@&lt;/i&gt;shutdown&lt;/a&gt;标志和&lt;a href="/todo" class="user-mention" title="@todo"&gt;&lt;i&gt;@&lt;/i&gt;todo&lt;/a&gt;任务队列可以让其退出。&lt;/p&gt;
&lt;h3 id="Reactor与Proactor两种模式"&gt;Reactor 与 Proactor 两种模式&lt;/h3&gt;
&lt;p&gt;Reactor 与 Proactor 是两种典型 io 事件处理模式。这两种模式都是让 io 数据的处理者只需要专心处理业务，而 io 事件的监听与通知则交给独立的第三方（一般称为事件分离者）。Reactor 模式是基于同步 I/O 的，而 Proactor 模式是和异步 I/O 相关的。&lt;/p&gt;

&lt;p&gt;在 Reactor 模式中，事件分离者等待某个事件或者可应用或个操作的状态发生（比如文件描述符可读写，或者是 socket 可读写），事件分离者就把这个事件传给事先注册的事件处理者（回调函数），由后者来做实际的读写操作。&lt;/p&gt;

&lt;p&gt;而在 Proactor 模式中，事件处理者 (或者代由事件分离者发起) 直接发起一个异步读写操作 (相当于请求)，而实际的工作是由操作系统来完成的。发起时，需要提供的参数包括用于存放读到数据的缓存区，读的数据大小，或者用于存放外发数据的缓存区，以及这个请求完后的回调函数等信息。事件分离者得知了这个请求，它默默等待这个请求的完成，然后转发完成事件给相应的事件处理者或者回调。&lt;/p&gt;

&lt;p&gt;Reactor 模式中，实际的 io 读写还是需要事件处理者完成，而 Proactor 模式中，事件处理者只是接收 io 完成的通知，实际的 io 已经由操作系统完成了。这里针对 Reactor 与 Proactor 的讨论比较抽象，下面的分析中看到具体的代码的时候会清楚一些。&lt;/p&gt;

&lt;p&gt;Puma 的 io 处理采用的是 Reactor 模式。&lt;/p&gt;
&lt;h3 id="Socket连接建立"&gt;Socket 连接建立&lt;/h3&gt;
&lt;p&gt;先来看 Socket 连接建立的代码。前面提到，为了控制和退出这个循环，使用了 pipe 机制。所以整个循环其实有两块代码，处理 pipe 的代码和处理 socket 连接建立的代码。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stdio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="vi"&gt;@app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="vi"&gt;@check&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@notify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;
    &lt;span class="o"&gt;......&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_servers&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@check&lt;/span&gt;
      &lt;span class="n"&gt;sockets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="vi"&gt;@binder.ios&lt;/span&gt;   &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;同时监听pipe和连接端口&lt;/span&gt;
      &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@thread_pool&lt;/span&gt;
      &lt;span class="n"&gt;queue_requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@queue_requests&lt;/span&gt;

      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;
        &lt;span class="k"&gt;begin&lt;/span&gt;
          &lt;span class="n"&gt;ios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="n"&gt;sockets&lt;/span&gt;         &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;核心调用&lt;/span&gt;
          &lt;span class="n"&gt;ios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;              &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;处理pipe的事件&lt;/span&gt;
              &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;handle_check&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
              &lt;span class="k"&gt;begin&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept_nonblock&lt;/span&gt;      &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;处理连接的建立&lt;/span&gt;
                  &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@binder.env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                  &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
                  &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_until_not_full&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;queue_requests&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;
              &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;SystemCallError&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ECONNABORTED&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="vi"&gt;@events.fire&lt;/span&gt; &lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt;
      &lt;span class="n"&gt;graceful_shutdown&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:stop&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;queue_requests&lt;/span&gt;
        &lt;span class="vi"&gt;@reactor.clear&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;
        &lt;span class="vi"&gt;@reactor.shutdown&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="o"&gt;......&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@events.fire&lt;/span&gt; &lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:done&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;整个循环中最核心的是 select(2) 系统调用。要理解这块代码，先要熟悉 select 方法，这里先把 select 的说明整体摘抄下来：select monitors given arrays of IO objects, waits one or more of IO objects ready for reading, are ready for writing, and have pending exceptions respectively, and returns an array that contains arrays of those IO objects. It will return nil if optional timeout value is given and no IO object is ready in timeout seconds。&lt;a href="http://ruby-doc.org/core-2.2.1/IO.html#method-c-select" rel="nofollow" target="_blank" title=""&gt;文档在这里&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;我们来分析这一行代码：&lt;code&gt;ios = IO.select sockets&lt;/code&gt;。IO.select 一共有四个参数，其中 sockets 是 select 的第一个参数，代表需要等待可读的 IO 对象的数组，其它的三个参数这里没用到。返回值 ios 是一个最多三个元素的数组（分别表示可读的／可写的／异常的），其中的每一个元素也是一个素组。所以 ios.first 代表可读的所有 io 对象。&lt;/p&gt;

&lt;p&gt;如果可读的 io 对象是自己的检查 pipe，那么调用 handle_check 处理 server 的停止／重启等；如果是有新的连接，那么初始化一个 Client 对象并加入到线程池。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_check&lt;/span&gt;
  &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@check.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;STOP_COMMAND&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:stop&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;HALT_COMMAND&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:halt&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;RESTART_COMMAND&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Reactor的循环"&gt;Reactor 的循环&lt;/h3&gt;
&lt;p&gt;下面来看看 Reactor 的循环。Reactor 里事件多路分发机制采用的也是 select(2) 系统调用，这是一种 io 多路复用的非阻塞同步 io。Reactor 的循环也是通过 pipe 来控制，所以循环代码有包含两块逻辑：pipe 处理和 socket 就绪处理。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Reactor&lt;/span&gt;
  &lt;span class="no"&gt;DefaultSleepFor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;
    &lt;span class="vi"&gt;@app_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_pool&lt;/span&gt;
    &lt;span class="vi"&gt;@ready&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@trigger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;
    &lt;span class="vi"&gt;@input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="vi"&gt;@timeouts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="vi"&gt;@sockets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vi"&gt;@ready&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_internal&lt;/span&gt;
    &lt;span class="n"&gt;sockets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@sockets&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@sleep_for&lt;/span&gt;      &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;核心调用&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;IOError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closed?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="no"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Error in select: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
          &lt;span class="no"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace&lt;/span&gt;
          &lt;span class="n"&gt;sockets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closed?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;retry&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="k"&gt;raise&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;reads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;reads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="vi"&gt;@ready&lt;/span&gt;             &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;处理pipe控制部分&lt;/span&gt;
            &lt;span class="vi"&gt;@mutex.synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="vi"&gt;@ready.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;
                &lt;span class="n"&gt;sockets&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="vi"&gt;@input&lt;/span&gt;
                &lt;span class="vi"&gt;@input.clear&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"c"&lt;/span&gt;
                &lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_if&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
                  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="vi"&gt;@ready&lt;/span&gt;
                    &lt;span class="kp"&gt;false&lt;/span&gt;
                  &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
                    &lt;span class="kp"&gt;true&lt;/span&gt;
                  &lt;span class="k"&gt;end&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"!"&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
              &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
          &lt;span class="k"&gt;else&lt;/span&gt;                        &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;处理socket部分&lt;/span&gt;
            &lt;span class="k"&gt;begin&lt;/span&gt;
              &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;try_to_finish&lt;/span&gt;
                &lt;span class="vi"&gt;@app_pool&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;      &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;可以处理的加入线程池&lt;/span&gt;
                &lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
              &lt;span class="k"&gt;end&lt;/span&gt;

            &lt;span class="c1"&gt;# The client doesn't know HTTP well&lt;/span&gt;
            &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;HttpParserError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
              &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_400&lt;/span&gt;
              &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;

              &lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;

              &lt;span class="vi"&gt;@events.parse_error&lt;/span&gt; &lt;span class="vi"&gt;@server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
            &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
              &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_500&lt;/span&gt;
              &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;

              &lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@timeouts.empty&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
        &lt;span class="vi"&gt;@mutex.synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;

          &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="vi"&gt;@timeouts.first.timeout_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@timeouts.shift&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_408&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_data_phase&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
            &lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;

            &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@timeouts.empty&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;

          &lt;span class="n"&gt;calculate_sleep&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码中的 pipe 控制逻辑有三种情况：“&lt;em&gt;”,"c"和"!"。“&lt;/em&gt;”代表增加一个待监控的客户端 socket 连接，"c"代表清空 reactor 中的 sockets 连接，"!"代表关闭 reactor。下面是给 reactor 增加客户端连接的方法，可以看到主要就是把连接 c 加入到&lt;a href="/input" class="user-mention" title="@input"&gt;&lt;i&gt;@&lt;/i&gt;input&lt;/a&gt;变量中，然后向 pipe 写入"*"。这样在主循环中收到 pipe 的写入字符就知道要增加一个客户端连接了。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@mutex.synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="vi"&gt;@input&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
    &lt;span class="vi"&gt;@trigger&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout_at&lt;/span&gt;
      &lt;span class="vi"&gt;@timeouts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
      &lt;span class="vi"&gt;@timeouts.sort&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout_at&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;calculate_sleep&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;循环中的另外一半处理 socket 连接。Socket 连接就绪时，通过运行 try_to_finish 判断 http 请求是否已经可以处理，如果可以的话，把 socket 连接加入到线程池。其它的大部分代码都是处理异常的，http 协议错误返回 400，连接超时错误返回 408，其它的错误都返回 500。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;try_to_finish&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;read_body&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@read_header&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@io.read_nonblock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;reactor模式下&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="n"&gt;还是需要事件处理方自己读io&lt;/span&gt;
    &lt;span class="vi"&gt;@buffer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
    &lt;span class="vi"&gt;@parsed_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@parser.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@parsed_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@parser.finished&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;setup_body&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从上面的代码可以看出，reactor 模式下还是需要事件处理方自己读 io。这是 reactor 模式与 proactor 的区别。当采用 proactor 模式，io 是操作系统完成的，事件处理方只需要处理 io 完成后的部分。代码中处理 http 协议解析的部分&lt;code&gt;parser.execute&lt;/code&gt;其实是 C 语言实现的，这部分后面再单独分析。先看看 parser 完成后设置 body 的部分。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;
  &lt;span class="no"&gt;EmptyBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;NullIO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup_body&lt;/span&gt;
    &lt;span class="vi"&gt;@in_data_phase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@parser.body&lt;/span&gt;
    &lt;span class="n"&gt;cl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;CONTENT_LENGTH&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;cl&lt;/span&gt;
      &lt;span class="vi"&gt;@buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;
      &lt;span class="vi"&gt;@body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;EmptyBody&lt;/span&gt;
      &lt;span class="vi"&gt;@requests_served&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="vi"&gt;@ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;remain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remain&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="vi"&gt;@body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
      &lt;span class="vi"&gt;@requests_served&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="vi"&gt;@ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remain&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MAX_BODY&lt;/span&gt;      &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Tempfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Const&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PUMA_TMP_BASE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@body.binmode&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c1"&gt;# The body[0,0] trick is to get an empty string in the same&lt;/span&gt;
      &lt;span class="c1"&gt;# encoding as body.&lt;/span&gt;
      &lt;span class="vi"&gt;@body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@body.write&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;
    &lt;span class="vi"&gt;@body_remain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remain&lt;/span&gt;
    &lt;span class="vi"&gt;@read_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从代码中可以看出，body 存在三种可能，如果 http 请求没有 body 部分，那么就是 EmptyBody；如果 body 大于 112Kb，那么 body 的内容独立保存为一个 Tempfile；其它情况下，body 是一个 StringIO 对象。&lt;/p&gt;
&lt;h3 id="线程池"&gt;线程池&lt;/h3&gt;
&lt;p&gt;最后来看看线程池的处理循环。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ThreadPool&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@not_empty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ConditionVariable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@not_full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ConditionVariable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@mutex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;      &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;待处理任务&lt;/span&gt;
    &lt;span class="vi"&gt;@workers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;      &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;所有的工作线程&lt;/span&gt;
    &lt;span class="vi"&gt;@mutex.synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="vi"&gt;@min.times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;spawn_thread&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="o"&gt;......&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;spawn_thread&lt;/span&gt;
    &lt;span class="vi"&gt;@spawned&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="n"&gt;th&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;todo&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@todo&lt;/span&gt;
      &lt;span class="n"&gt;extra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@extra.map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;work&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
        &lt;span class="n"&gt;continue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;mutex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@trim_requested&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
              &lt;span class="vi"&gt;@trim_requested&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
              &lt;span class="n"&gt;continue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
              &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@shutdown&lt;/span&gt;
              &lt;span class="n"&gt;continue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
              &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;

            &lt;span class="vi"&gt;@waiting&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;not_full&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signal&lt;/span&gt;
            &lt;span class="n"&gt;not_empty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt; &lt;span class="n"&gt;mutex&lt;/span&gt;
            &lt;span class="vi"&gt;@waiting&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;

          &lt;span class="n"&gt;work&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;continue&lt;/span&gt;  &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;取出任务&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# 实际执行任务&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;mutex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="vi"&gt;@spawned&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="vi"&gt;@workers.delete&lt;/span&gt; &lt;span class="n"&gt;th&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@workers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;th&lt;/span&gt;
    &lt;span class="n"&gt;th&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;线程池里有多个工作线程，保存在&lt;a href="/workers" class="user-mention" title="@workers"&gt;&lt;i&gt;@&lt;/i&gt;workers&lt;/a&gt;中。每一个工作线程有一个 while 循环，不断从任务队列&lt;a href="/todo" class="user-mention" title="@todo"&gt;&lt;i&gt;@&lt;/i&gt;todo&lt;/a&gt;中取可以执行的任务。线程之间用 mutex 进行同步。每一个任务的实际执行代码是&lt;code&gt;block.call(work, *extra)&lt;/code&gt;，其中的 block 是线程池在初始化的时候传递进来的，而 work 是一个 Client 对象。下面是 block 的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;
    &lt;span class="o"&gt;......&lt;/span&gt;
    &lt;span class="vi"&gt;@thread_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ThreadPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@min_threads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="vi"&gt;@max_threads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="no"&gt;IOBuffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;process_now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;queue_requests&lt;/span&gt;
          &lt;span class="n"&gt;process_now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eagerly_finish&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;
          &lt;span class="n"&gt;process_now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;HttpParserError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_400&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;

        &lt;span class="vi"&gt;@events.parse_error&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ConnectionError&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;process_now&lt;/span&gt;
          &lt;span class="n"&gt;process_client&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_timeout&lt;/span&gt; &lt;span class="vi"&gt;@first_data_timeout&lt;/span&gt;
          &lt;span class="vi"&gt;@reactor.add&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可见，线程池执行的 block 代码块是 Server 启动的时候在初始化 ThreadPool 时设置的。实际的 http 请求处理在 process_client 方法中处理，这块代码下一篇再分析。对于现在还不能处理的客户端，把它加入 reactor 中。&lt;/p&gt;

&lt;p&gt;下面是把任务加入到线程池的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ThreadPool&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@mutex.synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@shutdown&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Unable to add work while shutting down"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="vi"&gt;@todo&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@waiting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="vi"&gt;@todo.size&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="vi"&gt;@spawned&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="vi"&gt;@max&lt;/span&gt;
        &lt;span class="n"&gt;spawn_thread&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="vi"&gt;@not_empty.signal&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当线程池初始化的时候，先执行&lt;a href="/min" class="user-mention" title="@min"&gt;&lt;i&gt;@&lt;/i&gt;min&lt;/a&gt;次&lt;code&gt;spawn_thread&lt;/code&gt;方法，然后在加任务的时候，如果线程数还没有达到&lt;a href="/max" class="user-mention" title="@max"&gt;&lt;i&gt;@&lt;/i&gt;max&lt;/a&gt;，动态增加线程。&lt;/p&gt;

&lt;p&gt;最后，看一下如何停止线程池：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;shutdown&lt;/span&gt;
  &lt;span class="vi"&gt;@mutex.synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="vi"&gt;@shutdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="vi"&gt;@not_empty.broadcast&lt;/span&gt;
    &lt;span class="vi"&gt;@not_full.broadcast&lt;/span&gt;

    &lt;span class="vi"&gt;@auto_trim.stop&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@auto_trim&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Use this instead of #each so that we don't stop in the middle&lt;/span&gt;
  &lt;span class="c1"&gt;# of each and see a mutated object mid #each&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="vi"&gt;@workers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
      &lt;span class="vi"&gt;@workers.first.join&lt;/span&gt; &lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="vi"&gt;@workers.empty&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="vi"&gt;@spawned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="vi"&gt;@workers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="输出"&gt;输出&lt;/h3&gt;
&lt;p&gt;Puma 对输入的 socket 连接的处理很复杂，而对输出的处理则简单很多，代码如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fast_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;syswrite&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EAGAIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EWOULDBLOCK&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;WRITE_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ConnectionError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Socket timeout writing data"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;retry&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt;  &lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EPIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SystemCallError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;IOError&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ConnectionError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Socket timeout writing data"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytesize&lt;/span&gt;
    &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;byteslice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所有的输出最后都是调用 syswrite 实现的。Syswrite 是一种底层的写方法，它在写数据时不使用 ruby 层的缓冲。Syswrite 不能和普通的 write 混合使用。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Wed, 11 Mar 2015 18:37:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/24599</link>
      <guid>https://ruby-china.org/topics/24599</guid>
    </item>
    <item>
      <title>Puma 源代码分析 － 集群模式</title>
      <description>&lt;h2 id="Puma的集群运行模式"&gt;Puma 的集群运行模式&lt;/h2&gt;&lt;h3 id="Puma集群的运行时结构"&gt;Puma 集群的运行时结构&lt;/h3&gt;
&lt;p&gt;以集群模式启动的 Puma 服务器会有一个进程是 master 进程，然后有多个 worker 进程。Master 进程是通过 fork 的方式创建 worker 进程的，所以 master 进程是主进程，worker 进程是子进程。进程间通信采用的是 pipe 机制，每个 pipe 都是一对有两个元素的 io 对象，其中一个 io 对象只用于写，另一个 io 对象只用于读。&lt;/p&gt;

&lt;p&gt;要理解集群的运行，一定要先理解 pipe 的工作原理，其官方文档在&lt;a href="http://ruby-doc.org/core-2.1.0/IO.html#method-c-pipe" rel="nofollow" target="_blank" title=""&gt;ruby pipe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Puma 的运行时结构如图所示。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/32a64d5f746c86d0ed62ba7889b5ae4b.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在运行时，Puma 的 Master/worker 进程间有两个 pipe，一个 pipe 用于 worker 进程报告给 master 进程自己的状态，另外一个 pipe 用于 worker 进程检查 master 进程有没有退出。代码中 pipe 相关的变量有 6 个，3 读 3 写，按执行顺序出现的次序如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@wakeup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;
&lt;span class="vi"&gt;@check_pipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@suicide_pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;
&lt;span class="vi"&gt;@master_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@worker_write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@wakeup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码很奇葩，明明只有两个 pipe，只应该有 2 读 2 写，为什么偏偏多定义一组，而且是简单变量复制的方式多定义一组？仔细分析代码发现，read 和&lt;a href="/wakeup" class="user-mention" title="@wakeup"&gt;&lt;i&gt;@&lt;/i&gt;wakeup&lt;/a&gt;这一组 pipe，只有 master 进程向&lt;a href="/wakeup" class="user-mention" title="@wakeup"&gt;&lt;i&gt;@&lt;/i&gt;wakeup&lt;/a&gt;写内容，而且只有一处写。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wakeup!&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="vi"&gt;@wakeup.write&lt;/span&gt; &lt;span class="s2"&gt;"!"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@wakeup.closed&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;SystemCallError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;IOError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而&lt;a href="/master_read" class="user-mention" title="@master_read"&gt;&lt;i&gt;@&lt;/i&gt;master_read&lt;/a&gt;和&lt;a href="/worker_write" class="user-mention" title="@worker_write"&gt;&lt;i&gt;@&lt;/i&gt;worker_write&lt;/a&gt;这一组 pipe，&lt;a href="/master_read" class="user-mention" title="@master_read"&gt;&lt;i&gt;@&lt;/i&gt;master_read&lt;/a&gt;其实没有被使用，worker 进程有两处向&lt;a href="/worker_write" class="user-mention" title="@worker_write"&gt;&lt;i&gt;@&lt;/i&gt;worker_write&lt;/a&gt;写内容。一处是 worker 进程内的 server 启动后，通过写"b#{Process.pid}\n"通知 master 进程；一处是 worker 进程内的独立线程，每 5 秒钟写一次"p#{Process.pid}\n"，报告 master 进程自己的状态。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start_server&lt;/span&gt;
  &lt;span class="vi"&gt;@worker_write&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"b&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@worker_write&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"p&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
      &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以图中的 state report pipe 其实有两个独立的用途，一个用途是主进程自己使用，用于 wakeup，另一个用途是子进程报告状态。因为有两种独立的用途，所以定义了两组体现用途的名字。&lt;/p&gt;

&lt;p&gt;在 Master 进程中，有一个&lt;a href="/workers" class="user-mention" title="@workers"&gt;&lt;i&gt;@&lt;/i&gt;workers&lt;/a&gt;对象，保存的是 Worker 的数组，这个 Worker 对象其实不是真的 worker，而只是子进程 worker 信息的记录对象。因为在不同的进程中，ruby 对象是完全对立的，主进程中的&lt;a href="/workers" class="user-mention" title="@workers"&gt;&lt;i&gt;@&lt;/i&gt;workers&lt;/a&gt;对象和子进程中的任何 ruby 对象都没有关系。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;
    &lt;span class="vi"&gt;@pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;
    &lt;span class="vi"&gt;@phase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;phase&lt;/span&gt;
    &lt;span class="vi"&gt;@stage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:started&lt;/span&gt;
    &lt;span class="vi"&gt;@signal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"TERM"&lt;/span&gt;
    &lt;span class="vi"&gt;@options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;
    &lt;span class="vi"&gt;@first_term_sent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="vi"&gt;@last_checkin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last_checkin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Worker 对象的几个关键属性为：index 指子进程编号，从 0 开始；pid 指子进程的 pid，phase 指第几次 phased_restart，signal 指要发送给子进程的信号，last_checkin 指子进程最后一次报告状态的时间。&lt;/p&gt;

&lt;p&gt;Master 主进程并不实际处理 Web 请求，而是由 worker 进程来处理。worker 进程启动的时候，会在内部启动一个 puma 的 server。&lt;/p&gt;
&lt;h3 id="Puma集群的启动过程"&gt;Puma 集群的启动过程&lt;/h3&gt;
&lt;p&gt;集群的启动执行的是 Cluster 类的 run 方法。其中绑定端口，写状态文件之类的函数和单进程模式是一样的，这里就不重复说明了。可以看到，集群模式多了很多信号处理的代码，pipe 处理的代码，以及最关键的启动 worker 进程的代码 spawn_workers。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cluster&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Runner&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;
    &lt;span class="n"&gt;output_header&lt;/span&gt; &lt;span class="s2"&gt;"cluster"&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"* Process workers: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:workers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="vi"&gt;@cli.binder.parse&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:binds&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;

    &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@wakeup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;

    &lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"SIGCHLD"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;wakeup!&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"TTIN"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:workers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="n"&gt;wakeup!&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"TTOU"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:workers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:workers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="vi"&gt;@workers.last.term&lt;/span&gt;
      &lt;span class="n"&gt;wakeup!&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;master_pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;

    &lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"SIGTERM"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;master_pid&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"Early termination of worker"&lt;/span&gt;
        &lt;span class="nb"&gt;exit!&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;stop&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@check_pipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@suicide_pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;

    &lt;span class="n"&gt;redirect_io&lt;/span&gt;
    &lt;span class="n"&gt;start_control&lt;/span&gt;
    &lt;span class="vi"&gt;@cli.write_state&lt;/span&gt;

    &lt;span class="vi"&gt;@master_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@worker_write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@wakeup&lt;/span&gt;

    &lt;span class="n"&gt;spawn_workers&lt;/span&gt;

    &lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"SIGINT"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;stop&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@cli.events.fire_on_booted&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;#主进程的主循环，见下一小节&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主进程一共处理 5 种类型的信号：SIGCHLD 让主进程 wakeup，TTIN 增加一个 worker 进程，TTOU 减少一个 worker 进程，SIGTERM 终止主进程，SIGINT 也是终止进程。&lt;/p&gt;
&lt;h4 id="Master进程的主循环"&gt;Master 进程的主循环&lt;/h4&gt;
&lt;p&gt;集群的主进程启动完成后，执行下面的主循环。循环中读取 read 对象，这个 read 就是前面提到的状态检查 pipe 的读取对象。该 pipe 一共可能写入三种数据，"!","b#{pid}","p#{pid}"，所以读取的时候判断是哪种情况并相应进行处理。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;force_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
          &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_nonblock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"!"&lt;/span&gt;
          &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@workers.find&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
            &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"b"&lt;/span&gt;
              &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boot!&lt;/span&gt;
              &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"- Worker &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (pid: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) booted, phase: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;phase&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
              &lt;span class="n"&gt;force_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
            &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"p"&lt;/span&gt;
              &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping!&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
          &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"! Out-of-sync worker list, no &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; worker"&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@phased_restart&lt;/span&gt;
          &lt;span class="n"&gt;start_phased_restart&lt;/span&gt;
          &lt;span class="vi"&gt;@phased_restart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;check_workers&lt;/span&gt; &lt;span class="n"&gt;force_check&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Interrupt&lt;/span&gt;
        &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:stop&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;stop_workers&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:halt&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt;
    &lt;span class="vi"&gt;@check_pipe.close&lt;/span&gt;
    &lt;span class="vi"&gt;@suicide_pipe.close&lt;/span&gt;
    &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
    &lt;span class="vi"&gt;@wakeup.close&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当主进程收到"b#{pid}"和"p#{pid}"格式的数据时，根据 pid 找到 Worker 对象，然后执行该对象的 boot! 和 ping! 方法，主要就是更新 Worker 的最后签到信息。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;boot!&lt;/span&gt;
    &lt;span class="vi"&gt;@last_checkin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
    &lt;span class="vi"&gt;@stage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:booted&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ping!&lt;/span&gt;
    &lt;span class="vi"&gt;@last_checkin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Puma集群的Worker的启动过程"&gt;Puma 集群的 Worker 的启动过程&lt;/h3&gt;
&lt;p&gt;上面提到，Master 进程会调用 spawn_workers 方法启动 worker 进程，这里就是相关代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;spawn_workers&lt;/span&gt;
  &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:workers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="vi"&gt;@workers.size&lt;/span&gt;

  &lt;span class="n"&gt;master&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;

  &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_worker_index&lt;/span&gt;

    &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fork&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="vi"&gt;@cli.debug&lt;/span&gt; &lt;span class="s2"&gt;"Spawned worker: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="vi"&gt;@workers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:after_worker_boot&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="vi"&gt;@phased_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:idle&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中的核心部分就是 fork 调用创建一个新的 worker 进程，该进程会立即执行 worker(idx, master) 方法。有 java 语言背景的人，容易误认为这里的 worker 方法是 Worker 类的构造函数，其实它们之间没有关系。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"puma: cluster worker &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;" [&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:tag&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:tag&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="vg"&gt;$0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;

  &lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"SIGINT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"IGNORE"&lt;/span&gt;

  &lt;span class="vi"&gt;@workers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="vi"&gt;@master_read.close&lt;/span&gt;
  &lt;span class="vi"&gt;@suicide_pipe.close&lt;/span&gt;

  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vi"&gt;@check_pipe&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"! Detected parent died, dying"&lt;/span&gt;
    &lt;span class="nb"&gt;exit!&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;hooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:before_worker_boot&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start_server&lt;/span&gt;

  &lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"SIGTERM"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="vi"&gt;@worker_write&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"b&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;SystemCallError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;IOError&lt;/span&gt;
    &lt;span class="no"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Master seems to have exitted, exitting."&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@worker_write&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"p&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
      &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;

  &lt;span class="n"&gt;hooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:before_worker_shutdown&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;ensure&lt;/span&gt;
  &lt;span class="vi"&gt;@worker_write.close&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从代码中可以看出，worker 进程忽略 SIGINT 信号，收到 SIGTERM 信号时会调用 server.stop。但是如果 start_server 还没有调用的时候，worker 进程就收到了 SIGTERM 信号，那么 worker 进程会执行 exit。把主子进程两处定义的 SIGTERM 处理函数放在一起会更清楚一些。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"SIGTERM"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# The worker installs their own SIGTERM when booted.&lt;/span&gt;
  &lt;span class="c1"&gt;# Until then, this is run by the worker and the worker&lt;/span&gt;
  &lt;span class="c1"&gt;# should just exit if they get it.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;master_pid&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"Early termination of worker"&lt;/span&gt;
    &lt;span class="nb"&gt;exit!&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;stop&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"SIGTERM"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;     
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 Linux 的 fork 创建进程时是写时拷贝的，所以当主进程设置的 SIGTERM 的处理代码，而子进程还没有修改时，子进程执行的也是同样的处理代码。而当子进程设置了 SIGTERM 的处理代码，主子进程各自拥有独立的 SIGTERM 的处理代码。&lt;/p&gt;

&lt;p&gt;由于子进程不需要&lt;a href="/workers" class="user-mention" title="@workers"&gt;&lt;i&gt;@&lt;/i&gt;workers&lt;/a&gt;对象，所以将&lt;a href="/workers" class="user-mention" title="@workers"&gt;&lt;i&gt;@&lt;/i&gt;workers&lt;/a&gt;置空。子进程中不需要的 pipe 端口也关闭。子进程中有一个单独的线程来判断主进程是否退出。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vi"&gt;@check_pipe&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"! Detected parent died, dying"&lt;/span&gt;
  &lt;span class="nb"&gt;exit!&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;前面提到&lt;a href="/check_pipe" class="user-mention" title="@check_pipe"&gt;&lt;i&gt;@&lt;/i&gt;check_pipe&lt;/a&gt;和&lt;a href="/suicide_pipe" class="user-mention" title="@suicide_pipe"&gt;&lt;i&gt;@&lt;/i&gt;suicide_pipe&lt;/a&gt;是一对读写的 pipe。但是实际上没有任何代码往&lt;a href="/suicide_pipe" class="user-mention" title="@suicide_pipe"&gt;&lt;i&gt;@&lt;/i&gt;suicide_pipe&lt;/a&gt;里写数据。所以 IO.select 返回的唯一情况就是&lt;a href="/suicide_pipe" class="user-mention" title="@suicide_pipe"&gt;&lt;i&gt;@&lt;/i&gt;suicide_pipe&lt;/a&gt;被关闭了。此时主进程一定挂了。&lt;/p&gt;

&lt;p&gt;当 worker 进程执行了 start_server 以后，会通过 pipe 写状态数据“b#{pid}”和"p#{pid}"，这部分代码已经分析过了。最后，worker 进程执行 server.run.join 来实际处理 web 请求。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Sun, 08 Mar 2015 21:34:06 +0800</pubDate>
      <link>https://ruby-china.org/topics/24529</link>
      <guid>https://ruby-china.org/topics/24529</guid>
    </item>
    <item>
      <title>Puma 源代码分析 － 单进程模式</title>
      <description>&lt;h2 id="Puma的单进程运行模式"&gt;Puma 的单进程运行模式&lt;/h2&gt;&lt;h3 id="Puma的类结构"&gt;Puma 的类结构&lt;/h3&gt;
&lt;p&gt;在 puma 的启动过程代码中可以看到，负责单进程运行模式的类是 Single。在分析单进程运行模式之前，先总体看一下 puma 的单进程／集群架构。其类图如下所示，可以看出很像策略模式。
&lt;img src="https://l.ruby-china.com/photo/2015/fb2ac4bf4a840c9e3ab2eeca9019b394.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;最右边是 puma 的启动入口类 PUAM::CLI，它包含一个成员变量&lt;a href="/runner" class="user-mention" title="@runner"&gt;&lt;i&gt;@&lt;/i&gt;runner&lt;/a&gt;，属于 Runner 类。Runner 类有两个子类 Single 和 Cluster，分别负责单进程和集群运行模式。Single 和 Cluster 都提供一个 run 接口，CLI 的 run 方法会调用对应类的 run 方法。可以看出，这类似 GOF 的策略模式。但是 ruby 是动态语言，puma 的作者其实应该不是按照策略模式来实现这套框架的。&lt;/p&gt;

&lt;p&gt;下面是 puma 早期的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;clustered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:workers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;clustered&lt;/span&gt;
  &lt;span class="n"&gt;run_cluster&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="n"&gt;run_single&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可见在 puma 早期，单进程执行 run_single 方法，集群执行 run_cluster 方法，没有 Runner/Single/Cluster 的类继承体系，CLI 里直接写单进程和集群的运行模式代码。&lt;/p&gt;

&lt;p&gt;所以，现在的这个架构是逐步重构出来的。Single 和 Cluster 有很多雷同的代码，于是增加一个 Runner 类，把共同的代码提取到基类中，形成了现在的架构。我自己本人也是这样的编程习惯，通过不断的重构，清除重复代码，厘清代码的结构。&lt;/p&gt;

&lt;p&gt;Runner 中有四个成员变量，分别是 cli／options／app／control，各代表命令行启动类，命令行参数类、配置的 rack app，运行时控制 puma 的 PUMA::ControlCLI 类。这四个类对单进程和多进程来说都是一样的。然后 Single 包含一个 PUAM::Server 类，而 Cluster 则包含一个 PUMA::Worker 的数组，这是单进程与集群模式真正有区别的地方。&lt;/p&gt;
&lt;h3 id="单进程的执行流程"&gt;单进程的执行流程&lt;/h3&gt;
&lt;p&gt;单进程的执行流程由 Single 类的 run 方法实现，它首先判断是否要以 daemon 的方法运行 puma，然后加载配置文件并确定监听哪些网络接口，接着把 puma 的进程 pid 以及状态信息写入状态文件中，然后启动运行时控制服务器的网络接口，然后通知启动监听者 puma 启动了，最后是运行 Server 类的 run 方法。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Single&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Runner&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;daemon?&lt;/span&gt;
         &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"* Daemonizing..."&lt;/span&gt;
         &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;daemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="n"&gt;redirect_io&lt;/span&gt;
       &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="n"&gt;load_and_bind&lt;/span&gt;
      &lt;span class="vi"&gt;@cli.write_state&lt;/span&gt;
      &lt;span class="n"&gt;start_control&lt;/span&gt;
      &lt;span class="vi"&gt;@server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start_server&lt;/span&gt;
      &lt;span class="vi"&gt;@cli.events.fire_on_booted&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Interrupt&lt;/span&gt;
        &lt;span class="c1"&gt;# Swallow it&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;       
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="绑定网络端口"&gt;绑定网络端口&lt;/h4&gt;
&lt;p&gt;绑定网络端口的实现代码如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Runner&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_and_bind&lt;/span&gt;
      &lt;span class="err"&gt;……&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="vi"&gt;@app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@cli.config.app&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"! Unable to load application: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="vi"&gt;@cli.binder.parse&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:binds&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;  &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;实际绑定网络接口发生在这里&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先加载 Rack 的&lt;a href="/app" class="user-mention" title="@app"&gt;&lt;i&gt;@&lt;/i&gt;app&lt;/a&gt;，然后调用 Binder 类的 parse 方法实现网络绑定端口的解析和绑定。我感觉这个 parse 方法名字取的不好，叫 parse_and_bind 会更合适一些。另外一个违反直觉的是启动 TCPServer 的代码在 Binder 类中，而不在 Server 类中。下面看看 parse 方法的实现：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Binder&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;binds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheme&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
          &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_query&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;
          &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'low_latency'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;bak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'backlog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
          &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"* Listening on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
          &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_tcp_listener&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bak&lt;/span&gt;
          &lt;span class="vi"&gt;@listeners&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"unix"&lt;/span&gt;
          &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"* Listening on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
          &lt;span class="n"&gt;umask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
          &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_query&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'umask'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="c1"&gt;# Use Integer() to respect the 0 prefix as octal&lt;/span&gt;
              &lt;span class="n"&gt;umask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'mode'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
          &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_unix_listener&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;umask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;
          &lt;span class="vi"&gt;@listeners&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s2"&gt;"ssl"&lt;/span&gt;
          &lt;span class="o"&gt;......&lt;/span&gt;
         &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的核心代码是 add_tcp_listener 和 add_unix_listener，分别启动 TCPServer 和 UNIXServer 并监听对应的网络端口。Puma 可以一次监听多个网络端口，其中 unix 快于 tcp，tcp 快于 ssl。由于实际应用中，ssl 都是前端接入的 web 服务器做 ssl offloading，所以这里的 ssl 相关代码就不分析了。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_tcp_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optimize_for_latency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backlog&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'['&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;optimize_for_latency&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setsockopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IPPROTO_TCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TCP_NODELAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setsockopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SOL_SOCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SO_REUSEADDR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt; &lt;span class="n"&gt;backlog&lt;/span&gt;
  &lt;span class="vi"&gt;@ios&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_unix_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;umask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@unix_paths&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
  &lt;span class="o"&gt;......&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UNIXServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@ios&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
  &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@proto_env.dup&lt;/span&gt;
  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;REMOTE_ADDR&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;
  &lt;span class="vi"&gt;@envs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="写状态文件"&gt;写状态文件&lt;/h4&gt;
&lt;p&gt;当网络端口绑定以后，下一步是写入 pid 文件和状态文件，这两个文件的地址都是在 config 文件中通过 pidfile／state_path 命令配置的。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_state&lt;/span&gt;
  &lt;span class="n"&gt;write_pid&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'yaml'&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"pid"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@config.dup&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_yaml&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_pid&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:pidfile&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;
    &lt;span class="nb"&gt;at_exit&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pid&lt;/span&gt;
        &lt;span class="n"&gt;delete_pidfile&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写状态文件的代码很简单，基本就是把 Configuration 的对象实例序列化为 yaml 格式并写入文件中。写 pid 文件也很简单，只是 ruby 进程退出的时候要把老的 pid 文件删除。&lt;/p&gt;

&lt;p&gt;下一步是启动 puma 的 control server，这是通过在 config 文件中配置 control_url 实现的。这块代码和主流程没有太大关系，先放放。&lt;/p&gt;
&lt;h4 id="启动Server"&gt;启动 Server&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Runner&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_server&lt;/span&gt;
    &lt;span class="n"&gt;min_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:min_threads&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;max_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:max_threads&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@cli.events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;min_t&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_t&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inherit_binder&lt;/span&gt; &lt;span class="vi"&gt;@cli.binder&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:mode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:tcp&lt;/span&gt;
      &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp_mode!&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;development?&lt;/span&gt;
      &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;leak_stack_on_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动 Server 的方法 start_server 其实只是初始化了一个 Server 类的实例，赋值一些变量。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stdio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="vi"&gt;@app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="vi"&gt;@events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;
    &lt;span class="vi"&gt;@check&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@notify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:stop&lt;/span&gt;
    &lt;span class="vi"&gt;@min_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="vi"&gt;@max_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
    &lt;span class="vi"&gt;@auto_trim_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="vi"&gt;@thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="vi"&gt;@thread_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="vi"&gt;@persistent_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PERSISTENT_TIMEOUT&lt;/span&gt;
    &lt;span class="vi"&gt;@binder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Binder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@own_binder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="vi"&gt;@first_data_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FIRST_DATA_TIMEOUT&lt;/span&gt;
    &lt;span class="vi"&gt;@leak_stack_on_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="vi"&gt;@options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;
    &lt;span class="vi"&gt;@queue_requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:queue_requests&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:queue_requests&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'RACK_ENV'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="s2"&gt;"development"&lt;/span&gt;
    &lt;span class="vi"&gt;@mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:http&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一个 Server 类用来服务一个 rack app。&lt;/p&gt;
&lt;h4 id="boot的事件通知"&gt;boot 的事件通知&lt;/h4&gt;
&lt;p&gt;当 Server 初始化完成以后，通知事件监听者 puma 启动了。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Events&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fire_on_booted!&lt;/span&gt;
    &lt;span class="vi"&gt;@on_booted.each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="Server的run方法"&gt;Server 的 run 方法&lt;/h4&gt;
&lt;p&gt;单进程模式的最后一步是调用 Server 的 run 方法。如果 background 设置为 true，那么会在一个新线程中运行实际的 handle_servers 代码，否则会同步执行。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;  
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;BasicSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;do_not_reverse_lookup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="vi"&gt;@events.fire&lt;/span&gt; &lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:booting&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;
    &lt;span class="n"&gt;queue_requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@queue_requests&lt;/span&gt;
    &lt;span class="vi"&gt;@thread_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ThreadPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@min_threads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="vi"&gt;@max_threads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="no"&gt;IOBuffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;当有数据来的时候的回调block&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="n"&gt;后面详细分析io代码时分析此处省略的代码&lt;/span&gt;&lt;span class="err"&gt;。&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;queue_requests&lt;/span&gt;
      &lt;span class="vi"&gt;@reactor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Reactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@thread_pool&lt;/span&gt;
      &lt;span class="vi"&gt;@reactor.run_in_thread&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@events.fire&lt;/span&gt; &lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:running&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;
      &lt;span class="vi"&gt;@thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;handle_servers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@thread&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;handle_servers&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一章里就不讨论 io 处理部分的代码（io 也是最复杂的一块），先看总体流程。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_servers&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@check&lt;/span&gt;
    &lt;span class="n"&gt;sockets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="vi"&gt;@binder.ios&lt;/span&gt;
    &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@thread_pool&lt;/span&gt;
    &lt;span class="n"&gt;queue_requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@queue_requests&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="n"&gt;ios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="n"&gt;sockets&lt;/span&gt;
        &lt;span class="err"&gt;＃&lt;/span&gt;&lt;span class="n"&gt;网络数据处理部分&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="n"&gt;后面详细分析io代码时分析此处省略的代码&lt;/span&gt;&lt;span class="err"&gt;。&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ECONNABORTED&lt;/span&gt;
        &lt;span class="c1"&gt;# client closed the socket even before accept&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt; &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Object&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="vi"&gt;@events.unknown_error&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Listen loop"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@events.fire&lt;/span&gt; &lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt;
    &lt;span class="n"&gt;graceful_shutdown&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:stop&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;queue_requests&lt;/span&gt;
      &lt;span class="vi"&gt;@reactor.clear&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;
      &lt;span class="vi"&gt;@reactor.shutdown&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@check.close&lt;/span&gt;
    &lt;span class="vi"&gt;@notify.close&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="vi"&gt;@own_binder&lt;/span&gt;
      &lt;span class="vi"&gt;@binder.close&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="vi"&gt;@events.fire&lt;/span&gt; &lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:done&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puma 服务器通过&lt;a href="/status" class="user-mention" title="@status"&gt;&lt;i&gt;@&lt;/i&gt;status&lt;/a&gt;变量控制其运行。当&lt;a href="/status" class="user-mention" title="@status"&gt;&lt;i&gt;@&lt;/i&gt;status&lt;/a&gt;为:run 的时候，服务器处于运行状态，当外部操作导致&lt;a href="/status" class="user-mention" title="@status"&gt;&lt;i&gt;@&lt;/i&gt;status&lt;/a&gt;变化时，puma 进入关闭／重启流程。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Tue, 03 Mar 2015 10:26:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/24426</link>
      <guid>https://ruby-china.org/topics/24426</guid>
    </item>
    <item>
      <title>Puma 源代码分析 － 启动流程</title>
      <description>&lt;h2 id="Puma的启动方式"&gt;Puma 的启动方式&lt;/h2&gt;
&lt;p&gt;puma 有两种启动方式：一种是通过标准的 rack 接口启动，一种是通过 puma 提供的命令行工具启动。&lt;/p&gt;
&lt;h3 id="rack接口"&gt;rack 接口&lt;/h3&gt;
&lt;p&gt;首先，Puma 会把自己设置为缺省的 rack handler，见文件 lib/puma/rack_default.rb&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Rack::Handler&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Puma&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后，在 Rack::Handler::Puma 的 run 方法中启动 puma，见文件 lib/rack/handler/puma.rb&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="o"&gt;......&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Threads&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Puma &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Const&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PUMA_VERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; starting..."&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"* Listening on tcp://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Host&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Port&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_tcp_listener&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Host&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Port&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;

  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Interrupt&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"* Gracefully stopping, waiting for requests to finish"&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"* Goodbye!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;run 方法中的参数 app 是一个 rack 应用，options 只支持主机名／端口号／线程数／日志等有限的几个参数。从代码中可见，rack 接口启动 puma 只监听 tcp 端口，且使用单进程模式，不支持集群。所以通过 rack 接口启动 puma 不能完全利用 puam 的高级功能。&lt;/p&gt;

&lt;p&gt;通过下面的命令都会以 rack 接口启动 puma：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rackup &lt;span class="nt"&gt;-s&lt;/span&gt; Puma
rails s Puma
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="命令行工具bin/puma"&gt;命令行工具 bin/puma&lt;/h3&gt;
&lt;p&gt;另外一种启动 puma 的方法是通过命令行的 bin/puma 命令，这是一个 ruby 写的可执行脚本，代码如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puma/cli'&lt;/span&gt;
&lt;span class="n"&gt;cli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;
&lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可见，实际处理命令行参数和启动 puma 的是 Puma::CLI 类。命令行启动的时候，可以传递一些参数给 puma。参数有两种格式：短的和长的，比如-p 和 --port 都是设置监听端口。你可以在命令行把所有的 puma 参数都设置好，也可以把设置参数写在一个文件里，然后通过-C 来引用。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;puma &lt;span class="nt"&gt;-b&lt;/span&gt; tcp://127.0.0.1:9292 &lt;span class="nt"&gt;-t&lt;/span&gt; 8:32 &lt;span class="nt"&gt;-w&lt;/span&gt; 3
&lt;span class="nv"&gt;$ &lt;/span&gt;puma &lt;span class="nt"&gt;-C&lt;/span&gt; /path/to/config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CLI 启动 puma 的具体流程见下一节。&lt;/p&gt;
&lt;h2 id="Puma的启动流程"&gt;Puma 的启动流程&lt;/h2&gt;
&lt;p&gt;这里主要讲解命令行启动 puma 的流程。启动过程分两步，首先是初始化 Puma::CLI 类，然后执行 cli.run 方法。&lt;/p&gt;
&lt;h3 id="Puma的CLI初始化"&gt;Puma 的 CLI 初始化&lt;/h3&gt;
&lt;p&gt;CLI 初始化的代码如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stdio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;......&lt;/span&gt;
  &lt;span class="vi"&gt;@events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;
  &lt;span class="n"&gt;setup_options&lt;/span&gt;
  &lt;span class="n"&gt;generate_restart_data&lt;/span&gt;
  &lt;span class="vi"&gt;@binder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Binder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@binder.import_from_env&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主要步骤包括：初始化事件的标准输出与错误输出、设置命令行参数的缺省值、设置解析命令行参数的代码、设置重启 puma 的命令行、初始化 Binder 类。&lt;/p&gt;

&lt;p&gt;setup_options 方法首先设置命令行参数的缺省值，比如缺省的最小和最大线程数是 0 和 16。然后初始化&lt;a href="/parser" class="user-mention" title="@parser"&gt;&lt;i&gt;@&lt;/i&gt;parser&lt;/a&gt;对象，实现命令行参数的解析。解析代码也很简单，每找到一个命令行参数，就把它加入&lt;a href="/options" class="user-mention" title="@options"&gt;&lt;i&gt;@&lt;/i&gt;options&lt;/a&gt;数组。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup_options&lt;/span&gt;
  &lt;span class="vi"&gt;@options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;:min_threads&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:max_threads&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;......&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="vi"&gt;@parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OptionParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt; &lt;span class="s2"&gt;"-b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--bind URI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"URI to bind to (tcp://, unix://, ssl://)"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:binds&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="o"&gt;......&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后是获取重启 puma 的命令行，并保存到&lt;a href="/restart_argv" class="user-mention" title="@restart_argv"&gt;&lt;i&gt;@&lt;/i&gt;restart_argv&lt;/a&gt;中。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_restart_data&lt;/span&gt;
  &lt;span class="vi"&gt;@restart_dir&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pwd&lt;/span&gt;
  &lt;span class="vi"&gt;@original_argv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Gem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ruby&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Gem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ruby&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"-S"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# Detect and reinject -Ilib from the command line&lt;/span&gt;
  &lt;span class="n"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_path&lt;/span&gt; &lt;span class="s2"&gt;"lib"&lt;/span&gt;
  &lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-I"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vg"&gt;$:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;lib&lt;/span&gt;
  &lt;span class="vi"&gt;@restart_argv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于 generate_restart_data 方法比较难懂，这里演示一下阅读 puma 源代码时的调试过程。首先下载 puma 的源代码并编译。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ylt~/tmp/&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/puma/puma.git
ylt~/tmp/&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;puma
ylt~/tmp/puma&lt;span class="nv"&gt;$ &lt;/span&gt;rake
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在文件 lib/puma/cli.rb 的 374 行设置断点，我使用的是 byebug。接着使用下面的命令启动 puma：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ylt~/tmp/puma&lt;span class="nv"&gt;$ &lt;/span&gt;ruby &lt;span class="nt"&gt;-I&lt;/span&gt; lib/ bin/puma
&lt;span class="o"&gt;[&lt;/span&gt;371, 380] &lt;span class="k"&gt;in&lt;/span&gt; /Users/ylt/tmp/puma/lib/puma/cli.rb
   371:         &lt;span class="k"&gt;else
   &lt;/span&gt;372:           @restart_argv &lt;span class="o"&gt;=&lt;/span&gt; arg0 + ARGV
   373:         end
   374:         byebug
   375:       end
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 376:     end
   377: 
   378:     def restart_args
   379:       &lt;span class="k"&gt;if &lt;/span&gt;cmd &lt;span class="o"&gt;=&lt;/span&gt; @options[:restart_cmd]
   380:         cmd.split&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; + @original_argv
&lt;span class="o"&gt;(&lt;/span&gt;byebug&lt;span class="o"&gt;)&lt;/span&gt; @restart_argv
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/Users/ylt/.rvm/rubies/ruby-2.1.2/bin/ruby"&lt;/span&gt;, &lt;span class="s2"&gt;"-I"&lt;/span&gt;, &lt;span class="s2"&gt;"/Users/ylt/tmp/puma/lib"&lt;/span&gt;, &lt;span class="s2"&gt;"bin/puma"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;byebug&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$0&lt;/span&gt;
&lt;span class="s2"&gt;"bin/puma"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码中有不懂的地方，直接在调试环境中执行代码，比如查看&lt;a href="/restart_argv" class="user-mention" title="@restart_argv"&gt;&lt;i&gt;@&lt;/i&gt;restart_argv&lt;/a&gt;和$0到底是什么值。
如果想知道 restart_argv 到底有什么用，用 grep 搜索代码目录，看看在哪个地方使用了这个变量。最终会发现只有一个地方使用了它，简化后的代码是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;restart!&lt;/span&gt;
  &lt;span class="n"&gt;argv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;restart_args&lt;/span&gt;
  &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chdir&lt;/span&gt; &lt;span class="vi"&gt;@restart_dir&lt;/span&gt;
  &lt;span class="n"&gt;argv&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;redirects&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="no"&gt;RUBY_VERSION&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'1.9'&lt;/span&gt;
  &lt;span class="no"&gt;Kernel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就是通过 Kernel.exec 来执行命令行来重启。&lt;/p&gt;

&lt;p&gt;###Puma 的运行流程
完成 Puma::CLI 的初始化以后，调用其 run 方法将 puma 实际运行起来，这个 run 方法是 puma 执行的主函数体。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;
  &lt;span class="n"&gt;parse_options&lt;/span&gt;
  &lt;span class="n"&gt;set_rack_environment&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;clustered?&lt;/span&gt;
    &lt;span class="vi"&gt;@events.formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PidFormatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:logger&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@events&lt;/span&gt;
    &lt;span class="vi"&gt;@runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="vi"&gt;@runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;setup_signals&lt;/span&gt;
  &lt;span class="n"&gt;set_process_title&lt;/span&gt;
  &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;
  &lt;span class="vi"&gt;@runner.run&lt;/span&gt;
  &lt;span class="c1"&gt;# 这里等待runner运行结束，一般是收到了KILL类信号&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:halt&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"* Stopping immediately!"&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:stop&lt;/span&gt;
    &lt;span class="n"&gt;graceful_stop&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"* Restarting..."&lt;/span&gt;
    &lt;span class="vi"&gt;@runner.before_restart&lt;/span&gt;
    &lt;span class="n"&gt;restart!&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:exit&lt;/span&gt;
    &lt;span class="c1"&gt;# nothing&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puma 运行的第一步是解析命令行参数，然后设置 rack 环境。下一步判断是否运行为集群模式，如果是集群模式，初始化 Cluster 类；如果是单进程模式，初始化 Single 类。然后是设置操作系统信号的处理函数、设置 puma 进程标题。紧接着是设置 puma 主进程的状态为:run，并执行 runner 的 run 方法。run 方法进行几层的封装，最终会执行到 Server 类的 run 方法，其中的关键循环是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:run&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;ios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="n"&gt;sockets&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时 puma 服务器就可以接收 web 客户端的请求了，同时等待操作系统的退出信号。当收到操作系统的退出信号后，代码执行到 case &lt;a href="/status" class="user-mention" title="@status"&gt;&lt;i&gt;@&lt;/i&gt;status&lt;/a&gt;处，这里判断信号类型来决定是停止还是重启 puma。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Sat, 28 Feb 2015 16:42:59 +0800</pubDate>
      <link>https://ruby-china.org/topics/24393</link>
      <guid>https://ruby-china.org/topics/24393</guid>
    </item>
    <item>
      <title>Puma 源代码分析 － 概述</title>
      <description>&lt;p&gt;2015 年春节期间，通读了 Puma 的源代码。阅读 Puma 的源码是一个愉快的技术探索过程，这里把阅读所得分享出来。&lt;/p&gt;
&lt;h2 id="Puma总体架构"&gt;Puma 总体架构&lt;/h2&gt;&lt;h3 id="什么是Puma?"&gt;什么是 Puma?&lt;/h3&gt;
&lt;p&gt;Puma 是一个面向 ruby 语言的并发的 Web 服务器（a modern, concurrent web server for ruby）。它的官方网站是&lt;a href="http://puma.io/" rel="nofollow" target="_blank" title=""&gt;puma.io&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="什么是Web服务器？"&gt;什么是 Web 服务器？&lt;/h3&gt;
&lt;p&gt;一个标准的 Web 服务器遵循 HTTP 协议，接受 HTTP Request 请求，返回 HTTP Response 的响应结果。请求和响应都是字符串流的格式。如图 1 所示：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/69b94273cee7435c70527fd7a4df7863.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;使用 curl 命令可以很方便的测试和学习 HTTP 协议。比如执行下面的 curl 命令，可以看到 http 的请求和响应。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl  &lt;span class="nt"&gt;-v&lt;/span&gt; http://z.cn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Http 的请求由 curl 发出，curl 此时就是 Web Client。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET / HTTP/1.1
User-Agent: curl/7.37.1
Host: z.cn
Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后 z.cn 的 web 服务器收到 HTTP Request 请求，并返回 HTTP Response 的响应结果，如下：&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;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

&lt;span class="cp"&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;301 Moved Permanently&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Moved Permanently&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;The document has moved &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://www.amazon.cn/ref=z_cn?tag=zcn0e-23"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;here&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Web 服务器根据 Http 请求的路径和其他头部信息来决定如何输出 Http 的响应结果。Web 服务器有三种方式处理 Http 请求：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;代理到下一级的 Web 服务器；&lt;/li&gt;
&lt;li&gt;对静态文件请求，直接读取本地文件内容并输出；&lt;/li&gt;
&lt;li&gt;对动态请求，则通过接口调用动态程序。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于第三种的动态请求，又有很多接口规范，比如 cgi/fastcgi, servlet, rack 等。其中 cgi/fastcgi 是语言无关的，servlet 是特定于 java 语言的，而 rack 是特定于 ruby 语言的。&lt;/p&gt;
&lt;h3 id="Ruby/Rack Web服务器"&gt;Ruby/Rack Web 服务器&lt;/h3&gt;
&lt;p&gt;Rack 的官方网站是&lt;a href="http://rack.github.io/" rel="nofollow" target="_blank" title=""&gt;rack.github.io&lt;/a&gt;。支持 Rack 的 Web 服务器的架构如图 2 所示：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/f5ff2f449880f609346b48dcd94aa750.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Rack 应用的输入是一个表示环境的 hash，输出是一个数组包括三个元素：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;HTTP 响应码&lt;/li&gt;
&lt;li&gt;HTTP 响应头的 Hash&lt;/li&gt;
&lt;li&gt;HTTP 响应体，该响应体必须支持 each 方法调用&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Puma 是一个支持 Ruby/Rack applications 的 Web 服务器。其它支持 Rack 规范的 Web 服务器还有 Unicorn／Thin／Passenger 等。&lt;/p&gt;
&lt;h3 id="Puma的源代码结构"&gt;Puma 的源代码结构&lt;/h3&gt;
&lt;p&gt;Puma 是一个典型的 Rack 服务器。其总体目录结构与关键源代码文件如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── bin   可执行脚本
│&amp;nbsp;&amp;nbsp; ├── puma      启动脚本
│&amp;nbsp;&amp;nbsp; ├── puma-wild
│&amp;nbsp;&amp;nbsp; └── pumactl   控制脚本
├── docs 文档
├── examples  一些例子文件
├── ext   ruby的原生扩展，实现快速解析HTTP的头部
│&amp;nbsp;&amp;nbsp; └── puma_http11  
├── lib
│&amp;nbsp;&amp;nbsp; ├── puma
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── app
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── status.rb     响应pumactl的控制请求
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── binder.rb         启动并绑定Tcp／Unix端口
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── cli.rb            Puma的命令行接口，解析命令行参数
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── client.rb         代表Http请求的客户端
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── cluster.rb            Puma的集群模式
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── configuration.rb  Puma的配置参数
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── control_cli.rb     命令行控制脚本的实现代码
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── daemon_ext.rb      实现puma进程的daemon化
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── events.rb          服务器的事件支持
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── null_io.rb            当request没有body时rack.input的值
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── rack_default.rb   指定缺省的rack handler
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── rack_patch.rb     Patch CommonLogger to use after_reply
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── reactor.rb        所有需要监听新数据的client链接，放到reactor的@sockets队列中
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── runner.rb     启动Puma Server的基类,Single/Cluster继承它
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── server.rb     The HTTP Server itself. Serves out a single Rack app.
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── single.rb     Puma的单进程模式
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── thread_pool.rb 线程池，其中是所有可执行的worker
│&amp;nbsp;&amp;nbsp; ├── puma.rb
│&amp;nbsp;&amp;nbsp; └── rack
│&amp;nbsp;&amp;nbsp;     └── handler
│&amp;nbsp;&amp;nbsp;         └── puma.rb       缺省的puma rack handler实现
├── test  测试代码
└── tools 进程监控脚本：init.d/upstart
    ├── jungle
    │&amp;nbsp;&amp;nbsp; ├── init.d
    │&amp;nbsp;&amp;nbsp; └── upstart
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本系列后续部分将详细分析 Puma 如何启动、如何监听 tcp 连接，如何解析 http header，如何执行 rack app，如何响应控制命令，如何处理文件上传，如何实现集群模式，如何使用 reactor 模型处理 io 等。&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://ruby-china.org/topics/24393" title=""&gt;Puma 源代码分析 － 启动流程&lt;/a&gt;
&lt;a href="https://ruby-china.org/topics/24426" title=""&gt;Puma 源代码分析 － 单进程模式&lt;/a&gt;
&lt;a href="https://ruby-china.org/topics/24529" title=""&gt;Puma 源代码分析 － 集群模式&lt;/a&gt;
&lt;a href="https://ruby-china.org/topics/24599" title=""&gt;Puma 源代码分析 － IO 处理&lt;/a&gt;
&lt;a href="https://ruby-china.org/topics/24654" title=""&gt;Puma 源代码分析 － http 协议解析&lt;/a&gt;
&lt;a href="https://ruby-china.org/topics/24837" title=""&gt;Puma 源代码分析 － 完结篇&lt;/a&gt;&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Fri, 27 Feb 2015 23:32:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/24378</link>
      <guid>https://ruby-china.org/topics/24378</guid>
    </item>
    <item>
      <title>Rails / Nginx 与 Weak Etag</title>
      <description>&lt;p&gt;Ruby-china 上有几篇讨论 Etag 的帖子，比如&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ruby-china.org/topics/5257" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/5257&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ruby-china.org/topics/19389" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/19389&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;要了解 Etag 的使用，推荐先看看上面的文章和讨论。但是我好像没有看到过介绍 rails 中使用 Weak Etag 的。&lt;/p&gt;

&lt;p&gt;在讨论 Weak Etag 前，先解释一下 rails 自带的中间件 Rack::ETag 与自己写 fresh_when/is_stale 的区别。使用 Rack::ETag 能够节省网络传输时间，并不节省服务器端的计算量；而使用 fresh_when 则能节省页面渲染＋网络传输时间，复杂一些的 Rails 的页面渲染一般需要 10ms 以上，这是 fresh_when 相比 Rack::ETag 能够节省下来的。我的做法是重要的页面自己使用 fresh_when 计算 Etag，其它统统让 Rack::ETag 处理。&lt;/p&gt;

&lt;p&gt;但是用 Rails 生成 Etag 配合 Nginx 有个坑，就是使用 Nginx 的压缩的时候，Nginx 会简单的把 Etag 去掉，理由是压缩后的内容变化了，Etag 无效了。Quakewang 的帖子中提到了这个问题，并给出了两种解决办法：1.用 Rack 中间件进行 gzip 压缩，2.修改 nginx 源代码。其实这两种方案都不能让人满意。这个坑就引出了本文的主角 Weak Etag，对 Etag 详细的说明见下面的链接：&lt;/p&gt;

&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/HTTP_ETag" rel="nofollow" target="_blank"&gt;http://en.wikipedia.org/wiki/HTTP_ETag&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;简单来说，Weak Etag 和 (strong) ETag 的区别在于：Etag 保证文件的内容是完全相同的，而 Weak Etag 只保证文件的内容是语义上相同的。区分 ETag 和 Weak Etag 对一些 byte by byte 的操作有影响，比如断点续传等。&lt;/p&gt;

&lt;p&gt;Weak Etag 就是在 Etag 前面增加了两个字符 W/，比如：&lt;/p&gt;

&lt;p&gt;"123456789"   -- A strong ETag validator
W/"123456789"  -- A weak ETag validator&lt;/p&gt;

&lt;p&gt;从 Nginx1.7.3 版本开始，其 gzip 模块能正确的处理 Weak Etag 了。源代码在下面两个文件中：
src/http/modules/ngx_http_gzip_filter_module.c
src/http/ngx_http_core_module.c&lt;/p&gt;

&lt;p&gt;如果 response 中带 Weak Etag，那么 Nginx 的 gzip 模块不处理；如果 response 中带 strong Etag，那么 Nginx 会把它转化为 Weak Etag。&lt;/p&gt;

&lt;p&gt;所以如果使用了 Nginx1.7.3 以后版本，让 rails 应用支持 etag 很简单。让 rails 生成的 Etag 成为 Weak Etag 即可，代码如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeakEtagMiddleware&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# make request etags "strong"&lt;/span&gt;
    &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_IF_NONE_MATCH'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^W\//&lt;/span&gt;
      &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_IF_NONE_MATCH'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@app.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# make response etags "weak"&lt;/span&gt;
    &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ETag'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;!~&lt;/span&gt; &lt;span class="sr"&gt;/^W\//&lt;/span&gt;
      &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ETag'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"W/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以使用 gem 'rails_weak_etags', 代码拷贝自 &lt;a href="http://stackoverflow.com/questions/18693718/weak-etags-in-rails" rel="nofollow" target="_blank"&gt;http://stackoverflow.com/questions/18693718/weak-etags-in-rails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;此外，Rack::ETag 在 1.6.0.beta2 以后，默认也生成 Weak Etag。
  &lt;a href="https://github.com/rack/rack/commit/12528d4567d8e6c1c7e9422fee6cd8b43c4389bf" rel="nofollow" target="_blank"&gt;https://github.com/rack/rack/commit/12528d4567d8e6c1c7e9422fee6cd8b43c4389bf&lt;/a&gt;
如果你使用的 Rails 版本足够新，那么不需要做任何事情就能正确处理 Etag 了。我目前还是使用的 Rack 1.5 的版本，所以还是需要上面的解决方案。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Mon, 15 Dec 2014 00:15:45 +0800</pubDate>
      <link>https://ruby-china.org/topics/23193</link>
      <guid>https://ruby-china.org/topics/23193</guid>
    </item>
    <item>
      <title>[杭州] 脸脸 招聘 Web/Ruby 团队 Leader</title>
      <description>&lt;p&gt;脸脸是全球首款基于场所的社交 APP。当人们在同一个地点，在脸脸中就可以进入同一个场所，然后就会有一些好玩的事情发生。比如我们国庆节的时候在杭州西溪印象城搞了一场哆啦 A 梦的活动就很成功。脸脸的目标是成为所有线下场所的首选 APP。
&lt;img src="https://l.ruby-china.com/photo/2014/a137f82d64444211dc0209d2b7353ed7.jpeg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;国庆期间有 30 万人参与了杭州西溪印象城的哆啦 A 梦的活动。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2014/d2ba9adf1b118f44e2a8ad7b938eb43c.jpeg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2014/9084b53425a37e5d8a85d237ed6b1d52.jpg" title="" alt=""&gt;
整点抢答时刻，人潮涌动。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2014/c494a18ae42e47f585b3c53709ecccde.jpg" title="" alt=""&gt;
通过猜图活动，在脸脸中拿到的优惠券。
在印象城这种综合性消费场所，各种吃喝玩乐商家很多，即时收到的优惠券对我接下来去那个店消费有很大的影响力。因为是近场营销，转换率非常高。&lt;/p&gt;

&lt;p&gt;介绍一下脸脸现在的技术栈：数据库层混合使用 Mongdb、Mysql、Redis，其中地点数据／Json 数据等保存在 Mongodb 中，需要交易与事务支持的数据保存在 Mysql 中、社交类数据如好友关系和赞等数据保存在 Redis 中。以前还尝试过 Riak／Cassandra／Hadoop 等，最后发现目前的阶段不合适。&lt;/p&gt;

&lt;p&gt;使用的开发语言主要是 Ruby，大部分的 Web 应用都是用 Rails 开发的，应用服务器是 Puma，后台任务有 Resque/Sidekiq。除了 Ruby 外还使用了 Erlang／Nodejs／Php，其中 Erlang 用于即时通讯、Nodejs 用于网络代理、Php 则用在接入国内各种第三方 Api。Web 接入使用的是 Nginx，进程监控用的是 God。&lt;/p&gt;

&lt;p&gt;我们刚刚敲定了 A 轮的风投，正在扩充技术团队，现需要一位 Web／Ruby 团队 Leader。&lt;/p&gt;

&lt;p&gt;岗位要求&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;三年以上的 Web 应用开发经验，一年以上的技术团队管理经验，有带队使用 Rails 成功开发项目的经验&lt;/li&gt;
&lt;li&gt;熟悉 Ruby 编程语言和 Rails 开发框架，熟悉一种常见的 Sql 和 Nosql 数据库&lt;/li&gt;
&lt;li&gt;良好的沟通能力和团队合作能力，以及独立调研新技术的能力&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;岗位职责&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;带领团队进行 Web 应用和项目的开发，对研发全过程进行管理。&lt;/li&gt;
&lt;li&gt;负责 Web／Ruby 技术栈关键技术研究及技术预研，确定系统总体架构、业务架构、数据架构。&lt;/li&gt;
&lt;li&gt;需要有编码能力，需要编写系统核心部分的代码以及解决技术难题。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;待遇&lt;/p&gt;

&lt;p&gt;创业公司条件受限，但是我们会提供能力范围内的最好的待遇，基本月薪 15k - 25k，根据个人能力而定，提供期权。&lt;/p&gt;

&lt;p&gt;加分项： &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;有 nodejs 或者 erlang 的编程经验&lt;/li&gt;
&lt;li&gt;在 github 上有 star 多于 10 个开源项目&lt;/li&gt;
&lt;li&gt;有 10 人以上的技术团队的管理经验&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;简历发到 yuan_xin_yu@hotmail.com 邮箱，标题请写：应聘 Ruby 团队 Leader
也可以电话联系：15868870052&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Tue, 14 Oct 2014 11:50:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/22029</link>
      <guid>https://ruby-china.org/topics/22029</guid>
    </item>
    <item>
      <title>[杭州] 脸脸招聘前端程序员／Erlang 程序员／UI 设计师</title>
      <description>&lt;p&gt;脸脸是一个创业公司，半年前在 RubyChina 上发贴招到了两个 ruby 程序员，上次招聘的详情见&lt;a href="http://ruby-china.org/topics/10929" rel="nofollow" target="_blank"&gt;http://ruby-china.org/topics/10929&lt;/a&gt;。Ruby 程序员我们常年招聘。&lt;/p&gt;

&lt;p&gt;脸脸的发展非常快速，急需其它方向的靠谱的人加盟。这次招聘的职位包括前端程序员／Erlang 程序员／UI 设计师各一名。&lt;/p&gt;

&lt;p&gt;应聘者要求有较强的专业技能，具体的招聘要求就不列了。&lt;/p&gt;

&lt;p&gt;如果你看过精益创业、乔布斯传、浪潮之巅，如果你用 github、mac、开源软件，如果你是一个自我要求高且希望有所成就的人，那么你就是我们要找的人。&lt;/p&gt;

&lt;p&gt;脸脸的介绍可以参考：&lt;a href="https://itunes.apple.com/cn/app/lianlian/id577710538" rel="nofollow" target="_blank"&gt;https://itunes.apple.com/cn/app/lianlian/id577710538&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;薪资待遇：
创业公司薪资不高，目前能提供月薪 6k - 16k，根据个人能力而定。
期权也会有，脸脸近期会进行一次较大的 A 轮融资。&lt;/p&gt;

&lt;p&gt;工作地点：
杭州市西湖区&lt;/p&gt;

&lt;p&gt;如果你觉得有兴趣，欢迎发 email：yuan_xin_yu@hotmail.com 和我联系。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Wed, 15 Jan 2014 20:06:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/16819</link>
      <guid>https://ruby-china.org/topics/16819</guid>
    </item>
    <item>
      <title>[杭州] 脸脸招聘 Ruby 程序员 / 架构师</title>
      <description>&lt;p&gt;脸脸是一个创业公司，正在开发一款同名的移动设备上的现场交友应用，目前 IOS 正式版已经上线，下载地址&lt;a href="http://www.dface.cn" rel="nofollow" target="_blank"&gt;http://www.dface.cn&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;脸脸的服务器端基于 Linux/Ruby/Mongodb 等开源技术开发，我们希望找同样喜欢开源技术、喜欢写代码的程序员／架构师加盟。&lt;/p&gt;

&lt;p&gt;岗位要求：
除了热爱写代码、有自己的开源项目外，没有学位／户口／性别／精通 XXX 等任何其它的要求。&lt;/p&gt;

&lt;p&gt;薪资待遇：
创业公司薪资不高，目前能提供月薪 8k - 16k，根据个人能力而定。&lt;/p&gt;

&lt;p&gt;工作地点：
杭州市西湖区&lt;/p&gt;

&lt;p&gt;如果你觉得有兴趣，可以发 email：yuan_xin_yu@hotmail.com&lt;/p&gt;

&lt;p&gt;谢谢大家发的应聘邮件，建议应聘者把自己的 github／google code 主页，参与的开源项目直接以文字的方式写在邮件里。&lt;/p&gt;</description>
      <author>ylt</author>
      <pubDate>Sun, 12 May 2013 23:22:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/10929</link>
      <guid>https://ruby-china.org/topics/10929</guid>
    </item>
  </channel>
</rss>
