<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>jjym (2622)</title>
    <link>https://ruby-china.org/jjym</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>用 Ruby 体验快速开发 P2P 网络程序</title>
      <description>&lt;p&gt;很多开发者很熟悉 Server-Client 这一套网络结构，Server-Client 是构建互联网应用的基础。
但在区块链技术这里就有点过时了，区块链的世界普遍采用 P2P 网络。&lt;/p&gt;

&lt;p&gt;P2P network 是什么呢？&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;对等式网络（peer-to-peer，简称 P2P），又称点对点技术，是无中心服务器、依靠用户群（peers）交换信息的互联网体系，它的作用在于，减低以往网路传输中的节点，以降低资料遗失的风险。与有中心服务器的中央网络系统不同，对等网络的每个用户端既是一个节点，也有服务器的功能，任何一个节点无法直接找到其他节点，必须依靠其户群进行信息交流。&lt;/p&gt;

&lt;p&gt;From 维基百科&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;ciri-p2p&lt;/code&gt; 是在我尝试实现以太坊协议 (&lt;a href="https://github.com/ciri-ethereum/ciri" rel="nofollow" target="_blank" title=""&gt;Ciri Ethereum&lt;/a&gt;) 时的一个副产品，使用这个库可以轻松的用 Ruby 来实现 P2P network 服务&lt;/p&gt;

&lt;p&gt;考虑到如果可以用 Ruby 来试验各种 P2P 网络协议会非常爽，
所以我把以太坊的底层 P2P 通信协议 -  DevP2P 的实现作为了单独的一个库抽离了出来。&lt;/p&gt;

&lt;p&gt;DevP2P 是一个相对独立的协议，和区块链、以太坊都没什么太多关联，
所以 &lt;code&gt;ciri-p2p&lt;/code&gt; 也可以用来做和区块链完全不相关的事情：比如实现个“基于 P2P 网络的 DNS 服务”，我们会在下文中用 &lt;code&gt;ciri-p2p&lt;/code&gt; 来实现这个例子。&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;DevP2P 主要包括了以下部分：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;基于公私钥的端到端加密&lt;/li&gt;
&lt;li&gt;基于 DHT 的发现协议&lt;/li&gt;
&lt;li&gt;架构上分层，支持多个子协议复用连接&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在 &lt;code&gt;ciri-p2p&lt;/code&gt; 中，前两点对于用户来说是透明的，我们只需要实现一个子协议来完成我们的应用逻辑&lt;/p&gt;

&lt;p&gt;我们的示例是实现一个在 P2P 网络中可以与其他节点交换、发现网址的服务。&lt;/p&gt;

&lt;p&gt;为了简化设计，我们设计为只去发现程序员们最喜爱的网站 - 1024 的最新网址列表。&lt;/p&gt;

&lt;p&gt;实现子协议的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'ciri/p2p/server'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'ciri/p2p/protocol'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'ciri/key'&lt;/span&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;'async'&lt;/span&gt;

&lt;span class="c1"&gt;# 来给 1024 发现协议起个优雅的名字，就叫 GossipDNS&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GossipDNS&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Ciri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;P2P&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Protocol&lt;/span&gt;
  &lt;span class="c1"&gt;# code of our messages&lt;/span&gt;
  &lt;span class="c1"&gt;# 定义两个消息的 ID&lt;/span&gt;
  &lt;span class="c1"&gt;# 我们的简化协议只支持&lt;/span&gt;
  &lt;span class="c1"&gt;# FIND_URLS 查询 url 列表&lt;/span&gt;
  &lt;span class="c1"&gt;# NEW_URLS 返回 url 列表&lt;/span&gt;
  &lt;span class="no"&gt;FIND_URLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="no"&gt;NEW_URLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:dns_records&lt;/span&gt;

  &lt;span class="c1"&gt;# 传入我们知道的 1024 最新网址列表&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;urls&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt; 
    &lt;span class="c1"&gt;# 设置我们的协议名称、版本、长度&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"1024discovery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;version: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;length: &lt;/span&gt;&lt;span class="mi"&gt;8096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="vi"&gt;@urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt; 
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# 协议初始化时会调用，context 代表当前网络的上下文&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Service started!"&lt;/span&gt;
    &lt;span class="c1"&gt;# ciri-p2p 依赖 async 框架进行异步 IO 操作&lt;/span&gt;
    &lt;span class="c1"&gt;# 这里我们用 async 框架提供的定时功能每 10s 向所有 peers 请求最新列表&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Async&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt; 
    &lt;span class="c1"&gt;# 每 10s 执行&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&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="k"&gt;do&lt;/span&gt;
      &lt;span class="c1"&gt;# 取到当前连接的所有 peer&lt;/span&gt;
      &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
        &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;async&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="c1"&gt;# 对每个 peer 发送 FIND_URLS 消息, 注意这里 data 为空&lt;/span&gt;
          &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FIND_URLS&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="ss"&gt;peer: &lt;/span&gt;&lt;span class="n"&gt;peer&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;span class="k"&gt;end&lt;/span&gt;


  &lt;span class="c1"&gt;# 收到消息时会调用&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;received&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="c1"&gt;# check msg code&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt; 
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;NEW_URLS&lt;/span&gt;
      &lt;span class="c1"&gt;# 收到新的 url 列表&lt;/span&gt;
      &lt;span class="n"&gt;urls&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="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"receive &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; urls from &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="vi"&gt;@urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@urls&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt; 
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;FIND_URLS&lt;/span&gt;
      &lt;span class="c1"&gt;# 收到请求消息，返回 url 列表&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"send &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@urls.count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; urls to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NEW_URLS&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="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@urls&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"received invalid message code &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, ignoring"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# 输出当前节点和 url 列表&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;local_node_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;short_hex&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] current urls:"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="vi"&gt;@urls&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;connected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"connected new peer &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;disconnected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"disconnected peer &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;这段代码实现了 1024 发现协议的核心逻辑，注意 received 方法里我们针对收到的两个消息进行了不同的处理。整个应用由初始化时的定时任务触发。&lt;/p&gt;

