<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>xiongbo (Ian)</title>
    <link>https://ruby-china.org/xiongbo</link>
    <description>Inner Peace</description>
    <language>en-us</language>
    <item>
      <title>浅谈 Ruby 中的并发， 并行和全局锁</title>
      <description>&lt;p&gt;最近在看《Working With Ruby Thread》这本书，以下是我对前几章内容的一点总结 : )&lt;/p&gt;
&lt;h2 id="并发不等于并行"&gt;并发不等于并行&lt;/h2&gt;
&lt;p&gt;几乎所有谈到并发和并行的文章都会提到一点：并发并不等于并行。那么如何理解这句话呢，这里以餐馆下订单为例子进行说明：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;并发&lt;/strong&gt;：同时有 2 桌客人点了菜，厨师同时接收到了两个菜单&lt;br&gt;
&lt;strong&gt;顺序执行&lt;/strong&gt;：如果只有一个厨师，那么他只能一个菜单，一个菜单的去完成&lt;br&gt;
&lt;strong&gt;并行执行&lt;/strong&gt;：如果有两个厨师，那么就可以并行，两个人一起做菜  &lt;/p&gt;

&lt;p&gt;将这个例子扩展到我们的 web 开发中，就可以这样理解：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;并发&lt;/strong&gt;：同时有两个客户端对服务器发起了请求&lt;br&gt;
&lt;strong&gt;顺序执行&lt;/strong&gt;：服务器只有一个进程（线程）处理请求，完成了第一个请求才能完成第二个请求，所以第二个请求就需要等待。&lt;br&gt;
&lt;strong&gt;并行执行&lt;/strong&gt;：服务器有两个进程（线程）处理请求，两个请求都能得到响应，而不存在先后的问题。  &lt;/p&gt;
&lt;h2 id="线程的处理"&gt;线程的处理&lt;/h2&gt;
&lt;p&gt;那么，ruby 中如何描述一个并发的行为呢，看这样一段代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&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="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; 
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"不用等3秒就可以看到我"&lt;/span&gt;
&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:join&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thread 的创建是非阻塞的，所以文字立即就可以输出，这样就模拟了一个并发的行为。&lt;br&gt;
接下来，对代码做一点修改：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;time&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="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&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="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;3&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;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:join&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"现在需要等3秒才可以看到我"&lt;/span&gt;
&lt;span class="nb"&gt;p&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;time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当我们执行 join 的时候，只有等到所有线程的任务都执行完成，才会最后输出。所以我们需要等待 3 秒才能看到输出的文字。&lt;/p&gt;

&lt;p&gt;但是，等等，这里是不是就是实现了并行了呢？&lt;br&gt;
从表面上来看是这样，但是很遗憾，这是一种伪并行，我们再对代码做一点修改：&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;'benchmark'&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiple_threads&lt;/span&gt;
  &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&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="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
    &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="mi"&gt;2_500_000&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="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:join&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;single_threads&lt;/span&gt;
  &lt;span class="n"&gt;time&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="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;10_000_000&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="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bm&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;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;multiple_threads&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;single_threads&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;user&lt;/span&gt;     &lt;span class="nb"&gt;system&lt;/span&gt;      &lt;span class="n"&gt;total&lt;/span&gt;        &lt;span class="n"&gt;real&lt;/span&gt;
&lt;span class="mf"&gt;0.510000&lt;/span&gt;   &lt;span class="mf"&gt;0.000000&lt;/span&gt;   &lt;span class="mf"&gt;0.510000&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="mf"&gt;0.508958&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;0.500000&lt;/span&gt;   &lt;span class="mf"&gt;0.000000&lt;/span&gt;   &lt;span class="mf"&gt;0.500000&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="mf"&gt;0.506755&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从这里可以看出，即便我们将同一个任务分成了 4 个线程并行，但是时间并没有减少，这是为什么呢？ &lt;/p&gt;

&lt;p&gt;因为有 GIL 的存在！！！&lt;/p&gt;
&lt;h2 id="全局锁"&gt;全局锁&lt;/h2&gt;
&lt;p&gt;MRI，也就是我们通常使用的 ruby 采用了一种称之为 GIL 的机制，看看它的解释：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The GIL is a global lock around the execution of Ruby code&lt;/p&gt;

