<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>ane (刘宏庆)</title>
    <link>https://ruby-china.org/ane</link>
    <description>做一个快乐的程序员</description>
    <language>en-us</language>
    <item>
      <title>数组哈希累加</title>
      <description>&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"delivery_income_num"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;62&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"delivery_num"&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="s2"&gt;"income_fee"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"pickup_alipay_pay_num"&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="s2"&gt;"pickup_wechat_pay_num"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"box_num"&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="p"&gt;}]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sum_overview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;delivery_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
      &lt;span class="n"&gt;income_fee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
      &lt;span class="n"&gt;box_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
      &lt;span class="n"&gt;records&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;record&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;delivery_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;delivery_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:delivery_num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;income_fee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;income_fee&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:income_fee&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;box_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;box_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:box_num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;delivery_num: &lt;/span&gt;&lt;span class="n"&gt;delivery_num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;income_fee: &lt;/span&gt;&lt;span class="n"&gt;income_fee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;delivery_rate: &lt;/span&gt;&lt;span class="n"&gt;delivery_num&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;box_num&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;有个哈希数组，需要对数组里的每个哈希的 value 累加，有什么代码简洁而且还高效的方法
或者有没有类似处理功能的 gem 推荐&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Wed, 14 Nov 2018 19:19:32 +0800</pubDate>
      <link>https://ruby-china.org/topics/37760</link>
      <guid>https://ruby-china.org/topics/37760</guid>
    </item>
    <item>
      <title>IO 模式和 IO 多路复用</title>
      <description>&lt;h2 id="基础知识"&gt;基础知识&lt;/h2&gt;&lt;h4 id="1用户空间和内核空间"&gt;1 用户空间和内核空间&lt;/h4&gt;
&lt;p&gt;现在操作系统都采用虚拟寻址，处理器先产生一个虚拟地址，通过地址翻译成物理地址（内存的地址），再通过总线的传递，最后处理器拿到某个物理地址返回的字节。&lt;/p&gt;

&lt;p&gt;对 32 位操作系统而言，它的寻址空间（虚拟存储空间）为 4G（2 的 32 次方）。&lt;/p&gt;

&lt;p&gt;核空间：在 liunx 中，将最高的 1G 字节（从虚拟地址 0xC0000000 到 0xFFFFFFFF），供内核使用，称为“内核空间”。&lt;/p&gt;

&lt;p&gt;用户空间：在 liunx 中，将较低的 3G 字节（从虚拟地址 0x00000000 到 0xBFFFFFFF），供各个进程使用，称为“用户空间）。&lt;/p&gt;

&lt;p&gt;内核态：当一个任务（进程）执行系统调用而陷入内核代码中执行时，我们就称进程处于内核运行态&lt;/p&gt;

&lt;p&gt;用户态：每个进程都有自己的内核栈。当进程在执行用户自己的代码时，则称其处于用户运行态（用户态）&lt;/p&gt;

&lt;p&gt;因为每个进程可以通过系统调用进入内核，因此，Linux 内核由系统 内的所有进程共享。于是，从具体进程的角度来看，每个进程可以拥有 4G 字节的虚拟空间。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;进程寻址空间 0~4G&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;进程在用户态只能访问 0~3G，只有进入内核态才能访问 3G~4G&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;进程通过系统调用进入内核态&lt;/li&gt;
&lt;li&gt;每个进程虚拟空间的 3G~4G 部分是相同的&lt;br&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="1.2 进程上下文切换（进程切换）"&gt;1.2 进程上下文切换（进程切换）&lt;/h4&gt;
&lt;p&gt;为了控制进程的执行，内核必须有能力挂起正在 CPU 上运行的进程，并恢复以前挂起的某个进程的执行。这种行为被称为进程切换（也叫调度）。因此可以说，任何进程都是在操作系统内核的支持下运行的，是与内核紧密相关的。&lt;/p&gt;

&lt;p&gt;从一个进程的运行转到另一个进程上运行，这个过程中经过下面这些变化：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;保存当前进程 A 的上下文。
上下文就是内核再次唤醒当前进程时所需要的状态，由一些对象（程序计数器、状态寄存器、用户栈等各种内核数据结构）的值组成。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这些值包括描绘地址空间的页表、包含进程相关信息的进程表、文件表等。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;切换页全局目录以安装一个新的地址空间。&lt;/li&gt;
&lt;li&gt;恢复进程 B 的上下文。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;可以理解成一个比较耗资源的过程。&lt;/p&gt;
&lt;h4 id="1.3 直接I/O和缓存I/O"&gt;1.3 直接 I/O 和缓存 I/O&lt;/h4&gt;
&lt;p&gt;缓存 I/O 又被称作标准 I/O，大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中，以 write 为例，数据会先被拷贝进程缓冲区，在拷贝到操作系统内核的缓冲区中，然后才会写到存储设备中。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/d410db1c-f699-4122-8c4d-e28a348d31dd.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="IO模式"&gt;IO 模式&lt;/h2&gt;
&lt;p&gt;对于一次 IO 访问（这回以 read 举例），数据会先被拷贝到操作系统内核的缓冲区中，然后才会从操作系统内核的缓冲区拷贝到应用程序的缓冲区，最后交给进程。所以说，当一个 read 操作发生时，它会经历两个阶段：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. 等待数据准备 (Waiting for the data to be ready)&lt;/li&gt;
&lt;li&gt;2. 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;正式因为这两个阶段，linux 系统产生了下面五种网络模式的方案：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;-- 阻塞 I/O（blocking IO）&lt;/li&gt;
&lt;li&gt;-- 非阻塞 I/O（nonblocking IO）&lt;/li&gt;
&lt;li&gt;-- I/O 多路复用（IO multiplexing）&lt;/li&gt;
&lt;li&gt;-- 信号驱动 I/O（signal driven IO）&lt;/li&gt;
&lt;li&gt;-- 异步 I/O（asynchronous IO）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="block I/O模型（阻塞I/O）"&gt;block I/O 模型（阻塞 I/O）&lt;/h4&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/56b3cedc-cd0e-4d54-a001-28004acc71ad.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;read 为例：&lt;/p&gt;

&lt;p&gt;（1）进程发起 read，进行 recvfrom 系统调用；&lt;/p&gt;

&lt;p&gt;（2）内核开始第一阶段，准备数据（从磁盘拷贝到缓冲区），进程请求的数据并不是一下就能准备好；准备数据是要消耗时间的；&lt;/p&gt;

&lt;p&gt;（3）与此同时，进程阻塞（进程是自己选择阻塞与否），等待数据 ing；&lt;/p&gt;

&lt;p&gt;（4）直到数据从内核拷贝到了用户空间，内核返回结果，进程解除阻塞。&lt;/p&gt;

&lt;p&gt;也就是说，内核准备数据和数据从内核拷贝到进程内存地址这两个过程都是阻塞的。&lt;/p&gt;
&lt;h4 id="non-block（非阻塞I/O模型）"&gt;non-block（非阻塞 I/O 模型）&lt;/h4&gt;
&lt;p&gt;可以通过设置 socket 使其变为 non-blocking。当对一个 non-blocking socket 执行读操作时，流程是这个样子：
&lt;img src="https://l.ruby-china.com/photo/2018/77179aa5-7754-4822-8ab4-903566840175.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;（1）当用户进程发出 read 操作时，如果 kernel 中的数据还没有准备好；&lt;/p&gt;

&lt;p&gt;（2）那么它并不会 block 用户进程，而是立刻返回一个 error，从用户进程角度讲，它发起一个 read 操作后，并不需要等待，而是马上就得到了一个结果；&lt;/p&gt;

&lt;p&gt;（3）用户进程判断结果是一个 error 时，它就知道数据还没有准备好，于是它可以再次发送 read 操作。一旦 kernel 中的数据准备好了，并且又再次收到了用户进程的 system call；&lt;/p&gt;

&lt;p&gt;（4）那么它马上就将数据拷贝到了用户内存，然后返回。&lt;/p&gt;

&lt;p&gt;所以，nonblocking IO 的特点是用户进程在内核准备数据的阶段需要不断的主动询问数据好了没有。&lt;/p&gt;
&lt;h4 id="I/O多路复用"&gt;I/O 多路复用&lt;/h4&gt;
&lt;p&gt;I/O 多路复用实际上就是用 select, poll, epoll 监听多个 io 对象，对一个 IO 端口，两次调用，两次返回，比阻塞 IO 并没有什么优越性；关键是能实现同时对多个 IO 端口进行监听；当 io 对象有变化（有数据）的时候就通知用户进程。现在先来看下 I/O 多路复用的流程：
&lt;img src="https://l.ruby-china.com/photo/2018/6b727e99-b45e-4104-ba1d-1dd2dd281e52.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;（1）当用户进程调用了 select，那么整个进程会被 block；&lt;/p&gt;

&lt;p&gt;（2）而同时，kernel 会“监视”所有 select 负责的 socket；&lt;/p&gt;

&lt;p&gt;（3）当任何一个 socket 中的数据准备好了，select 就会返回；&lt;/p&gt;

&lt;p&gt;（4）这个时候用户进程再调用 read 操作，将数据从 kernel 拷贝到用户进程。&lt;/p&gt;

