<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>pathbox (tequila)</title>
    <link>https://ruby-china.org/pathbox</link>
    <description>Somebody always does set exercise with your maximum power</description>
    <language>en-us</language>
    <item>
      <title>Rails App 之间的三种"通讯"方式实践</title>
      <description>&lt;h3 id="三种方式"&gt;三种方式&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;API 接口通讯&lt;/li&gt;
&lt;li&gt;Sidekiq&lt;/li&gt;
&lt;li&gt;gRPC&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="Rails环境"&gt;Rails 环境&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Rails 5.1.4&lt;/li&gt;
&lt;li&gt;Ruby 2.3.3&lt;/li&gt;
&lt;li&gt;redis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two Rails App:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;jerry_app&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;listen: 127.0.0.1:9001&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;tom_app&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;listen: 127.0.0.1:9002&lt;/p&gt;

&lt;p&gt;定义一个请求方向：tom_app -&amp;gt; 发起请求 -&amp;gt; jerry_app&lt;/p&gt;
&lt;h5 id="文章Rails App完整的代码在：完整代码repo"&gt;文章 Rails App 完整的代码在：&lt;a href="https://github.com/pathbox/Rails_Communicate_Rails" rel="nofollow" target="_blank" title=""&gt;完整代码 repo&lt;/a&gt;
&lt;/h5&gt;&lt;h3 id="API接口通讯"&gt;API 接口通讯&lt;/h3&gt;
&lt;p&gt;简单、稳定的方式，没有"第三方的依赖"。&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;jerry_app&lt;/code&gt;定义一个对外接口，&lt;code&gt;get&lt;/code&gt;请求，url 是 &lt;code&gt;http://127.0.0.1/product&lt;/code&gt;，controller 的代码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# products_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:do_validation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: :index&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;code: &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="s2"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_validation&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:timestamp&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token&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;timestamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;code: &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="s2"&gt;"timestamp or token can't be blank"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;time_now&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;time_now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;time_now&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;code: &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="s2"&gt;"Timestamp is expired"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;APP_ID&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
    &lt;span class="n"&gt;hex_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HMAC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SHA1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;APP_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str&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;token&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;hex_token&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;code: &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="s2"&gt;"token is wrong"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;401&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;code&gt;tom_app&lt;/code&gt;定义一个方法，请求&lt;code&gt;jerry_app&lt;/code&gt;开放的接口：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tom_app/app/services/product_service.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductService&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_products&lt;/span&gt;
      &lt;span class="n"&gt;timestamp&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
      &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;APP_ID&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
      &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HMAC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SHA1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;APP_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"token=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;timestamp=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1:9001/products?&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# jerry_app listens 9001&lt;/span&gt;

      &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Typhoeus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Products result: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;body&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接口进行了参数验证和 token 方式的权限认证，保证一定的安全性。实际生产环境使用 https 会更好。&lt;/p&gt;

&lt;p&gt;鉴权的算法是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;APP_ID&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HMAC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SHA1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;APP_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后对 token 进行了过期验证：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;time_now&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;time_now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;time_now&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;code: &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="s2"&gt;"Timestamp is expired"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你还可以用 IP 白名单的方式，让接口只允许白名单的 IP 访问。&lt;/p&gt;
&lt;h3 id="使用 Sidekiq "&gt;使用 Sidekiq "通讯"&lt;/h3&gt;
&lt;p&gt;其实这是一种使用消息队列，进行异步调用的方式。并不是只有&lt;code&gt;Sidekiq&lt;/code&gt;能做到，一般的消息队列框架都可以实现，简单的使用 redis 也可以实现。&lt;/p&gt;

&lt;p&gt;在 Rails(Ruby) 环境，顺其自然的，就选择了&lt;code&gt;Sidekiq&lt;/code&gt;。分别在两个 Rails App 进行 Sidekiq 操作，代码示例：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tom_app/config/initializers/tom.rb&lt;/span&gt;

&lt;span class="n"&gt;jerry_redis_config&lt;/span&gt; &lt;span class="o"&gt;=&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;port: &lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;db: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;jerry_redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&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;jerry_redis_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;jerry_sidekiq_redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Namespace&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;:jerry_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;redis: &lt;/span&gt;&lt;span class="n"&gt;jerry_redis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;JERRY_REDIS_POOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ConnectionPool&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;timeout: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;jerry_sidekiq_redis&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面我们定义了一个全局的 &lt;code&gt;ConnectionPool&lt;/code&gt;实例对象&lt;code&gt;JERRY_REDIS_POOL&lt;/code&gt;。使用到的 redis 配置是我们要调用的&lt;code&gt;jerry_app&lt;/code&gt;的 redis 配置：&lt;code&gt;jerry_redis_config&lt;/code&gt;。
也就是&lt;code&gt;JERRY_REDIS_POOL&lt;/code&gt;是&lt;code&gt;jerry_app&lt;/code&gt;的&lt;code&gt;ConnectionPool&lt;/code&gt;实例对象。&lt;/p&gt;

&lt;p&gt;然后我们定义一个 Sidekiq Worker：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tom_app/app/workers/update_product_worker.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpdateProductWorker&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Worker&lt;/span&gt;
  &lt;span class="n"&gt;sidekiq_options&lt;/span&gt; &lt;span class="ss"&gt;:queue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:pool&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;JERRY_REDIS_POOL&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 sidekiq_options 中配置 pool 的值为 JERRY_REDIS_POOL。在这里不需要定义&lt;code&gt;perform&lt;/code&gt;方式。&lt;/p&gt;

&lt;p&gt;在你想要向&lt;code&gt;jerry_app&lt;/code&gt;发起调用时，调用&lt;code&gt;UpdateProductWorker&lt;/code&gt;就行。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tom_app/app/services/product_service.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductService&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="c1"&gt;# ......&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_product_worker&lt;/span&gt;
      &lt;span class="no"&gt;UpdateProductWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_async&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="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;code&gt;jerry_app&lt;/code&gt;也定义相同名称的&lt;code&gt;UpdateProductWorker&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# jerry_app/app/services/product_service.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpdateProductWorker&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Worker&lt;/span&gt;
  &lt;span class="n"&gt;sidekiq_options&lt;/span&gt; &lt;span class="ss"&gt;:queue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:default&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;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# do something for example:&lt;/span&gt;
    &lt;span class="c1"&gt;# product = Product.find(product_id)&lt;/span&gt;
    &lt;span class="c1"&gt;# product.update!(amount: 0)&lt;/span&gt;
    &lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"++++++product_id: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"UpdateProductWorker Success"&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;tom_app&lt;/code&gt;中打开&lt;code&gt;rails c&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ProductService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_product_worker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在&lt;code&gt;jerry_app&lt;/code&gt;中，&lt;code&gt;tailf sidekiq.log -n 20&lt;/code&gt;得到了：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="no"&gt;T05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;681&lt;/span&gt;&lt;span class="no"&gt;Z&lt;/span&gt; &lt;span class="mi"&gt;28855&lt;/span&gt; &lt;span class="no"&gt;TID&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hooyg&lt;/span&gt; &lt;span class="no"&gt;UpdateProductWorker&lt;/span&gt; &lt;span class="no"&gt;JID&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2714364&lt;/span&gt;&lt;span class="n"&gt;eff0c2d6714d38221&lt;/span&gt; &lt;span class="no"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
&lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="no"&gt;T05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;683&lt;/span&gt;&lt;span class="no"&gt;Z&lt;/span&gt; &lt;span class="mi"&gt;28855&lt;/span&gt; &lt;span class="no"&gt;TID&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hooyg&lt;/span&gt; &lt;span class="no"&gt;UpdateProductWorker&lt;/span&gt; &lt;span class="no"&gt;JID&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2714364&lt;/span&gt;&lt;span class="n"&gt;eff0c2d6714d38221&lt;/span&gt; &lt;span class="no"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;++++++&lt;/span&gt;&lt;span class="ss"&gt;product_id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="no"&gt;T05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;683&lt;/span&gt;&lt;span class="no"&gt;Z&lt;/span&gt; &lt;span class="mi"&gt;28855&lt;/span&gt; &lt;span class="no"&gt;TID&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hooyg&lt;/span&gt; &lt;span class="no"&gt;UpdateProductWorker&lt;/span&gt; &lt;span class="no"&gt;JID&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2714364&lt;/span&gt;&lt;span class="n"&gt;eff0c2d6714d38221&lt;/span&gt; &lt;span class="no"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UpdateProductWorker&lt;/span&gt; &lt;span class="no"&gt;Success&lt;/span&gt;
&lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="no"&gt;T05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;683&lt;/span&gt;&lt;span class="no"&gt;Z&lt;/span&gt; &lt;span class="mi"&gt;28855&lt;/span&gt; &lt;span class="no"&gt;TID&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hooyg&lt;/span&gt; &lt;span class="no"&gt;UpdateProductWorker&lt;/span&gt; &lt;span class="no"&gt;JID&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2714364&lt;/span&gt;&lt;span class="n"&gt;eff0c2d6714d38221&lt;/span&gt; &lt;span class="no"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;done: &lt;/span&gt;&lt;span class="mf"&gt;0.002&lt;/span&gt; &lt;span class="n"&gt;sec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原理：tom_app 是"消费者"，jerry_app 是"生产者"。借助 Sidekiq，"消费者"得到参数并执行具体的逻辑。显然，这是一种异步请求调用。如果你需要同步的请求调用，这种方式就不合适了。&lt;/p&gt;
&lt;h3 id="gRPC in Ruby on Rails"&gt;gRPC in Ruby on Rails&lt;/h3&gt;
&lt;p&gt;如果不了解 gRPC，可以阅读这里&lt;a href="https://grpc.io/" rel="nofollow" target="_blank" title=""&gt;gRPC 官网&lt;/a&gt;，是一种 rpc 的方式。我在之前 Go 的微服务项目中，服务之间使用了 gRPC 进行通讯。
gRPC 对 ruby 也有支持，&lt;a href="https://github.com/grpc/grpc/tree/master/examples/ruby" rel="nofollow" target="_blank" title=""&gt;gRPC ruby example&lt;/a&gt;。阅读了它的例子和文档，觉得实现方式和 Rails 并没有融合的很好。
然后在 github 上搜了一下，找到了这个 gem: &lt;a href="https://github.com/bigcommerce/gruf" rel="nofollow" target="_blank" title=""&gt;gruf&lt;/a&gt;。它是将 gRPC ruby 进行了一定的封装。于是我就选择了使用它。不过我发现，&lt;code&gt;gruf&lt;/code&gt;的&lt;code&gt;README&lt;/code&gt;
在一些细节上不够完整，有些坑，我按照 README 的操作，没有成功。直到把他们的 demo 的代码看完，和 demo 中 rake 测试命令的代码看完，才明白了所有细节。我修改了 README，并给他们提了 push。
如果你在本地没法跑通例子，也许你需要安装 gRPC 和 protocol buffers。&lt;/p&gt;

&lt;p&gt;看具体的代码例子，主要的代码逻辑在&lt;code&gt;app/rpc&lt;/code&gt;目录下：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tom_app&lt;/code&gt;中&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tom_app/config/initializers/gruf.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'gruf'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'app/proto/helloworld_services_pb'&lt;/span&gt;

