<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Robbs Luo</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://www.robbs.win/en/</id>
  <link href="https://www.robbs.win/en/" rel="alternate"/>
  <link href="https://www.robbs.win/en/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Robbs Luo</rights>
  <subtitle>己所不欲，勿施于人</subtitle>
  <title>Robbs</title>
  <updated>2026-07-01T02:18:03.442Z</updated>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="JavaScript" scheme="https://www.robbs.win/en/categories/JavaScript/"/>
    <category term="JavaScript" scheme="https://www.robbs.win/en/tags/JavaScript/"/>
    <category term="Async" scheme="https://www.robbs.win/en/tags/Async/"/>
    <content>
      <![CDATA[<h2 id="Problem-Statement"><a href="#Problem-Statement" class="headerlink" title="Problem Statement"></a>Problem Statement</h2><p>Given n asynchronous tasks, these n asynchronous tasks must be executed sequentially, and each subsequent asynchronous task depends on the result of the previous one as its parameter. How do we implement this?</p><h2 id="Solution-1-for-loop-await"><a href="#Solution-1-for-loop-await" class="headerlink" title="Solution 1: for loop + await"></a>Solution 1: for loop + await</h2><p>A simple for loop iterates sequentially, unlike Array.forEach and Array.map, which execute concurrently. Leveraging this characteristic along with async &#x2F; await, it is easy to write code like the following:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">(<span class="title function_">async</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> <span class="title function_">sleep</span> = delay =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="built_in">setTimeout</span>(<span class="function"><span class="params">_</span> =&gt;</span> <span class="title function_">resolve</span>(), delay)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">const</span> <span class="title function_">task</span> = (<span class="params">i</span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="title function_">async</span> (resolve, reject) =&gt; &#123;</span><br><span class="line">      <span class="keyword">await</span> <span class="title function_">sleep</span>(<span class="number">500</span>)</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`now is <span class="subst">$&#123;i&#125;</span>`</span>)</span><br><span class="line">      ++i</span><br><span class="line">      <span class="title function_">resolve</span>(i)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">let</span> param = <span class="number">0</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">4</span>; i++) &#123;</span><br><span class="line">    param = <span class="keyword">await</span> <span class="title function_">task</span>(param)</span><br><span class="line">  &#125;  </span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure><p>Output:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">now is 0</span><br><span class="line">now is 1</span><br><span class="line">now is 2</span><br><span class="line">now is 3</span><br></pre></td></tr></table></figure><p>Although this achieves the desired effect, the <code>param</code> local variable is rather unpleasant to look at. See Solution 2.</p><h2 id="Solution-2-Array-prototype-reduce"><a href="#Solution-2-Array-prototype-reduce" class="headerlink" title="Solution 2: Array.prototype.reduce"></a>Solution 2: Array.prototype.reduce</h2><p>When most people first encounter the <code>Array.prototype.reduce</code> method, they use it to sum an array. If you are not familiar with it, you can click the link to learn about <a href="https://developer.mozilla.org/zhCN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce">reduce</a>.</p><p><code>reduce</code> has the concepts of an <code>initial value</code>, an <code>accumulator</code>, and a <code>current value</code>. The <code>accumulator</code> can be thought of as the previous value, and by returning the <code>accumulator</code> it can also be thought of as the next value (this may sound convoluted; you can refer to the source code of Redux’s middleware execution order, which also uses reduce). The code that uses <code>reduce</code> to solve the problem is:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">sleep</span> = delay =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function"><span class="params">_</span> =&gt;</span> <span class="title function_">resolve</span>(), delay)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">task</span> = (<span class="params">i</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="title function_">async</span> (resolve, reject) =&gt; &#123;</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">sleep</span>(<span class="number">500</span>)</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`now is <span class="subst">$&#123;i&#125;</span>`</span>)</span><br><span class="line">    ++i</span><br><span class="line">    <span class="title function_">resolve</span>(i)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">[task, task, task, task].<span class="title function_">reduce</span>(<span class="title function_">async</span> (prev, task) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> res = <span class="keyword">await</span> prev</span><br><span class="line">  <span class="keyword">return</span> <span class="title function_">task</span>(res)</span><br><span class="line">&#125;, <span class="number">0</span>)</span><br></pre></td></tr></table></figure><p>Output:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">now is 0</span><br><span class="line">now is 1</span><br><span class="line">now is 2</span><br><span class="line">now is 3</span><br></pre></td></tr></table></figure><p>You can understand <code>prev</code> and <code>task</code> as follows:</p><ul><li>prev: the previous asynchronous task (promise)</li><li>task: the current asynchronous task</li></ul><p>The current asynchronous task needs the result of the previous asynchronous task as its parameter, so it obviously needs to <code>await prev</code>.</p>]]>
    </content>
    <id>https://www.robbs.win/en/2018-06-14/JavaScript-implementation-successively-executes-the-asynchronous-task.html</id>
    <link href="https://www.robbs.win/en/2018-06-14/JavaScript-implementation-successively-executes-the-asynchronous-task.html"/>
    <published>2018-06-14T09:10:10.000Z</published>
    <summary>Given n asynchronous tasks, these n asynchronous tasks must be executed sequentially, and each subsequent task depends on the result of the previous one as its parameter. How do we implement this?</summary>
    <title>Implementing Sequential Execution of Asynchronous Tasks in JavaScript</title>
    <updated>2026-07-01T02:18:03.442Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="JavaScript" scheme="https://www.robbs.win/en/categories/JavaScript/"/>
    <category term="JavaScript" scheme="https://www.robbs.win/en/tags/JavaScript/"/>
    <category term="Currying" scheme="https://www.robbs.win/en/tags/Currying/"/>
    <content>
      <![CDATA[<p><code>Currying</code> is a feature common to functional languages such as Perl, Python, and JavaScript. This post uses JavaScript to illustrate the idea behind currying and its applications. Suppose a function library provides the following function that assembles a URL:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">simpleURL</span>(<span class="params">protocol, domain, path</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> protocol + <span class="string">&quot;://&quot;</span> + domain + <span class="string">&quot;/&quot;</span> + path;</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">simpleURL</span>(<span class="string">&#x27;http&#x27;</span>,<span class="string">&#x27;www.jackzxl.net&#x27;</span>, <span class="string">&#x27;index.html&#x27;</span>);     <span class="comment">//http://www.jackzxl.net/index.html</span></span><br></pre></td></tr></table></figure><p>This is a perfectly ordinary function with nothing remarkable about it. But for your own site, the first argument is fixed to <code>http</code>, and the second is fixed to <code>www.jackzxl.net</code>; only the third argument ever changes. In other words, for any page or resource on your site, the first two arguments are always the same, and you only need to vary the third.</p><p>Obviously you don’t want to manually type those first two arguments every time you call the function — it’s tedious and error-prone. So what can you do? You might think about simply rewriting the library function to take a single argument:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">simpleURL</span>(<span class="params">path</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;http://www.jackzxl.net/&quot;</span> + path;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>This change has two problems. First, if the library function is also used by other people or in other places, modifying it directly is out of the question. Second, even if you have full control over the function, this approach is inflexible — what if one day you want to enable SSL on your site? You’d have to put the first argument back. The correct choice, therefore, is currying. Currying means: binding a function to a subset of its arguments and returning a new function. If that feels abstract, you can draw an analogy to partial specialization in C++ templates, which makes it easier to grasp. Let’s curry the example above:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myURL = simpleURL.<span class="title function_">bind</span>(<span class="literal">null</span>, <span class="string">&#x27;http&#x27;</span>, <span class="string">&#x27;www.jackzxl.net&#x27;</span>);</span><br><span class="line"><span class="title function_">myURL</span>(<span class="string">&#x27;myfile.js&#x27;</span>);     <span class="comment">//http://www.jackzxl.net/myfile.js</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// The site adds SSL</span></span><br><span class="line"><span class="keyword">var</span> mySslURL = simpleURL.<span class="title function_">bind</span>(<span class="literal">null</span>, <span class="string">&#x27;https&#x27;</span>, <span class="string">&#x27;www.jackzxl.net&#x27;</span>);</span><br><span class="line"><span class="title function_">mySslURL</span>(<span class="string">&#x27;myfile.js&#x27;</span>);  <span class="comment">//https://www.jackzxl.net/myfile.js</span></span><br></pre></td></tr></table></figure><p>The code above uses <code>bind</code> to implement currying. Now revisit the definition of currying: binding a function to a subset of its arguments and returning a new function. After currying, the function becomes more flexible and more fluent — it’s a concise way to delegate behavior. Why use <code>bind</code> to implement currying? Because it’s simple — there’s no need to reinvent the wheel when a built-in is already available. But since this post is about currying, let’s implement it ourselves to deepen our understanding. It needs to satisfy two requirements: accept a subset of arguments, and return a new function:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> currying = <span class="keyword">function</span>(<span class="params">fn</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> args = [].<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> newArgs = args.<span class="title function_">concat</span>([].<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>));</span><br><span class="line">        <span class="keyword">return</span> fn.<span class="title function_">apply</span>(<span class="literal">null</span>, newArgs);</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> myURL2 = <span class="title function_">currying</span>(simpleURL, <span class="string">&#x27;https&#x27;</span>, <span class="string">&#x27;www.jackzxl.net&#x27;</span>);</span><br><span class="line"><span class="title function_">myURL2</span>(<span class="string">&#x27;myfile.js&#x27;</span>);    <span class="comment">//http://www.jackzxl.net/myfile.js</span></span><br></pre></td></tr></table></figure><p>The effect is the same as using <code>bind</code>. Let’s examine the custom <code>currying</code> function more closely: the first argument, <code>fn</code>, is the function to be curried (<code>simpleURL</code>), and the rest are variadic arguments (for more on a function’s <code>arguments</code>, see here). The result of each line in <code>currying</code> is as follows:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> currying = <span class="keyword">function</span>(<span class="params">fn</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> args = [].<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>, <span class="number">1</span>);</span><br><span class="line">    <span class="comment">// args is [&quot;https&quot;, &quot;www.jackzxl.net&quot;]</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> newArgs = args.<span class="title function_">concat</span>([].<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>));</span><br><span class="line">        <span class="comment">// newArgs is [&quot;https&quot;, &quot;www.jackzxl.net&quot;, &quot;myFile.js&quot;]</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> fn.<span class="title function_">apply</span>(<span class="literal">null</span>, newArgs);</span><br><span class="line">        <span class="comment">// equivalent to return simpleURL(&quot;https&quot;, &quot;www.jackzxl.net&quot;, &quot;myFile.js&quot;);</span></span><br><span class="line">    &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>We’ve now covered the principle and the implementation of currying. So what is currying actually good for? The common benefits are:</p><ul><li>Parameter reuse</li><li>Deferred execution</li><li>Flattening</li></ul><p><code>Parameter reuse</code> was already demonstrated in the example above, so I won’t belabor it.</p><p><code>Deferred execution</code> is actually very intuitive: because the function returns a new function instead of a computed result, execution is naturally deferred. For example, <code>bind</code> is a representative case of deferred execution, so again I won’t dwell on it.</p><p><code>Flattening</code> makes functions more readable. For instance, suppose you want to extract the titles of all posts from your site’s JSON data:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// JSON data</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;user&quot;</span>: <span class="string">&quot;Jack&quot;</span>,</span><br><span class="line">    <span class="string">&quot;posts&quot;</span>: [</span><br><span class="line">        &#123; <span class="string">&quot;title&quot;</span>: <span class="string">&quot;JavaScript Curry&quot;</span>, <span class="string">&quot;contents&quot;</span>: <span class="string">&quot;...&quot;</span> &#125;,</span><br><span class="line">        &#123; <span class="string">&quot;title&quot;</span>: <span class="string">&quot; JavaScript Function&quot;</span>, <span class="string">&quot;contents&quot;</span>: <span class="string">&quot;...&quot;</span> &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Get the titles of all posts from the JSON data</span></span><br><span class="line"><span class="title function_">fetchFromServer</span>()</span><br><span class="line">    .<span class="title function_">then</span>(<span class="title class_">JSON</span>.<span class="property">parse</span>)</span><br><span class="line">    .<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">data</span>)&#123; <span class="keyword">return</span> data.<span class="property">posts</span> &#125;)</span><br><span class="line">    .<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">posts</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span> posts.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">post</span>)&#123; <span class="keyword">return</span> post.<span class="property">title</span> &#125;)</span><br><span class="line">    &#125;)</span><br></pre></td></tr></table></figure><p>Of course you could write more elegant code than this… but that’s beside the point. The point is to use currying to make the code more readable and maintainable:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> curry = <span class="built_in">require</span>(<span class="string">&#x27;curry&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> get = <span class="title function_">curry</span>(<span class="keyword">function</span>(<span class="params">property, object</span>)&#123; <span class="keyword">return</span> object[property] &#125;);</span><br><span class="line"></span><br><span class="line"><span class="title function_">fetchFromServer</span>()</span><br><span class="line">    .<span class="title function_">then</span>(<span class="title class_">JSON</span>.<span class="property">parse</span>)</span><br><span class="line">    .<span class="title function_">then</span>(<span class="title function_">get</span>(<span class="string">&#x27;posts&#x27;</span>))</span><br><span class="line">    .<span class="title function_">then</span>(<span class="title function_">map</span>(<span class="title function_">get</span>(<span class="string">&#x27;title&#x27;</span>)))</span><br></pre></td></tr></table></figure><p><strong>Early return?</strong></p><p>Finally, another use case you’ll find online is early return. For example, IE handles events differently from other browsers; to achieve compatibility you could write:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> addEvent = (<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="keyword">if</span> (target.<span class="property">addEventListener</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params">target, eventType, handler</span>) &#123;</span><br><span class="line">            target.<span class="title function_">addEventListener</span>(eventType, handler, <span class="literal">false</span>);</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;        <span class="comment">// IE</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params">target, eventType, handler</span>) &#123;</span><br><span class="line">            target.<span class="title function_">attachEvent</span>(<span class="string">&quot;on&quot;</span> + eventType, handler);</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)();  </span><br></pre></td></tr></table></figure><p>In my view, however, currying doesn’t add much value here. Currying certainly has many advantages, but it has equally obvious drawbacks — notably a non-trivial learning curve. Using currying to implement “early return” costs more in maintenance than it gains.</p><p>How can you achieve the same thing without currying? A single ternary operator does the trick:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> addHandler = <span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">addEventListener</span> ?</span><br><span class="line">    <span class="keyword">function</span>(<span class="params">target, eventType, handler</span>)&#123;</span><br><span class="line">        target.<span class="title function_">addEventListener</span>(eventType, handler, <span class="literal">false</span>);</span><br><span class="line">    &#125; :</span><br><span class="line">    <span class="keyword">function</span>(<span class="params">target, eventType, handler</span>)&#123;</span><br><span class="line">        target.<span class="title function_">attachEvent</span>(<span class="string">&quot;on&quot;</span> + eventType, handler);</span><br><span class="line">    &#125;;</span><br></pre></td></tr></table></figure><p>Or you can rewrite the function from within itself:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">addHandler</span>(<span class="params">target, eventType, handler</span>)&#123;</span><br><span class="line">    <span class="keyword">if</span> (target.<span class="property">addEventListener</span>)&#123;</span><br><span class="line">        addHandler = <span class="keyword">function</span>(<span class="params">target, eventType, handler</span>)&#123;  <span class="comment">// rewrite this function</span></span><br><span class="line">            target.<span class="title function_">addEventListener</span>(eventType, handler, <span class="literal">false</span>);</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;        <span class="comment">// IE</span></span><br><span class="line">        addHandler = <span class="keyword">function</span>(<span class="params">target, eventType, handler</span>)&#123;  <span class="comment">// rewrite this function</span></span><br><span class="line">            target.<span class="title function_">attachEvent</span>(<span class="string">&quot;on&quot;</span> + eventType, handler);</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">addHandler</span>(target, eventType, handler); <span class="comment">// call the new function</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Both approaches are straightforward and crystal clear. Don’t reach for currying just for the sake of using currying.</p><h2 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h2><p>Although currying has a somewhat mysterious name, it isn’t really mysterious once you see through it. On the frontend its use cases are limited (though that may just reflect my own limited experience); it’s more often applied to asynchronous functions on the backend, such as in Node.js, where currying async APIs can reduce callback nesting.</p><hr><blockquote><p>Source: <a href="https://www.jianshu.com/p/9b6b5c7527fc">https://www.jianshu.com/p/9b6b5c7527fc</a></p></blockquote>]]>
    </content>
    <id>https://www.robbs.win/en/2018-06-11/JavaScript-Currying.html</id>
    <link href="https://www.robbs.win/en/2018-06-11/JavaScript-Currying.html"/>
    <published>2018-06-11T00:55:54.000Z</published>
    <summary>Currying is a feature common to functional languages such as Perl, Python, and JavaScript. This post uses JavaScript to illustrate the idea of currying and its applications.</summary>
    <title>JavaScript Currying</title>
    <updated>2026-07-01T02:27:13.699Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="DevOps" scheme="https://www.robbs.win/en/categories/DevOps/"/>
    <category term="DevOps" scheme="https://www.robbs.win/en/tags/DevOps/"/>
    <category term="VIPS" scheme="https://www.robbs.win/en/tags/VIPS/"/>
    <content>
      <![CDATA[<p>Today’s talk covers four areas:</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526622683089-e53de777-e7e9-43a3-9796-082929f6da12-image-resized.png"> </p><h2 id="Problems-We-Once-Faced"><a href="#Problems-We-Once-Faced" class="headerlink" title="Problems We Once Faced"></a>Problems We Once Faced</h2><p>Vipshop is a mid-sized internet company in terms of scale, and we hope our practices can serve as a reference for peers of a similar size.</p><p>Vipshop’s business logic is quite extensive. Beyond e-commerce, it also covers logistics and finance. During the early phase of rapid growth, the priority was to ship features quickly, which resulted in a wide variety of technology architectures. Later on, the introduction of third-party software for the finance business also brought some less-than-standard practices.</p><p>Different teams handled different business lines. Because the business lines were not unified, operations had many blind spots, making it hard to share headcount.</p><p>We built a large number of platforms for release, change management, and so on, yet it always felt like something was missing. We could not form a strong enough force to support the business teams, and we were operating in a fragmented way. Because the technology stacks were inconsistent, building platforms meant accounting for all kinds of special cases and even making compromises.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526622757566-c7d84c80-d860-44a9-98eb-d46e267b748e-image-resized.png"> </p><p>So we began to reflect:</p><ul><li>First, how can engineers strike a balance among <code>quality</code>, <code>cost</code>, and <code>efficiency</code>? The diagram below highlights the key questions. No one can claim to have perfected all three; the goal is to find the best balance point among them.</li><li>Second, after building so many tooling platforms, why are operations staff still exhausted?</li><li>Third, while racing headlong forward on operations platform construction, how do we stay true to our original intent?</li></ul><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526622822459-efc353d7-1eb0-4d67-8eae-06818735481e-image-resized.png"><br>From this we summarized four lessons:</p><ul><li>First, platform builders must deeply understand the pain points of operations.</li><li>Second, technology selection for platform construction is not the most important factor; truly understanding operations is what matters most. That is not to say technology selection is unimportant — it just is not the most important.</li><li>Third, the degree of standardization sets the ceiling for operations automation.</li><li>Fourth, the level of automation determines new growth points for operations. In a word: standardization is urgent.</li></ul><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526622880585-b4096aba-b0d8-4c68-9ef6-912d8824cfbd-image-resized.png"> </p><h2 id="The-Road-of-Standardization-Construction"><a href="#The-Road-of-Standardization-Construction" class="headerlink" title="The Road of Standardization Construction"></a>The Road of Standardization Construction</h2><h3 id="The-Component-Concept"><a href="#The-Component-Concept" class="headerlink" title="The Component Concept"></a>The Component Concept</h3><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526622977798-6bee3895-00bc-4961-bff5-6315e81afd13-image-resized.png"> </p><p>The component mindset laid the foundation for standardization:</p><ul><li>On the technical growth side, our approach is to have component expert teams take charge. The expert team defines the direction of the component and explores best practices, which supports technical accumulation and the skill development of the team members.</li><li>Component-based service-ification: operations staff transform outward into technology output, providing service-based products. Developers only need to consume standard APIs without worrying about underlying details.</li><li>Eliminate business silos and give business teams new goals to pursue.</li></ul><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623024415-6b09350f-eb66-483a-a012-88ab0dc81405-image-resized.png"> </p><h3 id="Standardization-Construction-Blueprint"><a href="#Standardization-Construction-Blueprint" class="headerlink" title="Standardization Construction Blueprint"></a>Standardization Construction Blueprint</h3><p>This is the big picture of standardization construction. Operations standardization is a very large program, so we split it into more than a dozen smaller projects.<br><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623121436-ccb8e90d-c823-47ec-bbcf-01fb8520055a-image-resized.png"> </p><p>Combined with Vipshop’s business characteristics, we broke it down based on the company’s specific business situation. On the left is the technology process stack, and on the right are the deliverables. The items in red relate to business characteristics, while those below red relate to operations.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623181608-62dd2199-2ef8-4caa-98bc-ae13f28f6d9c-image-resized.png"> </p><h3 id="Configuration-Repository-Management"><a href="#Configuration-Repository-Management" class="headerlink" title="Configuration Repository Management"></a>Configuration Repository Management</h3><p>When a developer submits a request for operations to change a configuration in production, it is very painful. Does the developer have permission to do it themselves? Or does operations handle it — but operations doesn’t understand it, while the developer thinks operations is clueless and can’t be bothered to explain. Operations has no time to study hundreds of business systems.</p><p>The solution is layered governance: let the specialists do what they specialize in.</p><p>For example, when a shopping-cart parameter change affects business logic, that kind of task is handed to the developers. Tasks related to components are handled by the expert team.</p><p>Of course, developers cannot directly modify files in some path on production. So we built the Janitors platform, which organizes all kinds of configuration files in a standardized way and grants developers permission to manage the parameters of their own business systems, allowing them to make changes that take effect immediately.</p><p>Operations focuses on the layer below, performing operations management based on Puppet.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623270372-27bca87a-88b8-4c9e-8dbf-ade82a2418d1-image-resized.png"> </p><h3 id="Monitoring-Standardization"><a href="#Monitoring-Standardization" class="headerlink" title="Monitoring Standardization"></a>Monitoring Standardization</h3><p>When the number of machines exceeds 10,000, Zabbix becomes overwhelmed. Vipshop initially ran multiple sets of Zabbix for monitoring.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623312925-f65f893d-6806-404c-bbc1-b2ad4b960bd6-image-resized.png"> </p><p>Ideal monitoring:</p><ul><li>Unified, fast, and precise</li><li>A single entry point</li><li>Automated, with standardized monitoring plugins</li><li>No manual intervention required for deploying or adding monitoring</li><li>Standardized monitoring plugins. Everyone has their own idea of what monitoring standards should be, and the result is multiple monitoring metrics — which can be fatal.</li><li>Customizable monitoring views that fully unlock the value of the data. This is operations-leaning. We want to maximize the value of the runtime metrics from production — to see which systems make money while using the fewest resources, and which systems burn money while consuming the most.</li><li>Empower developers and keep the system scalable.</li></ul><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623366408-20a56b8f-2bd4-434d-93b3-a82644a01a97-image-resized.png"> </p><p>None of the above is satisfied by Zabbix.</p><p>Breakdown of monitoring standardization goals:</p><ul><li>The first layer is the event source, i.e., CMDB application information standardization.</li><li>The second layer is monitoring module standardization. The expert team is responsible for designing monitoring and setting thresholds for technical components, with monitoring templates placed under unified version control.</li><li>The third layer is alerting rule standardization. The monitoring system and the alerting system are decoupled, each with its own responsibility. Alerts are differentiated by device tier, application tier, and severity.</li></ul><p>The unified source for alert recipients is the CMDB.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623412346-0e342a3b-3734-4ab5-b1db-66b405ad9fb3-image-resized.png"> </p><p>We built our own product, VIPFalcon, based on secondary development of open-falcon. It currently monitors around 25,000 nodes with more than 5 million metrics, and uses data stream computing to re-aggregate the collected data. This data lands in Hive for data analysis, fully unlocking the value of the data.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623459856-530145b7-8c67-4d75-b36e-3ca76aa3a4d3-image-resized.png"> </p><p>After monitoring standardization, comparing VIPFalcon with Zabbix: from a single place you can see all the infrastructure monitoring information of the entire company, without needing multiple entry points. In terms of collection extensibility, VIPFalcon is plugin-based, oriented around the HostGroup dimension, and maintained via Git. In terms of management, integration with the operations ecosystem, and programming language support, VIPFalcon is well ahead of Zabbix.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623492880-06a3fdfd-86d0-4dd6-8de2-aa2a6f4ca7a4-image-resized.png"> </p><p>As a result of this work, internally we achieved quality improvements, higher work efficiency, lower maintenance costs, better user experience, and better risk control.</p><p>Outwardly, we exported value by opening up the operations ecosystem, empowering developers, and helping them think about resource usage from their perspective.</p><p>Fine-grained operations and business cost accounting are an output of the value of the data.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623668710-5d19c184-cdda-4ee0-a774-2b657ca3d9eb-image-resized.png"> </p><h3 id="Change-Standardization"><a href="#Change-Standardization" class="headerlink" title="Change Standardization"></a>Change Standardization</h3><p>Changes are tightly coupled with everyone in operations — every “blame” that operations takes is related to this area. The key is two ideas:</p><ul><li>First, we proposed a risk matrix. We built an SDK. To describe the matrix in one sentence: it splits the original change risk into two dimensions — the object and the technical risk. Briefly, the object refers to a business system: it may be a core system, an important system, or an unimportant one. We can profile it and assign a score. Combined together, these give each change a precise score.</li><li>A standard change template library means that changes go through expert-designed templates for every change to each component. Changes must be based on the experts’ templates; you cannot improvise a change plan however you like.</li></ul><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623711827-15fb2866-1d6e-40bb-bce6-abc617fb9639-image-resized.png"> </p><p>After this work was solidified, we turned all kinds of changes into individual APPs, so that on the change platform you can supply parameters and execute a change with one click.</p><h2 id="Connecting-the-Ecosystem-and-Empowering-Everyone"><a href="#Connecting-the-Ecosystem-and-Empowering-Everyone" class="headerlink" title="Connecting the Ecosystem and Empowering Everyone"></a>Connecting the Ecosystem and Empowering Everyone</h2><p>What do we mean by ecosystem? We have built many automation systems. If each one still requires a person to perform a task — for example, making a change still requires someone to click buttons — we are still in the early stage of automation. The ecosystem we want to build is one where systems drive systems; all systems interact through API calls, and humans are involved as little as possible.</p><h3 id="CMDB-Replaces-Process"><a href="#CMDB-Replaces-Process" class="headerlink" title="CMDB Replaces Process"></a>CMDB Replaces Process</h3><p>To connect the operations ecosystem, the original approach was process-centric. Most people dislike processes; no one really loves them. Later, we shifted to an approach centered on operations processes + the CMDB. A process is a rather dogmatic thing. For example, a process might tell me that I can do change A at 3 PM this afternoon — but can I really? It does not weigh the context, the object, or the timing. If the object has a problem at 3 PM, the process should not proceed, but the process naively tells you to go ahead.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623803451-9dc28059-81c2-40a6-9b8d-12a31d720abd-image-resized.png"> </p><p>This is Vipshop’s specific business. In the middle are process control and a CMDB-centric approach, and from top to bottom are the operations-related deliverables — from components, to deployment standards, to all kinds of self-service platforms. Once the processes are connected, monitoring can fire alerts, and we want monitoring to drive self-healing. For example, when a disk alert reaches 90%, an alert is generated, which invokes a disk-cleanup APP on the automation platform, and that APP executes the cleanup. After cleanup, it tells the monitoring system to send a text message: a 90% alert was detected and the cleanup has been executed. This is a fairly ideal working state, and it has already been realized.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623850017-9060d80e-f9aa-466b-b0c6-6ae9fda6218b-image-resized.png"> </p><h3 id="Approach-to-Connecting-Changes"><a href="#Approach-to-Connecting-Changes" class="headerlink" title="Approach to Connecting Changes"></a>Approach to Connecting Changes</h3><p>When it comes to ecosystem connectivity, our initial thinking was rather grand. We originally wanted to design a perfect process that would completely connect everything. We later realized that was naive; it is very hard to have a perfect plan that connects all the operations-related tooling. So our approach shifted to connecting them one by one. For example, to connect systems A, B, C, and D: first connect A-B, then connect C-D. A few issues with changes are ones that everyone encounters (see the slides). We built a very good and very flexible automated change system, but this is a double-edged sword: DevOps changes can go out of control and change processes may not be followed.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623909934-7bea1880-1054-4e9d-8e18-ca53600fa7ab-image-resized.png"><br>Design principles for connecting changes:</p><ul><li>First, the process system provides an SDK to all automation tools, offering change control, change collection, and SDK self-governance capabilities.</li><li>Second, the automation capability platform sorts out the change risk matrix, integrates the SDK, and reports the change risk matrix.</li><li>Third, the process system provides an SDK to all automation tools, offering change control, change collection, and SDK self-governance capabilities.</li></ul><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623944201-368e7f65-28d4-4bf6-80dd-fed7fdffc643-image-resized.png"> </p><p>This is one of our systems (see diagram below). In the middle is a standard template library where the technical expert team designed a set of standard changes. The SDK is linked on the left side to the load balancing management platform, the scheduled-task management platform, the cloud platform, the operations platform, and others — connecting these platforms to empower developers.</p><p>When a developer wants to make a simple configuration change on a business line, the old way was for the developer to find operations and ask for the change. Operations would say “wait,” submit a change process, and once the process was rolling, find that the boss wasn’t around and the boss’s approval was needed. If everything went smoothly, this took the better part of a day.</p><p>Now the developer submits a request on the tools platform, and if central control considers the risk very low, it goes through directly — the developer’s task is done.</p><p>The benefit is that standard changes are solidified, processes are simplified, change risk is effectively controlled, and developers are empowered.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526623980216-a03adf71-a356-4373-b78f-75e5ea381397-image-resized.png"> </p><h3 id="Monitoring-Connectivity-Enables-Automation"><a href="#Monitoring-Connectivity-Enables-Automation" class="headerlink" title="Monitoring Connectivity Enables Automation"></a>Monitoring Connectivity Enables Automation</h3><p>Our idea is that across the entire server lifecycle — initialization, deployment, running, pausing, and service decommissioning — none of the monitoring setup requires manual intervention. All entry points are connected between the monitoring system and the CMDB. There is only one source of information: the CMDB. When the CMDB detects a change in some piece of information, it writes to a message queue; the monitoring system then consumes that queue and executes the corresponding monitoring mount or unmount flow.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526624031971-3cd2945b-633e-45a8-95a3-7dd1f0331ddc-image-resized.png"> </p><p>This is the concrete implementation (see diagram below). In the middle is still the monitoring system, and on the left are alerting and data aggregation.</p><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526624050910-6b4e1f4a-83ac-4952-bb2b-9162ab111592-image-resized.png"> </p><p>After the previous standardization and ecosystem connectivity, the gains are twofold:</p><ul><li>First, operations finally has comprehensive, centralized data. Monitoring data is captured, all production change data is in hand, and runtime data all settles into a unified pool. Why is this important? If you want to do AIOps, everything is empty talk without data. By connecting the ecosystem this way, data is deposited in one place, laying the groundwork for future intelligent operations.</li><li>Second, through these efforts, we struck a balance between efficiency and process — ensuring efficiency while keeping risk under control.</li></ul><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526624081158-be8607c6-40a8-4441-8a7d-d8d7ccb0f857-image-resized.png"> </p><h3 id="Looking-Back"><a href="#Looking-Back" class="headerlink" title="Looking Back"></a>Looking Back</h3><p>Returning to the three questions posed at the beginning, there are clear gains across quality, efficiency, and cost.</p><ul><li>On quality, we transformed from manual labor into platform builders, taking the first step from automation toward intelligence.</li><li>On efficiency, we lowered the barrier to entry, improved efficiency, and enforced process control.</li><li>On cost, after aggregating the data, we profiled each application and gained data-backed insight into resource utilization.</li></ul><p><img src="/en/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance/1526624130023-948f7fee-19ed-4c58-a632-4bcda7f7020b-image-resized.png"> </p><h2 id="A-Few-Reflections"><a href="#A-Few-Reflections" class="headerlink" title="A Few Reflections"></a>A Few Reflections</h2><ul><li><strong>Only on the soil of standardization can automation and intelligence take root and grow.</strong></li><li><strong>Standardization requires strong leadership and a clear methodology.</strong></li><li><strong>DevOps construction needs a business perspective. We don’t do technology for technology’s sake; we build around cost, quality, and efficiency, and the vision for platform building should aim higher.</strong></li><li><strong>A single branch of service must be consolidated into a combined-arms force to deliver real combat power. Climbing one more rung up the food chain depends on the ability to integrate.</strong></li></ul>]]>
    </content>
    <id>https://www.robbs.win/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance.html</id>
    <link href="https://www.robbs.win/en/2018-05-18/The-Road-Of-Standardization-Construction-Of-VIPS-Operation-And-Maintenance.html"/>
    <published>2018-05-18T06:22:00.000Z</published>
    <summary>This article is edited from Wang Junfeng's speech at the GOPS Global Operations Conference 2018 Shenzhen.</summary>
    <title>The Road of Standardization Construction of VIPS Operation and Maintenance</title>
    <updated>2026-07-01T02:19:06.635Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Architect" scheme="https://www.robbs.win/en/categories/Architect/"/>
    <category term="Architect" scheme="https://www.robbs.win/en/tags/Architect/"/>
    <category term="OAuth" scheme="https://www.robbs.win/en/tags/OAuth/"/>
    <content>
      <![CDATA[<p><a href="http://en.wikipedia.org/wiki/OAuth">OAuth</a> is an open standard for authorization, widely adopted around the world. Its current version is 2.0.<br>OAuth (Open Authorization) is an open standard that allows third-party websites, with the user’s consent, to access information the user has stored at a service provider. This kind of authorization is achieved without the user having to share their username and password with the third-party site. Instead, OAuth lets the user hand a token to the third-party site; each token is bound to a specific third-party site, and it can only access specific resources within a specific time window.</p><p>This article gives a concise and approachable explanation of the design rationale and operating flow of OAuth 2.0. The primary reference is <a href="http://www.rfcreader.com/#rfc6749">RFC 6749</a>.</p><h2 id="1-Application-Scenario"><a href="#1-Application-Scenario" class="headerlink" title="1. Application Scenario"></a>1. Application Scenario</h2><p>To understand where OAuth applies, let me use a hypothetical example.</p><p>Imagine a website called “Cloud Photo Print” that can print photos a user has stored on Google. To use this service, the user must allow “Cloud Photo Print” to read the photos they have stored on Google.<br><img src="/en/en/2018-05-17/Oauth2/Oauth2/1526537733448-2448dfdd-e958-4e93-922e-8158d24e7ca2-image.png" alt="Cloud Photo Print"></p><p>The problem is that Google will only let “Cloud Photo Print” read those photos if the user authorizes it. So how does “Cloud Photo Print” obtain the user’s authorization?</p><p>The traditional approach is for the user to give their Google username and password to “Cloud Photo Print”, which then reads the user’s photos. This approach has several serious drawbacks:</p><ol><li>“Cloud Photo Print” would store the user’s password for subsequent services, which is highly insecure.</li><li>Google would have to deploy password-based login, and as we know, plain password login is not secure.</li><li>“Cloud Photo Print” would gain the power to access everything the user has stored on Google, and the user would have no way to limit the scope or validity period of the authorization granted to “Cloud Photo Print”.</li><li>The only way for the user to revoke the power granted to “Cloud Photo Print” is to change their password. But doing so would also invalidate every other third-party application the user has authorized.</li><li>If any single third-party application is compromised, the user’s password would be leaked, along with all data protected by that password.</li></ol><p>OAuth was created to solve exactly these problems.</p><h2 id="2-Terminology"><a href="#2-Terminology" class="headerlink" title="2. Terminology"></a>2. Terminology</h2><p>Before diving into OAuth 2.0, we need to understand a few specialized terms. They are essential for following the rest of the explanation, especially the diagrams.</p><ol><li>Third-party application: the third-party application, also referred to as the “client” in this article – i.e., “Cloud Photo Print” in the example above.</li><li>HTTP service: the HTTP service provider, also referred to as the “service provider” in this article – i.e., Google in the example above.</li><li>Resource Owner: the resource owner, also referred to as the “user” in this article.</li><li>User Agent: the user agent, which in this article means the browser.</li><li>Authorization server: the authorization server, the server the service provider uses specifically to handle authentication.</li><li>Resource server: the resource server, the server where the service provider stores user-generated resources. It may be the same server as the authorization server, or a different one.</li></ol><p>With these terms in mind, it is not hard to see that the purpose of OAuth is to let the “client” securely and controllably obtain the “user’s” authorization and interact with the “service provider”.</p><h2 id="3-The-Idea-Behind-OAuth"><a href="#3-The-Idea-Behind-OAuth" class="headerlink" title="3. The Idea Behind OAuth"></a>3. The Idea Behind OAuth</h2><p>OAuth introduces an authorization layer between the “client” and the “service provider”. The “client” cannot log in directly to the “service provider”; it can only log in to the authorization layer, which keeps the user and the client separate. The token used by the “client” to log in to the authorization layer is different from the user’s password. At login time, the user can specify the scope and validity period of the authorization-layer token.</p><p>Once the “client” has logged in to the authorization layer, the “service provider” opens up the user’s stored data to the “client” based on the token’s scope and validity period.</p><h2 id="4-Operating-Flow"><a href="#4-Operating-Flow" class="headerlink" title="4. Operating Flow"></a>4. Operating Flow</h2><p>The operating flow of OAuth 2.0 is shown in the diagram below, taken from RFC 6749.<br><img src="/en/en/2018-05-17/Oauth2/Oauth2/1526538171063-df6e4c8f-a38d-493e-922f-b5607de02c81-image-resized.png"></p><blockquote><p><strong>A.  After the user opens the client, the client requests authorization from the user.</strong><br><strong>B.  The user agrees to grant the client authorization.</strong><br><strong>C.  The client uses the authorization obtained in the previous step to request a token from the authorization server.</strong><br><strong>D.  The authorization server authenticates the client and, once everything checks out, agrees to issue a token.</strong><br><strong>E.  The client uses the token to request resources from the resource server.</strong><br><strong>F.  The resource server verifies the token and agrees to expose resources to the client.</strong></p></blockquote><p>It is easy to see that among these six steps, B is the key – i.e., how the user grants authorization to the client. With this authorization, the client can obtain a token, and then use that token to obtain resources.</p><p>The following sections explain the four authorization modes one by one.</p><h2 id="5-Client-Authorization-Modes"><a href="#5-Client-Authorization-Modes" class="headerlink" title="5. Client Authorization Modes"></a>5. Client Authorization Modes</h2><p>The client must obtain the user’s authorization grant before it can receive an access token. OAuth 2.0 defines four authorization modes.</p><ul><li>Authorization Code</li><li>Implicit</li><li>Resource Owner Password Credentials</li><li>Client Credentials</li></ul><h3 id="5-1-Authorization-Code-Mode"><a href="#5-1-Authorization-Code-Mode" class="headerlink" title="5.1.  Authorization Code Mode"></a>5.1.  Authorization Code Mode</h3><p>Authorization Code mode is the most feature-complete and rigorous of the authorization flows. Its defining characteristic is that the client’s back-end server interacts directly with the “service provider’s” authorization server.<br><img src="/en/en/2018-05-17/Oauth2/Oauth2/1526538388731-000001cf-8ce8-428e-b84c-1843f840d674-image-resized.png"></p><blockquote><p><strong>A. The user accesses the client, which then redirects the user to the authorization server.</strong><br><strong>B. The user chooses whether to grant the client authorization.</strong><br><strong>C. Assuming the user grants authorization, the authorization server redirects the user to a “redirection URI” previously registered by the client, and includes an authorization code.</strong><br><strong>D. The client receives the authorization code and, together with the earlier “redirection URI”, requests a token from the authorization server. This step happens on the client’s back-end server and is invisible to the user.</strong><br><strong>E. The authorization server verifies the authorization code and the redirection URI, and once everything checks out, sends an access token and a refresh token to the client.</strong></p></blockquote><p>Below are the parameters required for each of these steps.</p><p>In step A, the URI the client uses to request authorization contains the following parameters:</p><ul><li><p>response_type: indicates the authorization type, required; the value here is fixed as “code”.</p></li><li><p>client_id: the client ID, required.</p></li><li><p>redirect_uri: the redirection URI, optional.</p></li><li><p>scope: the scope of access being requested, optional.</p></li><li><p>state: the client’s current state; any value may be used, and the authorization server will return it verbatim.</p></li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GET</span> <span class="string">/authorize?response_type=code&amp;client_id=s6BhdRkqt3&amp;state=xyz&amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>server.example.com</span><br></pre></td></tr></table></figure><p>In step C, the URI the server uses to respond to the client contains the following parameters:</p><ul><li><p>code: the authorization code, required. The code should have a very short validity period, typically set to 10 minutes, and the client may use it only once; otherwise the authorization server will reject it. The code is bound one-to-one to the client ID and the redirection URI.</p></li><li><p>state: if the client’s request contained this parameter, the authorization server’s response must include it with exactly the same value.</p></li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">HTTP/1.1</span> <span class="number">302</span> Found</span><br><span class="line"><span class="attribute">Location</span><span class="punctuation">: </span>https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&amp;state=xyz</span><br></pre></td></tr></table></figure><p>In step D, the HTTP request the client sends to the authorization server to request a token contains the following parameters:</p><ul><li><p>grant_type: indicates the authorization mode being used, required; the value here is fixed as “authorization_code”.</p></li><li><p>code: the authorization code obtained in the previous step, required.</p></li><li><p>redirect_uri: the redirection URI, required; it must match the value used in step A.</p></li><li><p>client_id: the client ID, required.</p></li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/token</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>server.example.com</span><br><span class="line"><span class="attribute">Authorization</span><span class="punctuation">: </span>Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>application/x-www-form-urlencoded</span><br><span class="line"></span><br><span class="line"><span class="language-apache"><span class="attribute">grant_type</span>=authorization_code&amp;code=SplxlOBeZQQYbYS6WxSbIA&amp;redirect_uri=https%<span class="number">3</span>A%<span class="number">2</span>F%<span class="number">2</span>Fclient%<span class="number">2</span>Eexample%<span class="number">2</span>Ecom%<span class="number">2</span>Fcb</span></span><br></pre></td></tr></table></figure><p>In step E, the HTTP response from the authorization server contains the following parameters:</p><ul><li><p>access_token: the access token, required.</p></li><li><p>token_type: the token type, required; the value is case-insensitive and can be of type “bearer” or “mac”.</p></li><li><p>expires_in: the expiration time, in seconds. If omitted, the expiration time must be set through some other means.</p></li><li><p>refresh_token: the refresh token, used to obtain the next access token; optional.</p></li><li><p>scope: the scope of access; if it matches the scope requested by the client, this field may be omitted.</p></li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">HTTP/1.1</span> <span class="number">200</span> OK</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>application/json;charset=UTF-8</span><br><span class="line"><span class="attribute">Cache-Control</span><span class="punctuation">: </span>no-store</span><br><span class="line"><span class="attribute">Pragma</span><span class="punctuation">: </span>no-cache</span><br><span class="line"></span><br><span class="line"><span class="language-json"><span class="punctuation">&#123;</span>    </span></span><br><span class="line"><span class="language-json">   <span class="attr">&quot;access_token&quot;</span><span class="punctuation">:</span><span class="string">&quot;2YotnFZFEjr1zCsicMWpAA&quot;</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">   <span class="attr">&quot;token_type&quot;</span><span class="punctuation">:</span><span class="string">&quot;example&quot;</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">   <span class="attr">&quot;expires_in&quot;</span><span class="punctuation">:</span><span class="number">3600</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">   <span class="attr">&quot;refresh_token&quot;</span><span class="punctuation">:</span><span class="string">&quot;tGzv3JOkF0XG5Qx2TlKWIA&quot;</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">   <span class="attr">&quot;example_parameter&quot;</span><span class="punctuation">:</span><span class="string">&quot;example_value&quot;</span></span></span><br><span class="line"><span class="language-json"><span class="punctuation">&#125;</span></span></span><br></pre></td></tr></table></figure><p>As you can see from the code above, the relevant parameters are sent in JSON format (Content-Type: application&#x2F;json). In addition, the HTTP headers explicitly specify that the response must not be cached.</p><h3 id="5-2-Implicit-Mode"><a href="#5-2-Implicit-Mode" class="headerlink" title="5.2 Implicit Mode"></a>5.2 Implicit Mode</h3><p>The Implicit grant type does not go through the third-party application’s server; instead, it requests a token directly from the authorization server in the browser, skipping the “authorization code” step – hence the name. All steps take place in the browser, the token is visible to the visitor, and the client does not need to be authenticated.<br><img src="/en/en/2018-05-17/Oauth2/Oauth2/1526538829881-8152eca9-54a1-489b-b802-7fc9447eb4bd-image.png"></p><blockquote><p><strong>A. The client redirects the user to the authorization server.</strong><br><strong>B. The user decides whether to grant the client authorization.</strong><br><strong>C. Assuming the user grants authorization, the authorization server redirects the user to the “redirection URI” specified by the client and includes the access token in the fragment portion of the URI.</strong><br><strong>D. The browser sends a request to the resource server, without the fragment value received in the previous step.</strong><br><strong>E. The resource server returns a web page containing code that can extract the token from the fragment.</strong><br><strong>F. The browser executes the script obtained in the previous step and extracts the token.</strong><br><strong>G. The browser sends the token to the client.</strong></p></blockquote><p>Below are the parameters required for these steps.<br>In step A, the HTTP request sent by the client contains the following parameters:</p><ul><li><p>response_type: indicates the authorization type; the value here is fixed as “token”, required.</p></li><li><p>client_id: the client ID, required.</p></li><li><p>redirect_uri: the redirection URI, optional.</p></li><li><p>scope: the scope of access, optional.</p></li><li><p>state: the client’s current state; any value may be used, and the authorization server will return it verbatim.</p></li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GET</span> <span class="string">/authorize?response_type=token&amp;client_id=s6BhdRkqt3&amp;state=xyz&amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>server.example.com</span><br></pre></td></tr></table></figure><p>In step C, the URI the authorization server returns to the client contains the following parameters:</p><ul><li><p>access_token: the access token, required.</p></li><li><p>token_type: the token type, required; the value is case-insensitive.</p></li><li><p>expires_in: the expiration time, in seconds. If omitted, the expiration time must be set through some other means.</p></li><li><p>scope: the scope of access; if it matches the scope requested by the client, this field may be omitted.</p></li><li><p>state: if the client’s request contained this parameter, the authorization server’s response must include it with exactly the same value.</p></li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">HTTP/1.1</span> <span class="number">302</span> Found</span><br><span class="line"><span class="attribute">Location</span><span class="punctuation">: </span>http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&amp;state=xyz&amp;token_type=example&amp;expires_in=3600</span><br></pre></td></tr></table></figure><p>In the example above, the authorization server uses the Location field of the HTTP headers to specify the URL the browser should be redirected to. Note that the token is contained in the fragment portion of this URL.</p><p>Following step D, the browser next visits the URL specified by Location, but the fragment portion is not sent. In step E, the code served by the service provider’s resource server extracts the token from the fragment.</p><h3 id="5-3-Password-Mode"><a href="#5-3-Password-Mode" class="headerlink" title="5.3 Password Mode"></a>5.3 Password Mode</h3><p>In Password mode (Resource Owner Password Credentials Grant), the user provides their username and password directly to the client. The client uses this information to request authorization from the “service provider”.</p><p>In this mode, the user must hand their password to the client, but the client must not store the password. This is typically used in situations where the user has a high degree of trust in the client – for example, when the client is part of the operating system or is produced by a well-known company. The authorization server should only consider this mode when the other authorization modes cannot be used.<br><img src="/en/en/2018-05-17/Oauth2/Oauth2/1526540998970-0d6ff6d7-f940-499a-ba77-ffcfe6b66a9c-image-resized.png"></p><blockquote><p><strong>A. The user provides their username and password to the client.</strong><br><strong>B. The client sends the username and password to the authorization server, requesting a token from it.</strong><br><strong>C. The authorization server verifies everything and, once it checks out, provides the client with an access token.</strong></p></blockquote><p>In step B, the HTTP request sent by the client contains the following parameters:</p><ul><li><p>grant_type: indicates the authorization type; the value here is fixed as “password”, required.</p></li><li><p>username: the username, required.</p></li><li><p>password: the user’s password, required.</p></li><li><p>scope: the scope of access, optional.</p></li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/token</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>server.example.com</span><br><span class="line"><span class="attribute">Authorization</span><span class="punctuation">: </span>Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>application/x-www-form-urlencoded</span><br><span class="line"></span><br><span class="line"><span class="language-dts">grant_<span class="attr">type</span><span class="operator">=</span>password<span class="variable">&amp;username</span>=johndoe<span class="variable">&amp;password</span>=A3ddj3w</span></span><br></pre></td></tr></table></figure><p>In step C, the authorization server sends the access token to the client. Here is an example.</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">HTTP/1.1</span> <span class="number">200</span> OK</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>application/json;charset=UTF-8</span><br><span class="line"><span class="attribute">Cache-Control</span><span class="punctuation">: </span>no-store</span><br><span class="line"><span class="attribute">Pragma</span><span class="punctuation">: </span>no-cache</span><br><span class="line"></span><br><span class="line"><span class="language-json"><span class="punctuation">&#123;</span></span></span><br><span class="line"><span class="language-json">  <span class="attr">&quot;access_token&quot;</span><span class="punctuation">:</span><span class="string">&quot;2YotnFZFEjr1zCsicMWpAA&quot;</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">  <span class="attr">&quot;token_type&quot;</span><span class="punctuation">:</span><span class="string">&quot;example&quot;</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">  <span class="attr">&quot;expires_in&quot;</span><span class="punctuation">:</span><span class="number">3600</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">  <span class="attr">&quot;refresh_token&quot;</span><span class="punctuation">:</span><span class="string">&quot;tGzv3JOkF0XG5Qx2TlKWIA&quot;</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">  <span class="attr">&quot;example_parameter&quot;</span><span class="punctuation">:</span><span class="string">&quot;example_value&quot;</span></span></span><br><span class="line"><span class="language-json"><span class="punctuation">&#125;</span></span></span><br></pre></td></tr></table></figure><p>For the meaning of each parameter in the code above, see the “Authorization Code Mode” section.</p><p>Throughout the entire process, the client must not save the user’s password.</p><h3 id="5-4-Client-Credentials-Mode"><a href="#5-4-Client-Credentials-Mode" class="headerlink" title="5.4 Client Credentials Mode"></a>5.4 Client Credentials Mode</h3><p>In Client Credentials mode, the client authenticates with the “service provider” on its own behalf, rather than on behalf of the user. Strictly speaking, Client Credentials mode does not fall under the problem the OAuth framework is designed to solve. In this mode, the user registers directly with the client, and the client requests services from the “service provider” in its own name – there is really no authorization problem to speak of.<br><img src="/en/en/2018-05-17/Oauth2/Oauth2/1526541188587-46b33537-c148-4620-ae68-4c527f671f8f-image-resized.png"></p><blockquote><p><strong>A. The client authenticates itself with the authorization server and requests an access token.</strong><br><strong>B. The authorization server verifies everything and, once it checks out, provides the client with an access token.</strong></p></blockquote><p>In step A, the HTTP request sent by the client contains the following parameters:</p><ul><li><p>granttype: indicates the authorization type; the value here is fixed as “clientcredentials”, required.</p></li><li><p>scope: the scope of access, optional.</p></li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/token</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>server.example.com</span><br><span class="line"><span class="attribute">Authorization</span><span class="punctuation">: </span>Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>application/x-www-form-urlencoded</span><br><span class="line"></span><br><span class="line"><span class="language-ini"><span class="attr">grant_type</span>=client_credentials</span></span><br></pre></td></tr></table></figure><p>The authorization server must verify the client’s identity in some way.</p><p>In step B, the authorization server sends the access token to the client. Here is an example.</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">HTTP/1.1</span> <span class="number">200</span> OK</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>application/json;charset=UTF-8</span><br><span class="line"><span class="attribute">Cache-Control</span><span class="punctuation">: </span>no-store</span><br><span class="line"><span class="attribute">Pragma</span><span class="punctuation">: </span>no-cache</span><br><span class="line"></span><br><span class="line"><span class="language-json"><span class="punctuation">&#123;</span></span></span><br><span class="line"><span class="language-json">  <span class="attr">&quot;access_token&quot;</span><span class="punctuation">:</span><span class="string">&quot;2YotnFZFEjr1zCsicMWpAA&quot;</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">  <span class="attr">&quot;token_type&quot;</span><span class="punctuation">:</span><span class="string">&quot;example&quot;</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">  <span class="attr">&quot;expires_in&quot;</span><span class="punctuation">:</span><span class="number">3600</span><span class="punctuation">,</span></span></span><br><span class="line"><span class="language-json">  <span class="attr">&quot;example_parameter&quot;</span><span class="punctuation">:</span><span class="string">&quot;example_value&quot;</span></span></span><br><span class="line"><span class="language-json"><span class="punctuation">&#125;</span></span></span><br></pre></td></tr></table></figure><p>For the meaning of each parameter in the code above, see the “Authorization Code Mode” section.</p><h3 id="5-5-Refreshing-the-Token"><a href="#5-5-Refreshing-the-Token" class="headerlink" title="5.5 Refreshing the Token"></a>5.5 Refreshing the Token</h3><p>If, when the user tries to access the service, the client’s “access token” has already expired, the client must use a “refresh token” to request a new access token.</p><p>The HTTP request the client sends to refresh the token contains the following parameters:</p><ul><li><p>granttype: indicates the authorization mode being used; the value here is fixed as “refreshtoken”, required.</p></li><li><p>refresh_token: the refresh token received earlier, required.</p></li><li><p>scope: the scope of access being requested; it must not exceed the scope of the previous request. If this parameter is omitted, the scope is treated as identical to the previous one.</p></li></ul><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/token</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>server.example.com</span><br><span class="line"><span class="attribute">Authorization</span><span class="punctuation">: </span>Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>application/x-www-form-urlencoded</span><br><span class="line"></span><br><span class="line"><span class="language-angelscript">grant_type=<span class="built_in">ref</span>resh_token&amp;<span class="built_in">ref</span>resh_token=tGzv3JOkF0XG5Qx2TlKWIA</span></span><br></pre></td></tr></table></figure><hr><blockquote><p>Source: <a href="http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html">http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html</a></p></blockquote>]]>
    </content>
    <id>https://www.robbs.win/en/2018-05-17/Oauth2.html</id>
    <link href="https://www.robbs.win/en/2018-05-17/Oauth2.html"/>
    <published>2018-05-17T07:20:46.000Z</published>
    <summary>This article provides a clear and accessible explanation of the design rationale and operating flow of OAuth 2.0.</summary>
    <title>Understanding OAuth 2.0</title>
    <updated>2026-07-01T02:15:11.712Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Architect" scheme="https://www.robbs.win/en/categories/Architect/"/>
    <category term="Architect" scheme="https://www.robbs.win/en/tags/Architect/"/>
    <category term="Humanity" scheme="https://www.robbs.win/en/tags/Humanity/"/>
    <content>
      <![CDATA[<p>Recently I ran an internal architecture exchange&#x2F;training session at my company, covering the concept of architecture, its forms, and the principles of architectural design.<br><a href="http://www.rowkey.me/arch-ppt/index.html">View the slides</a></p><h2 id="What-Is-Architecture"><a href="#What-Is-Architecture" class="headerlink" title="What Is Architecture"></a>What Is Architecture</h2><ul><li>Personnel allocation and project planning within a department</li><li>Floor planning and functional facility planning in building design</li><li>Road layout, functional building design, and entertainment facility design in a city</li><li>Urban planning, expressway planning, and high-speed rail route planning for a country<blockquote><p><strong>All is architecture!!!</strong></p></blockquote></li></ul><h2 id="The-Essence-of-Architecture"><a href="#The-Essence-of-Architecture" class="headerlink" title="The Essence of Architecture"></a>The Essence of Architecture</h2><ul><li><strong>Core lifecycle</strong>: Sub-lifecycles whose subject remains unchanged after splitting.</li><li><strong>Non-core lifecycle</strong>: Sub-lifecycles whose subject changes after splitting.<br><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526437772448-7783bdeb-4306-4847-85db-3754065cdea8-image.png"><blockquote><p><strong>The essence of architecture lies in continuously splitting lifecycles (in a tree structure) so that business can run in parallel spatially. Each lifecycle that is split off has its own boundary and does not affect other lifecycles; all changes are settled within its own lifecycle. This is what high cohesion means.</strong></p></blockquote></li></ul><h2 id="Software-Architecture"><a href="#Software-Architecture" class="headerlink" title="Software Architecture"></a>Software Architecture</h2><blockquote><p><strong>Software lifecycle: software development lifecycle + software runtime lifecycle (software access, software features, software monitoring)</strong></p></blockquote><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526437909260-9eff08f6-b8c8-436d-81ff-2ebc3f639464-image.png"></p><h2 id="Three-Elements-of-Good-Software-Architecture"><a href="#Three-Elements-of-Good-Software-Architecture" class="headerlink" title="Three Elements of Good Software Architecture"></a>Three Elements of Good Software Architecture</h2><ul><li><strong>Firmness</strong>: Achieve a satisfactory level of freedom from damaging failure.</li><li><strong>Commodity</strong>: Utility to accomplish the tasks it is purported to be for.</li><li><strong>Delight</strong>: Pleasure in use.<blockquote><p><strong>Buildings (Solid, Useful, Beautiful) -&gt; Software (Firmness, Commodity, Delight)</strong></p></blockquote></li></ul><h2 id="Overview-of-the-Architecture-Process"><a href="#Overview-of-the-Architecture-Process" class="headerlink" title="Overview of the Architecture Process"></a>Overview of the Architecture Process</h2><ol><li><strong>Business architecture</strong>: A top-down view of the architecture, including business rules, business modules, and business processes. It mainly decomposes the business of the whole system, designs the domain models, and transforms real-world business into abstract objects.</li><li><strong>Technical architecture</strong>: A cross-sectional view of the architecture, an abstraction from hardware to application, including abstraction layers and programming interfaces. Technical architecture and business architecture are complementary; every part of the business architecture has its own technical architecture. These two parts must be done well first.</li><li><strong>Data architecture</strong>: The storage architecture, mainly referring to the design of data structures. It determines the characteristics of the application’s data sources and is the foundation of both business architecture and technical architecture.</li><li><strong>Deployment architecture</strong>: The topology architecture, including how many nodes the system is deployed on, the relationships between nodes, high availability of servers, fault tolerance, network interfaces and protocols, etc. It determines how the application runs, its runtime performance, maintainability, and scalability, and is the foundation of all architectures.</li><li><strong>Organizational architecture</strong>: The team architecture, including the organizational form of the project, personnel composition, and responsibilities. It is the supporting facility for all the architectures above. A good organizational architecture ensures the effective implementation and advancement of the other architectures.<blockquote><p><strong>As business and load change, the architecture needs to be continuously reviewed and refactored to drive its evolution.</strong></p></blockquote></li></ol><h2 id="Business-Architecture"><a href="#Business-Architecture" class="headerlink" title="Business Architecture"></a>Business Architecture</h2><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526438152354-6a181918-13db-4d45-855f-88257de3f429-image.png" alt="Top-down view"></p><ul><li>Business execution is the core module of the application and its primary function.</li><li>Data analysis is an auxiliary module of the application, helping with data-driven R&amp;D, business intelligence research, and improving user experience.</li><li>System management is the foundational part of the application. Doing a good job of deployment, monitoring of various metrics, and backup of critical data helps the application iterate and deploy quickly and run stably.</li></ul><h2 id="Technical-Architecture-Overview"><a href="#Technical-Architecture-Overview" class="headerlink" title="Technical Architecture - Overview"></a>Technical Architecture - Overview</h2><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526438216252-267d0e5e-63b3-44dc-b988-c9eb1758a10f-image.png" alt="Cross-sectional view"></p><ul><li>Business data sources, data rule engines, and analysis rules support the presentation of the interactive UI.</li><li>Infrastructure and common services build up the underlying business logic.</li></ul><h2 id="Technical-Architecture-Specifics"><a href="#Technical-Architecture-Specifics" class="headerlink" title="Technical Architecture - Specifics"></a>Technical Architecture - Specifics</h2><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526438239613-1cdabcae-9196-4aad-869d-94285ae76d41-image.png"></p><ul><li>Both UI and applications belong to the application layer, which provides the concrete business implementation. Among them, UI is the primary form of expression for the view layer.</li><li>Applications and services belong to the control logic and access channels, where services hold the main business logic. Services can expose service interfaces for applications to call.</li><li>Core, drivers, and data make up the data layer. This is the operation logic for all data related to the business and corresponds to the model layer. Among them, the core can expose interfaces outward for services to call.</li></ul><h2 id="Evolution-of-Architecture"><a href="#Evolution-of-Architecture" class="headerlink" title="Evolution of Architecture"></a>Evolution of Architecture</h2><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526438275615-26fee321-9534-4a04-b51e-7806434b66e7-image.png"></p><ul><li>In a monolithic application architecture, all logic resides within the application and core modules, including business logic, data operations, and so on.</li><li>Through a data-driven approach, data operation interfaces are unified to shield the differences between data sources, allowing vertical partitioning based on resources.</li><li>Modules are decoupled and expose APIs outward as services, forming a distributed service architecture.</li></ul><h2 id="Deployment-Architecture"><a href="#Deployment-Architecture" class="headerlink" title="Deployment Architecture"></a>Deployment Architecture</h2><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526438314473-c5da5a10-c62d-49a0-9209-5e43e54b88ca-image-resized.png"></p><ul><li>The interactive UI is deployed independently, including forms such as web and app.</li><li>The application is deployed independently as a single node or a cluster.</li><li>Services, the core, and drivers are deployed as a whole.</li><li>Data sources are deployed independently.</li><li>A simple application&#x2F;service cluster: LVS (using keepalived for active&#x2F;standby) + Nginx (reverse proxy) + Tomcat (business container).</li></ul><h2 id="Data-Architecture"><a href="#Data-Architecture" class="headerlink" title="Data Architecture"></a>Data Architecture</h2><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526438343067-0e351afe-72f7-4a7f-a6be-72fab9cd54c8-image.png"></p><ul><li>The data interaction logic and data flow presented by the interactive UI determine the main data design of the business.</li><li>The original business data, logs, and the data needed for statistics support the report output required by data analysis.</li><li>Real-time information, under certain rules and state machine engines, can power dashboard features such as real-time status monitoring.</li></ul><h2 id="Five-Attributes-of-Data"><a href="#Five-Attributes-of-Data" class="headerlink" title="Five Attributes of Data"></a>Five Attributes of Data</h2><ul><li><strong>Access frequency</strong>: Read&#x2F;write frequency. Read-only and frequently accessed data can be redundantly stored in multiple copies.</li><li><strong>Consistency requirement</strong>: Data with high consistency requirements must be strictly guaranteed to be accurate.</li><li><strong>Access permission</strong>: In API design, data of different granularities is exposed according to different permissions. PO -&gt; VO is a description of the same entity under different permissions.</li><li><strong>Data importance</strong>: Cannot be lost &#x2F; partial loss allowed &#x2F; only a cache &#x2F; no need to persist.</li><li><strong>Data confidentiality</strong>: Plaintext internally allowed &#x2F; plaintext internally not allowed &#x2F; can be made public.</li></ul><h2 id="Data-Design"><a href="#Data-Design" class="headerlink" title="Data Design"></a>Data Design</h2><ul><li>Fully understand the interactive UI so you know which data the UI is associated with and which data can be cached.</li><li>Fully understand the business so you know clearly which data needs to be recorded and the relationships between data.</li><li>Database design should pay attention to storage efficiency:<ul><li>Reduce transactions</li><li>Reduce JOIN queries</li><li>Use indexes appropriately</li><li>Consider using caching</li><li>…</li></ul></li><li>In data statistics scenarios, data statistics with high real-time requirements can use Redis; non-real-time data can use separate tables, with data updated through asynchronous queue computation or scheduled calculations. In addition, for statistical data with high consistency requirements, transactions or scheduled reconciliation mechanisms are needed to ensure accuracy.</li></ul><h2 id="What-Is-an-Architect"><a href="#What-Is-an-Architect" class="headerlink" title="What Is an Architect"></a>What Is an Architect</h2><ul><li>Lifecycle identification: rationally split lifecycles.</li><li>Identify problems and the subject of the problem. Never mistake a solution for a problem. Discovering a problem is always more important than solving it!!!</li><li>Focus on business and technology, and ensure business growth.</li><li>OKR architecture: be responsible for breakthroughs in key technologies, resolve technical feasibility issues, and deliver the key results that take things from 0 to 1.</li><li><strong>Authority must match responsibility, to ensure the architecture is executed!!!</strong></li></ul><h2 id="Essential-Qualities-of-an-Architect"><a href="#Essential-Qualities-of-an-Architect" class="headerlink" title="Essential Qualities of an Architect"></a>Essential Qualities of an Architect</h2><ul><li>Stand high, see far, dig deep.</li><li>Master a certain technology so you can draw analogies at a fundamental level and quickly grasp other technologies.</li><li>Treat all technologies equally: there is only suitable or unsuitable, never liked or disliked.</li><li>Have a broad vision, understand the pros and cons of different technologies, know which open-source project can directly satisfy this or that requirement, and be able to judge whether you need to reinvent the wheel.</li><li>Master design patterns, but do not overuse them.</li><li>Split the system into multiple subsystems or modules, keeping modules as loosely coupled as possible, so that development tasks that previously could only run serially can proceed in parallel, and the timeline can be shortened by investing more people.</li><li>Clearly know where the system’s bottlenecks are, continuously locate bottlenecks in technical difficulty, R&amp;D progress, performance, memory, and other aspects, constantly assign key personnel to resolve bottlenecks, and eliminate hidden risks before they explode.</li><li>Be able to anticipate how requirements may change and make forward-looking designs accordingly.</li></ul><h2 id="Six-Step-Architecture-Thinking-Method"><a href="#Six-Step-Architecture-Thinking-Method" class="headerlink" title="Six-Step Architecture Thinking Method"></a>Six-Step Architecture Thinking Method</h2><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526438498119-90bbb34f-ef90-43cb-939b-aa0f696262ce-image.png" alt="Six-Step Architecture Thinking Method"></p><h2 id="Architecture-Principles-Overview"><a href="#Architecture-Principles-Overview" class="headerlink" title="Architecture Principles - Overview"></a>Architecture Principles - Overview</h2><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526438513492-00ba90a2-24fb-460d-aa4c-d30bed417433-image.png"></p><h2 id="Architecture-Principles"><a href="#Architecture-Principles" class="headerlink" title="Architecture Principles"></a>Architecture Principles</h2><ul><li><strong>Avoid over-engineering</strong>: The simplest solution is the easiest to implement and maintain and also avoids wasting resources. But the solution should include provisions for extension.</li><li><strong>Redundancy design</strong>: Provide node redundancy for services and databases to ensure high availability. This is achieved through database master-slave replication and application clusters.</li><li><strong>Active-active data centers</strong>: For disaster recovery and to fundamentally guarantee high availability of the application. Multiple active data centers must be built so that a failure of one data center due to uncontrollable factors does not render the entire system unavailable.</li><li><strong>Stateless design</strong>: APIs, interfaces, and so on must not have front-back dependencies; one resource is not affected by changes to another. A stateless system can scale better. If state is unavoidable, either the client manages it or the server manages it with a distributed cache.</li><li><strong>Rollback capability</strong>: Any business, especially critical business, must have a recovery mechanism. This can be implemented with log-based WAL or event-based Event Sourcing, etc.</li><li><strong>Disabling &#x2F; self-protection</strong>: Provide rate-limiting mechanisms so that when upstream traffic exceeds the system’s load capacity, overflowing requests can be rejected. This can be done via manual switches or automatic switches (monitoring abnormal traffic behavior) to block traffic at the front of the application.</li><li><strong>Traceability</strong>: When a problem occurs in the system, you can locate the trajectory of the request and the request information at each step. Distributed tracing systems solve this kind of problem.</li><li><strong>Monitorability</strong>: Being monitorable is key to keeping the system running stably. This includes monitoring of business logic, application processes, and system resources the application depends on (CPU, disk, etc.). Every system needs to monitor well at these levels.</li><li><strong>Fault isolation</strong>: Isolating the resources (threads, CPU) and services that a system depends on ensures that a failure of one service does not affect calls to other services. Fault isolation can be achieved through thread pools or by deploying nodes separately.</li><li><strong>Mature and controllable technology selection</strong>: Use mainstream, mature technologies with good documentation and ample support resources. Choose the appropriate technology rather than the hottest one to implement the system.</li><li><strong>Tiered storage</strong>: Memory -&gt; SSD disk -&gt; traditional hard disk -&gt; magnetic tape. Data can be stored in tiers according to its importance and lifecycle.</li><li><strong>Caching design</strong>: Isolating requests from backend logic and storage is a mechanism based on the locality principle. This includes client-side caching (pre-distributing resources), Nginx caching, local caching, and distributed caching.</li><li><strong>Asynchronous design</strong>: For interfaces where the caller does not care about the result or allows a delayed result, responding asynchronously via a queue can significantly improve system performance; when calling other services, not waiting for the service to return before returning directly also improves response performance. Asynchronous queues are also a common means of solving distributed transactions.</li><li><strong>Forward-looking design</strong>: Based on industry experience and judgment, design extensibility and backward compatibility in advance.</li><li><strong>Horizontal scaling</strong>: Compared to vertical scaling, being able to solve problems by adding more machines is the top priority, and the system’s load capacity can scale close to infinitely. In addition, automatically adjusting capacity based on system load via <code>cloud computing</code> technology can save costs while ensuring service availability.</li><li><strong>Build and release in small steps</strong>: Iterate the project quickly, and fail fast. Avoid project plans with overly long spans of time.</li><li><strong>Automation</strong>: Automating packaging and testing is called continuous integration; automating deployment is called continuous deployment. Automation is the fundamental guarantee for rapid iteration and trial-and-error.</li></ul><h2 id="Architecture-Principle-Scalability"><a href="#Architecture-Principle-Scalability" class="headerlink" title="Architecture Principle - Scalability"></a>Architecture Principle - Scalability</h2><p><img src="/en/en/2018-05-16/Talk-About-The-Architecture/Talk-About-The-Architecture/1526438712437-0ddc81c3-0e3f-437a-8b2f-721e5a730e49-image.png" alt="Three-axis scaling theory"></p><ul><li>X-axis: horizontal duplication or cloning, goal-oriented. Examples include database read-write separation, table replication, replication, etc. Monolithic applications or dependent services are made redundant, and load balancing improves the system’s load capacity.</li><li>Y-axis: function&#x2F;service-oriented, such as vertical applications and distributed services. This splits a monolithic application into smaller applications or services based on function.</li><li>Z-axis: resource-oriented, such as horizontal database sharding. Resources are partitioned to spread the load across different nodes.<blockquote><ul><li><strong>Avoid relying on the database’s computational features (functions, stored procedures, triggers, etc.). Put the load on the business application side, which is easier to scale.</strong></li><li><strong>Scalability plan principle: Design for 20x, implement for 3x, deploy for 1.5x (DID).</strong></li></ul></blockquote></li></ul><h2 id="Five-Techniques-for-Improving-System-Response-Performance"><a href="#Five-Techniques-for-Improving-System-Response-Performance" class="headerlink" title="Five Techniques for Improving System Response Performance"></a>Five Techniques for Improving System Response Performance</h2><ul><li><strong>Asynchrony</strong>: Queue buffering, asynchronous requests.</li><li><strong>Concurrency</strong>: Use multiple CPUs and threads to execute business logic.</li><li><strong>Locality principle</strong>: Caching, tiered storage.</li><li><strong>Reduce IO</strong>: Merge fine-grained interfaces into coarse-grained ones; for frequent overwrite operations, only perform the last one. One thing to pay special attention to here: <strong>avoid calling external services inside loops in your code. A better practice is to use a coarse-grained batch interface outside the loop and make a single request.</strong></li><li><strong>Partitioning</strong>: Keep the size of frequently accessed data sets within a reasonable range.</li></ul><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li>“Scalability Rules” by Martin L. Abbott &#x2F; Michael T. Fisher (Chinese edition edited by Chen Bin)</li><li>“Liao Liao Jia Gou (On Architecture)” by Wang Gaikai</li><li>“An Architect’s First Lesson” by Cai Xueyong</li><li>Six-Step Architecture Thinking Method by Xia Huaxia @ Meituan</li></ul><hr><blockquote><p>Source: <a href="http://www.rowkey.me/arch-ppt/index.html">http://www.rowkey.me/arch-ppt/index.html</a></p></blockquote>]]>
    </content>
    <id>https://www.robbs.win/en/2018-05-16/Talk-About-The-Architecture.html</id>
    <link href="https://www.robbs.win/en/2018-05-16/Talk-About-The-Architecture.html"/>
    <published>2018-05-16T03:26:29.000Z</published>
    <summary>A systematic introduction to architecture</summary>
    <title>On Architecture</title>
    <updated>2026-07-01T02:18:33.570Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Architect" scheme="https://www.robbs.win/en/categories/Architect/"/>
    <category term="Container" scheme="https://www.robbs.win/en/tags/Container/"/>
    <category term="Architect" scheme="https://www.robbs.win/en/tags/Architect/"/>
    <content>
      <![CDATA[<p>The widespread adoption of containers and container orchestration (Kubernetes) makes it easy to build microservice-based “cloud-native” applications. Containers have become the new unit of programming in the cloud era, analogous to the <code>object</code> in object-oriented concepts, the <code>component</code> in J2EE, or the <code>function</code> in functional programming.</p><p>In the object-oriented era, there were many well-known design principles, patterns, and anti-patterns, for example:</p><ul><li><p>[SOLID][SOLID] (<strong>Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion</strong>)</p></li><li><p><a href="https://zh.wikipedia.org/wiki/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E5%8F%AF%E5%A4%8D%E7%94%A8%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%BD%AF%E4%BB%B6%E7%9A%84%E5%9F%BA%E7%A1%80">Design Patterns: Elements of Reusable Object-Oriented Software</a></p></li><li><p><a href="https://zh.wikipedia.org/wiki/%E5%8F%8D%E9%9D%A2%E6%A8%A1%E5%BC%8F">Anti-Pattern</a></p></li></ul><p>In the new container context, the corresponding principles and patterns help us build better “cloud-native” applications. As we will see, these principles and patterns are not a wholesale rejection of previous patterns; rather, they are evolutionary versions adapted to the new environment.</p><h2 id="Principles"><a href="#Principles" class="headerlink" title="Principles"></a>Principles</h2><h4 id="Single-Concern-Principle-SCP"><a href="#Single-Concern-Principle-SCP" class="headerlink" title="Single Concern Principle (SCP)"></a>Single Concern Principle (SCP)</h4><p>Corresponding to the single responsibility of OO, each container should provide a single concern and focus on doing one thing well. A single concern makes a container easier to reuse. Typically a container corresponds to one process, and that process focuses on doing one thing well.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526261691982-7ead3978-fe73-423f-a675-90309a8bc859-image.png" alt="SINGLE CONCERN PRINCIPLE (SCP)"> </p><h4 id="High-Observability-Principle-HOP"><a href="#High-Observability-Principle-HOP" class="headerlink" title="High Observability Principle (HOP)"></a>High Observability Principle (HOP)</h4><p>Containers, like objects, should be well-encapsulated black boxes. However, in a cloud environment, this black box should expose good observability interfaces so that it can be monitored and managed appropriately in the cloud. In this way, the entire application can provide consistent lifecycle management.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526261707323-33e04e90-0449-4fea-8846-49a7a3cf6915-image-resized.png" alt="HIGH OBSERVABILITY PRINCIPLE (HOP)"> </p><p>Observability includes:</p><ul><li>Providing health checks, or heartbeats</li><li>Providing status</li><li>Outputting logs to standard output (STDOUT) and standard error (STDERR)</li><li>etc.</li></ul><h4 id="Life-Cycle-Conformance-Principle-LCP"><a href="#Life-Cycle-Conformance-Principle-LCP" class="headerlink" title="Life-Cycle Conformance Principle (LCP)"></a>Life-Cycle Conformance Principle (LCP)</h4><p>The life-cycle conformance principle means that containers should interact with the platform to handle the corresponding lifecycle changes.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526261801128-be721a67-971b-4f35-960e-0ce907328f73-image-resized.png" alt="LIFE-CYCLE CONFORMANCE PRINCIPLE (LCP)"> </p><ul><li>Capture and respond to the Terminate (SIGTERM) signal to gracefully terminate the service process as quickly as possible, in order to avoid being forcibly killed by the kill (SIGKILL) signal. For example, the following NodeJS code.</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">process.<span class="title function_">on</span>(<span class="string">&#x27;SIGTERM&#x27;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;Received SIGTERM. Exiting.&quot;</span>)</span><br><span class="line">  server.<span class="title function_">close</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    process.<span class="title function_">exit</span>(<span class="number">0</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><ul><li>Return an exit code</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">process.<span class="title function_">exit</span>(<span class="number">0</span>);</span><br></pre></td></tr></table></figure><h4 id="Image-Immutability-Principle-IIP"><a href="#Image-Immutability-Principle-IIP" class="headerlink" title="Image Immutability Principle (IIP)"></a>Image Immutability Principle (IIP)</h4><p>At runtime, configurations may differ, but the image should be immutable.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526261901806-402b717e-5379-476a-ab90-d419ff516bc8-image.png" alt="IMAGE IMMUTABILITY PRINCIPLE (IIP)"><br>We can think of an image as a class and a container as an object instance: the class is immutable, while the container is an instance of the image with different configuration parameters.</p><h4 id="Process-Disposability-Principle-PDP"><a href="#Process-Disposability-Principle-PDP" class="headerlink" title="Process Disposability Principle (PDP)"></a>Process Disposability Principle (PDP)</h4><p>In a cloud environment, we should assume that all containers are ephemeral and may be replaced by other container instances at any time.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526261921352-50cf453f-a1eb-4d16-bde3-c47f7ecb5c96-image.png" alt="PROCESS DISPOSABILITY PRINCIPLE (PDP)"><br>This means that a container’s state should be stored outside the container, and containers should start and stop as quickly as possible. Generally, the smaller the container, the easier this is to achieve.</p><h4 id="Self-Containment-Principle-S-CP"><a href="#Self-Containment-Principle-S-CP" class="headerlink" title="Self-Containment Principle (S-CP)"></a>Self-Containment Principle (S-CP)</h4><p>A container should include all of its dependencies at build time; in other words, a container should not have any external dependencies at runtime.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526261951920-41f3d5f6-38ef-4619-91a1-1f802c90696a-image.png" alt="SELF-CONTAINMENT PRINCIPLE (S-CP)"> </p><h4 id="Runtime-Confinement-Principle-RCP"><a href="#Runtime-Confinement-Principle-RCP" class="headerlink" title="Runtime Confinement Principle (RCP)"></a>Runtime Confinement Principle (RCP)</h4><p>A best practice for containers is to declare the resource configuration requirements at runtime, such as how much memory, CPU, and so on. Doing so enables the container orchestrator to schedule and manage resources more effectively.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526261967663-aaa59614-51c8-43cc-bde6-237e644fbbb2-image.png" alt="RUNTIME CONFINEMENT PRINCIPLE (RCP)"> </p><h2 id="Patterns"><a href="#Patterns" class="headerlink" title="Patterns"></a>Patterns</h2><p>Many container application patterns relate to the concept of a Pod. A Pod is a concept introduced by Kubernetes to manage containers effectively; it is a collection of containers, which we can think of as a “super-container” (a term I just made up). Containers within a Pod behave as if they are running on the same machine: they share the localhost address, can communicate locally, share volumes, and so on.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526261996216-83455d4d-0512-4dbb-877e-1cd6e97e76ed-image.png"> </p><p>Kubernetes is like an OS in the cloud, providing best practices for building cloud-native applications with containers. Let’s look at some of the common patterns.</p><h4 id="Sidecar"><a href="#Sidecar" class="headerlink" title="Sidecar"></a>Sidecar</h4><p><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262020618-7b150d6f-b6a3-4791-8212-38551258c423-image.png"><br>The Sidecar is the most common pattern. Within the same Pod, we separate different responsibilities into different containers that together provide a complete feature to the outside world.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262054188-bc378617-e76c-4498-9c0b-82f7704e6496-image-resized.png"> </p><p>There are many such examples, for instance:</p><ul><li>The Node backend and the Redis cache in the figure above</li><li>A web server and a log-collection service</li><li>A web server and a service responsible for collecting server performance metrics</li></ul><p>This is somewhat analogous to the object-oriented <a href="https://en.wikipedia.org/wiki/Composite_pattern">Composite pattern</a>, and there are many benefits:<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262095283-87098624-d8fc-4286-a2af-ac94f4b0f47b-image.png" alt="Composite Pattern Implementation - UML Class Diagram"> </p><ul><li>Apply the Single Concern Principle: each container focuses on doing one thing well.</li><li>Isolation: containers do not compete with each other for resources. When a secondary function (such as log collection or caching) fails or crashes, the impact on the primary function is minimized.</li><li>Each container can be managed independently throughout its lifecycle.</li><li>Each container can scale elastically and independently.</li><li>Any one container can be easily replaced.</li></ul><h4 id="Ambassador-Proxy-Container"><a href="#Ambassador-Proxy-Container" class="headerlink" title="Ambassador (Proxy) Container"></a>Ambassador (Proxy) Container</h4><p><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262238219-a6221168-99d3-4f03-bfc6-ca2fad38c325-image.png" alt="Proxy Pattern Implementation - UML Class Diagram"><br>Similar to the object-oriented <a href="https://en.wikipedia.org/wiki/Proxy_pattern">Proxy pattern</a>, this uses a container within the Pod to provide the external access connection. As shown in the figure below, the Node backend always communicates with the outside world through the Service Discovery container.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262264669-62a724a4-88d8-4511-9120-328cd7a4af15-image-resized.png"> </p><p>In this way, the developers of the Node module only need to assume that all communication comes from the local machine, while the complexity of communication is delegated to the ambassador container, which handles concerns such as load balancing, security, request filtering, and, when necessary, terminating communication.</p><h4 id="Adapter-Container"><a href="#Adapter-Container" class="headerlink" title="Adapter Container"></a>Adapter Container</h4><p><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262303219-9324cca1-515f-4424-b9b8-c19b8814b9c3-image.png" alt="Adapter  Pattern Implementation - UML Class Diagram"> </p><p>People often confuse the object-oriented Proxy pattern, Bridge pattern, and <a href="https://en.wikipedia.org/wiki/Adapter_pattern">Adapter pattern</a>, because the UML diagrams look largely the same. It seems like they are just different names for the same thing. This is indeed the case—just as almost all OO patterns are derivatives of the Composite pattern, all container patterns are derivatives of the Sidecar pattern.</p><p>In the example below, if the name “Logging Adapter” did not mention “Adapter,” we would not consider it an adapter pattern.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262348948-fce2d10f-320e-4d52-89eb-931bf394454c-image-resized.png"> </p><p>In fact, the adapter pattern is concerned with how to uniformly expose the functionality of different containers inside the Pod through an adapter. In the figure above, it becomes clearer if we add one more container that also writes logs to the volume. The Logging Adapter adapts the logs produced by different containers through different interfaces and provides a unified access interface.</p><h4 id="Container-Chain"><a href="#Container-Chain" class="headerlink" title="Container Chain"></a>Container Chain</h4><p><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262383289-ee67fbb1-8251-4af3-bb61-f28db3e9bbc0-image.png" alt="hain of Responsability Implementation - UML Class Diagram"> </p><p>Similar to the OO <a href="http://www.oodesign.com/chain-of-responsibility-pattern.html">Chain of Responsibility pattern</a>, chaining together containers responsible for different functions in dependency order is also a common pattern.<br><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262452948-23ea67c9-470f-4f7b-8a03-c76307b4415f-image-resized.png"> </p><h4 id="Ready-Pod"><a href="#Ready-Pod" class="headerlink" title="Ready Pod"></a>Ready Pod</h4><p><img src="/en/en/2018-05-14/Container-Application-Design/Container-Application-Design/1526262469915-5b17a04f-67ce-46de-967f-8d8ef906ac68-image.png"> </p><p>Typically, a container running as a service has a startup process during which the service is unavailable. Kubernetes provides a <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/">Readiness</a> probe feature.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">readinessProbe:</span></span><br><span class="line">  <span class="attr">httpGet:</span></span><br><span class="line">    <span class="attr">path:</span> <span class="string">/</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">5000</span></span><br><span class="line">  <span class="attr">timeoutSeconds:</span> <span class="number">1</span></span><br><span class="line">  <span class="attr">periodSeconds:</span> <span class="number">5</span></span><br></pre></td></tr></table></figure><p>Compared with the other patterns, this is more of a Kubernetes best practice.</p><h2 id="Anti-Patterns"><a href="#Anti-Patterns" class="headerlink" title="Anti-Patterns"></a>Anti-Patterns</h2><h4 id="Mixing-the-Build-Environment-with-the-Runtime-Environment"><a href="#Mixing-the-Build-Environment-with-the-Runtime-Environment" class="headerlink" title="Mixing the Build Environment with the Runtime Environment"></a>Mixing the Build Environment with the Runtime Environment</h4><p>The image used for the production runtime environment should be as small as possible, and should avoid including leftovers from the build time.</p><p>Consider the following Dockerfile example:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">FROM ubuntu:14.04</span><br><span class="line"></span><br><span class="line">RUN apt-get update</span><br><span class="line">RUN apt-get install gcc</span><br><span class="line">RUN gcc hello.c -o /hello</span><br></pre></td></tr></table></figure><p>The resulting image contains many things that are neither needed nor appropriate for a production environment, such as gcc and the source code hello.c. This is both insecure (directly exposing the source code) and incurs a performance overhead (an oversized image leads to slower loading).</p><p>The <a href="https://docs.docker.com/develop/develop-images/multistage-build/">multi-stage builds</a> introduced in Docker 17.05 can solve this problem.</p><h4 id="Using-Pods-Directly"><a href="#Using-Pods-Directly" class="headerlink" title="Using Pods Directly"></a>Using Pods Directly</h4><p>Avoid using Pods directly; manage Pods with a Deployment. Using a Deployment makes it easy to scale and manage Pods.</p><h4 id="Using-the-latest-Tag"><a href="#Using-the-latest-Tag" class="headerlink" title="Using the latest Tag"></a>Using the latest Tag</h4><p>The latest tag is meant to mark the most recent stable version. However, when creating containers, you should avoid using the latest tag in production environments as much as possible, even when the imagePullPolicy option is set to Always.</p><h4 id="Fast-Failing-Jobs"><a href="#Fast-Failing-Jobs" class="headerlink" title="Fast-Failing Jobs"></a>Fast-Failing Jobs</h4><p>A Job is a Kubernetes container that runs only once, the opposite of a service. Avoid fast failure.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">batch/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Job</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">bad</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="attr">template:</span></span><br><span class="line">    <span class="attr">metadata:</span></span><br><span class="line">      <span class="attr">name:</span> <span class="string">bad</span></span><br><span class="line">    <span class="attr">spec:</span></span><br><span class="line">      <span class="attr">restartPolicy:</span> <span class="string">Never</span></span><br><span class="line">      <span class="attr">containers:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">box</span></span><br><span class="line">        <span class="attr">image:</span> <span class="string">busybox</span></span><br><span class="line">        <span class="attr">command:</span> [<span class="string">&quot;/bin/sh&quot;</span>, <span class="string">&quot;-c&quot;</span>, <span class="string">&quot;exit 1&quot;</span>]</span><br></pre></td></tr></table></figure><p>If you try to create the above Job in your cluster, you might encounter the following status.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">$ kubectl describe <span class="built_in">jobs</span> </span><br><span class="line">Name:   bad</span><br><span class="line">Namespace:  default</span><br><span class="line">Image(s): busybox</span><br><span class="line">Selector: controller-uid=18a6678e-11d1-11e7-8169-525400c83acf</span><br><span class="line">Parallelism:  1</span><br><span class="line">Completions:  1</span><br><span class="line">Start Time: Sat, 25 Mar 2017 20:05:41 -0700</span><br><span class="line">Labels:   controller-uid=18a6678e-11d1-11e7-8169-525400c83acf</span><br><span class="line">    job-name=bad</span><br><span class="line">Pods Statuses:  1 Running / 0 Succeeded / 24 Failed</span><br><span class="line">No volumes.</span><br><span class="line">Events:</span><br><span class="line">  FirstSeen LastSeen  Count From      SubObjectPath Type    Reason      Message</span><br><span class="line">  --------- --------  ----- ----      ------------- --------  ------      -------</span><br><span class="line">  1m    1m    1 &#123;job-controller &#125;     Normal    SuccessfulCreate  Created pod: bad-fws8g</span><br><span class="line">  1m    1m    1 &#123;job-controller &#125;     Normal    SuccessfulCreate  Created pod: bad-321pk</span><br><span class="line">  1m    1m    1 &#123;job-controller &#125;     Normal    SuccessfulCreate  Created pod: bad-2pxq1</span><br><span class="line">  1m    1m    1 &#123;job-controller &#125;     Normal    SuccessfulCreate  Created pod: bad-kl2tj</span><br><span class="line">  1m    1m    1 &#123;job-controller &#125;     Normal    SuccessfulCreate  Created pod: bad-wfw8q</span><br><span class="line">  1m    1m    1 &#123;job-controller &#125;     Normal    SuccessfulCreate  Created pod: bad-lz0hq</span><br><span class="line">  1m    1m    1 &#123;job-controller &#125;     Normal    SuccessfulCreate  Created pod: bad-0dck0</span><br><span class="line">  1m    1m    1 &#123;job-controller &#125;     Normal    SuccessfulCreate  Created pod: bad-0lm8k</span><br><span class="line">  1m    1m    1 &#123;job-controller &#125;     Normal    SuccessfulCreate  Created pod: bad-q6ctf</span><br><span class="line">  1m    1s    16  &#123;job-controller &#125;     Normal    SuccessfulCreate  (events with common reason combined)</span><br></pre></td></tr></table></figure><p>Because the Job fails fast, Kubernetes considers the Job to have failed to start successfully and tries to create new containers to recover from this failure, causing the cluster to create a large number of containers in a short period of time, which can consume a significant amount of computing resources.</p><p>Use <code>.spec.activeDeadlineSeconds</code> in the Spec to avoid this problem. This parameter defines how long to wait before retrying a failed Job.</p><hr><blockquote><p>Source: <a href="https://my.oschina.net/taogang/blog/1809904">https://my.oschina.net/taogang/blog/1809904</a></p></blockquote><p>[SOLID]: <a href="https://zh.wikipedia.org/wiki/SOLID_(%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%AE%BE%E8%AE%A1)">https://zh.wikipedia.org/wiki/SOLID_(%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%AE%BE%E8%AE%A1)</a>“SOLID”</p>]]>
    </content>
    <id>https://www.robbs.win/en/2018-05-14/Container-Application-Design.html</id>
    <link href="https://www.robbs.win/en/2018-05-14/Container-Application-Design.html"/>
    <published>2018-05-14T02:24:02.000Z</published>
    <summary>In the new container context, corresponding principles and patterns help us better build &quot;cloud-native&quot; applications. As we will see, these principles and patterns are not a wholesale overturning of previous patterns; rather, they are evolutionary versions adapted to a new environment.</summary>
    <title>Principles, Patterns, and Anti-Patterns of Container-Based Application Design</title>
    <updated>2026-07-01T02:18:03.394Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Architect" scheme="https://www.robbs.win/en/categories/Architect/"/>
    <category term="Architect" scheme="https://www.robbs.win/en/tags/Architect/"/>
    <category term="Humanity" scheme="https://www.robbs.win/en/tags/Humanity/"/>
    <content>
      <![CDATA[<h2 id="Broad-Knowledge"><a href="#Broad-Knowledge" class="headerlink" title="Broad Knowledge"></a>Broad Knowledge</h2><p>In my view, architects have always been a talent pool for future CTOs, because the range of skills required is so broad.</p><blockquote><p><strong>Doing architecture, in its simplest understanding, boils down to one sentence: figuring out how to solve problems under various constraints.</strong></p></blockquote><p>Those constraints are factors like <code>performance</code>, <code>stability</code>, <code>development efficiency</code>, and <code>maintainability</code>.</p><ul><li><p>An application like Baidu Tieba might see hundreds of millions of visits and tens of millions — or even hundreds of millions — of writes per day. Performance clearly has to come first, and you may have to trade some development efficiency for performance gains.</p></li><li><p>In a banking scenario, user experience and access latency matter less, but data security and consistency are paramount. In that case, security and stability come first, with performance considered later.</p></li></ul><p>Making tradeoffs under constraints means making a lot of choices. And to make choices, you first have to know what the options are.</p><p>For performance and security to mean anything beyond the words themselves, you have to know, in concrete technical terms, what solutions are actually available.</p><ul><li><p>For example, the Java ecosystem, the PHP ecosystem, the C ecosystem, and Python&#x2F;Node.js&#x2F;Golang each have their own strengths and weaknesses; you need hands-on development experience with them to make the right choice. Hearsay gives you no standing to decide.</p></li><li><p>Although MySQL is by far the most widely used database today, Oracle and PostgreSQL have their own advantages.</p></li><li><p>Almost no modern project avoids big data altogether, so you need at least some understanding of big-data algorithms and some hands-on experience with big-data platforms.</p></li><li><p>Running an entire project involves a great many other concerns: how source code is managed, how releases are deployed, how testing keeps the bug rate down, system monitoring, server provisioning, canary releases, and so on.</p></li></ul><p>Don’t listen to people who make “architect” sound lofty and rarefied, as if it were purely a designer’s job — system design, software design, and so on. That’s just second-hand romanticizing.</p><p><strong>No internet company will hire you purely to do design, because without exception every internet company’s leadership needs the implementation of features and the realization of strategic and tactical ideas.</strong></p><p><strong>Every large-scale system architecture is iterated step by step in response to the problems it faces. There is no scenario in which someone gets to design a brilliant system all in one go (even with a head start of a while), because the world changes too fast. If the project isn’t implemented quickly, it may die tomorrow — who has the leisure to design for years into the future?</strong></p><p><strong>So the single most critical thing for an architect is the ability to steer the entire project and keep it running efficiently.</strong></p><h2 id="Excellent-Coding-Ability"><a href="#Excellent-Coding-Ability" class="headerlink" title="Excellent Coding Ability"></a>Excellent Coding Ability</h2><p>If you want to become an architect, you first have to be an excellent programmer. So what makes an excellent programmer?</p><p>Just writing code without thinking or learning obviously won’t do.</p><p>Concretely, you have to deeply master all kinds of data structures, design patterns, computer networking, operating systems, and common architectural patterns. These get mentioned constantly, but I suspect very few people truly understand them in depth.</p><p>I include myself: when I first studied design patterns, I felt within a month or so that I understood all of them. But every time I revisited the topic, or came across exceptionally well-written code, and went back to review this material, I always found something new and reached a deeper level of understanding.</p><p>And understanding is only the beginning. The real key is fully integrating these ideas into your own code.</p><p>Writing code is itself shot through with the feel of architectural design. In fact, I’d argue that when a programmer writes code it’s called “coding,” but when an architect writes code it’s called “architectural design.”</p><p>That’s because the two consider problems from completely different angles. A programmer mainly thinks about how to implement a feature, while an excellent programmer also thinks about things like <code>performance</code>, <code>readability</code>, and <code>maintainability</code>.</p><p>For an architect, those concerns are mandatory, and the dimensions considered are usually even broader.</p><p>So don’t imagine you can skip “excellent programmer” and jump straight to “architect” in one step. It’s unrealistic.</p><h2 id="Depth-in-Certain-Relevant-Domains"><a href="#Depth-in-Certain-Relevant-Domains" class="headerlink" title="Depth in Certain Relevant Domains"></a>Depth in Certain Relevant Domains</h2><p>I just talked about the breadth of technology. But if you know a little about everything yet excel at nothing — if you’re not genuinely expert in anything — then you’ll remain just a programmer.</p><p>So which domains count as key domains?</p><p>From here onward, architects essentially diverge according to their business direction.</p><p>For example, an architect in the financial domain may need financial knowledge.</p><p>In the big-data domain, deep knowledge of Hadoop, Spark, Hive, and similar technologies may be required.</p><p>In the high-concurrency domain, deeper expertise in system-wide performance optimization and distributed-system design may be needed.</p><h2 id="Technical-Insight"><a href="#Technical-Insight" class="headerlink" title="Technical Insight"></a>Technical Insight</h2><p>“Technical insight” is a term I’m borrowing from the book <em>How Google Works</em> (originally 《重新定义公司》). A more intuitive phrasing would be “technical foresight and vision.”</p><p>A few examples, with the benefit of hindsight:</p><ol><li><p>If JD.com had not chosen the Windows platform back then, it might have developed far better than it has today.</p></li><li><p>If it weren’t for the short-sightedness of a certain Baidu leader, who was always a few beats behind the market, Baidu wouldn’t now be left far behind by AT.</p></li><li><p>……</p></li></ol><p>This shouldn’t feel abstract. If you’re currently an architect at a startup, a seemingly correct decision you make today could directly cause major losses for the company in the future — or even its collapse.</p><h2 id="Management-Ability"><a href="#Management-Ability" class="headerlink" title="Management Ability"></a>Management Ability</h2><p>Few architects lead neither projects nor people, so management ability is certainly a must.</p><p>But management is a huge topic in its own right, so I won’t expand on it here.</p><hr><p>Looking back, what I’ve written is all fairly directional.</p><p>But given the particular nature of the architect role, it’s genuinely hard to lay out a concrete path such that, if you just follow it, you’ll become an excellent architect.</p><p>There are, however, some principled approaches.</p><p>As an architect, so-called design ability is not really the key. There are few opportunities to design a project entirely from scratch, and in most cases you can simply combine other people’s design solutions, weighed against the situation at hand.</p><p>That’s why, once you’ve read enough architect-related posts, you realize that so-called distributed architectures and large-site architectures are basically the same handful of patterns over and over. As a result, anyone can come out and mouth a few sentences about “architecture.”</p><p>So what is the key? It’s the ability to steer a project, and to solve concrete problems as they arise.</p><p>And developing project-steering ability and concrete problem-solving ability sometimes requires more than just the projects at your company.</p><p>In company projects you’re usually only one member of a team, doing one specific piece of work — frontend JS, an app, backend business logic, etc. You generally can’t see how the project as a whole runs. Even if you’re lucky and the project lead has the whole thing under control and is willing to explain it all to you, you still haven’t built those parts with your own hands, and listening to someone describe them is unlikely to give you a deep understanding.</p><p><strong>So what should you do? Carve out time to build an entirely new project of your own — for example a BBS site, or an app with a back end. The site or app itself doesn’t matter; what matters is that you develop it yourself, get the deployment system working, get the monitoring system working, get the release system working, get the testing system working, and so on.</strong></p><p><strong>Treat your own project exactly like an official product at a real company.</strong></p><p>After a few rounds of this, you’ll find your sense of the entire project dramatically sharpened. You’ll no longer be fuzzy about everything except the part you personally own.</p><hr><blockquote><p>Source: <a href="https://zhuanlan.zhihu.com/p/27979747">https://zhuanlan.zhihu.com/p/27979747</a></p></blockquote>]]>
    </content>
    <id>https://www.robbs.win/en/2018-05-10/How-to-become-an-excellent-architect.html</id>
    <link href="https://www.robbs.win/en/2018-05-10/How-to-become-an-excellent-architect.html"/>
    <published>2018-05-10T01:46:38.000Z</published>
    <summary>What capabilities does it take to become an architect, and how should you work toward becoming an excellent one?</summary>
    <title>How to Become an Excellent Architect</title>
    <updated>2026-07-01T02:28:29.292Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Server" scheme="https://www.robbs.win/en/categories/Server/"/>
    <category term="Aliyun" scheme="https://www.robbs.win/en/tags/Aliyun/"/>
    <category term="IPv6" scheme="https://www.robbs.win/en/tags/IPv6/"/>
    <content>
      <![CDATA[<h2 id="Enabling-the-limit"><a href="#Enabling-the-limit" class="headerlink" title="Enabling the limit"></a>Enabling the limit</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/sysctl.conf</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Add the configuration</span></span><br><span class="line">net.ipv6.conf.all.disable_ipv6 = 0</span><br><span class="line">net.ipv6.conf.default.disable_ipv6 = 0</span><br><span class="line">net.ipv6.conf.lo.disable_ipv6 = 0</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="Verify-that-it-took-effect"><a href="#Verify-that-it-took-effect" class="headerlink" title="Verify that it took effect"></a>Verify that it took effect</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">ifconfig</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Output</span></span><br><span class="line">eth1: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500</span><br><span class="line">        inet 114.55.127.xxx  netmask 255.255.252.0  broadcast 114.55.119.255</span><br><span class="line">        inet6 fe80::219:3eff:fexx:32ec  prefixlen 64  scopeid 0x20&lt;link&gt;</span><br><span class="line">        ether 00:16:3e:01:49:37  txqueuelen 1000  (Ethernet)</span><br><span class="line">        RX packets 68348  bytes 41664492 (39.7 MiB)</span><br><span class="line">        RX errors 0  dropped 0  overruns 0  frame 0</span><br><span class="line">        TX packets 18859  bytes 3632548 (3.4 MiB)</span><br><span class="line">        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>fe80::219:3eff:fexx:32ec</code> is the IPv6 address.</p>]]>
    </content>
    <id>https://www.robbs.win/en/2017-05-01/Aliyun-Open-IPv6.html</id>
    <link href="https://www.robbs.win/en/2017-05-01/Aliyun-Open-IPv6.html"/>
    <published>2017-05-01T15:36:43.000Z</published>
    <summary>Enable IPv6 support on Alibaba Cloud</summary>
    <title>Enabling IPv6 Support on Alibaba Cloud</title>
    <updated>2026-07-01T02:18:33.674Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Server" scheme="https://www.robbs.win/en/categories/Server/"/>
    <category term="Ubuntu" scheme="https://www.robbs.win/en/tags/Ubuntu/"/>
    <category term="MongoDB" scheme="https://www.robbs.win/en/tags/MongoDB/"/>
    <content>
      <![CDATA[<h2 id="Add-the-Software-Source"><a href="#Add-the-Software-Source" class="headerlink" title="Add the Software Source"></a>Add the Software Source</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">sudo</span> apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">echo &quot;deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu &quot;$(lsb_release -sc)&quot;/mongodb-org/3.4 multiverse&quot; | sudo tee /etc/apt/sources.list.d/mongodb-3.4.list</span><br></pre></td></tr></table></figure><h2 id="Install-MongoDB"><a href="#Install-MongoDB" class="headerlink" title="Install MongoDB"></a>Install MongoDB</h2><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">apt-get update</span><br><span class="line">apt-get install mongodb-org -y</span><br></pre></td></tr></table></figure><h2 id="Start-the-Service"><a href="#Start-the-Service" class="headerlink" title="Start the Service"></a>Start the Service</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">service mongod start</span><br><span class="line">service mongod stop</span><br></pre></td></tr></table></figure><h2 id="Create-a-Username-and-Password"><a href="#Create-a-Username-and-Password" class="headerlink" title="Create a Username and Password"></a>Create a Username and Password</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Edit the configuration file</span></span><br><span class="line">vim /ect/mongod.conf</span><br><span class="line"></span><br><span class="line">security:  </span><br><span class="line">authorization: &quot;enabled&quot;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Restart the service</span></span><br><span class="line">service mongod stop</span><br><span class="line">service mongod start</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Enter the database</span></span><br><span class="line">mongo</span><br><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">use admin</span></span><br><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">db.createUser(&#123;user:<span class="string">&quot;user_test&quot;</span>,<span class="built_in">pwd</span>:<span class="string">&quot;pwd_test&quot;</span>,roles:[<span class="string">&quot;root&quot;</span>]&#125;) <span class="comment"># Create the account</span></span>  </span><br><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">db.auth(<span class="string">&quot;user_test&quot;</span>,<span class="string">&quot;pwd_test&quot;</span>) <span class="comment"># You can now log in</span></span></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.robbs.win/en/2017-04-25/Install-MongoDB-on-ubuntu16-04.html</id>
    <link href="https://www.robbs.win/en/2017-04-25/Install-MongoDB-on-ubuntu16-04.html"/>
    <published>2017-04-25T07:03:36.000Z</published>
    <summary>How to install MongoDB on Ubuntu 16.04 and add username/password access control.</summary>
    <title>Install MongoDB on Ubuntu 16.04</title>
    <updated>2026-07-01T02:19:06.735Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Server" scheme="https://www.robbs.win/en/categories/Server/"/>
    <category term="Server" scheme="https://www.robbs.win/en/tags/Server/"/>
    <category term="AirFlow" scheme="https://www.robbs.win/en/tags/AirFlow/"/>
    <category term="Supervisord" scheme="https://www.robbs.win/en/tags/Supervisord/"/>
    <content>
      <![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>AirFlow is an open-source workflow scheduler written in Python that ships with a rich UI.</p><h2 id="Installation"><a href="#Installation" class="headerlink" title="Installation"></a>Installation</h2><h3 id="Python"><a href="#Python" class="headerlink" title="Python"></a>Python</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">aptitude install python</span><br><span class="line">aptitude install python-dev</span><br><span class="line">aptirude install python-pip</span><br><span class="line">aptitude install libmysqlclient-dev</span><br></pre></td></tr></table></figure><h3 id="AirFlow"><a href="#AirFlow" class="headerlink" title="AirFlow"></a>AirFlow</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install airflow</span><br></pre></td></tr></table></figure><h3 id="Supervisor"><a href="#Supervisor" class="headerlink" title="Supervisor"></a>Supervisor</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">aptitude install supervisor</span><br></pre></td></tr></table></figure><h2 id="Configuration"><a href="#Configuration" class="headerlink" title="Configuration"></a>Configuration</h2><h3 id="AirFlow-1"><a href="#AirFlow-1" class="headerlink" title="AirFlow"></a>AirFlow</h3><h4 id="Initialization"><a href="#Initialization" class="headerlink" title="Initialization"></a>Initialization</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">airflow initdb</span><br></pre></td></tr></table></figure><h4 id="Adding-User-Login"><a href="#Adding-User-Login" class="headerlink" title="Adding User Login"></a>Adding User Login</h4><p>Install the corresponding module.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install &quot;airflow[password]&quot;</span><br></pre></td></tr></table></figure><p>Add the configuration.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">vim airflow.cfg</span><br><span class="line">## Under [webserver], add</span><br><span class="line">authenticate = True</span><br><span class="line">auth_backend = airflow.contrib.auth.backends.password_auth</span><br></pre></td></tr></table></figure><p>Switch into the airflow directory.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd ~/airflow</span><br><span class="line">python</span><br></pre></td></tr></table></figure><p>Run the Python commands.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">import airflow</span><br><span class="line">from airflow import models, settings</span><br><span class="line">from airflow.contrib.auth.backends.password_auth import PasswordUser</span><br><span class="line">user = PasswordUser(models.User())</span><br><span class="line">user.username = &#x27;user_name&#x27;</span><br><span class="line">user.email = &#x27;email@example.com&#x27;</span><br><span class="line">user.password = &#x27;password&#x27;</span><br><span class="line">session = settings.Session()</span><br><span class="line">session.add(user)</span><br><span class="line">session.commit()</span><br><span class="line">session.close()</span><br><span class="line">exit()</span><br></pre></td></tr></table></figure><h4 id="Supervisord"><a href="#Supervisord" class="headerlink" title="Supervisord"></a>Supervisord</h4><p>Add startup management for the webserver and scheduler.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/supervisor/conf.d/airflow.conf </span><br><span class="line"></span><br><span class="line">## Add</span><br><span class="line">[program:airflow_webserver]</span><br><span class="line">command=airflow webserver</span><br><span class="line">user=ubuntu</span><br><span class="line">stderr_logfile=/var/log/airflow/webserver.err.log</span><br><span class="line">stdout_logfile=/var/log/airflow/webserver.out.log</span><br><span class="line">[program:airflow_scheduler]</span><br><span class="line">command=airflow scheduler</span><br><span class="line">user=ubuntu</span><br><span class="line">stderr_logfile=/var/log/airflow/scheduler.err.log</span><br><span class="line">stdout_logfile=/var/log/airflow/scheduler.out.log</span><br></pre></td></tr></table></figure><h2 id="Issues"><a href="#Issues" class="headerlink" title="Issues"></a>Issues</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">ImportError: No module named pidlockfile</span><br><span class="line"></span><br><span class="line">## Solution</span><br><span class="line"></span><br><span class="line">aptitude remove python-lockfile</span><br><span class="line">pip install lockfile</span><br><span class="line">ImportError: cannot import name MySqlOperator</span><br><span class="line"></span><br><span class="line">## Solution</span><br><span class="line"></span><br><span class="line">pip install airflow[celery]</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.robbs.win/en/2017-01-19/Install-And-Deploy-AirFlow.html</id>
    <link href="https://www.robbs.win/en/2017-01-19/Install-And-Deploy-AirFlow.html"/>
    <published>2017-01-19T08:44:02.000Z</published>
    <summary>A brief introduction to installing and deploying AirFlow.</summary>
    <title>Installing and Deploying AirFlow</title>
    <updated>2026-07-01T02:19:06.672Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Software" scheme="https://www.robbs.win/en/categories/Software/"/>
    <category term="rbenv" scheme="https://www.robbs.win/en/tags/rbenv/"/>
    <category term="brew" scheme="https://www.robbs.win/en/tags/brew/"/>
    <category term="ruby" scheme="https://www.robbs.win/en/tags/ruby/"/>
    <content>
      <![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>There are currently two tools for managing Ruby versions on Mac OS: RVM and rbenv. This article mainly covers how to install and use rbenv, and how to manage your Ruby environment through it.</p><h2 id="Installation"><a href="#Installation" class="headerlink" title="Installation"></a>Installation</h2><h3 id="Installation-1"><a href="#Installation-1" class="headerlink" title="Installation"></a>Installation</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">brew install rbenv</span><br><span class="line">brew install ruby-build</span><br><span class="line">brew install rbenv-gemset</span><br><span class="line">brew install rbenv-gem-rehash</span><br></pre></td></tr></table></figure><h3 id="Initialization"><a href="#Initialization" class="headerlink" title="Initialization"></a>Initialization</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">echo &#x27;export PATH=&quot;$HOME/.rbenv/bin:$PATH&quot;&#x27; &gt;&gt; ~/.bashrc</span><br><span class="line">echo &#x27;eval &quot;$(rbenv init -)&quot;&#x27; &gt;&gt; ~/.bashrc</span><br><span class="line"></span><br><span class="line"># If you are using Zsh</span><br><span class="line">echo &#x27;export PATH=&quot;$HOME/.rbenv/bin:$PATH&quot;&#x27; &gt;&gt; ~/.zshrc</span><br><span class="line">echo &#x27;eval &quot;$(rbenv init -)&quot;&#x27; &gt;&gt; ~/.zshrc</span><br></pre></td></tr></table></figure><h3 id="Installing-Ruby"><a href="#Installing-Ruby" class="headerlink" title="Installing Ruby"></a>Installing Ruby</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">List available versions</span></span><br><span class="line">rbenv install --list</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Install 2.4.0</span></span><br><span class="line">rbenv install 2.4.0</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Show installed versions</span></span><br><span class="line">rbenv versions</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"> system</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">  2.1.5</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">  2.2.1</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">* 2.2.4 (<span class="built_in">set</span> by /Users/Robbs/.rbenv/version)</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Set the global version</span></span><br><span class="line">rbenv global 2.4.0</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Set the <span class="built_in">local</span> version</span></span><br><span class="line">rbenv local 2.4.0</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Unset the <span class="built_in">local</span> version</span></span><br><span class="line">rbenv local --unset</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Set the version <span class="keyword">for</span> the current shell</span></span><br><span class="line">rbenv shell 2.4.0</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Use the system Ruby</span></span><br><span class="line">rbenv global system</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">You must run this <span class="built_in">command</span> after switching Ruby versions and after running bundle install</span></span><br><span class="line">rbenv rehash</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Uninstall a Ruby version</span></span><br><span class="line">rbenv uninstall 2.4.0</span><br></pre></td></tr></table></figure><h3 id="Switching-the-mirror"><a href="#Switching-the-mirror" class="headerlink" title="Switching the mirror"></a>Switching the mirror</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">rbenv-china-mirror</span></span><br><span class="line">git clone https://github.com/andorchen/rbenv-china-mirror.git ~/.rbenv/plugins/rbenv-china-mirror</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.robbs.win/en/2016-12-26/Use-Brew-Install-Rbenv.html</id>
    <link href="https://www.robbs.win/en/2016-12-26/Use-Brew-Install-Rbenv.html"/>
    <published>2016-12-26T08:41:17.000Z</published>
    <summary>rbenv and rvm are used to manage the installation and usage of multiple versions of Ruby.</summary>
    <title>Installing rbenv on Mac OS</title>
    <updated>2026-07-01T02:22:57.631Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Software" scheme="https://www.robbs.win/en/categories/Software/"/>
    <category term="Software" scheme="https://www.robbs.win/en/tags/Software/"/>
    <category term="OSX" scheme="https://www.robbs.win/en/tags/OSX/"/>
    <content>
      <![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>This article introduces the essential software used on Mac OS.</p><h2 id="Software"><a href="#Software" class="headerlink" title="Software"></a>Software</h2><h3 id="Command-Line-Tool"><a href="#Command-Line-Tool" class="headerlink" title="Command Line Tool"></a>Command Line Tool</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">xcode-select --install</span><br></pre></td></tr></table></figure><h3 id="Homebrew"><a href="#Homebrew" class="headerlink" title="Homebrew"></a>Homebrew</h3><p><a href="http://brew.sh/,">Homebrew</a></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/usr/bin/ruby -e &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)&quot;</span><br></pre></td></tr></table></figure><h3 id="Oh-My-Zsh"><a href="#Oh-My-Zsh" class="headerlink" title="Oh My Zsh"></a>Oh My Zsh</h3><p><a href="https://github.com/robbyrussell/oh-my-zsh">Oh My Zsh</a></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">brew install zsh</span><br><span class="line"></span><br><span class="line">sh -c &quot;$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)&quot;</span><br></pre></td></tr></table></figure><h3 id="Prezto"><a href="#Prezto" class="headerlink" title="Prezto"></a>Prezto</h3><p><a href="https://github.com/sorin-ionescu/prezto">Prezto</a></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"># Launch Zsh:</span><br><span class="line">zsh</span><br><span class="line"></span><br><span class="line">#Clone the repository:</span><br><span class="line">git clone --recursive https://github.com/sorin-ionescu/prezto.git &quot;$&#123;ZDOTDIR:-$HOME&#125;/.zprezto&quot;</span><br><span class="line"></span><br><span class="line">#Create a new Zsh configuration by copying the Zsh configuration files provided:</span><br><span class="line">setopt EXTENDED_GLOB</span><br><span class="line">for rcfile in &quot;$&#123;ZDOTDIR:-$HOME&#125;&quot;/.zprezto/runcoms/^README.md(.N); do</span><br><span class="line">  ln -s &quot;$rcfile&quot; &quot;$&#123;ZDOTDIR:-$HOME&#125;/.$&#123;rcfile:t&#125;&quot;</span><br><span class="line">done</span><br><span class="line"></span><br><span class="line"># Set Zsh as your default shell:</span><br><span class="line">chsh -s /bin/zsh</span><br></pre></td></tr></table></figure><h3 id="Brew-Cask"><a href="#Brew-Cask" class="headerlink" title="Brew Cask"></a>Brew Cask</h3><p><a href="https://caskroom.github.io/">Homebrew Cask</a></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">brew tap phinze/cask</span><br><span class="line">brew install brew-cask</span><br></pre></td></tr></table></figure><h3 id="TotalTerminal"><a href="#TotalTerminal" class="headerlink" title="TotalTerminal"></a>TotalTerminal</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew cask install totalterminal</span><br></pre></td></tr></table></figure><h3 id="LaunchRocket"><a href="#LaunchRocket" class="headerlink" title="LaunchRocket"></a>LaunchRocket</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew cask install launchrocket</span><br></pre></td></tr></table></figure><h3 id="shadowssocks"><a href="#shadowssocks" class="headerlink" title="shadowssocks"></a>shadowssocks</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://github.com/shadowsocks/shadowsocks-iOS/releases</span><br></pre></td></tr></table></figure><h3 id="alfred-3"><a href="#alfred-3" class="headerlink" title="alfred 3"></a>alfred 3</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew cask install alfred</span><br></pre></td></tr></table></figure><h3 id="moom"><a href="#moom" class="headerlink" title="moom"></a>moom</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew cask install moom</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.robbs.win/en/2016-12-26/Mac-OSX-Basic-SoftWare.html</id>
    <link href="https://www.robbs.win/en/2016-12-26/Mac-OSX-Basic-SoftWare.html"/>
    <published>2016-12-26T08:39:02.000Z</published>
    <summary>An overview of essential software to use on Mac OS.</summary>
    <title>Mac OS Essential Software</title>
    <updated>2026-07-01T02:15:11.753Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Rails" scheme="https://www.robbs.win/en/categories/Rails/"/>
    <category term="Ruby" scheme="https://www.robbs.win/en/tags/Ruby/"/>
    <category term="Cache" scheme="https://www.robbs.win/en/tags/Cache/"/>
    <category term="Rails" scheme="https://www.robbs.win/en/tags/Rails/"/>
    <content>
      <![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>Reasonable use of caching can greatly improve a website’s performance, making it an indispensable part of performance optimization.</p><h2 id="Caching"><a href="#Caching" class="headerlink" title="Caching"></a>Caching</h2><ul><li><p>Model-layer caching</p><p>Through manual configuration, you can store selected query results in the corresponding cache system.</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Rails</span>.cache.fetch(<span class="string">&#x27;all_products&#x27;</span>, <span class="symbol">expires_in:</span> <span class="number">1</span>.days) <span class="keyword">do</span> </span><br><span class="line">  <span class="title class_">Product</span>.all.to_a</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p><strong>Make sure to verify that the returned result is the final result set.</strong></p></li></ul><ul><li><h4 id="Controller-layer-caching"><a href="#Controller-layer-caching" class="headerlink" title="Controller-layer caching"></a>Controller-layer caching</h4><ul><li><h5 id="Action-caching"><a href="#Action-caching" class="headerlink" title="Action caching"></a>Action caching</h5><p>Caches the Action response, implemented with the help of Fragment Cache and callbacks.</p><p>Various verification mechanisms can be added via <code>before_action</code>.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">before_action :authentication, only: :show</span><br><span class="line">cache_action :show, expires_in: 1.hour</span><br></pre></td></tr></table></figure><p><strong>This method was removed in Rails 4, but it can be re-enabled through a Gem.</strong></p></li><li><h5 id="Page-caching"><a href="#Page-caching" class="headerlink" title="Page caching"></a>Page caching</h5><p>Caches the page as a static page; it cannot perform authentication or similar checks.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">class ProductsController &lt; ActionController</span><br><span class="line">  caches_page :index</span><br><span class="line">end</span><br></pre></td></tr></table></figure><p><strong>This method was removed in Rails 4, but it can be re-enabled through a Gem.</strong></p></li></ul></li><li><h4 id="View-layer-caching"><a href="#View-layer-caching" class="headerlink" title="View-layer caching"></a>View-layer caching</h4><ul><li><h5 id="Fragment-Cache"><a href="#Fragment-Cache" class="headerlink" title="Fragment Cache"></a>Fragment Cache</h5><p>As a page becomes more complex, full-page caching is no longer feasible; the page must instead be split into different fragments.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">- cache(&#x27;xxx&#x27; , expires_in: 1.days ) do </span><br><span class="line">  %ul</span><br><span class="line">    = @product.name</span><br></pre></td></tr></table></figure><p><strong>Fragment caching can be nested to form a special pattern known as Russian Doll Caching.</strong></p></li></ul></li><li><h4 id="SQL-caching"><a href="#SQL-caching" class="headerlink" title="SQL caching"></a>SQL caching</h4><p>  This is a built-in feature of the Rails framework; it caches the result set of each query.</p>  <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">class ProductsController &lt; ActionController</span><br><span class="line">  def index</span><br><span class="line">    # First Query</span><br><span class="line">    @products = Product.all</span><br><span class="line">  </span><br><span class="line">    # Second Query (Cache)</span><br><span class="line">    @products = Product.all</span><br><span class="line">  end</span><br></pre></td></tr></table></figure><p>  When the second query runs, it reads the result set cached in memory by the first query directly.</p><p>  <strong>Tips: The cache is valid for the lifetime of the action.</strong></p></li></ul><h2 id="Configuration"><a href="#Configuration" class="headerlink" title="Configuration"></a>Configuration</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Enable caching</span></span><br><span class="line">config.action_controller.perform_caching = <span class="literal">true</span></span><br><span class="line"><span class="comment"># Cache store backend</span></span><br><span class="line">config.cache_store = :memory_store  <span class="comment"># memory_store mem_cache_store file_store</span></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://www.robbs.win/en/2016-11-18/Raiils-Cache-Simple.html</id>
    <link href="https://www.robbs.win/en/2016-11-18/Raiils-Cache-Simple.html"/>
    <published>2016-11-18T08:32:38.000Z</published>
    <summary>A brief introduction to the caching mechanisms commonly used in Rails.</summary>
    <title>Introduction to Rails Caching</title>
    <updated>2026-07-01T02:15:11.771Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Software" scheme="https://www.robbs.win/en/categories/Software/"/>
    <category term="DynamoDB" scheme="https://www.robbs.win/en/tags/DynamoDB/"/>
    <category term="Software" scheme="https://www.robbs.win/en/tags/Software/"/>
    <content>
      <![CDATA[<h2 id="Installation"><a href="#Installation" class="headerlink" title="Installation"></a>Installation</h2><ol><li><h4 id="Java-SDK"><a href="#Java-SDK" class="headerlink" title="Java SDK"></a>Java SDK</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">aptitude install openjdk-8-jdk</span><br></pre></td></tr></table></figure></li><li><h4 id="Install-the-extraction-tool"><a href="#Install-the-extraction-tool" class="headerlink" title="Install the extraction tool"></a>Install the extraction tool</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Install the extraction tool</span></span><br><span class="line">aptitude install unzip</span><br></pre></td></tr></table></figure></li><li><h4 id="Download-and-install-DynamoDB"><a href="#Download-and-install-DynamoDB" class="headerlink" title="Download and install DynamoDB"></a>Download and install DynamoDB</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Download the archive</span></span><br><span class="line">wget http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.zip</span><br><span class="line"></span><br><span class="line"><span class="comment"># Extract</span></span><br><span class="line">unzip ./dynamodb_local_latest.zip</span><br><span class="line"></span><br><span class="line"><span class="comment"># Start</span></span><br><span class="line">java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb</span><br><span class="line"></span><br><span class="line"><span class="comment"># View help</span></span><br><span class="line">java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -<span class="built_in">help</span></span><br></pre></td></tr></table></figure></li><li><h4 id="javascript-shell"><a href="#javascript-shell" class="headerlink" title="javascript shell"></a>javascript shell</h4><p><a href="http://127.0.0.1:8000/shell/">http://127.0.0.1:8000/shell/</a></p></li></ol>]]>
    </content>
    <id>https://www.robbs.win/en/2016-11-16/DynamoDB-Ubuntu.html</id>
    <link href="https://www.robbs.win/en/2016-11-16/DynamoDB-Ubuntu.html"/>
    <published>2016-11-16T02:28:46.000Z</published>
    <summary>A record of installing DynamoDB on Ubuntu</summary>
    <title>Installing and Using DynamoDB on Ubuntu</title>
    <updated>2026-07-01T02:27:08.736Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Server" scheme="https://www.robbs.win/en/categories/Server/"/>
    <category term="Server" scheme="https://www.robbs.win/en/tags/Server/"/>
    <category term="Shadowsocks" scheme="https://www.robbs.win/en/tags/Shadowsocks/"/>
    <content>
      <![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>As the domestic “wall” grows ever “taller”, mastering a convenient, fast, and affordable way to “leap over” it has become a necessity. Below we introduce <a href="https://github.com/shadowsocks/shadowsocks" title="Shadowsocks">Shadowsocks</a>, a lightweight, cross-platform, open-source tool that is very easy to install and configure.</p><h2 id="Installation"><a href="#Installation" class="headerlink" title="Installation"></a>Installation</h2><h3 id="Prerequisites"><a href="#Prerequisites" class="headerlink" title="Prerequisites"></a>Prerequisites</h3><ul><li>A VPS<br>  You can use the SFO region of <a href="https://www.digitalocean.com/">digitalocean</a></li><li>A Python environment</li></ul><h3 id="Server-Installation"><a href="#Server-Installation" class="headerlink" title="Server Installation"></a>Server Installation</h3><ol><li><h4 id="Install-via-pip"><a href="#Install-via-pip" class="headerlink" title="Install via pip"></a>Install via pip</h4><ul><li>Install pip<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apt-get install python-pip</span><br></pre></td></tr></table></figure></li><li>Install shadowsocks   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install shadowsocks</span><br></pre></td></tr></table></figure></li></ul></li></ol><h3 id="Configuration"><a href="#Configuration" class="headerlink" title="Configuration"></a>Configuration</h3>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/shadowsocks.json</span><br></pre></td></tr></table></figure><p>  Add the following content<br>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">&quot;server&quot;</span>: <span class="string">&quot;my_server_ip&quot;</span>,               <span class="comment"># server IP</span></span><br><span class="line">    <span class="string">&quot;server_port&quot;</span>: 8000,                    <span class="comment"># listening port</span></span><br><span class="line">    <span class="string">&quot;local_address&quot;</span>: <span class="string">&quot;x.x.x.x&quot;</span>,             <span class="comment"># server local address</span></span><br><span class="line">    <span class="string">&quot;local_port&quot;</span>: 1080,                     <span class="comment"># server local port</span></span><br><span class="line">    <span class="string">&quot;password&quot;</span>: <span class="string">&quot;mypassword&quot;</span>,               <span class="comment"># connection password</span></span><br><span class="line">    <span class="string">&quot;timeout&quot;</span>: 300,                         <span class="comment"># connection timeout</span></span><br><span class="line">    <span class="string">&quot;method&quot;</span>: <span class="string">&quot;rc4-md5&quot;</span></span><br><span class="line">    <span class="string">&quot;fast_open&quot;</span>: <span class="literal">true</span>                       <span class="comment"># whether to enable TCP_FASTOPEN (requires kernel support)</span></span><br><span class="line">    <span class="string">&quot;workers&quot;</span>: 5                            <span class="comment"># number of worker processes</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="System-Optimization"><a href="#System-Optimization" class="headerlink" title="System Optimization"></a>System Optimization</h3><p>  Confirm the kernel version is 3.7.1 or above<br>   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">uname</span> -r</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4.4.0-45-generic</span></span><br></pre></td></tr></table></figure></p><ol><li><h4 id="Maximum-number-of-file-descriptors"><a href="#Maximum-number-of-file-descriptors" class="headerlink" title="Maximum number of file descriptors"></a>Maximum number of file descriptors</h4><ul><li>Before each Shadowsocks launch<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">ulimit</span> -SHn 51200</span><br></pre></td></tr></table></figure></li><li>Take effect at system boot<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/security/limits.conf</span><br><span class="line"><span class="comment"># Add</span></span><br><span class="line">* soft nofile 51200</span><br><span class="line">* hard nofile 51200</span><br><span class="line"><span class="comment"># First column: user or group</span></span><br><span class="line"><span class="comment"># Second column: hard = hard limit, soft = soft limit. Generally soft is smaller than hard; exceeding soft triggers a warning, while hard is the ceiling</span></span><br><span class="line"><span class="comment"># Third column: nofile = number of open files</span></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">vim  /etc/pam.d/common-session</span><br><span class="line"><span class="comment"># Add the line</span></span><br><span class="line">session required pam_limits.so</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/profile</span><br><span class="line"><span class="comment"># Append at the end of the file</span></span><br><span class="line"><span class="built_in">ulimit</span> -SHn 51200</span><br></pre></td></tr></table></figure></li></ul></li><li><h4 id="Tune-kernel-parameters"><a href="#Tune-kernel-parameters" class="headerlink" title="Tune kernel parameters"></a>Tune kernel parameters</h4>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/sysctl.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># Add the configuration</span></span><br><span class="line"></span><br><span class="line">fs.file-max = 51200                            <span class="comment"># max open files</span></span><br><span class="line"></span><br><span class="line"> net.core.rmem_max = 67108864                  <span class="comment"># max read buffer</span></span><br><span class="line"> net.core.wmem_max = 67108864                  <span class="comment"># max write buffer</span></span><br><span class="line"> net.core.netdev_max_backlog = 250000          <span class="comment"># max processor input queue</span></span><br><span class="line"> net.core.somaxconn = 4096                     <span class="comment"># max backlog</span></span><br><span class="line"></span><br><span class="line"> net.ipv4.tcp_syncookies = 1                   <span class="comment"># resist SYN flood attacks</span></span><br><span class="line"> net.ipv4.tcp_tw_reuse = 1                     <span class="comment"># reuse timewait sockets when safe</span></span><br><span class="line"> net.ipv4.tcp_tw_recycle = 0                   <span class="comment"># turn off fast timewait sockets recycling</span></span><br><span class="line"> net.ipv4.tcp_fin_timeout = 30                 <span class="comment"># short FIN timeout</span></span><br><span class="line"> net.ipv4.tcp_keepalive_time = 1200            <span class="comment"># short keepalive time</span></span><br><span class="line"> net.ipv4.ip_local_port_range = 10000 65000    <span class="comment"># outbound port range</span></span><br><span class="line"> net.ipv4.tcp_max_syn_backlog = 8192           <span class="comment"># max SYN backlog</span></span><br><span class="line"> net.ipv4.tcp_max_tw_buckets = 5000            <span class="comment"># max timewait sockets held by system simultaneously</span></span><br><span class="line"> net.ipv4.tcp_rmem = 4096 87380 67108864       <span class="comment"># TCP receive buffer</span></span><br><span class="line"> net.ipv4.tcp_wmem = 4096 65536 67108864       <span class="comment"># TCP write buffer</span></span><br><span class="line"> net.ipv4.tcp_mtu_probing = 1                  <span class="comment"># turn on path MTU discovery</span></span><br><span class="line"></span><br><span class="line"> net.ipv4.tcp_fastopen = 3                     <span class="comment"># enable TCP_FASTOPEN</span></span><br><span class="line"></span><br><span class="line"> net.ipv4.tcp_congestion_control = hybla</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Apply the configuration</span></span><br><span class="line"> sysctl -p</span><br></pre></td></tr></table></figure><ul><li><p>TCP_FASTOPEN<br>The Linux kernel version on both the server and client sides must be newer than 3.7.1</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Check whether it is in effect</span></span><br><span class="line">sysctl net.ipv4.tcp_fastopen</span><br><span class="line"></span><br><span class="line"><span class="comment"># net.ipv4.tcp_fastopen = 3</span></span><br></pre></td></tr></table></figure></li><li><p>TCP congestion control algorithms<br>Linux ships with several TCP congestion control algorithms.</p><ol><li>reno is the most basic congestion control algorithm and the experimental prototype of the TCP protocol.</li><li>bic suits links with high RTT but extremely rare packet loss, such as the route between North America and Europe; it was the default algorithm for Linux kernels from 2.6.8 to 2.6.18.</li><li>cubic is a modified version of bic and covers a broader range of scenarios than bic; it is the default algorithm for Linux kernels after 2.6.19.</li><li>hybla suits networks with high latency and high packet loss rates, such as satellite links — and equally the route between China and the United States.</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># List algorithms supported by the system</span></span><br><span class="line">sysctl net.ipv4.tcp_available_congestion_control</span><br><span class="line"></span><br><span class="line"><span class="comment"># net.ipv4.tcp_available_congestion_control = hybla cubic reno</span></span><br></pre></td></tr></table></figure></li></ul></li></ol><h3 id="Launch"><a href="#Launch" class="headerlink" title="Launch"></a>Launch</h3><ul><li>Launch directly</li></ul> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssserver -p 8000 -k password -m rc4-md5 -d &#123;start | stop&#125;</span><br></pre></td></tr></table></figure><ul><li>Launch from a configuration file</li></ul> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssserver -c /etc/shadowsocks.json -d &#123;start | stop&#125;</span><br></pre></td></tr></table></figure><h2 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h2><p>With the digitalocean SFO2 region at 300+ ms latency, you can smoothly stream 1080P video on YouTube (Hunan Telecom).</p>]]>
    </content>
    <id>https://www.robbs.win/en/2016-11-08/Install-Shadowsocks-Server.html</id>
    <link href="https://www.robbs.win/en/2016-11-08/Install-Shadowsocks-Server.html"/>
    <published>2016-11-08T02:23:20.000Z</published>
    <summary>How to install and configure Shadowsocks on a server.</summary>
    <title>Installing the Shadowsocks Server</title>
    <updated>2026-07-01T02:17:54.225Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Develop" scheme="https://www.robbs.win/en/categories/Develop/"/>
    <category term="PHP" scheme="https://www.robbs.win/en/tags/PHP/"/>
    <category term="Yii2" scheme="https://www.robbs.win/en/tags/Yii2/"/>
    <category term="Mina" scheme="https://www.robbs.win/en/tags/Mina/"/>
    <category term="Deploy" scheme="https://www.robbs.win/en/tags/Deploy/"/>
    <content>
      <![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><h3 id="Mina"><a href="#Mina" class="headerlink" title="Mina"></a>Mina</h3><p>  Really fast deployer and server automation tool.<br>  Mina is a fast deployment tool. Its deployment scripts are simple, it is highly extensible, deployment is fast (only a single SSH connection), and the deployment output is concise.</p><h2 id="Deployment"><a href="#Deployment" class="headerlink" title="Deployment"></a>Deployment</h2><h3 id="Prerequisites"><a href="#Prerequisites" class="headerlink" title="Prerequisites"></a>Prerequisites</h3><ul><li>A <a href="/2016-10-25/Create-Yii2-Project.html" title="Creating a Yii2 Project">Yii2</a> project repository</li><li>A VPS with a <a href="/2016-09-30/Install-Nginx-PHP7-MySQL-on-Ubuntu16-04.html">basic environment</a> installed</li><li>A development machine with Ruby installed</li></ul><h3 id="Install-Mina"><a href="#Install-Mina" class="headerlink" title="Install Mina"></a>Install Mina</h3>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gem install mina</span><br></pre></td></tr></table></figure><h3 id="Initialize-the-configuration"><a href="#Initialize-the-configuration" class="headerlink" title="Initialize the configuration"></a>Initialize the configuration</h3>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">mina init</span><br><span class="line"></span><br><span class="line"><span class="comment"># Generated config file</span></span><br><span class="line"><span class="comment"># ├── config</span></span><br><span class="line"><span class="comment"># │   └── deploy.rb</span></span><br></pre></td></tr></table></figure><h3 id="Edit-the-configuration-file"><a href="#Edit-the-configuration-file" class="headerlink" title="Edit the configuration file"></a>Edit the configuration file</h3>   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim deploy.rb</span><br></pre></td></tr></table></figure><p>   My configuration:<br>   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line">require <span class="string">&#x27;mina/git&#x27;</span>  <span class="comment"># git support</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Basic settings:</span></span><br><span class="line"><span class="comment">#   domain       - The hostname to SSH to.</span></span><br><span class="line"><span class="comment">#   deploy_to    - Path to deploy into.</span></span><br><span class="line"><span class="comment">#   repository   - Git repo to clone from. (needed by mina/git)</span></span><br><span class="line"><span class="comment">#   branch       - Branch name to deploy. (needed by mina/git)</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> :deploy_to, <span class="string">&#x27;/var/www/html/test&#x27;</span>             <span class="comment"># Directory on the VPS used for deployment</span></span><br><span class="line"><span class="built_in">set</span> :repository, <span class="string">&#x27;git@github.com:xxx/test.git&#x27;</span>   <span class="comment"># GitHub repository URL</span></span><br><span class="line"><span class="built_in">set</span> :branch, <span class="string">&#x27;develop&#x27;</span>                           <span class="comment"># Branch used for deployment</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> :keep_releases, 4              <span class="comment"># Number of releases to keep</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Manually create these paths in shared/ (eg: shared/config/database.yml) in your server.</span></span><br><span class="line"><span class="comment"># They will be linked in the &#x27;deploy:link_shared_paths&#x27; step.</span></span><br><span class="line"><span class="built_in">set</span> :shared_paths, [<span class="string">&#x27;vendor&#x27;</span>, <span class="string">&#x27;runtime&#x27;</span>, <span class="string">&#x27;web/assets&#x27;</span>]    <span class="comment"># Shared directories</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Optional settings:</span></span><br><span class="line"><span class="comment">#   set :user, &#x27;foobar&#x27;    # Username in the server to SSH to.</span></span><br><span class="line"><span class="comment">#   set :port, &#x27;30000&#x27;     # SSH port number.</span></span><br><span class="line"><span class="comment">#   set :forward_agent, true     # SSH forward_agent.</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> :user, <span class="string">&#x27;ubuntu&#x27;</span>                              <span class="comment"># Username for logging into the VPS</span></span><br><span class="line"><span class="built_in">set</span> :domain, <span class="string">&#x27;x.x.x.x&#x27;</span>                           <span class="comment"># IP address of the VPS</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># This task is the environment that is loaded for most commands, such as</span></span><br><span class="line"><span class="comment"># `mina deploy` or `mina rake`.</span></span><br><span class="line">task :environment <span class="keyword">do</span></span><br><span class="line">end</span><br><span class="line"></span><br><span class="line"><span class="comment"># Put any custom mkdir&#x27;s in here for when `mina setup` is ran.</span></span><br><span class="line"><span class="comment"># For Rails apps, we&#x27;ll make some of the shared paths that are shared between</span></span><br><span class="line"><span class="comment"># all releases.</span></span><br><span class="line">task :setup =&gt; :environment <span class="keyword">do</span></span><br><span class="line">  <span class="comment"># Project initialization: create shared folders and install Yii2 Composer support</span></span><br><span class="line">  queue! %[<span class="built_in">mkdir</span> -p <span class="string">&quot;#&#123;deploy_to&#125;/#&#123;shared_path&#125;/runtime&quot;</span>]</span><br><span class="line">  queue! %[<span class="built_in">mkdir</span> -p <span class="string">&quot;#&#123;deploy_to&#125;/#&#123;shared_path&#125;/vendor&quot;</span>]</span><br><span class="line">  queue! %[<span class="built_in">mkdir</span> -p <span class="string">&quot;#&#123;deploy_to&#125;/#&#123;shared_path&#125;/web/assets&quot;</span>]</span><br><span class="line">  queue! %[<span class="built_in">chmod</span> -R 777 <span class="string">&quot;#&#123;deploy_to&#125;/#&#123;shared_path&#125;/runtime&quot;</span>]</span><br><span class="line">  queue! %[<span class="built_in">chmod</span> -R 777 <span class="string">&quot;#&#123;deploy_to&#125;/#&#123;shared_path&#125;/web/assets&quot;</span>]</span><br><span class="line">  queue <span class="string">&#x27;composer global require &quot;fxp/composer-asset-plugin:^1.2.0&quot;&#x27;</span></span><br><span class="line">end</span><br><span class="line"></span><br><span class="line">desc <span class="string">&quot;Deploys the current version to the server.&quot;</span></span><br><span class="line">task :deploy =&gt; :environment <span class="keyword">do</span></span><br><span class="line">  to :before_hook <span class="keyword">do</span></span><br><span class="line">    <span class="comment"># Put things to run locally before ssh</span></span><br><span class="line">  end</span><br><span class="line">  deploy <span class="keyword">do</span></span><br><span class="line">    <span class="comment"># Put things that will set up an empty directory into a fully set-up</span></span><br><span class="line">    <span class="comment"># instance of your project.</span></span><br><span class="line">    invoke :<span class="string">&#x27;git:clone&#x27;</span>                          <span class="comment"># Update code</span></span><br><span class="line">    invoke :<span class="string">&#x27;deploy:link_shared_paths&#x27;</span>           <span class="comment"># Link shared directories</span></span><br><span class="line">    queue <span class="string">&#x27;chmod -R 755 yii&#x27;</span>                     <span class="comment"># Permissions </span></span><br><span class="line">    queue <span class="string">&#x27;composer install&#x27;</span>                     <span class="comment"># Install Composer packages</span></span><br><span class="line">    queue <span class="string">&#x27;./yii migrate&#x27;</span>                        <span class="comment"># Database migration</span></span><br><span class="line">    quequ <span class="string">&#x27;rm -rf runtime/cache/*&#x27;</span>               <span class="comment"># Clear the cache</span></span><br><span class="line">    quequ <span class="string">&#x27;service nginx restart&#x27;</span>                <span class="comment"># Restart Nginx</span></span><br><span class="line">    quequ <span class="string">&#x27;service php7.0-fpm restart&#x27;</span>           <span class="comment"># Restart PHP</span></span><br><span class="line">    invoke :<span class="string">&#x27;deploy:cleanup&#x27;</span>                     <span class="comment"># Clean up redundant releases</span></span><br><span class="line">  end</span><br><span class="line">end</span><br><span class="line"></span><br><span class="line"><span class="comment"># Used to roll back to the previous version</span></span><br><span class="line">desc <span class="string">&quot;Rollback to previous verison.&quot;</span></span><br><span class="line">task :rollback =&gt; :environment <span class="keyword">do</span></span><br><span class="line">  queue %[<span class="built_in">echo</span> <span class="string">&quot;----&gt; Start to rollback&quot;</span>]</span><br><span class="line">  queue %[<span class="keyword">if</span> [ $(<span class="built_in">ls</span> <span class="comment">#&#123;deploy_to&#125;/releases | wc -l) -gt 1 ]; then echo &quot;----&gt;Relink to previos release&quot; &amp;&amp; unlink #&#123;deploy_to&#125;/current &amp;&amp; ln -s #&#123;deploy_to&#125;/releases/&quot;$(ls #&#123;deploy_to&#125;/releases | tail -2 | head -1)&quot; #&#123;deploy_to&#125;/current &amp;&amp; echo &quot;Remove old releases&quot; &amp;&amp; rm -rf #&#123;deploy_to&#125;/releases/&quot;$(ls #&#123;deploy_to&#125;/releases | tail -1)&quot; &amp;&amp; echo &quot;$(ls #&#123;deploy_to&#125;/releases | tail -1)&quot; &gt; #&#123;deploy_to&#125;/last_version &amp;&amp; echo &quot;Done. Rollback to v$(cat #&#123;deploy_to&#125;/last_version)&quot; ; else echo &quot;No more release to rollback&quot; ; fi]</span></span><br><span class="line">end</span><br></pre></td></tr></table></figure></p><h3 id="Initialize-the-deployment-environment"><a href="#Initialize-the-deployment-environment" class="headerlink" title="Initialize the deployment environment"></a>Initialize the deployment environment</h3> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mina setup</span><br></pre></td></tr></table></figure><p> After running this, Mina creates its specific release directory structure in the configured deployment directory and executes the <code>setup</code> section of the configuration.</p><h3 id="Modify-Nginx"><a href="#Modify-Nginx" class="headerlink" title="Modify Nginx"></a>Modify Nginx</h3>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">    listen       80;</span><br><span class="line">    server_name  test.com;</span><br><span class="line">    index index.html index.htm index.php;</span><br><span class="line">    root /var/www/html/test/current;    <span class="comment"># Append current to the deployment directory</span></span><br></pre></td></tr></table></figure><h3 id="Start-deploying"><a href="#Start-deploying" class="headerlink" title="Start deploying"></a>Start deploying</h3><p> Run the following command every time you need to deploy.<br>   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mina deploy</span><br></pre></td></tr></table></figure></p><h2 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h2><ul><li>Add the development machine used for deployment to the VPS passwordless login configuration.</li><li>Add the VPS’s SSH key to the Deploy keys of the GitHub repository.</li></ul>]]>
    </content>
    <id>https://www.robbs.win/en/2016-10-25/Use-Mina-Deploy-Yii2.html</id>
    <link href="https://www.robbs.win/en/2016-10-25/Use-Mina-Deploy-Yii2.html"/>
    <published>2016-10-25T15:03:20.000Z</published>
    <summary>Quickly deploy a Yii2 project using Mina.</summary>
    <title>Deploying a Yii2 Project with Mina</title>
    <updated>2026-07-01T02:18:33.609Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Develop" scheme="https://www.robbs.win/en/categories/Develop/"/>
    <category term="PHP" scheme="https://www.robbs.win/en/tags/PHP/"/>
    <category term="Yii2" scheme="https://www.robbs.win/en/tags/Yii2/"/>
    <content>
      <![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><h3 id="Yii"><a href="#Yii" class="headerlink" title="Yii"></a>Yii</h3><p>  <a href="http://www.yiiframework.com/">Yii</a> is a high-performance PHP framework suitable for developing Web 2.0 applications.<br>  Yii ships with a rich set of features, including MVC, DAO&#x2F;ActiveRecord, I18N&#x2F;L10N, caching, authentication and role-based access control, scaffolding, testing, and more, which can significantly shorten development time.</p><h2 id="Creation"><a href="#Creation" class="headerlink" title="Creation"></a>Creation</h2><h3 id="Prerequisites"><a href="#Prerequisites" class="headerlink" title="Prerequisites"></a>Prerequisites</h3><ul><li><a href="/2016-09-30/Install-Nginx-PHP7-MySQL-on-Ubuntu16-04.html">PHP runtime environment</a></li><li>Composer environment<ul><li><p>Mac OS X</p><pre><code><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install composer</span><br></pre></td></tr></table></figure></code></pre></li><li><p>Ubuntu</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Download</span></span><br><span class="line">curl -sS https://getcomposer.org/installer | php -d detect_unicode=Off </span><br><span class="line"><span class="comment"># Make it global</span></span><br><span class="line"><span class="built_in">mv</span> composer.phar /usr/local/bin/composer </span><br><span class="line"><span class="comment"># Permissions</span></span><br><span class="line"><span class="built_in">chmod</span> a+x /usr/local/bin/composer</span><br><span class="line"><span class="comment"># Update</span></span><br><span class="line">composer self-update</span><br></pre></td></tr></table></figure></li></ul></li></ul><h3 id="Installation"><a href="#Installation" class="headerlink" title="Installation"></a>Installation</h3><ol><li><h4 id="Install-via-Composer"><a href="#Install-via-Composer" class="headerlink" title="Install via Composer"></a>Install via Composer</h4>  Install the composer-asset-plugin.   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Switch to the China mirror (http://pkg.phpcomposer.com/)</span></span><br><span class="line">composer config -g repo.packagist composer https://packagist.phpcomposer.com</span><br><span class="line"></span><br><span class="line">composer global require <span class="string">&quot;fxp/composer-asset-plugin:^1.2.0&quot;</span></span><br></pre></td></tr></table></figure>   Install the basic edition.   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">composer create-project yiisoft/yii2-app-basic xxx</span><br></pre></td></tr></table></figure>   Install the advanced edition.   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">composer create-project yiisoft/yii2-app-advanced xxx</span><br></pre></td></tr></table></figure></li><li><h4 id="Download-and-install"><a href="#Download-and-install" class="headerlink" title="Download and install"></a>Download and install</h4>   <a href="https://github.com/yiisoft/yii2/releases/download/2.0.10/yii-basic-app-2.0.10.tgz">Basic edition</a><br>   <a href="https://github.com/yiisoft/yii2/releases/download/2.0.10/yii-advanced-app-2.0.10.tgz">Advanced edition</a></li></ol><h2 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h2>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">The zip extension and unzip <span class="built_in">command</span> are both missing, skipping.</span><br></pre></td></tr></table></figure><p>  Solution</p>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apt-get install php7.0-zip</span><br></pre></td></tr></table></figure><hr>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">codeception/base 2.2.3 requires phpunit/phpunit &gt;4.8.20 &lt;5.5 -&gt; satisfiable by phpunit/phpunit</span><br><span class="line">phpunit/phpunit 5.6.2 requires ext-dom * -&gt; the requested PHP extension dom is missing from your system.</span><br></pre></td></tr></table></figure><p>  Solution<br>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apt-get install php7.0-xml</span><br></pre></td></tr></table></figure></p>]]>
    </content>
    <id>https://www.robbs.win/en/2016-10-25/Create-Yii2-Project.html</id>
    <link href="https://www.robbs.win/en/2016-10-25/Create-Yii2-Project.html"/>
    <published>2016-10-25T14:07:50.000Z</published>
    <summary>A brief guide to creating a Yii2 project, along with a few things to watch out for.</summary>
    <title>Creating a Yii2 Project</title>
    <updated>2026-07-01T02:18:33.654Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Software" scheme="https://www.robbs.win/en/categories/Software/"/>
    <category term="Server" scheme="https://www.robbs.win/en/tags/Server/"/>
    <category term="Siege" scheme="https://www.robbs.win/en/tags/Siege/"/>
    <content>
      <![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>  <a href="https://github.com/JoeDog/siege" title="SIege">Siege</a> is a load testing and benchmarking tool for Linux, designed for web developers to evaluate how well an application holds up under stress. It can be configured to perform concurrent access by multiple users against a web site, recording the response time of every request for each user, and repeating this under a given number of concurrent users. It supports multiple connections as well as GET and POST requests.</p><h2 id="Installation"><a href="#Installation" class="headerlink" title="Installation"></a>Installation</h2><ul><li>Mac OSX<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install siege</span><br></pre></td></tr></table></figure></li><li>Ubuntu<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">aptitude install siege</span><br></pre></td></tr></table></figure></li></ul><h2 id="Parameters"><a href="#Parameters" class="headerlink" title="Parameters"></a>Parameters</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">-c:  Simulate N concurrent <span class="built_in">users</span> accessing the target</span><br><span class="line"></span><br><span class="line">-r:  Repeat the <span class="built_in">test</span> run N <span class="built_in">times</span></span><br><span class="line"></span><br><span class="line">-t:  Duration of the <span class="built_in">test</span> run. The default unit is minutes, e.g. 5s (5 seconds) or 5 (5 minutes).</span><br><span class="line"><span class="comment"># -r and -t are generally not used together</span></span><br><span class="line"></span><br><span class="line">-f:  The URL list <span class="keyword">for</span> the workload</span><br><span class="line"></span><br><span class="line">-i:  Randomly access URLs from the url.txt file specified by -f, <span class="keyword">in</span> order to simulate real access patterns (randomness)</span><br><span class="line"></span><br><span class="line">-b:  Run a stress <span class="built_in">test</span> with no delay between requests (delay=0)</span><br><span class="line"></span><br><span class="line">-A:  Specify the User-Agent <span class="keyword">for</span> the requests</span><br><span class="line">-H:  Specify a Header <span class="keyword">for</span> the requests</span><br><span class="line">-T:  Specify the Content-Type <span class="keyword">for</span> the requests</span><br></pre></td></tr></table></figure><blockquote><p><code>siege -c 200 -r 100 http://www.google.com</code><br><code>siege  -c 200 -r 100 -f urls.txt</code><br><code>siege  -c 200 -r 100 -f urls.txt -i </code><br><code>siege -c 200 -r 100 -f urls.txt -i -b</code> delay&#x3D;0, a more accurate stress test rather than a functional test<br><code>siege -H &quot;Content-Type:application/json&quot; -c 200 -r 100 -f urls.txt -i -b</code></p></blockquote><h2 id="Result-Explanation"><a href="#Result-Explanation" class="headerlink" title="Result Explanation"></a>Result Explanation</h2>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Transactions:            20 hits            <span class="comment"># Total number of tests performed</span></span><br><span class="line">Availability:            100.00 %           <span class="comment"># Success ratio</span></span><br><span class="line">Elapsed <span class="keyword">time</span>:            1.52 secs          <span class="comment"># Total time elapsed</span></span><br><span class="line">Data transferred:            0.80 MB            <span class="comment"># Total data transferred</span></span><br><span class="line">Response <span class="keyword">time</span>:            0.14 secs          <span class="comment"># Response time</span></span><br><span class="line">Transaction rate:            13.16 trans/sec    <span class="comment"># Requests processed per second</span></span><br><span class="line">Throughput:            0.52 MB/sec        <span class="comment"># Throughput rate</span></span><br><span class="line">Concurrency:            1.82               <span class="comment"># Highest concurrency</span></span><br><span class="line">Successful transactions:            20                 <span class="comment"># Number of successful requests</span></span><br><span class="line">Failed transactions:                0                  <span class="comment"># Number of failed requests</span></span><br><span class="line">Longest transaction:                0.51               <span class="comment"># Longest time for a single transaction</span></span><br><span class="line">Shortest transaction:               0.05               <span class="comment"># Shortest time for a single transaction</span></span><br></pre></td></tr></table></figure><h2 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h2><blockquote><p>When sending a POST request, the URL format is: <code>http://www.xxxx.com/ POST p1=v1&amp;p2=v2</code></p></blockquote><blockquote><p> If the URL contains spaces or non-ASCII characters, it must be encoded first.</p></blockquote>]]>
    </content>
    <id>https://www.robbs.win/en/2016-10-24/Siege.html</id>
    <link href="https://www.robbs.win/en/2016-10-24/Siege.html"/>
    <published>2016-10-24T06:46:29.000Z</published>
    <summary>A brief introduction to the parameters of the load testing tool Siege</summary>
    <title>Introduction to Siege</title>
    <updated>2026-07-01T02:27:08.696Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="Server" scheme="https://www.robbs.win/en/categories/Server/"/>
    <category term="Ubuntu" scheme="https://www.robbs.win/en/tags/Ubuntu/"/>
    <category term="Server" scheme="https://www.robbs.win/en/tags/Server/"/>
    <category term="Swap" scheme="https://www.robbs.win/en/tags/Swap/"/>
    <content>
      <![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><h3 id="What-Is-Swap"><a href="#What-Is-Swap" class="headerlink" title="What Is Swap"></a>What Is Swap</h3><p>   Swap in a Linux system, also known as swap space, is similar to the virtual memory (pagefile.sys) of Windows. When memory runs low, a portion of the disk space is turned into virtual memory to store data that is not being used at the moment.</p><h2 id="Setup"><a href="#Setup" class="headerlink" title="Setup"></a>Setup</h2><ol><li><h4 id="Check-the-current-state"><a href="#Check-the-current-state" class="headerlink" title="Check the current state"></a>Check the current state</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">free -m</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">             total       used       free     shared    buffers     cached</span><br><span class="line">Mem:          3952       2035       1916          9        217       1392</span><br><span class="line">-/+ buffers/cache:        425       3526</span><br><span class="line">Swap:            0          0          0</span><br></pre></td></tr></table></figure><p>As you can see, Swap is not enabled. Below we will increase it to match the memory size (4G).</p></li><li><h4 id="Create-the-Swap-file"><a href="#Create-the-Swap-file" class="headerlink" title="Create the Swap file"></a>Create the Swap file</h4> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> swap</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> swap</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">dd</span> <span class="keyword">if</span>=/dev/zero of=swapfile bs=1024 count=4M   <span class="comment"># bs is the block size, count is the number of blocks; 1024 * 4M = 4G</span></span><br><span class="line"><span class="comment"># 4194304+0 records in</span></span><br><span class="line"><span class="comment"># 4194304+0 records out</span></span><br><span class="line"><span class="comment"># 4294967296 bytes (4.3 GB) copied, 88.3999 s, 48.6 MB/s</span></span><br></pre></td></tr></table></figure><p> Convert the file into a swap file.</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> mkswap -f  swapfile </span><br><span class="line"><span class="comment"># Setting up swapspace version 1, size = 4194300 KiB</span></span><br><span class="line"><span class="comment"># no label, UUID=bebbcbad-dda2-49f9-9aab-4b24b1d62d87</span></span><br></pre></td></tr></table></figure></li><li><h4 id="Activate-Swap"><a href="#Activate-Swap" class="headerlink" title="Activate Swap"></a>Activate Swap</h4> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> swapon swapfile</span><br></pre></td></tr></table></figure><p> Verify the activation.</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">free -m  </span><br></pre></td></tr></table></figure> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">             total       used       free     shared    buffers     cached</span><br><span class="line">Mem:          3952       3842        109          9          1       3369</span><br><span class="line">-/+ buffers/cache:        470       3481</span><br><span class="line">Swap:         4095          0       4095</span><br></pre></td></tr></table></figure></li><li><h4 id="Configuration"><a href="#Configuration" class="headerlink" title="Configuration"></a>Configuration</h4><ul><li><p>Adjust swappiness<br>swappiness is a value from 0 to 100. A higher value means the system will more actively use Swap.</p><ul><li>Temporary change<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> sysctl vm.swappiness=40</span><br></pre></td></tr></table></figure></li><li>Permanent change <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/sysctl.conf</span><br><span class="line"><span class="comment"># Add a line</span></span><br><span class="line">vm.swappiness = 40</span><br></pre></td></tr></table></figure></li></ul></li><li><p>Change permissions<br>   Set the file so that only the root user has read and write permissions.</p>   <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">chown</span> root:root /swap/swapfile</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chmod</span> 0600 /swap/swapfile</span><br></pre></td></tr></table></figure></li></ul></li><li><h4 id="Enable-on-boot"><a href="#Enable-on-boot" class="headerlink" title="Enable on boot"></a>Enable on boot</h4> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/fstab</span><br></pre></td></tr></table></figure><p> Add the following line at the end of the file.</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/swap/swapfile       none    swap    sw      0       0</span><br></pre></td></tr></table></figure></li></ol>]]>
    </content>
    <id>https://www.robbs.win/en/2016-10-21/Ubuntu-Swap.html</id>
    <link href="https://www.robbs.win/en/2016-10-21/Ubuntu-Swap.html"/>
    <published>2016-10-21T02:29:11.000Z</published>
    <summary>Many VPS instances running Ubuntu do not enable Swap by default, or their Swap size is insufficient. This article describes how to add swap space on Ubuntu.</summary>
    <title>Adding Swap on Ubuntu</title>
    <updated>2026-07-01T02:18:33.632Z</updated>
  </entry>
  <entry>
    <author>
      <name>Robbs Luo</name>
    </author>
    <category term="PHP" scheme="https://www.robbs.win/en/categories/PHP/"/>
    <category term="PHP" scheme="https://www.robbs.win/en/tags/PHP/"/>
    <category term="Tips" scheme="https://www.robbs.win/en/tags/Tips/"/>
    <content>
      <![CDATA[<h2 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h2><ol><li><h4 id="Prefer-static-when-possible"><a href="#Prefer-static-when-possible" class="headerlink" title="Prefer static when possible"></a>Prefer static when possible</h4> If a function can be made static, prefer to make it static.<br>  The difference mainly shows up in memory handling: static methods allocate memory when the program starts, whereas instance methods allocate memory only when the object is instantiated.<br>  Static methods can be called directly, while instance methods require you to first create an instance and then call through it.</li></ol><blockquote><p>Too many static methods will consume memory.</p></blockquote><ol><li><h4 id="echo-VS-print"><a href="#echo-VS-print" class="headerlink" title="echo VS print"></a>echo VS print</h4><p>echo is faster than print, because echo has no return value while print returns an integer.</p><blockquote><p>When echoing large strings, the corresponding configuration needs to be done on the server.</p></blockquote></li><li><h4 id="echo-multiple-strings"><a href="#echo-multiple-strings" class="headerlink" title="echo multiple strings"></a>echo multiple strings</h4><p>When echo-ing multiple strings, use <code>,</code> instead of <code>.</code> to join them.</p></li><li><h4 id="error-suppression"><a href="#error-suppression" class="headerlink" title="@ error suppression"></a>@ error suppression</h4><p>Using @ to suppress errors slows down the script. In particular, avoid using @ inside loops.</p></li><li><h4 id="row-‘id’-row-id-row-1"><a href="#row-‘id’-row-id-row-1" class="headerlink" title="$row[‘id’] &amp; $row[id] &amp; $row[1]"></a>$row[‘id’] &amp; $row[id] &amp; $row[1]</h4><p> <code>&#39;id&#39;</code> looks up the key <code>&#39;id&#39;</code> directly. Without quotes, a variable or constant is parsed by first determining its type and then resolving the value.</p></li><li><h4 id="isset-empty"><a href="#isset-empty" class="headerlink" title="isset() &amp; empty()"></a>isset() &amp; empty()</h4><p>isset() tests whether a variable has been assigned.<br>empty() tests whether an already-assigned variable is empty. Referencing a variable that hasn’t been assigned is allowed but produces a notice.</p><blockquote><p>If a variable is assigned an empty value <code>$t = &quot;&quot;; $t = 0; $t = false;</code>, both <code>empty($t)</code> and <code>isset($t)</code> return true.<br>To destroy a variable, use <code>unset($t)</code> or <code>$t = NULL</code>.</p></blockquote></li><li><h4 id="Confirm-the-maximum-count-before-looping"><a href="#Confirm-the-maximum-count-before-looping" class="headerlink" title="Confirm the maximum count before looping"></a>Confirm the maximum count before looping</h4><p> Determine the maximum count before entering a for loop; don’t recompute the maximum on every iteration.</p> <figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Don&#x27;t do this</span></span><br><span class="line"><span class="keyword">for</span> (<span class="variable">$i</span>=<span class="number">0</span>;<span class="variable">$i</span>&lt;=<span class="title function_ invoke__">count</span>(<span class="variable">$array</span>);<span class="variable">$i</span>++)&#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Do this instead</span></span><br><span class="line"><span class="variable">$len</span> = <span class="title function_ invoke__">count</span>(<span class="variable">$array</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="variable">$i</span>=<span class="number">0</span>;<span class="variable">$i</span>&lt;=<span class="variable">$len</span>;<span class="variable">$i</span>++)&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><h4 id="include-include-once-require-require-once"><a href="#include-include-once-require-require-once" class="headerlink" title="include() &amp; include_once() &amp; require() &amp; require_once()"></a>include() &amp; include_once() &amp; require() &amp; require_once()</h4><p>All four load a file. The <code>_once</code> variants first check whether the file has already been loaded, to avoid loading it twice.<br>The main differences are:</p><ul><li>Error handling<ul><li><code>require()</code> throws a <code>fatal error</code> and halts the script if the file doesn’t exist.</li><li><code>include()</code> throws a <code>warning</code> but the script continues if the file doesn’t exist.</li></ul></li><li>Performance<ul><li><code>require()</code> processes the file only once (in effect, the file’s content replaces the require() statement).</li><li><code>include()</code> reads and evaluates the file on every execution.<br>If code containing one of these directives may run multiple times, <code>require()</code> is more efficient.</li></ul></li><li>Flexibility<ul><li><code>require()</code> is usually placed at the very top of a script; the specified file is loaded before execution, becoming part of the script.</li><li><code>include()</code> is usually placed inside control-flow blocks. The file is loaded only when execution reaches it. This can simplify the runtime flow.</li></ul></li></ul></li></ol><blockquote><p>Use full paths when including files, to reduce the time spent resolving paths.</p></blockquote><ol><li><h4 id="‘-‘-“-“"><a href="#‘-‘-“-“" class="headerlink" title="‘ ‘ &amp; “ “"></a>‘ ‘ &amp; “ “</h4><p> PHP allows both single and double quotes to delimit string variables.<br> <code>&quot; &quot;</code> first reads the string contents, then looks for variables inside it and interpolates their values.</p></li><li><h4 id="Don’t-copy-variables-carelessly"><a href="#Don’t-copy-variables-carelessly" class="headerlink" title="Don’t copy variables carelessly"></a>Don’t copy variables carelessly</h4><p> Copying one variable into another doubles the memory consumption.</p></li><li><h4 id="if-else-switch-case"><a href="#if-else-switch-case" class="headerlink" title="if else &amp; switch case"></a>if else &amp; switch case</h4><p> A switch&#x2F;case is better than a long chain of if&#x2F;else if statements, and the code is easier to read and maintain.</p></li><li><h4 id="Not-everything-has-to-be-object-oriented"><a href="#Not-everything-has-to-be-object-oriented" class="headerlink" title="Not everything has to be object-oriented"></a>Not everything has to be object-oriented</h4><p> Object orientation often carries significant overhead; every method call and object instantiation consumes memory.</p></li><li><h4 id="Don’t-split-methods-too-finely"><a href="#Don’t-split-methods-too-finely" class="headerlink" title="Don’t split methods too finely"></a>Don’t split methods too finely</h4><p> Every method call consumes memory.</p></li><li><h4 id="Prefer-PHP’s-built-in-functions-where-possible"><a href="#Prefer-PHP’s-built-in-functions-where-possible" class="headerlink" title="Prefer PHP’s built-in functions where possible."></a>Prefer PHP’s built-in functions where possible.</h4></li><li><h4 id="Don’t-declare-variables-inside-loops-especially-large-ones-objects"><a href="#Don’t-declare-variables-inside-loops-especially-large-ones-objects" class="headerlink" title="Don’t declare variables inside loops, especially large ones: objects."></a>Don’t declare variables inside loops, especially large ones: objects.</h4></li><li><h4 id="Destroy-variables-to-release-memory-especially-large-arrays"><a href="#Destroy-variables-to-release-memory-especially-large-arrays" class="headerlink" title="Destroy variables to release memory, especially large arrays."></a>Destroy variables to release memory, especially large arrays.</h4></li><li><h4 id="Use-string-functions-instead-of-regular-expressions"><a href="#Use-string-functions-instead-of-regular-expressions" class="headerlink" title="Use string functions instead of regular expressions."></a>Use string functions instead of regular expressions.</h4></li><li><h4 id="split-is-faster-than-explode"><a href="#split-is-faster-than-explode" class="headerlink" title="split is faster than explode."></a>split is faster than explode.</h4></li></ol>]]>
    </content>
    <id>https://www.robbs.win/en/2016-10-20/PHP-Matters-Needing-Attention.html</id>
    <link href="https://www.robbs.win/en/2016-10-20/PHP-Matters-Needing-Attention.html"/>
    <published>2016-10-20T03:08:57.000Z</published>
    <summary>A collection of things to keep in mind during everyday PHP development. They can, to a certain extent, make your programs run faster and more reliably.</summary>
    <title>PHP Matters Needing Attention</title>
    <updated>2026-07-01T02:28:47.189Z</updated>
  </entry>
</feed>
