<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>nine</title>
    <link>https://ruby-china.org/nine</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>使用 Windows 10 + WSL 进行无缝 Ruby 开发</title>
      <description>&lt;p&gt;最近使用 Windows 10 比较多，越来越觉得 Mac 的 GUI 是上个世纪的产物了，所以一直在琢磨要不要在 Win 上搞 Ruby。刚巧，本月微软公布了 3 个讨好 Linux/Unix 开发者的产品/功能。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Windows Subsystem for Linux 2 ( WSL2 ) ，预期年底发布。&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/microsoft/Terminal" rel="nofollow" target="_blank" title=""&gt;Windows Terminal&lt;/a&gt;  ，预期年底发布，现在可以自行编译（功能没有视频上的牛 X）。&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/docs/remote/remote-overview" rel="nofollow" target="_blank" title=""&gt;VSCode Remote&lt;/a&gt; ，VS 的插件，现在可以安装。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其中 VSCode Remote 是一组插件，由 3 个插件组成&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remote SSH&lt;/li&gt;
&lt;li&gt;Remote Containers&lt;/li&gt;
&lt;li&gt;Remote WSL&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;具体介绍可以看 &lt;a href="https://www.oschina.net/news/106412/vscode-remote-development" rel="nofollow" target="_blank" title=""&gt;VS Code Remote 发布！开启远程开发新时代&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;于是尝试了一把 Win 下开发。之前也 try 了一下虚拟机方案，感觉不是我想要的，而且挂载系统有 bug。看到新闻后，想起来之前同事在 2017 年就使用 WSL 了，而且在我们公司的实际项目中使用了大半年，并没有什么不妥。&lt;/p&gt;
&lt;h3 id="WSL"&gt;WSL&lt;/h3&gt;
&lt;p&gt;在 Windows 上运行的 Linux，和虚拟机不同，WSL 和宿主 Windows 是几乎无缝的。&lt;/p&gt;

&lt;p&gt;有很多发行版可以用，我选择的是 Ubuntu，WSL 2 还早，目前可安装的 WSL 1 完全可以胜任（如何安装？在 Microsoft Store 里点击安装，网上有图文详细教程，可自行 Google）&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2019/a11096f3-29a6-47f8-8d8a-3d2c2717d5db.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="Windows Terminal"&gt;Windows Terminal&lt;/h3&gt;
&lt;p&gt;编译过程比较麻烦，我没有编译成功，用网友编译好的版本，提示证书问题，装不上。后来按 &lt;a href="https://www.bilibili.com/video/av51726432" rel="nofollow" target="_blank" title=""&gt;第 45 期 Windows Terminal 微软新版终端工具安装教程&lt;/a&gt; 的步骤，把证书搞定，安装成功了。不过实际体验和 Hyper、ConEmu 没有什么质的差别。&lt;/p&gt;

&lt;p&gt;附 2 个网友编译版下载地址：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cloud.natfrp.org/s/AaxfrxK96JebHsj" rel="nofollow" target="_blank"&gt;https://cloud.natfrp.org/s/AaxfrxK96JebHsj&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="http://cdn.lr3800.52qdw.cn/Terminal.7z" rel="nofollow" target="_blank"&gt;http://cdn.lr3800.52qdw.cn/Terminal.7z&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="VSCode Remote"&gt;VSCode Remote&lt;/h3&gt;
&lt;p&gt;VSCode 的 Remote 方案也尝试了，实际结果是没有什么卵用。&lt;/p&gt;
&lt;h3 id="实际需要做的是"&gt;实际需要做的是&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;安装 WSL&lt;/li&gt;
&lt;li&gt;使用任意终端（包括 cmd、powershell 都可以），进入工作目录，输入 wsl + 回车，即可进入 wsl 的 shell
实际的效果如下
&lt;img src="https://l.ruby-china.com/photo/2019/c36ac3f3-bf7e-4831-ba5c-54961ae711f9.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;也就是说使用任意 Windows 的编辑器/IDE 对 win 上的目录进行编码，实际代码运行在 WSL 内。rails s 后，打开 Windows 的浏览器，输入&lt;a href="http://localhost:3000" rel="nofollow" target="_blank"&gt;http://localhost:3000&lt;/a&gt; 即可访问 rails 项目。完全无缝。&lt;/p&gt;

&lt;p&gt;其中 windows 的文件系统按盘符，自动挂载在 WSL 的“/mnt/盘符”目录下，图中的 d:\work\demo3 挂载在 /mnt/d/work/demo3 目录下。
所以也可以输入 wsl 后 cd 到 /mnt/d/work/demo3 运行 rails s&lt;/p&gt;

