<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>davidgao (TTalkIM)</title>
    <link>https://ruby-china.org/davidgao</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>Erlang 语言的教程</title>
      <description>&lt;p&gt;之前一直想做一些 Erlang 的视频教程，但是一直迟迟没有开始。但是挖的坑总是要填的，也是受到一个人的鼓励说做自己就好。那么就开始一点点填这个坑好了。&lt;/p&gt;

&lt;p&gt;第一次录制，录了好多次都不太成功，后面稿子都记不住了，自己对这次录制不是很满意，但是万事总要有个开始。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=xyX3iHKXOP8&amp;amp;list=PLiuYY2qq9bJiUAy4ospyVPZL9s2-mpw-l" rel="nofollow" target="_blank" title=""&gt;油管&lt;/a&gt;
&lt;a href="https://www.bilibili.com/video/av82130710/" rel="nofollow" target="_blank" title=""&gt;B 站&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;还请各位大佬们多提意见&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Sun, 05 Jan 2020 10:44:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/39405</link>
      <guid>https://ruby-china.org/topics/39405</guid>
    </item>
    <item>
      <title>Erlang 服务端渲染引擎 aihtml 发布 v0.3.2</title>
      <description>&lt;p&gt;使用 Erlang 开发 Web 是一个非常有意思的事情，但是很多时候需要自己创造一些小轮子。Erlang 虽然有很多 Web 开发方案，但是对模版渲染这部分相对较弱。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DavidAlphaFox/aihtml" rel="nofollow" target="_blank" title=""&gt;aihtml&lt;/a&gt;是个人在使用 Erlang 开发企业 Web 应用时，开发的一款 mustache 模版渲染引擎。全面支持 mustache 的标准语法，同时增加自己的 lambda 语法，解决 layout 式的渲染。&lt;/p&gt;

&lt;p&gt;当然如果您的 Web 项目需要非常复杂的服务端渲染，除了&lt;a href="https://github.com/erlydtl/erlydtl" rel="nofollow" target="_blank" title=""&gt;erlydtl&lt;/a&gt;外，zontonic 也发布了重写的版本 &lt;a href="https://github.com/zotonic/template_compiler" rel="nofollow" target="_blank" title=""&gt;template_compiler&lt;/a&gt;，该版本 Bug 更少，速度更快。相对于 erlydtl 和 zontonic，aihtml 更加简单，属于无逻辑模版范畴，适用与轻量快捷开发的项目。&lt;/p&gt;

&lt;p&gt;希望更多的 Erlang 的 Web 开发者能适用和提交 issues 来优化 aihtml 这个简单的模版引擎。&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Tue, 31 Dec 2019 15:22:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/39387</link>
      <guid>https://ruby-china.org/topics/39387</guid>
    </item>
    <item>
      <title>NPM 仓库镜像工具发布 0.1.0</title>
      <description>&lt;p&gt;故事就不讲了，因为一些特殊需要，无法定制现有开源产品，就自己开发了一个 NPM 镜像工具。&lt;/p&gt;

&lt;p&gt;今天按照计划发布了 0.1.0 版本，虽然它还很弱小，但是相信在公司和开源的各位大神的帮助下，它会不断强大起来。&lt;/p&gt;

&lt;p&gt;项目地址：&lt;a href="https://github.com/DavidAlphaFox/ai_npm/releases/tag/0.1.0" rel="nofollow" target="_blank"&gt;https://github.com/DavidAlphaFox/ai_npm/releases/tag/0.1.0&lt;/a&gt;&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Thu, 01 Nov 2018 10:52:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/37713</link>
      <guid>https://ruby-china.org/topics/37713</guid>
    </item>
    <item>
      <title>深入浅出 Mnesia－schema 创建 (2)</title>
      <description>&lt;h2 id="FALLBACK.BUP生成schema.DAT时机"&gt;FALLBACK.BUP 生成 schema.DAT 时机&lt;/h2&gt;
&lt;p&gt;前面的文章提到了&lt;a href="https://www.ttalk.im/topics/52" rel="nofollow" target="_blank" title=""&gt;如何生成 FALLBACK.BUP&lt;/a&gt;，但没有提到 FALLBACK.BUP 是怎样生成 schema.DAT 文件的。想要知道 FALLBACK.BUP 是如何生成 schema.DAT，就需要去观察 Mnesia 的启动流程和监控树。&lt;/p&gt;

&lt;p&gt;通过对代码的分析，可以非常清晰的看到，Mnesia 的主要进程都被 mnesia_kernel_sup 这个监控者进程下&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;ProcLib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mnesia_monitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proc_lib&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nv"&gt;Flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;one_for_all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt; &lt;span class="c"&gt;% Trust the top supervisor
&lt;/span&gt;    &lt;span class="c"&gt;%% 最先启动的是mnesia_monitor
&lt;/span&gt;    &lt;span class="c"&gt;%% mnesia_monitor持有mnesia_gvar和mnesia_stats两张ets表
&lt;/span&gt;    &lt;span class="c"&gt;%% mnesia的全局变量全都保存在此处
&lt;/span&gt;    &lt;span class="nv"&gt;Workers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;worker_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_monitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;seconds&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gen_server&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="c"&gt;%% mnesia_subscr 创建订阅管理进程
&lt;/span&gt;        &lt;span class="c"&gt;%% 自动将mnesia_event加入到系统订阅表中
&lt;/span&gt;           &lt;span class="nf"&gt;worker_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_subscr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;seconds&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gen_server&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="c"&gt;%% mnesia的锁管理进程
&lt;/span&gt;           &lt;span class="nf"&gt;worker_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_locker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;seconds&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="nv"&gt;ProcLib&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
         &lt;span class="c"&gt;%% mnesia恢复进程
&lt;/span&gt;           &lt;span class="nf"&gt;worker_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_recover&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;minutes&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gen_server&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
         &lt;span class="c"&gt;%% mnesia事务进程
&lt;/span&gt;           &lt;span class="nf"&gt;worker_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_tm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;ProcLib&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
         &lt;span class="c"&gt;%% 检察点监控者进程
&lt;/span&gt;           &lt;span class="nf"&gt;supervisor_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_checkpoint_sup&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
         &lt;span class="c"&gt;%% snmp监控者进程
&lt;/span&gt;           &lt;span class="nf"&gt;supervisor_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_snmp_sup&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
         &lt;span class="c"&gt;%% mnesia主控进程
&lt;/span&gt;           &lt;span class="nf"&gt;worker_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;seconds&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gen_server&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
         &lt;span class="c"&gt;%% mnesia数据加载进程