&lt;span class="no"&gt;Gruf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&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;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_client_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1:9003'&lt;/span&gt; &lt;span class="c1"&gt;# 在这里默认配置了host，如果不配置，则需要在每次调用的时候传host值&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 在 tom_app/app/rpc/app/ 执行 grpc_tools_ruby_protoc -Iproto --ruby_out=proto --grpc_out=proto proto/helloworld.proto 会生成　helloworld_pb.rb和helloworld_services_pb.rb文件。这样有一个问题，就是helloworld_pb 文件的加载问题，应该和加载路径有关。我就手动修改了 helloworld_services_pb.rb文件&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'app/proto/helloworld_pb'&lt;/span&gt; &lt;span class="c1"&gt;# 这样就正确加载了helloworld_pb.rb了&lt;/span&gt;

&lt;span class="c1"&gt;# 这里不列出来了，具体的client端和server端，就是使用helloworld_pb.rb和helloworld_services_pb.rb文件中的类和方法&lt;/span&gt;

&lt;span class="c1"&gt;# tom_app/app/rpc/greeter_client.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GreeterClient&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&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;"say_hello: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;# hostname: '127.0.0.1:9003',&lt;/span&gt;
      &lt;span class="ss"&gt;username: &lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Gruf&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&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;service: &lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helloworld&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helloworld&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;options: &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:SayHello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&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;"+"&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt; &lt;span class="c1"&gt;# Helloworld::HelloReply instance&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Gruf&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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;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;tom_app&lt;/code&gt;中&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tom_app/config/initializers/gruf.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'gruf'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'app/proto/helloworld_services_pb'&lt;/span&gt;

&lt;span class="no"&gt;Gruf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&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;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_binding_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1:9003'&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Gruf&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Interceptors&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Instrumentation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RequestLogging&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Interceptor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;formatter: :logstash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# basic auth&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="no"&gt;Gruf&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Interceptors&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Authentication&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Basic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;credentials: &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="ss"&gt;username: &lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},{&lt;/span&gt;
      &lt;span class="ss"&gt;username: &lt;/span&gt;&lt;span class="s1"&gt;'another-username'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="s1"&gt;'another-password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},{&lt;/span&gt;
      &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="s1"&gt;'a-password-only'&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="c1"&gt;# helloworld.proto&lt;/span&gt;
&lt;span class="c1"&gt;# helloworld_pb.rb&lt;/span&gt;
&lt;span class="c1"&gt;# helloworld_services_pb.rb&lt;/span&gt;
&lt;span class="c1"&gt;# 这三个文件和　tom_app一样&lt;/span&gt;

&lt;span class="c1"&gt;# jerry_app/app/rpc/greeter_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GreeterController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Gruf&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controllers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;bind&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helloworld&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helloworld&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Service&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_hello&lt;/span&gt;
    &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"+++ Hello &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;+++"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="no"&gt;Helloworld&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HelloReply&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;message: &lt;/span&gt;&lt;span class="n"&gt;result&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;jerry_app&lt;/code&gt;根目录执行&lt;code&gt;bundle exec gruf&lt;/code&gt;,你可以看到&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="no"&gt;T15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;46.719862&lt;/span&gt; &lt;span class="c1"&gt;#31505]  INFO -- : handling /helloworld.Greeter/SayHello with #&amp;lt;Method: Helloworld::Hello::Service#say_hello&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在&lt;code&gt;tom_app&lt;/code&gt; rails c 中执行&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;001&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;GreeterClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Tom"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;say_hello: &lt;/span&gt;&lt;span class="no"&gt;Tom&lt;/span&gt;
&lt;span class="no"&gt;D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="no"&gt;T15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;15.095475&lt;/span&gt; &lt;span class="c1"&gt;#32363] DEBUG -- : calling 127.0.0.1:9003:/helloworld.Greeter/SayHello&lt;/span&gt;
&lt;span class="o"&gt;++++++++++++++++++++++++++++++&lt;/span&gt;
&lt;span class="o"&gt;+++&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt; &lt;span class="no"&gt;Tom&lt;/span&gt;&lt;span class="o"&gt;+++&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在&lt;code&gt;jerry_app　gruf&lt;/code&gt; 服务，你可以看到这样的日志：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="o"&gt;+++&lt;/span&gt; &lt;span class="no"&gt;Hello&lt;/span&gt; &lt;span class="no"&gt;Tom&lt;/span&gt;&lt;span class="o"&gt;+++&lt;/span&gt;
&lt;span class="no"&gt;I&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="no"&gt;T15&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;span class="mf"&gt;49.945836&lt;/span&gt; &lt;span class="c1"&gt;#31505]  INFO -- : {"message":"[GRPC::Ok] (helloworld.hello.say_hello)","status":0,"service":"helloworld.hello","method":"say_hello","action":"say_hello","grpc_status":"GRPC::Ok","duration":0.74,"thread_id":40308860,"time":"2018-01-05 15:35:49 +0800","host":"20161125","format":"json"}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;整个过程：将 gRPC client 端实现为一个类，定义调度的类方法或实例方法，使用了账号密码的 basic auth；在 gRPC server 端具体实现要调度的方法代码逻辑。
你可以查阅更多&lt;code&gt;gruf&lt;/code&gt;的内容，它实现了其他一些中间件 (interceptor),让你更好的使用 gRPC。你可以选择用&lt;a href="https://github.com/ddollar/foreman" rel="nofollow" target="_blank" title=""&gt;foreman&lt;/a&gt;来控制&lt;code&gt;gruf&lt;/code&gt;服务的启动。&lt;/p&gt;
&lt;h3 id="其他"&gt;其他&lt;/h3&gt;
&lt;p&gt;我用 go-grpc 的作为 client，调用&lt;code&gt;jerry_app&lt;/code&gt;的&lt;code&gt;gruf&lt;/code&gt;服务，结果报了错。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;W&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="no"&gt;T15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;22.046980&lt;/span&gt; &lt;span class="c1"&gt;#31505]  WARN -- : UNIMPLEMENTED: #&amp;lt;struct Struct::NewServerRpc method="/proto.Hello/SayHello", host="127.0.0.1:9003", deadline=1970-01-01 07:59:59 +0800, metadata={"user-agent"=&amp;gt;"grpc-go/1.8.0-dev"}, call=#&amp;lt;GRPC::Core::Call:0x000000045a7e50&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关于跨语言的 rpc 调用，我还未尝试。&lt;a href="https://github.com/apache/thrift" rel="nofollow" target="_blank" title=""&gt;thrift&lt;/a&gt;应该会是一个不错的选择。&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Sat, 06 Jan 2018 16:35:20 +0800</pubDate>
      <link>https://ruby-china.org/topics/34842</link>
      <guid>https://ruby-china.org/topics/34842</guid>
    </item>
    <item>
      <title>Raft 算法动画演示</title>
      <description>&lt;p&gt;今天发现了有人做了这个 Raft 算法演示。做的挺好的，动画简洁，台词通俗易懂。对分布式算法感兴趣的同学可以了解&lt;/p&gt;

&lt;p&gt;&lt;a href="http://thesecretlivesofdata.com/raft/" rel="nofollow" target="_blank" title=""&gt;演示地址&lt;/a&gt;&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Thu, 14 Dec 2017 17:54:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/34712</link>
      <guid>https://ruby-china.org/topics/34712</guid>
    </item>
    <item>
      <title>简记 Rails 中的 logger 实用技巧</title>
      <description>&lt;h5 id="Gem lograge"&gt;Gem lograge&lt;/h5&gt;
&lt;p&gt;&lt;a href="https://github.com/roidrage/lograge" rel="nofollow" target="_blank" title=""&gt;lograge&lt;/a&gt; is awesome for formating log.&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# Gemfile&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"lograge"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/lograge.rb&lt;/span&gt;
&lt;span class="c1"&gt;# OR&lt;/span&gt;
&lt;span class="c1"&gt;# config/environments/production.rb&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;# Logger&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ignore_actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'home#welcome'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;custom_options&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;event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&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;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:params&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;reject&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;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'controller'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'action'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# add params to lograge&lt;/span&gt;
      &lt;span class="ss"&gt;time:   &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;time&lt;/span&gt; &lt;span class="c1"&gt;# add time to lograge&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;lograge&lt;/code&gt; 能帮你格式化日志输出，定义日志输出的格式和内容。去掉了 view 层渲染的日志内容。如果你不需要日志具体打印 view 层的渲染内容，用&lt;code&gt;lograge&lt;/code&gt;是极好的，如果你需要，&lt;code&gt;lograge&lt;/code&gt; 就不适合你。一般应用不关心 view 层渲染的具体日志内容。&lt;/p&gt;

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