&lt;p&gt;在 WSL 下可以使用 windows 命令，输入 code-insiders . 之后会自动使用 VSCode 预览版打开 wsl 的目录，并且安装 VSCode 的 Remote WSL 插件
&lt;img src="https://l.ruby-china.com/photo/2019/ceb9f886-be74-48cf-8f2d-f337e33164d9.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;此时新打开的 VSCode 左下角会多出一个 WSL 的图标
&lt;img src="https://l.ruby-china.com/photo/2019/ead319f8-5110-4ded-988e-a8ca32e2b3a3.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;点击 WSL 图标会出现菜单
&lt;img src="https://l.ruby-china.com/photo/2019/92d46b7f-4714-4220-a5fc-e81a7be2b1a8.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这个模式用于编辑 WSL 系统内文件比较方便。但是对于 Rails 开发，WSL 模式是没有什么用的，直接在 mnt 目录下编辑代码就行了。毕竟我对 WSL 的文件系统不是很放心，不想把代码放在上面。另外 WSL 的文件系统默认在 C 盘的某个目录下，还没有研究如何更改位置。&lt;/p&gt;
&lt;h3 id="最后，说下数据库。"&gt;最后，说下数据库。&lt;/h3&gt;
&lt;p&gt;我这边数据库是在 win 下安装的，理由也是对 WSL 不太放心，所以此时 Rails 的 database.yml 文件需要配置上 host 和 port。不配 host 和 port 的话会默认连接 WSL 里的 unix 数据库套接字。&lt;/p&gt;

&lt;p&gt;也就是说&lt;strong&gt;这套方案只有程序运行环境在 WSL 内，而程序代码、编辑器、数据库，和所有开发配套装备全部都是 windows 上的。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="坑"&gt;坑&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Win10 和 WSL 是两个系统使用的是不同的公钥，这点需要注意一下。&lt;/li&gt;
&lt;li&gt;WSL 系统长时间不使用会挂起，第一次做 apt-get update 的时候切到别的界面了，回来发现不动了，然后按回车之后就开始动了，不知道是挂起了还是 terminal 的 bug。&lt;/li&gt;
&lt;li&gt;直觉上速度不是那么快，不知道和上一条是否有关。&lt;/li&gt;
&lt;li&gt;node 的 hmr 有时不太灵光，目前还不能确定是不是 WSL 的锅。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;不过&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;台式机 CPU 总比笔记本快。&lt;/li&gt;
&lt;li&gt;程序运行效率不是第一位，最重要的还是 GUI 对 CPU 的利用率和 GUI 软件操作的便捷性。&lt;/li&gt;
&lt;li&gt;比起虚拟机 WSL 可以使用全部的 CPU 内核和内存。可以按 htop 看下效果（具体性能待测试）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其他：想到再补充。&lt;/p&gt;
&lt;h3 id="适用人群"&gt;适用人群&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;对 Windows 有需求的，比如经常装机刷机，经常使用带 VBS 的 Excel，经常测试各种 CS ERP，经常使用 IE 专属系统的，经常使用 Win 专有软件的，等等……&lt;/li&gt;
&lt;li&gt;对 Mac 特性需求不强烈的，或对移动办公需求不强烈的。&lt;/li&gt;
&lt;li&gt;对 Linux/Mac 操作系统不满的。&lt;/li&gt;
&lt;li&gt;资金紧张的新手，或对 Linux/Mac 系统不熟悉的新手。&lt;/li&gt;
&lt;li&gt;资金紧张的团队，或需要大规模招募新手的团队。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="顺便说下VSCode Remote SSH的用法。"&gt;顺便说下 VSCode Remote SSH 的用法。&lt;/h3&gt;
&lt;p&gt;安装插件后，按 Ctrl + Shift + P 调出菜单选择 Connect to Host
&lt;img src="https://l.ruby-china.com/photo/2019/fee8e422-05b0-4699-acb0-d4ed6fd71943.png!large" title="" alt=""&gt;
选择 Configure SSH Hosts 编辑远程服务器&lt;/p&gt;

