<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>killyfreedom (盛翔)</title>
    <link>https://ruby-china.org/killyfreedom</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>[上海] 上海致维网络招聘 Ruby 开发工程师 1 名 (10k-20k) </title>
      <description>&lt;h2 id="关于我们"&gt;关于我们&lt;/h2&gt;
&lt;p&gt;上海致维于 2010 年在上海成立，是一家长期专注于母婴零售产业互联网化的高新技术企业，通过大数据决策和软件云服务，与区域母婴连锁零售商一起开展全渠道会员经营服务。同时基于致维云平台沉淀的行业大数据和直达消费者、门店导购、区域零售商的数字通道，上海致维也面向制造商、供应商和服务商提供生态渠道、联合营销和供应链优化等一系列服务与合作。&lt;/p&gt;

&lt;p&gt;公司现有员工 100 多人，总部位于上海，并在长沙、厦门等地设有运营和技术服务中心。公司汇聚了来自全球知名科技企业的技术大咖、顶尖咨询公司的数据大牛和国内一线零售企业的运营精英，构建了一支结构合理、专业能力强、具有团队协作精神的产品研发和零售运营服务队伍。&lt;/p&gt;

&lt;p&gt;目前与致维合作的二十几家母婴连锁零售商遍布中国的 11 个省 30 个城市，服务近 1500 家门店。&lt;/p&gt;
&lt;h2 id="我们的愿景"&gt;我们的愿景&lt;/h2&gt;
&lt;p&gt;上海致维提供的“智零售 · Nebula”产品和服务，聚焦于运营管理、营销过程、购物体验等零售核心业务过程，利用最新数字科技，将消费者、一线员工、零售管理者、产业参与者等多方者紧密连接起来，专心做好三件事：&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;h2 id="招聘职位"&gt;招聘职位&lt;/h2&gt;&lt;h2 id="Ruby开发工程师 2名"&gt;Ruby 开发工程师 2 名&lt;/h2&gt;&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;参与核心需求的设计&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="职位要求"&gt;职位要求&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;有 2 年以上 rails 开发经验&lt;/li&gt;
&lt;li&gt;熟悉 mysql,mongodb,redis&lt;/li&gt;
&lt;li&gt;熟悉 grape,rails&lt;/li&gt;
&lt;li&gt;熟练使用 git,jira&lt;/li&gt;
&lt;li&gt;了解常用的设计模式&lt;/li&gt;
&lt;li&gt;有良好的代码习惯&lt;/li&gt;
&lt;li&gt;能独立解决问题，有责任心，有良好的沟通能力&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="加分项"&gt;加分项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;了解除了 ruby 之外另外一门语言&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="待遇"&gt;待遇&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;月薪：10-20K&lt;/li&gt;
&lt;li&gt;按照工作结果对应相应的年终奖&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="工作地点"&gt;工作地点&lt;/h3&gt;
&lt;p&gt;普陀区真北路 988 号 2 号楼 320&lt;/p&gt;
&lt;h2 id="联系我们"&gt;联系我们&lt;/h2&gt;
&lt;p&gt;邮箱：xiang.sheng#joowing.com&lt;/p&gt;

&lt;p&gt;简历请注明 &lt;strong&gt;来自 RubyChina&lt;/strong&gt;&lt;/p&gt;</description>
      <author>killyfreedom</author>
      <pubDate>Wed, 26 Apr 2017 19:05:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/32877</link>
      <guid>https://ruby-china.org/topics/32877</guid>
    </item>
    <item>
      <title>Ruby China 自己给自己点赞的小 BUG</title>
      <description>&lt;p&gt;场景：进入自己写文章中，给自己点了个赞，再刷新下，点餐的图标变成灰色，依然可以再次点赞，但是再次刷新后，数据并没有增加，后台数据应该是正常的
期望：进入自己写的文章中，点赞应该触发一点提示&lt;/p&gt;

&lt;p&gt;额&lt;/p&gt;

&lt;p&gt;这个 bug 是不是只有自己会给自己点赞的人才会发现....&lt;/p&gt;</description>
      <author>killyfreedom</author>
      <pubDate>Sun, 21 Feb 2016 11:56:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/29044</link>
      <guid>https://ruby-china.org/topics/29044</guid>
    </item>
    <item>
      <title>Fiber and EventMachine 一些心得</title>
      <description>&lt;p&gt;大家都知道 Ruby 是有 GIL，为了实现并发处理，我们有很多方案，最近花了一点时间，写了点 Fiber 加上 EventMachine 的实现，拿现有的业务代码做了一部分实现以及各种场景下 benchmark，有点心得，也跟大家分享一下&lt;/p&gt;

