<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>davidqhr (david)</title>
    <link>https://ruby-china.org/davidqhr</link>
    <description>博观而约取，厚积而博发</description>
    <language>en-us</language>
    <item>
      <title>Webrick 源码阅读笔记</title>
      <description>&lt;h2 id="写在前面"&gt;写在前面&lt;/h2&gt;
&lt;p&gt;从 rack webrick handler 开始，顺藤摸瓜一样的往下读。&lt;/p&gt;
&lt;h2 id="一些基础"&gt;一些基础&lt;/h2&gt;&lt;h3 id="SizedQueue"&gt;SizedQueue&lt;/h3&gt;
&lt;p&gt;一个线程安全的队列，有大小限制。队列为空的时候，pop 操作的线程会被阻塞。队列满的时候，push 操作的线程会被阻塞。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;q&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="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&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="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;
        &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="TCPServer"&gt;TCPServer&lt;/h3&gt;
&lt;p&gt;TCP/IP stream 型连接的服务器端套接字的类。accept 实例方法会受理客户端的连接请求，返回已连接的 TCPSocket 的实例。&lt;/p&gt;
&lt;h3 id="IO::select"&gt;IO::select&lt;/h3&gt;
&lt;p&gt;多路复用 IO。参数列表前三项为输入／输出／异常的 IO（或者子类）的实例数组。第四个参数是 timeout。第四个参数是 timeout 可以是整数、Float 或 nil(省略时的默认值)。指定为 nil 时，将会一直等到 IO 变成就绪状态。timeout 时将返回 nil，除此以外将返回一个包含 3 个元素的数组，这 3 个元素分别是等待输入／输出／异常的对象的数组 (指定数组的子集)。&lt;/p&gt;
&lt;h2 id="从rack开始"&gt;从 rack 开始&lt;/h2&gt;
&lt;p&gt;rack 可以简单的理解成 ruby frameword 和 webserver 之间的一个通用接口。一份基于 rack 开发的 web 服务可以使用 rack 支持的各种 server 来运行。rack 中的所有 server 都具有一个叫做 run 的方法，这个是 web server 的入口。那么从 rack/lib/rack/handler/webrick.rb 中可以找到如下代码。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'RACK_ENV'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'development'&lt;/span&gt;
    &lt;span class="n"&gt;default_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'development'&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="s1"&gt;'0.0.0.0'&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;:BindAddress&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="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:Host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;default_host&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:Port&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="mi"&gt;8080&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;:OutputBufferSize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="vi"&gt;@server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;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="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;@server.mount&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WEBrick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="vi"&gt;@server&lt;/span&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
    &lt;span class="vi"&gt;@server.start&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么就从 WEBrick::HTTPServer 开始，看看 mount 和 start 方法是怎么工作的。&lt;/p&gt;
&lt;h2 id="进入webrick"&gt;进入 webrick&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HTTPServer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;GenericServer&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。
其中有两个只读的实例变量：listeners, tokens。
listeners 是监听连接的 socket 数组。
tokens 是最大连接数量（并发数量）。&lt;/p&gt;
&lt;h3 id="start方法"&gt;start 方法&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;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="o"&gt;...&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="o"&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="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="vi"&gt;@logger.debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;svrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;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="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="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="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="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="o"&gt;...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;start 中，是一个循环。当没有请求的时候，主线程会被 select 阻塞。有请求的时候，针对每个输入就绪的 socket，会通过调用 socket 的 accept 方法，来产生一个与客户端通信的新 socket，而原来的 socket 依然在端口上监听。&lt;/p&gt;