&lt;p&gt;编辑后点击左侧电脑图标，可以看到常用的 host，点击后可以连接到服务器&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2019/e331b4f9-11f3-477e-8529-c228b0b028d3.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;之后会打开一个新的 VSCode 窗口，左下角显示 SSH 连接标志。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2019/564cb937-bd71-48b2-8e4a-3edb5b44e725.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;之后可以编辑服务器上的文件
&lt;img src="https://l.ruby-china.com/photo/2019/ae959c68-872f-47f9-b12a-e0c8f243e38f.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;RemoteSSH 默认使用公钥连接，所以连接服务器之前需要安装 OpenSSH，生成公钥，然后在服务器上配置好公钥，不然就会一直连接不上。&lt;/p&gt;
&lt;h3 id="Remote Containers"&gt;Remote Containers&lt;/h3&gt;
&lt;p&gt;连接容器的，容器没怎么玩过，未测试，理论上应该和 Remote SSH 差不太多。&lt;/p&gt;
&lt;h3 id="于是"&gt;于是&lt;/h3&gt;
&lt;p&gt;SOHO 的 007 们可以在工作累了的时候无缝切换到游戏模式了。 (逃&lt;/p&gt;
&lt;h3 id="补充，dd测试磁盘写入IO基本测试"&gt;补充，dd 测试磁盘写入 IO 基本测试&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mnt/e/test &lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fdatasync &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1024 &lt;span class="c"&gt;#1M写入性能&lt;/span&gt;
&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mnt/e/test &lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fdatasync &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4k &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;262144   &lt;span class="c"&gt;#4K写入性能&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mac 没有 fdatasync 参数，默认使用 osync，输出结果为 bytes/s&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;硬件环境&lt;/th&gt;
&lt;th&gt;dd 测试路径&lt;/th&gt;
&lt;th&gt;配置&lt;/th&gt;
&lt;th&gt;系统&lt;/th&gt;
&lt;th&gt;1M 写&lt;/th&gt;
&lt;th&gt;4K 写&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;台式机+SSD 系统盘&lt;/td&gt;
&lt;td&gt;/tmp/test&lt;/td&gt;
&lt;td&gt;7700K + 32G 内存 + 960EVO 1TB&lt;/td&gt;
&lt;td&gt;WSL&lt;/td&gt;
&lt;td&gt;870 MB/s&lt;/td&gt;
&lt;td&gt;409 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;台式机+SSD 非系统盘&lt;/td&gt;
&lt;td&gt;/mnt/d/work/test&lt;/td&gt;
&lt;td&gt;7700K + 32G 内存 + 960EVO 1TB&lt;/td&gt;
&lt;td&gt;WSL&lt;/td&gt;
&lt;td&gt;963 MB/s&lt;/td&gt;
&lt;td&gt;470 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;台式 + 机械硬盘&lt;/td&gt;
&lt;td&gt;/mnt/e/test&lt;/td&gt;
&lt;td&gt;7700K + 32G 内存 + 西数 3T 蓝盘&lt;/td&gt;
&lt;td&gt;WSL&lt;/td&gt;
&lt;td&gt;132 MB/s&lt;/td&gt;
&lt;td&gt;127 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Surface Pro4&lt;/td&gt;
&lt;td&gt;/tmp/test&lt;/td&gt;
&lt;td&gt;I7 + 8G 内存 + 256G 硬盘&lt;/td&gt;
&lt;td&gt;WSL&lt;/td&gt;
&lt;td&gt;177 MB/s&lt;/td&gt;
&lt;td&gt;105 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Macbook Pro 15 2016&lt;/td&gt;
&lt;td&gt;/tmp/test&lt;/td&gt;
&lt;td&gt;I7 + 16G + 512SSD&lt;/td&gt;
&lt;td&gt;macOS 10.14.4&lt;/td&gt;
&lt;td&gt;≈340MB/s&lt;/td&gt;
&lt;td&gt;≈570MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;国内某云（非阿里云）高性能磁盘&lt;/td&gt;
&lt;td&gt;/tmp/test&lt;/td&gt;
&lt;td&gt;2 核 4G 高性能磁盘&lt;/td&gt;
&lt;td&gt;CentOS 7.4&lt;/td&gt;
&lt;td&gt;266MB/s&lt;/td&gt;
&lt;td&gt;234MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;国内某云（非阿里云）企业磁盘&lt;/td&gt;
&lt;td&gt;/tmp/test&lt;/td&gt;
&lt;td&gt;4 核 8G 企业级磁盘&lt;/td&gt;
&lt;td&gt;CentOS 7.4&lt;/td&gt;
&lt;td&gt;137MB/s&lt;/td&gt;
&lt;td&gt;127MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google 云 + SSD 永久性磁盘&lt;/td&gt;
&lt;td&gt;/tmp/test&lt;/td&gt;
&lt;td&gt;4 核 15G SSD 永久性磁盘&lt;/td&gt;
&lt;td&gt;CentOS 7.4&lt;/td&gt;
&lt;td&gt;213MB/s&lt;/td&gt;
&lt;td&gt;199MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google 云 + 标准磁盘&lt;/td&gt;
&lt;td&gt;/tmp/test&lt;/td&gt;
&lt;td&gt;4 核 15G 标准磁盘&lt;/td&gt;
&lt;td&gt;CentOS 7.4&lt;/td&gt;
&lt;td&gt;116MB/s&lt;/td&gt;
&lt;td&gt;113MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;结论：&lt;/p&gt;

&lt;p&gt;Mac 由于 dd 参数不同，结果仅供参考，不过 Mac 磁盘似乎是针对小文件优化过的，4K 写速度要快于 1M 写速度。
960EVO 仅仅 4k 写速度似乎略小于 Macbook Pro 512SSD（缺少 fdatasync 参数）。
所以担心磁盘 IO 的基本可以放心了，即使是普通的机械蓝盘，也不会差太多。&lt;/p&gt;

&lt;p&gt;速度瓶颈，主要应该在 CPU 上，对比之前将 i7 的 MacBook Air 更新为 Macbook Pro，程序运行实际感官提升超过不止 10 倍。
（完）&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Thu, 09 May 2019 22:46:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/38499</link>
      <guid>https://ruby-china.org/topics/38499</guid>
    </item>
    <item>
      <title>Passenger 能否开启 JIT？</title>
      <description>&lt;p&gt;目前没有找到正确的打开方式。&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Tue, 05 Feb 2019 18:31:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/38088</link>
      <guid>https://ruby-china.org/topics/38088</guid>
    </item>
    <item>
      <title>Ruby 的 Websocket Server 发送压缩后的 Binary Frame 格式的数据。</title>
      <description>&lt;p&gt;正常使用 websocket，通常发送的数据格式都是 json。
但是在做强实时应用时，如果每秒发几百 k 的数据的话，带宽还是比较捉急的，需要压缩一下。&lt;/p&gt;
&lt;h2 id="Node js"&gt;Node js&lt;/h2&gt;
&lt;p&gt;用 nodejs 做 server 的话比较简单。
nodejs 可直接推送 gzip 后的数据。
引入一个叫 pako 的处理 gzip 的库 &lt;a href="https://github.com/nodeca/pako" rel="nofollow" target="_blank"&gt;https://github.com/nodeca/pako&lt;/a&gt;   pako.deflate&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faye-websocket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;http&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;pako&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pako&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upgrade&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isWebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aaa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aaa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bbb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bbb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pako&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deflate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;ws&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="nx"&gt;output&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;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// ws = null;&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;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;前端直接解压数据后即可使用 pako.inflate&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ws://localhost:8080/websocket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onopen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="nx"&gt;ws&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pako&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inflate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&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;我们看到的效果是这样的
&lt;img src="https://l.ruby-china.com/photo/2018/b991ef0a-0dab-489f-a61c-6304671e1c0a.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;看起来是 text 格式的。&lt;/p&gt;

&lt;p&gt;ruby gzip 压缩只需要&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;'active_support/gzip'&lt;/span&gt;

&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'balabala'&lt;/span&gt;
&lt;span class="n"&gt;gzip_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Gzip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compress&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是在 Ruby 的 websocket server 中，压缩后的数据，无法直接 send，因为 Ruby 的 websocket 发送的 text 数据格式只能是 utf8。&lt;/p&gt;
&lt;h2 id="websocket-eventmachine-server"&gt;websocket-eventmachine-server&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/imanel/websocket-eventmachine-server" rel="nofollow" target="_blank" title=""&gt;websocket-eventmachine-server&lt;/a&gt; 中提供了二进制模式，只要指定数据类型是 binary 即可。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt; &lt;span class="n"&gt;gzip_string&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:binary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'json'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'active_support/gzip'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'websocket-eventmachine-server'&lt;/span&gt;



&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;aaa: &lt;/span&gt;&lt;span class="s1"&gt;'bbb'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ccc: &lt;/span&gt;&lt;span class="s1"&gt;'ddd'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gzip_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Gzip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compress&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="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;WebSocket&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;Server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:host&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:port&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8080&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;ws&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

    &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onopen&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Client connected"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onmessage&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;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt; &lt;span class="n"&gt;gzip_string&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:binary&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onclose&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Client disconnected"&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;这时前端看到的效果有点酷了
&lt;img src="https://l.ruby-china.com/photo/2018/1a2a006b-0eda-4be1-bb28-7c81a04756ea.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;事实上这也是目前主流的 websocket 的做法。自带加密效果。&lt;/p&gt;

&lt;p&gt;（如果想达到更好的加密，需要把客户端 send 的 message 也 gzip 一下。）&lt;/p&gt;

&lt;p&gt;前端 js 接收数据的方式要改变一下了，用 FileReader 读取二进制流。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ws://localhost:8080/websocket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onopen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="nx"&gt;ws&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readAsBinaryString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pako&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inflate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;h2 id="faye-websocket-ruby"&gt;faye-websocket-ruby&lt;/h2&gt;
&lt;p&gt;使用 websocket-eventmachine-server 需要自己管理进程，比较烦，用&lt;a href="https://github.com/faye/faye-websocket-ruby" rel="nofollow" target="_blank" title=""&gt;faye&lt;/a&gt;的话就可以随 passenger 启动。&lt;/p&gt;

&lt;p&gt;faye 也是支持 binary frame 的，但要求我们手工转成二进制 array
&lt;img src="https://l.ruby-china.com/photo/2018/5608e3de-6bd2-4447-abe7-14b8342c982d.png!large" title="" alt=""&gt;
转换的方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;zip_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Gzip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JSON&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;unpack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zip_string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'C*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;全部代码&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#config.ru&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'faye/websocket'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'active_support/gzip'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'json'&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;'PATH_INFO'&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;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Faye&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WebSocket&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;timer&lt;/span&gt; &lt;span class="o"&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;add_periodic_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;aaa: :bbb&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ccc: :ddd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;zip_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Gzip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JSON&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;unpack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zip_string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'C*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;ws&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="n"&gt;unpack&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;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:message&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;event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;ws&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;"You sent: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt; &lt;span class="ss"&gt;:close&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;event&lt;/span&gt;&lt;span class="o"&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;cancel_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s1"&gt;'close'&lt;/span&gt;
      &lt;span class="n"&gt;ws&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="c1"&gt;# Return async Rack response&lt;/span&gt;
    &lt;span class="n"&gt;ws&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;else&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"text/plain"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Not found"&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="c1"&gt;# See https://www.phusionpassenger.com/library/config/tuning_sse_and_websockets/&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PhusionPassenger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;PhusionPassenger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;advertised_concurrency_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;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;随 passenger 的启动方式见 &lt;a href="https://github.com/phusion/passenger-ruby-faye-websocket-demo" rel="nofollow" target="_blank"&gt;https://github.com/phusion/passenger-ruby-faye-websocket-demo&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Action Cable"&gt;Action Cable&lt;/h2&gt;
&lt;p&gt;并没有发现 ActionCable 有提供 Binary 传输参数。&lt;/p&gt;

&lt;p&gt;分析了一下。ActionCable 利用了 redis 的 pub/sub。而直接往 redis 里推 gzip 后的数据是推不进去的，但是可以把 gzip 后的数据 base64 一下。&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;CommentRelayJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&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="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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast&lt;/span&gt; &lt;span class="s2"&gt;"messages:1:comments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Gzip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compress&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="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时我们看到的数据是酱紫的
&lt;img src="https://l.ruby-china.com/photo/2018/04270bfe-394b-4d41-bd27-3fd7b8d2f231.png!large" title="" alt=""&gt;
前端只需要 base64 解码后 gzip 解压数据即可食用。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CommentsChannel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-channel='comments']&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;

  &lt;span class="nx"&gt;received&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="nx"&gt;pako&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inflate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="压缩效果对比"&gt;压缩效果对比&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Gzip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compress&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="mi"&gt;9&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="c1"&gt;#最大压缩比&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;压缩效果对比&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;压缩比&lt;/th&gt;