&lt;/span&gt;           &lt;span class="nf"&gt;worker_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_late_loader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;seconds&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="nv"&gt;ProcLib&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;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Workers&lt;/span&gt;&lt;span class="p"&gt;}}.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过逐个进程的检察，在 mnesia_tm 进程初始化的时候，会通过 mnesia_bup:tm_fallback_start 函数来使用 FALLBACK.BUP 进行数据恢复，在此过程中就会生成 schema.DAT。&lt;/p&gt;
&lt;h2 id="恢复流程"&gt;恢复流程&lt;/h2&gt;&lt;h3 id="tm_fallback_start函数"&gt;tm_fallback_start 函数&lt;/h3&gt;
&lt;p&gt;在 mnesia_bup:tm_fallback_start 函数中，整个操作过程都是锁住 schema 表进行操作的，在这个过程中，本节点上所有的其它 schema 操作都会进行等待。同时，这个函数的整体操作都是在 mnesia_tm 进程内进行的。&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="c"&gt;%执行回滚操作
&lt;/span&gt;&lt;span class="nf"&gt;do_fallback_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting from fallback...&lt;/span&gt;&lt;span class="si"&gt;~n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
    &lt;span class="c"&gt;%拿到备份文件
&lt;/span&gt;    &lt;span class="nv"&gt;BupFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fallback_bup&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nv"&gt;Mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mnesia_backup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c"&gt;%创建一个ets，用来保存本地表
&lt;/span&gt;    &lt;span class="nv"&gt;LocalTabs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nf"&gt;ets_new_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mnesia_local_tables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;keypos&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="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="nf"&gt;iterate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;restore_tables&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;BupFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;LocalTabs&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="nv"&gt;Res&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;%%  让dets关闭掉schema
&lt;/span&gt;            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="nn"&gt;dets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c"&gt;%% 设置临时的文件为schema.TMP
&lt;/span&gt;            &lt;span class="nv"&gt;TmpSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;mnesia_lib&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;tab2tmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c"&gt;%% 设置数据文件为schema.DAT
&lt;/span&gt;            &lt;span class="nv"&gt;DatSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;mnesia_lib&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;tab2dat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c"&gt;%% 得到所有本地表
&lt;/span&gt;              &lt;span class="nv"&gt;AllLT&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nf"&gt;ets_match_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;LocalTabs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c"&gt;%关闭ets
&lt;/span&gt;              &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nf"&gt;ets_delete_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;LocalTabs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c"&gt;%% schema.TMP重命名为schema.DAT
&lt;/span&gt;            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nn"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;TmpSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;DatSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="c"&gt;%% 除了schema表外，全部进行swap操作
&lt;/span&gt;                        &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="nv"&gt;LT&lt;/span&gt;&lt;span class="nl"&gt;#local_tab.swap&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nv"&gt;LT&lt;/span&gt;&lt;span class="nl"&gt;#local_tab.name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;LT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt;
                             &lt;span class="nv"&gt;LT&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nv"&gt;AllLT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;LT&lt;/span&gt;&lt;span class="nl"&gt;#local_tab.name&lt;/span&gt; &lt;span class="o"&gt;=/=&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="nn"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;BupFile&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="nn"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;TmpSchema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Cannot start from fallback. Rename error."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reason&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="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Cannot start from fallback"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reason&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;'EXIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Cannot start from fallback"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reason&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;do_fallback_start 函数会进行数据恢复的准备工作，它进行了下面这些工作&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;建立 mnesia_local_tables 的 ets 表，用来保存 restore_tables 函数在恢复过程中，恢复出本节点内的表的信息&lt;/li&gt;
&lt;li&gt;生成 schema.DAT 文件&lt;/li&gt;
&lt;li&gt;生成本节点内所有表的.DAT 文件，.DCL 文件和.DCD 文件&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="restore_tables函数"&gt;restore_tables 函数&lt;/h3&gt;
&lt;p&gt;restore_tables 函数依旧是依赖 mnesia_bup 的通用函数 iterate，将 FALLBACK.BUP 文件中的 schema 数据和表项目数据读取出来，并逐条遍历。&lt;/p&gt;

&lt;p&gt;restore_tables 函数会有几个状态，这几个状态分别是&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;{start, LocalTabs}，从 FALLBACK.BUP 中读取 schema 信息，根据表信息构建 local_tab 这个 record，并保存到 mnesia_local_tables 中，为了可以在后面的恢复操作中使用&lt;/li&gt;
&lt;li&gt;{new, LocalTabs} ，开始各表的数据恢复，会对 FALLBACK 中的数据项的 record 名和 mnesia_local_tables 的 table 名称进行比对，从而决定是恢复数据还是忽略&lt;/li&gt;
&lt;li&gt;{not_local, LocalTabs, Tab}，如果在 mnesia_local_tables 查找不到对应的表名称的时候，就会进入此状态，这个过程中读取的数据会全部忽略掉&lt;/li&gt;
&lt;li&gt;{local, LocalTabs, LT}，在 mnesia_local_tables 中查找到对应表明，进入此状态，进行数据恢复&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="init_dat_files函数"&gt;init_dat_files 函数&lt;/h3&gt;
&lt;p&gt;init_dat_files 是 restore_tables 在构建 mnesia_local_tables 中项目的重要函数。&lt;/p&gt;

&lt;p&gt;它的主要工作有：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;根据 schema 提供的信息生成 local_tab 的 record，包括 schema 表自身的信息&lt;/li&gt;
&lt;li&gt;创建除了 schema 表外所有的表的数据文件&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在仔细观察这个函数会发现，针对存储类型为 disc_only_copies 的表会建立一个 dets，而对 ram_copies 和 disc_copies 类型存储的表只会建立.DCL 日志存储和.DCD 存储。存储方式的不同直接影响到了 Mnesia 如何读取数据和管理数据，在后面的文章将逐步分析相关内容。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;从整个过程中，可以看到 Mnesia 创建 schema 的过程后半段不仅仅可以创建一个 schema.DAT 文件，而且能创建包含数据的表。同时可以看出，Mnesia 的 Schema.DAT 就是一个 dets 文件，完全可以简单的创建出来，但是这个过程确大费周章。当然这样做是有很大的原因的，这就需要考察 Mnesia 的备份机制和远程安装数据的机制了，在分析完 Mnesia 的启动和读写过程后，将会逐步对相关机制展开分析。&lt;/p&gt;

&lt;p&gt;转载自&lt;a href="https://www.ttalk.im/topics/54" rel="nofollow" target="_blank" title=""&gt;TechTalk&lt;/a&gt;&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Tue, 19 Dec 2017 09:59:51 +0800</pubDate>
      <link>https://ruby-china.org/topics/34726</link>
      <guid>https://ruby-china.org/topics/34726</guid>
    </item>
    <item>
      <title>为了推广函数类语言建立一个小社区</title>
      <description>&lt;h2 id="感谢"&gt;感谢&lt;/h2&gt;
