<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>allenlsy</title>
    <link>https://ruby-china.org/allenlsy</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>[译] 网站架构基础 - 分析 Storyblocks 的架构</title>
      <description>&lt;p&gt;&lt;a href="http://allenlsy.com/web-architecture-101" rel="nofollow" target="_blank" title=""&gt;我的博客，更多架构文章&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://engineering.videoblocks.com/web-architecture-101-a3224e126947" rel="nofollow" target="_blank" title=""&gt;英文原文链接&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/8094a8ef-2e8d-486a-8570-2277a1294b49.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;我们先来看一个 use case 流程：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;用户在 google 搜到我们网站的一篇文章，于是点击。用户会发送一个 request 给 DNS，询问如何解析我们的域名，也就是如何将域名转化为 IP 地址。之后用户的浏览器用这个 IP 地址，发送 request 到我们的 load balancer&lt;/li&gt;
&lt;li&gt;load balancer 会从 web server 中分配一台合适的给 request（业界有各种不同的 load balancing 算法）。web server 的职责一般是负责给用户提供渲染好的网页。&lt;/li&gt;
&lt;li&gt;web server 会从 cache 中加载已经缓存的图片，从 database 加载其他数据。如果 web server 发现有个图片没有被缓存，甚至没有被生成，那么它会生成一个 job，加到 job queue 里面。而 job server 会异步的生成图片，并且把数据更新到 database&lt;/li&gt;
&lt;li&gt;如果说，这个页面还需要显示比如说同类型的其他一些图片，那么 web server 会发送一个 request 给我们自己的全文搜索引擎（full text search engine)&lt;/li&gt;
&lt;li&gt;对于已登陆的用户，页面还要加载用户信息&lt;/li&gt;
&lt;li&gt;最后，发送一个 event 到 data firehose，evert 最终会被记录在 data warehouse，以便数据分析和 BI。&lt;/li&gt;
&lt;li&gt;这时，web server 才把 html 渲染好，发送会用户浏览器。这个过程还会经过 load balancer。而页面中的 javascript 和 css 一般存储在 CDN。这之后，用户能在浏览器上看到完整的页面&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;下面我们来详细说说，图中的各个部分是用来干什么的&lt;/p&gt;
&lt;h3 id="1. DNS"&gt;1. DNS&lt;/h3&gt;
&lt;p&gt;DNS 是域名服务器。它类似于一个 hash table，把域名映射到 IP 地址。类比一下我们常见的通讯录，是把人名映射到电话号码一样。&lt;/p&gt;

&lt;p&gt;当然，很多大公司的域名会被映射到很多不同的 IP 地址。现在不展开讲解。&lt;/p&gt;
&lt;h3 id="2. Load balancer"&gt;2. Load balancer&lt;/h3&gt;
&lt;p&gt;中文里一般叫做 负载均衡。在讲这个之前，我们需要来说说 horizontal vs. vertical scaling。&lt;/p&gt;
&lt;h4 id="Scaling"&gt;Scaling&lt;/h4&gt;
&lt;p&gt;Scaling 是指扩展，当网站的需求量大于了网站的接纳能力时，网站需要 scale out。Vertical scaling（纵向扩展），是通过扩大一台服务器的硬件能力来扩展，比如加硬盘，加内存，加 CPU。但它的问题在于，它很容易达到上限。想象现在单块硬盘的容量才多大。而 horizontal scaling（横向扩展），是通过增加服务器的数量来扩展。这显然比 vertical scaling 的上限要高得多。以 Storyblocks 为例，现在有 150 - 400 台 AWS EC2 在运行。而这不可能由一台巨大的服务器来完成。&lt;/p&gt;

&lt;p&gt;在网站架构中，一个稍微大点的网站肯定会做 horizontal scaling。这还有助于避免 single point of failure（也就是当一台服务器挂了的时候，网站不会整个挂掉）。换句专业术语说，网站变得 fault tolerant。&lt;/p&gt;