&lt;th&gt;Base64&lt;/th&gt;
&lt;th&gt;体积&lt;/th&gt;
&lt;th&gt;压缩比&lt;/th&gt;
&lt;th&gt;base64 体积增大百分比&lt;/th&gt;
&lt;th&gt;平均耗时&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;不压缩&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;121713&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;27106&lt;/td&gt;
&lt;td&gt;22.27%&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;0.005862s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;36747&lt;/td&gt;
&lt;td&gt;30.19%&lt;/td&gt;
&lt;td&gt;35.56%&lt;/td&gt;
&lt;td&gt;0.005878s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;max&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;24761&lt;/td&gt;
&lt;td&gt;20.34%&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;0.012196s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;max&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;33567&lt;/td&gt;
&lt;td&gt;27.58%&lt;/td&gt;
&lt;td&gt;35.56%&lt;/td&gt;
&lt;td&gt;0.012197s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;对比结果，默认压缩性价比比较高。最大压缩比耗时多用了 1 倍。base64 看起来不耗时。&lt;/p&gt;

&lt;p&gt;不压缩测试字符串 121k，默认压缩后 27k，base64 后 36k，base64 后体积增大 9k、增大 35.56%。&lt;/p&gt;

&lt;p&gt;对于没有太多保密需求的应用，用 actioncable + gzip+base64 体积压缩了 70%，应该还是可以接受的。&lt;/p&gt;