&lt;p&gt;所以，I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符，而这些文件描述符（套接字描述符）其中的任意一个进入读就绪状态，select() 函数就可以返回。&lt;/p&gt;

&lt;p&gt;这个图和 blocking IO 的图其实并没有太大的不同，事实上，还更差一些。因为这里需要使用两个 system call (select 和 recvfrom)，而 blocking IO 只调用了一个 system call (recvfrom)。
但是，用 select 的优势在于它可以同时处理多个 connection。&lt;/p&gt;

&lt;p&gt;所以，如果处理的连接数不是很高的话，使用 select/epoll 的 web server 不一定比使用多线程 + 阻塞 IO 的 web server 性能更好，可能延迟还更大。&lt;/p&gt;

&lt;p&gt;select/epoll的优势并不是对于单个连接能处理得更快，而是在于能处理更多的连接。&lt;/p&gt;

&lt;p&gt;在 IO multiplexing Model 中，实际中，对于每一个 socket，一般都设置成为 non-blocking，但是，如上图所示，整个用户的 process 其实是一直被 block 的。只不过 process 是被 select 这个函数 block，而不是被 socket IO 给 block。&lt;/p&gt;
&lt;h4 id="信号驱动I/O模型"&gt;信号驱动 I/O 模型&lt;/h4&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/c672dd70-c22a-42a6-a798-0c12aa7bf765.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;我们首先开启套接口的信号驱动 I/O 功能，并通过 sigaction 系统调用安装一个信号处理函数。该系统调用立即返回，我们的进程继续工作，也就是说它没有被阻塞。当数据报准备好读取时，内核就为该进程产生一个 SIGIO 信号。我们随后既可以在信号处理函数中调用 recvfrom 读取数据报，并通知主循环数据已准备好待处理，也可以立即通知主循环，让它读取数据报。&lt;/p&gt;

&lt;p&gt;无论如何处理 SIGIO 信号，这种模型的优势在于等待数据报到达期间，进程不被阻塞。主循环可以继续执行，只要不时等待来自信号处理函数的通知：既可以是数据已准备好被处理，也可以是数据报已准备好被读取。&lt;/p&gt;
&lt;h4 id="asynchronous I/O（异步 I/O）"&gt;asynchronous I/O（异步 I/O）&lt;/h4&gt;
&lt;p&gt;异步 I/O（asynchronous I/O）由 POSIX 规范定义。一般地说，这些函数的工作机制是：告知内核启动某个操作，并让内核在整个操作（包括将数据从内核拷贝到我们自己的缓冲区）完成后通知我们。这种模型与信号驱动模型的主要区别在于：信号驱动 I/O 是由内核通知我们何时启动一个 I/O 操作，而异步 I/O 模型是由内核通知我们 I/O 操作何时完成。&lt;/p&gt;

&lt;p&gt;我们调用 aio_read 函数（POSIX 异步 I/O 函数以 aio_或 lio_开头），给内核传递描述字、缓冲区指针、缓冲区大小（与 read 相同的三个参数）、文件偏移（与 lseek 类似），并告诉内核当整个操作完成时如何通知我们。该系统调用立即返回，在等待 I/O 完成期间，我们的进程不被阻塞。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/9ad0c25d-72e9-4a2d-8182-5cea6dc73b28.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;（1）用户进程发起 read 操作之后，立刻就可以开始去做其它的事。&lt;/p&gt;

&lt;p&gt;（2）而另一方面，从 kernel 的角度，当它受到一个 asynchronous read 之后，首先它会立刻返回，所以不会对用户进程产生任何 block。&lt;/p&gt;

&lt;p&gt;（3）然后，kernel 会等待数据准备完成，然后将数据拷贝到用户内存，当这一切都完成之后，kernel 会给用户进程发送一个 signal，告诉它 read 操作完成了。&lt;/p&gt;
&lt;h2 id="select/poll/epoll的区别及其Ruby示例"&gt;select/poll/epoll的区别及其Ruby示例&lt;/h2&gt;
&lt;p&gt;首先前文已述 I/O 多路复用的本质就是用 select/poll/epoll，去监听多个 socket 对象，如果其中的 socket 对象有变化，只要有变化，用户进程就知道了。&lt;/p&gt;

&lt;p&gt;select 是不断轮询去监听的 socket，socket 个数有限制，一般为 1024 个；&lt;/p&gt;

&lt;p&gt;poll 还是采用轮询方式监听，只不过没有个数限制；&lt;/p&gt;

&lt;p&gt;epoll 并不是采用轮询方式去监听了，而是当 socket 有变化时通过回调的方式主动告知用户进程。&lt;/p&gt;
&lt;h4 id="阻塞 I/O（blocking IO）"&gt;阻塞 I/O（blocking IO）&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;

&lt;span class="n"&gt;one_hundred_kb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp_server_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4481&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;connection&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="c1"&gt;#  readpartial(1) It blocks only if ios has no data immediately available.&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readpartial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;one_hundred_kb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;data&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;EOFError&lt;/span&gt; &lt;span class="c1"&gt;# EOFError Raised by some IO operations when reaching the end of file.&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;connection&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;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="非阻塞 I/O（nonblocking IO）"&gt;非阻塞 I/O（nonblocking IO）&lt;/h4&gt;
&lt;p&gt;IO.select 会阻塞并且轮询 socket&lt;/p&gt;

&lt;p&gt;read&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;'socket'&lt;/span&gt;

&lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp_server_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4481&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;connection&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;connection&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;4096&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;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EAGAIN&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;connection&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;rescue&lt;/span&gt; &lt;span class="no"&gt;EOFError&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;end&lt;/span&gt;

  &lt;span class="n"&gt;connection&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;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;wirte&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;'socket'&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;TCPSocket&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="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4481&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'hihihihi'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10_00&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;bytes&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;wirte_nonblock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&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;bytes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;

    &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice!&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;bytes&lt;/span&gt;&lt;span class="p"&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="n"&gt;client&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;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="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="n"&gt;client&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;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;除了非阻塞读写，server 端有非阻塞接受 accept_nonblock。client 有非阻塞连接 connect_nonblock&lt;/p&gt;
&lt;h4 id="I/O 多路复用（ IO multiplexing）"&gt;I/O 多路复用（IO multiplexing）&lt;/h4&gt;
&lt;p&gt;select 版本&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;connections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;TCPSocket&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;TCPSocket&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;TCPSocket&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&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;connections&lt;/span&gt;

    &lt;span class="n"&gt;readable_connections&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;readable_connections&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;conn&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;conn&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;4096&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;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EAGAIN&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="sb"&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;select&lt;/span&gt; &lt;span class="n"&gt;可以减低无用连接的开销&lt;/span&gt;&lt;span class="err"&gt;。&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>ane</author>
      <pubDate>Wed, 18 Apr 2018 10:32:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/35472</link>
      <guid>https://ruby-china.org/topics/35472</guid>
    </item>
    <item>
      <title>Ruby 为啥没有 List 数据结构</title>
      <description>&lt;p&gt;突然好奇这个问题&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Mon, 09 Apr 2018 20:51:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/35412</link>
      <guid>https://ruby-china.org/topics/35412</guid>
    </item>
    <item>
      <title>Ruby 的回调方法</title>
      <description>&lt;h3 id="大部分定义在Module中"&gt;大部分定义在 Module 中&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;append_features