&lt;p&gt;环境：Rails 5.1.4&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lograge&lt;/code&gt;还提供了不同的 formatters。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lograge::Formatters::Lines.new
Lograge::Formatters::Cee.new
Lograge::Formatters::Graylog2.new
Lograge::Formatters::KeyValue.new  # default lograge format
Lograge::Formatters::Json.new
Lograge::Formatters::Logstash.new
Lograge::Formatters::LTSV.new
Lograge::Formatters::Raw.new       # Returns a ruby hash object
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认的日志格式是 key=value 的格式：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2017&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;INFO&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;localhost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;127.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1593754&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bc95&lt;/span&gt;&lt;span class="o"&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;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;GET&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/ format=*/&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;HomeController&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;welcome&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;472.00&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.00&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2017&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;0800&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如，配置 Lograge::Formatters::Json.new&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lograge&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Formatters&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;new&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="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2017&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;INFO&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;localhost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;127.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;74&lt;/span&gt;&lt;span class="n"&gt;cc1bef&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2155&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"method"&lt;/span&gt;&lt;span class="ss"&gt;:"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;&lt;span class="ss"&gt;:"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"format"&lt;/span&gt;&lt;span class="ss"&gt;:"*/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"controller"&lt;/span&gt;&lt;span class="ss"&gt;:"HomeController"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"action"&lt;/span&gt;&lt;span class="ss"&gt;:"welcome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;3.09&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="s2"&gt;"home"&lt;/span&gt;&lt;span class="p"&gt;:{}},&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;&lt;span class="ss"&gt;:"2017-12-01 22:43:34 +0800"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lograge&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Formatters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Graylog2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&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="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2017&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;INFO&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;localhost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;127.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;18654&lt;/span&gt;&lt;span class="n"&gt;bb8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:_method&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_path&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_format&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_controller&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"HomeController"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_action&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"welcome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_status&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_duration&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;368.34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_view&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_params&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"home"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{}},&lt;/span&gt; &lt;span class="ss"&gt;:_time&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2017&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;0800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:short_message&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"[200] GET / (HomeController#welcome)"&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="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lograge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lograge&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Formatters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Logstash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要配合安装 &lt;code&gt;logstash-event&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"logstash-event"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样很方便的能够得到你想要的日志内容结构，并且被各种日志监控系统兼容。这是&lt;code&gt;lograge&lt;/code&gt;简单的功能，更多配置查看&lt;a href="https://github.com/roidrage/lograge" rel="nofollow" target="_blank" title=""&gt;文档&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="自定义生成log文件"&gt;自定义生成 log 文件&lt;/h5&gt;
&lt;p&gt;有时候，我们想要将日志存储到一个新的 log 文件中，和主日志文件区分开来。&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;create_my_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;logger&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/log/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;file_name&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="c1"&gt;# 根据file_name，创建一个logger实例。会在Rails.root/log目录下生成file_name文件，用来记录日志&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DEBUG&lt;/span&gt;  &lt;span class="c1"&gt;# 设置日志的level&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&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;severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;progname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="c1"&gt;# 设置日志的formatter&lt;/span&gt;
    &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;datetime&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="ss"&gt;:db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] [&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;severity&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;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;logger&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在想要使用该日志实例的代码中：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_my_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"third_api_service.log"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# do something&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_third_api_service&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Third api service success"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Third api service fail: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;result&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;个人认为这种方法适用于调用一些关键服务或操作，想要快速了解该服务的正确性，当有错误的时候，可以快速查阅日志，了解服务信息。也便于调试和快速排查问题&lt;/p&gt;

&lt;p&gt;还可以给 logger 实例扩展一些方法：&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;MyLogger&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_json&lt;/span&gt;
    &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;yield&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;info&lt;/span&gt;

    &lt;span class="n"&gt;timestamp&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%Y%m%d_%H%M%S_%L'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;timestamp&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;flatten&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&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;merge!&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&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="c1"&gt;# 打印json日志内容&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_my_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;logger&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/log/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;file_name&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="c1"&gt;# 根据file_name，创建一个logger实例。会在Rails.root/log目录下生成file_name文件，用来记录日志&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DEBUG&lt;/span&gt;  &lt;span class="c1"&gt;# 设置日志的level&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&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;severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;progname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="c1"&gt;# 设置日志的formatter&lt;/span&gt;
    &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;datetime&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="ss"&gt;:db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] [&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;severity&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;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;MyLogger&lt;/span&gt;

  &lt;span class="n"&gt;logger&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用示例：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;SYNC_LOGGER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_my_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sync.log'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;SYNC_LOGGER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_json&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;info&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'user_id:1'&lt;/span&gt;
  &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:action&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'sync success'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成 sync.log 文件会打印以下信息：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2017&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="ss"&gt;:"20171202_141419_311-user_id:1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"action"&lt;/span&gt;&lt;span class="ss"&gt;:"sync success"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="为调用外部服务增加必要日志"&gt;为调用外部服务增加必要日志&lt;/h5&gt;
&lt;p&gt;当系统中有需要 http client 形式调用外部服务时，比如：微信开发的微信接口，推送接口，第三方开放 API，我会这么做：&lt;/p&gt;

&lt;p&gt;使用示例：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;API_LOGGER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_my_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'xxx_api.log'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;API_LOGGER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_json&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;info&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'xxx:api:user_id:1'&lt;/span&gt;
    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:resquest&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request_params&lt;/span&gt;
    &lt;span class="n"&gt;start&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="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;do&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="n"&gt;request_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;duration&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="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:response&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:duration&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;
    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:res&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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;将&lt;code&gt;请求参数&lt;/code&gt;、&lt;code&gt;返回响应&lt;/code&gt;、&lt;code&gt;报错信息&lt;/code&gt;都记录下来，查看日志时一目了然。出问题时，能帮助你很快的定位出是系统中的 bug 还是外部服务的 bug。&lt;/p&gt;

&lt;p&gt;证明"代码的清白"，就靠他们了。&lt;/p&gt;

&lt;p&gt;一个好的日志设计和使用习惯，是一个系统稳定、健壮和可控的重要保证。&lt;/p&gt;

&lt;p&gt;本文简单小结了在 Rails 项目中日志的使用。&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Sat, 02 Dec 2017 15:26:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/34663</link>
      <guid>https://ruby-china.org/topics/34663</guid>
    </item>
    <item>
      <title>为 Rails 项目升级使用 ElasticSearch 5.x 版本</title>
      <description>&lt;p&gt;最近为项目系统中使用的 Elasticsearch 进行了升级。从原来 1.4 版本的 Elasticsearch 升级到 5.x 版本。&lt;/p&gt;

&lt;p&gt;由于 5.x 版本的 Elasticsearch 不再支持 1.4 版本中的很多查询语法，所以不可避免的进行了查询层的重构。
每次 Elasticsearch 有重大版本升级的时候，会在官方文档中列出相应的改变，你需要认真阅读的&lt;code&gt;Breaking changes&lt;/code&gt;内容。&lt;/p&gt;

&lt;p&gt;最重要的事情是，终于可以利用这次机会，为 Elasticsearch 加入了索引别名机制&lt;/p&gt;
&lt;h5 id="安装"&gt;安装&lt;/h5&gt;
&lt;p&gt;Elasticsearch-5.x server 版本的安装可以&lt;a href="http://pathbox.github.io/work/2017/09/13/elasticsearch-5.5.2-install-and-config.html" rel="nofollow" target="_blank" title=""&gt;参考之前的博文&lt;/a&gt;。我是安装在 Ubuntu16.04 上，Ubuntu14.04 也是可以的。&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;Gemfile&lt;/code&gt;中增加&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"elasticsearch"&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"elasticsearch-model"&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"elasticsearch-rails"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用最新的 elasticsearch gem 包，至少大于 5.0 版本&lt;/p&gt;
&lt;h5 id="加入索引别名机制"&gt;加入索引别名机制&lt;/h5&gt;
&lt;p&gt;为什么说别名机制是最重要的事情呢？我们知道 Elasticsearch 的索引，当你配置并建立好之后，是无法进行更新修改的。
想要进行更新修改，需要删除原来的整个索引，然后再根据新的配置，重建索引，包括索引的文档数据。这就不可避免了该索引在重建过程中
有一段时间是不可用的。&lt;/p&gt;

&lt;p&gt;举个简单的例子：Elasticsearch 有一个 orders 索引，大概有 1000w 条记录数据。你的客户会经常使用查询 orders 的功能，这个功能查的就是 orders 索引。
现在你要修改 orders 索引的分词器。这时你需要重建 orders 索引 (删除再新建)，假设这个过程，让 orders 索引完全恢复到可用状态需要 10 分钟。那么，你的客户就会
有 10 分钟没法使用 orders 的搜索功能。当然，你可以在凌晨 3 点进行这个操作。这里只是举个例子。Elasticsearch 索引别名机制可以帮助你进行索引&lt;code&gt;无缝&lt;/code&gt;的升级或更新。&lt;/p&gt;
&lt;h5 id="别名机制的过程及原理"&gt;别名机制的过程及原理&lt;/h5&gt;
&lt;p&gt;关键的操作是：取别名。现在我要重构 orders 这个索引，我会先创建一个叫做 orders_v1 的索引，为其配置名称为 orders 的别名。
这样，在查询代码层，你就可以使用类似&lt;code&gt;Order.__elasticsearch__.search()&lt;/code&gt;这样的查询了。其实，也就是 在代码层面上，不知道有 orders_v1 这个索引额存在，
一切对 Elasticsearch 的操作，都通过别名 orders 操作。&lt;/p&gt;

&lt;p&gt;现在，我们需要修改 orders_v1 索引，只要简单的三步：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. 用新的索引配置新建一个叫 orders_v2 的索引，将文档数据也建立好&lt;/li&gt;
&lt;li&gt;2. 进行切换别名的操作，将 orders 别名由原来 order_v1 指向 orders_v2&lt;/li&gt;
&lt;li&gt;3. 看你的需要，再进行增量变更数据的更新&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;切换别名的操作非常快，并且你的代码层的修改会变得很轻松。几乎不会对用户的使用造成影响。&lt;/p&gt;

&lt;p&gt;简单的图表示：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/2184e3db-a600-4752-95ac-6a44ae8dd777.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在项目中，实现别名机制不难。详见&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html" rel="nofollow" target="_blank" title=""&gt;官方文档的例子&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;新建索引别名&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /_aliases
{
    "actions" : [
        { "add" : { "index" : "order_v1", "alias" : "orders" } }
    ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;切换索引别名&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /_aliases
{
    "actions" : [
        { "remove" : { "index" : "order_v1", "alias" : "orders" } },
        { "add" : { "index" : "order_v2", "alias" : "orders" } }
    ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 rails 项目中，我通过 rake 脚本实现，下面是我写的一个 rake 脚本，在新建或重建索引的时候使用，仅供参考。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#coding: utf-8&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Elasticsearch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&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;host: &lt;/span&gt;&lt;span class="n"&gt;elasticsearch_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;retry_on_failure: &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;log: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;transport_options: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;request: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;10&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="c1"&gt;# ++++++++++++++++++++++++++++++++++使用方式++++++++++++++++++++++++++++++++++++++&lt;/span&gt;

&lt;span class="c1"&gt;#1 创建索引 　　 # bundle exec rake es_action:create_index table=Order index_name=orders_v1&lt;/span&gt;
&lt;span class="c1"&gt;#2 索引别名配置　#　bundle exec rake es_action:update_index_alias alias=orders new_index=orders_v1 old_index=orders_v2(可选，交换索引的时候使用)&lt;/span&gt;
&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:es_action&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"重建es索引,根据传入的参数指定重置的索引"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;create_index: :environment&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'table'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;index_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'index_name'&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;"++++++ start remapping index &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"table 和 index_name 参数不能为空"&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;camelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constantize&lt;/span&gt;

    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__elasticsearch__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_index!&lt;/span&gt; &lt;span class="ss"&gt;index: &lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# ++++++创建索引&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__elasticsearch__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh_index!&lt;/span&gt; &lt;span class="ss"&gt;index: &lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"新建或交换es的别名（将别名由旧的索引指向新的索引）"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;update_index_alias: :environment&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;old_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'old_index'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;new_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'new_index'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;alias_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'alias'&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;"++++++ start update_index_alias &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;alias_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;alias_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"++++++ alias new_index 参数不能为空"&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;actions: &lt;/span&gt;&lt;span class="p"&gt;[]}&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:actions&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;remove: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;index: &lt;/span&gt;&lt;span class="n"&gt;old_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;alias: &lt;/span&gt;&lt;span class="n"&gt;alias_name&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;old_index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="c1"&gt;# old_index存在，表示是进行别名切换&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:actions&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;add: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;index: &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;alias: &lt;/span&gt;&lt;span class="n"&gt;alias_name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_aliases&lt;/span&gt; &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;

    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"++++++ update_index_alias success"&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;h5 id="mapping的配置"&gt;mapping 的配置&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;app/models/order.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;1.4 版本中的 mapping 配置&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'multi_field'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;index: :not_analyzed&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:tokenized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;analyzer: :ik_smart&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :integer&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :long&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :date&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :date&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :integer&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;index: :not_analyzed&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5.x 版本中的 mapping 配置&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :keyword&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :keyword&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:tokenized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;analyzer: :ik_smart&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :text&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :integer&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :long&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :date&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :date&lt;/span&gt;
&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :integer&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :keyword&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;5.x 版本没有了 string 类型，拆分成了 keyword 和 text。如果你指定了 analyzer，则是 text 类型；如果指定了 text 类型但是没有指定 analyzer，则会使用默认的 analyzer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;不再有 type: 'multi_field'，这个好像是 2.x 版本就废弃了&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;5.x 的 IK 也分成了两种配置。 &lt;code&gt;ik_smart&lt;/code&gt;和&lt;code&gt;ik_max_word&lt;/code&gt;， &lt;code&gt;ik_max_word&lt;/code&gt;拥有更细的分词粒度。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="查询语句的重构"&gt;查询语句的重构&lt;/h5&gt;
&lt;p&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.0/breaking_50_search_changes.html" rel="nofollow" target="_blank" title=""&gt;官方文档的 query changes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;修改例子：&lt;/p&gt;
&lt;h5 id="不再使用filtered"&gt;不再使用 filtered&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;filtered: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;filter: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;bool: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;must: &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;or: &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;terms: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}}]},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;term: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;order_id: &lt;/span&gt;&lt;span class="n"&gt;order_id&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="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 重构为：&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;bool: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;filter: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;bool: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;term: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;order_id: &lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
       &lt;span class="p"&gt;],&lt;/span&gt;
       &lt;span class="ss"&gt;should: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;terms: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;user_id&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="p"&gt;}&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;h5 id="不再使用or"&gt;不再使用 or&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:must&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;or: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;term: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;order_id: &lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;term: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;user_id&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="c1"&gt;#重构为:&lt;/span&gt;