&lt;p&gt;下面来实现创建节点的代码&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;start_node&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="ss"&gt;private_key: &lt;/span&gt;&lt;span class="no"&gt;Ciri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
               &lt;span class="n"&gt;bootnodes&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; 
               &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;tcp_port: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;udp_port: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="no"&gt;Ciri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;P2P&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="ss"&gt;private_key: &lt;/span&gt;&lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# private key of our node, used for encrypted communication&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="c1"&gt;# 节点运行的协议&lt;/span&gt;
    &lt;span class="ss"&gt;bootnodes: &lt;/span&gt;&lt;span class="n"&gt;bootnodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 节点启动时去连接的其他节点&lt;/span&gt;
    &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="ss"&gt;tcp_port: &lt;/span&gt;&lt;span class="n"&gt;tcp_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# node port&lt;/span&gt;
    &lt;span class="ss"&gt;udp_port: &lt;/span&gt;&lt;span class="n"&gt;udp_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# port for discovery&lt;/span&gt;
    &lt;span class="ss"&gt;discovery_interval_secs: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# try discovery more nodes every 5 seconds&lt;/span&gt;
    &lt;span class="ss"&gt;dial_outgoing_interval_secs: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# try connect to new nodes every 10 seconds&lt;/span&gt;
    &lt;span class="ss"&gt;max_outgoing: &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# number of nodes we will try to connect&lt;/span&gt;
    &lt;span class="ss"&gt;max_incoming: &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# number of nodes we will accept&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意这个方法的 protocols 参数，这里我们传入要启动的子协议，这些协议就会自动被节点运行。其他的参数是节点启动时的一些配置，查阅注释应该可以理解。&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_example&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"start example"&lt;/span&gt;
  &lt;span class="c1"&gt;# 启动 async 框架的 reactor&lt;/span&gt;
  &lt;span class="no"&gt;Async&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Reactor&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
    &lt;span class="c1"&gt;# 随机一个 key 作为 node1 的私钥&lt;/span&gt;
    &lt;span class="c1"&gt;# 补充下公钥会作为节点地址的一部分，所以我们这里事先生成 key&lt;/span&gt;
    &lt;span class="n"&gt;node1_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ciri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt; 
    &lt;span class="c1"&gt;# 初始化我们的协议，填入 1024 的最新网址获取方法&lt;/span&gt;
    &lt;span class="n"&gt;protocol1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;GossipDNS&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;urls: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'baidu.com'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; 
    &lt;span class="c1"&gt;# node2 的协议&lt;/span&gt;
    &lt;span class="n"&gt;protocol2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;GossipDNS&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;urls: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'google.com'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; 

    &lt;span class="c1"&gt;# start node1&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;async&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;start_node&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="n"&gt;protocol1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;private_key: &lt;/span&gt;&lt;span class="n"&gt;node1_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;bootnodes: &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="ss"&gt;tcp_port: &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;udp_port: &lt;/span&gt;&lt;span class="mi"&gt;3000&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;end&lt;/span&gt;

    &lt;span class="c1"&gt;# start node2&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;async&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="c1"&gt;# 注意这里的地址包含了 node_id 和 address，DevP2P 中节点地址是由这两部分组成。&lt;/span&gt;
      &lt;span class="n"&gt;node1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ciri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;P2P&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Node&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;node_id: &lt;/span&gt;&lt;span class="no"&gt;Ciri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;P2P&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NodeID&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;node1_key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="ss"&gt;addresses: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; 
          &lt;span class="no"&gt;Ciri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;P2P&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Address&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;ip: &lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="ss"&gt;udp_port: &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="ss"&gt;tcp_port: &lt;/span&gt;&lt;span class="mi"&gt;3000&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="n"&gt;start_node&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="n"&gt;protocol2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;bootnodes: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;tcp_port: &lt;/span&gt;&lt;span class="mi"&gt;3001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;udp_port: &lt;/span&gt;&lt;span class="mi"&gt;3001&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;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;if&lt;/span&gt; &lt;span class="nb"&gt;caller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="c1"&gt;# 设置 ciri-p2p 的日志&lt;/span&gt;
  &lt;span class="no"&gt;Ciri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Utils&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;level: :info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="n"&gt;start_example&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述代码设计了一些 &lt;code&gt;async&lt;/code&gt; 框架的部分，分别启动了两个节点，设置了 node2 的 bootnodes 参数，这样 node2 会去主动连接 node1。等待 10s 左右就会看到节点间互相传递数据的日志。&lt;/p&gt;

&lt;p&gt;全部代码整理在 &lt;a href="https://github.com/jjyr/gossip-dns-example" rel="nofollow" target="_blank" title=""&gt;gossip-dns-example&lt;/a&gt; ，可以直接 clone repo 来启动。&lt;/p&gt;