&lt;p&gt;代码改动量很小，业务比较容易写，websocket 进程管理又可以丢给 nginx+passenger。&lt;/p&gt;
&lt;h2 id="Action Cable Patch"&gt;Action Cable Patch&lt;/h2&gt;
&lt;p&gt;仔细研究了一下 action cable 源码，发现其实在 ActionCable::Connection::ClientSocket 里二进制传输是有预留的，只要给的 message 是个 array 就行了。&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;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="o"&gt;...&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;message&lt;/span&gt;&lt;span class="p"&gt;)&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="vi"&gt;@ready_state&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;OPEN&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Numeric&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="vi"&gt;@driver.text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;  &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="vi"&gt;@driver.text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;   &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="vi"&gt;@driver.binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="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;不过从 redis pub 过来的数据肯定是个 string。&lt;/p&gt;

&lt;p&gt;所以需要通过配置或参数，通知 actioncable 将数据转成 binary array。&lt;/p&gt;

&lt;p&gt;翻看源码，只需要在 ActionCable::Connection::Base 初始化时指定 encode decode 的 coder 即可，默认是 ActiveSupport::JSON&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;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;Base&lt;/span&gt;
      &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Identification&lt;/span&gt;
      &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;InternalChannel&lt;/span&gt;
      &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Authorization&lt;/span&gt;

      &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:server&lt;/span&gt;&lt;span class="p"&gt;,&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;:subscriptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:worker_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:protocol&lt;/span&gt;
      &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:event_loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:pubsub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :server&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;server&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="ss"&gt;coder: &lt;/span&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;JSON&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="vi"&gt;@env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@coder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;coder&lt;/span&gt;

        &lt;span class="vi"&gt;@worker_pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;worker_pool&lt;/span&gt;
        &lt;span class="vi"&gt;@logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_tagged_logger&lt;/span&gt;

        &lt;span class="vi"&gt;@websocket&lt;/span&gt;      &lt;span class="o"&gt;=&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;Connection&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WebSocket&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="nb"&gt;self&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="vi"&gt;@subscriptions&lt;/span&gt;  &lt;span class="o"&gt;=&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;Connection&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Subscriptions&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="vi"&gt;@message_buffer&lt;/span&gt; &lt;span class="o"&gt;=&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;Connection&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MessageBuffer&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="vi"&gt;@_internal_subscriptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
        &lt;span class="vi"&gt;@started_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
      &lt;span class="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;coder&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module ActionCable
  module Connection
    class coder
      def self.encode(data)    
        ActiveSupport::Gzip.compress(data.to_s)
      end
      def self.decode(data)
        ActiveSupport::Gzip.decompress(data)
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过 actioncable 启动时并没有给配置 coder 的地方，所以看来需要打 patch 或提 PR 了。&lt;/p&gt;

