<?xml version="1.0" encoding="utf-8" ?>


<feed xmlns="http://www.w3.org/2005/Atom">
  <title>长电风扇</title>

  <description>iOS, Objc, Objective-C，龙凡，longfan，apple，苹果，iPhone, Xcode, WWDC, 16, 2016, iOS10</description>

  <link href="http://blog.longfan.me/"></link>

  <link ref="self" href="http://blog.longfan.me/feed"></link>

  <id>0d7c69d8b60f3b27780b987e905c514aa866096e-longfan.me</id>


  <updated>2022-04-02T09:31:00Z</updated>


  <entry>


    <title>Apipost的localhost的访问坑</title>

    <link href="http://blog.longfan.me/post/system/2022-04-02"  rel="alternate"></link>

    <updated>2022-04-02T09:31:00Z</updated>
    <id>system/2022-04-02</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;WSL起个服务，localhost访问嘛报错 connect ECONNREFUSED&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;开始以为是apipost的问题，postman也好浏览器也好都能访问，然后就apipost不行。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;这边的同学用远程协助之后，我观察到apipost的log里面，localhost解析出来是127.0.0.1，但是我自己去控制台ping，解析出来是[::1] （win vista之后的默认）。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;侧面说明了这127.0.0.1不是解析出来的，是代码里面写死的（code review？）。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;这个么好办了，既然解析出来的 loopback address是IPV6的，那么我们直接用。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;浏览器访问没问题，怀着高兴的心情去用，搞笑的是apipost不支持V6的地址，直接给我parse成http字符了。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;好了只能另寻出路，查啊查，研究了一下WSL的网络（搞了n年linux现在来研究windows。。。），发现这货不是绑定到localhost的。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;WSL2的虚拟机自己有个虚拟的NIC，你可以理解为localhost作为一个NAT网关，直接转发到下面设置好端口转发的wsl虚拟网卡上面。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;想研究深入的自己看文档：https://docs.microsoft.com/en-us/windows/wsl/&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;这样么挺好，我们找到wsl自己的ip就好了，进wsl&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;~ ip -4 addr show eth0