&lt;p&gt;If one of those MRI processes spawns multiple threads, that group of threads will share the GIL for that process.&lt;/p&gt;

&lt;p&gt;If one of these threads wants to execute some Ruby code, it will have to acquire this lock. One, and only one, thread can hold the lock at any given time. While one thread holds the lock, other threads need to wait for their turn to acquire the lock&lt;/p&gt;

&lt;p&gt;--------- Working With Ruby Threads By Jesse Storimer -----------&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说，即便我们希望使用多线程来实现代码的并行，由于这个全局锁的存在，每次只有一个线程能够执行代码，至于哪个线程能够执行，这个取决于底层操作系统的实现。&lt;br&gt;
即便我们拥有多个 CPU，也只是为每个线程的执行多提供了几个选择而已。&lt;/p&gt;

&lt;p&gt;但是我们之前&lt;code&gt;sleep&lt;/code&gt;的时候，明明实现了并行啊！ &lt;/p&gt;

&lt;p&gt;这个就是 Ruby 设计高级的地方——所有的阻塞操作是可以并行的，也就是说包括读写文件，网络请求在内的操作都是可以并行的，有代码为证：）&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'benchmark'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'net/http'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiple_threads&lt;/span&gt;
  &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://www.baidu.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&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="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
    &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="mi"&gt;25&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="no"&gt;Net&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:join&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;single_threads&lt;/span&gt;
  &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://www.baidu.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;100&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="no"&gt;Net&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="nf"&gt;join&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bm&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;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;multiple_threads&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;single_threads&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;user&lt;/span&gt;     &lt;span class="nb"&gt;system&lt;/span&gt;      &lt;span class="n"&gt;total&lt;/span&gt;        &lt;span class="n"&gt;real&lt;/span&gt;
&lt;span class="mf"&gt;0.240000&lt;/span&gt;   &lt;span class="mf"&gt;0.110000&lt;/span&gt;   &lt;span class="mf"&gt;0.350000&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="mf"&gt;3.659640&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;0.270000&lt;/span&gt;   &lt;span class="mf"&gt;0.120000&lt;/span&gt;   &lt;span class="mf"&gt;0.390000&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mf"&gt;14.167703&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么，既然有了这个锁的存在，是否意味着我们的代码就是线程安全了呢？很遗憾，不是！因为我们无法控制什么时候操作系统会终止我们当前线程的执行，并切换到另外一个线程上。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MultipleThreadTest&lt;/span&gt;
  &lt;span class="vi"&gt;@n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;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;cal&lt;/span&gt;
    &lt;span class="mi"&gt;10000&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="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&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="vi"&gt;@n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@n&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;MultipleThreadTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cal&lt;/span&gt; &lt;span class="c1"&gt;# 9584&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于&lt;code&gt;n += 1&lt;/code&gt;这种非线程安全的代码，即便有锁的存在，依旧是不安全的。&lt;/p&gt;

&lt;p&gt;最后，我们用 Sidekiq 的作者 Mike Perham 的话来结束这篇入门文章：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As soon as you introduce the Thread constant, you've probably just introduced 5 new bugs into your code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;更多的内容请参考：《Working With Ruby Thread》这本书 : )&lt;/p&gt;</description>
      <author>xiongbo</author>
      <pubDate>Sat, 29 Apr 2017 20:55:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/32902</link>
      <guid>https://ruby-china.org/topics/32902</guid>
    </item>
    <item>
      <title>请教大家关于服务器在 HTTPS 下访问出现速度非常慢的问题？</title>
      <description>&lt;h2 id="背景:"&gt;背景：&lt;/h2&gt;
&lt;p&gt;目前我们系统每天的数据量在 1000w-2000w 之间，由一台 loadbalance 和 10 台 web 服务器进行处理，web 服务器用于处理外部 api 的访问请求。服务器都部署在 AWS 上，loadbalance 上有配置 SSL, 还有 1 台备用的 loadbalance&lt;/p&gt;
&lt;h2 id="问题："&gt;问题：&lt;/h2&gt;
&lt;p&gt;不定期的会出现 HTTPS 访问速度非常慢的情况，但是只要我们切换一下服务器，就可以解决这个问题，只是过一段时间就又会出现。目前所观察到与之关联的情况是在服务器的负载突然增大的时候下会出现这个问题，但不是绝对。&lt;/p&gt;