&lt;p&gt;先简单说下结论：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;多线程在绝大多数的情况下表现比较好&lt;/li&gt;
&lt;li&gt;Fiber and EventMachine 实现的并发需要写很多通讯层和业务层之间产生调度的代码&lt;/li&gt;
&lt;li&gt;只有在有非常频繁需要调用非常耗时的远程接口的时候，Fiber 相对于多线程更加轻量级的原因，可以支撑更多的访问&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="一点点小历史"&gt;一点点小历史&lt;/h2&gt;
&lt;p&gt;在 1.8 的时代，一个 ruby 的进程在绝大多数的情况下是消耗不了一个 CPU 核心的性能，因为当访问到 io 操作的时候，整个进程都会被挂起，为了的问题，通常的方式起多个进程，借助系统的进程的调度来实现使用更多的 CPU 性能&lt;/p&gt;

&lt;p&gt;到了 1.9,ruby 开始依托操作系统的实现来实现线程，这个时候，多线程的方案被慢慢开始热了起来，借助多线程，虽然一个 ruby 进程最多只能使用一个 CPU，但是在，一个进程的之间的多个线程在 io 操作的时候依然是可以做到并发的，当这个时候，一个 ruby 进程已经可以消耗掉一个 CPU 内核的性能，于此同时，ruby 1.8 上的那套线程调度模型，改吧改吧，推出了一个新的概念，Fiber.&lt;/p&gt;

&lt;p&gt;题外话，ruby 多线程方案的真正火热应该是 rails4 之后，在 rails3 的时候，rails 还是遮遮掩掩的说明，似乎我们应该是支持多线程的，如果有问题，恩，一定是外部库的问题，到了 rails4 的时候，社区多线程的方案基本已经成熟，同期，passenger 推出来多线程的支撑 [虽然是收费的],rainbows,puma 开始慢慢火热。&lt;/p&gt;
&lt;h2 id="多线程 vs 事件驱动"&gt;多线程 vs 事件驱动&lt;/h2&gt;
&lt;p&gt;本质上来说，线程是代码运行的一种容器，而 Fiber 也是一种代码运行的容器，但是，Fiber 和 Thread 的概念有很大的区别，Thread 是一种 Concurrent Mode，但是 Fiber 只是一种调度模式，与多线程对等应该是在 node 界十分火热的事件驱动模型，本文另一个主角 eventmachine 其实就是 ruby 上事件驱动模型的一种实现。&lt;/p&gt;

&lt;p&gt;多线程和事件驱动在 ruby 上都可以实现并发的处理，但是相对于多线程而言，eventmachine 所代表的事件驱动往往在代码上并不直观&lt;/p&gt;

&lt;p&gt;多线程实现 http 代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RestClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://ruby-china.org/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;status: headers: &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;EventMachine 实现 http 代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;EventMachine&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HttpRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://ruby-china.org/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callback&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;resp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;resp&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errback&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;resp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;resp&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当只有一次的时候似乎还好，如果你在 callback 中如果还需要调用其它异步 io，你会发现好多好多的 callback 的层叠调用，这就被称为&lt;code&gt;callback hell&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;但是，当你使用 Fiber 层桥接了 EventMachine 和业务逻辑，那么，依然可以使用同步的代码写法异步的逻辑&lt;/p&gt;
&lt;h2 id="简单介绍下Fiber"&gt;简单介绍下 Fiber&lt;/h2&gt;
&lt;p&gt;Fiber 是一种流程控制逻辑，通常需要依托于线程做真正的实现，fiber 作为一个流程的运行单元，可以非常快的将自身切换到另一个 fiber，当你 require 了 fiber 之后，主线程将会切换进入一个主 Fiber 中&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;当一个 Fiber 挂起了之后，后一步运行的是前一个 fiber 的代码&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这一段其实很重要，不然，很多 fiber 的代码会变得很难理解，不管你 Fiber 切换了多少次，一旦当前 fiber 被挂起，前一个 fiber 就会紧接着开始运行&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;'fiber'&lt;/span&gt; &lt;span class="c1"&gt;# (1)这时候的代码进入了一个主的Fiber中&lt;/span&gt;

