<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>falm (Jason)</title>
    <link>https://ruby-china.org/falm</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>[上海] 薄荷科技 2018 诚聘 Ruby 工程师</title>
      <description>&lt;h2 id="关于薄荷"&gt;关于薄荷&lt;/h2&gt;
&lt;p&gt;薄荷是一家在健康领域领先的移动互联网公司，主要产品包括“薄荷”、“食物库”、“萤火虫”和“超模 25”。“薄荷”是减肥第一应用，长期位于 App Store 健康健美榜和各大 Android 应用市场健康或生活类应用前列，总用户已达数千万级别。“食物库”是食物营养第一应用，拥有中国最全的食物营养数据。详细介绍看这里 薄荷系列 app。详细介绍看这里 薄荷系列 app。&lt;/p&gt;

&lt;p&gt;薄荷是国内较早使用 Ruby 的公司，从 2007 年开始所有的后端核心系统全部基于 Ruby 构建。随着系统规模不断增长，使用 Ruby 过程中遇到很多问题和挑战，甚至受到过很多质疑，但是我们没有放弃，迎难而上，经受住了考验，我们一直对 Ruby 充满了爱和信心。薄荷技术团队崇尚极客精神，热情参与 Ruby 社区各种活动，经常分享我们技术演进的各种经验教训。&lt;/p&gt;
&lt;h2 id="薄荷的后端团队"&gt;&lt;strong&gt;薄荷的后端团队&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;工程师们在打牌&lt;/strong&gt;
&lt;img src="http://upload-images.jianshu.io/upload_images/1767848-ca090955e95b79a4.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt="团建打牌"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;娃娃机&lt;/strong&gt; 代码写累了，走抓个娃娃
&lt;img src="http://upload-images.jianshu.io/upload_images/1767848-ab09a7f5e25bbf9b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt="抓娃娃"&gt;&lt;/p&gt;
&lt;h2 id="分享活动"&gt;分享活动&lt;/h2&gt;
&lt;p&gt;薄荷的后端团队活跃于各种技术分享会，并且还经常作为分享嘉宾，分享一下薄荷在 Ruby 方面的使用经验。
&lt;strong&gt;上海 Ruby 线下活动&lt;/strong&gt;
&lt;a href="https://ruby-china.org/topics/29997" title=""&gt;2016 年 5 月 21 日 SH ❤ Ruby 聚会 (薄荷主办)&lt;/a&gt;
&lt;img src="https://l.ruby-china.com/photo/2016/18b7bdbdbf4474e12a9443b21f8d7bce.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;薄荷工程师&lt;a href="/RubyConfChina" class="user-mention" title="@RubyConfChina"&gt;&lt;i&gt;@&lt;/i&gt;RubyConfChina&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=gxCk4jIEwJw&amp;amp;t=477s&amp;amp;index=11&amp;amp;list=PLTUHmtFhYC6i5_L6XQEqsIh7I0ZU8xfCm" rel="nofollow" target="_blank" title=""&gt;RubyConfChina 2016&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=Bolnzxxxb7A&amp;amp;index=5&amp;amp;list=PLTUHmtFhYC6iEwu4h2REYhNKCHFFHYngS" rel="nofollow" target="_blank" title=""&gt;RubyConfChina 2017&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/e314f6ec-8487-4cf5-a196-800c13f861ab.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="薄荷后端团队在做什么"&gt;薄荷后端团队在做什么&lt;/h2&gt;
&lt;p&gt;2018 年我们会有很多大动作，所以希望更多的 Ruby 小伙伴加入，薄荷的后端基本下面这四件事情。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;设计和构建自动化服务系统，提供薄荷服务效率。&lt;/li&gt;
&lt;li&gt;利用机器学习帮助薄荷 App 屏蔽广告和垃圾信息，给用户智能推荐健康知识&lt;/li&gt;
&lt;li&gt;针对薄荷现有系统继续数据分析，统计和数据埋单&lt;/li&gt;
&lt;li&gt;为薄荷 App 及 H5 应用开发 API 接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="关于职位"&gt;关于职位&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;我们希望你是有 1~3 年的后端开发经验（Ruby）工程师&lt;/li&gt;
&lt;li&gt;&lt;p&gt;熟练掌握 Ruby 和 Rails, 以及 Web 开发相关技术，追求极致用户体验；&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;编程基础良好，了解常见设计模式和编程最佳实践，追求优雅的代码；&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;热爱技术开发，有极客精神，参与过开源项目或常写技术博客加分哦！&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;加分项&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Github 的开源项目比较活跃。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;有自己的技术博客。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;熟悉前端开发，有使用 React 的经验。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="待遇"&gt;待遇&lt;/h4&gt;
&lt;p&gt;初中级职位 10~15k，高级职位 15~30k，上不封顶，每季度还可能有丰厚的绩效奖金。目前公司处于快速发展阶段，公司商业模式确定，营收稳健，并且已有顶尖美元风投基金加入（晨兴，SIG，DCM 和高通），前景可期。我们将把你视为伙伴，希望通过一起努力，能够提供超出你预期的回报。&lt;/p&gt;
&lt;h2 id="工作环境"&gt;工作环境&lt;/h2&gt;
&lt;p&gt;最后抛一下薄荷新办公室的照片，薄荷科技搬到世纪大道了，4 个线路的换乘站，陆家嘴中心位置，交通非常方便。&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" title="" alt="IMG_0089.jpg"&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" title="" alt="IMG_0093.jpg"&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" title="" alt="IMG_0095.jpg"&gt;&lt;/p&gt;
&lt;h2 id="联系方式"&gt;联系方式&lt;/h2&gt;
&lt;p&gt;最后快到碗里来，薄荷期待你的加入 E-mail: vincent(at)boohee.com&lt;/p&gt;</description>
      <author>falm</author>
      <pubDate>Sat, 03 Mar 2018 11:22:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/35131</link>
      <guid>https://ruby-china.org/topics/35131</guid>
    </item>
    <item>
      <title>[RubyConfChina2017 话题分享] Ruby Web 实时通讯方案剖析 (资料和参会感受)</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;首先非常感谢这次大会的组织者们（ &lt;a href="/jasl" class="user-mention" title="@jasl"&gt;&lt;i&gt;@&lt;/i&gt;jasl&lt;/a&gt;  &lt;a href="/lgn21st" class="user-mention" title="@lgn21st"&gt;&lt;i&gt;@&lt;/i&gt;lgn21st&lt;/a&gt; &lt;a href="/Rei" class="user-mention" title="@Rei"&gt;&lt;i&gt;@&lt;/i&gt;Rei&lt;/a&gt;  &lt;a href="/martin91" class="user-mention" title="@martin91"&gt;&lt;i&gt;@&lt;/i&gt;martin91&lt;/a&gt; 还有其他我不认识的小伙伴) 可以想象举办一个 500 人规模的技术大会，而且还要保持高质量是何其的不容易，谢谢你们。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="其他讲师的分享"&gt;其他讲师的分享&lt;/h2&gt;