&lt;p&gt;Horizontal scaling 的另一点好处是，可以 decouple server 之间的职能。网站可以按照 web server, database, cache, service X 等来划分。（而且，还可以由不同的 team 来管理不同的 server，从而影响公司的组织结构，译者注）。&lt;/p&gt;
&lt;h4 id="Load balancing"&gt;Load balancing&lt;/h4&gt;
&lt;p&gt;Load balancing 简单说就是把请求合理的分配给服务器，比如不让某台服务器特别忙而其他的特别闲。这就要求 load balancer 所管理的这些 server 要能按照完全一样的工作方式工作。一个 request 不管被分配到哪台 server，它所返回的结果应该完全一样。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;另外，有些 request 是跟用户相关的。同一类型请求来自不同用于，所返回的结果也不同。这时 load balancer 该如何处理？这样的问题叫 sticky session problem&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="3. web server"&gt;3. web server&lt;/h3&gt;
&lt;p&gt;Web server 可以说是用户做业务逻辑请求的入口。所以一般 web server 会和后端 services 进行通讯。为了避免 single point of failure，一般再小的网站，web server 也应该至少两台，如果预算允许的话。&lt;/p&gt;

&lt;p&gt;web server 所使用的技术，一般是 MVC 或者 MVP web framework。常见的 web framework 所使用的语言可以是 node.js, ruby, php, python, java, c# .NET 等等。&lt;/p&gt;
&lt;h3 id="4. Database"&gt;4. Database&lt;/h3&gt;
&lt;p&gt;Database 提供对数据的增删查改，一般称为 CRUD（create, read, update, delete)。在 microservices 架构中，很多 service 会有属于自己的 database。这样做的原因也是为了 decouple。&lt;/p&gt;

&lt;p&gt;这里主要提两个技术：不严谨的说叫做 SQL vs. NoSQL。严谨一些，应该叫做 relational database vs. non-relational database&lt;/p&gt;

&lt;p&gt;SQL 是 relational database 最流行的语言，所以一般也用来代指了这类 database。它擅长处理 data entities 之间的关系，比如一对一，一对多，多对多。&lt;/p&gt;

&lt;p&gt;更多关于 SQL 的细节，我个人推荐 &lt;a href="https://www.w3schools.com/sql/" rel="nofollow" target="_blank" title=""&gt;W3Cschools 的 SQL 教程&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;而 NoSQL，泛指所有 non-relational database，其实是有很多不同类型的。它们很多都不擅长处理 data entities relation。但它们有自己擅长的。有的是能存储更大量的数据，比如 mongodb，有的是擅长处理 key value storage，比如 redis，有的擅长处理地理数据，比如 neo4j。所以 relational database 并不是架构中 database 的唯一选择。&lt;/p&gt;
&lt;h3 id="5. Cache"&gt;5. Cache&lt;/h3&gt;
&lt;p&gt;Cache (缓存) 就是将常用的数据存储在一个单独的地方，这个地方一般来说提供了更快速的硬件，可能比硬盘快 10 倍以上。而 database 一般存储在硬盘上，所以 cache 的读取速度远快于 database。&lt;/p&gt;

&lt;p&gt;Cache 理论上可以存储任何类型的数据，不过一般大家经常存储的是 key value pair、文件、渲染好的 page、复杂的 data query 的结果等等。&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Google 把 taylor swift 的搜索结果缓存了起来&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@shagun/scaling-memcache-at-facebook-1ba77d71c082" rel="nofollow" target="_blank" title=""&gt;Facebook 会把特别活跃用户的首页应该显示的内容缓存起来&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Storyblocks 缓存一些 rendered page，搜索结果等&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="6. Job queue"&gt;6. Job queue&lt;/h3&gt;
&lt;p&gt;Job 是指在后台异步处理的工作。Job 出现的原因是，比如按照 request 压缩一张图片。这个任务可能需要 1 秒。如果在 web server 上进行，那么这台 web server 可能在 1 秒内不能接受其他 request，或者至少是影响 web server 的性能。这对于流量很大的网站是不可接受的。所以有了 job server 来异步处理 job。当 job 完成后，job server 会以某种方式通知其他 server。&lt;/p&gt;