&lt;p&gt;首先毋庸置疑的是要感谢 Mr.Li 的 homeland，以及 Ruby China。同时感谢 Github 提供了一个非常完善的用户平台。&lt;/p&gt;
&lt;h2 id="Tech Talk的由来及目标"&gt;
&lt;a href="https://ttalk.im" rel="nofollow" target="_blank" title=""&gt;Tech Talk&lt;/a&gt;的由来及目标&lt;/h2&gt;
&lt;p&gt;今年 RubyConf 上似乎有人做了两篇关于函数类语言的演讲，一篇关于 Erlang，一篇关于 Haskell。但是反响似乎不是很高。纵观国内对函数类语言的使用是非常少的，甚至是少的可怜。&lt;/p&gt;

&lt;p&gt;Erlang 在国内曾有一段热度很高，但是就如昙花一现，很快就消退了。种种原因致使，Erlang，Haskell 以及 Clojure 等没在国内得到充分推广。包括七牛的许式伟可以明目张胆的说瞎话攻击 Erlang 语言，就是因为国内知道的人太少了，很难成为一种大趋势。&lt;/p&gt;

&lt;p&gt;为了让更多的人知道函数类语言，以及它能做什么，使用 Homeland 搭建了一个小社区。希望能把为数不多的人聚集到一起，大家一起开开心心的讨论一些问题，把知识和经验积累下来，让新人更容易接触到相关产品和解决问题的经验。并希望能为已经使用的函数类语言的公司，提供一些帮助和人才招聘的地方，提高函数类语言在国内的影响力。从实际的方面来讲，也希望更多公司能采用一些函数语言，让函数类语言的程序员有更多的选择。&lt;/p&gt;
&lt;h2 id="和Ruby China的节点有什么不同"&gt;和 Ruby China 的节点有什么不同&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Ruby China 也有 Elixir／Erlang，Haskell 等这样的节点，但是 Ruby China 本身更注重的是 Ruby 以及 ruby 周边产品的研究，而&lt;a href="https://ttalk.im" rel="nofollow" target="_blank" title=""&gt;Tech Talk&lt;/a&gt;更倾注于 Erlang，Haskell 等这种函数类语言和相关产品，以及人工智能和机器学习。&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ttalk.im" rel="nofollow" target="_blank" title=""&gt;Tech Talk&lt;/a&gt; 虽然基于 Homeland，但是关闭掉了非 Github 的注册方式，尽量避免让大家重复注册帐号。&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ttalk.im" rel="nofollow" target="_blank" title=""&gt;Tech Talk&lt;/a&gt; 整个站点偏向博客风格多一些，当前并不开启 Wiki 功能，待文章数量和问答数量达到一定程度再整体整理并建立 Wiki。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;来自于&lt;a href="https://ttalk.im" rel="nofollow" target="_blank" title=""&gt;Tech Talk&lt;/a&gt; 的感谢和问候&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Sat, 23 Sep 2017 16:12:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/34236</link>
      <guid>https://ruby-china.org/topics/34236</guid>
    </item>
    <item>
      <title>像架构师一样来思考微服务接口设计</title>
      <description>&lt;h2 id="如何做接口的设计"&gt;如何做接口的设计&lt;/h2&gt;
&lt;p&gt;从我个人角度来说，可以从以下几个特性进行分析：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;大规模系统和小规模系统&lt;/li&gt;
&lt;li&gt;面向内部系统接口和面向外部系统的接口&lt;/li&gt;
&lt;li&gt;大数据传输的接口和小数据传输的接口&lt;/li&gt;
&lt;li&gt;长链接的接口和短链接的接口&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;很多时候我们优先考虑的是系统有多大，扩张性要有多好，对内还是对外以及我们有多大的能力。很多时候这个东西并没有一个定论，更多是基于业务和团队人员组成而决定的。&lt;/p&gt;
&lt;h2 id="接口的实施"&gt;接口的实施&lt;/h2&gt;
&lt;p&gt;一定要做的事情&lt;/p&gt;

&lt;p&gt;不管接口是对内的还是对外的，我们都要做以下几件事情：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;接口功能定义是否明确，是否有功能重复的地方&lt;/li&gt;
&lt;li&gt;接口的升级机制，是否能兼容以前的数据&lt;/li&gt;
&lt;li&gt;接口的数据量是多少，是否需要使用传输压缩机制&lt;/li&gt;
&lt;li&gt;接口的熔断点在何处，何时该降级或停止服务&lt;/li&gt;
&lt;li&gt;接口的安全机制是怎么样的，如何将非法调用隔离开来&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这些事情是我们在开始设计和实现接口的时候，必须要先想到的。但是不要认为我们想到了这些东西，我们就可以高枕无忧，然后事情就会像我们期待的那样发展下去。很多时候，接口都会变成像阿米巴原虫一样，不是圆的而是不规则的多边形。&lt;/p&gt;
&lt;h3 id="对内的接口"&gt;对内的接口&lt;/h3&gt;
&lt;p&gt;对内的接口简单说就是 SOA，但是 SOA 也有很多种做法，例如常见的 dubbo 框架。在 dubbo 框架下，我们所做的事情完全可以说是在 dubbo 框架下进行业务开发，并定义 interface 然后暴露出去，我们此时貌似没有进行接口设计，但是实际上我们是完全按照 dubbo 的规范完成了接口的定义，没错就是那个 interface。看起来对内部的接口完全非常明确了，没什么可讲的，但是其中还是有很多东西可讲的，我先讲讲我们常见的。&lt;/p&gt;

