<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>razertory (razertory)</title>
    <link>https://ruby-china.org/razertory</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title> Linux 下的 IO 多路复用的三个问题</title>
      <description>&lt;h3 id="什么是 IO 多路复用？"&gt;什么是 IO 多路复用？&lt;/h3&gt;
&lt;p&gt;IO 多路复用，源名称是  I/O multiplexing。
「multiplexing」在剑桥辞典的解释为：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;an electronic process that allows more than one electrical signal to be sent using only one connection&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;意为：只在一个电路连接下可以发送多个信号的电子化过程。&lt;/p&gt;

&lt;p&gt;这里的 multiplexing 指的是在单个线程通过记录跟踪每一个 Sock(I/O 流) 的状态来同时管理多个 I/O 流。&lt;/p&gt;

&lt;p&gt;再通俗一点，就是单个线程管理多个 socket。现今主流的方法分别叫做 select，poll 和 epoll。&lt;/p&gt;
&lt;h3 id="它们都在哪儿？"&gt;它们都在哪儿？&lt;/h3&gt;
&lt;p&gt;它们分别来自于  &lt;code&gt;&amp;lt;sys/select.h&amp;gt;&lt;/code&gt;，&lt;code&gt;&amp;lt;sys/poll.h&amp;gt;&lt;/code&gt;，&lt;code&gt;&amp;lt;sys/epoll.h&amp;gt;&lt;/code&gt; 这三个头文件中。我们开发常用的 Mac OS 下，没有 epoll.h 的哦。所以如果你想试试 linux
的 epoll 最好找一台内核  2.5.44 以上的机器跑一下。比如说去你自己的 VPS 上，或者上你家主子的开发服务器上试一试。如果你想看源代码，推荐 &lt;a href="https://code.woboq.org/gcc/include/sys/select.h.html" rel="nofollow" target="_blank" title=""&gt;select&lt;/a&gt;, &lt;a href="https://code.woboq.org/gcc/include/sys/poll.h.html" rel="nofollow" target="_blank" title=""&gt;poll&lt;/a&gt;, &lt;a href="https://code.woboq.org/gcc/include/sys/epoll.h.html" rel="nofollow" target="_blank" title=""&gt;epoll&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="它们的原理各是什么？"&gt;它们的原理各是什么？&lt;/h3&gt;
&lt;p&gt;不讲，答案在下一个问题。&lt;/p&gt;
&lt;h3 id="我如何在代码里面用上？"&gt;我如何在代码里面用上？&lt;/h3&gt;
&lt;p&gt;如果你只是写简单的 Web 服务，那肯定是用不上的。如果你真想学一下，分别 gcc 跑一下这&lt;a href="https://github.com/razertory/c-code-lab/blob/master/async_io/README.md" rel="nofollow" target="_blank" title=""&gt;三个文件&lt;/a&gt;就知道了，都已经写好了。&lt;/p&gt;</description>
      <author>razertory</author>
      <pubDate>Sat, 11 May 2019 12:01:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/38508</link>
      <guid>https://ruby-china.org/topics/38508</guid>
    </item>
    <item>
      <title>GraphQL 的推进历程</title>
      <description>&lt;p&gt;时间大概是 17 年九月，我司的新 App 开工。在技术选型上，我们很激进地选择了&lt;code&gt;graphql&lt;/code&gt; instead of restful。当时想到的理由是很简单，但是也无可厚非的：我们的 App 和后期将要做的新 Web 在业务上有很大的相通性，并且功能模块有不少类似的地方，所以用 graphql 这样的技术来做 API 层复用性会很高。后期在做一些管理后台的时候，效率也会高不少。&lt;/p&gt;