&lt;p&gt;再举个例子，google 每时每刻都要关注世界上其他网站每天发布的新的页面，抓取页面并 index。这些都是在他的 job server 上进行的。&lt;/p&gt;

&lt;p&gt;Job queue 一般由两个部分组成，一个部分是 queue 本身，另一个是 worker，用于执行 job。&lt;/p&gt;

&lt;p&gt;最简单的 job queue 就是一个 FIFO 的数据结构。你也可以使用 priority queue 来实现，如果你的 job 有 priority 的话。&lt;/p&gt;
&lt;h3 id="7. Full-text search service"&gt;7. Full-text search service&lt;/h3&gt;
&lt;p&gt;这个技术是，用户提供一个 text query，service 返回相关结果。它们一般通过一个 &lt;a href="https://en.wikipedia.org/wiki/Inverted_index" rel="nofollow" target="_blank" title=""&gt;inverted index(反向索引) 技术&lt;/a&gt; 来实现。&lt;/p&gt;

&lt;p&gt;比如说，我们在网站的搜索条里输入 &lt;code&gt;man&lt;/code&gt; ，网站应该返回所有含有词语 &lt;code&gt;man&lt;/code&gt; 的网页。&lt;/p&gt;

&lt;p&gt;确实，我们可以使用 database 自带的 full-text search 功能实现，比如 mysql 和 postgres 都支持。但是更多时候我们会使用专门做 full-text search 的工具来实现，比如 elasticsearch, sphinx 或 apache solr。&lt;/p&gt;
&lt;h3 id="8. Services"&gt;8. Services&lt;/h3&gt;
&lt;p&gt;一般当网站大到一定规模后会发现，把所有业务逻辑放在一起并不方便开发、部署、管理。近年来流行的 microservices architecture 就是把不同类型的业务服务切分到不同的 services 里面，各自开发管理。各个 services 之间可以进行数据交流。&lt;/p&gt;

&lt;p&gt;比如在 Storyblocks，我们有这些 services：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Account service 负责用户账户&lt;/li&gt;
&lt;li&gt;Content service 管理视频、音频、图片、下载&lt;/li&gt;
&lt;li&gt;Payment service 负责收费，计费&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="9. Data"&gt;9. Data&lt;/h3&gt;
&lt;p&gt;数据对于公司来说，在当下就是命根子。data pipeline 负责搜集、存储、分析数据。&lt;/p&gt;

&lt;p&gt;一个典型的 data pipeline 的有 3 个阶段：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; app 发送数据（一般来说是 event ) 到 data firehose。data firehose 提供接口来接收处理元数据。AWS Kinesis 和 Kafka 是两个常用的工具。&lt;/li&gt;
&lt;li&gt;存储数据。AWS Kinesis 可以非常方便的将数据存储在 AWS S3&lt;/li&gt;
&lt;li&gt;处理之后的数据存入 data。Storyblocks 使用 AWS Redshift&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="10. Cloud storage"&gt;10. Cloud storage&lt;/h3&gt;
&lt;p&gt;云存储，是指把一些原本放在比如 web server 上的文件放到云端，比如 AWS S3 上。通常静态不怎么修改的文件都会放在 cloud storage，比如 video, photo, audio, css, javascript 等等&lt;/p&gt;
&lt;h3 id="11. CDN"&gt;11. CDN&lt;/h3&gt;
&lt;p&gt;CDN 的基本原理是，通过将文件在世界各个不同地区的服务器 ( edge server ) 上，来提高在不同地区加载文件的速度。用户在加载文件时，CDN 服务会自动选择从最近的服务器上加载。而文件，一般是由网站主动式的将文件传到 CDN 上。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.creative-artworks.eu/why-use-a-content-delivery-network-cdn/" rel="nofollow" target="_blank" title=""&gt;这篇文章&lt;/a&gt;更详细的讲解了 CDN。&lt;/p&gt;</description>
      <author>allenlsy</author>
      <pubDate>Wed, 08 Aug 2018 10:17:37 +0800</pubDate>
      <link>https://ruby-china.org/topics/37299</link>
      <guid>https://ruby-china.org/topics/37299</guid>
    </item>
    <item>
      <title>Airbnb 的微服务架构</title>
      <description>&lt;p&gt;软广我的博客：&lt;a href="http://allenlsy.com/airbnb-microservices-architecture" rel="nofollow" target="_blank"&gt;http://allenlsy.com/airbnb-microservices-architecture&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;微信订阅号：网站架构札记&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;原文：&lt;a href="https://www.youtube.com/watch?v=sakGeE4xVZs" rel="nofollow" target="_blank" title=""&gt;From Monorail to Monorepo: Airbnb’s journey into Microservices&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Airbnb 最开始是一个 single repo Ruby on Rails 项目。前端用过 backbone 和 react，后端有 Ruby 和 Java。&lt;/p&gt;