&lt;p&gt;对服务发现的方案选择：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;使用主动推送的方式，注册中心每次发生变化都会推送最新的列表给服务的使用者&lt;/li&gt;
&lt;li&gt;使用被动拉取的方式，注册中心每次变化都保存好，然后使用者每次调用服务者的时候，先到注册中心查询一次&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;好了，让我分别来说说这两个方案&lt;/p&gt;
&lt;h4 id="使用主动推送"&gt;使用主动推送&lt;/h4&gt;
&lt;p&gt;可以让使用者很快的更新服务者信息，使用者调用服务者的时候只需要在本地的一个 hash 表中查询一下即可，并且注册中心挂掉了之后，也不影响使用者调用服务者，看起来不错吧。那么让我来说说这方案的弊端，首先要实现 watch－notify 机制，大概有人会说不是有 Zookeeper 吗？自带该机制和数据冗余机制，那么我想说的是，当业务量起来的时候，Zookeeper 的 watch 机制真的能顶住吗？接着是，服务者的负载均衡并不好处理。那么有没有解决方法，这个可以参看 dubbo 中的注册中心是如何玩耍的。&lt;/p&gt;
&lt;h4 id="使用被动拉取"&gt;使用被动拉取&lt;/h4&gt;
&lt;p&gt;这个好像很直观，但是每次都查询注册中心，这性能，注册中心能处理的了吗？大家不妨想一下 DNS 服务器，其实该方案完全可以使用简单的内部 DNS 实现。那么该方案的好处不言而喻，负载均衡好处理，并且非常简单。但是问题呢，性能和稳定性是要深入考虑的事情。&lt;/p&gt;
&lt;h3 id="传输协议"&gt;传输协议&lt;/h3&gt;
&lt;p&gt;剩下的就是需要考虑的传输协议了，为什么要考虑传输协议？原因很简单：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;接口平均传输的数据量和自己的内网带宽的平衡&lt;/li&gt;
&lt;li&gt;是否要跨语言协作&lt;/li&gt;
&lt;li&gt;是否侵入业务了&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="为何考虑带宽"&gt;为何考虑带宽&lt;/h4&gt;
&lt;p&gt;虽然注册中心第一步解决了我们的快速扩张的问题，但是呢，内网带宽毕竟是有限的。随着服务数量增多和调用量的增加，有时候我们会发现，同一个服务我们明明增加了 N 台部署响应时间却下降了很多，按照公式应该响应时间不变的呀？这个时候，我们可能猛然看到监控上我们的内网带宽已经跑满了。&lt;/p&gt;
&lt;h4 id="为何考虑跨语言"&gt;为何考虑跨语言&lt;/h4&gt;
&lt;p&gt;难道一个公司不就是一种后端语言？其实不然，我曾见面试过一个公司，内部的业务之复杂，语言使用之繁多。很多时候，我们需要站在一个公司发展的角度上考虑这个问题，而不是一个纯技术的细节上考虑这个问题。&lt;/p&gt;
&lt;h4 id="为什么要考虑是否侵入业务"&gt;为什么要考虑是否侵入业务&lt;/h4&gt;
&lt;p&gt;不侵入业务，就是尽可能的封装底层的实现，让业务线更少的去考虑底层发生什么了。很多人说，这对业务线的人不公平，阻碍了他们的技术发展。其实不然，让业务线的同仁们更多，更深入的思考业务发展是非常重要的事情，我个人认为研发分两类，一类是玩算法和底层的，另一类就是深入业务的，他们都有自己的长处和短处。其实减少业务的侵入是为了更快的实现产品功能，让产品上线，让公司的业务快速迭代起来，这样对任何人都是有好处的。&lt;/p&gt;
&lt;h3 id="接口升级"&gt;接口升级&lt;/h3&gt;
&lt;p&gt;这个与其说是升级，不如说是怎么做不同版本的数据共存和 A/B 测试。一般在很多成行的 SOA 系统中，已经很完善了，我没必要在这里面多废话。但是还是要多说一句，数据多版本不易，且升且小心。&lt;/p&gt;
&lt;h3 id="对外接口"&gt;对外接口&lt;/h3&gt;
&lt;p&gt;对外接口，大家很快就会想到 Restful。随着现在创业的兴起，应当说是智能手机和 Web2.0 的兴起（更应该说的本质是，网络带宽变好，手机流量降价）。但是对外接口并不限于 Restful，还有大家不愿意谈的纯 Socket 接口。对外接口可讲的东西非常多，不过思路上基本上和对内接口没太大的差别，所以我这里就主要讲下为什么选择纯 Socket 的接口。&lt;/p&gt;

&lt;p&gt;我们不愿意面对的长链接，很多研发，甚至公司级别，都不愿意去尝试这个技术。原因嘛，请看下面：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;调试复杂，研发成本高&lt;/li&gt;
&lt;li&gt;国内网络环境复杂，加重了第一条&lt;/li&gt;
&lt;li&gt;国内用户对流量敏感，长链接心跳控制不好，容易被认为是偷流量&lt;/li&gt;
&lt;li&gt;协议设计比较复杂，对研发的要求上升了很多&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;但是长链接真的就那么难嘛，其实不然。更多时候，是产品层面用不上，一般只有 IM 类型的应用或者实时对战类的游戏才会选择长链接。当然偶尔我们也想提供一些高互动的交互，如果只是在应用内短暂使用，完全可以选择 websocket（不过面对中国强大的高铁和运营商基础建设的规划 TT）。&lt;/p&gt;
&lt;h2 id="接口的保护"&gt;接口的保护&lt;/h2&gt;&lt;h3 id="安全保护"&gt;安全保护&lt;/h3&gt;
&lt;p&gt;当我们面对很多外部接口的时候，我们需要考虑数据的安全性。为什么要考虑安全性：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;包含用户数据&lt;/li&gt;
&lt;li&gt;包含交易数据&lt;/li&gt;
&lt;li&gt;以及甚至你不想让用户自己知道的数据&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;保护接口的方式最基本的是 SSL/TLS，然后呢：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;对称加密的方式&lt;/li&gt;
&lt;li&gt;非对称加密的方式&lt;/li&gt;
&lt;li&gt;动态秘钥&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;先说下我们为什么要在 SSL/TLS 下面再次进行加密呢？大家可能听说过以色列一个网络安全公司的事情了，换句话说一旦根证书被释放出去了，分分钟可以做 SSL/TLS 的 man in middle 的攻击。同时有些稍微高级的用户，会针对你的接口进行刷接口的行为。&lt;/p&gt;
&lt;h4 id="对称加密"&gt;对称加密&lt;/h4&gt;
&lt;p&gt;简单且易用。但是问题也明显，一旦秘钥泄漏或者被用户强猜出来了，那么影响还是很大的。&lt;/p&gt;
&lt;h4 id="非对称加密的方式"&gt;非对称加密的方式&lt;/h4&gt;
&lt;p&gt;实现略复杂，同样也面临第一种方式的问题。但是可以有一个专门的秘钥管理人员，生成公钥和秘钥对后，将公钥交付给客户端，将秘钥交付给服务器端，大大减少了泄漏的可能性。同时用户即便猜出了客户端的公钥，也无法解密别的用户提交的数据，而只能伪造请求。&lt;/p&gt;
&lt;h4 id="动态秘钥"&gt;动态秘钥&lt;/h4&gt;
&lt;p&gt;机器在运行的时候，定期自动和秘钥管控中心进行秘钥交换，每台机器在交互的时候使用的秘钥都不同。虽然可以带来一定的安全性，但是会给秘钥管理中心带来巨大的压力，同时调试也比较麻烦。这种方式个人认为适合使用在，传统小交易量的行业中，例如说银行的 ATM 机。&lt;/p&gt;
&lt;h3 id="熔断保护"&gt;熔断保护&lt;/h3&gt;&lt;h4 id="内部接口需要吗？"&gt;内部接口需要吗？&lt;/h4&gt;
&lt;p&gt;我们可以假定一个场景，服务者 A 有 10 个服务器，但是由于使用者 B 的算法错误，总是先选择服务者 A 的某台服务器，那么我们可以想象到服务者 A 的某台服务器压力非常大，然后逐步的就失去了响应，接着就会被认为被离线，接着使用者 B 又同样的方式打掉了第二台服务器。带来的影响就是，轻者响应速度很慢，严重的就是整个系统雪崩性的逐个崩溃停止服务。&lt;/p&gt;
&lt;h4 id="一般怎么做"&gt;一般怎么做&lt;/h4&gt;
&lt;p&gt;不管对内部还是对外部，我们都可以选择使用漏桶和令牌桶等算法来保护接口。对外部，我们还可以通过使用时间戳加整个 URL 整体签名技术来防止重放攻击和进行限流保护。&lt;/p&gt;