&lt;p&gt;另外关于切换服务器，我解释一下：就是我们一共有两台服务器作为 loadbalance, 我们通过 AWS 的静态域名绑定功能可以实现动态的切换域名所指定的服务器，所以一旦出现问题，我们就把 ip 指向到另一台服务器就好了。&lt;/p&gt;
&lt;h2 id="请教："&gt;请教：&lt;/h2&gt;
&lt;p&gt;所以，现在我们想请教一下大家，有遇到过类似的问题吗？或者可能的原因是什么？如何解决？
如果需要知道更详细的信息，请回复，我会进行补充，谢谢大家！&lt;/p&gt;

&lt;p&gt;补上出现问题时 NewRelic 上的信息：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/34aa309e06083ee469b66170ebfea625.png!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2016/126ca5ac22dae899612c2c3d34a2f8f1.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>xiongbo</author>
      <pubDate>Mon, 19 Dec 2016 17:05:28 +0800</pubDate>
      <link>https://ruby-china.org/topics/31936</link>
      <guid>https://ruby-china.org/topics/31936</guid>
    </item>
    <item>
      <title>[翻译] Rails 3.2 开发标准指南</title>
      <description>&lt;p&gt;&lt;a href="https://github.com/hopsoft/rails_standards/blob/rails-3-2/README.md" rel="nofollow" target="_blank" title=""&gt;原文地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;注：我已经 fork 了一份，以后就在&lt;a href="https://github.com/xiongbo/rails_standards" rel="nofollow" target="_blank" title=""&gt;那里&lt;/a&gt;更新了貌似作者还在不停的添加新的内容&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这是 git 上的一篇关于 rails 开发实践的指南，我尝试翻译了一下，但是还是有些地方不是很明白，希望借助大家的力量纠正其中的错误，并探讨其中的合理性。&lt;/p&gt;

&lt;p&gt;翻译水平有限，还请大家多多指教！&lt;/p&gt;
&lt;h2 id="Approach"&gt;Approach&lt;/h2&gt;&lt;h2 id="使用途径"&gt;使用途径&lt;/h2&gt;
&lt;p&gt;Apply the &lt;a href="http://en.wikipedia.org/wiki/You_ain't_gonna_need_it" rel="nofollow" target="_blank" title=""&gt;YAGNI&lt;/a&gt; and 
&lt;a href="http://en.wikipedia.org/wiki/KISS_principle" rel="nofollow" target="_blank" title=""&gt;KISS&lt;/a&gt; principles to all of the following.&lt;/p&gt;

&lt;p&gt;在以下实践中贯彻 &lt;a href="http://en.wikipedia.org/wiki/You_ain't_gonna_need_it" rel="nofollow" target="_blank" title=""&gt;YAGNI&lt;/a&gt; 和
&lt;a href="http://en.wikipedia.org/wiki/KISS_principle" rel="nofollow" target="_blank" title=""&gt;KISS&lt;/a&gt; 原则&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General architecture&lt;/li&gt;
&lt;li&gt;Product and API features&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implementation specifics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;一般架构&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;产品及 API 功能&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;实现细节&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Files"&gt;Files&lt;/h2&gt;&lt;h2 id="文件"&gt;文件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Spaces not tabs&lt;/li&gt;
&lt;li&gt;tab = 2 spaces&lt;/li&gt;
&lt;li&gt;Unix line endings&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UTF-8 encoding&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;采用空格作为缩进而不是制表符&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;采用 2 个空格作为一个缩进&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;采用 Unix 行尾结束符&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;采用 UTF-8 编码格式&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Documentation"&gt;Documentation&lt;/h2&gt;&lt;h2 id="文档"&gt;文档&lt;/h2&gt;
&lt;p&gt;All classes, modules, and methods must be documented using &lt;a href="http://yardoc.org/" rel="nofollow" target="_blank" title=""&gt;YARD&lt;/a&gt; formatted comments.&lt;/p&gt;