&lt;p&gt;很有幸的是，自己成了研发团队中这个技术的主要推进者。我们的服务端主语言是 Ruby，所以像 github，Shopify 这样的 GraphQL + Ruby 的领先者对我们而言如同教科书一般。我研究了几乎每个 Github 的 Query 和 Mutation，所以很大程度上也受于他们优质的思路，和客户端同学们踩了一些坑，但最后也都得出了这个在开发过程中，很多方面都会优于 rest 的结论。具体的优势，和官方说的差不多，我也不愿再赘述。这篇博客，更多的是关于技术的感悟和一些架构上的理念。吹架构可能有点悬乎。好吧，就再抽象一点，应该是做工程的一些总结；当然 GraphQL 的干货内容也不会少的。&lt;/p&gt;
&lt;h2 id="开发模式"&gt;开发模式&lt;/h2&gt;
&lt;p&gt;用 graphql 很大程度上，省去了一些不必要的沟通成本。当你受够了用 rest 要去构建 API 文档之后，graphql 这种天然的&lt;code&gt;代码即文档&lt;/code&gt;的便捷性让团队受益匪浅。服务端只需要在编码期间，按照标准的写法，加上对应的 desc 方法，就可以无缝生成对应的文档。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;GuardianType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;GraphQL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="s1"&gt;'Guardian'&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s1"&gt;'A guardian has his/her children'&lt;/span&gt;

  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ID&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ChildType&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;但实际上，graphql 的自动化 + 强类型 + '强结构'让客户端的工作量反而下降了。首先，客户端的的确是要写查询代码，就像是服务端写 SQL 一样。但是 graph 的查询要比 SQL 简单了不知道多少倍，更准确的，我可以称之为&lt;code&gt;描述&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;guardian&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;guardain(id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9527&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;children&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;avatar&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在传统的 rest 开发模式中，客户端并不知道一个请求服务端会返回什么结果。除非有准确，准时，详尽的文档。客户端会恐惧，服务端某个返回结果数据结构不对自己 crash 了。所以反而会在这一层上，思考很多。（我们评价一个技术体系的工作成本，除了实际的编码产出，还应该评估思考，权衡所带来的开销）维护未知响应结果的开销，总是会大于自己决定响应结果的开销。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;技术上的恐惧感，往往来源于未知。没有测试，不知道原理，没有约定，没有规范，都是会带来未知。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="查询性能"&gt;查询性能&lt;/h2&gt;
&lt;p&gt;解决 N+1 query 和查询环几乎是 graphql 服务端使用者遇到的第一个问题。&lt;/p&gt;

&lt;p&gt;我记得在遇到这个问题的时候，我看到了 Facebook 官方出的 dataloader 来解决。所以像我们这种 Ruby 栈的团队，自然就选用了 Shopify 团队的&lt;a href="https://github.com/razertory/graphql-batch" rel="nofollow" target="_blank" title=""&gt;graphql-batch&lt;/a&gt;，现在回忆起来依旧记得当时的兴奋感。好吧，还是贴两张图，对比一下。&lt;/p&gt;
&lt;h4 id="before"&gt;before&lt;/h4&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.019141s) DESCRIBE `activities`&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.018571s) SELECT * FROM `activities` WHERE (`subject_id` = 26)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.017604s) SELECT * FROM `activities` WHERE (`subject_id` = 32)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.015813s) SELECT * FROM `activities` WHERE (`subject_id` = 25)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.016458s) SELECT * FROM `activities` WHERE (`subject_id` = 28)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.016015s) SELECT * FROM `activities` WHERE (`subject_id` = 29)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.016588s) SELECT * FROM `activities` WHERE (`subject_id` = 27)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.019075s) SELECT * FROM `activities` WHERE (`subject_id` = 33)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.014484s) SELECT * FROM `activities` WHERE (`subject_id` = 31)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.017730s) SELECT * FROM `activities` WHERE (`subject_id` = 35)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.017332s) SELECT * FROM `activities` WHERE (`subject_id` = 34)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.015393s) SELECT * FROM `activities` WHERE (`subject_id` = 36)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.016545s) SELECT * FROM `activities` WHERE (`subject_id` = 39)&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.014443s) SELECT * FROM `activities` WHERE (`subject_id` = 40)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="after"&gt;after&lt;/h4&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.022229s) SELECT * FROM `subjects` WHERE ((`clazz_id` = 1) AND (`grade_id` = 1)) ORDER BY `ordering`&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.018933s) DESCRIBE `activities`&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt; &lt;span class="c1"&gt;-- : (0.105159s) SELECT * FROM `activities` WHERE (`subject_id` IN (26, 32, 25, 28, 29, 27, 33, 31, 35, 34, 36, 39, 40))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;作为一个非主流入门的 Ruby 用户（我第一个开始做的 Ruby 项目是用 Grape + ActiveRecord 搭建的），rails 其实也是最近才开始真正用上的。当遇到第一个让人兴奋的东西时，自然得去读他的源码。不过读之前，当发现了这个神奇的一幕时，我的第一直觉告诉我是不是和 JDBC 规范中的 prepared statement 一样？让相同语法的 sql 不再多执行，只要换 sql 的参数就行。这样 MySQl 编译开销就小了很多。反正缓存无处不在。不，难道是动态规划？。。好吧，都是猜的。既然这个玩意儿解决了一个重要瓶颈问题，那么不管出于兴趣层面，还是业务层面，尽可能去熟悉它一定是很好的想法。&lt;/p&gt;