&lt;p&gt;转载自&lt;a href="https://www.ttalk.im/pages/995338350745354250" rel="nofollow" target="_blank" title=""&gt;TechTalk&lt;/a&gt;&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Fri, 04 Aug 2017 13:44:58 +0800</pubDate>
      <link>https://ruby-china.org/topics/33723</link>
      <guid>https://ruby-china.org/topics/33723</guid>
    </item>
    <item>
      <title>Erlang 的 RPC 模块代码分析</title>
      <description>&lt;h2 id="RPC的代码分析"&gt;RPC 的代码分析&lt;/h2&gt;&lt;h3 id="进程创建"&gt;进程创建&lt;/h3&gt;
&lt;p&gt;RPC 模块本身是一个 gen_server 会随着 kernel 模块启动，也就是说，在 Erlang/OTP 启动后我们就免费获得了一个 RPC 进程。
RPC 进程启动的时候，会在 Erts 中通过 local 注册一个名字 rex 的进程，这样没有经过修改的 Erlang/OTP 都会有这个名字在它的名字列表上。&lt;/p&gt;
&lt;h3 id="RPC调用逻辑"&gt;RPC 调用逻辑&lt;/h3&gt;
&lt;p&gt;不管是同步调用还是广播调用，在 RPC 模块中的调用都是依赖 gen_server 的相关方和 erlang:send 方法来完成。这样尽最大可能的重用代码，保证了整个 OTP 中对远程调用的表现的一致性。
并且 RPC 模块不单单可以调用远程节点的方法或进程，也可以调用本地节点的方法或进程，这样保证了整个 RPC 的系统位置透明性，并且 RPC 模块针对本地节点作了相关优化。&lt;/p&gt;

&lt;p&gt;例如说 call 方法针对本地节点就采用了下面的方法：&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;local_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="nb"&gt;is_atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;is_atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;is_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="nb"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;'EXIT'&lt;/span&gt;&lt;span class="p"&gt;,_}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;V&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;badrpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;V&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nv"&gt;Other&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;Other&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="call和block_call方法"&gt;call 和 block_call 方法&lt;/h3&gt;
&lt;p&gt;这两个方法都是同步的调用，但是实现的细节非常不同，对 rex 进程的影响也是不同的。当然使用两个方法在并发执行的情况下，得到的结果是完全不同的。
不管是 call 也好，block_call 也好，都会在执行阶段暂时的将被调用者进程的 console 输出重定向到调用者进程所在节点的 group leader 上。&lt;/p&gt;
&lt;h4 id="call方法"&gt;call 方法&lt;/h4&gt;
&lt;p&gt;在调用发起者一侧，RPC 模块会立刻建立一个监控下的 Erlang 进程，并在该进程内通过 gen_server:call 方法来调用远程节点。&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;do_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;rpc_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="nn"&gt;gen_server&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="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nf"&gt;do_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;Tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Mref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;spawn_monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="nb"&gt;process_flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trap_exit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nv"&gt;Result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;gen_server&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="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Timeout&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="nv"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&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="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;receive&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;'DOWN'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Mref&lt;/span&gt;&lt;span class="p"&gt;,_,_,{&lt;/span&gt;&lt;span class="nv"&gt;Receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;rpc_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;'DOWN'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Mref&lt;/span&gt;&lt;span class="p"&gt;,_,_,&lt;/span&gt;&lt;span class="nv"&gt;Reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;rpc_check_t&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;'EXIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Reason&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;RPC 模块会将超时或对方节点失去连接的情况处理为 bad_rpc，让顶层逻辑发现并非业务本身引起的远程调用问题。&lt;/p&gt;

&lt;p&gt;在被调用者一些，RPC 模块也会立刻创建一个监控下的 Erlang 进程，并在该进程内处理调用者的 call 消息，同时会将相关信息保存在 rex 进程的进程上下文中。当新的进程完成了业务处理，就会把处理结果返回给被调用者节点的 rex 进程，然后再将结果返回给调用发起者。
我们可以仔细观察它的代码：&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;handle_call_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Gleader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;To&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;RpcServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="c"&gt;%% Spawn not to block the rpc server.
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Caller&lt;/span&gt;&lt;span class="p"&gt;,_}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;spawn_monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="nf"&gt;set_group_leader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Gleader&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nv"&gt;Reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="nb"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;'EXIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Exit&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;badrpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
              &lt;span class="nv"&gt;Result&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                  &lt;span class="nv"&gt;Result&lt;/span&gt;
              &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nv"&gt;RpcServer&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reply&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="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;gb_trees&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Caller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;To&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="block_call方法"&gt;block_call 方法&lt;/h4&gt;
&lt;p&gt;于 call 方法一样，在调用发起者一侧，RPC 模块会立刻建立一个监控下的 Erlang 进程，并在该进程内通过 gen_server:call 方法来调用远程节点。&lt;/p&gt;

&lt;p&gt;但是在被调用者一些，RPC 模块会选择使用被调用者所在节点的 rex 直接执行相关代码&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;handle_call&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;block_call&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Gleader&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="nv"&gt;To&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;MyGL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;group_leader&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;set_group_leader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Gleader&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nv"&gt;Reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="nb"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;'EXIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Exit&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;badrpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nv"&gt;Other&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nv"&gt;Other&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;group_leader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;MyGL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="c"&gt;% restore
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="同步调用总结"&gt;同步调用总结&lt;/h4&gt;
&lt;p&gt;call 方法可以保证，同一调用者的远程请求按序列执行，但是不保证多个调用者的远程请求按序列执行。
block_call 方法保证，多个调用者的远程请求按序列执行。
不管是 call 还是 block_call 的方法都会给调用者带来大量的进程创建的压力（Erlang 创建进程很快，但不代表没有代价）。
call 方法还会给被调用者节点带来大量的进程创建压力。&lt;/p&gt;
&lt;h3 id="cast方法"&gt;cast 方法&lt;/h3&gt;
&lt;p&gt;RPC 模块的 cast 方法直接依赖于 gen_sever:cast，并没有做更多的事情。&lt;/p&gt;

&lt;p&gt;针对本地节点，cast 方法会在调用者节点内创建一个进程来执行相关代码：&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="nv"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;=:=&lt;/span&gt; &lt;span class="nb"&gt;node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="nb"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nn"&gt;gen_server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;group_leader&lt;/span&gt;&lt;span class="p"&gt;()}),&lt;/span&gt;
    &lt;span class="n"&gt;true&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 erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;handle_cast&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Gleader&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="nf"&gt;set_group_leader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Gleader&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nb"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Args&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="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="cast方法总结"&gt;cast 方法总结&lt;/h4&gt;