&lt;p&gt;针对每个与客户端通信的 socket，webrick 会创建一个线程（相关代码在 start_thread 中，稍后提及）来处理请求，这里&lt;a href="/tokens" class="user-mention" title="@tokens"&gt;&lt;i&gt;@&lt;/i&gt;tokens&lt;/a&gt;的作用类似信号量，初始化 server 的时候，会把&lt;a href="/tokens" class="user-mention" title="@tokens"&gt;&lt;i&gt;@&lt;/i&gt;tokens&lt;/a&gt;用 nil 填充满，只有能从&lt;a href="/token" class="user-mention" title="@token"&gt;&lt;i&gt;@&lt;/i&gt;token&lt;/a&gt;获取到信号的时候，才可以创建线程，获取不到信号的时候，会阻塞主线程，以此控制并发数量。这里参见之前提到的 SizedQueue。&lt;/p&gt;

&lt;p&gt;每个请求的具体行为，就要继续查看 start_thread 了。&lt;/p&gt;
&lt;h3 id="start_thread"&gt;start_thread&lt;/h3&gt;
&lt;p&gt;这个方法中是一些异常和 logger 的处理，主要的一句是&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_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;显而易见，run(sock) 就是下个目标。&lt;/p&gt;
&lt;h3 id="run"&gt;run&lt;/h3&gt;
&lt;p&gt;这个方法，就要回到::WEBrick::HTTPServer 了。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="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="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;sock&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;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;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="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="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="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;h3 id="req.parse"&gt;req.parse&lt;/h3&gt;
&lt;p&gt;从 socket 读取请求报文，构造 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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&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="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;read_request_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@http_version.major&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="n"&gt;read_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&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;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sr"&gt;/close/io&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="s2"&gt;"connection"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@keep_alive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="sr"&gt;/keep-alive/io&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="s2"&gt;"connection"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@keep_alive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;elsif&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="vi"&gt;@keep_alive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="vi"&gt;@keep_alive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先是解析请求行，再是请求报文头部解析，最后确定 keep_alive&lt;/p&gt;

&lt;p&gt;回到 run。&lt;/p&gt;

&lt;p&gt;一般的使用情况下 server 都是 self，lookup_server 与 virtual_hosts 有关。server.service 就是 self.service，其中，找到了真正的 servlet 的实例，并调用实例的 service 方法。其中可以看看 mount 方法的作用：可以把不同的 servlet mount 不同的 url 上，形成一个路由表。&lt;/p&gt;

&lt;p&gt;rack 的 webrick handler 就是一个 webrick servlet，并且复写了 service 这个方法。server.service(req, res) 调用完毕，那么 response 的各个属性也就填好了，接着&lt;code&gt;res.send_response(sock)&lt;/code&gt;会通过 socket 来发送数据。最后根据链接是否 keep-alive 来决定是否跳出循环。&lt;/p&gt;</description>
      <author>davidqhr</author>
      <pubDate>Mon, 28 Oct 2013 23:57:28 +0800</pubDate>
      <link>https://ruby-china.org/topics/15102</link>
      <guid>https://ruby-china.org/topics/15102</guid>
    </item>
    <item>
      <title>chrome 中 websoet handshake header 中为啥没有 cookie</title>
      <description>&lt;h3 id="疑问如题"&gt;疑问如题&lt;/h3&gt;