&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:must&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;bool: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;should: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;term: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;order_id: &lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;term: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;user_id&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="不再使用missing"&gt;不再使用 missing&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;missing: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;field: &lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="c1"&gt;#重构为：&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="s2"&gt;"must_not"&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="s2"&gt;"exists"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
           &lt;span class="s2"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;field_name&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="execution: 'and'不再使用"&gt;execution: 'and'不再使用&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;terms: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;execution: &lt;/span&gt;&lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="c1"&gt;#重构为：&lt;/span&gt;
&lt;span class="n"&gt;terms_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;term: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;bool: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;must: &lt;/span&gt;&lt;span class="n"&gt;terms_array&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Elasticsearch 的功能还是很强大的，这次 Elasticsearch 升级没有使用自定义的 Routing 的机制，一个是任务时间考虑，另一个是现在的服务负载还能撑一段时间，并不急迫需求这样的优化。现在有了索引别名，以后就可以放心的考虑优化进程了～&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Wed, 01 Nov 2017 15:47:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/34486</link>
      <guid>https://ruby-china.org/topics/34486</guid>
    </item>
    <item>
      <title>注意使用 inject 和 reduce</title>
      <description>&lt;p&gt;最近在使用 inject 的时候遇到了一个问题。我的代码例子是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果报错：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;NoMethodError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`&amp;lt;&amp;lt;' for nil:NilClass
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一开始很奇怪，在 inject([]) 的时候，我给 sum 赋值初始值为数组了，报错信息怎么会报 sum 是一个 nil:NilClass 呢？&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;test_inject&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="mi"&gt;2&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pry&lt;/span&gt;
      &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&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;发现确实 sum 是 nil。想到了可能是 if i &amp;gt; 1 这个条件判断的影响。修改一下测试代码&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;test_inject_0&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="mi"&gt;2&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&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;def&lt;/span&gt; &lt;span class="nf"&gt;test_inject_3&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;inject&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;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pry&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
      &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&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;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;test_inject_0&lt;/span&gt;  &lt;span class="c1"&gt;# [1, 2, 3, 4, 5, 6]&lt;/span&gt;

&lt;span class="n"&gt;test_inject_3&lt;/span&gt;  
&lt;span class="n"&gt;sum&lt;/span&gt;
&lt;span class="c1"&gt;# []&lt;/span&gt;
&lt;span class="c1"&gt;# [4]&lt;/span&gt;
&lt;span class="c1"&gt;# [4,5]&lt;/span&gt;
&lt;span class="c1"&gt;# [4,5,6]&lt;/span&gt;
&lt;span class="c1"&gt;# NoMethodError: undefined method `&amp;lt;&amp;lt;' for nil:NilClass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根据 test_inject_3，当 i 的值不满足 if i &amp;gt; 3 的条件的时候，sum 的值就被置为 nil 了。&lt;/p&gt;

&lt;p&gt;如果你需要在 inject 的 block 中进行对操作元素的条件过滤操作，你会将 sum 变为 nil。&lt;/p&gt;

&lt;p&gt;所以建议不在 inject 的 block 中进行条件过滤操作，而是在 inject 方法之前过滤好。&lt;/p&gt;

&lt;p&gt;同理 reduce 方法。&lt;/p&gt;

&lt;p&gt;Ruby 版本：ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux]&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Thu, 24 Aug 2017 14:43:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/33921</link>
      <guid>https://ruby-china.org/topics/33921</guid>
    </item>
    <item>
      <title>升级 Elasticsearch 集群数量实战记录</title>
      <description>&lt;p&gt;现在线上有一个 elasticsearch 集群搜索服务有三台 elasticsearch 实例（es1、es2、es3），打算将其升级为 5 台（增加 es4、es5）。这篇文章主要是对整个操作的过程记录，以及出现的问题总结，包括移动数据量所需要的时间。因为，一开始由于不知道线上数据量全部分配完需要多少时间，如果从凌晨开始操作，到早上 8 点都还没有同步完，这样会影响到白天线上业务的正常使用。&lt;/p&gt;
&lt;h4 id="准备阶段"&gt;准备阶段&lt;/h4&gt;
&lt;p&gt;线上 es 集群使用的是阿里云服务器，copy 其中一个镜像。然后更改其 elasticsearch.yml 配置文件，检查 IK 插件是否安装成功。按照这个流程，准备两台新的服务器放入阿里云的隔离组，并安装好 elasticsearch，测试 elasticsearch 实例可以正确启动。也做了将这两台服务器构建一个集群的测试。开始升级操作前 30 分钟，再次检查 elasticsearch.yml 配置。主要的修改是：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;discovery.zen.minimum_master_nodes:3
discovery.zen.ping.unicast.hosts: ["es1_ip", "es2_ip","es3_ip","es4_ip","es5_ip"]
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="升级操作"&gt;升级操作&lt;/h4&gt;
&lt;p&gt;关闭 es 集群 shard 分配功能。对 es1 执行：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -XPUT es1_ip:9200/_cluster/settings -d '{
  "transient": {
   "cluster.routing.allocation.enable": "none"
   }
}'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后检查：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl es1_ip:9200/_cluster/settings
curl es2_ip:9200/_cluster/settings
curl es3_ip:9200/_cluster/settings
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到的结果是：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"transient":{"cluster":{"routing":{"allocation":{"enable":"none"}}}}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;说明 es 集群已经关闭 shard 分配功能&lt;/p&gt;
&lt;h4 id="关闭es1、es2、es3上的monit"&gt;关闭 es1、es2、es3 上的 monit&lt;/h4&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo service monit stop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;手动控制 elasticsearch 进程的启动，避免 monit 自动拉起 elasticsearch 进程导致意外问题&lt;/p&gt;
&lt;h4 id="这时，新的两台服务器es4、es5还在隔离组。把隔离取消，然后启动这两台服务器的elasticsearch实例"&gt;这时，新的两台服务器 es4、es5 还在隔离组。把隔离取消，然后启动这两台服务器的 elasticsearch 实例&lt;/h4&gt;
&lt;p&gt;使用目录下面写好的启动脚本，这个脚本可以在启动时，为 elasticsearch 获取所设定的内存 (控制分配给 es 进程的最大内存、最小内存)，JVM 的设置等&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./elasticsearch.sh start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl es1_ip:9200/_cat/nodes
curl es1_ip:9200/_cat/shards
curl es1_ip:9200/_cat/health
curl es1_ip:9200/_cat/indices
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证 nodes 节点信息是否变为五个，以及 shard 现在的分布情况，应该只分布在 es1、es2、es3 上。es4 和 es5 还没有 shard&lt;/p&gt;

&lt;p&gt;然后记录下当前的 indices 信息&lt;/p&gt;

&lt;p&gt;例子：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;health status index    pri rep docs.count docs.deleted store.size pri.store.size
green  open   users     5   1   15036978      4262221     13.2gb          6.6gb
green  open   posts     5   1   15036978      4262221     13.2gb          6.6gb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以看到索引的健康度、状态、文档数量、数据大小和主分片数量、副分片的倍数设置。记录下这些信息，在最后升级完的时候
进行比对，看是否有偏差。如果一切正常，这些信息是不会变的。&lt;/p&gt;
&lt;h4 id="启动es集群shard分配功能"&gt;启动 es 集群 shard 分配功能&lt;/h4&gt;
&lt;p&gt;这是集群操作，所以只要对其中一台 es 实例操作即可&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -XPUT es1_ip:9200/_cluster/settings -d '{
  "transient": {
   "cluster.routing.allocation.enable": "all"
   }
}'
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="shard分配开始"&gt;shard 分配开始&lt;/h4&gt;
&lt;p&gt;执行：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl es1_ip:9200/_cat/shards

curl es1_ip:9200/_cat/health

curl es1_ip:9200/_cat/indices

curl es1_ip:9200/_cat/recovery
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;观察 shards 分布情况，是否向 es4 和 es5 分配 shards。以及分配的百分比进度&lt;/li&gt;
&lt;li&gt;监控 es4 和 es5 服务器的 elasticsearch 的 log&lt;/li&gt;
&lt;li&gt;登入 es4 和 es5，查看挂载的 SSD 数据盘数据量是否在增长&lt;/li&gt;
&lt;li&gt;测试进行 es 搜索的测试，快速搜索，工单筛选，客户筛选，观察对线上业务的影响&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这段时间，主要使用：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl es1_ip:9200/_cat/shards

curl es1_ip:9200/_cat/health

curl es1_ip:9200/_cat/recovery

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;观测 shard 分配进度和情况，以及线上系统健康度。&lt;/p&gt;

&lt;p&gt;curl es1_ip:9200/_cat/shards 可以看见具体哪个索引的，哪个 es 服务器在移动 shard 到另一台 es 服务器&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;正在shard移动时的状态
posts r RELOCATING  3628884   5.4gb es_ip1   elasticsearch1 -&amp;gt; es_ip5 elasticsearch5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到哪个索引，哪个 shard 分片，从哪台服务器上移到另一台服务器上&lt;/p&gt;

&lt;p&gt;这个过程发现，elasticsearch 会选择将 reproduction shard(副本分片) 移到新的 elasticsearch 服务器上，而避免移到主分片。&lt;/p&gt;

&lt;p&gt;这样可以避免移到主分片时候发生数据丢失的情况，而移动副本分配不用担心这个问题，并且线上的 health 一直是 green。尽可能不影响线上正常搜索业务。&lt;/p&gt;

&lt;p&gt;移动 shard 默认是两个并发操作。一开始误解了，以为每个 es 实例会进行两个并发的 shard 移动，会有 6 个 shard 在并发移动，实际情况是，整个集群只有 2 个 shard 在并发移动。下次可以将这个值调大一些，加快 shard 的移动速度，幸好 shard 数据移动的速度比想象的要快。&lt;/p&gt;

&lt;p&gt;通过 htop 观察服务器的负载，在进行 shard 分配的服务器，CPU 使用率一般在 20%-40% 之间。服务器是 16 核，也就是一个 elasticsearch 进程 shard 的移动，一个核心都没有跑满，服务器负载在 0.2。可见 elasticsearch 的分片移动还是很保守的，对服务器几乎没有很大的压力。并且观察发现，elasticsearch 不会一次把某个索引该分配的 shard 都分配完再选择下一个索引，而是轮询的分配索引的 shard。A 索引分配了一个 shard，就分配 B 索引的 shard，一圈之后，又回到 A 索引继续分配 shard。直到最后所有索引 shard 数量在集群中平衡。&lt;/p&gt;
&lt;h4 id="收尾操作"&gt;收尾操作&lt;/h4&gt;
&lt;p&gt;大约一个小时时间，shard 分配结束。一共将 88G 左右的数据分配到了两台新的服务器上。这是在默认 shard 并发分配数 2 的情况下的时间记录。大家以后可以根据这个记录，预估自己的 elasticsearch shard 分配会需要多少时间。&lt;/p&gt;

&lt;p&gt;动态修改 master 选举数量。根据 elasticsearch 文档推荐的公式： (N(master)+1)/2&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -XPUT es1_ip:9200/_cluster/settings -d '{
      "persistent" : {
          "discovery.zen.minimum_master_nodes" : 3
      }
    }'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;检查配置：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"persistent":{"discovery":{"zen":{"minimum_master_nodes":"3"}}},"transient":{"cluster":{"routing":{"allocation":{"enable":"all"}}}}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再检测 API 命令&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl es1_ip:9200/_cat/health

