<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>vincent (谢文威)</title>
    <link>https://ruby-china.org/vincent</link>
    <description>写优雅的程序，做一个优雅的人</description>
    <language>en-us</language>
    <item>
      <title>[上海] 薄荷 2019 年春季诚聘 Ruby 工程师</title>
      <description>&lt;h3 id="概要"&gt;概要&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;公司：薄荷健康科技 &lt;/li&gt;
&lt;li&gt;坐标：上海浦东世纪大道地铁站附近&lt;/li&gt;
&lt;li&gt;职位：Ruby 工程师 2~3 名&lt;/li&gt;
&lt;li&gt;工作：薄荷后端系统开发，架构设计，代码重构和性能优化&lt;/li&gt;
&lt;li&gt;薪酬：中级（15~20k），高级（20~35k）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="关于薄荷"&gt;关于薄荷&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;薄荷&lt;/strong&gt; 是一家在健康领域领先的互联网公司，主要应用包括“薄荷健康”和“食物库”。“薄荷健康”是中国减肥第一应用，长期位于 App Store 健康健美榜和各大 Android 应用市场健康或生活类应用前列，总用户已达数千万级别。“食物库”是中国食物营养第一应用，拥有中国最全的食物营养数据以及专业饮食营养指导工具和服务。&lt;/p&gt;

&lt;p&gt;薄荷是国内较早使用 Ruby 的公司，从 2007 年开始所有的后端核心系统全部基于 Ruby 构建。随着系统规模不断增长，使用 Ruby 过程中遇到很多问题和挑战，但是我们没有放弃，迎难而上，经受住了考验。我们一直对 Ruby 充满了爱和信心，但我们也认为每一种语言和框架都有它的优势和劣势、擅长的应用场景，所以我们很早实施了微服务化，积极、开放地引入其它语言，如 Go、Java 和 Python，薄荷未来后端技术栈的主要语言会是 Ruby 和 Go。&lt;/p&gt;
&lt;h3 id="关于职位"&gt;关于职位&lt;/h3&gt;
&lt;p&gt;2019 年春节后薄荷迎来显著增长，诚挚邀请优秀的 Ruby 工程师加盟，一些要求如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1、2 年以上软件开发经验，1 年以上 Ruby 开发经验；&lt;/li&gt;
&lt;li&gt;2、熟练掌握 Web 前端技术，对微服务、系统性能调优、数据库等有实践和见解；&lt;/li&gt;
&lt;li&gt;3、技术扎实，追求事物的本质，追求优雅的代码，欣赏单元测试文化；&lt;/li&gt;
&lt;li&gt;4、擅长解决问题，善于沟通协作，热爱技术开发，有极客精神。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们对人最看重的点包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;完全成熟，但保持年轻；&lt;/li&gt;
&lt;li&gt;完全自主，但高度自律；&lt;/li&gt;
&lt;li&gt;志存高远，但脚踏实地。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="薄荷的优势"&gt;薄荷的优势&lt;/h3&gt;
&lt;p&gt;Ruby 的工作机会不少，如果你足够优秀，一定有很多的选择，我们有些什么不同呢？&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;宽阔的成长空间&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薄荷的 Ruby 系统已经远不是一个 startup 的项目，我们的用户量早已达到千万级（未来向亿级挺进），并发量很高，系统复杂性经历多年的演化，因此一开始就要应对性能和并发性的严格挑战，同时需要良好的代码质量以应对未来扩展要求。从薄荷毕业的每一位 Ruby 工程师都有独当一面的能力。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;良好的技术氛围&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薄荷的技术团队崇尚极客精神、注重分享，工程师团队十分精干，其中有多位技术大牛 / 技术大 V，我们已经坚持多年每周三内部技术分享的传统，技术研究和讨论氛围很热烈。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;丰厚的薪酬回报&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;中级职位 15~20k，高级职位 20~35k。目前公司处于快速发展阶段，商业模式明确，营收稳健，并且已有顶尖风投基金加入（晨兴，SIG，DCM 和高通），未来发展前景可期。我们将视你为伙伴，希望通过一起努力，能够提供超出你预期的回报。&lt;/p&gt;
&lt;h3 id="联系方式"&gt;联系方式&lt;/h3&gt;
&lt;p&gt;如果您对我们的工作职位感兴趣，请把您的简历或情况发邮件到 vincent(at)boohee.com&lt;/p&gt;

&lt;p&gt;&lt;em&gt;附上一些薄荷工作环境照片&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/1767848-13ef6a06b7322471.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="640px" height="426px" alt="公司前台"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/1767848-eb3e76d306b56e18.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="640px" height="426px" alt="自由工位"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/1767848-ef0f611f7d5db261.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="640px" height="426px" alt="小餐厅"&gt;&lt;/p&gt;

&lt;p&gt;The end.&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Sun, 10 Mar 2019 12:36:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/38215</link>
      <guid>https://ruby-china.org/topics/38215</guid>
    </item>
    <item>
      <title>[上海] 薄荷 2017 春季诚聘 Ruby 工程师</title>
      <description>&lt;h3 id="薄荷诚聘 Ruby 工程师"&gt;薄荷诚聘 Ruby 工程师&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;坐标：上海浦东张江&lt;/li&gt;
&lt;li&gt;职位：Ruby 工程师 3 名&lt;/li&gt;
&lt;li&gt;工作：薄荷移动 App 和 Web App 开发，架构设计，代码重构，性能优化。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="关于薄荷"&gt;关于薄荷&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;薄荷&lt;/code&gt;是一家在健康领域领先的移动互联网公司，主要产品包括“薄荷”、“食物库”、“萤火虫”和“超模 25”。“薄荷”是减肥第一应用，长期位于 App Store 健康健美榜和各大 Android 应用市场健康或生活类应用前列，总用户已达数千万级别。“食物库”是食物营养第一应用，拥有中国最全的食物营养数据。详细介绍看这里 薄荷系列 app。
详细介绍看这里  &lt;a href="http://m.boohee.com/apps/" rel="nofollow" target="_blank" title=""&gt;薄荷系列 app&lt;/a&gt; 。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/4e005c76817cfa2351ec5e47a04b4388.png" width="300px" height="75px" alt="Logos"&gt;&lt;/p&gt;

&lt;p&gt;薄荷是国内较早使用 Ruby 的公司，从 2007 年开始所有的后端核心系统全部基于 Ruby 构建。随着系统规模不断增长，使用 Ruby 过程中遇到很多问题和挑战，甚至受到过很多质疑，但是我们没有放弃，迎难而上，经受住了考验，我们一直对 Ruby 充满了爱和信心。薄荷技术团队崇尚极客精神，热情参与 Ruby 社区各种活动，经常分享我们技术演进的各种经验教训。&lt;/p&gt;
&lt;h3 id="关于职位"&gt;关于职位&lt;/h3&gt;
&lt;p&gt;因为业务快速发展，我们诚挚邀请 Ruby 好手加盟，一些要求如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 年以上工作经验，也欢迎喜欢折腾的优秀应届毕业生；&lt;/li&gt;
&lt;li&gt;熟练掌握 Ruby 和 Rails, 以及 Web 开发相关技术，追求极致用户体验；&lt;/li&gt;
&lt;li&gt;编程基础良好，了解常见设计模式和编程最佳实践，追求优雅的代码；&lt;/li&gt;
&lt;li&gt;热爱技术开发，有极客精神，参与过开源项目或常写技术博客加分哦！&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们注重能力包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;解决问题能力。自信，上进，积极主动，目标导向。&lt;/li&gt;
&lt;li&gt;学习能力。善于学习，乐于分享，渴望每天都在进步。&lt;/li&gt;
&lt;li&gt;团队协作能力。诚实，坦率，有责任心，信任、尊重、包容。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="薄荷的优势"&gt;薄荷的优势&lt;/h3&gt;
&lt;p&gt;Ruby 的工作机会不少，如果你足够优秀，一定有很多的选择，我们有些什么不同呢？&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;丰厚的薪酬回报&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;初中级职位 10~15k，高级职位 15~30k，上不封顶，每季度还可能有丰厚的绩效奖金。目前公司处于快速发展阶段，公司商业模式确定，营收稳健，并且已有顶尖美元风投基金加入（晨兴，SIG，DCM 和高通），前景可期。我们将把你视为伙伴，希望通过一起努力，能够提供超出你预期的回报。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;宽阔的成长空间&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薄荷的 Ruby 系统已经远不是一个 startup 的项目，我们的用户量已经达到千万级，并发量很高，系统的复杂性经历了好几年的演化，因此一开始就要应对性能和并发性的严格挑战，同时必须有良好的代码质量以应对系统变化和扩展的要求。薄荷很推崇技术跨界，乐见你成长为技术专家，全栈工程师，产品经理或者增长黑客。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;良好的工作氛围，舒适的工作环境&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薄荷的技术团队崇尚极客精神、注重分享，几乎每一位工程师都经常更新自己的技术博客，每周有一个晚上是薄荷技术之夜，技术研究和讨论氛围很热烈。公司的办公室宽敞开放，我们尽可能为您提供舒适的工作环境。&lt;/p&gt;
&lt;h3 id="联系方式"&gt;联系方式&lt;/h3&gt;
&lt;p&gt;如果您对我们的工作职位感兴趣，请把您的简历或情况发邮件到 vincent(at)boohee.com
&lt;em&gt;附上一些薄荷工作环境照片&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;「进门映入眼帘的公司前台和会客处」
&lt;img src="https://l.ruby-china.com/photo/2016/12822a4126a40fe084c05991ac47aa13.jpg" width="640px" height="426px" alt="公司前台"&gt;&lt;/p&gt;

&lt;p&gt;「公司餐厅，薄荷技术之夜就在这里进行的」
&lt;img src="https://l.ruby-china.com/photo/2016/284836d42ef2eb51ac8f355eb21bda75.jpg" width="640px" height="426px" alt="公司餐厅1"&gt;&lt;/p&gt;

&lt;p&gt;「来吧，我们虚位以待」
&lt;img src="https://l.ruby-china.com/photo/2016/85a4a75d04d9d2c2d0857207dc4e6f20.jpg" width="426px" height="640px" alt="公司餐厅2"&gt;&lt;/p&gt;

&lt;p&gt;end.&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Mon, 27 Feb 2017 15:22:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/32391</link>
      <guid>https://ruby-china.org/topics/32391</guid>
    </item>
    <item>
      <title>使用 slideit 展示 markdown 格式的 slides</title>
      <description>&lt;p&gt;现在 html5 的 slides 工具已经十分流行，其中 reveal.js 是非常出色的一个，它制作出来的 slides 十分简洁美观，功能特性很丰富，几乎可以完全替代传统的 ppt 或 keynote 了。&lt;/p&gt;

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

&lt;p&gt;这个时代 markdown 是一个伟大的发明，程序员都爱它，不是吗？markdown 与 html5 slides 结合，一定十分美妙。reveal.js 的确做到了这一点，完全可以基于 markdown 语法编写 slides。&lt;/p&gt;

&lt;p&gt;不过也许强大的开源的 reveal.js 过于想让大家使用它提供的商业化的在线编辑工具了，reveal.js 本身外围的配套工具并不完整。我只想简单清爽的用 markdown 写几页 slides 而已，用 reveal.js 还得应付一些繁琐重复的配置工作，实在烦透了，所以萌发自己写一个的念头。&lt;/p&gt;