&lt;p&gt;chrome 版本是 26.0.1410.65
firefox 中没有这个问题。&lt;/p&gt;
&lt;h3 id="chrome 下"&gt;chrome 下&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET ws://localhost:4567/games/matching-game/1 HTTP/1.1
Pragma: no-cache
Origin: http://localhost:4567
Host: localhost:4567
Sec-WebSocket-Key: 9NJMQbfnt2FSN2b2MkQ9cQ==
Upgrade: websocket
Sec-WebSocket-Extensions: x-webkit-deflate-frame
Cache-Control: no-cache
Connection: Upgrade
Sec-WebSocket-Version: 13
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="firefox 下"&gt;firefox 下&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /games/matching-game/1 HTTP/1.1
Host: localhost:4567
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: http://localhost:4567
Sec-WebSocket-Key: iTKGrDte+JmWeHmpHnFCpQ==
Cookie: rack.session=BAh7CUkiD3Nlc3Npb25faWQGOgZFRiJFODNiOTA0NmJmZDcwNDM0NjVlYjNl%0AMGU3YThiNjU1OGM1ODkxZmYwYWVjOGFhNDUwOGVlN2QxMTdjOGRlMDEzMEki%0ADXRyYWNraW5nBjsARnsISSIUSFRUUF9VU0VSX0FHRU5UBjsARiItNTA1OTFk%0AZWE0YTk3MGM2ZGM2ODhhNzEzZTFlNWUyZWRlMzEzYzA3NUkiGUhUVFBfQUND%0ARVBUX0VOQ09ESU5HBjsARiItYTBiZmM4NzZkNjhmZTdhZWE3MDBkYTVlYTg5%0AMjVhYmFjNmYyZjc5NEkiGUhUVFBfQUNDRVBUX0xBTkdVQUdFBjsARiItNTBj%0AYTM5YWY5NDU1ZTM1NmY2ZTlkOTRjZjU4NzE4ZTQzNzdhZjI5NEkiCWNzcmYG%0AOwBGIkU1Yzk1ZTQ5NTlhN2YwNjVhZDE2YTA1ZWYyMjFkMWY0ODNjMzc1Yzlh%0ANWQxZDQ4NmFhMThhYmRjYjA5ZDE1OTRlSSIMdXNlcl9pZAY7AEZpBg%3D%3D%0A--8122297af5825b47cfae2bf8f30342551dfba2a4
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
GET ws://localhost:4567/games/matching-game/1 HTTP/1.1
Pragma: no-cache
Origin: http://localhost:4567
Host: localhost:4567
Sec-WebSocket-Key: 9NJMQbfnt2FSN2b2MkQ9cQ==
Upgrade: websocket
Sec-WebSocket-Extensions: x-webkit-deflate-frame
Cache-Control: no-cache
Connection: Upgrade
Sec-WebSocket-Version: 13
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>davidqhr</author>
      <pubDate>Mon, 15 Apr 2013 19:03:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/10223</link>
      <guid>https://ruby-china.org/topics/10223</guid>
    </item>
    <item>
      <title>observe 中获取 session</title>
      <description>&lt;p&gt;正常来说，model 层拿不到 session 的信息的，但是如果想直接拿到 session，有什么好办法么？&lt;/p&gt;

&lt;p&gt;目前想到的一种方法是这样，可是会不会有线程安全问题？&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyObserverClass&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Observer&lt;/span&gt;
  &lt;span class="n"&gt;cattr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:current_user&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;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;before_filter&lt;/span&gt; &lt;span class="ss"&gt;:set_current_user_for_observer&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_current_user_for_observer&lt;/span&gt;
    &lt;span class="no"&gt;MyObserverClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user&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;如上问题，和 rails 的线程机制，有人能给我解答一下么？  &lt;/p&gt;</description>
      <author>davidqhr</author>
      <pubDate>Fri, 25 May 2012 11:19:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/3488</link>
      <guid>https://ruby-china.org/topics/3488</guid>
    </item>
    <item>
      <title>这头像咋弄回来啊！？</title>
      <description>&lt;p&gt;上传了个图片当头像，但是！咋还原回 gravatar 的啊？  &lt;/p&gt;</description>
      <author>davidqhr</author>
      <pubDate>Mon, 23 Apr 2012 19:19:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/2842</link>
      <guid>https://ruby-china.org/topics/2842</guid>
    </item>
    <item>
      <title>请教 Test::unit 下 sunspot 测试的问题</title>
      <description>&lt;p&gt;在测试的时候，要运行 RAILS_ENV=test rake sunspot:solr:start 来启动 8981 端口的 solr 引擎，有没有什么方法，能做到不用手动去启动服务器，由测试中的代码来控制开关，而且只在想用到 sunspot 的测试文件中启动。&lt;/p&gt;</description>
      <author>davidqhr</author>
      <pubDate>Sat, 07 Apr 2012 12:12:01 +0800</pubDate>
      <link>https://ruby-china.org/topics/2446</link>
      <guid>https://ruby-china.org/topics/2446</guid>
    </item>
    <item>
      <title>instance_exec 和 instance_eval 有啥区别啊？</title>
      <description>&lt;p&gt;还有 class_exec  和 class_eval