curl es1_ip:9200/_cat/indices

curl es1_ip:9200/_cat/shards
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时候，观察到 shard 分配的结果是，每个索引有 10 个 shard，每个 es 服务器拥有一个索引的两个 shard。新加入的 es4、es5 上都是副本 shard，原有集群中的 es1、es2、es3 拥有主 shard 和副本 shard。&lt;/p&gt;

&lt;p&gt;例子：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;index              shard   prirep   state     docs     store  ip    node
posts               2       r       STARTED   993718   3.5gb es1_ip es1
posts               2       p       STARTED   993718   3.5gb es1_ip es1
posts               0       p       STARTED   993428   3.7gb es2_ip es2
posts               0       p       STARTED   993428   3.7gb es2_ip es2
posts               3       p       STARTED   993653   3.6gb es3_ip es3
posts               3       p       STARTED   993653   3.6gb es3_ip es3
posts               1       r       STARTED   994063   3.5gb es4_ip es4
posts               1       r       STARTED   994063   3.5gb es4_ip es4
posts               4       r       STARTED   993938   3.5gb es5_ip es5
posts               4       r       STARTED   993938   3.5gb es5_ip es5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;posts 索引的十个 shard 分片在每台 elasticsearch 服务器上有两个分片。perfect!(实际结果是乱序的)&lt;/p&gt;
&lt;h4 id="后悔的操作选择"&gt;后悔的操作选择&lt;/h4&gt;
&lt;p&gt;我们修改了 es1 的配置文件，想要重启 es1 elasticsearch 实例。重启后，发生了意想不到的事情，索引的 shard 又开始分配移动，等了快 40 分钟分配移动结束，但是，这时候不是每个 es 服务器平均拥有一个索引的两个 shard，而是有的 es 服务器有该索引的三个 shard。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;posts               2 r STARTED   993718   3.5gb es4_ip es4
posts               2 p STARTED   993718   3.5gb es2_ip es2
posts               0 p STARTED   993428   3.7gb es4_ip es4
posts               0 r STARTED   993428   3.7gb es1_ip es1
posts               3 r STARTED   993653   3.6gb es1_ip es1
posts               3 p STARTED   993653   3.6gb es2_ip es2
posts               1 r STARTED   994063   3.5gb es5_ip es5
posts               1 p STARTED   994063   3.5gb es1_ip es1
posts               4 r STARTED   993938   3.5gb es5_ip es5
posts               4 p STARTED   993938   3.5gb es3_ip es3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;posts 在 elasticsearch1 上出现了三个 shard&lt;/p&gt;

&lt;p&gt;实际上，原本集群中的三台服务器是不用重启的，你可以修改他们 elasticsearch.yml 配置中的单拨数组设置：discovery.zen.ping.unicast.hosts。加上新加的两台服务器的 ip，和 discovery.zen.minimum_master_nodes:3。下次重启的时候，就会读取这个新的配置，而不需要马上重新。因为，k 可以通过调用 API 的方式，动态配置 discovery.zen.minimum_master_nodes，而 discovery.zen.ping.unicast.hosts 的配置，在新的 elasticsearch 服务器上配置五台服务器的 ip 地址就可以了。&lt;/p&gt;

&lt;p&gt;打开所有 es 服务器的 monit，测试线上 elasticsearch 搜索功能&lt;/p&gt;

&lt;p&gt;修改项目代码，加上新加的两台 elasticsearch 服务器 IP&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Mon, 31 Jul 2017 15:47:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/33676</link>
      <guid>https://ruby-china.org/topics/33676</guid>
    </item>
    <item>
      <title>如何更可控、稳定地写 Rails 脚本操作线上数据库的记录</title>
      <description>&lt;p&gt;在公司，有各种 Ruby 老司机。我们都喜欢用 Ruby 来解决各种问题。比如：操作线上的数据库记录，修改 ElasticSearch 等。&lt;/p&gt;

&lt;p&gt;描述：某个功能需要为数据库增加相关数据。大概逻辑是：根据表 Company，根据某些条件往 Department 和 DepartmentsUser 表中插入数据。
然后将 User 表中满足筛选条件的 status 字段进行更改。&lt;/p&gt;

&lt;p&gt;结果：需要超过一个小时才将整个脚本跑完，也就是这一小时都在影响线上数据库&lt;/p&gt;

&lt;p&gt;反思：可以更可控、稳定地写 Ruby 脚本操作线上数据库的表吗？&lt;/p&gt;
&lt;h2 id="一、思考脚本是否可以在项目升级前运行"&gt;一、思考脚本是否可以在项目升级前运行&lt;/h2&gt;
&lt;p&gt;如果脚本可以在项目升级前运行，则可以在一定程度上更早的发现问题，以做出对正式升级时的预防。比如，操作新的表，或新的字段。这种情况是完全可以提早运行脚本的。
  有的脚本是必须要升级之前运行的，由于线上代码逻辑依赖于这个脚本的数据。所以，需要在升级之前跑一下，升级之后再跑一下。这样的脚本需要写成可重复执行。
  但是有的脚本是只能升级后跑的，则千万别在升级前跑&lt;/p&gt;
&lt;h2 id="二、是否有长时间commit操作"&gt;二、是否有长时间 commit 操作&lt;/h2&gt;
&lt;p&gt;警惕长时间 commit 的操作。举个例子：比如用 update_all 或批量插入在一定程度上可以更快的运行 sql，但是，这些操作的 commit 的时间可能会很长 (如果你用的是 MySQL)。这时候用户可以进行 select 或 insert 操作，但是对 update 操作就会阻塞了。
  如果时间过长则很有可能超时。这样会让用户感知到系统的延迟或崩溃。所以，不使用这种批量操作，排除 commit 长时间的操作，使用粒度小的 create，update，destroy 操作或者每次小批量的 commit 操作。如果一个 commit 很大，中间异常断了，则前面跑的
  sql 语句就废弃了。而粒度小的操作则可以避免这样。虽然，总的执行时间会更长，但是，这样就不会长时间占用数据库的某个表而影响用户的一些使用。
  如果真的无法避免长时间的操作，就需要在后半夜大家睡觉的时候跑脚本或者进行数据库操作了 (比如新增字段，新增索引等)。&lt;/p&gt;
&lt;h2 id="三、捕获异常错误"&gt;三、捕获异常错误&lt;/h2&gt;
&lt;p&gt;使用异常，捕获异常错误，将异常打印出来。或者可以在全局新建一个数组，然后将某次异常的操作中相关记录的 id 存入数组中，最后打印出来，就可以知道在执行过程中哪个记录是有问题的，可以很方便的追踪错误，后续就可以
  很方便的进行验证和纠错了。&lt;/p&gt;
&lt;h2 id="四、让脚本可以重复运行"&gt;四、让脚本可以重复运行&lt;/h2&gt;
&lt;p&gt;尽量让脚本可以重复运行。即使加入额外的 find 或者 find_or_create_by 查询。或者捕获异常，而不是让异常中断操作。不要使用清空数据库的方式，因为这样需要从 0 条记录开始跑，所需要的时间更长。
  脚本重复运行要注意的是，避免每次的重复运行增加脏数据。只有 update 操作的脚本可以重复运行，如果是 create 的操作，可以在操作前先判断一下是否存在，不存在则创建。
  find_or_create_by 方法可以使用 block 方法，更方便的使用。&lt;/p&gt;
&lt;h2 id="五、使用find_each 或 find_in_batches"&gt;五、使用 find_each 或 find_in_batches&lt;/h2&gt;
&lt;p&gt;使用 find_each 或 find_in_batches 分批次的进行查询循环操作。注意，find_in_batches 得到的是对象数组集合。&lt;/p&gt;
&lt;h2 id="六、使用gem 'ruby-progressbar' 愉快的展示执行进度"&gt;六、使用 gem 'ruby-progressbar' 愉快的展示执行进度&lt;/h2&gt;
&lt;p&gt;在 Gemfile 中假如 gem 'ruby-progressbar', require: false。然后在脚本文件中  require 'ruby-progressbar' 进行使用。可以很方便的展示脚本执行的百分比和进度。&lt;/p&gt;
&lt;h2 id="七、统计脚本结果信息，对比正确的信息以确定脚本运行无误。"&gt;七、统计脚本结果信息，对比正确的信息以确定脚本运行无误。&lt;/h2&gt;&lt;h2 id="八、踩过的坑。。。。。"&gt;八、踩过的坑。。。。。&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;在 rails c(用的是 pry 环境) 不要跑脚本，因为脚本代码中如果有 next 方法，会使用到的是 pry 的 next 方法而不是 ruby 的 next 方法，然后你就掉坑里了这对 rails 4.0 的版本有这个问题，4.2 以上的版本修复了这个问题&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;代码中不要定义和数据库字段相同名称的方法，如果有重名的方法，请先确认是需要该方法还是需要的是数据库的字段。是数据库的字段请使用 read_attributes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;让每个脚本只执行一个循环模块，进行一种数据的更改。尽量保持独立。如果有互相依赖的循环逻辑，再写在一个脚本中。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="九、对执行脚本后，线上数据的分析和校验。我觉得这也是最难的一步了，这一步根据不同项目而不同。我觉得应该在执行脚本之前，将验证数据的方案也准备好，"&gt;九、对执行脚本后，线上数据的分析和校验。我觉得这也是最难的一步了，这一步根据不同项目而不同。我觉得应该在执行脚本之前，将验证数据的方案也准备好，&lt;/h2&gt;
&lt;p&gt;而不是脚本执行完就过了，而不知道线上数据到底正确不正确。所以，执行脚本其实应该分两部分。一部分是正常的更改逻辑，另一部分是验证线上数据正确与否的方案。&lt;/p&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'ruby-progressbar'&lt;/span&gt;
&lt;span class="n"&gt;progressbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ProgressBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:total&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;format: &lt;/span&gt;&lt;span class="s2"&gt;"%p%% [%B] %c/%C %E"&lt;/span&gt;
&lt;span class="n"&gt;progressbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"= 开始初始化 ="&lt;/span&gt;
&lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_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;company&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;company_id: &lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subdomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;parent_id: &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;is_default: &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;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;agents&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;agent&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="no"&gt;DepartmentsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;department_id: &lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="k"&gt;next&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="k"&gt;next&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;progressbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;progressbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"=== 初始化完毕 ==="&lt;/span&gt;
&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;p: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'UG'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;update_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="代码示例小结:"&gt;代码示例小结：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;脚本是为新的数据库补充对应数据，所以可以在项目升级前运行&lt;/li&gt;
&lt;li&gt;脚本使用了 find_each&lt;/li&gt;
&lt;li&gt;脚本进行了异常捕获&lt;/li&gt;
&lt;li&gt;脚本使用了 ruby-progressbar&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="可以改进的地方:"&gt;可以改进的地方：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;脚本不能重复跑，如果重复跑的话，对于 create 操作会产生脏数据；将 create! 改为 find_or_create_by!&lt;/li&gt;
&lt;li&gt;没有收集异常错误的记录&lt;/li&gt;
&lt;li&gt;没有统计脚本结果信息，对比正确的信息&lt;/li&gt;
&lt;li&gt;建议将 progressbar.increment 放到循环的最开始&lt;/li&gt;
&lt;li&gt;使用了 update_all 这种可能长时间锁表的操作&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="代码示例改进:"&gt;代码示例改进：&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'ruby-progressbar'&lt;/span&gt;
&lt;span class="n"&gt;progressbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ProgressBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:total&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;format: &lt;/span&gt;&lt;span class="s2"&gt;"%p%% [%B] %c/%C %E"&lt;/span&gt;
&lt;span class="n"&gt;progressbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"= 开始初始化 ="&lt;/span&gt;
&lt;span class="n"&gt;error_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;error_company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;update_user_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_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;company&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;progressbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_or_create_by!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;company_id: &lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subdomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;parent_id: &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;is_default: &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;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;users&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;user&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="no"&gt;DepartmentsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_or_create_by!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;department_id: &lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;error_user&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
        &lt;span class="k"&gt;next&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;error_company&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;next&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;p: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'UG'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;find_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;user&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;update_user_error&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;progressbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"Company count: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Company&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; -- Department count: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Department&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; -- DepartmentsUser count: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;DepartmentsUser&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; User count: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;progressbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"error: error_user: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;error_user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; error_company: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;error_company&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;progressbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt; &lt;span class="s2"&gt;"=== 初始化完毕 ==="&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;执行： bundle exec rails runner -e development init_department.rb