&lt;p&gt;使用 &lt;a href="http://yardoc.org/" rel="nofollow" target="_blank" title=""&gt;YARD&lt;/a&gt; 为所有的类、模块和方法格式化注释。&lt;/p&gt;
&lt;h2 id="General Guidelines"&gt;General Guidelines&lt;/h2&gt;&lt;h2 id="一般准则"&gt;一般准则&lt;/h2&gt;
&lt;p&gt;These guidelines are based on Sandy Metz's programming "rules" which she introduced on 
&lt;a href="http://rubyrogues.com/087-rr-book-clubpractical-object-oriented-design-in-ruby-with-sandi-metz/" rel="nofollow" target="_blank" title=""&gt;Ruby Rogues&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;以下的准则均基于 Sandy Metz 在 &lt;a href="http://rubyrogues.com/087-rr-book-clubpractical-object-oriented-design-in-ruby-with-sandi-metz/" rel="nofollow" target="_blank" title=""&gt;Ruby Rogues&lt;/a&gt; 上所介绍的编程“规则”.&lt;/p&gt;

&lt;p&gt;The rules are purposefully aggressive and are designed to give you pause so your app won't run amok.
It's expected that you will break them for pragmatic reasons... &lt;strong&gt;alot&lt;/strong&gt;.
&lt;em&gt;See the note on &lt;a href="#approach" title=""&gt;YAGNI and KISS&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这些规则是故意设计成具有强制性，就是为了通过适度的“中止”，以使你的应用不会失去控制。
希望你是在基于&lt;strong&gt;很多&lt;/strong&gt;务实的原因才会打破它。&lt;em&gt;参考&lt;a href="#approach" title=""&gt;YAGNI and KISS&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Classes can be no longer than 100 lines of code. &lt;/li&gt;
&lt;li&gt;Methods can be no longer than 5 lines of code.&lt;/li&gt;
&lt;li&gt;Methods can take a maximum of 4 parameters.&lt;/li&gt;
&lt;li&gt;Controllers should only instantiate 1 object.&lt;/li&gt;
&lt;li&gt;Views should only have access to 1 instance variable.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Never directly reference another class/module from within a class. 
&lt;em&gt;Such references should be passed in&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类中的代码不超过 100 行&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;方法中的代码不超过 5 行&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;方法最多采用 4 个参数&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;控制器应该只实例化一个对象&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;视图应该只获取一个实例变量&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;永远不要直接在类中直接引用其他的类或者模块 &lt;em&gt;而应该传递这些引用&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Models"&gt;Models&lt;/h2&gt;&lt;h2 id="模型"&gt;模型&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Never use dynamic finders. e.g. &lt;code&gt;find_by_...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be thoughtful about using callbacks. They can lead to unwanted coupling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;永远不要使用动态的查找方法。例如：&lt;code&gt;find_by_...&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;仔细斟酌对回调的使用。他们会导致不必要的耦合。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;All models should be organized using the following format.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;所有的模型请采用以下的格式组织代码&lt;/strong&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;MyModel&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;Base&lt;/span&gt;
  &lt;span class="c1"&gt;# extends ...................................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# includes ..................................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# security (i.e. attr_accessible) ...........................................&lt;/span&gt;
  &lt;span class="c1"&gt;# relationships .............................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# validations ...............................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# callbacks .................................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# scopes ....................................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# additional config .........................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# class methods .............................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# public instance methods ...................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# protected instance methods ................................................&lt;/span&gt;
  &lt;span class="c1"&gt;# private instance methods ..................................................&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;NOTE: The comments listed above should exist in the file to serve as a visual reminder of the format.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;注意：以上所列出的注释需明确的编写在文件中，用于在视觉上给出必要的格式提示。&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="Model Implementation"&gt;Model Implementation&lt;/h3&gt;&lt;h3 id="模型的实现"&gt;模型的实现&lt;/h3&gt;
