Quericy Eden*
以白羽为衣,于天际起舞。
2020-06-14T08:10:32.000Z
https://quericy.me/
quericy
Hexo
梅林384路由器安装KodExplorer文件管理器
https://quericy.me/blog/868/
2020-06-14T08:10:32.000Z
2020-06-14T08:10:32.000Z
<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>路由器刷了koolshare梅林的384版,挂上移动硬盘,移植了armv7l的frps适配384软件中心,upx压缩二进制文件,搭好内网穿透……一顿操作下来还感觉差了一块功能–私有云的文件管理.</p>
<p>自带的airDisk功能薄弱,samba的目录和权限配置简单粗暴,易有云倒是挺不错的,但是奔溃太频繁了,也不是很满足个人需求.</p>
<p>这时候就想起以前使用的web文件管理器KodExplorer了,整体安装流程还是比较简单的: <code>外接磁盘上装Enware</code>-><code>opkg安装nginx+php</code>-><code>配置web站点和扩展</code>.</p>
<h1 id="安装步骤"><a href="#安装步骤" class="headerlink" title="安装步骤"></a>安装步骤</h1><p>这篇文章没太多技术含量,主要还是作个备忘,哪天路由再刷机了还能用上。另外koolshare论坛由于有onmp一键脚本,手动搭建的文章比较少,也可供有需要的人参考。</p>
<h2 id="1-安装Entware"><a href="#1-安装Entware" class="headerlink" title="1, 安装Entware"></a>1, 安装Entware</h2><p>安装Entware可参考:<br><a href="https://koolshare.cn/thread-178594-1-1.html" rel="external nofollow" target="_blank">https://koolshare.cn/thread-178594-1-1.html</a></p>
<p>ssh登录路由器,执行<code>entware-setup.sh</code><br><code>ep</code> 安装 Entware packages,选择挂载的磁盘</p>
<p><img src="https://static.quericy.me/1drive/blog/hexo/kod-setup-entware.jpg" alt="enware-setup"></p>
<p><code>e</code>,退出Entware界面</p>
<h3 id="2-安装nginx-php7"><a href="#2-安装nginx-php7" class="headerlink" title="2, 安装nginx+php7"></a>2, 安装nginx+php7</h3><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">opkg update && opkg upgrade</span><br><span class="line">opkg install nginx php7</span><br></pre></td></tr></table></figure>
<h3 id="3-配置nginx站点"><a href="#3-配置nginx站点" class="headerlink" title="3, 配置nginx站点"></a>3, 配置nginx站点</h3><p>1,编辑nginx配置</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">vi /opt/etc/nginx/nginx.conf</span><br></pre></td></tr></table></figure>
<p>在<code>http {</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">include /opt/etc/nginx/sites-enabled/*.conf;</span><br></pre></td></tr></table></figure>
<p>如果要能访问整个路由器的文件,需要以admin角色运行(高权限运行需慎重考虑,暴露到外网请注意风险):</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将 user nobody; 改为admin和root用户组</span></span><br><span class="line">user admin root;</span><br></pre></td></tr></table></figure>
<p>2,添加kod站点配置文件</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">vi /opt/etc/nginx/sites-enabled/kodExplorer.conf</span><br></pre></td></tr></table></figure>
<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><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">server {</span><br><span class="line"> <span class="comment"># 监听http端口号</span></span><br><span class="line"> listen 29080;</span><br><span class="line"> server_name localhost;</span><br><span class="line"> root /opt/share/nginx/kod;</span><br><span class="line"></span><br><span class="line"> location / {</span><br><span class="line"> index index.html index.htm index.php;</span><br><span class="line"> try_files <span class="variable">$uri</span> <span class="variable">$uri</span>/ /index.html;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> error_page 500 502 503 504 /50x.html;</span><br><span class="line"> location = /50x.html {</span><br><span class="line"> root html;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> location ~ \.php$ {</span><br><span class="line"> root /opt/share/nginx/kod;</span><br><span class="line"> fastcgi_pass 127.0.0.1:9000;</span><br><span class="line"> fastcgi_index index.php;</span><br><span class="line"> fastcgi_param SCRIPT_FILENAME <span class="variable">$document_root</span><span class="variable">$fastcgi_script_name</span>;</span><br><span class="line"> include fastcgi_params;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="4-下载kodExplorer"><a href="#4-下载kodExplorer" class="headerlink" title="4, 下载kodExplorer"></a>4, 下载kodExplorer</h3><p><a href="https://kodcloud.com/" rel="external nofollow" target="_blank">kodExplorer</a>可道云(即原来的芒果云),是一款基于php的web文件管理系统,而且附带了各种花式的功能,具体可以见它的官网.</p>
<p>官网: <a href="https://kodcloud.com/" rel="external nofollow" target="_blank">https://kodcloud.com/</a></p>
<p>github仓库: <a href="https://github.com/kalcaddle/KodExplorer" rel="external nofollow" target="_blank">https://github.com/kalcaddle/KodExplorer</a></p>
<p>在线Demo体验地址: <a href="http://demo.kodcloud.com/" rel="external nofollow" target="_blank">http://demo.kodcloud.com/</a></p>
<p>这里直接从github上下载解压到配置好的nginx站点目录下</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">mkdir /opt/etc/nginx/sites-enabled</span><br><span class="line"><span class="built_in">cd</span> /opt/share/nginx && wget https://github.com/kalcaddle/KodExplorer/archive/master.zip -O kod.zip</span><br><span class="line"></span><br><span class="line">unzip kod.zip && chmod +x -R kod/*</span><br></pre></td></tr></table></figure>
<h3 id="5-配置php-amp-安装kodExplorer所需的php扩展"><a href="#5-配置php-amp-安装kodExplorer所需的php扩展" class="headerlink" title="5, 配置php&安装kodExplorer所需的php扩展"></a>5, 配置php&安装kodExplorer所需的php扩展</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">opkg install php7-mod-mbstring php7-mod-curl php7-mod-iconv php7-mod-session php7-mod-json php7-mod-gd</span><br></pre></td></tr></table></figure>
<p>编辑<code>php.ini</code>:</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">vi /opt/etc/php.ini</span><br></pre></td></tr></table></figure>
<p>使用<code>;</code>注释掉<code>doc_root</code>,然后:</p>
<figure class="highlight ini"><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="comment">;配置告警级别和日志</span></span><br><span class="line"><span class="attr">error_reporting</span> = E_ERROR & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED</span><br><span class="line"><span class="attr">log_errors</span> = <span class="literal">On</span></span><br><span class="line"><span class="attr">error_log</span> = /opt/var/log/php_errors.log</span><br><span class="line"><span class="comment">;display_errors = Off</span></span><br><span class="line"></span><br><span class="line"><span class="comment">;配置扩展</span></span><br><span class="line"><span class="attr">extension</span>=session.so</span><br><span class="line"><span class="attr">extension</span>=iconv.so</span><br><span class="line"><span class="attr">extension</span>=curl.so</span><br><span class="line"><span class="attr">extension</span>=mbstring.so</span><br><span class="line"><span class="attr">extension</span>=gd.so</span><br><span class="line"><span class="attr">extension</span>=json.so</span><br></pre></td></tr></table></figure>
<h3 id="6-启动服务"><a href="#6-启动服务" class="headerlink" title="6, 启动服务"></a>6, 启动服务</h3><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">/opt/etc/init.d/S80nginx restart</span><br><span class="line">/opt/etc/init.d/S80php7-cgi restart</span><br></pre></td></tr></table></figure>
<p>访问 <a href="http://192.168.50.1:29080" rel="external nofollow" target="_blank">http://192.168.50.1:29080</a> 可以看到效果:</p>
<p><img src="https://static.quericy.me/1drive/blog/hexo/kod-login-ui.jpg" alt="kod-login-ui"></p>
<p>配合frpc反带到公网再Let’s encrypt绑个泛域名证书,一套外网可访问的私有云文件管理器就搞定了.</p>
<p>整体功能上基本该有的都有了:</p>
<ul>
<li>web界面拖拽上传到挂载磁盘:</li>
</ul>
<p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gfrvm136tkj30xu0qagoz.jpg" alt="file-upload"></p>
<ul>
<li><p>在线播放:<br><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gfrurcwdozj31gr0u07wi.jpg" alt="video-play"></p>
</li>
<li><p>代码高亮&MarkDown预览:</p>
<p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gfruu0r9j8j31as0qmdsj.jpg" alt="markdown-view"></p>
</li>
<li><p>文件加密分享:</p>
</li>
</ul>
<p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gfruyfpsl0j314i0sktfl.jpg" alt="image-20200614153855054"></p>
<ul>
<li>插件(免费版能用的不多)</li>
</ul>
<p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gfrv2cpytkj31as0qmwvs.jpg" alt="image-20200614154240962"></p>
<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>路由器刷了koolshare梅林的384版,挂上移动硬盘,移植了armv7l的frps适配384软件中心,upx压缩二进制文件,搭好内网穿透
记一次数据双写平滑迁移实践
https://quericy.me/blog/867/
2019-03-23T15:52:41.000Z
2019-03-29T12:53:49.000Z
<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>最近在进行业务上的服务化重构,并对其中一块业务进行了优化调整。这里将数据迁移的过程记录下来并进行了简单的整理。</p>
<h3 id="业务背景"><a href="#业务背景" class="headerlink" title="业务背景"></a>业务背景</h3><p>优化调整的是素材分组业务,在业务关系上,<code>店铺->素材分组->素材</code>的三层一对多结构,即每一个拥有者(店铺)可拥有多个不同类型的素材分组,每个素材分组下可挂靠多个素材。</p>
<p>素材分组功能有很深的历史债务,且随着业务的推进,原有的分表设计无法满足业务需求,所以在服务化过程中,对分组相关模块做了技术改造和优化。</p>
<h3 id="历史坑点"><a href="#历史坑点" class="headerlink" title="历史坑点"></a>历史坑点</h3><ul>
<li>分组概念中存在默认分组,且所有店铺共有默认分组,每次查询、移动、删除分组数据时需要实时group by重新计算,给数据库带来较大的慢查压力;</li>
<li>视频类素材和图片音频素材的分组概念一致,但却单独实现业务逻辑和数据模型,同时维护两套分组方案不利于后续业务扩展;</li>
<li>旧分组表使用归属者的店铺id作为数据表分片字段,无法支持非店铺类型的业务方接入素材分组管理服务;</li>
</ul>
<h3 id="优化方案"><a href="#优化方案" class="headerlink" title="优化方案"></a>优化方案</h3><ul>
<li>引入内部id(partnerId)取代原有的数据表分片字段,支持更多维度的业务方接入,作为通用共享服务能力做铺垫;</li>
<li>将默认分组作为一个实际存在的分组记录,初始化时生成默认分组记录,分配单独的分组id,使用类型标识和普通分组做区分判断;</li>
<li>将各种类型(图片、音频、视频)的素材分组进行整合,共用一套分组关系的领域模型,涉及分组id使用新发号器重新发号处理;</li>
<li>拆分不属于分组关系领域的字段(如素材大小),整合到统计领域做分组下素材的增量统计,从根本上消除慢查;</li>
</ul>
<p>下面两张图简述了分组结构上的调整变化(不包含发号器和分组下素材统计部分):</p>
<ul>
<li><p>当前现状<br><img src="https://static.quericy.me/1drive/blog/hexo/category_migrate_before.png" alt="category_migrate_before"></p>
</li>
<li><p>规划方案<br><img src="https://static.quericy.me/1drive/blog/hexo/category_migrate_after.png" alt="category_migrate_after"></p>
</li>
</ul>
<h2 id="分组数据平滑迁移"><a href="#分组数据平滑迁移" class="headerlink" title="分组数据平滑迁移"></a>分组数据平滑迁移</h2><h3 id="数据表结构设计"><a href="#数据表结构设计" class="headerlink" title="数据表结构设计"></a>数据表结构设计</h3><p>对分组的优化将需要新的数据表结构来支撑,我们需要将原有分组表的数据迁移到新表。<br>涉及迁移的表包括分组表和分组下素材关联关系表。</p>
<ul>
<li>分组表数据量较小(百万级别),主要改动面为单表拆分为分片表,以及分组id重新发号;</li>
<li>分组关系表数据量较大(10亿级别),主要改动面为分片键和分组id变更,素材id保持不变;<br>下图为新旧表的结构(部分字段名和注释已重命名和脱敏处理):<br><img src="https://static.quericy.me/1drive/blog/hexo/mysql_table_migrated.png" alt="mysql_table_migrated"></li>
</ul>
<h3 id="数据迁移方案设计"><a href="#数据迁移方案设计" class="headerlink" title="数据迁移方案设计"></a>数据迁移方案设计</h3><p>因为迁移周期会比较长,在数据迁移期间,需要确保现有服务正常运行及迁移后数据的一致性,所以需要确认一下数据平滑迁移方案。</p>
<p>由于在迁移过程需要保持服务,离线迁移不在考虑范围内。日志追加法受限于重新分配发号的原因难以记录映射关系,而常规的双写方案稍加改造是能适用于这次的数据迁移场景的。所以我们使用带切换标记的双写法进行数据迁移。</p>
<h4 id="双写数据迁移"><a href="#双写数据迁移" class="headerlink" title="双写数据迁移"></a>双写数据迁移</h4><p>数据迁移分为存量数据迁移和增量数据迁移两个部分,存量数据整理比较容易,主要精力集中在增量数据的处理方式上。</p>
<h5 id="1,维护迁移分组映射关系"><a href="#1,维护迁移分组映射关系" class="headerlink" title="1,维护迁移分组映射关系"></a>1,维护迁移分组映射关系</h5><p>追踪源表分组和关联关系的增删改比较困难,但是创建一个临时表用以维护新旧表的<code>分组映射关系</code>相对比较容易:</p>
<table>
<thead>
<tr>
<th>字段</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>old_category_id</td>
<td>老分组id</td>
</tr>
<tr>
<td>new_category_id</td>
<td>新分组id</td>
</tr>
<tr>
<td>partner_id</td>
<td>合作方id</td>
</tr>
</tbody>
</table>
<p>有了映射关系,在双写时就可以针对旧分组的操作,找到对应的新分组进行双写</p>
<h5 id="2,迁移粒度细化"><a href="#2,迁移粒度细化" class="headerlink" title="2,迁移粒度细化"></a>2,迁移粒度细化</h5><p>对整个表的存量数据刷入和增量数据维护的成本较大,但是在业务上分组是有归属者的(旧表归属于某个店铺id,新表扩充到归属于某个合作方id)。所以再创建一个临时表用以维护<code>迁移进度</code>,将迁移粒度细化到每个合作方id/店铺id:</p>
<table>
<thead>
<tr>
<th>字段</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>shop_id</td>
<td>店铺id</td>
</tr>
<tr>
<td>partner_id</td>
<td>合作方id</td>
</tr>
<tr>
<td>status</td>
<td>迁移状态(双写开关)</td>
</tr>
</tbody>
</table>
<h5 id="3,数据迁移和双写切流开关"><a href="#3,数据迁移和双写切流开关" class="headerlink" title="3,数据迁移和双写切流开关"></a>3,数据迁移和双写切流开关</h5><p>所以数据迁移的主体过程是,按照每个店铺id/合作方id,依次对该店铺下所拥有的分组数据进行迁移,在迁移时调用发号器对分组id重新发号并生成对应的<code>分组映射关系</code>记录。在迁移完成并完成一致性校验后,将该店铺/合作方的<code>迁移进度</code>记录的状态标记为”完成”。这里的”迁移状态”作为双写的切流开关,具体表现为:</p>
<p>在分组和分组关系的写逻辑上,对所有已完成迁移的店铺和新增的店铺,启用双写逻辑(确保增量数据后续一致),未完成的跳过双写逻辑(只写旧表,因为未迁移完对增量数据无需关注)。</p>
<p>分组和分组关系的读逻辑仍然读旧表,以确保现有业务的不受影响。</p>
<h4 id="数据一致性校验"><a href="#数据一致性校验" class="headerlink" title="数据一致性校验"></a>数据一致性校验</h4><h5 id="数据检查"><a href="#数据检查" class="headerlink" title="数据检查"></a>数据检查</h5><p>在数据迁移过程中,每个拥有者(店铺/合作方)迁移完成后,会进行数据一致性校验。这是由于迁移的过程中仍然有写操作的流量进来,正在迁移的部分老分组和关系数据还有可能发生变化。所以每个迁移完成的店铺,会有至少一次的数据一致性校验。</p>
<p>数据一致性校验的对比方式由具体业务决定,尽量统计多个维度来确保数据的正确性以及发现数据迁移是否有被忽视的问题。</p>
<p>如果一切顺利,数据迁移任务完成后一致性检查通过,即可将状态标识为迁移完成,此时该店铺的分组写操作将启用双写逻辑(先按现有逻辑写旧表,再根据映射关系写新表);</p>
<p>如果比对不一致,则可能迁移时源数据有变更,需要进行数据订正,并在再次重试数据检查操作。如果变更频繁可能会失败多次,这时可以延后重试或者在凌晨等流量低峰期进行重试操作。</p>
<h5 id="数据修正"><a href="#数据修正" class="headerlink" title="数据修正"></a>数据修正</h5><p>数据修正方式有很多,比如重做迁移期间操作记录进行补偿,或者清空该店铺/合作方的迁移数据并重做迁移。由于读写都走的旧数据表,清空重做迁移对现有业务无影响,且实际业务场景下出现的频率不高,这里直接使用清空并重做的方式进行数据修正。</p>
<h4 id="读逻辑切换和表下线"><a href="#读逻辑切换和表下线" class="headerlink" title="读逻辑切换和表下线"></a>读逻辑切换和表下线</h4><p>当所有的店铺迁移完成且校验一致后,此时新旧表的记录已经按照预订的映射关系保持一致,且写操作双写也已经全量,读操作仍然在读取旧数据表。<br>接下来持续观察一段时间(一周左右),并可多次执行数据检查任务,待观察期后新表数据依旧稳定正确,即可将读逻辑切换到读新表。</p>
<p>读取逻辑回归无问题后,再移除双写中旧表、<code>分组映射关系</code>表和<code>迁移进度</code>表的读写逻辑,并下线对应的数据表。</p>
<p>至此,通过双写进行的数据平滑迁移完成。回顾下整个过程的大致流程如下:<br><img src="https://static.quericy.me/1drive/blog/hexo/migrated_BPMN.png" alt="migrated_BPMN"></p>
<p>实际上,数据迁移双写的流程归纳总结起来都大同小异,我们需要关注的是根据业务实际需求,做一些最贴合业务场景的调整。</p>
<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>最近在进行业务上的服务化重构,并对其中一块业务进行了优化调整。这里将数据迁移的过程记录下来并进行了简单的整理。</p>
<h3 id="业务
七牛域名回收后的文件导出和迁移
https://quericy.me/blog/866/
2018-10-09T10:06:47.000Z
2019-03-24T12:58:12.000Z
<h1 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h1><h2 id="七牛回收测试域名"><a href="#七牛回收测试域名" class="headerlink" title="七牛回收测试域名"></a>七牛回收测试域名</h2><p>最近七牛在回收<code>qbox.me</code>的测试子域名,然而很不幸博客图床当初注册七牛使用的是匿名邮箱,转发后全进了垃圾箱。关注到这个事情的时候域名已经回收。</p>
<p>更尴尬的是,如果存储空间绑定的七牛测试域名被回收,不仅无法访问资源,而且登录到七牛后台也是无法查看和下载COS中的文件。除非新绑定融合CDN域名且主域通过备案(即使只用海外CDN加速节点)。</p>
<p>由于短期内没有做这事的计划,所以决定将博客的静态资源和图片从七牛迁移到其他图床,不过首先得导出COS上的存量文件。</p>
<h1 id="未绑定域名的七牛存储空间文件导出"><a href="#未绑定域名的七牛存储空间文件导出" class="headerlink" title="未绑定域名的七牛存储空间文件导出"></a>未绑定域名的七牛存储空间文件导出</h1><h2 id="创建新的存储空间"><a href="#创建新的存储空间" class="headerlink" title="创建新的存储空间"></a>创建新的存储空间</h2><p>由于源空间已经没有域名了,在不绑定自定义域名的情况下是无法获取到COS的文件的。<br>而新创建的存储空间会带一个30天有效期的七牛测试域名(见七牛文档:<a href="https://developer.qiniu.com/fusion/kb/1319/test-domain-access-restriction-rules" rel="external nofollow" target="_blank">测试域名使用规范</a>)<br>所以可以新创建一个存储空间,将源空间下的资源复制到新空间中,借用新空间的测试域名,将资源拉取出来。</p>
<p>在管理后台新建一个公开的存储空间,假定创建的空间名是<code>target-bucket</code>,存储区域选择和源空间相同,就能在<code>空间概览-融合CDN测试域名</code>中看到测试域名了,这个域名后面批量下载时会用到。</p>
<h2 id="使用qshell导出文件"><a href="#使用qshell导出文件" class="headerlink" title="使用qshell导出文件"></a>使用qshell导出文件</h2><p>七牛的qshell是利用七牛文档上公开的API实现的一个方便开发者测试和使用七牛API服务的命令行工具,文档可见:<a href="https://github.com/qiniu/qshell" rel="external nofollow" target="_blank">https://github.com/qiniu/qshell</a><br>添加账户鉴权信息(AK/SK)照文档说明即可,这里略去不提。</p>
<h4 id="1-导出源空间下的所有文件清单"><a href="#1-导出源空间下的所有文件清单" class="headerlink" title="1,导出源空间下的所有文件清单"></a>1,导出源空间下的所有文件清单</h4><p>使用<a href="https://github.com/qiniu/qshell/blob/master/docs/listbucket.md" rel="external nofollow" target="_blank">listbucket</a>命令,如果文件实在太多的话也可以考虑用<a href="https://github.com/qiniu/qshell/blob/master/docs/listbucket2.md" rel="external nofollow" target="_blank">listbucket2</a></p>
<p>这里假定要导出的源空间名称是<code>source-bucket</code>,导出的清单文件名是<code>backup.log</code><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">qshell -m listbucket <span class="built_in">source</span>-bucket backup.log</span><br></pre></td></tr></table></figure></p>
<h3 id="2-提取清单文件中的路径"><a href="#2-提取清单文件中的路径" class="headerlink" title="2.提取清单文件中的路径"></a>2.提取清单文件中的路径</h3><p>清单文件大致是这样的格式:<br><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">hello.jpg 1710619 FlUqUK7zqbqm3NPwzq2q7TMZ-Ijs 14209629320769140 image/jpeg 1</span><br><span class="line">hello.mp4 8495868 lns2dAHvO0qYseZFgDn3UqZlMOi- 14207312835630132 video/mp4 0</span><br><span class="line">hhh 1492031 FjiRl_U0AeSsVCHXscCGObKyMy8f 14200176147531840 image/jpeg 1</span><br><span class="line">jemygraw.jpg 1900176 FtmHAbztWfPEqPMv4t4vMNRYMETK 14208960018750329 application/octet-stream 1 QiniuAndroid</span><br></pre></td></tr></table></figure></p>
<p>后续步骤只需要路径字段,可以在IDE里用正则匹配过滤一下,并将分组1整理输出来:<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">^([^\s]+)\s+.+\n</span><br></pre></td></tr></table></figure></p>
<p>因为路径字段在第一列,也可以直接使用<code>awk</code>命令更为方便(这里排除了第一行表头):<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">awk <span class="string">'NR>1 {print $1}'</span> backup.log > file-list.log</span><br></pre></td></tr></table></figure></p>
<p>整理的文件路径清单保存在<code>file-list.log</code>中</p>
<h3 id="3-将清单中的文件拷贝到新的存储空间"><a href="#3-将清单中的文件拷贝到新的存储空间" class="headerlink" title="3.将清单中的文件拷贝到新的存储空间"></a>3.将清单中的文件拷贝到新的存储空间</h3><p>使用qshell的<a href="batchcopy">https://github.com/qiniu/qshell/blob/master/docs/batchcopy.md</a>命令,将清单文件从源空间拷贝到目标空间:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">qshell -m batchcopy <span class="built_in">source</span>-bucket target-bucket file-list.log</span><br></pre></td></tr></table></figure></p>
<p>这一步会要求输入一个验证码来确认操作,按指示说明即可。</p>
<h3 id="4-批量下载文件"><a href="#4-批量下载文件" class="headerlink" title="4.批量下载文件"></a>4.批量下载文件</h3><p>其实到了这一步,已经能在后台管理页面的新存储空间<code>target-bucket</code>中看到文件并手动下载了<br>当然如果不愿意一个个手动下载的话,可以使用<a href="https://github.com/qiniu/qshell/blob/master/docs/qdownload.md" rel="external nofollow" target="_blank">qdownload</a>命令批量下载。<br>在下载之前需要先创建一个下载配置文件:<br><figure class="highlight"><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><br><span class="line"> "cdn_domain" : "xxx.bkt.clouddn.com",//cdn域名,即新创建的存储空间分配的测试域名</span><br><span class="line"> "dest_dir" : "/path/to/downloadDir",//批量下载的目标目录</span><br><span class="line"> "bucket" : "target-bucket",//存储空间名称</span><br><span class="line"> "log_file" : "download.log",//下载日志输出文件</span><br><span class="line"> "log_level" : "info",//下载日志输出级别</span><br><span class="line"> "log_stdout" : true</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>注意上述配置中需要设置<code>cdn_domain</code>才可以使用七牛的10G免费流量</p>
<p>10线程并发下载:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">qshell -m qdownload 10 qdisk_down.conf</span><br></pre></td></tr></table></figure></p>
<p>这样就成功把存储空间下的所有文件下载到本地了</p>
<h1 id="新图床选择"><a href="#新图床选择" class="headerlink" title="新图床选择"></a>新图床选择</h1><p>新图床的选择花费了一点时间,各方面了解了下以后,决定利用现有闲置资源将OneDrive改造为一个私人图床,比较成熟的解决方案是<a href="https://github.com/donwa/oneindex" rel="external nofollow" target="_blank">OneIndex</a>,大致看了下实现方式:</p>
<ul>
<li>通过创建Azure AD应用程序,获取OneDrive的SharePoint Online访问授权</li>
<li>OneIndex应用程序通过留存的<code>refreshToken</code>,定时刷新维护<code>accessToken</code></li>
<li>在网盘中维护单独的目录结构,访问资源时通过302跳转到解析的对应资源文件访问路径,且携带临时的资源访问授权信息</li>
</ul>
<p>而且这东西也有Docker,部署起来更为方便。</p>
<h1 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h1><h2 id="七牛回收测试域名"><a href="#七牛回收测试域名" class="headerlink" title="七牛回收测试域名"
博客获得GFW认证
https://quericy.me/blog/865/
2017-10-19T15:32:24.000Z
2017-10-19T15:32:24.000Z
<p>如题所示。。。昨天发现ip挂了,traceroute一下果然被认证。。今天抽空折腾了下</p>
<p>嘛,就这样吧,现在暂时可以访问了,不知道能坚持多久。。</p>
<p>实在不行以后git hook同步后直接改到github pages上吧</p>
<p>如题所示。。。昨天发现ip挂了,traceroute一下果然被认证。。今天抽空折腾了下</p>
<p>嘛,就这样吧,现在暂时可以访问了,不知道能坚持多久。。</p>
<p>实在不行以后git hook同步后直接改到github pages上吧</p>
OSX Sierra/Windows10双系统踩坑记录
https://quericy.me/blog/864/
2017-06-25T07:10:24.000Z
2017-06-25T07:10:24.000Z
<!--toc-->
<h1 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h1><p>MacBook作为办公和编程使用还是极为优秀的,但OSX和Windows毕竟是两套软件生态。出于一些互补的软件需求,以及游戏性的考虑(恩…其实最重要的还是能玩游戏),还是有必要再装个Windows。虽然OSX上有VMWare和Parallels这类优秀的虚拟机软件,但仍然无法完全发挥出硬件性能。所以最终,还是决定折腾一下在MacbookPro上安装双系统,并记录安装和踩坑过程,用于备忘与分享。</p>
<h1 id="OSX-Sierra-Win10双系统"><a href="#OSX-Sierra-Win10双系统" class="headerlink" title="OSX Sierra/Win10双系统"></a>OSX Sierra/Win10双系统</h1><p>之前有转过小伙伴折腾<a href="https://quericy.me/blog/425/">2014新款retina MacBooK双系统</a>的记录,但是方法已经过时。谷歌了一下,发现现在通过BootCamp安装双系统已经有非常成熟便捷和友好的方案,不需要U盘,不需要复杂的操作,只需要一个win10原版镜像和能联网即可。</p>
<h3 id="前置准备"><a href="#前置准备" class="headerlink" title="前置准备"></a>前置准备</h3><ul>
<li>在<a href="http://msdn.itellyou.cn/" rel="external nofollow" target="_blank">MSDN I Tell You</a>中下载所需版本的win10镜像,检查下Hash和Sha1是否和微软发布的一致</li>
<li>Mac连接互联网和外接电源,在<code>系统设置-节能</code>中关闭硬盘自动休眠,防止下载中断</li>
<li>如果担心玩脱,可以做个Time Machine备份</li>
</ul>
<h3 id="BootCamp分区和下载驱动"><a href="#BootCamp分区和下载驱动" class="headerlink" title="BootCamp分区和下载驱动"></a>BootCamp分区和下载驱动</h3><ul>
<li>在Spotlight中搜索<code>BootCamp助理</code>并打开</li>
<li>继续并选择下载好的win10安装镜像</li>
<li>设置分区大小,分区设定了就不能改,所以需要考虑清楚给Windows分配多大的空间;</li>
<li>点击安装,会自动分区并在线下载所需驱动,完成后自动重启</li>
</ul>
<h3 id="安装Windows10"><a href="#安装Windows10" class="headerlink" title="安装Windows10"></a>安装Windows10</h3><ul>
<li><p>重启后进入安装界面:<br><img src="https://static.quericy.me/1drive/blog/hexo/1-setup.jpg" alt="setup-windows"></p>
</li>
<li><p>具体步骤就如同普通安装windows一样,略去不提。需要注意的一点是,分区的时候,选择BOOTCAMP分区,格式化,其他的无需调整。<br><img src="https://static.quericy.me/1drive/blog/hexo/2-format.jpg" alt="format-windows"></p>
</li>
<li><p>一路下去,很顺利的就能完成安装。<br><img src="https://static.quericy.me/1drive/blog/hexo/3-windows.jpg" alt="install-complete"></p>
</li>
</ul>
<h3 id="双系统切换方法"><a href="#双系统切换方法" class="headerlink" title="双系统切换方法"></a>双系统切换方法</h3><p>重启时按住<code>option</code>键(即<code>alt</code>键),直到BootCamp选择磁盘界面,左边OSX右边Windows(鼠标和键盘均可用)<br><img src="https://static.quericy.me/1drive/blog/hexo/4-switchSystem.jpg" alt="switch-system"></p>
<h1 id="双系统蓝牙鼠标配对问题"><a href="#双系统蓝牙鼠标配对问题" class="headerlink" title="双系统蓝牙鼠标配对问题"></a>双系统蓝牙鼠标配对问题</h1><p>双系统安装很顺利,鼠标问题上反倒是踩了不少坑。</p>
<p>首先在BootCamp选择界面可以使用蓝牙鼠标,进了windows反而无法使用。在系统设置中的蓝牙搜索,可以发现鼠标,但一致卡在无法连接上。</p>
<p>排查了一圈推测应该是系统驱动的逻辑判断上不完善,已经配对过的蓝牙设备重新搜寻配对无法刷新PIN码。解决方法如下:</p>
<ul>
<li>进入OSX,在<code>系统偏好设置-鼠标-设置蓝牙鼠标</code>中,删除蓝牙鼠标;</li>
<li>进入Windows,右键单击<code>开始按钮</code>,找到<code>控制面板-设备和打印机-添加设备</code>,此时激活鼠标上的蓝牙配对功能,能搜索到鼠标;</li>
<li>右键鼠标图标,在Bluetooth服务 标签页中勾选”键盘和鼠标驱动HID”(win10 无需此步);</li>
<li>回到搜索到鼠标的对话框中,配对蓝牙鼠标,即可成功连接。</li>
<li>重启进入OSX,激活鼠标上的蓝牙配对功能,重新添加配对蓝牙鼠标,即可双系统使用蓝牙鼠标;</li>
</ul>
<h1 id="蓝牙鼠标受网卡干扰延迟卡顿"><a href="#蓝牙鼠标受网卡干扰延迟卡顿" class="headerlink" title="蓝牙鼠标受网卡干扰延迟卡顿"></a>蓝牙鼠标受网卡干扰延迟卡顿</h1><p>使用了一会儿,便明显感觉到Windows上的鼠标指针卡顿和延迟很严重,在有下载流量的时候,更是如同自带了100的ping一样。。。</p>
<p>按照此思路排查了包括关闭省电模式在内的一堆无用选项,找了很多资料,最终确定问题发生在无线网卡和蓝牙模块上。这个问题不仅仅是Mac安装Windows10时会出现,一些无线网卡和蓝牙模块共用的Windows设备都有可能发生蓝牙鼠标卡顿延迟。</p>
<ul>
<li>右键单击<code>开始按钮</code>,打开<code>设备管理器</code>-<code>网络适配器</code>,找到类似<code>Broadcom 802.11ac Network Adapter</code>的网络适配器(不同硬件名称上大同小异)</li>
<li>右键-属性-高级,找到属性: Bluetooth协作(即Bluetooth Collaboration),右侧值修改为”禁用”,并点击确定</li>
<li>此时网络会断开重启,鼠标便可恢复如丝般顺滑:<br><img src="https://static.quericy.me/1drive/blog/hexo/5-bluetooth.png" alt="bluetooth-collaboration"></li>
</ul>
<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://www.macx.cn/thread-2182916-1-1.html" rel="external nofollow" target="_blank">(不需要U盘版)新Bootcamp Mac 安装Win10…</a></p>
<p><a href="http://tieba.baidu.com/p/2899871269" rel="external nofollow" target="_blank">巧妙解决蓝牙鼠标、蓝牙键盘卡顿冲突办法</a></p>
<!--toc-->
<h1 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h1><p>MacBook作为办公和编程使用还是极为优秀的,但OSX和Windows毕竟是两套软件生态。出于一些互补的软件需求,以
再见,多说
https://quericy.me/blog/863/
2017-05-28T07:05:24.000Z
2017-05-28T07:05:24.000Z
<!--toc-->
<h1 id="多说关闭"><a href="#多说关闭" class="headerlink" title="多说关闭"></a>多说关闭</h1><p>国内的博客评论服务就那么几家,做的最好的就是多说了。对第三方SNS帐号的整合,符合国人的操作习惯,以及免费特性等,多说在国内算是做的比较出色的。虽然多说的问题也很多,如垃圾评论,头像https,嵌套评论脏数据,其他各种bug等等…但是这些都不重要了。由于没有找到盈利模式,今年(2017)的6月1号多说将正式关停服务。</p>
<h1 id="选择disqus"><a href="#选择disqus" class="headerlink" title="选择disqus"></a>选择disqus</h1><p>唏嘘过后,还是要解决问题。多说既然关了,迁移自然也就提上日程。选择性不多,最后还是选择了disqus。</p>
<p>disqus在综合各方面来看,其实都比多说要好,最大的问题是,disqus由于不可描述的原因,在国内是无法访问的。但是还是有解决方案的。</p>
<p>由于懒癌犯了,再加上最近确实没太多精力,一直拖到现在才开始折腾博客评论的迁移工作。</p>
<h1 id="数据迁移"><a href="#数据迁移" class="headerlink" title="数据迁移"></a>数据迁移</h1><p>当初从wordpress迁移到hexo费了不少劲,迁移评论也差不多。果然该来的总会来的,这块的工作量也是逃不掉。</p>
<h2 id="迁移工具"><a href="#迁移工具" class="headerlink" title="迁移工具"></a>迁移工具</h2><p>网上一堆版本,我就试了其中两个工具,将从多说后台导出数据转换为disqus支持的Generic (WXR)格式:</p>
<ul>
<li><a href="https://github.com/JamesPan/duoshuo-migrator" rel="external nofollow" target="_blank">JamesPan/duoshuo-migrator</a> :python版转换工具, <a href="https://blog.jamespan.me/2015/04/18/goodbye-duoshuo/" rel="external nofollow" target="_blank">JamesPan的博客</a> 中对迁移过程描述的比较细致,可惜工具导入时报错,而且似乎也疏于维护了,便换用下面的PHP版迁移工具;</li>
<li><a href="https://gist.github.com/quericy/42fdf93781c6ae08a324c3eb9e9a0ed3#file-duoshuo-migrate-to-disqus-php" rel="external nofollow" target="_blank">duoshuo-migrate-to-disqus</a> :取自 <a href="https://urouge.github.io/migrate-to-disqus/" rel="external nofollow" target="_blank">Urouge的博客</a> 中的PHP版本迁移工具 ,在迁移时遇到点问题,PHP的改起来比较快捷,便修改了一下整到gist中;</li>
</ul>
<p>迁移工具的操作方式,上述作者的博客中都有了详细的叙述,这里就略去不提。</p>
<h2 id="踩坑记录"><a href="#踩坑记录" class="headerlink" title="踩坑记录"></a>踩坑记录</h2><ul>
<li>threads里空url会导致导入时报错。这个是多说后台的脏数据,一些不存在的文章(或者草稿)虽然在后台里处理删除过了,但是在导出的json中仍然存在,甚至还有循环嵌套的上级评论id。多说的历史数据确实问题不小,导出的数据基本都要检查处理一下。</li>
<li>换行符问题。转换为xml,导入disqus时,估计disqus导入逻辑有bug,一些文章的特殊情景的换行符<code>\n</code>无法正确识别,会报错<code>XML syntax error</code>并终止导入。折腾了一会,发现替换为<code>\r</code>可以避免这个bug,并且导入disqus后会被转换为<code><br></code>标签,对评论的换行没有影响。</li>
<li>disqus对域名需要精确匹配的,于是在脚本中添加匹配,对自己的域名做了匹配,统一https协议头和最后的<code>/</code> (非必需,代码中已注释);</li>
</ul>
<h2 id="遗留问题"><a href="#遗留问题" class="headerlink" title="遗留问题"></a>遗留问题</h2><ul>
<li>导入到disqus的评论都是匿名评论,且没有头像。这个是由于disqus的机制决定的,disqus提供的api只能导入匿名评论,而且,哪怕是disqus自己导出的评论,也不支持再次重新导入。</li>
<li>由于上面这个原因,连博主本人的评论也只能作为匿名评论导入。但是api是支持导入自己的信息的(见<a href="https://help.disqus.com/customer/portal/articles/472150-custom-xml-import-format" rel="external nofollow" target="_blank">官方文档中的Format部分注释</a>),翻了一圈文档,需要SSO登录才能带上自己的头像等信息。但是,SSO单点登录在disqus中需要Pro订阅才行,只能作罢。</li>
</ul>
<h1 id="disqus代理"><a href="#disqus代理" class="headerlink" title="disqus代理"></a>disqus代理</h1><h2 id="代理方式"><a href="#代理方式" class="headerlink" title="代理方式"></a>代理方式</h2><p>使用PHP的curl代理转发请求。利用disqus公开的api,获取文章信息和评论列表,使用其半公开的一个public的token,发表匿名评论。前端部分构造评论框,修改请求地址,实现查看和发布匿名评论的基本功能。如果用户能够访问disqus,或者需要更高级别的操作时(如登录发表评论),再加载原始的评论框。</p>
<h2 id="创建代理"><a href="#创建代理" class="headerlink" title="创建代理"></a>创建代理</h2><p>感谢<a href="https://github.com/fooleap" rel="external nofollow" target="_blank">fooleap</a>的<a href="https://github.com/fooleap/disqus-php-api" rel="external nofollow" target="_blank">disqus-php-api</a>,是现阶段比较完善的一个代理disqus的解决方案了。后端使用PHP,降低了搭建难度。毕竟相比于VPS,还有大量的php主机空间可供使用,能带来更多高速和低成本的选择。</p>
<h2 id="解决问题"><a href="#解决问题" class="headerlink" title="解决问题"></a>解决问题</h2><p>搭建过程还算顺利,遇到了几个问题记录一下备忘:</p>
<ul>
<li><p>由于是从Hexo的主题tranquilpeak移植魔改的,手动剔除了tranquilpeak的disqus代码,单独加载css和js。虽然css多了一个请求,但js使用简易评论框加载的东西还更少了。之前需要手动在Markdown中设置每篇文章的identifier作为唯一性标识ID,使用新的接口匹配的是url,解除了对identifierID的依赖,看起来更好了。但是要是换域名的话,可能会有麻烦。</p>
</li>
<li><p>curl相关函数CURLOPT_SSL_VERIFYPEER需要置为false。部分PHP空间支持HTTPS,但是不能很好的检查SSL证书,SSL certificate problem将导致curl结果为null;</p>
</li>
<li><p>修改选择匹配,解决了当页面中有名为<code>comment</code>的class时的问题。不过目前评论框使用comment作为选择器id,不够灵活,毕竟每个人的博客都不一样,其他地方要是也有一样的id会出问题。</p>
</li>
<li><p>修改comment-header的兼容问题,不确定是否通用;移动端貌似也有点问题,但是移动端涉及的设备太多了,没法兼顾。</p>
</li>
</ul>
<!--toc-->
<h1 id="多说关闭"><a href="#多说关闭" class="headerlink" title="多说关闭"></a>多说关闭</h1><p>国内的博客评论服务就那么几家,做的最好的就是多说了。对第三方SNS帐号的整合,符合国人的操作习惯,以及
PHP 常见排序算法
https://quericy.me/blog/862/
2017-04-03T09:05:24.000Z
2017-04-03T09:05:24.000Z
<!--toc-->
<div class="alert success"><p>总算闲下来了,梳理了一下上个月面试相关的东西,发现以前收集的OneNote笔记里,算法这块记的比较凌乱,就把常见排序算法整理成MarkDown的,按时间复杂度区分,顺带写了Demo。嗯,还有,每个Demo的测试部分…仅供娱乐,因为shuffle出来的数组总是在best case和worst case之间,没有太大的实用性。</p>
</div>
<h2 id="O-n²-排序"><a href="#O-n²-排序" class="headerlink" title="O(n²)排序"></a>O(n²)排序</h2><h3 id="冒泡排序-Bubble-Sort"><a href="#冒泡排序-Bubble-Sort" class="headerlink" title="冒泡排序 Bubble Sort"></a>冒泡排序 Bubble Sort</h3><ul>
<li>best case: 顺序时,O(n)。worst case: 逆序时,O(n²)</li>
<li>依次比较相邻的两个数,然后根据大小做出排序,直至最后两位数</li>
<li>在排序过程中总是小数往前放,大数往后放</li>
<li>交换算法中,按位异或效率与临时变量相当但是异或更省空间</li>
<li>按位异或和临时变量交换的效率均大于php的<code>list($a,$b)=[$b,$a]</code></li>
<li>按位异或的三个特点:<ol>
<li>0^1=1 0^0=0 =>因此,0异或任何数等于任何数本身</li>
<li>1^0=1 1^1=0 =>因此,1异或任何数等于任何数取反</li>
<li>任何数异或自己=>把自己置0</li>
</ol>
</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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="comment">#冒泡排序</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bubble_sort</span><span class="params">($arr)</span></span><br><span class="line"></span>{</span><br><span class="line"> <span class="keyword">for</span> ($i = <span class="number">0</span>; $i < count($arr); $i++) {</span><br><span class="line"> <span class="keyword">for</span> ($k = $i + <span class="number">1</span>; $k < count($arr); $k++) {</span><br><span class="line"> <span class="keyword">if</span> ($arr[$i] > $arr[$k]) {</span><br><span class="line"> bubble_swap($arr[$i], $arr[$k]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $arr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bubble_swap</span><span class="params">(&$a, &$b)</span></span><br><span class="line"></span>{</span><br><span class="line"> <span class="comment">//按位异或进行变量交换</span></span><br><span class="line"> $a = $a ^ $b;</span><br><span class="line"> $b = $a ^ $b;<span class="comment">//(a^b)^b => a^(b^b) => a^1 => a</span></span><br><span class="line"> $a = $b ^ $a;<span class="comment">//a^(a^b) => (a^a)^b => 1^b => b</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Test(耗时370ms~460ms)</span></span><br><span class="line">$a = array_rand(range(<span class="number">1</span>, <span class="number">3000</span>), <span class="number">1500</span>);</span><br><span class="line">shuffle($a); <span class="comment">//获取已经打乱了顺序的数组</span></span><br><span class="line">$t1 = microtime(<span class="keyword">true</span>);</span><br><span class="line">bubble_sort($a); <span class="comment">//排序</span></span><br><span class="line">$t2 = microtime(<span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">echo</span> (($t2 - $t1) * <span class="number">1000</span>) . <span class="string">'ms'</span>;</span><br><span class="line"><span class="keyword">die</span>;</span><br></pre></td></tr></table></figure>
<h3 id="选择排序-Selection-Sort"><a href="#选择排序-Selection-Sort" class="headerlink" title="选择排序 Selection Sort"></a>选择排序 Selection Sort</h3><ul>
<li>best case: O(n²)。worst case: O(n²)</li>
<li>遍历选出最小数放到开头,再从剩余未排序元素中继续寻找最小数放到已排序序列的末尾</li>
<li>不通过两两交换,而是记下最小数,偏移之后,把最小的数放回开头</li>
<li>由于两两交换的成本比偏移高,所以一般来说,选择排序比冒泡好</li>
<li>选择排序对右半边(unsorted)偏移</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#选择排序</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">selection_sort</span><span class="params">($arr)</span></span><br><span class="line"></span>{</span><br><span class="line"> $length = count($arr);</span><br><span class="line"> <span class="keyword">for</span> ($i = <span class="number">0</span>; $i < $length - <span class="number">1</span>; $i++) {</span><br><span class="line"> <span class="comment">//初始化最小数</span></span><br><span class="line"> $min = $i;</span><br><span class="line"> <span class="comment">//找到未排序部分的最小数的序号</span></span><br><span class="line"> <span class="keyword">for</span> ($j = $i + <span class="number">1</span>; $j < $length; $j++) {</span><br><span class="line"> <span class="keyword">if</span> ($arr[$j] < $arr[$min]) $min = $j;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//不同则交换</span></span><br><span class="line"> <span class="keyword">if</span> ($min != $i) {</span><br><span class="line"> selection_swap($arr[$i], $arr[$min]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $arr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">selection_swap</span><span class="params">(&$a, &$b)</span></span><br><span class="line"></span>{</span><br><span class="line"> <span class="comment">//按位异或进行变量交换</span></span><br><span class="line"> $a = $a ^ $b;</span><br><span class="line"> $b = $a ^ $b;</span><br><span class="line"> $a = $b ^ $a;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Test(耗时100~130ms)</span></span><br><span class="line">$a = array_rand(range(<span class="number">1</span>, <span class="number">3000</span>), <span class="number">1500</span>);</span><br><span class="line">shuffle($a); <span class="comment">//获取已经打乱了顺序的数组</span></span><br><span class="line">$t1 = microtime(<span class="keyword">true</span>);</span><br><span class="line">selection_sort($a); <span class="comment">//排序</span></span><br><span class="line">$t2 = microtime(<span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">echo</span> (($t2 - $t1) * <span class="number">1000</span>) . <span class="string">'ms'</span>;</span><br><span class="line"><span class="keyword">die</span>;</span><br></pre></td></tr></table></figure>
<h3 id="插入排序-Insertion-Sort"><a href="#插入排序-Insertion-Sort" class="headerlink" title="插入排序 Insertion Sort"></a>插入排序 Insertion Sort</h3><ul>
<li>best case: 顺序时,O(n)。worst case: 逆序时,O(n²)</li>
<li>选取一个数,保证这个数之前的数组已经排序好,把这个数插入到之前的数组中合适的位置</li>
<li>对于要插入的数据,在已排序序列中从后向前扫描</li>
<li>插入排序对左半边(sorted)偏移</li>
<li>插入排序只要找到左半边里合适的插入位置就能停下来,所以一般来说,它比选择排序好</li>
<li>算法步骤:<ol>
<li>从第一个元素开始,该元素可以认为已经被排序</li>
<li>取出下一个元素,在已经排序的元素序列中从后向前扫描</li>
<li>如果该元素(已排序)大于新元素,将该元素移到下一位置</li>
<li>重复步骤3,直到找到已排序的元素小于或者等于新元素的位置</li>
<li>将新元素插入到该位置后,重复步骤2~5</li>
</ol>
</li>
<li>示例图:</li>
</ul>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Insertion-sort-example-300px.gif/220px-Insertion-sort-example-300px.gif" alt="插入排序过程"></p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#插入排序</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">insertion_sort</span><span class="params">($arr)</span></span><br><span class="line"></span>{</span><br><span class="line"> <span class="comment">//首个元素视为已经排好序的</span></span><br><span class="line"> <span class="keyword">for</span> ($i = <span class="number">1</span>; $i < count($arr); $i++) {</span><br><span class="line"> $value = $arr[$i];<span class="comment">//取出元素值</span></span><br><span class="line"> $index = $i - <span class="number">1</span>;<span class="comment">//待插入的位置</span></span><br><span class="line"> <span class="comment">//当待插入的位置还有元素,且比取出的元素值大的时候</span></span><br><span class="line"> <span class="keyword">while</span> ($index >= <span class="number">0</span> && $arr[$index] > $value) {</span><br><span class="line"> <span class="comment">//将待插入的位置的元素后移(即复值给后面的元素)</span></span><br><span class="line"> $arr[$index + <span class="number">1</span>] = $arr[$index];</span><br><span class="line"> <span class="comment">//在待插入的位置继续向前搜寻</span></span><br><span class="line"> $index--;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//前面发生过移动才插入</span></span><br><span class="line"> <span class="keyword">if</span> ($index != $i - <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//在最后插入的位置,插入取出的元素值</span></span><br><span class="line"> $arr[$index + <span class="number">1</span>] = $value;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $arr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Test(耗时80~100ms)</span></span><br><span class="line">$a = array_rand(range(<span class="number">1</span>, <span class="number">3000</span>), <span class="number">1500</span>);</span><br><span class="line">shuffle($a); <span class="comment">//获取已经打乱了顺序的数组</span></span><br><span class="line">$t1 = microtime(<span class="keyword">true</span>);</span><br><span class="line">insertion_sort($a); <span class="comment">//排序</span></span><br><span class="line">$t2 = microtime(<span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">echo</span> (($t2 - $t1) * <span class="number">1000</span>) . <span class="string">'ms'</span>;</span><br><span class="line"><span class="keyword">die</span>;</span><br></pre></td></tr></table></figure>
<h2 id="O-n-log-n-排序"><a href="#O-n-log-n-排序" class="headerlink" title="O(n log n)排序"></a>O(n log n)排序</h2><h3 id="归并排序-Merge-Sort"><a href="#归并排序-Merge-Sort" class="headerlink" title="归并排序 Merge Sort"></a>归并排序 Merge Sort</h3><ul>
<li>best case: O(n logn)。worst case: O(n logn)</li>
<li>arr长度<=1的时候,什么都不用做。</li>
<li>把arr对半分成left、right两个list,分别把left、right排序好。(递归)</li>
<li>最后,把排序好的left、right合并,按顺序放回arr里。</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#归并排序</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">merge_sort</span><span class="params">($arr)</span></span><br><span class="line"></span>{</span><br><span class="line"> <span class="comment">//等同于count($arr) <= 1</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">isset</span>($arr[<span class="number">1</span>])) <span class="keyword">return</span> $arr;</span><br><span class="line"> <span class="comment">//对半切割成两个数组</span></span><br><span class="line"> $mid = intval(count($arr) / <span class="number">2</span>);</span><br><span class="line"> $arr_left = array_slice($arr, <span class="number">0</span>, $mid);</span><br><span class="line"> $arr_right = array_slice($arr, $mid);</span><br><span class="line"> <span class="comment">//分治法,递归切割以及合并</span></span><br><span class="line"> <span class="keyword">return</span> merge(merge_sort($arr_left), merge_sort($arr_right));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">merge</span><span class="params">($arr_left, $arr_right)</span></span><br><span class="line"></span>{</span><br><span class="line"> $combine_arr = [];</span><br><span class="line"> <span class="comment">//取最小的,放入合并数组中</span></span><br><span class="line"> <span class="keyword">while</span> (<span class="keyword">isset</span>($arr_left[<span class="number">0</span>]) && <span class="keyword">isset</span>($arr_right[<span class="number">0</span>])) {</span><br><span class="line"> $combine_arr[] = ($arr_left[<span class="number">0</span>] <= $arr_right[<span class="number">0</span>]) ? array_shift($arr_left) : array_shift($arr_right);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//排完left或right其中一个,由于递归保证剩下的有序,对剩下的进行合并追加</span></span><br><span class="line"> <span class="keyword">return</span> array_merge($combine_arr, $arr_left, $arr_right);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Test(耗时25ms~48ms)</span></span><br><span class="line">$a = array_rand(range(<span class="number">1</span>,<span class="number">3000</span>), <span class="number">1500</span>);</span><br><span class="line">shuffle($a); <span class="comment">//获取已经打乱了顺序的数组</span></span><br><span class="line">$t1 = microtime(<span class="keyword">true</span>);</span><br><span class="line">merge_sort($a); <span class="comment">//排序</span></span><br><span class="line">$t2 = microtime(<span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">echo</span> (($t2-$t1)*<span class="number">1000</span>).<span class="string">'ms'</span>;</span><br><span class="line"><span class="keyword">die</span>;</span><br></pre></td></tr></table></figure>
<h3 id="快速排序-Quick-Sort"><a href="#快速排序-Quick-Sort" class="headerlink" title="快速排序 Quick Sort"></a>快速排序 Quick Sort</h3><ul>
<li>best case: O(n logn)。worst case: O(n²)</li>
<li>先对数组进行分割, 把大的元素数值放到一个临时数组里,把小的元素数值放到另一个临时数组里</li>
<li>这个分割的点可以是数组中的任意一个元素值,一般用第一个元素</li>
<li>递归地把这两个临时数组重复上面拆分</li>
<li>最后把小的数组元素和大的数组元素合并起来</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#快速排序</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">quick_sort</span><span class="params">($arr)</span></span><br><span class="line"></span>{</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">isset</span>($arr[<span class="number">1</span>])) <span class="keyword">return</span> $arr;</span><br><span class="line"> <span class="comment">//取出首个元素作为分割关键字</span></span><br><span class="line"> $mid = array_shift($arr);</span><br><span class="line"> $left_arr = $right_arr = <span class="keyword">array</span>();</span><br><span class="line"> <span class="keyword">foreach</span> ($arr <span class="keyword">as</span> $each) {</span><br><span class="line"> <span class="comment">//遍历,对比关键字大小划分到两个数组</span></span><br><span class="line"> ($each <= $mid) ? ($left_arr[] = $each) : ($right_arr[] = $each);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//递归合并</span></span><br><span class="line"> <span class="keyword">return</span> array_merge(quick_sort($left_arr), <span class="keyword">array</span>($mid), quick_sort($right_arr));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Test(耗时8~20ms)</span></span><br><span class="line">$a = array_rand(range(<span class="number">1</span>,<span class="number">3000</span>), <span class="number">1500</span>);</span><br><span class="line">shuffle($a); <span class="comment">//获取已经打乱了顺序的数组</span></span><br><span class="line">$t1 = microtime(<span class="keyword">true</span>);</span><br><span class="line">quick_sort($a); <span class="comment">//排序</span></span><br><span class="line">$t2 = microtime(<span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">echo</span> (($t2-$t1)*<span class="number">1000</span>).<span class="string">'ms'</span>;</span><br><span class="line"><span class="keyword">die</span>;</span><br></pre></td></tr></table></figure>
<h2 id="O-n-排序"><a href="#O-n-排序" class="headerlink" title="O(n)排序"></a>O(n)排序</h2><ul>
<li>通过两数字比较的排序最快也需要O(n logn),O(n)的排序都不通过两数比较来排序</li>
</ul>
<h3 id="计数排序-Counting-Sort"><a href="#计数排序-Counting-Sort" class="headerlink" title="计数排序 Counting Sort"></a>计数排序 Counting Sort</h3><ul>
<li>O(n),稳定的线性时间排序算法。不是比较排序,排序的速度快于任何比较排序算法</li>
<li>对于数据范围很大的数组,需要大量时间和内存</li>
<li>属于桶排序的特殊情况</li>
<li>使用一个额外的数组,其中第<code>i</code>个元素是待排序数组里,值等于i的元素的个数</li>
<li>算法步骤:<ol>
<li>找出待排序的数组中最大和最小的元素</li>
<li>统计数组中每个值为<em>i</em>的元素出现的次数,存入数组 <em>C</em> 的第 <em>i</em> 项</li>
<li>对所有的计数累加(从<em>C</em>中的第一个元素开始,每一项和前一项相加)</li>
<li>反向填充目标数组:将每个元素<em>i</em>放在新数组的第<em>C(i)</em>项,每放一个元素就将<em>C(i)</em>减去1</li>
</ol>
</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#计数排序</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">counting_sort</span><span class="params">($arr)</span></span><br><span class="line"></span>{</span><br><span class="line"> $length = count($arr);</span><br><span class="line"> <span class="keyword">if</span> ($length <= <span class="number">1</span>) <span class="keyword">return</span> $arr;</span><br><span class="line"> <span class="comment">//找出最大值最小值</span></span><br><span class="line"> $min=min($arr);</span><br><span class="line"> $max=max($arr);</span><br><span class="line"> <span class="comment">//创建计数器(这里不用array_fill因为其无法创建多个负值的键名)</span></span><br><span class="line"> $count_arr = <span class="keyword">array</span>();</span><br><span class="line"> <span class="keyword">for</span> ($i = $min; $i <= $max; $i++) {</span><br><span class="line"> $count_arr[$i] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//进行计数,得到等于计数器键名的数有多少个</span></span><br><span class="line"> <span class="keyword">foreach</span> ($arr <span class="keyword">as</span> $each) {</span><br><span class="line"> $count_arr[$each]++;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//相邻计数,得到小于等于计数器键名的数有多少个</span></span><br><span class="line"> <span class="keyword">for</span> ($i = $min + <span class="number">1</span>; $i <= $max; $i++) {</span><br><span class="line"> $count_arr[$i] += $count_arr[$i - <span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//数组翻转,由计数器决定了每个元素在第几个</span></span><br><span class="line"> $flip_arr = <span class="keyword">array</span>();</span><br><span class="line"> <span class="keyword">for</span> ($i = $length - <span class="number">1</span>; $i >= <span class="number">0</span>; $i--) {</span><br><span class="line"> $flip_arr[$count_arr[$arr[$i]]] = $arr[$i];</span><br><span class="line"> <span class="comment">//每找到一个数,该键计数器减1,给相同大小的数预留位置(即有重复时的特殊处理)</span></span><br><span class="line"> $count_arr[$arr[$i]]--;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//将翻转数组整理为顺序</span></span><br><span class="line"> <span class="keyword">for</span> ($i = <span class="number">1</span>; $i <= $length; $i++) {</span><br><span class="line"> $return_arr[] = $flip_arr[$i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $return_arr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Test(耗时1~6ms)</span></span><br><span class="line">$a = array_rand(range(<span class="number">1</span>, <span class="number">3000</span>), <span class="number">1500</span>);</span><br><span class="line">shuffle($a); <span class="comment">//获取已经打乱了顺序的数组</span></span><br><span class="line">$t1 = microtime(<span class="keyword">true</span>);</span><br><span class="line">counting_sort($a); <span class="comment">//排序</span></span><br><span class="line">$t2 = microtime(<span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">echo</span> (($t2 - $t1) * <span class="number">1000</span>) . <span class="string">'ms'</span>;</span><br><span class="line"><span class="keyword">die</span>;</span><br></pre></td></tr></table></figure>
<h3 id="桶排序-Bucket-Sort"><a href="#桶排序-Bucket-Sort" class="headerlink" title="桶排序 Bucket Sort"></a>桶排序 Bucket Sort</h3><ul>
<li>线性时间O(n)。桶排序不是比较排序,不受O(n log n)下限的影响。</li>
<li>桶排序比计数排序简单,只是桶中元素不能顺序放入和顺序取出</li>
<li>桶越多,时间效率就越高,空间占用却越大。</li>
<li>因此范围很大时,也可以设计成:按区间划分进桶,在桶里对每个元素排序(随便什么排序算法都行),再依次收集桶里的元素</li>
<li>算法步骤:<ol>
<li>设置一个定量的数组当作空桶。</li>
<li>寻访序列,并且把项目一个一个放到对应的桶去。</li>
<li>对每个不是空的桶进行排序。</li>
<li>从不是空的桶里把项目再放回原来的序列中。</li>
</ol>
</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#桶排序(特殊实现)</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bucket_sort</span><span class="params">($arr)</span></span><br><span class="line"></span>{</span><br><span class="line"> $length = count($arr);</span><br><span class="line"> <span class="keyword">if</span> ($length <= <span class="number">1</span>) <span class="keyword">return</span> $arr;</span><br><span class="line"> $min = min($arr);</span><br><span class="line"> $max = max($arr);</span><br><span class="line"> <span class="comment">//创建桶(这里不用array_fill因为其无法创建多个负值的键名)</span></span><br><span class="line"> <span class="keyword">for</span> ($i = $min; $i <= $max; $i++) {</span><br><span class="line"> $bucket_arr[$i] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//分配桶</span></span><br><span class="line"> <span class="keyword">foreach</span> ($arr <span class="keyword">as</span> $each) {</span><br><span class="line"> $bucket_arr[$each]++;</span><br><span class="line"> }</span><br><span class="line"> $return_arr = <span class="keyword">array</span>();</span><br><span class="line"> <span class="comment">//按顺序从每个桶里取出</span></span><br><span class="line"> <span class="keyword">foreach</span> ($bucket_arr <span class="keyword">as</span> $each => $each_count) {</span><br><span class="line"> <span class="comment">//桶里有元素的,依次收集</span></span><br><span class="line"> <span class="keyword">for</span> ($i = <span class="number">1</span>; $i <= $each_count; $i++) {</span><br><span class="line"> $return_arr[] = $each;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $return_arr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Test(耗时1~10ms)</span></span><br><span class="line">$a = array_rand(range(<span class="number">1</span>, <span class="number">3000</span>), <span class="number">1500</span>);</span><br><span class="line">shuffle($a); <span class="comment">//获取已经打乱了顺序的数组</span></span><br><span class="line">$t1 = microtime(<span class="keyword">true</span>);</span><br><span class="line">bucket_sort($a); <span class="comment">//排序</span></span><br><span class="line">$t2 = microtime(<span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">echo</span> (($t2 - $t1) * <span class="number">1000</span>) . <span class="string">'ms'</span>;</span><br><span class="line"><span class="keyword">die</span>;</span><br></pre></td></tr></table></figure>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#桶排序(常规实现)</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bucket_sort</span><span class="params">($arr,$chunk = <span class="number">10</span>)</span></span><br><span class="line"></span>{</span><br><span class="line"> $length = count($arr);</span><br><span class="line"> <span class="keyword">if</span> ($length <= <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> $arr;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//初始化桶</span></span><br><span class="line"> $min = floor(min($arr) / $chunk);</span><br><span class="line"> $max = ceil(max($arr) / $chunk);</span><br><span class="line"> $bucket_arr = <span class="keyword">array</span>();</span><br><span class="line"> <span class="keyword">for</span> ($i = $min; $i <= $max; $i++) {</span><br><span class="line"> $bucket_arr[$i] = <span class="keyword">array</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//按区间填充桶</span></span><br><span class="line"> <span class="keyword">foreach</span> ($arr <span class="keyword">as</span> $each) {</span><br><span class="line"> <span class="keyword">if</span> ($each >= <span class="number">0</span>) {</span><br><span class="line"> array_push($bucket_arr[ceil($each / $chunk)], $each);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> array_push($bucket_arr[floor($each / $chunk)], $each);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//从桶里取出</span></span><br><span class="line"> $return_arr = <span class="keyword">array</span>();</span><br><span class="line"> <span class="keyword">foreach</span> ($bucket_arr <span class="keyword">as</span> $bucket) {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">empty</span>($bucket)) {</span><br><span class="line"> <span class="comment">//对每个桶内的元素进行排序,这里用了快排</span></span><br><span class="line"> $each_arr = quick_sort($bucket);</span><br><span class="line"> <span class="keyword">foreach</span> ($each_arr <span class="keyword">as</span> $each) {</span><br><span class="line"> $return_arr[] = $each;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $return_arr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">quick_sort</span><span class="params">($arr)</span></span><br><span class="line"></span>{</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">isset</span>($arr[<span class="number">1</span>])) <span class="keyword">return</span> $arr;</span><br><span class="line"> $mid = array_shift($arr);</span><br><span class="line"> $left_arr = $right_arr = <span class="keyword">array</span>();</span><br><span class="line"> <span class="keyword">foreach</span> ($arr <span class="keyword">as</span> $each) {</span><br><span class="line"> $each < $mid ? ($left_arr[] = $each) : ($right_arr[] = $each);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> array_merge(quick_sort($left_arr), <span class="keyword">array</span>($mid), quick_sort($right_arr));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Test(耗时3~10ms)</span></span><br><span class="line">$a = array_rand(range(<span class="number">1</span>, <span class="number">3000</span>), <span class="number">1500</span>);</span><br><span class="line">shuffle($a); <span class="comment">//获取已经打乱了顺序的数组</span></span><br><span class="line">$t1 = microtime(<span class="keyword">true</span>);</span><br><span class="line">bucket_sort($a); <span class="comment">//排序</span></span><br><span class="line">$t2 = microtime(<span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">echo</span> (($t2 - $t1) * <span class="number">1000</span>) . <span class="string">'ms'</span>;</span><br><span class="line"><span class="keyword">die</span>;</span><br></pre></td></tr></table></figure>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="https://medium.com/@czheo/排序-b5ef06533b11" rel="external nofollow" target="_blank">排序算法</a></p>
<p><a href="http://www.shucunwang.com/RunCode/php5.4/" rel="external nofollow" target="_blank">在线调试</a></p>
<p><a href="https://segmentfault.com/a/1190000005760963" rel="external nofollow" target="_blank">PHP数组排序算法实现</a></p>
<p><a href="https://www.byvoid.com/zhs/blog/sort-radix" rel="external nofollow" target="_blank">三种线性排序算法 计数排序、桶排序与基数排序</a></p>
<p><a href="https://www.zybuluo.com/phper/note/85911" rel="external nofollow" target="_blank">PHP中的四种基本排序算法</a></p>
<!--toc-->
<div class="alert success"><p>总算闲下来了,梳理了一下上个月面试相关的东西,发现以前收集的OneNote笔记里,算法这块记的比较凌乱,就把常见排序算法整理成MarkDown的,按时间复杂度区分,顺带写了Demo。嗯,还有,每个D
社区好友动态Feed流的Redis实现
https://quericy.me/blog/861/
2017-03-28T01:28:24.000Z
2017-03-28T02:01:24.000Z
<!--toc-->
<div class="alert success"><p>这些天整理交接文档,打算把自己在这份工作最后一个需求的设计稍微梳理一下。虽然实现只能交给其它人了,而且设计上也比较粗糙,存在不少瑕疵,但这也相当于自己当前技术水平的一个脚印。希望过段时间再回头来看,能发现自己有更大的进步吧!</p>
</div>
<h1 id="需求沟通"><a href="#需求沟通" class="headerlink" title="需求沟通"></a>需求沟通</h1><p>和产品沟通后,综合考虑开发时限,性能要求和产品逻辑,协商后调整的需求细节总结以下几点:</p>
<ol>
<li>添加关注后,需要显示关注者最近的发帖动态(包括关注前)</li>
<li>取消关注后, 需要从好友动态列表删除</li>
<li>取消关注后重新关注,同添加关注逻辑处理</li>
<li>好友动态保留最近300条</li>
</ol>
<h1 id="方案设计"><a href="#方案设计" class="headerlink" title="方案设计"></a>方案设计</h1><p>好友动态的原理同微博的Feed流,常见有以下几种模式:</p>
<ul>
<li>集中模式。将所有用户的动态汇集到统一的流,不同用户拉取后各自根据关注信息筛选处理。性能要求和复杂度较高,此方案暂不作考虑。</li>
<li>推模式。即写扩散。每当用户发帖,对所有粉丝推送一条该用户的动态消息记录。优点是查看动态的读取场景下效率最高。缺点是对关注列表的变动不符合需求。</li>
<li>拉模式。即读扩散。每当请求好友动态接口,拉取用户所有关注者的最近动态,然后汇总排序。优点是对关注列表的变化能实时体现。缺点是拉取所有关注者的数据,做汇总排序分页的代价较大。</li>
<li>推拉模式。即读+写扩散。是上面两种模式的结合,依据业务需求对推模式做补充,在开销较小的部分使用拉模式。</li>
</ul>
<h1 id="技术选型"><a href="#技术选型" class="headerlink" title="技术选型"></a>技术选型</h1><h2 id="选型原因"><a href="#选型原因" class="headerlink" title="选型原因"></a>选型原因</h2><p>最终选择推拉模式,原因如下:</p>
<ul>
<li>推模式无法满足需求,拉模式开销大于收益。</li>
<li>用户关注数量统计,最大用户关注数量为四位数,平均关注数量也在推模式可接受的开销范围内。</li>
<li>好友动态功能可接受延迟显现,耗时操作可拆分为队列异步处理。</li>
<li>对好友动态的一致性要求较高,关注和取关后,好友动态列表需要保持正确的显示效果。</li>
<li>读需求大于写需求。在可预期的范围内,社区活跃用户的发帖动态数量在可接受范围。</li>
<li>当前版本开发周期较短,推拉模式在满足前期需求和性能的条件上可较为迅速实现,且之后可做<a href="#功能扩展">功能扩展</a>。</li>
</ul>
<h2 id="实现机制"><a href="#实现机制" class="headerlink" title="实现机制"></a>实现机制</h2><p>实现机制为使用Redis的SortedSets实现。主要考虑以下原因:</p>
<ul>
<li>基于内存操作,性能和吞吐量优于使用MySQL。</li>
<li>项目处于平稳增长期,活跃用户带来的操作性能使用Redis尚有余力。</li>
<li>用户发布的数据已有其他缓存做持久化存储,好友动态仅需展示,不用作其他后续分析,过期后可直接丢弃。</li>
<li>目前线上业务对Redis依赖较多,有现成闲置的缓存服务器可用,搭建适合消息订阅分发的分布式环境需要额外成本。</li>
</ul>
<h2 id="缓存结构"><a href="#缓存结构" class="headerlink" title="缓存结构"></a>缓存结构</h2><ul>
<li>每个用户一个<code>收Feed</code>有序集用于存放接收到的好友动态ID。有序集key名包含UID,成员为动态记录(动态ID和动态发布者UID的封装),分数为时间戳。</li>
<li>每个用户一个<code>发Feed</code>有序集用于存放该用户自己发的动态ID。有序集key名包含UID,成员为动态ID,分数为时间戳。</li>
<li>一个<code>动态发布处理队列</code>,用于在用户发帖时处理动态推送</li>
<li>一个<code>关注取关处理队列</code>,用于在用户关注/取关时处理动态的增减</li>
<li>一个数据清理脚本,用于定时清理回收过时数据</li>
</ul>
<h1 id="设计实现"><a href="#设计实现" class="headerlink" title="设计实现"></a>设计实现</h1><h2 id="用户发帖"><a href="#用户发帖" class="headerlink" title="用户发帖"></a>用户发帖</h2><ul>
<li>用户发帖时触发事件通知,将<code>帖子ID</code>,<code>用户UID</code>和<code>时间戳</code>封装为一条消息。</li>
<li>消息放入<code>动态发布处理队列</code>,交给队列进行异步处理。</li>
</ul>
<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><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">s=>start: 开始</span><br><span class="line">e=>end: 结束</span><br><span class="line"></span><br><span class="line">A=>operation: 用户A</span><br><span class="line">send_queue=>operation: 入发布处理队列</span><br><span class="line">send_data=>subroutine: {id,uid,time}</span><br><span class="line">submit=>inputoutput: 发帖</span><br><span class="line"></span><br><span class="line">s->A->submit->send_data->send_queue->e</span><br></pre></td></tr></table></figure>
<p><img src="https://static.quericy.me/1drive/blog/hexo/1zheFeed1.png" alt="用户发帖flow"></p>
<h2 id="用户关注-取消关注"><a href="#用户关注-取消关注" class="headerlink" title="用户关注/取消关注"></a>用户关注/取消关注</h2><ul>
<li>当用户执行关注或者取消关注动作时,将<code>用户UID</code>,<code>关注者UID</code>和<code>动作标识</code>封装为一条消息。</li>
<li><code>动作标识</code>为数字,表示当前操作为关注,取消关注,后续可扩展。</li>
<li>消息放入<code>关注取关处理队列</code>,交给队列进行异步处理。</li>
</ul>
<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><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></pre></td><td class="code"><pre><span class="line">s=>start: 开始</span><br><span class="line">e=>end: 结束</span><br><span class="line"></span><br><span class="line">A=>operation: 用户A</span><br><span class="line">follow_queue=>operation: 入关注处理队列</span><br><span class="line"></span><br><span class="line">action=>condition: 关注?</span><br><span class="line"></span><br><span class="line">follow=>inputoutput: 关注</span><br><span class="line">unfollow=>inputoutput: 取关</span><br><span class="line"></span><br><span class="line">follow_send_data=>subroutine: {uid,feed_uid,1}</span><br><span class="line">unfollow_send_data=>subroutine: {uid,feed_uid,0}</span><br><span class="line"></span><br><span class="line">s->A->action</span><br><span class="line">action(yes)->follow->follow_send_data->follow_queue->e</span><br><span class="line">action(no)->unfollow->unfollow_send_data->follow_queue->e</span><br></pre></td></tr></table></figure>
<p><img src="https://static.quericy.me/1drive/blog/hexo/1zheFeed2.png" alt="用户关注取关flow"></p>
<h2 id="动态发布处理队列"><a href="#动态发布处理队列" class="headerlink" title="动态发布处理队列"></a>动态发布处理队列</h2><ul>
<li>动态发布处理队列发现新消息时,取队首消息出队列。</li>
<li>根据消息中的发布者UID,遍历其粉丝列表(当以后全站粉丝量较大时,可扩展为选择性推送)。</li>
<li>给每个粉丝推送一条动态,将动态ID和时间戳写入粉丝的收Feed有序集中。</li>
<li>消息处理完成,检查队列是否还有消息,无则阻塞。</li>
</ul>
<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><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></pre></td><td class="code"><pre><span class="line">s=>start: 发布处理队列</span><br><span class="line">e=>end: 队列阻塞</span><br><span class="line"></span><br><span class="line">in_feed=>operation: 写入粉丝收feed</span><br><span class="line">send_feed=>operation: 写入用户自己的发feed</span><br><span class="line"></span><br><span class="line">follower_list=>operation: 遍历粉丝列表</span><br><span class="line"></span><br><span class="line">is_while=>condition: 遍历完成?</span><br><span class="line">is_queue=>condition: 处理完成?</span><br><span class="line"></span><br><span class="line">send_data=>subroutine: {id,uid,time}出队列</span><br><span class="line"></span><br><span class="line">s->is_queue</span><br><span class="line">is_queue(yes,right)->e</span><br><span class="line">is_queue(no)->send_data->follower_list->is_while</span><br><span class="line">is_while(no)->in_feed->is_while</span><br><span class="line">is_while(yes)->send_feed->is_queue</span><br></pre></td></tr></table></figure>
<p><img src="https://static.quericy.me/1drive/blog/hexo/1zheFeed3.png" alt="动态发布队列flow"></p>
<h2 id="关注取关处理队列"><a href="#关注取关处理队列" class="headerlink" title="关注取关处理队列"></a>关注取关处理队列</h2><ul>
<li>关注取关处理队列发现新消息时,取队首消息出队列。</li>
<li>根据<code>动作标识</code>判断是关注还是取关操作。</li>
<li>如果是关注,拉取关注者的发Feed有序集中的动态,将最近的动态ID写入用户自己的收Feed中。</li>
<li>如果是取关,遍历用户自己的收Feed,剔除其中取关UID的动态记录。</li>
<li>消息处理完成,检查队列是否还有消息,无则阻塞。</li>
</ul>
<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><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></pre></td><td class="code"><pre><span class="line">s=>start: 关注取关处理队列</span><br><span class="line">e=>end: 队列阻塞</span><br><span class="line"></span><br><span class="line">in_feed=>operation: 获取用户的收feed</span><br><span class="line">in_feed2=>operation: 写入用户自己的收feed</span><br><span class="line">send_feed=>operation: 关注者的发feed</span><br><span class="line"></span><br><span class="line">continue=>operation: 消费完成,继续遍历队列</span><br><span class="line"></span><br><span class="line">follower_list=>operation: 遍历粉丝列表</span><br><span class="line"></span><br><span class="line">is_queue=>condition: 处理完成?</span><br><span class="line">is_follow=>condition: 关注?</span><br><span class="line">recently=>condition: 有近期的动态?</span><br><span class="line"></span><br><span class="line">delete=>inputoutput: 剔除其中取关uid的动态</span><br><span class="line">pull=>inputoutput: 拉取关注uid的动态</span><br><span class="line"></span><br><span class="line">send_data=>subroutine: {uid,feed_uid,type}出队列</span><br><span class="line"></span><br><span class="line">s->is_queue</span><br><span class="line">is_queue(yes,right)->e</span><br><span class="line">is_queue(no)->send_data->is_follow</span><br><span class="line">is_follow(no)->in_feed->delete->continue</span><br><span class="line">is_follow(yes,right)->send_feed->pull->recently</span><br><span class="line">recently(no)->continue</span><br><span class="line">recently(yes,right)->in_feed2->continue</span><br><span class="line">continue->is_queue</span><br></pre></td></tr></table></figure>
<p><img src="https://static.quericy.me/1drive/blog/hexo/1zheFeed4.png" alt="关注取关处理队列flow"></p>
<h2 id="Feed数据清理"><a href="#Feed数据清理" class="headerlink" title="Feed数据清理"></a>Feed数据清理</h2><ul>
<li>脚本作为定时任务启动,时间间隔由功能上线后数据增长情况决定</li>
<li>脚本遍历用户的收Feed和发Feed</li>
<li>判断每组有序集的数量,对大于300条的数据,从最早的记录开始剔除,直到数量小于等于300条为止</li>
</ul>
<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><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></pre></td><td class="code"><pre><span class="line">s=>start: 定时清理脚本</span><br><span class="line">e=>end: 结束</span><br><span class="line"></span><br><span class="line">A=>operation: 遍历用户</span><br><span class="line">delete=>operation: 剔除大于300条的早期数据</span><br><span class="line"></span><br><span class="line">action=>condition: Feed类型</span><br><span class="line"></span><br><span class="line">in_feed=>inputoutput: 收Feed有序集</span><br><span class="line">send_feed=>inputoutput: 发Feed有序集</span><br><span class="line"></span><br><span class="line">in_feed_data=>subroutine: {id\uid,time}</span><br><span class="line">send_feed_data=>subroutine: {id,time}</span><br><span class="line"></span><br><span class="line">s->A->action</span><br><span class="line">action(yes)->in_feed->in_feed_data->delete->e</span><br><span class="line">action(no)->send_feed->send_feed_data->delete->e</span><br></pre></td></tr></table></figure>
<p><img src="https://static.quericy.me/1drive/blog/hexo/1zheFeed5.png" alt="数据清理队列flow"></p>
<h1 id="功能扩展"><a href="#功能扩展" class="headerlink" title="功能扩展"></a>功能扩展</h1><h2 id="操作合并"><a href="#操作合并" class="headerlink" title="操作合并"></a>操作合并</h2><ul>
<li>当关注/取关操作量较大,或有用户频繁重复执行关注/取关时,可能需要对关注取关处理队列的操作进行合并</li>
<li>入队列时,可判断队列中是否已存在相同uid和feed_uid的记录在等待消费,若存在,则剔除之前的,以最后操作为准</li>
</ul>
<h2 id="选择性推送"><a href="#选择性推送" class="headerlink" title="选择性推送"></a>选择性推送</h2><ul>
<li>当用户的粉丝量较大时,动态发布队列处理延迟会比较久</li>
<li>可扩展为选择性推送,优先发送给当前在线的用户,对不在线的用户等待其上线后再执行拉取</li>
<li>需要客户端配合,对社区用户在线状态进行处理,通知给服务端</li>
</ul>
<h2 id="动态类型扩展"><a href="#动态类型扩展" class="headerlink" title="动态类型扩展"></a>动态类型扩展</h2><ul>
<li>当前动态ID类型仅为帖子ID,即在用户发帖时产生动态,后续可能会对评论,回复等作为动态进行扩展</li>
<li>当前动作标识仅有关注/取消关注,后续可能会有不看该好友动态/不展示给该好友我的动态这类的扩展</li>
</ul>
<h2 id="时间精度扩展"><a href="#时间精度扩展" class="headerlink" title="时间精度扩展"></a>时间精度扩展</h2><ul>
<li>Feed有序集Score的时间精度为秒,如果并发量大时同秒的动态先后顺序按照的是Hash中的顺序排序</li>
<li>如果有高精度要求,可能需要将有序集Score的时间精度设置为毫秒,将增加一定量的内存占用</li>
</ul>
<!--toc-->
<div class="alert success"><p>这些天整理交接文档,打算把自己在这份工作最后一个需求的设计稍微梳理一下。虽然实现只能交给其它人了,而且设计上也比较粗糙,存在不少瑕疵,但这也相当于自己当前技术水平的一个脚印。希望过段时间再回头来看,
SSL证书自动更新并应用到IKEv2, Nginx
https://quericy.me/blog/860/
2017-01-08T14:28:24.000Z
2017-01-08T14:28:24.000Z
<!--toc-->
<h1 id="SSL证书免费申请和自动续期"><a href="#SSL证书免费申请和自动续期" class="headerlink" title="SSL证书免费申请和自动续期"></a>SSL证书免费申请和自动续期</h1><p>申请SSL证书,首先需要避免用被Mozilla 拉黑的沃通(WoSign)及被沃通收购的StartCom. 沃通的一系列乱像和作死的缘由网上搜一下就清楚了.</p>
<p>这里还是推荐由美国加州公益组织ISRG 的 Let’s Encrypt . 优点在于可靠,可自动化,免费,口碑好. 缺点(其实算不上缺点)为有效期只有90天,但是完全可以自动化的进行续期来避免这个问题.</p>
<p>自动申请Let’s Encrypt证书的工具,用的最顺手的自动化工具还是<a href="https://github.com/Neilpang/acme.sh/" rel="external nofollow" target="_blank">acme.sh</a>.<br>证书颁发可以看该项目的<a href="https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E" rel="external nofollow" target="_blank">Github Wiki中文文档</a> ,写得很详细了.</p>
<p>主要需要注意区分的是:</p>
<ul>
<li><p>颁发用<code>--issue</code>,仅在首次申请的时候使用(以及配合 <code>--dns</code> 并做好dns参数解析)</p>
</li>
<li><p>续期用<code>--renew</code> ,可执行任意次数(如果离过期时间比较远,可使用 <code>--force</code> 强制执行更新)</p>
</li>
</ul>
<p>至于自动续期,以域名<code>yourdomain.com</code>为例:</p>
<ul>
<li><p>给<code>yourdomain.com</code> 设置每30天自动续期:</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">acme.sh --days 30 --renew --dns <span class="_">-d</span> yourdomain.com</span><br></pre></td></tr></table></figure>
</li>
<li><p>给<code>yourdomain.com</code> 的ECC证书设置每30天自动续期</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">acme.sh --days 30 --renew --dns --ecc --keylength ec-256 <span class="_">-d</span> yourdomain.com</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h1 id="定时将证书替换到IKEv2"><a href="#定时将证书替换到IKEv2" class="headerlink" title="定时将证书替换到IKEv2"></a>定时将证书替换到IKEv2</h1><p>IKEv2使用SSL证书的好处是可以不用导入到客户端,但是也需要注意证书过期对服务的影响.所以在上面一节设置好对Let’s Encrypt证书的自动续期以后,也别忘了将它应用到IPsec服务中来:</p>
<ul>
<li><p>写个bash脚本ipsec.sh,用于替换证书并重启服务,注意替换<code>cert_file</code>和<code>key_file</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><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="meta">#! /bin/bash</span></span><br><span class="line">cert_file=<span class="string">"/home/user/.acme.sh/yourdomain/yourdomain.cer"</span></span><br><span class="line">key_file=<span class="string">"/home/user/.acme.sh/yourdomain/yourdomain.key"</span></span><br><span class="line"></span><br><span class="line">sudo cp <span class="_">-f</span> <span class="variable">$cert_file</span> /usr/<span class="built_in">local</span>/etc/ipsec.d/certs/server.cert.pem</span><br><span class="line">sudo cp <span class="_">-f</span> <span class="variable">$key_file</span> /usr/<span class="built_in">local</span>/etc/ipsec.d/private/server.pem</span><br><span class="line">sudo cp <span class="_">-f</span> <span class="variable">$cert_file</span> /usr/<span class="built_in">local</span>/etc/ipsec.d/certs/client.cert.pem</span><br><span class="line">sudo cp <span class="_">-f</span> <span class="variable">$key_file</span> /usr/<span class="built_in">local</span>/etc/ipsec.d/private/client.pem</span><br><span class="line">sudo /usr/<span class="built_in">local</span>/sbin/ipsec restart</span><br></pre></td></tr></table></figure>
</li>
<li><p>编写定时任务: <code>crontab -e</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">59 02 1 * * bash /your/path/to/ipsec.sh > /dev/null</span><br></pre></td></tr></table></figure>
</li>
<li><p>这样,每个月1日的凌晨2点59分就会替换证书并重启IPsec服务.也可以根据自己的需求自行调整参数.</p>
</li>
</ul>
<h1 id="定时将证书替换到Nginx"><a href="#定时将证书替换到Nginx" class="headerlink" title="定时将证书替换到Nginx"></a>定时将证书替换到Nginx</h1><p>acme.sh自动申请到的证书不仅可以给IPsec服务使用,也可以给Nginx使用.<br>既能免于IKEv2自签名CA导入问题的烦恼,又能给自己的站点上HTTPS,一举多得.</p>
<p>虽然acme的脚本带了一些十分方便的参数指定(如keypath, fullchainpath, reloadcmd, 自动部署到apache等)<br>但也无法满足一些复杂的需求,如同时应用普通证书和ECC证书,同时发布到IPsec等.<br>因而也可以自己写定时脚本(或者写在上一节的脚本里),步骤和原理同上,也就不多赘述:</p>
<ul>
<li><p>bash file <code>nginx.sh</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><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#! /bin/bash</span></span><br><span class="line">sudo cp <span class="_">-f</span> /home/user/.acme.sh/yourdomain/fullchain.cer /path/to/nginx/ssl/fullchain.pem</span><br><span class="line">sudo cp <span class="_">-f</span> /home/user/.acme.sh/yourdomain/yourdomain.key /path/to/nginx/ssl/privatekey.pem</span><br><span class="line">sudo cp <span class="_">-f</span> /home/user/.acme.sh/yourdomain_ecc/fullchain.cer /path/to/nginx/ssl/fullchain.pem</span><br><span class="line">sudo cp <span class="_">-f</span> /home/user/.acme.sh/yourdomain_ecc/yourdomain.key /path/to/nginx/ssl/privatekey.pem</span><br><span class="line">sudo service nginx restart</span><br></pre></td></tr></table></figure>
</li>
<li><p>add crontab:</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">59 02 1 * * bash /your/path/to/nginx.sh > /dev/null</span><br></pre></td></tr></table></figure>
</li>
</ul>
<!--toc-->
<h1 id="SSL证书免费申请和自动续期"><a href="#SSL证书免费申请和自动续期" class="headerlink" title="SSL证书免费申请和自动续期"></a>SSL证书免费申请和自动续期</h1><p>申请SSL证书,首先需
写于2016年末
https://quericy.me/blog/859/
2016-12-31T07:35:47.000Z
2022-06-28T05:27:25.671Z
<p>感觉这一年自己又咸鱼了一年,本该年初就下的决断,一时犹豫就错过了许多.</p>
<p>前半年里技术提升缓慢,一部分原因是感觉好迷,另一部分原因还是怠惰了~</p>
<p>后半年还是有所进步的,人生果然还是需要折腾.只可惜工作忙起来了就容易乱,分神的结果就是效率也就不见得提升.</p>
<p>然而博客荒废了这么久还是拖延症啊,这病得治.连个Ingress都拖延到今天才升8….</p>
<p>好多存稿都丢在OneNote里,看看什么时候有空闲了整理点出来,不然过阵子都不知道自己写的是啥了- -||</p>
<p>感觉不能再生鱼忧患死鱼安乐下去了.</p>
<p>PS:封面图片出处:<a href="http://www.pixiv.net/member_illust.php?mode=medium&illust_id=58991926" rel="external nofollow" target="_blank">pixiv</a></p>
<p>感觉这一年自己又咸鱼了一年,本该年初就下的决断,一时犹豫就错过了许多.</p>
<p>前半年里技术提升缓慢,一部分原因是感觉好迷,另一部分原因还是怠惰了~</p>
<p>后半年还是有所进步的,人生果然还是需要折腾.只可惜工作忙起来了就容易乱,分神的结果就是效率也就不见得提升
写了个贴吧云签到的自定义邮件通知扩展
https://quericy.me/blog/858/
2016-08-16T02:27:24.000Z
2016-08-18T03:47:03.000Z
<!--toc-->
<h1 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h1><p>其实原因是原来的云签到坏了,旧的VPS也不打算续费了,拉取了最新版本的云签到在云平台上,发现邮件发不了<br>检查了下发现是被辣鸡163给当做spam给ban了 = =||</p>
<p>于是改用Exchange发,但是发现贴吧云签到自带的SMTP貌似不支持TLS,而且原来的邮件格式进垃圾箱的概率好高,模板是硬编码在代码里的…</p>
<p>然后这两天就自己用零散时间,基于原来D丶L的版本写了个改进版的<strong>自定义每日签到邮件通知</strong>的扩展(怎么感觉越走越远了…)</p>
<h1 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h1><p>丢在Github上:<a href="https://github.com/quericy/quericy_sign_mail" rel="external nofollow" target="_blank">https://github.com/quericy/quericy_sign_mail</a><br>反馈也可以丢在这里,嗯,如果有的话.</p>
<h1 id="初版功能"><a href="#初版功能" class="headerlink" title="初版功能"></a>初版功能</h1><ul>
<li><p>集成了innomatic-libs的<a href="https://github.com/innomatic-libs/kmmailer" rel="external nofollow" target="_blank">kmmailer</a> ,很轻量级的SMTP的邮件扩展类, 支持TLS/SSL/无加密 三种方式,基本是够用了.</p>
</li>
<li><p>独立的SMTP配置是有好处的,站内发的消息邮件通知使用内置的SMTP,日常签到邮件通知使用另一个SMTP(no reply),互不影响.</p>
</li>
<li><p>设置了可以每日发邮件的时间,大概预估下签到需要多久,差不多等签完了再发邮件通知</p>
</li>
<li><p>然后重写了下报告页面,去除了无意义的遍历…(有点不理解原作者的思路,展示页面为啥要把所有用户遍历一次)</p>
</li>
<li><p>脚本的sql还没优化,这个倒不影响,本来一次也是分多次可以继续执行的</p>
</li>
<li><p>然后模板化了邮件的标题和正文内容,可以在后台自定义HTML,结合正则匹配一些常用的模板变量进行内容替换,被spam了修改下模板内容就ok.</p>
</li>
<li><p>输出日志,方便查看邮件发送成功和失败的统计数据</p>
</li>
</ul>
<h1 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h1><p>看心情了,也许会加个设置,可选邮件模板是发[简略]还是[详情],简略只发签到报告的链接(效率高),[详情]则在邮件里直接带上完整报告(体验好).</p>
<h1 id="附-截图说明"><a href="#附-截图说明" class="headerlink" title="附:截图说明"></a>附:截图说明</h1><h2 id="插件设置"><a href="#插件设置" class="headerlink" title="插件设置"></a>插件设置</h2><h3 id="插件管理-gt-自定义签到邮件通知-gt-插件设置:(需要管理员权限)"><a href="#插件管理-gt-自定义签到邮件通知-gt-插件设置:(需要管理员权限)" class="headerlink" title="插件管理->自定义签到邮件通知->插件设置:(需要管理员权限)"></a>插件管理->自定义签到邮件通知->插件设置:(需要管理员权限)</h3><p><img src="https://i.imgur.com/duyzD5y.png" alt="如图所示"></p>
<h3 id="进入设置页面后可配置详细的插件功能:"><a href="#进入设置页面后可配置详细的插件功能:" class="headerlink" title="进入设置页面后可配置详细的插件功能:"></a>进入设置页面后可配置详细的插件功能:</h3><p><img src="https://i.imgur.com/yhiK0xk.png" alt="如图所示"></p>
<h2 id="插件日志"><a href="#插件日志" class="headerlink" title="插件日志"></a>插件日志</h2><h3 id="计划任务-gt-quericy-sign-mail-gt-查看日志-或点击编辑查看日志"><a href="#计划任务-gt-quericy-sign-mail-gt-查看日志-或点击编辑查看日志" class="headerlink" title="计划任务->quericy_sign_mail->查看日志(或点击编辑查看日志)"></a>计划任务->quericy_sign_mail->查看日志(或点击编辑查看日志)</h3><p><img src="https://i.imgur.com/kRxWsBS.png" alt="如图所示"></p>
<h2 id="用户页面"><a href="#用户页面" class="headerlink" title="用户页面"></a>用户页面</h2><h3 id="每个用户的个人设置页面:"><a href="#每个用户的个人设置页面:" class="headerlink" title="每个用户的个人设置页面:"></a>每个用户的个人设置页面:</h3><p><img src="https://i.imgur.com/CksC6gh.png" alt="如图所示"></p>
<h2 id="签到报告"><a href="#签到报告" class="headerlink" title="签到报告"></a>签到报告</h2><h3 id="发送给用户的邮件报告:"><a href="#发送给用户的邮件报告:" class="headerlink" title="发送给用户的邮件报告:"></a>发送给用户的邮件报告:</h3><p><img src="https://i.imgur.com/AzpaLSd.png" alt="如图所示"></p>
<h3 id="点击跳转的详细报告:"><a href="#点击跳转的详细报告:" class="headerlink" title="点击跳转的详细报告:"></a>点击跳转的详细报告:</h3><p><img src="https://i.imgur.com/1Dsuslq.png" alt="如图所示"></p>
<h3 id="默认报告模板在mail-tester的邮件测试评分-邮件服务器正确配置DKIM等校验能提升评分-坏链不知道为啥将正常链接识别成404-:"><a href="#默认报告模板在mail-tester的邮件测试评分-邮件服务器正确配置DKIM等校验能提升评分-坏链不知道为啥将正常链接识别成404-:" class="headerlink" title="默认报告模板在mail-tester的邮件测试评分(邮件服务器正确配置DKIM等校验能提升评分,坏链不知道为啥将正常链接识别成404):"></a>默认报告模板在<a href="http://www.mail-tester.com" rel="external nofollow" target="_blank">mail-tester</a>的邮件测试评分(邮件服务器正确配置DKIM等校验能提升评分,坏链不知道为啥将正常链接识别成404):</h3><p><img src="https://i.imgur.com/2mvaFie.png" alt="如图所示"></p>
<!--toc-->
<h1 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h1><p>其实原因是原来的云签到坏了,旧的VPS也不打算续费了,拉取了最新版本的云签到在云平台上,发现邮件发不了<br>检查了下
WordPress迁移Hexo之主题安装与修改
https://quericy.me/blog/857/
2016-07-16T05:27:24.000Z
2016-07-25T07:39:35.000Z
<!-- toc -->
<h1 id="添加主题Tranquilpeak"><a href="#添加主题Tranquilpeak" class="headerlink" title="添加主题Tranquilpeak"></a>添加主题Tranquilpeak</h1><h2 id="主题添加说明"><a href="#主题添加说明" class="headerlink" title="主题添加说明"></a>主题添加说明</h2><ul>
<li><p>本次迁移到Hexo博客,采用的主题是LouisBarranqueiro的<a href="https://github.com/LouisBarranqueiro/hexo-theme-tranquilpeak" rel="external nofollow" target="_blank">Tranquilpeak</a>,同时参考了<a href="https://github.com/kaedea/hexo-theme-themia" rel="external nofollow" target="_blank">Themia主题</a> 的多处调整,进行了一系列修改,并发布在Github上:<a href="https://github.com/quericy/tranquilpeak" rel="external nofollow" target="_blank">https://github.com/quericy/tranquilpeak</a> 。</p>
</li>
<li><p>本文叙述的Hexo主题安装和调整的方法均基于Tranquilpeak主题的,其他主题若有不同之处,请自行甄别与修改。</p>
</li>
<li><p>本文具有时效性,编写时使用的hexo版本为3.2.0,Tranquilpeak主题版本为1.7.1,以后的版本可能会与本文描述的内容有所出入。</p>
</li>
</ul>
<hr>
<h2 id="主题添加方法"><a href="#主题添加方法" class="headerlink" title="主题添加方法"></a>主题添加方法</h2><ul>
<li><p>在Hexo项目的themes主题文件夹下clone项目:</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">git <span class="built_in">clone</span> https://github.com/quericy/tranquilpeak.git</span><br></pre></td></tr></table></figure>
</li>
<li><p>修改Hexo项目根目录的_config.yml文件的<code>theme</code>部分,将主题名称修改为tranquilpeak;</p>
</li>
<li><p>将themes/tranquilpeak/_config.yml.bak重名为themes/tranquilpeak/_config.yml(如果主题和博客分开版本控制,为了安全考虑,也可以将themes/tranquilpeak/_config.yml排除出公开仓库,并做软链接映射到目录内);</p>
</li>
<li><p>参考配置文件中的说明,修改主题配置文件themes/tranquilpeak/_config.yml。<strong>注意:</strong>配置文件中的归档、分类、Tags、搜索、关于等页面,是需要自行创建Hexo page的,否则渲染生成静态文件的时候也会有相应的报错提示。例如,需要添加展示所有tag的一个tags页面,则需要以下步骤(其他以此类推):</p>
<ul>
<li><p>使用命令创建一个hexo page:</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">hexo new page <span class="string">"tags"</span> <span class="comment">#创建名为tags的页面</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>此时会在hexo项目的source文件夹中创建tags文件夹。在source/tags/中创建index.md文件,并编写Front-matter区域:</p>
<figure class="highlight markdown"><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">title: tags</span><br><span class="line"><span class="section">layout: "all-tags"</span><br><span class="line">---</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>上一步的layout的值需要对应主题的某一个布局,如<code>layout: "all-tags"</code>对应themes/tranquilpeak/layout/all-tags.ejs 的文件名。</p>
</li>
<li><p>修改themes/tranquilpeak/_config.yml中侧边栏的tags一节,url对应为新建的hexo page名称:</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">tags:</span></span><br><span class="line"><span class="attr"> title:</span> global.tags</span><br><span class="line"><span class="attr"> url:</span> /tags/</span><br><span class="line"><span class="attr"> icon:</span> tags</span><br></pre></td></tr></table></figure>
</li>
</ul>
</li>
<li><p>重新渲染Hexo生成静态文件,即可看到效果了:</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">hexo g <span class="_">-d</span></span><br></pre></td></tr></table></figure>
</li>
</ul>
<hr>
<h1 id="定制主题"><a href="#定制主题" class="headerlink" title="定制主题"></a>定制主题</h1><h2 id="主题定制流程"><a href="#主题定制流程" class="headerlink" title="主题定制流程"></a>主题定制流程</h2><p>定制主题修改.ejs布局文件后只需要执行<code>hexo g -d</code>即可重新生成文件,但是如果要修改样式,则需要按照以下流程:</p>
<ul>
<li><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"><span class="built_in">cd</span> themes/tranquilpeak/</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装npm依赖(需要root权限),如hexo在特定用户组运行,执行完成后还需再用chown修改回所属用户组:</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 npm install</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装bower依赖:</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">bower install</span><br></pre></td></tr></table></figure>
</li>
<li><p>每次修改js和css和资源文件以后,均需要在themes/tranquilpeak/目录下使用以下指令重新编译主题,该指令会生成编译且压缩过的文件并输出到themes/tranquilpeak/source/assets/目录中,以供Hexo生成静态文件时使用:</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">grunt buildProd</span><br></pre></td></tr></table></figure>
</li>
<li><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">hexo g <span class="_">-d</span></span><br></pre></td></tr></table></figure>
</li>
</ul>
<hr>
<h2 id="主题定制内容"><a href="#主题定制内容" class="headerlink" title="主题定制内容"></a>主题定制内容</h2><p>本主题Fork自Tranquilpeak的1.7.1版本,定制调整的内容如下: </p>
<ul>
<li><p>修改首页截断(自动截断,原作者的每篇文章打标记实在不适合我)</p>
</li>
<li><p>重命名layout解决TypeError错误</p>
</li>
<li><p>文章最大宽度从 750px 调整为 80%(更适合国内主流分辨率)</p>
</li>
<li><p>侧边栏添加rel nofollow配置(需要<strong>依赖hexo-autonofollow扩展</strong>),可通过配置文件中添加<code>rel: external nofollow</code>设置不输出权重给sidebar的链接</p>
</li>
<li><p>修改文章列表页面的布局,包括无图片时以及MarkDown文件Front-matter区域未指定布局时使用的默认布局和配图位置</p>
</li>
<li><p>每篇文章内容的末尾加上著作说明提示</p>
</li>
<li><p>修改代码高亮区域的最大宽度在小屏幕上的体验效果</p>
</li>
<li><p>自制多说js文件并使用cdn加速(如需使用请修改cdn链接)</p>
</li>
<li><p>修改鼠标悬停链接样式,去除下划线</p>
</li>
<li><p>关于页面头像和姓名可点击跳转到主页</p>
</li>
<li><p>修改文章title的标题和关键字分隔符,利于SEO</p>
</li>
<li><p>添加背景半透明</p>
</li>
<li><p>文章Meta信息中添加最后更新时间</p>
</li>
<li><p>多说评论逻辑优化。<strong>注意:</strong>为了方便迁移时划分多说评论到每篇需要评论的文章,需要在Front-matter区域指定id:duoshuo thread-key ,如多说文章的thread key为699时,需要指定<code>id: 699</code> ,字符串也可以。不带id的文章将不开启多说评论,不使用多说评论则无影响。</p>
</li>
<li><p>抛弃Swiftype搜索(这货收费实在是太贵了),添加自定义的站内搜索(需要<strong>依赖hexo-generator-search扩展</strong>),效果其实还不错。</p>
</li>
</ul>
<p>详情可见 <a href="https://github.com/quericy/tranquilpeak/" rel="external nofollow" target="_blank">https://github.com/quericy/tranquilpeak/</a>,修改更新记录可见<a href="https://github.com/quericy/tranquilpeak/commits/master" rel="external nofollow" target="_blank">Github提交记录</a>。</p>
<hr>
<h1 id="解决主题报错"><a href="#解决主题报错" class="headerlink" title="解决主题报错"></a>解决主题报错</h1><h2 id="修复TypeError-Cannot-read-property-‘compile’-of-undefined"><a href="#修复TypeError-Cannot-read-property-‘compile’-of-undefined" class="headerlink" title="修复TypeError: Cannot read property ‘compile’ of undefined"></a>修复TypeError: Cannot read property ‘compile’ of undefined</h2><p>这是tranquilpeak 1.7.1的一个bug,编译时会提示类似如下错误信息:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">ERROR Process failed: source/_css/layout/_about.scss</span><br><span class="line">ERROR Process failed: source/_css/layout/_blog.scss</span><br><span class="line">ERROR Process failed: source/_css/layout/_bottom-bar.scss</span><br><span class="line">ERROR Process failed: source/_css/layout/_cover.scss</span><br><span class="line">ERROR Process failed: source/_css/layout/_footer.scss</span><br><span class="line">ERROR Process failed: source/_css/layout/_header.scss</span><br><span class="line">ERROR Process failed: source/_css/layout/_main.scss</span><br><span class="line">ERROR Process failed: source/_css/layout/_sidebar.scss</span><br><span class="line"><span class="built_in">TypeError</span>: Cannot read property <span class="string">'compile'</span> <span class="keyword">of</span> <span class="literal">undefined</span></span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p><del>~修复方法如下</del>~(tranquilpeak的后续版本和我的定制版本已经修复此问题):</p>
<ul>
<li><p>将 source/_css/layout 目录重命名为 source/_css/layouts</p>
</li>
<li><p>修改<code>source/_css/tranquilpeak.scss</code>文件中的以下部分:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">import</span></span><br><span class="line"><span class="string">'layouts/about'</span>,</span><br><span class="line"><span class="string">'layouts/blog'</span>,</span><br><span class="line"><span class="string">'layouts/bottom-bar'</span>,</span><br><span class="line"><span class="string">'layouts/cover'</span>,</span><br><span class="line"><span class="string">'layouts/footer'</span>,</span><br><span class="line"><span class="string">'layouts/header'</span>,</span><br><span class="line"><span class="string">'layouts/main'</span>,</span><br><span class="line"><span class="string">'layouts/sidebar'</span>;</span><br></pre></td></tr></table></figure>
</li>
</ul>
<hr>
<h2 id="解决-ENOSPC-Error"><a href="#解决-ENOSPC-Error" class="headerlink" title="解决 ENOSPC Error"></a>解决 ENOSPC Error</h2><p>在npm install后运行hexo server报错ENOSPC Error,在Hexo<a href="https://hexo.io/docs/troubleshooting.html" rel="external nofollow" target="_blank">官方文档</a>中找到了答案,以作备忘:</p>
<ul>
<li><p>尝试使用<code>npm dedupe</code>修复(未成功);</p>
</li>
<li><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"><span class="built_in">echo</span> fs.inotify.max_user_watches=524288 | sudo tee <span class="_">-a</span> /etc/sysctl.conf && sudo sysctl -p</span><br></pre></td></tr></table></figure>
</li>
</ul>
<!-- toc -->
<h1 id="添加主题Tranquilpeak"><a href="#添加主题Tranquilpeak" class="headerlink" title="添加主题Tranquilpeak"></a>添加主题Tranquilpeak</h1><h2
WordPress迁移Hexo之数据迁移
https://quericy.me/blog/856/
2016-07-04T11:57:11.000Z
2016-07-12T05:57:11.000Z
<div class="alert success"><p>由于之前用的WordPress里存了不少文章,而且用了wordpress的一些插件和特殊格式,所以要先去WordPress的后台里导出文章的xml格式文件到本地,然后使用Hexo的迁移插件进行一些特殊处理并生成MarkDown格式的文件。</p>
</div>
<!-- toc -->
<h1 id="文章导出"><a href="#文章导出" class="headerlink" title="文章导出"></a>文章导出</h1><p>按照<a href="https://hexo.io/zh-cn/docs/migration.html" rel="external nofollow" target="_blank">Hexo官方文档</a>的说明,在 WordPress 仪表盘中导出数据(“Tools” → “Export” → “WordPress”),检查生成并下载到本地的xml文件,去除其中的无用分类(因为当前版本的Hexo的同级分类只能有一个,多个分类会变成父分类-子分类的格式。或许此问题以后会有妥善的解决方案)。</p>
<hr>
<h1 id="文章转换"><a href="#文章转换" class="headerlink" title="文章转换"></a>文章转换</h1><h2 id="安装迁移扩展"><a href="#安装迁移扩展" class="headerlink" title="安装迁移扩展"></a>安装迁移扩展</h2><p>在Hexo的项目目录下安装迁移扩展hexo-migrator-wordpress:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-migrator-wordpress --save</span><br></pre></td></tr></table></figure></p>
<h2 id="防止扩展生成的markdown文件名乱码"><a href="#防止扩展生成的markdown文件名乱码" class="headerlink" title="防止扩展生成的markdown文件名乱码"></a>防止扩展生成的markdown文件名乱码</h2><ol>
<li>编辑Hexo项目中的node_modules/hexo-migrator-wordpress/index.js 文件</li>
<li><p>修改56行的slug,改为 slug = item[‘wp:post_id’][0] ,这里是将slug修改为文章id</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> title = item.title[<span class="number">0</span>],</span><br><span class="line">id = item[<span class="string">'wp:post_id'</span>][<span class="number">0</span>],</span><br><span class="line">date = item[<span class="string">'wp:post_date'</span>][<span class="number">0</span>],</span><br><span class="line">slug = item[<span class="string">'wp:post_id'</span>][<span class="number">0</span>],<span class="comment">//修改这里</span></span><br><span class="line">content = item[<span class="string">'content:encoded'</span>][<span class="number">0</span>],</span><br><span class="line">comment = item[<span class="string">'wp:comment_status'</span>][<span class="number">0</span>],</span><br><span class="line">status = item[<span class="string">'wp:status'</span>][<span class="number">0</span>],</span><br><span class="line">type = item[<span class="string">'wp:post_type'</span>][<span class="number">0</span>],</span><br><span class="line">categories = [],</span><br><span class="line">tags = [];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!title && !slug) <span class="keyword">return</span> next();</span><br><span class="line"><span class="keyword">if</span> (type !== <span class="string">'post'</span> && type !== <span class="string">'page'</span>) <span class="keyword">return</span> next();</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> content !== <span class="string">'string'</span>) content = <span class="string">''</span>;</span><br></pre></td></tr></table></figure>
</li>
<li><p>解释下上一步的原因:我之前的WordPress文章是按照数据库里的文章id作为唯一别名的<code>(http://域名/blog/数字)</code>,而Hexo的文章链接是按照MarkDown文件名来决定的<code>(http://域名/可选前缀/MarkDown文件名/)</code>。为了新的站的链接也和之前相同,使用post_id作为slug即可迁移生成按文章id的文章文件。如果你的文章slug有特殊含义,请勿用这种方式处理。</p>
</li>
</ol>
<h2 id="WordPress特殊格式转换"><a href="#WordPress特殊格式转换" class="headerlink" title="WordPress特殊格式转换"></a>WordPress特殊格式转换</h2><p>同样编辑Hexo项目中的node_modules/hexo-migrator-wordpress/index.js 文件,然后根据自己需要修改的部分,在文件中分别进行以下处理.</p>
<h3 id="html实体转换-附加防止转换删除线"><a href="#html实体转换-附加防止转换删除线" class="headerlink" title="html实体转换(附加防止转换删除线)"></a>html实体转换(附加防止转换删除线)</h3><ol>
<li><p>添加函数replaceHTMLEntity:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">replaceHTMLEntity</span>(<span class="params">str</span>)</span>{</span><br><span class="line"> str = str.replace(<span class="regexp">/amp;/g</span>, <span class="string">''</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/&lt;/g</span>, <span class="string">'<'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/&gt;/g</span>, <span class="string">'>'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/&quot;/g</span>, <span class="string">'"'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/&#92;/g</span>, <span class="string">'\\'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/&#48;/g</span>, <span class="string">'0'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/~~/g</span>, <span class="string">'~ ~ '</span>);<span class="comment">//防止被转为删除线</span></span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
</li>
<li><p>添加一行代码到content = tomd(content).replace(/\r\n/g, ‘\n’);前面 :</p>
<figure class="highlight javascript"><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">content = replaceHTMLEntity(content);<span class="comment">//添加这行</span></span><br><span class="line">content = tomd(content).replace(<span class="regexp">/\r\n/g</span>, <span class="string">'\n'</span>);</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id="高亮代码标签转换-以及短代码提示框转换"><a href="#高亮代码标签转换-以及短代码提示框转换" class="headerlink" title="高亮代码标签转换,以及短代码提示框转换"></a>高亮代码标签转换,以及短代码提示框转换</h3><p>在WordPress使用了Syntax Highlighter代码高亮插件,还有一个短代码提示框S-shortcodes插件,请根据实际插件使用情况自行修改函数中的替换内容。</p>
<ol>
<li><p>添加函数replaceCodeTag :</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">replaceCodeTag</span>(<span class="params">str</span>)</span>{</span><br><span class="line"> <span class="comment">//Syntax Highlighter代码高亮插件</span></span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\]/gi</span>, <span class="string">'```\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[\/code\]/gi</span>, <span class="string">'\n```'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\ lang\=\"bash\"\ collapse=\"true\"\]/gi</span>, <span class="string">'```bash\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\ lang\=\"bash\"\]/gi</span>, <span class="string">'```bash\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\ lang\=\"php\"\]/gi</span>, <span class="string">'```php\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\ lang\=\"shell\"\]/gi</span>, <span class="string">'```bash\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\ lang\=\"csharp\"\]/gi</span>, <span class="string">'```cs\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\ lang\=\"html\"\]/gi</span>, <span class="string">'```html\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\ lang\=\"js\"\]/gi</span>, <span class="string">'```javascript\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\ lang\=\"javascript\"\]/gi</span>, <span class="string">'```javascript\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[code\ lang\=\"sql\"\]/gi</span>, <span class="string">'```sql\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[php\]/gi</span>, <span class="string">'```php\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[\/php\]/gi</span>, <span class="string">'\n```'</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//短代码提示框插件S-shortcodes</span></span><br><span class="line"> str = str.replace(<span class="regexp">/\[box\ style\=\"alert\"\]/gi</span>, <span class="string">'{% alert danger %}\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[box\ style\=\"warning\"\]/gi</span>, <span class="string">'{% alert warning %}\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[box\ style\=\"download\"\]/gi</span>, <span class="string">'{% alert info %}\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[box\ style\=\"info\"\]/gi</span>, <span class="string">'{% alert success %}\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[box\ style\=\"tip\"\]/gi</span>, <span class="string">'{% alert success %}\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[box\ style\=\"blue\"\]/gi</span>, <span class="string">'{% alert info %}\n'</span>);</span><br><span class="line"> str = str.replace(<span class="regexp">/\[\/box\]/gi</span>, <span class="string">'\n{% endalert %}'</span>);</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
</li>
<li><p>添加一行代码到content = tomd(content).replace(/\r\n/g, ‘\n’);前面 :</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">content = content = replaceCodeTag(content);<span class="comment">//添加这行</span></span><br><span class="line">content = replaceHTMLEntity(content);</span><br><span class="line">content = tomd(content).replace(<span class="regexp">/\r\n/g</span>, <span class="string">'\n'</span>);</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id="生成和tag相同的Keywords"><a href="#生成和tag相同的Keywords" class="headerlink" title="生成和tag相同的Keywords"></a>生成和tag相同的Keywords</h3><p>后续我们还要做SEO优化,这里可以先对其做一些前期准备。<br><code>Front-matter</code> 是Hexo文章的MarkDown文件最上方以 <code>---</code> 分隔的区域,可以用来指定个别文件的变量。<br>由于迁移插件会自动在Hexo文章最前面的Front-matter区域生成tags,我们可以也同时在Front-matter中生成与tags相同的关键词Keywords字符串。只需要在tag赋值处后面加一行:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (categories.length && type === <span class="string">'post'</span>) data.categories = categories;</span><br><span class="line"><span class="keyword">if</span> (tags.length && type === <span class="string">'post'</span>) data.tags = tags;</span><br><span class="line"><span class="keyword">if</span> (tags.length && type === <span class="string">'post'</span>) data.keywords = tags.join();<span class="comment">//添加这行</span></span><br></pre></td></tr></table></figure></p>
<p>这样在迁移文件生成的时候,每篇文章的tag还会以逗号分隔生成keywords项.</p>
<hr>
<h1 id="迁移文章导入"><a href="#迁移文章导入" class="headerlink" title="迁移文章导入"></a>迁移文章导入</h1><p>在hexo目录下,执行迁移指令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo migrate wordpress ***.xml</span><br></pre></td></tr></table></figure></p>
<ul>
<li>其中,<em>*</em>.xml为导出的WordPress文章数据文件。</li>
<li>执行完成后,就可以看到hexo项目的source/_posts文件夹下生成了对应的MarkDown文件。</li>
<li>如果有需要调整迁移脚本的地方,可以把生成的文件都删除(如果posts文件夹下已有文章记得转移或备份),修改迁移扩展的脚本文件后,重新执行迁移指令。</li>
<li>WordPress的加密文章也是会迁移过来的,需要注意处理。</li>
</ul>
<p>剩下的就是修改和微调啦,毕竟有些地方转换为markdown的版式和缩进细节难以完美,可能需要手动校对修改一遍。</p>
<div class="alert success"><p>由于之前用的WordPress里存了不少文章,而且用了wordpress的一些插件和特殊格式,所以要先去WordPress的后台里导出文章的xml格式文件到本地,然后使用Hexo的迁移插件进行一些特殊处理并生成MarkD
博客现已迁移至Hexo
https://quericy.me/blog/855/
2016-06-30T04:02:11.000Z
2022-06-28T05:27:25.671Z
<p>迁移完成~ ~!(<del>额,这句话好像很久以前说过?</del>)</p>
<p>最近一段时间都没有写blog,一方面是忙着抛弃臃肿的WordPress转战Hexo,另一方面是用OneNote用得比较嗨(<strong>其实主要还是懒癌犯了</strong>).</p>
<p>主要还是利用零碎的时间一点点做的修改,效率不高但也获益不少</p>
<p>从看Hexo文档,到修改定制主题,到迁移数据整理,到优化和特性调整,总算是在月底大体上完成了</p>
<p>之后会整理出一份完整的迁移文档,算是对这段时间折腾的一个总结吧,总是用OneNote的话,笔记量大了就有点乱糟糟的</p>
<p>迁移完成~ ~!(<del>额,这句话好像很久以前说过?</del>)</p>
<p>最近一段时间都没有写blog,一方面是忙着抛弃臃肿的WordPress转战Hexo,另一方面是用OneNote用得比较嗨(<strong>其实主要还是懒癌犯了</strong>).</p>
打算换个VPS试试
https://quericy.me/blog/854/
2016-04-05T06:57:11.000Z
2022-06-28T05:27:25.671Z
<p>自从 SoftLayer 开始绕路,速度就慢成狗了,600+ms的ping简直蛋疼~ ~ </p>
<p>无意间在V2看到 kdatacenter.com ,月付 19 刀略贵,但是speedtest 跑起来的数据貌似不错,平均90ms的延迟,对电信比较友好,或许还能搭建个饥荒的服务器?<br>大概看了下,最低配置500G 流量,100G SSD ,应该还是够用的~</p>
<p>链接(带aff):<a href="http://www.kdatacenter.com/myportal/?affid=72" rel="external nofollow" target="_blank">http://www.kdatacenter.com/</a></p>
<p>自从 SoftLayer 开始绕路,速度就慢成狗了,600+ms的ping简直蛋疼~ ~ </p>
<p>无意间在V2看到 kdatacenter.com ,月付 19 刀略贵,但是speedtest 跑起来的数据貌似不错,平均90ms的延迟,对电信比较友好,或许还能搭建个
Homestead离线安装踩坑记录
https://quericy.me/blog/827/
2016-02-21T12:31:02.000Z
2022-06-28T05:27:25.671Z
<div class="alert success"><p>之前一直使用vagrant跑Minimal版的CentOS,搭建和项目一致的环境费了不少时间.最近折腾Laravel的时候看了下Homestead,感觉确实会省事很多,便试着安装了下,结果还是踩坑了.现记录下来以供个人总结和参考.</p>
</div>
<p>进行Homestead离线安装前,先确认了一下本地环境.操作系统为Windows10,现有的vagrant版本为1.7.4,距最新Releases差了两个小版本号倒也足够用.但是由于国内某些众所周知的原因,直接vagrant box add laravel/homestead下载的话会慢成Doge,于是只能选择离线安装的方式.</p>
<h3 id="下载Homestead-box"><a href="#下载Homestead-box" class="headerlink" title="下载Homestead box"></a>下载Homestead box</h3><p>首先在<a href="https://atlas.hashicorp.com/laravel/boxes/homestead/" rel="external nofollow" target="_blank">hashicorp</a>中找到合适的版本,再在链接后加上”版本号/providers/虚拟机类型.box”即可获得下载链接.<br>如我们要下载版本号为0.4.1的virtualbox版的box文件,链接即为: <a href="https://atlas.hashicorp.com/laravel/boxes/homestead/versions/0.4.1/providers/virtualbox.box" rel="external nofollow" target="_blank">https://atlas.hashicorp.com/laravel/boxes/homestead/versions/0.4.1/providers/virtualbox.box</a></p>
<h3 id="离线导入box文件"><a href="#离线导入box文件" class="headerlink" title="离线导入box文件"></a>离线导入box文件</h3><p>在box所在目录下执行add指令导入即可,注意替换box文件名:</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">vagrant box add laravel/homestead homestead-v0.4.1.box</span><br></pre></td></tr></table></figure>
<h3 id="安装HomeStead"><a href="#安装HomeStead" class="headerlink" title="安装HomeStead"></a>安装HomeStead</h3><p>Git clone拉取<a href="https://github.com/laravel/homestead.git" rel="external nofollow" target="_blank">HomeStead源码</a>,双击init.bat脚本安装(或者运行bash init.sh)完成安装.其实看下源码就发现安装只是拷贝3个文件到用户目录下的.homestead文件夹中.其中有个Homestead.yaml便是我们要修改的HomeStead配置文件.</p>
<h3 id="配置Homestead-yaml"><a href="#配置Homestead-yaml" class="headerlink" title="配置Homestead.yaml"></a>配置Homestead.yaml</h3><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">authorize:<span class="comment">#手动生成的openSSH公钥</span></span><br><span class="line">keys:<span class="comment">#手动生成的openSSH私钥</span></span><br><span class="line">folders:<span class="comment">#对应本地需要映射到虚拟机中的文件夹</span></span><br><span class="line"> map:<span class="comment">#指定本地路径,Win下目录各级间使用"/"分割,包括盘符(如C:/SomeFolders/TargetFolders)</span></span><br><span class="line"> to:<span class="comment">#指定映射到vagrant内的目录路径,保持默认即可.如果设置正常,在虚拟机运行时,可在虚拟机配置中找到挂载的共享文件夹.</span></span><br><span class="line">sites:<span class="comment">#站点配置</span></span><br><span class="line"> map:<span class="comment">#站点的域名,设置后记得在本地hosts中绑定指向Homestead虚拟机ip或127.0.0.1</span></span><br><span class="line"> to:<span class="comment">#站点在vagrant中的默认目录</span></span><br></pre></td></tr></table></figure>
<h3 id="启动Homestead虚拟机"><a href="#启动Homestead虚拟机" class="headerlink" title="启动Homestead虚拟机"></a>启动Homestead虚拟机</h3><p>进入之前Git clone拉取下来的homestead目录,运行vagrant up,正常情况下就可启动homestead虚拟机…..<br>—-当然,那是指正常情况,homestead就可以跑起来了,那也就不必往下看了.然而可能由于homestead的最新版本对离线安装的box判定问题,或是已有vagrant的其他环境冲突,所以这里踩了几个坑.</p>
<h3 id="坑1-box-XXX-could-not-be-found"><a href="#坑1-box-XXX-could-not-be-found" class="headerlink" title="坑1:box XXX could not be found"></a>坑1:box XXX could not be found</h3><p>首次执行vagrant up就告诉我不存在,然后它开始自顾自的跑去下载了- -||<br>而vagrant box list指令返回的box中确实已经有了该box了啊?谷歌一圈没有找到满意的答案,这坑看来还是要自己填~ ~ ~<br>先从自身找原因,一开始我使用vagrant box add 指令创建的是一个别名,难道它只能识别name为laravel/homestead的box?删掉box重新添 加,错误依旧.确实不大可能和别名有关,真是想多了.<br>然后从vagrant开始排查,由于之前装过vagrnat,且使用了<a href="http://wing2south.com/post/44371306891/vagrant/" rel="external nofollow" target="_blank">《将Vagrant移出系统盘的方法》</a>将.vagrant.d转移到其他目录了.便推测homestead是否无视了vagrant的环境变量VAGRANT_HOME,而去寻找默认目录了.<br>由于不想影响现有的其他box,便使用mklink做了个junction硬链接过去,但是并没有效果,于是尝试硬链接到homestead源码的虚拟机目录下,结果悲剧了,没想homestead初始化虚拟机目录时会删除目录下的所有文件….不作死就不会死啊,尝试的时候真是不要太过无脑和随意.(庆幸的是被删的box我做过package了)<br>冷静下来,看了一下源码,再使用debug模式调用vagrant up,大致理解调用的判断还是依据vagrant的,而其他box没问题的话,确实和.vagrant.d目录的迁移无关.</p>
<p>于是考虑是不是因为box没展开的原因,因为每个add的box首次vagrnat up是会进行初始化的,因为墙的原因我也并没法知道在线下载是不是比离线add多了一步展开.<br>先将HomeStead源码目录的Vagrantfile重命名为Vagrantfile.bak,然后在当前目录执行<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vagrant init laravel/homestead <span class="comment">#vagrant box add时的名称</span></span><br></pre></td></tr></table></figure></p>
<p>这时又生成了一个Vagrantfile文件,再在当前目录执行vagrant up<br>这相当于使用vagrant默认的方法进行box的展开安装了.完成后删掉刚才生成的Vagrantfile文件,把Homestead的Vagrantfile.bak改回Vagrantfile.<br>这时再使用vagrant up,果然找到了box了,并可自动根据配置文件进行一系列homestead的配置操作了.</p>
<h3 id="坑2-Warning-Authentication-failure-Retrying…"><a href="#坑2-Warning-Authentication-failure-Retrying…" class="headerlink" title="坑2:Warning: Authentication failure. Retrying…"></a>坑2:Warning: Authentication failure. Retrying…</h3><p>这个错误发生在我将该homestead项目进行开发后,用package指令打包后放到另一台电脑,并按照上面的步骤进行启动(区别在于使用的box自己为打包的box)时出现.<br>仔细想想就发现,使用自己打包的box已经存在有自动生成的密钥对了,再手动生成openSSH必然无法通过授权.<br>于是先进入.vagrant.d\boxes\laravel-VAGRANTSLASH-homestead\0\virtualbox\目录下,找到vagrant_private_key私钥文件.<br>这里我先是将Homestead.yaml替换成这个解包出来的私钥和对应的公钥了,但是仍然无法通过授权= =||</p>
<p>然后查看了一下使用的key的路径<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vagrant ssh-config</span><br></pre></td></tr></table></figure></p>
<p>发现其中的IdentityFile指向了.vagrant.d目录的private_key文件,这个就比较费解了,为嘛homestead无视了配置文件里的公钥和私钥指定,却调用了这个随机生成的私钥?将该目录的private_key文件内容替换为解包出来的那个私钥,授权通过.</p>
<h3 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接:"></a>参考链接:</h3><p><a href="http://urouge.github.io/how-to-download-vagrant-boxes/" rel="external nofollow" target="_blank">如何下载Vagrant的Box</a><br><a href="https://www.alwayscoder.com/offline-install-laravel-homestead/" rel="external nofollow" target="_blank">离线安装&配置Laravel开发环境Homestead</a><br><a href="https://github.com/summerblue/phphub/wiki/PHPhub-%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E9%83%A8%E7%BD%B2#user-content-%E5%AE%89%E8%A3%85-virtualbox-%E5%92%8C-vagrant" rel="external nofollow" target="_blank">PHPhub 开发环境部署</a><br><a href="http://rrylee.github.io/2015/07/25/Homestead%E6%90%AD%E5%BB%BA/" rel="external nofollow" target="_blank">Windows下Homestead环境部署</a></p>
<div class="alert success"><p>之前一直使用vagrant跑Minimal版的CentOS,搭建和项目一致的环境费了不少时间.最近折腾Laravel的时候看了下Homestead,感觉确实会省事很多,便试着安装了下,结果还是踩坑了.现记录下来以供个人总
果然临近年关是最忙碌的时刻
https://quericy.me/blog/818/
2016-01-30T11:55:47.000Z
2022-06-28T05:27:25.671Z
<p>忽然发现已经好久没有来折腾博客了,其实还是因为最近忙成Doge了….去年因为很多忙帮不上,还没有直观的感受<br>当然,今天把工作处理完了,感觉到年前这段时间会稍微轻松一点了~ ~<br>嗯,怎么说呢,最近这段时间感觉最糟糕的是,没有太大的进步,忙于解决重复和冗杂的需求,纯粹的体力活对技术的提升收效甚微.<br>于是给自己定下了新年后的以下几个目标:<br>1,▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇<br>2,▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇<br>3,▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇<br>4,▇▇▇▇▇▇▇▇<br>5,▇▇▇▇▇▇▇▇▇▇</p>
<p>(PS:路过的各位,如果有的话,看到这篇吐槽的时候,请<strong>务必不要用鼠标刮</strong>,嗯,相信我)</p>
<p>以上的目标已经默默记下,我也会默默的完成和实现的.</p>
<p>是时候换份心情了,记得有句话是这样说的—-<br><code>"如果你不赶紧按照你想的方式去活,那迟早会按照你活的方式去想"</code></p>
<p>忽然发现已经好久没有来折腾博客了,其实还是因为最近忙成Doge了….去年因为很多忙帮不上,还没有直观的感受<br>当然,今天把工作处理完了,感觉到年前这段时间会稍微轻松一点了~ ~<br>嗯,怎么说呢,最近这段时间感觉最糟糕的是,没有太大的进步,忙于解决重复和冗杂的需求,纯
巧用七牛https域名,无需反代让多说支持SSL和CDN加速
https://quericy.me/blog/788/
2015-11-14T07:13:51.000Z
2022-06-28T05:27:25.671Z
<div class="alert success"><p>之前在<a href="http://quericy.me/blog/465" title="解决多说插件在HTTPS下访问的问题">这篇文章</a>中尝试解决多说在https下的问题,但是后来由于ds.duoshuo.com停止对https的解析导致头像在SSL中加载失效.前几天看到了<a href="https://imququ.com/post/duoshuo-and-https.html" rel="external nofollow" target="_blank">Jerry Qu的博客</a>中的使用ngnix反代缓存头像的方法,稍作修改,可借用七牛,实现无需设置服务器反向代理,即可让多说完美支持SSL.</p>
</div>
<p>相比于使用反代缓存多说头像,直接利用七牛有以下几点好处:<br><strong>1,</strong>可以让不支持反代的环境(比如托管在git的静态页面)中的多说也支持https.<br><strong>2,</strong>借用七牛CDN能让像自己这样的海外VPS的评论头像图片也获得加速效果.<br><strong>3,</strong>免除了服务器缓存图片的磁盘空间开销.</p>
<p>其实实现起来非常简单:</p>
<div class="alert info"><p>配置七牛空间</p>
</div>
<p>1,首先,需要4个七牛云存储的空间.<br>如果是七牛的标准用户以上的话,可以创建20个空间,直接创建4个公开空间即可;<br>如果是体验用户,只能创建一个空间,那就需要注册4个体验用户并分别创建公开空间了.</p>
<p>2,进入创建的空间,选择”空间设置”->”基本设置”,在”镜像存储”填写需要缓存的头像域名,四个空间分别填写的镜像源为:<br>镜像源: <a href="http://himg.bdimg.com/" rel="external nofollow" target="_blank">http://himg.bdimg.com/</a><br>镜像源: <a href="http://ds.cdncache.org/" rel="external nofollow" target="_blank">http://ds.cdncache.org/</a><br>镜像源: <a href="http://app.qlogo.cn/" rel="external nofollow" target="_blank">http://app.qlogo.cn/</a><br>镜像源: <a href="http://tp1.sinaimg.cn/" rel="external nofollow" target="_blank">http://tp1.sinaimg.cn/</a></p>
<p><a href="https://static.quericy.me/1drive/blog/wp-content/uploads/2015/11/qiniu-ssl-box1.jpg" rel="external nofollow" target="_blank"><img src="https://static.quericy.me/1drive/blog/wp-content/uploads/2015/11/qiniu-ssl-box1.jpg" alt="qiniu-ssl-box1"></a></p>
<p>3,选择”域名设置”,在HTTPS一栏填写自定义的qbox.me的二级域名(自己取名),并提交审核.(审核一般是2个工作日,然而我国庆后提交的审核整整等了7个工作日= =|| )</p>
<p>4,在空间的”基本设置”中可设置”防盗链”选项,控制来访域名,毕竟七牛的免费配额和请求数有限,还是稍微限制下防止滥用.</p>
<p>5,记下4个https的域名,等审核通过后就可以使用了,这之前可以先行准备,修改多说的js文件:</p>
<div class="alert warning"><p>修改多说embed.js</p>
</div>
<p>1,下载<a href="http://static.duoshuo.com/embed.js" rel="external nofollow" target="_blank">http://static.duoshuo.com/embed.js</a><br>修改以下部分(请自行修改替换):<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">avatarUrl: <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">document</span>.location.protocol == <span class="string">"https:"</span>) {</span><br><span class="line"> <span class="keyword">if</span> (e.avatar_url) {</span><br><span class="line"> e.avatar_url = e.avatar_url.replace(<span class="regexp">/^http\:\/\//</span>, <span class="string">"https://"</span>);</span><br><span class="line"> e.avatar_url = e.avatar_url.replace(<span class="regexp">/himg\.bdimg\.com/</span>, <span class="string">"dn-***.qbox.me"</span>);<span class="comment">//镜像源是baidu的https域名</span></span><br><span class="line"> e.avatar_url = e.avatar_url.replace(<span class="regexp">/ds\.cdncache\.org/</span>, <span class="string">"dn-***.qbox.me"</span>);<span class="comment">//镜像源是cdncache的https域名</span></span><br><span class="line"> e.avatar_url = e.avatar_url.replace(<span class="regexp">/img\d+\.douban\.com/</span>, <span class="string">"img1.doubanio.com"</span>);<span class="comment">//豆瓣无需镜像</span></span><br><span class="line"> e.avatar_url = e.avatar_url.replace(<span class="regexp">/app\.qlogo\.cn/</span>, <span class="string">"dn-***.qbox.me"</span>);<span class="comment">//镜像源是qlogo的https域名</span></span><br><span class="line"> e.avatar_url = e.avatar_url.replace(<span class="regexp">/tp\d+\.sinaimg\.cn/</span>, <span class="string">"dn-***.qbox.me"</span>);<span class="comment">//镜像源是sina的https域名</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> rt.data.default_avatar_url = <span class="string">"https://***/***.jpg"</span>;<span class="comment">//评论区默认头像的地址,请自行修改</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> e.avatar_url || rt.data.default_avatar_url</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p><strong>注意1:</strong> 以上代码中域名部分的<code>***</code>请自行替换成上一步当中提交审核的那4个自定义的qbox.me域名,如需压缩js请先把注释内容删干净.<br><strong>注意2:</strong> 默认头像地址自行设置,需支持https,可直接上传需要的图片到七牛得到外链.<br><strong>注意3:</strong> 豆瓣默认的头像地址由于禁止了外链,七牛请求缓存会被豆瓣禁止,这里直接参考了豆瓣官方api文档,<del>域名替换成 <a href="https://img2.doubanio.com/" rel="external nofollow" target="_blank">https://img2.doubanio.com/</a> 即可显示头像</del>,域名替换成 <a href="https://img1.doubanio.com/" rel="external nofollow" target="_blank">https://img1.doubanio.com/</a> 或者 <a href="https://img3.doubanio.com/" rel="external nofollow" target="_blank">https://img3.doubanio.com/</a> 即可显示头像(2016-08-24: 豆瓣最近修改了api,img2.doubanio.com 无效了,而img1和img3均有效,且多说貌似也自己用了doubanio的头像地址,一般情况下可以不用正则匹配了),不需要用到七牛.</p>
<p>2,分组表情建议直接在多说后台设置禁止使用表情,一来多说表情里的坑太多,embed.js中对表情的地方改了好几次了,不保证修改了以后还能不出问题,二来,没什么必要,带的那些表情实在是丑.<br>如果实在需要表情的话也可以,WordPress的头像可以直接取<a href="https://static.duoshuo.com/,其他的微博头像域名是http://img.t.sinajs.cn/,也可用上面的方法新建个七牛空间进行表情缓存.不过显示表情的时候也要进行正则替换就是了" rel="external nofollow" target="_blank">https://static.duoshuo.com/,其他的微博头像域名是http://img.t.sinajs.cn/,也可用上面的方法新建个七牛空间进行表情缓存.不过显示表情的时候也要进行正则替换就是了</a>.</p>
<p>3,将修改后的embed.js上传到七牛,并将多说默认引用的地址改为七牛的https外链地址.这样即可实现多说的本地化(包括CDN加速)和SSL下完美头像缓存,除了数据部分,静态资源和图片的存取均交由七牛完成.<br>至于多说引用的地方需要根据项目自行查找,如WordPress就在wp-content/plugins/duoshuo/的WordPress.php里,其他请自行谷歌”多说 本地化”.</p>
<h4 id="PS"><a href="#PS" class="headerlink" title="PS:"></a>PS:</h4><p>其实这些篇文章的内容在国庆后就已经实现了,愣是忙到双十一后才整理出来orz~ ~<br>第二次解决多说这个蛋疼的问题了,反正现在暂时可以放心的全站强制SSL了.<br>但是本文依然具有时效性,以后不知道多说是否会再改.而且多说的稳定性真的不敢恭维啊…或许该试试用Disqus了么?</p>
<p>参考链接:<br><a href="https://imququ.com/post/duoshuo-and-https.html" rel="external nofollow" target="_blank">让多说评论框完美支持 HTTPS</a></p>
<p><a href="https://developers.douban.com/wiki/?title=user_v2" title="user_v2" rel="external nofollow" target="_blank">豆瓣用户API V2</a></p>
<div class="alert success"><p>之前在<a href="http://quericy.me/blog/465" title="解决多说插件在HTTPS下访问的问题">这篇文章</a>中尝试解决多说在https下的问题,但是后来由于ds.duoshuo.
更换Linux内核优化锐速,为shadowsocks和IkeV2加速
https://quericy.me/blog/750/
2015-11-12T16:10:46.000Z
2022-06-28T05:27:25.671Z
<div class="alert info"><p>之前曾经在<a href="http://quericy.me/blog/556" title="如何让高延迟不稳定的服务器下载速度翻倍">这篇文章</a>里试过用net-speeder来提升速度,有优有劣.这次换用锐速来尝试提速,并整理归纳成本文,供参考与交流使用.</p>
</div>
<hr>
<div class="alert success"><p>优劣对比</p>
</div>
<p>由于net-speeder无脑双倍发包,在某些偶尔丢包的时候能有比较好的效果,同时无需适配内核均可使用.但在丢包非常严重的情况下,锐速的算法效果要比net-speeder好的多,在低延迟以及较为稳定的线路上,锐速的表现也优于net-speeder.但是锐速的缺点也很明显,一个是支持的内核不多,一旦不一致需要更换内核,甚至openVZ不支持更换内核,如在支持列表中就只能放弃.另一个问题是,相比于开源的net-speeder,锐速是不开源的,进出的数据包理论上都可以被锐速监听到,所以不建议在重要的生产环境中使用锐速.</p>
<div class="alert warning"><p>内核匹配</p>
</div>
<p>在做其他的事情之前,首先请先访问<a href="http://my.serverspeeder.com/ls.do?m=availables" rel="external nofollow" target="_blank">锐速支持列表</a>来查看当前锐速所支持的官方发行版本,判断自己的vps的内核版本是否在锐速支持的列表中提供.<br>以下两个命令分别可以查看当前的操作系统和内核版本:</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">cat /etc/issue</span><br><span class="line">uname <span class="_">-a</span></span><br></pre></td></tr></table></figure>
<p>如果匹配,恭喜,可以跳到下个步骤直接安装了,如果并没有找到,那么想使用锐速的话,就需要进行内核的升/降级.当然,OpenVZ的vps是不支持更换内核的,就不需要考虑了.(<strong>对内核的操作请慎重考虑,有可能需要自行承担使用的风险</strong>)</p>
<p>这里列举收集和整理的CentOS6和Ubuntu的内核降级方法.</p>
<h4 id="CentOS内核修改"><a href="#CentOS内核修改" class="headerlink" title="CentOS内核修改"></a>CentOS内核修改</h4><p><strong>1</strong>,首先禁止版本升级,编辑/etc/yum.conf文件,在最后添加:</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">exclude=kernel* centos-release</span><br></pre></td></tr></table></figure>
<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">exclude=kernel*,centos-release*</span><br></pre></td></tr></table></figure>
<p>注意:一种是用空格分开,一种是用逗号分开,如果是redhat,则把centos-release<em>改为redhat-release</em></p>
<p><strong>2</strong>,下载所需要内核版本的rpm文件:<br>由于国内的源(比如<a href="http://mirrors.aliyun.com/centos/" rel="external nofollow" target="_blank">阿里的源</a>)已经没有CentOS6.7以前的文件了,这里我们从下面这个url来找需要的文件(需自行替换):<br><a href="http://ftp.scientificlinux.org/linux/scientific/**系统版本**/**位数**/updates/security/" rel="external nofollow" target="_blank">http://ftp.scientificlinux.org/linux/scientific/**系统版本**/**位数**/updates/security/</a></p>
<p>这里的位数,如果是32位的话则是<strong>i386</strong>,如果是64位的话则是<strong>x86_64</strong>,<br><strong>注意:</strong>如果是CentOS 5并且uname -a命令输出中有el5xen字样 请务必下载Xen版内核,否则可能导致无法开机!CentOS6则没有Xen内核和非Xen内核区别.<br>系统版本则替换成CentOS系统的版本号.<br>例如,系统是CentOS6.6 64位的话,则打开:<br><a href="http://ftp.scientificlinux.org/linux/scientific/6.6/x86_64/updates/security/" rel="external nofollow" target="_blank">http://ftp.scientificlinux.org/linux/scientific/6.6/x86_64/updates/security/</a></p>
<p>然后在列表中找到我们需要的(锐速的支持列表中的对应的)内核版本的rpm文件并下载.</p>
<p>例如,这里我们在锐速列表中发现CentOS6.6所支持的版本有2.6.32-504.3.3.el6.x86_64,在rpm下载列表中也存在对应的rpm文件,即使用wget下载:</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">wget http://ftp.scientificlinux.org/linux/scientific/6.6/x86_64/updates/security/kernel-2.6.32-504.3.3.el6.x86_64.rpm</span><br></pre></td></tr></table></figure>
<p>CentOS 5和6不可跨大版本使用内核,下载的文件请务必对应自己所需.</p>
<p><strong>3</strong>,强制安装内核:</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">rpm -ivh kernel-2.6.32-504.3.3.el6.x86_64.rpm --force</span><br></pre></td></tr></table></figure>
<p>文件名自行替换所下载的内核rpm.静待安装成功后重启计算机.</p>
<hr>
<h4 id="Ubuntu内核修改"><a href="#Ubuntu内核修改" class="headerlink" title="Ubuntu内核修改"></a>Ubuntu内核修改</h4><p><strong>内核升级:</strong><br>Ubuntu内核升级普遍使用以下指令:</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 apt-get dist-upgrade</span><br></pre></td></tr></table></figure>
<p><del>当然升级到哪个版本就不好说了~ ~ ~</del><br><strong>内核降级:</strong><br>1,首先在锐速支持列表中选好想要降级到的内核版本,例如3.13.0-24-generic.然后执行以下指令(自行替换两处版本号):</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 aptitude install -y linux-image-3.13.0-24-generic linux-headers-3.13.0-24</span><br></pre></td></tr></table></figure>
<p>2,执行以下命令:</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">grep submenu /boot/grub/grub.cfg</span><br></pre></td></tr></table></figure>
<p>看到单引号中的父选项,类似:<br>Advanced options for Ubuntu<br>将引号内的内容复制下来,如图蓝框中所选:<br><a href="https://static.quericy.me/1drive/blog/wp-content/uploads/2015/11/ubuntu-submenu.jpg" rel="external nofollow" target="_blank"><img src="https://static.quericy.me/1drive/blog/wp-content/uploads/2015/11/ubuntu-submenu.jpg" alt="ubuntu-submenu"></a></p>
<p>3,执行以下命令:</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">grep menuentry /boot/grub/grub.cfg</span><br></pre></td></tr></table></figure>
<p>找到类似以下的单引号中的子选项:<br>Ubuntu, with Linux 3.13.0-24-generic<br>将引号内的内容复制下来,如图蓝框中所选:<br><a href="https://static.quericy.me/1drive/blog/wp-content/uploads/2015/11/ubuntu-menuentry.jpg" rel="external nofollow" target="_blank"><img src="https://static.quericy.me/1drive/blog/wp-content/uploads/2015/11/ubuntu-menuentry.jpg" alt="ubuntu-menuentry"></a></p>
<p>4,用编辑器修改/etc/default/grub文件:<br>将其中<br>GRUB_DEFAULT=0<br>改为<br>GRUB_DEFAULT=”<strong>Advanced options for Ubuntu</strong>><strong>Ubuntu, with Linux 3.13.0-24-generic</strong>“<br>其实GRUB_DEFAULT的值就是之前复制的两段内容用>号拼接后,加双引号.</p>
<p>5,保存文件后执行:</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 update-grub</span><br></pre></td></tr></table></figure>
<p>此时如果有报错,可能因为不在次级选项中,将GRUB_DEFAULT是否需要将前面部分”Advanced options for Ubuntu>”去掉,修改为:<br>GRUB_DEFAULT=”Ubuntu, with Linux 3.13.0-24-generic”<br>没有错误提示,即可reboot重启系统.</p>
<p>6,重启后使用uname -r可查看当前的内核版本号.如果是新的则表示成功.<br>有点vps提供方会限制内核,所以还要具体视情况而定,并不是所有的都可以替换,还有的需要在面板中调整.<br>如Linode就需要在后台面板中把kernel设置成pv-grub-x86_32 或 pv-grub-x86_64才可以更换内核.</p>
<div class="alert success"><p>锐速安装</p>
</div>
<p>以锐速官网说明为准,这里就不浪费篇幅了,自动安装参考<a href="http://my.serverspeeder.com/w.do?m=lsl" rel="external nofollow" target="_blank">http://my.serverspeeder.com/w.do?m=lsl</a>,手动安装参考<a href="http://my.serverspeeder.com/w.do?m=lslm" rel="external nofollow" target="_blank">http://my.serverspeeder.com/w.do?m=lslm</a></p>
<p>安装时的各个配置选项可以直接回车使用括号中的默认值,也可根据实际情况进行调整.(建议对入口和出口带宽,按实际情况手动输入)</p>
<div class="alert danger"><p>锐速优化</p>
</div>
<p>1,首先使用ifconfig确认网卡接口(如eth0),然后执行以下指令:</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">ethtool -k eth0</span><br></pre></td></tr></table></figure>
<p>这里的eth0请自行替换为自己的网卡.<br>然后记下以下加速接口的状态:<br>tso (tcp segmentation offload)<br>gso (generic segmentation offload)<br>gro(generic receive offload)<br>lro(large receive offload)<br>sg(scatter gather)</p>
<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">vim /serverspeeder/etc/config</span><br></pre></td></tr></table></figure>
<p><strong>以下几点是需要特别注意配置的:</strong><br>◆如果上面的tso、gso、gro、lro 和 sg 其中之一是可以打开的,则设置gso=”1”<br>◆如果gro和lro是可以开启的,则设置rsc=”1”.(有些网卡虽然未开启,但也支持RSC算法的,也可尝试设置为1)<br>◆在丢包率较高的环境下,设置maxmode=”1” 能非常明显的获得速度的提升.此功能和net-speeder暴力发包类似,为了整体网络环境考虑,虽然锐速有自己的拥塞控制,但还是建议视情况决定是否需要开启此开关.<br>◆流入方向流量加速,设置advinacc=”1”.<br>◆还有个可以得到更好的加速效果的开关:设置advacc=”1”,但有可能会造成有效数据率下降,是否开启自行考虑.<br>◆如果需要锐速对PPTP,L2TP和IkeV2进行加速,则开启accppp=”1”.<br>◆对于OpenVPN,需要先用ifconfig指令确定OpenVPN对应接口(大多是tun0),然后在accif后面追加接口,如accif=”eth0 tun0”.</p>
<p>配置完成后,使用service serverSpeeder restart重启锐速服务,shadowsocks无需特别配置,只需遵从上面的优化配置,在锐速重启后稍等片刻即可提速.</p>
<p><strong>另附:锐速配置文件详解,有更详细的定制需要时可参考此配置说明进行调整(点击展开)</strong></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line">accif=<span class="string">"eth*"</span></span><br><span class="line"><span class="comment">#加速接口,eth*一般为服务器上对外提供服务的网络接口,例如 eth0 eth1,一般通过 ifconfig 命令可以看到,可以同时设定多个接口作为加速接口(接口之间用空格分开),如 accif="eth0 eth1",默认为"eth0"。</span></span><br><span class="line">acc=<span class="string">"1"</span></span><br><span class="line"><span class="comment">#TCP 加速开关,设为 1 表示开启 LotServer 的 TCP 加速功能,设为 0 表示关闭LotServer 的 TCP 加速功能,默认为 1。</span></span><br><span class="line">advacc=<span class="string">"1"</span></span><br><span class="line"><span class="comment">#高级加速开关,设为 1 表示开启,设为 0 表示关闭,开启此功能可以得到更好的加速效果,但有可能会造成有效数据率下降,默认为 1</span></span><br><span class="line">advinacc=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#高级入向加速开关,设为 1 表示开启,设为 0 表示关闭,开启此功能可以得到更好的流入方向流量加速效果。</span></span><br><span class="line">wankbps=<span class="string">"1000000"</span></span><br><span class="line"><span class="comment">#加速接口上行带宽,指的是从服务器流出到 Internet 的最大带宽,单位为 Kbps,例如服务器所连接的 Internet 带宽为 1G,则设置为 1000000,默认为 1000000。</span></span><br><span class="line">waninkbps=<span class="string">"1000000"</span></span><br><span class="line"><span class="comment">#加速接口下行带宽,指的是从 Internet 流入服务器的最大带宽,单位为 Kbps,例如服务器所连接的 Internet 带宽为 1G,则设置为 1000000,默认为 1000000,wankbps 和 waninkbps 的设置一般与实际带宽相同最理想,如果无法确定实际带宽,一般设置为网卡的最大吞吐能力即可,例如网卡为千兆全双工网卡,则 wankbps和 waninkbps 都设置为 1000000,当设置过小时,例如实际带宽有 100Mbps,但 wankbps 和 waninkbps 设置为10Mbps,则数据流量会被限制在 10Mbps 以内,此时会降低服务器的吞吐。</span></span><br><span class="line">csvmode=<span class="string">"0"</span> highcsv=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#csvmode 拥塞控制模式开关,设为 1 表示开启,设为 0 表示关闭,开启 tcp 加速后,在某些丢包较大的网络中可能会造成有效数据率下降的问题,此时开启拥塞控制模式,可以有效避免该问题,但开启此选项时,可能加速效果也会下降,默认为 0,csvmode="1" highcsv="0"时,为普通拥塞控制模式,对拥塞具有一定控制的同时对加速效果的影响较小,csvmode="1" highcsv="1"时,为高级拥塞控制模式,可以更好的控制拥塞但是相比于普通拥塞控制模式对加速效果的影响较大。</span></span><br><span class="line">subnetAcc=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#局域网加速开关,设为 1 表示开启,设为 0 表示关闭,不开启时对于同一局域网内的连接不会加速,开启后对同一网段的 TCP 连接也进行加速。</span></span><br><span class="line">maxmode=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#最大传输模式,设为 1 表示开启,设为 0 表示关闭,开启后会进一步提高加速效果,但是可能会降低有效数据率。</span></span><br><span class="line">maxTxEffectiveMS=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#最大传输模式生效时间,用于控制最大传输模式对每一个连接的生效时间,设为0 表示从连接建立到连接结束,都采用最大传输模式传输,单位为 ms,1000 即 1s。</span></span><br><span class="line">pcapEnable=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#抓包开关,设为 1 表示开启,设为 0 表示关闭,开启此功能后会抓取经过加速引擎处理的数据包,在/appex/log 目录下生成抓包文件。</span></span><br><span class="line">bypassOverFlows=<span class="string">"1"</span></span><br><span class="line"><span class="comment">#流量 bypass 开关,设为 1 表示开启,设为 0 表示关闭,开启此功能后当服务器的连接数超过引擎设定后会对超过的连接的流量 bypass,否则会无法新建连接。</span></span><br><span class="line">initialCwndWan=<span class="string">"44"</span></span><br><span class="line"><span class="comment">#初始 TCP 发送窗口能够发送的数据包的数量,该值设置的高会获得更好的加速效果,但是可能会造成网络的拥塞。</span></span><br><span class="line">l2wQLimit=<span class="string">"256 2048"</span></span><br><span class="line"><span class="comment">#从 LAN 到 WAN 加速引擎在缓冲池充满和空闲时分别能够缓存的数据包队列的长度的上限,该值设置的高会获得更好的加速效果,但是会消耗更多的内存。</span></span><br><span class="line">w2lQLimit=<span class="string">"256 2048"</span></span><br><span class="line"><span class="comment">#从 WAN 到 LAN 加速引擎在缓冲池充满和空闲时分别能够缓存的数据包队列的长度的上限,该值设置的高会获得更好的加速效果,但是会消耗更多的内存。</span></span><br><span class="line">shrinkPacket=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#slab 缓存缩减开关,设为 1 表示开启,设为 0 表示关闭,当服务器内存消耗很高时可以将该值设定为 1,这样会降低内存的消耗。</span></span><br><span class="line">retranWaitListMS=<span class="string">"32"</span></span><br><span class="line"><span class="comment">#重传等待时间,当服务器没有收到 ack 或者丢包产生之后客户端重复 ack 时间达到 32ms,服务器重传数据,默认为 32ms。</span></span><br><span class="line">halfCwndM<span class="keyword">in</span>SRtt=<span class="string">"500"</span> halfCwndLossRateShift=<span class="string">"3"</span></span><br><span class="line"><span class="comment">#以上两个值用于判断网络拥塞,分别为延时和丢包率,丢包率默认值为 3,即1/2^3,当拥塞产生时,退出第三代 Learning-based TCP 算法,采用类似传统 TCP 的算法。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#byte cache</span></span><br><span class="line">byteCache=<span class="string">"0"</span> </span><br><span class="line">httpComp=<span class="string">"1"</span> </span><br><span class="line">byteCacheMemory=<span class="string">"250"</span> <span class="comment">#MB </span></span><br><span class="line">byteCacheDisk=<span class="string">"0"</span> <span class="comment">#MB </span></span><br><span class="line">diskDev=<span class="string">"/dev/sda2"</span></span><br><span class="line"><span class="comment">#以上为双边缓存的设置,当前只有部分版本支持该功能。分别是缓存开关,数据压缩开关,内存大小,硬盘大小,指定硬盘位置。</span></span><br><span class="line">shaperEnable=<span class="string">"1"</span></span><br><span class="line"><span class="comment">#流量整形开关,配合上行和下行带宽设置开启,设为 1 表示开启,设为 0 表示关闭,开启此功能,会采用 wankbps 以及 waninkbps 设置的带宽大小运行,关闭此功能,会根据实时传输自动测算带宽大小。</span></span><br><span class="line">tcpOnly=<span class="string">"1"</span></span><br><span class="line"><span class="comment">#设为 1 表示开启,设为 0 表示关闭,开启此功能,即只处理 TCP 流量,关闭此功能,也处理除 TCP 以外的流量(不会对非 TCP 流量加速),一般用于配置策略对非 TCP 数据丢弃时。</span></span><br><span class="line">SmBurstMS=<span class="string">"15"</span></span><br><span class="line"><span class="comment">#引擎允许的最大突发时间,设为 0,则关闭,该值越大,数据包发送量越大,当前推荐默认为 15。</span></span><br><span class="line">rsc=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#网卡接收端合并开关,设为 1 表示开启,设为 0 表示关闭,在有些较新的网卡驱动中,带有 RSC 算法的,需要打开该功能。</span></span><br><span class="line">gso=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#GSO 开关,设为 1 表示开启,设为 0 表示关闭,当网卡需要开启 gso 功能时,开启此功能,LotServer变为支持gso的模式,关闭此功能,如果网卡开启gso,LotServer会关闭网卡的 gso 功能。</span></span><br><span class="line">lanSegment=<span class="string">""</span></span><br><span class="line"><span class="comment">#当 LotServer 不是 TCP 连接的最终节点并且采用单臂模式部署时,如果 LotServer和服务器之间网络环境很好,不需要加速,将此参数的值设为 Lan 一侧的子网网段(参数的值为子网网段的十六进制表示加子网掩码,例:ip 为 172.33.0.0/16,lanSegment="AC210000/16"),则 LotServer 和服务器之间不进行加速,可以节省LotServer 使用的资源并获得更好的加速效果。如果不设置此参数,则 LotServer 会对所有经过的 TCP 连接进行加速。(注:如果参数转换为十六进制后,第一位数字为 0时,需要省略,不然无法正常写入配置。例如:参数十六进制值为:0C210000/16,那么 lanSegment="C210000/16"。)</span></span><br><span class="line">configTimeout=<span class="string">"30"</span></span><br><span class="line"><span class="comment">#设置开启 LotServer 时,写入配置所需要的时间。当引擎的数目很多时需要写入配置的时间较长,默认为 30s。</span></span><br><span class="line">engineNum=<span class="string">"0"</span></span><br><span class="line"><span class="comment">#LotServer 启动的加速引擎的的个数,在多处理器(核)的系统环境下,启用多个加速引擎可以使网络流量的负载在多个处理器(核)之间做均衡分配,从而帮助优化系统CPU 资源的利用,启用多个加速引擎时,引擎序号从 0 开始,分别为 engine0, engine1,engine2,等等,每个引擎的负载对应交给相同序号的处理器来处理,即,engine0 使用 cpu0, engine1 使用 cpu1 等,engineNum 默认为 0,表示启用的加速引擎个数与系统的处理器个数相同,64bit 架构下,当实际的 engineNum 参数大于 4 时,检查内存:当总内存减去 engine 占用内存剩余数小于 2G 时,提示用户一个 warning:LotServer Warning: $CPUNUM engines will be launched according to the config file.Your system total RAM is $memTotal(KB), which might be insufficient to run all theengines without performance penalty under extreme network conditions. </span></span><br><span class="line">shortRttMS=<span class="string">"10"</span></span><br><span class="line"><span class="comment">#白名单开关,打开后 LotServer 将不对 RTT 小于 shortRttMS 的连接加速,节约系统资源,提高性能,设置为 0 时表示关闭此功能,LotServer 会加速所有的连接,设置为其他值时,例如 10,则表示当第一次建立连接时,Lotserver 会测量所有的新建连接的 RTT,但是不对其进行加速,如果 RTT 大于 10ms,LotServer 会从第二次建立连接开始对其加速,如果 RTT 小于 10ms,LotServer 则一直不会对其加速。默认值 10ms。重启 LotServer 后,原来记录的每个连接的 RTT 会被清除,重新开始记录。LotServer 使用五元组判断是否为同一连接,五元组包括:源 ip,目的 ip,源端口,目的端口和协议。</span></span><br><span class="line">apxexe=<span class="string">" /appex/bin/acce-***"</span></span><br><span class="line"><span class="comment">#LotServer 模块文件的路径。</span></span><br><span class="line">apxlic=<span class="string">"/appex/etc/apx-***.lic"</span></span><br><span class="line"><span class="comment">#LotServer 授权文件的路径。</span></span><br></pre></td></tr></table></figure>
<p>以上内容为本人搜集整理资料后结合自身实践得出的结果,如有错误欢迎指正与交流~ ~ </p>
<h4 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接:"></a>参考链接:</h4><p><a href="http://www.oldcai.com/archives/1026" rel="external nofollow" target="_blank">降级linux内核</a><br><a href="http://vv15.pbhz.com/2015/09/kernel-2-6-32-504-el6-i686-rpm/" rel="external nofollow" target="_blank">Centos 6.7内核降级到kernel-2.6.32-504.el6.i686.rpm</a><br><a href="http://bbs.tcp.hk/thread-27-1-1.html" rel="external nofollow" target="_blank">锐速评测报告指导帖</a><br><a href="http://blog.deartanker.com/post/3659.html" rel="external nofollow" target="_blank">Appex/锐速TCP加速教程和最佳优化设置教程</a><br><a href="http://www.777s.me/serverspeeder-config.html" rel="external nofollow" target="_blank">Serverspeeder 锐速config配置文件详解</a></p>
<div class="alert info"><p>之前曾经在<a href="http://quericy.me/blog/556" title="如何让高延迟不稳定的服务器下载速度翻倍">这篇文章</a>里试过用net-speeder来提升速度,有优有劣.这次换用锐速来尝试
本站现已开启全站强制SSL
https://quericy.me/blog/764/
2015-11-06T15:27:48.000Z
2022-06-28T05:27:25.671Z
<p>没错,就是这样,现已加入全站豪华https套餐~ ~ ~ ~ </p>
<p><a href="https://static.quericy.me/1drive/blog/wp-content/uploads/2015/11/ssl-rate.jpg" rel="external nofollow" target="_blank"><img src="https://static.quericy.me/1drive/blog/wp-content/uploads/2015/11/ssl-rate-1024x445.jpg" alt="ssl-rate"></a></p>
<p>其实大半年前就开启SSL了,但是由于某些原因(比如度娘不收录https,比如DO机房速度不给力,<del>又比如我很懒</del>…),再加上多说的头像对https的支持一再失效,因而一直处于http/https的可选状态.</p>
<p>虽然双十一前忙成Doge了,在(暂时)完美解决了多说https的问题后,想想泛域名野卡不能一直这样浪费是吧,但还是抽了点时间优化了下SSL,然后做了重定向~ ~ ~</p>
<p>就是这样,小绿锁的感觉还是不错的.至于SEO什么的都是浮云,虽然百度前些日子也开始支持主动抓取https了,也学谷歌声称https的排名会优先考虑…嘛,管他呢,都是些然并卵的东西.</p>
<p><strong>另附:</strong><br>Mozilla出的:<a href="https://mozilla.github.io/server-side-tls/ssl-config-generator/" rel="external nofollow" target="_blank">Apache推荐SSL配置</a></p>
<p>SSL<a href="https://www.ssllabs.com/ssltest/" rel="external nofollow" target="_blank">https://www.ssllabs.com/ssltest/</a></p>
<p>没错,就是这样,现已加入全站豪华https套餐~ ~ ~ ~ </p>
<p><a href="https://static.quericy.me/1drive/blog/wp-content/uploads/2015/11/ssl-rate.jpg" rel="exter