&lt;p&gt;cast 方法是非常简单的。和 call 方法一样，会给被调用者节点带来大量的进程创建压力。
同样不要忘记了，cast 方法也会将新创建的进程的 console 输出重新定向调用者所在节点的 group leader 上。&lt;/p&gt;
&lt;h3 id="abcast和sbcast"&gt;abcast 和 sbcast&lt;/h3&gt;
&lt;p&gt;这两个方法都是通过 erlang:send 将调用者的消息发送到被调用者节点上。&lt;/p&gt;
&lt;h4 id="abcast"&gt;abcast&lt;/h4&gt;
&lt;p&gt;abcast 采用的是纯异步，发出去就不管了，直接将消息不经过 rex 进程直接发送到目标进程上&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;
&lt;span class="nf"&gt;abcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;abcast&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nb"&gt;node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;

&lt;span class="nf"&gt;abcast&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;Dest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c"&gt;%这么做的好处是不会让进程被trap
&lt;/span&gt;    &lt;span class="c"&gt;%从而保证了异步性
&lt;/span&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Dest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;noconnect&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
    &lt;span class="n"&gt;noconnect&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Dest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;abcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;abcast&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="p"&gt;_,_)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;abcast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此处 abcast 完全是异步的，如果发现了目标节点是没有连接的时候，直接创建一个新的进程来进行消息发送，完全不会进入 Trap 状态等待节点连接。&lt;/p&gt;
&lt;h4 id="sbcast"&gt;sbcast&lt;/h4&gt;
&lt;p&gt;sbcast 算是同步的广播方式，发送后会回收广播结果，并且当节点没有完成连接的时候，会进入 Trap 状态等待节点连接完成&lt;/p&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;sbcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;sbcast&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nb"&gt;node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;

&lt;span class="nf"&gt;sbcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;Monitors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;send_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sbcast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Mess&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
    &lt;span class="nf"&gt;rec_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Monitors&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;