= 开始初始化 =                                                                                                                                                                        
100% [======================================================================================================================================================================] xxx/xxx Time: 00:00:00

Company count: xxx -- Department count: xxx -- DepartmentsUser count: xxx User count: xxx         

error: error_user: []; error_company: []                     

=== 初始化完毕 ===

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本文提供的几个思路其实都不难，只是往往我们都忽略了。&lt;/p&gt;

&lt;p&gt;无论是写 rails 脚本，rake 脚本，复杂的 migration 或者其他语言的脚本操作线上的数据库，都应该思考如何对线上的数据库影响最小，对用户的使用影响最小，以及最终的结果是否正确
也许在本地或测试环境你的脚本运行的没有问题，但是，线上的数据数量往往是本地和测试环境的 n 倍，并且已经有很多隐藏的脏数据，各种奇妙的事情都有可能发生。即使是在半夜运行脚本，
能够预先做更多的准备对应出现的异常情况，也能更快的找到问题的所在。&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Thu, 11 May 2017 18:46:09 +0800</pubDate>
      <link>https://ruby-china.org/topics/32978</link>
      <guid>https://ruby-china.org/topics/32978</guid>
    </item>
    <item>
      <title>阅读 GitHub 仓库中项目源码的好帮手</title>
      <description>&lt;p&gt;今天在 Github 上查看源码的时候，作者有个推荐查看文档的连接，点进去后来到了 &lt;a href="https://sourcegraph.com" rel="nofollow" target="_blank"&gt;https://sourcegraph.com&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这是一个有点仿照 Visual stadio code 的在线阅读源码的查看器，为什么说是查看器，因为我没有发现可以编辑。也许是我没有注册账户。界面的主题是 Dracula 吧。&lt;/p&gt;

&lt;p&gt;这个界面是这样的：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/a42e4e2e-02e3-4c4e-8831-2ebf76c1c023.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Logo 的右边是一个搜索框，可以搜索 Github 上的项目仓库名称。我这里搜索出了 ruby-china/homeland 项目。之后，右边就会列出项目的所有代码，选择某个文件后就能看见源码。
左边的搜索图标点击后，可以在项目中进行搜索。有点像是全局搜索。在代码中，按 ctrl+f 可以进行本文件的搜索。还可以显示多个选项框。暂时只发现这几个功能。&lt;/p&gt;

&lt;p&gt;对于想方便的阅读 Github 代码，但是并不想把代码下载到本地的时候，用这个在线查看器还是很舒服的。不过，好像搜到的应该是 master 分支。暂不清楚是否可以选择分支。
当然，如果要跳到 Github 上，右上角也可以很方便的跳转到 Github，不过可能会被谷歌浏览器拦截为弹出窗口。主页下面还有 Kelsey Hightower 为其做广告。如果你感兴趣，可以试一试。&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Fri, 05 May 2017 11:58:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/32935</link>
      <guid>https://ruby-china.org/topics/32935</guid>
    </item>
    <item>
      <title>在 Rails 项目中管理、自定义配置你的 ElasticSearch indexes</title>
      <description>&lt;p&gt;这周对 Rails 项目中的 ElasticSearch 进行了总结并写成了文档，觉得有一些内容值得记录和分享。原文连接：&lt;a href="http://pathbox.github.io/tech/2017/03/01/manage-your-elasticsearch-index-in-rails-project.html" rel="nofollow" target="_blank" title=""&gt;超链接&lt;/a&gt;
  这篇文章主要是在 Rails 项目中对索引，mapping 的设置、管理操作的总结，不涉及搜索方面的内容。
源数据是存在 MySQL 中，ES 的数据是 MySQL 写操作的时候进行了回调同步到 ES 中，这个应该和很多人的使用同步策略一样。&lt;/p&gt;
&lt;h4 id="1.Model#mapping"&gt;1.Model#mapping&lt;/h4&gt;
&lt;p&gt;在 model 文件中，使用 mapping 的声明方式。&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;Article&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Elasticsearch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Elasticsearch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Callbacks&lt;/span&gt;
  &lt;span class="n"&gt;index_name&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt;  &lt;span class="c1"&gt;# 这里可以自定义Article的ES索引名称&lt;/span&gt;

  &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'multi_field'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;index: :not_analyzed&lt;/span&gt;
      &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:tokenized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;analyzer: &lt;/span&gt;&lt;span class="s1"&gt;'ik'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;analyzer: &lt;/span&gt;&lt;span class="s1"&gt;'ik'&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;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'multi_field'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:original&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;index: :not_analyzed&lt;/span&gt;
  &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:tokenized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;analyzer: &lt;/span&gt;&lt;span class="s1"&gt;'ik'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用了 &lt;em&gt;multi_field&lt;/em&gt; 定义了两个 indexes，这样在 ES 中会产生下面的 mapping 结构&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"subject": {
   "type": "string",
   "index": "no",
   "fields": {
      "original": {
         "type": "string",
         "index": "not_analyzed"
      },
      "tokenized": {
         "type": "string",
         "analyzer": "ik"
      }
   }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于 MySQL articles 表的 subject 数据，在 ES 的 articles 索引中用了两个 field 来存储 subject.original 和 subject.tokenized。他们存储的数据是一样的，不同的是 subject.original 没有被分词，subject.tokenized 使用了&lt;em&gt;ik&lt;/em&gt;进行了分词。这样做的效果是当想要对 subject 进行全文搜索时，就可以使用 subject.tokenized，想要对 subject 进行条件过滤的时候，就可以使用 subject.original 了。&lt;/p&gt;

&lt;p&gt;Tip: ik 是一个优秀的中文分析器  &lt;a href="https://github.com/medcl/elasticsearch-analysis-ik" rel="nofollow" target="_blank" title=""&gt;github 地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我们知道  include Elasticsearch::Model::Callbacks 这个模块帮我们做了：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;__elasticsearch__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index_document&lt;/span&gt;  &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="ss"&gt;on: :create&lt;/span&gt;
    &lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;__elasticsearch__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_document&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="ss"&gt;on: :update&lt;/span&gt;
    &lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;__elasticsearch__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_document&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="ss"&gt;on: :destroy&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就是写操作的模块。&lt;/p&gt;

&lt;p&gt;我们也可以这样做：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="ss"&gt;:create_es_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: :create&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_es_index&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;__elasticsearch__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index_document&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'article_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
    &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'time'&lt;/span&gt;&lt;span class="p"&gt;]&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="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'error'&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="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
    &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'backtrace'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;app_backtrace&lt;/span&gt;
    &lt;span class="no"&gt;ErrorESMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_error_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deliver&lt;/span&gt;  &lt;span class="c1"&gt;# send a error email&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;这样，如果有回调的同步 ES 的操作有异常，则会捕获这个异常并且发送 error 的相关信息邮件给开发人员，开发人员可以及时的处理异常。同理 update 和 destroy 操作。&lt;/p&gt;
&lt;h4 id="2. Model.import  force:true"&gt;2. Model.import  force:true&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt; &lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="ss"&gt;:true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ES 会根据 Article 中 setting 和 mapping 的配置，在 ES 中 构建 articles 的索引 (清空新建索引 + 导入数据)，对应的 type 为 article。&lt;/p&gt;

&lt;p&gt;下面的三个操作，同样可以创建索引并导入数据&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__elasticsearch__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_index!&lt;/span&gt; &lt;span class="ss"&gt;force: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# 根据mapping和setting 创建articles索引，该索引没有任何数据&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__elasticsearch__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh_index!&lt;/span&gt;  &lt;span class="c1"&gt;# refresh 操作&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_in_batches&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;articles&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="c1"&gt;# 批量同步导入MySQL articles的数据&lt;/span&gt;
    &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__elasticsearch__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bulk&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="ss"&gt;index: &lt;/span&gt;&lt;span class="s1"&gt;'articles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'article'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="n"&gt;articles&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;article&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;index: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;_id: &lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_indexed_json&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;假设，你需要往 ES articles 索引中同步一批数据，这批数据已经被导入到了 MySQL 中，如果数据量不大，使用 import: true 的方法快速导入完。如果数据量比较大，导入耗时很多。还使用 import 的方式导入的话，会导致在 ES articles 被清空后，如果线上有用户在对 articles 的数据进行 ES 的搜索，很有可能会导致没有搜索结果。这时候，你可以使用上面的第三个操作，不需要重建整个 articles 索引。而是往 articles 索引中添加索引数据。&lt;/p&gt;
&lt;h4 id="3. as_indexed_json"&gt;3. as_indexed_json&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;as_indexed_json&lt;/em&gt; 是一个很重要的方法。如果你明白了它，你就能知道 ES 索引中存的 documents 数据是怎样的。我们知道 ES 不是关系型数据库，ES 中的 documents 数据和 MongoDB 的 documents 类似，每个 document 是一个 json。所以，知道索引中 documents 数据存了哪些 fields，你才能更好地结合灵活多变的搜索条件和方式，构造多种多样的搜索情况。&lt;/p&gt;

&lt;p&gt;下面我们看&lt;em&gt;as_indexed_json&lt;/em&gt; 的源码：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# File 'lib/elasticsearch/model/serializing.rb', line 26&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;as_indexed_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="c1"&gt;# TODO: Play with the `MyModel.indexes` method -- reject non-mapped attributes, `:as` options, etc&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt; &lt;span class="ss"&gt;root: &lt;/span&gt;&lt;span class="kp"&gt;false&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;这里的 self 其实是 model 的一个 instance。我们可以在 model 中 monkey patch 这个方法。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# article.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Elasticsearch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Elasticsearch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Callbacks&lt;/span&gt;
  &lt;span class="n"&gt;index_name&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt;  &lt;span class="c1"&gt;# 这里可以自定义Article的ES索引名称&lt;/span&gt;

  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:followers&lt;/span&gt;

  &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'multi_field'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;index: :not_analyzed&lt;/span&gt;
      &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:tokenized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;analyzer: &lt;/span&gt;&lt;span class="s1"&gt;'ik'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;analyzer: &lt;/span&gt;&lt;span class="s1"&gt;'ik'&lt;/span&gt;
    &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :date&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;as_indexed_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;as_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;except: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:update_at&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
      &lt;span class="ss"&gt;methods: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:parse_content&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;include: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;comments: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
        &lt;span class="ss"&gt;followers: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&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="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;hash&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;other_hash&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s2"&gt;"My title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;owner: &lt;/span&gt;&lt;span class="s2"&gt;"My owner"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_content&lt;/span&gt;
    &lt;span class="s2"&gt;"Article: "&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&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;em&gt;as_indexed_json&lt;/em&gt; 方法构造了一个 hash 然后返回。except 表示排除某个字段，这个字段不会出现在 hash 的 key 中，methods 表示将某个方法作为 hash 的 key，对应的 value 就是该方法的返回值。include 表示会找相关联的表，并取定义的字段作为 hash 的 key，value 则为该字段的值。&lt;/p&gt;