&lt;span class="n"&gt;fiber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# (4)被主fiber唤醒了之后,打出1&lt;/span&gt;
    &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt; &lt;span class="c1"&gt;# (5)挂起当前fiber&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;# (2)我们创建了一个fiber&lt;/span&gt;

&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt; &lt;span class="c1"&gt;# (3)这时候,主fiber切换到了我们新创建的fiber的代码中&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;# (6)当我们创建的fiber挂起了之后,程序便跳转到了这里&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;说实话，这也是一个挺拗口的方案&lt;/p&gt;
&lt;h2 id="EventMachine and Fiber"&gt;EventMachine and Fiber&lt;/h2&gt;
&lt;p&gt;当开始使用 Fiber 和 EventMachine 了之后，代码会变成这个样子&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;current_fiber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;
  &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;EventMachine&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HttpRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://ruby-china.org/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callback&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;resp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;current_fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&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;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errback&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;resp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;current_fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&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;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="kp"&gt;nil&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;EventMachine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
  &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call_http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我把这个样子的代码分为两个部分，一个是正常的业务代码，一个是作为 EventMachine 和正常业务代码之间的 bridge 代码&lt;/p&gt;

&lt;p&gt;在刚才的代码例子中，只有&lt;code&gt;resp = call_http(uri)&lt;/code&gt; 是业务层面的代码，其它的代码都是用来和 EventMachine 做桥接的&lt;/p&gt;

&lt;p&gt;画个图解释下&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
--------  Thread.main  ------------------------------------------------------------------------
== Event Loop                               |== Fiber                                         
=                                           |=  # 获取当前fiber                                  
=                                           |=  current_fiber = Fiber.current                                  
=                                           |=  # 创建EM HTTP REQUest
=                                           |=  http = EventMachine::HttpRequest.new(uri)
=                                           |=  # 设定正常回掉
=                                           |=  http.callback { current_fiber.resume(0, resp) } 
=                                           |=  http.errback { ... } # 设定异常回掉                                  
=                                           |=  # 启动http调用,但是由于是异步机制                                  
=                                           |=  # 正真的操作将会在事件循环中执行                                 
=                                           |=  http.start                                   
=                                           |=  # 挂起当前的fiber
=                                           |=  # 由于主fiber中运行的是EventMachine的事件核心
=                                           |=  # 因此将会运行EM中的其它逻辑                                  
=                                           |=  # 注意,在当前状态下,yield方法是尚未返回的                                 
=                                           |=  status, resp = current_fiber.yield                                  
= # 由于Fiber的yield,得到了CPU的使用权         |=
= # 开始正常调用EM loop 执行http通讯           |=                                   
= # 当http调用完毕                            |=                                    
= # EventMachine调动正常callback             |=                                    
= current_fiber.resume(0, resp)             |= # 这个时候,由于EM上调用了resume                                   
=                                           |= # 这个fiber被重新激活                                  
=                                           |= # yield方法开始return                                   
=                                           |= # status 和 resp被赋值, http调用完成                                   
=                                           |=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样子就完成了一次可并发的 http 调用，
所有的外部 io 的场景都可以用这种模式运作&lt;/p&gt;

&lt;p&gt;从理论上来说，EventMachine 可以维护上 K 个 io，那么，我们自然也可以有上千个 Fiber 在挂起的状态等待 EventMachine 做 io 的唤醒&lt;/p&gt;

&lt;p&gt;但是，这样的 io 操作必须是它本身就支持 EventMachine，但事实上，大部分库其实是不支持 EventMachine 的，比如 mysql2，这个时候怎么办？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EventMachine and Fiber and ThreadPool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在这种情况下，EventMachine 并不做 io 的控制，io 依然由原始 Socket 控制，但是为了不阻塞当前线程，可以将 io 操作放到线程池里面，那么 call_http 就会变成这样：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;current_fiber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;
  &lt;span class="n"&gt;thread_pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="no"&gt;RestClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;EventMachine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_tick&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
      &lt;span class="n"&gt;current_fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="kp"&gt;nil&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;这里还需要 EventMachine 的唯一一个原因是 fiebr 的创建和 resume 必须要是同一个线程中，不然会异常，所以需要 EventMachine 的 next_tick 来支撑将代码 block 传回到主线程中的工作，当然，如果你自己创建一个 loop 的实现，那么就不再需要 EventMachine&lt;/p&gt;
