<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>happyhacker 的博客</title>
  <id>http://pipe.b3log.org/blogs/happyhacker</id>
  <updated></updated>
  <subtitle>记录精彩的程序人生</subtitle>
  <link href="http://pipe.b3log.org/blogs/happyhacker"></link>
  <entry>
    <title>BasicAuth简介</title>
    <updated>2018-07-02T18:23:51+08:00</updated>
    <id>tag:pipe.b3log.org,2018-07-02:/blogs/happyhacker/articles/2018/07/02/1530527031277</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/07/02/1530527031277" rel="alternate"></link>
    <summary type="html">&lt;p&gt;我在维护的一个内部服务用到了 BasicAuth 的认证方式, 但总是有人不断的问我要怎么用, 这里详细的说一下.&lt;/p&gt;&#xA;&lt;h2&gt;使用场景&lt;/h2&gt;&#xA;&lt;p&gt;要知道 BasicAuth 的使用场景, 它是最简单的一种认证方式, 说白了就是用户名+密码, 这种方式有很多问题, 比如它通过网络发送用户名和密码, 而这些都是以一种很容易解码的形式表示的. 虽然它是用 base64_encode 加密过了, 但这种加密的作用也仅仅是&lt;strong&gt;让可信任的用户不太可能在进行网络观测时无意中看到密码&lt;/strong&gt;, 而不能防止恶意用户. 所以也仅限在一些安全要求不是那么高的场景下使用.&lt;/p&gt;&#xA;&lt;h2&gt;原理和实现&lt;/h2&gt;&#xA;&lt;p&gt;其次要明白 BasicAuth 的原理和实现. 简单来说, 它是检查你的 Headers 中的&lt;code&gt;Authorization&lt;/code&gt;. 从中解析出 username 和 password, 和服务器保存的进行对比, 如果一致则通过, 否则返回401/403状态码, 表示客户端没有携带或携带了错误的认证信息（具体可参见我的文章&lt;a href=&#34;http://pipe.b3log.org/blogs/happyhacker/articles/2018/04/02/1522649549923&#34; rel=&#34;nofollow&#34;&gt;HTTP 状态码 401 和 403 深度解析&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;h2&gt;使用方法&lt;/h2&gt;&#xA;&lt;p&gt;下面详细说一下怎么调用一个有 BasicAuth 认证的服务. 比如你的 username 是 zhangsan, password是 helloworld(当然不建议用这种太简单的密码), 那么你只需要在你调用接口时在 Header 里加上一个 Header&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;Authorization: Basic $(base64_encode({username}:{password}))&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;即可. 更具体的, 也就是&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;Authorization: Basic emhhbmdzYW46aGVsbG93b3JsZA==&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;具体到 postman就更简单了, 它支持多种认证方式, 当调用需要BasicAuth 认证信息的接口时, 如图所示选择并填写即可.&lt;br/&gt;&#xA;&lt;img data-src=&#34;https://ws1.sinaimg.cn/large/006tKfTcly1fjnke4l71nj30nv08qjs4.jpg&#34; alt=&#34;&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;具体到某一种语言, 就不太能全面覆盖了, 如果你用了某种封装好的客户端, 比如 PHP 的 guzzlehttp 或 Java 的 OKHttp之类, 它们肯定是会像 postman 一样对这种应用相当广泛的认证方式直接支持的. 如果你用的是自己封装的客户端,那么就可以按照前面说的自己生成&lt;code&gt;Authorization&lt;/code&gt;即可.&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>Ubuntu下重新打包预编译安装的PHP</title>
    <updated>2018-06-29T10:33:06+08:00</updated>
    <id>tag:pipe.b3log.org,2018-06-29:/blogs/happyhacker/articles/2018/06/29/1530239586307</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/06/29/1530239586307" rel="alternate"></link>
    <summary type="html">&lt;h2&gt;起因&lt;/h2&gt;&#xA;&lt;p&gt;写这篇文章的起因是要安装Swoole，而它又依赖php_sockets扩展，因为我是通过apt安装的php7.2，没办法直接重新编译，所以就找到了方法，这里记录一下。&lt;/p&gt;&#xA;&lt;h2&gt;依赖&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Ubuntu/Debian的打包工具&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo apt install devscripts&lt;/code&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;重新打包PHP7.2需要的依赖&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo apt build-dep php7.2&lt;/code&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;下载源码&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo apt source php7.2&lt;/code&gt;&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;这里要注意一点，Ubuntu默认不打开&lt;code&gt;deb-src&lt;/code&gt;段，也就是默认情况下你在&lt;code&gt;apt-get source&lt;/code&gt;下是找不到任何东西的。所以需要在&lt;code&gt;/etc/apt/sources.list&lt;/code&gt;中删除相应的&lt;code&gt;deb-src&lt;/code&gt;的注释。然后执行&lt;code&gt;sudo apt update&lt;/code&gt;更新本地缓存。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2&gt;更改配置&lt;/h2&gt;&#xA;&lt;p&gt;今天终于知道为什么很多流行的项目源码目录下总是有一个&lt;code&gt;debian&lt;/code&gt;目录了，原来是给Debian打包用的配置。所以给PHP打包也不例外，这里就不说具体怎么修改了，因为如果自己编译过PHP，很容易就能找到需要修改哪里。&lt;/p&gt;&#xA;&lt;h2&gt;更新版本号&lt;/h2&gt;&#xA;&lt;p&gt;这个步骤是可选的，简单说就是你更新了这个包，如果你要发布它，那它肯定不能和当前已经发布的包同名。&lt;br/&gt;&#xA;做法是执行&lt;code&gt;dch -i&lt;/code&gt;，这样就会把版本号的最后一位加1。例如当前版本是&lt;code&gt;7.2.5-0ubuntu0.18.04.1&lt;/code&gt;, 执行之后就是&lt;code&gt;7.2.5-0ubuntu0.18.04.2&lt;/code&gt;，当然这时会打开一个交互界面让你填一些类似git commit时的Message。&lt;/p&gt;&#xA;&lt;h2&gt;重新打包&lt;/h2&gt;&#xA;&lt;p&gt;这一步很简单，就是执行一下&lt;code&gt;debuild&lt;/code&gt;。我试了一下在源码根目录或者在debian目录下都是没问题的。应该是脚本做了容错处理。&lt;/p&gt;&#xA;&lt;p&gt;值得注意的是，不能用sudo执行&lt;code&gt;debuild&lt;/code&gt;，该命令默认使用fakeroot执行。而且你可以指定&lt;code&gt;DEB_BUILD_OPTIONS=nocheck&lt;/code&gt;来跳过编译选项的检查，我觉得还是稳妥一些好，所以就不加任何选项的执行了。&lt;/p&gt;&#xA;&lt;h2&gt;打包完成&lt;/h2&gt;&#xA;&lt;p&gt;打包完成之后会生成&lt;code&gt;*.deb&lt;/code&gt;包。看到它你一定知道下一步怎么做了。&lt;/p&gt;&#xA;&lt;h2&gt;安装新包&lt;/h2&gt;&#xA;&lt;p&gt;其实我从执行&lt;code&gt;debuild&lt;/code&gt;开始写这篇记录，到现在它还没有执行完。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo dpkg -i 7.2.5-0ubuntu0.18.04.2.deb&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h2&gt;后记&lt;/h2&gt;&#xA;&lt;p&gt;这篇记录看起来是为了重新打包预编译的包，以自定义一些编译选项，其实本质上就是一个包的maintainer的工作过程，只不过这里少了发布过程。这里是处理PHP的包，读者们也可以举一反三，根据自己的需求定制一些别的包。&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>谈谈PHP-FPM模式下的MySQL持久连接</title>
    <updated>2018-05-17T01:09:31+08:00</updated>
    <id>tag:pipe.b3log.org,2018-05-17:/blogs/happyhacker/articles/2018/05/17/1526490570855</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/05/17/1526490570855" rel="alternate"></link>
    <summary type="html">&lt;p&gt;闲来无事研究一下PHP的MySQL持久连接问题。在mysql扩展的年代，应该用的是&lt;code&gt;mysql_pconnect&lt;/code&gt;，可是那时候我还没有开始接触PHP，　所以我们直接上PDO。&lt;/p&gt;&#xA;&lt;p&gt;首先说一下本次测试用的环境。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/6bd8f94bf8384d6c9806e9aa21c1ce42.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;br/&gt;&#xA;关键还要看一下PHP的配置。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/a6ca5d0e5d75463784de0b3f42ad952c.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;注意其中的最重要的参数&lt;code&gt;pm.max_children=1&lt;/code&gt;，　这决定了只能有一个FPM的worker进程来处理所有请求。这样把问题简化更容易发现特征。&lt;/p&gt;&#xA;&lt;p&gt;我们知道，　PHP的FPM有一个master进程和若干个worker进程， 而worker进程并不是像最早的fastcgi一样每次处理完一个请求之后就销毁， 下次再来请求需要重新启动。也就是说一个worker进程是可以处理多个请求的。这给“持久连接”提供了理论基础。 那么到底它能不能支持持久连接呢？如果支持持久连接， 它的特征又是什么呢？下面直接上代码。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/231460ee2e1a4a67bcb96d46cf995502.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;我在之前的文章里也提到过MySQL的&lt;a href=&#34;http://pipe.b3log.org/blogs/happyhacker/articles/2018/03/07/1520407392299&#34; rel=&#34;nofollow&#34;&gt;general_log&lt;/a&gt;，按这里的描述配置一下就可以很清晰的看到哪个连接在请求服务器了。&lt;/p&gt;&#xA;&lt;p&gt;反复执行&lt;code&gt;curl http://fpm.org/index.php&lt;/code&gt;（我给fpm.org配置了hosts）， 就可以看到不断变化的lastInsertId了。 但这不是重点， 要看两个现象。&lt;/p&gt;&#xA;&lt;p&gt;用root账户登录mysql， 执行&lt;code&gt;show processlist;&lt;/code&gt;可以看到类似如下的输出：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/f7cb80fd514648d3b7cfa7e4b92be3fd.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;可以看到， 有两个客户端和服务器保持了连接。是谁呢？第一个是Ubuntu（其实是Debian）维护的MySQL包自带的默认管理员用户， 其实表示的就是当前的这个MySQL cli连接。另外一个当然就是刚才调用&lt;code&gt;curl&lt;/code&gt;通过PHP的pdo创建的了。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/8886b89c67b34fb795a1a1678b4d5c22.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;现在再来看general_log，&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/d576c3f20afc466eb592c6f6478a0981.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;可以看到多次请求服务器端都是同一个线程ID（14）. 参考&lt;a href=&#34;https://dba.stackexchange.com/questions/42532/format-of-mysql-query-log?utm_medium=organic&amp;amp;utm_source=google_rich_qa&amp;amp;utm_campaign=google_rich_qa&#34; rel=&#34;nofollow&#34;&gt;这里&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;那么怎么验证它是由当前这个FPM的worker维持的呢？很简单， 重启一下FPM再看它有没有变化就知道了。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo systemctl restart php-fpm.service&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/85ab088cfde842e2b1bd650228bb3a45.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;可以看到一个新的worker已经在工作了。 现在重新访问刚才的地址&lt;/p&gt;&#xA;&lt;p&gt;先再去执行一下&lt;code&gt;show processlist;&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/0401a8bcb25943ca98409aa235321d63.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;可以看到14已经不在了。因为维持14这个连接的fpm worker已经挂了，当然对应的服务器的线程也已经销毁了。但15出现了。那么这几次请求用的是不是15呢？&lt;/p&gt;&#xA;&lt;p&gt;当然是。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/5a14bb8522184bf9853f738088a2bfc5.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;所以结论很明显了，在FPM模式下是可以使用mysql持久化连接的。&lt;br/&gt;&#xA;&lt;del&gt;所以理论上也可以实现MySQL连接池。有时间可以研究一下。&lt;/del&gt; &lt;strong&gt;想了想是没有办法实现连接池的, 因为一个worker只能维持一个长连接,无法和别的worker共享, 只能通过配置&lt;code&gt;pm.max_children&lt;/code&gt;来让FPM维持的长连接没有那么多不要超过MySQL的最大连接数&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;不过这是一个危险操作， 因为你也看到了， 我在写这篇文章的过程中在没有手动重启FPM进程之前这个长连接是一直保持的，而如果这个fpm进程是空闲的， 那么这个连接就是被浪费的。这有可能导致大量无用的连接占用MySQL的连接数， 而连接数是有上限的，超过之后就无法再建立新的连接， 导致后续的连接失败。所以必须设置长连接数的上限， 同时保证worker空闲一段时间后退出，（使用&lt;code&gt;pm.max_spare_servers&lt;/code&gt;实现）或者再处理若干次请求之后重新启动（通过&lt;code&gt;pm.max_requests&lt;/code&gt;实现）， 以保证MySQL的正常连接数。&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>Debian执行apt update失败解决方案</title>
    <updated>2018-05-09T20:19:13+08:00</updated>
    <id>tag:pipe.b3log.org,2018-05-09:/blogs/happyhacker/articles/2018/05/09/1525868352772</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/05/09/1525868352772" rel="alternate"></link>
    <summary type="html">&lt;p&gt;我的一个Debian虚拟机最近在执行&lt;code&gt;apt update&lt;/code&gt;时总是报这个错误&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/eea8f0cb3c5f4dd8a4bf75b06d942fa4.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;我猜测是本地时间和mirror的时间不同导致的(本地时间滞后, 导致校验远端Release文件失败). 我认为可以通过设置正确的本地时间来解决.&lt;/p&gt;&#xA;&lt;p&gt;果然&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/ca44dfb34ee845b3bed9e5a1b1c98b65.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;而现在的时间是5月9号了.&lt;/p&gt;&#xA;&lt;p&gt;当然是通过ntp服务器而不是手动设置.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/2e4861af1dbd40aea165c6b51ad7f6a2.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;果然时间正确之后更新正常了.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/b8305262417145b79825cce6ba9d85d4.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>查看PHP-FPM进程占用内存大小</title>
    <updated>2018-05-07T14:12:39+08:00</updated>
    <id>tag:pipe.b3log.org,2018-05-07:/blogs/happyhacker/articles/2018/05/07/1525673559071</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/05/07/1525673559071" rel="alternate"></link>
    <summary type="html">&lt;p&gt;1. 按FPM进程实际占用内存大小排序&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash highlight-chroma&#34;&gt;ps -ylC php-fpm --sort:rss&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://ws3.sinaimg.cn/large/006tKfTcly1fr2quqg5tmj30xq04kdgx.jpg&#34; alt=&#34;&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;这样的结果是按&lt;code&gt;KB&lt;/code&gt;大小显示的, 而&lt;code&gt;ps&lt;/code&gt;命令本身不支持将其转换为按&lt;code&gt;MB&lt;/code&gt;显示, 所以需要使用&lt;code&gt;awk&lt;/code&gt;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash highlight-chroma&#34;&gt;ps -ylC php-fpm --sort:rss &lt;span class=&#34;highlight-p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;highlight-s1&#34;&gt;&amp;#39;NR&amp;gt;1 {$8=int($8/1024)&amp;#34;M&amp;#34;;}{ print;}&amp;#39;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://ws3.sinaimg.cn/large/006tKfTcly1fr2qv5lojwj30ry04o3zl.jpg&#34; alt=&#34;&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;2. 查看所有FPM进程占用内存的平均值&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash highlight-chroma&#34;&gt;ps --no-headers -o &lt;span class=&#34;highlight-s2&#34;&gt;&amp;#34;rss,cmd&amp;#34;&lt;/span&gt; -C php-fpm &lt;span class=&#34;highlight-p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;highlight-s1&#34;&gt;&amp;#39;{ sum+=$1 } END { printf (&amp;#34;%d%s\n&amp;#34;, sum/NR/1024,&amp;#34;M&amp;#34;) }&amp;#39;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://ws2.sinaimg.cn/large/006tKfTcly1fr2qvkhauyj303g01adfo.jpg&#34; alt=&#34;&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;这样计算完之后就可以根据自己机器内存的大小设置合适的pm.max_children的值了.&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>HTTP状态码401和403深度解析</title>
    <updated>2018-04-02T14:12:30+08:00</updated>
    <id>tag:pipe.b3log.org,2018-04-02:/blogs/happyhacker/articles/2018/04/02/1522649549923</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/04/02/1522649549923" rel="alternate"></link>
    <summary type="html">&lt;p&gt;一直只是知道401和403都是拒绝访问的意思, 但没有仔细研究它们之间准确的区别, 现在详细记录一下.&lt;/p&gt;&#xA;&lt;h2&gt;表面上是这样&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;401 Unauthorized&#xA;403 Forbidden&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;从w3.org的解释来看:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/e999631877164d3186e900f6715241f0.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/79cbdaa0f12f45fdb07c0b828e2bc343.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;作为非英语国家的我们而言, 可能不太能发觉这两个描述中的问题, 只是能看明白401是没有带认证信息或者带了错误的认证信息, 这时客户端可以修改认证信息进行重试; 而403是客户端带了正确的认证信息, 但服务器认为这个认证信息对应的用户是没有对应资源的访问权限的, 因此, 在向管理员获取相关权限之前, 是没有重试的必要的.&lt;/p&gt;&#xA;&lt;p&gt;但实际上这个描述中忽视了两个单词: Authorized和Authenticated.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/837afe0231224e74bedad5c5ca1493c1.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/92ad1ab0b462430f87a4fe31d69fce60.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;imagepng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;这样看就很明显了, 前者指的是&amp;#34;用户被允许/授权做某事&amp;#34;, 而后者只是表示&amp;#34;用户是认证用户&amp;#34;.&lt;/p&gt;&#xA;&lt;p&gt;举个简单的例子, 在一般的后台系统中, 用户一般是通过LDAP或类似系统导入的, 也即是所有可以登录后台系统的用户都是&amp;#34;Authenticated&amp;#34;的, 他们有自己的Credentials. 但后台系统也都会有很详细的权限管理机制, 你虽然是认证用户, 但恐怕大多数资源你还是无法访问, 这时你就是&amp;#34;Unauthorized&amp;#34;的了.&lt;/p&gt;&#xA;&lt;p&gt;所以这样看, 401的正确解释应该是&amp;#34;Unauthenticated&amp;#34;, 而且在&lt;code&gt;HTTP Authentication: Basic and Digest Access Authentication&lt;/code&gt;中需要带的请求头也应该是&lt;code&gt;Authentication&lt;/code&gt;而不是&lt;code&gt;Authorization&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;这个是历史遗留问题了, 估计以后也不会有什么改变, 但我们还是需要知道这个问题, 免得在使用时混淆了401和403.&lt;/p&gt;&#xA;&lt;p&gt;参考: &lt;a href=&#34;https://stackoverflow.com/questions/3297048/403-forbidden-vs-401-unauthorized-http-responses?utm_medium=organic&amp;amp;utm_source=google_rich_qa&amp;amp;utm_campaign=google_rich_qa&#34; rel=&#34;nofollow&#34;&gt;403 Forbidden vs 401 Unauthorized HTTP responses&lt;br/&gt;&#xA;&lt;/a&gt;&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>Python setup.py 卸载包</title>
    <updated>2018-03-07T17:22:05+08:00</updated>
    <id>tag:pipe.b3log.org,2018-03-07:/blogs/happyhacker/articles/2018/03/07/1520414525011</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/03/07/1520414525011" rel="alternate"></link>
    <summary type="html">&lt;p&gt;不是很了解 Python的生态环境, 但知道现在主流的包管理工具是 pip, 而原来是 setup.py, 本质上 setup.py 是读取 requirements.txt 从而获取该包需要的依赖, 然后把它们安装在本地, 但它的问题是没有办法完整的卸载.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;python setup.py install&lt;/code&gt; 爽完了发现这个包并不能满足需求, 或者无法执行, 要卸载?不好意思, 只有两个命令, 一个是 install, 一个build, 没有卸载选项.&lt;/p&gt;&#xA;&lt;p&gt;一个 workaroud 是重新安装一次, 加上一个选项&lt;code&gt;--record&lt;/code&gt;让它这次把它创建的文件打印到一个文件里, 然后就可以欢乐的删除了.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-python highlight-chroma&#34;&gt;&lt;span class=&#34;highlight-n&#34;&gt;python&lt;/span&gt; &lt;span class=&#34;highlight-n&#34;&gt;setup&lt;/span&gt;&lt;span class=&#34;highlight-o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;highlight-n&#34;&gt;py&lt;/span&gt; &lt;span class=&#34;highlight-n&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;highlight-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;highlight-n&#34;&gt;record&lt;/span&gt; &lt;span class=&#34;highlight-n&#34;&gt;rubbish&lt;/span&gt;&lt;span class=&#34;highlight-o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;highlight-n&#34;&gt;txt&lt;/span&gt;&#xA;&lt;span class=&#34;highlight-n&#34;&gt;cat&lt;/span&gt; &lt;span class=&#34;highlight-n&#34;&gt;rubbish&lt;/span&gt;&lt;span class=&#34;highlight-o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;highlight-n&#34;&gt;txt&lt;/span&gt; &lt;span class=&#34;highlight-o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;highlight-n&#34;&gt;xargs&lt;/span&gt; &lt;span class=&#34;highlight-n&#34;&gt;rm&lt;/span&gt; &lt;span class=&#34;highlight-o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;highlight-n&#34;&gt;rf&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;参考: &lt;a href=&#34;https://stackoverflow.com/questions/1550226/python-setup-py-uninstall&#34; rel=&#34;nofollow&#34;&gt;python setup.py uninstall&lt;/a&gt;&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>Docker 开发环境中使用 MySQL general_log</title>
    <updated>2018-03-07T15:23:12+08:00</updated>
    <id>tag:pipe.b3log.org,2018-03-07:/blogs/happyhacker/articles/2018/03/07/1520407392299</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/03/07/1520407392299" rel="alternate"></link>
    <summary type="html">&lt;p&gt;有了 docker 可以快速的创建一个 mysql 实例,&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;db:&#xA;        image: mysql&#xA;        restart: always&#xA;        environment:&#xA;            MYSQL_ROOT_PASSWORD: iampassword&#xA;        ports:&#xA;            - 3306:3306&#xA;        volumes:&#xA;            - ./env/mysql/data:/var/lib/mysql&#xA;            - ./env/mysql/conf:/etc/mysql/conf.d&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;因为是开发环境, 所以我希望 MySQL 能尽可能多的告诉我更多的信息, 也就是能记录详细的日志, 可以在 mysql 控制台执行以下语句:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;SET global log_output = &amp;#39;file&amp;#39;;&#xA;SET global general_log_file=&amp;#39;/var/log/mysql/query.log&amp;#39;; &#xA;SET global general_log = on;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;这样, 日志就打印到了容器中&lt;code&gt;/var/log/mysql/query.log&lt;/code&gt;中, 至于你想到容器中查看日志还是在本地查看, 完全看你喜好. 如果想在容器中看&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;docker exec -it centos_db_1 tail -f /var/log/mysql/query.log&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;如果想在本地看, 就在 &lt;code&gt;docker-compose.yml&lt;/code&gt;中, 把上面&lt;code&gt;db&lt;/code&gt;段修改成&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;db:&#xA;        image: mysql&#xA;        restart: always&#xA;        environment:&#xA;            MYSQL_ROOT_PASSWORD: iampassword&#xA;        ports:&#xA;            - 3306:3306&#xA;        volumes:&#xA;            - ./env/mysql/data:/var/lib/mysql&#xA;            - ./env/mysql/conf:/etc/mysql/conf.d&#xA;            - ./env/log/mysql:/var/log/mysql&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;这样一个不好的地方是当容器重启后上面的三个&lt;code&gt;SET&lt;/code&gt;语句都会失效, 如果想永久的改变配置, 就需要把这段配置放在 &lt;code&gt;my.cnf&lt;/code&gt;中&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;[mysqld]&#xA;log_output = file&#xA;general_log_file = /var/log/mysql/query.log&#xA;general_log      = 1&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;这样既不用提交镜像, 又能保存配置咯.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img data-src=&#34;https://ws2.sinaimg.cn/large/006tNc79ly1fp4ap6cv8pj31fa0dagpq.jpg&#34; alt=&#34;&#34;/&gt;&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>搭建 docker-registry</title>
    <updated>2018-03-05T16:13:22+08:00</updated>
    <id>tag:pipe.b3log.org,2018-03-05:/blogs/happyhacker/articles/2018/03/05/1520237601784</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/03/05/1520237601784" rel="alternate"></link>
    <summary type="html">&lt;h1&gt;准备工作&lt;/h1&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Ubuntu 16.04(with docker installed)&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h1&gt;步骤&lt;/h1&gt;&#xA;&lt;h2&gt;1. 安装必要的包&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo apt install -y docker-compose apache2-utils curl&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h2&gt;2. 创建相关目录&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;mkdir /docker-registry&#xA;mkdir  /docker-registry/data&#xA;mkdir /docker-registry/nginx&#xA;chown root:root /docker-registry&#xA;cd /docker-registry&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2&gt;3. 创建 &lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml highlight-chroma&#34;&gt;&lt;span class=&#34;highlight-nt&#34;&gt;nginx&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;highlight-s2&#34;&gt;&amp;#34;nginx:1.9&amp;#34;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;highlight-m&#34;&gt;443&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-m&#34;&gt;443&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;links&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;highlight-l&#34;&gt;registry:registry&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;highlight-l&#34;&gt;/docker-registry/nginx/:/etc/nginx/conf.d&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;registry&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;highlight-l&#34;&gt;registry:2&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;highlight-m&#34;&gt;127.0.0.1&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-m&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-m&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;highlight-l&#34;&gt;/data&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;highlight-nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;highlight-l&#34;&gt;/docker-registry/data:/data&lt;/span&gt;&lt;span class=&#34;highlight-w&#34;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;执行&lt;code&gt;docker-compose up&lt;/code&gt;, 就会看到一堆信息, 没有看到错误提示的话就是安装成功了.&lt;/p&gt;&#xA;&lt;h2&gt;4. 如果是在生产环境, 还是有必要让它保持后台运行&lt;/h2&gt;&#xA;&lt;p&gt;方案可以选择 systemd 或 supervisord, 这里提供 systemd 的方案&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;[Unit]&#xA;Description=Starting docker registry&#xA;&#xA;[Service]&#xA;Environment= MY_ENVIRONMENT_VAR = /docker-registry/docker-compose.yml&#xA;WorkingDirectory=/docker-registry&#xA;ExecStart=/usr/bin/docker-compose up&#xA;Restart=always&#xA;&#xA;[Install]&#xA;WantedBy=multi-user.target&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2&gt;5. 修改 Nginx 配置&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;# /docker-registry/nginx/regstry.conf&#xA;upstream docker-registry {&#xA;  server registry:5000;&#xA;}&#xA;&#xA;server {&#xA;  listen 443;&#xA;  server_name myregistrydomain.com;&#xA;&#xA;  # SSL&#xA;  # ssl on;&#xA;  # ssl_certificate /etc/nginx/conf.d/domain.crt;&#xA;  # ssl_certificate_key /etc/nginx/conf.d/domain.key;&#xA;&#xA;  # disable any limits to avoid HTTP 413 for large image uploads&#xA;  client_max_body_size 0;&#xA;&#xA;  # required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)&#xA;  chunked_transfer_encoding on;&#xA;&#xA;  location /v2/ {&#xA;    # Do not allow connections from docker 1.5 and earlier&#xA;    # docker pre-1.6.0 did not properly set the user agent on ping, catch &amp;#34;Go *&amp;#34; user agents&#xA;    if ($http_user_agent ~ &amp;#34;^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$&amp;#34; ) {&#xA;      return 404;&#xA;    }&#xA;&#xA;    # To add basic authentication to v2 use auth_basic setting plus add_header&#xA;    # auth_basic &amp;#34;registry.localhost&amp;#34;;&#xA;    # auth_basic_user_file /etc/nginx/conf.d/registry.password;&#xA;    # add_header &amp;#39;Docker-Distribution-Api-Version&amp;#39; &amp;#39;registry/2.0&amp;#39; always;&#xA;&#xA;    proxy_pass                          http://docker-registry;&#xA;    proxy_set_header  Host              $http_host;   # required for docker client&amp;#39;s sake&#xA;    proxy_set_header  X-Real-IP         $remote_addr; # pass on real client&amp;#39;s IP&#xA;    proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;&#xA;    proxy_set_header  X-Forwarded-Proto $scheme;&#xA;    proxy_read_timeout                  900;&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;重启服务使配置生效 &lt;code&gt;sudo systemctl restart docker-registry&lt;/code&gt;&lt;br/&gt;&#xA;执行&lt;code&gt;curl http://localhost:5000/v2/&lt;/code&gt;, 如果正常, 应该输出&lt;code&gt;{}&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h2&gt;6. 配置认证信息&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;cd /docker-registry/nginx&#xA;htpasswd -c registry.password mydocker&#xA;New password:&#xA;Re-type new password:&#xA;Adding password for user mydocker&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;修改 nginx 配置(删除相应行前面的注释)&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;auth_basic &amp;#34;registry.localhost&amp;#34;;&#xA;auth_basic_user_file /etc/nginx/conf.d/registry.password;&#xA;add_header &amp;#39;Docker-Distribution-Api-Version&amp;#39; &amp;#39;registry/2.0&amp;#39; always;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;重启服务使配置生效 &lt;code&gt;sudo systemctl restart docker-registry&lt;/code&gt;&lt;br/&gt;&#xA;执行&lt;code&gt;curl http://localhost:443/v2/&lt;/code&gt;, 会提示&lt;code&gt;401 Authorization Required&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;把刚刚创建的用户名和密码加上会得到正确的返回结果&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;curl http://mydocker:123456@localhost:443/v2/&lt;/code&gt; 结果是&lt;code&gt;{}&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h2&gt;7. 配置证书&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;cd /docker-registry/nginx&#xA;# 生成一个 root key&#xA;openssl genrsa -out dockerCA.key 2048&#xA;# 生成根证书( ComonName 写你的域名, 比如域名是 registry.abc.com, 这里就写 registry.abc.com)&#xA;openssl req -x509 -new -nodes -key dockerCA.key -days 10000 -out dockerCA.crt&#xA;# 生成 server key, 在 nginx 的配置ssl_certificate_key中引用&#xA;openssl genrsa -out domain.key 2048&#xA;# 申请一个新证书( ComonName 写你的域名, 比如域名是 registry.abc.com, 这里就写 abc.com)&#xA;openssl req -new -key domain.key -out docker-registry.com.csr&#xA;# 给证书签名&#xA;openssl x509 -req -in docker-registry.com.csr -CA dockerCA.crt -CAkey dockerCA.key -CAcreateserial -out domain.crt -days 10000&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;因为我们生成的是一个&amp;#34;自签名&amp;#34;证书, 所以是无法被其他证书发布机构认证的, 也就是必须让客户端强制信任该证书.&lt;/p&gt;&#xA;&lt;p&gt;在宿主机上执行&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;cd /docker-registry/nginx&#xA;cp dockerCA.crt /usr/local/share/ca-certificates/&#xA;update-ca-certificates &amp;amp;&amp;amp; service docker restart &amp;amp;&amp;amp; service docker-registry restart&#xA;curl https://mydocker:123456@docker-server.com/v2/&#xA;# 结果应该是&#xA;{}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;然后手工把自签名证书发布给需要的客户端,&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;scp dockerCA.crt ja@192.168.0.59:/usr/local/share/ca-certificates&#xA;ja@192.168.0.59&amp;#39;s password:&#xA;dockerCA.crt 100% 1302 1.3KB/s 00:00&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;在客户端上, 执行&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;update-ca-certificates &amp;amp;&amp;amp; service docker restart&#xA;#test login to fresh created repository:&#xA;docker login https://docker-server.com&#xA;Username: mydocker&#xA;Password:&#xA;Login Succeeded&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2&gt;8. 在客户端上测试 container 的功能&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;docker run -it ubuntu # 从中心 library 下载并执行 ubuntu&#xA;#re-tag images DOMAIN-NAME/NEW-TAG&#xA;docker tag ubuntu docker-server.com/test-image # 给已有container重新打 tag&#xA;#push image to repository:&#xA;docker push docker-server.com/test-image # push 到新建的 registry&#xA;docker rmi -f docker-server.com/test-image # 删除本地镜像&#xA;docker pull docker-server.com/test-image  # 从新的 registry 拉取镜像&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>搭建 Docker 开发环境----安装 OpenResty 和 PHP7(基于 centos7)</title>
    <updated>2018-03-05T13:48:42+08:00</updated>
    <id>tag:pipe.b3log.org,2018-03-05:/blogs/happyhacker/articles/2018/03/05/1520228922173</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/03/05/1520228922173" rel="alternate"></link>
    <summary type="html">&lt;h2&gt;openresty&lt;/h2&gt;&#xA;&lt;p&gt;安装 openresty&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;yum -y install yum-utils&#xA;yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo&#xA;yum -y install openresty&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;检查openresty 相关的包&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;yum --disablerepo=&amp;#34;*&amp;#34; --enablerepo=&amp;#34;openresty&amp;#34; list available&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;nginx 日志&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;./env/openresty/nginx/log:/usr/local/openresty/nginx/logs&#xA;./env/openresty/nginx/vhost:/usr/local/openresty/nginx/conf/vhost&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2&gt;php7.1&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm&#xA;rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm&#xA;yum -y install php71w-{pecl-redis,mbstring,pdo,mcrypt,mysqlnd,gd,common,cli,imap,intl,devel,json,ldap,xml,fpm,opcache}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;php errorlog&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;./env/php/fpm/log:/var/log/php-fpm/error.log&#xA;./env/php/fpm/php-fpm.conf:/etc/php-fpm.conf&#xA;./env/php/fpm/:/etc/php-fpm.d&#xA;./env/php/php.d/:/etc/php.d&#xA;./env/php/php.ini:/etc/php.ini&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>关于wkhtmltopdf，你一定想知道这些</title>
    <updated>2018-02-28T17:20:39+08:00</updated>
    <id>tag:pipe.b3log.org,2018-02-28:/blogs/happyhacker/articles/2018/02/28/1519809638546</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/02/28/1519809638546" rel="alternate"></link>
    <summary type="html">&lt;h2&gt;前言&lt;/h2&gt;&#xA;&lt;p&gt;如果你有 Markdown 或者 HTML 转到 PDF 的需求, 有非常大的可能你选了一圈方案, 最后找到了大名鼎鼎的wkhtmltopdf. 但找到它之后并没有解决所有问题, 相反, 它会带来更多的问题.&lt;br/&gt;&#xA;首先说一下它的优点:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;由于是基于 WebKit, 所以渲染结果和 Chrome( 包括其他基于 Chromium的浏览器几乎完全一致)&lt;/li&gt;&#xA;&lt;li&gt;可以和前端公用一套 CSS 样式, 结果还是体验的一致性&lt;/li&gt;&#xA;&lt;li&gt;支持大量的定制, 包括页头页脚, 页码, 目录等等, 后面会详细说&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;再来说一下缺点:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;对特别长的表格支持有问题. 其实原因还是来自 WebKit 引擎, 因为WebKit本身是用来渲染网页的, 而网页是不需要上下分页的, 所以引擎本身并不支持表格跨页显示, 需要做一些特殊处理, 划重点了.&lt;/li&gt;&#xA;&lt;li&gt;对于特别宽的表格, 如果不做处理, 它会被横向截断，同时会在表格下方出现一个 scrollbar, 但尴尬的是这个 scrollbar 不能拖动, 因此也就无法解决完整显示的问题.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;所以, 优点很明显, 缺点也很明显. 而且通常如果你有需求生成 PDF, 那多数情况下是有表格的, 不解决这个问题, 这个方案就无法实施。&lt;/p&gt;&#xA;&lt;h2&gt;实践&lt;/h2&gt;&#xA;&lt;p&gt;下面让我们通过实验一步步解决上面的两个大问题.&lt;/p&gt;&#xA;&lt;h3&gt;长表格&lt;/h3&gt;&#xA;&lt;p&gt;~~这里我简单写了一长段表格, 然后用 &lt;a href=&#34;https://macdown.uranusjr.com/&#34; rel=&#34;nofollow&#34;&gt;MacDown&lt;/a&gt; 编辑器把它导出为 html, 这里只是用来说明问题, 所以就没有用我实际在生产环境使用的 &lt;a href=&#34;https://github.com/erusev/parsedown&#34; rel=&#34;nofollow&#34;&gt;Parsedown&lt;/a&gt;.~~写着写着觉得还是用代码能说的更清晰一些，&lt;br/&gt;&#xA;原始 markdown 文件和 html 文件就不贴在这里了, 可以查看&lt;a href=&#34;https://github.com/subtlephp/phpwkhtmltox&#34; rel=&#34;nofollow&#34;&gt;phpwkhtmltopdf&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;在 article 目录里执行&lt;br/&gt;&#xA;&lt;code&gt;wkhtmltopdf longtable.html longtable-01.pdf&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;生成的 PDF 在分页处如下的效果&lt;br/&gt;&#xA;&lt;img data-src=&#34;http://upload-images.jianshu.io/upload_images/9764160-0978eb9282a95838.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240&#34; alt=&#34;表头重复, 字体重叠&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;很明显可以看到表头重复出现了. 其实实际的问题不止如此, 如果单元格很宽导致换行, 很大概率会和重复显示的表头重叠.&lt;br/&gt;&#xA;我翻看了不下100个网页, 花费了大量时间最终找到了解决方案&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-css highlight-chroma&#34;&gt;&lt;span class=&#34;highlight-nt&#34;&gt;thead&lt;/span&gt; &lt;span class=&#34;highlight-p&#34;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&#34;highlight-k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;highlight-kc&#34;&gt;table&lt;/span&gt;&lt;span class=&#34;highlight-o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;highlight-kc&#34;&gt;row&lt;/span&gt;&lt;span class=&#34;highlight-o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;highlight-n&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;highlight-p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;解决了表头重复和文字重叠的问题.&lt;br/&gt;&#xA;&lt;img data-src=&#34;http://upload-images.jianshu.io/upload_images/9764160-70f04c0156b18945.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240&#34; alt=&#34;表头不重复, 字体不重叠, 但一行被分割&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;有所改善,  但还是明显的发现表格的一行被切成了两部分.&lt;br/&gt;&#xA;下面这个就是如果你搜索这个问题最经常看到的答案了&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-css highlight-chroma&#34;&gt;&lt;span class=&#34;highlight-nt&#34;&gt;tr&lt;/span&gt; &lt;span class=&#34;highlight-p&#34;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&#34;highlight-k&#34;&gt;page-break-before&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;highlight-kc&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&#34;highlight-k&#34;&gt;page-break-after&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;highlight-kc&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&#34;highlight-k&#34;&gt;page-break-inside&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;highlight-kc&#34;&gt;avoid&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;highlight-p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;于是, 变成了这样子&lt;br/&gt;&#xA;&lt;img data-src=&#34;http://upload-images.jianshu.io/upload_images/9764160-f9728c57af2aad17.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240&#34; alt=&#34;长表格问题解决&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;长表格的问题到此解决.&lt;/p&gt;&#xA;&lt;h3&gt;宽表格&lt;/h3&gt;&#xA;&lt;p&gt;同样我将 markdown 文件和 HTML 文件放在了 github 上.&lt;br/&gt;&#xA;不加特殊处理, 得到的表格将是这样的&lt;br/&gt;&#xA;&lt;img data-src=&#34;http://upload-images.jianshu.io/upload_images/9764160-3939f48b0dd7eb2d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240&#34; alt=&#34;表格右侧被截断&#34;/&gt;&lt;br/&gt;&#xA;这是因为默认的样式中并没有&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-css highlight-chroma&#34;&gt;&lt;span class=&#34;highlight-nt&#34;&gt;table&lt;/span&gt; &lt;span class=&#34;highlight-p&#34;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&#34;highlight-k&#34;&gt;word-wrap&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;highlight-kc&#34;&gt;break-word&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;highlight-p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;的设置. 我这里的例子举的有些不合理, 因为不会全是一样长的单元格宽度, 实际情况总会有长有短, 当同一列中存在一个很长的单词(或者根本不是单词, 而仅仅是很长的连起来的无意义的字符串, 像本例中那样), 该列单元格的最短列宽就会以它为准. 这样导致表格的总宽度很容易超出页面限制.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;因为&lt;code&gt;word-wrap&lt;/code&gt; 控制的是有多个单词的情况&lt;/strong&gt;, 如果只有一个单词, 它是不做处理的. 所以需要另外加一个样式&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-css highlight-chroma&#34;&gt;&lt;span class=&#34;highlight-nt&#34;&gt;table&lt;/span&gt; &lt;span class=&#34;highlight-nt&#34;&gt;td&lt;/span&gt; &lt;span class=&#34;highlight-p&#34;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&#34;highlight-k&#34;&gt;word-break&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;highlight-n&#34;&gt;break-all&lt;/span&gt;&lt;span class=&#34;highlight-p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;highlight-p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;这样的结果就是所有表格都完整的展示出来了.&lt;/p&gt;&#xA;&lt;h2&gt;其他选项&lt;/h2&gt;&#xA;&lt;p&gt;提到 wkhtmltopdf 的选项也真是让人头疼, 我本来是用了&lt;a href=&#34;https://github.com/KnpLabs/snappy&#34; rel=&#34;nofollow&#34;&gt;snappy&lt;/a&gt;和它推荐的二进制包, 所有东西都准备好了, 一个问题一直解决不了: 我已经安装了宋体, PDF 的正文中的宋体也可以正常显示了, 但唯独 header/footer 里面的中文字体死活都出不来. 因为我的需求是要有页眉和页脚, 当然也要有相应的 &lt;code&gt;header-line&lt;/code&gt;和&lt;code&gt;footer-line&lt;/code&gt;, 如果用HTML 来填充 header, 那么 header-line 就会消失, 这时如果用 html 中的&lt;code&gt;&amp;lt;hr&amp;gt;&lt;/code&gt;来代替, 又会看起来很奇怪, 后来发现直接调用二进制文件加上相应的选项在相同的机器上可以得出我需要的结果, 那么问题就很清楚了, 问题出在这个 snappy 上, 于是我一气之下直接用 PHP 的&lt;code&gt;shell_exec&lt;/code&gt;简单封装了一下 wkhtmltopdf, 简直不能更好用, 所有问题迎刃而解.&lt;/p&gt;&#xA;&lt;h2&gt;总结&lt;/h2&gt;&#xA;&lt;p&gt;宽度的问题其实在网页设计里面更常见, 我不是专业的前端, 所以对相应的样式比较陌生, 导致花了一些时间. 但分页的问题真是困扰了每个用过 wkhtmltopdf 的人, 随便一搜, 几乎全是关于这个问题, 但没有一个人真正的给出普适的真正可用的解决方案. 本文给出了我自己的一些经验, 供大家参考.&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>MySQL 简单性能测试记录</title>
    <updated>2018-02-28T17:15:53+08:00</updated>
    <id>tag:pipe.b3log.org,2018-02-28:/blogs/happyhacker/articles/2018/02/28/1519809353000</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/articles/2018/02/28/1519809353000" rel="alternate"></link>
    <summary type="html">&lt;h1&gt;机器配置:&lt;/h1&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;CPU: Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz&lt;/li&gt;&#xA;&lt;li&gt;Memory: 8GB&lt;/li&gt;&#xA;&lt;li&gt;MySQL: 5.7.21-0ubuntu0.16.04.1 (Ubuntu)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h1&gt;准备工作:&lt;/h1&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;sysbench --mysql-db=db_test \&#xA;--mysql-port=3306 --db-driver=mysql --mysql-user=root \&#xA;--mysql-password=root --test=oltp --oltp-table-size=10000000 \&#xA;--num-threads=100 --max-time=60 \&#xA;--oltp-test-mode=nontrx --oltp-nontrx-mode=select \&#xA;--max-requests=0 --percentile=95 prepare&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2&gt;1. 读取性能测试&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;sysbench --mysql-db=db_test \&#xA;--mysql-port=3306 --db-driver=mysql --mysql-user=root \&#xA;--mysql-password=root --test=oltp --oltp-table-size=10000000 \&#xA;--num-threads=100 --max-time=60 \&#xA;--oltp-test-mode=nontrx --oltp-nontrx-mode=select \&#xA;--max-requests=0 --percentile=95 run&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;结果:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;OLTP test statistics:&#xA;    queries performed:&#xA;        read:                            4248201&#xA;        write:                           0&#xA;        other:                           0&#xA;        total:                           4248201&#xA;    transactions:                        4248201 (70801.29 per sec.)&#xA;    deadlocks:                           0      (0.00 per sec.)&#xA;    read/write requests:                 4248201 (70801.29 per sec.)&#xA;    other operations:                    0      (0.00 per sec.)&#xA;&#xA;Test execution summary:&#xA;    total time:                          60.0017s&#xA;    total number of events:              4248201&#xA;    total time taken by event execution: 5998.0902&#xA;    per-request statistics:&#xA;         min:                                  0.03ms&#xA;         avg:                                  1.41ms&#xA;         max:                                266.43ms&#xA;         approx.  95 percentile:               2.72ms&#xA;&#xA;Threads fairness:&#xA;    events (avg/stddev):           42482.0100/686.55&#xA;    execution time (avg/stddev):   59.9809/0.00&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;可以看到差不多7万的 QPS&lt;/p&gt;&#xA;&lt;h2&gt;2. 写入性能测试&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;sysbench --mysql-db=db_test \&#xA;--mysql-port=3306 --db-driver=mysql --mysql-user=root \&#xA;--mysql-password=root --test=oltp --oltp-table-size=10000000 \&#xA;--num-threads=100 --max-time=60 \&#xA;--oltp-test-mode=nontrx --oltp-nontrx-mode=insert \&#xA;--max-requests=0 --percentile=95 run&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;OLTP test statistics:&#xA;    queries performed:&#xA;        read:                            0&#xA;        write:                           49227&#xA;        other:                           0&#xA;        total:                           49227&#xA;    transactions:                        49227  (817.14 per sec.)&#xA;    deadlocks:                           0      (0.00 per sec.)&#xA;    read/write requests:                 49227  (817.14 per sec.)&#xA;    other operations:                    0      (0.00 per sec.)&#xA;&#xA;Test execution summary:&#xA;    total time:                          60.2432s&#xA;    total number of events:              49227&#xA;    total time taken by event execution: 6022.6811&#xA;    per-request statistics:&#xA;         min:                                 29.59ms&#xA;         avg:                                122.35ms&#xA;         max:                               1234.17ms&#xA;         approx.  95 percentile:             357.95ms&#xA;&#xA;Threads fairness:&#xA;    events (avg/stddev):           492.2700/8.26&#xA;    execution time (avg/stddev):   60.2268/0.03&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;可以看到就只有800多的 QPS 了, 远远低于简单查询.&lt;/p&gt;&#xA;&lt;h2&gt;3. 更新(带索引)&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;sysbench --mysql-db=db_test \&#xA;--mysql-port=3306 --db-driver=mysql --mysql-user=root \&#xA;--mysql-password=root --test=oltp --oltp-table-size=10000000 \&#xA;--num-threads=100 --max-time=60 \&#xA;--oltp-test-mode=nontrx --oltp-nontrx-mode=update_key \&#xA;--max-requests=0 --percentile=95 run&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;结果&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;OLTP test statistics:&#xA;    queries performed:&#xA;        read:                            0&#xA;        write:                           21942&#xA;        other:                           0&#xA;        total:                           21942&#xA;    transactions:                        21942  (363.68 per sec.)&#xA;    deadlocks:                           0      (0.00 per sec.)&#xA;    read/write requests:                 21942  (363.68 per sec.)&#xA;    other operations:                    0      (0.00 per sec.)&#xA;&#xA;Test execution summary:&#xA;    total time:                          60.3338s&#xA;    total number of events:              21942&#xA;    total time taken by event execution: 6012.0994&#xA;    per-request statistics:&#xA;         min:                                 33.29ms&#xA;         avg:                                274.00ms&#xA;         max:                               2546.19ms&#xA;         approx.  95 percentile:             629.71ms&#xA;&#xA;Threads fairness:&#xA;    events (avg/stddev):           219.4200/6.41&#xA;    execution time (avg/stddev):   60.1210/0.04&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2&gt;4. 更新(不带 key)&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;sysbench --mysql-db=db_test \&#xA;--mysql-port=3306 --db-driver=mysql --mysql-user=root \&#xA;--mysql-password=root --test=oltp --oltp-table-size=10000000 \&#xA;--num-threads=100 --max-time=60 \&#xA;--oltp-test-mode=nontrx --oltp-nontrx-mode=update_nokey \&#xA;--max-requests=0 --percentile=95 run&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;结果:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;OLTP test statistics:&#xA;    queries performed:&#xA;        read:                            0&#xA;        write:                           28065&#xA;        other:                           0&#xA;        total:                           28065&#xA;    transactions:                        28065  (464.81 per sec.)&#xA;    deadlocks:                           0      (0.00 per sec.)&#xA;    read/write requests:                 28065  (464.81 per sec.)&#xA;    other operations:                    0      (0.00 per sec.)&#xA;&#xA;Test execution summary:&#xA;    total time:                          60.3791s&#xA;    total number of events:              28065&#xA;    total time taken by event execution: 6034.7862&#xA;    per-request statistics:&#xA;         min:                                 32.02ms&#xA;         avg:                                215.03ms&#xA;         max:                               1426.55ms&#xA;         approx.  95 percentile:             574.59ms&#xA;&#xA;Threads fairness:&#xA;    events (avg/stddev):           280.6500/8.18&#xA;    execution time (avg/stddev):   60.3479/0.06&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;可以看到, update_nokey 比 update_key 略微高一些, 这也证明了对于更新频繁的表, 不要设置太多的索引的正确性.&lt;/p&gt;&#xA;&lt;h2&gt;4. 删除&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;sysbench --mysql-db=db_test \&#xA;--mysql-port=3306 --db-driver=mysql --mysql-user=root \&#xA;--mysql-password=root --test=oltp --oltp-table-size=10000000 \&#xA;--num-threads=100 --max-time=60 \&#xA;--oltp-test-mode=nontrx --oltp-nontrx-mode=delete \&#xA;--max-requests=0 --percentile=95 run&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;结果:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;highlight-chroma&#34;&gt;OLTP test statistics:&#xA;    queries performed:&#xA;        read:                            0&#xA;        write:                           8062&#xA;        other:                           0&#xA;        total:                           8062&#xA;    transactions:                        8062   (119.45 per sec.)&#xA;    deadlocks:                           0      (0.00 per sec.)&#xA;    read/write requests:                 8062   (119.45 per sec.)&#xA;    other operations:                    0      (0.00 per sec.)&#xA;&#xA;Test execution summary:&#xA;    total time:                          67.4955s&#xA;    total number of events:              8062&#xA;    total time taken by event execution: 6290.0099&#xA;    per-request statistics:&#xA;         min:                                 33.34ms&#xA;         avg:                                780.20ms&#xA;         max:                              16901.57ms&#xA;         approx.  95 percentile:            3838.98ms&#xA;&#xA;Threads fairness:&#xA;    events (avg/stddev):           80.6200/12.56&#xA;    execution time (avg/stddev):   62.9001/2.30&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h1&gt;总结&lt;/h1&gt;&#xA;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;操作类型&lt;/th&gt;&#xA;&lt;th&gt;性能(QPS)&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;select&lt;/td&gt;&#xA;&lt;td&gt;70801/s&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;insert&lt;/td&gt;&#xA;&lt;td&gt;817/s&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;update&lt;/td&gt;&#xA;&lt;td&gt;363/s&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;update_nokey&lt;/td&gt;&#xA;&lt;td&gt;464/s&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;delete&lt;/td&gt;&#xA;&lt;td&gt;119/s&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
  <entry>
    <title>macOS点击链接Chrome打开空白新窗口解决方法</title>
    <updated>2018-02-25T17:06:04+08:00</updated>
    <id>tag:pipe.b3log.org,2018-02-25:/blogs/happyhacker/fix-chrome-blank-window-opening-external-url</id>
    <link href="http://pipe.b3log.org/blogs/happyhacker/fix-chrome-blank-window-opening-external-url" rel="alternate"></link>
    <summary type="html">&lt;p&gt;&lt;img data-src=&#34;https://img.hacpai.com/pipe/pipe/happyhacker/happyhacker/d6462083b6c84747b32fb88443aea7b7.png?imageView2/2/w/1280/format/jpg/interlace/1/q/100&#34; alt=&#34;QQ201802261623042xpng&#34;/&gt;&lt;/p&gt;&#xA;&lt;p&gt;最近一个多月了吧, 从微信或者其他地方点击链接从 Chrome 打开(默认 Chrome 浏览器)时 Chrome 总是只打开一个新的空白窗口, 对源地址完全不感知. 把地址复制下来放在地址栏里可以正常打开.&lt;/p&gt;&#xA;&lt;p&gt;解决方案非常简单, 找到 Settings-&amp;gt;Advanced-&amp;gt;System-&amp;gt;Use hardware acceleration when available,(我用的是英文系统, 懒得找中文对应的翻译了). 相应的项目右边会有一个开关, 切换一下开关, 左边会有一个 RELAUNCH 按钮, 点击后 Chrome 会重启, 问题就解决了. 如果你还想开启/关闭硬件加速, 重新执行上述操作即可.&lt;/p&gt;&#xA;</summary>
    <author>
      <name>happyhacker</name>
    </author>
  </entry>
</feed>