&lt;p&gt;这里我先分享一下几个我印象比较深比较感兴趣的的讲师的演讲，（其他讲师的分享也很精彩，只不过有些并未涉猎）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;nginx with ruby - &lt;a href="/tylerdiaz" class="user-mention" title="@tylerdiaz"&gt;&lt;i&gt;@&lt;/i&gt;tylerdiaz&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;tylerdiaz 分享的主题的 mruby，据我了解是被广泛应用于日本的嵌入式开发领域。国内在这一块的应用没怎么听过，这也是 Ruby 社区近几年推的几个发展方向之一吧，国内的 Ruby 社区的路子相对来说没有那么宽。tylerdiaz 作为开场的英文演讲舞台风格非常好，尤其考虑到了我们英语可能听不懂听不清怕，刻意的把自己的语速降的很慢 (英文演讲的语速都比较快)。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;异步编程奥德赛 - &lt;a href="/dsh0416" class="user-mention" title="@dsh0416"&gt;&lt;i&gt;@&lt;/i&gt;dsh0416&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;之前队长有过多少次交流，在上海的分享会上也聊过关于异步编程的话题，midori 在我研究实时通讯的时候研究过它的源代码，本来是在我的分享上也准备了 midori 的介绍，不过后来看到队长的分享排在了我的前面，所以也就无所谓讲了，另外队长在台下和我说，这么多人的分享还是非常紧张的。不过他上台之后在聚光灯下，幽默风趣的演讲风格一点也看不出来紧张哈哈。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;为 Ruby 设计一款 AOT - &lt;a href="/psvr" class="user-mention" title="@psvr"&gt;&lt;i&gt;@&lt;/i&gt;psvr&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这个工具做的很实用，我之前的工作中就是有一个将 Rails 项目打包发布的需求，当时考虑的还是使用 JRuby 打成一个 Jar 包进行处理，现在有这种解决方案，可以解决很多的现实问题。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;金数据是如何鉴黄的 - &lt;a href="/warmwind" class="user-mention" title="@warmwind"&gt;&lt;i&gt;@&lt;/i&gt;warmwind&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;金数据使用 SVM 做鉴黄的分享非常棒，近些年的技术发展的方向，Ruby 社区现在也开始关注机和实践机器学习了，薄荷其实也在广告过滤上面应用了机器学习不过使用的是贝叶斯方法。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;机器学习入门 - &lt;a href="/ihower" class="user-mention" title="@ihower"&gt;&lt;i&gt;@&lt;/i&gt;ihower&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
ihower 的分享作为机器学习的 101 非常不错，我之前对深度学习是没有什么了解的，其实在之前吃饭交流的时候，ihower 就已经给我们讲了一下关于深度神经网络的相关知识，可以说收获颇丰。&lt;/p&gt;
&lt;h2 id="我的分享 - Ruby Web 实时通讯方案剖析"&gt;我的分享 - Ruby Web 实时通讯方案剖析&lt;/h2&gt;
&lt;p&gt;报名讲师的契机是在大会征集题目开始后，&lt;a href="/vincent" class="user-mention" title="@vincent"&gt;&lt;i&gt;@&lt;/i&gt;vincent&lt;/a&gt;就和我说，我们要不要把最近这大半年在 Ruby Web 实时通讯上面的研究和实践给大家分享一下，（其实今年 3 月份的时候，Ruby 上海活动的时候分享了一下）这样我们才真正的把之前涉及到的关于 web 实时通讯的演讲的方案，重新整理，有些还不是很透彻的地方，有重新温习。最后整理出一份比较完整的介绍 RubyWeb 实时通讯方案的分享。&lt;/p&gt;

&lt;p&gt;我自己的分享感觉语速和节奏没有控制好，有一有要讲的东西没表达出来，虽然我经常在公司内部做分享，但是十几二十人和 500 人还是比不了的。还有当&lt;a href="/bhuztez" class="user-mention" title="@bhuztez"&gt;&lt;i&gt;@&lt;/i&gt;bhuztez&lt;/a&gt; 说出成功人士才跑 benchmark 之后压力山大，我的分享里面有六个 benchmark🤣。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://falm.github.io/presentations/slides/ruby_realtime/" rel="nofollow" target="_blank" title=""&gt;Ruby Web 实时通讯方案剖析 Slides&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下面内容是摘取自我在薄荷内部分享的关于实时通讯的四篇文章：&lt;/p&gt;
&lt;h3 id="一 Hijacking API VS『native』Websocket"&gt;一 Hijacking API VS『native』Websocket&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;hijacking api&lt;/strong&gt; 是在 Rack 1.5 是被加入的功能，因为 Rack API 本身无法支持想 http streaming websocket 这两长连接的请求，但是这个 API 是在 Rack 1.x 的临时方案，基本上不会再 Rack 2.0 中出现。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;native websocket&lt;/strong&gt; 这里的 native 是指定由 application server 提供内置的 websocket 功能，通过对外暴露 on_message on_open 这些回调接接口来进行请求处理，而不是像 hijacking API 通过直接暴露 socket io 对象。&lt;/p&gt;