&lt;h2 id="性能结果"&gt;性能结果&lt;/h2&gt;
&lt;p&gt;所有的性能结果都是拿同样的业务代码，只不过在底层切换了并发的机制
server 是 rainbows&lt;/p&gt;
&lt;h3 id="EventMachine and Fiber and ThreadPool"&gt;EventMachine and Fiber and ThreadPool&lt;/h3&gt;
&lt;p&gt;具体业务代码是：在 msyql 的数据库中一张大概 10W 条数据的表中查询 3 次数据库&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql2的版本: 0.3.11
rainbows: 4.6.5
ruby: 2.1.0
event_machine: 1.0.3
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="EventMachine and Fiber and ThreadPool 性能"&gt;EventMachine and Fiber and ThreadPool 性能&lt;/h5&gt;
&lt;p&gt;改动了 MySQL2 的查询机制，利用 MySQL2 原生多 EventMachine 的部分支持，当 SQL 写入到 socket 之后，用 EventMachine 的 watch_io 等待 notify_readable, 当可读的时候，将 io 放入线程池中做读取操作，之后通过 EventMachine 的 next_tick 回到主线程 resume 当前挂起的 Fiber&lt;/p&gt;

&lt;p&gt;性能：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrency Level:      10000
Time taken for tests:   9.877 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      3360000 bytes
HTML transferred:       1710000 bytes
Requests per second:    1012.44 [#/sec] (mean)
Time per request:       9877.135 [ms] (mean)
Time per request:       0.988 [ms] (mean, across all concurrent requests)
Transfer rate:          332.21 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%   5012
  66%   6487
  75%   7330
  80%   7792
  90%   8708
  95%   9157
  98%   9446
  99%   9542
 100%   9723 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="纯粹多线程 性能"&gt;纯粹多线程 性能&lt;/h4&gt;
&lt;p&gt;性能：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrency Level:      10000
Time taken for tests:   10.565 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      3360000 bytes
HTML transferred:       1710000 bytes
Requests per second:    946.49 [#/sec] (mean)
Time per request:       10565.321 [ms] (mean)
Time per request:       1.057 [ms] (mean, across all concurrent requests)
Transfer rate:          310.57 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%   5522
  66%   7005
  75%   7940
  80%   8399
  90%   9376
  95%   9938
  98%  10212
  99%  10264
 100%  10315 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="EventMachine and Fiber"&gt;EventMachine and Fiber&lt;/h3&gt;
&lt;p&gt;具体业务场景是使用 Mongoid 在一张 10W 条数据的 collection 中查询三次&lt;/p&gt;
&lt;h4 id="EventMachine and Fiber 性能"&gt;EventMachine and Fiber 性能&lt;/h4&gt;
&lt;p&gt;重写了 Mongoid 的 socket 的用法，所有 socket 的写入写出通过 EventMachine 控制&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document Path:          /test/test5.json
Document Length:        1680 bytes

Concurrency Level:      10000
Time taken for tests:   8.431 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      18460000 bytes
HTML transferred:       16800000 bytes
Requests per second:    1186.09 [#/sec] (mean)
Time per request:       8431.032 [ms] (mean)
Time per request:       0.843 [ms] (mean, across all concurrent requests)
Transfer rate:          2138.21 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%   4162
  66%   5298
  75%   6282
  80%   6909
  90%   7671
  95%   7984
  98%   8183
  99%   8252
 100%   8316 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="纯粹多线程 性能"&gt;纯粹多线程 性能&lt;/h4&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document Path:          /test/test5.json
Document Length:        1680 bytes

Concurrency Level:      10000
Time taken for tests:   6.715 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      18460000 bytes
HTML transferred:       16800000 bytes
Requests per second:    1489.22 [#/sec] (mean)
Time per request:       6714.927 [ms] (mean)
Time per request:       0.671 [ms] (mean, across all concurrent requests)
Transfer rate:          2684.67 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%   3293
  66%   4331
  75%   4954
  80%   5229
  90%   5799
  95%   6261
  98%   6435
  99%   6511
 100%   6632 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="性能总结"&gt;性能总结&lt;/h2&gt;&lt;h3 id="EventMachine and Fiber and ThreadPool"&gt;EventMachine and Fiber and ThreadPool&lt;/h3&gt;
&lt;p&gt;在这个情况下，EM 和线程池的性能基本一致，EM 稍稍快一点&lt;/p&gt;
&lt;h3 id="EventMachine and Fiber"&gt;EventMachine and Fiber&lt;/h3&gt;
&lt;p&gt;当 socket 由 EventMachine 接管的时候，性能还是比线程池的要慢好一些，基本上要慢 20%&lt;/p&gt;

&lt;p&gt;在打开 GClog 之后，发现&lt;code&gt;EventMachine and Fiber模式&lt;/code&gt;明显会有更多的 GC，不过在内存上，&lt;code&gt;EventMachine and Fiber模式&lt;/code&gt;是多线程模式的一半&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EventMachine 在处理 socket 上，其实性能并没有想象的那么好&lt;/li&gt;
&lt;li&gt;多线程模式在绝大多数情况下对 socket 还是有比较好的性能的&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但是 EventMachine and Fiber 模式在这个场景下，会有些用处：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;当你突然会有大量访问远程 api 的请求的时候，这个时候，我们可以开更多的 fiber 来支撑这些 cpu 占用不高，纯粹是要等待返回的请求，我们可以在一个进程上开 1024 个 fiber，每次远程访问 api 慢的时候，但是，应该不会有人开 1024 个线程吧&lt;/li&gt;
&lt;li&gt;当在微服务模式下，每次客户的请求或多或少会访问多个组件，以及组件调用组件，每次客户的请求将会消耗掉多个线程资源，相对而言，在 fiber 模式下，一次用户调用占用多个 fiber，似乎会占用更少的资源&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="一点额外的话"&gt;一点额外的话&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;要支持 C10K 的话，一定要注意 nginx 和 rainbows 上 backlog 的问题，关于具体的 backlog，有时间我再开一贴讲讲在支持 C10K 的时候，服务器需要配置些啥&lt;/li&gt;
&lt;li&gt;如果你是通过 http 来调用自己的微服务框架，一定要记住，socket 要复用!!socket 复用的 http 调用比不复用的&lt;strong&gt;快将近 4 倍&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;希望对大家有帮助&lt;/p&gt;</description>
      <author>killyfreedom</author>
      <pubDate>Sat, 20 Feb 2016 17:00:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/29041</link>
      <guid>https://ruby-china.org/topics/29041</guid>
    </item>
    <item>
      <title>聊聊 MRI Ruby Concurrent [未完成]</title>
      <description>&lt;p&gt;随着项目越做越大，对 MRI Ruby 的几种并发服务模型也有了一部分的了解，也抛出来和大家聊聊，希望有点新的收获。&lt;/p&gt;

&lt;p&gt;众所周知，MRI Ruby 是一个拥有 GIL 的 Ruby 实现，先天决定了在同一时刻，只会有一个线程被运行，虽然依然无法在根本上解决线程安全的问题，但是根本上来说，一个基础架构的偏向，会导致整体语言相关的社区的导向，简而言之，在 ruby 的世界里，绝大部分时间里，Thread，这个词，就是被忽略的命，纵使 rails，也是才开始重视多线程的问题。&lt;/p&gt;

&lt;p&gt;然而，开发越来越多，做的越来越多，慢慢发现，GIL 真的是一个好大好大的限制，但，即使在这样的限制条件下，我们依然在尝试着各种不同的并发模型。&lt;/p&gt;

&lt;p&gt;所有的并发模型，在 MRI 的 ruby 这个有 GIL 的实现下，最大的目的就是：&lt;/p&gt;

&lt;p&gt;分离 IO 操作和 CPU 操作，让 IO 操作在执行的同时，CPU 并不会堵塞在 IO 等待中，从而实现更高的程序效率。&lt;/p&gt;

&lt;p&gt;由 function1 --- io1 --- function2 --- io2 --- 这样的执行策略变成:
function1 -- function2 -- funtion1 --- function 2
        io1 --------io2---------io1-----------io2----&lt;/p&gt;

&lt;p&gt;用一个比较粗略的方式来比较下这几种并发服务模型的效率.
假设，我们认为每个 function 就是一个独立的事务，那么，在事务和事务之间切换的性能也代表了其不考虑 IO 干扰的最高的并行能力，也就是说，只考虑这几个模型下的 Context Switch 的能力。&lt;/p&gt;

&lt;p&gt;做了这几年，用到的最多的，就这几种了：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;EventMachine&lt;/li&gt;
&lt;li&gt;MultiThread&lt;/li&gt;
&lt;li&gt;Celluloid&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;EventMachine 是最早接触的了，本质上来说，EM 是一个巨大的 for 循环，将 IO 操作独立具体事务之外，将控制权让渡于事件核心，由事件核心以事件的方式触发流程继续。&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;'eventmachine'&lt;/span&gt;
&lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;EM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="no"&gt;EM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="no"&gt;EM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="mi"&gt;20&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="nb"&gt;puts&lt;/span&gt; &lt;span class="vg"&gt;$c&lt;/span&gt;
  &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"avg: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&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;r&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="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到的平均值:
&lt;code&gt;avg: 96511&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;EventMachin 的缺点:
callback 太多...&lt;/p&gt;

&lt;p&gt;我们想要的代码是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;do_a&lt;/span&gt;
&lt;span class="n"&gt;do_b&lt;/span&gt;
&lt;span class="n"&gt;do_c&lt;/span&gt;
&lt;span class="n"&gt;do_d&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="n"&gt;do_a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;do_b&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;do_c&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;do_d&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就是 MultiThread:
很神奇，在 1.9 之前，ruby 甚至是不支持 Native 线程的，其线程创建在其 VM 之上，直到 1.9 之后，线程才真正使用了系统级别的线程实现，线程之间的调度，由用户级变成了内核级，从轻量级的实现变成了重量级的实现，切换速度下降了，倒是也带来了不少更接近与原生实践的能力。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#encoding: utf-8&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'thread'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'irb'&lt;/span&gt;
&lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="mi"&gt;2&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="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="c1"&gt;# $cv.wait#($m)&lt;/span&gt;
      &lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="c1"&gt;# $cv.signal&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;pass&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="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="mi"&gt;20&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="nb"&gt;puts&lt;/span&gt; &lt;span class="vg"&gt;$c&lt;/span&gt;
  &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="vg"&gt;$c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"avg: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&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;r&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="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 1 thread, avg: 755507
# 2 thread, avg: 114600
# 3 thread, avg: 56838
# 4 thread, avg: 37229
# 5 thread, avg: 35603
# 100 thread, avg: 31825
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;困了...Celluloid 等过两天再写....&lt;/p&gt;</description>
      <author>killyfreedom</author>
      <pubDate>Mon, 07 Jul 2014 23:35:53 +0800</pubDate>
      <link>https://ruby-china.org/topics/20367</link>
      <guid>https://ruby-china.org/topics/20367</guid>
    </item>
    <item>
      <title>Apache Benchmark 的使用的个人浅薄经验</title>
      <description>&lt;p&gt;之前为了 bench node 和 ruby 在 EM 环境下的性能差距，写了一个帖子，之后有人问我是如何使用工具来评测 http 服务的并发性能的，于是乎就总结了下自己是如何使用 apache benchmark 的经验，懂得技术大牛就呵呵一过，不知道或者有点了解的大家就一起讨论讨论。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;apache benchmark&lt;/code&gt;作为&lt;code&gt;apache&lt;/code&gt;旗下的 http server 的性能评测工具，个人觉得是非常简单非常好用的一个东西的，而且，在大并发的情况下，并不会吃掉太多性能，个人用起来觉得还是很爽的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;安装方式&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Linux&lt;/code&gt;: &lt;code&gt;sudo apt-get install apache2-utils&lt;/code&gt;
&lt;code&gt;MacOS&lt;/code&gt;: 装了 XCODE 或者 XCODE 的命令行工具之后就自然有了&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;使用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;一般最简单的使用方式是：&lt;code&gt;ab -c 并发数量 -n 总数量 url&lt;/code&gt;的方式，具体很多参数可以参照帮主文档
&lt;code&gt;man ab&lt;/code&gt; 或者
&lt;code&gt;ab -h&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;授人以鱼不如授人以渔&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;值得提醒的是对于那些需要上下文的请求，可以使用 ab 的 cookie 的参数来设置 cookie 值从而达到在保存了登录状态的上下文的情况下测试性能&lt;/p&gt;

&lt;p&gt;通常，我们被测试的机器和测试的机器往往不在一台物理服务器上，如果在一台物理服务器上，测试和服务的提供是会产生影响的，毕竟 CPU 在进程间切换是会带来很大的性能损耗的，如果做严谨的测试，不推荐在一台服务器上做这个事情&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;输出&lt;/strong&gt;
当你尝试编辑 bench 了一个 url 之后，往往会得到很多输出，比如&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;This is ApacheBench, Version 2.3 &amp;lt;&lt;span class="nv"&gt;$Revision&lt;/span&gt;: 655654 &lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 &lt;span class="o"&gt;(&lt;/span&gt;be patient&lt;span class="o"&gt;)&lt;/span&gt;
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        
Server Hostname:        127.0.0.1                        
Server Port:            4001                                    

Document Path:          /
Document Length:        0 bytes

// 以上是你打压力的host, port等一部分的信息, 应该都看得懂把...

Concurrency Level:      100
// 这个标志了这1000个请求完成了总共消耗了这些时间
Time taken &lt;span class="k"&gt;for &lt;/span&gt;tests:   2.987 seconds 
Complete requests:      1000
// 这1000个请求中失败的次数,如果又失败,它也有输出非2XX的失败
// 如果测试中出现了错误,最好看下这些错误是不是你所期望的
Failed requests:        0
Write errors:           0
// 这1000个请求总共传输了这些数据
Total transferred:      637272 bytes
HTML transferred:       0 bytes
// 每秒执行了334个请求,这个可以看到你服务器每秒能够处理多少个这样的url请求
// 是一个很重要的性能指标
Requests per second:    334.74 &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="c"&gt;#/sec] (mean)&lt;/span&gt;
// 第一个时间是以你的并发数为一组,这里我是设置了并发100,那么
// 就是这一组请求所能消耗的总时间
Time per request:       298.739 &lt;span class="o"&gt;[&lt;/span&gt;ms] &lt;span class="o"&gt;(&lt;/span&gt;mean&lt;span class="o"&gt;)&lt;/span&gt;
// 这个就是平均下来每个请求的消耗的时间了
Time per request:       2.987 &lt;span class="o"&gt;[&lt;/span&gt;ms] &lt;span class="o"&gt;(&lt;/span&gt;mean, across all concurrent requests&lt;span class="o"&gt;)&lt;/span&gt;
// 测试的时候通过网络的传输的速率
Transfer rate:          208.32 &lt;span class="o"&gt;[&lt;/span&gt;Kbytes/sec] received

Connection Times &lt;span class="o"&gt;(&lt;/span&gt;ms&lt;span class="o"&gt;)&lt;/span&gt;
              min  mean[+/-sd] median   max
Connect:        0    0   0.6      0       3
Processing:    24  297 780.6     39    2648
Waiting:       23  295 780.5     36    2646
Total:         25  298 781.1     39    2650
// 以上这段数据标志了一个请求从连接,发送数据,接收数据这个三个大致的时间,最小以及平均值


Percentage of the requests served within a certain &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;ms&lt;span class="o"&gt;)&lt;/span&gt;
  50%     39
  66%     40
  75%     41
  80%     41
  90%   2624
  95%   2647
  98%   2650
  99%   2650
 100%   2650 &lt;span class="o"&gt;(&lt;/span&gt;longest request&lt;span class="o"&gt;)&lt;/span&gt;

// 以上的数据标志了所有的api请求的消耗时间分布的区间
// 可以看到, 50%的请求是在39MS以下, 66%的请求实在40MS一下
// 这个数据其实可以看出很多问题来
//       如果这个数据很平均,即第二列的数据的值几乎都差不多,其实可以说明,至少
//       你的服务器在这个并发的情况下对于各个请求所能提供的能力是均衡的
//       在面对这种压力的并发的情况下,对资源没有明显的使用短缺
//       如果这个数据的差距非常大,这个情况就要结合自身的应用分析了
//       就像现在这种情况,可以发现,明显有一部分请求在很久之后才得到响应
//       说明,在并发的情况下,CPU或者IO或者其它资源没有被均衡的使用

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我尝试解析了下这个输出，如有不对的地方也请大家指出呐&lt;/p&gt;

&lt;p&gt;还有就是个人经验，对于 api 服务器来说，重要的不是单个请求处理的有多快，而是
&lt;strong&gt;在同一段时间能够处理多少请求&lt;/strong&gt;
这一段时间在各个场景下的解读，往往能够影响整个服务的设计&lt;/p&gt;</description>
      <author>killyfreedom</author>
      <pubDate>Tue, 03 Sep 2013 23:44:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/13870</link>
      <guid>https://ruby-china.org/topics/13870</guid>
    </item>
    <item>
      <title>Ruby 性能真的比 Node 差这么多么？</title>
      <description>&lt;p&gt;公司内部有个项目，很简单，几乎没什么业务逻辑，就是根据几个 Key 到 mongodb 中去查询，然后前端返回 JSON&lt;/p&gt;

&lt;p&gt;一开始是使用 rails 配合 grape 出的第一版，跑在 passenger 下，然后被 passenger 蛋疼的并发模式伤到了，切到了 rack 配合 grape 跑在 thin 上，快了很多，2 核机器 4 个 process 下，性能提高了 2 倍，但是由于历史原因还是载入整个 rails 环境，而且，Mongodb 使用了 Mongoid 的驱动&lt;/p&gt;

&lt;p&gt;看了 robbin 同志关于 api 后台的描述，后面又不满足了，去掉了 rails 环境，单独使用 grape 配合 mongodb 的原生驱动，加入 rainbows 的线程池模式，4 个 process,64 个线程池加上 64 个连接池，大概也就提升了 50%&lt;/p&gt;

&lt;p&gt;后面，闲着无聊，觉得 mongodb 的原始驱动是基于阻塞的 socket 模式，在线程池模式下，将会带来大量的线程切换的消耗，因此萌生了基于 Eventmachine 搞一个异步 io 查询的连接池，先尝试了下用 Node 写了个基本的从 Mongod 中查询 api，然后基于 mongodb 的原生驱动写了个基本可用&lt;del&gt;报错不管&lt;/del&gt;的异步游标的实现，对比下，顿时，我和我的小伙伴都惊呆了&lt;/p&gt;

&lt;p&gt;使用&lt;code&gt;ruby&lt;/code&gt; + 自制的异步游标 + &lt;code&gt;rainbows&lt;/code&gt; , 单进程大概可以负载 15 个请求每秒，而同样的查询同样的数据源，NODE 单个进程可以负担 50+ 的请求每秒，而且，平均消耗时间都在 ruby 的一半以下&lt;/p&gt;

&lt;p&gt;ruby 是 1.9.3--p286，有点老，
node 用的是 apt-get 的装的
在 2K 的数据中查询出 113 个数据，数据 JSON 化之后大概有 70+KB，有点大，
但是差距真的感觉十分明显啊&lt;/p&gt;

&lt;p&gt;具体代码在：
&lt;a href="https://github.com/shengxiang205/async_cursor_test.git" rel="nofollow" target="_blank"&gt;https://github.com/shengxiang205/async_cursor_test.git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;node 的项目在  &lt;code&gt;node/query_app&lt;/code&gt; 下&lt;/p&gt;

&lt;p&gt;代码写的有点乱，但是结果还是很震惊啊，我一直以为如果用 EM 配合异步 IO 的话，虽然可能会比 node 差一点，但是应该也差的不多，自己测试了下，结果还是不是这样的。。。&lt;/p&gt;

&lt;p&gt;不知道是不是我代码有问题。。&lt;/p&gt;

&lt;p&gt;内牛满面啊&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;最新修正&lt;/strong&gt;
将 113 个数据切换成 1 个数据再次做对比
ruby 大概跑出了&lt;code&gt;400+ req/s&lt;/code&gt;, node 跑出了 &lt;code&gt;760 req/s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;在接受 &lt;a href="/robbin" class="user-mention" title="@robbin"&gt;&lt;i&gt;@&lt;/i&gt;robbin&lt;/a&gt; 的提议，加入 GC 参数的设置：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUBY_HEAP_MIN_SLOTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;600000
&lt;span class="nv"&gt;RUBY_FREE_MIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;200000
&lt;span class="nv"&gt;RUBY_GC_MALLOC_LIMIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60000000
&lt;span class="nb"&gt;export &lt;/span&gt;RUBY_HEAP_MIN_SLOTS RUBY_FREE_MIN RUBY_GC_MALLOC_LIMIT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ruby 大概提升到了 &lt;code&gt;530 req/s&lt;/code&gt;, 提升巨大，虽有差距，但是在这种情况下差的也不多了&lt;/p&gt;

&lt;p&gt;得出以下结论：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;纯 IO 的并发并非是 ruby 的强项，就算是基于 EM 和 Node 依然有差距&lt;/li&gt;
&lt;li&gt;JSON 序列化的性能上，ruby 的比 Node 差比较多，我这边是基于 JSON(1.7.7) 的序列化库，从 113 个数据到 1 个数据，ruby 追上了 node 不少&lt;/li&gt;
&lt;li&gt;GC 参数对于这种场景下还是有比较大的影响的&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;有时间尝试下基于 &lt;code&gt;celluloid-io&lt;/code&gt; 做个类似的 demo&lt;/p&gt;</description>
      <author>killyfreedom</author>
      <pubDate>Mon, 19 Aug 2013 22:29:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/13442</link>
      <guid>https://ruby-china.org/topics/13442</guid>
    </item>
  </channel>
</rss>