5: eth0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu &lt;span class="m"&gt;1500&lt;/span&gt; qdisc mq state UP group default qlen 1000
    inet 172.17.118.219/20 brd 172.17.127.255 scope global eth0
       valid_lft forever preferred_lft forever
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;这样这个东西 172.17.118.219， 就是wsl的ip，这个可以直接放到apipost的环境里面去用，没有任何问题。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;问题解决。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;但是呢，wsl这东西每次启动都会换ip，很烦，怎么办，写个powershell脚本，等每次wsl启动完了跑一下，把这ip写到一个固定的host上面就好了 （所谓本地DDNS）&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;$hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;wsl&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# find ip of eth0&lt;/span&gt;
&lt;span class="nv"&gt;$ifconfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;wsl -- ip -4 addr show eth0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;$ipPattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;((\d+\.?){4})&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;$ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;([&lt;/span&gt;regex&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;inet &lt;/span&gt;&lt;span class="nv"&gt;$ipPattern&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.Match&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ifconfig&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.Groups&lt;span class="o"&gt;[&lt;/span&gt;1&lt;span class="o"&gt;]&lt;/span&gt;.Value
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;-not &lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
Write-Host &lt;span class="nv"&gt;$ip&lt;/span&gt;

&lt;span class="nv"&gt;$hostsPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;:windir/system32/drivers/etc/hosts&amp;quot;&lt;/span&gt;

&lt;span class="nv"&gt;$hosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Get-Content -Path &lt;span class="nv"&gt;$hostsPath&lt;/span&gt; -Raw -ErrorAction Ignore&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$null&lt;/span&gt; -eq &lt;span class="nv"&gt;$hosts&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$hosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$hosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$hosts&lt;/span&gt;.Trim&lt;span class="o"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# update or add wsl ip&lt;/span&gt;
&lt;span class="nv"&gt;$find&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$ipPattern&lt;/span&gt;&lt;span class="s2"&gt;\s+&lt;/span&gt;&lt;span class="nv"&gt;$hostname&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;$entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$hostname&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hosts&lt;/span&gt; -match &lt;span class="nv"&gt;$find&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$hosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$hosts&lt;/span&gt; -replace &lt;span class="nv"&gt;$find&lt;/span&gt;, &lt;span class="nv"&gt;$entry&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$hosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$hosts&lt;/span&gt;&lt;span class="s2"&gt;`n&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;.Trim&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

try &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$hostsPath&lt;/span&gt;&lt;span class="s2"&gt;.new&amp;quot;&lt;/span&gt;
    New-Item -Path &lt;span class="nv"&gt;$temp&lt;/span&gt; -ItemType File -Force &lt;span class="p"&gt;|&lt;/span&gt; Out-Null
    Set-Content -Path &lt;span class="nv"&gt;$temp&lt;/span&gt; &lt;span class="nv"&gt;$hosts&lt;/span&gt;

    Move-Item -Path &lt;span class="nv"&gt;$temp&lt;/span&gt; -Destination &lt;span class="nv"&gt;$hostsPath&lt;/span&gt; -Force
&lt;span class="o"&gt;}&lt;/span&gt;
catch &lt;span class="o"&gt;{&lt;/span&gt;
    Write-Error &lt;span class="s2"&gt;&amp;quot;cannot update wsl ip&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;脚本抄来的，写得很不错没必要自己改，原文在这里：https://abdus.dev/posts/fixing-wsl2-localhost-access-issue/，设置自动trigger这些都有。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;本来想发到apipost自己论坛去的，给我报什么非法字符，也是无奈啊&lt;/span&gt;
&lt;/p&gt;</summary>

  </entry>


  <entry>


    <title>gRPC 'Error: 14 UNAVAILABLE: TCP Write failed' issue</title>

    <link href="http://blog.longfan.me/post/devops/2020-07-09"  rel="alternate"></link>

    <updated>2020-07-09T07:53:00Z</updated>
    <id>devops/2020-07-09</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;h1 id="toc_0" class="h16"&gt;Phenomenon&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;最近在把公司的服务逐步从物理机部署迁移到 kubernetes 的过程中遇到了一些奇怪的问题。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;其他服务都没啥问题，但是 nodejs 服务调用 grpc 服务的时候偶然会出现这种 Error，仅在 k8s 中出现:&lt;/span&gt;
&lt;/p&gt;


&lt;pre class="lang_log"&gt;&lt;code&gt; Error: 14 UNAVAILABLE: TCP Write failed
    at Object.exports.createStatusError (/app/node_modules/grpc/src/common.js:91:15)
    at Object.onReceiveStatus (/app/node_modules/grpc/src/client_interceptors.js:1209:28)
    at InterceptingListener._callNext (/app/node_modules/grpc/src/client_interceptors.js:568:42)
    at InterceptingListener.onReceiveStatus (/app/node_modules/grpc/src/client_interceptors.js:618:8)
    at callback (/app/node_modules/grpc/src/client_interceptors.js:847:24)
  code: 14,
  metadata: Metadata { _internal_repr: {}, flags: 0 },
  details: 'TCP Write failed'&lt;/code&gt;&lt;/pre&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;因为的确偶然，偶然到在 UAT 测试的时候都没有遇到过，QA 也无法重现，非常头疼。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;按着这个&lt;code&gt;Error: 14 UNAVAILABLE: TCP Write failed&lt;/code&gt;去找，找来找去也都是一些说 grpc 版本的问题，看起来就不像。&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_1" class="h16"&gt;Action&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;既然表面上看不出来，我们就打开 grpc 的 debug 模式看看 grpc 库的内部到底具体报的什么错吧。&lt;/span&gt;
&lt;/p&gt;


&lt;pre class="lang_env"&gt;&lt;code&gt;GRPC_TRACE = all
GRPC_VERBOSITY = DEBUG&lt;/code&gt;&lt;/pre&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;生产加上这个重新部署之后在偶然的错误里面发现了一些细节，每个&lt;code&gt;Error: 14 UNAVAILABLE: TCP Write failed&lt;/code&gt;错误后面的 grpc 的 log 都会出现这种类型的 debug 日志:&lt;/span&gt;
&lt;/p&gt;


&lt;pre class="lang_log"&gt;&lt;code&gt;I0709 13:20:21.758757992      16 chttp2_transport.cc:839]    W:0x39fe7b0 CLIENT [ipv4:10.98.85.83:80] state WRITING -&amp;gt; IDLE [finish writing]
I0709 13:20:21.758762763      16 chttp2_transport.cc:2866]   transport 0x39fe7b0 set connectivity_state=4
I0709 13:20:21.758768114      16 connectivity_state.cc:147]  SET: 0x39fea58 client_transport: READY --&amp;gt; SHUTDOWN [close_transport]
I0709 13:20:21.758772872      16 connectivity_state.cc:160]  NOTIFY: 0x39fea58 client_transport: 0x3645a68
I0709 13:20:21.758791048      16 tcp_custom.cc:287]          TCP 0x3772c30 shutdown why={"created":"@1594272021.758670258","description":"Delayed close due to in-progress write","file":"../deps/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.cc","file_line":593,"referenced_errors":[{"created":"@1594272021.758639866","description":"TCP Write failed","file":"../deps/grpc/src/core/lib/iomgr/tcp_uv.cc","file_line":72,"grpc_status":14,"os_error":"connection reset by peer"},{"created":"@1594272021.758743251","description":"TCP Write failed","file":"../deps/grpc/src/core/lib/iomgr/tcp_uv.cc","file_line":72,"grpc_status":14,"os_error":"broken pipe"}]}&lt;/code&gt;&lt;/pre&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;error 里面的第一个原因是一个&lt;code&gt;&amp;quot;os_error&amp;quot;:&amp;quot;connection reset by peer&amp;quot;&lt;/code&gt;这样的 error，但是具体什么会造成这样的错误呢？&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;研究了一下之前所有服务的 log，发现我们的测试服完全没有&lt;code&gt;Error: 14 UNAVAILABLE: TCP Write failed&lt;/code&gt;，出现的仅仅在 UAT 和生产服里面，而测试服和生产服最大的区别就是测试服使用的 kube-proxy 是 iptables, UAT 和生产是 ipvs.&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;尝试寻找了一下网上看有没有人踩过这样的坑，结果一搜就搜出来一篇相关的文章，还是中文的[Kubernetes IPVS 模式下服务间长连接通讯的优化，解决 Connection reset by peer 问题 ---- 青蛙小白][https://blog.frognew.com/2018/12/kubernetes-ipvs-long-connection-optimize.html]. 看作者的问题描述，基本判断是遇到的同样的问题。&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_2" class="h16"&gt;Reason&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;通过阅读文章，原因锁定到了文章开头的一段话:_我们知道 gRPC 是基于 HTTP/2 协议的，gRPC 的 client 和 server 在交互时会建立多条连接，为了性能，这些连接都是长连接并且是一直保活的。 这段环境中不管是客户端服务还是 gRPC 服务都被调度到各个相同配置信息的 Kubernetes 节点上，这些 k8s 节点的 keep-alive 是一致的，如果出现连接主动关闭的问题，因为从 client 到 server 经历了一层 ipvs，所以最大的可能就是 ipvs 出将连接主动断开，而 client 端还不知情_&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;因为我们并没有改动过 k8s 节点的&lt;code&gt;net.ipv4.tcp_keepalive_time&lt;/code&gt;，所以经过检查我们发现系统符合出现 issue 的描述&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_probes net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_time &lt;span class="o"&gt;=&lt;/span&gt; 7200
net.ipv4.tcp_keepalive_probes &lt;span class="o"&gt;=&lt;/span&gt; 9
net.ipv4.tcp_keepalive_intvl &lt;span class="o"&gt;=&lt;/span&gt; 75

ipvsadm -l --timeout
Timeout &lt;span class="o"&gt;(&lt;/span&gt;tcp tcpfin udp&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="m"&gt;900&lt;/span&gt; &lt;span class="m"&gt;120&lt;/span&gt; 300
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;通过研究 net.ipv4.tcp_keepalive 的具体用途和设置方法之后，找到了解决方法&lt;br /&gt;&lt;/span&gt;
    &lt;span style='font-family:https://webhostinggeeks.com/howto/configure-linux-tcp-keepalive-setting'  class="md_line md_line_end"&gt;[How to Configure Linux TCP keepalive Setting]&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_3" class="h16"&gt;Solution&lt;/h1&gt;&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;net.ipv4.tcp_keepalive_time &lt;span class="o"&gt;=&lt;/span&gt; 600
net.ipv4.tcp_keepalive_probes &lt;span class="o"&gt;=&lt;/span&gt; 9
net.ipv4.tcp_keepalive_intvl &lt;span class="o"&gt;=&lt;/span&gt; 30

&lt;span class="m"&gt;600&lt;/span&gt; + &lt;span class="m"&gt;9&lt;/span&gt; * &lt;span class="nv"&gt;30&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;870&lt;/span&gt; &amp;lt; 900
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;尝试把&lt;code&gt;net.ipv4.tcp_keepalive_time + net.ipv4.tcp_keepalive_probes * net.ipv4.tcp_keepalive_intvl&lt;/code&gt;改成了小于 900 的值以后，经过观察和测试，貌似出现&lt;code&gt;Error: 14 UNAVAILABLE: TCP Write failed&lt;/code&gt;错误信息的概率变小了, 问题看起来暂时解决了一部分，准备再观察一段时间看看情况。&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_4" class="h16"&gt;Solution+&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;观察了一晚上，发现&lt;code&gt;Error: 14 UNAVAILABLE: TCP Write failed&lt;/code&gt;还是存在，如果理论上出现错误的原因是找正确了的，那么问题就出在解决问题的方法是否有效上面了。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;在所有宿主机上面观察了&lt;code&gt;net.ipv4.tcp_keepalive*&lt;/code&gt;的设置都是正确的&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_probes net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_time &lt;span class="o"&gt;=&lt;/span&gt; 600
net.ipv4.tcp_keepalive_probes &lt;span class="o"&gt;=&lt;/span&gt; 9
net.ipv4.tcp_keepalive_intvl &lt;span class="o"&gt;=&lt;/span&gt; 30
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;但是容器里面真实的情况呢？&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;进入容器之后运行同样的命令，发现令人沮丧的消息&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_probes net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_time &lt;span class="o"&gt;=&lt;/span&gt; 7200
net.ipv4.tcp_keepalive_probes &lt;span class="o"&gt;=&lt;/span&gt; 9
net.ipv4.tcp_keepalive_intvl &lt;span class="o"&gt;=&lt;/span&gt; 75
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;宿主机的&lt;code&gt;sysctl&lt;/code&gt;设置并没有进入容器，看来 namespace 的隔离还是非常彻底的，查阅了一些资料发现，如果要设置容器内部的&lt;code&gt;net.ipv4.tcp_keepalive*&lt;/code&gt;，会涉及到使用非安全的内核设置，需要使用&lt;code&gt;privilieged&lt;/code&gt;POD 设置，不太安全[Reference][https://stackoverflow.com/questions/54552379/tcp-keepalive-time-in-docker-container/54564456#54564456].看看能不能另辟蹊径。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;通过阅读&lt;code&gt;grpc&lt;/code&gt;库的[C++文档][https://grpc.github.io/grpc/cpp/md_doc_keepalive.html]我们发现可以使用一些设置来注入&lt;code&gt;grpc&lt;/code&gt;库，来改变库本身的对于 tcp keepalive 的频率设置。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;&lt;code&gt;GRPC_ARG_KEEPALIVE_TIME_MS&lt;/code&gt;可以设置库对于 keepalive 探针时间的频率，我们可以设置得小于等于 900 秒，同时打开设置&lt;code&gt;GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS&lt;/code&gt;允许没有请求的时候也发送 keepalive 探针，&lt;code&gt;GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA&lt;/code&gt;设为 0 避免服务端认为过高频率的探针是恶意探针而屏蔽掉。以 nodejs 为例&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_js  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;grpcClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;GrpcService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GRPC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;grpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createInsecure&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;grpc.keepalive_time_ms&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GRPC_ARG_KEEPALIVE_TIME_MS&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;7200000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;grpc.keepalive_permit_without_calls&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;grpc.http2.max_pings_without_data&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;又观察了一晚上发现没有任何报错了，问题解决。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;鸣谢：&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;fangzhoutang@hotmail.com的勘误：&amp;quot;GRPC_ARG_KEEPALIVE_TIME_MS可以设置库对于 keepalive 探针时间的频率，我们可以设置得小于等于 900 毫秒&amp;quot; 此处的毫秒应该为秒&lt;/span&gt;
&lt;/p&gt;</summary>

  </entry>


  <entry>


    <title>The 坑 of install Kong and Konga in Docker</title>

    <link href="http://blog.longfan.me/post/devops/2019-12-05"  rel="alternate"></link>

    <updated>2019-12-05T07:53:00Z</updated>
    <id>devops/2019-12-05</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;h2 id="toc_0" class="h16"&gt;Install Kong with PostgreSQL in docker&lt;/h2&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;Create docker network&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; $ docker network create kong-net
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;Start database (psql should be 9.6!!)&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; $ docker run -d --name kong-database &lt;span class="se"&gt;\&lt;/span&gt;
               --network&lt;span class="o"&gt;=&lt;/span&gt;kong-net &lt;span class="se"&gt;\&lt;/span&gt;
               -p 5432:5432 &lt;span class="se"&gt;\&lt;/span&gt;
               -e &lt;span class="s2"&gt;&amp;quot;POSTGRES_USER=kong&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               -e &lt;span class="s2"&gt;&amp;quot;POSTGRES_DB=kong&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               postgres:9.6
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;Bootstrap data&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ docker run --rm &lt;span class="se"&gt;\&lt;/span&gt;
     --network&lt;span class="o"&gt;=&lt;/span&gt;kong-net &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_DATABASE=postgres&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_PG_HOST=kong-database&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     kong:latest kong migrations bootstrap
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;Start Kong, donot expose admin port to public! konga will handle it locally. 9000 and 9443 is for grpc.&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; $ docker run -d --name kong &lt;span class="se"&gt;\&lt;/span&gt;
     --network&lt;span class="o"&gt;=&lt;/span&gt;kong-net &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_DATABASE=postgres&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_PG_HOST=kong-database&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_PROXY_ACCESS_LOG=/dev/stdout&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_ADMIN_ACCESS_LOG=/dev/stdout&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_PROXY_ERROR_LOG=/dev/stderr&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_ADMIN_ERROR_LOG=/dev/stderr&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONG_PROXY_LISTEN=0.0.0.0:9000 http2, 0.0.0.0:9443 http2 ssl&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -p 80:8000 &lt;span class="se"&gt;\&lt;/span&gt;
     -p 443:8443 &lt;span class="se"&gt;\&lt;/span&gt;
     -p 8001:8001 &lt;span class="se"&gt;\&lt;/span&gt;
     -p 8444:8444 &lt;span class="se"&gt;\&lt;/span&gt;
     -p 9000:9000 &lt;span class="se"&gt;\&lt;/span&gt;
     -p 9443:9443 &lt;span class="se"&gt;\&lt;/span&gt;
     kong:latest
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;&lt;h2 id="toc_1" class="h16"&gt;Install Konga with PostgreSQL in docker&lt;/h2&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;Make sure there&amp;#39;s not any database named &lt;code&gt;konga&lt;/code&gt; in your psql. Use develpment env to create the db. Set &lt;code&gt;KONGA_HOOK_TIMEOUT&lt;/code&gt; to more than &lt;code&gt;60000&lt;/code&gt; to avoid hook break up.&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; $ docker run --name konga &lt;span class="se"&gt;\&lt;/span&gt;
     --network kong-net &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;KONGA_HOOK_TIMEOUT=120000&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;DB_ADAPTER=postgres&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -e &lt;span class="s2"&gt;&amp;quot;DB_URI=postgresql://kong:@172.18.0.1:5432/konga&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     -p 1337:1337 &lt;span class="se"&gt;\&lt;/span&gt;
     pantsel/konga:latest
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;If you see this&lt;/span&gt;
&lt;/p&gt;


&lt;pre class="lang_log"&gt;&lt;code&gt;debug: Hook:api_health_checks:process() called
debug: Hook:health_checks:process() called
debug: Hook:start-scheduled-snapshots:process() called
debug: Hook:upstream_health_checks:process() called
debug: Hook:user_events_hook:process() called
debug: Seeding User...
debug: User seed planted
debug: Seeding Kongnode...
debug: Kongnode seed planted
debug: Emailtransport seeds updated
debug: -------------------------------------------------------
debug: :: Thu Dec 05 2019 03:01:53 GMT+0000 (Coordinated Universal Time)
debug: Environment : development
debug: Host        : 0.0.0.0
debug: Port        : 1337
debug: -------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;All done&lt;/span&gt;
&lt;/p&gt;</summary>

  </entry>


  <entry>


    <title>Certbot for wildcard domain</title>

    <link href="http://blog.longfan.me/post/devops/2019-12-04"  rel="alternate"></link>

    <updated>2019-12-04T07:53:00Z</updated>
    <id>devops/2019-12-04</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; $ certbot certonly --manual -d *.foo.bar -d foo.bar --server https://acme-v02.api.letsencrypt.org/directory --preferred-challenges dns
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;Then following the instruction add add TXT record on your domain DNS&lt;/span&gt;
&lt;/p&gt;</summary>

  </entry>


  <entry>


    <title>psycopg2 macOS 安装问题</title>

    <link href="http://blog.longfan.me/post/python/2019-04-30"  rel="alternate"></link>

    <updated>2019-04-30T03:31:00Z</updated>
    <id>python/2019-04-30</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;备忘&lt;/span&gt;
&lt;/p&gt;

&lt;h2 id="toc_0" class="h16"&gt;报个这个错&lt;/h2&gt;&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    ld: library not found &lt;span class="k"&gt;for&lt;/span&gt; -lssl
    clang: error: linker &lt;span class="nb"&gt;command&lt;/span&gt; failed with &lt;span class="nb"&gt;exit&lt;/span&gt; code &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;use -v to see invocation&lt;span class="o"&gt;)&lt;/span&gt;
    error: &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gcc&amp;#39;&lt;/span&gt; failed with &lt;span class="nb"&gt;exit&lt;/span&gt; status 1
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;libssl.dylib 和 libcrypto.dylib 的版本问题&lt;/span&gt;
&lt;/p&gt;

&lt;h2 id="toc_1" class="h16"&gt;奇技淫巧&lt;/h2&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;用brew安装的openssl的两个库来替代系统库&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;brew install openssl
env &lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-I/usr/local/Cellar/openssl/x.x.x/include -L/usr/local/Cellar/openssl/x.x.x/lib&amp;quot;&lt;/span&gt; pip3 install psycopg2
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;好了&lt;/span&gt;
&lt;/p&gt;</summary>

  </entry>


  <entry>


    <title>MongoDB Query 导出到文件</title>

    <link href="http://blog.longfan.me/post/mongodb/2017-11-03"  rel="alternate"></link>

    <updated>2017-11-03T09:31:00Z</updated>
    <id>mongodb/2017-11-03</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;备忘&lt;/span&gt;
&lt;/p&gt;

&lt;h2 id="toc_0" class="h16"&gt;要看一个query出来的大量信息怎么办&lt;/h2&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;先要有一个js文件，来避免双引号之间的冲突，比如&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_javascript  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasNext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;printjson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;然后&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;mongo 127.0.0.1/db script.js &amp;gt;&amp;gt; output.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;</summary>

  </entry>


  <entry>


    <title>坑爹的 iOS Developer Profile</title>

    <link href="http://blog.longfan.me/post/ios/2017-05-08"  rel="alternate"></link>

    <updated>2017-05-08T09:31:00Z</updated>
    <id>ios/2017-05-08</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;是的，就是很坑爹，实在忍不了了，弄清楚了一下，然后写个备忘。&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_0" class="h16"&gt;首先&lt;/h1&gt;
&lt;p class="md_block  md_has_block_below md_has_block_below_ul"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;在&lt;a class="md_compiled" href="https://developer.apple.com/account/ios/certificate"&gt;developer.apple.com/account/ios/certificate&lt;/a&gt;底下，有两个东西。&lt;/span&gt;
&lt;/p&gt;


&lt;ul&gt;
&lt;li class="md_li"&gt;&lt;span&gt;Certificates
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;Provisioning Profiles
&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;这两个东西一个在最上面，一个在最下面。ok，首先保证你有了自己的AppID，那么点击&lt;/span&gt;
&lt;/p&gt;

&lt;h3 id="toc_1" class="h16"&gt;Certificates&lt;/h3&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;你会看见你或许有一堆证书，我们不管这些，找到里面Type是&lt;code&gt;iOS Development&lt;/code&gt;和&lt;code&gt;iOS Distribution&lt;/code&gt;的证书，其中&lt;code&gt;iOS Development&lt;/code&gt;是每个开发者在每台机器上能用来真机测试的证书，而&lt;code&gt;iOS Distribution&lt;/code&gt;是用来打包app的证书并且最多只能有两个。然后我们点击&lt;/span&gt;
&lt;/p&gt;

&lt;h3 id="toc_2" class="h16"&gt;Provisioning Profiles&lt;/h3&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;其中，&lt;code&gt;Development&lt;/code&gt;的Profile可以只有一个并且包含了所有用户的&lt;code&gt;iOS Development Certificate&lt;/code&gt;, 但是&lt;code&gt;Distribution&lt;/code&gt;的Profile只能包含一个&lt;code&gt;iOS Distribution Certificate&lt;/code&gt;。也就是说不管你是用AppStore的发布还是adHoc，都只能包含一个发布证书，其他的一般不会valid。&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_3" class="h16"&gt;然后&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;我们清理掉所有不active的Profile，保证只有一个&lt;code&gt;iOS Development Profile&lt;/code&gt;和每个&lt;code&gt;iOS Distribution Certificate&lt;/code&gt;对应的&lt;code&gt;iOS Distribution Profile&lt;/code&gt;。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;回到我们本机，关掉Xcode&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Library/MobileDevice/Provisioning&lt;span class="se"&gt;\ &lt;/span&gt;Profiles
rm *
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;打开Xcode，进入preference-&amp;gt;account-&amp;gt;Apple IDs-&amp;gt;Dowload All Profiles&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;妈妈再也不用担心我的Profile混乱啦~&lt;/span&gt;
&lt;/p&gt;</summary>

  </entry>


  <entry>


    <title>WWDC 2016 iOS10 拾遗</title>

    <link href="http://blog.longfan.me/post/ios/2016-06-14"  rel="alternate"></link>

    <updated>2016-06-14T03:24:00Z</updated>
    <id>ios/2016-06-14</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;为什么要在大家都在谈论Swift3的时候谈论ObjC? 因为Swift3还是太**（误）。&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_0" class="h16"&gt;iOS10变更&lt;/h1&gt;
&lt;p class="md_block  md_has_block_below md_has_block_below_ul"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;根据这里&lt;a class="md_compiled" href="https://developer.apple.com/library/prerelease/content/releasenotes/General/iOS10APIDiffs/index.html"&gt;iOS 9.3 to iOS 10.0 API Differences&lt;/a&gt;现在的文档表明，现有接口并没有什么大改变，是不可能的。根据苹果的尿性，怎么可能不大改接口呢。嗯，由于实在改的太多，不可能写完，那么就只罗列一下增加的几个主要的新特性吧。&lt;/span&gt;
&lt;/p&gt;


&lt;ul&gt;
&lt;li class="md_li"&gt;&lt;span&gt;SiriKit 
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;Proactive Suggestions 主动推荐
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;Messages
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;User Notifications
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;Speech Recognition 语音转换文字
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;更广的色域
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;自定义True Tone(就是那种黄黄的夜间模式)
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;CallKit
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;App插件
&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;那就一个一个说。&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_1" class="h16"&gt;SiriKit&lt;/h1&gt;
&lt;p class="md_block  md_has_block_below md_has_block_below_ul"&gt;
    &lt;span class="md_line md_line_start"&gt;这次开放Siri接口估计是没多少人真正的把Siri用起来，苹果官方想增加Siri的适用面，加上的开发者接口。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line"&gt;按照官方的说法，Intents Framework 就是专门拿来做Siri识别响应的接口，而响应过后， Intents UI Framework就是通过系统唤起的界面使用App里功能的API了。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line"&gt;当然，苹果还提到了使用Siri和Map都可以达到上述效果，而且接口是通用的，也就是说实现了某个行为的响应之后，不管是Siri还是Map，只要系统识别到了是要你来干这个，那么就会调用你。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;可以响应的行为：&lt;/span&gt;
&lt;/p&gt;


&lt;ul&gt;
&lt;li class="md_li"&gt;&lt;span&gt;音频视频电话
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;信息
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;接收或发出支付请求（这么吊？）
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;搜索图片
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;预定行程（滴滴和Uber的了）
&lt;/span&gt;&lt;/li&gt;
&lt;li class="md_li"&gt;&lt;span&gt;健身管理
&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;文档传送门:&lt;a class="md_compiled" href="https://developer.apple.com/library/prerelease/content/documentation/Intents/Conceptual/SiriIntegrationGuide/index.html#//apple_ref/doc/uid/TP40016875"&gt;SiriKit Programming Guide&lt;/a&gt;,&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_dom_embed md_line_end"&gt;&lt;a class="md_compiled" href="https://developer.apple.com/reference/intents"&gt;Intents Framework Reference&lt;/a&gt;, &lt;a class="md_compiled" href="https://developer.apple.com/reference/intentsui"&gt;Intents UI Framework Reference&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_2" class="h16"&gt;Proactive Suggestions 主动推荐&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;这玩意儿其实就是加强版的SiriKit逻辑的东西，也就是像演示里面地图上出现大众点评的那样的推荐。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;官方的说法是在地图，Siri，CarPlay，QuickType上会出现推荐，并且媒体播放类的还能出现在锁屏界面上，真是666。&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_3" class="h16"&gt;Messages&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;其实就是两点，一点是你可以自己开发iMassage的表情了，还有就是开发发送到iMassage的媒体块。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line"&gt;当然，对于表情包来说。。为什么不去开发微信的。。毕竟Massage在国内没什么生态圈，也就是拿来收垃圾短信用。。。。而另一点更甚。。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line"&gt;当然，既然有，那还是放上去吧。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;而且苹果都说了要建立个MessageStore专门装这些附加媒体app，想来还是有点搞头。&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;文档传送门:&lt;a class="md_compiled" href="https://developer.apple.com/reference/messages"&gt;Messages Framework Reference&lt;/a&gt;, &lt;a class="md_compiled" href="https://developer.apple.com/library/prerelease/content/documentation/General/Conceptual/ExtensibilityPG/index.html#//apple_ref/doc/uid/TP40014214"&gt;App Extension Programming Guide&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_4" class="h16"&gt;User Notifications&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;好可惜的是。。貌似API还没出来，找不到UserNotifications.framework的文档呢，只能按照官方的来猜了。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;基本上就是Notification类似Push的那种，带有了时间啊地点什么的特殊属性，以便于在收到通知后进行处理。而还包含的UI组件应该是对特殊类型通知的响应View，也就是官方给你个框，在里面自定义想要的东西。（苹果居心叵测想要什么都嵌入到系统里面啊简直）&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_5" class="h16"&gt;语音转换文字&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;Speech.framework这玩意儿也没上线，但是官方贴了一段代码(注意是Swift)&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_swift  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;recognizer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SFSpeechRecognizer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SFSpeechURLRecognitionRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;audioFileURL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;recognizer&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;recognitionTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
     &lt;span class="bp"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;bestTranscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formattedString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;说白了也就是能在你的App里面实现把语音转换为文字的技术，就不需要自己造轮子啦，不需要自己造轮子啦，造轮子啦。&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Speech ----- intent ----- Action ----- Response
   &lt;span class="se"&gt;\ &lt;/span&gt;          /&lt;span class="se"&gt;\ &lt;/span&gt;          &lt;span class="p"&gt;|&lt;/span&gt;           / &lt;span class="p"&gt;|&lt;/span&gt;
    &lt;span class="se"&gt;\ &lt;/span&gt;        /     &lt;span class="se"&gt;\ &lt;/span&gt;      &lt;span class="p"&gt;|&lt;/span&gt;        /    &lt;span class="p"&gt;|&lt;/span&gt;
     &lt;span class="se"&gt;\ &lt;/span&gt;      /         &lt;span class="se"&gt;\ &lt;/span&gt;   &lt;span class="p"&gt;|&lt;/span&gt;    /        &lt;span class="p"&gt;|&lt;/span&gt;
     Vocabulary         App Logic    User Interface
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;</summary>

  </entry>


  <entry>


    <title>Mac命令行环境配置备忘（常更）</title>

    <link href="http://blog.longfan.me/post/ios/2016-06-02"  rel="alternate"></link>

    <updated>2016-06-02T09:31:00Z</updated>
    <id>ios/2016-06-02</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;h1 id="toc_0" class="h16"&gt;Ruby gem&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;Mac 内建了Ruby， or (没有？)不管了。墙外忽略。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;Ruby gem 更换淘宝源，不换慢到死。&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ gem sources --add https://ruby.taobao.org/ --remove https://rubygems.org/
$ gem sources -l
*** CURRENT SOURCES ***

https://ruby.taobao.org
&lt;span class="c1"&gt;# 请确保只有 ruby.taobao.org&lt;/span&gt;
$ gem install rails
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_dom_embed md_line_start md_line_end"&gt;&lt;a class="md_compiled" href="https://ruby.taobao.org/"&gt;ruby.taobao.org/&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_1" class="h16"&gt;Home brew&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;虽然这货去面试被拒了，哈哈哈哈，也有今天，但是不可否认brew这个命令实在是无可替代啊.&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;/usr/bin/ruby -e &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;接下来的很多东西都有靠他来安装，当然，如果想自己配置环境，那就忽略吧。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_dom_embed md_line_end"&gt;&lt;a class="md_compiled" href="http://brew.sh/"&gt;brew.sh/&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_2" class="h16"&gt;iTerm2&lt;/h1&gt;&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ brew cask install iTerm
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;好了，有了一个比较屌的终端了，嗯，跟原生的比的话，见仁见智，开心就好。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_dom_embed md_line_end"&gt;&lt;a class="md_compiled" href="https://iterm2.com/"&gt;iterm2.com/&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_3" class="h16"&gt;zsh&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;厌倦了原生bash那种无聊无色的代码么？用zsh啊。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line"&gt;zsh什么最好？哪里有最好，最喜欢Oh-My-Zsh。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;*curl&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ sh -c &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;*wget&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_bash  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ sh -c &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;wget https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;接下来就能有了一个碉堡的可配置项了&lt;code&gt;$ ~/.zshrc&lt;/code&gt;&lt;/span&gt;
&lt;/p&gt;

&lt;h2 id="toc_4" class="h16"&gt;.zshrc配置&lt;/h2&gt;&lt;h1 id="toc_5" class="h16"&gt;Python&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;没有内建？没有内建？没有内建找brew。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;要Pyton3？找brew。&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_6" class="h16"&gt;CMake&lt;/h1&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;找brew&lt;/span&gt;
&lt;/p&gt;

&lt;h1 id="toc_7" class="h16"&gt;Vim&lt;/h1&gt;</summary>

  </entry>


  <entry>


    <title>UIImage的scale坑</title>

    <link href="http://blog.longfan.me/post/2016-02-19"  rel="alternate"></link>

    <updated>2016-02-19T07:53:00Z</updated>
    <id>2016-02-19</id>

    <author>
      <name>长电风扇</name>

    </author>
    <summary type="html">&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;在做Play+的图片剪切的时候遇到个神坑的问题，同样的剪切数据，同样样子的图，对本地图片的剪切效果就是跟对网络图片的剪切效果不一样，而且大概是iOS9.1左右时候出现的问题。简直百思不得其解，但是这种问题又不算小问题，不可能搁置一边，使劲跟了跟，跟到一个UIImage的神坑。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;UIImage有个属性叫做scale(废话)，官方文档给的描述是这样的:&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_objc  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;@property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nonatomic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;CGFloat&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;

&lt;span class="n"&gt;Discussion&lt;/span&gt;
&lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;whose&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;includes&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="mi"&gt;@2&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="n"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;also&lt;/span&gt; &lt;span class="n"&gt;specify&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;explicit&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;initializing&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;Core&lt;/span&gt; &lt;span class="n"&gt;Graphics&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;All&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;assumed&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;logical&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stored&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;dimensions&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;也就是说scale的来源有你从文件名读取的图片名字带@nx的那个n。要么就是从CGImage转换来的自己写的scale。也就是，这个方法：&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_objc  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;UIImage&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;imageWithCGImage:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CGImageRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;cgImage&lt;/span&gt; &lt;span class="nf"&gt;scale:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CGFloat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;scale&lt;/span&gt; &lt;span class="nf"&gt;orientation:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UIImageOrientation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;orientation&lt;/span&gt; &lt;span class="n"&gt;NS_AVAILABLE_IOS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;_0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start md_line_end"&gt;仔细看了看文件，跟scale有关的还有这几个方法：&lt;/span&gt;
&lt;/p&gt;

&lt;div class="codehilite code_lang_objc  highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="bp"&gt;UIImage&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;imageWithData:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;NSData&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="nf"&gt;scale:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CGFloat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;scale&lt;/span&gt; &lt;span class="n"&gt;NS_AVAILABLE_IOS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;_0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;UIImage&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;imageWithCIImage:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;CIImage&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ciImage&lt;/span&gt; &lt;span class="nf"&gt;scale:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CGFloat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;scale&lt;/span&gt; &lt;span class="nf"&gt;orientation:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UIImageOrientation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;orientation&lt;/span&gt; &lt;span class="n"&gt;NS_AVAILABLE_IOS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;_0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="kt"&gt;instancetype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;initWithData:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;NSData&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="nf"&gt;scale:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CGFloat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;scale&lt;/span&gt; &lt;span class="n"&gt;NS_AVAILABLE_IOS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;_0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;instancetype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;initWithCGImage:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CGImageRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;cgImage&lt;/span&gt; &lt;span class="nf"&gt;scale:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CGFloat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;scale&lt;/span&gt; &lt;span class="nf"&gt;orientation:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UIImageOrientation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;orientation&lt;/span&gt; &lt;span class="n"&gt;NS_AVAILABLE_IOS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;_0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;instancetype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;initWithCIImage:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;CIImage&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ciImage&lt;/span&gt; &lt;span class="nf"&gt;scale:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CGFloat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;scale&lt;/span&gt; &lt;span class="nf"&gt;orientation:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UIImageOrientation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;orientation&lt;/span&gt; &lt;span class="n"&gt;NS_AVAILABLE_IOS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;_0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;!--block_code_end--&gt;
&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_start"&gt;然后，然后在并没有使用这几个跟scale相关方法，奇怪的事情发生了。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;从手机相册直接导入的图片的scale如下&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_dom_embed md_line_with_image md_line_start"&gt;&lt;img class="md_compiled " src="/_image/2016-02-19/屏幕快照 2016-02-19 下午5.21.19.png" alt="" title="" &gt;&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line img_before only_img_before md_line_end"&gt;就是这张UIImage，上传到7牛服务器，再用SDWebImage下载成为一个新的UIImage的时候，scale变成了这样&lt;/span&gt;
&lt;/p&gt;


&lt;p class="md_block"&gt;
    &lt;span class="md_line md_line_dom_embed md_line_with_image md_line_start"&gt;&lt;img class="md_compiled " src="/_image/2016-02-19/屏幕快照 2016-02-19 下午5.20.31.png" alt="" title="" &gt;&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line img_before only_img_before"&gt;这就是导致同样的图片，同样的剪切，但是剪切出来的图片区域不一样的原因了。&lt;br /&gt;&lt;/span&gt;
    &lt;span class="md_line md_line_end"&gt;但是这个问题为什么会产生呢。&lt;/span&gt;
&lt;/p&gt;</summary>

  </entry>


</feed>