&lt;p&gt;Its generally a good idea to isolate different concerns into separate modules.
We recommend using Concerns as outlined in &lt;a href="http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns" rel="nofollow" target="_blank" title=""&gt;this blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;针对不同的关注点进行模块分离通常是一个好主意。我们推荐从&lt;a href="http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns" rel="nofollow" target="_blank" title=""&gt;这篇博客&lt;/a&gt;中的介绍开始。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-project
  |-app
    |-assets
    |-controllers
    |-helpers
    |-mailers
    |-models
      |-concerns &amp;lt;-----
    |-views
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="Guidelines"&gt;Guidelines&lt;/h4&gt;&lt;h4 id="指导方针"&gt;指导方针&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;CRUD operations that are limited to a single model should be implemented in the model.
For example, a &lt;code&gt;full_name&lt;/code&gt; method that concats &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CRUD operations that reach beyond this model should be implemented as a Concern.
For example, a &lt;code&gt;status&lt;/code&gt; method that needs to look at several other models to calculate.&lt;/li&gt;
&lt;li&gt;Simple non-CRUD operations should be implemented as a Concern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Important!&lt;/strong&gt; Concerns should be isolated and self contained. 
They should NOT make assumptions about how the receiver is composed at runtime.
It's unacceptable for a concern to invoke methods defined in other concerns; however, 
invoking methods defined in the intended receiver is permissible.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complex &lt;strong&gt;multi-step&lt;/strong&gt; operations should be implemented as a process. &lt;u&gt;See below.&lt;/u&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;限于单个模型的 CRUD 操作应该在模型内实现。比如，采用&lt;code&gt;full_name&lt;/code&gt;方法合并&lt;code&gt;first_name&lt;/code&gt;和&lt;code&gt;last_name&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;超出当前模型的 CRUD 操作应该作为一个关注点。比如，&lt;code&gt;status&lt;/code&gt;方法就需要依据许多其他的模型进行计算。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;简单的非 CRUD 操作应该作为一个关注点实现。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;非常重要！&lt;/strong&gt; 关注点应该独立而且自包含。他们&lt;strong&gt;不&lt;/strong&gt;应该假设接收者在运行时是如何组成的。一个关注去调用其他关注中的方法是不被接受的。然而，调用定义在预定接收者中的方法是允许的。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;复杂的 &lt;strong&gt;多步&lt;/strong&gt; 操作应该作为过程实现。&lt;u&gt;见下文&lt;/u&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Processes"&gt;Processes&lt;/h2&gt;&lt;h2 id="过程"&gt;过程&lt;/h2&gt;
&lt;p&gt;A process is defined as a &lt;strong&gt;multi-step&lt;/strong&gt; operation which includes any of the following.&lt;/p&gt;

&lt;p&gt;过程是对“多步”操作的定义，它包含以下内容。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A complex task oriented transaction is being performed.&lt;/li&gt;
&lt;li&gt;A call is made to an external service.&lt;/li&gt;
&lt;li&gt;Any OS level interaction is performed.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sending emails, exporting files, etc...&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;需要执行复杂的、任务导向的事务。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;对外部服务的调用。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;执行任何操作系统级别的交互。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;发送邮件、导出文件，等等...。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In an attempt to better manage processes, we loosely follow some domain driven development (DDD) principles.
Namely, we have added a &lt;code&gt;processes&lt;/code&gt; directory under &lt;code&gt;app&lt;/code&gt; to hold our process implementations.&lt;/p&gt;

&lt;p&gt;为了更好的管理流程，我们自由（loosely）遵循一些领域驱动模型开发原则。即，我们在&lt;code&gt;app&lt;/code&gt;目录下添加&lt;code&gt;processes&lt;/code&gt;文件夹，用于支持（hold）我们对过程的实现。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-project
  |-app
    |-assets
    |-controllers
    |-helpers
    |-mailers
    |-models
    |-processes &amp;lt;-----
    |-views
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We recommend using a tool like &lt;a href="https://github.com/hopsoft/hero" rel="nofollow" target="_blank" title=""&gt;Hero&lt;/a&gt; to help model these processes.&lt;/p&gt;

&lt;p&gt;我们推荐使用类似&lt;a href="https://github.com/hopsoft/hero" rel="nofollow" target="_blank" title=""&gt;Hero&lt;/a&gt;的工具来帮助我们对这些过程建模。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; &lt;em&gt;Do not use model or controller callbacks to invoke a process. Instead, invoke processes directly from the controller.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;重要&lt;/strong&gt; &lt;em&gt;不要通过模型或者控制器回调去调用过程。而应该取而代之的直接在控制器中调用。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="Logging"&gt;Logging&lt;/h2&gt;&lt;h2 id="日志"&gt;日志&lt;/h2&gt;
&lt;p&gt;We use the &lt;a href="https://github.com/rudionrails/yell" rel="nofollow" target="_blank" title=""&gt;Yell gem&lt;/a&gt; for logging. 
Here is an example configuration.&lt;/p&gt;