&lt;p&gt;另外 js 里还需要解决 channel 相关的逻辑。是个比较麻烦的问题。留个坑慢慢填吧。&lt;/p&gt;

&lt;p&gt;如有大神知道有先人有解决的方案的话，麻烦告知一下&lt;img title=":grinning:" alt="😀" src="https://twemoji.ruby-china.com/2/svg/1f600.svg" class="twemoji"&gt; &lt;/p&gt;
&lt;h2 id="Tips"&gt;Tips&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;数据未经压缩也可以用 Binary Frame 格式推送。&lt;/li&gt;
&lt;li&gt;在小数据的时候 gzip 后体积反而增大。&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>nine</author>
      <pubDate>Mon, 14 May 2018 02:50:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/36767</link>
      <guid>https://ruby-china.org/topics/36767</guid>
    </item>
    <item>
      <title>[郑州] 民生药业集团，招聘 Ruby on Rails 高级开发，JS 高级开发。</title>
      <description>&lt;h2 id="关于民生药业集团"&gt;关于民生药业集团&lt;/h2&gt;
&lt;p&gt;民生药业集团是在国家中成药重点生产企业——民生制药厂（始建于 1911 年）的基础上，由北京天地民生国际投资有限公司全资控股、重新组建的，集中成药、生物医药、科研、医疗、生产、销售、贸易、电子商务等为一体的大型综合性医药产业集团。
集团现有员工 3600 余人，各类技术人员 300 余人。其中，研究员、高级工程师、硕士、博士、执业药师、注册会计师等 100 多人。建立了以世界著名免疫学科学家乔治﹒西博博士为核心的科研团队和博士后工作站，拥有发明和实用新型专利 150 余件。
集团旗下拥有十余家全资或控股子公司，截止 2015 年底，年销售额近百亿。&lt;/p&gt;
&lt;h3 id="医药电商和信息化系统"&gt;医药电商和信息化系统&lt;/h3&gt;
&lt;p&gt;致力发展产业链 B2B 电商和 B2C 网络售药，开发医药行业 ERP 和物流仓储 WMS 管理系统 SaaS 平台。&lt;/p&gt;
&lt;h3 id="招聘岗位"&gt;招聘岗位&lt;/h3&gt;&lt;h4 id="Ruby开发工程师"&gt;Ruby 开发工程师&lt;/h4&gt;
&lt;p&gt;10-15K&lt;/p&gt;

&lt;p&gt;1 两年以上 Ruby on Rails 开发经验。
2 熟练掌握 Bootstrap，jQuery 等前端技术。
3 熟悉 linux 环境，熟练使用 git。
4 熟悉单元测试流程。
5 具有良好的团队精神，优秀的沟通技巧，能适应快节奏的工作环境。&lt;/p&gt;

&lt;p&gt;有电商、ERP 开发经验优先。
熟悉 React、Ember.js、Vue.js 优先。
如有 Github 账号请附带。
五险一金、双休、班车，水平高者薪资可面议。&lt;/p&gt;
&lt;h4 id="JS高级开发工程师"&gt;JS 高级开发工程师&lt;/h4&gt;
&lt;p&gt;10K-15K&lt;/p&gt;