&lt;p&gt;这样，最后得到的 hash 大概是这样的，比如 Article.first.as_indexed_json:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'subject'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'这是第一篇文章的主题'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'这是第一篇文章的内容'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'My title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'owner'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'My owner'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'created_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Tue, 02 Aug 2016 20:40:08 CST +08:00'&lt;/span&gt;
  &lt;span class="s1"&gt;'parse_content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Article: 这是第一篇文章的内容'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'comments'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'content'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'点赞'&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'content'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'good'&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="s1"&gt;'followers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&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;em&gt;as_indexed_json&lt;/em&gt; 中可以如何构造想要的 hash 数据了。&lt;/p&gt;

&lt;p&gt;在什么时候用到 &lt;em&gt;as_indexed_json&lt;/em&gt;方法呢？&lt;/p&gt;

&lt;p&gt;在 index_document 方法的源码中：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# File 'lib/elasticsearch/model/indexing.rb', line 333&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index_document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_indexed_json&lt;/span&gt; &lt;span class="c1"&gt;# Hi! I'm here!&lt;/span&gt;

  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;index: &lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;type:  &lt;/span&gt;&lt;span class="n"&gt;document_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;id:    &lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;body:  &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就明白了，当一个 model instance(一条记录) 从 MySQL 同步到 ES 的时候，将这条记录转为 hash 结构。其实，&lt;em&gt;as_indexed_json&lt;/em&gt; 返回的这个 hash 就是 ES 对应的一条 document。这个过程就是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;MySQL&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;as_indexed_json&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ES&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在我们明白了&lt;em&gt;as_indexed_json&lt;/em&gt;，就可以根据自己的需要，往 ES 索引中存储 documents 数据了。并不是 ES 索引的 mapping field 只能和数据库对应表的字段一样，如果你什么都不做，ES 会用默认的配置帮你做这些事情。如果你想要存储更多的数据字段到 ES 的 document 中进行索引分词，你就需要自己动手做更多的自定义的配置。合理的 documents 数据是 ES 进行高效搜索功能的保证。&lt;/p&gt;
&lt;h4 id="4. ES的mapping只能增加"&gt;4. ES 的 mapping 只能增加&lt;/h4&gt;
&lt;p&gt;ES 的 mapping 一旦创建，只能增加字段，而不能修改已经 mapping 的字段。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Elasticsearch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_mapping&lt;/span&gt; &lt;span class="ss"&gt;index: &lt;/span&gt;&lt;span class="s2"&gt;"articles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s2"&gt;"article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;article: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;properties: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;organization: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;properties: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="ss"&gt;id:   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;type: :integer&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;index: :not_analyzed&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="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到的 mappin 是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s2"&gt;"organization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"integer"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"analyzer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"not_analyzed"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的方法代码可以写成 rails 脚本，使用 rails runner 执行。也可以写成 rake 命令。然后，建议把新增的 mapping field 写在 Article 的 mapping 定义中。让 mapping 定义在代码层面保持显示最完整的定义结构。&lt;/p&gt;
&lt;h4 id="5. 设置自定义的analysis"&gt;5. 设置自定义的 analysis&lt;/h4&gt;
&lt;p&gt;什么是 analysis、analyzer、tokenizer 和 filter，如果不知道或者有遗忘了，请看&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/2.4/analysis.html" rel="nofollow" target="_blank" title=""&gt;ElasticSearch 的官方文档&lt;/a&gt; 。官方文档描述的简单清晰，例子也易懂。&lt;/p&gt;

&lt;p&gt;在上面的 Article 例子中，我们没有对 analysis 进行设置，这样，ES 会使用默认的 analysis 设置。但是，也许默认的设置并不是我们真正想要的 analysis。我们现在开始自定义 analysis。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# article.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Elasticsearch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Elasticsearch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Callbacks&lt;/span&gt;
  &lt;span class="n"&gt;index_name&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt;  &lt;span class="c1"&gt;# 这里可以自定义Article的ES索引名称&lt;/span&gt;

  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:followers&lt;/span&gt;

  &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="n"&gt;analysis&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
    &lt;span class="ss"&gt;analyzer: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;my_custom_analyzer&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'custom'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;tokenizer: &lt;/span&gt;&lt;span class="s1"&gt;'ngram_tokenizer'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;tokenizer: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;ngram_tokenizer: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'nGram'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;min_gram: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;max_gram: &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;token_chars: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'lettler, '&lt;/span&gt;&lt;span class="n"&gt;digit&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="n"&gt;punctuation&lt;/span&gt;&lt;span class="s1"&gt;']}
    }
} do
    mapping do
      indexes :subject, type: '&lt;/span&gt;&lt;span class="n"&gt;multi_field&lt;/span&gt;&lt;span class="s1"&gt;' do
        indexes :raw, index: :not_analyzed
        indexes :tokenized, analyzer: '&lt;/span&gt;&lt;span class="n"&gt;ik&lt;/span&gt;&lt;span class="s1"&gt;'
      end
      indexes :content, type: :string, analyzer: '&lt;/span&gt;&lt;span class="n"&gt;ik&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;
      &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="ss"&gt;:created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :date&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt; 
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;as_indexed_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;as_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;except: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:update_at&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
      &lt;span class="ss"&gt;methods: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:parse_content&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;include: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;comments: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
        &lt;span class="ss"&gt;followers: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&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="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;hash&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;other_hash&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s2"&gt;"My title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;owner: &lt;/span&gt;&lt;span class="s2"&gt;"My owner"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_content&lt;/span&gt;
    &lt;span class="s2"&gt;"Article: "&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;自定义的主要代码是这部分：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="n"&gt;analysis&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
    &lt;span class="ss"&gt;analyzer: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;my_custom_analyzer&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'custom'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;tokenizer: &lt;/span&gt;&lt;span class="s1"&gt;'ngram_tokenizer'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;tokenizer: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;ngram_tokenizer: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'nGram'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;min_gram: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;max_gram: &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;token_chars: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'lettler, '&lt;/span&gt;&lt;span class="n"&gt;digit&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="n"&gt;punctuation&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里你可能需要倒回来看，我们自定义定义了一个 &lt;em&gt;tokenizer&lt;/em&gt;，取名为 &lt;em&gt;ngram_tokenizer&lt;/em&gt; ，type 表示 使用的 tokenizer。我们使用的是 ES built-in 的 &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/2.4/analysis-edgengram-tokenizer.html" rel="nofollow" target="_blank" title=""&gt;nGram tokenizer&lt;/a&gt; ，具体配置参数请看它的文档。ES built-in 了不同的 tokenizer，开发人员可以自由选择使用。&lt;/p&gt;

&lt;p&gt;我们还定义了一个 analyzer，取名为 &lt;em&gt;my_custom_analyzer&lt;/em&gt;。type custom 表示这个 analyzer 是自定义的。使用了 &lt;em&gt;ngram_tokenizer&lt;/em&gt; 这个 tokenizer。这时你应该发现了，我们使用的就是这里我们自定义的 &lt;em&gt;ngram_tokenizer&lt;/em&gt; 。这里我们没有对 filter 进行自定义，看过 ES 文档的朋友，应该知道 filter 也是可以自定义的。&lt;/p&gt;