求解释，求例子啊～
先谢过各位～  &lt;/p&gt;</description>
      <author>davidqhr</author>
      <pubDate>Fri, 30 Mar 2012 20:14:59 +0800</pubDate>
      <link>https://ruby-china.org/topics/2286</link>
      <guid>https://ruby-china.org/topics/2286</guid>
    </item>
    <item>
      <title>做 rails 全文搜索的 gem，哪个最好呢？</title>
      <description>&lt;p&gt;数据库用的是关系型数据库，看过 wiki，里边提到了 thinking sphinx
还有其他的不错的么，请各位给推荐推荐呗～  &lt;/p&gt;</description>
      <author>davidqhr</author>
      <pubDate>Tue, 27 Mar 2012 10:14:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/2157</link>
      <guid>https://ruby-china.org/topics/2157</guid>
    </item>
    <item>
      <title>thinking sphinx 嵌入关联 建立索引的问题</title>
      <description>&lt;h3 id="问题如下"&gt;问题如下&lt;/h3&gt;
&lt;p&gt;有三个模型 &lt;strong&gt;Bill&lt;/strong&gt; ,&lt;strong&gt;LineItem&lt;/strong&gt; and &lt;strong&gt;product&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bill has_many :line_items
LineItem belongs_to  :product


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Product 有两个属性：number and name&lt;/p&gt;
&lt;h3 id="例子:"&gt;例子：&lt;/h3&gt;
&lt;p&gt;假设  a_bill.line_items.first.product.number = "a_product_number" &lt;/p&gt;

&lt;p&gt;我现在想在搜索矿中输入"a_product_number"，找到包含这个 product 的 bill&lt;/p&gt;

&lt;p&gt;应该如何定义 index 呢？&lt;/p&gt;

&lt;p&gt;这是当前的定义，可是找不到我想要的那个 bill 记录&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;define_index do
    ...
    indexes line_items.product(:number)
    ...

    set_property :delta =&amp;gt; true  
end


&lt;/code&gt;&lt;/pre&gt;</description>
      <author>davidqhr</author>
      <pubDate>Fri, 16 Mar 2012 17:35:24 +0800</pubDate>
      <link>https://ruby-china.org/topics/1910</link>
      <guid>https://ruby-china.org/topics/1910</guid>
    </item>
    <item>
      <title>thinking-sphinx 建立索引速度慢的问题</title>
      <description>&lt;p&gt;问题 RT
数据库使用的是 mysql
给一张表建立索引，一共 36 条记录，不过记录很相似。竟然用了 200 多秒，这是什么问题导致的呢？麻烦各位帮解答下，mem_limit: 128M&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;indexing index 'product_core'...
collected 36 docs, 0.0 MB
sorted 0.0 Mhits, 100.0% done
total 36 docs, 5864 bytes
total 234.095 sec, 25 bytes/sec, 0.15 docs/sec
indexing index 'product_delta'...
collected 0 docs, 0.0 MB
total 0 docs, 0 bytes
total 0.002 sec, 0 bytes/sec, 0.00 docs/sec
skipping non-plain index 'product'...
total 4 reads, 0.000 sec, 1.4 kb/call avg, 0.0 msec/call avg
total 14 writes, 0.000 sec, 0.8 kb/call avg, 0.0 msec/call avg
rotating indices: succesfully sent SIGHUP to searchd (pid=3637).

real    4m9.765s
user    0m40.307s
sys 0m17.785s


&lt;/code&gt;&lt;/pre&gt;</description>
      <author>davidqhr</author>
      <pubDate>Sun, 11 Mar 2012 10:22:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/1777</link>
      <guid>https://ruby-china.org/topics/1777</guid>
    </item>
  </channel>
</rss>