&lt;p&gt;1 熟练掌握 HTML CSS Javascript 基础，熟练掌握 Bootstrap，jQuery 和各种插件。
2 熟悉不同浏览器内核 API 差异。
3 熟练掌握 React.js / Vue.js /  Angular.js / Ember.js 等任意一种前端框架。
4 熟练掌握 grunt / gulp / webpack 之一，了解 PHP / Python / Ruby / Java  等任意一种后端语言。
5 对前端模块化开发有心得。&lt;/p&gt;

&lt;p&gt;如有 github 账号请附带。&lt;/p&gt;

&lt;p&gt;五险一金、双休、班车，水平高者薪资可面议。&lt;/p&gt;
&lt;h2 id="简历可发往"&gt;简历可发往&lt;/h2&gt;
&lt;p&gt;kyuubi#chinacluster.com&lt;/p&gt;
&lt;h3 id="工作地点"&gt;工作地点&lt;/h3&gt;
&lt;p&gt;河南郑州，郑东新区，电子商务大厦 20 楼。图片随后补~&lt;/p&gt;

&lt;p&gt;可加我微信&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Mon, 22 Aug 2016 16:31:20 +0800</pubDate>
      <link>https://ruby-china.org/topics/30877</link>
      <guid>https://ruby-china.org/topics/30877</guid>
    </item>
    <item>
      <title>Rake 任务中如何关闭 PostgreSQL 事务？</title>
      <description>&lt;p&gt;目前服务器上运行了 Rake 任务来持续导入数据，使用 God 管理进程。
但是某些异常会导致数据回滚，最多一次回滚了 8 天，超级吓人。
是否有方法仅在 rake 任务中关闭 PostgreSQL 的事务？&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Thu, 21 Apr 2016 14:17:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/29804</link>
      <guid>https://ruby-china.org/topics/29804</guid>
    </item>
    <item>
      <title>Mina 部署的 Ruby 默认版本为何被锁定在 2.2.0?</title>
      <description>&lt;p&gt;虚拟机是一个干净的 CentOS 7
rvm 干净安装了 2.2.3&lt;/p&gt;

&lt;p&gt;使用 mina 部署时，提示我重新安装已安装过的 gem&lt;/p&gt;

&lt;p&gt;进入 shared/bundle/ruby 目录下 赫然停放着一个 2.2.0 的目录&lt;/p&gt;

&lt;p&gt;在 current 目录下运行  rails c 也是 2.2.0&lt;/p&gt;

&lt;p&gt;可是&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find / &lt;span class="nt"&gt;-name&lt;/span&gt; ruby 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;什么也没有&lt;/p&gt;

&lt;p&gt;这是什么情况？&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Wed, 04 Nov 2015 10:26:23 +0800</pubDate>
      <link>https://ruby-china.org/topics/27942</link>
      <guid>https://ruby-china.org/topics/27942</guid>
    </item>
    <item>
      <title>[已解决] 制作 Gem 包时，在 .gemspec 添加了 add_dependency，可仍然要在 Rails 的 Gemfile 里写上依赖的包，否则不能正常运行？</title>
      <description>&lt;p&gt;打了个 gem 包
需要加载 kaminari&lt;/p&gt;