&lt;p&gt;HijackingAPI 和『native』websocket 的主要区别就是两种方式使用 reactor 和事件循环的数量。Hijacking API 是从 Server 中获取 socket，这就意味着应用程序需要自己监听 socket 的事件，因为这时候 server 已经忽略了这个 socket. 也就是说你需要自己在应用程序中管理你的 socket，再启动一个 reactor，一个事件循环器和线程池。使用 hijacking API 同时也需要应用程序自己去管理 socket 的 output buffer ( IO#write 和 read 在 network buffer 已满的时候，只能做部分写入或读取，application server 为处理 HTTP 请求的时候就已经内置该功能了，所以你又要为 websocket 在来实现一套）。&lt;/p&gt;

&lt;p&gt;同样 websocket 协议的处理也是需要应用程序自己处理的（当然如果 server 和应用程序都没有使用 C 扩展或其他来加速处理协议的话，也无所谓）&lt;/p&gt;

&lt;p&gt;总结下来没有使用 hijacking API 的“native”websocket 可以用少的代码和资源获得更好的性能，并且所有的事件和网络处理都可以集中一个模块还中，而 hijacking API 的实现需要在 server 和应用程序中各自实现一套。&lt;/p&gt;
&lt;h3 id="二 Understanding Midori"&gt;二 Understanding Midori&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;注：本文基于 em-midori v0.4.1 版本所写&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/heckpsi-lab/em-midori" rel="nofollow" target="_blank" title=""&gt;Midori&lt;/a&gt;从 web 框架的角度去看，它是一种类 Sinatra 式的 Web 开发框架，不同点就是它是基于事件循环模式的异步 web 框架，这样它真正要对标的就是 async_sinatra 这里的异步框架。与 sinatra 一样，midori 不是一个全栈式的 MVC 框架，当前版本强调的是通过 DSL 来到达简洁易用，快速构建 API 系统的 web 框架。&lt;/p&gt;

&lt;p&gt;从 em-midori 开始 midori 底层使用的 I/O 库是 EventMachine，并且是用 EventMachine 事件循环库。后期的 midori 该为使用 nio4r 来作为底层支持，使用它的原因是 nio4r 的开发支持，稳定性和特性要比 EM 好。midori 的路由解析器使用了 sinatra 出品的 Mustermann，在这方面我觉得是做的比较好的，使用业界成熟的经过考验的组件，有助于提高框架的稳定性和实用性，并且在路由匹配功能上使用深度优先搜索算法，加快路由速度。&lt;/p&gt;
&lt;h3 id="结构"&gt;结构&lt;/h3&gt;
&lt;p&gt;大体上来说 midori 的架构是建立在 fiber 和 nio4r 之上，也是框架本身宣传的特点异步框架，那么底层就要使用支持异步的框架。Midori 是使用自制生态的 Muraski 组件来使用 nio4r 的，而 nio4r 底层又是使用 libev 网络库实现 I/O 能力的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Midori 框架启动和接收请求的执行流程：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/1767848-b2d3616581a5f807.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt="image.png"&gt;&lt;/p&gt;
&lt;h4 id="Runner"&gt;Runner&lt;/h4&gt;
&lt;p&gt;Midori 的启动器&lt;strong&gt;Midori::Runner&lt;/strong&gt;的内部使用了 Ruby 标准库的 TCPServer 来启动 web 服务，在将端口 i/o 对象通过 EventLoop 注册到 nio4r 的监听器上，当有 socket 数据通过 I/O 端口发送过来后，就会执行回调函数，调用&lt;strong&gt;Midori::Connection&lt;/strong&gt;来处理请求。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Midori::Runner&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;running?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;EventLoop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;running?&lt;/span&gt;
    &lt;span class="vi"&gt;@logger.info&lt;/span&gt; &lt;span class="s2"&gt;"Midori &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Midori&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is now running on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;port&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="nf"&gt;blue&lt;/span&gt;
    &lt;span class="vi"&gt;@server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@bind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;EventLoop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:r&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;monitor&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept_nonblock&lt;/span&gt;
      &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Midori&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Connection&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;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@logger&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;async_fiber&lt;/span&gt;&lt;span class="p"&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;do&lt;/span&gt;
      &lt;span class="vi"&gt;@before.call&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;EventLoop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
    &lt;span class="kp"&gt;nil&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="EventLoop"&gt;EventLoop&lt;/h4&gt;
&lt;p&gt;整个事件循环和监听 I/O 的 nio4r 都被封装在了 Muraski 组件中。其核心代码就是支持 I/O 事件回调函数和启动事件循环。&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;EventLoop&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;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rw&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;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@selector.nil&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;@queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
        &lt;span class="vi"&gt;@queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@selector.nil&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
        &lt;span class="vi"&gt;@selector.register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@ios&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;callback: &lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="vi"&gt;@queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&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;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;running?&lt;/span&gt;
      &lt;span class="vi"&gt;@stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
      &lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="vi"&gt;@stop&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@selector.nil&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
        &lt;span class="vi"&gt;@selector.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.2&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;monitor&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="c1"&gt;# Timeout for 0.2 secs&lt;/span&gt;
          &lt;span class="vi"&gt;@ios&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monitor&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;timer_once&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="vi"&gt;@stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="Connection &amp;amp; Server"&gt;Connection &amp;amp; Server&lt;/h4&gt;
&lt;p&gt;当 EventLoop 注册的回调函数被调用后，从框架的角度就是一个连接被建立一个请求被接受，那么接下来就需用 Connection 去处理连接，在 Connection 的内部非常有意思的是，其主要职责就是再注册一个事件循环器来持续监听建立起来的 socket 连接，剩下的处理请求的数据是交由 mixin 的 Server 模块处理。&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;Midori::Connection&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Midori&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Server&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;EventLoop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:rw&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;monitor&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="vi"&gt;@monitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readable?&lt;/span&gt;
        &lt;span class="n"&gt;receive_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writable?&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="vi"&gt;@data&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
          &lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_nonblock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="vi"&gt;@data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
        &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="vi"&gt;@close_flag&lt;/span&gt;
          &lt;span class="n"&gt;close_connection&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="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样分解的作用就是，Midori 是作用一个同事支持短连接和 websocket 长连接的 web 框架，底层的应用服务器部分也是要自己处理的（因为没有使用 Rack)。那么对一个长连接的数据不是在 socket 建立和就发送过来的，而是会持续不断的发送和接受直到连接关闭，所以 Midori 在主事件循环监听 I/O 端口外，还内嵌了事件循环监听特定 socket 连接。&lt;/p&gt;
&lt;h4 id="Fiber"&gt;Fiber&lt;/h4&gt;
&lt;p&gt;Midori 中 Fiber 使用的最大用途其实是在 midori-contrib 的数据库驱动中，以 mysql2 为例 midori 利用元编程打开了 mysql2 的 I/O 查询部分，使用 Fiber 在 I/O 阻塞的时候挂起，等 I/O 返回时候恢复。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MYSQL_SOCKETS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;await&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="no"&gt;EventLoop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:rw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# Writable&lt;/span&gt;
      &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;database_timezone: &lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;application_timezone: &lt;/span&gt;&lt;span class="no"&gt;Sequel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application_timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;stream: &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;async: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c1"&gt;# Readable&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="no"&gt;EventLoop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deregister&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;async_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Mysql2&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PromiseException&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;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;next&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;http 请求和 websocket 也都是通过 Fiber 作为执行单元来执行。但是 Fiber 在这里并没有起到什么实质性的作用 fiber 被创建后就直接被执行了，期间也没有挂起调度。它可能是要在 Midori 未来的版本中会有特殊的用途，目前的版本是完全可以使用 proc 对象替代的。&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;Midori::Server&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;receive_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;async_fiber&lt;/span&gt;&lt;span class="p"&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;do&lt;/span&gt;
        &lt;span class="k"&gt;begin&lt;/span&gt;
          &lt;span class="n"&gt;_sock_domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remote_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_remote_hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remote_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peeraddr&lt;/span&gt;
          &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remote_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remote_ip&lt;/span&gt;
          &lt;span class="vi"&gt;@request.ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;
          &lt;span class="vi"&gt;@request.port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;
          &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_nonblock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16_384&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;@request.parsed&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="vi"&gt;@request.body_parsed&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
            &lt;span class="n"&gt;websocket_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;StringIO&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;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="vi"&gt;@request.parse&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="n"&gt;receive_new_request&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@request.parsed&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="vi"&gt;@request.body_parsed&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
          &lt;span class="n"&gt;close_connection&lt;/span&gt;
          &lt;span class="vi"&gt;@logger.warn&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@request.ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - - &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yellow&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&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;h4 id="最后"&gt;最后&lt;/h4&gt;
&lt;p&gt;Midori 作为一个年轻的 Ruby Web 框架站在巨人的肩膀上，做出了不错的性能，而且是国人所做，所以应当支持和鼓励，但是客观的说在目前国内的 Ruby 生产实践中，依然是以 Rails 为核心的全栈式 web 框架为主流，类似 sinatra 或其他的异步框架，市场份额非常少而且 Midori 现在发布稳定的正式版。另外从性能角度看 Midor 上随有优势，但按照 roadmap v2.0 版本，要支持 MVC 和 http2，在性能上难免有所下降。&lt;/p&gt;

&lt;p&gt;造轮子容易，用轮子难，所以希望 Midori 项目能够改进不足，补齐文档，多加宣传。主厨推荐 (&lt;em&gt;Omakase&lt;/em&gt;) 吃了这么多年，是不是该换换口味了。&lt;/p&gt;
&lt;h3 id="三 Reactor &amp;amp; I/O多路复用"&gt;三 Reactor &amp;amp; I/O 多路复用&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;I/O 多路复用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/1767848-4136f7cc8dfec231.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt="epoll"&gt;&lt;/p&gt;

&lt;p&gt;当应用程序（用户线程或进程）发送监听端口的系统调用后，内核会在自身不断的轮询给端口看看是否有网络数据到了（socket 套接字）如果有数据到了，就会通知刚才的用户线程，在这个阶段 I/O 还是阻塞的，但是不同点就是它在发送监听系统调用的时候是可以监听多个套接字的，这也就是在同一个路径是复用了 I/O，剩下其他的部分和 I/O 阻塞都是差不多的，别小看这点改进，在多路复用情况下，select 系统调用在 64bit 系统上可以 切换处理 2048 个 socket，这样的优势并不是对于单个连接能处理得更快，而是在于能处理更多的连接。&lt;/p&gt;

&lt;p&gt;那么我们常用的 libevent 框架，它的底层就是使用了这个这些 I/O 多路复用的系统调用，包括 select epoll poll 和 kqueue，（这里说一下 libevent 是跨平台的 事件驱动系统函数库，主要作用就是抹平了，不同操作系统对于 I/O 多路复用系统调用函数的不同 API，（就是统一 API) )&lt;/p&gt;