&lt;p&gt;于是就写了一个，名字叫 slideit，一个简单小巧的使用 markdown，基于 reveal.js 展示 html slides 的 gem。&lt;/p&gt;

&lt;p&gt;用法很简单。首先安装一下 gem，&lt;code&gt;gem install slideit&lt;/code&gt; 。
然后用 markdown 写一个 slides，比如 下面的 test.md&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Demo 1
Slide 1

---

## Demo 2.1
Slide 2.1

----

## Demo 2.2
Slide 2.2

----

## Demo 2.3
Slide 2.3

---

## Demo 3
Slide 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接着在命令行里运行下面的命令就 OK 了。
&lt;code&gt;slideit test.md&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;markdown 里的约定十分简单，&lt;code&gt;---&lt;/code&gt; 代表 slide 分割符，&lt;code&gt;----&lt;/code&gt; 代表父子 slide 分隔符。除此之外，你知道的 mardown 语法，比如图片和代码高亮在里面都可以用的。&lt;/p&gt;

&lt;p&gt;slideit 还支持一些特性，通过命令行可以查看：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ slideit -h
Usage: slideit [options] slide-file
    -p, --port PORT                  The port for slides server
    -t, --theme THEME                The theme for slides
        --pdf                        Output pdf file
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中最重要就是输出 pdf 文件了，只需要在命令行加上 &lt;code&gt;--pdf&lt;/code&gt; 选项就 OK 了。&lt;/p&gt;

&lt;p&gt;这是一个很小的 ruby gem，代码在 &lt;a href="https://github.com/xiewenwei/slideit" rel="nofollow" target="_blank"&gt;https://github.com/xiewenwei/slideit&lt;/a&gt; ，5 分钟就可以看完，如果有额外的需求，你可以自己改，也欢迎告诉我，：）&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Sun, 16 Oct 2016 23:43:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/31341</link>
      <guid>https://ruby-china.org/topics/31341</guid>
    </item>
    <item>
      <title>分享一个 Ruby 写的小工具 - RedisScanner</title>
      <description>&lt;p&gt;RedisScanner 是我用 Ruby 写的一个小工具，用于了解 redis 中 key 的分布情况，可以根据 pattern 统计 key 的数量，类型和长度信息。&lt;/p&gt;

&lt;p&gt;薄荷的系统中大量使用 redis，有做存储也有做缓存，但是其实对这些 redis 实例中的 key 分布情况缺乏了解。在 MySQL 或 Postgress 中可以通过 schema 了解数据库概况，在 redis 中只能通过 info 命令了解有限情况，没法简便获取 redis 实际 schema 信息，RedisScanner 就是用来解决这个问题的。&lt;/p&gt;

&lt;p&gt;它主要有以下特性：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;通过 redis scan 命令进行 key 扫描，避免 keys 命令阻塞实例；&lt;/li&gt;
&lt;li&gt;通过 patten 方式分组统计 key 个数；&lt;/li&gt;
&lt;li&gt;根据 key 的 type 统计更详细的信息，比如 list，hash 和 sorted set 的 size 信息。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;安装：
&lt;code&gt;gem install redis_scanner&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;运行：
&lt;code&gt;redis_scanner&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;默认扫描本机的 redis 实例，输出如下所示：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;+------------------------------------+-------+
| Key                                | Count |
+------------------------------------+-------+
| demo:user:&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;:counter             | 10000 |
| u:&amp;lt;uuid&amp;gt;:pf                        |    52 |
| sidekiq_demo:stat:failed:&amp;lt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;    |     4 |
| sidekiq_demo:stat:processed:&amp;lt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; |     4 |
| _sp_one:queue:default              |     1 |
| bh:queues                          |     1 |
...
+------------------------------------+-------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过增加 &lt;code&gt;-d&lt;/code&gt; 参数获取更丰富的信息：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;redis_scanner &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出如下：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;+------------------------------------+--------+-------+--------+---------+
| Key                                | Type   | Count | Size   | AvgSize |
+------------------------------------+--------+-------+--------+---------+
| demo:user:&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;:counter             | string | 10000 | 927510 |   92.75 |
| u:&amp;lt;uuid&amp;gt;:pf                        | &lt;span class="nb"&gt;hash&lt;/span&gt;   |    52 |    108 |    2.08 |
| sidekiq_demo:stat:failed:&amp;lt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;    | string |     4 |      5 |    1.25 |
| sidekiq_demo:stat:processed:&amp;lt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; | string |     4 |      6 |     1.5 |
| _sp_one:queue:default              | list   |     1 |      1 |     1.0 |
| bh:queues                          | &lt;span class="nb"&gt;set&lt;/span&gt;    |     1 |      1 |     1.0 |
| bh:retry                           | zset   |     1 |      1 |     1.0 |
| bh:stat:failed                     | string |     1 |      1 |     1.0 |
| bh:stat:processed                  | string |     1 |      1 |     1.0 |
| bus_app:app_two                    | &lt;span class="nb"&gt;hash&lt;/span&gt;   |     1 |      1 |     1.0 |
+------------------------------------+--------+-------+--------+---------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;redis_scanner 完整参数如下所示：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;redis_scanner &lt;span class="nt"&gt;--help&lt;/span&gt;