&lt;p&gt;如果在 .gemspec 里写上  &lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_dependency&lt;/span&gt; &lt;span class="s1"&gt;'kaminari'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;bundle install 就可以正常执行了。
但是在项目里仍然需要添加&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'kaminari'&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;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`page' for #&amp;lt;ActiveRecord::Relation []&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;求正确的打开方式。&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Sat, 31 Oct 2015 20:46:01 +0800</pubDate>
      <link>https://ruby-china.org/topics/27906</link>
      <guid>https://ruby-china.org/topics/27906</guid>
    </item>
    <item>
      <title>Rails.cache.fetch 不可以缓存对象集合的么？</title>
      <description>&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@cates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt; &lt;span class="ss"&gt;:cates&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
  &lt;span class="no"&gt;Cate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而单个对象是可以的
真奇怪&lt;/p&gt;

&lt;p&gt;Rails 版本 4.2.4&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Tue, 13 Oct 2015 04:18:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/27648</link>
      <guid>https://ruby-china.org/topics/27648</guid>
    </item>
    <item>
      <title>ActiveRecord 有无按数量切割结果的内置方法？</title>
      <description>&lt;p&gt;达到如下效果&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;pool&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="no"&gt;ModelName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_index&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;obj&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;num&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;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;pool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>nine</author>
      <pubDate>Sat, 11 Jul 2015 02:37:01 +0800</pubDate>
      <link>https://ruby-china.org/topics/26441</link>
      <guid>https://ruby-china.org/topics/26441</guid>
    </item>
    <item>
      <title>有没有网页去噪的 gem？</title>
      <description>&lt;p&gt;抽取 html 主体内容。不用正则。&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Sun, 05 Jul 2015 23:56:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/26333</link>
      <guid>https://ruby-china.org/topics/26333</guid>
    </item>
    <item>
      <title>Gemfile 里使用 git repo 之后这个 gem 安装到哪里去了？</title>
      <description>&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'twitter-bootstrap-rails'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;git: &lt;/span&gt;&lt;span class="s1"&gt;'https://github.com/seyhunak/twitter-bootstrap-rails'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我想知道 bundle install 之后，这个 gem 安装到系统哪个地方了。&lt;/p&gt;

&lt;p&gt;.gem 或是“源代码”的位置都可以。&lt;/p&gt;

&lt;p&gt;不可能每一次 rails s 都去 github 上获取一次吧？&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Sat, 04 Apr 2015 23:24:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/25001</link>
      <guid>https://ruby-china.org/topics/25001</guid>
    </item>
    <item>
      <title>如何实时获得 stdout 里的内容？</title>
      <description>&lt;p&gt;run.rb&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt;  &lt;span class="err"&gt;·&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;&lt;span class="err"&gt;·&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sleep.rb&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&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;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;t&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行 ruby run.rb 的时候需要 10 秒之后才能打印出信息，而不是每秒打印一个。&lt;/p&gt;

&lt;p&gt;真实的需求是在 web 上 textarea 里输入 sleep.rb 的内容，并实时在另一个 texarea 里看到类似 console 里执行的效果
最好可以终止代码运行&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Tue, 16 Dec 2014 18:17:03 +0800</pubDate>
      <link>https://ruby-china.org/topics/23236</link>
      <guid>https://ruby-china.org/topics/23236</guid>
    </item>
    <item>
      <title>[北京 - 保福寺桥] Ruby 开发工程师（代招）</title>
      <description>&lt;p&gt;1、精通 Ruby，2 年以上 Ruby On Rails 经验，3 年以上互联网应用开发经验；
2、精通 HTML、CSS、JSP、Ajax、JS 等 Web 应用开发技术；
3、精通社交网站的设计与实现，精通 Ruby 远程调用技术和数据库编程；
4、了解 Linux 文件系统优先；
5、了解或者精通 Git 优先；&lt;/p&gt;

&lt;p&gt;工作地点：北京中关村东路 四环交叉口（保福寺桥）世纪科贸大厦&lt;/p&gt;

&lt;p&gt;薪水：12K-20K
公司简介&lt;/p&gt;

&lt;p&gt;简历请投至 service#cyhz.com&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Sat, 30 Aug 2014 00:31:26 +0800</pubDate>
      <link>https://ruby-china.org/topics/21296</link>
      <guid>https://ruby-china.org/topics/21296</guid>
    </item>
    <item>
      <title>出个 HHKB pro2 9.9 成新，黑色有刻，包顺丰 [郑州可同城交易，送货上门^_^]</title>
      <description>&lt;p&gt;已出&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Sat, 19 Jul 2014 01:18:51 +0800</pubDate>
      <link>https://ruby-china.org/topics/20585</link>
      <guid>https://ruby-china.org/topics/20585</guid>
    </item>
    <item>
      <title>turbolinks 中如何 reload 页面 dom？</title>
      <description>&lt;p&gt;location.reload() 会刷新页面
求不刷新方法&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Tue, 01 Apr 2014 21:35:18 +0800</pubDate>
      <link>https://ruby-china.org/topics/18325</link>
      <guid>https://ruby-china.org/topics/18325</guid>
    </item>
    <item>
      <title>请问大家 InnoDB count (*) 问题都是怎么解决的？</title>
      <description>&lt;p&gt;目前用的 WiceGrid&lt;/p&gt;

&lt;p&gt;默认的分页是 select count(*)  随便有点数据都巨慢&lt;/p&gt;

&lt;p&gt;大家都怎么解决的？自己 hack 吗？&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Wed, 19 Feb 2014 10:35:32 +0800</pubDate>
      <link>https://ruby-china.org/topics/17347</link>
      <guid>https://ruby-china.org/topics/17347</guid>
    </item>
    <item>
      <title>Macbook Air 能否连双显示器 (非苹果显示器)？</title>
      <description>&lt;p&gt;苹果显示器应该可以串雷电接口，但是不考虑那个，成本太高。
希望是外接双 Dell U2412，哪位有经验的来说说
知乎上也有提问
&lt;a href="http://www.zhihu.com/question/22600143" rel="nofollow" target="_blank"&gt;http://www.zhihu.com/question/22600143&lt;/a&gt;&lt;/p&gt;</description>
      <author>nine</author>
      <pubDate>Sat, 25 Jan 2014 13:39:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/16989</link>
      <guid>https://ruby-china.org/topics/16989</guid>
    </item>
  </channel>
</rss>