&lt;p&gt;epoll 和 kqueue 是这个模型上的 select 和 poll 的升级版，目前最流行使用（包括 nginx nodejs) 主要区别就是 select 在它监听的所有 socket(fd) 有任意一个有数据返回了，内核就会将所有的 fd 都复制会用户线程中，然后再由 select 去遍历看看到底是哪一个 fd 有数据了，最后再处理请求。而 epoll 是使用了一个 callback 的机制，在它监听的每一个 fd 上都有一个 callback，当内核收到数据后，就会使用有数据的 fd 的 callback 通知 epoll，具体是哪一个 fd 有了数据，然后在处理特定的请求，这样的好处就是可以有效减少 fd 集合在内核和用户空间之间的复制操作。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;reactor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;是一种服务器端的事件驱动模式，服务处理程序使用解多路分配策略，底层是一种 I/O 的多路复用，I/O 同步非阻塞它的是有以下几个部分组成：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;事件源 input 输入具体可以是 Linux 上的 fd(file description)  windows 上的 socket。&lt;/li&gt;
&lt;li&gt;event demultiplexer 事件多路分发机制。它是使用操作系统提供的 I/O 多路复用机制实现的，select epoll 这都是操作系统上的 I/O 多路复用 系统调用函数。它的处理过程是需要，先在分发器上注册具体的事件源，然后等待 reactor 通知触发特定的事件源对用的事件处理器。&lt;/li&gt;
&lt;li&gt;reactor 反应器是 事件管理器，&lt;/li&gt;
&lt;li&gt;事件处理器&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;EventMachine 实际上在事件驱动模型上就是根据 reactor 模式实现的，上面介绍了它的工作原理，我们再看看它的优点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;响应快，不必为单个同步时间所阻塞，虽然 Reactor 本身依然是同步的&lt;/li&gt;
&lt;li&gt;编程相对简单，可以最大程序避免复杂的多线程及同步问题，并且避免了多线程/进程的切换开销&lt;/li&gt;
&lt;li&gt;可扩展性，可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源&lt;/li&gt;
&lt;li&gt;可复用性，Reactor 框架本身与具体事件处理逻辑无关，具有很高的复用性&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="四 - 使用C提高Web实时性能 - Iodine"&gt;四 - 使用 C 提高 Web 实时性能 - Iodine&lt;/h3&gt;
&lt;p&gt;iodine 是支持 rack 的，事件驱动并发框架，作为 plezi 的底层库，基本上 I/O，多线程，pub/sub 都是使用 iodine 来实现的&lt;/p&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/1767848-cc0b7b15a79e3fd3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt="plezi"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Websockets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;iodine 处理 websocket 的方式与其他 Rack based 的 server 有所不同，它没有使用 rack.env 的 hijack API 来截获 websocket upgrade 请求，而是使用 C 扩展在 Ruby 处理请求之前，就在 socket 上判断是否为 websocket upgrade 请求然后，再执行 websocket parser（在 Ruby 之前运行所以不受 GIL 的影响，parser 是 CPU 密集操作如果有 GIL 的话多个 I/O 请求需要排队执行 parser）最后设置 rack.env[websocket.upgrade?] 变量后面的 rack app 自行判断如何处理 websocket upgrade 请求。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PubSub&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;iodine 底层使用了 facil.io 库提供原生的 pub/sub 支持，同时也可以通过 Redis 进行扩展，iodine 的 RedisEngine 支持 redis 单机和集群两种模式。并且&lt;code&gt;Iodine::PubSub::RedisEngine&lt;/code&gt;的 Redis 客户端是异步非阻塞的客户端。&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;'uri'&lt;/span&gt;
&lt;span class="c1"&gt;# 使用Redis 作为pub/sub queue&lt;/span&gt;
&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"REDIS_URL"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="no"&gt;Iodine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_pubsub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Iodine&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RedisEngine&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;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&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="nf"&gt;port&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;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 非阻塞Redis Client 发送命令使用block 回调&lt;/span&gt;
&lt;span class="no"&gt;Iodine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"CLIENT LIST"&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;reply&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="GIL  &amp;amp; Lock"&gt;GIL  &amp;amp; Lock&lt;/h5&gt;
&lt;p&gt;Iodine 使用里多线程（pthread) 处理一些可以并行执行的任务（例如协议解析）因有共享资源处理，所以 iodine 使用了 spin-locks，在处理锁的问题上，得益于应用使用同一个 reactor, 服务器可以检查资源锁的状态，然后重新调整安排其他任务的执行，最大化利用资源。多线程并行执行因为在 C 层面使用 pthread 处理，所以在事件转移到上层的应用程序之前，内部的处理都是不受 GIL 的影响的。&lt;/p&gt;

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