const_missing
extended
included
method_added
method_removed
prepend_features
prepended
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="在Class中定义的"&gt;在 Class 中定义的&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inherited
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="定义在BaseObject中的"&gt;定义在 BaseObject 中的&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;method_missing
singleton_method_added
singleton_method_removed
singleton_method_undefined
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>ane</author>
      <pubDate>Thu, 19 Oct 2017 10:54:23 +0800</pubDate>
      <link>https://ruby-china.org/topics/34404</link>
      <guid>https://ruby-china.org/topics/34404</guid>
    </item>
    <item>
      <title>源码探索 Rails 路由的处理</title>
      <description>&lt;p&gt;首先 Rails4+，默认的 routes 是’config/routes.rb’。要想拆分 routes，可以往 paths 里加入：&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;Application&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'config/routes.rb'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'config/routes2.rb'&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;在 rails3 里，采用的是 config.paths['config/routes’]&lt;/p&gt;
&lt;h2 id="Http的处理"&gt;Http 的处理&lt;/h2&gt;
&lt;p&gt;ActionDispatch::Routing::Mapper::HttpHelpers 中定义了在 route 里可以设置的 5 种 HTTP via&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get 'bacon', to: 'food#bacon’
post 'bacon', to: 'food#bacon’
patch 'bacon', to: 'food#bacon’
put 'bacon', to: 'food#bacon’
delete 'broccoli', to: 'food#broccoli’
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最终调用的还是 match 方法&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;match 'path' =&amp;gt; 'controller#action', via: patch
match 'path', to: 'controller#action', via: :post
match 'path', 'otherpath', on: :member, via: :together
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以，直接写成 match 方法，似乎，也算少执行一些代码。&lt;/p&gt;
&lt;h2 id="Rails::Application::RoutesReloader"&gt;Rails::Application::RoutesReloader&lt;/h2&gt;
&lt;p&gt;
Rails 路由的加载的地方，所有被拆分的路由文件放在&lt;a href="/paths" class="user-mention" title="@paths"&gt;&lt;i&gt;@&lt;/i&gt;paths&lt;/a&gt;里&lt;/p&gt;
&lt;h2 id="ActionDispatch::Routing::Mapper::Base"&gt;ActionDispatch::Routing::Mapper::Base&lt;/h2&gt;
&lt;p&gt;ActionDispatch::Routing::Mapper::Base 里定义了路由的匹配规则，具体可以看看代码注释
ActionDispatch::Routing::RouteSet&lt;/p&gt;
&lt;h2 id="RouteSet其实就是一个rack"&gt;RouteSet 其实就是一个 rack&lt;/h2&gt;
&lt;p&gt;通过 initializer :add_routing_paths 的初始器，指定了 routes.rb 的文件位置，&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;initializer&lt;/span&gt; &lt;span class="ss"&gt;:add_routing_paths&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;app&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;routing_paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"config/routes.rb"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;existent&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;routes?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;routing_paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&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;routes_reloader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;routing_paths&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;routes_reloader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route_sets&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;routes&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;可以在 app.routes_reloader.paths 中加入多个 routes 文件。&lt;/p&gt;

&lt;p&gt;set_routes_reloader_hook 初始化器，开始执行 routes 文件里的代码，&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;initializer&lt;/span&gt; &lt;span class="ss"&gt;:set_routes_reloader_hook&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;app&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;reloader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;routes_reloader&lt;/span&gt;
  &lt;span class="n"&gt;reloader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_if_updated&lt;/span&gt;
  &lt;span class="n"&gt;reloaders&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;reloader&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;reloader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# We configure #execute rather than #execute_if_updated because if&lt;/span&gt;
    &lt;span class="c1"&gt;# autoloaded constants are cleared we need to reload routes also in&lt;/span&gt;
    &lt;span class="c1"&gt;# case any was used there, as in&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;#   mount MailPreview =&amp;gt; 'mail_view'&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;# This means routes are also reloaded if i18n is updated, which&lt;/span&gt;
    &lt;span class="c1"&gt;# might not be necessary, but in order to be more precise we need&lt;/span&gt;
    &lt;span class="c1"&gt;# some sort of reloaders dependency support, to be added.&lt;/span&gt;
    &lt;span class="n"&gt;require_unload_lock!&lt;/span&gt;
    &lt;span class="n"&gt;reloader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&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;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;initializer&lt;/span&gt; &lt;span class="ss"&gt;:build_middleware_stack&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;build_middleware_stack&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="ss"&gt;:build_middleware_stack&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;app&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;@app_build_lock.synchronize&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="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;default_middleware_stack&lt;/span&gt;
      &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge_into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&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;endpoint&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;routes&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;routes&lt;/span&gt;
  &lt;span class="vi"&gt;@routes&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RouteSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_with_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@routes.append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Proc&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;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
  &lt;span class="vi"&gt;@routes&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简而言之，就是:build_middleware_stack 初始化一个 RouteSet，作为第一个 rack 加入 middleware，:add_routing_paths 指定了路由的 path，:set_routes_reloader_hook 执行路由文件，装配路由。&lt;/p&gt;
&lt;h2 id="处理request"&gt;处理 request&lt;/h2&gt;
&lt;p&gt;当发送请求时，http 服务器会开始调用 middleware 的 call 方法，最底层的 RouteSet，会根据 env 生成一个 ActionDispatch::Request 对象。&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;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="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_request&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Router&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;normalize_path&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="nf"&gt;path_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="vi"&gt;@router.serve&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="/router" class="user-mention" title="@router"&gt;&lt;i&gt;@&lt;/i&gt;router&lt;/a&gt; 是一个   ActionDispatch::Journey::Router 对象，里面包含一个&lt;a href="/routes" class="user-mention" title="@routes"&gt;&lt;i&gt;@&lt;/i&gt;routes&lt;/a&gt;，是 ActionDispatch::Journey::Routes 的对象，&lt;a href="/routes" class="user-mention" title="@routes"&gt;&lt;i&gt;@&lt;/i&gt;routes&lt;/a&gt;里包含许多 ActionDispatch::Journey::Route 对象，每一个都是一条 http 的请求匹配模式。&lt;/p&gt;
&lt;h2 id="那么 @user14 和 RouteSet怎么关联的？"&gt;那么 &lt;a href="/router" class="user-mention" title="@router"&gt;&lt;i&gt;@&lt;/i&gt;router&lt;/a&gt; 和 RouteSet 怎么关联的？&lt;/h2&gt;
&lt;p&gt;每一个 http 请求先包装成一个 ActionDispatch::Routing::Mapper 对象，指定了&lt;a href="/scope_level" class="user-mention" title="@scope_level"&gt;&lt;i&gt;@&lt;/i&gt;scope_level&lt;/a&gt;，&lt;a href="/concerns" class="user-mention" title="@concerns"&gt;&lt;i&gt;@&lt;/i&gt;concerns&lt;/a&gt;，&lt;a href="/scope" class="user-mention" title="@scope"&gt;&lt;i&gt;@&lt;/i&gt;scope&lt;/a&gt;。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;via&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options_constraints&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;anchor&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;@set.add_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每一个 Mapper 对象，包装成 ActionDispatch::Routing::Mapper::Mapping 对象。
RouteSet 的初始化方法&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DEFAULT_CONFIG&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="nf"&gt;named_routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;NamedRouteCollection&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="nf"&gt;resources_path_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_resources_path_names&lt;/span&gt;
       &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_url_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;@config&lt;/span&gt;                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
       &lt;span class="vi"&gt;@append&lt;/span&gt;                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
       &lt;span class="vi"&gt;@prepend&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
       &lt;span class="vi"&gt;@disable_clear_and_finalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
       &lt;span class="vi"&gt;@finalized&lt;/span&gt;                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
       &lt;span class="vi"&gt;@env_key&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ROUTES_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;object_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_SCRIPT_NAME"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;

       &lt;span class="vi"&gt;@set&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Routes&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;@router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Router&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;@set&lt;/span&gt;
       &lt;span class="vi"&gt;@formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Formatter&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此处的&lt;a href="/set" class="user-mention" title="@set"&gt;&lt;i&gt;@&lt;/i&gt;set&lt;/a&gt;就是 RouteSet 对象，&lt;a href="/set.add_route" class="user-mention" title="@set.add_route"&gt;&lt;i&gt;@&lt;/i&gt;set.add_route&lt;/a&gt;里调用了 Journey::Routes.new.add_route&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_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_route&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;
  &lt;span class="n"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;
  &lt;span class="n"&gt;partition_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;clear_cache!&lt;/span&gt;
  &lt;span class="n"&gt;route&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;make_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;precedence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Route&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;application&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;conditions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;required_defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;defaults&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="n"&gt;precedence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="vi"&gt;@internal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;route&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Journey::Routes.new.add_route 里，调用 mapping.make_route，make_route 生成一条 http 请求的匹配模式，加入到 Journey::Routes 中
Journey::Routes &amp;lt;&amp;lt; Journey::Route
RouteSet.@set = Journey::Routes
RouteSet.@router = Journey::Router.new &lt;a href="/set" class="user-mention" title="@set"&gt;&lt;i&gt;@&lt;/i&gt;set&lt;/a&gt;
至此 RouteSet 和 &lt;a href="/router" class="user-mention" title="@router"&gt;&lt;i&gt;@&lt;/i&gt;router&lt;/a&gt;，Routes，Route 关联起来了。&lt;/p&gt;

&lt;p&gt;当调用 RouteSet 的 call 方法，就调用&lt;a href="/router" class="user-mention" title="@router"&gt;&lt;i&gt;@&lt;/i&gt;router&lt;/a&gt;的 serve 方法&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;serve&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;find_routes&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="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;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;set_params&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;path_parameters&lt;/span&gt;
    &lt;span class="n"&gt;path_info&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;path_info&lt;/span&gt;
    &lt;span class="n"&gt;script_name&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;script_name&lt;/span&gt;

    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;anchored&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;script_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;match&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;chomp&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post_match&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;path_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&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;path_info&lt;/span&gt; &lt;span class="k"&gt;unless&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;path_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_with?&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path_parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;set_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt; &lt;span class="n"&gt;parameters&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="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serve&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="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;"pass"&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="s2"&gt;"X-Cascade"&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="nf"&gt;script_name&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;script_name&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;path_info&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path_info&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;path_parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;set_params&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;return&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;return&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="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"X-Cascade"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"pass"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Not Found"&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;此处的 route.app 是 make_route 创建 route 是，传入的 application，是一个
Routing::Endpoint 类，这里是它的一个子类 ActionDispatch::Routing::RouteSet::Dispatcher 对象&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;serve&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;params&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;path_parameters&lt;/span&gt;
  &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_response!&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
  &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:action&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;res&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;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RoutingError&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@raise_on_name_error&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;return&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="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"X-Cascade"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"pass"&lt;/span&gt; &lt;span class="p"&gt;},&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;此时 make_response 定义在 ActionController::Base 中，生成 ActionDispatch::Response 对象&lt;/p&gt;

&lt;p&gt;dispatch 调用到对应的 action，返回 rack 的返回值&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;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&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;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#:nodoc:&lt;/span&gt;
  &lt;span class="n"&gt;set_request!&lt;/span&gt;&lt;span class="p"&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;set_response!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit_flash&lt;/span&gt;
  &lt;span class="nb"&gt;to_a&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rack_response&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;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;NO_CONTENT_CODES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&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="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;header&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;status&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="no"&gt;RackBody&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此处的 self 就是 ActionDispatch::Response 对象。&lt;/p&gt;
&lt;h2 id="至此，路由的配置，和处理一个http请求的过程就完整了。"&gt;至此，路由的配置，和处理一个 http 请求的过程就完整了。&lt;/h2&gt;</description>
      <author>ane</author>
      <pubDate>Wed, 13 Sep 2017 17:54:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/34110</link>
      <guid>https://ruby-china.org/topics/34110</guid>
    </item>
    <item>
      <title>Ruby webrick</title>
      <description>&lt;h2 id="写在开始前, 一个简易的webrick服务"&gt;写在开始前，一个简易的 webrick 服务&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt;  &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="n"&gt;webrick&lt;/span&gt;&lt;span class="s1"&gt;'
root = File.expand_path '&lt;/span&gt;&lt;span class="n"&gt;public_html&lt;/span&gt;&lt;span class="s1"&gt;'
server = WEBrick::HTTPServer.new :Port =&amp;gt; 8000, :DocumentRoot =&amp;gt; root
# trap '&lt;/span&gt;&lt;span class="no"&gt;INT&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;control&lt;/span&gt;&lt;span class="o"&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;的中断信号就退出server&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="n"&gt;用过&lt;/span&gt;&lt;span class="s1"&gt;'rails s'&lt;/span&gt; &lt;span class="n"&gt;的应该都清楚&lt;/span&gt;
&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'INT'&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;shutdown&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;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码 so 简单，效果如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  rake ruby webrick.rb
[2017-08-24 10:36:45] INFO  WEBrick 1.3.1
[2017-08-24 10:36:45] INFO  ruby 2.3.0 (2015-12-25) [x86_64-darwin15]
[2017-08-24 10:36:45] INFO  WEBrick::HTTPServer#start: pid=11803 port=8000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 public_html 文件夹下放入 index.html，这就算你的首页了。
这里涉及的类就很少了，只有一个 WEBrick::HTTPServer，和它的父类::WEBrick::GenericServer。&lt;/p&gt;
&lt;h2 id="初始化"&gt;初始化&lt;/h2&gt;&lt;h3 id="GenericServer的初始化"&gt;GenericServer 的初始化&lt;/h3&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;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;General&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&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;:Stop&lt;/span&gt;
    &lt;span class="vi"&gt;@config&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="no"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@config&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="c1"&gt;# 初始化最大连接数&lt;/span&gt;
    &lt;span class="vi"&gt;@tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SizedQueue&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;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:MaxClients&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:MaxClients&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="vi"&gt;@tokens.push&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;webrickv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;WEBrick&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VERSION&lt;/span&gt;
    &lt;span class="n"&gt;rubyv&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="no"&gt;RUBY_VERSION&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="no"&gt;RUBY_RELEASE_DATE&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="no"&gt;RUBY_PLATFORM&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt;
    &lt;span class="vi"&gt;@logger.info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"WEBrick &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;webrickv&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="vi"&gt;@logger.info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ruby &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rubyv&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="vi"&gt;@listeners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="vi"&gt;@shutdown_pipe&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;unless&lt;/span&gt; &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:DoNotListen&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;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Listen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nb"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;":Listen option is deprecated; use GenericServer#listen"&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;# 监听端口和IP，最终返回一个TCPServer。&lt;/span&gt;
      &lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:BindAddress&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="vi"&gt;@config&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="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@config&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="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="vi"&gt;@config&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="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@listeners&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;addr&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="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;整个初始化，就是返回一个 TCPServer 的 socket。TCP/IP stream 型连接的服务器端套接字的类。accept 实例方法会受理客户端的连接请求，返回已连接的 TCPSocket 的实例。&lt;/p&gt;
&lt;h3 id="HTTPServer的初始化"&gt;HTTPServer 的初始化&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Creates a new HTTP server according to +config+&lt;/span&gt;
   &lt;span class="c1"&gt;#&lt;/span&gt;
   &lt;span class="c1"&gt;# An HTTP server uses the following attributes:&lt;/span&gt;
   &lt;span class="c1"&gt;#&lt;/span&gt;
   &lt;span class="c1"&gt;# :AccessLog:: An array of access logs.  See WEBrick::AccessLog&lt;/span&gt;
   &lt;span class="c1"&gt;# :BindAddress:: Local address for the server to bind to&lt;/span&gt;
   &lt;span class="c1"&gt;# :DocumentRoot:: Root path to serve files from&lt;/span&gt;
   &lt;span class="c1"&gt;# :DocumentRootOptions:: Options for the default HTTPServlet::FileHandler&lt;/span&gt;
   &lt;span class="c1"&gt;# :HTTPVersion:: The HTTP version of this server&lt;/span&gt;
   &lt;span class="c1"&gt;# :Port:: Port to listen on&lt;/span&gt;
   &lt;span class="c1"&gt;# :RequestCallback:: Called with a request and response before each&lt;/span&gt;
   &lt;span class="c1"&gt;#                    request is serviced.&lt;/span&gt;
   &lt;span class="c1"&gt;# :RequestTimeout:: Maximum time to wait between requests&lt;/span&gt;
   &lt;span class="c1"&gt;# :ServerAlias:: Array of alternate names for this server for virtual&lt;/span&gt;
   &lt;span class="c1"&gt;#                hosting&lt;/span&gt;
   &lt;span class="c1"&gt;# :ServerName:: Name for this server for virtual hosting&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;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="vi"&gt;@http_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTPVersion&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:HTTPVersion&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

     &lt;span class="vi"&gt;@mount_tab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MountTable&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;if&lt;/span&gt; &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:DocumentRoot&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
       &lt;span class="n"&gt;mount&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="no"&gt;HTTPServlet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FileHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:DocumentRoot&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
             &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:DocumentRootOptions&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="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:AccessLog&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
       &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:AccessLog&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="p"&gt;[&lt;/span&gt; &lt;span class="vg"&gt;$stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;AccessLog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;COMMON_LOG_FORMAT&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
         &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="vg"&gt;$stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;AccessLog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;REFERER_LOG_FORMAT&lt;/span&gt; &lt;span class="p"&gt;]&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;@virtual_hosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Array&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;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之前有人问 mount 的意思，这里就写的很清楚了&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;##&lt;/span&gt;
&lt;span class="c1"&gt;# Mounts +servlet+ on +dir+ passing +options+ to the servlet at creation&lt;/span&gt;
&lt;span class="c1"&gt;# time&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;servlet&lt;/span&gt;&lt;span class="p"&gt;,&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="vi"&gt;@logger.debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%s is mounted on %s."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;servlet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="vi"&gt;@mount_tab&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dir&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="n"&gt;servlet&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;mount 就是把不同 servlet 和不同的 dir 对应起来。
webrick 自带 4 个 servlet，分别处理 CGI scripts, ERB pages, Ruby blocks，directory
HTTPServlet::FileHandler，就不言而喻了。&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;start&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="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ServerError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"already started."&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="n"&gt;server_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:ServerType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;SimpleServer&lt;/span&gt;

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

    &lt;span class="n"&gt;server_type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="vi"&gt;@logger.info&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&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;#start: pid=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&gt;$$&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; port=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@config&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;call_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:StartCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;shutdown_pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@shutdown_pipe&lt;/span&gt;

      &lt;span class="n"&gt;thgroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ThreadGroup&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;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:Running&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;:Running&lt;/span&gt;
          &lt;span class="k"&gt;begin&lt;/span&gt;
            &lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shutdown_pipe&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="c1"&gt;# 多路复用IO。参数列表前三项为输入／输出／异常的IO（或者子类）的实例数组。第四个参数是timeout。返回一个包含3个元素的数组，这3个元素分别是等待输入/输&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;svrs&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;sp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="vi"&gt;@listeners&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="mf"&gt;2.0&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;svrs&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;include?&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;
                &lt;span class="c1"&gt;# swallow shutdown pipe&lt;/span&gt;
                &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
                &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt;
                          &lt;span class="n"&gt;sp&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="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nread&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;exception: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
              &lt;span class="k"&gt;end&lt;/span&gt;
              &lt;span class="n"&gt;svrs&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;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;svr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
                &lt;span class="vi"&gt;@tokens.pop&lt;/span&gt;          &lt;span class="c1"&gt;# blocks while no token is there.&lt;/span&gt;
                &lt;span class="c1"&gt;# 本质就是调用socket的accept&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;accept_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;svr&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;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:DoNotReverseLookup&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;nil?&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;do_not_reverse_lookup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:DoNotReverseLookup&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;# start_thread下面会做进一步解释&lt;/span&gt;
                  &lt;span class="n"&gt;th&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start_thread&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="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="n"&gt;th&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:WEBrickThread&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;thgroup&lt;/span&gt;&lt;span class="p"&gt;.&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;th&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;@tokens.push&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="k"&gt;end&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;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;EBADF&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;ENOTSOCK&lt;/span&gt;&lt;span class="p"&gt;,&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;ex&lt;/span&gt;
            &lt;span class="c1"&gt;# if the listening socket was closed in GenericServer#shutdown,&lt;/span&gt;
            &lt;span class="c1"&gt;# IO::select raise it.&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;ex&lt;/span&gt;
            &lt;span class="n"&gt;msg&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;ex&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;ex&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="se"&gt;\n\t&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace&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="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            &lt;span class="vi"&gt;@logger.error&lt;/span&gt; &lt;span class="n"&gt;msg&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;ex&lt;/span&gt;
            &lt;span class="vi"&gt;@logger.fatal&lt;/span&gt; &lt;span class="n"&gt;ex&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;ensure&lt;/span&gt;
        &lt;span class="n"&gt;cleanup_shutdown_pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shutdown_pipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cleanup_listener&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;:Shutdown&lt;/span&gt;
        &lt;span class="vi"&gt;@logger.info&lt;/span&gt; &lt;span class="s2"&gt;"going to shutdown ..."&lt;/span&gt;
        &lt;span class="n"&gt;thgroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&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;th&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;th&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;if&lt;/span&gt; &lt;span class="n"&gt;th&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:WEBrickThread&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;call_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:StopCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@logger.info&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&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;#start done."&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="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tokens 的作用类似信号量，初始化 server 的时候，会把 tokens 用 nil 填充满，只有能从 token 获取到信号的时候，才可以创建线程，获取不到信号的时候，会阻塞主线程，以此控制并发数量，具体看 SizedQueue 的定义吧&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;##&lt;/span&gt;
 &lt;span class="c1"&gt;# Accepts a TCP client socket from the TCP server socket +svr+ and returns&lt;/span&gt;
 &lt;span class="c1"&gt;# the client socket.&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;accept_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;svr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;sock&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;begin&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;svr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&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;sync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
     &lt;span class="no"&gt;Utils&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;set_non_blocking&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="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;ECONNRESET&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;ECONNABORTED&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;EPROTO&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;EINVAL&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;ex&lt;/span&gt;
     &lt;span class="n"&gt;msg&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;ex&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;ex&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="se"&gt;\n\t&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace&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="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
     &lt;span class="vi"&gt;@logger.error&lt;/span&gt; &lt;span class="n"&gt;msg&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;sock&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中 accept_client 会不断返回一个连接好的 socket，针对每个与客户端通信的 socket，webrick 会创建一个线程，在 start_thread 中处理，start_thread 里只要一行主要代码，执行&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;run&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_thread&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="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="o"&gt;...&lt;/span&gt;
   &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="p"&gt;?&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;sock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&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="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以，对作者 GenericServer 类的注释，只有这一行，可见 run 方法很重要&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;##&lt;/span&gt;
&lt;span class="c1"&gt;# Base TCP server class.  You must subclass GenericServer and provide a #run&lt;/span&gt;
&lt;span class="c1"&gt;# method.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;##&lt;/span&gt;
&lt;span class="c1"&gt;# Processes requests on +sock+&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;sock&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTPResponse&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;@config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTPRequest&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;@config&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="nb"&gt;self&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:RequestTimeout&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;timeout&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;break&lt;/span&gt; &lt;span class="k"&gt;if&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;to_io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_readable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&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;@status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="ss"&gt;:Running&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;HTTPStatus&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EOFError&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;timeout&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="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;:Running&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;HTTPStatus&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EOFError&lt;/span&gt; &lt;span class="k"&gt;if&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;eof?&lt;/span&gt;

 &lt;span class="c1"&gt;# Parses a request from +socket+.  先是解析请求行，再是请求报文头部解析，最后确#定keep_alive&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;parse&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_method&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;request_method&lt;/span&gt;
      &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_uri&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;request_uri&lt;/span&gt;
      &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_http_version&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;http_version&lt;/span&gt;
      &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keep_alive&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;keep_alive?&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;lookup_server&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="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:RequestCallback&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;callback&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:RequestHandler&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;":RequestHandler is deprecated, please use :RequestCallback"&lt;/span&gt;
        &lt;span class="vi"&gt;@logger.warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;callback&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&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;# 调用get_instance实例化对应的servlet，并且调用servlet的service方法，组装res.body等等&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;service&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;res&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;HTTPStatus&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EOFError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;HTTPStatus&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RequestTimeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;
      &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&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;HTTPStatus&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;
      &lt;span class="vi"&gt;@logger.error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&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;HTTPStatus&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;
      &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&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;ex&lt;/span&gt;
      &lt;span class="vi"&gt;@logger.error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&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="k"&gt;ensure&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;request_line&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;keep_alive?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keep_alive?&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;fixup&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_response&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;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;access_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@config&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;res&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="k"&gt;break&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@http_version&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"1.1"&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keep_alive?&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keep_alive?&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;h2 id="自定义servlet"&gt;自定义 servlet&lt;/h2&gt;
&lt;p&gt;只需要继承 WEBrick::HTTPServlet::AbstractServlet，并且提供相应的 do_GET 方法，就基本能实现一个简单的 servlet。可以对 request 和 response 做一些处理。&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;'webrick'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Simple&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;WEBrick&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTPServlet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AbstractServlet&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_GET&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;response&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;content_type&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="n"&gt;do_stuff_with&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&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;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Hello, World!"&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;do_stuff_with&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;200&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="s1"&gt;'you got a page'&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;Configurable&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Simple&lt;/span&gt;
 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&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;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;
   &lt;span class="k"&gt;super&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;
   &lt;span class="vi"&gt;@color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;
   &lt;span class="vi"&gt;@size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;size&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;do_stuff_with&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
   &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;p "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
             &lt;span class="sx"&gt;%q{style="color: #{@color}; font-size: #{@size}"}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
             &lt;span class="s2"&gt;"&amp;gt;Hello, World!"&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&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="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;WEBrick&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTPServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="ss"&gt;:Port&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8000&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;mount&lt;/span&gt; &lt;span class="s1"&gt;'/simple'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Simple&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;mount&lt;/span&gt; &lt;span class="s1"&gt;'/configurable'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Configurable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'red'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2em'&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;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="其它"&gt;其它&lt;/h2&gt;
&lt;p&gt;除了提到的，webrick 里还涉及到 log，cookie，auth，https，proxy，cgi，HTTPRequest 和 HTTPResponse 相关的处理，可以具体看代码，其实已经不算很复杂了&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Thu, 24 Aug 2017 15:59:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/33929</link>
      <guid>https://ruby-china.org/topics/33929</guid>
    </item>
    <item>
      <title>Rails 里的 cache</title>
      <description>&lt;p&gt;Rails 的 cache 统一入口是 Rails.cache。通常会在 environments 里进行配置，配置方式为：config.cache_store = :null_store.
在 Rails 的 bootstrap 中，将 config.cache_store 赋值给 Rails.cache，从而让 Rails.cache 变成一个全局统一的入口。
&lt;code&gt;Rails.cache = ActiveSupport::Cache.lookup_store(config.cache_store)&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="Rails自带的cache有四种"&gt;Rails 自带的 cache 有四种&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:FileStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="s2"&gt;"active_support/cache/file_store"&lt;/span&gt;
&lt;span class="nb"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;:MemoryStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s2"&gt;"active_support/cache/memory_store” 
autoload :MemCacheStore, "&lt;/span&gt;&lt;span class="n"&gt;active_support&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mem_cache_store&lt;/span&gt;&lt;span class="s2"&gt;"
autoload :NullStore,     "&lt;/span&gt;&lt;span class="n"&gt;active_support&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;null_store&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ActiveSupport::Cache.lookup_store 的参数如果是 nil
&lt;code&gt;If no arguments are passed to this method, then a new ActiveSupport::Cache::MemoryStore object will be returned.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;所以默认的存储系统是 MemoryStore。ActiveSupport::Cache.lookup_store 接受自定义的存储对象。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#   ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)&lt;/span&gt;
&lt;span class="c1"&gt;#    =&amp;gt; returns MyOwnCacheStore.new&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ActiveSupport::Cache::Store 是四个存储类型的基类，提供了统一的 API：
+fetch+, +write+, +read+, +exist?+, and +delete+.&lt;/p&gt;

&lt;p&gt;需要特别强调 fetch 的:race_condition_ttl 参数，延长了过期数据的过期时间，避免死锁发生，但我觉得也是鸡肋而已。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Writes the value to the cache, with the key.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Options are passed to the underlying cache implementation.&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&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;options&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;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;merged_options&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="n"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:write&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&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="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Entry&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;value&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="n"&gt;write_entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normalize_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&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="n"&gt;entry&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="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每次缓存的数据，都被包装成了 Entry 对象。这样就可以在过期的情况下，从 Time.now 开始，再续命 race_condition_ttl 时间&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expired?&lt;/span&gt;
  &lt;span class="n"&gt;race_ttl&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;:race_condition_ttl&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;race_ttl&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_f&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expires_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;race_ttl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache&lt;/span&gt;
    &lt;span class="c1"&gt;# for a brief period while the entry is being recalculated.&lt;/span&gt;
    &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expires_at&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;race_ttl&lt;/span&gt;
    &lt;span class="n"&gt;write_entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;expires_in: &lt;/span&gt;&lt;span class="n"&gt;race_ttl&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;else&lt;/span&gt;
    &lt;span class="n"&gt;delete_entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;entry&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;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;write_entry(key, entry, expires_in: race_ttl * 2) 看了看 write_entry 方法，似乎对 expires_in 没做任何处理，所以，估计还是只是续了 race_condition_ttl 一倍的命&lt;/p&gt;

&lt;p&gt;FileStore 有 ActiveSupport::Cache::Strategy::LocalCache 配合本地使用缓存&lt;/p&gt;
&lt;h2 id="下面接着讲Rails的具体cache方案"&gt;下面接着讲 Rails 的具体 cache 方案&lt;/h2&gt;&lt;h4 id="1. rails框架自动进行SQL缓存"&gt;1. rails 框架自动进行 SQL 缓存&lt;/h4&gt;
&lt;p&gt;rails 在内存中缓存了每次 sql 查询的结果，那么在同一次经过 ActionPack 时，相同的 sql 会命中缓存，而提高性能
ActiveRecord::Core 中的方法。&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;cached_find_by_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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="c1"&gt;# :nodoc:&lt;/span&gt;
  &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@find_by_statement_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepared_statements&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synchronize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;StatementCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&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="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就 find 查询而言，缓存的是一个关于 sql 语句的抽象二叉树，而不是所谓的查询结果。&lt;/p&gt;
&lt;h4 id="2.ActiveRecord层缓存"&gt;2.ActiveRecord 层缓存&lt;/h4&gt;
&lt;p&gt;在 rails 的 model 层中可以手动缓存某些业务结果到对应的缓存存储系统里。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&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="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;expires_in: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hour&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="p"&gt;.&lt;/span&gt;
&lt;span class="nf"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="3. ActionController层"&gt;3. ActionController 层&lt;/h4&gt;
&lt;p&gt;action 缓存：rails4 中移除了 action 的缓存，需要 gem(actionpack-action_caching) 才能实现。缓存了 action response 的 html 结果，但可以进入 action-pack 进行 authenticaion 等判断。其内部实现主要是借助 fragment cache 和各种 callback 实现的。
如&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:authentication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: :show&lt;/span&gt;
&lt;span class="n"&gt;cache_action&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;expires_in: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hour&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;采取的是讲缓存的方法设置为 around_action，每次执行 action 的时候，就会 around 这个缓存操作。&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_fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&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="kp"&gt;nil&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;content&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;cache_configured?&lt;/span&gt;

  &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fragment_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;instrument_fragment_cache&lt;/span&gt; &lt;span class="ss"&gt;:write_fragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_str&lt;/span&gt;
    &lt;span class="n"&gt;cache_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&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="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而这个 cache_store，就是在 AbstractController::Caching 中定义的
config.cache_store = ActiveSupport::Cache.lookup_store(store)，也就是 Rails.cache
page 缓存：rails4 中移除了 page 的缓存，需要 gem(actionpack-page_caching) 才能实现。缓存了 action response 的 html，无需进入 action-pack，直接可以返回结果，速度是最快的。
&lt;code&gt;cache_page :show, expires_in: 1.hour&lt;/code&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;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&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;gzip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;FileUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makedirs&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;dirname&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="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;"wb+"&lt;/span&gt;&lt;span class="p"&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;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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&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="n"&gt;gzip&lt;/span&gt;
    &lt;span class="no"&gt;Zlib&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;GzipWriter&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="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;".gz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="p"&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;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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&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;page 缓存就干脆存入文件里，如果可以的话 gzip 压缩&lt;/p&gt;
&lt;h4 id="4. ActionView层缓存"&gt;4. ActionView 层缓存&lt;/h4&gt;
&lt;p&gt;随着互联网应用的逐渐发展，页面复杂程度加剧，后端无法做到整页面的缓存，只能分割成片段。片段缓存的概念逐渐强化。
虽然调用的是 Helper 里的 cache，本质还是 ActionController 层的缓存&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Fri, 18 Aug 2017 17:15:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/33876</link>
      <guid>https://ruby-china.org/topics/33876</guid>
    </item>
    <item>
      <title>字符串截取后面</title>
      <description>&lt;p&gt;今天看到一段 erlang 代码，大致功能就是输入“xxxx.erl”或者“xxxx”的字符串。截取其中的“.erl”，来进行判断是否是 erlang 源文件&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;EXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".erl"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;

 &lt;span class="nn"&gt;lists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;sub_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;lists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Str&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="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;EXT&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码的思路也只是先把字符串先 reverse 成“lre.xxxx”,再截取 [1..".erl".length],然后再 reverse 成“.erl”。&lt;/p&gt;

&lt;p&gt;这种先 reverse 再截取，再 reverse 的方式，不是很清楚，会是从遍历字符串效率角度考虑的吗？发个问题，想让大家多提点思路吧&lt;/p&gt;

&lt;p&gt;以前一些算法题类似于字符串首部移到尾部，到是这样的思路方式。&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Wed, 05 Jul 2017 15:07:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/33405</link>
      <guid>https://ruby-china.org/topics/33405</guid>
    </item>
    <item>
      <title>想在一个 Rails 项目里使用同一个 gem 的不同版本，有什么好的实现方式</title>
      <description>&lt;p&gt;简单举例，一个 rails 项目里，使用了 redis 的 3.0.4 和 3.3.3 两个版本，想要在不同的 url 里动态使用不同 redis 版本&lt;/p&gt;

&lt;p&gt;刚刚折腾了半天，终于找到一个方式，思路就是别在 gemfile 中添加任何 gem 的依赖，采用 $LOAD_PATH 加入依赖，每次 require 一个 gem 以后，记得使用完的时候在全局里删掉&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vg"&gt;$LOADED_FEATURES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;set_book&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;
   &lt;span class="vg"&gt;$LOADED_FEATURES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_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;a&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;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"redis-3.3.3"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
   &lt;span class="vg"&gt;$LOAD_PATH&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unshift&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="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'vendor/bundle/ruby/2.3.0/gems/redis-3.0.4/lib'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nb"&gt;require&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="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'vendor/bundle/ruby/2.3.0/gems/redis-3.0.4/lib/redis'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt;  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt;
    &lt;span class="vg"&gt;$LOADED_FEATURES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_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;a&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;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"redis-3.0.4"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
   &lt;span class="vg"&gt;$LOAD_PATH&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unshift&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="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'vendor/bundle/ruby/2.3.0/gems/redis-3.3.3/lib'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;require&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="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'vendor/bundle/ruby/2.3.0/gems/redis-3.3.3/lib/redis'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&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;@book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&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;</description>
      <author>ane</author>
      <pubDate>Tue, 09 May 2017 14:55:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/32958</link>
      <guid>https://ruby-china.org/topics/32958</guid>
    </item>
    <item>
      <title>[北京] 三元桥 听云大家庭招聘 Ruby 工程师</title>
      <description>&lt;h3 id="关于我们"&gt;关于我们&lt;/h3&gt;
&lt;p&gt;（北京基调网络股份有限公司）国内专业的应用性能管理 (APM) 解决方案提供商，国内首家完整实现全栈溯源。专注于帮助开发者解决应用上线后性能问题的监控与管理。及时发现问题，减少用户流失。听云拥有 20 万个遍布全国的真实用户节点，平台每日帮助监控超 100 亿次真实用户请求，每天发现用户性能问题超过 15 万个。每天可为开发者留住用户 175 万个，挽回损失 1400 万元。包括 BAT 在内，Alexa 排名前 100 企业有 81 家使用听云。&lt;/p&gt;
&lt;h3 id="听云十年"&gt;听云十年&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/ef009475e70b6197db8282971e533f92.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/41b1f09a00147197f08dcc94d1cedab6.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/02ec95f434d79ff3eb45b686759b9fa5.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/fe9bf601c976932364babc516f5aac26.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="产品分类"&gt;产品分类&lt;/h3&gt;
&lt;p&gt;听云 App，听云 Network，听云 Browser，听云 Server，听云 System。&lt;/p&gt;
&lt;h3 id="研发力量"&gt;研发力量&lt;/h3&gt;
&lt;p&gt;听云 65% 的研发团队，采用的开发语言有 Java，Net，Php，Ruby，Go，Python，Node，Javascript，Android，IOS, Erlang。&lt;/p&gt;

&lt;p&gt;听云鼓励内部员工岗位流动，今天负责的产品线，明天也许就转入其它产品线部门。同时，听云也鼓励员工同时做多个语言的开发工作。&lt;/p&gt;
&lt;h3 id="基本要求"&gt;基本要求&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;熟悉 Ruby 语言

&lt;ol&gt;
&lt;li&gt;熟悉 Ruby 语言特性，元编程&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;熟悉 RubyOnRails 的技术栈

&lt;ol&gt;
&lt;li&gt;Railtie，Engine，Initializable 不会感到陌生&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;喜欢简洁，能够高效工作

&lt;ol&gt;
&lt;li&gt;review 和 定期 refactor&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;熟悉自动化测试，MiniTest 或 RSpec&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="加分项"&gt;加分项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;熟悉 Sinatra&lt;/li&gt;
&lt;li&gt;熟悉 Jruby&lt;/li&gt;
&lt;li&gt;热爱开源&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="工薪与福利"&gt;工薪与福利&lt;/h3&gt;
&lt;p&gt;七险一金。13 薪。零食水果自助。健身房。晚餐自助。另有交通补，餐补等补助多项。&lt;/p&gt;
&lt;h4 id="薪资待遇：10k-20k"&gt;薪资待遇：10k-20k&lt;/h4&gt;&lt;h4 id="接受简历邮箱 liuhq@tingyun.com"&gt;接受简历邮箱 liuhq@tingyun.com&lt;/h4&gt;</description>
      <author>ane</author>
      <pubDate>Sun, 18 Sep 2016 14:23:24 +0800</pubDate>
      <link>https://ruby-china.org/topics/31084</link>
      <guid>https://ruby-china.org/topics/31084</guid>
    </item>
    <item>
      <title>干掉你代码中的坏味道</title>
      <description>&lt;h4 id="最近团队开始抓代码质量了，总结一下自己的经验，先看看坏代码有哪些特点："&gt;最近团队开始抓代码质量了，总结一下自己的经验，先看看坏代码有哪些特点：&lt;/h4&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/175268f65aba234133049a3362a43b65.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h5 id="“幸福的家庭都一样,不幸的家庭却各有不同”，这句话放到代码里也同样适用。接下来，我们聊一聊如何解决坏代码问题。"&gt;“幸福的家庭都一样，不幸的家庭却各有不同”，这句话放到代码里也同样适用。接下来，我们聊一聊如何解决坏代码问题。&lt;/h5&gt;&lt;h5 id="如果我问你，“你们是如何保证团队代码质量的”，你的回答可能是：“我们每次写完代码，都会花一些时间review一下。”"&gt;如果我问你，“你们是如何保证团队代码质量的”，你的回答可能是：“我们每次写完代码，都会花一些时间 review 一下。”&lt;/h5&gt;
&lt;p&gt;恩，做的确实不错，但是，做的还不够，除非你是门门考试都 100 的学霸，否则，借助一些工具还是比较稳妥的办法。 &lt;/p&gt;

&lt;p&gt;在这里简单介绍一个代码分析工具 RubyCritic，这是一个专门针对 Ruby 的一个静态代码分析工具。其它语言的，也有相似功能的工具链，我就不做介绍了。 &lt;/p&gt;

&lt;p&gt;这是一个命令行工具，第一步就是添加到你的 gem 库中，当然，还可以使用 guard 自动化分析。（Ruby 的世界，你懂得~）第二 step，在 console 运行【RubyCritic】命令，就像这样：  &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/bf93cd852e7eeba002d27de3fe556901.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在命令的最后，会生成一个静态页面。长这个样子：  &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/2d0d795930e8c73914671178b62db01d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;x 轴代表改动频率，Y 轴代表代码复杂程度 &lt;/p&gt;

&lt;p&gt;这是分析结果的 overview，超过 200 的复杂度的，基本都是坏代码。 &lt;/p&gt;

&lt;p&gt;再看看 code 里的内容： &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/0bfd816451eaebbf4839d397517e234a.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;对不同文件按照改动频率、复杂度、重复度和坏味道 4 个维度进行综合评定代码质量等级（和美国考试的成绩打分规则一样）。  &lt;/p&gt;

&lt;p&gt;RubyCritic 对代码分析的原理，其实就是分析一些，被它认为是坏代码的点。注意，我这里使用的措辞是“被它认为，所以，有时候，它不是绝对的正确。”还可以查看具体的类文件中的代码质量问题。&lt;/p&gt;

&lt;p&gt;更多的介绍，详见 &lt;a href="https://github.com/whitesmith/rubycritic" rel="nofollow" target="_blank"&gt;https://github.com/whitesmith/rubycritic&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;下面，我们针对 RubyCritic 给我们的一些坏代码的点，有针对性的做些代码调整。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/f2b2fc79af433916e36f07a4d35285e0.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这里使用 git diff 比较新旧版本的差异。operator 原来是实例方法，代码行 7，并且里面还有一个 if 结构体。started_time_and_node 原来是实例方法，代码行 4，并且里面还不止一个 if 结构体。 &lt;/p&gt;

&lt;p&gt;笔者 review 的方式：&lt;/p&gt;
&lt;h5 id="1.实例方法修改为类方法（减少混入方法，解耦合，减低负责度）"&gt;1.实例方法修改为类方法（减少混入方法，解耦合，减低负责度）&lt;/h5&gt;&lt;h5 id="2.多使用Ruby原生链式操作（减少中间变量，更少的代码，对于脚本语言，就是更快的执行效率，而且很多原生方法是C语言实现。）"&gt;2.多使用 Ruby 原生链式操作（减少中间变量，更少的代码，对于脚本语言，就是更快的执行效率，而且很多原生方法是 C 语言实现。）&lt;/h5&gt;&lt;h5 id="3.去掉结构体 （现代编程语言的结构体，让代码具有丰富的逻辑性和可读性，但是缺点就是cpu的额外开销。）"&gt;3.去掉结构体（现代编程语言的结构体，让代码具有丰富的逻辑性和可读性，但是缺点就是 cpu 的额外开销。）&lt;/h5&gt;
&lt;p&gt;以上部分，属于语法层面的奇技淫巧。      &lt;/p&gt;

&lt;p&gt;第二部分，我们从设计角度分析一下。&lt;/p&gt;

&lt;p&gt;它的代码行只有 141 行，方法也只有 7 个。但是评级却是 C。再看看代码分析细节，这里就展现一小部分，简直就是惨不忍睹，不好意思全展现给大家看了。        &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/3a86e4fdc7e9a74293240c5a53da81f7.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;没有人会一开始就这样写代码，这种坏代码，永远都是渐渐变馊的。不过笔记仔细想来，当年遇到过比着还馊 1000 倍的代码（1000 倍都不算过分）。            &lt;/p&gt;

&lt;p&gt;这是笔者做的第一版重构结果。&lt;/p&gt;
&lt;h5 id="这里使用了策略模式。Stats_hash不再是充当一个集合的作用，现在变成了一个环境类，将原来依赖if结构数据分装到不同的行为类中。"&gt;这里使用了策略模式。Stats_hash 不再是充当一个集合的作用，现在变成了一个环境类，将原来依赖 if 结构数据分装到不同的行为类中。&lt;/h5&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/88aa007ca48e6ae5cf6676fee293b616.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;第二版的改动计划是，引入 work-job 的模式，并发执行 4 个 job。&lt;/p&gt;

&lt;p&gt;第三版改动计划就是利用回调方式，去掉与该类不相干的代码，将逻辑分装到行为类里。    &lt;/p&gt;

&lt;p&gt;好了，写到这里，基本的代码层面的优化思路就这些了，其它就是开支散叶的过程，这里就不冗余了。下一节，咱俩聊一聊性能优化的一些思路。&lt;/p&gt;
&lt;h6 id="更多细节 --&gt;  干掉你代码中的坏味道"&gt;更多细节 --&amp;gt;  &lt;a href="http://blog.tingyun.com/web/article/detail/1094" rel="nofollow" target="_blank" title=""&gt;干掉你代码中的坏味道&lt;/a&gt;
&lt;/h6&gt;</description>
      <author>ane</author>
      <pubDate>Fri, 05 Aug 2016 17:00:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/30746</link>
      <guid>https://ruby-china.org/topics/30746</guid>
    </item>
    <item>
      <title>如何在 controller 中捕获 SyntaxError 异常</title>
      <description>&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;index&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="vi"&gt;@articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&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="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"error:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&gt;$!&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; at:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&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;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="vg"&gt;$!&lt;/span&gt; &lt;span class="c1"&gt;#表示异常信息  &lt;/span&gt;
&lt;span class="vg"&gt;$@&lt;/span&gt; &lt;span class="c1"&gt;#表示异常出现的代码位置 &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果我查找一个不存在的 article，比如这样：&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;index&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="vi"&gt;@articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt; &lt;span class="c1"&gt;# 10000是不存在的&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="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"error:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&gt;$!&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; at:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&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;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样是可以顺利捕获这个 StandardError 异常。&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;index&lt;/span&gt;
    &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="vi"&gt;@articles&lt;/span&gt;  &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt; &lt;span class="c1"&gt;#少等号&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="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"error:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&gt;$!&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; at:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vg"&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;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;console 报一个&lt;code&gt;syntax error, unexpected tCONSTANT, expecting keyword_end&lt;/code&gt;,但是代码里没法目前没有捕获到，有什么方法可以获取这个异常？&lt;/p&gt;

&lt;p&gt;SyntaxError 异常，在 web-console 这个 gem 中是可以捕获的，目前没搞明白，它是通过怎样的方式捕获&lt;/p&gt;

&lt;p&gt;rails4 中使用了 web-console，event = ActiveSupport::Notifications::Event.new (*args).payload[:request].env["web_console.exception"] 这种方式是可以获取 SyntaxError&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Thu, 10 Sep 2015 18:26:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/27275</link>
      <guid>https://ruby-china.org/topics/27275</guid>
    </item>
    <item>
      <title>如何在回掉函数中跳出 while 循环</title>
      <description>&lt;p&gt;虽然是 js 代码，但是还是求助万能的 ruby-china.&lt;/p&gt;

&lt;p&gt;需求是这样的：“抓取某个网站的数据，因为我不知道它具体分多少页，所以我只能在抓取的时候判断是否是最后一页”&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while(flag){
    download(url, function(data) {
        if(data){
            var $ = cheerio.load(data);
            if($(".noresult").length==1){
                console.log('空');
                               flag=false;
            }
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是因为 node 的异步原因，这样是没法实现的，能有什么好的办法吗？&lt;/p&gt;

&lt;p&gt;============================================================================================
昨天一下午写不出来，今天早上居然 5 分钟内写出来了，思路是如此的清晰。拿出来，求拍砖，求改进。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var flag =true;
while(flag){
    page++;
    flag= (function(flag){ 
       return flag;
    })(download(url+page, function(data) {
        if(data){
            var $ = cheerio.load(data);
            if($(".noresult").length==1){
                console.log('空');
                return  false;
            }
                  //保存抓取的信息
             return true;
        }
    }));
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>ane</author>
      <pubDate>Wed, 03 Dec 2014 09:59:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/22991</link>
      <guid>https://ruby-china.org/topics/22991</guid>
    </item>
    <item>
      <title>rails 源码里的测试如何运行啊</title>
      <description>&lt;p&gt;我跑 activemodel 里的测试文件提示错误，应该怎么测试？&lt;/p&gt;

&lt;p&gt;====================================================================================&lt;/p&gt;

&lt;p&gt;最近也觉得 gem 有点乱了，所以花了点时间重新整理了一下，重新执行 bundle install 的时候，依旧提示 2-1-stable 的错误。&lt;/p&gt;

&lt;p&gt;最后结论是，原来自己 fork 了分支的 master 和原来 rails/rails 的 master 不是一个分支，自己 fork 的分支会将指引到一个 gemfile 是调用 github 地址的 2-1-stable.大家多注意一下。可以用 git tag 看看应该切到那个分支&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Wed, 26 Nov 2014 10:36:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/22874</link>
      <guid>https://ruby-china.org/topics/22874</guid>
    </item>
    <item>
      <title> ActiveSupport::Autoload 中的一点疑惑</title>
      <description>&lt;p&gt;刚刚跟踪代码的时候，恰好看到了  ActiveSupport::Autoload 里面的一个奇怪的地方，&lt;/p&gt;

&lt;p&gt;github 地址&lt;a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/dependencies/autoload.rb" rel="nofollow" target="_blank"&gt;https://github.com/rails/rails/blob/master/activesupport/lib/active_support/dependencies/autoload.rb&lt;/a&gt; 37 行&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;autoload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;const_name&lt;/span&gt;&lt;span class="p"&gt;,&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;@_at_path&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;path&lt;/span&gt;
    &lt;span class="n"&gt;full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@_under_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const_name&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;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Inflector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;underscore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full&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="vi"&gt;@_eager_autoload&lt;/span&gt;
    &lt;span class="vi"&gt;@_autoloads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;const_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;path&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;super&lt;/span&gt; &lt;span class="n"&gt;const_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码里的这行&lt;code&gt;full = [name, @_under_path, const_name.to_s].compact.join("::")&lt;/code&gt;中的这个 name 变量是那里来的？找半天也没发现个所以然。&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Tue, 25 Nov 2014 10:39:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/22849</link>
      <guid>https://ruby-china.org/topics/22849</guid>
    </item>
    <item>
      <title>动态定义路由</title>
      <description>&lt;p&gt;本着大胆假设，小心求证的路线。&lt;/p&gt;

&lt;p&gt;假如有这样的一个需求，就一个静态页面，页面有个表单，理论上的做法就是建 controller,model  balbalbal.&lt;/p&gt;

&lt;p&gt;但是这样的需求一个接一个出现，而且彼此都是毫无关联的，于是就想做成一个动态的功能，直接将 HTML 页面存起来，表单数据存&lt;/p&gt;

&lt;p&gt;mongo 里。&lt;/p&gt;

&lt;p&gt;但是，路由似乎还是一个需要程序员去写。若采用程序写入路由的方式，你还得需要重新加载一次 application。&lt;/p&gt;

&lt;p&gt;so, 类似这样的需求有什么经验之谈？&lt;/p&gt;

&lt;p&gt;最后，楼主 采用了 1，2 楼的建议，写了这样的路由&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get 'auto/:kind/page' =&amp;gt; 'pages#show'

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是为了满足市场的需求（要求 URL 是 xxx.com/market/mic），又不得不暂时加入这样一条路由
 &lt;code&gt;get 'market/mic', controller:  :pages, action: :show, kind: :mic&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;controller 中采用 &lt;code&gt;Mongo.find_by_kind(params[:kind]).view&lt;/code&gt;的写法。而&lt;code&gt;view中就存储了HTML代片&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;但是撸主依然想找一个好方法，去实现这样的需求，&lt;/p&gt;

&lt;p&gt;至少将 &lt;code&gt;get 'market/mic', controller: :pages, action: :show, kind: :mic&lt;/code&gt; 这样的&lt;/p&gt;

&lt;p&gt;路由美化一下也是极好的。若是能提出更好改良方案，那便更是不负恩泽了。&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Mon, 17 Nov 2014 11:50:19 +0800</pubDate>
      <link>https://ruby-china.org/topics/22713</link>
      <guid>https://ruby-china.org/topics/22713</guid>
    </item>
    <item>
      <title>Homebrew 在 Mac 中如何维护</title>
      <description>&lt;p&gt;前两天发现 Homebrew 的安装目录，又有许多未提交。&lt;/p&gt;

&lt;p&gt;一时不知道怎么处理，求破！&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2014/4b8c348d3fd44eb8f1c77cb20c69f5c9.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Mon, 03 Nov 2014 15:35:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/22433</link>
      <guid>https://ruby-china.org/topics/22433</guid>
    </item>
    <item>
      <title>写了段 swift 代码，以为会死循环</title>
      <description>&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class TriangleAndSquare {
    var triangle: EquilateralTriangle {
    willSet {
        square.sideLength = newValue.sideLength
        println("--------")
    }
    didSet {
        println("------++++++")
        Square(sideLength: 50, name: "larger square")
    }
    }
    var square: Square {
    willSet {
        println("++++++++++++")
        triangle.sideLength = newValue.sideLength
    }
    didSet {
        println("+++++----------")
        EquilateralTriangle(sideLength: 150, name: "larger TriangleAndSquare")
    }
    }
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.triangle = EquilateralTriangle(sideLength: 150, name: "larger TriangleAndSquare")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原以为会死循环，可貌似没有，有清楚机制的吗？&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Wed, 01 Oct 2014 10:11:28 +0800</pubDate>
      <link>https://ruby-china.org/topics/21806</link>
      <guid>https://ruby-china.org/topics/21806</guid>
    </item>
    <item>
      <title>extend main 的作业</title>
      <description>&lt;p&gt;先上代码吧&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'thread_safe'
require 'active_support/core_ext/array/prepend_and_append'
require 'active_support/i18n'

module ActiveSupport
  module Inflector
    extend self

    # A singleton instance of this class is yielded by Inflector.inflections,
    # which can then be used to specify additional inflection rules. If passed
    # an optional locale, rules for other languages can be specified. The
    # default locale is &amp;lt;tt&amp;gt;:en&amp;lt;/tt&amp;gt;. Only rules for English are provided.
    #
    #   ActiveSupport::Inflector.inflections(:en) do |inflect|
    #     inflect.plural /^(ox)$/i, '\1\2en'
    #     inflect.singular /^(ox)en/i, '\1'
    #
    #     inflect.irregular 'octopus', 'octopi'
    #
    #     inflect.uncountable 'equipment'
    #   end
    #
    # New rules are added at the top. So in the example above, the irregular
    # rule for octopus will now be the first of the pluralization and
    # singularization rules that is runs. This guarantees that your rules run
    # before any of the rules that may already have been loaded.
    class Inflections
     ....
    end

    # Yields a singleton instance of Inflector::Inflections so you can specify
    # additional inflector rules. If passed an optional locale, rules for other
    # languages can be specified. If not specified, defaults to &amp;lt;tt&amp;gt;:en&amp;lt;/tt&amp;gt;.
    # Only rules for English are provided.
    #
    #   ActiveSupport::Inflector.inflections(:en) do |inflect|
    #     inflect.uncountable 'rails'
    #   end
    def inflections(locale = :en)
      if block_given?
        yield Inflections.instance(locale)
      else
        Inflections.instance(locale)
      end
    end
  end
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是 rails 的 Inflector.rb 的代码结构，一直疑问这个 self 是什么，&lt;code&gt;ActiveSupport::Inflector.inflections {p self}&lt;/code&gt;发现居然是‘main’。不解了，这个为何要继承这个 main?&lt;/p&gt;</description>
      <author>ane</author>
      <pubDate>Sat, 13 Sep 2014 09:32:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/21515</link>
      <guid>https://ruby-china.org/topics/21515</guid>
    </item>
    <item>
      <title>3 种写法有什么区别</title>
      <description>&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def index
  if current_user
    redirect_to market_path(current_market) and return
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def index
  if current_user
     return  redirect_to market_path(current_market) 
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def index
  if current_user
     redirect_to market_path(current_market) 
  end
end
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>ane</author>
      <pubDate>Wed, 03 Sep 2014 23:23:45 +0800</pubDate>
      <link>https://ruby-china.org/topics/21374</link>
      <guid>https://ruby-china.org/topics/21374</guid>
    </item>
  </channel>
</rss>