&lt;p&gt;2014 年之前的 deployment workflow 是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code review &amp;amp; CI&lt;/li&gt;
&lt;li&gt;merge to master&lt;/li&gt;
&lt;li&gt;staging, manual test&lt;/li&gt;
&lt;li&gt;production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;用于支持 deploy 的内部项目：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deployment dashboard&lt;/li&gt;
&lt;li&gt;SCRAM: rollback system, error tracking，类似于 bugsnag 和 honeybadger &lt;/li&gt;
&lt;li&gt;merge queue：确保 merge to master 是有序的&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/d55d86de-f17b-4fea-a61d-5a9bd8ba39e6.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/01ff222c-e76c-4476-9bdd-6acbb2ad83da.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;随着这个 single repo project 不断变大，Airbnb 开始采用 microservices architecture。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;拆分为 前端 和 后端 两个部分&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;前端使用 react，但 page 是使用 server side rendering 传回给前端的&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="后端"&gt;后端&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Airbnb 有 700+ 个 microservices&lt;/li&gt;
&lt;li&gt;synchronous communication 使用 thrift schema，比 json 的格式要求更严谨&lt;/li&gt;
&lt;li&gt;asynchronous communication 使用 kafka&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/27388853-3659-49c4-add8-45d4079210dd.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="架构中使用的工具"&gt;架构中使用的工具&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;service generation：用于新开一个 service。自带 thrift 配置&lt;/li&gt;
&lt;li&gt;所有配置文件放在一个 repo&lt;/li&gt;
&lt;li&gt;deployment with Kubernetes &lt;/li&gt;
&lt;li&gt;microservice 测试工具&lt;/li&gt;
&lt;li&gt;infrastructure as code, using Kubernetes, Amazon EKS&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>allenlsy</author>
      <pubDate>Mon, 30 Jul 2018 14:05:36 +0800</pubDate>
      <link>https://ruby-china.org/topics/37252</link>
      <guid>https://ruby-china.org/topics/37252</guid>
    </item>
    <item>
      <title>全栈笔记：每天推荐几篇优质的全栈开发文章 (以及我对全栈的看法)</title>
      <description>&lt;p&gt;首先我承认，这是一篇广告贴。我在广告我的新公众号。&lt;/p&gt;

&lt;p&gt;但光是广告还不行，我还是要在这里写一些干货。&lt;/p&gt;

&lt;p&gt;这个公众号叫做&lt;strong&gt;“全栈笔记 Fullstack Note”&lt;/strong&gt;，网址是 fsnote.co。每天（或者每两天），我会推荐 3 篇左右优质的全栈开发文章给大家，英文为主。对于每篇文章，&lt;strong&gt;我会附上简短的介绍和心得，以及预计阅读时间&lt;/strong&gt;。希望能帮助全栈开发工程师们成长，也帮助我自己成长。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/80a255d0-f801-467a-afe2-f6a591a13a8d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;我作为一个 RoR 全栈工程师有 4 年左右的时间了，这期间做过也做过 Android iOS，也做过设计图纸转 HTML+CSS，也曾经当过公司唯一一个工程师。我不能说自己的技术实力有多高，毕竟我很少参与各种开源项目，水平有限。不过，当使用 RoR 几年之后，随着对框架的慢慢熟悉，也渐渐缺乏了些新鲜感和挑战性。而在工作中，和团队合作的过程中发现，&lt;strong&gt;如果缺少其他发面的知识，比如负载均衡、CDN、系统架构，DevOps，很难称自己是优秀的全栈工程师&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;所以对于全栈网站工程师，绝不仅仅是使用自己熟悉的框架进行业务逻辑开发，其实要求挺高。需要懂得一点前端技术，中间业务逻辑，懂得后端服务器，数据库。每天有很多东西值得关注和学习。&lt;/p&gt;