&lt;p&gt;&lt;a href="http://www.jianshu.com/p/b462c222862d" rel="nofollow" target="_blank" title=""&gt;支撑 ActionCable 的底层库&lt;/a&gt;&lt;br&gt;
 &lt;a href="https://falm.github.io/presentations/ppt/io_model.html" rel="nofollow" target="_blank" title=""&gt;I/O 模型&lt;/a&gt;&lt;br&gt;
 &lt;a href="https://item.jd.com/11728741.html" rel="nofollow" target="_blank" title=""&gt;Unix 网络编程&lt;/a&gt;&lt;br&gt;
 &lt;a href="https://github.com/rack/rack/issues/1093" rel="nofollow" target="_blank" title=""&gt; Websocket / Upgrade support proposal&lt;/a&gt;&lt;/p&gt;</description>
      <author>falm</author>
      <pubDate>Tue, 19 Sep 2017 20:59:20 +0800</pubDate>
      <link>https://ruby-china.org/topics/34188</link>
      <guid>https://ruby-china.org/topics/34188</guid>
    </item>
    <item>
      <title>Ruby 3 Guilds 并发模型</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;文本为翻译 Olivier Lacan 在其博客上发布的关于 Ruby 3 Guilds 介绍的文章。我对内容上做了适当的调整，这里给大家分享一下，翻译水平有限，见谅了。&lt;/p&gt;

&lt;p&gt;英文原文地址：&lt;a href="http://olivierlacan.com/posts/concurrency-in-ruby-3-with-guilds/" rel="nofollow" target="_blank"&gt;http://olivierlacan.com/posts/concurrency-in-ruby-3-with-guilds/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在 Ruby Kaigi 2016 大会上，Ruby VM 和 GC 的作者 &lt;a href="http://www.atdot.net/~ko1/" rel="nofollow" target="_blank" title=""&gt;Koichi Sasada&lt;/a&gt;&amp;nbsp;为 Ruby 3 提出了一个新的并发模型。&lt;/p&gt;

&lt;p&gt;我们都知道 Ruby 提供了线程去实现并发，但是 MRI 并不能让 Ruby 的代码并行的运行。Koichi 致力于解决 Ruby 并行中，遇到的各种挑战，包括可变对象，竞态条件和线程间同步，最终他提出了一个新的并行并发机制，称为 Guilds。&lt;/p&gt;
&lt;h3 id="并发的目标"&gt;并发的目标&lt;/h3&gt;
&lt;p&gt;如果你对并发和并行不太了解，可以先阅读一下这篇文章：&lt;a href="http://bytearcher.com/articles/parallel-vs-concurrent/" rel="nofollow" target="_blank" title=""&gt;并发与并行的区别&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;为 Ruby3 提出 Guilds 的基本前提是，保证与 Ruby2 兼容的情况下实现并行，考虑到 GIL 限制了并行，所以要尝试通过使用快速对象共享和特殊结构对象，来实现共享可变对象。&lt;/p&gt;

&lt;p&gt;实现并发在目前的 Ruby 版本中是一件很困难的事情，这因为程序员需要手动的管理线程，确保不会出现竞态条件。通用的做法是在线程中引入『锁』，像 Ruby 就提供了线程互斥锁，但它或多或少违反了并行的初衷，『锁』容易使程序变慢，而且不当使用的话，很可能使得并发程序比相同的同步程序还要慢。&lt;/p&gt;
&lt;h3 id="Guilds工作原理"&gt;Guilds 工作原理&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;在&lt;em&gt;Koichi&lt;/em&gt;的允许下，我为了让它更好理解，就对他的提案做了适当的缩减。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Guild 是基于现有的 线程 (thread) 和纤程 (fiber) 实现的。它至少由一个线程组成，并且该线程至少由一个纤程组成。不同 Guild 中的线程可以并行运行，而相同 Guild 中的线程不能。一个 Guild 不能对其他 Guild 中的对象进行读写。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://olivierlacan.com/assets/ruby_3_guilds_threads_and_fibers.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;同一个 Guild 中的不同线程不能并行运行，这是因为 Guild 存在一个叫 GGL(Giant Guild Lock) 的锁，它是用来确保线程是先后运行的，而不同 Gulid 的线程是可以并行运行的。&lt;/p&gt;

