<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>DeiMo&#39;s blog</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://deimo.github.io/"/>
  <updated>2019-06-08T05:05:34.562Z</updated>
  <id>https://deimo.github.io/</id>
  
  <author>
    <name>DeiMo</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>走近Python密码学</title>
    <link href="https://deimo.github.io/2019/06/08/%E8%B5%B0%E8%BF%91Python%E5%AF%86%E7%A0%81%E5%AD%A6/"/>
    <id>https://deimo.github.io/2019/06/08/走近Python密码学/</id>
    <published>2019-06-07T19:16:17.000Z</published>
    <updated>2019-06-08T05:05:34.562Z</updated>
    
    <content type="html"><![CDATA[<h1 id="走近Python的加解密"><a href="#走近Python的加解密" class="headerlink" title="走近Python的加解密"></a>走近Python的加解密</h1><h2 id="相关概念"><a href="#相关概念" class="headerlink" title="相关概念"></a>相关概念</h2><h4 id="数据加密技术"><a href="#数据加密技术" class="headerlink" title="数据加密技术"></a>数据加密技术</h4><blockquote><p>所谓<a href="https://baike.baidu.com/item/数据加密/11048982" target="_blank" rel="noopener">数据加密</a>（Data Encryption）技术是指将一个信息（或称明文，plain text）经过加<a href="https://baike.baidu.com/item/密钥" target="_blank" rel="noopener">密钥</a>匙（Encryption key）及加密函数转换，变成无意义的密文（cipher text），而接收方则将此密文经过解密函数、解密钥匙（Decryption key）还原成明文。 ——以上内容节选自【<a href="[https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%8A%A0%E5%AF%86%E6%8A%80%E6%9C%AF](https://baike.baidu.com/item/数据加密技术">百度百科</a>)】</p></blockquote><h4 id="信息编解码技术"><a href="#信息编解码技术" class="headerlink" title="信息编解码技术"></a>信息编解码技术</h4><blockquote><p>编码是<a href="https://baike.baidu.com/item/信息/111163" target="_blank" rel="noopener">信息</a>从一种形式或格式转换为另一种形式的过程，也称为计算机编程语言的代码简称编码。用预先规定的方法将文字、数字或其它对象编成数码，或将信息、数据转换成规定的电脉冲信号。编码在<a href="https://baike.baidu.com/item/电子计算机/191373" target="_blank" rel="noopener">电子计算机</a>、<a href="https://baike.baidu.com/item/电视/228945" target="_blank" rel="noopener">电视</a>、遥控和通讯等方面广泛使用。编码是信息从一种形式或格式转换为另一种形式的过程。<a href="https://baike.baidu.com/item/解码/10944752" target="_blank" rel="noopener">解码</a>，是编码的逆过程。——以上内容节选自 【<a href="[https://baike.baidu.com/item/%E7%BC%96%E7%A0%81/80092](https://baike.baidu.com/item/编码/80092">百度百科</a>)】</p></blockquote><p>从以上定义可以总结如下：</p><ul><li>数据加密和信息编码都会对原有数据进行一定的转换</li><li>两者进行转换的目的是不同的：<ul><li>数据加密技术更多的出于信息安全的考量</li><li>数据的编解码更多的出于计算机传输和存储和解析的考量</li></ul></li></ul><p>因此我们平时在工作交流和表达过程中所说的<strong>“Base64加密”</strong>其实并非加密，而只是为了某种传输需要将数据明文进行了某种转换。转换的编解码规则是公开的，理论上，任何了解<strong>Base64</strong>编解码规则的同学，都可以将<strong>Base64</strong>编码串解码后得到原有数据！</p><h4 id="消息摘要算法"><a href="#消息摘要算法" class="headerlink" title="消息摘要算法"></a>消息摘要算法</h4><blockquote><p>消息摘要算法的主要特征是加密过程不需要<a href="https://baike.baidu.com/item/密钥/101144" target="_blank" rel="noopener">密钥</a>，并且经过加密的数据无法被解密，目前可以被解密逆向的只有CRC32算法，只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。消息摘要算法不存在密钥的管理与分发问题，适合于<a href="https://baike.baidu.com/item/分布式网络/8951687" target="_blank" rel="noopener">分布式网络</a>上使用。——以上内容节选自【<a href="[https://baike.baidu.com/item/%E6%B6%88%E6%81%AF%E6%91%98%E8%A6%81%E7%AE%97%E6%B3%95/3286770?fr=aladdin](https://baike.baidu.com/item/消息摘要算法/3286770?fr=aladdin">百度百科</a>)】</p></blockquote><p>在实际工作中我们常用的信息摘要算法有<strong>MD5</strong>和<strong>SHA1</strong></p><p>后端开发童鞋经常提到：用户密码不可以明文保存，要使用<strong>MD5</strong>算法对用户密码进行”加密”后再存至数据库。</p><p>同样，这里”加密”也并非我们严格定义上的”加密”，使用<strong>MD5</strong>处理的原因是信息摘要过程是不可逆，且中间不需要任何密钥的参与！理论上攻击者无法通过信息摘要算法得到结果反推出原始数据，这个特性使得信息摘要算法的主要用途是：</p><ul><li><p>验证数据的完整性和一致性</p></li><li><p>对数据进行签名及校验</p></li></ul><p>在了解了有关概念和定义后，我们就可以进一步探讨数据加密技术了。</p><p>数据加密算法根据加密和解密时是否使用同一密钥可分为</p><ul><li><p>对称加密算法：在加密和解密时使用的是同一个密钥</p><blockquote><p>常见对称加密算法有：DES算法（安全性上不如AES，逐步被AES替代）， AES算法</p></blockquote></li><li><p>非对称加密算法：在加密和解密时使用不同密钥</p><blockquote><p>常见的非对称算法：RSA算法，椭圆曲线算法(又称ECC，被广泛用于区块链业务中，被视为RSA算法的有力挑战者)</p></blockquote></li></ul><p>本文以当前企业应用中最为常见的AES算法和RSA算法进行介绍，相关代码可在我的<a href="https://github.com/deimo/edcrypt" target="_blank" rel="noopener"><strong>github repo</strong></a>中找到</p><h2 id="AES加解密算法"><a href="#AES加解密算法" class="headerlink" title="AES加解密算法"></a>AES加解密算法</h2><h4 id="流程"><a href="#流程" class="headerlink" title="流程"></a>流程</h4><p>AES加解密的流程，先以我看到的一张图描述(如有侵权，请联系博主删除)</p><p><img src="https://img-blog.csdn.net/20170219082909688?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjgyMDUxNTM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="AES简介">更多有关AES数学原理介绍及算法实现，可参考这篇<a href="https://blog.csdn.net/qq_28205153/article/details/55798628" target="_blank" rel="noopener">文章</a>(上述加密流程图即出自此文)</p><h4 id="相关概念-1"><a href="#相关概念-1" class="headerlink" title="相关概念"></a>相关概念</h4><ul><li><p>密钥</p><blockquote><p>密钥是AES算法实现加密和解密的根本。对称加密算法之所以对称，是因为这类算法对明文的加密和解密需要使用<strong>同一个密钥</strong>。</p><p>AES支持三种长度的密钥：<strong>128位，192位，256位</strong></p></blockquote><p>如果对于密钥没有一个直观印象，可以将密钥理解为发送方和传输方共同拥有的钥匙。</p></li><li><p>填充</p><p>要想了解填充，先要了解AES的<strong>分组加密</strong>特性</p><p><img src="http://5b0988e595225.cdn.sohucs.com/images/20171018/c2975f0c10114f0fb6bac5dd30040e8c.png" alt="分组加密"></p><p>AES算法在对明文加密的时候，并不是把整个明文一股脑加密成一整段密文，而是把明文拆分成一个个独立的明文块，每一个明文块长度128bit。假如一段明文长度是196bit，如果按每128bit一个明文块来拆分的话，第二个明文块只有64bit，不足128bit，这个时候就需要对明文块进行<strong>填充</strong>。根据填充算法的不同有以下几种常见的填充方式：</p><ul><li>PKCS#7Padding：假设数据长度需要填充n(n&gt;0)个字节才对齐，那么填充n个字节，每个字节都是n;如果数据本身就已经对齐了，则填充一块长度为块大小的数据，每个字节都是块大小</li><li>ZeroPadding：数据长度不对齐时使用0填充，否则不填</li><li>PKCS#5Padding：PKCS7Padding的子集，块大小固定为8字节</li></ul></li><li><p>模式</p><p>AES的工作模式，体现在把明文块加密成密文块的处理过程中。AES加密算法提供了五种不同的工作模式：CBC、ECB、CTR、CFB、OFB</p><p>其中最为常见且推荐使用的便是CBC模式了</p><p>有关AES算法各模式运作原理不是本文重点，如果你想了解更多有关此内容，推荐阅读这两篇：<a href="https://www.cnblogs.com/starwolf/p/3365834.html" target="_blank" rel="noopener">文章1</a>和<a href="https://www.cnblogs.com/liangxuehui/p/4651351.html" target="_blank" rel="noopener">文章2</a></p></li></ul><h4 id="过程描述"><a href="#过程描述" class="headerlink" title="过程描述"></a>过程描述</h4><p>基于此前密钥就是钥匙的假设，可以认为加密过程理解为将数据装进一个带锁的箱子，这个锁只有钥匙(密钥)拥有者才能打开，而相应的解密过程就是使用密钥打开上锁的箱子并取出其中数据</p><h4 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h4><p>代码参考<a href="https://www.jianshu.com/p/5d27888e7c93" target="_blank" rel="noopener">简书文章</a>，基于pycryptodome使用Python编写</p><h6 id="加密流程"><a href="#加密流程" class="headerlink" title="加密流程"></a>加密流程</h6><p><img src="/img/aes/aes1.png" alt="加密流程"></p><h6 id="解密流程"><a href="#解密流程" class="headerlink" title="解密流程"></a>解密流程</h6><p><img src="/img/aes/aes2.png" alt="解密流程"></p><p>以上部分的具体代码可在我的<a href="https://github.com/deimo/edcrypt" target="_blank" rel="noopener"><strong>github repo</strong></a>上找到</p><h2 id="RSA加解密算法"><a href="#RSA加解密算法" class="headerlink" title="RSA加解密算法"></a>RSA加解密算法</h2><h4 id="流程与场景"><a href="#流程与场景" class="headerlink" title="流程与场景"></a>流程与场景</h4><p>RSA加解密的流程，先以我看到的一张图描述(如有侵权，请联系博主删除)</p><p><img src="/img/rsa/RSA1.png" alt="RSA工作流程"></p><p>其中public_key表示公钥，private_key表示私钥。<br>我们举两个栗子吧</p><p><strong>场景1：</strong></p><blockquote><p>Bob要写情书（明文）给Alice，但是Alice希望情书内容不能被人自己父母发现，于是就采用了RSA加密的方式来传递情书。<br>Alice先用RSA生成了一对密钥（有一个公钥，和一个私钥），Bob拿走公钥对情书A（明文）进行加密，生成情书B（密文）。Alice拿到情书B后，用手上的密钥，破解了B，把B还原为原来的A。这样一来，其他人因为没有私钥，无法查看他们之间的甜言蜜语，只有Alice一个人知道其中的秘密</p></blockquote><p><strong>场景2：</strong></p><blockquote><p>现在Alice要给Bob回情书，告诉他今晚12点村头桥下约会，那么，如何让Bob知道情书真是Alice写的呢？注意，Alice手上有私钥，Bob手上有公钥。这时，Alice把情书用私钥进行签名，带着签名和情书内容一同寄出给Bob，Bob拿着公钥验证签名，发现确实是Alice写的，这样一来，他就完全可以相信情书的内容了。</p><p>这里的签名，本质上，是在允许信息内容别第三者发现的情况下，为了让信息接受者可以验证信息的真实性</p></blockquote><p>上述两个场景的总结：</p><p>场景一：公钥加密，私钥解密</p><p>场景二：私钥签名，公钥验证。</p><h4 id="原理及验证"><a href="#原理及验证" class="headerlink" title="原理及验证"></a>原理及验证</h4><p>有关RSA算法的数学原理极其验证过程不是本文的重点，此部分内容建议阅读阮一峰老师的博文：</p><p><a href="[http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html](http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html">RSA算法原理（一）</a>)</p><p><a href="[http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html](http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html">RSA算法原理（二）</a>)</p><h4 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h4><p>根据RSA算法的两个不同应用场景，但无论何种场景都需要生成密钥对，这是RSA算法的基础，这里介绍两种生成密钥对的方法：</p><p>方法1：使用openSSL工具</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 生成1024位的私钥</span></span><br><span class="line">openssl genrsa -out rsa_private_key.pem 1024</span><br><span class="line"><span class="comment"># 把RSA私钥转换成PKCS8格式</span></span><br><span class="line">openssl pkcs8 -topk8 -inform PEM -<span class="keyword">in</span> rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem &amp;&amp; rm rsa_private_key.pem </span><br><span class="line"><span class="comment"># 生成RSA公钥</span></span><br><span class="line">openssl rsa -<span class="keyword">in</span> private_key.pem -out public_key.pem -pubout</span><br></pre></td></tr></table></figure><p>有关openSSL工具的更多用法可查阅<a href="https://www.e-learn.cn/content/linux/1448064" target="_blank" rel="noopener">《openssl命令使用》</a></p><p>方法2：通过pycryptodome库自行创建</p><figure class="highlight python"><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="keyword">from</span> Crypto.PublicKey <span class="keyword">import</span> RSA</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">create_rsa_key</span><span class="params">()</span>:</span></span><br><span class="line">    <span class="string">"""</span></span><br><span class="line"><span class="string">    生成RSA加解密的公钥和私钥</span></span><br><span class="line"><span class="string">    :return:</span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    key = RSA.generate(<span class="number">1024</span>)</span><br><span class="line">    encrypt_key = key.export_key(pkcs=<span class="number">8</span>)</span><br><span class="line">    print(<span class="string">'encrypted_key: '</span>, encrypt_key)</span><br><span class="line">    <span class="keyword">with</span> open(<span class="string">'my_private_rsa_key.pem'</span>, <span class="string">'wb'</span>) <span class="keyword">as</span> f:</span><br><span class="line">        f.write(encrypt_key)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> open(<span class="string">'my_rsa_public.pem'</span>, <span class="string">'wb'</span>) <span class="keyword">as</span> f:</span><br><span class="line">        f.write(key.publickey().export_key())</span><br></pre></td></tr></table></figure><p><strong>场景1</strong></p><h6 id="加密过程"><a href="#加密过程" class="headerlink" title="加密过程"></a>加密过程</h6><p><img src="/img/rsa/rsa2.png" alt="rsa加密过程"></p><h6 id="解密过程"><a href="#解密过程" class="headerlink" title="解密过程"></a>解密过程</h6><p><img src="/img/rsa/rsa3.png" alt="rsa解密过程"></p><p><strong>场景2</strong></p><h6 id="签名过程"><a href="#签名过程" class="headerlink" title="签名过程"></a>签名过程</h6><p><img src="/img/rsa/rsa4.png" alt="签名过程"></p><h6 id="验签过程"><a href="#验签过程" class="headerlink" title="验签过程"></a>验签过程</h6><p><img src="/img/rsa/rsa5.png" alt="验签过程"></p><p>以上部分的具体代码可在我的<a href="https://github.com/deimo/edcrypt" target="_blank" rel="noopener"><strong>github repo</strong></a>上找到</p><h2 id="参考列表"><a href="#参考列表" class="headerlink" title="参考列表"></a>参考列表</h2><p><strong>AES部分，以下排名不分先后</strong></p><ol><li><a href="[http://www.sohu.com/a/198681357_505794](http://www.sohu.com/a/198681357_505794">漫画：什么是AES算法？</a>)</li><li><a href="https://www.cnblogs.com/liangxuehui/p/4651351.html" target="_blank" rel="noopener">AES加密的四种模式详解(https://www.cnblogs.com/liangxuehui/p/4651351.html)</a></li><li><a href="https://www.cnblogs.com/starwolf/p/3365834.html" target="_blank" rel="noopener">AES五种加密模式</a></li><li><a href="https://www.jianshu.com/p/5d27888e7c93" target="_blank" rel="noopener">python3 AES 加密</a></li><li><a href="https://www.jianshu.com/p/1ec9f8819db9" target="_blank" rel="noopener">AES模式和填充</a></li><li><a href="https://www.jianshu.com/p/de84d355c96d" target="_blank" rel="noopener">三种填充模式的区别(PKCS7Padding/PKCS5Padding/ZeroPadding)</a></li><li><a href="https://blog.csdn.net/qq_28205153/article/details/55798628" target="_blank" rel="noopener">AES加密算法的详细介绍与实现</a></li></ol><p><strong>RSA部分，以下排名不分先后</strong></p><ol><li><a href="https://www.e-learn.cn/content/linux/1448064" target="_blank" rel="noopener">openssl命令使用</a></li><li><a href="https://www.jianshu.com/p/518fa5d59f89" target="_blank" rel="noopener">Python RSA 加密</a></li><li><a href="https://blog.csdn.net/Saintyyu/article/details/55806247" target="_blank" rel="noopener">RSA算法原理及其在HTTPS中的应用</a></li><li><a href="https://www.shangyang.me/2017/05/24/encrypt-rsa-keyformat/" target="_blank" rel="noopener">RSA(三) 密钥的格式</a></li><li><a href="https://www.jianshu.com/p/c5e11b1687bf" target="_blank" rel="noopener">RSA加密解密算法—编程实战</a></li><li><a href="[http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html](http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html">RSA算法原理（一）</a>)</li><li><a href="[http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html](http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html">RSA算法原理（二）</a>)</li></ol><h2 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h2><p>如果你喜欢我的文章，请扫描以下二维码，给我小额赞赏，如果没有特殊声明，赞赏…将用于改善我的个人生活~比如：奶茶，咖啡，酸奶或其它零食~😁<br><img src="/img/life/qrcode.jpg" alt="二维码"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;走近Python的加解密&quot;&gt;&lt;a href=&quot;#走近Python的加解密&quot; class=&quot;headerlink&quot; title=&quot;走近Python的加解密&quot;&gt;&lt;/a&gt;走近Python的加解密&lt;/h1&gt;&lt;h2 id=&quot;相关概念&quot;&gt;&lt;a href=&quot;#相关概念&quot; cla
      
    
    </summary>
    
      <category term="密码学" scheme="https://deimo.github.io/categories/%E5%AF%86%E7%A0%81%E5%AD%A6/"/>
    
    
      <category term="Python AES RSA" scheme="https://deimo.github.io/tags/Python-AES-RSA/"/>
    
  </entry>
  
  <entry>
    <title>如何托管微信小程序注册(2)</title>
    <link href="https://deimo.github.io/2019/01/11/%E5%A6%82%E4%BD%95%E6%89%98%E7%AE%A1%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%B3%A8%E5%86%8C-2/"/>
    <id>https://deimo.github.io/2019/01/11/如何托管微信小程序注册-2/</id>
    <published>2019-01-11T12:14:37.000Z</published>
    <updated>2019-01-13T10:59:45.780Z</updated>
    
    <content type="html"><![CDATA[<p>在<a href="https://deimo.github.io/2018/12/16/%E5%A6%82%E4%BD%95%E6%89%98%E7%AE%A1%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%B3%A8%E5%86%8C-1/">上一篇</a>的教程中我向大家介绍了如何识别验证码，向自己的邮箱发送邮件等一系列的车技，在这一次的教程中我将以微信小程序注册的Step2，向大家介绍以下内容：</p><ul><li>如何根据IMAP协议轮询读取邮箱账户中的邮件</li><li>如何模拟请求激活自己的邮箱账户</li></ul><h3 id="根据IMAP协议轮询读取邮箱账户中的邮件"><a href="#根据IMAP协议轮询读取邮箱账户中的邮件" class="headerlink" title="根据IMAP协议轮询读取邮箱账户中的邮件"></a>根据IMAP协议轮询读取邮箱账户中的邮件</h3><h4 id="IMAP协议简介"><a href="#IMAP协议简介" class="headerlink" title="IMAP协议简介"></a>IMAP协议简介</h4><blockquote><p>IMAP（Internet Mail Access Protocol，Internet邮件访问协议）以前称作交互邮件访问协议（Interactive Mail Access Protocol）。IMAP是<a href="https://baike.baidu.com/item/%E6%96%AF%E5%9D%A6%E7%A6%8F%E5%A4%A7%E5%AD%A6/278716" target="_blank" rel="noopener">斯坦福大学</a>在1986年开发的一种邮件获取协议。它的主要作用是邮件<a href="https://baike.baidu.com/item/%E5%AE%A2%E6%88%B7%E7%AB%AF" target="_blank" rel="noopener">客户端</a>（例如MS Outlook Express)可以通过这种协议从邮件<a href="https://baike.baidu.com/item/%E6%9C%8D%E5%8A%A1%E5%99%A8" target="_blank" rel="noopener">服务器</a>上获取邮件的信息，下载邮件等。当前的权威定义是RFC3501。IMAP协议运行在<a href="https://baike.baidu.com/item/TCP%2FIP%E5%8D%8F%E8%AE%AE" target="_blank" rel="noopener">TCP/IP协议</a>之上，使用的端口是143。它与POP3协议的主要区别是用户可以不用把所有的<a href="https://baike.baidu.com/item/%E9%82%AE%E4%BB%B6/3110293" target="_blank" rel="noopener">邮件</a>全部下载，可以通过<a href="https://baike.baidu.com/item/%E5%AE%A2%E6%88%B7%E7%AB%AF/101081" target="_blank" rel="noopener">客户端</a>直接对<a href="https://baike.baidu.com/item/%E6%9C%8D%E5%8A%A1%E5%99%A8/100571" target="_blank" rel="noopener">服务器</a>上的邮件进行操作。</p></blockquote><p>以上内容节选自<a href="https://baike.baidu.com/item/imap/350154?fr=aladdin" target="_blank" rel="noopener">百度百科</a>，更多有关IMAP协议的细节请自行查阅相关资料，总之我们需要明确的是：我们可以使用IMAP协议去读取自己的邮箱中的邮件</p><h4 id="Python-IMAPClient库简介"><a href="#Python-IMAPClient库简介" class="headerlink" title="Python IMAPClient库简介"></a>Python IMAPClient库简介</h4><p>官方文档<a href="https://imapclient.readthedocs.io/en/2.1.0/" target="_blank" rel="noopener">传送门</a>！！！</p><p> IMAPClient的底层基于Python标准库imaplib，但提供了一组更易用更Pythonic更完整的API</p><h5 id="IMAPClient相关概念"><a href="#IMAPClient相关概念" class="headerlink" title="IMAPClient相关概念"></a>IMAPClient相关概念</h5><p>声明：以下内容皆为个人于官方<a href="https://imapclient.readthedocs.io/en/2.1.0/concepts.html#working-with-fetched-messages" target="_blank" rel="noopener">IMAPClient库</a>中了解到并结合个人理解，并不保证绝对的权威和正确，如有勘误，请及时指出</p><ol><li><p><strong>Message Identifiers</strong></p><p>【消息标识】</p><blockquote><p>官方解释如下：<br>在IMAP邮件传输协议中，每一封邮件信息被整数来定义，这些消息的数字id对于给定的邮件文件夹而言是一定的</p></blockquote><p>你可以将这里的消息的标识理解为数据库表中的自增主键ID</p></li><li><p><strong>Message Flags</strong> </p><p>【消息标记】</p><blockquote><p>解释如下：</p><p>对于收件箱中的邮件总是存在一些状态的变化（最近接受、已读、已删除、草稿、已回复等），这些用于描述邮件的读取或收发状态</p></blockquote></li><li><p><strong>Folder Name </strong></p><p>【文件夹名】</p><blockquote><p>解释如下：</p><p>个人登录邮箱时总是会有一些“容器”用于存放我们的分类邮件，如：收件箱，垃圾箱，已删除等，有图有真相</p></blockquote><p><img src="/img/vxregister/2-1.jpg" alt="文件夹截图"></p></li></ol><h5 id="IMAPClient库的使用"><a href="#IMAPClient库的使用" class="headerlink" title="IMAPClient库的使用"></a>IMAPClient库的使用</h5><p>关于IMAPClient的库的使用，不是本文的重点，建议阅读官方文档，理清概念，总结要点，查看demo，自己编写demo亲试之~博主会将所有代码上传至github中，有需要的童鞋可以自行查看，下面po一个IMAP协议读取邮件的demo截图吧</p><p><img src="/img/vxregister/2-2.jpg" alt="IMAPClient使用demo"></p><h4 id="激活你的小程序邮箱账户"><a href="#激活你的小程序邮箱账户" class="headerlink" title="激活你的小程序邮箱账户"></a>激活你的小程序邮箱账户</h4><p>在你的可以成功读取自己的激活邮件内容之后，接下来需要干的自然便是激活邮件内容中的链接。这一过程我们仍然可以使用requests库去完成</p><p>需要指出的是邮件内容中激活链接的获取，需要自己去解析<strong>html</strong>信息中的内容，你可以使用任何解析技术手段（XPath，BeautifulSoup）去处理，想必对于成功获取到邮件内容的你，这一定不是什么难题了~</p><h5 id="1-激活过程分析"><a href="#1-激活过程分析" class="headerlink" title="1. 激活过程分析"></a>1. 激活过程分析</h5><p>当你在浏览器中访问邮件中的链接时，最终会被重定向到一个这样的一个地址</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://mp.weixin.qq.com/wxopen/wacontractorpage?action=step3&amp;lang=zh_CN&amp;token=532810804</span><br></pre></td></tr></table></figure><p>上述地址中的token参数值非常重要，你可以认为这是微信小程序端serve返回的此次激活标识，并且这个参数的值会在后续的表单请求中使用到，因此需要全局妥善保存~</p><p>事实上，当你访问过邮件中的激活链接后，再次访问时，链接便已失效，将无法再得到任何正常响应。因此所谓的邮件激活你可以简单理解为直接向邮件内容中的地址发送请求</p><h5 id="2-代码实战"><a href="#2-代码实战" class="headerlink" title="2. 代码实战"></a>2. 代码实战</h5><p>requests库对于重定向过程有一个默认的策略，如果你希望自己定义请求的过程中的全部行为，可以禁用它</p><figure class="highlight python"><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">from</span> urllib.parse <span class="keyword">import</span> parse_qs</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line">session = requests.Session()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_token</span><span class="params">(activate_url)</span>:</span></span><br><span class="line">    rsp1 = session.get(activate_url, allow_redirects=<span class="keyword">False</span>)</span><br><span class="line">    real_url = rsp1.headers[<span class="string">'location'</span>]</span><br><span class="line">    print(<span class="string">'real_url: '</span>, real_url)</span><br><span class="line">    pr = parse_qs(real_url)</span><br><span class="line">    token = pr[<span class="string">'token'</span>][<span class="number">0</span>]</span><br><span class="line">    print(<span class="string">'token: '</span>, token)</span><br></pre></td></tr></table></figure><p>是的，你没看错，只需要简单几行代码就可以实现微信小程序的激活邮件激活并拿到全局请求token值了</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>好啦，今天的第二部分教程就到此为止啦~其实第三部分教程会依赖第二部分教程中的结果，但考虑到阅读量和学习量，将这部分内容分开来介绍了。</p><p>在下一次教程中我将带给大家如下内容：</p><ul><li>模拟生成中国大陆居民身份证号（请勿用于非法目的！）</li><li>向微信小程序serve申请获取短信验证码（请勿用于刷tencent的短信or短信轰炸他人！）</li><li>基于IMAP协议轮询邮件列表</li><li>异步通知：确认用户的微信个人信息和小程序申请中的填写的个人信息匹配一致</li></ul><p>如果你喜欢我的文章，可以考虑<strong>buy me a cup of coffie</strong></p><p><img src="/img/vxregister/qr.jpg" alt="赞赏"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;在&lt;a href=&quot;https://deimo.github.io/2018/12/16/%E5%A6%82%E4%BD%95%E6%89%98%E7%AE%A1%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%B3%A8%E
      
    
    </summary>
    
      <category term="微信小程序" scheme="https://deimo.github.io/categories/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
    
      <category term="微信 原创教程" scheme="https://deimo.github.io/tags/%E5%BE%AE%E4%BF%A1-%E5%8E%9F%E5%88%9B%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>如何托管微信小程序注册(1)</title>
    <link href="https://deimo.github.io/2018/12/16/%E5%A6%82%E4%BD%95%E6%89%98%E7%AE%A1%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%B3%A8%E5%86%8C-1/"/>
    <id>https://deimo.github.io/2018/12/16/如何托管微信小程序注册-1/</id>
    <published>2018-12-16T12:52:43.000Z</published>
    <updated>2019-01-13T10:59:48.891Z</updated>
    
    <content type="html"><![CDATA[<p>随着微信小程序的功能越来越完善，小程序的应用也越来越广泛，需要小程序开发的个人与公司也越来越多~然而小程序的注册是一个比较麻烦的设计，里面涉及众多的表单交互，这次我会以自己的工作经验来帮助大家一步步实现小程序的注册托管。需要指出的是，托管小程序的注册平台并不意味着可以完全规避小程序的注册规则和限制。</p><p>本教程计划按照小程序注册的三步骤分为三部分，在阅读这一系列教程前，假设你和我一样是依托<strong>Python</strong>平台进行开发，而且对<strong>http</strong>协议已经有了一个比较深入的认识</p><p>在第一部分的教程当中我将教会大家破除以下3个难题：</p><ul><li>如何在不自建SMTP服务下拥有“无限”邮箱</li><li>如何识别小程序注册时的验证码</li><li>让微信像我们的指名的邮箱发送邮件</li></ul><p>下面我们就进入小程序注册的step1吧~如下图：</p><p><img src="/img/vxregister/1-1.jpg" alt="微信小程序注册首页"></p><h2 id="“无限“邮箱之道"><a href="#“无限“邮箱之道" class="headerlink" title="“无限“邮箱之道"></a>“无限“邮箱之道</h2><h6 id="整体介绍"><a href="#整体介绍" class="headerlink" title="整体介绍"></a>整体介绍</h6><p>小程序注册平台时的邮箱，将作为小程序的管理员账号登录。</p><h6 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h6><ol><li>一个可以使用的域名（不需要备案，因为不是建站）</li><li>一个可供使用并且具有代收功能的SMTP服务邮箱用户</li></ol><h6 id="分析过程"><a href="#分析过程" class="headerlink" title="分析过程"></a>分析过程</h6><p>经观察不难得出以下结论：</p><ol><li>不能让用户输入自己的邮箱，这将无法管理，也无法处理后续的邮箱激活步骤</li><li>小程序平台不care你的邮箱是否真实“存在”，只要能够按照他所规定的步骤激活（发送请求）即可</li></ol><p>想必你和我一样，会首先考虑在自己的服务器上搭建一个自定义域名的SMTP服务。当我提出这种解决方案时，马上被另一个更好的解决方案替代了：使用具有代收功能的SMTP服务代收该域名下的全部邮件即可。</p><p>当然搭建一个SMTP服务肯定也是可行的，但如果你和我一样没有搭建SMTP服务的经验，还是建议你使用代收邮件作为解决方案</p><h6 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h6><ol><li><p>首先请以自己的域名注册一个腾讯企业邮箱（如果你对其它平台的企业邮箱比较熟悉也没问题），<a href="https://exmail.qq.com/" target="_blank" rel="noopener">地址</a></p></li><li><p>开启邮件代收功能（需使用管理员登陆），操作步骤如下：</p><p><img src="/img/vxregister/1-3.jpg" alt="管理员登录"></p><p><img src="/img/vxregister/1-4.jpg" alt="开启邮件转移"></p></li></ol><p>在完成上述两步后，那么恭喜你，你的“无限”邮箱就已经具备啦，下面开始处理另外的2个问题</p><h2 id="“焰狰马“识别之法"><a href="#“焰狰马“识别之法" class="headerlink" title="“焰狰马“识别之法"></a>“焰狰马“识别之法</h2><h6 id="整体介绍-1"><a href="#整体介绍-1" class="headerlink" title="整体介绍"></a>整体介绍</h6><p>小程序注册时的验证码其实不算复杂，稍稍对http协议或爬虫技术有所了解的人是很容易破解的。我们这里的破解手段就是俗称的OCR（光学文字识别），需要说明的是肯定不是我们自己去做图像处理，然后OCR识别，这里我推荐大家使用<strong>斐斐打码平台</strong>，易接入，识别率很高，价格也很实在（绝无广告水分！）。在之前公司的爬虫的项目中，我所使用的是阿里的OCR，不过不知出于何种原因各大云服务商的验证码服务全部下架…只剩普通图片文字服务…值得一提的是，小程序验证码识别的原理，也可同样运用于其它简单验证码网站上~</p><h6 id="前提-1"><a href="#前提-1" class="headerlink" title="前提"></a>前提</h6><ol><li>熟悉http协议</li><li>斐斐打码平台账号</li></ol><h6 id="分析过程-1"><a href="#分析过程-1" class="headerlink" title="分析过程"></a>分析过程</h6><ol><li>验证码请求过程</li></ol><p>   透过chrome自带的抓包工具，我们不难发现，验证码的获取接口如下：</p><p>   <img src="/img/vxregister/1-2.jpg" alt="验证码抓包"></p><p>   于是我们很自然会在另一个窗口中访问<a href="https://mp.weixin.qq.com/wxopen/waverifycode?r=1545052181759" target="_blank" rel="noopener">https://mp.weixin.qq.com/wxopen/waverifycode?r=1545052181759</a></p><p>   但是当你在另外一个浏览器中打开时会发现得到的验证码和之前的并不一样…起初我以为是参数“r”不同的原因所导致，后来证实并非如此，小程序的注册平台其实是依托<strong>session</strong>机制来保证注册时的邮箱和验证码处于同一的会话之下，而这个所谓的”r”参数个人猜测是以微秒为单位的UNIX时间戳，拿去一些工具网站验证之后证明我个人的猜想是正确的。</p><p>   那么现在的问题是如何保证我获取验证码时的请求和我打开<a href="https://mp.weixin.qq.com/wxopen/waregister?action=step1" target="_blank" rel="noopener">首页</a>时的请求在同一会话下呢~想必细心的你其实已经发现我上图中红色方框圈住的<strong>set-cookie</strong>部分吧~是的，正是如此，小程序的注册平台正是依托<strong>sig</strong>这个cookie值来验证你填写邮箱等表单时和你获取验证码的请求是否在统一会话之下。好在Python的requests库封装了Session API，可以令你很方便的操作session对象。相关核心示例代码:</p>   <figure class="highlight python"><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">s = requests.Session()</span><br><span class="line">url = <span class="string">'https://mp.weixin.qq.com/wxopen/waregister?action=step1'</span></span><br><span class="line">r = s.get(url, headers=HEADERS)</span><br><span class="line"><span class="keyword">if</span> r.status_code != <span class="number">200</span>:</span><br><span class="line">    <span class="keyword">raise</span></span><br><span class="line">sig, captcha = _user_ocr(s)</span><br></pre></td></tr></table></figure><ol start="2"><li><p>验证码的识别过程</p><p>在了解了验证码的请求过程之后，接下来我们就可以进行识别了，我的解决方案是：将验证码图片直接下载下来，然后交由第三方API（斐斐打码）去识别即可。这一过程相对容易，并没有太多可细说的，有关斐斐打码平台的注册和接入不是本教程重点，感兴趣的童鞋可以走这里——<a href="http://www.fateadm.com/" target="_blank" rel="noopener">传送门</a></p><p>相关接入代码</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_user_ocr</span><span class="params">(session)</span>:</span></span><br><span class="line">    t = time.time()</span><br><span class="line">    r = str(int(t))</span><br><span class="line">    code_url = <span class="string">'https://mp.weixin.qq.com/wxopen/waverifycode'</span> + <span class="string">'?r='</span> + r</span><br><span class="line">    code_rsp = session.get(code_url, headers=HEADERS)</span><br><span class="line">    code_content = code_rsp.content</span><br><span class="line">    sig = code_rsp.cookies[<span class="string">'sig'</span>]</span><br><span class="line">    print(<span class="string">'sig: '</span>, sig)</span><br><span class="line"></span><br><span class="line">    filename = r + <span class="string">'.png'</span></span><br><span class="line">    <span class="keyword">with</span> open(filename, <span class="string">'wb'</span>) <span class="keyword">as</span> f:</span><br><span class="line">        f.write(code_content)</span><br><span class="line"></span><br><span class="line">    sign, asign = _cacu_sign(r)</span><br><span class="line">    data = &#123;<span class="string">'user_id'</span>: PD_ID, <span class="string">'timestamp'</span>: r, <span class="string">'sign'</span>: sign, <span class="string">'predict_type'</span>: <span class="string">'30400'</span>,</span><br><span class="line">            <span class="string">'asign'</span>: asign, <span class="string">'up_type'</span>: <span class="string">'mt'</span>&#125;</span><br><span class="line">    files = &#123;<span class="string">'img_data'</span>: (<span class="string">'img_data'</span>, code_content)&#125;</span><br><span class="line">    url = <span class="string">'http://pred.fateadm.com/api/capreg'</span></span><br><span class="line">    rsp = requests.post(url, data=data, files=files)</span><br><span class="line">    res = rsp.json()</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        captcha = json.loads(res[<span class="string">'RspData'</span>])</span><br><span class="line">        captcha = captcha[<span class="string">'result'</span>]</span><br><span class="line">        print(<span class="string">'验证码：'</span>, captcha)</span><br><span class="line">    <span class="keyword">except</span> Exception:</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">None</span>, <span class="keyword">None</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        os.remove(filename)</span><br><span class="line">        <span class="keyword">return</span> sig, captcha</span><br></pre></td></tr></table></figure></li></ol><h2 id="发送邮件到我们指定的邮箱"><a href="#发送邮件到我们指定的邮箱" class="headerlink" title="发送邮件到我们指定的邮箱"></a>发送邮件到我们指定的邮箱</h2><h6 id="整体介绍-2"><a href="#整体介绍-2" class="headerlink" title="整体介绍"></a>整体介绍</h6><p>在以上两部分完成之后，最后一步令小程序发送邮件其实就是抓取一个表达提交接口，想必这对各位同鞋来说不是什么难事，这里也没有太多的内容需要分析，需要的注意的地方仍然是两点：</p><ol><li>仍然要保证你最后提交数据的请求和之前的请求在同一session之下</li><li>需要更改requests的<strong>User-Agent</strong>头信息来模拟浏览器，否则请求会被微信直接拦截</li><li>需要修改<strong>Referer</strong>请求头信息，越过微信的referer防盗链检查</li></ol><p>相关代码:</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_send_auth_request</span><span class="params">(session, sig, verifycode)</span>:</span></span><br><span class="line">    HEADERS[<span class="string">'Referer'</span>] = <span class="string">'https://mp.weixin.qq.com/wxopen/waregister?action=step1'</span></span><br><span class="line">    url = <span class="string">'https://mp.weixin.qq.com/wxopen/waregister'</span></span><br><span class="line">    data = &#123;<span class="string">'token'</span>: <span class="string">''</span>, <span class="string">'lang'</span>: <span class="string">'zh_CN'</span>, <span class="string">'f'</span>: <span class="string">'json'</span>, <span class="string">'ajax'</span>: <span class="number">1</span>, <span class="string">'verifycode'</span>: verifycode,</span><br><span class="line">            <span class="string">'random'</span>: random.random(), <span class="string">'regtype'</span>: <span class="number">2</span>, <span class="string">'email'</span>: <span class="string">'ssaaxx@lvpjob.club'</span>,</span><br><span class="line">            <span class="string">'pwd'</span>: <span class="string">'35096a66eb1884db71501a2691680baf'</span>&#125;</span><br><span class="line">    print(<span class="string">'data: '</span>, data)</span><br><span class="line">    r = session.post(url=url, data=data, headers=HEADERS)</span><br><span class="line">    print(<span class="string">'r: '</span>, r.json())</span><br></pre></td></tr></table></figure><p>测试结果如下：</p><p><img src="/img/vxregister/1-5.jpg" alt="测试结果"></p><p>收到的邮件如图:</p><p><img src="/img/vxregister/1-6.jpg" alt="收到邮件"></p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>好啦，今天的第一部分教程就到此为止啦，下一次我将为大家带来有关IMAP协议的介绍，读取邮件并激活，敬请期待。如果你喜欢我的文章，可以扫一下这个二维码给我一点鼓励</p><p><img src="/img/vxregister/qr.jpg" alt="赞赏"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;随着微信小程序的功能越来越完善，小程序的应用也越来越广泛，需要小程序开发的个人与公司也越来越多~然而小程序的注册是一个比较麻烦的设计，里面涉及众多的表单交互，这次我会以自己的工作经验来帮助大家一步步实现小程序的注册托管。需要指出的是，托管小程序的注册平台并不意味着可以完全规
      
    
    </summary>
    
      <category term="微信小程序" scheme="https://deimo.github.io/categories/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
    
      <category term="微信 原创教程" scheme="https://deimo.github.io/tags/%E5%BE%AE%E4%BF%A1-%E5%8E%9F%E5%88%9B%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>怎样使用Hexo生成自己的github主页</title>
    <link href="https://deimo.github.io/2018/09/26/%E6%80%8E%E6%A0%B7%E4%BD%BF%E7%94%A8hexo%E7%94%9F%E6%88%90%E8%87%AA%E5%B7%B1%E7%9A%84githup%E4%B8%BB%E9%A1%B5/"/>
    <id>https://deimo.github.io/2018/09/26/怎样使用hexo生成自己的githup主页/</id>
    <published>2018-09-26T09:11:03.303Z</published>
    <updated>2018-09-26T09:11:03.304Z</updated>
    
    <content type="html"><![CDATA[<p>欢迎使用<a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>. 好吧，我还是保留原文这段描述吧～接下来就是干货了，需要注意的事本教程环境是在Mac os下完成</p><h2 id="配置环境"><a href="#配置环境" class="headerlink" title="配置环境"></a>配置环境</h2><blockquote><p>安装有node.js  和 npm～（你去node官网下载时，肯定都会带有npm吧）</p></blockquote><blockquote><p>安装有Git</p></blockquote><blockquote><p>一个github账号</p></blockquote><p>上述工具的安装和使用我不是本教程的重点，如果你对于它们都很陌生，那本教程就不大适合于你了🙁</p><h2 id="生成本地Hexo站点"><a href="#生成本地Hexo站点" class="headerlink" title="生成本地Hexo站点"></a>生成本地Hexo站点</h2><p>在你安装好了node.js，并且修改了它的镜像源后，就可以真正开始接下来的操作了。（还没修改的赶紧去改改哈，天朝网络原因…我就不解释了）</p><p>1.安装Hexo：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-cli -g</span><br></pre></td></tr></table></figure></p><p>2.生成一个Hexo站点<br><figure class="highlight plain"><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">hexo init [website-name] 名字任意， 这里我就使用的是blog， 完整命令是</span><br><span class="line">hexo init blog</span><br></pre></td></tr></table></figure></p><p>你会发现在你的目录下多了这些文件…项目中各文件夹的定义情查阅官网doc，这里选几个重要描述<img src="/img/hexo/tree.png" alt="tree"></p><table><thead><tr><th>目录名或文件名</th><th style="text-align:center">作用</th></tr></thead><tbody><tr><td>source</td><td style="text-align:center">用于存放你所生成各种原始资源，你如你的帖子</td></tr><tr><td>themes</td><td style="text-align:center">存放你的站点主题及主题配置文件</td></tr><tr><td>_config.yml</td><td style="text-align:center">站点配置文件，部署，安装插件，修改样式时会用</td></tr></tbody></table><p>3.安装Hexo所需依赖<br><figure class="highlight plain"><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 blog (你之前生成站点名)</span><br><span class="line">然后执行 npm install</span><br></pre></td></tr></table></figure></p><p>4.本地预览调试<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo s --debug</span><br></pre></td></tr></table></figure></p><p>之后你就可以代开浏览器访问<strong>localhost:4000</strong>地址预览博客页面，下面帖上一张图吧(已经很漂亮吧，如果你还不满意，后续会介绍如何修改主题)<img src="/img/hexo/preview.png" alt="preview"></p><h2 id="部署到Githup"><a href="#部署到Githup" class="headerlink" title="部署到Githup"></a>部署到Githup</h2><p>部署到github之前你需要完成以下几个工作</p><p>1.在github上新建一个Repository，仓库名必须为【your_user_name.github.io】写法固定，这样github才会为你提供域名服务</p><p>2.安装hexo与github相关联的package</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure><p>3.修改站点配置文件，上文提到过的 <strong>_config.yml</strong>文件(位置在最下面)，格式如下</p><figure class="highlight plain"><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">deploy:</span><br><span class="line">  type: git</span><br><span class="line">  repository: https://github.com/deimo/deimo.github.io.git(我的)</span><br><span class="line">  branch: master</span><br></pre></td></tr></table></figure><p>4.生成静态页面</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo generate （或hexo g）</span><br></pre></td></tr></table></figure><p>如果有报错发生，执行下列操作</p><figure class="highlight plain"><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">npm install hexo-renderer-ejs --save</span><br><span class="line">npm install hexo-renderer-stylus --save</span><br><span class="line">npm install hexo-renderer-marked --save</span><br></pre></td></tr></table></figure><p>5.发布到你的github</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo deploy</span><br></pre></td></tr></table></figure><p>此时也许会让你输入你的github用户名和密码…当然如果你使用ssh key，那应该不会要求你的用户名和密码</p><p>此时就可以在浏览器中访问:<strong><a href="https://deimo.github.io">https://deimo.github.io</a></strong>，如果有内容显示～那么恭喜你👏</p><p>至此～你的github主页就已经搭建好了～让我们庆祝一下🎁</p><h2 id="发布新的博文"><a href="#发布新的博文" class="headerlink" title="发布新的博文"></a>发布新的博文</h2><p>好吧，这里我就偷懒了…我就直接用生成站点时的原文内容好了…</p><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a><strong>Create</strong> a new post</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">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</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">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</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">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</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">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="noopener">Deployment</a></p><p>所以每次在你的博文写完后，你的部署操作大致是</p><figure class="highlight plain"><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">hexo clean</span><br><span class="line">hexo generate</span><br><span class="line">hexo deploy</span><br></pre></td></tr></table></figure><p>一些其它常用的hexo命令参考，更多请参见Hexo官网</p><figure class="highlight plain"><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">hexo new &quot;postName&quot; #新建文章</span><br><span class="line">hexo new page &quot;pageName&quot; #新建页面</span><br><span class="line">hexo generate #生成静态页面至public目录</span><br><span class="line">hexo server #开启预览访问端口（默认端口4000，&apos;ctrl + c&apos;关闭server）</span><br><span class="line">hexo deploy #将.deploy目录部署到GitHub</span><br><span class="line">hexo help  #查看帮助</span><br><span class="line">hexo version  #查看Hexo的版本</span><br></pre></td></tr></table></figure><h2 id="其它参考"><a href="#其它参考" class="headerlink" title="其它参考"></a>其它参考</h2><p>1.关于主题<a href="https://github.com/hexojs/hexo/wiki/Themes" target="_blank" rel="noopener">themes</a> 当然你也可以知乎上搜索更多关于主题的信息</p><p>2.Hexo官网<a href="https://hexo.io/" target="_blank" rel="noopener">hexo</a></p><p>3.🙏感谢这位大佬的blog，才让我有了此教程,<a href="http://baixin.io/2015/08/HEXO%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2" target="_blank" rel="noopener">传送门</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;欢迎使用&lt;a href=&quot;https://hexo.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Hexo&lt;/a&gt;! This is your very first post. Check &lt;a href=&quot;https://hexo.io/docs
      
    
    </summary>
    
      <category term="Hexo" scheme="https://deimo.github.io/categories/Hexo/"/>
    
    
      <category term="教程" scheme="https://deimo.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>glances实现服务器资源监控</title>
    <link href="https://deimo.github.io/2018/08/05/glances%E5%AE%9E%E7%8E%B0%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%B5%84%E6%BA%90%E7%9B%91%E6%8E%A7/"/>
    <id>https://deimo.github.io/2018/08/05/glances实现服务器资源监控/</id>
    <published>2018-08-05T14:26:42.000Z</published>
    <updated>2018-09-26T09:12:01.995Z</updated>
    
    <content type="html"><![CDATA[<h2 id="glances实现服务器资源监控"><a href="#glances实现服务器资源监控" class="headerlink" title="glances实现服务器资源监控"></a>glances实现服务器资源监控</h2><p>在这篇文章中我将会传授使用glances10min搭建一个服务器性能实时监控系统的车技~！</p><h4 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h4><ol><li>什么是glances</li></ol><p>glances是一个由python实现的类似<code>top</code>或<code>htop</code>的服务器性能监控工具，区别在于glances提供的信息和可使用的参数更多，具体其中包括信息：</p><ul><li>CPU 使用率</li><li>内存使用情况</li><li>内核统计信息和运行队列信息</li><li>磁盘 I/O 速度、传输和读/写比率</li><li>文件系统中的可用空间</li><li>磁盘适配器</li><li>网络 I/O 速度、传输和读/写比率</li><li>页面空间和页面速度</li><li>消耗资源最多的进程</li><li>计算机信息和系统资源</li></ul><ol start="2"><li><p>安装glances</p><p>建议安装py2.7.x下的glances，原因是由于在搭建glances的web server环境时py3下的glances的配套bottle框架貌似没有完全兼容（我在我的mac测试未通过）出现字符编码错误</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">pip install glances</span><br></pre></td></tr></table></figure></li></ol><h4 id="在命令行中使用glances"><a href="#在命令行中使用glances" class="headerlink" title="在命令行中使用glances"></a>在命令行中使用glances</h4><p>安装完成后直接在终端下执行命令</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">glances</span><br></pre></td></tr></table></figure><p>如果出现类似这样的画面，那么恭喜你，你的glances工具就算是安装好了</p><p><img src="/img/glances/1.jpg" alt="glances运行图"></p><p>当然glances的用法远不止如此，更多的glances用法及命令行参数释义，请查阅官方的<a href="http://glances.readthedocs.io/en/latest/" target="_blank" rel="noopener">doc</a></p><h4 id="搭建glances实时监控web服务"><a href="#搭建glances实时监控web服务" class="headerlink" title="搭建glances实时监控web服务"></a>搭建glances实时监控web服务</h4><ol><li><p>安装bottle</p><p>glances的web服务需要和bottle框架配合才能使用，所以需要先安装bottle，同样是建议安装py2.7.x版本</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">pip install bottle</span><br></pre></td></tr></table></figure></li><li><p>启动glances服务</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">glances -w</span><br></pre></td></tr></table></figure><p>此时终端上就会有相应的输出，你可以根据终端提示在浏览器端进行访问，页面如下</p><p><img src="/img/glances/2.jpg" alt="web端的运行截图"></p></li></ol><h4 id="服务器上的简单部署"><a href="#服务器上的简单部署" class="headerlink" title="服务器上的简单部署"></a>服务器上的简单部署</h4><p>由于bottle也是符合WSGI规范的web应用，所以部署思路和其它WSGI应用如Django，Flask相同：使用nginx作为反向代理将请求转发至glances服务端口即可</p><ol><li><p>部署前的工作</p><ul><li><p>上文说得全部</p></li><li><p>nginx</p></li><li>screen</li></ul></li><li><p>开始部署</p><p>i. 进入screen，然后开启glances服务</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">screen -S glances<span class="comment"># 新建一个名叫一个[glances]的任务session</span></span><br><span class="line">glances -w    <span class="comment">#  开启glances服务</span></span><br></pre></td></tr></table></figure><p>然后Ctrl + a, Ctrl + d 将glances任务Detached掉</p><p>ii. 配置nginx</p><p>1）我们肯定不希望自己服务器的信息被别人知道，所以要使用nginx提供的认证功能</p><p>我以自己的实际部署情况为例</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"># 请使用带有root身份的用户登录</span></span><br><span class="line">➜  <span class="built_in">cd</span> /etc/nginx/passwd<span class="comment"># 进入自己密码文件所在目录</span></span><br><span class="line">➜  htpasswd -c ./auth.txt <span class="built_in">test</span> <span class="comment"># 新建一个test用户</span></span><br><span class="line">New password:<span class="comment"># 输入密码</span></span><br><span class="line">Re-type new password:</span><br><span class="line">Adding password <span class="keyword">for</span> user <span class="built_in">test</span></span><br><span class="line">➜  cat auth.txt <span class="comment"># 查看文件信息</span></span><br><span class="line"><span class="built_in">test</span>:<span class="variable">$apr1</span><span class="variable">$xQWpR2dM</span><span class="variable">$228RMBelnZn</span>.8o3GPE7E/1</span><br></pre></td></tr></table></figure><p>2）编写nginx配置文件</p><figure class="highlight nginx"><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"><span class="section">server</span> &#123;</span><br><span class="line">    <span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line">    <span class="attribute">server_name</span> [your domain];</span><br><span class="line">    <span class="attribute">auth_basic</span> <span class="string">"Please input password"</span>;</span><br><span class="line">    <span class="attribute">auth_basic_user_file</span> /etc/nginx/passwd/auth.txt;</span><br><span class="line">    <span class="attribute">location</span> / &#123;</span><br><span class="line">        <span class="comment">#include uwsgi_params;</span></span><br><span class="line">        <span class="attribute">proxy_pass</span> http://127.0.0.1:61208;</span><br><span class="line">        <span class="attribute">proxy_set_header</span>    Host            <span class="variable">$host</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span>    X-Real-IP       <span class="variable">$remote_addr</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span>    X-Forwarded-for <span class="variable">$remote_addr</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>3）检查nginx语法然后重启nginx</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">sudo nginx -t </span><br><span class="line">sudo systemctl restart nginx</span><br></pre></td></tr></table></figure></li><li><p>测试</p><p>接下来你就可以测试你的nginx认证和性能监控是否正常运行了</p><p><img src="/img/glances/3.jpg" alt="nginx认证"></p><p>认证成功之后就可以看到之前类似本机的web测试页面了，如果出错了，你可以进入screen查看报错原因</p><p>怎么样，有没有觉得很easy呢~😋😋😋</p></li></ol><h4 id="参考列表"><a href="#参考列表" class="headerlink" title="参考列表"></a>参考列表</h4><ol><li><a href="https://www.ibm.com/developerworks/cn/linux/1304_caoyq_glances/" target="_blank" rel="noopener">使用资源监控工具 glances</a></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;glances实现服务器资源监控&quot;&gt;&lt;a href=&quot;#glances实现服务器资源监控&quot; class=&quot;headerlink&quot; title=&quot;glances实现服务器资源监控&quot;&gt;&lt;/a&gt;glances实现服务器资源监控&lt;/h2&gt;&lt;p&gt;在这篇文章中我将会传授使用g
      
    
    </summary>
    
      <category term="后端" scheme="https://deimo.github.io/categories/%E5%90%8E%E7%AB%AF/"/>
    
    
      <category term="运维，性能监控" scheme="https://deimo.github.io/tags/%E8%BF%90%E7%BB%B4%EF%BC%8C%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7/"/>
    
  </entry>
  
  <entry>
    <title>CentOS7+上安装Docker CE</title>
    <link href="https://deimo.github.io/2018/08/03/CentOS7-%E4%B8%8A%E5%AE%89%E8%A3%85Docker-CE/"/>
    <id>https://deimo.github.io/2018/08/03/CentOS7-上安装Docker-CE/</id>
    <published>2018-08-03T06:35:55.000Z</published>
    <updated>2018-09-26T09:10:28.057Z</updated>
    
    <content type="html"><![CDATA[<h2 id="CentOS-安装-Docker-CE-转载"><a href="#CentOS-安装-Docker-CE-转载" class="headerlink" title="CentOS 安装 Docker CE [转载]"></a>CentOS 安装 Docker CE <strong>[转载]</strong></h2><p>本文转载自博主<a href="https://www.cnblogs.com/yu-hailong/p/7629120.html" target="_blank" rel="noopener">【开始战斗】</a>的文章，并在其原文之上略有修改，转载时请注明原作者 <strong>开始战斗</strong>出处</p><h4 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h4><h6 id="系统要求"><a href="#系统要求" class="headerlink" title="系统要求"></a>系统要求</h6><p>Docker CE 支持 64 位版本 CentOS 7，并且要求内核版本不低于 3.10。 CentOS 7 满足最低内核的要求，但由于内核版本比较低，部分功能（如 <code>overlay2</code> 存储层驱动）无法使用，并且部分功能可能不太稳定。</p><h6 id="卸载旧版本"><a href="#卸载旧版本" class="headerlink" title="卸载旧版本"></a>卸载旧版本</h6><p>旧版本的 Docker 称为 <code>docker</code> 或者 <code>docker-engine</code>，使用以下命令卸载旧版本：</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">sudo yum remove docker \</span><br><span class="line">                  docker-common \</span><br><span class="line">                  docker-selinux \</span><br><span class="line">                  docker-engine</span><br></pre></td></tr></table></figure><h4 id="使用yum-源安装"><a href="#使用yum-源安装" class="headerlink" title="使用yum 源安装"></a>使用yum 源安装</h4><p>执行以下命令安装依赖包</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">sudo yum install -y yum-utils device-mapper-persistent-data lvm2</span><br></pre></td></tr></table></figure><p>鉴于国内网络问题，强烈建议使用国内源，下面先介绍国内源的使用。</p><h6 id="国内源"><a href="#国内源" class="headerlink" title="国内源"></a>国内源</h6><p>执行下面的命令添加 <code>yum</code> 软件源：</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">sudo yum-config-manager \</span><br><span class="line">     --add-repo \</span><br><span class="line">     https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo</span><br></pre></td></tr></table></figure><p>以上命令会添加稳定版本的 Docker CE yum 源。从 Docker 17.06 开始，edge test 版本的 yum 源也会包含稳定版本的 Docker CE</p><h6 id="官方源"><a href="#官方源" class="headerlink" title="官方源"></a>官方源</h6><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">sudo yum-config-manager \</span><br><span class="line">--add-repo \</span><br><span class="line">https://download.docker.com/linux/centos/docker-ce.repo</span><br></pre></td></tr></table></figure><p>如果需要最新版本的 Docker CE 请使用以下命令：</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">sudo yum-config-manager --<span class="built_in">enable</span> docker-ce-edge</span><br><span class="line">sudo yum-config-manager --<span class="built_in">enable</span> docker-ce-test</span><br></pre></td></tr></table></figure><h4 id="安装-Docker-CE"><a href="#安装-Docker-CE" class="headerlink" title="安装 Docker CE"></a>安装 Docker CE</h4><p>更新 <code>yum</code> 软件源缓存，并安装 <code>docker-ce</code>。 </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">sudo yum makecache fast</span><br><span class="line">sudo yum install docker-ce</span><br></pre></td></tr></table></figure><h4 id="启动-Docker-CE"><a href="#启动-Docker-CE" class="headerlink" title="启动 Docker CE"></a>启动 Docker CE</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">sudo systemctl <span class="built_in">enable</span> docker</span><br><span class="line">sudo systemctl start docker</span><br></pre></td></tr></table></figure><h4 id="建立-docker-用户组"><a href="#建立-docker-用户组" class="headerlink" title="建立 docker 用户组"></a>建立 docker 用户组</h4><p>默认情况下，<code>docker</code> 命令会使用 <a href="https://en.wikipedia.org/wiki/Unix_domain_socket" target="_blank" rel="noopener">Unix socket</a> 与 Docker 引擎通讯。而只有 <code>root</code> 用户和 <code>docker</code> 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑，一般 Linux 系统上不会直接使用 <code>root</code> 用户。因此，更好地做法是将需要使用 <code>docker</code> 的用户加入 <code>docker</code> 用户组</p><p>建立<code>docker</code>组：</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">sudo groupadd docker</span><br></pre></td></tr></table></figure><p>将当前用户加入 <code>docker</code> 组</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">sudo usermod -aG docker <span class="variable">$USER</span></span><br></pre></td></tr></table></figure><h4 id="镜像加速"><a href="#镜像加速" class="headerlink" title="镜像加速"></a>镜像加速</h4><p>鉴于国内网络问题，后续拉取 Docker 镜像十分缓慢，强烈建议安装 Docker 之后配置</p><p>配置方法如下（节选自gitbook<a href="https://yeasy.gitbooks.io/docker_practice/content/install/mirror.html" target="_blank" rel="noopener">《Docker——从入门到实践》</a>）：</p><blockquote><p>Ubuntu 16.04+、Debian 8+、CentOS 7</p><p>对于使用 <a href="https://www.freedesktop.org/wiki/Software/systemd/" target="_blank" rel="noopener">systemd</a> 的系统，请在 <code>/etc/docker/daemon.json</code> 中写入如下内容（如果文件不存在请新建该文件)</p></blockquote><figure class="highlight json"><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">&#123;</span><br><span class="line">  <span class="attr">"registry-mirrors"</span>: [</span><br><span class="line">    <span class="string">"https://registry.docker-cn.com"</span></span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>注意，一定要保证该文件符合json规范，否则Docker将不能启动</p></blockquote><p>之后重新启动服务</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">sudo systemctl daemon-reload</span><br><span class="line">sudo systemctl restart docker</span><br></pre></td></tr></table></figure><p>检查加速器是否生效</p><p>配置加速器之后，如果拉取镜像仍然十分缓慢，请手动检查加速器配置是否生效，在命令行执行 <code>docker info</code>，如果从结果中看到了如下内容，说明配置成功</p><figure class="highlight plain"><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">Registry Mirrors:</span><br><span class="line"> https://registry.docker-cn.com/</span><br></pre></td></tr></table></figure><p>更多docker学习相关请查阅<a href="https://yeasy.gitbooks.io/docker_practice/content/install/mirror.html" target="_blank" rel="noopener">这里</a></p><h4 id="本文参考"><a href="#本文参考" class="headerlink" title="本文参考"></a>本文参考</h4><ol><li>本文转载自博主<a href="https://www.cnblogs.com/yu-hailong/p/7629120.html" target="_blank" rel="noopener">【开始战斗】</a>，如有侵权，请联系本人删除</li><li>部分内容参考GitBook<a href="https://yeasy.gitbooks.io/docker_practice/" target="_blank" rel="noopener">《从入门到实践》</a></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;CentOS-安装-Docker-CE-转载&quot;&gt;&lt;a href=&quot;#CentOS-安装-Docker-CE-转载&quot; class=&quot;headerlink&quot; title=&quot;CentOS 安装 Docker CE [转载]&quot;&gt;&lt;/a&gt;CentOS 安装 Docker C
      
    
    </summary>
    
      <category term="Docker CE" scheme="https://deimo.github.io/categories/Docker-CE/"/>
    
    
      <category term="容器化技术，转载" scheme="https://deimo.github.io/tags/%E5%AE%B9%E5%99%A8%E5%8C%96%E6%8A%80%E6%9C%AF%EF%BC%8C%E8%BD%AC%E8%BD%BD/"/>
    
  </entry>
  
  <entry>
    <title>与后端的爱恨情仇</title>
    <link href="https://deimo.github.io/2018/04/16/%E4%B8%8E%E5%90%8E%E7%AB%AF%E7%9A%84%E7%88%B1%E6%81%A8%E6%83%85%E4%BB%87/"/>
    <id>https://deimo.github.io/2018/04/16/与后端的爱恨情仇/</id>
    <published>2018-04-15T16:11:54.000Z</published>
    <updated>2019-06-08T05:03:54.679Z</updated>
    
    <content type="html"><![CDATA[<h2 id="与后端的爱恨情仇"><a href="#与后端的爱恨情仇" class="headerlink" title="与后端的爱恨情仇"></a>与后端的爱恨情仇</h2><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>接上篇——《Android开发从小白到入门再到放弃》</p><p>从客户端到后端，从JAVA到Python…往后又该是何去何从…程序设计真的超有意思呢~总会冒出一些让你不明觉厉的新名词~总会有些你从未接触的新鲜玩具</p><h4 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h4><p>印象中第一次接触后端…是我在大连实习时~那个时候做了一个小屁小屁（20多个API接口，7张表的样子）的android项目…不过麻雀虽小，但也五脏俱全~</p><p>第一次嘛…总是痛并快乐着😈…想到我第一次真正意义上与后端对接时（以前使用Bmob API，下载SDK接入直接在本地操作数据表），那真是令人倍感沮丧…就简简单单仨儿接口…却没有一次成功…当然啦~毫无疑问…全是我客户端的问题…💀自然而然…内心产生了一阵阵劝退的声音…不过好在…有一群暖心的朋友…哎，我直接贴图吧</p><p><img src="/img/life/chen.jpg" alt="朋友的鼓励"></p><p><img src="/img/life/group.jpg" alt="大佬们的鼓励"></p><p>那个时候我的连<strong>POST</strong>请求都没能整明白…回到学校后倍受打击…质疑自己是不是不适合干这行…不过朋友，前辈们的鼓励和经验又让我重拾信心~</p><p>从哪跌倒，自然就得从哪爬起嘛～那个时候就暗自下定决心，自己也一定要会后端~（大概那个时候，我所理解的后端…也就是简单的API吧…）实习时公司使用的后端技术是php技术栈，据说是世界最好的语言～😵</p><p>哇，有点害怕呀…难道我也要去学世界最好的语言并且是宇宙第一的Php～？反复推敲对比后，终于要在java，python，php中做抉择了…其实碍于当时自己的眼界…也仅限于此…老实说，如果那时的我早日体验到ruby和node.js的优雅及强大，也说不定转到ruby或者node去了呢～当然啦，选择python，不仅仅是因为它可以作为一门优秀的服务端脚本语言，更重要的是它对数据处理的强大支持以及在未来人工智能的无限潜力（那个时候是2016年）～</p><p>正是如此，我才敲定了python～不过万万没想到的是…日后毕业入职的第一份job将会是Python后端开发工程师～不知是命中注定还是机缘巧合又或许是我的强烈坚持和洞察先机…我毕业之时已是2017年…移动客户端领域已基本成熟~移动客户端领域…貌似很难再对其传统业务做进一步的深化与扩展了…正是基于此种论断…所以待我去到羊城时并没有投递有关Android开发工程师的岗位，而是选择Python！那个时候…我所有Python的学习及编写代码的时间加起来…貌似还不足一个月…😢</p><p>有时总会慨叹自己脸实在黑得口怕，在选择和机遇上貌似总是不尽人意…第一家公司…至今还拖欠着我三个月的实习工资…～不过倒也无所谓了，无论如何我都不后悔，从哲学角度来看这世上就从没白费的努力～这样的思维和态度很符合一位科技工作者，而非一个洞察商机的企业家，不是嘛？～难道你能说在牛顿万有引力横空出世以前，那些拿着望远镜看了一辈子星星的科技工作者就是zz？从这点来看，拒绝为互联网申请专利的“互联网之父”蒂姆比起微软帝国的老大盖茨更加值得令人尊敬和敬佩～一个是科学家～一个是企业家～然而事实是…知道盖茨的朋友远比知道蒂姆的多…呜呜呜呜…心塞塞～貌似学术明星在大众知名度里总是无法同娱乐明星相比较吧…不过这也是显而易见的，两者的群众基础本就不在同一数量级上…</p><p>回过头继续说说为什么说说选择后端(ps：这里木有轻视各位前端朋友的意思嗷，纯粹是个人心路旅程及个人观点～)对于任何一款成功的互联网产品而言…随着自身的发展，累计的用户和数据逐渐增多…有三个问题会让你如坐针毡：</p><ol><li>我该怎样存储更大的数据量？</li><li>我该怎样更加快速地响应用户的操作？</li><li>我该怎样在同一时刻为更多的用户提供服务(并发)？简而言之就是“更大”，“更快”，“更高”～！</li></ol><p>任何一家互联网企业在自己的发展和产品迭代中…其本质无非也是上述问题在不同阶段的解决方案～我们知道早期计算机的主要任务是为了处理数值计算，而随着信息量的迅猛发展，现在计算机的任务更多的是处理的“字符串”，所以如果你有一款基础软件(如操作系统，数据库)…它能将字符串处理的速度提高哪怕万分之一，那么所产生的经济价值和世界影响难以估量…也许今后计算机的任务将是直接处理富媒体信息，好期待那一日的到来…～😍</p><p>上述3个问题的产生，首先在学术界催生了数据存储的NoSQL理论，之前的RDBMS(关系型数据库管理系统)理论在更大的数据量前显得力不从心…然后拉开了各个互联网企业之间的距离，最后是带来了一系列的财富和机遇，大数据、数据挖掘、分布式处理，各种概念再次响起并深入人心～基于大数据分析的精准网络个人营销正逐渐成为现实…在这个移动互联网浪潮的发展已过去将半的背景下，“大数据+”概念貌似呼之欲出😎（不过…怎么比…貌似也比不过…势头更猛的…“人工智能”和“区块链”吧😏…）~</p><p>最后我想说的是…对于一款未遇到性能瓶颈的产品…任何一名后端工程师都足以胜任，但是在BAT下，那些看似简单的功能和操作…却毫无例外地凝聚着全球无数最顶尖工程师们的智慧和心血…所以拜托各位圈外的亲们…你们在享受程序员构建的自动化世界时，不要再说：不就是在电脑键盘上敲敲嘛…怎么薪资比我高那么多…我特么在xx地方做xx那么辛苦才挣那么点儿…</p><p>不过呢～我是个例外～因为，我可能是个假程序员～啊，哈哈哈哈哈～😭😭😭</p><h4 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h4><p>如果你喜欢我的文章，请扫描以下二维码，加我微信，并给我小额赞赏，如果没有特殊声明，赞赏…将用于改善我的个人生活~比如：零食等~😁</p><p><img src="/img/life/pay.jpeg" alt="二维码"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;与后端的爱恨情仇&quot;&gt;&lt;a href=&quot;#与后端的爱恨情仇&quot; class=&quot;headerlink&quot; title=&quot;与后端的爱恨情仇&quot;&gt;&lt;/a&gt;与后端的爱恨情仇&lt;/h2&gt;&lt;h4 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; titl
      
    
    </summary>
    
      <category term="个人随笔" scheme="https://deimo.github.io/categories/%E4%B8%AA%E4%BA%BA%E9%9A%8F%E7%AC%94/"/>
    
    
      <category term="生活记录" scheme="https://deimo.github.io/tags/%E7%94%9F%E6%B4%BB%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>travis-ci车技要点</title>
    <link href="https://deimo.github.io/2018/03/29/travis-ci%E8%BD%A6%E6%8A%80%E8%A6%81%E7%82%B9/"/>
    <id>https://deimo.github.io/2018/03/29/travis-ci车技要点/</id>
    <published>2018-03-29T14:31:53.000Z</published>
    <updated>2018-09-26T09:10:12.585Z</updated>
    
    <content type="html"><![CDATA[<h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>今天学习并使用了Travis.CI，遇到了不少问题，所幸都一一得到了解决，为了让自己印象深刻，决心写个blog，既可当成是一个问题记录，也可以看作是一个有关travis.ci的教程</p><h4 id="什么是CI"><a href="#什么是CI" class="headerlink" title="什么是CI"></a>什么是CI</h4><p>CI，英文全称：Continuous Integration，即持续集成。那么什么又是持续集成？</p><p>持续集成指的是只要代码有变更，就自动运行构建和测试，反馈运行结果。确保符合预期以后，再将新代码”集成”到主干。</p><p>持续集成的好处在于，每次代码的小幅变更，就能看到运行结果，从而不断累积小的变更，而不是在开发周期结束时，一下子合并一大块代码。</p><p>本文要介绍的就是大名鼎鼎的Travis CI</p><h4 id="Travis-CI"><a href="#Travis-CI" class="headerlink" title="Travis CI"></a>Travis CI</h4><p>Travis CI提供的就是一个持续集成服务，它在开源社区（github）上非常流行，想必你在逛github时会经常看到这样的徽章</p><p><a href="https://travis-ci.org/michaelliao/openweixin" target="_blank" rel="noopener"><img src="https://travis-ci.org/michaelliao/openweixin.svg?branch=master" alt="Build Status"></a></p><p>是的，这就是使用Travis.CI的标志啦，有木有觉得很有逼格呢~</p><h4 id="Travis-CI的特点"><a href="#Travis-CI的特点" class="headerlink" title="Travis CI的特点"></a>Travis CI的特点</h4><p>简单易学（学会了之后就是这种感觉呗😏）</p><p>高端大气上档次😎</p><p>免费！（Travis CI对github上公开的仓库完全免费，对于私有仓库则会收取一定费用，如果你已经付费购买了github的私有仓库，那么就不必再额外支付Travis CI的费用）</p><h4 id="Travis-CI的使用条件"><a href="#Travis-CI的使用条件" class="headerlink" title="Travis CI的使用条件"></a>Travis CI的使用条件</h4><p>前面有提到过…Travis CI在开源社区中十分的流行…事实上…Travis CI也只能结合Github去使用</p><p>对于免费版Travis CI，它的地址在这里 <a href="https://travis-ci.org/" target="_blank" rel="noopener">travis CI 免费版</a>，对于付费版Travis CI，它的地址如下<a href="https://travis-ci.com/" target="_blank" rel="noopener">travis CI 付费版</a></p><p>对Travis CI的使用条件总结如下</p><ul><li>拥有 GitHub 帐号</li><li>该帐号下面有一个项目</li><li>该项目里面有可运行的代码</li><li>该项目还包含构建或测试脚本</li></ul><p>无论是免费版还是付费版，它们的使用方法都是相同的</p><h4 id="Travis-CI的使用"><a href="#Travis-CI的使用" class="headerlink" title="Travis CI的使用"></a>Travis CI的使用</h4><ol><li>使用github账号登录Travis.CI</li><li>选择需要集成CI服务的仓库并激活，如下图所示</li></ol><p><img src="http://www.ruanyifeng.com/blogimg/asset/2017/bg2017121902.png" alt="选择并激活仓库"></p><ol start="3"><li><p>在你项目的根目录下新建一个.travis.yml的文件，该文件指定了travis CI的行为且必须保存在github的仓库中</p><p>一旦仓库中有了新的commit，Travis就会去找这个文件，并执行其中的相关命令，该文件使用<a href="http://www.ruanyifeng.com/blog/2016/07/yaml.html" target="_blank" rel="noopener">YAML</a>文件格式进行描述，不得不多提一句，一旦你的YAML文件格式错误，Travis将无法识别并自动build，报错如下</p><p><img src="/img/CI/parse_error.png" alt="解析错误"></p><p>这里有一个在线工具可用于检验你<strong>.travis.yml</strong>文件的格式是否正确。<a href="http://www.yamllint.com/" target="_blank" rel="noopener">在线检测YAML文件格式</a></p></li><li><p>编写你的.travis.yml文件</p><p>有关该文件的内容，Travis 提供了很多功能，详情请移步<a href="https://docs.travis-ci.com/" target="_blank" rel="noopener">官网</a></p><p>下面就一些简单的内容做一下解释，假设有.travis.yml文件内容如下</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></pre></td><td class="code"><pre><span class="line"><span class="attr">language:</span> <span class="string">python</span></span><br><span class="line"><span class="attr">sudo:</span> <span class="string">required</span></span><br><span class="line"><span class="attr">script:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr">before_install:</span> <span class="string">sudo</span> <span class="string">pip</span> <span class="string">install</span> <span class="string">foo</span></span><br><span class="line"><span class="attr">script:</span> <span class="string">py.test</span></span><br></pre></td></tr></table></figure><p>上面代码中，设置了四个字段：运行环境是 <code>Python</code>，需要<code>sudo</code>权限，在安装依赖之前需要安装<code>foo</code>模块，然后执行脚本<code>py.test</code></p><p>Travis的运行流程很简单，任何项目都会经过两个阶段</p><figure class="highlight plain"><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">install 阶段：安装依赖</span><br><span class="line">script 阶段：运行脚本</span><br></pre></td></tr></table></figure></li><li><p>使用Travis.CI登录服务器实现服务的持续集成</p><p>想要实现此功能，需要一定的前提：</p><ul><li>ssh key</li><li>ruby 2.0 + </li><li>基于某些原因下的科学上网</li></ul><p>在具备上述条件后就可以开始我们的Travis CI与服务器的交互集成了</p><p>首先需要安装一个gem包：travis</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gem install travis</span><br></pre></td></tr></table></figure><p>安装完成后，切换到你仓库根目录，执行</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">travis login</span><br></pre></td></tr></table></figure><p>根据提示，输入你刚刚用于travis-ci网站登录的Github账户名及密码。</p><p>做好基本的项目配置之后，我们需要配置持续部署的自动运行脚本。</p><p>首先使用<code>travis encrypt-file</code>命令对你刚刚在开发环境生成的密匙进行加密（这样一来可以放心地将密匙保存在公开的开源项目当中）</p><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">#</span><span class="bash"> 此处的--add参数表示自动添加脚本到.travis.yml文件中</span></span><br><span class="line">travis encrypt-file ~/.ssh/id_rsa --add</span><br></pre></td></tr></table></figure><p>之后再打开.travis.yml文件，会发现多了一个<code>before_install</code>的钩子</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></pre></td><td class="code"><pre><span class="line"><span class="attr">before_install:</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">openssl</span> <span class="string">aes-256-cbc</span> <span class="bullet">-K</span> <span class="string">$encrypted_830d3b21a25d_key</span> <span class="bullet">-iv</span> <span class="string">$encrypted_830d3b21a25d_iv</span></span><br><span class="line"><span class="bullet">    -</span><span class="string">in</span> <span class="string">id_rsa.enc</span> <span class="bullet">-out</span> <span class="string">~\/.ssh/id_rsa</span> <span class="bullet">-d</span></span><br></pre></td></tr></table></figure><p>Travis 一共提供了7个钩子，总结如下</p></li></ol><table><thead><tr><th>钩子名称</th><th>阶段解释</th></tr></thead><tbody><tr><td>before_install</td><td>install 阶段之前执行</td></tr><tr><td>before_script</td><td>script 阶段之前执行</td></tr><tr><td>after_failure</td><td>script 阶段失败时执行</td></tr><tr><td>after_success</td><td>script 阶段成功时执行</td></tr><tr><td>before_deploy</td><td>deploy 步骤之前执行</td></tr><tr><td>after_deploy</td><td>deploy 步骤之后执行</td></tr><tr><td>after_script</td><td>script 阶段之后执行</td></tr></tbody></table><p>   ​</p><p>   注意！！！😡😈默认生成的命令可能会在<code>/</code>前面带转义符<code>\</code>，我们不需要这些转义符，手动删掉所有的转义符，否则可能在后面引发莫名的错误 like this</p><p>   <img src="/img/CI/auth_error.jpeg" alt="转义错误"></p><p>   ​</p><p>   在完成上述内容后，我们还需要正确地设置权限和认证，这是为了避免…出现诸如..需要添加ssh key…确认提示…然而travis CI并没有一个交互式环境…错误如下</p><p>   <img src="/img/CI/key_error.jpg" alt="权限错误"></p><p>   ​</p><p>   在全部的准备工作都完成后，就可以添加部署脚本了，这里以我自己的Python项目为例，给出我的<code>.travis.yml</code>文件内容</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><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">script:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">ls</span></span><br><span class="line"><span class="attr">after_success:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">ssh</span> <span class="string">ubuntu@139.199.7.86</span> <span class="string">'cd /www/ynpublaw &amp;&amp; git pull &amp;&amp; env/bin/pip install -r requirements.txt'</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">ci.sh</span></span><br><span class="line"></span><br><span class="line"><span class="attr">before_install:</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">openssl</span> <span class="string">aes-256-cbc</span> <span class="bullet">-K</span> <span class="string">$encrypted_fed0778d0d20_key</span> <span class="bullet">-iv</span> <span class="string">$encrypted_fed0778d0d20_iv</span></span><br><span class="line"><span class="bullet">    -</span><span class="string">in</span> <span class="string">id_rsa.enc</span> <span class="bullet">-out</span> <span class="string">~/.ssh/id_rsa</span> <span class="bullet">-d</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">chmod</span> <span class="number">600</span> <span class="string">~/.ssh/id_rsa</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">echo</span> <span class="bullet">-e</span> <span class="string">"Host 139.199.7.86\n\tStrictHostKeyChecking no\n"</span> <span class="string">&gt;&gt;</span> <span class="string">~/.ssh/config</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># safelist</span></span><br><span class="line"><span class="attr">branches:</span></span><br><span class="line"><span class="attr">  only:</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">dev</span></span><br></pre></td></tr></table></figure><h4 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h4><p>本文参考如下：<br><a href="https://zhuanlan.zhihu.com/p/25066056" target="_blank" rel="noopener">《一点都不高大上，手把手教你使用Travis CI实现持续部署》</a></p><p><a href="http://www.ruanyifeng.com/blog/2017/12/travis_ci_tutorial.html" target="_blank" rel="noopener">《持续集成服务 Travis CI 教程》</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h4&gt;&lt;p&gt;今天学习并使用了Travis.CI，遇到了不少问题，所幸都一一得到了解决，为了让自己印象深刻，决心写个blog，既可当成是一个问题记录，也可
      
    
    </summary>
    
      <category term="Travis.CI 持续集成" scheme="https://deimo.github.io/categories/Travis-CI-%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/"/>
    
    
      <category term="问题记录" scheme="https://deimo.github.io/tags/%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>Android开发从小白到入门再到放弃</title>
    <link href="https://deimo.github.io/2018/03/07/Android%E5%BC%80%E5%8F%91%E4%BB%8E%E5%B0%8F%E7%99%BD%E5%88%B0%E5%85%A5%E9%97%A8%E5%86%8D%E5%88%B0%E6%94%BE%E5%BC%83/"/>
    <id>https://deimo.github.io/2018/03/07/Android开发从小白到入门再到放弃/</id>
    <published>2018-03-07T09:29:01.000Z</published>
    <updated>2018-09-26T09:09:46.378Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Android开发从小白到入门再到放弃"><a href="#Android开发从小白到入门再到放弃" class="headerlink" title="Android开发从小白到入门再到放弃"></a>Android开发从小白到入门再到放弃</h2><h4 id="前言："><a href="#前言：" class="headerlink" title="前言："></a>前言：</h4><p>哪里有什么专业~对于大学专业~我一直以来都比较确信：学啥儿专业都一样，反正到时毕业都找不到工作~ 😬<br>钱伟长大佬曾言：“我没有专业，国家的需要就是我的专业”~的确，哪里有什么专业呢？时代缺什么，什么就是我的专业~英雄造时势~可惜不可能人人竟是英雄…只有不断跟进~方才能够让自己落于不败之地~互联网行业的日新月异~恰恰需要你去不断学习~</p><h4 id="正文："><a href="#正文：" class="headerlink" title="正文："></a>正文：</h4><p>13年大学入学，是我第一次接触智能手机，不久后才发现原来在手机上就可以实现日常衣食住行需求…于是感慨移动终端的强大…那时就在心里盘算着…要是日后我也能在手机上开发App，那该是一件多么炫酷的事情哇~</p><p>也许你会想身为一名程序员的我接触互联网的时间居然那么晚？是的，没有错，受制于家庭背景和传统教育，甚至于我上大学时的报考专业也是传统化工类（无机非金属材料工程）。再后来…随着自己对所学专业的进一步了解…以及对自己所在大学的教育的失望（虽然还拿着微薄的学院奖学金~）…外加两位任课老师的言论~则是令我下定决心~转行IT！~在这里吐槽一下自己对所谓“学生组织”的认识：尽是一些无所事事、浪得虚名、沽名钓誉的无耻之徒~鲜有精英~所以大学四年我没有加入任何一个校园组织~告诉自己：只有无名之人才会去追逐名利~</p><p>至今我还记得我的大学物理老师说：男生，没事儿就多去学点英语和编程~线性代数的老师也说过：错过了大航海时代，错过了工业革命时代~我们现在正处于互联网时代，多么美好的时代呀~！这个时代，我们赶上了！~</p><p>那时身处大二上…又貌似选修了一门《安卓开发》的课程…是东软的徐铭老师授课~课讲得很好…很吸引人…然而并没有什么卵用…老师也直说…上了我得课，你肯定做不了什么东西…但能评价一个项目的好坏…我只记得那时啥都不懂得我…看着他在命令行使用mysql帅气的样子~在myeclipse上打开…tomcat，演示钓鱼网站原理时的兴奋与懵逼…虽然…那些都只是javaWeb里…基础中的渣渣…但依旧深深吸引了我…于是我果断回图书馆翻阅下android开发相关书籍…嗯，不大懂…只是感觉自己不明就里地照着上面写就能学会了，</p><p>于是…我下了一本电子书（书名貌似是《Android从入门到精通》来着？）…上课看，下课看…回宿舍练…没有语言基础的我…只在vc6.0上写过几行c的我…光是开发环境…就折腾了好久…不过也有好处…能让我对android开发环境的搭建遇到的各种问题都能有非常快的解决方案…也熟悉了SDK下各个文件包的意义…那个时候android studio还不像现在这么普遍…操着eclipse的我…也只是感觉这破玩意儿也就比记事本好用点…完全不能体会到它的强大…对它的使用…仅限于记事本水平…当然啦，书上所有代码…全是硬背…背下来…</p><p>照着电子书上的代码…敲了一段时间后…觉得一边看电子书，一边操作IDE很不方便…于是上当当上买了自己的第一本android开发书籍——李刚的《疯狂android讲义》。一本全面…但又不知道去总结经验的书…书上大部分内容都是细枝末节的东西…书上的示例代码给人感觉也是不够健壮，不够有示范性…不过作者经历到是挺励志…对我还算是有激励作用…让我算是坚持了下来…现在想来，如果当时自己的入门书籍，选得是郭琳大神的《第一行代码》话，想必我的android开发之路~会顺畅得多…那时电脑配置不佳（因为我是AMD处理器，无法使用intel的硬件加速技术…后来又听说了tensorflow对AMD也不尽友好…）不能使用google自带的x86镜像模拟器…那个ARM和MIPS 架构的模拟器…速度感人~无奈只能使用真机调试…（后来使用了genymotion）即使是eclipse…也觉得速度…不合时宜…</p><p>有了《疯狂android讲义》后…我便开始…研究它…没什么技巧…一点点看，一点点敲…进展很慢，很慢…因为代码全靠背…心里觉得恐惧…偌大一本书~我啥时候能读完…一天6小时的学习时间…全拿来处理代码的小瑕疵了…</p><p>我觉得这样的学习效率，肯定是有问题的…于是在贴吧…知乎…寻求经验…这才得知，要学android，必先修java…我一开始甚至对此甚是怀疑…觉得没有java语言基础…硬着头皮来…肯定也能行…~后来自然是发现自己…简直愚不可及…~这里也提醒下广大想学习android的童鞋们…想学android，请先修java…    </p><p>可惜当我意识到这点的时候…大二上…已经过了大半…于是狠补java基础，我也不对此有所隐瞒，我从某位大佬那里拿到了毕向东的JAVA SE视频。说真的，他讲的实在太好太好了！！！（哪怕是到现在…我也会偶尔回头再去看看java语言）而我也确实很用功很用心的看了他的完整视频，当真是收获满满，所获甚丰，应该说就java SE的内容而言，我怕是很难会忘记了，时至今日我也觉得自己的java SE基础是足够足够扎实的！曾经有幸面试过几位java程序员…面向对象解释的一塌糊涂，<strong>private</strong> ， <strong>public</strong>权限模糊不清…甚至可以毫不掩饰夸张地说，从毕向东的视频里所学习到的面向对象思想，再一次的开发了我的智力，改变了我的思维习惯！港真…语言基础真的很重要…我在大连的一家公司实习过一段时间的Android…曾有同事与我探讨问题…我发现那些问题大部分都是由于其对java语言基础及其特点不熟悉造成的…而不是由android SDK带来</p><p>有了语言基础，再学起android来~果然事半功倍，原来几个小时也学不明白的东西~现在几十分钟下来~豁然开朗：原来是这么简单的东西~ 就这样…对于android我终于算是入门了~之后在校内我积极地和老师、同学（研究生）参与各种比赛，做一些学生项目~也在大连市有过近两个月的Android实习经历，可以说那个时候的我对于Android程序设计基础该是比较熟悉了~后来的后来我开始前往进阶之路：各种框架的学习与使用~最新系统AP的I特性~设计模式…view适配…动画…RPC…然而~也不知为何…我未能坚持下去…也许是进阶之路过于漫长…也许是自己过于“花心”~我最终抛弃了android客户端，选择了Python后端~</p><p>是的，从我开始接触互联网到成为一名合格程序员，这期间全是靠着自己的胡乱摸索和摸爬滚打…直至今日~</p><p>欲知后事如何~请看下篇连载~  😈</p><h4 id="结尾："><a href="#结尾：" class="headerlink" title="结尾："></a>结尾：</h4><p>如果你喜欢我的文章，扫描以下二维码，加我微信，给我小额赞赏</p><p><img src="/img/life/qrcode.jpg" alt="二维码"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Android开发从小白到入门再到放弃&quot;&gt;&lt;a href=&quot;#Android开发从小白到入门再到放弃&quot; class=&quot;headerlink&quot; title=&quot;Android开发从小白到入门再到放弃&quot;&gt;&lt;/a&gt;Android开发从小白到入门再到放弃&lt;/h2&gt;&lt;h4 i
      
    
    </summary>
    
      <category term="个人随笔" scheme="https://deimo.github.io/categories/%E4%B8%AA%E4%BA%BA%E9%9A%8F%E7%AC%94/"/>
    
    
      <category term="生活记录" scheme="https://deimo.github.io/tags/%E7%94%9F%E6%B4%BB%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>RabbitMQ基础（一）</title>
    <link href="https://deimo.github.io/2018/02/12/RabbitMQ%E5%9F%BA%E7%A1%80%EF%BC%88%E4%B8%80%EF%BC%89/"/>
    <id>https://deimo.github.io/2018/02/12/RabbitMQ基础（一）/</id>
    <published>2018-02-12T00:48:08.000Z</published>
    <updated>2018-12-16T13:55:09.668Z</updated>
    
    <content type="html"><![CDATA[<h2 id="RabbitMQ基础（一）"><a href="#RabbitMQ基础（一）" class="headerlink" title="RabbitMQ基础（一）"></a>RabbitMQ基础（一）</h2><h4 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h4><h6 id="1-什么是消息队列"><a href="#1-什么是消息队列" class="headerlink" title="1. 什么是消息队列"></a>1. 什么是消息队列</h6><p>​    消息（Message）是指在应用间传送的数据。消息可以非常简单，比如只包含文本字符串，也可以更复杂，可能包含嵌入对象。</p><p>消息队列（Message Queue）是一种应用间的通信方式，消息发送后可以立即返回，由消息系统来确保消息的可靠传递。消息发布者只管把消息发布到 MQ 中而不用管谁来取，消息使用者只管从 MQ 中取消息而不管是谁发布的。这样发布者和使用者都不用知道对方的存在</p><h6 id="2-为什么需要使用消息队列"><a href="#2-为什么需要使用消息队列" class="headerlink" title="2. 为什么需要使用消息队列"></a>2. 为什么需要使用消息队列</h6><p>​    以常见的订单系统为例，用户点击【下单】按钮之后的业务逻辑可能包括：扣减库存、生成相应单据、发红包、发短信通知。在业务发展初期这些逻辑可能放在一起同步执行，随着业务的发展订单量增长，需要提升系统服务的性能，这时可以将一些不需要立即生效的操作拆分出来异步执行，比如发放红包、发短信通知等。这种场景下就可以用 MQ ，在下单的主流程（比如扣减库存、生成相应单据）完成之后发送一条消息到 MQ 让主流程快速完结，而由另外的单独线程拉取MQ的消息（或者由 MQ 推送消息），当发现 MQ 中有发红包或发短信之类的消息时，执行相应的业务逻辑。</p><p>以上是用于业务解耦的情况，其它常见场景包括最终一致性、广播、错峰流控等等。</p><h6 id="3-AMQP协议简介"><a href="#3-AMQP协议简介" class="headerlink" title="3. AMQP协议简介"></a>3. AMQP协议简介</h6><p>​    AMQP ：Advanced Message Queue，高级消息队列协议。它是应用层协议的一个开放标准，为面向消息的中间件设计，基于此协议的客户端与消息中间件可传递消息，并不受产品、开发语言等条件的限制。</p><p>​    AMQP从一开始就设计成为开放标准，以解决众多消息队列需求的拓扑结构问题。凭借开放，任何人都可以执行这一标准，针对标准编码的任何人都可以和任意AMQP供应商提供的MQ服务器进行交互</p><h6 id="4-RabbitMQ-特点"><a href="#4-RabbitMQ-特点" class="headerlink" title="4. RabbitMQ 特点"></a>4. RabbitMQ 特点</h6><p>​    RabbitMQ 最初起源于金融系统，用于在分布式系统中存储转发消息，在易用性、扩展性、高可用性等方面表现不俗。具体特点包括:</p><ul><li><p>可靠性（Reliability）</p><blockquote><p>RabbitMQ 使用一些机制来保证可靠性，如持久化、传输确认、发布确认。</p></blockquote></li></ul><ul><li><p>灵活的路由（Flexible Routing）</p><blockquote><p>在消息进入队列之前，通过 Exchange 来路由消息的。对于典型的路由功能，RabbitMQ 已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能，可以将多个 Exchange 绑定在一起，也通过插件机制实现自己的 Exchange 。</p></blockquote></li><li><p>消息集群（Clustering）</p><blockquote><p>队列可以在集群中的机器上进行镜像，使得在部分节点出问题的情况下队列仍然可用。</p></blockquote></li><li><p>多种协议（Multi-protocol）</p><blockquote><p>RabbitMQ 支持多种消息队列协议，比如 STOMP、MQTT 等等。</p></blockquote></li><li><p>多语言客户端（Many Clients）</p><blockquote><p>RabbitMQ 几乎支持所有常用语言，比如 Java、.NET、Ruby 等等。</p></blockquote></li><li><p>管理界面（Management UI）</p><blockquote><p>RabbitMQ 提供了一个易用的用户界面，使得用户可以监控和管理消息 Broker 的许多方面。</p></blockquote></li><li><p>跟踪机制（Tracing）</p><blockquote><p>如果消息异常，RabbitMQ 提供了消息跟踪机制，使用者可以找出发生了什么。</p></blockquote></li><li><p>插件机制（Plugin System）</p><blockquote><p>RabbitMQ 提供了许多插件，来从多方面进行扩展，也可以编写自己的插件。</p></blockquote></li></ul><h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><p>Mac上可以使用brew</p><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">brew install RabbitMQ</span><br></pre></td></tr></table></figure><blockquote><p>注意在Mac OS 10.12以后，不在允许brew使用Root权限，如果只是单单纯学习使用，可使用官网二进制程序集<a href="http://www.rabbitmq.com/download.html" target="_blank" rel="noopener">地址</a></p></blockquote><h4 id="RabbitMQ的消息通信模型"><a href="#RabbitMQ的消息通信模型" class="headerlink" title="RabbitMQ的消息通信模型"></a>RabbitMQ的消息通信模型</h4><h6 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h6><p>消息（message）被发布者（publisher）发送给交换机（exchange），交换机常常被比喻成邮局或者邮箱。然后交换机将收到的消息根据路由规则分发给绑定的队列（queue）。最后AMQP代理会将消息投递给订阅了此队列的消费者，或者消费者按照需求自行获取。如下图</p><p><img src="/img/rabbitmq/hello-world.png" alt="rabbitmq消息通信模型"></p><h6 id="RabbitMQ的相关概念介绍"><a href="#RabbitMQ的相关概念介绍" class="headerlink" title="RabbitMQ的相关概念介绍"></a>RabbitMQ的相关概念介绍</h6><ol><li><p>生产者和消费者</p><blockquote><p>生产者：发送消息的程序</p><p>消费者：一个主要等待接收消息的程序</p></blockquote></li><li><p>交换机</p><blockquote><p>交换机是用来发送消息的AMQP实体。交换机常常被比喻成邮局或者邮箱</p></blockquote><p>RabbitMQ具有以下几种交换机类型</p></li></ol><table><thead><tr><th style="text-align:center">Name（交换机类型）</th><th style="text-align:center">Default pre-declared names（预声明的默认名称）</th></tr></thead><tbody><tr><td style="text-align:center">Direct exchange（直连交换机）</td><td style="text-align:center">(Empty string) and amq.direct</td></tr><tr><td style="text-align:center">Fanout exchange（扇型交换机）</td><td style="text-align:center">amq.fanout</td></tr><tr><td style="text-align:center">Topic exchange（主题交换机）</td><td style="text-align:center">amq.topic</td></tr><tr><td style="text-align:center">Headers exchange（头交换机）</td><td style="text-align:center">amq.match (and amq.headers in RabbitMQ)</td></tr></tbody></table><ol start="3"><li><p>信道</p><blockquote><p>有些应用需要与AMQP代理建立多个连接。无论怎样，同时开启多个TCP连接都是不合适的，因为这样做会消耗掉过多的系统资源并且使得防火墙的配置更加困难。AMQP 0-9-1提供了通道（channels）来处理多连接，可以把通道理解成共享一个TCP连接的多个轻量化连接。</p><p>在涉及多线程/进程的应用中，为每个线程/进程开启一个通道（channel）是很常见的，并且这些通道不能被线程/进程共享。</p><p>一个特定通道上的通讯与其他通道上的通讯是完全隔离的，因此每个AMQP方法都需要携带一个通道号，这样客户端就可以指定此方法是为哪个通道准备的</p></blockquote></li><li><p>队列</p><blockquote><p>队列如同具名邮箱，消息最终到达队列中并等待消费</p></blockquote></li><li><p>虚拟主机</p><blockquote><p>为了在一个单独的代理上实现多个隔离的环境（用户、用户组、交换机、队列 等），AMQP提供了一个虚拟主机（virtual hosts - vhosts）的概念。这跟Web servers虚拟主机概念非常相似，这为AMQP实体提供了完全隔离的环境。当连接被建立的时候，AMQP客户端来指定使用哪个虚拟主机</p></blockquote></li></ol><h4 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h4><p>本文参考列表如下</p><ol><li>《消息队列之 RabbitMQ》<a href="https://www.jianshu.com/p/79ca08116d57" target="_blank" rel="noopener">简书地址</a></li><li>《RabbitMQ 中文文档》<a href="http://rabbitmq.mr-ping.com/" target="_blank" rel="noopener">Gitbook地址</a></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;RabbitMQ基础（一）&quot;&gt;&lt;a href=&quot;#RabbitMQ基础（一）&quot; class=&quot;headerlink&quot; title=&quot;RabbitMQ基础（一）&quot;&gt;&lt;/a&gt;RabbitMQ基础（一）&lt;/h2&gt;&lt;h4 id=&quot;简介&quot;&gt;&lt;a href=&quot;#简介&quot; cla
      
    
    </summary>
    
      <category term="RabbitMQ" scheme="https://deimo.github.io/categories/RabbitMQ/"/>
    
    
      <category term="消息队列" scheme="https://deimo.github.io/tags/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/"/>
    
  </entry>
  
  <entry>
    <title>微信小程序踩坑记</title>
    <link href="https://deimo.github.io/2017/11/20/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E8%B8%A9%E5%9D%91%E8%AE%B0/"/>
    <id>https://deimo.github.io/2017/11/20/微信小程序踩坑记/</id>
    <published>2017-11-20T14:42:36.000Z</published>
    <updated>2018-09-26T09:12:29.598Z</updated>
    
    <content type="html"><![CDATA[<p>曾有一段时间，一个叫”包你说”的小程序风靡微信平台，据产品大佬们的调查分析，日流水近百万，每日纯利润达十万~如此可观的收益，公司自然是想着要”截流”了~于是乎…这个重任就落到了本菜菜身上…如果你还不知道”包你说”的话…建议你先尝试着体验下，体验后你就发现，所谓的包你说，最大的难点无非就是在那个语音识别上…其它地方一如正常的CRUD…当然还有微信的小程序支付…让我郁闷了一阵</p><h2 id="踩坑一：微信小程序支付"><a href="#踩坑一：微信小程序支付" class="headerlink" title="踩坑一：微信小程序支付"></a>踩坑一：微信小程序支付</h2><p>先说简单的小程序支付吧…如果你对支付类业务比较熟悉，你总是能够发现，无论是原生支付，还是第三方支付…无论哪个平台…其最核心的步骤无外乎两步：<strong><strong>下单</strong></strong>  and  <strong><strong>通知</strong></strong> </p><p>根据不同的支付文档，有时需要客户端访问商户系统，由商户后端下单，成功后返回客户端，有时这是由商户系统生成下单参数，由客户端去请求下单。而微信的小程序支付则是后一种…嗯，这到没什么…不过需要注意的是…微信的小程序支付文档上有个<strong><strong>再签名</strong></strong>过程，同时，一定要注意<strong><strong>字段名</strong></strong>和<strong><strong>字段示例值</strong></strong> ！！！我就是被可恶的微信字段名坑得不浅…当然这也怨我自己粗心大意…其它诸如签名算法并没有什么难度的…贴一张图以示提醒了<img src="/img/wxapp/wxapp_pay1.jpeg" alt="小程序支付流程"> <img src="/img/wxapp/wxapp_pay2.jpeg" alt="小程序支付参数"></p><p>看到了吗…那个字段的示例值…更可恶的是…下单时的文档参数说明使用的是snakeCase，到了这里…全部变成camelCase…完全看心情有木有…没办法…也怨我看文档不仔细吧…</p><h2 id="踩坑二：微信语音识别"><a href="#踩坑二：微信语音识别" class="headerlink" title="踩坑二：微信语音识别"></a>踩坑二：微信语音识别</h2><p>在完成大概所有的CRUD和支付功能后…开始去玩…看起来最高大上的”语音识别”功能…我们最终选取的语音识别平台是百度…原因嘛…因为它免费…不过话又说起来…百度的语音识别接起来…还真挺简单的…直接提供了pip包，非常方便集成在flask框架中，我按照了示例…自己测试了文件上传…又调用百度语音识别api成功后…觉得自己大事已成…万万没想到的是…微信的语音文件格式并不可以被百度语音直接识别…WTF！怎么办…当时考虑使用讯飞…以为它可以转万能的语音格式…官方查阅之…原来不是…那就只有一种办法了…自行转码…于是又去搜索python的音频转码库…然而都没有对微信那种语音文件格式的支持…所以没办法了…到了这里…要么使用C语言…自己编写转码库和程序…让C调用…要么…嗯…使用第三方外部转码程序…好在Python和Unix/Linux对调用第三方应用支持比较好，这点没什么难度…那么话又说回来了…微信的语音文件到底是什么格式呢？百度之…大佬们众说纷坛…不过一个比较可信的主流的观点是<strong><strong>SILK</strong></strong>文件格式…嗯…于是各种搜索…终于让我找到了一款神器：ffmpeg万能解码器，支持丰富cli处理！在研究使用ffmpeg的时候…发现了 一个有趣的词…<strong><strong>Hall of Shame</strong></strong>有兴趣的朋友们可以自己去看看鹅厂的黑历史…然鹅…一番折腾后发现…ffmpeg并没有支持silk音频的解码库…需要自己额外下载…WTF！后来我又百度之…终于…发现了这位大佬,<a href="https://github.com/kn007/silk-v3-decoder" target="_blank" rel="noopener">传送门</a>，于是我按照文档…发现…居然告诉我这个<img src="/img/wxapp/decoder_fail.jpg" alt="转码错误"></p><p>WTF！不是标准SILK格式，那是什么？！千翻查找…叫我发现了这个…<a href="http://www.iosre.com/t/topic/3199" target="_blank" rel="noopener">传送门</a>，原来是在文件头信息中多了一个无用的空字节…于是…我在自己的mac中搜索微信接收到的语音文件，发现是.aud.silk结尾…嗯，应该称之为silk变种文件比较合适。于是按照思路…先去掉第一个字符…然后转码…这次果然有效！！！然后再调用百度语音识别，居然…成功了！不过值得一提的是…我在转码时，没有使用那个Shell脚本，而是用到了<a href="https://github.com/Kronopath/SILKCodec" target="_blank" rel="noopener">这个</a>，这份转码库的用法很简单：download下来后，进入ARM平台，然后make编译出可执行程序…编译过程可能会提示你安装一些编译的工具…（如果你没安装的话）之后就可以使用python去调用这个外部的第三方的程序了。这样就实现了微信的语音文件从上传到转码再到识别的过程，初步完成该核心功能，后续的识别率优化…可以考虑比对拼音…这样就可以避免同音不同字的识别尴尬，在一定程度上…提高识别率。</p><h2 id="踩坑三：微信小程序的通知"><a href="#踩坑三：微信小程序的通知" class="headerlink" title="踩坑三：微信小程序的通知"></a>踩坑三：微信小程序的通知</h2><p>用过摩拜单车小程序的童鞋们肯定都知道，摩拜单车在解锁和开锁后都会向用户发送一条通知。好吧，其实这个通知没什么难度，唯一觉得比较不舒服的是它的参数…嗯，尤其你要注意对于微信的access_token间断刷新…这个使用定时任务+redis就可以很好处理。接下来就是通知的核心参数form_id对于该参数…如果有支付过程的话，这个参数就会好拿很多…如果没有的话…那就会稍稍麻烦…当然我也只是研究使用了…支付情形…剩下的情形留着以后再去考虑吧</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;曾有一段时间，一个叫”包你说”的小程序风靡微信平台，据产品大佬们的调查分析，日流水近百万，每日纯利润达十万~如此可观的收益，公司自然是想着要”截流”了~于是乎…这个重任就落到了本菜菜身上…如果你还不知道”包你说”的话…建议你先尝试着体验下，体验后你就发现，所谓的包你说，最大
      
    
    </summary>
    
      <category term="微信小程序" scheme="https://deimo.github.io/categories/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
    
      <category term="微信 问题记录" scheme="https://deimo.github.io/tags/%E5%BE%AE%E4%BF%A1-%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>mysql中GROUP_CONCAT的长度问题</title>
    <link href="https://deimo.github.io/2017/09/20/mysql%E4%B8%ADGROUP-CONCAT%E7%9A%84%E9%95%BF%E5%BA%A6%E9%97%AE%E9%A2%98/"/>
    <id>https://deimo.github.io/2017/09/20/mysql中GROUP-CONCAT的长度问题/</id>
    <published>2017-09-20T02:39:08.000Z</published>
    <updated>2018-09-26T09:10:43.253Z</updated>
    
    <content type="html"><![CDATA[<p>在mysql中，有个函数叫“GROUP_CONCAT”，平常使用可能发现不了问题，然而一旦数据量增大时，会发现内容被截取了！！！是的，你没看错，就是被截取了！！真的是非常令人蛋疼的问题呀，尤其是在类似聚合分组的业务，将数据反馈给客户端时，就更为严重了！多番查找折腾后发现，原来…<br>MYSQL内部对这个是有设置的，默认不设置的长度是1024，可以通过使用<code>show variables like &quot;group_concat_max_len</code>命令来查看该变量值<img src="/img/mysql/group_concat.png" alt="group_concat_max_len"></p><p>想要指定GROUP_CONCAT的长度有两种方法：</p><ol><li><p>修改变量 group_concat_max_len的值</p><ul><li><p>修改session变量，只对该连接客户有效</p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SET</span> [<span class="keyword">SESSION</span>] group_concat_max_len=<span class="number">102400</span>;</span><br></pre></td></tr></table></figure></li><li><p>修改global变量，对所有连接都生效</p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SET</span> <span class="keyword">GLOBAL</span> group_concat_max_len=<span class="number">102400</span>;</span><br></pre></td></tr></table></figure></li></ul></li><li><p>修改配置文件（全局生效）</p></li></ol><p>在mysql启动的配置文件下，进行设置，各平台下配置文件存放的目录不同，配置文件的具体存放位置，不是本文要讨论的内容</p><p>以我的mac os系统为例，我已经将mysql的配置文件写在/etc目录下，只需要在mysqld节点下group_concat_max_len = [size] (size是你定义的大小，上限为4294967295)<img src="/img/mysql/group_concat_len.png" alt="group_concat_len"></p><p>曾经看到过一些文章的说法是将group_concat_max_len设置为-1，然后就可以达到上限4294967295，我在自己的机器下试了试，发现<strong>并不可以</strong>…我的系统是mac os 10.12 ，mysql的版本是5.7.19</p><p>本文参考自CSDN，<a href="http://blog.csdn.net/alibert/article/details/51019351" target="_blank" rel="noopener">原文地址</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;在mysql中，有个函数叫“GROUP_CONCAT”，平常使用可能发现不了问题，然而一旦数据量增大时，会发现内容被截取了！！！是的，你没看错，就是被截取了！！真的是非常令人蛋疼的问题呀，尤其是在类似聚合分组的业务，将数据反馈给客户端时，就更为严重了！多番查找折腾后发现，原
      
    
    </summary>
    
      <category term="mysql" scheme="https://deimo.github.io/categories/mysql/"/>
    
    
      <category term="问题记录 转载" scheme="https://deimo.github.io/tags/%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95-%E8%BD%AC%E8%BD%BD/"/>
    
  </entry>
  
  <entry>
    <title>mysql非主键删除记录</title>
    <link href="https://deimo.github.io/2017/09/12/mysql_delete_problem/"/>
    <id>https://deimo.github.io/2017/09/12/mysql_delete_problem/</id>
    <published>2017-09-11T15:56:16.000Z</published>
    <updated>2018-09-26T09:10:00.388Z</updated>
    
    <content type="html"><![CDATA[<p>这几天在使用mysql过程中发现了这样一个有趣问题：如果在执行delete操作时，where子句不是直接指定条件，mysql就会报错，内容如下：</p><blockquote><p>Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -&gt; SQL Editor and reconnect.</p></blockquote><p>查阅官方的手册后，官方时如此介绍的：</p><blockquote><p>For beginners, a useful startup option is <a href="https://dev.mysql.com/doc/refman/5.7/en/mysql-command-options.html#option_mysql_safe-updates" target="_blank" rel="noopener"><code>--safe-updates</code></a> (or <a href="https://dev.mysql.com/doc/refman/5.7/en/mysql-command-options.html#option_mysql_safe-updates" target="_blank" rel="noopener"><code>--i-am-a-dummy</code></a>, which has the same effect). It is helpful for cases when you might have issued a <code>DELETE FROM *tbl_name*</code> statement but forgotten the <code>WHERE</code> clause. Normally, such a statement deletes all rows from the table. With <a href="https://dev.mysql.com/doc/refman/5.7/en/mysql-command-options.html#option_mysql_safe-updates" target="_blank" rel="noopener"><code>--safe-updates</code></a>, you can delete rows only by specifying the key values that identify them. This helps prevent accidents.</p></blockquote><p>原来mysql为了避免出现初学者在执行delete操作时未制定where条件导致整个表被删除，因此在系统变量中开启了<strong>SAVE-UPDATES</strong>模式，这样就能有效减少错误的产生了。</p><p><strong>SAVE-UPDATES</strong>下有3个作用：这里就直接po出文档了</p><ul><li>You are not permitted to execute an <a href="https://dev.mysql.com/doc/refman/5.7/en/update.html" target="_blank" rel="noopener"><code>UPDATE</code></a> or <a href="https://dev.mysql.com/doc/refman/5.7/en/delete.html" target="_blank" rel="noopener"><code>DELETE</code></a> statement unless you specify a key constraint in the <code>WHERE</code> clause or provide a <code>LIMIT</code> clause (or both)</li><li>The server limits all large <a href="https://dev.mysql.com/doc/refman/5.7/en/select.html" target="_blank" rel="noopener"><code>SELECT</code></a> results to 1,000 rows unless the statement includes a <code>LIMIT</code> clause</li><li>The server aborts multiple-table <a href="https://dev.mysql.com/doc/refman/5.7/en/select.html" target="_blank" rel="noopener"><code>SELECT</code></a> statements that probably need to examine more than 1,000,000 row combinations</li></ul><p>综上，可以使用如下办法解决</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SET</span> SQL_SAVE_UPDATES = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>并不建议全局设置该变量，小心驶得万年船，尤其对经验不足童鞋来说~</p><p>更多内容请看这里，<a href="https://dev.mysql.com/doc/refman/5.7/en/mysql-tips.html" target="_blank" rel="noopener">传送门</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;这几天在使用mysql过程中发现了这样一个有趣问题：如果在执行delete操作时，where子句不是直接指定条件，mysql就会报错，内容如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Error Code: 1175. You are using safe update 
      
    
    </summary>
    
      <category term="mysql" scheme="https://deimo.github.io/categories/mysql/"/>
    
    
      <category term="问题记录" scheme="https://deimo.github.io/tags/%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>python多线程技术(二)</title>
    <link href="https://deimo.github.io/2017/09/05/python%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%8A%80%E6%9C%AF-%E4%BA%8C/"/>
    <id>https://deimo.github.io/2017/09/05/python多线程技术-二/</id>
    <published>2017-09-05T14:03:07.000Z</published>
    <updated>2018-09-26T09:12:15.361Z</updated>
    
    <content type="html"><![CDATA[<p>在《python多线程技术(一)》篇当中，曾粗略介绍了多线程技术及运行状态，及python对于多线程的支持和简单实用，还有印象的朋友一定还记得python中实现多线程的两种方式吧，如果不熟悉的话，建议你先去读一读我上一篇的博文哦🤓</p><p>本篇是python多线程技术的精华所在，在真实项目开发中所使用到的多线程编程模型大部分都可以在我将要给出的代码示例中找到，如果本篇的内容过于繁多的话，按照我一如既往，短小精悍的写作特点来看的话～我也许还会写上第三篇哦😛</p><h2 id="常见线程运行模型"><a href="#常见线程运行模型" class="headerlink" title="常见线程运行模型"></a>常见线程运行模型</h2><p>在开始撸码之前，我们还是看看的常见的线程模型有哪些～一起看看这些言简意赅，图文并茂的示意图（感谢麦子学院丁敬香老师提供的教学资料）</p><h5 id="1-井水不犯河水"><a href="#1-井水不犯河水" class="headerlink" title="1.井水不犯河水"></a>1.井水不犯河水<img src="/img/pythread/module1.png" alt="井水不犯河水"></h5><h5 id="2-独木桥上相遇－线程等待"><a href="#2-独木桥上相遇－线程等待" class="headerlink" title="2.独木桥上相遇－线程等待"></a>2.独木桥上相遇－线程等待<img src="/img/pythread/module2.png" alt="独木桥上相遇－线程等待"></h5><h5 id="3-甘当幕后英雄－后台线程"><a href="#3-甘当幕后英雄－后台线程" class="headerlink" title="3.甘当幕后英雄－后台线程"></a>3.甘当幕后英雄－后台线程<img src="/img/pythread/module3.png" alt="甘当幕后英雄－后台线程"></h5><h5 id="4-我先用你后用－线程同步"><a href="#4-我先用你后用－线程同步" class="headerlink" title="4. 我先用你后用－线程同步"></a>4. 我先用你后用－线程同步<img src="/img/pythread/module4.png" alt="我先用你后用－线程同步"></h5><h5 id="5-操作也有先后－线程同步"><a href="#5-操作也有先后－线程同步" class="headerlink" title="5.操作也有先后－线程同步"></a>5.操作也有先后－线程同步<img src="/img/pythread/module5.png" alt="操作也有先后－线程同步"></h5><h5 id="6共享要悠着用－线程同步"><a href="#6共享要悠着用－线程同步" class="headerlink" title="6共享要悠着用－线程同步"></a>6共享要悠着用－线程同步<img src="/img/pythread/module6.png" alt="共享要悠着用－线程同步"></h5><h5 id="7-我用完后叫你－线程通信"><a href="#7-我用完后叫你－线程通信" class="headerlink" title="7.我用完后叫你－线程通信"></a>7.我用完后叫你－线程通信<img src="/img/pythread/module7.png" alt="我用完后叫你－线程通信"></h5><h2 id="具体代码实例"><a href="#具体代码实例" class="headerlink" title="具体代码实例"></a>具体代码实例</h2><p>#####1. 线程等待实例</p><p>在python的多线程编程中实现线程的等待非常容易，只需要调用线程对象的<strong>join(self, timeout=None)</strong>方法即可</p><p>被调用join()方法的线程会一直阻塞调用者的线程，直到自己结束（正常结束，或引发未处理异常），或超出timeout的时间。</p><p>1.1.1 一般情形</p><figure class="highlight python"><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="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyThread</span><span class="params">(threading.Thread)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">20</span>):</span><br><span class="line">            print(<span class="string">'threading: '</span>, i)</span><br><span class="line">            time.sleep(<span class="number">0.1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    t = MyThread()</span><br><span class="line">    t.start()</span><br><span class="line">    <span class="comment">#t.join()# 取消注释即转变为线程等待情形</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">10</span>):</span><br><span class="line">        print(<span class="string">'Main:'</span>, i)</span><br><span class="line">        time.sleep(<span class="number">0.1</span>)</span><br></pre></td></tr></table></figure><p>运行结果如下<br><img src="/img/pythread/th_join1.png" alt="非等待"></p><p>可以看到是两个线程交叉运行，如果我们需要在子线程运行完成后再运行主进程，调用<strong>join</strong>方法即可</p><p>1.1.2 等待情形</p><p>代码如上，取消<strong>join</strong>方法所在行的注释即可</p><p>运行结果如下<br><img src="/img/pythread/th_join2.png" alt="等待"></p><h5 id="2-守护线程（后台线程）"><a href="#2-守护线程（后台线程）" class="headerlink" title="2. 守护线程（后台线程）"></a>2. 守护线程（后台线程）</h5><p>在python中实现后台线程的情景相当容易，其步骤如下：</p><blockquote><p>1.建立线程                              </p><p>2.设置线程的daemon属性为True</p><p>3.启动线程                              </p></blockquote><p>需要注意的是被设定为后台运行的线程，会在主程序退出时主动自杀。</p><p>2.1.1 一般情形</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">dmn</span><span class="params">()</span>:</span></span><br><span class="line">    <span class="string">"""模拟后台线程的运行"""</span></span><br><span class="line">    print(<span class="string">'dmn start ...'</span>)</span><br><span class="line">    time.sleep(<span class="number">2</span>) <span class="comment"># 后台线程来不及结束，将会随着主线程的结束而退出</span></span><br><span class="line">    print(<span class="string">'dmn end.'</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ndmn</span><span class="params">()</span>:</span></span><br><span class="line">    <span class="string">"""非后台线程运行"""</span></span><br><span class="line">    print(<span class="string">'ndmn start ...'</span>)</span><br><span class="line">    time.sleep(<span class="number">1</span>)</span><br><span class="line">    print(<span class="string">'ndmn end.'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    d = threading.Thread(target=dmn)</span><br><span class="line">    <span class="comment">#d.daemon= True# 模拟后台线程时，请取消次注释</span></span><br><span class="line">    n = threading.Thread(target=ndmn)</span><br><span class="line">    print(<span class="string">'Main statr'</span>)</span><br><span class="line">    d.start()</span><br><span class="line">    n.start()</span><br><span class="line">    print(<span class="string">'Main end.'</span>）</span><br></pre></td></tr></table></figure><p>运行结果如下<br><img src="/img/pythread/th_daem1.png" alt="非等待线程"></p><p>可以看到主线程结束后，两个线程也相继结束</p><p>2.2.2 守护线程情形</p><p>代码如上，取消注释后，运行结果如下<br><img src="/img/pythread/th_daem2.png" alt="守护线程"></p><p>可以看到主线程结束后，守护线程就自动退出，还来不及执行<em>print(‘dmn end.’)</em></p><h5 id="3-线程同步（重点，难点）"><a href="#3-线程同步（重点，难点）" class="headerlink" title="3. 线程同步（重点，难点）"></a>3. 线程同步（重点，难点）</h5><p>python中线程同步的方式纷繁复杂，多种多样，为处理不同业务情形提供相应的工具和概念。python的theading模块主要提供了以下<strong>3</strong>种线程同步的方式：</p><blockquote><p>1.指令锁（Lock）</p><p>2.条件变量（Condition）</p><p>3.信号量（Semaphore）</p></blockquote><h6 id="3-1-指令锁"><a href="#3-1-指令锁" class="headerlink" title="3.1 指令锁"></a>3.1 指令锁</h6><h6 id="3-2-条件变量"><a href="#3-2-条件变量" class="headerlink" title="3.2 条件变量"></a>3.2 条件变量</h6><h6 id="3-3-信号量"><a href="#3-3-信号量" class="headerlink" title="3.3 信号量"></a>3.3 信号量</h6>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;在《python多线程技术(一)》篇当中，曾粗略介绍了多线程技术及运行状态，及python对于多线程的支持和简单实用，还有印象的朋友一定还记得python中实现多线程的两种方式吧，如果不熟悉的话，建议你先去读一读我上一篇的博文哦🤓&lt;/p&gt;
&lt;p&gt;本篇是python多线程技
      
    
    </summary>
    
      <category term="python 多线程" scheme="https://deimo.github.io/categories/python-%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
    
    
      <category term="语言基础" scheme="https://deimo.github.io/tags/%E8%AF%AD%E8%A8%80%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>python多线程技术(一)</title>
    <link href="https://deimo.github.io/2017/08/28/python%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%8A%80%E6%9C%AF(%E4%B8%80)/"/>
    <id>https://deimo.github.io/2017/08/28/python多线程技术(一)/</id>
    <published>2017-08-28T09:35:53.000Z</published>
    <updated>2018-09-26T09:11:33.290Z</updated>
    
    <content type="html"><![CDATA[<p>最近一直在学习python的当中多线程技术，感觉使用起来的确比java要方便多～正好乘这个机会，当作是一个记录和总结了</p><h2 id="多线程技术概述"><a href="#多线程技术概述" class="headerlink" title="多线程技术概述"></a>多线程技术概述</h2><p>1.进程与线程</p><ul><li>进程：正在运行中的程序</li><li>线程：进程中负责程序执行的控制单元（执行路径）一个进程可以有多个执行路径，称之为多线程</li></ul><p>2.多线程技术的意义</p><blockquote><p>开启多个线程是为了同时运行多部分代码，每一个线程都有自己运行的内容。这个内容就是多线程要执行的任务</p></blockquote><p>3.多线程的好处与弊端</p><ul><li><p>好处：解决了多部分程序同时运行的问题，由主机的操作系统给每个进程/线程安排一个小的时间片，</p><p>在所有进程/线程间快速循环，使得每个执行单位都得到CPU的执行时间。</p></li><li><p>弊端：线程太多反而会导致cpu处理效率的降低，这是因为应用程序的执行都是cpu做着快速的切换完成的，而这个切换是随机的。</p></li></ul><p>4.python中多线程局限性（这里的python如不加特殊说明都是指CPython）</p><blockquote><p>Python解释器内部使用了全局解释器锁（GIL），限制了一个Python程序只能在一个CPU核心上运行。</p></blockquote><p>所以…某种以上上…python语言的多线程特性…只是一个美丽的谎言…但这并不足以作为拒绝learning的理由，君不见node.js的异步单线程模型在大部分应用场合下～速度不都是杠杠滴嘛</p><h2 id="创建多线程的方式"><a href="#创建多线程的方式" class="headerlink" title="创建多线程的方式"></a>创建多线程的方式</h2><p>python当中封装了有专门针对多线程解决方案的threading模块，我们对于多线程技术的各种功能需求，都可以在此模块当中找到，挑几个重要的介绍下吧～<img src="/img/pythread/threadingmodule.jpg" alt="threadingmodule"></p><table><thead><tr><th style="text-align:center">类名</th><th style="text-align:center">功能描述</th></tr></thead><tbody><tr><td style="text-align:center">Thread</td><td style="text-align:center">表示控制线程的类，封装线程对象一般属性和开启以及等待方法</td></tr><tr><td style="text-align:center">Lock</td><td style="text-align:center">指令锁，封装由同步所需的获取🔒和释放锁的方法</td></tr><tr><td style="text-align:center">RLock</td><td style="text-align:center">可重入锁，用于同一线程多次获取同一资源，相应地也要release匹配</td></tr><tr><td style="text-align:center">Condition</td><td style="text-align:center">条件变量，常用于生产者，消费者模型中的共享变量控制及线程间的通信</td></tr><tr><td style="text-align:center">Semaphore</td><td style="text-align:center">信号量，内部维护着一个计数器，可用于实现对稀缺资源的控制，如数据库连接池等</td></tr><tr><td style="text-align:center">Event</td><td style="text-align:center">事件，用于</td></tr></tbody></table><p>在python中有两种方式创建线程，以下来自Thread类doc说明</p><blockquote><p>There are two ways to specify the activity: by passing a callable object to the constructor, or by overriding the run() method in a subclass</p></blockquote><p>下面先介绍下Thread对象相关属性和方法，以及线程的创建方式<img src="/img/pythread/thread.png" alt="Thread对象"></p><ul><li><p>第一种方式：继承Thread类并重写run()方法</p><figure class="highlight python"><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="class"><span class="keyword">class</span> <span class="title">MyThread</span><span class="params">(threading.Thread)</span>:</span></span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">  <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">      super().__init__()</span><br><span class="line">        </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">(self)</span>:</span></span><br><span class="line">        s = <span class="number">0</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">30</span>):</span><br><span class="line">            s += i;</span><br><span class="line">            time.sleep(<span class="number">0.1</span>)</span><br><span class="line">        print(s)</span><br><span class="line"> </span><br><span class="line">th = MyThread()</span><br></pre></td></tr></table></figure></li><li><p>第二种方式：在实例化Thread对象时传入线程的运行目标任务（一个函数）</p><figure class="highlight python"><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="function"><span class="keyword">def</span> <span class="title">thfun</span><span class="params">()</span>:</span></span><br><span class="line">    s = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">30</span>):</span><br><span class="line">        s += i;</span><br><span class="line">        time.sleep(<span class="number">0.1</span>)</span><br><span class="line">    print(s)</span><br><span class="line"></span><br><span class="line">th = threading.Thread(target=thfun)</span><br></pre></td></tr></table></figure></li></ul><p>创建线程后即可调用 <strong>start()</strong>方法调用开启线程</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">th.start()</span><br></pre></td></tr></table></figure><h2 id="多线程的运行状态"><a href="#多线程的运行状态" class="headerlink" title="多线程的运行状态"></a>多线程的运行状态</h2><p>引用某大佬的一幅图来解释吧，在此先膜拜下大佬～<img src="/img/pythread/threadstatus.png" alt="threadstatus"></p><h2 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h2><p>受篇幅限制，本次关于python下多线程技术就暂且聊到这里了，太长的文章想必大佬们也没耐心看下去…相信这里的内容对任何有一定语言基础的朋友来说都是再容易不过了🤓～接下来的博主要更新的文章将是多线程技术中的核心与精华（不限语言），暂且剧透下吧😈：指令锁实现同步、条件变量模拟生产者消费者模型、信号量实现对稀缺资源的控制、使用事件实现线程间的通信…敬请期待，预计1～2篇，下次我会po上全部完整代码😎</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近一直在学习python的当中多线程技术，感觉使用起来的确比java要方便多～正好乘这个机会，当作是一个记录和总结了&lt;/p&gt;
&lt;h2 id=&quot;多线程技术概述&quot;&gt;&lt;a href=&quot;#多线程技术概述&quot; class=&quot;headerlink&quot; title=&quot;多线程技术概述&quot;&gt;&lt;/
      
    
    </summary>
    
      <category term="python 多线程" scheme="https://deimo.github.io/categories/python-%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
    
    
      <category term="语言基础" scheme="https://deimo.github.io/tags/%E8%AF%AD%E8%A8%80%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
</feed>