&lt;p&gt;我们使用 &lt;a href="https://github.com/rudionrails/yell" rel="nofollow" target="_blank" title=""&gt;Yell gem&lt;/a&gt; 进行日志记录。下面是一份配置范例。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# example/config/application.rb &lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Example&lt;/span&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;log_levels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:debug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:warn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:fatal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# %m : The message to be logged&lt;/span&gt;
    &lt;span class="c1"&gt;# %d : The ISO8601 Timestamp&lt;/span&gt;
    &lt;span class="c1"&gt;# %L : The log level, e.g INFO, WARN&lt;/span&gt;
    &lt;span class="c1"&gt;# %l : The log level (short), e.g. I, W&lt;/span&gt;
    &lt;span class="c1"&gt;# %p : The PID of the process from where the log event occured&lt;/span&gt;
    &lt;span class="c1"&gt;# %t : The Thread ID from where the log event occured&lt;/span&gt;
    &lt;span class="c1"&gt;# %h : The hostname of the machine from where the log event occured&lt;/span&gt;
    &lt;span class="c1"&gt;# %f : The filename from where the log event occured&lt;/span&gt;
    &lt;span class="c1"&gt;# %n : The line number of the file from where the log event occured&lt;/span&gt;
    &lt;span class="c1"&gt;# %F : The filename with path from where the log event occured&lt;/span&gt;
    &lt;span class="c1"&gt;# %M : The method where the log event occured&lt;/span&gt;
    &lt;span class="n"&gt;log_format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Yell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"[%d] [%L] [%h][%p][%t] [%F:%n:%M] %m"&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;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Yell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;adapter&lt;/span&gt; &lt;span class="no"&gt;STDOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:level&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log_levels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:format&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log_format&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;h2 id="Extensions &amp;amp; Monkey Patches"&gt;Extensions &amp;amp; Monkey Patches&lt;/h2&gt;&lt;h2 id="扩展和猴子补丁"&gt;扩展和猴子补丁&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Be thoughtful about monkey patching and look for alternative solutions first.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use an &lt;code&gt;initializer&lt;/code&gt; to load extensions &amp;amp; monkey patches.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;仔细斟酌是否需要使用猴子补丁，首先应该考虑是否有能够替代的解决方案。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;使用初始化程序（&lt;code&gt;initializer&lt;/code&gt;）载入扩展和猴子补丁。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All extensions &amp;amp; monkey patches should live under an &lt;code&gt;extensions&lt;/code&gt; directory in &lt;code&gt;lib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;所有的扩展和猴子补丁都应该存放在&lt;code&gt;lib&lt;/code&gt;目录下的&lt;code&gt;extensions&lt;/code&gt;文件夹中。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-project
  |-app
  |-config
  |-db
  |-lib
    |-extensions &amp;lt;-----
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use modules to extend objects or add monkey patches.
&lt;em&gt;This provides some introspection assistance when you need to track down weirdness.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;采用模块的方式扩展对象或添加猴子补丁。&lt;em&gt;这能够在你跟踪异常的时候为你提供一些自省的帮助&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;/p&gt;

&lt;p&gt;下面是一个例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CowboyString&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;downcase&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;upcase&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CowboyString&lt;/span&gt;&lt;span class="p"&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;ancestors&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [String, CowboyString, Enumerable, Comparable, Object, Kernel]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;***********&lt;strong&gt;&lt;em&gt;2013-01-29 更新&lt;/em&gt;&lt;/strong&gt;****************&lt;/p&gt;
&lt;h2 id="Gem Dependencies"&gt;Gem Dependencies&lt;/h2&gt;&lt;h2 id="Gem依赖性"&gt;Gem 依赖性&lt;/h2&gt;
&lt;p&gt;Gem dependencies should be hardened before deploying the application to production.
This will ensure application stability.&lt;/p&gt;

&lt;p&gt;应用所依赖的 Gem 版本在发布到生产环境的时候，必须固定。这样可以保证应用的可靠性。&lt;/p&gt;

&lt;p&gt;We recommend using &lt;a href="http://gembundler.com/v1.2/gemfile.html" rel="nofollow" target="_blank" title=""&gt;exact or tilde version specifiers&lt;/a&gt;.
Here's an example.&lt;/p&gt;