&lt;p&gt;你也可以认为 Ruby 2.x 的程序，就相等于是存在一个 Guild 的程序。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://olivierlacan.com/assets/ruby_3_guilds_concurrency.png" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="Guilds间通信"&gt;Guilds 间通信&lt;/h3&gt;
&lt;p&gt;不同 Guild 间不可以相互读写可变对象，这保证了 Guild 并发运行时，不会因为并发读写而引发问题。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://olivierlacan.com/assets/ruby_3_guilds_object_access_restrictions.png" title="" alt="不可相互读写"&gt;&lt;/p&gt;

&lt;p&gt;不过，Guild 可以通过&lt;code&gt;Guild::Channel&lt;/code&gt; 提供的接口，将对象复制或移动到其他的 Guild 中。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Guild::Channel&lt;/code&gt;-&lt;code&gt;transfer(object)&lt;/code&gt;可以将一个对象深度拷贝到目标 Guild 中。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://olivierlacan.com/assets/ruby_3_guilds_channels_object_copy.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;同样也可以使用，&lt;code&gt;Guild::Channel&lt;/code&gt;中的&lt;code&gt;transfer_membership(object)&lt;/code&gt;将一个对象完全移动到其他的 Guild 中。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://olivierlacan.com/assets/ruby_3_guilds_channels_object_move.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;一旦对象被移动到新的 Guild 后，如果原来持有该对象的 Guild 再对它进行访问，将会抛出异常。&lt;/p&gt;

&lt;p&gt;这里我们就知道了，Guild 是不能在没有复制或移动的情况下共享可变对象的，还有一点非常重要，那就是不可变对象（immutable objects）在『深度冻结』（意思是被该对象引用的对象也是冻结不可变的）的情况下是可以直接在多个 Guild 间共享的。&lt;/p&gt;

&lt;p&gt;下面这个例子，展示可变和不可变对象的区别：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 像整数，这样的数值类型，默认就是不可变的，而哈希不是。&lt;/span&gt;
&lt;span class="n"&gt;mutable&lt;/span&gt; &lt;span class="o"&gt;=&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="s2"&gt;"key"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"value"&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="nf"&gt;freeze&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 而如果 数组实例及其引用的，字符串和哈希的实例都进行冻结，&lt;/span&gt;
&lt;span class="c1"&gt;# 就会得到一个"深度冻结" 的不可变对象。&lt;/span&gt;
&lt;span class="n"&gt;immutable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"key"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="使用方法"&gt;使用方法&lt;/h3&gt;
&lt;p&gt;在 Koichi Sasada 的研究中，他给出了几个关于如何使用 Guilds 的例子，我在这里将其中最小的，关于使用 Guild 并行计算『斐波那契』的例子进行了简化进行展示。&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;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&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;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&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;2&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;guild_fibonacci&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Guild&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="ss"&gt;script: &lt;/span&gt;&lt;span class="sx"&gt;%q{
  channel = Guild.default_channel

  while(n, return_channel = channel.receive)
    return_channel.transfer( fibonacci(n) )
  end
}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Guild&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; 
&lt;span class="n"&gt;guild_fibonacci&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&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="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Guild对比线程的优势"&gt;Guild 对比线程的优势&lt;/h3&gt;
&lt;p&gt;在线程中，我们很难判别出，哪些可变对象已经被共享了。而 Guilds 禁止可变对象的共享，转而提供一个简单的方式去共享不可变对象，而且 Koichi 计划，通过使用『特殊数据结构』来共享可变对象，『特殊数据结构』会自动隔离有风险的可变代码。&lt;/p&gt;

&lt;p&gt;当然，使用 Guilds 相较于线程来说，有些繁琐，这也是需要有取舍的。 &lt;/p&gt;
&lt;h3 id="性能"&gt;性能&lt;/h3&gt;
&lt;p&gt;我的理解是，目前 Guilds 的"C"实现仅有 400 行代码，虽然该实现现阶段还不能用，但 Koichi 展示了，运行多个 Guilds 相比于运行单个 Guilds 在斐波那契例子上的性能优势。&lt;/p&gt;

&lt;p&gt;在双核的 Linux 虚拟机上运行 Window 7，Koichi 观察到以下结果：&lt;/p&gt;

&lt;p&gt;&lt;img src="http://olivierlacan.com/assets/ruby_3_guilds_fibonacci_performance.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这也许不能代表，现实场景中大部分 Ruby 应用会得到性能上的提升，但我还是等不及想知道，Guilds 在&lt;a href="https://rubybench.org" rel="nofollow" target="_blank" title=""&gt;RubyBench&lt;/a&gt;上的测试结果。&lt;/p&gt;
&lt;h3 id="总结"&gt;总结&lt;/h3&gt;
&lt;p&gt;Guilds 是一种面向 Ruby 的，简单易用并且安全的并发方式，非常希望 Koichi Sasada 和 Ruby 核心团队能在之后，分享更多关于 Guilds 的信息。&lt;/p&gt;

&lt;p&gt;我对 Guilds 中移动和复制对象的方法的命名，有一些小看法，因为&lt;code&gt;Guild::Channel. transfer(object)&lt;/code&gt;看上去更像是交换的意思，而结果仅仅是对象的深度拷贝，我相信&lt;code&gt;transfer_copy(object)&lt;/code&gt;或是更简单的&lt;code&gt;copy(object)&lt;/code&gt; 更加适合。还有&lt;code&gt;transfer_membership(object)&lt;/code&gt; 移动对象的方法，可以简化命名为&lt;code&gt;channel.transfer(object)&lt;/code&gt; 。当然了这些方法的命名也不会是一成不变的。&lt;/p&gt;

&lt;p&gt;我非常期望，Ruby 核心团队能够将这个新功能，作为实验性质的可选功能发布出去，这样 Ruby 社区就能参与进去帮助改进和测试。还可以让我们在 2020 年的 Ruby3 版本之前，就使用上这个新特性。Guilds 将会对 Ruby 在并发友好性上，做出了积极的影响。&lt;/p&gt;</description>
      <author>falm</author>
      <pubDate>Mon, 17 Oct 2016 14:44:18 +0800</pubDate>
      <link>https://ruby-china.org/topics/31348</link>
      <guid>https://ruby-china.org/topics/31348</guid>
    </item>
    <item>
      <title>聊一聊 ActionCable 背后的技术</title>
      <description>&lt;p&gt;Rails5 中引入的可以实现实时通讯的新功能，&lt;strong&gt;ActionCable&lt;/strong&gt;，可以说是这个版本的 Rails 的重大特性之一，ActionCable 底层究竟是如何进行通讯，本文就来聊一聊这些相关的技术。&lt;/p&gt;