Usage: redis_scanner &lt;span class="o"&gt;[&lt;/span&gt;options]
    &lt;span class="nt"&gt;-f&lt;/span&gt;, &lt;span class="nt"&gt;--file&lt;/span&gt; FILE                  Output to file
    &lt;span class="nt"&gt;-m&lt;/span&gt;, &lt;span class="nt"&gt;--match&lt;/span&gt; MATCH                Only scan the pattern
    &lt;span class="nt"&gt;-l&lt;/span&gt;, &lt;span class="nt"&gt;--limit&lt;/span&gt; LIMIT                Only show top &amp;lt;limit&amp;gt; keys
    &lt;span class="nt"&gt;-d&lt;/span&gt;, &lt;span class="nt"&gt;--detail&lt;/span&gt;                     Show detail info&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &amp;amp; size&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nt"&gt;-t&lt;/span&gt;, &lt;span class="nt"&gt;--format&lt;/span&gt; FORMAT              Format&lt;span class="o"&gt;(&lt;/span&gt;simple or talbe. default is table&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nt"&gt;-h&lt;/span&gt;, &lt;span class="nt"&gt;--host&lt;/span&gt; HOST                  Server &lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;default: 127.0.0.1&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt;, &lt;span class="nt"&gt;--port&lt;/span&gt; PORT                  Server port &lt;span class="o"&gt;(&lt;/span&gt;default: 6379&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt;, &lt;span class="nt"&gt;--socket&lt;/span&gt; SOCKET              Server socket &lt;span class="o"&gt;(&lt;/span&gt;overrides &lt;span class="nb"&gt;hostname &lt;/span&gt;and port&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nt"&gt;-a&lt;/span&gt;, &lt;span class="nt"&gt;--password&lt;/span&gt; PASSWORD          Password to use when connecting to the server.
    &lt;span class="nt"&gt;-n&lt;/span&gt;, &lt;span class="nt"&gt;--db&lt;/span&gt; DB                      Database number
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;项目代码在 &lt;a href="https://github.com/xiewenwei/redis_scanner" rel="nofollow" target="_blank"&gt;https://github.com/xiewenwei/redis_scanner&lt;/a&gt; ，欢迎使用或者提交 issue.&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Sun, 10 Jul 2016 16:38:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/30490</link>
      <guid>https://ruby-china.org/topics/30490</guid>
    </item>
    <item>
      <title>[转] Ruby 2015 年回顾</title>
      <description>&lt;p&gt;英文原址  &lt;a href="http://www.sitepoint.com/a-retrospective-on-ruby-in-2015/" rel="nofollow" target="_blank" title=""&gt;A Retrospective on Ruby in 2015&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;中文翻译原址  &lt;a href="http://www.oschina.net/translate/a-retrospective-on-ruby-in-2015" rel="nofollow" target="_blank" title=""&gt;Ruby 2015 年回顾&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;我们来到了 2015 年末，今年对于 Ruby 来说是重要的一年。我想回顾一下今年重要的主题和其中的故事是很有必要的。就像来一次敏捷过程回顾，我将把 2015 年的工作划分成几个主题，以此回顾下我们做的怎么样。
为了保证我们想法的一致性，我将会先定义几个要考虑的主题。其实每年评价 Ruby 是否成功的完成了冲刺，都会通过以下几个方面是否来评价。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;语言的改进&lt;/li&gt;
&lt;li&gt;社区的发展&lt;/li&gt;
&lt;li&gt;跟上编程业界的其他方面&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些就是我预先定义的主题。为了衡量 Ruby 社区做的如何，我将回顾本年度上述主题相关的一些重要的博客、文档、视频。我相信肯定会疏漏，同时我也相信小伙伴儿们肯定会在评论里补上的，对吧？
最后，这一年里与 Ruby 相关的任何值得回顾的事件都可以来交流下，今天我有充足的时间来和大家讨论。我猜肯定会有好多机智的读者想向我的列表里加东西的。&lt;/p&gt;

&lt;p&gt;......&lt;/p&gt;

&lt;p&gt;总结的不错，翻译也还不错，推荐一下，详见原文&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Wed, 13 Jan 2016 12:22:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/28715</link>
      <guid>https://ruby-china.org/topics/28715</guid>
    </item>
    <item>
      <title>缓存使用的 N+1 问题 - 缓存使用陷阱 1 </title>
      <description>&lt;h3 id="缓存可能让你的应用更慢"&gt;缓存可能让你的应用更慢&lt;/h3&gt;
&lt;p&gt;缓存是提升系统性能非常有效的手段，常常起到立竿见影的效果，但是有时不恰当的使用不但起不到优化效果，反而可能让系统更慢。下面总结缓存使用过程中常见的一些陷阱。&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="缓存使用的 N+1 问题"&gt;缓存使用的 N+1 问题&lt;/h3&gt;
&lt;p&gt;大家应该比较熟悉数据库查询时的 N+1 问题，在缓存中同样存在 N+1 问题。当应用中出现需要多次读取缓存的时候，虽然单次读取缓存速度很快，但是多次读取缓存累计时间相当可观，很可能会成为一个性能瓶颈。&lt;/p&gt;

&lt;p&gt;直接给一个演示例子，生成 10000 个缓存对象 user:&lt;i&gt;:counter 存储整数，然后分别单次，批量读取缓存，统计每种方式消耗时间。&lt;/i&gt;&lt;/p&gt;

&lt;p&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;'dalli'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'active_support'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'active_support/cache/dalli_store'&lt;/span&gt;

&lt;span class="no"&gt;CACHE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DalliStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s1"&gt;'192.168.1.20'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;namespace: &lt;/span&gt;&lt;span class="s1"&gt;'demo'&lt;/span&gt;

&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;n&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;CACHE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="s2"&gt;"user:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:counter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_in_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch_size&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;slice&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
    &lt;span class="n"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slice&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;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"user:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:counter"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="no"&gt;CACHE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_multi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;batch&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="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bmbm&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;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;"1-1"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
      &lt;span class="no"&gt;CACHE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt; &lt;span class="s2"&gt;"user:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:counter"&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;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;"2-1"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;read_in_batch&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt; 

  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;" 10"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;read_in_batch&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;" 30"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;read_in_batch&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;  

  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;" 50"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;read_in_batch&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;"100"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;read_in_batch&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;  
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行结果如下所示：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;          user     system      total        real
1-1   0.660000   0.620000   1.280000 &lt;span class="o"&gt;(&lt;/span&gt;  4.236520&lt;span class="o"&gt;)&lt;/span&gt;
2-1   1.400000   0.750000   2.150000 &lt;span class="o"&gt;(&lt;/span&gt;  5.674942&lt;span class="o"&gt;)&lt;/span&gt;
 10   0.450000   0.100000   0.550000 &lt;span class="o"&gt;(&lt;/span&gt;  1.114594&lt;span class="o"&gt;)&lt;/span&gt;
 30   0.420000   0.070000   0.490000 &lt;span class="o"&gt;(&lt;/span&gt;  0.772291&lt;span class="o"&gt;)&lt;/span&gt;
 50   0.410000   0.060000   0.470000 &lt;span class="o"&gt;(&lt;/span&gt;  0.678551&lt;span class="o"&gt;)&lt;/span&gt;
100   0.380000   0.060000   0.440000 &lt;span class="o"&gt;(&lt;/span&gt;  0.560123&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从结果中可以看到，分批读取速度更快，使用 read_multi 读取（每次 50 个），要比单次 read 读取 快 6~7 倍。【特别感谢 &lt;a href="/lithium4010" class="user-mention" title="@lithium4010"&gt;&lt;i&gt;@&lt;/i&gt;lithium4010&lt;/a&gt; 指出原来测试代码一个 bug】&lt;/p&gt;
&lt;h3 id="薄荷生产环境的例子"&gt;薄荷生产环境的例子&lt;/h3&gt;
&lt;p&gt;在薄荷生产系统性能优化中，我们遇到过好几次类似问题。例如有一个 api 需要返回多个存放缓存的用户资料，单个用户资料缓存读取时间接近 1 ms，50 个用户资料消耗接近 45 ms 时间，它导致这个 api 响应时间很长，把 50 次用户资料缓存读取放到一次批量读取后，缓存读取时间减少为 3 ms 左右，应用性能立即大幅提升。&lt;/p&gt;
&lt;h3 id="N + 1 问题的原因"&gt;N + 1 问题的原因&lt;/h3&gt;
&lt;p&gt;为什么批量读取时间消耗大幅减少呢？因为每一次缓存读取过程有很多固定开销，包括加锁，系统（网络）调用等等，当使用批量读取时，这些固定开销统统节省了，而缓存服务器单次 key 查找和数据返回时间和批量相比差异没有那么大，所以整体时间大幅减少。&lt;/p&gt;
&lt;h3 id="注意事项"&gt;注意事项&lt;/h3&gt;
&lt;p&gt;批量读取增加了应用的复杂度，如果应用性能没有问题，或者缓存读取次数很少，并没有必要改造成批量读取形式。
通常我们以 fetch 方法使用缓存对象，这时批量读取方法如下所示：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_ids&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;|&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"user:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user_id&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="n"&gt;user_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&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_multi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keys&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;key&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_id&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="总结"&gt;总结&lt;/h3&gt;
&lt;p&gt;缓存虽然很快，但它毕竟也是一次 IO 操作，同样需要消耗一定时间，如果某一次特别大量读写缓存，很可能会造成性能问题，通过批量读取方式是解决该问题的有效手段。 &lt;/p&gt;
&lt;h3 id="后续"&gt;后续&lt;/h3&gt;
&lt;p&gt;这是“缓存使用陷阱”系列文章的第 1 篇，接下来还会带来：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; 2. 空数据导致缓存失效&lt;/li&gt;
&lt;li&gt; 3. 缓存大量无用数据导致内存浪费&lt;/li&gt;
&lt;li&gt; 4. 过度使用缓存&lt;/li&gt;
&lt;li&gt; 5. 把缓存当成存储使用导致数据丢失&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;打算陆续分享薄荷系统中遇到并解决的一些问题，希望对大家有所帮助。
薄荷正在招聘 ruby 伙伴，想和我们一起 pair 吗？有兴趣请看 &lt;a href="https://ruby-china.org/topics/25585" title=""&gt;薄荷热聘&lt;/a&gt;&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Wed, 27 May 2015 18:30:58 +0800</pubDate>
      <link>https://ruby-china.org/topics/25772</link>
      <guid>https://ruby-china.org/topics/25772</guid>
    </item>
    <item>
      <title>[上海] 薄荷诚聘 Ruby 工程师 3 名</title>
      <description>&lt;h3 id="薄荷科技寻找靠谱的 Ruby 工程师"&gt;薄荷科技寻找靠谱的 Ruby 工程师&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;工作地点：上海浦东张江&lt;/li&gt;
&lt;li&gt;职位：Ruby 工程师 3 名&lt;/li&gt;
&lt;li&gt;职责：薄荷移动 App 和 Web App 开发，架构设计，代码重构，性能优化。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="关于薄荷"&gt;关于薄荷&lt;/h3&gt;
&lt;p&gt;薄荷是中国领先移动健康公司，主要产品包括“薄荷”、“轻卡减肥”和“食物库”长期位于 App Store 健康健美榜前列，长期位于各大 Android 应用市场健康或生活类应用前列，总用户已达千万级。详细介绍看这里  &lt;a href="http://m.boohee.com/apps/" rel="nofollow" target="_blank" title=""&gt;薄荷系列 app&lt;/a&gt; 。&lt;/p&gt;

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

&lt;p&gt;我叫谢文威，是薄荷联合创始人，CTO。薄荷是国内较早使用 Ruby 的公司，从 2007 年开始所有的后端核心系统全部基于 Ruby 构建。我们的技术团队比较崇尚极客精神，热情的参与 Ruby 社区各种活动，去年底还在薄荷的办公室成功举办了上海年度 Ruby 聚会（见 &lt;a href="https://ruby-china.org/topics/22798" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/22798&lt;/a&gt; ）&lt;/p&gt;
&lt;h3 id="关于职位"&gt;关于职位&lt;/h3&gt;
&lt;p&gt;因为业务快速发展，我们诚挚邀请 Ruby 好手加盟，一些要求如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 年以上工作经验；&lt;/li&gt;
&lt;li&gt;熟练掌握 Ruby, Web 开发相关技术，追求极致用户体验，&lt;/li&gt;
&lt;li&gt;编程基础良好，追求优雅的代码；&lt;/li&gt;
&lt;li&gt;热爱技术开发，有极客精神，参与过开源项目或常写技术博客加分哦！&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们注重能力包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;解决问题能力。自信，积极主动，做事有条理，目标导向。&lt;/li&gt;
&lt;li&gt;学习能力。乐于学习，善于总结分享，渴望每天都在进步。&lt;/li&gt;
&lt;li&gt;团队协作能力。诚实，坦率，有责任心，信任、尊重、包容。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="薄荷有什么不同"&gt;薄荷有什么不同&lt;/h3&gt;
&lt;p&gt;Ruby 的工作机会不少，如果你足够优秀，一定有很多的选择，我们有些什么不同呢？&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;挑战你能力的舞台&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薄荷的 Ruby 系统已经远不是一个 startup 的项目，我们的用户量已经达到数千万级，并发量很高，系统的复杂性经历了好几年的演化，因此要应对大数据量，性能和并发性的严格挑战，同时必须有良好的代码质量以应对系统变化和扩展的要求。在薄荷你的能力一定能快速提升。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;良好的工作氛围，舒适的工作环境&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薄荷的技术团队崇尚极客精神、注重分享，几乎每一位工程师都经常更新自己的技术博客，平常每周有固定的主题分享，技术氛围很热烈。公司实现弹性工作制，上班时间可以相对自由选择，公司的办公室宽敞开放，我们尽可能为您提供舒适的工作环境。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;超出你预期的回报&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;中级职位 10~15k，高级职位 15~30k，上不封顶，取决于你的能力和业绩。目前公司处于快速发展阶段，公司商业模式确定，已经实现盈利，并且已有顶尖美元风投基金加入（晨兴，SIG，DCM 和高通），前景一片光明。我们将把你视为伙伴，希望通过一起努力提供超你预期的回报。&lt;/p&gt;
&lt;h3 id="联系方式"&gt;联系方式&lt;/h3&gt;
&lt;p&gt;如果您对我们的工作职位感兴趣，请把您的简历或情况发邮件到 vincent(at)boohee.com，最好能充分展示您的能力，谢谢！另外，我们也在招聘 Web 前端，iOS 和 Android 工程师。&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Fri, 15 May 2015 00:18:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/25585</link>
      <guid>https://ruby-china.org/topics/25585</guid>
    </item>
    <item>
      <title>2015年3月29日 Rubyist 上海聚会 《Ruby 服务间通信模式》PPT</title>
      <description>&lt;p&gt;这是昨天（2015.3.29）活动分享主题的 ppt，已经传到 speakerdeck，地址是 &lt;a href="https://speakerdeck.com/xiewenwei/ruby-fu-wu-jian-tong-xin-mo-shi" rel="nofollow" target="_blank"&gt;https://speakerdeck.com/xiewenwei/ruby-fu-wu-jian-tong-xin-mo-shi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;百度云盘链接 &lt;a href="http://pan.baidu.com/s/1qWqiuuS" rel="nofollow" target="_blank"&gt;http://pan.baidu.com/s/1qWqiuuS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/ec7a3583dcbd3e32b178460bf5a8dcec.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Mon, 30 Mar 2015 21:33:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/24916</link>
      <guid>https://ruby-china.org/topics/24916</guid>
    </item>
    <item>
      <title>[转] Linux 云主机幽灵漏洞修复建议 (glibc gethostbyname 缓冲区溢出漏洞)</title>
      <description>&lt;p&gt;今天收到云提供商的安全告示，问题还比较严重，转发一下，提醒大家注意。
转自：&lt;a href="http://www.ksyun.com/indexNotice/info/2015/2185.html#80b8ba67-599d-4f7d-a0e3-196dcc238f03" rel="nofollow" target="_blank"&gt;http://www.ksyun.com/indexNotice/info/2015/2185.html#80b8ba67-599d-4f7d-a0e3-196dcc238f03&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="一、漏洞背景"&gt;一、漏洞背景&lt;/h2&gt;
&lt;p&gt;代码审计公司 Qualys 的研究人员在 glibc 库中的__nss_hostname_digits_dots() 函数中发现了一个缓冲区溢出的漏洞，这个 bug 可以经过 gethostbyname*() 函数被本地或者远程的触发。&lt;/p&gt;

&lt;p&gt;1）通过 gethostbyname() 函数或 gethostbyname2() 函数，将可能产生一个堆上的缓冲区溢出。经由 gethostbyname_r() 或 gethostbyname2_r()，则会触发调用者提供的缓冲区溢出 (理论上说，调用者提供的缓冲区可位于堆，栈，.data 节和.bss 节等。但是，我们实际操作时还没有看到这样的情况)。&lt;/p&gt;

&lt;p&gt;2）漏洞产生时至多 sizeof(char* ) 个字节可被覆盖 (注意是 char*指针的大小，即 32 位系统上为 4 个字节，64 位系统为 8 个字节)。但是 payload 中只有数字 ( '0 '...' 9') ，点 ( “.”) ，和一个终止空字符 ('\0' ) 可用。&lt;/p&gt;

&lt;p&gt;3）尽管有这些限制，我们依然可以执行任意的代码。&lt;/p&gt;
&lt;h2 id="二、影响范围"&gt;二、影响范围&lt;/h2&gt;
&lt;p&gt;该漏洞影响 glibc 库版本 2.2-2.17 的 Linux 操作系统&lt;/p&gt;

&lt;p&gt;操作系统类型包括&lt;/p&gt;

&lt;p&gt;CentOS 6 &amp;amp; 7&lt;/p&gt;

&lt;p&gt;Debian 7&lt;/p&gt;

&lt;p&gt;Red Hat Enterprise Linux 6 &amp;amp; 7&lt;/p&gt;

&lt;p&gt;Ubuntu 10.04 &amp;amp; 12.04&lt;/p&gt;

&lt;p&gt;各 Linux 发行版&lt;/p&gt;
&lt;h2 id="三、漏洞测试"&gt;三、漏洞测试&lt;/h2&gt;
&lt;p&gt;1、编译以下测试代码&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include &amp;lt;netdb.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;errno.h&amp;gt;
#include &amp;lt;gnu/libc-version.h&amp;gt;
#define CANARY "in_the_coal_mine"
struct {
char buffer[1024];
char canary[sizeof(CANARY)];
} temp = { "buffer", CANARY };
int main(void) {
struct hostent resbuf;
struct hostent *result;
int herrno;
int retval;
/*** strlen (name) = size_needed - sizeof (*host_addr) - sizeof (*h_addr_ptrs) - 1; ***/
size_t len = sizeof(temp.buffer) - 16*sizeof(unsigned char) - 2*sizeof(char *) - 1;
char name[sizeof(temp.buffer)];
memset(name, '0', len);
name[len] = '\0';
retval = gethostbyname_r(name, &amp;amp;resbuf, temp.buffer, sizeof(temp.buffer), &amp;amp;result, &amp;amp;herrno);
if (strcmp(temp.canary, CANARY) != 0) {
puts("vulnerable");
exit(EXIT_SUCCESS);
}
if (retval == ERANGE) {
puts("not vulnerable");
exit(EXIT_SUCCESS);
}
puts("should not happen");
exit(EXIT_FAILURE);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试结果：
&lt;img src="https://l.ruby-china.com/photo/2015/9be06f41cfb316d85c5cd30d64235a99.jpg" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="四、漏洞修复"&gt;四、漏洞修复&lt;/h2&gt;
&lt;p&gt;更新是在 glibc 包中，但是这个库会被很多运行中的服务使用。在更新之后，每个服务都要重启一下。要找到所有依赖 glibc 的服务，请使用如下命令，它会显示所有打开的文件 (lsof)，然后找到引用 glibc 库的文件。
&lt;code&gt;$ lsof | grep libc | awk '{print $1}' | sort | uniq&lt;/code&gt;
&lt;img src="https://l.ruby-china.com/photo/2015/64bcab749d1a1846f04d1210a58aa57f.png" title="" alt=""&gt;
最安全的办法是重启所有你上面用 lsof 找到的服务。当然也可以重启服务器。建议如果机器或服务不是对外开放访问的，还是别升 glibc 了。优先升级重启对外开放的服务器，如 Web 和 Mail 等等。&lt;/p&gt;

&lt;p&gt;RHEL/CentOS目前没有补丁，红帽发布了CVE信息(&lt;a href="https://access.redhat.com/security/cve/CVE-2015-0235" rel="nofollow" target="_blank"&gt;https://access.redhat.com/security/cve/CVE-2015-0235&lt;/a&gt;)，你可以在这个页面跟踪一下修复进度。&lt;/p&gt;

&lt;p&gt;CentOS 也正在处理这个事情，处理完之后会发布到它的镜像上。&lt;/p&gt;

&lt;p&gt;Debian 和 Ubuntu 已经有更新了，你可以直接更新它们。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt­-get update

apt-get install libc6

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/9e478fff1dbfcb506ce5048772b2b63a.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Thu, 29 Jan 2015 22:41:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/24012</link>
      <guid>https://ruby-china.org/topics/24012</guid>
    </item>
    <item>
      <title>Rails 中 mattr_accessor 一处文档错误</title>
      <description>&lt;h2 id="发现错误"&gt;发现错误&lt;/h2&gt;
&lt;p&gt;最近写一个 gem 的时候偶然接触到 Rails ActiveSupport 扩展 module 的 mattr_accessor 系列方法，包括 mattr_accessor、mattr_reader 和 mattr_writer。
记得以前探索 Rails 源代码的时候经常遇到 mattr_accessor 方法，当时并没有细究，这次碰巧要自己用到，所以仔细研究了其文档和实现源码，居然发现文档描述有明显的错误。&lt;/p&gt;

&lt;p&gt;Rails 的官方文档中提到，mattr_accessor 用于为类属性定义类和实例对象两者的访问器，然后还提供一段示例代码演示其用法。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;mattr_accessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;syms&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;blk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kp"&gt;public&lt;/span&gt;
&lt;span class="no"&gt;Defines&lt;/span&gt; &lt;span class="n"&gt;both&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;accessors&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="o"&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;module&lt;/span&gt; &lt;span class="nn"&gt;HairColors&lt;/span&gt;
  &lt;span class="n"&gt;mattr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:hair_colors&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;Person&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;HairColors&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hair_colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:brown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:blonde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:red&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hair_colors&lt;/span&gt;     &lt;span class="c1"&gt;# =&amp;gt; [:brown, :black, :blonde, :red]&lt;/span&gt;
&lt;span class="no"&gt;Person&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="nf"&gt;hair_colors&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [:brown, :black, :blonde, :red]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当我运行该代码的时候，发现无法运行，报错在 &lt;code&gt;Person.hair_colors&lt;/code&gt; 处，信息如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hair_colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:brown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:blonde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:red&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="no"&gt;NoMethodError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`hair_colors=' for Person:Class
from (pry):7:in `&lt;/span&gt;&lt;span class="n"&gt;__pry__&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;刚开始还有点不相信，分别在 Rails 4.1.8，4.2.0 和 3.2.x，Ruby 1.9.3，2.0.0 和 2.1.5 下运行，都出现这个错误，这下确信文档描述应该是有问题的。我想把问题彻底搞清楚，于是仔细查看 Rails ActiveSupport 中相关的源代码，发现的确是文档描述的行为和程序实际行为不符。&lt;/p&gt;
&lt;h2 id="探寻原因"&gt;探寻原因&lt;/h2&gt;
&lt;p&gt;mattr_accessor 系列方法的代码在 &lt;code&gt;rails/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb&lt;/code&gt; 文件中，相关的代码并不复杂，部分代码如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 此处忽略注释&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mattr_writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;syms&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;syms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_options!&lt;/span&gt;
  &lt;span class="n"&gt;syms&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;sym&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NameError&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="s2"&gt;"invalid attribute name: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sym&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="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^[_A-Za-z]\w*$/&lt;/span&gt;
    &lt;span class="nb"&gt;class_eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&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="sh"&gt;
      @@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; = nil unless defined? @@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;

      def self.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;=(obj)
        @@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; = obj
      end
&lt;/span&gt;&lt;span class="no"&gt;    EOS&lt;/span&gt;

    &lt;span class="k"&gt;unless&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;:instance_writer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kp"&gt;false&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;:instance_accessor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
      &lt;span class="nb"&gt;class_eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;__LINE__&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="sh"&gt;
        def &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;=(obj)
          @@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; = obj
        end
&lt;/span&gt;&lt;span class="no"&gt;      EOS&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="nb"&gt;send&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="n"&gt;sym&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="k"&gt;yield&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="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 此处忽略注释&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mattr_accessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;syms&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;blk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;mattr_reader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;syms&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;blk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;mattr_writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;syms&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;blk&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;从中可以看到，mattr_accessor 分别 call mattr_reader 和 mattr_writer，mattr_writer 的主要逻辑是定义类变量（class variable，命名为 &lt;code&gt;@@#{sym}&lt;/code&gt;），然后定义类方法（见 &lt;code&gt;def self.#{sym}=(obj)&lt;/code&gt;）和普通实例方法（见 &lt;code&gt;def #{sym}=(obj)&lt;/code&gt;）。当 &lt;code&gt;Person include HairColors&lt;/code&gt; 的时候，普通实例方法被 mix 进 Person 的，但类方法并不会被 mix 进 Person。可以用下面更简明的例子演示 include module 并不能 mix 类方法。&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;Foo&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method1&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"method1 in Foo"&lt;/span&gt;
  &lt;span class="k"&gt;end&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;method2&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"method2 in Foo as class method"&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;Bar&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Foo&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Bar&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="nf"&gt;method1&lt;/span&gt;  &lt;span class="c1"&gt;# =&amp;gt; "method1 in Foo"&lt;/span&gt;

&lt;span class="no"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method2&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "method2 in Foo as class method"&lt;/span&gt;

&lt;span class="no"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method2&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; NoMethodError: undefined method `method2' for #&amp;lt;Bar:0x007fdb0a121488&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原来 mattr_accessor 为 HairColors 生成的 &lt;code&gt;def self.hair_colors&lt;/code&gt; 类方法不能 mix 进 Person，从而导致 Person.hair_colors 出错，但是 Person.new.hair_colors 能够正常运行的。&lt;/p&gt;
&lt;h2 id="解决方法"&gt;解决方法&lt;/h2&gt;
&lt;p&gt;如果希望 Person 通过类方法和实例方法都能使用 hair_colors，应该怎么做呢？
可以把 mattr_accessor 放在 included 钩子中执行，代码如下所示：&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;HairColors&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;
  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;mattr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:hair_colors&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;Person&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;HairColors&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hair_colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:brown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:blonde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:red&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hair_colors&lt;/span&gt;     &lt;span class="c1"&gt;# =&amp;gt; [:brown, :black, :blonde, :red]&lt;/span&gt;
&lt;span class="no"&gt;Person&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="nf"&gt;hair_colors&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [:brown, :black, :blonde, :red]&lt;/span&gt;
&lt;span class="no"&gt;HairColors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hair_colors&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; undefined method `hair_colors' for HairColors:Module&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它引发的一个新问题是 &lt;code&gt;HairColors.hair_colors&lt;/code&gt;  变得不可用了，但我发现 Rails 中大多是使用这种手法处理的，这种情况下估计不怎么要直接用到 &lt;code&gt;HairColors.hair_colors&lt;/code&gt; 吧。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;Rails 官方文档中关于 mattr_accessor 的描述的确有问题，示例代码不能正确运行，而且还会误导使用者，通过仔细探索 Rails 的源代码，找到了问题的源头，并且彻底弄清楚了它的实现机制。我已经修正文档的错误，提交了 &lt;code&gt;pull request&lt;/code&gt;，希望能够被接受。&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Sun, 11 Jan 2015 23:08:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/23675</link>
      <guid>https://ruby-china.org/topics/23675</guid>
    </item>
    <item>
      <title>[上海] 薄荷诚邀 Ruby 好手 (新传图片，薄荷 girl 向你招手，是否考虑换个不一样的环境呢)</title>
      <description>&lt;h3 id="薄荷科技寻找靠谱的 Ruby 工程师"&gt;薄荷科技寻找靠谱的 Ruby 工程师&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;工作地点：上海浦东张江&lt;/li&gt;
&lt;li&gt;职位：Ruby 工程师（中级 2 名，高级 2 名）&lt;/li&gt;
&lt;li&gt;职责：薄荷移动 App 和 Web App 开发，架构设计，代码重构，性能优化。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="关于薄荷"&gt;关于薄荷&lt;/h3&gt;
&lt;p&gt;薄荷是中国领先移动健康公司，主要产品包括“薄荷”、“轻卡减肥”和“食物图书馆”长期位于 App Store 健康健美榜前列，长期位于各大 Android 应用市场健康或生活类应用前列，总用户已达千万级。详细介绍看这里  &lt;a href="http://m.boohee.com/apps/" rel="nofollow" target="_blank" title=""&gt;薄荷系列 app&lt;/a&gt; 。&lt;/p&gt;

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

&lt;p&gt;我叫谢文威，是薄荷联合创始人，CTO。薄荷是国内较早使用 Ruby 的公司，从 2007 年开始所有的后端核心系统全部基于 Ruby 构建。我们的技术团队比较崇尚极客精神，热情的参与 Ruby 社区各种活动，上个月还在薄荷的办公室成功举办了长三角年度 Ruby 聚会（见 &lt;a href="https://ruby-china.org/topics/22798" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/22798&lt;/a&gt; ）&lt;/p&gt;
&lt;h3 id="关于职位"&gt;关于职位&lt;/h3&gt;
&lt;p&gt;因为业务快速发展，我们诚挚邀请 Ruby 好手加盟，一些要求如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 年以上工作经验；&lt;/li&gt;
&lt;li&gt;熟练掌握 Ruby, Web 开发相关技术，追求极致用户体验，&lt;/li&gt;
&lt;li&gt;编程基础良好，追求优雅的代码；&lt;/li&gt;
&lt;li&gt;热爱技术开发，有极客精神，参与过开源项目或常写技术博客加分哦！&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们注重能力包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;解决问题能力。自信，积极主动，做事有条理，目标导向。&lt;/li&gt;
&lt;li&gt;学习能力。乐于学习，善于总结分享，渴望每天都在进步。&lt;/li&gt;
&lt;li&gt;团队协作能力。诚实，坦率，有责任心，信任、尊重、包容。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="这里有什么不同"&gt;这里有什么不同&lt;/h3&gt;
&lt;p&gt;Ruby 的工作机会不少，如果你足够优秀，一定有很多的选择，我们有些什么不同呢？&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;挑战你能力的舞台&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薄荷的 Ruby 系统已经远不是一个 startup 的项目，我们的用户量已经达到千万级，并发量很高，系统的复杂性经历了好几年的演化，因此一开始就要应对性能和并发性的严格挑战，同时必须有良好的代码质量以应对系统变化和扩展的要求。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;良好的工作氛围，舒适的工作环境&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;薄荷的技术团队崇尚极客精神、注重分享，几乎每一位工程师都经常更新自己的技术博客，平常每周有固定的主题分享，技术研究和讨论氛围很热烈。公司实现弹性工作制，上班时间可以相对自由选择，公司的办公室宽敞开放，我们尽可能为您提供舒适的工作环境。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;超出你预期的回报&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;中级职位 10~15k，高级职位 15~30k，上不封顶，取决于你的能力和业绩。目前公司处于快速发展阶段，公司商业模式确定，并且已有顶尖美元风投基金加入（晨兴，SIG，DCM 和高通），未来充满无限想象。我们将把你视为伙伴，希望通过一起努力，能够提供远超你预期的回报。&lt;/p&gt;
&lt;h3 id="联系方式"&gt;联系方式&lt;/h3&gt;
&lt;p&gt;如果您对我们的工作职位感兴趣，请把您的简历或情况发邮件到 vincent(at)boohee.com，最好能充分展示您的能力，谢谢！另外，我们也在招聘 Web 前端，iOS 和 Android 工程师哦。&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Fri, 02 Jan 2015 00:25:23 +0800</pubDate>
      <link>https://ruby-china.org/topics/23506</link>
      <guid>https://ruby-china.org/topics/23506</guid>
    </item>
    <item>
      <title>《Ruby 快与慢》的 slides</title>
      <description>&lt;p&gt;习惯把 ppt 放 slideshare 上了，访问需要翻墙。
&lt;a href="http://www.slideshare.net/vincent253/ruby-speed" rel="nofollow" target="_blank"&gt;http://www.slideshare.net/vincent253/ruby-speed&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;百度空间也放了一份：
&lt;a href="http://pan.baidu.com/s/1bnnMOSv" rel="nofollow" target="_blank"&gt;http://pan.baidu.com/s/1bnnMOSv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;十分抱歉！这次主题没选好，过于面向初学者了，也没有讲好，没什么干货，让大家失望了。
拜托拍砖轻一点，希望今后能发些有质量的技术文章弥补。&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Mon, 03 Nov 2014 21:15:32 +0800</pubDate>
      <link>https://ruby-china.org/topics/22444</link>
      <guid>https://ruby-china.org/topics/22444</guid>
    </item>
    <item>
      <title>[上海] 薄荷诚聘 Ruby 工程师 2~3 名 Change the women Change the world </title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2014/cf7870cc60be6e1fee776179a51a2e1b.png" title="" alt="薄荷诚聘"&gt;&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Wed, 10 Sep 2014 19:32:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/21461</link>
      <guid>https://ruby-china.org/topics/21461</guid>
    </item>
    <item>
      <title>MongoDB 那些坑</title>
      <description>&lt;p&gt;MongoDB 是目前炙手可热的 NoSQL 文档型数据库，它提供的一些特性很棒：如自动 failover 机制，自动 sharding，无模式 schemaless，大部分情况下性能也很棒。但是薄荷在深入使用 MongoDB 过程中，遇到了不少问题，下面总结几个我们遇到的坑。特别申明：我们目前用的 MongoDB 版本是 2.4.10，曾经升级到 MongoDB 2.6.0 版本，问题依然存在，又回退到 2.4.10 版本。&lt;/p&gt;
&lt;h2 id="MongoDB 数据库级锁"&gt;MongoDB 数据库级锁&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;坑爹指数：5 星（最高 5 星）&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MongoDB 的锁机制和一般关系数据库如 MySQL（InnoDB）, Oracle 有很大的差异，InnoDB 和 Oracle 能提供行级粒度锁，而 MongoDB 只能提供 &lt;strong&gt;&lt;em&gt;库级粒度锁&lt;/em&gt;&lt;/strong&gt;，这意味着当 MongoDB 一个写锁处于占用状态时，其它的读写操作都得干等。&lt;/p&gt;

&lt;p&gt;初看起来库级锁在大并发环境下有严重的问题，但是 MongoDB 依然能够保持大并发量和高性能，这是因为 MongoDB 的锁粒度虽然很粗放，但是在锁处理机制和关系数据库锁有很大差异，主要表现在：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MongoDB 没有完整事务支持，操作原子性只到单个 document 级别，所以通常操作粒度比较小； &lt;/li&gt;
&lt;li&gt;MongoDB 锁实际占用时间是内存数据计算和变更时间，通常很快；&lt;/li&gt;
&lt;li&gt;MongoDB 锁有一种临时放弃机制，当出现需要等待慢速 IO 读写数据时，可以先临时放弃，等 IO 完成之后再重新获取锁。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通常不出问题不等于没有问题，如果数据操作不当，依然会导致长时间占用写锁，比如下面提到的前台建索引操作，当出现这种情况的时候，整个数据库就处于完全阻塞状态，无法进行任何读写操作，情况十分严重。&lt;/p&gt;

&lt;p&gt;解决问题的方法，尽量避免长时间占用写锁操作，如果有一些集合操作实在难以避免，可以考虑把这个集合放到一个单独的 MongoDB 库里，因为 MongoDB 不同库锁是相互隔离的，分离集合可以避免某一个集合操作引发全局阻塞问题。&lt;/p&gt;
&lt;h2 id="建索引导致数据库阻塞"&gt;建索引导致数据库阻塞&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;坑爹指数：3 星&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;上面提到了 MongoDB 库级锁的问题，建索引就是一个容易引起长时间写锁的问题，MongoDB 在前台建索引时需要占用一个写锁（而且不会临时放弃），如果集合的数据量很大，建索引通常要花比较长时间，特别容易引起问题。&lt;/p&gt;

&lt;p&gt;解决的方法很简单，MongoDB 提供了两种建索引的访问，一种是 background 方式，不需要长时间占用写锁，另一种是非 background 方式，需要长时间占用锁。使用 background 方式就可以解决问题。
例如，为超大表 posts 建立索引，
&lt;strong&gt;&lt;em&gt;千万不用使用&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ensureIndex&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;user_id&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;而应该使用&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ensureIndex&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;user_id&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="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;background&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;/code&gt;&lt;/pre&gt;&lt;h2 id="不合理使用嵌入 embed document"&gt;不合理使用嵌入 embed document&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;坑爹指数：5 星&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;embed document 是 MongoDB 相比关系数据库差异明显的一个地方，可以在某一个 document 中嵌入其它子 document，这样可以在父子 document 保持在单一 collection 中，检索修改比较方便。&lt;/p&gt;

&lt;p&gt;比如薄荷的应用情景中有一个 Group document，用户申请加入 Group 建模为 GroupRequest document，我们最初的时候使用 embed 方式把 GroupRequest 放置到 Group 中。
Ruby 代码如下所示（使用了 Mongoid ORM）:&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;Group&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;embeds_many&lt;/span&gt; &lt;span class="ss"&gt;:group_requests&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroupRequest&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;embedded_in&lt;/span&gt; &lt;span class="ss"&gt;:group&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;这个使用方式让我们掉到坑里了，差点就爬不出来，它导致有接近两周的时间系统问题，高峰时段常有几分钟的系统卡顿，最严重一次甚至引起 MongoDB 宕机。&lt;/p&gt;

&lt;p&gt;仔细分析后，发现某些活跃的 Group 的 group_requests 增加（当有新申请时）和更改（当通过或拒绝用户申请时）异常频繁，而这些操作经常长时间占用写锁，导致整个数据库阻塞。原因是当有增加 group_request 操作时，Group 预分配的空间不够，需要重新分配空间（内存和硬盘都需要），耗时较长，另外 Group 上建的索引很多，移动 Group 位置导致大量索引更新操作也很耗时，综合起来引起了长时间占用锁问题。&lt;/p&gt;

&lt;p&gt;解决问题的方法，说起来也简单，就是把 embed 关联更改成的普通外键关联，就是类似关系数据库的做法，这样 group_request 增加或修改都只发生在 GroupRequest 上，简单快速，避免长时间占用写锁问题。当关联对象的数据不固定或者经常发生变化时，一定要避免使用 embed 关联，不然会死的很惨。&lt;/p&gt;
&lt;h2 id="不合理使用 Array 字段"&gt;不合理使用 Array 字段&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;坑爹指数：4 星&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MongoDB 的 Array 字段是比较独特的一个特性，它可以在单个 document 里存储一些简单的一对多关系。&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;User&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Mongoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:follower_user_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &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;User 中通过一个 Array 类型字段 follower_user_ids 保存用户关注的人的 id，用户关注的人从 10 个到 3000 个不等，变化是比较频繁的，和上面 embed 引发的问题类似，频繁的 follower_user_ids 增加修改操作导致大量长时间数据库写锁，从而引发 MongoDB 数据库性能急剧下降。&lt;/p&gt;

&lt;p&gt;解决问题的方法：我们把 follower_user_ids 转移到了内存数据库 redis 中，避免了频繁更改 MongoDB 中的 User, 从而彻底解决问题。如果不使用 redis，也可以建立一个 UserFollower 集合，使用外键形式关联。&lt;/p&gt;

&lt;p&gt;先列举上面几个坑吧，都是害人不浅的陷阱，使用 MongoDB 过程一定要多加注意，避免掉到坑里。&lt;/p&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1.&lt;a href="http://docs.mongodb.org/manual/faq/concurrency/" rel="nofollow" target="_blank" title=""&gt;MongoDB 锁机制详解&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2.&lt;a href="http://docs.mongodb.org/manual/tutorial/build-indexes-in-the-background/" rel="nofollow" target="_blank" title=""&gt;MongoDB 建立索引操作文档&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="http://xiewenwei.github.io/" rel="nofollow" target="_blank" title=""&gt;原创首发于我的 blog http://xiewenwei.github.io/&lt;/a&gt; &lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Mon, 23 Jun 2014 22:36:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/20128</link>
      <guid>https://ruby-china.org/topics/20128</guid>
    </item>
    <item>
      <title>[上海] 薄荷热聘 Ruby 工程师 4 名 Change the women Change the world </title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2014/cf7870cc60be6e1fee776179a51a2e1b.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Fri, 13 Jun 2014 09:51:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/19916</link>
      <guid>https://ruby-china.org/topics/19916</guid>
    </item>
    <item>
      <title>[上海] 薄荷科技寻找靠谱的 Ruby，Web 前端，iOS 和 Android 工程师</title>
      <description>&lt;h2 id="上海薄荷科技寻找靠谱的 Ruby，Web 前端工程师"&gt;上海薄荷科技寻找靠谱的 Ruby，Web 前端工程师&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;工作地点：上海浦东张江&lt;/li&gt;
&lt;li&gt;职位：Ruby，Web 前端各 1~2 名&lt;/li&gt;
&lt;li&gt;职责：薄荷网站和移动 App 程序设计开发，架构设计，代码重构，性能优化。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="我们是谁"&gt;我们是谁&lt;/h2&gt;
&lt;p&gt;薄荷科技是一家专注女性健康生活领域的互联网公司，主要产品有：&lt;/p&gt;

&lt;p&gt;1.薄荷网 &lt;a href="http://www.boohee.com/" rel="nofollow" target="_blank"&gt;http://www.boohee.com/&lt;/a&gt;
中国最大最活跃的减肥网站，倡导健康减肥观念，提供在线减肥指导服务 NICE，注册会员 200 多万，已帮助一百多万人健康减肥。&lt;/p&gt;

&lt;p&gt;2.薄荷 App &lt;a href="http://www.boohee.com/app/" rel="nofollow" target="_blank"&gt;http://www.boohee.com/app/&lt;/a&gt;
中国最受欢迎的健康健美类 App 之一，用户已近千万，长期位于 App Store 健康健美榜前列，长期位于各大 Android 应用市场健康或生活类应用前列。薄荷在 App Store 最高排名中国区总榜第 3，减肥类第 1，6 次获得 App Store 首页推荐，入选苹果编辑最爱应用。&lt;/p&gt;

&lt;p&gt;我是谢文威，也可以叫我 Vincent，是薄荷的联合创始人，我在 2006 年底和大学同学、上下铺的兄弟马海华一起建立了薄荷。在创立薄荷之前，我曾经做过多年的程序员，DBA，数据挖掘工程师和项目经理，我在薄荷主要负责技术。薄荷是我第一次创业，走过很多弯路，可谓屡败屡战，所幸我们顽强的生存下来，现在还算有不错的进展。&lt;/p&gt;

&lt;p&gt;薄荷一直专注女性健康生活领域，我们十分信奉法国一位哲学家卢梭的一条格言 --  “在所有的人类知识中，最重要而又最缺乏的是关于人的知识” ，我们相信随着大数据时代，数据化自我时代的到来，基于数据应用改善人的健康和美丽方向一定大有可为。&lt;/p&gt;

&lt;p&gt;薄荷与 Ruby 有很深的渊源。薄荷是中国最早一批基于 Ruby on Rails 技术建立的规模网站之一，薄荷网和薄荷 App 后端全部基于 Ruby on Rails 构建。从 2007 年全面转向 Ruby，经历了无数的质疑，痛苦和快乐，坚持到现在，见证了 Ruby 在中国稳步发展。薄荷是 &lt;strong&gt;RubyChinaConf 2012、2013 的赞助商&lt;/strong&gt; 之一，我们一直十分乐意为 Ruby 在中国的发展贡献一点力量，那怕曾是我们很艰难的时候。&lt;/p&gt;

&lt;p&gt;我也是一位 Rubyist，经常参加 Ruby 社区的一些活动，Ruby 是我这几年来最明智的选择，因为 Ruby 是一门让编程变得快乐的语言！我依然保持着对写代码的热爱，希望自己能够写优雅的程序，做一个优雅的人。&lt;/p&gt;
&lt;h2 id="寻找什么样的人"&gt;寻找什么样的人&lt;/h2&gt;
&lt;p&gt;我们希望找到各领域 Ruby, Web 前端靠谱的工程师，一些要求如下：
1、所在领域有至少 1 年或以上开发经验，欢迎跨多领域人才；
2、编程基础良好，追求优雅的代码；
3、热爱技术开发，有极客精神，参与过开源项目或常写技术博客加分哦！&lt;/p&gt;

&lt;p&gt;工作年限，学历专业都不是硬性要求，我们更注重能力：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;解决问题能力。
自信，积极主动，做事有条理，目标导向。&lt;/li&gt;
&lt;li&gt;学习能力。
乐于学习，渴望每天都在进步。&lt;/li&gt;
&lt;li&gt;团队协作能力。
诚实，坦率，有责任心，信任、尊重、包容。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="我们能为你提供什么"&gt;我们能为你提供什么&lt;/h2&gt;
&lt;p&gt;1.发挥你能力的舞台
薄荷的技术系统正处于从 &lt;strong&gt;数百万用户量到数千万量级&lt;/strong&gt; 演进过程中，这里充满了机会和挑战，如果你是技术大牛，或者渴望快速成长为技术大牛，这里有供你发挥的舞台。如果想未来成为产品经理，或者自己创业，这里也将为你打下坚实的基础。&lt;/p&gt;

&lt;p&gt;2.优雅舒适的环境
为了让你写出优雅的代码，我们当然为你提供优雅的工作环境，详见后面的图片。相比其它 IT 公司，薄荷一个特点是女员工比例很高，男女比例是 1 比 2，我们有美女程序员，美女设计师，美女咨询师和美女产品经理 ... 因为我们从事的是女性美丽相关的业务，我们的口号是：与美女一起共事，为美女服务！&lt;/p&gt;

&lt;p&gt;3.超出你预期的回报
年薪 8~25 万，上不封顶，取决于你的能力和业绩。目前公司处于快速发展阶段，已有重量级风投加入，未来充满无限想象。我们将把你视为伙伴，希望通过一起努力，能够提供远超你预期的回报。&lt;/p&gt;
&lt;h2 id="联系方式"&gt;联系方式&lt;/h2&gt;
&lt;p&gt;如果您对我们的工作岗位感兴趣，请把您的简历或情况发邮件到 vincent(at)boohee.com，最好能充分展示您的能力，谢谢！&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Mon, 17 Mar 2014 00:01:58 +0800</pubDate>
      <link>https://ruby-china.org/topics/17926</link>
      <guid>https://ruby-china.org/topics/17926</guid>
    </item>
    <item>
      <title>[上海] 薄荷科技寻找靠谱的 Rubyist</title>
      <description>&lt;h2 id="上海薄荷科技寻找靠谱的 Rubyist 2 名"&gt;上海薄荷科技寻找靠谱的 Rubyist 2 名&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;工作地点：上海浦东张江&lt;/li&gt;
&lt;li&gt;职位：高级 Ruby 工程师&lt;/li&gt;
&lt;li&gt;职责：网站和移动 App 后端服务程序设计开发，架构设计，代码重构，性能优化。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;推荐成功即赠送 RubyChina Conf 2013 VIP 门票&lt;/strong&gt;
每位 VIP 门票持有者，将享受到的待遇是：大会前一天晚上的盛大迎宾晚宴，跟所有来自海外和全国各地的讲师们同桌交流，大会主会场的前排 VIP 席位。想和心中的大神同桌共进晚餐吗？那就赶快推荐吧&lt;/p&gt;
&lt;h2 id="我们是谁"&gt;我们是谁&lt;/h2&gt;
&lt;p&gt;薄荷科技是一家专注女性减肥领域的互联网公司，主要产品有：&lt;/p&gt;

&lt;p&gt;1.薄荷网 &lt;a href="http://www.boohee.com/" rel="nofollow" target="_blank"&gt;http://www.boohee.com/&lt;/a&gt;
中国最大最活跃的减肥网站，倡导健康减肥观念，提供在线减肥指导服务 NICE，注册会员 200 多万，已帮助一百多万人健康减肥。&lt;/p&gt;

&lt;p&gt;2.薄荷 App &lt;a href="http://www.boohee.com/app/" rel="nofollow" target="_blank"&gt;http://www.boohee.com/app/&lt;/a&gt;
中国最受欢迎的减肥 App，用户 300 多万，长期位于 App Store 健康健美榜前列，长期占据健康健美畅销榜第 1 名。薄荷在 App Store 最高排名中国区总榜第 3，减肥类第 1，6 次获得 App Store 首页推荐，入选苹果编辑最爱应用。&lt;/p&gt;

&lt;p&gt;我是谢文威，也可以叫我 Vincent，是薄荷的联合创始人，我在 2006 年底和大学同学、上下铺的兄弟马海华一起建立了薄荷。在创立薄荷之前，我曾经做过多年的程序员，DBA，数据挖掘工程师和项目经理，我在薄荷主要负责技术。薄荷是我第一次创业，走过很多弯路，可谓屡败屡战，所幸我们顽强的生存下来，现在还算有不错的进展。&lt;/p&gt;

&lt;p&gt;很多人对减肥有很深的偏见，的确这是一个类似保险、保健品之类被做烂的市场，里面充斥着无数损害用户利益的事情，比如虚假的产品，对身体伤害很大的减肥方式，虚假的宣传等等，这是我们创立之初就意识到的问题，而且我们发觉这其实是一个机会，如果在这样的环境中坚持做对用户好的事情，坚持不做任何损害用户利益的事情，应该是非常有价值的。我们就这么坚持做到了，抵制住无数虚假减肥产品广告的诱惑，艰难但稳步做了下来，渐渐得到用户的信赖。&lt;/p&gt;

&lt;p&gt;减肥是一个非常传统的行业，很多人也许觉得可能没有什么技术含量，这么说有对有不对，如果仅仅搞了一个减肥资讯网站或者减肥论坛，当然没有多大的技术含量。但是我们的想法是通过大量的数据分析，数据挖掘的方式发现减肥中一些微妙因素，从而帮助用户更好的减肥。我们十分信奉的一条格言，是法国的一位哲学家卢梭说的， “在所有的人类知识中，最重要而又最缺乏的是关于人的知识” ，我们相信随着大数据时代，数据化自我时代的到来，基于数据应用改善人的健康和美丽方向一定大有可为。&lt;/p&gt;

&lt;p&gt;薄荷与 Ruby 有很深的渊源。薄荷是中国最早一批基于 Ruby on Rails 技术建立的规模网站之一，薄荷网和薄荷 App 后端全部基于 Ruby on Rails 构建。从 2007 年全面转向 Ruby，经历了无数的质疑，痛苦和快乐，坚持到现在，见证了 Ruby 在中国稳步发展。薄荷是 &lt;strong&gt;RubyChinaConf 2012、2013 的赞助商&lt;/strong&gt; 之一，我们一直十分乐意为 Ruby 在中国的发展贡献一点力量，那怕曾是我们很艰难的时候。&lt;/p&gt;

&lt;p&gt;我也是一位 Rubyist，经常参加 Ruby 社区的一些活动，Ruby 是我这几年来最明智的选择，因为 Ruby 是一门让编程变得快乐的语言！我依然保持着对写代码的热爱，希望自己能够写优雅的程序，做一个优雅的人。我们的产品技术团队不是来自硅谷的什么海龟精英，我们都是一些普通人，但是也是一群非常努力，非常靠谱的人，我们相信所谓优秀团队就是 &lt;strong&gt;让一群平凡的人做出不平凡的事情&lt;/strong&gt; 。&lt;/p&gt;
&lt;h2 id="寻找什么样的人"&gt;寻找什么样的人&lt;/h2&gt;
&lt;p&gt;我们希望找到靠谱的 Rubyist 或架构师，一些要求如下：
1、会 Ruby 语言，用过 1 年以上，精通更好；
2、熟悉 Web 开发的相关技术，编程基础良好，追求优雅的代码；
3、熟悉 Linux/Unix，熟练使用 SQL，如果你还有 MongoDB、Redis 经验更好；
4、热爱技术开发，有技术追求，参与过开源项目或常写技术博客加分哦！&lt;/p&gt;

&lt;p&gt;工作年限，学历专业都不是硬性要求，我们更注重能力：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;解决问题能力。
自信，积极主动，做事有条理，有计划，目标导向，等等。&lt;/li&gt;
&lt;li&gt;学习能力。
乐于学习，善于总结，渴望每天都在进步。&lt;/li&gt;
&lt;li&gt;沟通协作能力。
诚实，坦率，善于倾听，有责任心，信任、尊重、包容，等等。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="我们能为你提供什么"&gt;我们能为你提供什么&lt;/h2&gt;
&lt;p&gt;1.发挥你能力的舞台
薄荷的技术系统正处于从 &lt;strong&gt;数百万用户量到数千万量级&lt;/strong&gt; 演进过程中，这里充满了机会和挑战，如果你是技术大牛，或者渴望快速成长为技术大牛，这里有供你发挥的舞台。如果想未来成为产品经理，或者自己创业，这里也将为你打下坚实的基础。&lt;/p&gt;

&lt;p&gt;2.优雅舒适的环境
为了让你写出优雅的代码，我们当然为你提供优雅的工作环境，详见后面的图片。相比其它 IT 公司，薄荷一个特点是女员工比例很高，男女比例是 1 比 2，我们有美女程序员，美女设计师，美女咨询师和美女产品经理 ... 因为我们从事的是女性美丽相关的业务，我们的口号是：与美女一起共事，为美女服务！&lt;/p&gt;

&lt;p&gt;3.超出你预期的回报
年薪 8~25 万，上不封顶，取决于你的能力和业绩。目前公司处于快速发展阶段，已有重量级的风投加入，未来充满无限想象。我们将把你视为伙伴，希望通过一起努力，能够提供远超你预期的回报。&lt;/p&gt;
&lt;h2 id="联系方式"&gt;联系方式&lt;/h2&gt;
&lt;p&gt;如果您对我们的工作岗位感兴趣，请把您的简历或情况发邮件到 ruby(at)boohee.com，最好能充分展示您的能力，谢谢！&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Tue, 27 Aug 2013 22:50:51 +0800</pubDate>
      <link>https://ruby-china.org/topics/13680</link>
      <guid>https://ruby-china.org/topics/13680</guid>
    </item>
    <item>
      <title>实例说明 Ruby 多线程的潜力和弱点</title>
      <description>&lt;p&gt;Web 应用大多是 IO 密集型的，利用 Ruby 多进程 + 多线程模型将能大幅提升系统吞吐量。其原因在于：当 Ruby 某个线程处于 IO Block 状态时，其它的线程还可以继续执行。但由于存在 Ruby GIL (Global Interpreter Lock)，MRI Ruby 并不能真正利用多线程进行并行计算。JRuby 去除了 GIL，是真正意义的多线程，既能应付 IO Block，也能充分利用多核 CPU 加快整体运算速度。&lt;/p&gt;

&lt;p&gt;上面说得比较抽象，下面就用例子一一加以说明。&lt;/p&gt;
&lt;h2 id="Ruby 多线程和 IO Block"&gt;Ruby 多线程和 IO Block&lt;/h2&gt;
&lt;p&gt;先看下面一段代码（演示目的，没有实际用途）：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# File: block_io1.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;func1&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"sleep 3 seconds in func1&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;func2&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"sleep 2 seconds in func2&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;func3&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"sleep 5 seconds in func3&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;func1&lt;/span&gt;
&lt;span class="n"&gt;func2&lt;/span&gt;
&lt;span class="n"&gt;func3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码很简单，3 个方法，用 sleep 模拟耗时的 IO 操作。
运行代码（环境 MRI Ruby 1.9.3）结果是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="n"&gt;block_io1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;
&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;func1&lt;/span&gt;
&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;func2&lt;/span&gt;
&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;func3&lt;/span&gt;

&lt;span class="n"&gt;real&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m11&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;681&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;086&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;152&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比较慢，时间都耗在 sleep 上了，总共花了 10 多秒。&lt;/p&gt;

&lt;p&gt;采用多线程的方式，改写如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# File: block_io2.rb&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;func1&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"sleep 3 seconds in func1&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;func2&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"sleep 2 seconds in func2&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;func3&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"sleep 5 seconds in func3&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&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;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;func1&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;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;func2&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;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;func3&lt;/span&gt; &lt;span class="p"&gt;}&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;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;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;t&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行的结果是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="n"&gt;block_io2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;
&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;func1&lt;/span&gt;
&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;func2&lt;/span&gt;
&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;func3&lt;/span&gt;

&lt;span class="n"&gt;real&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m6&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;543&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;169&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;147&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;总共花了 6 秒多，明显快了许多，只比最长的 sleep 5 秒多了一点。&lt;/p&gt;

&lt;p&gt;上面的例子说明，** Ruby 的多线程能够应付 IO Block，当某个线程处于 IO Block 状态时，其它的线程还可以继续执行，从而使整体处理时间大幅缩短 **。&lt;/p&gt;
&lt;h2 id="Ruby GIL 的影响"&gt;Ruby GIL 的影响&lt;/h2&gt;
&lt;p&gt;还是先看一段代码（演示目的）：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# File: gil1.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'securerandom'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'zlib'&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4096000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&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;Deflate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deflate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码先随机生成一些数据，然后对其进行压缩，压缩是非常耗 CPU 的，在我机器 (双核 CPU, MRI Ruby 1.9.3) 运行结果如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="n"&gt;gil1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;

&lt;span class="n"&gt;real&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;572&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;359&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="n"&gt;s&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="c1"&gt;# File: gil2.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'securerandom'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'zlib'&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4096000&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="p"&gt;[]&lt;/span&gt;
&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Zlib&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Deflate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deflate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&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;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;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;t&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;多线程的版本运行结果如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="n"&gt;gil2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;

&lt;span class="n"&gt;real&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;616&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;377&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;211&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从结果可以看出，由于 MRI Ruby GIL 的存在，Ruby 多线程并不能重复利用多核 CPU，使用多线程后整体所花时间并不缩短，反而由于线程切换的影响，所花时间还略有增加。&lt;/p&gt;
&lt;h2 id="JRuby 去除了 GIL"&gt;JRuby 去除了 GIL&lt;/h2&gt;
&lt;p&gt;使用 JRuby (我的机器上是 JRuby 1.7.0) 运行 gil1.rb 和 gil2.rb，得到很不一样的结果。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;jruby&lt;/span&gt; &lt;span class="n"&gt;gil1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;

&lt;span class="n"&gt;real&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m12&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;225&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m14&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;060&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;615&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;jruby&lt;/span&gt; &lt;span class="n"&gt;gil2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;

&lt;span class="n"&gt;real&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;584&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m22&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;822&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;819&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，JRuby 使用多线程时，整体运行时间有明显缩短（7.58 比 12.22），这是由于 JRuby 去除了 GIL，可以真正并行的执行多线程，充分利用了多核 CPU。&lt;/p&gt;
&lt;h2 id="补充说明，Ruby 2.0 Zlib 库去除了 GIL"&gt;补充说明，Ruby 2.0 Zlib 库去除了 GIL&lt;/h2&gt;
&lt;p&gt;在 Ruby 2.0 下，由于 Zlib 去除了 GIL，见：&lt;a href="https://github.com/ruby/ruby/blob/v2_0_0_0/NEWS#L512-L513/" rel="nofollow" target="_blank"&gt;https://github.com/ruby/ruby/blob/v2_0_0_0/NEWS#L512-L513/&lt;/a&gt;，执行多线程版本 gil2.rb 有非常大的性能提升。
详细数据见：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="n"&gt;gil1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;

&lt;span class="n"&gt;real&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;708&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;664&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;025&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="n"&gt;gil2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;

&lt;span class="n"&gt;real&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m17&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;630&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;147&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是在一台 单 CPU 6 核（带超线程）机器，在 ruby-2.0.0-p195 的执行结果。
但是对于普通的 Ruby 代码 和 类库，Ruby 2.0 还是有 GIL 存在，限制利用多线程并行能力。 
我另外构建了例子 &lt;code&gt;gil_digest1.rb&lt;/code&gt; 和 &lt;code&gt;gil_digest2.rb&lt;/code&gt; （见 Github 项目），在 Ruby 2.0 下运行并没有性能提升。
感谢 &lt;a href="/zj0713001" class="user-mention" title="@zj0713001"&gt;&lt;i&gt;@&lt;/i&gt;zj0713001&lt;/a&gt; &lt;a href="/hooopo" class="user-mention" title="@hooopo"&gt;&lt;i&gt;@&lt;/i&gt;hooopo&lt;/a&gt; &lt;a href="/5long" class="user-mention" title="@5long"&gt;&lt;i&gt;@&lt;/i&gt;5long&lt;/a&gt; 的提醒和说明。&lt;/p&gt;

&lt;p&gt;总结：&lt;strong&gt;Ruby 多线程可以在某个线程 IO Block 时，依然能够执行其它线程，从而降低 IO Block 对整体的影响，但由于 MRI Ruby GIL 的存在，MRI Ruby 并不是真正的并行执行，JRuby 去除了 GIL，可以做到真正的多线程并行执行&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;上述涉及的代码在 github 上：
&lt;a href="https://github.com/xiewenwei/ruby-multiple-threads/" rel="nofollow" target="_blank"&gt;https://github.com/xiewenwei/ruby-multiple-threads/&lt;/a&gt;&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Mon, 27 May 2013 00:18:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/11248</link>
      <guid>https://ruby-china.org/topics/11248</guid>
    </item>
    <item>
      <title>Ruby on Rails 线程安全代码</title>
      <description>&lt;p&gt;Ruby on Rails 4.0 的 rc 版本已经 release 了，Rails 4 时代最大的变化当属默认开启多线程模式。Rails 4 的多线程将给大家带来什么好处呢？至少有两方面：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.更高的吞吐量&lt;/strong&gt;
    Web 应用多是 IO 密集型的，多线程可以让 Application 在等待 IO 操作的同时，还能接收处理请求，大大提升系统吞吐量，增强系统稳定性和安全性。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.更省内存&lt;/strong&gt;
    Ruby on Rails 是内存消耗大户，一个 Applicaion 占用几百兆是常事，以前仅使用多进程并发模式时，整体内存消耗巨大，使用多进程 + 多线程的并发模式，不单系统吞吐量大大提供，系统整体使用内存也大幅下降。&lt;/p&gt;

&lt;p&gt;但是天下没有免费的午餐，在享用这些好处的同时，我们也必须付出一定的代价，代价就是要应付多线程编程带来的复杂性。程序中需要处理多线程可能导致问题的地方，如果程序中出现问题也变得更加难以发现调试。&lt;/p&gt;

&lt;p&gt;好在需要注意的地方也不是太多，综合网上的资料和自己的实践，下面把这几个需要注意的地方说明一下。&lt;/p&gt;
&lt;h2 id="代码加载"&gt;代码加载&lt;/h2&gt;
&lt;p&gt;Ruby 的 require 并非线程安全的，在多线程中 require，可能会导致严重的不可预期的错误。例如下面的一个演示程序在 Ruby 1.9 环境下执行会发生死锁。&lt;/p&gt;

&lt;p&gt;a.rb 文件：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;current&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: a.rb"&lt;/span&gt;
&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;current&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: requiring b"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'b'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;b.rb 文件：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;current&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: b.rb"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;current&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: requiring a"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'a'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;test.rb 文件：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vg"&gt;$LOAD_PATH&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"."&lt;/span&gt;

&lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'b'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
&lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Ruby 1.9 环境下运行 test.rb 将报死锁错误：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#&amp;lt;Thread:0x007fd2da90a268&amp;gt;: b.rb&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;Thread:0x007fd2da90a268&amp;gt;: requiring a&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;Thread:0x007fd2da90a2e0&amp;gt;: a.rb&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;Thread:0x007fd2da90a2e0&amp;gt;: requiring b&lt;/span&gt;
test.rb:5:in &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="s1"&gt;': deadlock detected (fatal)
  from test.rb:5:in `&amp;lt;main&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 Ruby require 不是线程安全的，所以 Rails 中使用多线程环境时，需要对 require 做一定的限制，简单的说就是在 Application 启动的时候，把所有需要加载的代码全部加载完成，避免启动后还 require。Rails 4 的生产环境配置中该选项已经默认生效。需要注意的时，如果你的代码不在 Rails 默认的几个目录中，你需要手动配置你的目录进入 eager_load_path，例如：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eager_load_paths&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/lib"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="全局变量和类变量写操作"&gt;全局变量和类变量写操作&lt;/h2&gt;
&lt;p&gt;在 Rails 多线程环境，所有的全局变量（包括 $var @@var 和 类实例变量），在实例方法中都应该是只读的，尽量应该避免写操作。&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;HomeController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;before_filter&lt;/span&gt; &lt;span class="ss"&gt;:set_site&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_site&lt;/span&gt;
    &lt;span class="vi"&gt;@site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_subdomain&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;subdomains&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@site.layout&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;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@site.layout_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&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;layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'default_lay'&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;上面代码的意图是根据域名设置不同的 layout。&lt;code&gt;self.class.layout(value)&lt;/code&gt; 中，Rails 把 value 保存在类变量 &lt;code&gt;@@layout_&lt;/code&gt;，然后在 render 的时候使用。&lt;/p&gt;

&lt;p&gt;设想这样一种情况 UserA 的 subdomain 是 foo1，他的 layout 应该是 foo1，
UserB 的 subdomain 是 foo2，他的 layout 应该是 foo2。&lt;/p&gt;

&lt;p&gt;UserA 和 UserB 同时请求应用，他们的请求分别在 Thread1 和 Thread2 中执行，执行顺序可能是：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Thread1, 执行进入 set_site 方法，设置 &lt;code&gt;@@layout_&lt;/code&gt; 为 foo1；&lt;/li&gt;
&lt;li&gt;Thread2, 执行进入 set_site 方法，设置 &lt;code&gt;@@layout_&lt;/code&gt; 为 foo2；&lt;/li&gt;
&lt;li&gt;Thread1, render response，使用最新的 &lt;code&gt;@@layout_&lt;/code&gt; foo2 render.&lt;/li&gt;
&lt;li&gt;Thread2，render response，使用最新的 &lt;code&gt;@@layout_&lt;/code&gt; foo2 render.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们期望 Thread1 使用 foo1 layout render，这样的执行结果和期望的不相符。&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;HomeController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;before_filter&lt;/span&gt; &lt;span class="ss"&gt;:set_site&lt;/span&gt;
  &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="ss"&gt;:site_layout&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_site&lt;/span&gt;
    &lt;span class="vi"&gt;@site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_subdomain&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;subdomains&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;site_layout&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@site.layout&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
      &lt;span class="vi"&gt;@site.layout_name&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="s1"&gt;'default_lay'&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;程序在每次需要使用 layout 时，调用实例方法 &lt;code&gt;site_layout&lt;/code&gt;，避免写类变量。&lt;/p&gt;
&lt;h2 id="IO Connection"&gt;IO Connection&lt;/h2&gt;
&lt;p&gt;Rails 应用通常会用到多个 IO Connection，比如 ActiveRecord 的数据库 Connection，缓存 Memcached 的 Connection，Redis 的 Connection 等等。这些 IO Connection 在 Rails 多线程环境下并不都是线程安全的。&lt;/p&gt;

&lt;p&gt;ActiveRecord 的 Connection 是线程安全的，而且 ActiveRecord 还可配置 Connection Pool，这样可以更高效率的利用数据库连接。&lt;/p&gt;

&lt;p&gt;Memcached 的 Connection memchached-client 并不是线程安全的，最新的 dalli 是线程安全的。不过 dalli 的线程安全机制是在每个读写操作时加上互斥信号控制，这意味着同一时间只有一个线程可以操作，如果操作非常频繁的话，可能有性能上的问题，这个时候可以使用一个单独的 Connection Pool Gem 解决。&lt;/p&gt;

&lt;p&gt;这个 Connection Pool Gem 的地址是 &lt;a href="https://github.com/mperham/connection_pool" rel="nofollow" target="_blank"&gt;https://github.com/mperham/connection_pool&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;Redis 的 Connection 和 dalli 类似，本身通过加上互斥信号控制保证线程安全，可以通过 Connection Pool 增强效率。 &lt;/p&gt;
&lt;h2 id="使用互斥信号控制非线程安全操作"&gt;使用互斥信号控制非线程安全操作&lt;/h2&gt;
&lt;p&gt;在程序中，如果存在某些不希望多个线程同时执行的操作，可以使用互斥信号控制其执行，这样当已经有一个线程进入执行时，其他进入的 thread 都会被 block 住，等前面的进程执行结束后才会进入执行，从而保证在一个时间只有一个线程会执行。&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;HomeController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="vc"&gt;@@lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vc"&gt;@@lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;thread_unsafe_code&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;thread_unsafe_code&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vc"&gt;@@something&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'hello'&lt;/span&gt;
      &lt;span class="n"&gt;do_hello&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="vc"&gt;@@something&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;
      &lt;span class="n"&gt;do_world&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="vc"&gt;@@something&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nothing'&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;总之，Rails 的多线程为我们提供了简便的提升系统伸缩性的能力，这也意味的程序复杂性的增加，有几处地方使我们需要注意的，只有这样才能很好的利用 Rails 多线程能力。&lt;/p&gt;

&lt;p&gt;参考：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://m.onkey.org/thread-safety-for-your-rails/" rel="nofollow" target="_blank"&gt;http://m.onkey.org/thread-safety-for-your-rails/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.headius.com/2008/08/qa-what-thread-safe-rails-means.html/" rel="nofollow" target="_blank"&gt;http://blog.headius.com/2008/08/qa-what-thread-safe-rails-means.html/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tenderlovemaking.com/2012/06/18/removing-config-threadsafe.html/" rel="nofollow" target="_blank"&gt;http://tenderlovemaking.com/2012/06/18/removing-config-threadsafe.html/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ruby 能提升 Web 应用吞吐量的原因，见我的另一个帖子：
实例说明 Ruby 多线程的潜力和弱点 &lt;a href="http://ruby-china.org/topics/11248" rel="nofollow" target="_blank"&gt;http://ruby-china.org/topics/11248&lt;/a&gt;&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Mon, 13 May 2013 09:46:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/10932</link>
      <guid>https://ruby-china.org/topics/10932</guid>
    </item>
    <item>
      <title>Ruby 的常量查找路径问题</title>
      <description>&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;M1&lt;/span&gt;
  &lt;span class="no"&gt;CT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ok"&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;C1&lt;/span&gt;
  &lt;span class="no"&gt;CK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ck"&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;M1&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;method1&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;CK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; in method1"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;CT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; in method1"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;    
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method2&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;CK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; in method1"&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;CT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; in method2"&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="no"&gt;C1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method1&lt;/span&gt;
&lt;span class="no"&gt;C1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method2&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出结果是&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;C1
ck &lt;span class="k"&gt;in &lt;/span&gt;method1
ok &lt;span class="k"&gt;in &lt;/span&gt;method1
C1
ck &lt;span class="k"&gt;in &lt;/span&gt;method2
NameError: uninitialized constant Class::CT
    from &lt;span class="o"&gt;(&lt;/span&gt;irb&lt;span class="o"&gt;)&lt;/span&gt;:16:in &lt;span class="sb"&gt;`&lt;/span&gt;method2&lt;span class="s1"&gt;'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;method1 和 method2 都是常见的类方法的定义方面，我向来认为它们是等价可替换的写法，但是从实际执行的结果看，它们里面的常量查找路径不一样，谁能解析一下？&lt;/p&gt;</description>
      <author>vincent</author>
      <pubDate>Tue, 07 Aug 2012 10:04:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/4777</link>
      <guid>https://ruby-china.org/topics/4777</guid>
    </item>
  </channel>
</rss>