&lt;p&gt;在示例仓库目录下运行 &lt;code&gt;bundle install &amp;amp;&amp;amp; bundle exec ruby app.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;产生如下日志，节点成功交换网址！&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;start example
Service started!                                        
I, &lt;span class="o"&gt;[&lt;/span&gt;2018-11-28 00:00:02#6306]  INFO &lt;span class="nt"&gt;--&lt;/span&gt; Ciri::P2P::Server: start accept connections &lt;span class="nt"&gt;--&lt;/span&gt; listen on localhost:hbci
Service started!
I, &lt;span class="o"&gt;[&lt;/span&gt;2018-11-28 00:00:02#6306]  INFO &lt;span class="nt"&gt;--&lt;/span&gt; Ciri::P2P::Server: start accept connections &lt;span class="nt"&gt;--&lt;/span&gt; listen on localhost:redwood-broker
I, &lt;span class="o"&gt;[&lt;/span&gt;2018-11-28 00:00:02#6306]  INFO &lt;span class="nt"&gt;--&lt;/span&gt; Ciri::P2P::Discovery::Service: start discovery server on udp_port: 3000 tcp_port: 3000
local_node_id: 0xfcf53c4df3f3202c9b4835cad688828abdd01e3123dee1bcb5a6408199360f92e225a4d2e2d04e2e0f7002c1bde328e13c107ccd43140f08ce052f53753d3458
I, &lt;span class="o"&gt;[&lt;/span&gt;2018-11-28 00:00:02#6306]  INFO &lt;span class="nt"&gt;--&lt;/span&gt; Ciri::P2P::Discovery::Service: start discovery server on udp_port: 3001 tcp_port: 3001
local_node_id: 0x1640206ef2b13fb80c4b4d7af34cd3e1b319f2a4c139724d157458df456569487b17cd787072b94de1776c40bf51b8d4d1cfff56358dc5ecf7472fa2fe2d1d28
I, &lt;span class="o"&gt;[&lt;/span&gt;2018-11-28 00:00:03#6306]  INFO &lt;span class="nt"&gt;--&lt;/span&gt; Ciri::P2P::NetworkState: &lt;span class="o"&gt;[&lt;/span&gt;0x1640206] connect to new peer &amp;lt;Peer:0xfcf53c4 direction: outgoing&amp;gt;
connected new peer &amp;lt;Peer:0xfcf53c4 direction: outgoing&amp;gt;
I, &lt;span class="o"&gt;[&lt;/span&gt;2018-11-28 00:00:03#6306]  INFO &lt;span class="nt"&gt;--&lt;/span&gt; Ciri::P2P::NetworkState: &lt;span class="o"&gt;[&lt;/span&gt;0xfcf53c4] connect to new peer &amp;lt;Peer:0x1640206 direction: incoming&amp;gt;
connected new peer &amp;lt;Peer:0x1640206 direction: incoming&amp;gt;
send 1 urls to &amp;lt;Peer:0xfcf53c4 direction: outgoing&amp;gt;
&lt;span class="o"&gt;[&lt;/span&gt;0x1640206] current urls:             
google.com                                  
receive 1 urls from &amp;lt;Peer:0x1640206 direction: incoming&amp;gt;                                                                        
&lt;span class="o"&gt;[&lt;/span&gt;0xfcf53c4] current urls:                                                                                                        
baidu.com                                                                                                                       
google.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是个很简单的示例程序，展示了 &lt;code&gt;ciri-p2p&lt;/code&gt; 的基本用法，&lt;code&gt;ciri-p2p&lt;/code&gt; 本身的功能还是很完善的，结合 &lt;code&gt;async&lt;/code&gt; 框架我们甚至可以把示例扩展为真正可用的 P2P 版 DNS。&lt;/p&gt;

&lt;p&gt;区块链或其他的 P2P 网络环境中还有很多有趣的协议值得研究，不妨用 &lt;code&gt;ciri-p2p&lt;/code&gt; 来实现下。&lt;/p&gt;

&lt;p&gt;要注意的是，真实的 P2P 网络中，一定要考虑到 peer 间是无法信任的，像示例中这样简单的协议，很可能跑一整天也发现不了真正的网址。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ciri-p2p &lt;a href="https://github.com/ciri-ethereum/ciri-p2p" rel="nofollow" target="_blank" title=""&gt;https://github.com/ciri-ethereum/ciri-p2p&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;示例代码 &lt;a href="https://github.com/jjyr/gossip-dns-example" rel="nofollow" target="_blank" title=""&gt;https://github.com/jjyr/gossip-dns-example&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;原文转自博客 - &lt;a href="http://justjjy.com/2018/11/27/yong-Ruby-ti-yan-kuai-su-kai-fa-p2p-wang-luo-cheng-xu/" rel="nofollow" target="_blank"&gt;http://justjjy.com/2018/11/27/yong-Ruby-ti-yan-kuai-su-kai-fa-p2p-wang-luo-cheng-xu/&lt;/a&gt;&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Wed, 28 Nov 2018 00:43:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/37825</link>
      <guid>https://ruby-china.org/topics/37825</guid>
    </item>
    <item>
      <title>简化实例变量的初始化，一个被连续拒绝了 7 年的语法...</title>
      <description>&lt;p&gt;&lt;a href="https://bugs.ruby-lang.org/issues/15192" rel="nofollow" target="_blank"&gt;https://bugs.ruby-lang.org/issues/15192&lt;/a&gt;&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Tue, 02 Oct 2018 13:48:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/37583</link>
      <guid>https://ruby-china.org/topics/37583</guid>
    </item>
    <item>
      <title>Ruby 的命名空间提案，引用包时避免污染全局环境</title>
      <description>&lt;p&gt;&lt;a href="https://bugs.ruby-lang.org/issues/14982" rel="nofollow" target="_blank"&gt;https://bugs.ruby-lang.org/issues/14982&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;大致思路就是加入一些对上下文的造作，让用户来控制当前的上下文（Binding），而不是 gem 作者&lt;/p&gt;

&lt;p&gt;示例：&lt;/p&gt;

&lt;p&gt;1 引用 ruby 文件到单独的 module 下&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# requiring into isolated Binding&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;into: :IsolatedBindingModule1&lt;/span&gt;
&lt;span class="no"&gt;IsolatedBindingModule1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="c1"&gt;# Module&lt;/span&gt;
&lt;span class="no"&gt;IsolatedBindingModule1&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Foo&lt;/span&gt; &lt;span class="c1"&gt;# access names&lt;/span&gt;

&lt;span class="c1"&gt;# the old way should still work&lt;/span&gt;
&lt;span class="c1"&gt;# requiring and polluting TOPLEVEL_BINDING&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;
&lt;span class="no"&gt;Foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2 允许用户使用“干净”的上下文 (Binding)&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Foo&lt;/span&gt;
&lt;span class="n"&gt;isolate&lt;/span&gt; &lt;span class="c1"&gt;# isolate 关键字创建了新的 Binding(独立于 TOPLEVEL_BINDING)&lt;/span&gt;
    &lt;span class="no"&gt;Foo&lt;/span&gt; &lt;span class="c1"&gt;# NameError&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;isolate&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Bar&lt;/span&gt; &lt;span class="c1"&gt;# 隔离 Bar 的 Binding&lt;/span&gt;
  &lt;span class="no"&gt;Foo&lt;/span&gt; &lt;span class="c1"&gt;# NameError&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3 import，从另外 Binding 中导入名称&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Foo&lt;/span&gt;
&lt;span class="n"&gt;isolate&lt;/span&gt;
  &lt;span class="c1"&gt;# import name from a ruby file&lt;/span&gt;
  &lt;span class="c1"&gt;# import primitive is a convenient way to use isolated requiring&lt;/span&gt;
  &lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="ss"&gt;:Foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;from: &lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="err"&gt;”&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;注意：这是个提案，目前还未被 Ruby 团队接受&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Sun, 19 Aug 2018 12:58:45 +0800</pubDate>
      <link>https://ruby-china.org/topics/37348</link>
      <guid>https://ruby-china.org/topics/37348</guid>
    </item>
    <item>
      <title>PUMA 实现简要分析</title>
      <description>&lt;p&gt;&lt;a href="http://justjjy.com/2018/02/21/PUMA-shi-xian-jian-yao-fen-xi/" rel="nofollow" target="_blank"&gt;http://justjjy.com/2018/02/21/PUMA-shi-xian-jian-yao-fen-xi/&lt;/a&gt;&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Thu, 22 Feb 2018 02:17:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/35053</link>
      <guid>https://ruby-china.org/topics/35053</guid>
    </item>
    <item>
      <title>Good news everyone! Ruby 又要添加绿色线程了, Thread::Green (可以理解为 go 的 goroutine)</title>
      <description>&lt;p&gt;&lt;a href="https://bugs.ruby-lang.org/issues/13618" rel="nofollow" target="_blank"&gt;https://bugs.ruby-lang.org/issues/13618&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;翻到了 ruby-lang 的这个 issue，总结下&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Eric Wong 给 ruby 增加了可以自动调度的 fiber，暂命名为 &lt;code&gt;Thread::Green&lt;/code&gt;。就是类似 go 的 goroutine 这样的轻量级线程&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Queue&lt;/code&gt;, &lt;code&gt;SizedQueue&lt;/code&gt; 等用于同步的类是可以和 &lt;code&gt;Thread::Green&lt;/code&gt; 一起使用的。意味着现有的 WebServer 成 Thread::Green 很简单可以迁移&lt;/li&gt;
&lt;li&gt;Matz, ko1 等大佬纷纷拍手称赞，(说不定很快就能用上了)&lt;/li&gt;
&lt;li&gt;之后 ruby 可以说摆脱异步编程模型了，直接起 &lt;code&gt;Thread::Green&lt;/code&gt; 然后用 blocking IO 就可以和 node 的 callback hell 怼一怼&lt;/li&gt;
&lt;li&gt;对‘应用级别’开发者意味着 Web Server 会更高效，Rails 等框架也会更高效&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;用法大概就是和 Thread api 会兼容 所以没什么区别，比如下面示例：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://..."&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="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Green&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;url&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;url&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="c1"&gt;# request url&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="nf"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;

&lt;p&gt;感觉 Eric Wong 这个人很神奇啊，Unicorn 应该也是他写的。很低调、很少出镜，不用非自由软件 (包括 js)..有谁知道他的故事..&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;坏消息就是我之前写的库已经失去存在意义了 &lt;a href="https://github.com/socketry/lightio" rel="nofollow" target="_blank" title=""&gt;lightio&lt;/a&gt;, 不过可以当作个教学示例吧，这些功能从 ruby 层面去实现大概就是这个库&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>jjym</author>
      <pubDate>Sun, 04 Feb 2018 14:49:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/34992</link>
      <guid>https://ruby-china.org/topics/34992</guid>
    </item>
    <item>
      <title>新年新项目，聊一聊最近在做的 LightIO，IO 性能的免费午餐</title>
      <description>&lt;h3 id="LightIO 是什么"&gt;LightIO 是什么&lt;/h3&gt;
&lt;p&gt;gevent 是 python 中我最喜欢库之一。&lt;/p&gt;

&lt;p&gt;只要 import gevent, 调用下 monkey patch 便会把线程替换为绿色线程，并且把标准库 socket 打上补丁。&lt;/p&gt;

&lt;p&gt;每次 IO 操作都会由类库自动切换到其他绿色线程。对用户几乎完全透明，堪称 IO 性能的免费午餐。（建议看这里了解详细： &lt;a href="http://justjjy.com/2017/12/17/jie-shao-xia-LightIO/" rel="nofollow" target="_blank" title=""&gt;gevent 和异步&lt;/a&gt;）&lt;/p&gt;

&lt;p&gt;身为 rubyist 一直很羡慕隔壁的 gevent。之前虽也有过 em-synchronize 之类使用 Fiber 来表达同步的库（ &lt;a href="http://justjjy.com/2017/12/17/jie-shao-xia-LightIO/" rel="nofollow" target="_blank" title=""&gt;也可以看这篇了解，不再累述&lt;/a&gt;），但总归昙花一现，而且无法做到对用户透明用起来没 gevent 这么方便。&lt;/p&gt;

&lt;p&gt;最近耐心看了 gevent 源码，尝试在 ruby 中实现类似能力。产出是 &lt;a href="https://github.com/socketry/lightio" rel="nofollow" target="_blank" title=""&gt;LightIO&lt;/a&gt;，目标是为 ruby 用户提供免费的 IO 午餐&lt;/p&gt;
&lt;h3 id="当前状态"&gt;当前状态&lt;/h3&gt;
&lt;p&gt;核心实现起来很简单。但 ruby 的标准库接口庞大，要实现可以 Monkey Patch 的类得花费一番功夫。所以目前只支持有限的几个类 (Socket, TCPSocket, IO 等等)&lt;/p&gt;

&lt;p&gt;和 gevent 一样，可以显式的使用 LightIO 来编写代码，目前已经能稳定的支持&lt;/p&gt;

&lt;p&gt;如下示例，使用 LightIO 下 的 &lt;code&gt;Beam&lt;/code&gt;， &lt;code&gt;TCPServer&lt;/code&gt; 相比标准库可以获得更好的性能。（&lt;a href="https://github.com/socketry/lightio/wiki/Basic-Usage" rel="nofollow" target="_blank" title=""&gt;基础用法&lt;/a&gt;）&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# echo server example, use telnet to test&lt;/span&gt;
&lt;span class="no"&gt;LightIO&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;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&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;server&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"start echo server"&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&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;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;LightIO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Beam&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
      &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&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;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peeraddr&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"accept connection from &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;host&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="kp"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readpartial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"client leave &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;host&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="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;code&gt;LightIO&lt;/code&gt; namespace 下的类进行全局 Monkey Patch, 目前仍未完美支持。&lt;/p&gt;
&lt;h3 id="Monkey Patch 和 Performance"&gt;Monkey Patch 和 Performance&lt;/h3&gt;
&lt;p&gt;最近成功的用 LightIO Monkey Patch 了 Webrick，用 ab 本机测了下，性能的确有所提升。
&lt;a href="https://github.com/jjyr/lightio_benchmark" rel="nofollow" target="_blank"&gt;https://github.com/jjyr/lightio_benchmark&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在 aws 的 1G 机器上大概每秒多处理 100 个 请求。提升不太明显，原因 1 是和 Webrick 代码本身有关，2 是目前大多数服务器也是异步 IO，只测 Hello World 不会有太大区别。
在更贴近真实环境的情况下 LightIO 的绿色线程会有更大优势。&lt;/p&gt;

&lt;p&gt;同时试了下 sinatra 还无法支持..应该是 sinatra 使用了目前 LightIO 没有包含的标准库。&lt;/p&gt;

&lt;p&gt;目前的下个目标就定位可以 Monkey Patch sinatra 应用&lt;/p&gt;

&lt;p&gt;道阻且长，完美的 Monkey Patch 标准库是难以短时间实现的。&lt;/p&gt;

&lt;p&gt;但 LightiO 已经可以提供很好的支持使用，如果有人写一些高性能的 Server／爬虫 等服务，可以考虑使用 LightIO 来实现。&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Mon, 01 Jan 2018 18:08:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/34802</link>
      <guid>https://ruby-china.org/topics/34802</guid>
    </item>
    <item>
      <title>[上海] 魔都最闪耀的容器云计算领域明星企业 DaoCloud 求贤若渴！ 推荐求职送豪礼~ </title>
      <description>&lt;p&gt;DaoCloud 成立于 2014 年末，是新一代容器云计算领域的明星企业。DaoCloud 产品线涵盖互联网应用的开发、交付、运维和运营全生命周期，并提供公有云、混合云和私有云等多种交付方式。DaoCloud 核心团队由来自 EMC、VMware、微软等知名企业的高管和技术专家组成，公司已经完成两轮超过千万美元规模融资，产品被企业级客户、互联网公司等广泛采用。DaoCloud 总部位于中国上海，并设立了北京、武汉、深圳等分支机构。我们的官网：&lt;a href="http://www.daocloud.io/" rel="nofollow" target="_blank"&gt;http://www.daocloud.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img title=":purple_heart:" alt="💜" src="https://twemoji.ruby-china.com/2/svg/1f49c.svg" class="twemoji"&gt; 区域销售经理（上海、南京、北京、济南、武汉、深圳、广州）
五年以上企业级 IT 软件产品或解决方案大客户的销售经验
熟悉云计算、大数据、容器、PaaS、软件开发等领域的知识
在本区域有一定企业客户、行业、渠道资源的积累
出色的商务谈判和沟通能力，良好的团队协作能力
具备责任心、服务意识、创业激情，能够承受较大的工作压力&lt;/p&gt;

&lt;p&gt;&lt;img title=":yellow_heart:" alt="💛" src="https://twemoji.ruby-china.com/2/svg/1f49b.svg" class="twemoji"&gt; 售前顾问（上海、南京、北京、济南、武汉、深圳、广州）
三年以上 IT 或互联网相关公司技术开发或售前相关工作经验
熟悉云计算行业的主流技术，了解 VMware、微软，或者 OpenStack 等虚拟化技术
对持续集成、持续交付、容器云平台等有较深入的研究和实战经验
求知欲强，有较好的学习能力，对企业 IT 架构和业务需求有较深入的理解
优秀的沟通能力和演讲能力，能够与客户的决策层有效沟通交流&lt;/p&gt;

&lt;p&gt;&lt;img title=":blue_heart:" alt="💙" src="https://twemoji.ruby-china.com/2/svg/1f499.svg" class="twemoji"&gt; 云平台后端工程师（上海）
熟练使用 Python 或 Golang
熟悉 Docker 和 Docker 相关的产品，比如 Registry、Swarm、Compose 等
熟悉 Linux 开发环境，有一定的系统管理能力和 MySQL 管理能力
开源项目的 Owner、核心贡献者或 DaoCloud 活跃用户优先考虑
对代码和工程质量有所追求&lt;/p&gt;

&lt;p&gt;「推荐送豪礼」&lt;/p&gt;

&lt;p&gt;欢迎一直默默支持 DaoCloud 的伙伴们积极参与本次活动！&lt;/p&gt;

&lt;p&gt;即日起，成功自荐或推荐身边亲朋好友入职转正后，即可获得以下任意一款惊喜极客大礼或等值现金红包：&lt;/p&gt;

&lt;p&gt;价值 4499 元 微软 Microsoft Xbox One 体感游戏机一台
价值 5199 元 大疆 Phantom 3 4K 航拍无人机四轴飞行器一台&lt;/p&gt;

&lt;p&gt;价值 4488 元 苹果 iPad Air 2（64G）平板电脑一台&lt;/p&gt;

&lt;p&gt;价值 4566 元 科思康双轮平衡体感车一辆&lt;/p&gt;

&lt;p&gt;参与方式：&lt;/p&gt;

&lt;p&gt;请猛烈投掷简历至：jobs@daocloud.io&lt;/p&gt;

&lt;p&gt;本次活动截至时间为 2016 年 7 月 15 日&lt;/p&gt;

&lt;p&gt;友情提醒：&lt;/p&gt;

&lt;p&gt;1）请在邮件标题中注明应聘或推荐的职位 ＋ 人名&lt;/p&gt;

&lt;p&gt;2）程序员请提供 Github 个人页面地址&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Wed, 29 Jun 2016 11:57:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/30401</link>
      <guid>https://ruby-china.org/topics/30401</guid>
    </item>
    <item>
      <title>minidown 2.1.0 版发布！一个又小又快的 markdown parser, 100% 的 GFM 支持</title>
      <description>&lt;p&gt;&lt;a href="https://github.com/jjyr/minidown/releases/tag/v2.1.0" rel="nofollow" target="_blank"&gt;https://github.com/jjyr/minidown/releases/tag/v2.1.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;很久前发过一次，但我当时没想到有很多隐藏的很难解决的 bug。(比如区分 html 和普通的 text，正确分析几种有歧义的语法等)&lt;/p&gt;

&lt;p&gt;最近才找到了思路，把这些难缠的 bug 都修掉了，并且加入了 table 的语法支持。&lt;/p&gt;

&lt;p&gt;现在已经做到了 100% 的 GFM 格式支持！！&lt;/p&gt;

&lt;p&gt;其实写这个的初衷就是很多 markdown parser 号称支持 GFM，但并非 100% 的支持，比如 redcarpet 的 underscore 语法，如果加上 escape 符号的话会&lt;a href="https://github.com/vmg/redcarpet/issues/214#issuecomment-22977188" rel="nofollow" target="_blank" title=""&gt;有问题&lt;/a&gt;，换行貌似也会有问题。marked 则是不支持 Task list.maruku 的话总是报错，rdiscount 解析速度又太慢。&lt;/p&gt;

&lt;p&gt;于是就打算写个纯 ruby，100% 支持 GFM，使用方便，不可配置 (这一点也是考虑到了减少复杂度) 的 markdown parser。写完后测了下，发现解析速度相比同是 ruby 所写的 maruku 和 rdiscount 还是有不少优势。&lt;/p&gt;

&lt;p&gt;目前 minidown 坚持的是小而方便路线&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;纯 ruby&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;无其他依赖&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;暂时不会考虑加入更多的自定义 (目前 code 块可以自定义，用于代码着色)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当初版本号升的太草率了，所以导致 2.1 版前隐藏着一些很难发现的 parse 上的问题。&lt;/p&gt;

&lt;p&gt;现在测试用例增加到了 120 多个，可以说正确性已经得到了解决，并且已经 100% 支持 GFM！&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Sun, 23 Feb 2014 23:26:21 +0800</pubDate>
      <link>https://ruby-china.org/topics/17438</link>
      <guid>https://ruby-china.org/topics/17438</guid>
    </item>
    <item>
      <title>偶然搜到了个很 cool 的东西，rhodes -- ruby 开发跨平台 mobile 应用</title>
      <description>&lt;p&gt;&lt;a href="https://github.com/rhomobile/rhodes" rel="nofollow" target="_blank"&gt;https://github.com/rhomobile/rhodes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Rhodes framework is a platform for building locally executing, device-optimized mobile applications for all major smartphone devices.&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Sat, 18 Jan 2014 23:10:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/16863</link>
      <guid>https://ruby-china.org/topics/16863</guid>
    </item>
    <item>
      <title>大家用了 ruby2.0 后感觉对 web 程序来说效率提升了吗？</title>
      <description>&lt;p&gt;最近试了下用 ruby2.0 + rails3.2，benchmark 感觉没 1.9.3 快。很出乎意料啊&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Wed, 18 Sep 2013 13:23:25 +0800</pubDate>
      <link>https://ruby-china.org/topics/14208</link>
      <guid>https://ruby-china.org/topics/14208</guid>
    </item>
    <item>
      <title>Github 两步认证</title>
      <description>&lt;p&gt;&lt;a href="https://github.com/blog/1614-two-factor-authentication" rel="nofollow" target="_blank"&gt;https://github.com/blog/1614-two-factor-authentication&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;发现没人在社区里发&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Fri, 06 Sep 2013 11:39:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/13921</link>
      <guid>https://ruby-china.org/topics/13921</guid>
    </item>
    <item>
      <title>bundler 1.4.0 增加并行支持，以后不能偷懒了...</title>
      <description>&lt;p&gt;&lt;a href="http://robots.thoughtbot.com/post/59584648154/parallel-gem-installing-using-bundler" rel="nofollow" target="_blank"&gt;http://robots.thoughtbot.com/post/59584648154/parallel-gem-installing-using-bundler&lt;/a&gt;&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Sat, 31 Aug 2013 22:47:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/13786</link>
      <guid>https://ruby-china.org/topics/13786</guid>
    </item>
    <item>
      <title>写了个 Markdown parser [附代码导读]</title>
      <description>&lt;p&gt;最近练手写了个 markdown parser
采用 GFM 格式 没有其他依赖
&lt;a href="https://github.com/jjyr/minidown" rel="nofollow" target="_blank"&gt;https://github.com/jjyr/minidown&lt;/a&gt;
比想像中的要麻烦不少..&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;可以完美解析&lt;a href="http://github.github.com/github-flavored-markdown/" rel="nofollow" target="_blank" title=""&gt;GFM 的 source 页&lt;/a&gt;！&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;另:
既然有人读就再增加了一些略详细的说明&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;大概思路是字符串传到Document类，然后split成行

这是markdown一个特点, 单行就可以进行解析，比如 `code` 等语法都是不允许换行的，即使是块结构的语法, 如```，也是可以从单行中判断开始与结束

之后`Document#parse_reference`会去解析一遍文档， 把markdown中的链接/图片定义替换

然后parse_line进行每行的解析

大概就是

while line = unparsed_lines.shift
    parsed_nodes &amp;lt;&amp;lt; (parse_line line)
end

parse_line中如果发现特殊结构
如: `&amp;gt;, *`等就由相应的类去解析(如`&amp;gt;`开头会由`BlockElement`类解析)

其余交给TextElement来解析文本内的语法，比如 *斜体*

....然后就是与各种正则的搏斗

之后对parse后的node调用to_html全部转换为HTML输出
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>jjym</author>
      <pubDate>Thu, 29 Aug 2013 19:32:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/13733</link>
      <guid>https://ruby-china.org/topics/13733</guid>
    </item>
    <item>
      <title>[Benchmark] Ruby 2.0 真的快了不少，还是我的 Python 代码有问题?</title>
      <description>&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ruby -v
ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux]

python3 --version
Python 3.3.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用传统的 fibonacci 来 benchmark&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#ruby&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'benchmark'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&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;2&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;fib&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;fib&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="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bm&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;)}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#python
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&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;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;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fib&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="nf"&gt;fib&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;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Timer&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;print(fib(35))&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from __main__ import fib&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#ruby

ruby fib.rb
       user     system      total        real
 14930352
  2.600000   0.000000   2.600000 (  2.601639)

#python3.2.2

fib.py 
14930352
9.720147652000378
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ruby 版本快了大概 3.7 倍
我使用 python2.7.5 版会稍微快些，大概 6.3 秒，当然还是比 ruby 慢的&lt;/p&gt;

&lt;p&gt;ruby2.0 真的快了不少，还是说我的 python 代码写的有问题？&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Mon, 22 Jul 2013 23:12:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/12688</link>
      <guid>https://ruby-china.org/topics/12688</guid>
    </item>
    <item>
      <title>请问有什么好用的微博推特客户端吗？</title>
      <description>&lt;p&gt;大家都用的什么&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Sun, 07 Jul 2013 20:31:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/12320</link>
      <guid>https://ruby-china.org/topics/12320</guid>
    </item>
    <item>
      <title>网站提供 atom feed 同时还需要 rss 吗？</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;Supporters of the ATOM format also needs to add support for RSS so that those who only have RSS support can also see the content, which is still the great majority of people who subscribe to web feeds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="http://www.differencebetween.net/technology/difference-between-rss-and-atom/#ixzz1RTwQ79rt" rel="nofollow" target="_blank"&gt;http://www.differencebetween.net/technology/difference-between-rss-and-atom/#ixzz1RTwQ79rt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;从这里看到的，大家做 rss 的时候都是提供两个吗？还是只 atom feed 或者只 rss feed?&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Fri, 24 May 2013 14:42:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/11211</link>
      <guid>https://ruby-china.org/topics/11211</guid>
    </item>
    <item>
      <title>哈哈哈哈，熬夜写了个半成品的 Lisp like interpreter</title>
      <description>&lt;p&gt;看了下 b 大发的&lt;a href="http://ruby-china.org/topics/11085" title=""&gt;参考&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;发现貌似没这么难，试着做了下&lt;/p&gt;

&lt;p&gt;虽然代码很烂，功能不完善 这也是自己写的第一个解释器啊
just for study
&lt;a href="https://github.com/jjyr/rblisp" rel="nofollow" target="_blank"&gt;https://github.com/jjyr/rblisp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;明天继续搞
怕挂了，先睡觉了&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;更新
已经比较完善了&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&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="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="s2"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s2"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#hello world&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>jjym</author>
      <pubDate>Sun, 19 May 2013 03:46:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/11088</link>
      <guid>https://ruby-china.org/topics/11088</guid>
    </item>
    <item>
      <title>ruby 的某个服务器 3 倍速了..</title>
      <description>&lt;p&gt;&lt;a href="https://groups.google.com/forum/?fromgroups=#!topic/celluloid-ruby/D-7fXYaBswU" rel="nofollow" target="_blank" title=""&gt;详见这个&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;另外&lt;a href="https://github.com/TechEmpower/FrameworkBenchmarks/issues/62" rel="nofollow" target="_blank" title=""&gt;作者还表示对某个 Benchmark 很感兴趣..&lt;/a&gt;&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Fri, 05 Apr 2013 15:02:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/9972</link>
      <guid>https://ruby-china.org/topics/9972</guid>
    </item>
    <item>
      <title>[已解决] ruby 调用 shell 时大括号不能展开</title>
      <description>&lt;p&gt;&lt;code&gt;mkdir {dir1,dir2}&lt;/code&gt;
这样写在 shell 是能展开的&lt;/p&gt;

&lt;p&gt;但是通过 ruby 调用只能得到文件夹{dir1,dir2}
`` exec system popen 好像都不能展开。。求解有啥好办法&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Thu, 29 Nov 2012 10:25:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/7181</link>
      <guid>https://ruby-china.org/topics/7181</guid>
    </item>
    <item>
      <title>[新手向] ActiveRecord::Base 与 ActiveRecord::Relation 关系及基本原理</title>
      <description>&lt;p&gt;之前在 github 上看 Rails 源码真的要疯了。。现在有 rubymine 这个神器。看源码方便多了。。&lt;/p&gt;

&lt;p&gt;正题：&lt;/p&gt;

&lt;p&gt;AR = ActiveRecord&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AR::Base 和 AR::Record 的关系&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在 AR::Base 上调用查询方法如 where,all,limit 等均会代理到 scoped 方法，而 scoped 方法大意就是会返回一个 AR::Relation 的实例，AR::Base 本身拥有一个 AR::Relation 实例，并且之后所有查询都在此 relation 上 clone 而来。所以我们在 Base 上调用的查询接口实际是 Relation 中的。&lt;/p&gt;

&lt;p&gt;补充：前几天看 Kaminari 源码，发现仅在 Base 上 include 了分页方法的模块，经人指点后发现这是因为 Relation 新建时会接受一个 Base 的引用，并且 Relation 的 method_missing 会调用该引用的方法。所以在 Base 上定义就 OK。&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;延迟执行很神奇吗？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;其实没什么神奇的，原理很简单（实现还是有困难的。。），每次把条件存下，然后真正查询的时候再调用&lt;/p&gt;

&lt;p&gt;比如&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;uniq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;relation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;clone&lt;/span&gt;
      &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniq_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="n"&gt;relation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;存下条件，返回 relation 这样就能进行链式调用了&lt;/p&gt;

&lt;p&gt;真正查询时在根据条件生成查询语句&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;什么时候查询？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;打开 rails c&lt;/p&gt;

&lt;p&gt;输入&lt;code&gt;Post.where "id &amp;gt; 5"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;生成&lt;code&gt;SELECT "posts".* FROM "posts" WHERE (id &amp;gt; 5)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;输入&lt;code&gt;Post.where("id &amp;gt; 5").first&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;生成&lt;code&gt;SELECT "posts".* FROM "posts" WHERE (id &amp;gt; 5) LIMIT 1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;where 做的应该只是保存查询条件才对。现在居然生成查询语句，这不科学！！&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt;

      &lt;span class="n"&gt;relation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;clone&lt;/span&gt;
      &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where_values&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;build_where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;relation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你没猜错！返回的的确是 relation，所以答案就在这个坑跌的 relation 中&lt;/p&gt;

&lt;p&gt;AR::Relation#inspect&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;inspect&lt;/span&gt;
      &lt;span class="nb"&gt;to_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 irb 每次执行都会输出返回值的 inspect，所以造成了 where 语句执行查询的错觉。&lt;/p&gt;

&lt;p&gt;在 Rails 中我们通常调用 each 来遍历，或者 first/last 来取值，这时查询才会发生！&lt;/p&gt;</description>
      <author>jjym</author>
      <pubDate>Tue, 21 Aug 2012 13:31:09 +0800</pubDate>
      <link>https://ruby-china.org/topics/5066</link>
      <guid>https://ruby-china.org/topics/5066</guid>
    </item>
  </channel>
</rss>