&lt;p&gt;全栈笔记是我思考之后开始的一个 side project。考虑到面向的读者应该是使用不同框架的工程师，所以我分享的文章不会偏向任何一个框架，更不会单纯介绍一段代码如何写。&lt;strong&gt;我会侧重于全栈工程师应该具备的基本知识储备，以及一些可以拓展见识，对工作有帮助的信息&lt;/strong&gt;。这也算是我对“全栈”的理解。&lt;/p&gt;

&lt;p&gt;最后，放上公众号的二维码。我曾经做过不少 side project，都不了了之。希望“全栈笔记”可以坚持下去。也希望大家多多支持，提出宝贵建议。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/baaae84f-da41-4d5b-80d0-ddde33ff55d3.jpg!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>allenlsy</author>
      <pubDate>Thu, 27 Apr 2017 10:29:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/32882</link>
      <guid>https://ruby-china.org/topics/32882</guid>
    </item>
    <item>
      <title>关于多线程服务器的问题</title>
      <description>&lt;p&gt;我尝试用 Rainbows! 或者 Puma 作为服务器，单进程 16 线程。现在在 Mac OSX localhost 上测试。Rails 4 + ruby 2&lt;/p&gt;

&lt;p&gt;现在假设一个用户请求调用了一个 method，里面有句话是&lt;code&gt;sleep 60&lt;/code&gt;. 在这 60 秒内，另一个用户想访问网站其他页面，我没有做成功，不知道是哪里错了。请大家帮忙看一下&lt;/p&gt;

&lt;p&gt;Rainbows! 配置文件&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rainbows&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="ss"&gt;:ThreadPool&lt;/span&gt; &lt;span class="c1"&gt;# 我也试过eventmachine&lt;/span&gt;
  &lt;span class="n"&gt;worker_connections&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 省略一些Unicorn下的配置。使用Unicorn的配置是服务器跑的很好&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puma 的配置文件&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env puma&lt;/span&gt;
&lt;span class="n"&gt;pidfile&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;state_path&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;stdout_redirect&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>allenlsy</author>
      <pubDate>Mon, 11 Nov 2013 14:07:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/15452</link>
      <guid>https://ruby-china.org/topics/15452</guid>
    </item>
    <item>
      <title>多主键的数据库</title>
      <description>&lt;p&gt;在 Rails 开发中一般大家怎么实现多主键的数据表呢？&lt;/p&gt;

&lt;p&gt;比如，我有一个表叫做 PURCHASE，用于存储用户购买商品的记录。里面有 user_id 和 product_id。我希望 [user_id, product_id] 是主键组，这样用户不能重复购买。&lt;/p&gt;

&lt;p&gt;听说的解决方案有使用 uuid，或者 Composite Primary Keys 这个 gem。&lt;/p&gt;

&lt;p&gt;大家什么意见？谢谢&lt;/p&gt;</description>
      <author>allenlsy</author>
      <pubDate>Tue, 08 Oct 2013 08:44:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/14575</link>
      <guid>https://ruby-china.org/topics/14575</guid>
    </item>
    <item>
      <title>从网页中获取最重要的图片</title>
      <description>&lt;p&gt;我想实现类似 pinterest 一个功能，当用户输入一个网页的 url 后，网站会获取这个 url 中最重要的图片作为 pin 的图片。手动实现的方法，我觉得就是在这个 url 的所有图片中找一个最大的图片（这要出来的结果不一定准确，我知道）。不知道有什么 gem 可以实现这个功能吗？&lt;/p&gt;</description>
      <author>allenlsy</author>
      <pubDate>Mon, 29 Apr 2013 09:17:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/10605</link>
      <guid>https://ruby-china.org/topics/10605</guid>
    </item>
  </channel>
</rss>