&lt;p&gt;我们推荐使用&lt;a href="http://gembundler.com/v1.2/gemfile.html" rel="nofollow" target="_blank" title=""&gt;精确或波浪线版本的说明符&lt;/a&gt;。下面是一个例子。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Gemfile&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rails'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'3.2.11'&lt;/span&gt; &lt;span class="c1"&gt;# GOOD: exact specifier&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'pg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt;0.9'&lt;/span&gt;     &lt;span class="c1"&gt;# GOOD: tilde specifier&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'nokogiri'&lt;/span&gt;        &lt;span class="c1"&gt;# BAD: no version specifier&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Bundler's Gemfile.lock solves the same problem; however, our team discovered that inadvertent &lt;code&gt;bundle update&lt;/code&gt;s were causing dependency problems.
It's much better to be explicit in the Gemfile and guarantee application stability.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;*Bundler 所使用的 Gemfile.lock 文件也可以用来解决这个问题；但是，我们的小组发现如果不小心使用了&lt;code&gt;bundle update&lt;/code&gt; 同样会导致依赖性问题。因此，为了保证应用的可靠性，在 Gemfile 中进行精确的定义还是最好的办法。&lt;/p&gt;

&lt;p&gt;This will cause your project to slowy drift away from the bleeding edge.
A strategy should be employed to ensure your project doesn't drift too far from contemporary gem versions.
For example, we are vigilant about security patch upgrades and we periodically upgrade gems on a regular basis.&lt;/p&gt;

&lt;p&gt;但是这可能会导致你的项目逐渐偏离前沿版本。
你必须采取一定的策略才能保证你不会偏得太远。
比如我们对安全补丁的升级保持关注，并周期性的进行常规升级。&lt;/p&gt;
&lt;h2 id="A Note on Client Side Frameworks"&gt;A Note on Client Side Frameworks&lt;/h2&gt;&lt;h2 id="关于客户端框架的注意事项"&gt;关于客户端框架的注意事项&lt;/h2&gt;
&lt;p&gt;Exciting things are happening in the world of client side frameworks.&lt;/p&gt;

&lt;p&gt;客户端框架的世界不断的有惊喜出现，它们包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://backbonejs.org/" rel="nofollow" target="_blank" title=""&gt;Backbone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://emberjs.com/" rel="nofollow" target="_blank" title=""&gt;Ember&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://angularjs.org/" rel="nofollow" target="_blank" title=""&gt;Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://knockoutjs.com/" rel="nofollow" target="_blank" title=""&gt;Knockout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;以及其他种种...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Be thoughtful about the decision to use a client side framework.&lt;/strong&gt;
Ask yourself if the complexity of maintaining 2 independent full stacks is the right decision.
Often times there are better and simpler solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;谨慎选择你所要使用的客户端框架。&lt;/strong&gt; 
请从自身考虑，维护 2 套独立完整的栈（stacks）是否是一个正确的决定。特别是通常都会有更好且更简单的解决方案。&lt;/p&gt;

&lt;p&gt;Read the following articles before deciding.
In the end, you should be able to articulate why your decision is the right one.&lt;/p&gt;

&lt;p&gt;在做出决定前，请先阅读以下的文章。最后，你应该能够说清楚为什么你的决定是正确的。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://37signals.com/svn/posts/3112-how-basecamp-next-got-to-be-so-damn-fast-without-using-much-client-side-ui" rel="nofollow" target="_blank" title=""&gt;How Basecamp Next got to be so damn fast without using much client-side UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://layervault.tumblr.com/post/30932219739/rails-in-realtime" rel="nofollow" target="_blank" title=""&gt;Rails in Realtime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://layervault.tumblr.com/post/31462727280/rails-in-realtime-part-2" rel="nofollow" target="_blank" title=""&gt;Rails in Realtime, Part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;In either case be mindful of "layout thrashing"
as &lt;a href="http://kellegous.com/j/2013/01/26/layout-performance/" rel="nofollow" target="_blank" title=""&gt;described here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;在以上的任何一种情况下都要留心“布局抖动”，可以参考&lt;a href="http://kellegous.com/j/2013/01/26/layout-performance/" rel="nofollow" target="_blank" title=""&gt;这篇文章&lt;/a&gt;.中的描述。&lt;/em&gt;&lt;/p&gt;</description>
      <author>xiongbo</author>
      <pubDate>Mon, 28 Jan 2013 14:28:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/8430</link>
      <guid>https://ruby-china.org/topics/8430</guid>
    </item>
  </channel>
</rss>