&lt;p&gt;首先，这个玩意儿是学习了 Facebook 官方的 dataloader 做的。如果有去了解，自然猜到，这个项目用了&lt;code&gt;promise.rb&lt;/code&gt;。可以在源码里面看到这样的调用，并且作者的 sample 里面也很清晰地说明了这个玩意儿的输出开始就是个 Promise 对象。所以对于 graphql ruby 的&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resolve&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;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&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;这样的 do block 块就应该和普通的 http api 框架不一样。可以理解的是，一次 graph 请求的，所需要的每个 field 对于服务端来说，是并发获取的。这里并发，肯定用了他自己的一套策略。可以猜测的是，就是用的异步 I/O 的方式。所以从这一点来说，这个库本质上和 graphql ruby 是完全契合的。&lt;/p&gt;
&lt;h2 id="架构"&gt;架构&lt;/h2&gt;
&lt;p&gt;Ruby 在编程表达能力上，几乎是我见过的所有服务端语言中最强的。元编程 + Rails + 各种库，几乎算得上是互联网创业公司的神兵利器。当然，语言这种东西，好坏得看上下文的，不同的话在不同的语境中效果还是不一样的。至少对于我司的业务和人员编制，加上对于往后的预估，在大量的论证下，大家都对技术选型达成了一致。我们需要技术团队有强大的业务推进能力，并且能做出高效稳定的服务。&lt;/p&gt;

&lt;p&gt;现在看来，这样的选择没有错。这里可以大致展示：&lt;a href="http://rc.kid17.com:10000" rel="nofollow" target="_blank" title=""&gt;我司 demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;随着产品的重构和业务的迁移，我们的流量开始逐步转向 graphql 服务，各个规范也建立起来。随着业务模块的复杂度增高，用户数量的增加和数据量的增大，新一轮的挑战即将开始。首先就是要把这个巨大复杂的 Rails + GraphQL 项目拆掉，第二个就是确定新的架构，第三点就是拥抱变化，让这个项目本身变得可以维护，扩展和改进。&lt;/p&gt;

&lt;p&gt;我司的的产品中，有两套用户体系，就像是美团的商家版和客户版，拉钩、Boss 的求职版和企业版。两种不同的身份的角色参与到整个业务。我们有 feed 流，电商，电子书，用户管理，教学管理等业务模块。我目前的想法是让 graphql 去代理这些服务给客户端调用。最外层用&lt;a href="https://github.com/Kong/kong" rel="nofollow" target="_blank" title=""&gt;Kong&lt;/a&gt;去完成用户鉴权和 ngixn 的一些基本功能。&lt;/p&gt;

&lt;p&gt;以上拙见，都来自于各种踩坑之后的感悟。&lt;/p&gt;

&lt;p&gt;ps: 很期待可以认识同样在用 graphql 的团队或个人，欢迎打扰 &lt;code&gt;wechat: chendalichun&lt;/code&gt;&lt;/p&gt;</description>
      <author>razertory</author>
      <pubDate>Thu, 19 Apr 2018 15:15:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/35490</link>
      <guid>https://ruby-china.org/topics/35490</guid>
    </item>
  </channel>
</rss>