&lt;span class="nf"&gt;send_nodes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Monitors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="nb"&gt;is_atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;Monitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;start_monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c"&gt;%% Handle non-existing names in rec_nodes.
&lt;/span&gt;    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Node&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="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;send_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Monitor&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;Monitors&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nf"&gt;send_nodes&lt;/span&gt;&lt;span class="p"&gt;([_&lt;/span&gt;&lt;span class="nv"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Monitors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;%% Skip non-atom _Node
&lt;/span&gt;    &lt;span class="nf"&gt;send_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Monitors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;send_nodes&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="nv"&gt;Req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Monitors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; 
    &lt;span class="nv"&gt;Monitors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nf"&gt;rec_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; 
    &lt;span class="nf"&gt;rec_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Nodes&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="nf"&gt;rec_nodes&lt;/span&gt;&lt;span class="p"&gt;(_&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;  &lt;span class="nv"&gt;Badnodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Replies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Replies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Badnodes&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nf"&gt;rec_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;R&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;Badnodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Replies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;receive&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;'DOWN'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;R&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="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;rec_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Badnodes&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;Replies&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nonexisting_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  
        &lt;span class="c"&gt;%% used by sbcast()
&lt;/span&gt;        &lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;demonitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="nf"&gt;rec_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Badnodes&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;Replies&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reply&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="c"&gt;%% Name is bound !!!
&lt;/span&gt;        &lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;demonitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="nf"&gt;rec_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Tail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Badnodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Reply&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Replies&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;转载自&lt;a href="https://www.ttalk.im/pages/118/RPC%20%E6%A8%A1%E5%9D%97%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90.html" rel="nofollow" target="_blank" title=""&gt;TechTalk&lt;/a&gt;&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Mon, 31 Jul 2017 16:08:23 +0800</pubDate>
      <link>https://ruby-china.org/topics/33677</link>
      <guid>https://ruby-china.org/topics/33677</guid>
    </item>
    <item>
      <title>Linux 线程和信号</title>
      <description>&lt;h2 id="什么是线程"&gt;什么是线程&lt;/h2&gt;
&lt;p&gt;线程，有时被称为轻量级进程 (Lightweight Process，LWP），是程序执行流的最小单元。一个标准的线程由线程 ID，当前指令指针 (PC），寄存器集合和堆栈组成，每一个程序都至少有一个线程，若程序只有一个线程，那就是程序本身。&lt;/p&gt;

&lt;p&gt;同时线程是进程中的一个实体，是被系统独立调度和分派的基本单位，线程自己不拥有系统资源，只拥有一点儿在运行中必不可少的资源，但它可与同属一个进程的其它线程共享进程所拥有的全部资源。&lt;/p&gt;

&lt;p&gt;一个线程可以创建和撤消另一个线程，同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约，致使线程在运行中呈现出间断性。因此线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件，逻辑上可以运行，在等待处理机；运行状态是指线程占有处理机正在运行；阻塞状态是指线程在等待一个事件（如某个信号量），逻辑上不可执行。&lt;/p&gt;
&lt;h2 id="什么是信号"&gt;什么是信号&lt;/h2&gt;
&lt;p&gt;信号是一种 IPC 通信的形式，一般在 Unix，类 Unix 或 POSIX 兼容的系统中使用。信号是一种异步通知进程或同进程中某个指定线程的方式。
当信号被发送到进程的时候，操作系统会中断进程的控制流程，并且在执行非原子性的 CPU 指令时可以中断进程。&lt;/p&gt;
&lt;h3 id="信号使用的风险（新手坑）"&gt;信号使用的风险（新手坑）&lt;/h3&gt;
&lt;p&gt;信号处理在存在竞态的，因为信号本身是异步的，在处理一个信号的过程中，令一个信号（甚至肯能是同类型的信号）会被直接发送到进程中请求进程处理。
信号是可以打断系统调用的，不谨慎处理会引起程序自身的混乱，所以进程的信号处理过程，尽量做到没有副作用，也不要使用不可重入的函数。&lt;/p&gt;
&lt;h2 id="Linux的线程"&gt;Linux 的线程&lt;/h2&gt;&lt;h3 id="LinuxThreads"&gt;LinuxThreads&lt;/h3&gt;
&lt;p&gt;在 Linux 的上古时代，Linux 的线程技术和 POSIX 的标准是不同的，它使用自己的 LinuxThreads 库。这会为我们带来什么影响呢？&lt;/p&gt;

&lt;p&gt;让我们来回顾一下 LinuxThreads 设计细节的一些基本理念：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;系统必须能够响应终止信号并杀死整个进程。&lt;/li&gt;
&lt;li&gt;以堆栈形式使用的内存回收必须在线程完成之后进行。因此，线程无法自行完成这个过程。&lt;/li&gt;
&lt;li&gt;终止线程必须进行等待，这样它们才不会进入僵尸状态。&lt;/li&gt;
&lt;li&gt;线程本地数据的回收需要对所有线程进行遍历；这必须由管理线程来进行。&lt;/li&gt;
&lt;li&gt;如果主线程需要调用 pthread_exit()，那么这个线程就无法结束。主线程要进入睡眠状态，而管理线程的工作就是在所有线程都被杀死之后来唤醒这个主线程。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了维护线程本地数据和内存，LinuxThreads 使用了进程地址空间的高位内存（就在堆栈地址之下）。
同步元语是使用信号来实现的。例如，线程会一直阻塞，直到被信号唤醒为止。并且，LinuxThreads 将每个线程都是作为一个具有惟一进程 ID 的进程实现的。LinuxThreads 接收到终止信号之后，管理线程就会使用相同的信号杀死所有其他线程（进程）。
由于异步信号是内核以进程为单位分发的，而 LinuxThreads 的每个线程对内核来说都是一个进程，且没有实现"线程组"，因此，某些语义不符合 POSIX 标准，比如没有实现向进程中所有线程发送信号。如果核心不提供实时信号，LinuxThreads 将使用 SIGUSR1 和 SIGUSR2 作为内部使用的 restart 和 cancel 信号，这样应用程序就不能使用这两个原本为用户保留的信号了。在 Linux kernel 2.1.60 以后的版本都支持扩展的实时信号（从_SIGRTMIN 到_SIGRTMAX），因此不存在这个问题。根据 LinuxThreads 的设计，如果一个异步信号被发送了，那么管理线程就会将这个信号发送给一个线程，如果这个线程现在阻塞了这个信号，那么这个信号也就会被挂起，因此某些信号的缺省动作难以在现行体系上实现，比如 SIGSTOP 和 SIGCONT，LinuxThreads 只能将一个线程挂起，而无法挂起整个进程。&lt;/p&gt;
&lt;h3 id="LinuxThreads带来了什么问题"&gt;LinuxThreads 带来了什么问题&lt;/h3&gt;
&lt;p&gt;首先我们说下 POSIX 是如何定义多线程的：POSIX 下一个多线程的进程只有一个 PID。
根据上面我们对 LinuxThreads 的描述，我们可以总结出 LinuxThreads 有下面这些问题：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;它使用管理线程来创建线程，并对每个进程所拥有的所有线程进行协调。这增加了创建和销毁线程所需要的开销。&lt;/li&gt;
&lt;li&gt;由于它是围绕一个管理线程来设计的，因此会导致很多的上下文切换的开销，这可能会妨碍系统的可伸缩性和性能。&lt;/li&gt;
&lt;li&gt;由于管理线程只能在一个 CPU 上运行，因此所执行的同步操作在 SMP 或 NUMA 系统上可能会产生可伸缩性的问题。&lt;/li&gt;
&lt;li&gt;由于线程的管理方式，以及每个线程都使用了一个不同的进程 ID，因此 LinuxThreads 与其他与 POSIX 相关的线程库并不兼容。&lt;/li&gt;
&lt;li&gt;信号用来实现同步原语，这会影响操作的响应时间。另外，将信号发送到主进程的概念也并不存在。因此，这并不遵守 POSIX 中处理信号的方法。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们在这里不关注性能如何只关注 POSIX 兼容和信号处理问题。&lt;/p&gt;
&lt;h3 id="NPTL"&gt;NPTL&lt;/h3&gt;
&lt;p&gt;LinuxThreads 的问题，特别是兼容性上的问题，严重阻碍了 Linux 上的跨平台应用（如 Apache）采用多线程设计，从而使得 Linux 上的线程应用一直保持在比较低的水平。在 Linux 社区中，已经有很多人在为改进线程性能而努力，其中既包括用户级线程库，也包括核心级和用户级配合改进的线程库。目前最为人看好的有两个项目，一个是 RedHat 公司牵头研发的 NPTL（Native Posix Thread Library），另一个则是 IBM 投资开发的 NGPT（Next Generation Posix Threading），二者都是围绕完全兼容 POSIX 1003.1c，同时在核内和核外做工作以而实现多对多线程模型。这两种模型都在一定程度上弥补了 LinuxThreads 的缺点，且都是重起炉灶全新设计的。
NPTL 的设计目标归纳可归纳为以下几点：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;POSIX 兼容性&lt;/li&gt;
&lt;li&gt;SMP 结构的利用&lt;/li&gt;
&lt;li&gt;低启动开销&lt;/li&gt;
&lt;li&gt;低链接开销（即不使用线程的程序不应当受线程库的影响）&lt;/li&gt;
&lt;li&gt;与 LinuxThreads 应用的二进制兼容性&lt;/li&gt;
&lt;li&gt;软硬件的可扩展能力&lt;/li&gt;
&lt;li&gt;多体系结构支持&lt;/li&gt;
&lt;li&gt;NUMA 支持&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在技术实现上，NPTL 仍然采用 1:1 的线程模型，并配合 glibc 和最新的 Linux Kernel2.5.x 开发版在信号处理、线程同步、存储管理等多方面进行了优化。和 LinuxThreads 不同，NPTL 没有使用管理线程，核心线程的管理直接放在核内进行，这也带了性能的优化。&lt;/p&gt;
&lt;h3 id="Linux线程总结"&gt;Linux 线程总结&lt;/h3&gt;
&lt;p&gt;比较新的 Linux 都已经开始使用 NPTL 了，所以我们可以忽略 LinuxThreads 的存在了，介绍它主要是为了让诸位读者更深入的了解线程和信号的恩恩怨怨（不要丢鸡蛋）。&lt;/p&gt;
&lt;h2 id="Linux的信号"&gt;Linux 的信号&lt;/h2&gt;&lt;h3 id="Linux是如何处理信号的"&gt;Linux 是如何处理信号的&lt;/h3&gt;
&lt;p&gt;随着 Linux 的内核版本不断提升，Linux 的信号现在已经可以按照线程级别的触发了，换句话说就是，每个线程可以关注自己的信号了，并且可以区别性对待了。那我们需要注意什么呢？&lt;/p&gt;

&lt;p&gt;在多线程应用中，我们应当使用 sigaction 来代替 singal 函数，因为按 POSIX 的说法 singal 函数并没有明确定义自己在多线程应用中的行为。&lt;/p&gt;

&lt;p&gt;可以使用 pthread_sigmask 来为每个线程设置独立的信号掩码。同时在多线程应用中应当避免使用 sigprocmask 这个函数，原因也是 POSIX 中该函数并没有明确定义自己在多线程应用中的行为。&lt;/p&gt;

&lt;p&gt;这个时候，有人会产生疑问了，那么多线程下 kill 发出的进程级别的信号 A 怎么办？Linux 是这样解决的，它会把这个信号交付给任意一个没有屏蔽信号 A 的线程。如果这信号没有被任何线程设置 handler 进行处理，就会触发 POSIX 规定的默认动作。&lt;/p&gt;

&lt;p&gt;接着有人就会问，我怎么向某个线程发消息呢，POSIX 为我们准备了 pthread_kill 函数，我们可以直接向特定的线程发送消息。那么如果一个线程收到信号 A，但是自己没有安装 handler 会发生什么？其实和进程级别的信号处理方法一样，直接触发默认动作，同样会结束整个进程。&lt;/p&gt;
&lt;h3 id="如何避免新手坑"&gt;如何避免新手坑&lt;/h3&gt;
&lt;p&gt;在具有事件循环的应用中，在信号的的 handler 中，可以将信号直接放入程序的队列中，立刻返回。这样直到线程从程序的队列中取出这个信号为止，整个线程看起来就像没有“中断”。
如果不知道该怎么做，去看看著名的 libev 吧。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;多读读 POSIX 标准和 Intel 的 CPU 体系结构，会让自己在开发变的容易些。&lt;/p&gt;

&lt;p&gt;转自&lt;a href="https://www.ttalk.im/pages/995338350741159956" rel="nofollow" target="_blank" title=""&gt;TechTalk&lt;/a&gt;&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Fri, 28 Jul 2017 09:46:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/33655</link>
      <guid>https://ruby-china.org/topics/33655</guid>
    </item>
    <item>
      <title>TTalkIM 的开发笔记</title>
      <description>&lt;h2 id="为什么是Haskell"&gt;为什么是 Haskell&lt;/h2&gt;
&lt;p&gt;因为想尝试下新东西，在决定使用 ttalk.im 这个域名前我还做个很多很多的尝试，例如&lt;a href="https://github.com/DavidAlphaFox/mate.im" rel="nofollow" target="_blank" title=""&gt;mate.im&lt;/a&gt;是使用 Elixir 开发的。当然还有一个叫 ailink.io 的网站，是使用 Clojure 开发的。&lt;/p&gt;

&lt;p&gt;令一方面，在使用了很久的 Erlang 之后，感觉函数类语言比较适合自己，当然我也拿 Haskell 写过一些非 Web 的产品，为了让自己更加专注，所以就选择了全部使用函数类语言开发后端（Erlang 和 Haskell）。&lt;/p&gt;
&lt;h2 id="Web开发选型"&gt;Web 开发选型&lt;/h2&gt;
&lt;p&gt;因为自己对 Haskell 的功底并没有达到很高的境界，就没有选择 Haskell 开发 Web 的神奇 Yesod，而是选择比较轻量级的 Scotty，当然当时也有想尝试 Spock（Live long and prosper 或者 Peace and long life）的想法。不过因为自己已经看了好一段时间 Scotty 的代码，所以就决定使用了 Scotty。&lt;/p&gt;

&lt;p&gt;选择 Postgresql-simple 完全是因为自己还不想接触复杂的 Haskell Teamplate，并且为了让整个项目看起来不太恐怖（比较害怕哪天自己都不知道自己在写什么）。&lt;/p&gt;

&lt;p&gt;选择 Blaze-html 的原因是因为个人一贯喜欢吧 View 直接写成代码，而不是渲染模板文件的原因，但是从生产角度上来讲，渲染模板文件的做法才更加实际。&lt;/p&gt;

&lt;p&gt;前端 CSS 框架并没有使用大家所熟知的 Bootstrap，而是选择使用了 semantic-ui，也是为了尝试些新的东西。不过在 JS 的选择上，并没有选择比较花哨的 ReactJS 或 Vue 这类的，更多是简单的 JQuery，因为这个项目的重点是后端 Haskell 开发 Web 的实践。&lt;/p&gt;
&lt;h3 id="Scotty使用心得"&gt;Scotty 使用心得&lt;/h3&gt;
&lt;p&gt;Scotty 作为一个 Web 框架，代码量非常少，非常轻量级。因此很多东西需要自己重新造轮子，但是也为每个人带来了很多灵活的地方，整个项目相当是我自己在 Scotty 基础上封装了一些便于操作数据库的东西。&lt;/p&gt;

&lt;p&gt;Scotty 的优势：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;轻量级，代码简单，有问题可以直接翻代码&lt;/li&gt;
&lt;li&gt;上手曲线平滑，没有大的波动&lt;/li&gt;
&lt;li&gt;路由解析和异常处理非常完善&lt;/li&gt;
&lt;li&gt;性能非常好&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Scotty 的缺陷：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;没有 URI 相关操作工具&lt;/li&gt;
&lt;li&gt;不提供很多常见的组件，例如说 Cache，Cookie，CSRF 等&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="整个项目总结"&gt;整个项目总结&lt;/h2&gt;
&lt;p&gt;Haskell 开发的 Web 项目，尤其是对大量的列表读取处理还是非常简单，因为有大量的工具函数，例如说 map，filiter，fold 等。
整个项目冗余代码偏多，并没有使用太多的 Haskell 的高级技巧，甚至没有使用 Class 来完成不同的数据类型进行同名操作。因此整个项目并不是优良的 Haskell 项目，但是依然不影响它作为一个 Scotty 入门教程的项目。&lt;/p&gt;
&lt;h3 id="代码地址"&gt;代码地址&lt;/h3&gt;
&lt;p&gt;整个项目被开源放到了&lt;a href="https://github.com/DavidAlphaFox/sblog" rel="nofollow" target="_blank" title=""&gt;Github&lt;/a&gt;上，有兴趣的同学可以帮忙添加一些特性。&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Thu, 27 Jul 2017 14:00:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/33646</link>
      <guid>https://ruby-china.org/topics/33646</guid>
    </item>
    <item>
      <title>Phoenix + AngularJS 的一个小站点的开发</title>
      <description>&lt;p&gt;#原因
自己本身做了 Erlang 很久，也玩了很久的 Ruby on Rails。当自己想写一个东西的时候，发现 Rails 的组件好多，配置也多，自我感觉一个人驾驭不了。用 AngularJS，纯粹就是想尝试下纯粹的客户端应用开发。&lt;/p&gt;

&lt;p&gt;#仓库
&lt;a href="https://github.com/DavidAlphaFox/mate.im" rel="nofollow" target="_blank"&gt;https://github.com/DavidAlphaFox/mate.im&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;#完成情况
基础功能还不完善，不过能发帖子和回帖子了&lt;/p&gt;

&lt;p&gt;#后记
个人感觉国内玩 Phoenix／Elixir 的好少，基本上都没有多少人的样子。不过确实是很有意义的一个语言和产品。Elixir 解决了 Erlang 的颜值问题，Phoenix 带来了和 Rails 相似的快速开发的框架。&lt;/p&gt;</description>
      <author>davidgao</author>
      <pubDate>Mon, 01 Feb 2016 09:11:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/28919</link>
      <guid>https://ruby-china.org/topics/28919</guid>
    </item>
  </channel>
</rss>