&lt;h2 id="WebSocket"&gt;WebSocket&lt;/h2&gt;
&lt;p&gt;简单的说，websocket 是一个基于 TCP 的应用层协议，使用 http 协议建立连接，并且能够通过一个已经建立的连接，进行双向的通讯，也就是不仅仅能够从客户端发送信息到服务器端，服务端还可以推送信息到客户端，而且这一切的是建立在一个连接中进行的，有了它我们就不需要再使用，polling 或 long polling 做轮询信息了。&lt;/p&gt;

&lt;p&gt;它的通讯过程是，通过向 http 头添加特定信息，然后发送到服务器，如果服务器能够支持 websocket 的话，就会识别出 http 头中关于 websocket 的信息，并且升级 http 连接为 websocket 连接并返回，一个同样包含 websocket 头信息的 http response，这样下来，客户端和服务器的连接就已经建立了，直到一方关闭连接。&lt;/p&gt;

&lt;p&gt;请求：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
GET /cable HTTP/1.1
Host: example.com:3000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应答：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的请求和应答头中 &lt;strong&gt;upgrade: websocket&lt;/strong&gt;代表着这是一个 websocket 的连接请求，Sec-WebSocket-Key 和 Sec-WebSocket-Accept，是用于确认服务器是否真的，能够出来 websocket 请求的验证方式，过程是，服务器端介绍到 websocket-key 后，通过组合&lt;strong&gt;magic string&lt;/strong&gt;，之后再进行 SHA-1 和 base64 编码，然后以 Sec-WebSocket-Accept 返回给客户端，客户端接受到后验证正确，这就表明服务器真正的可以处理 websocket 请求了。&lt;/p&gt;
&lt;h2 id="如何使用Websocket"&gt;如何使用 Websocket&lt;/h2&gt;
&lt;p&gt;ActionCable 本身可以作为一个单独的服务，绑定到单独的端口上运行，也可以与 rails 一起在同一个端口上一起启动运行，这是因为它实际上是一个处理 websocket 的 rack app。&lt;/p&gt;

&lt;p&gt;单独启动的 ActionCable Server，先加载 Rails 环境，然后再运行的 rack app。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# cable/config.ru
require ::File.expand_path('../../config/environment', __FILE__)
Rails.application.eager_load!

run ActionCable.server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;与 Rails 一同启动的时候，是通过挂载 ActionCable.server 到指定的 PATH 上。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# actioncable/lib/action_cable/engine.rb&lt;/span&gt;
    &lt;span class="n"&gt;initializer&lt;/span&gt; &lt;span class="s2"&gt;"action_cable.routes"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;after_initialize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;
        &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_cable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
          &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;ActionCable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_cable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;internal: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;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;不管是，单独端口启动的 ActionCable，或是与 Rails 一同启动的。它们实际都是用 rack up 作为统一入口的。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# actioncable/lib/action_cable/connection/base.rb&lt;/span&gt;
&lt;span class="c1"&gt;# Called by Rack to setup the server.&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;setup_heartbeat_timer&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;call 方法中第一是，每个三秒发送一个心跳包到客户端，已确定连接是否还可用，第二行就是初始化 action-cable 自己的连接类，执行响应。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# actioncable/lib/action_cable/connection/base.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt; &lt;span class="c1"&gt;#:nodoc:&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="n"&gt;started_request_message&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;possible?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;allow_request_origin?&lt;/span&gt;
    &lt;span class="n"&gt;respond_to_successful_request&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;respond_to_invalid_request&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;首先输出日志表示请求已经接受，然后判断请求是否为 websocket，并且判断 HTTP_ORIGIN 是否允许。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# actioncable/lib/action_cable/connection/base.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;respond_to_successful_request&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="n"&gt;successful_request_message&lt;/span&gt; &lt;span class="c1"&gt;# 输出日志：成功升级连接为 Websocket&lt;/span&gt;
  &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rack_response&lt;/span&gt; &lt;span class="c1"&gt;# 调用 websocket对象，返回websocket响应。&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;走到这一步，服务器端的连接已经确认是可以继续 websocket 通讯了，但是与客户端的握手还没有完成，还需要发送一个，验证服务器端接受并有能力处理 websocket 的信息给客户端。&lt;/p&gt;

&lt;p&gt;可以从下面看出来，action-cable 使用了&lt;strong&gt;Websocket-driver&lt;/strong&gt; 这个 Gem 来完成 websocket 的通讯工作。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# actioncable/lib/action_cable/connection/web_socket.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'websocket/driver'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActionCable&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Connection&lt;/span&gt;
    &lt;span class="c1"&gt;# Wrap the real socket to minimize the externally-presented API&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_socket_class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;protocols: &lt;/span&gt;&lt;span class="no"&gt;ActionCable&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;INTERNAL&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:protocols&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="vi"&gt;@websocket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;websocket?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;client_socket_class&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;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&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;def&lt;/span&gt; &lt;span class="nf"&gt;possible?&lt;/span&gt;
        &lt;span class="n"&gt;websocket&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;alive?&lt;/span&gt;
        &lt;span class="n"&gt;websocket&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alive?&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;transmit&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="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transmit&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;
        &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;protocol&lt;/span&gt;
        &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;protocol&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;rack_response&lt;/span&gt;
        &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rack_response&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="kp"&gt;protected&lt;/span&gt;
        &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:websocket&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="c1"&gt;# actioncable/lib/action_cable/connection/client_socket.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActionCable&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Connection&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientSocket&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="err"&gt;············&lt;/span&gt;
        &lt;span class="vi"&gt;@driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rack&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="ss"&gt;protocols: &lt;/span&gt;&lt;span class="n"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@driver.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:open&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;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="vi"&gt;@driver.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:message&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;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;receive_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="vi"&gt;@driver.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:close&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;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;begin_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="vi"&gt;@driver.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;emit_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="err"&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么接下来就不得不讲一讲，websocket-driver 这个 Gem 了。&lt;/p&gt;
&lt;h3 id="websocket-driver"&gt;websocket-driver&lt;/h3&gt;
&lt;p&gt;简单的说，websocket-driver 是一个利用，EventMachine，来读写 socket-io 对象的驱动器。使用了它的 API 就可以很轻松的实现 websocket-server。&lt;/p&gt;