&lt;p&gt;官方的示例：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;PUT&lt;/span&gt; &lt;span class="sr"&gt;/my_index
{
    "settings": {
        "analysis": {
            "char_filter": { ... custom character filters ... },
            "tokenizer":   { ...    custom tokenizers     ... },
            "filter":      { ...   custom token filters   ... },
            "analyzer":    { ...    custom analyzers      ... }
        }
    }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ElasticSearch 确实是一个优秀的全文搜索引擎。了解和实践更多的 ElasticSearch 的设置和搜索，能够体会到 ElasticSearch 更多的功能。即使看过 ElasticSearch 入门教程的朋友，我觉得 ElasticSearch 的官方文档也是非常值得阅读的。&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Fri, 03 Mar 2017 10:56:18 +0800</pubDate>
      <link>https://ruby-china.org/topics/32428</link>
      <guid>https://ruby-china.org/topics/32428</guid>
    </item>
    <item>
      <title>2017年 不同的和 Ruby 和 Rails 有关的 conference</title>
      <description>&lt;p&gt;这个网站 (&lt;a href="http://rubyconferences.org/" rel="nofollow" target="_blank" title=""&gt;友情链接&lt;/a&gt;) 列出了 2017 年已定的不同的和 Ruby 和 Rails 有关的 conference。去不了的，只好等 YouTube 视频了。。。&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Mon, 16 Jan 2017 16:30:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/32143</link>
      <guid>https://ruby-china.org/topics/32143</guid>
    </item>
    <item>
      <title> What Rails 5 has in store for you</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;Take a look at what Rails 5 has in store for you. We go through all the new features and improvements across development, testing, caching and much more. So let's dive in.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;YouTube&lt;a href="https://www.youtube.com/watch?v=ECDX1NH7yWE#t=1m10s" rel="nofollow" target="_blank" title=""&gt;超级链接&lt;/a&gt;&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Mon, 09 May 2016 17:51:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/29958</link>
      <guid>https://ruby-china.org/topics/29958</guid>
    </item>
    <item>
      <title>Resque vs. Sidekiq</title>
      <description>&lt;p&gt;工作中 Resque 和 Sidekiq 都有用过，这几天突然想他们的优缺点是什么？然后 Google。发现了一个 叫 Jeremy Green 的 coder 的比较全面的测试总结。连接在此 :&lt;a href="http://octo-labs.github.io/background_smackdown_slides/#/" rel="nofollow" target="_blank" title=""&gt;超级链接&lt;/a&gt;。仅供参考&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Thu, 25 Feb 2016 15:28:20 +0800</pubDate>
      <link>https://ruby-china.org/topics/29097</link>
      <guid>https://ruby-china.org/topics/29097</guid>
    </item>
    <item>
      <title>请教关于 find_each 的优化使用 </title>
      <description>&lt;p&gt;有一个需求，需要把表中 200w 左右的数据进行一次过滤。比如：过滤的方法是  &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;check&lt;/span&gt;
  &lt;span class="n"&gt;xxx&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每一条记录都需要调用一次过滤方法 check
不想一次把 200w 条记录取出来，预测对内存来说会是一个灾难。所以打算使用 find_each。
由于过滤操作不是后台异步操作，所以在使用 find_each 时希望能快一些处理完然后在前台将满足条件的记录马上展示出来。&lt;/p&gt;

&lt;p&gt;我先用了一个有 200w 左右数据的表在 rails console 中进行测试。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&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="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="n"&gt;b&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="s2"&gt;"show"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;TestOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;batch_size: &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;#         user         system       total          real      单位是：秒&lt;/span&gt;
&lt;span class="c1"&gt;#  show   28.340000   1.200000    29.540000    ( 33.036628)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我增加了 batch_size 到 5000&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#         user         system       total          real      单位是：秒&lt;/span&gt;
&lt;span class="c1"&gt;#  show   26.870000   0.840000    27.710000    ( 29.737033)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;两次使用的时间差不多。batch_size 为 10000 时也是差不多。
换成打印测试：
batch_size 分别为 1000 和 10000&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&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="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="n"&gt;b&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="s2"&gt;"show"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;  
       &lt;span class="no"&gt;TestOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;batch_size: &lt;/span&gt;&lt;span class="mi"&gt;1000&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="nb"&gt;p&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;    
          &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&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;两次结果大约需要  50s。
不能让用户等 20s 的时间，所以想请教是否有优化的方法，使 find_each 的效率更高呢？或者有其他更好的方法（除了异步的操作方法外）&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Mon, 22 Feb 2016 14:59:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/29049</link>
      <guid>https://ruby-china.org/topics/29049</guid>
    </item>
    <item>
      <title>在微信里看见的一个故事: 23 岁时这个女生从经济学转做程序媛</title>
      <description>&lt;p&gt;这是在微信里看见的一个 23 岁时这个女生从经济学转做程序媛，感慨好深啊。搜了下 Spitfire Athlete，是一个女性健身和举重相关的内容项目，主页网站和 iOS 软件都已经不错了。文中作者从几乎 0 基础开始学编程，她的经历也许可以给好多新手 (包括我自己) 很多启示。请点击这里：&lt;a href="http://mp.weixin.qq.com/s?__biz=MjM5OTA1MDUyMA==&amp;amp;mid=404496825&amp;amp;idx=1&amp;amp;sn=09b2d486ea4dc739b8cee985697ef773&amp;amp;scene=0#wechat_redirect" rel="nofollow" target="_blank" title=""&gt;文章内容链接&lt;/a&gt;&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Sun, 17 Jan 2016 12:35:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/28759</link>
      <guid>https://ruby-china.org/topics/28759</guid>
    </item>
    <item>
      <title>GitHub 秘籍</title>
      <description>&lt;p&gt;Git 和 Github 秘籍，灵感来自于 Zach Holman 在 2012 年 Aloha Ruby Conference 和 2013 年 WDCNZ 上所做的演讲：Git and GitHub Secrets(slides) 和 More Git and GitHub Secrets(slides)。
突然在 GitHub 上发现了这个。内容包括了:GitHub 的使用和 git 的使用，分享给大家。请点击链接：&lt;a href="https://github.com/tiimgreen/github-cheat-sheet/blob/master/README.zh-cn.md" rel="nofollow" target="_blank"&gt;https://github.com/tiimgreen/github-cheat-sheet/blob/master/README.zh-cn.md&lt;/a&gt;&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Mon, 11 Jan 2016 11:47:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/28681</link>
      <guid>https://ruby-china.org/topics/28681</guid>
    </item>
    <item>
      <title>谷歌浏览器 RailsPanel 插件</title>
      <description>&lt;p&gt;看了 railscast 上的 better_error 的视频，里面介绍了 Rails_Panel 插件。对于请求的分析一目了然。觉得挺适合接触 rails 时间不长的开发人员或 rails 的前端开发人员。&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://railscasts.com/episodes/402-better-errors-railspanel?autoplay=true" rel="nofollow" target="_blank" title=""&gt;better_error 视频链接&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>pathbox</author>
      <pubDate>Wed, 29 Jul 2015 11:50:23 +0800</pubDate>
      <link>https://ruby-china.org/topics/26687</link>
      <guid>https://ruby-china.org/topics/26687</guid>
    </item>
    <item>
      <title>scan 和 split 的区别记录</title>
      <description>&lt;p&gt;最近项目里出现了大量的 scan 方法。查看了下资料，明白了它的意思。 
scan 是 将满足条件（如正则表达式过滤后）的字符串返回到数组，split 是将满足条件的内容为分割点，将分割点左右两边的字符串返回到数组中。不知道这样理解是否有偏差
The output of String#split doesn't include the delimiter. Rather, everything except the delimiter would be returned in the output array, while the output of String#scan would only include what is matched by the delimiter.&lt;/p&gt;
&lt;h2 id="A delimited string split on | returns everything surrounding the | delimiters"&gt;A delimited string split on | returns everything surrounding the | delimiters&lt;/h2&gt;
&lt;p&gt;"a|delimited|string".split("|")&lt;/p&gt;
&lt;h2 id="Prints: ["&gt;Prints: ["a", "delimited", "string"]&lt;/h2&gt;&lt;h2 id="The same string scanninng for | only returns the matched |"&gt;The same string scanninng for | only returns the matched |&lt;/h2&gt;
&lt;p&gt;"a|delimited|string".scan("|")&lt;/p&gt;
&lt;h2 id="Prints: ["&gt;Prints: ["|", "|"]&lt;/h2&gt;
&lt;p&gt;Both of the above would also accept a regular expression in place of the simple string "|".&lt;/p&gt;
&lt;h2 id="Split on everything between and including two t's"&gt;Split on everything between and including two t's&lt;/h2&gt;
&lt;p&gt;"a|delimited|string".split(/t.+t/)&lt;/p&gt;
&lt;h2 id="Prints: ["&gt;Prints: ["a|delimi", "ring"]&lt;/h2&gt;&lt;h2 id="Search for everything between and including two t's"&gt;Search for everything between and including two t's&lt;/h2&gt;
&lt;p&gt;"a|delimited|string".scan(/t.+t/)&lt;/p&gt;
&lt;h2 id="Prints: ["&gt;Prints: ["ted|st"]&lt;/h2&gt;</description>
      <author>pathbox</author>
      <pubDate>Wed, 22 Jul 2015 17:11:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/26611</link>
      <guid>https://ruby-china.org/topics/26611</guid>
    </item>
    <item>
      <title>没法科学上网的时候如何使用 Google 搜索引擎</title>
      <description>&lt;p&gt;我们都知道科学上网对于 ruby on rails 程序员来说是何等重要。我想大家都做好了科学上网的各种配置，但是也许会有无法科学上网的时候发生，虽然几率很小。最近朋友和我说了一种不能科学上网的时候使用 Google 的方法。在这里分享给大家。&lt;/p&gt;

&lt;p&gt;首先 打开 这个网站  &lt;a href="http://www.aol.com" rel="nofollow" target="_blank" title=""&gt;www.aol.com&lt;/a&gt;
    对，这就是大名鼎鼎的美国在线（感兴趣可以搜索了解一下）。然后直接在网站的搜索栏上输入想要搜索的内容，
    美国在线使用的是 Google 的搜索引擎，也就是说，你也在使用 Google 搜索内容了。哈哈，美国在线没有被墙。不愧是美国最大的互联网提供商之一
&lt;img src="https://l.ruby-china.com/photo/2015/374db6946a2dee3361f6185d7f52e693.png" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2015/c2d4ebbaad065db9b2c14e6f76c2c039.png" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Tue, 07 Jul 2015 16:57:07 +0800</pubDate>
      <link>https://ruby-china.org/topics/26369</link>
      <guid>https://ruby-china.org/topics/26369</guid>
    </item>
    <item>
      <title>关于 rails_admin 的看法。</title>
      <description>&lt;p&gt;已经对 rails_admin 无力吐槽了。。。竟然出现了在本地 development 和 production 都正常的情况下，线上出现一句 select 查询 语句不执行的情况。决定再也不碰 rails_admin 了。。。。。。（之前用这 rails_admin 的家伙离职了。。。）&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Fri, 12 Jun 2015 17:40:20 +0800</pubDate>
      <link>https://ruby-china.org/topics/26000</link>
      <guid>https://ruby-china.org/topics/26000</guid>
    </item>
    <item>
      <title>关于 carrierwave 如何获取 original_filename 值感想</title>
      <description>&lt;p&gt;近工作使用 carrierwave 做上传文件的功能。只是上传文件，比如 word 文件，txt 文件。然后需要有一个列表页面，需要显示上传的文件的名称。比如：上传了一个 goodrails.txt 的文件，需要在列表的第二列显示 文件名称，即 goodrails.txt 要显示出来，让使用者日后查看时，知道上传了什么文件。在 carrierwave 中显示的上传文件名称会存储到 original_filename 中，所以思路就是拿到 original_filename 的值就行了。&lt;/p&gt;

&lt;p&gt;carrierwave 中有一个&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#def filename&lt;/span&gt;
&lt;span class="c1"&gt;#  "something.jpg" if original_filename&lt;/span&gt;
&lt;span class="c1"&gt;# end  方法，大家应该不陌生。就是自定义存入upload文件夹中的文件名称。比如在上传图片时，这样写&lt;/span&gt;
     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;参考大神的文章&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="ss"&gt;:/&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;huacnlee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;carrierwave&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;filename&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;original_filename&lt;/span&gt;
      &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MD5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@name&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extension&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过查看后台传的参数，如
    Processing by UpdateFilesController#create as HTML
  Parameters: {"utf8"=&amp;gt;"✓", "authenticity_token"=&amp;gt;"hGKDQsOQPBhpEyKDuLhOIOTNDGJ+VlZoNrLblcVNEl1cIXcTh+ePmy46G7ZdfUZlaUZJqsHcIG/ny4WTEo3MRw==", "update_file"=&amp;gt;{"name"=&amp;gt;"", "file_url"=&amp;gt;#, &lt;a href="/original_filename" class="user-mention" title="@original_filename"&gt;&lt;i&gt;@&lt;/i&gt;original_filename&lt;/a&gt;="welcome.html.bak.erb", &lt;a href="/content_type" class="user-mention" title="@content_type"&gt;&lt;i&gt;@&lt;/i&gt;content_type&lt;/a&gt;="application/octet-stream", &lt;a href="/headers" class="user-mention" title="@headers"&gt;&lt;i&gt;@&lt;/i&gt;headers&lt;/a&gt;="Content-Disposition: form-data; name=\"update_file[file_url]\"; filename=\"welcome.html.bak.erb\"\r\nContent-Type: application/octet-stream\r\n"&amp;gt;, "file_url_cache"=&amp;gt;""}, "commit"=&amp;gt;"提交"}&lt;/p&gt;

&lt;p&gt;然后在 modle 写了一个 after_create 回调 代码是这样的：&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;UpdateFile&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
   &lt;span class="n"&gt;mount_uploader&lt;/span&gt; &lt;span class="ss"&gt;:file_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;UpdatefileUploader&lt;/span&gt;
   &lt;span class="n"&gt;after_create&lt;/span&gt; &lt;span class="ss"&gt;:save_filename&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_filename&lt;/span&gt;
     &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file_url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_original_filename&lt;/span&gt;
         &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&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;直接得到 original_filename，存到数据库表里，但是这样报了一个 错误，private method `original_filename' called for #&lt;a rel="nofollow" target="_blank"&gt;UpdatefileUploader:0x007fdbaddbfc00&lt;/a&gt;. original_filename 是私有方法。外部不能调用。
  好吧，和同事讨论后。决定在 updatefile-uploader。rb 文件中，写一个 public 的方法，去得到私有的 original-filename
这样 &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;get_original_filename&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;original_filename&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在修改 model 中 after_create  的方法为 &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;save_filename&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file_url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_original_filename&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ok 顺利通过。 
    从这个问题也学习到了，如果一个方法或变量是私有的，可以在那个类里面写一个 public 的方法来获得这个方法的返回值或变量
    供给外部调用。第一次发帖，经验不足，请大家指正。&lt;/p&gt;</description>
      <author>pathbox</author>
      <pubDate>Wed, 27 May 2015 15:29:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/25765</link>
      <guid>https://ruby-china.org/topics/25765</guid>
    </item>
  </channel>
</rss>