&lt;p&gt;websocket-driver 提供了一个基于 rack 的接口，action-cable 就是使用这个接口进行通讯的。下面的代码通过一个 socket 代理对象，初始化一个 driver，然后再向客户端发送一段信息。这里最重要的地方就是，被传入的 socket 代理对象，它必须能够响应三个方法，&lt;strong&gt;env&lt;/strong&gt;，&lt;strong&gt;url&lt;/strong&gt;，和&lt;strong&gt;write&lt;/strong&gt;，分别是告诉 driver 向哪个 URL，如何发送信息。这里的 &lt;strong&gt;env&lt;/strong&gt; 返回的就是 rack app 的 env 变量，webscoket-driver 实际上就是利用 Rack hijacking API 来实现获取 socket-io 对象进行读写的，所以 action-cable 基于 websocket-driver 就代表着像 webrick 这类不支持 full hijacking API 的应用程序服务器，也不能支持 action-cable。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt; &lt;span class="s2"&gt;"I'm websocket server"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 rack hijacking API&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;'websocket/driver'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'eventmachine'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WS&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:url&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;

    &lt;span class="n"&gt;secure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&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;env&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ssl?&lt;/span&gt;
    &lt;span class="n"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secure&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'wss:'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'ws:'&lt;/span&gt;
    &lt;span class="vi"&gt;@url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'//'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_HOST'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="vi"&gt;@driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rack&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="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'rack.hijack'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
    &lt;span class="vi"&gt;@io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'rack.hijack_io'&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;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Reader&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;conn&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@driver&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="vi"&gt;@driver.start&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;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@io.write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&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;module&lt;/span&gt; &lt;span class="nn"&gt;Reader&lt;/span&gt;
    &lt;span class="nb"&gt;attr_writer&lt;/span&gt; &lt;span class="ss"&gt;:driver&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;receive_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@driver.parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&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;h2 id="Rack hijacking API"&gt;Rack hijacking API&lt;/h2&gt;
&lt;p&gt;Rack hijacking API 是在 Rack 1.5 中引入的，hijack 即为劫持，听起来好想是挺危险的样子，不过其实只是名字而已，它实际上是通过支持 Rack 的应用程序服务器上，拦截下 client 端的请求和 server 的相应的
Rack 的 hijacking API 提供了两种模式：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;全部劫持 (full hijacking)，在这个模式下应用对 socket 传输的数据有绝对的控制权，也就是应用程序服务器，将对 socket 的控制权移交给你应用程序本身去处理，通过这个特性我们就可以实现任意应用层协议的传输，比如 websocket 协议。当然这些也是有一个大前提的，就是如果你的应用程序服务器 (puma) 前端有反向代理 (nginx) 或者 负载均衡器的话，应用程序只能通过 socket 传输它们两个支持的协议。&lt;/li&gt;
&lt;li&gt;部分劫持 (partial hijacking)，而这个模式下应用程序可以在应用程序服务器设置完成 header 后，进行控制。
hijacking API 可以通过 Rack 的&lt;strong&gt;env&lt;/strong&gt;变量访问，要想知道应用程序服务器是否支持 hajacking API，要通过 &lt;strong&gt;env['hijacking?']&lt;/strong&gt;来判断，它会返回一个布尔值。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;接下来我们就通过 full hijacking 来写一个能够同时处理 HTTP 请求和 websocket 请求的 rack app
下面的代码通过&lt;strong&gt;env['rack.hijack'].call&lt;/strong&gt;来运行 full hijack，再使用&lt;strong&gt;env['rack.hijack_io']&lt;/strong&gt;返回 socket 对象，然后我们就可以通过这个 socket 对象进行通信了。&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'rack.hijack'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
  &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'rack.hijack_io'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'HTTP/1.1 101 Switching Protocols'&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Upgrade: websocket'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Connection: Upgrade'&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="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;  &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="mi"&gt;10&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="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Line &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&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="k"&gt;ensure&lt;/span&gt;
    &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;def&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'200'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'text/html'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Normal HTTP'&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;lambda&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;env&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'/cable'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_UPGRADE'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'websocket'&lt;/span&gt;
    &lt;span class="n"&gt;websocket&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="n"&gt;env&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;run&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的 Rack app 使用了两个方法分别处理，HTTP 和 websocket 请求，其中 webscoket 处理绑定在 '/cable'这个 URI 上。websocket 方法接受一个 rack env 变量，进行 websocket 处理，首先是运行 full hijack，然后再通过 socket 对象返回 websocket response header，这样就与客户端建立了 websocket 连接，并在连接上每个 1 秒发送一个消息，共 10 个。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;这个例子中的 websocket 方法是不严谨的仅作为参考。首先是判断请求是否是 websocket 请求，再一个是 reponse header 中缺少对 websocket-key 进行加密，然后返回的 WebSocket-Accept。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;使用 Puma 运行&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;puma app.ru
Puma starting &lt;span class="k"&gt;in &lt;/span&gt;single mode...
&lt;span class="k"&gt;*&lt;/span&gt; Version 3.4.0 &lt;span class="o"&gt;(&lt;/span&gt;ruby 2.3.0-p0&lt;span class="o"&gt;)&lt;/span&gt;, codename: Owl Bowl Brawl
&lt;span class="k"&gt;*&lt;/span&gt; Min threads: 0, max threads: 16
&lt;span class="k"&gt;*&lt;/span&gt; Environment: development
&lt;span class="k"&gt;*&lt;/span&gt; Listening on tcp://0.0.0.0:9292
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先发送一个常规的 HTTP 请求 &lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Cache-Control: no-cache"&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:9292"&lt;/span&gt;
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 11

Normal HTTP%
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来在发送一个 websocket 请求：&lt;/p&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/1767848-4b170059007f9fec.gif?imageMogr2/auto-orient/strip" title="" alt="websocket-request.gif"&gt;
通过 socket 对象连续发送 10 行数据后，关闭连接。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;ActionCable 最底层是一个利用 rack hijacking 和 EventMachine 的 Rack App，通过不断地使用 websocket-driver 和自身的抽象封装，在上层提供了一个非常好用的方法，与 rails server 也可以无缝的结合在一起。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.jianshu.com/p/f08393da80b5" rel="nofollow" target="_blank" title=""&gt;原文地址&lt;/a&gt;&lt;/p&gt;</description>
      <author>falm</author>
      <pubDate>Mon, 11 Jul 2016 10:49:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/30494</link>
      <guid>https://ruby-china.org/topics/30494</guid>
    </item>
  </channel>
</rss>
