<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0">
  <channel>
    <title>IT瘾编程推荐</title>
    <link>https://itindex.net/tags/编程</link>
    <description>IT社区推荐资讯 - ITIndex.net</description>
    <language>zh</language>
    <copyright>https://itindex.net/</copyright>
    <generator>https://itindex.net/</generator>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>https://itindex.net/images/logo.gif</url>
      <title>IT社区推荐资讯 - ITIndex.net</title>
      <link>https://itindex.net/tags/编程</link>
    </image>
    <item>
      <title>前端安全编码规范</title>
      <link>https://itindex.net/detail/60986-%E5%89%8D%E7%AB%AF-%E5%AE%89%E5%85%A8-%E7%BC%96%E7%A0%81</link>
      <description>&lt;h2&gt;前端安全编码规范&lt;/h2&gt; &lt;h3&gt;前言&lt;/h3&gt; &lt;p&gt;随着互联网高速的发展，信息安全已经成为企业重点关注焦点之一，而前端又是引发安全问题的高危据点，所以，作为一个前端开发人员，需要了解前端的安全问题，以及如何去预防、修复安全漏洞。  &lt;br /&gt;下面就以前端可能受到的攻击方式为起点，讲解web中可能存在的安全漏洞以及如何去检测这些安全漏洞，如何去防范潜在的恶意攻击。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h4&gt;1. 跨站脚本攻击（Cross Sites Script）&lt;/h4&gt; &lt;p&gt;  &lt;strong&gt;   &lt;code&gt;跨站脚本攻击&lt;/code&gt;&lt;/strong&gt;，Cross Site Script（简称 CSS或）。指黑客通过“  &lt;strong&gt;   &lt;code&gt;HTML注入&lt;/code&gt;&lt;/strong&gt;”篡改了网页，插入了恶意的脚本（主要是  &lt;strong&gt;   &lt;code&gt;JavaScript脚本&lt;/code&gt;&lt;/strong&gt;），从而在用户浏览网页时，控制用户浏览器的一种攻击。&lt;/p&gt; &lt;p&gt;了解了什么是XSS，那你一定想知道，它有什么危害以及如何去防御  &lt;br /&gt;这里罗列一张列表：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;挂马&lt;/li&gt;  &lt;li&gt;盗取用户Cookie。&lt;/li&gt;  &lt;li&gt;钓鱼攻击，高级的钓鱼技巧。&lt;/li&gt;  &lt;li&gt;删除目标文章、恶意篡改数据、嫁祸。&lt;/li&gt;  &lt;li&gt;劫持用户Web行为，甚至进一步渗透内网。&lt;/li&gt;  &lt;li&gt;爆发Web2.0蠕虫。&lt;/li&gt;  &lt;li&gt;蠕虫式挂马攻击、刷广告、刷浏量、破坏网上数据&lt;/li&gt;  &lt;li&gt;其它安全问题&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;常见的跨站脚本攻击也可分为：反射型XSS、存储型XSS、DOM Based XSS  &lt;br /&gt;下面针对这三种常见的类型做具体的分析&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h6&gt;1.1 反射型XSS--也可被称为是HTML注入&lt;/h6&gt; &lt;pre&gt;  &lt;code&gt;反射型XSS，也称为&amp;quot;非持久型XSS&amp;quot;简单的把用户输入的数据&amp;quot;反射&amp;quot;给浏览器，即黑客往往需要诱使用户&amp;quot;点击&amp;quot;一个恶意链接攻击才能成功，用户通过点击这个恶意链接，攻击者可以成功获取用户隐私数据的一种方式。如：&amp;quot;盗取用户Cookie信息&amp;quot;、&amp;quot;破坏页面结构&amp;quot;、&amp;quot;重定向到其他网站&amp;quot;，盗取内网IP等。 &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;那么既然反射型XSS也可以是HTML注入，那么它注入的关键自然也就从前端的HTML页面开始下手：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;1. 用户能够与浏览器页面产生交互动作（输入搜索的关键词，点击按钮，点击链接等等），但这些都需要去诱使用户去操作，说起来容易，做起来难。
2. 用户输入的数据会被攻击方拼接出合适的html去执行恶意的js脚本，这样的过程就像是&amp;quot;一次反射&amp;quot;
&lt;/code&gt;&lt;/pre&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h6&gt;1.2 存储型XSS&lt;/h6&gt; &lt;pre&gt;  &lt;code&gt;存储型XSS，也称为&amp;quot;`持久型XSS`&amp;quot;，它与`反射型XSS`不同之处在于，它会将用户输入的数据&amp;quot;存储&amp;quot;在攻击方的服务器上，具有很强的&amp;quot;稳定性&amp;quot;。
例如：访问某黑客写下的一篇含有恶意JavaScript代码的博客文章，黑客把恶意脚本保存到服务端。
&lt;/code&gt;&lt;/pre&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h6&gt;1.3 DOM based XSS&lt;/h6&gt; &lt;pre&gt;  &lt;code&gt;从效果上来说，也是&amp;quot;反射型XSS&amp;quot;，单独划分出来，是因为其形成是通过修改页面的&amp;quot;DOM节点&amp;quot;形成的XSS。
例如：通过修改DOM节点上的绑定方法，用户无意间通过点击、输入等行为执行这些方法获取到用户的相关信息
&lt;/code&gt;&lt;/pre&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h6&gt;1.4 如何去检测是否存在XSS&lt;/h6&gt; &lt;p&gt;一般方法是，用户可以在有关键字输入搜索的地方输入  &lt;strong&gt;   &lt;code&gt;&amp;lt;script&amp;gt;alert(123)&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/strong&gt;后点击搜索，若弹框出现展示123，说明存在XSS漏洞，这就说明前端并没有对用户输入的内容过滤处理。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h6&gt;1.5 XSS的攻击方式&lt;/h6&gt; &lt;p&gt;1.Cookie劫持&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;通过伪装一些`图片和按钮`等，诱使用户对其操作，使网页执行了攻击者的恶意脚本，使攻击者能够获取当前用户的Cookie信息&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;2.构造GET和POST请求&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;若某攻击者想删除某网站的一篇文章，首先获得当前文章的id，然后通过使用脚本`插入图片`发送一个`GET请求`，或`构造表单`，`XMLHTTPRequest`发送`POST请求`以达到删除该文章的目的&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;3.XSS钓鱼&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;`钓鱼`这个词一般认识是起源于`社会工程学`，黑客使用这个这门学科的理念思想，在未授权不知情的情况下诱骗用户，并得到对方对方的姓名、年龄、邮箱账号、甚至是银行卡密码等私人信息。

比如：&amp;quot;某用户在某网站（已被攻击）上操作黑客伪造的一个登录框，当用户在登录框中输入了用户名（这里可能是身份证号等）和密码之后，将其信息上传至黑客的服务器上（该用户的信息就已经从该网站泄漏）&amp;quot;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;4.获取用户真实的IP地址&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;通过第三方软件获取，比如客户端安装了Java环境（JRE），则可通过调用`Java Applet`的接口获取客户端本地的IP地址
&lt;/code&gt;&lt;/pre&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h6&gt;1.6 XSS的防御方式&lt;/h6&gt; &lt;p&gt;1.HttpOnly&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;原理：浏览器禁止页面的Javascript访问带有HttpOnly属性的cookie。（实质解决的是：XSS后的cookie劫持攻击）如今已成为一种“标准”的做法

解决方案：
JavaEE给Cookie添加HttpOnly的方式为：
response.setHeader(&amp;quot;Set-Cookie&amp;quot;,&amp;quot;cookiename=value; Path=/;Domain=domainvalue;Max-Age=seconds;HTTPOnly&amp;quot;);
&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;2.输入检查（XSS Filter）&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;原理：让一些基于特殊字符的攻击失效。（常见的Web漏洞如XSS、SQLInjection等，都要求攻击者构造一些特殊字符）
* 输入检查的逻辑，必须在服务端实现，因为客户端的检查也是很容易被攻击者绕过，现有的普遍做法是两端都做同样的检查，客户端的检查可以阻挡大部分误操作的正常用户，从而节约服务器的资源。

解决方案：
检查是否包含&amp;quot;JavaScript&amp;quot;，&amp;quot;&amp;lt;script&amp;gt;&amp;lt;/script&amp;gt;&amp;quot;等敏感字符。以及对字符串中的&amp;lt;&amp;gt;:&amp;quot;&amp;amp;/&amp;apos;等特殊字符做处理&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;3.输出检查&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;原理：一般来说除了富文本输出之外，在变量输出到HTML页面时，使用编码或转义的方式来防御XSS攻击

解决方案：
*   针对HTML代码的编码方式：HtmlEncode
*   PHP：htmlentities()和htmlspecialchars()两个函数
*   Javascript：JavascriptEncode（需要使用&amp;quot;&amp;quot;对特殊字符进行转义，同时要求输出的变量必须在引号内部）
*   在URL的path（路径）或者search（参数）中输出，使用URLEncode&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;4.更严格的做法&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;除了数字和字母外的所有字符，都使用十六进制的方式进行编码&lt;/code&gt;&lt;/pre&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h4&gt;2. 跨站点请求伪造（Cross Sites Request Forgery）&lt;/h4&gt; &lt;p&gt;跨站点请求伪造，指利用用户身份操作用户账户的一种攻击方式，即攻击者诱使用户访问一个页面，就以该用户身份在第三方有害站点中执行了一次操作，泄露了用户的身份信息，接着攻击者就可以使用这个伪造的，但真实存在的身份信息，到某网站冒充用户执行恶意操作。&lt;/p&gt; &lt;p&gt;但是，攻击者只有预测到URL的所有参数与参数值，才能成功地伪造一个请求（当然了，他可以在安全站点里以自己的身份实际去操作一下，还是能拿到参数的）；反之，攻击者无法攻击成功&lt;/p&gt; &lt;p&gt;下图通俗解释什么是  &lt;strong&gt;   &lt;code&gt;CSRF&lt;/code&gt;&lt;/strong&gt;，又是如何给用户带来危害的  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVcH9s3" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;参考上图，我们可以总结，完成一次CSRF攻击，必须满足两个条件&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;用户登录受信任网站A，并且在本地生成Cookie&lt;/li&gt;  &lt;li&gt;在不登出网站A的情况下，访问有害网站B&lt;/li&gt;&lt;/ul&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h6&gt;2.1 CSRF的原理&lt;/h6&gt; &lt;pre&gt;  &lt;code&gt;CSRF攻击是攻击者利用**`用户身份`**操作用户账户的一种攻击方式

如电影速度与激情5中吉赛尔使用内裤获取巴西大佬指纹，最后成功使用伪造指纹的手法打开保险柜，CSRF只不过是网络上这个手法的实现。
&lt;/code&gt;&lt;/pre&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h6&gt;2.2 CSRF的攻击方式&lt;/h6&gt; &lt;p&gt;1.浏览器的Cookie策略&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;浏览器所持有的策略一般分为两种：
Session Cookie，临时Cookie。保存在浏览器进程的内存中，浏览器关闭了即失效。
Third-party Cookie，本地Cookie。服务器在Set-Cookie时指定了Expire Time。过期了本地Cookie失效，则网站会要求用户重新登录。
&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;code&gt;* 在浏览网站的过程中，即使浏览器打开了Tab页，Session Cookie都是有效的，因此发起CSRF攻击是可行的。&lt;/code&gt;&lt;/p&gt; &lt;p&gt;2.P3P头的副作用&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;&amp;quot;P3P Header&amp;quot;是 &amp;quot;W3C&amp;quot; 制定的一项关于隐私的标准，全称是 &amp;quot;The Platform for Privacy Preference&amp;quot;（隐私偏好平台）

如果网站返回给浏览器的 HTTP 头包含有 P3P 头，则在某种程度上来说，将允许 浏览器发送第三方 Cookie。在 IE 下即使是&amp;quot;&amp;lt;iframe&amp;gt;&amp;quot;、`&amp;lt;script&amp;gt;`等标签页将不再拦截第三方 Cookie 的发送。主要应用在类似广告等需要跨域访问的页面。
&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;3.GET，POST请求&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;* 这里有个误区
大多数 CSRF 攻击，都是通过 &amp;lt;img&amp;gt; 、 &amp;lt;iframe&amp;gt; 、 &amp;lt;script&amp;gt; 等带 src 属性的标签，这类标签只能发送一次 GET 请求，而不能发送 POST 请求，由此也有了认为 CSRF 攻击只能由 GET 请求发起的错误观点。

构造一个 POST 请求，只需要在一个不可见的iframe窗口中，构造一个form表单，然后使用JavaScript自动提交这个表单。那么整个自动提交表单的过程，对于用户来说就是不可见的。
&lt;/code&gt;&lt;/pre&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h6&gt;2.3 CSRF的防御方式&lt;/h6&gt; &lt;p&gt;1.验证码&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;原理：
CSRF攻击过程中，用户在不知情的情况下构造了网络请求，添加验证码后，强制用户必须与应用进行交互

*  优点：简洁而有效
*  缺点：网站不能给所有的操作都加上验证码&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;2.Referer Check&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;原理：
* 利用HTTP头中的Referer判断请求来源是否合法
* Referer首部包含了当前请求页面的来源页面的地址，一般情况下Referer的来源页就是发起请求的那个页面，如果是在iframe中发起的请求，那么对应的页面URL就是iframe的src

*  优点：简单易操作（只需要在最后给所有安全敏感的请求统一添加一个拦截器来检查Referer的值就行）
*  缺点：服务器并非什么时候都能取到Referer
        1.很多出于保护用户隐私的考虑，限制了Referer的发送。
        2.比如从HTTPS跳转到HTTP，出于安全的考虑，浏览器不会发送Referer&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;3.使用Anti CSRF Token&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;原理：把参数加密，或者使用一些随机数，从而让攻击者无法猜测到参数值，也就无法构造请求的 URL，也就无法发起 CSRF 攻击。

例子（增加token）：
*  比如一个删除操作的URL是：`http://host/path/delete?uesrname=abc&amp;amp;item=123`
*  保持原参数不变，新增一个参数Token，Token值是随机的，不可预测
*  http://host/path/delete?username=abc&amp;amp;item=123&amp;amp;token=[random(seed)]

*  优点：比检查Referer方法更安全，并且不涉及用户隐私
*  缺点：
        加密
        1. 加密后的URL非常难读，对用户非常不友好
        2. 加密的参数每次都在改变，导致用户无法对页面进行搜索
        3. 普通参数也会被加密或哈希，将会给DBA工作带来很大的困扰，因为数据分析常常需要用到参数的明文
        
        token
        1. 对所有的请求都添加Token比较困难&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;strong&gt;   &lt;code&gt;需要注意的点&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;Token需要足够随机，必须用足够安全的随机数生成算法&lt;/li&gt;  &lt;li&gt;Token应该为用户和服务器所共同持有，不能被第三方知晓&lt;/li&gt;  &lt;li&gt;Token可以放在用户的Session或者浏览器的Cookie中&lt;/li&gt;  &lt;li&gt;尽量把Token放在表单中，把敏感操作由GET改为POST，以form表单的形式提交，可以避免Token泄露（比如一个页面：   &lt;code&gt;http://host/path/manage?username=abc&amp;amp;token=[random]&lt;/code&gt;，在此页面用户需要在这个页面提交表单或者单击“删除”按钮，才能完成删除操作，在这种场景下，如果这个页面包含了一张攻击者能指定地址的图片   &lt;code&gt;&amp;lt;img src=&amp;quot;http://evil.com/notexist&amp;quot; /&amp;gt;&lt;/code&gt;，则这个页面地址会作为HTTP请求的Refer发送到evil.com的服务器上，从而导致Token泄露）&lt;/li&gt;&lt;/ol&gt; &lt;h6&gt;2.4 XSRF&lt;/h6&gt; &lt;p&gt;当网站同时存在XSS和CSRF漏洞时，XSS可以模拟客户端浏览器执行任意操作，在XSS攻击下，攻击者完全可以请求页面后，读取页面内容中的Token值，然后再构造出一个合法的请求&lt;/p&gt; &lt;h4&gt;3. 点击劫持（ClickJacking）&lt;/h4&gt; &lt;p&gt;点击劫持是一种视觉上的欺骗手段。攻击者使用一个透明的、不可见的iframe，覆盖在一个网页上，然后诱使用户在网页上进行操作，此时用户将在不知情的情况下点击透明的iframe页面。通过调整iframe页面的位置，可以诱使用户恰好点击在iframe页面的一些功能性按钮上。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;比如，程序员小王在访问A网页时，点击空白区域，浏览器却意外打开了xx新葡京赌场的页面，于是他在A网页打开控制台，在空白区域发现了一个透明的iframe，该iframe嵌入了一个第三方网页的URL&lt;/code&gt;&lt;/p&gt; &lt;h6&gt;3.1 点击劫持防御方式&lt;/h6&gt; &lt;pre&gt;  &lt;code&gt;1.X-Frame-Options HTTP响应头是用来给浏览器指示允许一个页面能否在`&amp;lt;frame&amp;gt;、&amp;lt;iframe&amp;gt;、&amp;lt;object&amp;gt;`中展现的标记

#### 有三个可选的值
1.  DENY：浏览器会拒绝当前页面加载任何frame页面（即使是相同域名的页面也不允许）
2.  SAMEORIGIN：允许加载frame页面，但是frame页面的地址只能为同源域名下的页面
3.  ALLOW-FROM：可以加载指定来源的frame页面（可以定义frame页面的地址）

2.禁止iframe的嵌套
if(window.top.location !== window.loaction){window.top.location === window.self.location}&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;4. 其他安全问题&lt;/h4&gt; &lt;pre&gt;  &lt;code&gt;4.1 跨域问题处理
    当服务端设置 &amp;apos;Access-Control-Allow-Origin&amp;apos; 时使用了通配符 &amp;quot;*&amp;quot;,允许来自任意域的跨域请求，这是极其危险的

4.2 postMessage 跨窗口传递信息
    postMessage 允许每一个 window（包括当前窗口、弹出窗口、iframes等）对象往其他的窗口发送文本消息，从而实现跨窗口的消息传递。并且这个功能不受同源策略限制。
    必要时，在接受窗口验证 Domain，甚至验证URL，以防止来自非法页面的消息。实际上是在代码上实现一次同源策略的验证过程。接受窗口对接口的信息进行安全检查。
    
4.3 Web Storage
    Web Storage 分为 Session Storage 和 Local Storage。
    虽然受同源策略的约束，但当存有敏感信息时，也可能会成为攻击的目标。
    
&lt;/code&gt;&lt;/pre&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h4&gt;5. 总结&lt;/h4&gt; &lt;ol&gt;  &lt;li&gt;谨慎用户输入信息，进行输入检查（客户端和服务端同时检查）&lt;/li&gt;  &lt;li&gt;在变量输出到HTML页面时，都应该进行编码或转义来预防XSS攻击&lt;/li&gt;  &lt;li&gt;该用验证码的时候一定要添上&lt;/li&gt;  &lt;li&gt;尽量在重要请求上添加Token参数，注意Token要足够随机，用足够安全的随机数生成算法&lt;/li&gt;&lt;/ol&gt; &lt;h4&gt;6. 参考文献&lt;/h4&gt; &lt;ol&gt;  &lt;li&gt;   &lt;a href="https://blog.csdn.net/weixin_41656968/article/details/81270071" rel="nofollow noreferrer"&gt;十大常见web漏洞及防范&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;   &lt;a href="https://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html" rel="nofollow noreferrer"&gt;hyddd&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;   &lt;a href="https://blog.csdn.net/xiaoxinshuaiga/article/details/80766369" rel="nofollow noreferrer"&gt;CSRF攻击与防御&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;   &lt;a href="https://cloud.tencent.com/developer/article/1136202" rel="nofollow noreferrer"&gt;浅谈前端安全&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;   &lt;a href="https://juejin.im/post/6844904024337022984" rel="nofollow noreferrer"&gt;前端安全&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>前端 编码规范 安全漏洞 前端工程化</category>
      <guid isPermaLink="true">https://itindex.net/detail/60986-%E5%89%8D%E7%AB%AF-%E5%AE%89%E5%85%A8-%E7%BC%96%E7%A0%81</guid>
      <pubDate>Thu, 29 Oct 2020 17:59:52 CST</pubDate>
    </item>
    <item>
      <title>富文本编辑器杂谈</title>
      <link>https://itindex.net/detail/58017-%E5%AF%8C%E6%96%87%E6%9C%AC-%E7%BC%96%E8%BE%91</link>
      <description>&lt;p&gt;在开发内容型网站的时候，少不了要接触富文本编辑器。对开发人员来说，全新开发一个富文本编辑器会耗费大量的时间在细节的调整和坑的处理上。在针对内部用户使用的系统中，普遍采用的方式是接入现成的开源编辑器，做简单的功能配置。而针对面向普通用户的系统，富文本编辑器往往是单独进行设计和开发的。&lt;/p&gt;
 &lt;h2&gt;传统的富文本编辑器&lt;/h2&gt;
 &lt;p&gt;在把一些功能较多，且可以配置功能项的编辑器称为传统编辑器，这类编辑器是最早出现的我们身边的，典型的代表有：百度的  &lt;a href="http://ueditor.baidu.com/website/"&gt;ueditor&lt;/a&gt;、  &lt;a href="https://ckeditor.com/"&gt;ckeditor 4&lt;/a&gt;和  &lt;a href="https://www.tinymce.com/"&gt;tinymce&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="95" src="https://www.biaodianfu.com/wp-content/uploads/2018/02/classic-wysiwyg-editor.png" width="814"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主要特点是功能强大，通常用在一些后台管理系统中。&lt;/p&gt;
 &lt;p&gt;其他比较推荐的编辑器有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://quilljs.com/"&gt;https://quilljs.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.wangeditor.com/"&gt;http://www.wangeditor.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://summernote.org/"&gt;https://summernote.org/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.froala.com/wysiwyg-editor"&gt;https://www.froala.com/wysiwyg-editor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://textbox.io/"&gt;https://textbox.io/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://alex-d.github.io/Trumbowyg/"&gt;https://alex-d.github.io/Trumbowyg/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://textangular.com/"&gt;http://textangular.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://kindeditor.net/"&gt;http://kindeditor.net/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://simditor.tower.im/"&gt;http://simditor.tower.im/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;传统的富文本编辑器虽然功能强大，但是也会带来一些问题，很多功能日常用不到，妨碍视线等。普遍的做法是通过配置掩藏一些不需要的功能。当然也有重新开发精简版的案例，如  &lt;a href="http://www.layui.com/demo/layedit.html"&gt;layedit&lt;/a&gt;。&lt;/p&gt;
 &lt;h2&gt;面向程序员的markdown编辑器&lt;/h2&gt;
 &lt;p&gt;Markdown 是一种轻量级的标记语言，可以用一些简单语法来表达一些富文本内容。确切的说Markdown是一种语法规则而不是编辑器。markdown主要的优点是针对熟悉语言的用户，输入效率会非常的高，由于使用前需要先学习一下语法规则，使用大部分只有程序员在使用，不太适合普通大众的需求。&lt;/p&gt;
 &lt;p&gt;虽然Markdown编辑器可提供一些可视化的按钮选择项目，但是接受度目前还不是非常的高。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="357" src="https://www.biaodianfu.com/wp-content/uploads/2018/02/markdown-editor.png" width="419"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;比较推荐的编辑器有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://github.com/benweet/stackedit"&gt;https://github.com/benweet/stackedit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://pandao.github.io/editor.md/"&gt;https://pandao.github.io/editor.md/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/sparksuite/simplemde-markdown-editor"&gt;https://github.com/sparksuite/simplemde-markdown-editor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/lepture/editor"&gt;https://github.com/lepture/editor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/nhnent/tui.editor"&gt;https://github.com/nhnent/tui.editor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;悬浮式富文本编辑器&lt;/h2&gt;
 &lt;p&gt;第一次见到悬浮式的富文本编辑器来自于  &lt;a href="https://medium.com/"&gt;medium&lt;/a&gt;，目前很多的悬浮式编辑器大都参考的是medium。主要特点是简单。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="222" src="https://www.biaodianfu.com/wp-content/uploads/2018/02/balloon-wysiwyg-editor.png" width="717"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;比较推荐的编辑器有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://github.com/sofish/pen"&gt;https://github.com/sofish/pen&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://michelson.github.io/Dante/"&gt;http://michelson.github.io/Dante/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/jakiestfu/Medium.js"&gt;https://github.com/jakiestfu/Medium.js&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/yabwe/medium-editor"&gt;https://github.com/yabwe/medium-editor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/brijeshb42/medium-draft"&gt;https://github.com/brijeshb42/medium-draft&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://editor.ory.am/"&gt;http://editor.ory.am/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://getcontenttools.com/"&gt;http://getcontenttools.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://alloyeditor.com/"&gt;https://alloyeditor.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://ckeditor.com/ckeditor-5-builds/"&gt;https://ckeditor.com/ckeditor-5-builds/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;呈现排版编辑器（微信公众号编辑器）&lt;/h2&gt;
 &lt;p&gt;由于微信公众号自带的编辑器功能太弱，很多第三方网站开发了针对微信公众号的排版编辑器。这类编辑器的主要功能是排版。这样的编辑器场景独特，普通的网站一般很少用到。&lt;/p&gt;
 &lt;p&gt;比较流行的有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="http://www.135editor.com/"&gt;http://www.135editor.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://ipaiban.com/bianji.jsp"&gt;http://ipaiban.com/bianji.jsp&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.wxeditor.com/"&gt;http://www.wxeditor.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://xiumi.us/"&gt;https://xiumi.us/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://editor.wxb.com/"&gt;https://editor.wxb.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://yiban.io/"&gt;https://yiban.io/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://edit.newrank.cn/"&gt;https://edit.newrank.cn/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.xmyeditor.com/"&gt;http://www.xmyeditor.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;个人比较喜欢的编辑器&lt;/h2&gt;
 &lt;p&gt;在个人看来做的比较好的编辑器有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;传统型：   &lt;a href="https://zhuanlan.zhihu.com/"&gt;知乎专栏&lt;/a&gt;编辑器（功能精简且实用，包括了对公式Tex的支持）&lt;/li&gt;
  &lt;li&gt;悬浮行：   &lt;a href="https://medium.com/"&gt;medium&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Markdown型：   &lt;a href="https://segmentfault.com/"&gt;segmentfault&lt;/a&gt;的编辑器&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;关于编辑器是否要支持手机操作，我觉得这是个假议题，手机并不是一个生成力工具，手机上输入文字的速度也非常的低，目前的富文本编辑器大都采用选中设置，在触摸屏的手机上，选中是一个非常繁琐的工作。所以手机端的文本操作只要纯文本即可。如果是针对程序员的站点，可支持markdowm。&lt;/p&gt;
 &lt;p&gt;The post   &lt;a href="https://www.biaodianfu.com/wysiwyg-editor.html" rel="nofollow"&gt;富文本编辑器杂谈&lt;/a&gt; appeared first on   &lt;a href="https://www.biaodianfu.com" rel="nofollow"&gt;标点符&lt;/a&gt;.&lt;/p&gt;
 &lt;div&gt;
  &lt;p&gt;Related posts:&lt;/p&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/editor.html" rel="bookmark" title="&amp;#24320;&amp;#28304;&amp;#22312;&amp;#32447;&amp;#32534;&amp;#36753;&amp;#22120;&amp;#25512;&amp;#33616;"&gt;开源在线编辑器推荐 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/weui.html" rel="bookmark" title="&amp;#24494;&amp;#20449;&amp;#23448;&amp;#26041;UI&amp;#24211;&amp;#65306;WeUI"&gt;微信官方UI库：WeUI &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/ace-admin.html" rel="bookmark" title="&amp;#31649;&amp;#29702;&amp;#21518;&amp;#21488;&amp;#27169;&amp;#26495;&amp;#25512;&amp;#33616; Ace Admin"&gt;管理后台模板推荐 Ace Admin &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;  &lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>程序开发 编辑器</category>
      <guid isPermaLink="true">https://itindex.net/detail/58017-%E5%AF%8C%E6%96%87%E6%9C%AC-%E7%BC%96%E8%BE%91</guid>
      <pubDate>Thu, 01 Feb 2018 19:12:22 CST</pubDate>
    </item>
    <item>
      <title>线上存储服务崩溃问题分析记录</title>
      <link>https://itindex.net/detail/57069-%E7%BA%BF%E4%B8%8A-%E6%9C%8D%E5%8A%A1-%E9%97%AE%E9%A2%98</link>
      <description>&lt;p&gt;上周我们的存储服务在某个线上项目频繁出现崩溃，花了几天的时间来查找解决该问题。在这里，将这个过程做一下记录。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;加入调试信息&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;由于问题在线上发生，较难重现，首先想到的是能不能加上更多的信息，在问题出现时提供更多的解决思路。&lt;/p&gt;
 &lt;p&gt;首先，我们的代码里，在捕获到进程退出的信号比如SIGABRT、SIGSEGV、SIGILL等信号时，会打印出主线程的堆栈，用于帮助我们发现问题。&lt;/p&gt;
 &lt;p&gt;但是在崩溃的几次情况中，打印出来的信息并不足以帮助我们解决问题，因为打印的崩溃堆栈只有主线程，猜测是不是在辅助线程中发生的异常，于是采取了两个策略：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;ulimit命令打开线上一台服务器的coredump，当再次有崩溃发生时有core文件产生，能够帮助发现问题。&lt;/li&gt;
  &lt;li&gt;加入了一些代码，用于在崩溃的时候同时也打印出所有辅助线程的堆栈信息。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;在做这两部分工作之后，再次发生崩溃的情况下，辅助线程的堆栈并无异常，core文件由于数据错乱也看不出来啥有用的信息来。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;复现问题&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;由于第一步工作受挫，接下来我的思路就在考虑怎么能在开发环境下复现这个问题。&lt;/p&gt;
 &lt;p&gt;我们的存储服务在其他项目上已经上线了有一段时间了，但是并没有出现类似的问题。那么，出现问题的项目，与其他已经上线的服务有啥不同，这里也许是一个突破口。&lt;/p&gt;
 &lt;p&gt;经过咨询业务方，该业务的特点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;单条数据大：有的数据可能有几KB，而之前的项目都只有几百字节。&lt;/li&gt;
  &lt;li&gt;读请求并发大，而其他业务是写请求远大于读请求。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;由于我们的存储服务兼容memcached协议，出现问题时也是以memcached协议进行访问的，所以此时我的考虑是找一个memcached压测工具，模拟前面的数据和请求特点来做模拟压测。&lt;/p&gt;
 &lt;p&gt;最后选择的是twitter出品的工具twemperf，其特点是可以指定写入缓存的数据范围，同时还可以指定请求的频率。&lt;/p&gt;
 &lt;p&gt;有了这个工具，首先尝试了往存储中写入大量数据量分布在4KB~10KB的数据，此时没有发现服务有core的情况出现。&lt;/p&gt;
 &lt;p&gt;然后，尝试构造大量的读请求，果然出现了core情况，重试了几次，都能稳定的重现问题了。&lt;/p&gt;
 &lt;p&gt;有了能稳定重现问题的办法，总算给问题的解决打开了一个口子。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;首次尝试&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;此时，可以正式的在代码中查找问题的原因了。&lt;/p&gt;
 &lt;p&gt;来大概说明一下该存储服务的架构：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;主线程负责接收客户端请求，并且进行解析。&lt;/li&gt;
  &lt;li&gt;如果是读请求，将分派给读请求处理线程，由这个线程与存储引擎库进行交互，查询数据。此时该线程数量配置为2。&lt;/li&gt;
  &lt;li&gt;存储引擎库负责存储落地到磁盘的数据，类似leveldb，只不过这部分是我们自己写的存储引擎。&lt;/li&gt;
  &lt;li&gt;在读线程从存储引擎中查询数据返回后，将把数据返回给主线程，由主线程负责应答客户端。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;  &lt;a href="http://www.codedump.info/wp-content/uploads/2017/06/server.png"&gt;   &lt;img alt="" height="300" src="http://www.codedump.info/wp-content/uploads/2017/06/server-279x300.png" title="server" width="279"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;在这几步中，第1和第4步是在主线程中进行的，第2和第3步是在读存储引擎线程中进行的。在这个过程中，如果同一个客户端有多个读请求，那么只有按照这四步在处理完毕一个读请求之后，才会继续从该客户端中取出下一个请求进行处理。&lt;/p&gt;
 &lt;p&gt;在几次重现问题的过程中，发现出错的都是在第2步和第4步中，该请求客户端的数据结构某些成员出现了错乱，即要访问的指针地址已经无效了，导致的错误。&lt;/p&gt;
 &lt;p&gt;指针无效，一般来说有两种可能性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;被无效地址覆盖了这个指针。&lt;/li&gt;
  &lt;li&gt;指针已经被释放的情况下继续使用。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;当时尝试把一些错误的指针地址打印出来，发现有几次都是是字符串“pcm”的16进制表示，当时在想这个特殊的字符串到底是什么，百思不得其解的时候，一位曾经使用过mcperf工具的同事，想起来mcperf做压测时的key就是”mcp”开头的，而因为是小端方式，所以如果使用这个类型的字符串，去覆盖指针，那么就变成了”pcm”。我们很快验证了这个说法，mcperf确实是以这个为前缀来写入数据的。&lt;/p&gt;
 &lt;p&gt;此时，猜测问题的原因在于：当读存储引擎线程去访问存储引擎时，某些错误导致从存储引擎读出来的数据，将客户端请求数据写乱，从而导致了崩溃。&lt;/p&gt;
 &lt;p&gt;由于同时有两个读存储引擎的线程，猜测这里是不是因为多线程访问出了问题，导致的错误呢？&lt;/p&gt;
 &lt;p&gt;为了验证这个问题，最简单的办法就是将线程数量改成1，重新用mcperf试了几次，确实没有再次出现问题。此时已经是周五，我们缓了一口气，打算以此修改暂时上测试环境利用周末的时间观察一下情况。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;柳暗花明&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;前面提到过，猜测问题出现的原因，是多线程访问存储引擎时将某个数据写错乱了，导致其中的指针无效。&lt;/p&gt;
 &lt;p&gt;clang和gcc 4.8有对应的编译参数，可以用来检测内存错误的写操作，即Address Sanitizer工具。为了兼容线上比较老的系统，之前我们的服务都是在gcc 4.1的环境下进行编译的，为了使用这个工具，首先需要折腾到满足gcc版本号大于4.8的系统上进行编译。&lt;/p&gt;
 &lt;p&gt;然而，在折腾编译并且运行后，同样使用mcperf的情况下，并不能看到有内存错误覆盖写的提示，我尝试了多次都没有看到。难道是工具没有起作用？&lt;/p&gt;
 &lt;p&gt;为了验证该工具的作用，我简单在出错代码的前面加入了一段肯定有问题的代码，比如：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;char a[100] = {‘0′};&lt;/p&gt;
  &lt;p&gt;a[100] = ‘1′;&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;而在加入这段有问题的代码之后再次运行，就能看到编译器对这段代码的提示。可见，Address Sanitizer工具是起作用的。那么，前面的过程中没有看到问题，只能说明一个问题：并没有内存错误写的情况发生。&lt;/p&gt;
 &lt;p&gt;此时想到另一个可能，就是有没有可能是多线程在没有保护的情况下访问了某段数据导致的问题？&lt;/p&gt;
 &lt;p&gt;gcc同样也有类似的工具来检查这类错误，即Thread Sanitizer工具。然而，在给项目Makefile加入该编译参数后，程序一运行就退出了，根本看不出什么有用的信息来。&lt;/p&gt;
 &lt;p&gt;此时想到的另一个工具是valgrind。大多数时候，valgrind只是用来做内存泄露检测的，其实它也可以用来做线程数据竞争的检查，使用参数 –tool=helgrind 即可。使用valgrind之后，打印出来疑似有问题的代码如下：&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://www.codedump.info/wp-content/uploads/2017/06/valgrind.png"&gt;   &lt;img alt="" height="171" src="http://www.codedump.info/wp-content/uploads/2017/06/valgrind-300x171.png" title="valgrind" width="300"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;到了这里，猜测问题的原因就是由于多线程访问数据导致的错误。&lt;/p&gt;
 &lt;p&gt;因为有多个处理读请求数据的线程，首先猜测的是不是某些错误的处理，导致了可以在同一时间多个线程都操作该请求客户端的数据。但是通过review代码，发现这部分处理是没有问题的，另外在访问存储引擎查询数据时，入口处也确实进行了加锁的操作。&lt;/p&gt;
 &lt;p&gt;为了验证我的观点，我在每个请求客户端结构体中，新增了一个数据用于保存线程id，这个线程id在进入读线程中设置，而从读线程处理完毕转发给主线程之前重置为0。有了这个数据，在每次请求从主线程进入读线程中，都会判断一下这个值是不是非0，如果有非0的值那么说明此时有另外的读线程在进行处理。然而也并没有看到有异常的情况出现。&lt;/p&gt;
 &lt;p&gt;所以，这一步的猜测，即猜测是由于多线程操作客户端数据导致的问题，看起来方向是错误的了。而之前在测试环境上修改了读线程数为1的版本，也出现了崩溃。所以，问题的原因并不是由多个读线程导致的。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;终见曙光&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;前面提到过，目前能确定的是，与客户端连接相关的指针无效了，而一个指针无效，一般来说是两种可能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;被无效地址覆盖了这个指针。&lt;/li&gt;
  &lt;li&gt;指针已经被释放的情况下继续使用。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;既然前面验证了第一个方向，不存在多线程覆盖该指针数据导致无效的情况，那么接下来就看看是否存在已经释放的情况下继续使用指针而导致了问题。&lt;/p&gt;
 &lt;p&gt;为了查找释放该指针的情况，首先我找到完成这个释放操作的函数，查找所有可能调用该函数的地方。发现有十几处，每一处都看有点繁琐，为了辅助调试，修改了函数的实现，新增传入一个int型参数，每个调用者传入不同的参数以此来区分不同的调用者，同时在函数内将被释放的指针以及传入的参数都打印出来。&lt;/p&gt;
 &lt;p&gt;释放时的日志中有了指针，就可以与core的情况下与出错时的指针做对比，知道是哪个指针出的问题；而有了每个调用者传入的参数，又可以定位到这个出错的指针在释放时是被哪里调用了。&lt;/p&gt;
 &lt;p&gt;加上调试信息之后，又跑了几次都在同一个地方调用，我又重新review了一下这部分的代码逻辑，终于定位到了问题真实的原因。&lt;/p&gt;
 &lt;p&gt;一个连接，可能会一次性的从同一个客户端接收到多个请求，即所谓的“pipeline”方式：客户端在上一次请求还没有应答的情况下，继续往这个链接中发生请求数据。&lt;/p&gt;
 &lt;p&gt;而主线程的处理逻辑大体是这样的：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;while true:&lt;/p&gt;
  &lt;p&gt;如果客户端可写：&lt;/p&gt;
  &lt;p&gt;应答客户端请求&lt;/p&gt;
  &lt;p&gt;如果应答失败：&lt;/p&gt;
  &lt;p&gt;关闭客户端连接&lt;/p&gt;
  &lt;p&gt;如果客户端有未处理的请求：&lt;/p&gt;
  &lt;p&gt;读客户端的请求数据进行处理&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;这里的问题的时间序列大概是这样的，假设当前一个链接同时存在A、B两个请求：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;主线程已经处理了A请求，但是因为此时客户端还不可写，因此应答客户端，继续处理下一个B请求，将请求数据放到辅助线程中进行处理。&lt;/li&gt;
  &lt;li&gt;epoll通知客户端可写，主线程应答A请求的回复给客户端，但是应答出错，那么就会关闭这个连接。这种通知可写，又写失败的请求，大多发生在客户端关闭了连接时。&lt;/li&gt;
  &lt;li&gt;此时从辅助线程处理的B请求已经处理完毕了，辅助线程将处理结果告知主线程，主线程准备做下一步的处理时，第二步中已经关闭了连接，于是出错了。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;这个问题出现的临界条件是：请求并发量大，同时应用采用了使用pipeline方式的客户端，而在某些极端的情况下，比如同一个客户端已经堆积了很多请求在服务器的pipeline队列中，此时服务器处理应答也慢，导致客户度超时关闭了连接，而服务器主线程如果在这种情况下继续处理堆积的请求，就可能访问已经释放的连接对象导致出错。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;找到能稳定重现问题的办法很重要。一般来说，首先应该思考，同一个服务在别的业务下面没有出现问题，在另一个业务上出现了问题，那么以存储类服务来看，首先应该考虑环境以及数据有什么区别，从这里找突破口来重现问题。&lt;/li&gt;
  &lt;li&gt;需要熟悉使用各种工具。以C系列的服务器来说，出现问题大部分就是指针无效、内存泄露等，针对这些常用的工具就是Address Sanitizer、&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;valgrind等。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;大胆假设、小心求证。跟进这种复杂的bug时，最好不要在一旁呆着空想，因为经常疑难的问题是在某些临界条件下出现的，就是同一段代码正常情况下是没有问题的，这些临界条件才触发的问题。因此，有很多看上去觉得没问题的地方，在临界条件下就成了问题。此时空想是解决不了问题的，需要动手来做一些实验、加一些调试代码和日志等，来验证你的猜测对不对。常见的临界条件有：线程调度导致的错误，由于操作系统内核调度线程对应用程序而言不可知，即同样的代码，先调度线程A执行再调度线程B执行，和反之的情况有截然不同的效果；另外数据也有可能导致不同的临界条件，这个需要具体看不同的业务数据都有哪些特点来具体分析。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>C++ 网络编程</category>
      <guid isPermaLink="true">https://itindex.net/detail/57069-%E7%BA%BF%E4%B8%8A-%E6%9C%8D%E5%8A%A1-%E9%97%AE%E9%A2%98</guid>
      <pubDate>Mon, 19 Jun 2017 16:59:26 CST</pubDate>
    </item>
    <item>
      <title>浅谈移动应用的跨平台开发工具（Xamarin和React Native）</title>
      <link>https://itindex.net/detail/55657-%E7%A7%BB%E5%8A%A8%E5%BA%94%E7%94%A8-%E8%B7%A8%E5%B9%B3%E5%8F%B0-%E5%BC%80%E5%8F%91</link>
      <description>&lt;p&gt;谈移动应用的跨平台开发不能不提HTML5，PhoneGap和Sencha等平台一直致力于使用HTML5技术来开发跨平台的移动应用，现在看来这个方向基本算是失败的，基于HTML5的移动应用在用户体验上与原生应用仍然存在着明显的差距。&lt;/p&gt;
 &lt;p&gt;与上述HTML5平台不同，Xamarin和React Native通过各自的方式来实现跨平台。Xamarin基于Mono框架将C#代码编译为原生平台代码，React Native则是在UI主线程之外运行一个JavaScript线程，两者呈现给用户的都是原生体验。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="2in1" height="160" src="http://coolshell.cn//wp-content/uploads/2016/06/2in1.png" width="600"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;笔者恰巧两个平台都各使用过一段时间，在这里就抛砖引玉，分享一下个人观点。对于资源有限的创业团队，如果熟悉JavaScript，使用React Native再加上React，Redux等技术可以实现移动端、Web端、和Service端整套系统的开发，还可以重用一部分代码（比如Reducer和Action中的业务逻辑，以及通用的JavaScript组件代码），React Native也非常适合快速原型的开发。对于实力相对雄厚的大中型公司，如果已经在使用Microsoft的.Net技术，并且拥有成体系的系统架构，那么Xamarin或许是一个更好的选择，架构设计得好的话在代码重用方面并不逊于React Native。&lt;/p&gt;
 &lt;p&gt;下面从几个方面说一说两者各自的优缺点：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;从编程语言的角度来说，C#和JavaScript都是成熟的主流编程语言，都有丰富的第三方库和强大的社区支持。两种语言都能够实现从前端一直到后端的整套方案。&lt;/li&gt;
&lt;/ul&gt;
 &lt;ul&gt;
  &lt;li&gt;从开发工具的角度来说，Xamarin Studio的表现只能说刚刚及格，有种和Xamarin整个产品线不在一个水平的感觉，特别是重构和界面可视化编辑等方面还有很大的改善空间，并且在版本升级中经常会引入新的BUG，让笔者多少有点患上了升级恐惧症。React Native本身没有IDE，开发人员可以选择自己熟悉的JavaScript IDE，比如：IntelliJ等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;ul&gt;
  &lt;li&gt;从第三方库的角度来说，Xamarin的第三方库给人一种不多不少、刚好够用的感觉。在IDE中集成了Xamarin Component Store以后，第三方库的数量质量都有了提升，开发人员使用起来也非常方便。如果遇到特殊情况需要自己开发或者绑定（binding）原生代码库时可能会比较麻烦一些。React Native则完全依赖于JavaScript社区，NPM和GitHub，在需要自行开发和桥接（bridging）原生代码库时个人觉得比Xamarin容易一些。&lt;/li&gt;
&lt;/ul&gt;
 &lt;ul&gt;
  &lt;li&gt;价格方面，Xamarin有免费版本，但在应用包尺寸上有限制。对于企业级开发最好还是选择它的Enterprise License，虽然价格不菲，但是可以获得技术支持和使用平台的其他产品（如：Xamarin.Forms和Xamarin Test Cloud）。React Native则是完全免费的。&lt;/li&gt;
&lt;/ul&gt;
 &lt;ul&gt;
  &lt;li&gt;至于学习难度，很多人对JavaScript缺乏信心，觉得这门语言很难掌握和用好，而C#和Java则相对容易安全得多。这里笔者推荐图灵的   &lt;a href="http://www.ituring.com.cn/search?q=%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript&amp;type="&gt;《你不知道的JavaScript》系列&lt;/a&gt;，看过之后也许能够改变这一看法。除了JavaScript语言，React Native还需要掌握Facebook的React框架，它是React Native的核心。Xamarin要求掌握C#以及iOS和Android开发的相关知识，虽然使用React Native并不一定要求会iOS和Android开发，但是对于移动应用开发者来说，无论使用什么工具、怎样跨平台，了解各个平台的架构设计还是非常必要的。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;下面是对两者各方面的一个总结：&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://coolshell.cn/?attachment_id=17367" rel="1"&gt;   &lt;img alt="" height="365" src="http://coolshell.cn//wp-content/uploads/2016/06/01udjPy1m1I1-1024x623.jpg" width="600"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;不足和纰漏之处还望各位不吝赐教，欢迎交流讨论。
&lt;/p&gt; &lt;p align="center"&gt;  &lt;img src="http://coolshell.cn//wp-content/uploads/2009/04/qrcode_for_gh_dd9d8c843f20_860-300x300.jpg"&gt;&lt;/img&gt;  &lt;br /&gt;欢迎关注CoolShell微信公众账号&lt;/p&gt;
 &lt;div&gt;
  &lt;p align="center"&gt;   &lt;strong&gt;（转载本站文章请注明作者和出处     &lt;a href="http://coolshell.cn/"&gt;酷 壳 – CoolShell.cn&lt;/a&gt; ，请勿用于任何商业用途）&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;
 &lt;div&gt;——===   &lt;strong&gt;访问    &lt;a href="http://coolshell.cn/404/" target="_blank"&gt;酷壳404页面&lt;/a&gt; 寻找遗失儿童。&lt;/strong&gt; ===——&lt;/div&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;h3&gt;相关文章&lt;/h3&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;small&gt;2014年12月15日&lt;/small&gt;      &lt;a href="http://coolshell.cn/articles/12225.html"&gt;DHH 谈混合移动应用开发&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;small&gt;2014年11月26日&lt;/small&gt;      &lt;a href="http://coolshell.cn/articles/12136.html"&gt;Google Inbox如何跨平台重用代码？&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;small&gt;2015年04月14日&lt;/small&gt;      &lt;a href="http://coolshell.cn/articles/17066.html"&gt;关于移动端的钓鱼式攻击&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;small&gt;2011年04月06日&lt;/small&gt;      &lt;a href="http://coolshell.cn/articles/4220.html"&gt;一些有意思的文章和资源&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;small&gt;2010年10月09日&lt;/small&gt;      &lt;a href="http://coolshell.cn/articles/3083.html"&gt;三个教程&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;small&gt;2011年08月02日&lt;/small&gt;      &lt;a href="http://coolshell.cn/articles/5089.html"&gt;10个必需的iOS开发工具和资源&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;small&gt;2011年01月24日&lt;/small&gt;      &lt;a href="http://coolshell.cn/articles/3549.html"&gt;Android将允许纯C/C++开发应用&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;small&gt;2013年12月03日&lt;/small&gt;      &lt;a href="http://coolshell.cn/articles/10739.html"&gt;Lua简明教程&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>技术读物 编程工具 Android C++ Facebook</category>
      <guid isPermaLink="true">https://itindex.net/detail/55657-%E7%A7%BB%E5%8A%A8%E5%BA%94%E7%94%A8-%E8%B7%A8%E5%B9%B3%E5%8F%B0-%E5%BC%80%E5%8F%91</guid>
      <pubDate>Mon, 06 Jun 2016 12:35:26 CST</pubDate>
    </item>
    <item>
      <title>然而大部分期权并没有什么用</title>
      <link>https://itindex.net/detail/54343-%E7%84%B6%E8%80%8C-%E5%A4%A7%E9%83%A8-%E6%9C%9F%E6%9D%83</link>
      <description>&lt;p&gt;　　最近有个用户拿到了Uber的offer，其中包含期权，他看不懂便向我咨询期权的细节和意义。我的观点是：大部分的期权并没有什么卵用。&lt;/p&gt; &lt;h3&gt;　　期权激励的初心&lt;/h3&gt; &lt;p&gt;　　其实期权是有正向作用的，让我们从目标谈起：&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#28982;&amp;#32780;&amp;#22823;&amp;#37096;&amp;#20998;&amp;#26399;&amp;#26435;&amp;#24182;&amp;#27809;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#29992;" src="http://download.williamlong.info/upload/4359_1.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;　　假设一家公司的目标是1，团队会形成分工来拆解目标，比如研发团队的目标可能是1.1，而一个普通程序员被分配到的目标可能是1.1.1。如果只有基本薪资，程序员认为出色完成1.1.1就完成了工作，因为这是全部目标。&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#28982;&amp;#32780;&amp;#22823;&amp;#37096;&amp;#20998;&amp;#26399;&amp;#26435;&amp;#24182;&amp;#27809;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#29992;" src="http://download.williamlong.info/upload/4359_2.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;　　但如果采用高薪资结合期权的激励方式，大家会分享到公司的成长。员工会向上寻找目标，完成整个部门的目标，甚至整个公司的目标也义不容辞。在这个过程中，员工不仅与公司一起快速成长，自身能力还得到了提升，良好的激励方式会形成一个双赢的局面。&lt;/p&gt; &lt;p&gt;　　就像 Fenng（冯大辉）虽然是技术负责人，但是看看他的社交媒体，满屏尽是「丁香园产品推广」——这或许也是Fenng大大给自己设定的目标吧！&lt;/p&gt; &lt;p&gt;　　过去的互联网公司在处理「期权」这件事上，常出现这几种现象&lt;/p&gt; &lt;h4&gt;　　A、初创公司画饼忽悠&lt;/h4&gt; &lt;p&gt;　　A创业公司，招人时老板总谈降薪拿期权，爱招BAT背景的员工，口头禅是「员工要靠伴随公司成长来实现自我价值」。某员工降薪拿期权进入A公司，实际能力远高于offer岗位需求。工作了1年多后，他发现公司的成长完全不及预期，自己一直在做一些技术能力无法成长的事。失望之下，他试探了下外面的机会，发现世界变化很快，同类型的职位收到的薪资竟然是现公司的2~3倍。&lt;/p&gt; &lt;p&gt;　　B、成长型公司打土豪易、分田地难&lt;/p&gt; &lt;p&gt;　　B公司，国内风光一时的互联网上市公司，曾经期权是他们offer中重要的一环，某年12月IPO之夜，全公司员工们举杯、狂欢。然而现实很骨感，随着原先许诺好的期权变成了18：1换股，早期员工也失去了憧憬。&lt;/p&gt; &lt;p&gt;　　C、上市公司的成熟分配机制&lt;/p&gt; &lt;p&gt;　　阿里、百度、奇虎，为典型。期权被视为一种长期激励，而非利益捆绑。对优秀员工的激励方式是高薪资+部分期权。拿百度举例，T5工程师Package一般为30万/年，其中12-20%为期权；T9工程师Package一般为100~300万/年，40%左右为期权；由于已上市，兑现相对容易。&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#28982;&amp;#32780;&amp;#22823;&amp;#37096;&amp;#20998;&amp;#26399;&amp;#26435;&amp;#24182;&amp;#27809;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#29992;" src="http://download.williamlong.info/upload/4359_3.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h3&gt;　　那么，为何会出现AB式「反人性」的激励方式？&lt;/h3&gt; &lt;p&gt;　　我想，A公司的老板是这么想的：&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#28982;&amp;#32780;&amp;#22823;&amp;#37096;&amp;#20998;&amp;#26399;&amp;#26435;&amp;#24182;&amp;#27809;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#29992;" src="http://download.williamlong.info/upload/4359_4.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;　　然并卵，员工的真实心理是这样的：&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#28982;&amp;#32780;&amp;#22823;&amp;#37096;&amp;#20998;&amp;#26399;&amp;#26435;&amp;#24182;&amp;#27809;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#29992;" src="http://download.williamlong.info/upload/4359_5.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;　　我太理解这种态度了，这种想法也是很合理的：期权到最后套现，是一个漫长不确定的路，如果我失去该得的薪资和奖金，时间成本和机会成本的损失是划不来的。&lt;/p&gt; &lt;p&gt;　　对于B公司，老板的心理活动也许是这样的：&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#28982;&amp;#32780;&amp;#22823;&amp;#37096;&amp;#20998;&amp;#26399;&amp;#26435;&amp;#24182;&amp;#27809;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#29992;" src="http://download.williamlong.info/upload/4359_6.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;　　B类公司过去也有成功者，分布在传统行业居多，他们的行业竞争并没有互联网领域激烈，让老板误认为「我就是那么牛逼」。对于「老二非死不可」的互联网行业，公司出现此类现象，更要警惕。&lt;/p&gt; &lt;h3&gt;　　So，普通员工/程序员如何辨别靠谱的互联网公司？&lt;/h3&gt; &lt;p&gt;　　在期权这件事上，不同公司激励的方法、时间点均不相同，但靠谱的还是有几点共性：&lt;/p&gt; &lt;p&gt;　　1、成功并长久的企业都会提供期权激励&lt;/p&gt; &lt;p&gt;　　星巴克作为行业内绝对的利润垄断企业，期权激励应该也是星巴克成功的秘诀之一。&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#28982;&amp;#32780;&amp;#22823;&amp;#37096;&amp;#20998;&amp;#26399;&amp;#26435;&amp;#24182;&amp;#27809;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#29992;" src="http://download.williamlong.info/upload/4359_7.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;　　2、对于普通员工，期权建立在合理薪资的基础上&lt;/p&gt; &lt;p&gt;　　对于创业团队，期权+低工资的组合是很难招来靠谱的Team Leader的，用期权压低薪资的公司不太靠谱。&lt;/p&gt; &lt;p&gt;　　3、期权激励后置且大方&lt;/p&gt; &lt;p&gt;　　对比魅族和小米，我们可以发现与魅族不同的是，小米从第一天就很重视股权激励。留出足够的期权池，找到靠谱的团队，慷慨地配以股权或期权，是小米的特点。&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#28982;&amp;#32780;&amp;#22823;&amp;#37096;&amp;#20998;&amp;#26399;&amp;#26435;&amp;#24182;&amp;#27809;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#29992;" src="http://download.williamlong.info/upload/4359_8.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;小米合伙人构成图 | 杜国栋&lt;/p&gt; &lt;h3&gt;　　最后，我想说，&lt;/h3&gt; &lt;p&gt;　　如果能够获得必要的信息，同时自上而下地看问题，选择其实可以相对理性。但大部分情况下，你选择的不过是运气。若你斟酌了很久还在纠结，不如跟着第一感觉走。&lt;/p&gt; &lt;blockquote&gt;我知道现在要做出正确选择，  &lt;br /&gt;因为我不能再错下去。  &lt;br /&gt;我已等待一整夜，  &lt;br /&gt;为你等待一整夜。&lt;/blockquote&gt; &lt;p&gt;　　—— Stronger | 美国饶舌歌手/唱片制作人 Kanye West&lt;/p&gt; &lt;p&gt;　　来源：投稿，作者：贾智凡（  &lt;a href="http://www.100offer.com/join/charge?utm_source=charge&amp;utm_medium=essay&amp;utm_campaign=yueguangboke&amp;utm_content=0916qiquan" rel="nofollow" target="_blank"&gt;100offer CEO&lt;/a&gt;）&lt;/p&gt; &lt;p&gt;  &lt;a href="http://www.williamlong.info/archives/4359.html" target="_blank"&gt;评论《然而大部分期权并没有什么用》的内容...&lt;/a&gt;&lt;/p&gt; &lt;h3&gt;相关文章:&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/4233.html"&gt;这些年，这些挖掘机算法，这些反思&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/4223.html"&gt;谷歌推出全新Android开发框架Sky让App更流畅&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/4142.html"&gt;Google宣布淘汰SPDY&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/3925.html"&gt;打造有风格的团队&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/3806.html"&gt;关于Nginx支持.htaccess的分析&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt; &lt;br /&gt;微博： &lt;a href="http://weibo.com/williamlong"&gt;新浪微博&lt;/a&gt; - 微信公众号：williamlonginfo  &lt;br /&gt;月光博客投稿信箱：williamlong.info(at)gmail.com &lt;br /&gt;Created by William Long www.williamlong.info &lt;br /&gt; &lt;img alt="&amp;#26376;&amp;#20809;&amp;#21338;&amp;#23458;" src="http://www.williamlong.info/images/qrcode.jpg"&gt;&lt;/img&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程开发</category>
      <guid isPermaLink="true">https://itindex.net/detail/54343-%E7%84%B6%E8%80%8C-%E5%A4%A7%E9%83%A8-%E6%9C%9F%E6%9D%83</guid>
      <pubDate>Wed, 16 Sep 2015 22:43:19 CST</pubDate>
    </item>
    <item>
      <title>46 个非常有用的 PHP 代码片段</title>
      <link>https://itindex.net/detail/54328-php-%E4%BB%A3%E7%A0%81-%E7%89%87%E6%AE%B5</link>
      <description>&lt;p&gt;在编写代码的时候有个神奇的工具总是好的！下面这里收集了 40+ PHP 代码片段，可以帮助你开发 PHP 项目。&lt;/p&gt;
 &lt;p&gt;这些 PHP 片段对于 PHP 初学者也非常有帮助，非常容易学习，让我们开始学习吧～&lt;/p&gt;
 &lt;h3&gt;1. 发送 SMS&lt;/h3&gt;
 &lt;p&gt;在开发 Web 或者移动应用的时候，经常会遇到需要发送 SMS 给用户，或者因为登录原因，或者是为了发送信息。下面的 PHP 代码就实现了发送 SMS 的功能。&lt;/p&gt;
 &lt;p&gt;为了使用任何的语言发送 SMS，需要一个 SMS gateway。大部分的 SMS 会提供一个 API，这里是使用 MSG91 作为 SMS gateway。&lt;/p&gt;
 &lt;pre&gt;function send_sms($mobile,$msg)
{
$authKey = &amp;quot;XXXXXXXXXXX&amp;quot;;
date_default_timezone_set(&amp;quot;Asia/Kolkata&amp;quot;);
$date = strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;);
//Multiple mobiles numbers separated by comma
$mobileNumber = $mobile;
 
//Sender ID,While using route4 sender id should be 6 characters long.
$senderId = &amp;quot;IKOONK&amp;quot;;
 
//Your message to send, Add URL encoding here.
$message = urlencode($msg);
 
//Define route 
$route = &amp;quot;template&amp;quot;;
//Prepare you post parameters
$postData = array(
    &amp;apos;authkey&amp;apos; =&amp;gt; $authKey,
    &amp;apos;mobiles&amp;apos; =&amp;gt; $mobileNumber,
    &amp;apos;message&amp;apos; =&amp;gt; $message,
    &amp;apos;sender&amp;apos; =&amp;gt; $senderId,
    &amp;apos;route&amp;apos; =&amp;gt; $route
);
 
//API URL
$url=&amp;quot;https://control.msg91.com/sendhttp.php&amp;quot;;
 
// init the resource
$ch = curl_init();
curl_setopt_array($ch, array(
    CURLOPT_URL =&amp;gt; $url,
    CURLOPT_RETURNTRANSFER =&amp;gt; true,
    CURLOPT_POST =&amp;gt; true,
    CURLOPT_POSTFIELDS =&amp;gt; $postData
    //,CURLOPT_FOLLOWLOCATION =&amp;gt; true
));
 
 
//Ignore SSL certificate verification
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
 
 
//get response
$output = curl_exec($ch);
//Print error if any
if(curl_errno($ch))
{
    echo &amp;apos;error:&amp;apos; . curl_error($ch);
}
 
curl_close($ch);
}&lt;/pre&gt;
 &lt;p&gt;其中“$authKey = “XXXXXXXXXXX”;”需要你输入你的密码，“$senderId = “IKOONK”;”需要你输入你的 SenderID。当输入移动号码的时候需要指定国家代码 (比如，美国是 1，印度是 91 )。&lt;/p&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$message = &amp;quot;Hello World&amp;quot;;
$mobile = &amp;quot;918112998787&amp;quot;;
send_sms($mobile,$message);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;2. 使用 mandrill 发送邮件&lt;/h3&gt;
 &lt;p&gt;Mandrill 是一款强大的 SMTP 提供器。开发者倾向于使用一个第三方 SMTP provider 来获取更好的收件交付。&lt;/p&gt;
 &lt;p&gt;下面的函数中，你需要把 “Mandrill.php” 放在同一个文件夹，作为 PHP 文件，这样就可以使用TA来发送邮件。&lt;/p&gt;
 &lt;pre&gt;function send_email($to_email,$subject,$message1)
{
require_once &amp;apos;Mandrill.php&amp;apos;;
$apikey = &amp;apos;XXXXXXXXXX&amp;apos;; //specify your api key here
$mandrill = new Mandrill($apikey);
 
$message = new stdClass();
$message-&amp;gt;html = $message1;
$message-&amp;gt;text = $message1;
$message-&amp;gt;subject = $subject;
$message-&amp;gt;from_email = &amp;quot;blog@koonk.com&amp;quot;;//Sender Email
$message-&amp;gt;from_name  = &amp;quot;KOONK&amp;quot;;//Sender Name
$message-&amp;gt;to = array(array(&amp;quot;email&amp;quot; =&amp;gt; $to_email));
$message-&amp;gt;track_opens = true;
 
$response = $mandrill-&amp;gt;messages-&amp;gt;send($message);
}&lt;/pre&gt;
 &lt;p&gt;“$apikey = ‘XXXXXXXXXX&amp;apos;; //specify your api key here”这里需要你指定你的 API 密钥（从 Mandrill 账户中获得）。&lt;/p&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$to = &amp;quot;abc@example.com&amp;quot;;
$subject = &amp;quot;This is a test email&amp;quot;;
$message = &amp;quot;Hello World!&amp;quot;;
send_email($to,$subject,$message);
?&amp;gt;&lt;/pre&gt;
 &lt;p&gt;为了达到最好的效果，最好按照 Mandrill 的教程去配置 DNS。&lt;/p&gt;
 &lt;h3&gt;3. PHP 函数：阻止 SQL 注入&lt;/h3&gt;
 &lt;p&gt;SQL 注入或者 SQLi 常见的攻击网站的手段，使用下面的代码可以帮助你防止这些工具。&lt;/p&gt;
 &lt;pre&gt;function clean($input)
{
    if (is_array($input))
    {
        foreach ($input as $key =&amp;gt; $val)
         {
            $output[$key] = clean($val);
            // $output[$key] = $this-&amp;gt;clean($val);
        }
    }
    else
    {
        $output = (string) $input;
        // if magic quotes is on then use strip slashes
        if (get_magic_quotes_gpc()) 
        {
            $output = stripslashes($output);
        }
        // $output = strip_tags($output);
        $output = htmlentities($output, ENT_QUOTES, &amp;apos;UTF-8&amp;apos;);
    }
// return the clean text
    return $output;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$text = &amp;quot;&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&amp;quot;;
$text = clean($text);
echo $text;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;4. 检测用户位置&lt;/h3&gt;
 &lt;p&gt;使用下面的函数，可以检测用户是在哪个城市访问你的网站&lt;/p&gt;
 &lt;pre&gt;function detect_city($ip) {
        
        $default = &amp;apos;UNKNOWN&amp;apos;;
 
        $curlopt_useragent = &amp;apos;Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)&amp;apos;;
        
        $url = &amp;apos;http://ipinfodb.com/ip_locator.php?ip=&amp;apos; . urlencode($ip);
        $ch = curl_init();
        
        $curl_opt = array(
            CURLOPT_FOLLOWLOCATION  =&amp;gt; 1,
            CURLOPT_HEADER      =&amp;gt; 0,
            CURLOPT_RETURNTRANSFER  =&amp;gt; 1,
            CURLOPT_USERAGENT   =&amp;gt; $curlopt_useragent,
            CURLOPT_URL       =&amp;gt; $url,
            CURLOPT_TIMEOUT         =&amp;gt; 1,
            CURLOPT_REFERER         =&amp;gt; &amp;apos;http://&amp;apos; . $_SERVER[&amp;apos;HTTP_HOST&amp;apos;],
        );
        
        curl_setopt_array($ch, $curl_opt);
        
        $content = curl_exec($ch);
        
        if (!is_null($curl_info)) {
            $curl_info = curl_getinfo($ch);
        }
        
        curl_close($ch);
        
        if ( preg_match(&amp;apos;{&amp;lt;li&amp;gt;City : ([^&amp;lt;]*)&amp;lt;/li&amp;gt;}i&amp;apos;, $content, $regs) )  {
            $city = $regs[1];
        }
        if ( preg_match(&amp;apos;{&amp;lt;li&amp;gt;State/Province : ([^&amp;lt;]*)&amp;lt;/li&amp;gt;}i&amp;apos;, $content, $regs) )  {
            $state = $regs[1];
        }
 
        if( $city!=&amp;apos;&amp;apos; &amp;amp;&amp;amp; $state!=&amp;apos;&amp;apos; ){
          $location = $city . &amp;apos;, &amp;apos; . $state;
          return $location;
        }else{
          return $default; 
        }
        
    }&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$ip = $_SERVER[&amp;apos;REMOTE_ADDR&amp;apos;];
$city = detect_city($ip);
echo $city;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;5. 获取 Web 页面的源代码&lt;/h3&gt;
 &lt;p&gt;使用下面的函数，可以获取任意 Web 页面的 HTML 代码&lt;/p&gt;
 &lt;pre&gt;function display_sourcecode($url)
{
$lines = file($url);
$output = &amp;quot;&amp;quot;;
foreach ($lines as $line_num =&amp;gt; $line) { 
    // loop thru each line and prepend line numbers
    $output.= &amp;quot;Line #&amp;lt;b&amp;gt;{$line_num}&amp;lt;/b&amp;gt; : &amp;quot; . htmlspecialchars($line) . &amp;quot;&amp;lt;br&amp;gt;\n&amp;quot;;
}
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$url = &amp;quot;http://blog.koonk.com&amp;quot;;
$source = display_sourcecode($url);
echo $source;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;6. 计算喜欢你的 Facebook 页面的用户&lt;/h3&gt;
 &lt;pre&gt;function fb_fan_count($facebook_name)
{
    $data = json_decode(file_get_contents(&amp;quot;https://graph.facebook.com/&amp;quot;.$facebook_name));
    $likes = $data-&amp;gt;likes;
    return $likes;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$page = &amp;quot;koonktechnologies&amp;quot;;
$count = fb_fan_count($page);
echo $count;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;7. 确定任意图片的主导颜色&lt;/h3&gt;
 &lt;pre&gt;function dominant_color($image)
{
$i = imagecreatefromjpeg($image);
for ($x=0;$x&amp;lt;imagesx($i);$x++) {
    for ($y=0;$y&amp;lt;imagesy($i);$y++) {
        $rgb = imagecolorat($i,$x,$y);
        $r   = ($rgb &amp;gt;&amp;gt; 16) &amp;amp; 0xFF;
        $g   = ($rgb &amp;gt;&amp;gt;  &amp;amp; 0xFF;
        $b   = $rgb &amp;amp; 0xFF;
        $rTotal += $r;
        $gTotal += $g;
        $bTotal += $b;
        $total++;
    }
}
$rAverage = round($rTotal/$total);
$gAverage = round($gTotal/$total);
$bAverage = round($bTotal/$total);
}&lt;/pre&gt;
 &lt;h3&gt;8. whois 查询&lt;/h3&gt;
 &lt;p&gt;使用下面的函数可以获取任何域名用户的完整细节&lt;/p&gt;
 &lt;pre&gt;function whois_query($domain) {
 
    // fix the domain name:
    $domain = strtolower(trim($domain));
    $domain = preg_replace(&amp;apos;/^http:\/\//i&amp;apos;, &amp;apos;&amp;apos;, $domain);
    $domain = preg_replace(&amp;apos;/^www\./i&amp;apos;, &amp;apos;&amp;apos;, $domain);
    $domain = explode(&amp;apos;/&amp;apos;, $domain);
    $domain = trim($domain[0]);
 
    // split the TLD from domain name
    $_domain = explode(&amp;apos;.&amp;apos;, $domain);
    $lst = count($_domain)-1;
    $ext = $_domain[$lst];
 
    // You find resources and lists 
    // like these on wikipedia: 
    //
    // http://de.wikipedia.org/wiki/Whois
    //
    $servers = array(
        &amp;quot;biz&amp;quot; =&amp;gt; &amp;quot;whois.neulevel.biz&amp;quot;,
        &amp;quot;com&amp;quot; =&amp;gt; &amp;quot;whois.internic.net&amp;quot;,
        &amp;quot;us&amp;quot; =&amp;gt; &amp;quot;whois.nic.us&amp;quot;,
        &amp;quot;coop&amp;quot; =&amp;gt; &amp;quot;whois.nic.coop&amp;quot;,
        &amp;quot;info&amp;quot; =&amp;gt; &amp;quot;whois.nic.info&amp;quot;,
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;whois.nic.name&amp;quot;,
        &amp;quot;net&amp;quot; =&amp;gt; &amp;quot;whois.internic.net&amp;quot;,
        &amp;quot;gov&amp;quot; =&amp;gt; &amp;quot;whois.nic.gov&amp;quot;,
        &amp;quot;edu&amp;quot; =&amp;gt; &amp;quot;whois.internic.net&amp;quot;,
        &amp;quot;mil&amp;quot; =&amp;gt; &amp;quot;rs.internic.net&amp;quot;,
        &amp;quot;int&amp;quot; =&amp;gt; &amp;quot;whois.iana.org&amp;quot;,
        &amp;quot;ac&amp;quot; =&amp;gt; &amp;quot;whois.nic.ac&amp;quot;,
        &amp;quot;ae&amp;quot; =&amp;gt; &amp;quot;whois.uaenic.ae&amp;quot;,
        &amp;quot;at&amp;quot; =&amp;gt; &amp;quot;whois.ripe.net&amp;quot;,
        &amp;quot;au&amp;quot; =&amp;gt; &amp;quot;whois.aunic.net&amp;quot;,
        &amp;quot;be&amp;quot; =&amp;gt; &amp;quot;whois.dns.be&amp;quot;,
        &amp;quot;bg&amp;quot; =&amp;gt; &amp;quot;whois.ripe.net&amp;quot;,
        &amp;quot;br&amp;quot; =&amp;gt; &amp;quot;whois.registro.br&amp;quot;,
        &amp;quot;bz&amp;quot; =&amp;gt; &amp;quot;whois.belizenic.bz&amp;quot;,
        &amp;quot;ca&amp;quot; =&amp;gt; &amp;quot;whois.cira.ca&amp;quot;,
        &amp;quot;cc&amp;quot; =&amp;gt; &amp;quot;whois.nic.cc&amp;quot;,
        &amp;quot;ch&amp;quot; =&amp;gt; &amp;quot;whois.nic.ch&amp;quot;,
        &amp;quot;cl&amp;quot; =&amp;gt; &amp;quot;whois.nic.cl&amp;quot;,
        &amp;quot;cn&amp;quot; =&amp;gt; &amp;quot;whois.cnnic.net.cn&amp;quot;,
        &amp;quot;cz&amp;quot; =&amp;gt; &amp;quot;whois.nic.cz&amp;quot;,
        &amp;quot;de&amp;quot; =&amp;gt; &amp;quot;whois.nic.de&amp;quot;,
        &amp;quot;fr&amp;quot; =&amp;gt; &amp;quot;whois.nic.fr&amp;quot;,
        &amp;quot;hu&amp;quot; =&amp;gt; &amp;quot;whois.nic.hu&amp;quot;,
        &amp;quot;ie&amp;quot; =&amp;gt; &amp;quot;whois.domainregistry.ie&amp;quot;,
        &amp;quot;il&amp;quot; =&amp;gt; &amp;quot;whois.isoc.org.il&amp;quot;,
        &amp;quot;in&amp;quot; =&amp;gt; &amp;quot;whois.ncst.ernet.in&amp;quot;,
        &amp;quot;ir&amp;quot; =&amp;gt; &amp;quot;whois.nic.ir&amp;quot;,
        &amp;quot;mc&amp;quot; =&amp;gt; &amp;quot;whois.ripe.net&amp;quot;,
        &amp;quot;to&amp;quot; =&amp;gt; &amp;quot;whois.tonic.to&amp;quot;,
        &amp;quot;tv&amp;quot; =&amp;gt; &amp;quot;whois.tv&amp;quot;,
        &amp;quot;ru&amp;quot; =&amp;gt; &amp;quot;whois.ripn.net&amp;quot;,
        &amp;quot;org&amp;quot; =&amp;gt; &amp;quot;whois.pir.org&amp;quot;,
        &amp;quot;aero&amp;quot; =&amp;gt; &amp;quot;whois.information.aero&amp;quot;,
        &amp;quot;nl&amp;quot; =&amp;gt; &amp;quot;whois.domain-registry.nl&amp;quot;
    );
 
    if (!isset($servers[$ext])){
        die(&amp;apos;Error: No matching nic server found!&amp;apos;);
    }
 
    $nic_server = $servers[$ext];
 
    $output = &amp;apos;&amp;apos;;
 
    // connect to whois server:
    if ($conn = fsockopen ($nic_server, 43)) {
        fputs($conn, $domain.&amp;quot;\r\n&amp;quot;);
        while(!feof($conn)) {
            $output .= fgets($conn,128);
        }
        fclose($conn);
    }
    else { die(&amp;apos;Error: Could not connect to &amp;apos; . $nic_server . &amp;apos;!&amp;apos;); }
 
    return $output;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$domain = &amp;quot;http://www.blog.koonk.com&amp;quot;;
$result = whois_query($domain);
print_r($result);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;9. 验证邮箱地址&lt;/h3&gt;
 &lt;p&gt;有时候，当在网站填写表单，用户可能会输入错误的邮箱地址，这个函数可以验证邮箱地址是否有效。&lt;/p&gt;
 &lt;pre&gt;function is_validemail($email)
{
$check = 0;
if(filter_var($email,FILTER_VALIDATE_EMAIL))
{
$check = 1;
}
return $check;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$email = &amp;quot;blog@koonk.com&amp;quot;;
$check = is_validemail($email);
echo $check;
// If the output is 1, then email is valid.
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;10. 获取用户的真实  IP&lt;/h3&gt;
 &lt;pre&gt;function getRealIpAddr()  
{  
    if (!emptyempty($_SERVER[&amp;apos;HTTP_CLIENT_IP&amp;apos;]))  
    {  
        $ip=$_SERVER[&amp;apos;HTTP_CLIENT_IP&amp;apos;];  
    }  
    elseif (!emptyempty($_SERVER[&amp;apos;HTTP_X_FORWARDED_FOR&amp;apos;]))  
    //to check ip is pass from proxy  
    {  
        $ip=$_SERVER[&amp;apos;HTTP_X_FORWARDED_FOR&amp;apos;];  
    }  
    else  
    {  
        $ip=$_SERVER[&amp;apos;REMOTE_ADDR&amp;apos;];  
    }  
    return $ip;  
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$ip = getRealIpAddr();
echo $ip;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;11. 转换 URL：从字符串变成超链接&lt;/h3&gt;
 &lt;p&gt;如果你正在开发论坛，博客或者是一个常规的表单提交，很多时候都要用户访问一个网站。使用这个函数，URL 字符串就可以自动的转换为超链接。&lt;/p&gt;
 &lt;pre&gt;function makeClickableLinks($text) 
{  
 $text = eregi_replace(&amp;apos;(((f|ht){1}tp://)[-a-zA-Z0-9@:%_+.~#?&amp;amp;//=]+)&amp;apos;,  
 &amp;apos;&amp;lt;a href=&amp;quot;\1&amp;quot;&amp;gt;\1&amp;lt;/a&amp;gt;&amp;apos;, $text);  
 $text = eregi_replace(&amp;apos;([[:space:]()[{}])(www.[-a-zA-Z0-9@:%_+.~#?&amp;amp;//=]+)&amp;apos;,  
 &amp;apos;\1&amp;lt;a href=&amp;quot;http://\2&amp;quot;&amp;gt;\2&amp;lt;/a&amp;gt;&amp;apos;, $text);  
 $text = eregi_replace(&amp;apos;([_.0-9a-z-]+@([0-9a-z][0-9a-z-]+.)+[a-z]{2,3})&amp;apos;,  
 &amp;apos;&amp;lt;a href=&amp;quot;mailto:\1&amp;quot;&amp;gt;\1&amp;lt;/a&amp;gt;&amp;apos;, $text);  
  
return $text;  
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$text = &amp;quot;This is my first post on http://blog.koonk.com&amp;quot;;
$text = makeClickableLinks($text);
echo $text;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;12. 阻止多个 IP 访问你的网站&lt;/h3&gt;
 &lt;p&gt;这个代码片段可以方便你禁止某些特定的 IP 地址访问你的网站&lt;/p&gt;
 &lt;pre&gt;if ( !file_exists(&amp;apos;blocked_ips.txt&amp;apos;) ) {
 $deny_ips = array(
  &amp;apos;127.0.0.1&amp;apos;,
  &amp;apos;192.168.1.1&amp;apos;,
  &amp;apos;83.76.27.9&amp;apos;,
  &amp;apos;192.168.1.163&amp;apos;
 );
} else {
 $deny_ips = file(&amp;apos;blocked_ips.txt&amp;apos;);
}
// read user ip adress:
$ip = isset($_SERVER[&amp;apos;REMOTE_ADDR&amp;apos;]) ? trim($_SERVER[&amp;apos;REMOTE_ADDR&amp;apos;]) : &amp;apos;&amp;apos;;
 
// search current IP in $deny_ips array
if ( (array_search($ip, $deny_ips))!== FALSE ) {
 // address is blocked:
 echo &amp;apos;Your IP adress (&amp;apos;.$ip.&amp;apos;) was blocked!&amp;apos;;
 exit;
}&lt;/pre&gt;
 &lt;h3&gt;13. 强制性文件下载&lt;/h3&gt;
 &lt;p&gt;如果你需要下载特定的文件而不用另开新窗口，下面的代码片段可以帮助你。&lt;/p&gt;
 &lt;pre&gt;function force_download($file) 
{ 
    $dir      = &amp;quot;../log/exports/&amp;quot;; 
    if ((isset($file))&amp;amp;&amp;amp;(file_exists($dir.$file))) { 
       header(&amp;quot;Content-type: application/force-download&amp;quot;); 
       header(&amp;apos;Content-Disposition: inline; filename=&amp;quot;&amp;apos; . $dir.$file . &amp;apos;&amp;quot;&amp;apos;); 
       header(&amp;quot;Content-Transfer-Encoding: Binary&amp;quot;); 
       header(&amp;quot;Content-length: &amp;quot;.filesize($dir.$file)); 
       header(&amp;apos;Content-Type: application/octet-stream&amp;apos;); 
       header(&amp;apos;Content-Disposition: attachment; filename=&amp;quot;&amp;apos; . $file . &amp;apos;&amp;quot;&amp;apos;); 
       readfile(&amp;quot;$dir$file&amp;quot;); 
    } else { 
       echo &amp;quot;No file selected&amp;quot;; 
    } 
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;php
force_download(&amp;quot;image.jpg&amp;quot;);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;14. 创建 JSON 数据&lt;/h3&gt;
 &lt;p&gt;使用下面的 PHP 片段可以创建 JSON 数据，可以方便你创建移动应用的 Web 服务&lt;/p&gt;
 &lt;pre&gt;$json_data = array (&amp;apos;id&amp;apos;=&amp;gt;1,&amp;apos;name&amp;apos;=&amp;gt;&amp;quot;Mohit&amp;quot;);
echo json_encode($json_data);&lt;/pre&gt;
 &lt;h3&gt;15. 压缩 zip 文件&lt;/h3&gt;
 &lt;p&gt;使用下面的 PHP 片段可以即时压缩 zip 文件&lt;/p&gt;
 &lt;pre&gt;function create_zip($files = array(),$destination = &amp;apos;&amp;apos;,$overwrite = false) {  
    //if the zip file already exists and overwrite is false, return false  
    if(file_exists($destination) &amp;amp;&amp;amp; !$overwrite) { return false; }  
    //vars  
    $valid_files = array();  
    //if files were passed in...  
    if(is_array($files)) {  
        //cycle through each file  
        foreach($files as $file) {  
            //make sure the file exists  
            if(file_exists($file)) {  
                $valid_files[] = $file;  
            }  
        }  
    }  
    //if we have good files...  
    if(count($valid_files)) {  
        //create the archive  
        $zip = new ZipArchive();  
        if($zip-&amp;gt;open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {  
            return false;  
        }  
        //add the files  
        foreach($valid_files as $file) {  
            $zip-&amp;gt;addFile($file,$file);  
        }  
        //debug  
        //echo &amp;apos;The zip archive contains &amp;apos;,$zip-&amp;gt;numFiles,&amp;apos; files with a status of &amp;apos;,$zip-&amp;gt;status;  
          
        //close the zip -- done!  
        $zip-&amp;gt;close();  
          
        //check to make sure the file exists  
        return file_exists($destination);  
    }  
    else  
    {  
        return false;  
    }  
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$files=array(&amp;apos;file1.jpg&amp;apos;, &amp;apos;file2.jpg&amp;apos;, &amp;apos;file3.gif&amp;apos;);  
create_zip($files, &amp;apos;myzipfile.zip&amp;apos;, true); 
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;16. 解压文件&lt;/h3&gt;
 &lt;pre&gt;function unzip($location,$newLocation)
{
        if(exec(&amp;quot;unzip $location&amp;quot;,$arr)){
            mkdir($newLocation);
            for($i = 1;$i&amp;lt; count($arr);$i++){
                $file = trim(preg_replace(&amp;quot;~inflating: ~&amp;quot;,&amp;quot;&amp;quot;,$arr[$i]));
                copy($location.&amp;apos;/&amp;apos;.$file,$newLocation.&amp;apos;/&amp;apos;.$file);
                unlink($location.&amp;apos;/&amp;apos;.$file);
            }
            return TRUE;
        }else{
            return FALSE;
        }
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
unzip(&amp;apos;test.zip&amp;apos;,&amp;apos;unziped/test&amp;apos;); //File would be unzipped in unziped/test folder
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;17. 缩放图片&lt;/h3&gt;
 &lt;pre&gt;function resize_image($filename, $tmpname, $xmax, $ymax)  
{  
    $ext = explode(&amp;quot;.&amp;quot;, $filename);  
    $ext = $ext[count($ext)-1];  
  
    if($ext == &amp;quot;jpg&amp;quot; || $ext == &amp;quot;jpeg&amp;quot;)  
        $im = imagecreatefromjpeg($tmpname);  
    elseif($ext == &amp;quot;png&amp;quot;)  
        $im = imagecreatefrompng($tmpname);  
    elseif($ext == &amp;quot;gif&amp;quot;)  
        $im = imagecreatefromgif($tmpname);  
      
    $x = imagesx($im);  
    $y = imagesy($im);  
      
    if($x &amp;lt;= $xmax &amp;amp;&amp;amp; $y &amp;lt;= $ymax)  
        return $im;  
  
    if($x &amp;gt;= $y) {  
        $newx = $xmax;  
        $newy = $newx * $y / $x;  
    }  
    else {  
        $newy = $ymax;  
        $newx = $x / $y * $newy;  
    }  
      
    $im2 = imagecreatetruecolor($newx, $newy);  
    imagecopyresized($im2, $im, 0, 0, 0, 0, floor($newx), floor($newy), $x, $y);  
    return $im2;   
}&lt;/pre&gt;
 &lt;h3&gt;18. 使用 mail() 发送邮件&lt;/h3&gt;
 &lt;p&gt;之前我们提供了如何使用 Mandrill 发送邮件的 PHP 代码片段，但是如果你不想使用第三方服务，那么可以使用下面的 PHP 代码片段。&lt;/p&gt;
 &lt;pre&gt;function send_mail($to,$subject,$body)
{
$headers = &amp;quot;From: KOONK\r\n&amp;quot;;
$headers .= &amp;quot;Reply-To: blog@koonk.com\r\n&amp;quot;;
$headers .= &amp;quot;Return-Path: blog@koonk.com\r\n&amp;quot;;
$headers .= &amp;quot;X-Mailer: PHP5\n&amp;quot;;
$headers .= &amp;apos;MIME-Version: 1.0&amp;apos; . &amp;quot;\n&amp;quot;;
$headers .= &amp;apos;Content-type: text/html; charset=iso-8859-1&amp;apos; . &amp;quot;\r\n&amp;quot;;
mail($to,$subject,$body,$headers);
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$to = &amp;quot;admin@koonk.com&amp;quot;;
$subject = &amp;quot;This is a test mail&amp;quot;;
$body = &amp;quot;Hello World!&amp;quot;;
send_mail($to,$subject,$body);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;19. 把秒转换成天数，小时数和分钟&lt;/h3&gt;
 &lt;pre&gt;function secsToStr($secs) {
    if($secs&amp;gt;=86400){$days=floor($secs/86400);$secs=$secs%86400;$r=$days.&amp;apos; day&amp;apos;;if($days&amp;lt;&amp;gt;1){$r.=&amp;apos;s&amp;apos;;}if($secs&amp;gt;0){$r.=&amp;apos;, &amp;apos;;}}
    if($secs&amp;gt;=3600){$hours=floor($secs/3600);$secs=$secs%3600;$r.=$hours.&amp;apos; hour&amp;apos;;if($hours&amp;lt;&amp;gt;1){$r.=&amp;apos;s&amp;apos;;}if($secs&amp;gt;0){$r.=&amp;apos;, &amp;apos;;}}
    if($secs&amp;gt;=60){$minutes=floor($secs/60);$secs=$secs%60;$r.=$minutes.&amp;apos; minute&amp;apos;;if($minutes&amp;lt;&amp;gt;1){$r.=&amp;apos;s&amp;apos;;}if($secs&amp;gt;0){$r.=&amp;apos;, &amp;apos;;}}
    $r.=$secs.&amp;apos; second&amp;apos;;if($secs&amp;lt;&amp;gt;1){$r.=&amp;apos;s&amp;apos;;}
    return $r;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$seconds = &amp;quot;56789&amp;quot;;
$output = secsToStr($seconds);
echo $output;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;20. 数据库连接&lt;/h3&gt;
 &lt;p&gt;连接 MySQL 数据库&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$DBNAME = &amp;apos;koonk&amp;apos;;
$HOST = &amp;apos;localhost&amp;apos;;
$DBUSER = &amp;apos;root&amp;apos;;
$DBPASS = &amp;apos;koonk&amp;apos;;
$CONNECT = mysql_connect($HOST,$DBUSER,$DBPASS);
if(!$CONNECT)
{
    echo &amp;apos;MySQL Error: &amp;apos;.mysql_error();
}
$SELECT = mysql_select_db($DBNAME);
if(!$SELECT)
{
    echo &amp;apos;MySQL Error: &amp;apos;.mysql_error();
}
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;21. 目录清单&lt;/h3&gt;
 &lt;p&gt;使用下面的 PHP 代码片段可以在一个目录中列出所有文件和文件夹&lt;/p&gt;
 &lt;pre&gt;function list_files($dir)
{
    if(is_dir($dir))
    {
        if($handle = opendir($dir))
        {
            while(($file = readdir($handle)) !== false)
            {
                if($file != &amp;quot;.&amp;quot; &amp;amp;amp;&amp;amp;amp; $file != &amp;quot;..&amp;quot; &amp;amp;amp;&amp;amp;amp; $file != &amp;quot;Thumbs.db&amp;quot;/*pesky windows, images..*/)
                {
                    echo &amp;apos;&amp;lt;a target=&amp;quot;_blank&amp;quot; href=&amp;quot;&amp;apos;.$dir.$file.&amp;apos;&amp;quot;&amp;gt;&amp;apos;.$file.&amp;apos;&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;&amp;apos;.&amp;quot;\n&amp;quot;;
                }
            }
            closedir($handle);
        }
    }
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
    list_files(&amp;quot;images/&amp;quot;); //This will list all files of images folder
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;22. 检测用户语言&lt;/h3&gt;
 &lt;p&gt;使用下面的 PHP 代码片段可以检测用户浏览器所使用的语言&lt;/p&gt;
 &lt;pre&gt;function get_client_language($availableLanguages, $default=&amp;apos;en&amp;apos;){
    if (isset($_SERVER[&amp;apos;HTTP_ACCEPT_LANGUAGE&amp;apos;])) {
        $langs=explode(&amp;apos;,&amp;apos;,$_SERVER[&amp;apos;HTTP_ACCEPT_LANGUAGE&amp;apos;]);
        foreach ($langs as $value){
            $choice=substr($value,0,2);
            if(in_array($choice, $availableLanguages)){
                return $choice;
            }
        }
    } 
    return $default;
}&lt;/pre&gt;
 &lt;h3&gt;23. 查看 CSV 文件&lt;/h3&gt;
 &lt;pre&gt;function readCSV($csvFile){
    $file_handle = fopen($csvFile, &amp;apos;r&amp;apos;);
    while (!feof($file_handle) ) {
        $line_of_text[] = fgetcsv($file_handle, 1024);
    }
    fclose($file_handle);
    return $line_of_text;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$csvFile = &amp;quot;test.csv&amp;quot;;
$csv = readCSV($csvFile);
$a = csv[0][0]; // This will get value of Column 1 &amp;amp; Row 1
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;24. 从 PHP 数据创建 CSV 文件&lt;/h3&gt;
 &lt;pre&gt;function generateCsv($data, $delimiter = &amp;apos;,&amp;apos;, $enclosure = &amp;apos;&amp;quot;&amp;apos;) {
   $handle = fopen(&amp;apos;php://temp&amp;apos;, &amp;apos;r+&amp;apos;);
   foreach ($data as $line) {
           fputcsv($handle, $line, $delimiter, $enclosure);
   }
   rewind($handle);
   while (!feof($handle)) {
           $contents .= fread($handle, 8192);
   }
   fclose($handle);
   return $contents;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$data[0] = &amp;quot;apple&amp;quot;;
$data[1] = &amp;quot;oranges&amp;quot;;
generateCsv($data, $delimiter = &amp;apos;,&amp;apos;, $enclosure = &amp;apos;&amp;quot;&amp;apos;);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;25. 解析 XML 数据&lt;/h3&gt;
 &lt;pre&gt;$xml_string=&amp;quot;&amp;lt;?xml version=&amp;apos;1.0&amp;apos;?&amp;gt;
&amp;lt;moleculedb&amp;gt;
    &amp;lt;molecule name=&amp;apos;Benzine&amp;apos;&amp;gt;
        &amp;lt;symbol&amp;gt;ben&amp;lt;/symbol&amp;gt;
        &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt;
    &amp;lt;/molecule&amp;gt;
    &amp;lt;molecule name=&amp;apos;Water&amp;apos;&amp;gt;
        &amp;lt;symbol&amp;gt;h2o&amp;lt;/symbol&amp;gt;
        &amp;lt;code&amp;gt;K&amp;lt;/code&amp;gt;
    &amp;lt;/molecule&amp;gt;
&amp;lt;/moleculedb&amp;gt;&amp;quot;;
 
//load the xml string using simplexml function
$xml = simplexml_load_string($xml_string);
 
//loop through the each node of molecule
foreach ($xml-&amp;gt;molecule as $record)
{
   //attribute are accessted by
   echo $record[&amp;apos;name&amp;apos;], &amp;apos;  &amp;apos;;
   //node are accessted by -&amp;gt; operator
   echo $record-&amp;gt;symbol, &amp;apos;  &amp;apos;;
   echo $record-&amp;gt;code, &amp;apos;&amp;lt;br /&amp;gt;&amp;apos;;
}&lt;/pre&gt;
 &lt;h3&gt;26. 解析 JSON 数据&lt;/h3&gt;
 &lt;pre&gt;$json_string=&amp;apos;{&amp;quot;id&amp;quot;:1,&amp;quot;name&amp;quot;:&amp;quot;rolf&amp;quot;,&amp;quot;country&amp;quot;:&amp;quot;russia&amp;quot;,&amp;quot;office&amp;quot;:[&amp;quot;google&amp;quot;,&amp;quot;oracle&amp;quot;]} &amp;apos;;
$obj=json_decode($json_string);
//print the parsed data
echo $obj-&amp;gt;name; //displays rolf
echo $obj-&amp;gt;office[0]; //displays google&lt;/pre&gt;
 &lt;h3&gt;27. 获取当前页面 URL&lt;/h3&gt;
 &lt;p&gt;这个 PHP 片段可以帮助你让用户登录后直接跳转到之前浏览的页面&lt;/p&gt;
 &lt;pre&gt;function current_url()
{
$url = &amp;quot;http://&amp;quot; . $_SERVER[&amp;apos;HTTP_HOST&amp;apos;] . $_SERVER[&amp;apos;REQUEST_URI&amp;apos;];
$validURL = str_replace(&amp;quot;&amp;amp;&amp;quot;, &amp;quot;&amp;amp;amp;&amp;quot;, $url);
return validURL;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
echo &amp;quot;Currently you are on: &amp;quot;.current_url();
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;28. 从任意的 Twitter 账号获取最新的 Tweet&lt;/h3&gt;
 &lt;pre&gt;function my_twitter($username) 
{
     $no_of_tweets = 1;
     $feed = &amp;quot;http://search.twitter.com/search.atom?q=from:&amp;quot; . $username . &amp;quot;&amp;amp;rpp=&amp;quot; . $no_of_tweets;
     $xml = simplexml_load_file($feed);
    foreach($xml-&amp;gt;children() as $child) {
        foreach ($child as $value) {
            if($value-&amp;gt;getName() == &amp;quot;link&amp;quot;) $link = $value[&amp;apos;href&amp;apos;];
            if($value-&amp;gt;getName() == &amp;quot;content&amp;quot;) {
                $content = $value . &amp;quot;&amp;quot;;
        echo &amp;apos;&amp;lt;p class=&amp;quot;twit&amp;quot;&amp;gt;&amp;apos;.$content.&amp;apos; &amp;lt;a class=&amp;quot;twt&amp;quot; href=&amp;quot;&amp;apos;.$link.&amp;apos;&amp;quot; title=&amp;quot;&amp;quot;&amp;gt;&amp;amp;nbsp; &amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;apos;;
            }    
        }
    }    
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$handle = &amp;quot;koonktech&amp;quot;;
my_twitter($handle);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;29. 转发数量&lt;/h3&gt;
 &lt;p&gt;使用这个 PHP 片段可以检测你的页面 URL 有多少转发数量&lt;/p&gt;
 &lt;pre&gt;function tweetCount($url) {
    $content = file_get_contents(&amp;quot;http://api.tweetmeme.com/url_info?url=&amp;quot;.$url);
    $element = new SimpleXmlElement($content);
    $retweets = $element-&amp;gt;story-&amp;gt;url_count;
    if($retweets){
        return $retweets;
    } else {
        return 0;
    }
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$url = &amp;quot;http://blog.koonk.com&amp;quot;;
$count = tweetCount($url);
return $count;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;30. 计算两个日期的差&lt;/h3&gt;
 &lt;pre&gt;&amp;lt;?php
$date1 = date( &amp;apos;Y-m-d&amp;apos; );
$date2 = &amp;quot;2015-12-04&amp;quot;;
$diff = abs(strtotime($date2) - strtotime($date1));
$years = floor($diff / (365*60*60*24));
$months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24));
$days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));
printf(&amp;quot;%d years, %d months, %d days\n&amp;quot;, $years, $months, $days);
-------------------------------------------------------- OR
$date1 = new DateTime(&amp;quot;2007-03-24&amp;quot;);
$date2 = new DateTime(&amp;quot;2009-06-26&amp;quot;);
$interval = $date1-&amp;gt;diff($date2);
echo &amp;quot;difference &amp;quot; . $interval-&amp;gt;y . &amp;quot; years, &amp;quot; . $interval-&amp;gt;m.&amp;quot; months, &amp;quot;.$interval-&amp;gt;d.&amp;quot; days &amp;quot;; 
// shows the total amount of days (not divided into years, months and days like above)
echo &amp;quot;difference &amp;quot; . $interval-&amp;gt;days . &amp;quot; days &amp;quot;;
-------------------------------------------------------- OR
    
    
/**
 * Calculate differences between two dates with precise semantics. Based on PHPs DateTime::diff()
 * implementation by Derick Rethans. Ported to PHP by Emil H, 2011-05-02. No rights reserved.
 * 
 * See here for original code:
 * http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/tm2unixtime.c?revision=302890&amp;amp;view=markup
 * http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/interval.c?revision=298973&amp;amp;view=markup
 */
function _date_range_limit($start, $end, $adj, $a, $b, $result)
{
    if ($result[$a] &amp;lt; $start) {
        $result[$b] -= intval(($start - $result[$a] - 1) / $adj) + 1;
        $result[$a] += $adj * intval(($start - $result[$a] - 1) / $adj + 1);
    }
    if ($result[$a] &amp;gt;= $end) {
        $result[$b] += intval($result[$a] / $adj);
        $result[$a] -= $adj * intval($result[$a] / $adj);
    }
    return $result;
}
function _date_range_limit_days($base, $result)
{
    $days_in_month_leap = array(31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
    $days_in_month = array(31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
    _date_range_limit(1, 13, 12, &amp;quot;m&amp;quot;, &amp;quot;y&amp;quot;, &amp;amp;$base);
    $year = $base[&amp;quot;y&amp;quot;];
    $month = $base[&amp;quot;m&amp;quot;];
    if (!$result[&amp;quot;invert&amp;quot;]) {
        while ($result[&amp;quot;d&amp;quot;] &amp;lt; 0) {
            $month--;
            if ($month &amp;lt; 1) {
                $month += 12;
                $year--;
            }
            $leapyear = $year % 400 == 0 || ($year % 100 != 0 &amp;amp;&amp;amp; $year % 4 == 0);
            $days = $leapyear ? $days_in_month_leap[$month] : $days_in_month[$month];
            $result[&amp;quot;d&amp;quot;] += $days;
            $result[&amp;quot;m&amp;quot;]--;
        }
    } else {
        while ($result[&amp;quot;d&amp;quot;] &amp;lt; 0) {
            $leapyear = $year % 400 == 0 || ($year % 100 != 0 &amp;amp;&amp;amp; $year % 4 == 0);
            $days = $leapyear ? $days_in_month_leap[$month] : $days_in_month[$month];
            $result[&amp;quot;d&amp;quot;] += $days;
            $result[&amp;quot;m&amp;quot;]--;
            $month++;
            if ($month &amp;gt; 12) {
                $month -= 12;
                $year++;
            }
        }
    }
    return $result;
}
function _date_normalize($base, $result)
{
    $result = _date_range_limit(0, 60, 60, &amp;quot;s&amp;quot;, &amp;quot;i&amp;quot;, $result);
    $result = _date_range_limit(0, 60, 60, &amp;quot;i&amp;quot;, &amp;quot;h&amp;quot;, $result);
    $result = _date_range_limit(0, 24, 24, &amp;quot;h&amp;quot;, &amp;quot;d&amp;quot;, $result);
    $result = _date_range_limit(0, 12, 12, &amp;quot;m&amp;quot;, &amp;quot;y&amp;quot;, $result);
    $result = _date_range_limit_days(&amp;amp;$base, &amp;amp;$result);
    $result = _date_range_limit(0, 12, 12, &amp;quot;m&amp;quot;, &amp;quot;y&amp;quot;, $result);
    return $result;
}
/**
 * Accepts two unix timestamps.
 */
function _date_diff($one, $two)
{
    $invert = false;
    if ($one &amp;gt; $two) {
        list($one, $two) = array($two, $one);
        $invert = true;
    }
    $key = array(&amp;quot;y&amp;quot;, &amp;quot;m&amp;quot;, &amp;quot;d&amp;quot;, &amp;quot;h&amp;quot;, &amp;quot;i&amp;quot;, &amp;quot;s&amp;quot;);
    $a = array_combine($key, array_map(&amp;quot;intval&amp;quot;, explode(&amp;quot; &amp;quot;, date(&amp;quot;Y m d H i s&amp;quot;, $one))));
    $b = array_combine($key, array_map(&amp;quot;intval&amp;quot;, explode(&amp;quot; &amp;quot;, date(&amp;quot;Y m d H i s&amp;quot;, $two))));
    $result = array();
    $result[&amp;quot;y&amp;quot;] = $b[&amp;quot;y&amp;quot;] - $a[&amp;quot;y&amp;quot;];
    $result[&amp;quot;m&amp;quot;] = $b[&amp;quot;m&amp;quot;] - $a[&amp;quot;m&amp;quot;];
    $result[&amp;quot;d&amp;quot;] = $b[&amp;quot;d&amp;quot;] - $a[&amp;quot;d&amp;quot;];
    $result[&amp;quot;h&amp;quot;] = $b[&amp;quot;h&amp;quot;] - $a[&amp;quot;h&amp;quot;];
    $result[&amp;quot;i&amp;quot;] = $b[&amp;quot;i&amp;quot;] - $a[&amp;quot;i&amp;quot;];
    $result[&amp;quot;s&amp;quot;] = $b[&amp;quot;s&amp;quot;] - $a[&amp;quot;s&amp;quot;];
    $result[&amp;quot;invert&amp;quot;] = $invert ? 1 : 0;
    $result[&amp;quot;days&amp;quot;] = intval(abs(($one - $two)/86400));
    if ($invert) {
        _date_normalize(&amp;amp;$a, &amp;amp;$result);
    } else {
        _date_normalize(&amp;amp;$b, &amp;amp;$result);
    }
    return $result;
}
$date = &amp;quot;2014-12-04 19:37:22&amp;quot;;
echo &amp;apos;&amp;lt;pre&amp;gt;&amp;apos;;
print_r( _date_diff( strtotime($date), time() ) );
echo &amp;apos;&amp;lt;/pre&amp;gt;&amp;apos;; 
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;31. 删除文件夹内容&lt;/h3&gt;
 &lt;pre&gt;function Delete($path)
{
    if (is_dir($path) === true)
    {
        $files = array_diff(scandir($path), array(&amp;apos;.&amp;apos;, &amp;apos;..&amp;apos;));
        foreach ($files as $file)
        {
            Delete(realpath($path) . &amp;apos;/&amp;apos; . $file);
        }
        return rmdir($path);
    }
    else if (is_file($path) === true)
    {
        return unlink($path);
    }
    return false;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$path = &amp;quot;images/&amp;quot;;
Delete($path); // This will delete images folder along with its contents.
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;32. 搜索和高亮字符串中的关键字&lt;/h3&gt;
 &lt;pre&gt;function highlighter_text($text, $words)
{
    $split_words = explode( &amp;quot; &amp;quot; , $words );
    foreach($split_words as $word)
    {
        $color = &amp;quot;#4285F4&amp;quot;;
        $text = preg_replace(&amp;quot;|($word)|Ui&amp;quot; ,
            &amp;quot;&amp;lt;span style=\&amp;quot;color:&amp;quot;.$color.&amp;quot;;\&amp;quot;&amp;gt;&amp;lt;b&amp;gt;$1&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; , $text );
    }
    return $text;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$string = &amp;quot;I like chocolates and I like apples&amp;quot;;
$words = &amp;quot;apple&amp;quot;;
echo highlighter_text($string ,$words);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;33. 写入文件&lt;/h3&gt;
 &lt;pre&gt;&amp;lt;?
$filename = &amp;apos;blog.csv&amp;apos;;
$fp = fopen($filename, &amp;apos;w&amp;apos;);
$output = &amp;quot; Hello &amp;quot;;
$output .= &amp;quot; World! &amp;quot;;
$output .= &amp;quot;\r\n&amp;quot;;
fputs($fp, $output);
fclose($fp);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;34. 根据 URL 下载图片&lt;/h3&gt;
 &lt;pre&gt;function imagefromURL($image,$rename)
{
$ch = curl_init($image);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
$rawdata=curl_exec ($ch);
curl_close ($ch);
$fp = fopen(&amp;quot;$rename&amp;quot;,&amp;apos;w&amp;apos;);
fwrite($fp, $rawdata); 
fclose($fp);
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$url = &amp;quot;http://koonk.com/images/logo.png&amp;quot;;
$rename = &amp;quot;koonk.png&amp;quot;;
imagefromURL($url,$rename);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;35. 检测 URL 是否有效&lt;/h3&gt;
 &lt;pre&gt;function isvalidURL($url)
{
$check = 0;
if (filter_var($url, FILTER_VALIDATE_URL) !== false) {
  $check = 1;
}
return $check;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$url = &amp;quot;http://koonk.com&amp;quot;;
$check = checkvalidURL($url);
echo $check; //if returns 1 then URL is valid.
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;36. 生成二维码&lt;/h3&gt;
 &lt;pre&gt;function qr_code($data, $type = &amp;quot;TXT&amp;quot;, $size =&amp;apos;150&amp;apos;, $ec=&amp;apos;L&amp;apos;, $margin=&amp;apos;0&amp;apos;)  
{
     $types = array(&amp;quot;URL&amp;quot; =--&amp;gt; &amp;quot;http://&amp;quot;, &amp;quot;TEL&amp;quot; =&amp;gt; &amp;quot;TEL:&amp;quot;, &amp;quot;TXT&amp;quot;=&amp;gt;&amp;quot;&amp;quot;, &amp;quot;EMAIL&amp;quot; =&amp;gt; &amp;quot;MAILTO:&amp;quot;);
    if(!in_array($type,array(&amp;quot;URL&amp;quot;, &amp;quot;TEL&amp;quot;, &amp;quot;TXT&amp;quot;, &amp;quot;EMAIL&amp;quot;)))
    {
        $type = &amp;quot;TXT&amp;quot;;
    }
    if (!preg_match(&amp;apos;/^&amp;apos;.$types[$type].&amp;apos;/&amp;apos;, $data))
    {
        $data = str_replace(&amp;quot;\\&amp;quot;, &amp;quot;&amp;quot;, $types[$type]).$data;
    }
    $ch = curl_init();
    $data = urlencode($data);
    curl_setopt($ch, CURLOPT_URL, &amp;apos;http://chart.apis.google.com/chart&amp;apos;);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, &amp;apos;chs=&amp;apos;.$size.&amp;apos;x&amp;apos;.$size.&amp;apos;&amp;amp;cht=qr&amp;amp;chld=&amp;apos;.$ec.&amp;apos;|&amp;apos;.$margin.&amp;apos;&amp;amp;chl=&amp;apos;.$data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    $response = curl_exec($ch);
    curl_close($ch);
    return $response;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
header(&amp;quot;Content-type: image/png&amp;quot;);
echo qr_code(&amp;quot;http://koonk.com&amp;quot;, &amp;quot;URL&amp;quot;);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;37. 计算两个地图坐标之间的距离&lt;/h3&gt;
 &lt;pre&gt;function getDistanceBetweenPointsNew($latitude1, $longitude1, $latitude2, $longitude2) {
    $theta = $longitude1 - $longitude2;
    $miles = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)));
    $miles = acos($miles);
    $miles = rad2deg($miles);
    $miles = $miles * 60 * 1.1515;
    $feet = $miles * 5280;
    $yards = $feet / 3;
    $kilometers = $miles * 1.609344;
    $meters = $kilometers * 1000;
    return compact(&amp;apos;miles&amp;apos;,&amp;apos;feet&amp;apos;,&amp;apos;yards&amp;apos;,&amp;apos;kilometers&amp;apos;,&amp;apos;meters&amp;apos;); 
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$point1 = array(&amp;apos;lat&amp;apos; =&amp;gt; 40.770623, &amp;apos;long&amp;apos; =&amp;gt; -73.964367);
$point2 = array(&amp;apos;lat&amp;apos; =&amp;gt; 40.758224, &amp;apos;long&amp;apos; =&amp;gt; -73.917404);
$distance = getDistanceBetweenPointsNew($point1[&amp;apos;lat&amp;apos;], $point1[&amp;apos;long&amp;apos;], $point2[&amp;apos;lat&amp;apos;], $point2[&amp;apos;long&amp;apos;]);
foreach ($distance as $unit =&amp;gt; $value) {
    echo $unit.&amp;apos;: &amp;apos;.number_format($value,4).&amp;apos;&amp;lt;br /&amp;gt;&amp;apos;;
}
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;38. 获取一个特定话题标签的所有 Tweets&lt;/h3&gt;
 &lt;pre&gt;function getTweets($hash_tag) {
    $url = &amp;apos;http://search.twitter.com/search.atom?q=&amp;apos;.urlencode($hash_tag) ;
    echo &amp;quot;&amp;lt;p&amp;gt;Connecting to &amp;lt;strong&amp;gt;$url&amp;lt;/strong&amp;gt; ...&amp;lt;/p&amp;gt;&amp;quot;;
    $ch = curl_init($url);
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE);
    $xml = curl_exec ($ch);
    curl_close ($ch);
    //If you want to see the response from Twitter, uncomment this next part out:
    //echo &amp;quot;&amp;lt;p&amp;gt;Response:&amp;lt;/p&amp;gt;&amp;quot;;
    //echo &amp;quot;&amp;lt;pre&amp;gt;&amp;quot;.htmlspecialchars($xml).&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;;
    $affected = 0;
    $twelement = new SimpleXMLElement($xml);
    foreach ($twelement-&amp;gt;entry as $entry) {
        $text = trim($entry-&amp;gt;title);
        $author = trim($entry-&amp;gt;author-&amp;gt;name);
        $time = strtotime($entry-&amp;gt;published);
        $id = $entry-&amp;gt;id;
        echo &amp;quot;&amp;lt;p&amp;gt;Tweet from &amp;quot;.$author.&amp;quot;: &amp;lt;strong&amp;gt;&amp;quot;.$text.&amp;quot;&amp;lt;/strong&amp;gt;  &amp;lt;em&amp;gt;Posted &amp;quot;.date(&amp;apos;n/j/y g:i a&amp;apos;,$time).&amp;quot;&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;;
    }
    return true ;
}&lt;/pre&gt;
 &lt;h3&gt;39. 添加 th,st,nd 或者 rd 作为数字的后缀&lt;/h3&gt;
 &lt;p&gt;Friday the 13th&lt;/p&gt;
 &lt;pre&gt;function ordinal($cdnl){ 
    $test_c = abs($cdnl) % 10; 
    $ext = ((abs($cdnl) %100 &amp;lt; 21 &amp;amp;&amp;amp; abs($cdnl) %100 &amp;gt; 4) ? &amp;apos;th&amp;apos; 
            : (($test_c &amp;lt; 4) ? ($test_c &amp;lt; 3) ? ($test_c &amp;lt; 2) ? ($test_c &amp;lt; 1) 
            ? &amp;apos;th&amp;apos; : &amp;apos;st&amp;apos; : &amp;apos;nd&amp;apos; : &amp;apos;rd&amp;apos; : &amp;apos;th&amp;apos;)); 
    return $cdnl.$ext; 
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$number = 10;
echo ordinal($number); //output is 10th
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;40. 限制文件下载的速度&lt;/h3&gt;
 &lt;pre&gt;&amp;lt;?php
// local file that should be send to the client
$local_file = &amp;apos;test-file.zip&amp;apos;;
// filename that the user gets as default
$download_file = &amp;apos;your-download-name.zip&amp;apos;;
 
// set the download rate limit (=&amp;gt; 20,5 kb/s)
$download_rate = 20.5; 
if(file_exists($local_file) &amp;amp;&amp;amp; is_file($local_file)) {
    // send headers
    header(&amp;apos;Cache-control: private&amp;apos;);
    header(&amp;apos;Content-Type: application/octet-stream&amp;apos;); 
    header(&amp;apos;Content-Length: &amp;apos;.filesize($local_file));
    header(&amp;apos;Content-Disposition: filename=&amp;apos;.$download_file);
 
    // flush content
    flush();    
    // open file stream
    $file = fopen($local_file, &amp;quot;r&amp;quot;);    
    while(!feof($file)) {
 
        // send the current file part to the browser
        print fread($file, round($download_rate * 1024));    
 
        // flush the content to the browser
        flush();
 
        // sleep one second
        sleep(1);    
    }    
 
    // close file stream
    fclose($file);}
else {
    die(&amp;apos;Error: The file &amp;apos;.$local_file.&amp;apos; does not exist!&amp;apos;);
}
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;41. 把文本转换成图片&lt;/h3&gt;
 &lt;pre&gt;&amp;lt;?php
header(&amp;quot;Content-type: image/png&amp;quot;);
$string = $_GET[&amp;apos;text&amp;apos;];
$im = imagecreatefrompng(&amp;quot;images/button.png&amp;quot;);
$color = imagecolorallocate($im, 255, 255, 255);
$px = (imagesx($im) - 7.5 * strlen($string)) / 2;
$py = 9;
$fontSize = 1;
imagestring($im, fontSize, $px, $py, $string, $color);
imagepng($im);
imagedestroy($im);
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;42. 获取远程文件的大小&lt;/h3&gt;
 &lt;pre&gt;function remote_filesize($url, $user = &amp;quot;&amp;quot;, $pw = &amp;quot;&amp;quot;)
{
    ob_start();
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_NOBODY, 1);
    if(!empty($user) &amp;amp;&amp;amp; !empty($pw))
    {
        $headers = array(&amp;apos;Authorization: Basic &amp;apos; .  base64_encode(&amp;quot;$user:$pw&amp;quot;));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }
    $ok = curl_exec($ch);
    curl_close($ch);
    $head = ob_get_contents();
    ob_end_clean();
    $regex = &amp;apos;/Content-Length:\s([0-9].+?)\s/&amp;apos;;
    $count = preg_match($regex, $head, $matches);
    return isset($matches[1]) ? $matches[1] : &amp;quot;unknown&amp;quot;;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$file = &amp;quot;http://koonk.com/images/logo.png&amp;quot;;
$size = remote_filesize($url);
echo $size;
?&amp;gt;&lt;/pre&gt;
 &lt;h2&gt;43. 使用 imagebrick 进行 pdf 到图像的转换&lt;/h2&gt;
 &lt;pre&gt;&amp;lt;?php
$pdf_file   = &amp;apos;./pdf/demo.pdf&amp;apos;;
$save_to    = &amp;apos;./jpg/demo.jpg&amp;apos;;     //make sure that apache has permissions to write in this folder! (common problem)
//execute ImageMagick command &amp;apos;convert&amp;apos; and convert PDF to JPG with applied settings
exec(&amp;apos;convert &amp;quot;&amp;apos;.$pdf_file.&amp;apos;&amp;quot; -colorspace RGB -resize 800 &amp;quot;&amp;apos;.$save_to.&amp;apos;&amp;quot;&amp;apos;, $output, $return_var);
if($return_var == 0) {              //if exec successfuly converted pdf to jpg
    print &amp;quot;Conversion OK&amp;quot;;
}
else print &amp;quot;Conversion failed.&amp;lt;br /&amp;gt;&amp;quot;.$output;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;44. 使用 tinyurl 生成短网址&lt;/h3&gt;
 &lt;pre&gt;function get_tiny_url($url)  
{  
    $ch = curl_init();  
    $timeout = 5;  
    curl_setopt($ch,CURLOPT_URL,&amp;apos;http://tinyurl.com/api-create.php?url=&amp;apos;.$url);  
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);  
    curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$timeout);  
    $data = curl_exec($ch);  
    curl_close($ch);  
    return $data;  
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$url = &amp;quot;http://blog.koonk.com/2015/07/Hello-World&amp;quot;;
$tinyurl = get_tiny_url($url);
echo $tinyurl;
?&amp;gt;&lt;/pre&gt;
 &lt;h3&gt;45. youtube 下载链接生成器&lt;/h3&gt;
 &lt;p&gt;使用下面的 PHP 片段可以让你的用户下载 Youtube 视频&lt;/p&gt;
 &lt;pre&gt;function str_between($string, $start, $end)
{ 
$string = &amp;quot; &amp;quot;.$string; $ini = strpos($string,$start); if ($ini == 0) return &amp;quot;&amp;quot;; $ini += strlen($start); $len = strpos($string,$end,$ini) - $ini; return substr($string,$ini,$len); }
function get_youtube_download_link(){
    $youtube_link = $_GET[&amp;apos;youtube&amp;apos;];
    $youtube_page = file_get_contents($youtube_link);
    $v_id = str_between($youtube_page, &amp;quot;&amp;amp;video_id=&amp;quot;, &amp;quot;&amp;amp;&amp;quot;);
    $t_id = str_between($youtube_page, &amp;quot;&amp;amp;t=&amp;quot;, &amp;quot;&amp;amp;&amp;quot;);
    $flv_link = &amp;quot;http://www.youtube.com/get_video?video_id=$v_id&amp;amp;t=$t_id&amp;quot;;
    $hq_flv_link = &amp;quot;http://www.youtube.com/get_video?video_id=$v_id&amp;amp;t=$t_id&amp;amp;fmt=6&amp;quot;;
    $mp4_link = &amp;quot;http://www.youtube.com/get_video?video_id=$v_id&amp;amp;t=$t_id&amp;amp;fmt=18&amp;quot;;
    $threegp_link = &amp;quot;http://www.youtube.com/get_video?video_id=$v_id&amp;amp;t=$t_id&amp;amp;fmt=17&amp;quot;;
    echo &amp;quot;\t\tDownload (right-click &amp;amp;gt; save as)&amp;amp;#58;\n\t\t&amp;quot;;
    echo &amp;quot;&amp;lt;a href=\&amp;quot;$flv_link\&amp;quot;&amp;gt;FLV&amp;lt;/a&amp;gt;\n\t\t&amp;quot;;
    echo &amp;quot;&amp;lt;a href=\&amp;quot;$hq_flv_link\&amp;quot;&amp;gt;HQ FLV (if available)&amp;lt;/a&amp;gt;\n\t\t&amp;quot;;
    echo &amp;quot;&amp;lt;a href=\&amp;quot;$mp4_link\&amp;quot;&amp;gt;MP4&amp;lt;/a&amp;gt;\n\t\t&amp;quot;;
    echo &amp;quot;&amp;lt;a href=\&amp;quot;$threegp_link\&amp;quot;&amp;gt;3GP&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;\n&amp;quot;;
}&lt;/pre&gt;
 &lt;h3&gt;46. Facebook 样式的时间戳&lt;/h3&gt;
 &lt;p&gt;Facebook (x mins age, y hours ago etc)&lt;/p&gt;
 &lt;pre&gt;function nicetime($date)
{
    if(empty($date)) {
        return &amp;quot;No date provided&amp;quot;;
    }
    
    $periods         = array(&amp;quot;second&amp;quot;, &amp;quot;minute&amp;quot;, &amp;quot;hour&amp;quot;, &amp;quot;day&amp;quot;, &amp;quot;week&amp;quot;, &amp;quot;month&amp;quot;, &amp;quot;year&amp;quot;, &amp;quot;decade&amp;quot;);
    $lengths         = array(&amp;quot;60&amp;quot;,&amp;quot;60&amp;quot;,&amp;quot;24&amp;quot;,&amp;quot;7&amp;quot;,&amp;quot;4.35&amp;quot;,&amp;quot;12&amp;quot;,&amp;quot;10&amp;quot;);
    
    $now             = time();
    $unix_date         = strtotime($date);
    
       // check validity of date
    if(empty($unix_date)) {    
        return &amp;quot;Bad date&amp;quot;;
    }
    // is it future date or past date
    if($now &amp;gt; $unix_date) {    
        $difference     = $now - $unix_date;
        $tense         = &amp;quot;ago&amp;quot;;
        
    } else {
        $difference     = $unix_date - $now;
        $tense         = &amp;quot;from now&amp;quot;;
    }
    
    for($j = 0; $difference &amp;gt;= $lengths[$j] &amp;amp;&amp;amp; $j &amp;lt; count($lengths)-1; $j++) {
        $difference /= $lengths[$j];
    }
    
    $difference = round($difference);
    
    if($difference != 1) {
        $periods[$j].= &amp;quot;s&amp;quot;;
    }
    
    return &amp;quot;$difference $periods[$j] {$tense}&amp;quot;;
}&lt;/pre&gt;
 &lt;p&gt;语法：&lt;/p&gt;
 &lt;pre&gt;&amp;lt;?php
$date = &amp;quot;2015-07-05 03:45&amp;quot;;
$result = nicetime($date); // 2 days ago
?&amp;gt;&lt;/pre&gt;
 &lt;p&gt;via blog.koonk.com&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术</category>
      <guid isPermaLink="true">https://itindex.net/detail/54328-php-%E4%BB%A3%E7%A0%81-%E7%89%87%E6%AE%B5</guid>
      <pubDate>Sun, 13 Sep 2015 10:24:10 CST</pubDate>
    </item>
    <item>
      <title>HTTP 的长连接和短连接</title>
      <link>https://itindex.net/detail/55848-http</link>
      <description>&lt;p&gt;  &lt;strong&gt;本文总结分享网络编程中涉及的长连接、短连接概念。&lt;/strong&gt;&lt;/p&gt;
 &lt;h3&gt;一、什么是长连接&lt;/h3&gt;
 &lt;p&gt;HTTP1.1规定了默认保持长连接（HTTP persistent connection ，也有翻译为持久连接），数据传输完成了保持TCP连接不断开（不发RST包、不四次握手），等待在同域名下继续用这个通道传输数据；相反的就是短连接。&lt;/p&gt;
 &lt;p&gt;HTTP首部的Connection: Keep-alive是HTTP1.0浏览器和服务器的实验性扩展，当前的HTTP1.1 RFC2616文档没有对它做说明，因为它所需要的功能已经默认开启，无须带着它，但是实践中可以发现，浏览器的报文请求都会带上它。如果HTTP1.1版本的HTTP请求报文不希望使用长连接，则要在HTTP请求报文首部加上Connection: close。《HTTP权威指南》提到，有部分古老的HTTP1.0 代理不理解Keep-alive，而导致长连接失效：客户端–&amp;gt;代理–&amp;gt;服务端，客户端带有Keep-alive，而代理不认识，于是将报文原封不动转给了服务端，服务端响应了Keep-alive，也被代理转发给了客户端，于是保持了“客户端–&amp;gt;代理”连接和“代理–&amp;gt;服务端”连接不关闭，但是，当客户端第发送第二次请求时，代理会认为当前连接不会有请求了，于是忽略了它，长连接失效。书上也介绍了解决方案：当发现HTTP版本为1.0时，就忽略Keep-alive，客户端就知道当前不该使用长连接。其实，在实际使用中不需要考虑这么多，很多时候代理是我们自己控制的，如Nginx代理，代理服务器有长连接处理逻辑，服务端无需做patch处理，常见的是客户端跟Nginx代理服务器使用HTTP1.1协议&amp;amp;长连接，而Nginx代理服务器跟后端服务器使用HTTP1.0协议&amp;amp;短连接。&lt;/p&gt;
 &lt;p&gt;在实际使用中，HTTP头部有了Keep-Alive这个值并不代表一定会使用长连接，客户端和服务器端都可以无视这个值，也就是不按标准来，譬如我自己写的HTTP客户端多线程去下载文件，就可以不遵循这个标准，并发的或者连续的多次GET请求，都分开在多个TCP通道中，每一条TCP通道，只有一次GET，GET完之后，立即有TCP关闭的四次握手，这样写代码更简单，这时候虽然HTTP头有Connection: Keep-alive，但不能说是长连接。正常情况下客户端浏览器、web服务端都有实现这个标准，因为它们的文件又小又多，保持长连接减少重新开TCP连接的开销很有价值。&lt;/p&gt;
 &lt;p&gt;以前使用libcurl做的上传/下载，就是短连接，抓包可以看到：1、每一条TCP通道只有一个POST；2、在数据传输完毕可以看到四次握手包。只要不调用curl_easy_cleanup，curl的handle就可能一直有效，可复用。这里说可能，因为连接是双方的，如果服务器那边关掉了，那么我客户端这边保留着也不能实现长连接。&lt;/p&gt;
 &lt;p&gt;如果是使用windows的WinHTTP库，则在POST/GET数据的时候，虽然我关闭了句柄，但这时候TCP连接并不会立即关闭，而是等一小会儿，这时候是WinHTTP库底层支持了跟Keep-alive所需要的功能：即便没有Keep-alive，WinHTTP库也可能会加上这种TCP通道复用的功能，而其它的网络库像libcurl则不会这么做。以前观察过  &lt;a href="http://www.cnblogs.com/cswuyg/p/3516417.html" target="_blank"&gt;WinHTTP库不会及时断开TCP连接&lt;/a&gt;。&lt;/p&gt;
 &lt;h3&gt;二、长连接的过期时间&lt;/h3&gt;
 &lt;p&gt;客户端的长连接不可能无限期的拿着，会有一个超时时间，服务器有时候会告诉客户端超时时间，譬如：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://images.cnitblog.com/i/104985/201404/082313431229684.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;上图中的Keep-Alive: timeout=20，表示这个TCP通道可以保持20秒。另外还可能有max=XXX，表示这个长连接最多接收XXX次请求就断开。对于客户端来说，如果服务器没有告诉客户端超时时间也没关系，服务端可能主动发起四次握手断开TCP连接，客户端能够知道该TCP连接已经无效；另外TCP还有心跳包来检测当前连接是否还活着，方法很多，避免浪费资源。&lt;/p&gt;
 &lt;h3&gt;三、长连接的数据传输完成识别&lt;/h3&gt;
 &lt;p&gt;使用长连接之后，客户端、服务端怎么知道本次传输结束呢？两部分：1是判断传输数据是否达到了Content-Length指示的大小；2动态生成的文件没有Content-Length，它是分块传输（chunked），这时候就要根据chunked编码来判断，chunked编码的数据在最后有一个空chunked块，表明本次传输数据结束。更细节的介绍可以看  &lt;a href="http://www.cnblogs.com/skynet/archive/2010/12/11/1903347.html" target="_blank"&gt;这篇文章&lt;/a&gt;。&lt;/p&gt;
 &lt;h3&gt;四、并发连接数的数量限制&lt;/h3&gt;
 &lt;p&gt;在web开发中需要关注浏览器并发连接的数量，  &lt;a href="http://tools.ietf.org/html/rfc2616#page-47" target="_blank"&gt;RFC文档&lt;/a&gt;说，客户端与服务器最多就连上两通道，但服务器、个人客户端要不要这么做就随人意了，有些服务器就限制同时只能有1个TCP连接，导致客户端的多线程下载（客户端跟服务器连上多条TCP通道同时拉取数据）发挥不了威力，有些服务器则没有限制。浏览器客户端就比较规矩，  &lt;a href="http://www.zhihu.com/question/20474326" target="_blank"&gt;知乎这里有分析&lt;/a&gt;，限制了同域名下能启动若干个并发的TCP连接去下载资源。并发数量的限制也跟长连接有关联，打开一个网页，很多个资源的下载可能就只被放到了少数的几条TCP连接里，这就是TCP通道复用（长连接）。如果并发连接数少，意味着网页上所有资源下载完需要更长的时间（用户感觉页面打开卡了）；并发数多了，服务器可能会产生更高的资源消耗峰值。浏览器只对同域名下的并发连接做了限制，也就意味着，web开发者可以把资源放到不同域名下，同时也把这些资源放到不同的机器上，这样就完美解决了。&lt;/p&gt;
 &lt;h3&gt;五、容易混淆的概念——  &lt;strong&gt;TCP的keep alive和&lt;/strong&gt;  &lt;strong&gt;HTTP的Keep-alive&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;TCP的keep alive是检查当前TCP连接是否活着；HTTP的Keep-alive是要让一个TCP连接活久点。它们是不同层次的概念。&lt;/p&gt;
 &lt;p&gt;TCP keep alive的表现：&lt;/p&gt;
 &lt;p&gt;当一个连接“一段时间”没有数据通讯时，一方会发出一个心跳包（Keep Alive包），如果对方有回包则表明当前连接有效，继续监控。&lt;/p&gt;
 &lt;p&gt;这个“一段时间”可以设置。&lt;/p&gt;
 &lt;p&gt;WinHttp库的设置：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL   &lt;br /&gt;
Sets the interval, in milliseconds, to send a keep-alive packet over the connection. The default interval is 30000 (30 seconds). The minimum interval is 15000 (15 seconds). Using WinHttpSetOption to set a value lower than 15000 will return with ERROR_INVALID_PARAMETER.&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;libcurl的设置：&lt;/p&gt;
 &lt;p&gt;http://curl.haxx.se/libcurl/c/curl_easy_setopt.html&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;CURLOPT_TCP_KEEPALIVE&lt;/p&gt;
  &lt;p&gt;Pass a long. If set to 1, TCP keepalive probes will be sent. The delay and frequency of these probes can be controlled by the CURLOPT_TCP_KEEPIDLE and CURLOPT_TCP_KEEPINTVL options, provided the operating system supports them. Set to 0 (default behavior) to disable keepalive probes (Added in 7.25.0).&lt;/p&gt;
  &lt;p&gt;CURLOPT_TCP_KEEPIDLE&lt;/p&gt;
  &lt;p&gt;Pass a long. Sets the delay, in seconds, that the operating system will wait while the connection is idle before sending keepalive probes. Not all operating systems support this option. (Added in 7.25.0)&lt;/p&gt;
  &lt;p&gt;CURLOPT_TCP_KEEPINTVL&lt;/p&gt;
  &lt;p&gt;Pass a long. Sets the interval, in seconds, that the operating system will wait between sending keepalive probes. Not all operating systems support this option. (Added in 7.25.0)&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;CURLOPT_TCP_KEEPIDLE是空闲多久发送一个心跳包，CURLOPT_TCP_KEEPINTVL是心跳包间隔多久发一个。&lt;/p&gt;
 &lt;p&gt;打开网页抓包，发送心跳包和关闭连接如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://images.cnitblog.com/i/104985/201404/082323582627054.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="file:/C:/Users/cswuyg/AppData/Local/YNote/data/cs_wuyg@126.com/1a0dc51c16644a4293c45ddd853bd838/clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;从上图可以看到，大概过了44秒，客户端发出了心跳包，服务器及时回应，本TCP连接继续保持。到了空闲60秒的时候，服务器主动发起FIN包，断开连接。&lt;/p&gt;
 &lt;h3&gt;六、HTTP 流水线技术&lt;/h3&gt;
 &lt;p&gt;使用了HTTP长连接（HTTP persistent connection ）之后的好处，包括可以使用HTTP 流水线技术（HTTP pipelining，也有翻译为管道化连接），它是指，  &lt;strong&gt;在一个TCP连接内，多个HTTP请求可以并行，下一个HTTP请求在上一个HTTP请求的应答完成之前就发起。&lt;/strong&gt;从wiki上了解到这个技术目前并没有广泛使用，使用这个技术必须要求客户端和服务器端都能支持，目前有部分浏览器完全支持，而服务端的支持仅需要：按HTTP请求顺序正确返回Response（也就是请求&amp;amp;响应采用FIFO模式），wiki里也特地指出，只要服务器能够正确处理使用HTTP pipelinning的客户端请求，那么服务器就算是支持了HTTP pipelining。&lt;/p&gt;
 &lt;p&gt;由于要求服务端返回响应数据的顺序必须跟客户端请求时的顺序一致，这样也就是要求FIFO，这容易导致Head-of-line blocking：第一个请求的响应发送影响到了后边的请求，因为这个原因导致HTTP流水线技术对性能的提升并不明显（wiki提到，这个问题会在HTTP2.0中解决）。另外，使用这个技术的还必须是幂等的HTTP方法，因为客户端无法得知当前已经处理到什么地步，重试后可能发生不可预测的结果。POST方法不是幂等的：同样的报文，第一次POST跟第二次POST在服务端的表现可能会不一样。&lt;/p&gt;
 &lt;p&gt;在HTTP长连接的wiki中提到了HTTP1.1的流水线技术对RFC规定一个用户最多两个连接的指导意义：流水线技术实现好了，那么多连接并不能提升性能。我也觉得如此，并发已经在单个连接中实现了，多连接就没啥必要，除非瓶颈在于单个连接上的资源限制迫使不得不多开连接抢资源。&lt;/p&gt;
 &lt;p&gt;目前浏览器并不太重视这个技术，毕竟性能提升有限。&lt;/p&gt;
 &lt;h3&gt;七、学习资料&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;1、HTTP Keep-Alive模式：   &lt;a href="http://www.cnblogs.com/skynet/archive/2010/12/11/1903347.html"&gt;http://www.cnblogs.com/skynet/archive/2010/12/11/1903347.html&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2、浏览器的并发请求限制：   &lt;a href="http://www.zhihu.com/question/20474326"&gt;http://www.zhihu.com/question/20474326&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;3、RFC文档 connection部分：http://tools.ietf.org/html/rfc2616#page-44&lt;/li&gt;
  &lt;li&gt;4、C/C++网络编程中的TCP保活：    &lt;a href="http://blog.csdn.net/weiwangchao_/article/details/7225338"&gt;http://blog.csdn.net/weiwangchao_/article/details/7225338&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;5、HTTP persistent connection： http://en.wikipedia.org/wiki/HTTP_persistent_connection&lt;/li&gt;
  &lt;li&gt;6、HTTP pipelining：http://en.wikipedia.org/wiki/HTTP_pipelining&lt;/li&gt;
  &lt;li&gt;7、Head-of-line blocking：http://en.wikipedia.org/wiki/Head-of-line_blocking&lt;/li&gt;
  &lt;li&gt;8、《HTTP权威指南》第四章 连接管理&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;
  &lt;div&gt;
   &lt;h3&gt;相关文章&lt;/h3&gt;
   &lt;ul&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/84429/"&gt;通信协议：HTTP、TCP、UDP&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/93960/"&gt;HTTP 长连接和短连接&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/98633/"&gt;StackOverflow 这么大，它的架构是怎么样的？&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/94996/"&gt;Wireshark 基本介绍和学习 TCP 三次握手&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/86656/"&gt;理解 TCP/IP 网络栈  &amp;amp; 编写网络应用&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/42763/"&gt;HTTP代理与SPDY协议&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/96231/"&gt;为什么你得学些 TCP 的知识？&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/74795/"&gt;对TCP/IP网络协议的深入浅出归纳&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/97857/"&gt;从日常开发说起，浅谈HTTP协议是做什么的&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://blog.jobbole.com/21622/"&gt;SPDY 是什么？如何部署 SPDY？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
 &lt;p&gt;  &lt;a href="http://blog.jobbole.com/104108/"&gt;HTTP 的长连接和短连接&lt;/a&gt;，首发于  &lt;a href="http://blog.jobbole.com"&gt;文章 - 伯乐在线&lt;/a&gt;。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>IT技术 HTTP Keep-Alive TCP 网络编程</category>
      <guid isPermaLink="true">https://itindex.net/detail/55848-http</guid>
      <pubDate>Fri, 05 Aug 2016 17:14:38 CST</pubDate>
    </item>
    <item>
      <title>你了解实时计算吗？</title>
      <link>https://itindex.net/detail/54265-%E5%AE%9E%E6%97%B6%E8%AE%A1%E7%AE%97</link>
      <description>&lt;p&gt;  &lt;strong&gt;实时计算是什么？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;请看下面的图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="845" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/b254dc71jw1euhlmw22a6j20f70nh0w0.jpg?737aa3" width="547"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;我们以热卖产品的统计为例，看下传统的计算手段：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;将用户行为、log等信息清洗后保存在数据库中.&lt;/li&gt;
  &lt;li&gt;将订单信息保存在数据库中.&lt;/li&gt;
  &lt;li&gt;利用触发器或者协程等方式建立本地索引，或者远程的独立索引.&lt;/li&gt;
  &lt;li&gt;join订单信息、订单明细、用户信息、商品信息等等表，聚合统计20分钟内热卖产品，并返回top-10.&lt;/li&gt;
  &lt;li&gt;web或app展示.&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;这是一个假想的场景，但假设你具有处理类似场景的经验，应该会体会到这样一些问题和难处：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1.水平扩展问题（scale-out）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;显然，如果是一个具有一定规模的电子商务网站，数据量都是很大的。而交易信息因为涉及事务，所以很难直接舍弃关系型数据库的事务能力，迁移到具有更好的scale-out能力的NoSQL数据库中。&lt;/p&gt;
 &lt;p&gt;那么，一般都会做sharding。历史数据还好说，我们可以按日期来归档，并可以通过批处理式的离线计算，将结果缓存起来。但是，这里的要求是  &lt;strong&gt;20分钟内&lt;/strong&gt;，这很难。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.性能问题&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;这个问题，和scale-out是一致的，假设我们做了sharding，因为表分散在各个节点中，所以我们需要多次入库，并在业务层做聚合计算。&lt;/p&gt;
 &lt;p&gt;问题是，20分钟的时间要求，我们需要入库多少次呢？&lt;/p&gt;
 &lt;p&gt;10分钟呢？5分钟呢？实时呢？&lt;/p&gt;
 &lt;p&gt;而且，业务层也同样面临着单点计算能力的局限，需要水平扩展，那么还需要考虑一致性的问题。所以，到这里一切都显得很复杂。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.业务扩展问题&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;假设我们不仅仅要处理热卖商品的统计，还要统计广告点击、或者迅速根据用户的访问行为判断用户特征以调整其所见的信息，更加符合用户的潜在需求等，那么业务层将会更加复杂。&lt;/p&gt;
 &lt;p&gt;也许你有更好的办法，但实际上，我们需要的是一种新的认知：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;这个世界发生的事，是实时的。&lt;/p&gt;
  &lt;p&gt;所以我们需要一种实时计算的模型，而不是批处理模型。&lt;/p&gt;
  &lt;p&gt;我们需要的这种模型，必须能够处理很大的数据，所以要有很好的scale-out能力，最好是，我们都不需要考虑太多一致性、复制的问题。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;那么，这种计算模型就是实时计算模型，也可以认为是流式计算模型。&lt;/p&gt;
 &lt;p&gt;现在假设我们有了这样的模型，我们就可以愉快地设计新的业务场景：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;转发最多的微博是什么？&lt;/li&gt;
  &lt;li&gt;最热卖的商品有哪些？&lt;/li&gt;
  &lt;li&gt;大家都在搜索的热点是什么？&lt;/li&gt;
  &lt;li&gt;我们哪个广告，在哪个位置，被点击最多？&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;或者说，我们可以问：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;这个世界，在发生什么？&lt;/p&gt;&lt;/blockquote&gt;
 &lt;h2&gt;  &lt;strong&gt;最热的微博话题是什么？&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;我们以一个简单的滑动窗口计数的问题，来揭开所谓实时计算的神秘面纱。&lt;/p&gt;
 &lt;p&gt;假设，我们的业务要求是：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;统计20分钟内最热的10个微博话题。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;解决这个问题，我们需要考虑：&lt;/p&gt;
 &lt;p&gt;1.数据源&lt;/p&gt;
 &lt;p&gt;这里，假设我们的数据，来自微博长连接推送的话题。&lt;/p&gt;
 &lt;p&gt;2.问题建模&lt;/p&gt;
 &lt;p&gt;我们认为的话题是  &lt;code&gt;#&lt;/code&gt;号扩起来的话题，最热的话题是此话题出现的次数比其它话题都要多。&lt;/p&gt;
 &lt;p&gt;比如：@foreach_break : 你好,#世界#,我爱你，#微博#。&lt;/p&gt;
 &lt;p&gt;“世界”和“微博”就是话题。&lt;/p&gt;
 &lt;p&gt;3.计算引擎&lt;/p&gt;
 &lt;p&gt;我们采用storm。&lt;/p&gt;
 &lt;p&gt;4.定义时间&lt;/p&gt;
 &lt;h2&gt;  &lt;strong&gt;如何定义时间？&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;时间的定义是一件很难的事情，取决于所需的精度是多少。&lt;/p&gt;
 &lt;p&gt;根据实际，我们一般采用tick来表示时刻这一概念。&lt;/p&gt;
 &lt;p&gt;在storm的基础设施中，executor启动阶段，采用了定时器来触发“过了一段时间”这个事件。如下所示：&lt;/p&gt;
 &lt;pre&gt;(defn setup-ticks! [worker executor-data]
  (let [storm-conf (:storm-conf executor-data)
        tick-time-secs (storm-conf TOPOLOGY-TICK-TUPLE-FREQ-SECS)
        receive-queue (:receive-queue executor-data)
        context (:worker-context executor-data)]
    (when tick-time-secs
      (if (or (system-id? (:component-id executor-data))
              (and (= false (storm-conf TOPOLOGY-ENABLE-MESSAGE-TIMEOUTS))
                   (= :spout (:type executor-data))))
        (log-message &amp;quot;Timeouts disabled for executor &amp;quot; (:component-id executor-data) &amp;quot;:&amp;quot; (:executor-id executor-data))
        (schedule-recurring
          (:user-timer worker)
          tick-time-secs
          tick-time-secs
          (fn []
            (disruptor/publish
              receive-queue
              [[nil (TupleImpl. context [tick-time-secs] Constants/SYSTEM_TASK_ID Constants/SYSTEM_TICK_STREAM_ID)]]
              )))))))&lt;/pre&gt;
 &lt;p&gt;之前的博文中，已经详细分析了这些基础设施的关系，不理解的童鞋可以翻看前面的文章。&lt;/p&gt;
 &lt;p&gt;每隔一段时间，就会触发这样一个事件，当流的下游的bolt收到一个这样的事件时，就可以选择是增量计数还是将结果聚合并发送到流中。&lt;/p&gt;
 &lt;p&gt;bolt如何判断收到的tuple表示的是“tick”呢？&lt;/p&gt;
 &lt;p&gt;负责管理bolt的executor线程，从其订阅的消息队列消费消息时，会调用到bolt的execute方法，那么，可以在execute中这样判断：&lt;/p&gt;
 &lt;pre&gt;public static boolean isTick(Tuple tuple) {
    return tuple != null
           &amp;amp;&amp;amp; Constants.SYSTEM_COMPONENT_ID  .equals(tuple.getSourceComponent())
           &amp;amp;&amp;amp; Constants.SYSTEM_TICK_STREAM_ID.equals(tuple.getSourceStreamId());
}&lt;/pre&gt;
 &lt;p&gt;结合上面的setup-tick!的clojure代码，我们可以知道SYSTEM_TICK_STREAM_ID在定时事件的回调中就以构造函数的参数传递给了tuple，那么SYSTEM_COMPONENT_ID是如何来的呢？&lt;/p&gt;
 &lt;p&gt;可以看到，下面的代码中，SYSTEM_TASK_ID同样传给了tuple：&lt;/p&gt;
 &lt;pre&gt;//请注意SYSTEM_TASK_ID和SYSTEM_TICK_STREAM_ID
(TupleImpl. context [tick-time-secs] Constants/SYSTEM_TASK_ID Constants/SYSTEM_TICK_STREAM_ID)&lt;/pre&gt;
 &lt;p&gt;然后利用下面的代码，就可以得到SYSTEM_COMPONENT_ID：&lt;/p&gt;
 &lt;pre&gt;public String getComponentId(int taskId) {
        if(taskId==Constants.SYSTEM_TASK_ID) {
            return Constants.SYSTEM_COMPONENT_ID;
        } else {
            return _taskToComponent.get(taskId);
        }
    }&lt;/pre&gt;
 &lt;h2&gt;  &lt;strong&gt;滑动窗口&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;有了上面的基础设施，我们还需要一些手段来完成“工程化”，将设想变为现实。&lt;/p&gt;
 &lt;p&gt;这里，我们看看Michael G. Noll的滑动窗口设计。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="326" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/b254dc71jw1euhlmwgqudj20l009xjsd.jpg?737aa3" width="690"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;注：图片来自http://www.michael-noll.com/blog/2013/01/18/implementing-real-time-trending-topics-in-storm/&lt;/p&gt;
 &lt;h3&gt;  &lt;strong&gt;Topology&lt;/strong&gt;&lt;/h3&gt;
 &lt;pre&gt;String spoutId = &amp;quot;wordGenerator&amp;quot;;
    String counterId = &amp;quot;counter&amp;quot;;
    String intermediateRankerId = &amp;quot;intermediateRanker&amp;quot;;
    String totalRankerId = &amp;quot;finalRanker&amp;quot;;
    // 这里，假设TestWordSpout就是我们发送话题tuple的源
    builder.setSpout(spoutId, new TestWordSpout(), 5);
    // RollingCountBolt的时间窗口为9秒钟，每3秒发送一次统计结果到下游
    builder.setBolt(counterId, new RollingCountBolt(9, 3), 4).fieldsGrouping(spoutId, new Fields(&amp;quot;word&amp;quot;));
    // IntermediateRankingsBolt，将完成部分聚合，统计出top-n的话题
    builder.setBolt(intermediateRankerId, new IntermediateRankingsBolt(TOP_N), 4).fieldsGrouping(counterId, new Fields(
        &amp;quot;obj&amp;quot;));
        // TotalRankingsBolt， 将完成完整聚合，统计出top-n的话题
    builder.setBolt(totalRankerId, new TotalRankingsBolt(TOP_N)).globalGrouping(intermediateRankerId);&lt;/pre&gt;
 &lt;p&gt;上面的topology设计如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="364" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/b254dc71jw1euhlmwwymfj20kg0asq4p.jpg?737aa3" width="690"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;注：图片来自http://www.michael-noll.com/blog/2013/01/18/implementing-real-time-trending-topics-in-storm/&lt;/p&gt;
 &lt;h3&gt;  &lt;strong&gt;将聚合计算与时间结合起来&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;前文，我们叙述了tick事件，回调中会触发bolt的execute方法，那可以这么做：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;RollingCountBolt&lt;/strong&gt;:&lt;/p&gt;
 &lt;pre&gt;@Override
  public void execute(Tuple tuple) {
    if (TupleUtils.isTick(tuple)) {
      LOG.debug(&amp;quot;Received tick tuple, triggering emit of current window counts&amp;quot;);
      // tick来了，将时间窗口内的统计结果发送，并让窗口滚动
      emitCurrentWindowCounts();
    }
    else {
      // 常规tuple，对话题计数即可
      countObjAndAck(tuple);
    }
  }

  // obj即为话题，增加一个计数 count++
  // 注意，这里的速度基本取决于流的速度，可能每秒百万，也可能每秒几十.
  // 内存不足？ bolt可以scale-out.
  private void countObjAndAck(Tuple tuple) {
    Object obj = tuple.getValue(0);
    counter.incrementCount(obj);
    collector.ack(tuple);
  }

  // 将统计结果发送到下游
	private void emitCurrentWindowCounts() {
	  Map&amp;lt;Object, Long&amp;gt; counts = counter.getCountsThenAdvanceWindow();
	  int actualWindowLengthInSeconds = lastModifiedTracker.secondsSinceOldestModification();
	  lastModifiedTracker.markAsModified();
	  if (actualWindowLengthInSeconds != windowLengthInSeconds) {
	    LOG.warn(String.format(WINDOW_LENGTH_WARNING_TEMPLATE, actualWindowLengthInSeconds, windowLengthInSeconds));
	  }
	  emit(counts, actualWindowLengthInSeconds);
	}&lt;/pre&gt;
 &lt;p&gt;上面的代码可能有点抽象，看下这个图就明白了，tick一到，窗口就滚动：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="429" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/b254dc71jw1euhlmxgkbuj20jc0c1dhe.jpg?737aa3" width="690"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;注：图片来自http://www.michael-noll.com/blog/2013/01/18/implementing-real-time-trending-topics-in-storm/&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;IntermediateRankingsBolt &amp;amp; TotalRankingsBolt：&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;public final void execute(Tuple tuple, BasicOutputCollector collector) {
    if (TupleUtils.isTick(tuple)) {
      getLogger().debug(&amp;quot;Received tick tuple, triggering emit of current rankings&amp;quot;);
      // 将聚合并排序的结果发送到下游
      emitRankings(collector);
    }
    else {
      // 聚合并排序
      updateRankingsWithTuple(tuple);
    }
  }&lt;/pre&gt;
 &lt;p&gt;其中，IntermediateRankingsBolt和TotalRankingsBolt的聚合排序方法略有不同：&lt;/p&gt;
 &lt;p&gt;IntermediateRankingsBolt的聚合排序方法：&lt;/p&gt;
 &lt;pre&gt;// IntermediateRankingsBolt的聚合排序方法：
  @Override
  void updateRankingsWithTuple(Tuple tuple) {
    // 这一步，将话题、话题出现的次数提取出来
    Rankable rankable = RankableObjectWithFields.from(tuple);
    // 这一步，将话题出现的次数进行聚合，然后重排序所有话题
    super.getRankings().updateWith(rankable);
  }&lt;/pre&gt;
 &lt;p&gt;TotalRankingsBolt的聚合排序方法：&lt;/p&gt;
 &lt;pre&gt;// TotalRankingsBolt的聚合排序方法
  @Override
  void updateRankingsWithTuple(Tuple tuple) {
  // 提出来自IntermediateRankingsBolt的中间结果
    Rankings rankingsToBeMerged = (Rankings) tuple.getValue(0);
  // 聚合并排序
    super.getRankings().updateWith(rankingsToBeMerged);
  // 去0，节约内存
    super.getRankings().pruneZeroCounts();
  }&lt;/pre&gt;
 &lt;p&gt;而重排序方法比较简单粗暴，因为只求前N个，N不会很大：&lt;/p&gt;
 &lt;pre&gt;private void rerank() {
    Collections.sort(rankedItems);
    Collections.reverse(rankedItems);
  }&lt;/pre&gt;
 &lt;h2&gt;  &lt;strong&gt;结语&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;下图可能就是我们想要的结果，我们完成了t0 – t1时刻之间的热点话题统计，其中的foreach_break仅仅是为了防盗版 : ].&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="186" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/b254dc71jw1euhlmxyvq9j20b6056mxr.jpg?737aa3" width="402"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;文中对滑动窗口计数的概念和关键代码做了较为详细解释，如果还有不理解，请参考http://www.michael-noll.com/blog/2013/01/18/implementing-real-time-trending-topics-in-storm/ 的设计以及storm的源码.&lt;/p&gt;
 &lt;p&gt;希望你了解了什么是实时计算 ：]&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术 实时计算</category>
      <guid isPermaLink="true">https://itindex.net/detail/54265-%E5%AE%9E%E6%97%B6%E8%AE%A1%E7%AE%97</guid>
      <pubDate>Fri, 28 Aug 2015 19:41:48 CST</pubDate>
    </item>
    <item>
      <title>10年DotNet老程序员推荐的7个开发工具</title>
      <link>https://itindex.net/detail/54214-dotnet-%E7%A8%8B%E5%BA%8F%E5%91%98-%E5%BC%80%E5%8F%91</link>
      <description>&lt;p&gt;做.NET软件工作已经10年了，从程序员做到高级程序员，再到技术主管，技术总监。见证了Visual Studio .NET 2003,Visul Studio 2005, Visual Studio Team System 2008, Visual Studio 2010 Ultimate,Visual Studio 2013一系列近5个版本的变化与亲自使用。每一个版本都有深刻有意义的变革。Visual Studio .NET 2003 极力推广的Web Services虽然没有很流行，但是它开发ASP.NET的编译模型很快得到公司的技术部认可，完全重写了原来的ASP系统。&lt;/p&gt;
 &lt;p&gt;很幸运工作以来一直是.NET + SQL Server 的开发工作，其它比如Oracle或是Android Java类的技术工作并没有很长时间的经历，所以我推荐的工作就侧重于.NET + SQL Server类的工具。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1 ReSharper 7.1&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;现在电脑开发人员硬件的标准配置是i5+8G，在内存已经足够用的情况下，推荐安装好Visual Studio后第一个要安装的插件就是&lt;/p&gt;
 &lt;p&gt;ReSharper，这个软件的几百个功能点都很实用。最新版本的ReSharper是ReSharper.2015.1.Ultimate.9.1.0，支持Visual Studio 2015。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2 .NET Reflector&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;著名的.NET Reflector包含在此工具箱中。.NET Reflector我常常用来查看编译过的程序集，看命名空间是否规范，看类型的成员命名是否合理，如下图所示。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="437" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/aa213e02jw1eu5t1se5zqj20py0gfab6.jpg?8f3a10" width="690"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;作为一个多年从事开发的程序员，看到代码中包含WindowsApplication1,button1, panel1之类的命名总会是非常的反感。这样没有意义的命名，给程序代码的维护人员带来诸多的痛苦。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3 SQL Prompt 6&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;安装好SQL Server 后的第一个要做的事就是安装SQL Prompt插件，这个工具扩展了SQL Server Managment Studio中书写SQL代码的智能提示，让书写SQL语句更方便，快速，准确。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="392" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/aa213e02jw1eu5t1taov9j20qw0fadhi.jpg?8f3a10" width="690"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;图中是经典的SQL Prompt代码智能提示窗口。当鼠标旋停在一个对象(表名，视图名称，字段名）上时，SQL Prompt会显示这个对象的属性。比如上图中所示，显示了当前Part表的所有字段的脚本。&lt;/p&gt;
 &lt;p&gt;SQL Prompt常常包含在SQLToolbelt工具包中，这个系列的软件包工具都很实用。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4 SQL Compare&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;做数据库开发过程中，常常会添加一些脚本，当把这些脚本部署到其它电脑中时，用手工增加脚本的方法过于原始又容易出错，于是SQL Compare的出现，完全满足了我的需求。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="392" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/aa213e02jw1eu5t1tpfe7j20q60ev3zc.jpg?8f3a10" width="690"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;如上图所示，在开发的电脑上用SQL Server Management Studio修改表或视图定义，当需要把这个脚本部署到另外一个数据库或电脑中时，运行此工具，选择两边的数据库作一个计较即可完成。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5 Code Smith 6.5&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;工作中经常会遇到一些相似的代码编写工具，会考虑将这些共同的重复的代码封装成Code Smith模板文件。将可变部分提取为参数，运行时选择或输入参数即生成需要的代码。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;6 .NET Reactor&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;经常要给客户或是准客户做一些例子代码，又不想将代码完全给未付款的客户。对于.NET的程序集格式文件，把没有经过混淆的文件传送给客户，即可认为将源代码给客户。常常是与客户打交道时，客户还没有付款，程序又不能不发给客户测试，只好用.NET Reactor加壳混淆处理。这个也不是完全有作用，现在去壳反混淆的工具非常多，准确率也很高。&lt;/p&gt;
 &lt;p&gt;做这一步动作也只是为了让代码被反编译之后，阅读相对困难。不过，优质的客户是预付款之后，再提供程序或是技术支持，这个就没有加密混淆的必要了。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;7 LLBL Gen Pro&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;接触LLBL Gen Pro有6年多了，从一开始的不熟悉，困惑，到最后完全接受，并且能用这个工具独立带领团队开发项目，深深知道ORM对企业应用开发的意义重大。ORM生成表对应的映射的实体，通过对实体的操作，实现OOP面向对象编程，代码的可维护性，可阅读性方面有了极大的提升。在任何时候，我都会将这个工具作为首选工具推荐给未接触ORM开发的朋友。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术</category>
      <guid isPermaLink="true">https://itindex.net/detail/54214-dotnet-%E7%A8%8B%E5%BA%8F%E5%91%98-%E5%BC%80%E5%8F%91</guid>
      <pubDate>Fri, 21 Aug 2015 19:54:57 CST</pubDate>
    </item>
    <item>
      <title>10 个 Redis 建议/技巧</title>
      <link>https://itindex.net/detail/54202-redis-%E6%8A%80%E5%B7%A7</link>
      <description>&lt;p&gt;Redis 在当前的技术社区里是非常热门的。从来自 Antirez 一个小小的个人项目到成为内存数据存储行业的标准，Redis已经走过了很长的一段路。随之而来的一系列最佳实践，使得大多数人可以正确地使用 Redis。下面我们将探索正确使用 Redis 的10个技巧。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1、停止使用 KEYS *&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Okay，以挑战这个命令开始这篇文章，或许并不是一个好的方式，但其确实可能是最重要的一点。很多时候当我们关注一个redis实例的统计数据，我们会快速地输入”KEYS *”命令，这样key的信息会很明显地展示出来。平心而论，从程序化的角度出发往往倾向于写出下面这样的伪代码：&lt;/p&gt;
 &lt;pre&gt;for key in&amp;apos;keys *&amp;apos;:

doAllTheThings()&lt;/pre&gt;
 &lt;p&gt;但是当你有1300万个key时，执行速度将会变慢。因为KEYS命令的时间复杂度是O(n)，其中n是要返回的keys的个数，这样这个命令的复杂度就取决于数据库的大小了。并且在这个操作执行期间，其它任何命令在你的实例中都无法执行。&lt;/p&gt;
 &lt;p&gt;作为一个替代命令，看一下 SCAN 吧，其允许你以一种更友好的方式来执行… SCAN 通过增量迭代的方式来扫描数据库。这一操作基于游标的迭代器来完成的，因此只要你觉得合适，你可以随时停止或继续。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2、找出拖慢 Redis 的罪魁祸首&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;由于 Redis 没有非常详细的日志，要想知道在 Redis 实例内部都做了些什么是非常困难的。幸运的是 Redis 提供了一个下面这样的命令统计工具：&lt;/p&gt;
 &lt;pre&gt;127.0.0.1:6379&amp;gt; INFO commandstats
# Commandstats
cmdstat_get:calls=78,usec=608,usec_per_call=7.79
cmdstat_setex:calls=5,usec=71,usec_per_call=14.20
cmdstat_keys:calls=2,usec=42,usec_per_call=21.00
cmdstat_info:calls=10,usec=1931,usec_per_call=193.10&lt;/pre&gt;
 &lt;p&gt;通过这个工具可以查看所有命令统计的快照，比如命令执行了多少次，执行命令所耗费的毫秒数(每个命令的总时间和平均时间)&lt;/p&gt;
 &lt;p&gt;只需要简单地执行 CONFIG RESETSTAT 命令就可以重置，这样你就可以得到一个全新的统计结果。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3、 将 Redis-Benchmark 结果作为参考，而不要一概而论&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Redis 之父 Salvatore 就说过：“通过执行GET/SET命令来测试Redis就像在雨天检测法拉利的雨刷清洁镜子的效果”。很多时候人们跑到我这里，他们想知道为什么自己的Redis-Benchmark统计的结果低于最优结果 。但我们必须要把各种不同的真实情况考虑进来，例如：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;可能受到哪些客户端运行环境的限制？&lt;/li&gt;
  &lt;li&gt;是同一个版本号吗？&lt;/li&gt;
  &lt;li&gt;测试环境中的表现与应用将要运行的环境是否一致？&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Redis-Benchmark的测试结果提供了一个保证你的 Redis-Server 不会运行在非正常状态下的基准点，但是你永远不要把它作为一个真实的“压力测试”。压力测试需要反应出应用的运行方式，并且需要一个尽可能的和生产相似的环境。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4、Hashes 是你的最佳选择&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;以一种优雅的方式引入 hashes 吧。hashes 将会带给你一种前所未有的体验。之前我曾看到过许多类似于下面这样的key结构：&lt;/p&gt;
 &lt;pre&gt;foo:first_name
foo:last_name
foo:address&lt;/pre&gt;
 &lt;p&gt;上面的例子中，foo 可能是一个用户的用户名，其中的每一项都是一个单独的 key。这就增加了 犯错的空间，和一些不必要的 key。使用 hash 代替吧，你会惊奇地发现竟然只需要一个 key ：&lt;/p&gt;
 &lt;pre&gt;127.0.0.1:6379&amp;gt; HSET foo first_name &amp;quot;Joe&amp;quot;(integer) 1
127.0.0.1:6379&amp;gt; HSET foo last_name &amp;quot;Engel&amp;quot;(integer) 1
127.0.0.1:6379&amp;gt; HSET foo address &amp;quot;1 Fanatical Pl&amp;quot;(integer) 1
127.0.0.1:6379&amp;gt; HGETALL foo
1)&amp;quot;first_name&amp;quot;
2)&amp;quot;Joe&amp;quot;
3)&amp;quot;last_name&amp;quot;
4)&amp;quot;Engel&amp;quot;
5)&amp;quot;address&amp;quot;
6)&amp;quot;1 Fanatical Pl&amp;quot;
127.0.0.1:6379&amp;gt; HGET foo first_name
&amp;quot;Joe&amp;quot;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;5、设置 key 值的存活时间&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;无论什么时候，只要有可能就利用key超时的优势。一个很好的例子就是储存一些诸如临时认证key之类的东西。当你去查找一个授权key时——以OAUTH为例——通常会得到一个超时时间。这样在设置key的时候，设成同样的超时时间，Redis就会自动为你清除！而不再需要使用KEYS *来遍历所有的key了，怎么样很方便吧？&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;6、 选择合适的回收策略&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;既然谈到了清除key这个话题，那我们就来聊聊回收策略。当 Redis 的实例空间被填满了之后，将会尝试回收一部分key。根据你的使用方式，我强烈建议使用 volatile-lru 策略——前提是你对key已经设置了超时。但如果你运行的是一些类似于 cache 的东西，并且没有对 key 设置超时机制，可以考虑使用 allkeys-lru 回收机制。我的建议是先在这里查看一下可行的方案。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;7、如果你的数据很重要，请使用 Try/Except&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;如果必须确保关键性的数据可以被放入到 Redis 的实例中，我强烈建议将其放入 try/except 块中。几乎所有的Redis客户端采用的都是“发送即忘”策略，因此经常需要考虑一个 key 是否真正被放到 Redis 数据库中了。至于将 try/expect 放到 Redis 命令中的复杂性并不是本文要讲的，你只需要知道这样做可以确保重要的数据放到该放的地方就可以了。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;8、不要耗尽一个实例&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;无论什么时候，只要有可能就分散多redis实例的工作量。从3.0.0版本开始，Redis就支持集群了。Redis集群允许你基于key范围分离出部分包含主/从模式的key。完整的集群背后的“魔法”可以在这里找到。但如果你是在找教程，那这里是一个再适合不过的地方了。如果不能选择集群，考虑一下命名空间吧，然后将你的key分散到多个实例之中。关于怎样分配数据，在redis.io网站上有这篇精彩的评论。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;9、内核越多越好吗？！&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;当然是错的。Redis 是一个单线程进程，即使启用了持久化最多也只会消耗两个内核。除非你计划在一台主机上运行多个实例——希望只会是在开发测试的环境下！——否则的话对于一个 Redis 实例是不需要2个以上内核的。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;10、高可用&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;到目前为止 Redis Sentinel 已经经过了很全面的测试，很多用户已经将其应用到了生产环境中（包括 ObjectRocket ）。如果你的应用重度依赖于 Redis ，那就需要想出一个高可用方案来保证其不会掉线。当然，如果不想自己管理这些东西，ObjectRocket 提供了一个高可用平台，并提供7×24小时的技术支持，有意向的话可以考虑一下。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术 Redis 建议/技巧</category>
      <guid isPermaLink="true">https://itindex.net/detail/54202-redis-%E6%8A%80%E5%B7%A7</guid>
      <pubDate>Wed, 19 Aug 2015 19:58:44 CST</pubDate>
    </item>
    <item>
      <title>分布式RPC框架性能大比拼</title>
      <link>https://itindex.net/detail/55952-%E5%88%86%E5%B8%83-rpc-%E6%A1%86%E6%9E%B6</link>
      <description>&lt;p&gt;  &lt;a href="http://colobu.com/"&gt;Dubbo&lt;/a&gt; 是阿里巴巴公司开源的一个Java高性能优秀的服务框架，使得应用可通过高性能的 RPC 实现服务的输出和输入功能，可以和 Spring框架无缝集成。不过，略有遗憾的是，据说在淘宝内部，dubbo由于跟淘宝另一个类似的框架HSF（非开源）有竞争关系，导致dubbo团队已经解散（参见  &lt;a href="http://www.oschina.net/news/55059/druid-1-0-9" rel="external" target="_blank"&gt;http://www.oschina.net/news/55059/druid-1-0-9&lt;/a&gt; 中的评论），反到是当当网的扩展版本仍在持续发展，墙内开花墙外香。其它的一些知名电商如当当、京东、国美维护了自己的分支或者在dubbo的基础开发，但是官方的库缺乏维护，相关的依赖类比如Spring，Netty还是很老的版本(Spring 3.2.16.RELEASE, netty 3.2.5.Final),倒是有些网友写了升级Spring和Netty的插件。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://github.com/weibocom/motan" rel="external" target="_blank"&gt;Montan&lt;/a&gt;是新浪微博开源的一个Java 框架。它诞生的比较晚，起于2013年，2016年5月开源。Motan 在微博平台中已经广泛应用，每天为数百个服务完成近千亿次的调用。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://github.com/smallnest/rpcx" rel="external" target="_blank"&gt;rpcx&lt;/a&gt;是Go语言生态圈的Dubbo， 比Dubbo更轻量，实现了Dubbo的许多特性，借助于Go语言优秀的并发特性和简洁语法，可以使用较少的代码实现分布式的RPC服务。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://www.grpc.io/" rel="external" target="_blank"&gt;gRPC&lt;/a&gt;是Google开发的高性能、通用的开源RPC框架，其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计，基于ProtoBuf(Protocol Buffers)序列化协议开发，且支持众多开发语言。本身它不是分布式的，所以要实现上面的框架的功能需要进一步的开发。&lt;/p&gt;
 &lt;a&gt;&lt;/a&gt;
 &lt;p&gt;以下是它们的功能比较：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;&lt;/th&gt;
   &lt;th&gt;Dubbo&lt;/th&gt;
   &lt;th&gt;Montan&lt;/th&gt;
   &lt;th&gt;rpcx&lt;/th&gt;
   &lt;th&gt;gRPC&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;开发语言&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;Go&lt;/td&gt;
   &lt;td&gt;跨语言&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;分布式&lt;/td&gt;
   &lt;td&gt;√&lt;/td&gt;
   &lt;td&gt;√&lt;/td&gt;
   &lt;td&gt;√&lt;/td&gt;
   &lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;多序列化框架支持&lt;/td&gt;
   &lt;td&gt;√    &lt;br /&gt; &lt;/td&gt;
   &lt;td&gt;√     &lt;br /&gt;(当前支持Hessian2、Json,可扩展)&lt;/td&gt;
   &lt;td&gt;√    &lt;br /&gt; &lt;/td&gt;
   &lt;td&gt;×     &lt;br /&gt;(只支持protobuf)&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;多种注册中心&lt;/td&gt;
   &lt;td&gt;√&lt;/td&gt;
   &lt;td&gt;√&lt;/td&gt;
   &lt;td&gt;√&lt;/td&gt;
   &lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;管理中心&lt;/td&gt;
   &lt;td&gt;√&lt;/td&gt;
   &lt;td&gt;√&lt;/td&gt;
   &lt;td&gt;×&lt;/td&gt;
   &lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;跨编程语言&lt;/td&gt;
   &lt;td&gt;×&lt;/td&gt;
   &lt;td&gt;× (支持php client和C server)&lt;/td&gt;
   &lt;td&gt;×&lt;/td&gt;
   &lt;td&gt;√&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;对于RPC的考察， 性能是很重要的一点，因为RPC框架经常用在服务的大并发调用的环境中，性能的好坏决定服务的质量以及公司在硬件部署上的花费。&lt;/p&gt;
 &lt;p&gt;本文通过一个统一的服务，测试这四种框架实现的完整的服务器端和客户端的性能。  &lt;br /&gt;这个服务传递的消息体有一个protobuf文件定义：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;     &lt;div&gt;16&lt;/div&gt;     &lt;div&gt;17&lt;/div&gt;     &lt;div&gt;18&lt;/div&gt;     &lt;div&gt;19&lt;/div&gt;     &lt;div&gt;20&lt;/div&gt;     &lt;div&gt;21&lt;/div&gt;     &lt;div&gt;22&lt;/div&gt;     &lt;div&gt;23&lt;/div&gt;     &lt;div&gt;24&lt;/div&gt;     &lt;div&gt;25&lt;/div&gt;     &lt;div&gt;26&lt;/div&gt;     &lt;div&gt;27&lt;/div&gt;     &lt;div&gt;28&lt;/div&gt;     &lt;div&gt;29&lt;/div&gt;     &lt;div&gt;30&lt;/div&gt;     &lt;div&gt;31&lt;/div&gt;     &lt;div&gt;32&lt;/div&gt;     &lt;div&gt;33&lt;/div&gt;     &lt;div&gt;34&lt;/div&gt;     &lt;div&gt;35&lt;/div&gt;     &lt;div&gt;36&lt;/div&gt;     &lt;div&gt;37&lt;/div&gt;     &lt;div&gt;38&lt;/div&gt;     &lt;div&gt;39&lt;/div&gt;     &lt;div&gt;40&lt;/div&gt;     &lt;div&gt;41&lt;/div&gt;     &lt;div&gt;42&lt;/div&gt;     &lt;div&gt;43&lt;/div&gt;     &lt;div&gt;44&lt;/div&gt;     &lt;div&gt;45&lt;/div&gt;     &lt;div&gt;46&lt;/div&gt;     &lt;div&gt;47&lt;/div&gt;     &lt;div&gt;48&lt;/div&gt;     &lt;div&gt;49&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;syntax = &amp;quot;proto2&amp;quot;;&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;package main;&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;option optimize_for = SPEED;&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;message BenchmarkMessage {&lt;/div&gt;     &lt;div&gt;  required string field1 = 1;&lt;/div&gt;     &lt;div&gt;  optional string field9 = 9;&lt;/div&gt;     &lt;div&gt;  optional string field18 = 18;&lt;/div&gt;     &lt;div&gt;  optional bool field80 = 80 [default=false];&lt;/div&gt;     &lt;div&gt;  optional bool field81 = 81 [default=true];&lt;/div&gt;     &lt;div&gt;  required int32 field2 = 2;&lt;/div&gt;     &lt;div&gt;  required int32 field3 = 3;&lt;/div&gt;     &lt;div&gt;  optional int32 field280 = 280;&lt;/div&gt;     &lt;div&gt;  optional int32 field6 = 6 [default=0];&lt;/div&gt;     &lt;div&gt;  optional int64 field22 = 22;&lt;/div&gt;     &lt;div&gt;  optional string field4 = 4;&lt;/div&gt;     &lt;div&gt;  repeated fixed64 field5 = 5;&lt;/div&gt;     &lt;div&gt;  optional bool field59 = 59 [default=false];&lt;/div&gt;     &lt;div&gt;  optional string field7 = 7;&lt;/div&gt;     &lt;div&gt;  optional int32 field16 = 16;&lt;/div&gt;     &lt;div&gt;  optional int32 field130 = 130 [default=0];&lt;/div&gt;     &lt;div&gt;  optional bool field12 = 12 [default=true];&lt;/div&gt;     &lt;div&gt;  optional bool field17 = 17 [default=true];&lt;/div&gt;     &lt;div&gt;  optional bool field13 = 13 [default=true];&lt;/div&gt;     &lt;div&gt;  optional bool field14 = 14 [default=true];&lt;/div&gt;     &lt;div&gt;  optional int32 field104 = 104 [default=0];&lt;/div&gt;     &lt;div&gt;  optional int32 field100 = 100 [default=0];&lt;/div&gt;     &lt;div&gt;  optional int32 field101 = 101 [default=0];&lt;/div&gt;     &lt;div&gt;  optional string field102 = 102;&lt;/div&gt;     &lt;div&gt;  optional string field103 = 103;&lt;/div&gt;     &lt;div&gt;  optional int32 field29 = 29 [default=0];&lt;/div&gt;     &lt;div&gt;  optional bool field30 = 30 [default=false];&lt;/div&gt;     &lt;div&gt;  optional int32 field60 = 60 [default=-1];&lt;/div&gt;     &lt;div&gt;  optional int32 field271 = 271 [default=-1];&lt;/div&gt;     &lt;div&gt;  optional int32 field272 = 272 [default=-1];&lt;/div&gt;     &lt;div&gt;  optional int32 field150 = 150;&lt;/div&gt;     &lt;div&gt;  optional int32 field23 = 23 [default=0];&lt;/div&gt;     &lt;div&gt;  optional bool field24 = 24 [default=false];&lt;/div&gt;     &lt;div&gt;  optional int32 field25 = 25 [default=0];&lt;/div&gt;     &lt;div&gt;  optional bool field78 = 78;&lt;/div&gt;     &lt;div&gt;  optional int32 field67 = 67 [default=0];&lt;/div&gt;     &lt;div&gt;  optional int32 field68 = 68;&lt;/div&gt;     &lt;div&gt;  optional int32 field128 = 128 [default=0];&lt;/div&gt;     &lt;div&gt;  optional string field129 = 129 [default=&amp;quot;xxxxxxxxxxxxxxxxxxxxx&amp;quot;];&lt;/div&gt;     &lt;div&gt;  optional int32 field131 = 131 [default=0];&lt;/div&gt;     &lt;div&gt;}&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;



 &lt;p&gt;事实上这个文件摘自gRPC项目的测试用例，使用反射为每个字段赋值，使用protobuf序列化后的大小为 581 个字节左右。因为Dubbo和Motan缺省不支持Protobuf,所以序列化和反序列化是在代码中手工实现的。&lt;/p&gt;
 &lt;p&gt;服务很简单：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;service Hello {&lt;/div&gt;     &lt;div&gt;  // Sends a greeting&lt;/div&gt;     &lt;div&gt;  rpc Say (BenchmarkMessage) returns (BenchmarkMessage) {}&lt;/div&gt;     &lt;div&gt;}&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;接收一个BenchmarkMessage，更改它前两个字段的值为&amp;quot;OK&amp;quot; 和 100，这样客户端得到服务的结果后能够根据结果判断服务是否正常的执行。  &lt;br /&gt;Dubbo的测试代码改自   &lt;a href="https://github.com/alibaba/dubbo/tree/master/dubbo-demo" rel="external" target="_blank"&gt;dubo-demo&lt;/a&gt;,  &lt;br /&gt;Motan的测试代码改自   &lt;a href="https://github.com/weibocom/motan/tree/master/motan-demo" rel="external" target="_blank"&gt;motan-demo&lt;/a&gt;。  &lt;br /&gt;rpcx和gRPC的测试代码在   &lt;a href="https://github.com/smallnest/rpcx/tree/master/_benchmarks" rel="external" target="_blank"&gt;rpcx benchmark&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;正如左耳朵耗子对Dubbo批评一样，Dubbo官方的测试不正规 (  &lt;a href="http://coolshell.cn/articles/17381.html" rel="external" target="_blank"&gt;性能测试应该怎么做？&lt;/a&gt;)。  &lt;br /&gt;本文测试将用吞吐率、相应时间平均值、响应时间中位数、响应时间最大值进行比较(响应时间最小值都为0，不必比较)，当然最好以Top Percentile的指标进行比较，但是我没有找到Go语言的很好的统计这个库，所以暂时比较中位数。  &lt;br /&gt;另外测试中服务的成功率都是100%。&lt;/p&gt;
 &lt;p&gt;测试是在两台机器上执行的，一台机器做服务器，一台机器做客户端。&lt;/p&gt;
 &lt;p&gt;两台机器的配置都是一样的，比较老的服务器：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;CPU&lt;/strong&gt;:    Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz, 24 cores&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;Memory&lt;/strong&gt;: 16G&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;OS&lt;/strong&gt;:     Linux 2.6.32-358.el6.x86_64, CentOS 6.4&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;Go&lt;/strong&gt;:     1.7&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;Java&lt;/strong&gt;:   1.8&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;分别在client并发数为100、500、1000、2000 和 5000的情况下测试，记录吞吐率(每秒调用次数, Throughput)、响应时间(Latency) 、成功率。  &lt;br /&gt;(更精确的测试还应该记录CPU使用率、内存使用、网络带宽、IO等，本文中未做比较)&lt;/p&gt;
 &lt;p&gt;首先看在四种并发下各RPC框架的吞吐率：  &lt;br /&gt;  &lt;img alt="&amp;#21534;&amp;#21520;&amp;#29575;" src="http://colobu.com/throughput.png"&gt;&lt;/img&gt;  &lt;br /&gt;rpcx的性能遥遥领先，并且其它三种框架在并发client很大的情况下吞吐率会下降。&lt;/p&gt;
 &lt;p&gt;在这四种并发的情况下平均响应：  &lt;br /&gt;  &lt;img alt="&amp;#24179;&amp;#22343;&amp;#21709;&amp;#24212;&amp;#26102;&amp;#38388;" src="http://colobu.com/mean-latency.png"&gt;&lt;/img&gt;  &lt;br /&gt;这个和吞吐率的表现是一致的，还是rpcx最好，平均响应时间小于30ms, Dubbo在并发client多的情况下响应时间很长。  &lt;br /&gt;我们知道，在微服务流行的今天，一个单一的RPC的服务可能会被不同系统所调用，这些不同的系统会创建不同的client。如果调用的系统很多，就有可能创建很多的client。  &lt;br /&gt;这里统计的是这些client总的吞吐率和总的平均时间。&lt;/p&gt;
 &lt;p&gt;平均响应时间可能掩盖一些真相，尤其是当响应时间的分布不是那么平均，所以我们还可以关注另外一个指标，就是中位数。  &lt;br /&gt;这里的中位数指小于这个数值的测试数和大于这个数值的测试数相等。  &lt;br /&gt;  &lt;img alt="&amp;#21709;&amp;#24212;&amp;#26102;&amp;#38388;&amp;#20013;&amp;#20301;&amp;#25968;" src="http://colobu.com/median-latency.png"&gt;&lt;/img&gt;  &lt;br /&gt;gRPC框架的表现最好。&lt;/p&gt;
 &lt;p&gt;另外一个就是比较一下最长的响应时间，看看极端情况下各框架的表现：  &lt;br /&gt;  &lt;img alt="&amp;#26368;&amp;#22823;&amp;#21709;&amp;#24212;&amp;#26102;&amp;#38388;" src="http://colobu.com/max-latency.png"&gt;&lt;/img&gt;  &lt;br /&gt;rpcx的最大响应时间都小于1秒，Motan的表现也不错，都小于2秒，其它两个框架表现不是太好。&lt;/p&gt;
 &lt;p&gt;本文以一个相同的测试case测试了四种RPC框架的性能，得到了这四种框架在不同的并发条件下的性能表现。期望读者能提出宝贵的意见，以便完善这个测试，并能增加更多的RPC框架的测试。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>大并发编程</category>
      <guid isPermaLink="true">https://itindex.net/detail/55952-%E5%88%86%E5%B8%83-rpc-%E6%A1%86%E6%9E%B6</guid>
      <pubDate>Mon, 05 Sep 2016 21:18:30 CST</pubDate>
    </item>
    <item>
      <title>在Linux进行IO的正确姿势</title>
      <link>https://itindex.net/detail/53393-linux-io-%E6%AD%A3%E7%A1%AE</link>
      <description>&lt;p&gt;很多C/C++程序虽然在做网络编程, 但大多用别人封装好的库, 对底层不甚了解, 感觉 IO 操作不是很简单吗? 我敢说, 大多数人进行 IO 的姿势都不对, 所谓的 IO, 主要是 read()/write() 两个函数.&lt;/p&gt;
 &lt;p&gt;先说错误的 IO 读操作:&lt;/p&gt;
 &lt;pre&gt;
int ret = read(fd, buf, len);
if(ret == -1){
    exit(-1);
}else if(ret == 0){
    close(fd);
}
&lt;/pre&gt;
 &lt;p&gt;看起来好像很正确的样子, 返回值也判断了, 不仅判断 -1, 还判断 0, 应该姿势正确吧? 错! 完全错误! 因为你忽略了 errno 的处理. 仔细看文档, 函数返回 -1 不能完全代表 fd 错误, 还需要结合 errno.&lt;/p&gt;
 &lt;p&gt;接下来这样改:&lt;/p&gt;
 &lt;pre&gt;
int ret = read(fd, buf, len);
if(ret == -1){
    if(errno == EINTR){
        // 怎么办?
    }else if(errno == EAGAIN){
        // 怎么办?
    }
    exit(-1);
}else if(ret == 0){
    close(fd);
}
&lt;/pre&gt;
 &lt;p&gt;EINTR 表示 read() 函数调用被系统中断了, 调用者和 fd 都没有问题, 有问题的是操作系统. 而 EAGAIN 是在非阻塞 IO 时会出现. 上面的代码判断了 errno, 但不知道下一步该怎么做, 还不行.&lt;/p&gt;
 &lt;p&gt;在 Linux 下进行 IO 操作的正确姿势是:&lt;/p&gt;
 &lt;pre&gt;
while(1){
    int ret = read(fd, buf, len);
    if(ret == -1){
        if(errno == EINTR){
            continue;
        }else if(errno == EAGAIN){
            // 根据你和调用者的约定, 返回一个数值告诉它再次重试
            // 一般是结合 select/epoll 等 IO 多路复用函数
        }
        exit(-1);
    }else if(ret == 0){
        close(fd);
    }
    // proc
    break;
}
&lt;/pre&gt;
 &lt;p&gt;没错, 在 read() 外面包一个 while(1). 以后你看到有谁写的 read() 代码不是被包含在一个死循环里, 你就可以下结论说这段代码不完善.&lt;/p&gt;
 &lt;p&gt;写 IO 的 write() 函数也是类似用法. 建议有心人可以参考   &lt;a href="https://github.com/ideawu/sim"&gt;sim 框架&lt;/a&gt;的代码, 看看别人是怎么做的, 程序员应该多造轮子, 但不要关起门来造车.&lt;/p&gt;


 &lt;h3&gt;Related posts:&lt;/h3&gt; &lt;ol&gt;  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/634.html" rel="bookmark" title="Permanent Link: Linux &amp;#26680;&amp;#24515;&amp;#32534;&amp;#31243; &amp;#8211; fsync, write"&gt;Linux 核心编程 – fsync, write&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/267.html" rel="bookmark" title="Permanent Link: &amp;#36719;&amp;#20214;&amp;#20307;&amp;#31995;&amp;#32467;&amp;#26500;&amp;#27169;&amp;#24335;-&amp;#23618;"&gt;软件体系结构模式-层&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/537.html" rel="bookmark" title="Permanent Link: Lighttpd mod_fastcgi&amp;#28304;&amp;#30721;&amp;#20998;&amp;#26512;"&gt;Lighttpd mod_fastcgi源码分析&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/740.html" rel="bookmark" title="Permanent Link: &amp;#26500;&amp;#24314;C1000K&amp;#30340;&amp;#26381;&amp;#21153;&amp;#22120;(1) &amp;#8211; &amp;#22522;&amp;#30784;"&gt;构建C1000K的服务器(1) – 基础&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/122.html" rel="bookmark" title="Permanent Link: &amp;#32534;&amp;#20889;JSP/PHP+MySQL&amp;#30041;&amp;#35328;&amp;#26412;"&gt;编写JSP/PHP+MySQL留言本&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>C/C++语言编程 Linux</category>
      <guid isPermaLink="true">https://itindex.net/detail/53393-linux-io-%E6%AD%A3%E7%A1%AE</guid>
      <pubDate>Fri, 08 May 2015 10:56:24 CST</pubDate>
    </item>
    <item>
      <title>C/C++编程的现代习惯</title>
      <link>https://itindex.net/detail/53372-%E7%BC%96%E7%A8%8B-%E7%8E%B0%E4%BB%A3-%E4%B9%A0%E6%83%AF</link>
      <description>&lt;p&gt;相对于汇编语言是一门操作 CPU 寄存器的语言, C/C++ 是一门操作内存的语言, 这是传统的观点. 但现代的程序应用开发, 大多是把 C/C++ 当作一门应用层语言, 所以必须适当地减少对内存的关注. 这也是本文所要讲的 - C/C++ 编程的现代习惯.&lt;/p&gt;
 &lt;h3&gt;1. 不要害怕返回结构体和类的实例&lt;/h3&gt;
 &lt;p&gt;在一些古董级的编程书里, 你绝对看不到返回结构体或者类的实例, 它们告诉你&amp;quot;不能返回局部变量的内存&amp;quot;. 事实上, 返回结构体(类)的实例, 并不是把局部变量的内存(指针)返回给调用者使用, 而把局部变量复制到调用者栈上的内存. 而且, 很多情况下编译器会优化, 根本就不会发生内存拷贝.&lt;/p&gt;
 &lt;p&gt;返回结构体(类)的实例, 比返回 malloc() 分配的内存的指针在实践上具有更多的优势, 既能使代码更清晰, 也可以完全避免内存泄漏.  &lt;br /&gt;
&lt;/p&gt;
 &lt;h3&gt;2. 不要害怕传递 STL 的 string&lt;/h3&gt;
 &lt;p&gt;无论你把 STL 的 string 作为函数返回值还是参数, 都永远不要担心内存拷贝的问题, 永远不要! string 经过了良好的优化, 并且具有写时拷贝特性, 你将 string 传来传去, 就跟整数赋值的成本差不多. 相信我, 你可以这样认为.&lt;/p&gt;
 &lt;h3&gt;3. 利用 string 来减少显式的内存分配和释放&lt;/h3&gt;
 &lt;p&gt;STL 的 string 几乎可以完全替代 malloc/free 内存操作. 它有写时拷贝的特性, 它有自动扩大的特性, 你完全可以在许多场景用它来替代显式的内存分配, 而且利用它在退出作用域时自动释放内存的语言特性(和某些自动锁类似), 避免了内存泄露的可能性.&lt;/p&gt;
 &lt;h3&gt;4. 记住, STL 的 string 不是字符串!&lt;/h3&gt;
 &lt;p&gt;记住, string 不是字符串, 它是一段内存, 内存中的每一个字节可以是任意值, 多个 &amp;apos;\0&amp;apos; 字符也可以出现在 string 中. 只有当你调用了它的 c_str() 方法, 它才和 C 语言的字符串有联系, 在你调用 c_str() 之前, 记住, string 不是字符串!&lt;/p&gt;
 &lt;h3&gt;5. 你要理解代码导致内存的变化, 但不要被内存限制&lt;/h3&gt;
 &lt;p&gt;C/C++ 语言是一门操作内存的语言, 这是永远的基础. 你必须理解你的每一行代码导致的内存的变化, 这样你才能正确地进行 C/C++ 编程. 但是, 理解你的业务, 快速地封装出内存相关的核心代码, 然后把内存忘记.&lt;/p&gt;


 &lt;h3&gt;Related posts:&lt;/h3&gt; &lt;ol&gt;  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/805.html" rel="bookmark" title="Permanent Link: &amp;#23567;&amp;#24515; int &amp;#20056;&amp;#27861;&amp;#28322;&amp;#20986;!"&gt;小心 int 乘法溢出!&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/720.html" rel="bookmark" title="Permanent Link: &amp;#20351;&amp;#29992; jemalloc &amp;#32534;&amp;#35793;&amp;#36807;&amp;#31243;&amp;#20986;&amp;#38169;&amp;#30340;&amp;#38382;&amp;#39064;"&gt;使用 jemalloc 编译过程出错的问题&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/122.html" rel="bookmark" title="Permanent Link: &amp;#32534;&amp;#20889;JSP/PHP+MySQL&amp;#30041;&amp;#35328;&amp;#26412;"&gt;编写JSP/PHP+MySQL留言本&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/705.html" rel="bookmark" title="Permanent Link: SSDB &amp;#20351;&amp;#29992; jemalloc"&gt;SSDB 使用 jemalloc&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.ideawu.net/blog/archives/779.html" rel="bookmark" title="Permanent Link: &amp;#30334;&amp;#34892;&amp;#20195;&amp;#30721;&amp;#23454;&amp;#29616;&amp;#19968;&amp;#20010;&amp;#31616;&amp;#21333;&amp;#30340;Zset(SortedSet)"&gt;百行代码实现一个简单的Zset(SortedSet)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>C/C++语言编程</category>
      <guid isPermaLink="true">https://itindex.net/detail/53372-%E7%BC%96%E7%A8%8B-%E7%8E%B0%E4%BB%A3-%E4%B9%A0%E6%83%AF</guid>
      <pubDate>Tue, 05 May 2015 14:39:32 CST</pubDate>
    </item>
    <item>
      <title>谷歌推出全新Android开发框架Sky让App更流畅</title>
      <link>https://itindex.net/detail/53369-%E8%B0%B7%E6%AD%8C-%E6%8E%A8%E5%87%BA-android</link>
      <description>&lt;p&gt;　　据arstechnica  &lt;a href="http://arstechnica.com/gadgets/2015/05/01/googles-dart-language-on-android-aims-for-java-free-120-fps-apps/" target="_blank"&gt;报道&lt;/a&gt;，谷歌推出全新Android开发框架Sky，主要目的包括提高运行速度和响应速度，在大部分设备上，应用流畅的标准都是实现60FPS的帧率，Sky希望实现高达120FPS的帧率，用户界面将保持流畅的响应速度。&lt;/p&gt; &lt;p&gt;　　目前为止，除游戏之外的Android应用通常使用Java语言来开发编写，其运行效率和速度并不理想，而谷歌内部的一个团队正在探索全新的应用开发方式。他们利用了谷歌自主的网页开发语言  &lt;a href="https://www.dartlang.org/" target="_blank"&gt;Dart&lt;/a&gt;来开发Android应用。这帮助他们专注于速度，并可以与网页进行深度整合。&lt;/p&gt; &lt;p&gt;　　Dart由Chrome V8引擎的团队成员发明。该团队近期举行了Dart开发者峰会，展示了在Android项目中使用的Dart。在Android项目中使用的Dart并未被称作“Dart on Android”，而是有了一个新名字“Sky”。目前，Sky只是一次  &lt;a href="https://github.com/domokit/sky_sdk" target="_blank"&gt;开源的尝试&lt;/a&gt;。但相对于传统的Android应用开发方式，Sky有着多方面优势。&lt;/p&gt; &lt;p&gt;　　Sky的最主要目的包括提高运行速度和响应速度。在大部分设备上，应用流畅的标准都是实现60FPS的帧率。不过，Dart团队希望实现高达120FPS的帧率。目前在Android平台上，许多应用连标准的60FPS帧率都难以达到，更不用说120FPS。60FPS的帧率要求每16毫秒绘制一帧，当画面绘制速度达不到这一水平时，应用就会出现卡顿。&lt;/p&gt; &lt;p&gt;　　Dart团队展示了一款演示应用，每帧的绘制速度仅为1.2毫秒。尽管这只是一次简单的展示，但这意味着，对于开发流畅而复杂的应用来说，Sky有着很大的空间，这也使120FPS的帧率成为了可能。该团队表示，Sky的应用程序接口(API)不会影响界面的主线程，这意味着即使应用运行速度变慢，用户界面仍将保持流畅的响应速度。&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;　　Sky并不依赖于平台，其代码可以运行在Android、iOS，以及任何集成了Dart虚拟机的平台上。这类应用的运行类似于网站。应用的很大一部分基于HTTP，这意味着开发者可以进行持续的开发，并确保所有用户一直使用最新版本。这款演示应用的弱点在于无法离线运行，而启动应用则需要1到2秒钟时间，因为应用需要下载数据。不过，这两方面的问题可以通过缓存机制来解决。&lt;/p&gt; &lt;p&gt;　　基于HTTP的模式使开发变得很简单。开发者不必编辑代码、编译并打包，这些代码可以在HTTP服务器上编辑。而用户只需关闭并重新打开应用，即可完成应用的“升级”。这就像是一款网页浏览器。Android开发者可以使用Sky Framework，这提供了一整套Material Design小工具，帮助开发者方便地增加操作栏、触控效果、导航面板，以及Android应用中所需的一切元素。&lt;/p&gt; &lt;p&gt;　　与普通应用类似，Sky应用可以获得Android的全部权限和API，但结合来自网页服务器的自动升级功能，这将带来信息安全问题。不过目前，Sky仅仅只是一个试验项目。在Sky成为一种主流的Android应用解决方案之前，Sky团队需要解决这些问题。该团队的GitHub页面显示：“我们仍在对Sky进行频繁地迭代，这意味着框架和底层引擎有可能以不兼容的方式出现改变。”&lt;/p&gt; &lt;p&gt;  &lt;a href="http://www.williamlong.info/archives/4223.html" target="_blank"&gt;评论《谷歌推出全新Android开发框架Sky让App更流畅》的内容...&lt;/a&gt;&lt;/p&gt; &lt;h3&gt;相关文章:&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/4142.html"&gt;Google宣布淘汰SPDY&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/3925.html"&gt;打造有风格的团队&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/3806.html"&gt;关于Nginx支持.htaccess的分析&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/3805.html"&gt;手机软件开发者的系统选择&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/3744.html"&gt;IMDB评分排名算法&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt; &lt;br /&gt;微博： &lt;a href="http://weibo.com/williamlong"&gt;新浪微博&lt;/a&gt; - 微信公众号：williamlonginfo  &lt;br /&gt;月光博客投稿信箱：williamlong.info(at)gmail.com &lt;br /&gt;Created by William Long www.williamlong.info &lt;br /&gt; &lt;img alt="&amp;#26376;&amp;#20809;&amp;#21338;&amp;#23458;" src="http://www.williamlong.info/images/qrcode.jpg"&gt;&lt;/img&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程开发</category>
      <guid isPermaLink="true">https://itindex.net/detail/53369-%E8%B0%B7%E6%AD%8C-%E6%8E%A8%E5%87%BA-android</guid>
      <pubDate>Sun, 03 May 2015 23:36:06 CST</pubDate>
    </item>
    <item>
      <title>如何理解谷歌浏览器的安全警告信息</title>
      <link>https://itindex.net/detail/54185-%E7%90%86%E8%A7%A3-%E8%B0%B7%E6%AD%8C-%E6%B5%8F%E8%A7%88%E5%99%A8</link>
      <description>&lt;p&gt;最近如果使用Chrome访问国内的很多网站的时候，比如exmail.qq.com, 你可能会注意到这样一个对话框，这个是什么意思？访问链接没有私密性吗? 等等，这里好像有点不对， 网页私密性到底是个啥，为啥会提醒我这个问题，我不是已经输了密码登录了嘛？事情要从头说起。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="722" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/10081946_iTuw.jpg?9bae72" width="600"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;我上个邮箱，连私密性都没有了，那里面的照片应该怎么办，以前修电脑没有私密性，现在连上网都没有私密性，难道我又要红了？&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;一、HTTPS (安全超文本协议）怎么来的？&lt;/p&gt;
 &lt;p&gt;1997 年 CERN发明HTTP 协议并用于万维网的时候，仅仅是为了在学术界内部做一个共享数据的平台, 并没有想到太多传输中的安全性。毕竟当年网络规模非常小，而计算机以及昂贵的网络设备并不是每个人都可以买得起的。&lt;/p&gt;
 &lt;p&gt;他们当然没有料到之后万维网居然成了一个信息传递的通用平台，一帮人甚至丧心病狂地在上面做起了Web电子邮箱、网络银行一类的服务。这类服务对安全性和私密性的要求都非常严格， 因为基本上没有人希望自己的银行密码，私人的邮件在传输中被第三方看到。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;所以问题就来了， HTTP 是明文传输的。 HTTP倒是支持密码认证，只是不巧的是，密码也是明文传的。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;针对这种情况，在网景一帮科学家，特别是 Dr. Taher Elgamal （号称SSL 之父）的努力下， HTTPS 横空出世了。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;HTTPS 里面，所有传输的数据都是加密过的，于是第三方无法在数据的传输过程中获得任何有用的数据，数据传输中的私密性自然得到了保证。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;至少当初设计的目的是这样子。&lt;/p&gt;
 &lt;p&gt;HTTPS 并非是一个全新的协议，其实是在 HTTP的 基础上，加了 SSL （安全套接字）或者是后来的 TLS (传输安全协议)。 SSL/TLS 工作在 HTTP 之下, 负责加密所有传输的数据。&lt;/p&gt;
 &lt;p&gt;说个题外话，当时不仅仅是 HTTP，众多的互联网上层协议，即应用层协议，STMP 电子邮件协议 一类，大多都是明文传输的。而移动互联网或  &lt;br /&gt;
者其他网络，都是基于一些标准的协议，就是TCP/IP协议簇。早期时候，这些协议是由互联网领域专家联合制定的，就像现在制定法律的过程一样。而经过实  &lt;br /&gt;
际的验证，其不严谨性渐渐被发现，于是人们在此前的基础上进行不断更新，SSL/TLS就是这样出来的。 SSL/TLS  &lt;br /&gt;
由于是工作在TCP层和应用层之间，它可以加密任何应用层协议，包括STMP一类。 从这个角度说来，网景对互联网的贡献其实是非常深远的。&lt;/p&gt;
 &lt;p&gt;HTTPS 使用非对称算法交换密钥，这个也是一个非常精巧的算法，有兴趣的同学可以点击这里了解下，号称是20世纪最重要的算法之一。&lt;/p&gt;
 &lt;p&gt;HTTPS 除了解决加密问题以外，还需要还解决另外一个问题： 网站真实身份鉴别&lt;/p&gt;
 &lt;p&gt;比如，如果你上招行网站，你怎么知道你上的就是招商银行网站而不是一个做得和招商银行一模一样的钓鱼网站呢？&lt;/p&gt;
 &lt;p&gt;这个其实和现实生活中如何鉴定一个长的像警察并且突然站到你面前要你交罚款的人是否是真正的人民警察是一个场景。&lt;/p&gt;
 &lt;p&gt;”警官证可以给我看看吗，谢谢！“&lt;/p&gt;
 &lt;p&gt;HTTPS 用的是同一种方法，它要求每一个使用这个协议的网站从专业的第三方机构申请一个数字证书，数字证书中包括网站的域名，所有者等等 （当然也包括公钥，这里不详细展开协议细节了）。&lt;/p&gt;
 &lt;p&gt;这个数字证书其实就相当于现实中的警官证。&lt;/p&gt;
 &lt;p&gt;在访问这个网站的时候浏览器会对证书做一次检查，而这个对话框，就是检查的结果。&lt;/p&gt;
 &lt;p&gt;我们来看看这个对话框内容是个什么鬼。&lt;/p&gt;
 &lt;p&gt;二、如何鉴别你是警察？因为警官证也有可能是假的。&lt;/p&gt;
 &lt;p&gt;第一个：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;该网站的身份验证已经通过GeoTrust SSL CA–G2的验证，但没有公开审核记录。该网站的安全设置已过期，可能导致日后的Chrome 版本无法安全访问该网站。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;刚才有提到证书是由专业机构颁发的，不过，&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;－ 专业机构就没有坏人了嘛。&lt;/p&gt;
  &lt;p&gt;－ 证书就不会被人偷吗。&lt;/p&gt;
  &lt;p&gt;－ 专业机构被骗了怎么办。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;事实上，荷兰专业机构(DigiNotar)甚至被入侵过一次， 丢了好几百个证书，你可以自行脑补一下有人潜入公安部自己办了几百个警官证是一个多么壮观的场景。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;于   &lt;br /&gt;
是，IETF在2013年启动了一个叫做certificate-transparency的开源项目，把所有已知的合法证书做了一个白名单，浏览器在验   &lt;br /&gt;
证证书的时候同时也会去查看这个证书是不是在白名单里面。 如果不在的话，就会告知用户这个证书找不到记录，于是，有可能是假或者是被盗的证书。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;但是，这里有一个致命的问题：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;到目前为止，这个还只是一个试验性项目，而这个世界上那么多的网站， 你白名单得过来嘛。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;  &lt;img alt="" height="438" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/10081946_alMV.jpg?9bae72" width="307"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;（注意：已经没有警示标志）&lt;/p&gt;
 &lt;p&gt;比如上图所显示的，其实也没有审核纪录，不过警告的标示去掉了。说明谷歌其实自己也知道目前白名单的覆盖很差，一般找不到记录，并不会加上确切的警告标示。所以，目前你可以忽略它。&lt;/p&gt;
 &lt;p&gt;关键在第二个：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;本网站采用较弱的安全配置（SHA-1签名），所以你的连接可能不是私人的。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;这个就比较有意思了。&lt;/p&gt;
 &lt;p&gt;还是那个警官证的问题。 要搞一个警官证除了去偷/骗/潜入公安部自己做一个真的以外, 你还可以做个假的嘛。&lt;/p&gt;
 &lt;p&gt;对于数字证书来说，最重要的鉴别真假的部分是数字签名，而鉴于数字证书一般不小，不可能对每个字节都签一次名，一般来说是对数字证书的一个哈希值进行签名。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="196" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/10081946_6Jdd.png?9bae72" width="600"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;如果你不知道哈希值是什么，我给你打个比方。如果你是一个数字证书， 那你的照片就是你的哈希值。&lt;/p&gt;
 &lt;p&gt;它包含下面2个条件：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;－ 通过合适的手段，可以从你产生你的照片， 但是没法从照片产生你 。意思是，先有你，才能有照片。&lt;/p&gt;
  &lt;p&gt;－ 只有你可以精确的产生你的照片，别人都不行。你就是唯一的，你的特征是别人没有的。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;所以如果想检查一个人的警官证，只需要看看照片能不能对上人(哈希值符合)，照片上面的骑缝章对不对(数字签名)。但是这个骑缝章只需要盖在照片上，而不需要盖在警官兄的脸上。当然我知道这个比喻有非常多学术上的不严谨性，不过这个是我目前能找到最容易理解的比喻之一了。&lt;/p&gt;
 &lt;p&gt;数字证书中， SHA-1就是一种常见的哈希算法。 可以像照相机一样，给你的数字证书生成一个唯一值（照片）。&lt;/p&gt;
 &lt;p&gt;只是这个算法有一个问题。 这个算法这个函数由于设计时间早，强度太差，导致有可能用两个不同的数字证书可能会生成同样一个值。&lt;/p&gt;
 &lt;p&gt;这个就像如果你有一个照身份照的照相机，不过这个神奇的照相机拍的太模糊，以致于通过特殊的设定，可以用另外一个人照出和真实警官一模一样的照片。恭喜你，如果你发现了这个设定，你就可以大规模的制作套牌警官证了。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;这种现象在哈希函数中被称为是“碰撞”。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;对于SHA-1 算法 如果要找到这个“特殊的设定”大概需要2的74次方个操作，（也有论文指出，只需要2的61次方个操作即可完成）  这个在SHA-1发明的时候是不可想象，不过其实在现在也是不可行的。只是按照现在计算机的发展速度 2018 年左右使用价格合适服务器集群理论上就可以破解（可以参考这里）：&lt;/p&gt;
 &lt;p&gt;”A  &lt;br /&gt;
collision attack is therefore well within the range of what an  &lt;br /&gt;
organized crime syndicate can practically budget by 2018, and a  &lt;br /&gt;
university research project by 2021。“&lt;/p&gt;
 &lt;p&gt;（”因此，在一个有组织犯罪集团的范围内，一次碰撞攻击的实际预算是2018，而一个大学的研究项目是2021“）&lt;/p&gt;
 &lt;p&gt;于是，Chrome 认为使用SHA-1的哈希函数都是潜在不安全的，于是会对所有使用SHA-1的网站证书提出警告，督促所有使用SHA-1的网站换为SHA-2。&lt;/p&gt;
 &lt;p&gt;不过注意，仅仅是潜在不安全， 目前还没有可行可靠的SHA-1碰撞算法出现。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;所以，这些网站暂时是安全的，不过也希望站长们多多提高安全意识，因为SHA-1已经非常接近可以被“破解”边缘。很有可能会出现以上情况：被人找到碰撞算法或者说被破解，从而制作虚假警官证。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;如果要详细的证书设置以去除这个警告的步骤，可以参考这里（点击进入链接）&lt;/p&gt;
 &lt;p&gt;因为工作原因——欧朋Opera是Chromium安全组成员，所以我对这个内情比较了解。有兴趣可以去看看讨论组里面的撕逼贴, 截个屏放在这里：   &lt;img alt="" height="287" src="http://www.techug.com/wordpress/wp-content/uploads/2015/08/10081946_c1fF.png?9bae72" width="600"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术 谷歌浏览器</category>
      <guid isPermaLink="true">https://itindex.net/detail/54185-%E7%90%86%E8%A7%A3-%E8%B0%B7%E6%AD%8C-%E6%B5%8F%E8%A7%88%E5%99%A8</guid>
      <pubDate>Mon, 17 Aug 2015 17:13:35 CST</pubDate>
    </item>
    <item>
      <title>MySQL 调优/优化的 100 个建议</title>
      <link>https://itindex.net/detail/54172-mysql-%E4%BC%98%E5%8C%96</link>
      <description>&lt;p&gt;MySQL是一个强大的开源数据库。随着MySQL上的应用越来越多，MySQL逐渐遇到了瓶颈。这里提供 101 条优化 MySQL 的建议。有些技巧适合特定的安装环境，但是思路是相通的。我已经将它们分成了几类以帮助你理解。&lt;/p&gt;
 &lt;h1&gt;MySQL监控&lt;/h1&gt;
 &lt;h3&gt;MySQL服务器硬件和OS（操作系统）调优：&lt;/h3&gt;
 &lt;p&gt;1、有足够的物理内存，能将整个InnoDB文件加载到内存里 —— 如果访问的文件在内存里，而不是在磁盘上，InnoDB会快很多。&lt;/p&gt;
 &lt;p&gt;2、全力避免 Swap 操作 — 交换（swapping）是从磁盘读取数据，所以会很慢。&lt;/p&gt;
 &lt;p&gt;3、使用电池供电的RAM（Battery-Backed RAM）。&lt;/p&gt;
 &lt;p&gt;4、使用一个高级磁盘阵列 — 最好是 RAID10 或者更高。&lt;/p&gt;
 &lt;p&gt;5、避免使用RAID5 — 和校验需要确保完整性，开销很高。&lt;/p&gt;
 &lt;p&gt;6、将你的操作系统和数据分开，不仅仅是逻辑上要分开，物理上也要分开 — 操作系统的读写开销会影响数据库的性能。&lt;/p&gt;
 &lt;p&gt;7、将临时文件和复制日志与数据文件分开 — 后台的写操作影响数据库从磁盘文件的读写操作。&lt;/p&gt;
 &lt;p&gt;8、更多的磁盘空间等于更高的速度。&lt;/p&gt;
 &lt;p&gt;9、磁盘速度越快越好。&lt;/p&gt;
 &lt;p&gt;10、SAS优于SATA。&lt;/p&gt;
 &lt;p&gt;11、小磁盘的速度比大磁盘的更快，尤其是在 RAID 中。&lt;/p&gt;
 &lt;p&gt;12、使用电池供电的缓存 RAID（Battery-Backed Cache RAID）控制器。&lt;/p&gt;
 &lt;p&gt;13、避免使用软磁盘阵列。&lt;/p&gt;
 &lt;p&gt;14. 考虑使用固态IO卡（不是磁盘）来作为数据分区 — 几乎对所有量级数据，这种卡能够支持 2 GBps 的写操作。&lt;/p&gt;
 &lt;p&gt;15、在 Linux 系统上，设置 swappiness 的值为0 — 没有理由在数据库服务器上缓存文件，这种方式在Web服务器或桌面应用中用的更多。&lt;/p&gt;
 &lt;p&gt;16、尽可能使用 noatime 和 nodirtime 来挂载文件系统 — 没有必要为每次访问来更新文件的修改时间。&lt;/p&gt;
 &lt;p&gt;17、使用 XFS 文件系统 — 一个比ext3更快的、更小的文件系统，拥有更多的日志选项，同时，MySQL在ext3上存在双缓冲区的问题。&lt;/p&gt;
 &lt;p&gt;18、优化你的 XFS 文件系统日志和缓冲区参数 – -为了获取最大的性能基准。&lt;/p&gt;
 &lt;p&gt;19、在Linux系统中，使用 NOOP 或 DEADLINE IO 调度器 — CFQ 和 ANTICIPATORY 调度器已经被证明比 NOOP 和 DEADLINE 慢。&lt;/p&gt;
 &lt;p&gt;20、使用 64 位操作系统 — 有更多的内存能用于寻址和 MySQL 使用。&lt;/p&gt;
 &lt;p&gt;21、将不用的包和后台程序从服务器上删除 — 减少资源占用。&lt;/p&gt;
 &lt;p&gt;22、将使用 MySQL 的 host 和 MySQL自身的 host 都配置在一个 host 文件中 — 这样没有 DNS 查找。&lt;/p&gt;
 &lt;p&gt;23、永远不要强制杀死一个MySQL进程 — 你将损坏数据库，并运行备份。&lt;/p&gt;
 &lt;p&gt;24、让你的服务器只服务于MySQL — 后台处理程序和其他服务会占用数据库的 CPU 时间。&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;h3&gt;MySQL 配置：&lt;/h3&gt;
 &lt;p&gt;25、使用 innodb_flush_method=O_DIRECT 来避免写的时候出现双缓冲区。&lt;/p&gt;
 &lt;p&gt;26、避免使用 O_DIRECT 和 EXT3 文件系统 — 这会把所有写入的东西序列化。&lt;/p&gt;
 &lt;p&gt;27、分配足够 innodb_buffer_pool_size ，来将整个InnoDB 文件加载到内存 — 减少从磁盘上读。&lt;/p&gt;
 &lt;p&gt;28、不要让 innodb_log_file_size 太大，这样能够更快，也有更多的磁盘空间 — 经常刷新有利降低发生故障时的恢复时间。&lt;/p&gt;
 &lt;p&gt;29、不要同时使用 innodb_thread_concurrency 和 thread_concurrency 变量 — 这两个值不能兼容。&lt;/p&gt;
 &lt;p&gt;30、为 max_connections 指定一个小的值 — 太多的连接将耗尽你的RAM，导致整个MySQL服务器被锁定。&lt;/p&gt;
 &lt;p&gt;31、保持 thread_cache 在一个相对较高的数值，大约是 16 — 防止打开连接时候速度下降。&lt;/p&gt;
 &lt;p&gt;32、使用 skip-name-resolve — 移除 DNS 查找。&lt;/p&gt;
 &lt;p&gt;33、如果你的查询重复率比较高，并且你的数据不是经常改变，请使用查询缓存 — 但是，在经常改变的数据上使用查询缓存会对性能有负面影响。&lt;/p&gt;
 &lt;p&gt;34、增加 temp_table_size — 防止磁盘写。&lt;/p&gt;
 &lt;p&gt;35、增加 max_heap_table_size — 防止磁盘写。&lt;/p&gt;
 &lt;p&gt;36、不要将 sort_buffer_size 的值设置的太高 — 可能导致连接很快耗尽所有内存。&lt;/p&gt;
 &lt;p&gt;37、监控 key_read_requests 和 key_reads，以便确定 key_buffer 的值 — key 的读需求应该比 key_reads 的值更高，否则使用 key_buffer 就没有效率了。&lt;/p&gt;
 &lt;p&gt;38、设置 innodb_flush_log_at_trx_commit = 0 可以提高性能，但是保持默认值（1）的话，能保证数据的完整性，也能保证复制不会滞后。&lt;/p&gt;
 &lt;p&gt;39、有一个测试环境，便于测试你的配置，可以经常重启，不会影响生产环境。&lt;/p&gt;
 &lt;h3&gt;MySQL Schema 优化：&lt;/h3&gt;
 &lt;p&gt;40、保证你的数据库的整洁性。&lt;/p&gt;
 &lt;p&gt;41、归档老数据 — 删除查询中检索或返回的多余的行&lt;/p&gt;
 &lt;p&gt;42、在数据上加上索引。&lt;/p&gt;
 &lt;p&gt;43、不要过度使用索引，评估你的查询。&lt;/p&gt;
 &lt;p&gt;44、压缩 text 和 blob 数据类型 — 为了节省空间，减少从磁盘读数据。&lt;/p&gt;
 &lt;p&gt;45、UTF 8 和 UTF16 比 latin1 慢。&lt;/p&gt;
 &lt;p&gt;46、有节制的使用触发器。&lt;/p&gt;
 &lt;p&gt;47、保持数据最小量的冗余 — 不要复制没必要的数据.&lt;/p&gt;
 &lt;p&gt;48、使用链接表，而不是扩展行。&lt;/p&gt;
 &lt;p&gt;49、注意你的数据类型，尽可能的使用最小的。&lt;/p&gt;
 &lt;p&gt;50、如果其他数据需要经常需要查询，而 blob/text 不需要，则将 blob/text 数据域其他数据分离。&lt;/p&gt;
 &lt;p&gt;51、经常检查和优化表。&lt;/p&gt;
 &lt;p&gt;52、经常做重写 InnoDB 表的优化。&lt;/p&gt;
 &lt;p&gt;53、有时，增加列时，先删除索引，之后在加上索引会更快。&lt;/p&gt;
 &lt;p&gt;54、为不同的需求选择不同的存储引擎。&lt;/p&gt;
 &lt;p&gt;55、日志表或审计表使用ARCHIVE存储引擎 — 写的效率更高。&lt;/p&gt;
 &lt;p&gt;56、将 session 数据存储在 memcache 中，而不是 MySQL 中 — memcache 可以设置自动过期，防止MySQL对临时数据高成本的读写操作。&lt;/p&gt;
 &lt;p&gt;57、如果字符串的长度是可变的，则使用VARCHAR代替CHAR — 节约空间，因为CHAR是固定长度，而VARCHAR不是（utf8 不受这个影响）。&lt;/p&gt;
 &lt;p&gt;58、逐步对 schema 做修改 — 一个小的变化将产生的巨大的影响。&lt;/p&gt;
 &lt;p&gt;59、在开发环境测试所有 schema 变动，而不是在生产环境的镜像上去做。&lt;/p&gt;
 &lt;p&gt;60、不要随意改变你的配置文件，这可能产生非常大的影响。&lt;/p&gt;
 &lt;p&gt;61、有时候，少量的配置会更好。&lt;/p&gt;
 &lt;p&gt;62、质疑使用通用的MySQL配置文件。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://ww4.sinaimg.cn/large/7cc829d3gw1etuoy4sz9kj20c30c2tc4.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;查询优化：&lt;/h3&gt;
 &lt;p&gt;63、使用慢查询日志，找出执行慢的查询。&lt;/p&gt;
 &lt;p&gt;64、使用 EXPLAIN 来决定查询功能是否合适。&lt;/p&gt;
 &lt;p&gt;65、经常测试你的查询，看是否需要做性能优化 — 性能可能会随着时间的变化而变化。&lt;/p&gt;
 &lt;p&gt;66、避免在整个表上使用count(*) ，它可能会将整个表锁住。&lt;/p&gt;
 &lt;p&gt;67、保持查询一致，这样后续类似的查询就能使用查询缓存了。&lt;/p&gt;
 &lt;p&gt;68、如果合适，用 GROUP BY 代替 DISTINCT。&lt;/p&gt;
 &lt;p&gt;69、在 WHERE、GROUP BY 和 ORDER BY 的列上加上索引。&lt;/p&gt;
 &lt;p&gt;70、保证索引简单，不要在同一列上加多个索引。&lt;/p&gt;
 &lt;p&gt;71、有时，MySQL 会选择错误的索引，这种情况使用 USE INDEX。&lt;/p&gt;
 &lt;p&gt;72、使用 SQL_MODE=STRICT 来检查问题。&lt;/p&gt;
 &lt;p&gt;73、索引字段少于5个时，UNION 操作用 LIMIT，而不是 OR。&lt;/p&gt;
 &lt;p&gt;74、使用 INSERT ON DUPLICATE KEY 或 INSERT IGNORE 来代替 UPDATE，避免 UPDATE 前需要先 SELECT。&lt;/p&gt;
 &lt;p&gt;75、使用索引字段和 ORDER BY 来代替 MAX。&lt;/p&gt;
 &lt;p&gt;76、避免使用 ORDER BY RAND()。&lt;/p&gt;
 &lt;p&gt;77、LIMIT M,N 在特定场景下会降低查询效率，有节制使用。&lt;/p&gt;
 &lt;p&gt;78、使用 UNION 来代替 WHERE 子句中的子查询。&lt;/p&gt;
 &lt;p&gt;79、对 UPDATE 来说，使用 SHARE MODE 来防止排他锁。&lt;/p&gt;
 &lt;p&gt;80、重启 MySQL 时，记得预热数据库，确保将数据加载到内存，提高查询效率。&lt;/p&gt;
 &lt;p&gt;81、使用 DROP TABLE ，然后再 CREATE TABLE ，而不是 DELETE FROM ，以删除表中所有数据。&lt;/p&gt;
 &lt;p&gt;82、最小化你要查询的数据，只获取你需要的数据，通常来说不要使用 *。&lt;/p&gt;
 &lt;p&gt;83、考虑持久连接，而不是多次建立连接，已减少资源的消耗。&lt;/p&gt;
 &lt;p&gt;84、基准查询，包括服务器的负载，有时一个简单的查询会影响其他的查询。&lt;/p&gt;
 &lt;p&gt;85、当服务器的负载增加时，使用SHOW PROCESSLIST来查看慢的/有问题的查询。&lt;/p&gt;
 &lt;p&gt;86、在存有生产环境数据副本的开发环境中，测试所有可疑的查询。&lt;/p&gt;
 &lt;h3&gt;MySQL备份过程：&lt;/h3&gt;
 &lt;p&gt;87、在二级复制服务器上进行备份。&lt;/p&gt;
 &lt;p&gt;88、备份过程中停止数据的复制，以防止出现数据依赖和外键约束的不一致。&lt;/p&gt;
 &lt;p&gt;89、彻底停止MySQL之后，再从数据文件进行备份。&lt;/p&gt;
 &lt;p&gt;90、如果使用MySQL dump进行备份，请同时备份二进制日志 — 确保复制过程不被中断。&lt;/p&gt;
 &lt;p&gt;91、不要信任 LVM 快照的备份 — 可能会创建不一致的数据，将来会因此产生问题。&lt;/p&gt;
 &lt;p&gt;92、为每个表做一个备份，这样更容易实现单表的恢复 — 如果数据与其他表是相互独立的。&lt;/p&gt;
 &lt;p&gt;93、使用 mysqldump 时，指定 -opt 参数。&lt;/p&gt;
 &lt;p&gt;94、备份前检测和优化表。&lt;/p&gt;
 &lt;p&gt;95、临时禁用外键约束，来提高导入的速度。&lt;/p&gt;
 &lt;p&gt;96、临时禁用唯一性检查，来提高导入的速度。&lt;/p&gt;
 &lt;p&gt;97、每次备份完后，计算数据库/表数据和索引的大小，监控其增长。&lt;/p&gt;
 &lt;p&gt;98、使用定时任务（cron）脚本，来监控从库复制的错误和延迟。&lt;/p&gt;
 &lt;p&gt;99、定期备份数据。&lt;/p&gt;
 &lt;p&gt;100、定期测试备份的数据。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术 MySQL优化</category>
      <guid isPermaLink="true">https://itindex.net/detail/54172-mysql-%E4%BC%98%E5%8C%96</guid>
      <pubDate>Sat, 15 Aug 2015 17:40:10 CST</pubDate>
    </item>
    <item>
      <title>Cookie与特殊字符</title>
      <link>https://itindex.net/detail/54586-cookie-%E5%AD%97%E7%AC%A6</link>
      <description>&lt;p&gt;这个是一个发生在自己身边的故事，由于Cookie值中设置了一个特殊字符，导致部分手机由于兼容性问题在打开站点时产生5xx错误，经分析Cookie中的字符并不被部分Android手机机型支持。&lt;/p&gt;
 &lt;p&gt;为什么会出现这样的情况？我们先来看下关于使用Cookie的一些注意事项：&lt;/p&gt;
 &lt;p&gt;1. Cookie的兼容性问题&lt;/p&gt;
 &lt;p&gt;Cookie的格式有2个不同的版本，第一个版本，我们称为Cookie Version 0，是最初由Netscape公司制定的，也被几乎所有的浏览器支持。而较新的版本，Cookie Version 1，则是根据RFC 2109文档制定的。为了确保兼容性，JAVA规定，前面所提到的涉及Cookie的操作都是针对旧版本的Cookie进行的。而新版本的Cookie目前还不被Javax.servlet.http.Cookie包所支持。&lt;/p&gt;
 &lt;p&gt;2. Cookie的内容&lt;/p&gt;
 &lt;p&gt;同样的Cookie的内容的字符限制针对不同的Cookie版本也有不同。在Cookie Version 0中，某些特殊的字符，例如：空格，方括号，圆括号，等于号（=），逗号，双引号，斜杠，问号，@符号，冒号，分号都不能作为Cookie的内容。虽然在Cookie Version 1规定中放宽了限制，可以使用这些字符，但是考虑到新版本的Cookie规范目前仍然没有为所有的浏览器所支持，因而为保险起见，我们应该在Cookie的内容中尽量避免使用这些字符。&lt;/p&gt;
 &lt;p&gt;RFC2109 制定的规范：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="rfc2109" height="251" src="http://www.biaodianfu.com/wp-content/uploads/2015/10/rfc2109.jpg" width="405"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;RFC 2068 制定的规范：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="rfc2068" height="189" src="http://www.biaodianfu.com/wp-content/uploads/2015/10/rfc2068.jpg" width="391"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;最后的忠告，Cookie中永远不要存特殊字符，即使要存储也要进行编码以后再存。&lt;/p&gt;
 &lt;div&gt;
  &lt;p&gt;Related posts:   &lt;ol&gt;
    &lt;li&gt;     &lt;a href="http://www.biaodianfu.com/jiejue-ie6-jianrong-wenti-ie7js.html" rel="bookmark" title="IE7.JS &amp;#35299;&amp;#20915;IE&amp;#20860;&amp;#23481;&amp;#24615;&amp;#38382;&amp;#39064;"&gt;IE7.JS 解决IE兼容性问题 &lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://www.biaodianfu.com/php-api-framework.html" rel="bookmark" title="PHP API &amp;#26694;&amp;#26550;&amp;#24320;&amp;#21457;&amp;#30340;&amp;#23398;&amp;#20064;"&gt;PHP API 框架开发的学习 &lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="http://www.biaodianfu.com/ie-png-problem.html" rel="bookmark" title="IE&amp;#19981;&amp;#33021;&amp;#26174;&amp;#31034;PNG&amp;#22270;&amp;#29255;&amp;#38382;&amp;#39064;"&gt;IE不能显示PNG图片问题 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>程序开发 字符编码</category>
      <guid isPermaLink="true">https://itindex.net/detail/54586-cookie-%E5%AD%97%E7%AC%A6</guid>
      <pubDate>Sun, 25 Oct 2015 15:14:58 CST</pubDate>
    </item>
    <item>
      <title>提升网站性能开发的10个技巧</title>
      <link>https://itindex.net/detail/54563-%E6%8F%90%E5%8D%87-%E7%BD%91%E7%AB%99-%E6%80%A7%E8%83%BD</link>
      <description>&lt;p&gt;  &lt;img alt="" height="527" src="http://www.techug.com/wordpress/wp-content/uploads/2015/10/66372-20151021112921052-419564390.jpg?e3f00d" width="666"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;随着网络的高速发展，网络性能的持续提高成为能否在芸芸 App 中脱颖而出的关键。高度联结的世界意味着用户对网络体验提出了更严苛的要求。假如你的网站不能做到快速响应，又或你的 App 存在延迟，用户很快就会移情你的竞争对手。以下为大家总结 10 条有关性能提升的经验，以供参考：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1.   采用反向代理服务器(Reverse Proxy Server)来对应用进行加速和保护&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;其作用主要在以下三方面：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;负载平衡 – 运行在反向代理服务器上的负载平衡器会在不同的不服务器  间进行传输平衡。透过它，你可以进行无差别的服务器增添。&lt;/li&gt;
  &lt;li&gt;存静态文件 – 对于直接的文件请求，例如图片文件或代码文件，可以直接存储在反向代理服务器然后直接发送给用户，从而可以进行快速访问并为应用服务器进行减负使得程序性能得到提升。&lt;/li&gt;
  &lt;li&gt;安全保护 – 反向代理服务器可以进行高安全度配置和对威胁进行识别和监测。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;2.   增添一个负载平衡器&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;为网站增添一个负载平衡器是一个相对简单的变更，但是它可以带来不错的性能和安全性提升。负载平衡器的作用在于在不同服务器间进行传输分发。&lt;/p&gt;
 &lt;p&gt;负载平衡器的实施前提是有一个反向代理服务器，它在接收到 Internet 通信后把相关请求发送到其它服务器。平衡器的妙处在于它支持两个或以上的应用服务器，使用选择算法来分割服务器间的请求。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.   缓存静态和动态内容&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;缓存技术的使用可使内容更快地展示给用户，其处理策略有：在需求发出时更快地处理内容，把内容存放在更快的设备上，或是使内容离用户更近。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.   数据压缩&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;压缩技术是一个巨大的潜在性能加速器。其主要作用体现在对图片，视频或音频等文件，能够进行高效的压缩处理。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.   优化 SSL/TLS 访问&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;尽管 SSL/TLS 变得越来越流行，但是它对于性能的影响也应得到重视。其对性能的影响主要体现在两个方面：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;每当新的连接开启，初始化握手都是无法避免的，即浏览器每次都需要使用 HTTP/1.X 建立服务器连接。&lt;/li&gt;
  &lt;li&gt;存放于服务器上的加密数据会越来越大，加密后用户读取时也需要进行解码。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;那么该如何进行处理呢？&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;会话缓存—使用 ssl_session_cache 来直接缓存建立新 SSL/TLS 连接的参数&lt;/li&gt;
  &lt;li&gt;会话 ID 化—把指定 SSL/TLS 的标识/ID 存放起来，但要建立新连接时，就可以直接取用，从而免去重新建立通信的繁琐。&lt;/li&gt;
  &lt;li&gt;OCSP stapling 优化—通过抓取 SSL/TLS 认证信息来减免建立通信的时间。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;6.   部署 HTTP/2 或 SPDY&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;对于已经启用 SSL/TLS 的网站，一旦结合 HTTP/2 和 SPDY 将能实现性能上的强强联合；因为其结果是会让单一连接的建立仅需一次通信握手。SPDY 和 HTTP/2 的主要特性是它们使用的是单一连接而不是多方连接。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;7.   定期更新软件版本&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;8.   优化 Linux 性能&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;例如对 Linux 进行以下配置或处理：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Backlog 队列&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;如果你有一些将要停用的连接，可以考虑增加 net.core.somaxconn。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;文件描述符&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;NGINX 允许每个连接最多使用两个文件描述符。如果你的系统服务的是多个连接，你可能需要考虑增大 sys.fs.file_max 的值。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;瞬时端口&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;当作为一个代理使用时，NGINX 会为每个 upstream 服务器创建临时的瞬时(ephemeral)端口。因此可以尝试加大 net.ipv4.ip_local_port_range 的值来增加可用端口数。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;9.   优化 Web 服务器性能&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;访问日志优化&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在 NGINX 中，在 access_log 中加入 buffer=size 参数来实现日志的缓存写入；加入 flush=time 则可实现在某个时间间隔后进行缓存内容写入。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;缓存&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;启用缓存可使连接响应更快。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;客户端活动连接&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;活动连接可减少重连的次数，特别是启用 SSL/TLS 的情况下。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Upstream 活动连接&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Upstream 连接指的是连接到程序服务器，数据库服务器等的连接。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;限制资源的访问&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;采取合适的策略来限制资源访问可以提高性能和安全性。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;进行 worker 处理&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Worker 处理模式就是请求驱动处理模式。NGINX 使用了一个基于事件的模型和 OS 依赖机制来有效地对请求进行分发。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;进行 socket 分表&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Socket 分表可以为每个 worker 处理创建一个 socket 监听器，当核心委派连接分到给监听器时，可以马上知道哪个处理是即将执行的，从而使处理流程变得简洁。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;线程池处理&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;任何计算机线程都有可能由于单个缓慢的操作而挂起。对于 web 服务器软件来说，磁盘访问是一个性能瓶颈，例如进行数据复制等操作。当使用线程池来处理时，可以把一些响应慢的操作单独地放入某个任务组里面，从而不会对其它操作造成影响。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;10. 进行实时监控以快速解决问题和瓶颈&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;实施实时监控，可以全面掌握系统的运行情况，发现问题解决问题，甚至是找出造成性能瓶颈或运行缓慢的原因。&lt;/p&gt;
 &lt;p&gt;例如可对如下的问题进行监控：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;服务器宕机&lt;/li&gt;
  &lt;li&gt;连接访问丢失&lt;/li&gt;
  &lt;li&gt;服务器缓存丢失严重&lt;/li&gt;
  &lt;li&gt;服务器发送了错误的数据&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术 开发的10个技巧 提升网站性能</category>
      <guid isPermaLink="true">https://itindex.net/detail/54563-%E6%8F%90%E5%8D%87-%E7%BD%91%E7%AB%99-%E6%80%A7%E8%83%BD</guid>
      <pubDate>Thu, 22 Oct 2015 09:00:11 CST</pubDate>
    </item>
    <item>
      <title>HTML 5 APIs 是如何跟踪用户轨迹的</title>
      <link>https://itindex.net/detail/54535-html-apis-%E8%B7%9F%E8%B8%AA</link>
      <description>&lt;p&gt;如果你认为 IP 地址、cookies 和 HTTP 头是在 web 上唯一标识和跟踪用户的要素的话，那么你就大错特错了！&lt;/p&gt;
 &lt;p&gt;新的现代化特征辨别技术依赖于以下多种要素：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;IP地址&lt;/li&gt;
  &lt;li&gt;Cookies&lt;/li&gt;
  &lt;li&gt;语言&lt;/li&gt;
  &lt;li&gt;时区&lt;/li&gt;
  &lt;li&gt;HTTP 头（用户代理, 参考页等）&lt;/li&gt;
  &lt;li&gt;HTML5 APIs（WebRTC, Battery API等）&lt;/li&gt;
  &lt;li&gt;HTML5 和 CSS3 特征检测&lt;/li&gt;
  &lt;li&gt;CSS 媒体查询&lt;/li&gt;
  &lt;li&gt;WebGL&lt;/li&gt;
  &lt;li&gt;浏览器插件（Flash, Silverlight, Java等）&lt;/li&gt;
  &lt;li&gt;浏览器加载项&lt;/li&gt;
  &lt;li&gt;浏览器选项（阻止浏览器追踪等）&lt;/li&gt;
  &lt;li&gt;浏览器存储&lt;/li&gt;
  &lt;li&gt;系统字体&lt;/li&gt;
  &lt;li&gt;TLS/SSL 会话ID&lt;/li&gt;
  &lt;li&gt;硬件检测（摄像头，麦克风，触摸屏等）&lt;/li&gt;
  &lt;li&gt;屏幕（分辨率，色彩深度，像素等）&lt;/li&gt;
  &lt;li&gt;音视频编解码器&lt;/li&gt;
  &lt;li&gt;装配特征&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;最近 W3C 在 HTML 标准中允许开发者与用户设备通讯，以增强网页、App 和游戏设置。很多 API 被开发出来以便更精准的辨别用户身份已不足为奇。&lt;/p&gt;
 &lt;h3&gt;什么是指纹特征呢？&lt;/h3&gt;
 &lt;p&gt;想象一下，当你走进一家商店入口处，一个高级的摄像头对你进行扫描，然后保存你的相关信息，如：体型、身高、肤色、服饰、鞋子、走路姿态、语调等。所有这些数据将被排成一个序列，然后通过一个哈希函数来计算你的特征图谱。下次你再来到这家商店或属于相同机构的其他商店时，通过快速分析你的特征，即使你着装风格不同，也能将你与之前访问的那个人联系起来。&lt;/p&gt;
 &lt;p&gt;使用浏览器访问网页也同样如此（不需要用户做任何特别的操作）。无论你尚未登录或者禁用 cookies，它都可以将用户与一个标识联系起来，它还不是百分之百的准确但一直在改进。&lt;/p&gt;
 &lt;p&gt;电子前线基金会（  &lt;em&gt;Electronic Frontier Foundation&lt;/em&gt;）在出版的“Web浏览器如何独特？”（PDF）一文中研究了浏览器的用户跟踪。WebKit Wiki和Wikipedia上有设备识别的精确描述。&lt;/p&gt;
 &lt;h2&gt;客户端的JavaScript&lt;/h2&gt;
 &lt;p&gt;如果你想对指纹特征有更好的了解，可以查询一下 web 开发者专用的 JavaScript 库：Fingerprintjs2。&lt;/p&gt;
 &lt;p&gt;要想有效地防止识别指纹特征的脚本运行，你可以通过阻止全局 JavaScript 的运行来实现，又或者使用  &lt;strong&gt;NoScript &lt;/strong&gt;或者   &lt;strong&gt;uMatrix&lt;/strong&gt; 拓展。但还有很多因素会暴露过多与你相关的信息，并且会继续保存在其他未知的指纹特征方式中。&lt;/p&gt;
 &lt;h2&gt;HTML5 APIs&lt;/h2&gt;
 &lt;p&gt;得益于新型的 HTML5 标准，开发者在某些情况下不需请求批准，就有有权限去获得和检测的用户信息或硬件设备。以下的 API 仍  &lt;em&gt;处于无监管下&lt;/em&gt;被滥用。要避免这种情况，最普遍的方法是禁止 JavaScript 运行，或者使用特殊的加载项。&lt;/p&gt;
 &lt;h3&gt;Canvas&lt;/h3&gt;
 &lt;p&gt;这是令人讨厌，隐秘和（启用 javascript）几乎就不可停止的技术，从 2012 年以来就积极被利用，在广泛使用的脚本（记得添加这个 “探索”找到替代品放到 cookies 中）中偶尔嵌入。&lt;/p&gt;
 &lt;p&gt;当浏览器访问一个网页是一个 canvas 的指纹脚本，它就被要求去绘制一个隐藏的图形并转换为一个令牌。令牌的独特性等因素取决于浏览器，操作系统和安装的图形硬件。&lt;/p&gt;
 &lt;p&gt;为了避免 Canvas 指纹你可以：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;显示全局 javascript&lt;/li&gt;
  &lt;li&gt;使用   &lt;em&gt; NoScript,&lt;/em&gt;    &lt;em&gt;uMatrix&lt;/em&gt; 或者   &lt;em&gt;CanvasFingerprintBlock&lt;/em&gt; (仅Chrome支持) 扩展&lt;/li&gt;
  &lt;li&gt;使用    &lt;em&gt;Tor 浏览器&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;电池&lt;/h3&gt;
 &lt;p&gt;由于检测电池状况的 API 能够获得设备电池的充电时间和放电时间。这些数据一旦结合起来就会近似于每个设备和电池状态的特征，成为网络中跟踪活动的潜在因素。&lt;/p&gt;
 &lt;p&gt;一篇名为《正在泄露的电池——对 HTML 5电池信息 API 的隐私调查》的论文针对 Linux 系统上的 Firefox 浏览器用户进行了调查。其中最深刻的研究结构是：“我们打算对电池状态 API 进行小改造，并在 Firefox 浏览器里运行，去定位调查中的隐私流出。后来，我们给 Firefox 的漏洞汇报被接受了，而且开始修复漏洞。&lt;/p&gt;
 &lt;p&gt;在 Chrome 浏览器，你可以安装名为   &lt;em&gt;Battery Info Blocker &lt;/em&gt;的加载项去禁止网站获取你的电池信息。&lt;/p&gt;
 &lt;h3&gt;WebRTC(Web Real-Time Communication，网页实时通信)&lt;/h3&gt;
 &lt;p&gt;当你不使用 WebRTC 时，应当禁止它。WebRTC 不仅会泄露你的本地 IP 地址，还会在 VPN 上泄露你的 IP 地址（在 Windows 系统下），这不同于采集系统的指纹特征的因素。&lt;/p&gt;
 &lt;p&gt;为了避免 WebRTC 泄露你的隐私，应该使用 Firefox 浏览器并且禁止 WebRTC，这可以通过打开about:config，找到值为 media.peerconnection.enabled 的项并且设置成 false&lt;/p&gt;
 &lt;p&gt;在 Chrome 中，你可以安装名为 WebRTC 的加载项，否则会发生隐私泄露。&lt;/p&gt;
 &lt;h3&gt;资源时序&lt;/h3&gt;
 &lt;p&gt;开发者能够使用 API 去收集完整的与文件资源有关的时序信息。在 Working Draft 中有表达对隐私的担忧：“统计性的指纹特征是一种对隐私的担忧，恶意网站会通过检测浏览器缓存的时序信息，包括访问和忽略第三方网站的资源，来判断使用者是否访问过第三方网站“&lt;/p&gt;
 &lt;p&gt;如果你使用 Firefox 浏览器，可以通过打开  &lt;em&gt; about:config&lt;/em&gt;，并且把选项   &lt;em&gt;dom.enable_resource_timing&lt;/em&gt;,   &lt;em&gt;dom.enable_user_timing&lt;/em&gt; 和  &lt;em&gt;dom.performance.enable_user_timing_logging&lt;/em&gt; 设置为 false，来阻止这些 API 运行。&lt;/p&gt;
 &lt;p&gt;在 Chrome 浏览器下，唯一的办法是禁止 JavaScript 脚本运行。&lt;/p&gt;
 &lt;h3&gt;Geolocation（地理位置）&lt;/h3&gt;
 &lt;p&gt;如果使用这个功能，它会泄露你的物理位置，侵犯你的隐私。现代浏览器总是询问许可来泄露地理位置给网站和 app。&lt;/p&gt;
 &lt;p&gt;在 Firefox 上禁用这个功能，你应该在地址栏上输入 about:config，找到 geo.enabled 的值，设置其值为 false。&lt;/p&gt;
 &lt;p&gt;在 Chrome 点击设置（Settings），从显示高级设置（Show advanced settings）上，找到隐私（Privacy）并且点击内容设置（Content settings）， 在窗口里找到定位（Location）并设置选项不允许任何网站追踪你的物理位置（Do not allow any site to track your physical location）。&lt;/p&gt;
 &lt;h3&gt;硬件指纹识别&lt;/h3&gt;
 &lt;p&gt;一篇名为 “  &lt;em&gt;Hardware Fingerprinting Using HTML5&lt;/em&gt;” 的论文(PDF)提到，有一项新的潜在的技术，它基于与硬件设备通信的能力，来获取特定的硬件指纹，作为对基于软件的指纹（浏览器，操作系统，等）的补充。&lt;/p&gt;
 &lt;p&gt;这篇论文提到，例如 GPU（现代浏览器使用硬件来加速），摄像机，扬声器，麦克风，运动传感器，GPS 和电池等硬件，都可以被 HTML5（未必需要用户许可）访问，尤其是 GPU，对进行指纹识别的用户很有用。  &lt;br /&gt;
权益，请及时联系我们&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术</category>
      <guid isPermaLink="true">https://itindex.net/detail/54535-html-apis-%E8%B7%9F%E8%B8%AA</guid>
      <pubDate>Thu, 15 Oct 2015 19:00:51 CST</pubDate>
    </item>
    <item>
      <title>Ngnix 日志管理及 Shell 实现定时完成日志切割</title>
      <link>https://itindex.net/detail/52866-ngnix-%E6%97%A5%E5%BF%97-%E7%AE%A1%E7%90%86</link>
      <description>&lt;h2&gt;一、日志管理&lt;/h2&gt;
 &lt;p&gt;先来看看ngnix的配置文件的server段&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/03/17902d035981ebad6fc60a1ac613c98b.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;接下来我们解释一下默认格式的具体意思&lt;/p&gt;
 &lt;p&gt;#log_format main ‘$remote_addr（远程IP） – $remote_user（远程用户） [$time_local]（访问时间） “$request”（请求方式） ‘  &lt;br /&gt;
# ‘$status（状态302、404、401、403等） $body_bytes_sent（请求体 body 长度等） “$http_referer”（referer来源信息） ‘  &lt;br /&gt;
# ‘”$http_user_agent（用户代理）” “$http_x_forwarded_for（被转发的请求的原始IP）”‘;&lt;/p&gt;
 &lt;p align="left"&gt;注：（1）http_x_forwarded_for:在经过代理时,代理把你的本来IP加在此头信息中,传输你的原始IP&lt;/p&gt;
 &lt;p align="left"&gt;　　（2）日志的格式我们也可以自己定义&lt;/p&gt;
 &lt;p align="left"&gt;我们具体看一下日志记录的信息&lt;/p&gt;
 &lt;p align="left"&gt;  &lt;img alt="" height="138" src="http://static.codeceo.com/images/2015/03/838c979701e665737684e7f6a342a0ad.png" width="1116"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p align="left"&gt;Ngnix允许针对不同的server做不同的log，接下来我们自己做一个&lt;/p&gt;
 &lt;p align="left"&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/03/1b42ea2d3f57d8a8951494fb103dfbe2.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p align="left"&gt;保存并退出，然后重新加载一次配置文件&lt;/p&gt;
 &lt;p align="left"&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/03/3619f4566a626b7c4e63e3a70ad5d7e6.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p align="left"&gt;因为我们没开启日志格式，进入配置文件开启即可&lt;/p&gt;
 &lt;p align="left"&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/03/166ad1a92743e39d5d7cee4044c65517.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p align="left"&gt;接下来重载配置文件成功，然后访问下服务器，再查看log目录下是否有kelly.log日志文件，然后在more kelly.log看是否有日志记录&lt;/p&gt;
 &lt;p align="left"&gt;  &lt;img alt="" height="291" src="http://static.codeceo.com/images/2015/03/2eb3b26fddc364b47f6a60af97ef31d9.png" width="1107"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;二、用定时任务完成日志切割备份&lt;/h2&gt;
 &lt;p align="left"&gt;思路：用shell写一个脚本，每到00:00:01时就把昨天的日志按日期时间格式重命名，放在相应的目录下，再用USR1信息号控制ngnix重新生成新的日志文件。&lt;/p&gt;
 &lt;p align="left"&gt;接下来我们在/usr/local/ngnix/data目录下做实验，创建一个shell脚本，文件名为runlog.sh&lt;/p&gt;
 &lt;p align="left"&gt;我以下图对此脚本做详细解释&lt;/p&gt;
 &lt;p align="left"&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/03/d3a524d7c3e7bed39d3f040472453871.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p align="left"&gt;我附上该shell脚本源码，方便读者做测试：&lt;/p&gt;
 &lt;pre&gt;#!/bin/bash
base_path=&amp;apos;/usr/local/nginx/logs&amp;apos;
log_path=$(date -d yesterday +&amp;quot;%Y%m&amp;quot;)
day=$(date -d yesterday +&amp;quot;%d&amp;quot;)
mkdir -p $base_path/$log_path
mv $base_path/access.log $base_path/$log_path/access_$day.log
#echo $base_path/$log_path/access_$day.log
kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`&lt;/pre&gt;
 &lt;p&gt;接下来做个定时任务，按指定时间自动执行此脚本（这里我是做测试，所以我每分钟执行一下这个脚本，方便看效果）&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/03/a120e740cba8cdf5287a43624d5ec024.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/03/7dbbfe411aa4d24379a4a1d245954c75.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p align="left"&gt;然后查看最终效果&lt;/p&gt;
 &lt;p align="left"&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/03/81ee4d692e168785e246e42785a0ab07.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p align="left"&gt;此时，我们已经完成了用定时任务执行脚本，然后做日志切割备份。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>操作系统 编程开发 Ngnix shell 日志</category>
      <guid isPermaLink="true">https://itindex.net/detail/52866-ngnix-%E6%97%A5%E5%BF%97-%E7%AE%A1%E7%90%86</guid>
      <pubDate>Wed, 04 Mar 2015 08:29:04 CST</pubDate>
    </item>
    <item>
      <title>JavaScript性能优化小知识总结</title>
      <link>https://itindex.net/detail/52861-javascript-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-%E7%9F%A5%E8%AF%86</link>
      <description>&lt;p&gt;JavaScript的性能问题不容小觑，这就需要我们开发人员在编写JavaScript程序时多注意一些细节，本文非常详细的介绍了一下JavaScript性能优化方面的知识点，绝对是干货。&lt;/p&gt;
 &lt;h2&gt;前言&lt;/h2&gt;
 &lt;p&gt;一直在学习javascript，也有看过《犀利开发Jquery内核详解与实践》，对这本书的评价只有两个字犀利，可能是对javascript理解的还不够透彻异或是自己太笨，更多的是自己不擅于思考懒得思考以至于里面说的一些精髓都没有太深入的理解。&lt;/p&gt;
 &lt;p&gt;鉴于想让自己有一个提升，进不了一个更加广阔的天地，总得找一个属于自己的居所好好生存，所以平时会有意无意的去积累一些使用jQuerry的常用知识，特别是对于性能要求这一块，总是会想是不是有更好的方式来实现。&lt;/p&gt;
 &lt;p&gt;下面是我总结的一些小技巧，仅供参考。(我先会说一个总标题，然后用一小段话来说明这个意思 再最后用一个demo来简单言明)&lt;/p&gt;
 &lt;h2&gt;避免全局查找&lt;/h2&gt;
 &lt;p&gt;在一个函数中会用到全局对象存储为局部变量来减少全局查找，因为访问局部变量的速度要比访问全局变量的速度更快些&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function search() {
            //当我要使用当前页面地址和主机域名
            alert(window.location.href + window.location.host);
        }
        //最好的方式是如下这样  先用一个简单变量保存起来
        function search() {
            var location = window.location;
            alert(location.href + location.host);
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;定时器&lt;/h2&gt;
 &lt;p&gt;如果针对的是不断运行的代码，  &lt;strong&gt;不应该使用setTimeout，而应该是用setInterval&lt;/strong&gt;，因为setTimeout每一次都会初始化一个定时器，而setInterval只会在开始的时候初始化一个定时器&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var timeoutTimes = 0;
        function timeout() {
            timeoutTimes++;
            if (timeoutTimes &amp;lt; 10) {
                setTimeout(timeout, 10);
            }
        }
        timeout();
        //可以替换为：
        var intervalTimes = 0;
        function interval() {
            intervalTimes++;
            if (intervalTimes &amp;gt;= 10) {
                clearInterval(interv);
            }
        }
        var interv = setInterval(interval, 10);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;字符串连接&lt;/h2&gt;
 &lt;p&gt;如果要连接多个字符串，应该少使用+=，如&lt;/p&gt;
 &lt;p&gt;s+=a;&lt;/p&gt;
 &lt;p&gt;s+=b;&lt;/p&gt;
 &lt;p&gt;s+=c;&lt;/p&gt;
 &lt;p&gt;应该写成s+=a + b + c；&lt;/p&gt;
 &lt;p&gt;而如果是收集字符串，比如多次对同一个字符串进行+=操作的话，最好使用一个缓存，使用JavaScript数组来收集，最后  &lt;strong&gt;使用join方法连接起来&lt;/strong&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var buf = [];
        for (var i = 0; i &amp;lt; 100; i++) {
            buf.push(i.toString());
        }
        var all = buf.join(&amp;quot;&amp;quot;);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;避免with语句&lt;/h2&gt;
 &lt;p&gt;和函数类似 ，with语句会创建自己的作用域，因此会增加其中执行的代码的作用域链的长度，由于额外的作用域链的查找，在with语句中执行的代码肯定会比外面执行的代码要慢，在  &lt;strong&gt;能不使用with语句的时候尽量不要使用with语句&lt;/strong&gt;。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt; with (a.b.c.d) {
            property1 = 1;
            property2 = 2;
        }
        //可以替换为：
        var obj = a.b.c.d;
        obj.property1 = 1;
        obj.property2 = 2;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;数字转换成字符串&lt;/h2&gt;
 &lt;p&gt;般最好用”&amp;quot; + 1来将数字转换成字符串，虽然看起来比较丑一点，但事实上这个效率是最高的，性能上来说：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;(“” +) &amp;gt; String() &amp;gt; .toString() &amp;gt; new String()&lt;/strong&gt;&lt;/p&gt;
 &lt;h2&gt;浮点数转换成整型&lt;/h2&gt;
 &lt;p&gt;很多人喜欢使用parseInt()，其实parseInt()是用于将字符串转换成数字，而不是浮点数和整型之间的转换，我们应该使用Math.floor()或者Math.round()&lt;/p&gt;
 &lt;h2&gt;各种类型转换&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;var myVar = &amp;quot;3.14159&amp;quot;,
        str = &amp;quot;&amp;quot; + myVar, //  to string  
        i_int = ~ ~myVar,  //  to integer  
        f_float = 1 * myVar,  //  to float  
        b_bool = !!myVar,  /*  to boolean - any string with length 
                                and any number except 0 are true */
        array = [myVar];  //  to array&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;如果定义了toString()方法来进行类型转换的话，推荐  &lt;strong&gt;显式调用toString()&lt;/strong&gt;，因为内部的操作在尝试所有可能性之后，会尝试对象的toString()方法尝试能否转化为String，所以直接调用这个方法效率会更高&lt;/p&gt;
 &lt;h2&gt;多个类型声明&lt;/h2&gt;
 &lt;p&gt;在JavaScript中所有变量都可以使用单个var语句来声明，这样就是组合在一起的语句，以减少整个脚本的执行时间，就如上面代码一样，上面代码格式也挺规范，让人一看就明了。&lt;/p&gt;
 &lt;h2&gt;插入迭代器&lt;/h2&gt;
 &lt;p&gt;如var name=values[i]; i++;前面两条语句可以写成var name=values[i++]&lt;/p&gt;
 &lt;h2&gt;使用直接量&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;var aTest = new Array(); //替换为
        var aTest = [];
        var aTest = new Object; //替换为
        var aTest = {};
        var reg = new RegExp(); //替换为
        var reg = /../;
        //如果要创建具有一些特性的一般对象，也可以使用字面量，如下：
        var oFruit = new O;
        oFruit.color = &amp;quot;red&amp;quot;;
        oFruit.name = &amp;quot;apple&amp;quot;;
        //前面的代码可用对象字面量来改写成这样：
        var oFruit = { color: &amp;quot;red&amp;quot;, name: &amp;quot;apple&amp;quot; };&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;使用DocumentFragment优化多次append&lt;/h2&gt;
 &lt;p&gt;一旦需要更新DOM,请考虑使用文档碎片来构建DOM结构，然后再将其添加到现存的文档中。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;for (var i = 0; i &amp;lt; 1000; i++) {
            var el = document.createElement(&amp;apos;p&amp;apos;);
            el.innerHTML = i;
            document.body.appendChild(el);
        }
        //可以替换为：
        var frag = document.createDocumentFragment();
        for (var i = 0; i &amp;lt; 1000; i++) {
            var el = document.createElement(&amp;apos;p&amp;apos;);
            el.innerHTML = i;
            frag.appendChild(el);
        }
        document.body.appendChild(frag);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;使用一次innerHTML赋值代替构建dom元素&lt;/h2&gt;
 &lt;p&gt;对于大的DOM更改，使用innerHTML要比使用标准的DOM方法创建同样的DOM结构快得多。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var frag = document.createDocumentFragment();
        for (var i = 0; i &amp;lt; 1000; i++) {
            var el = document.createElement(&amp;apos;p&amp;apos;);
            el.innerHTML = i;
            frag.appendChild(el);
        }
        document.body.appendChild(frag);
        //可以替换为：
        var html = [];
        for (var i = 0; i &amp;lt; 1000; i++) {
            html.push(&amp;apos;&amp;lt;p&amp;gt;&amp;apos; + i + &amp;apos;&amp;lt;/p&amp;gt;&amp;apos;);
        }
        document.body.innerHTML = html.join(&amp;apos;&amp;apos;);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;通过模板元素clone，替代createElement&lt;/h2&gt;
 &lt;p&gt;很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低，如果需要直接插入HTML，可以找一个容器元素，比如指定一个div或者span，并设置他们的innerHTML来将自己的HTML代码插入到页面中。通常我们可能会使用字符串直接写HTML来创建节点，其实这样做，1无法保证代码的有效性2字符串操作效率低，所以应该是用document.createElement()方法，而如果文档中存在现成的样板节点，应该是用cloneNode()方法，因为使用createElement()方法之后，你需要设置多次元素的属性，使用cloneNode()则可以减少属性的设置次数——同样如果需要创建很多元素，应该先准备一个样板节点&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var frag = document.createDocumentFragment();
        for (var i = 0; i &amp;lt; 1000; i++) {
            var el = document.createElement(&amp;apos;p&amp;apos;);
            el.innerHTML = i;
            frag.appendChild(el);
        }
        document.body.appendChild(frag);
        //替换为：
        var frag = document.createDocumentFragment();
        var pEl = document.getElementsByTagName(&amp;apos;p&amp;apos;)[0];
        for (var i = 0; i &amp;lt; 1000; i++) {
            var el = pEl.cloneNode(false);
            el.innerHTML = i;
            frag.appendChild(el);
        }
        document.body.appendChild(frag);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;使用firstChild和nextSibling代替childNodes遍历dom元素&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;   &lt;img alt="" src="http://static.codeceo.com/images/2015/03/8d08ae397597d1b7ed9b79fc83e1db24.png"&gt;&lt;/img&gt;&lt;/strong&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var nodes = element.childNodes;
        for (var i = 0, l = nodes.length; i &amp;lt; l; i++) {
            var node = nodes[i];
            //……
        }
        //可以替换为：
        var node = element.firstChild;
        while (node) {
            //……
            node = node.nextSibling;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;删除DOM节点&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;删除dom节点之前,一定要删除注册在该节点上的事件&lt;/strong&gt;,不管是用observe方式还是用attachEvent方式注册的事件,否则将会产生无法回收的内存。另外，在removeChild和innerHTML=’’二者之间,尽量选择后者. 因为在sIEve(内存泄露监测工具)中监测的结果是用removeChild无法有效地释放dom节点&lt;/p&gt;
 &lt;h2&gt;使用事件代理&lt;/h2&gt;
 &lt;p&gt;任何可以冒泡的事件都不仅仅可以在事件目标上进行处理，目标的任何祖先节点上也能处理，使用这个知识就可以将事件处理程序附加到更高的地方负责多个目标的事件处理，同样，  &lt;strong&gt;对于内容动态增加并且子节点都需要相同的事件处理函数的情况，可以把事件注册提到父节点上，这样就不需要为每个子节点注册事件监听了&lt;/strong&gt;。另外，现有的js库都采用observe方式来创建事件监听,其实现上隔离了dom对象和事件处理函数之间的循环引用,所以应该尽量采用这种方式来创建事件监听&lt;/p&gt;
 &lt;h2&gt;重复使用的调用结果，事先保存到局部变量&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;        //避免多次取值的调用开销
        var h1 = element1.clientHeight + num1;
        var h2 = element1.clientHeight + num2;
        //可以替换为：
        var eleHeight = element1.clientHeight;
        var h1 = eleHeight + num1;
        var h2 = eleHeight + num2;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;注意NodeList&lt;/h2&gt;
 &lt;p&gt;最小化访问NodeList的次数可以极大的改进脚本的性能&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var images = document.getElementsByTagName(&amp;apos;img&amp;apos;);
        for (var i = 0, len = images.length; i &amp;lt; len; i++) {

        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;编写JavaScript的时候一定要知道何时返回NodeList对象，这样可以最小化对它们的访问&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;进行了对getElementsByTagName()的调用&lt;/li&gt;
  &lt;li&gt;获取了元素的childNodes属性&lt;/li&gt;
  &lt;li&gt;获取了元素的attributes属性&lt;/li&gt;
  &lt;li&gt;访问了特殊的集合，如document.forms、document.images等等&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;要了解了当使用NodeList对象时，合理使用会极大的提升代码执行速度&lt;/p&gt;
 &lt;h2&gt;优化循环&lt;/h2&gt;
 &lt;p&gt;可以使用下面几种方式来优化循环&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;减值迭代&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;大多数循环使用一个从0开始、增加到某个特定值的迭代器，在很多情况下，从最大值开始，在循环中不断减值的迭代器更加高效&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;简化终止条件&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;由于每次循环过程都会计算终止条件，所以必须保证它尽可能快，也就是说避免属性查找或者其它的操作，最好是将循环控制量保存到局部变量中，也就是说对数组或列表对象的遍历时，提前将length保存到局部变量中，避免在循环的每一步重复取值。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var list = document.getElementsByTagName(&amp;apos;p&amp;apos;);
        for (var i = 0; i &amp;lt; list.length; i++) {
            //……
        }

        //替换为：
        var list = document.getElementsByTagName(&amp;apos;p&amp;apos;);
        for (var i = 0, l = list.length; i &amp;lt; l; i++) {
            //……
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;简化循环体&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;循环体是执行最多的，所以要确保其被最大限度的优化&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;使用后测试循环&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在JavaScript中，我们可以使用for(;;),while(),for(in)三种循环，事实上，这三种循环中for(in)的效率极差，因为他需要查询散列键，只要可以，就应该尽量少用。for(;;)和while循环，while循环的效率要优于for(;;)，可能是因为for(;;)结构的问题，需要经常跳转回去。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var arr = [1, 2, 3, 4, 5, 6, 7];
        var sum = 0;
        for (var i = 0, l = arr.length; i &amp;lt; l; i++) {
            sum += arr[i];
        }

        //可以考虑替换为：

        var arr = [1, 2, 3, 4, 5, 6, 7];
        var sum = 0, l = arr.length;
        while (l--) {
            sum += arr[l];
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;最常用的for循环和while循环都是前测试循环，而如do-while这种后测试循环，可以避免最初终止条件的计算，因此运行更快。&lt;/p&gt;
 &lt;h2&gt;展开循环&lt;/h2&gt;
 &lt;p&gt;当循环次数是确定的，消除循环并使用多次函数调用往往会更快。&lt;/p&gt;
 &lt;h2&gt;避免双重解释&lt;/h2&gt;
 &lt;p&gt;如果要提高代码性能，尽可能避免出现需要按照JavaScript解释的字符串，也就是&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;尽量少使用&lt;/strong&gt;   &lt;strong&gt;eval&lt;/strong&gt;   &lt;strong&gt;函数&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;使用eval相当于在运行时再次调用解释引擎对内容进行运行，需要消耗大量时间，而且使用Eval带来的安全性问题也是不容忽视的。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;不要使用&lt;/strong&gt;   &lt;strong&gt;Function&lt;/strong&gt;   &lt;strong&gt;构造器&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;不要给setTimeout或者setInterval传递字符串参数&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var num = 0;
        setTimeout(&amp;apos;num++&amp;apos;, 10);
        //可以替换为：
        var num = 0;
        function addNum() {
            num++;
        }
        setTimeout(addNum, 10);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;缩短否定检测&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;       if (oTest != &amp;apos;#ff0000&amp;apos;) {
            //do something
        }
        if (oTest != null) {
            //do something
        }
        if (oTest != false) {
            //do something
        }
        //虽然这些都正确，但用逻辑非操作符来操作也有同样的效果：
        if (!oTest) {
            //do something
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;条件分支&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;将条件分支，按可能性顺序从高到低排列：可以减少解释器对条件的探测次数&lt;/li&gt;
  &lt;li&gt;在同一条件子的多（&amp;gt;2）条件分支时，使用switch优于if：switch分支选择的效率高于if，在IE下尤为明显。4分支的测试，IE下switch的执行时间约为if的一半。&lt;/li&gt;
  &lt;li&gt;使用三目运算符替代条件分支&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;
  &lt;pre&gt;        if (a &amp;gt; b) {
            num = a;
        } else {
            num = b;
        }
        //可以替换为：
        num = a &amp;gt; b ? a : b;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;使用常量&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;重复值&lt;/strong&gt;:任何在多处用到的值都应该抽取为一个常量&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;用户界面字符串&lt;/strong&gt;:任何用于显示给用户的字符串，都应该抽取出来以方便国际化&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;URLs&lt;/strong&gt;:在Web应用中，资源位置很容易变更，所以推荐用一个公共地方存放所有的URL&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;任意可能会更改的值:&lt;/strong&gt;每当你用到字面量值的时候，你都要问一下自己这个值在未来是不是会变化，如果答案是“是”，那么这个值就应该被提取出来作为一个常量。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;避免与null进行比较&lt;/h2&gt;
 &lt;p&gt;由于JavaScript是弱类型的，所以它不会做任何的自动类型检查，所以如果看到与null进行比较的代码，尝试使用以下技术替换&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果值应为一个引用类型，使用instanceof操作符检查其构造函数&lt;/li&gt;
  &lt;li&gt;如果值应为一个基本类型，作用typeof检查其类型&lt;/li&gt;
  &lt;li&gt;如果是希望对象包含某个特定的方法名，则使用typeof操作符确保指定名字的方法存在于对象上&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;避免全局量&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;全局变量应该全部字母大写，各单词之间用_下划线来连接&lt;/strong&gt;。尽可能避免全局变量和函数, 尽量减少全局变量的使用，因为在一个页面中包含的所有JavaScript都在同一个域中运行。所以如果你的代码中声明了全局变量或者全局函数的话，后面的代码中载入的脚本文件中的同名变量和函数会覆盖掉（overwrite）你的。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;//糟糕的全局变量和全局函数
var current = null;
function init(){
//...
}
function change() {
    //...
}
function verify() {
    //...
}
//解决办法有很多，Christian Heilmann建议的方法是：
//如果变量和函数不需要在“外面”引用，那么就可以使用一个没有名字的方法将他们全都包起来。
(function(){
var current = null;
function init() {
    //...
}
function change() {
    //...
}
function verify() {
    //...
}
})();
//如果变量和函数需要在“外面”引用，需要把你的变量和函数放在一个“命名空间”中
//我们这里用一个function做命名空间而不是一个var，因为在前者中声明function更简单，而且能保护隐私数据
myNameSpace = function() {
    var current = null;

    function init() {
        //...
    }

    function change() {
        //...
    }

    function verify() {
        //...
    }

//所有需要在命名空间外调用的函数和属性都要写在return里面
    return {
        init: init,
        //甚至你可以为函数和属性命名一个别名
        set: change
    };
};&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;尊重对象的所有权&lt;/h2&gt;
 &lt;p&gt;因为JavaScript可以在任何时候修改任意对象，这样就可以以不可预计的方式覆写默认的行为，所以如果你不负责维护某个对象，它的对象或者它的方法，那么你就不要对它进行修改，具体一点就是说：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;不要为实例或原型添加属性&lt;/li&gt;
  &lt;li&gt;不要为实例或者原型添加方法&lt;/li&gt;
  &lt;li&gt;不要重定义已经存在的方法&lt;/li&gt;
  &lt;li&gt;不要重复定义其它团队成员已经实现的方法，永远不要修改不是由你所有的对象，你可以通过以下方式为对象创建新的功能:&lt;/li&gt;
  &lt;li&gt;创建包含所需功能的新对象，并用它与相关对象进行交互&lt;/li&gt;
  &lt;li&gt;创建自定义类型，继承需要进行修改的类型，然后可以为自定义类型添加额外功能&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;循环引用&lt;/h2&gt;
 &lt;p&gt;如果循环引用中包含DOM对象或者ActiveX对象，那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前，即使是刷新页面，这部分内存不会被浏览器释放。&lt;/p&gt;
 &lt;p&gt;简单的循环引用：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
        var func = function () {
            //…
        }
        el.func = func;
        func.element = el;&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
        }
        init();&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;init在执行的时候，当前上下文我们叫做context。这个时候，context引用了el，el引用了function，function引用了context。这时候形成了一个循环引用。&lt;/p&gt;
 &lt;p&gt;下面2种方法可以解决循环引用：&lt;/p&gt;
 &lt;h2&gt;  &lt;strong&gt;1)  &lt;/strong&gt;  &lt;strong&gt;置空dom对象&lt;/strong&gt;&lt;/h2&gt;
 &lt;div&gt;
  &lt;pre&gt;       function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
        }
        init();
        //可以替换为：
        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
            el = null;
        }
        init();&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;将el置空，context中不包含对dom对象的引用，从而打断循环应用。&lt;/p&gt;
 &lt;p&gt;如果我们需要将dom对象返回，可以用如下方法：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
            return el;
        }
        init();
        //可以替换为：
        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
            try {
                return el;
            } finally {
                el = null;
            }
        }
        init();&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;  &lt;strong&gt;2)  &lt;/strong&gt;  &lt;strong&gt;构造新的context&lt;/strong&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = function () {
                //……
            }
        }
        init();
        //可以替换为：
        function elClickHandler() {
            //……
        }
        function init() {
            var el = document.getElementById(&amp;apos;MyElement&amp;apos;);
            el.onclick = elClickHandler;
        }
        init();&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;把function抽到新的context中，这样，function的context就不包含对el的引用，从而打断循环引用。&lt;/p&gt;
 &lt;h3&gt;通过javascript创建的dom对象，必须append到页面中&lt;/h3&gt;
 &lt;p&gt;IE下，脚本创建的dom对象，如果没有append到页面中，刷新页面，这部分内存是不会回收的！&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function create() {
            var gc = document.getElementById(&amp;apos;GC&amp;apos;);
            for (var i = 0; i &amp;lt; 5000; i++) {
                var el = document.createElement(&amp;apos;div&amp;apos;);
                el.innerHTML = &amp;quot;test&amp;quot;;
                //下面这句可以注释掉，看看浏览器在任务管理器中，点击按钮然后刷新后的内存变化
                gc.appendChild(el);
            }
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;释放dom元素占用的内存&lt;/h2&gt;
 &lt;p&gt;将dom元素的innerHTML设置为空字符串，可以释放其子元素占用的内存。&lt;/p&gt;
 &lt;p&gt;在rich应用中，用户也许会在一个页面上停留很长时间，可以使用该方法释放积累得越来越多的dom元素使用的内存。&lt;/p&gt;
 &lt;h2&gt;释放javascript对象&lt;/h2&gt;
 &lt;p&gt;在rich应用中，随着实例化对象数量的增加，内存消耗会越来越大。所以应当及时释放对对象的引用，让GC能够回收这些内存控件。&lt;/p&gt;
 &lt;p&gt;对象：obj = null&lt;/p&gt;
 &lt;p&gt;对象属性：delete obj.myproperty&lt;/p&gt;
 &lt;p&gt;数组item：使用数组的splice方法释放数组中不用的item&lt;/p&gt;
 &lt;h2&gt;避免string的隐式装箱&lt;/h2&gt;
 &lt;p&gt;对string的方法调用，比如’xxx’.length，浏览器会进行一个隐式的装箱操作，将字符串先转换成一个String对象。推荐对声明有可能使用String实例方法的字符串时，采用如下写法：&lt;/p&gt;
 &lt;p&gt;var myString = new String(‘Hello World’);&lt;/p&gt;
 &lt;h2&gt;松散耦合&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;1、解耦HTML/JavaScript&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;JavaScript和HTML的紧密耦合：直接写在HTML中的JavaScript、使用包含内联代码的&amp;lt;script&amp;gt;元素、使用HTML属性来分配事件处理程序等&lt;/p&gt;
 &lt;p&gt;HTML和JavaScript的紧密耦合：JavaScript中包含HTML，然后使用innerHTML来插入一段html文本到页面&lt;/p&gt;
 &lt;p&gt;其实应该是保持层次的分离，这样可以很容易的确定错误的来源，所以我们应确保HTML呈现应该尽可能与JavaScript保持分离&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2、解耦CSS/JavaScript&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;显示问题的唯一来源应该是CSS，行为问题的唯一来源应该是JavaScript，层次之间保持松散耦合才可以让你的应用程序更加易于维护，所以像以下的代码element.style.color=”red”尽量改为element.className=”edit”，而且不要在css中通过表达式嵌入JavaScript&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3、解耦应用程序/事件处理程序&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;将应用逻辑和事件处理程序相分离：一个事件处理程序应该从事件对象中提取，并将这些信息传送给处理应用逻辑的某个方法中。这样做的好处首先可以让你更容易更改触发特定过程的事件，其次可以在不附加事件的情况下测试代码，使其更易创建单元测试&lt;/p&gt;
 &lt;h2&gt;性能方面的注意事项&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;1、尽量使用原生方法&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2、switch语句相对if较快&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;通过将case语句按照最可能到最不可能的顺序进行组织&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3、位运算较快&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;当进行数字运算时，位运算操作要比任何布尔运算或者算数运算快&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;   &lt;strong&gt;4、    &lt;strong&gt;巧用&lt;/strong&gt;    &lt;strong&gt;||&lt;/strong&gt;    &lt;strong&gt;和&lt;/strong&gt;    &lt;strong&gt;&amp;amp;&amp;amp;&lt;/strong&gt;    &lt;strong&gt;布尔运算符&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function eventHandler(e) {
            if (!e) e = window.event;
        }
        //可以替换为：
        function eventHandler(e) {
            e = e || window.event;
        }&lt;/pre&gt;
&lt;/div&gt;
 &lt;div&gt;
  &lt;pre&gt;        if (myobj) {
            doSomething(myobj);
        }
        //可以替换为：
        myobj &amp;amp;&amp;amp; doSomething(myobj);&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;避免错误应注意的地方&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;1、每条语句末尾须加分号&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在if语句中，即使条件表达式只有一条语句也要用{}把它括起来，以免后续如果添加了语句之后造成逻辑错误&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2、使用+号时需谨慎&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;JavaScript 和其他编程语言不同的是，在 JavaScript 中，’+&amp;apos;除了表示数字值相加，字符串相连接以外，还可以作一元运算符用，把字符串转换为数字。因而如果使用不当，则可能与自增符’++’混淆而引起计算错误&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var valueA = 20;
        var valueB = &amp;quot;10&amp;quot;;
        alert(valueA + valueB);     //ouput: 2010 
        alert(valueA + (+valueB));  //output: 30 
        alert(valueA + +valueB);    //output:30 
        alert(valueA ++ valueB);     //Compile error&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;  &lt;strong&gt;3、使用return语句需要注意&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;一条有返回值的return语句不要用()括号来括住返回值，如果返回表达式，则表达式应与return关键字在同一行，以避免压缩时，压缩工具自动加分号而造成返回与开发人员不一致的结果&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        function F1() {
            var valueA = 1;
            var valueB = 2;
            return valueA + valueB;
        }
        function F2() {
            var valueA = 1;
            var valueB = 2;
            return
            valueA + valueB;
        }
        alert(F1());  //output: 3 
        alert(F2());  //ouput: undefined&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;==和===的区别&lt;/h2&gt;
 &lt;p&gt;避免在if和while语句的条件部分进行赋值，如if (a = b)，应该写成if (a == b)，但是在比较是否相等的情况下，最好使用全等运行符，也就是使用===和!==操作符会相对于==和!=会好点。==和!=操作符会进行类型强制转换&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;        var valueA = &amp;quot;1&amp;quot;;
        var valueB = 1;
        if (valueA == valueB) {
            alert(&amp;quot;Equal&amp;quot;);
        }
        else {
            alert(&amp;quot;Not equal&amp;quot;);
        }
        //output: &amp;quot;Equal&amp;quot;
        if (valueA === valueB) {
            alert(&amp;quot;Equal&amp;quot;);
        }
        else {
            alert(&amp;quot;Not equal&amp;quot;);
        }
        //output: &amp;quot;Not equal&amp;quot;&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;不要使用生偏语法&lt;/h2&gt;
 &lt;p&gt;不要使用生偏语法，写让人迷惑的代码，虽然计算机能够正确识别并运行，但是晦涩难懂的代码不方便以后维护&lt;/p&gt;
 &lt;h2&gt;函数返回统一类型&lt;/h2&gt;
 &lt;p&gt;虽然JavaScript是弱类型的，对于函数来说，前面返回整数型数据，后面返回布尔值在编译和运行都可以正常通过，但为了规范和以后维护时容易理解，应保证函数应返回统一的数据类型&lt;/p&gt;
 &lt;h2&gt;总是检查数据类型&lt;/h2&gt;
 &lt;p&gt;要检查你的方法输入的所有数据，一方面是为了安全性，另一方面也是为了可用性。用户随时随地都会输入错误的数据。这不是因为他们蠢，而是因为他们很忙，并且思考的方式跟你不同。用typeof方法来检测你的function接受的输入是否合法&lt;/p&gt;
 &lt;h2&gt;何时用单引号，何时用双引号&lt;/h2&gt;
 &lt;p&gt;虽然在JavaScript当中，双引号和单引号都可以表示字符串, 为了避免混乱，我们建议在HTML中使用双引号，在JavaScript中使用单引号，但为了兼容各个浏览器，也为了解析时不会出错，定义JSON对象时，最好使用双引号&lt;/p&gt;
 &lt;h2&gt;部署&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;用JSLint运行JavaScript验证器来确保没有语法错误或者是代码没有潜在的问&lt;/li&gt;
  &lt;li&gt;部署之前推荐使用压缩工具将JS文件压缩&lt;/li&gt;
  &lt;li&gt;文件编码统一用UTF-8&lt;/li&gt;
  &lt;li&gt;JavaScript 程序应该尽量放在 .js 的文件中，需要调用的时候在 HTML 中以 &amp;lt;script src=”filename.js”&amp;gt; 的形式包含进来。JavaScript 代码若不是该 HTML 文件所专用的，则应尽量避免在 HTML 文件中直接编写 JavaScript 代码。因为这样会大大增加 HTML 文件的大小，无益于代码的压缩和缓存的使用。另外，&amp;lt;script src=”filename.js”&amp;gt; 标签应尽量放在文件的后面,最好是放在&amp;lt;/body&amp;gt;标签前。这样会降低因加载 JavaScript 代码而影响页面中其它组件的加载时间。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;永远不要忽略代码优化工作，重构是一项从项目开始到结束需要持续的工作，只有不断的优化代码才能让代码的执行效率越来越好&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>WEB开发 编程开发 JavaScript 性能优化</category>
      <guid isPermaLink="true">https://itindex.net/detail/52861-javascript-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-%E7%9F%A5%E8%AF%86</guid>
      <pubDate>Wed, 04 Mar 2015 09:56:23 CST</pubDate>
    </item>
    <item>
      <title>使用shell脚本对Linux系统和进程资源进行监控</title>
      <link>https://itindex.net/detail/52860-shell-%E8%84%9A%E6%9C%AC-linux</link>
      <description>&lt;p&gt;在服务器运维过程中，经常需要对服务器的各种资源进行监控，例如：CPU的负载监控，磁盘的使用率监控，进程数目监控等等，以在系统出现异常时及时报警，通知系统管理员。本文介绍在Linux系统下几种常见的监控需求及其shell脚本的编写。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;文章目录：&lt;/p&gt;
  &lt;p&gt;1.Linux使用 Shell 检查进程是否存在&lt;/p&gt;
  &lt;p&gt;2.Linux使用 Shell检测进程 CPU 利用率&lt;/p&gt;
  &lt;p&gt;3.Linux使用 Shell检测进程内存使用量&lt;/p&gt;
  &lt;p&gt;4.Linux使用 Shell检测进程句柄使用量&lt;/p&gt;
  &lt;p&gt;5.Linux使用 Shell查看某个 TCP 或 UDP 端口是否在监听&lt;/p&gt;
  &lt;p&gt;6.Linux使用 Shell查看某个进程名正在运行的个数&lt;/p&gt;
  &lt;p&gt;7.Linux使用 Shell检测系统 CPU 负载&lt;/p&gt;
  &lt;p&gt;8.Linux使用 Shell检测系统磁盘空间&lt;/p&gt;
  &lt;p&gt;9.总结&lt;/p&gt;&lt;/blockquote&gt;
 &lt;h2&gt;检查进程是否存在&lt;/h2&gt;
 &lt;p&gt;在对进程进行监控时，我们一般需要得到该进程的 ID，进程 ID 是进程的唯一标识，但是有时可能在服务器上不同用户下运行着多个相同进程名的进程，下面的函数 GetPID 给出了获取指定用户下指定进程名的进程 ID 功能（目前只考虑这个用户下启动一个此进程名的进程），它有两个参数为用户名和进程名，它首先使用 ps 查找进程信息，同时通过 grep 过滤出需要的进程，最后通过 sed 和 awk 查找需要进程的 ID 值（此函数可根据实际情况修改，比如需要过滤其它信息等）。&lt;/p&gt;
 &lt;h5&gt;清单 1. 对进程进行监控&lt;/h5&gt;
 &lt;div&gt;
  &lt;pre&gt;function GetPID #User #Name 
 { 
    PsUser=$1 
    PsName=$2 
    pid=`ps -u $PsUser|grep $PsName|grep -v grep|grep -v vi|grep -v dbx\n 
    |grep -v tail|grep -v start|grep -v stop |sed -n 1p |awk &amp;apos;{print $1}&amp;apos;` 
    echo $pid 
 }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;示例演示：&lt;/p&gt;
 &lt;p&gt;1）源程序（例如查找用户为 root，进程名为 CFTestApp 的进程 ID）&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;PID=`GetPID root CFTestApp` 
 
    echo $PID&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;2）结果输出&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;11426 
    [dyu@xilinuxbldsrv shell]$&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;3）结果分析&lt;/p&gt;
 &lt;p&gt;从上面的输出可见：11426 为 root 用户下的 CFTestApp 程序的进程 ID。&lt;/p&gt;
 &lt;p&gt;4）命令介绍&lt;/p&gt;
 &lt;div&gt;1. ps: 查看系统中瞬间进程信息。 参数：-u&amp;lt; 用户识别码 &amp;gt; 列出属于该用户的程序的状况，也可使用用户名称来指定。 -p&amp;lt; 进程识别码 &amp;gt; 指定进程识别码，并列出该进程的状况。 -o 指定输出格式 2. grep: 用于查找文件中符合字符串的当前行。 参数：-v 反向选择，亦即显示出没有 ‘搜寻字符串’ 内容的那一行。 3. sed: 一个非交互性文本编辑器，它编辑文件或标准输入导出的文件，一次只能处理一行内容。 参数：-n 读取下一个输入行，用下一个命令处理新的行而不是用第一个命令。 p 标志 打印匹配行 4. awk：一种编程语言，用于在 linux/unix 下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件，或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能，是 linux/unix 下的一个强大编程工具。它在命令行中使用，但更多是作为脚本来使用。awk 的处理文本和数据的方式：它逐行扫描文件，从第一行到最后一行，寻找匹配的特定模式的行，并在这些行上进行你想要的操作。如果没有指定处理动作，则把匹配的行显示到标准输出 ( 屏幕 )，如果没有指定模式，则所有被操作所指定的行都被处理。 参数：-F fs or –field-separator fs ：指定输入文件折分隔符，fs 是一个字符串或者是一个正则表达式，如 -F:。&lt;/div&gt;
 &lt;p&gt;有时有可能进程没有启动，下面的功能是检查进程 ID 是否存在，如果此进程没有运行输出：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;The process does not exist. 
    # 检查进程是否存在
    if [ &amp;quot;-$PID&amp;quot; == &amp;quot;-&amp;quot; ] 
    then 
    { 
        echo &amp;quot;The process does not exist.&amp;quot;
    } 
    fi&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;检测进程 CPU 利用率&lt;/h2&gt;
 &lt;p&gt;在对应用服务进行维护时，我们经常遇到由于 CPU 过高导致业务阻塞，造成业务中断的情况。CPU 过高可能由于业务量过负荷或者出现死循环等异常情况，通过脚本对业务进程 CPU 进行时时监控，可以在 CPU 利用率异常时及时通知维护人员，便于维护人员及时分析，定位，以及避免业务中断等。下面的函数可获得指定进程 ID 的进程 CPU 利用率。它有一个参数为进程 ID，它首先使用 ps 查找进程信息，同时通过 grep -v 过滤掉 %CPU 行，最后通过 awk 查找 CPU 利用百分比的整数部分（如果系统中有多个 CPU，CPU 利用率可以超过 100%）。&lt;/p&gt;
 &lt;h5&gt;清单 2. 对业务进程 CPU 进行实时监控&lt;/h5&gt;
 &lt;div&gt;
  &lt;pre&gt;function GetCpu 
  { 
   CpuValue=`ps -p $1 -o pcpu |grep -v CPU | awk &amp;apos;{print $1}&amp;apos; | awk -  F. &amp;apos;{print $1}&amp;apos;` 
        echo $CpuValue 
    }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;下面的功能是通过上面的函数 GetCpu 获得此进程的 CPU 利用率，然后通过条件语句判断 CPU 利用率是否超过限制，如果超过 80%（可以根据实际情况进行调整），则输出告警，否则输出正常信息。&lt;/p&gt;
 &lt;h5&gt;清单 3. 判断 CPU 利用率是否超过限制&lt;/h5&gt;
 &lt;div&gt;
  &lt;pre&gt;function CheckCpu 
 { 
    PID=$1 
    cpu=`GetCpu $PID` 
    if [ $cpu -gt 80 ] 
    then 
    { 
 echo “The usage of cpu is larger than 80%”
    } 
    else 
    { 
 echo “The usage of cpu is normal”
    } 
    fi 
 }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;示例演示：&lt;/p&gt;
 &lt;p&gt;1）源程序（假设上面已经查询出 CFTestApp 的进程 ID 为 11426）&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;CheckCpu 11426&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;2）结果输出&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;The usage of cpu is 75 
    The usage of cpu is normal 
    [dyu@xilinuxbldsrv shell]$&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;3）结果分析&lt;/p&gt;
 &lt;p&gt;从上面的输出可见：CFTestApp 程序当前的 CPU 使用为 75%，是正常的，没有超过 80% 的告警限制。&lt;/p&gt;
 &lt;h2&gt;检测进程内存使用量&lt;/h2&gt;
 &lt;p&gt;在对应用服务进行维护时，也经常遇到由于内存使用过大导致进程崩溃，造成业务中断的情况（例如 32 位程序可寻址的最大内存空间为 4G，如果超出将申请内存失败，同时物理内存也是有限的）。内存使用过高可能由于内存泄露，消息堆积等情况，通过脚本对业务进程内存使用量进行时时监控，可以在内存使用量异常时及时发送告警（例如通过短信），便于维护人员及时处理。下面的函数可获得指定进程 ID 的进程内存使用情况。它有一个参数为进程 ID，它首先使用 ps 查找进程信息，同时通过 grep -v 过滤掉 VSZ 行 , 然后通过除 1000 取以兆为单位的内存使用量。&lt;/p&gt;
 &lt;h5&gt;清单 4. 对业务进程内存使用量进行监控&lt;/h5&gt;
 &lt;div&gt;
  &lt;pre&gt;function GetMem 
    { 
        MEMUsage=`ps -o vsz -p $1|grep -v VSZ` 
        (( MEMUsage /= 1000)) 
        echo $MEMUsage 
    }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;下面的功能是通过上面的函数   &lt;code&gt;GetMem&lt;/code&gt;获得此进程的内存使用，然后通过条件语句判断内存使用是否超过限制，如果超过 1.6G（可以根据实际情况进行调整），则输出告警，否则输出正常信息。&lt;/p&gt;
 &lt;h5&gt;清单 5. 判断内存使用是否超过限制&lt;/h5&gt;
 &lt;div&gt;
  &lt;pre&gt;mem=`GetMem $PID`                
 if [ $mem -gt 1600 ] 
 then 
 { 
     echo “The usage of memory is larger than 1.6G”
 } 
 else 
 { 
    echo “The usage of memory is normal”
 } 
 fi&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;示例演示：&lt;/p&gt;
 &lt;p&gt;1）源程序（假设上面已经查询出 CFTestApp 的进程 ID 为 11426）&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;mem=`GetMem 11426` 

    echo &amp;quot;The usage of memory is $mem M&amp;quot;

    if [ $mem -gt 1600 ] 
    then 
    { 
         echo &amp;quot;The usage of memory is larger than 1.6G&amp;quot;
    } 
    else 
    { 
        echo &amp;quot;The usage of memory is normal&amp;quot;
    } 
    fi&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;2）结果输出&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;The usage of memory is 248 M 
    The usage of memory is normal 
    [dyu@xilinuxbldsrv shell]$&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;3）结果分析&lt;/p&gt;
 &lt;p&gt;从上面的输出可见：CFTestApp 程序当前的内存使用为 248M，是正常的，没有超过 1.6G 的告警限制。&lt;/p&gt;
 &lt;h2&gt;检测进程句柄使用量&lt;/h2&gt;
 &lt;p&gt;在对应用服务进行维护时，也经常遇到由于句柄使用 过量导致业务中断的情况。每个平台对进程的句柄使用都是有限的，例如在 Linux 平台，我们可以使用 ulimit – n 命令（open files (-n) 1024）或者对 /etc/security/limits.conf 的内容进行查看，得到进程句柄限制。句柄使用过高可能由于负载过高，句柄泄露等情况，通过脚本对业务进程句柄使用量进行时时监控，可以在异常时及时发送告警（例如通过短信），便于维护人员及时处理。下面的函数可获得指定进程 ID 的进程句柄使用情况。它有一个参数为进程 ID，它首先使用 ls 输出进程句柄信息，然后通过 wc -l 统计输出句柄个数。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;function GetDes 
    { 
        DES=`ls /proc/$1/fd | wc -l` 
        echo $DES 
    }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;下面功能是通过上面的函数   &lt;code&gt;GetDes&lt;/code&gt;获得此进程的句柄使用量，然后通过条件语句判断句柄使用是否超过限制，如果超过 900（可以根据实际情况进行调整）个，则输出告警，否则输出正常信息。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;des=` GetDes $PID` 
 if [ $des -gt 900 ] 
 then 
 { 
     echo “The number of des is larger than 900”
 } 
 else 
 { 
    echo “The number of des is normal”
 } 
 fi&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;示例演示：&lt;/p&gt;
 &lt;p&gt;1）源程序（假设上面查询出 CFTestApp 的进程 ID 为 11426）&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;des=`GetDes 11426` 

    echo &amp;quot;The number of des is $des&amp;quot;

    if [ $des -gt 900 ] 
    then 
    { 
         echo &amp;quot;The number of des is larger than 900&amp;quot;
    } 
    else 
    { 
        echo &amp;quot;The number of des is normal&amp;quot;
    } 
    fi&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;2）结果输出&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;The number of des is 528 
    The number of des is normal 
    [dyu@xilinuxbldsrv shell]$&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;3）结果分析&lt;/p&gt;
 &lt;p&gt;从上面的输出可见：CFTestApp 程序当前的句柄使用为 528 个，是正常的，没有超过 900 个的告警限制。&lt;/p&gt;
 &lt;p&gt;4）命令介绍&lt;/p&gt;
 &lt;div&gt;wc: 统计指定文件中的字节数、字数、行数 , 并将统计结果显示输出。 参数：-l 统计行数。 -c 统计字节数。 -w 统计字数。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h2&gt;查看某个 TCP 或 UDP 端口是否在监听&lt;/h2&gt;
 &lt;p&gt;端口检测是系统资源检测经常遇到的，特别是在网络通讯情况下，端口状态的检测往往是很重要的。有时可能进程，CPU，内存等处于正常状态，但是端口处于异常状态，业务也是没有正常运行。下面函数可判断指定端口是否在监听。它有一个参数为待检测端口，它首先使用 netstat 输出端口占用信息，然后通过 grep, awk,wc 过滤输出监听 TCP 端口的个数，第二条语句为输出 UDP 端口的监听个数，如果 TCP 与 UDP 端口监听都为 0，返回 0，否则返回 1.&lt;/p&gt;
 &lt;h5&gt;清单 6. 端口检测&lt;/h5&gt;
 &lt;div&gt;
  &lt;pre&gt;function Listening 
 { 
    TCPListeningnum=`netstat -an | grep &amp;quot;:$1 &amp;quot; | \n
    awk &amp;apos;$1 == &amp;quot;tcp&amp;quot; &amp;amp;&amp;amp; $NF == &amp;quot;LISTEN&amp;quot; {print $0}&amp;apos; | wc -l` 
    UDPListeningnum=`netstat -an|grep &amp;quot;:$1 &amp;quot; \n
    |awk &amp;apos;$1 == &amp;quot;udp&amp;quot; &amp;amp;&amp;amp; $NF == &amp;quot;0.0.0.0:*&amp;quot; {print $0}&amp;apos; | wc -l` 
    (( Listeningnum = TCPListeningnum + UDPListeningnum )) 
    if [ $Listeningnum == 0 ] 
    then 
    { 
        echo &amp;quot;0&amp;quot;
    } 
    else 
    { 
       echo &amp;quot;1&amp;quot;
    } 
    fi 
 }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;示例演示：&lt;/p&gt;
 &lt;p&gt;1）源程序（例如查询 8080 端口的状态是否在监听）&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;isListen=`Listening 8080` 
    if [ $isListen -eq 1 ] 
    then 
    { 
        echo &amp;quot;The port is listening&amp;quot;
    } 
    else 
    { 
        echo &amp;quot;The port is not listening&amp;quot;
    } 
    fi&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;2）结果输出&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;The port is listening 
    [dyu@xilinuxbldsrv shell]$&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;3）结果分析&lt;/p&gt;
 &lt;p&gt;从上面的输出可见：这个 Linux 服务器的 8080 端口处在监听状态。&lt;/p&gt;
 &lt;p&gt;4）命令介绍&lt;/p&gt;
 &lt;div&gt;netstat: 用于显示与 IP、TCP、UDP 和 ICMP 协议相关的统计数据，一般用于检验本机各端口的网络连接情况。 参数：-a 显示所有连线中的 Socket。 -n 直接使用 IP 地址，而不通过域名服务器。&lt;/div&gt;
 &lt;p&gt;下面的功能也是检测某个 TCP 或者 UDP 端口是否处在正常状态。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;tcp: netstat -an|egrep $1 |awk &amp;apos;$6 == &amp;quot;LISTEN&amp;quot; &amp;amp;&amp;amp; $1 == &amp;quot;tcp&amp;quot; {print $0}&amp;apos;
 udp: netstat -an|egrep $1 |awk &amp;apos;$1 == &amp;quot;udp&amp;quot; &amp;amp;&amp;amp; $5 == &amp;quot;0.0.0.0:*&amp;quot; {print $0}&amp;apos;&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;命令介绍&lt;/p&gt;
 &lt;div&gt;egrep: 在文件内查找指定的字符串。egrep 执行效果如 grep -E，使用的语法及参数可参照 grep 指令，与 grep 不同点在于解读字符串的方法，egrep 是用扩展的正则表达式语法来解读，而 grep 则用基本的正则表达式语法，扩展的正则表达式比基本的正则表达式有更完整的表达规范。&lt;/div&gt;
 &lt;h2&gt;查看某个进程名正在运行的个数&lt;/h2&gt;
 &lt;p&gt;有时我们可能需要得到服务器上某个进程的启动个数，下面的功能是检测某个进程正在运行的个数，例如进程名为   &lt;code&gt;CFTestApp。&lt;/code&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;Runnum=`ps -ef | grep -v vi | grep -v tail | grep &amp;quot;[ /]CFTestApp&amp;quot; | grep -v grep | wc -l&lt;/pre&gt;
&lt;/div&gt;
 &lt;h2&gt;检测系统 CPU 负载&lt;/h2&gt;
 &lt;p&gt;在对服务器进行维护时，有时也遇到由于系统 CPU（利用率）负载 过量导致业务中断的情况。服务器上可能运行多个进程，查看单个进程的 CPU 都是正常的，但是整个系统的 CPU 负载可能是异常的。通过脚本对系统 CPU 负载进行时时监控，可以在异常时及时发送告警，便于维护人员及时处理，预防事故发生。下面的函数可以检测系统 CPU 使用情况 . 使用 vmstat 取 5 次系统 CPU 的 idle 值，取平均值，然后通过与 100 取差得到当前 CPU 的实际占用值。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;function GetSysCPU 
 { 
   CpuIdle=`vmstat 1 5 |sed -n &amp;apos;3,$p&amp;apos; \n
   |awk &amp;apos;{x = x + $15} END {print x/5}&amp;apos; |awk -F. &amp;apos;{print $1}&amp;apos;
   CpuNum=`echo &amp;quot;100-$CpuIdle&amp;quot; | bc` 
   echo $CpuNum 
 }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;示例演示：&lt;/p&gt;
 &lt;p&gt;1）源程序&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;cpu=`GetSysCPU` 

 echo &amp;quot;The system CPU is $cpu&amp;quot;

 if [ $cpu -gt 90 ] 
 then 
 { 
    echo &amp;quot;The usage of system cpu is larger than 90%&amp;quot;
 } 
 else 
 { 
    echo &amp;quot;The usage of system cpu is normal&amp;quot;
 } 
 fi&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;2）结果输出&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;The system CPU is 87 
 The usage of system cpu is normal 
 [dyu@xilinuxbldsrv shell]$&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;3）结果分析&lt;/p&gt;
 &lt;p&gt;从上面的输出可见：当前 Linux 服务器系统 CPU 利用率为 87%，是正常的，没有超过 90% 的告警限制。&lt;/p&gt;
 &lt;p&gt;4）命令介绍&lt;/p&gt;
 &lt;div&gt;vmstat：Virtual Meomory Statistics（虚拟内存统计）的缩写，可对操作系统的虚拟内存、进程、CPU 活动进行监视。  &lt;br /&gt;
参数： -n 表示在周期性循环输出时，输出的头部信息仅显示一次。&lt;/div&gt;
 &lt;h2&gt;检测系统磁盘空间&lt;/h2&gt;
 &lt;p&gt;系统磁盘空间检测是系统资源检测的重要部分，在系统维护维护中，我们经常需要查看服务器磁盘空间使用情况。因为有些业务要时时写话单，日志，或者临时文件等，如果磁盘空间用尽，也可能会导致业务中断，下面的函数可以检测当前系统磁盘空间中某个目录的磁盘空间使用情况 . 输入参数为需要检测的目录名，使用 df 输出系统磁盘空间使用信息，然后通过 grep 和 awk 过滤得到某个目录的磁盘空间使用百分比。&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;function GetDiskSpc 
 { 
    if [ $# -ne 1 ] 
    then 
        return 1 
    fi 

    Folder=&amp;quot;$1$&amp;quot;
    DiskSpace=`df -k |grep $Folder |awk &amp;apos;{print $5}&amp;apos; |awk -F% &amp;apos;{print $1}&amp;apos;
    echo $DiskSpace 
 }&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;示例演示：&lt;/p&gt;
 &lt;p&gt;1）源程序（检测目录为 /boot）&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;Folder=&amp;quot;/boot&amp;quot;

 DiskSpace=`GetDiskSpc $Folder` 

 echo &amp;quot;The system $Folder disk space is $DiskSpace%&amp;quot;

 if [ $DiskSpace -gt 90 ] 
 then 
 { 
    echo &amp;quot;The usage of system disk($Folder) is larger than 90%&amp;quot;
 } 
 else 
 { 
    echo &amp;quot;The usage of system disk($Folder)  is normal&amp;quot;
 } 
 fi&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;2）结果输出&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;The system /boot disk space is 14% 
 The usage of system disk(/boot)  is normal 
 [dyu@xilinuxbldsrv shell]$&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;3）结果分析&lt;/p&gt;
 &lt;p&gt;从上面的输出可见：当前此 Linux 服务器系统上 /boot 目录的磁盘空间已经使用了 14%，是正常的，没有超过使用 90% 的告警限制。&lt;/p&gt;
 &lt;p&gt;4）命令介绍&lt;/p&gt;
 &lt;div&gt;df：检查文件系统的磁盘空间占用情况。可以利用该命令来获取硬盘被占用了多少空间，目前还剩下多少空间等信息。 参数：-k 以 k 字节为单位显示。&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
 &lt;h2&gt;总结&lt;/h2&gt;
 &lt;p&gt;在 Linux 平台下，shell 脚本监控是一个非常简单，方便，有效的对服务器，进程进行监控的方法，对系统开发以及进程维护人员非常有帮助。它不仅可以对上面的信息进行监控，发送告警，同时也可以监控进程的日志等等的信息，希望本文对大家有帮助。&lt;/p&gt;
 &lt;p&gt;来源：  &lt;a href="http://www.ibm.com/developerworks/cn/linux/l-cn-shell-monitoring/index.html" target="_blank"&gt;IBM 于东海、宋波、池辰&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Linux脚本编程 Linux</category>
      <guid isPermaLink="true">https://itindex.net/detail/52860-shell-%E8%84%9A%E6%9C%AC-linux</guid>
      <pubDate>Tue, 03 Mar 2015 13:17:58 CST</pubDate>
    </item>
    <item>
      <title>PHP判断访客是否移动端浏览器访问</title>
      <link>https://itindex.net/detail/52856-php-%E8%AE%BF%E5%AE%A2-%E7%A7%BB%E5%8A%A8</link>
      <description>&lt;p&gt;今天要给大家分享一段PHP代码，该代码的功能是用来判断访客是否移动端浏览器访问，该功能的实现思路是通过HTTP_X_WAP_PROFILE、HTTP_VIA、HTTP_USER_AGENT等信息来判断访客是否通过移动端浏览器访问PHP网站。以下是PHP代码：&lt;/p&gt;
 &lt;pre&gt;/**
 * 是否移动端访问访问
 *
 * @return bool
 */
function isMobile()
{ 
    // 如果有HTTP_X_WAP_PROFILE则一定是移动设备
    if (isset ($_SERVER[&amp;apos;HTTP_X_WAP_PROFILE&amp;apos;]))
    {
        return true;
    } 
    // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
    if (isset ($_SERVER[&amp;apos;HTTP_VIA&amp;apos;]))
    { 
        // 找不到为flase,否则为true
        return stristr($_SERVER[&amp;apos;HTTP_VIA&amp;apos;], &amp;quot;wap&amp;quot;) ? true : false;
    } 
    // 脑残法，判断手机发送的客户端标志,兼容性有待提高
    if (isset ($_SERVER[&amp;apos;HTTP_USER_AGENT&amp;apos;]))
    {
        $clientkeywords = array (&amp;apos;nokia&amp;apos;,
            &amp;apos;sony&amp;apos;,
            &amp;apos;ericsson&amp;apos;,
            &amp;apos;mot&amp;apos;,
            &amp;apos;samsung&amp;apos;,
            &amp;apos;htc&amp;apos;,
            &amp;apos;sgh&amp;apos;,
            &amp;apos;lg&amp;apos;,
            &amp;apos;sharp&amp;apos;,
            &amp;apos;sie-&amp;apos;,
            &amp;apos;philips&amp;apos;,
            &amp;apos;panasonic&amp;apos;,
            &amp;apos;alcatel&amp;apos;,
            &amp;apos;lenovo&amp;apos;,
            &amp;apos;iphone&amp;apos;,
            &amp;apos;ipod&amp;apos;,
            &amp;apos;blackberry&amp;apos;,
            &amp;apos;meizu&amp;apos;,
            &amp;apos;android&amp;apos;,
            &amp;apos;netfront&amp;apos;,
            &amp;apos;symbian&amp;apos;,
            &amp;apos;ucweb&amp;apos;,
            &amp;apos;windowsce&amp;apos;,
            &amp;apos;palm&amp;apos;,
            &amp;apos;operamini&amp;apos;,
            &amp;apos;operamobi&amp;apos;,
            &amp;apos;openwave&amp;apos;,
            &amp;apos;nexusone&amp;apos;,
            &amp;apos;cldc&amp;apos;,
            &amp;apos;midp&amp;apos;,
            &amp;apos;wap&amp;apos;,
            &amp;apos;mobile&amp;apos;
            ); 
        // 从HTTP_USER_AGENT中查找手机浏览器的关键字
        if (preg_match(&amp;quot;/(&amp;quot; . implode(&amp;apos;|&amp;apos;, $clientkeywords) . &amp;quot;)/i&amp;quot;, strtolower($_SERVER[&amp;apos;HTTP_USER_AGENT&amp;apos;])))
        {
            return true;
        } 
    } 
    // 协议法，因为有可能不准确，放到最后判断
    if (isset ($_SERVER[&amp;apos;HTTP_ACCEPT&amp;apos;]))
    { 
        // 如果只支持wml并且不支持html那一定是移动设备
        // 如果支持wml和html但是wml在html之前则是移动设备
        if ((strpos($_SERVER[&amp;apos;HTTP_ACCEPT&amp;apos;], &amp;apos;vnd.wap.wml&amp;apos;) !== false) &amp;amp;&amp;amp; (strpos($_SERVER[&amp;apos;HTTP_ACCEPT&amp;apos;], &amp;apos;text/html&amp;apos;) === false || (strpos($_SERVER[&amp;apos;HTTP_ACCEPT&amp;apos;], &amp;apos;vnd.wap.wml&amp;apos;) &amp;lt; strpos($_SERVER[&amp;apos;HTTP_ACCEPT&amp;apos;], &amp;apos;text/html&amp;apos;))))
        {
            return true;
        } 
    } 
    return false;
}&lt;/pre&gt;
 &lt;p&gt;代码比较完整，有兴趣的同学可以多做一些测试，有任何bug可以在评论中留言。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>WEB开发 收集转载 编程开发 PHP 浏览器</category>
      <guid isPermaLink="true">https://itindex.net/detail/52856-php-%E8%AE%BF%E5%AE%A2-%E7%A7%BB%E5%8A%A8</guid>
      <pubDate>Tue, 03 Mar 2015 18:52:08 CST</pubDate>
    </item>
    <item>
      <title>jQuery对象和DOM对象之间的转换实现</title>
      <link>https://itindex.net/detail/52844-jquery-%E5%AF%B9%E8%B1%A1-dom</link>
      <description>&lt;p&gt;本文主要向大家介绍了jQuery对象和DOM对象之间互相转换的方法，其实转换过程十分简单，一起来看看吧。&lt;/p&gt;
 &lt;p&gt;在讨论jQuery对象和DOM对象的相互交换之前，先约定好定义变量的风格。如果获取的对象是jQuery对象，那么在变量前加上$，例如：&lt;/p&gt;
 &lt;pre&gt;var $variable = jQuery对象;&lt;/pre&gt;
 &lt;p&gt;如果获取的是DOM对象，则定义如下：&lt;/p&gt;
 &lt;pre&gt;var variable = DOM对象；&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="205" src="http://static.codeceo.com/images/2014/09/jquery-ui.jpg" title="jquery-ui" width="398"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;1.jQuery对象转成DOM对象&lt;/h2&gt;
 &lt;p&gt;jQuery对象不能使用DOM中的方法，但是如果对jQuery对象所提供的方法不熟悉，或者jQuery没有封装想要的方法，不得不实用DOM对象的时候，有以下两种处理方法。jQuery提供了两种方法将一个jQuery对象转换成DOM对象，即[index]和get(index).&lt;/p&gt;
 &lt;p&gt;(1)jQuery对象是一个数组对象，可以通过[index]的方法得到相应的DOM对象。&lt;/p&gt;
 &lt;p&gt;jQuery代码如下：&lt;/p&gt;
 &lt;pre&gt;var $cr = $(&amp;quot;#cr&amp;quot;); //jQuery对象
var cr = $cr[0] //DOM对象
alert(cr.checked) //检测这个checkbox是否选中了&lt;/pre&gt;
 &lt;p&gt;(2)另一种方法是jQuery本身提供的，通过get(index)方法得到相应的DOM对象。&lt;/p&gt;
 &lt;p&gt;jQuery代码如下：&lt;/p&gt;
 &lt;pre&gt;var $cr = $(&amp;quot;#cr&amp;quot;);
var cr = $cr.get(0);
alert(cr.checked)&lt;/pre&gt;
 &lt;h2&gt;2.DOM对象转换成jQuery对象&lt;/h2&gt;
 &lt;p&gt;对于一个DOM对象，只需要用$()把DOM对象包装起来，就可以获得一个jQuery对象了，方式为$(DOM对象)。&lt;/p&gt;
 &lt;p&gt;jQuery代码如下：&lt;/p&gt;
 &lt;pre&gt;var cr = document.getElementByID(&amp;quot;cr&amp;quot;); //DOM对象
var $cr = $(cr);&lt;/pre&gt;
 &lt;p&gt;转换后，可以任意使用jQuery中的方法。&lt;/p&gt;
 &lt;p&gt;通过以上方法，可以任意地相互转换jQuery对象和DOM对象。&lt;/p&gt;
 &lt;p&gt;最后强调，DOM对象才能使用DOM中的方法，jQuery对象不可以使用DOM中的方法，但jQuery对象提供了一套更加完善的工具用于操作DOM。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>WEB开发 编程开发 DOM jQuery</category>
      <guid isPermaLink="true">https://itindex.net/detail/52844-jquery-%E5%AF%B9%E8%B1%A1-dom</guid>
      <pubDate>Sun, 01 Mar 2015 21:46:29 CST</pubDate>
    </item>
    <item>
      <title>HHVM 是如何提升 PHP 性能的？</title>
      <link>https://itindex.net/detail/52808-hhvm-%E6%8F%90%E5%8D%87-php</link>
      <description>&lt;h2&gt;背景&lt;/h2&gt;
 &lt;p&gt;HHVM 是 Facebook 开发的高性能 PHP 虚拟机，宣称比官方的快9倍，我很好奇，于是抽空简单了解了一下，并整理出这篇文章，希望能回答清楚两方面的问题：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;HHVM 到底靠谱么？是否可以用到产品中？&lt;/li&gt;
  &lt;li&gt;它为什么比官方的 PHP 快很多？到底是如何优化的？&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;你会怎么做？&lt;/h2&gt;
 &lt;p&gt;在讨论 HHVM 实现原理前，我们先设身处地想想：假设你有个 PHP 写的网站遇到了性能问题，经分析后发现很大一部分资源就耗在 PHP 上，这时你会怎么优化 PHP 性能？&lt;/p&gt;
 &lt;p&gt;比如可以有以下几种方式：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;方案1，迁移到性能更好的语言上，如 Java、C++、Go。&lt;/li&gt;
  &lt;li&gt;方案2，通过 RPC 将功能分离出来用其它语言实现，让 PHP 做更少的事情，比如 Twitter 就将大量业务逻辑放到了 Scala 中，前端的 Rails 只负责展现。&lt;/li&gt;
  &lt;li&gt;方案3，写 PHP 扩展，在性能瓶颈地方换 C/C++。&lt;/li&gt;
  &lt;li&gt;方案4，优化 PHP 的性能。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;方案1几乎不可行，十年前 Joel 就  &lt;a href="http://www.joelonsoftware.com/articles/fog0000000069.html"&gt;拿 Netscape 的例子警告过&lt;/a&gt;，你将放弃多年的经验积累。尤其是像 Facebook 这种业务逻辑复杂的产品，PHP 代码实在太多了，据称有2千万行（引用自 [PHP on the Metal with HHVM]），修改起来的成本恐怕比写个虚拟机还大，而且对于一个上千人的团队，从头开始学习也是不可接受的。&lt;/p&gt;
 &lt;p&gt;方案2是最保险的方案，可以逐步迁移，事实上 Facebook 也在朝这方面努力了，而且还开发了 Thrift 这样的 RPC 解决方案。Facebook 内部主要使用的另一个语言是 C++，从早期的 Thrift 代码就能看出来，因为其它语言的实现都很简陋，没法在生产环境下使用。&lt;/p&gt;
 &lt;p&gt;目前在 Facebook 中据称 PHP:C++ 已经从 9:1   &lt;a href="http://zh.reddit.com/r/IAmA/comments/1nl9at/i_am_a_member_of_facebooks_hhvm_team_a_c_and_d/ccjlvoq"&gt;增加到 7:3 了&lt;/a&gt;，加上有 Andrei Alexandrescu 的存在，C++ 在 Facebook 中越来越流行。但这只能解决部分问题，毕竟 C++ 开发成本比 PHP 高得多，不适合用在经常修改的地方，而且太多 RPC 的调用也会严重影响性能。&lt;/p&gt;
 &lt;p&gt;方案3看起来美好，实际执行起来却很难，一般来说性能瓶颈并不会很显著，大多是不断累加的结果，加上 PHP 扩展开发成本高，这种方案一般只用在公共且变化不大的基础库上，所以这种方案解决不了多少问题。&lt;/p&gt;
 &lt;p&gt;可以看到，前面3个方案并不能很好地解决问题，所以 Facebook 其实没有选择的余地，只能去考虑 PHP 本身的优化了。&lt;/p&gt;
 &lt;h2&gt;更快的 PHP&lt;/h2&gt;
 &lt;p&gt;既然要优化 PHP，那如何去优化呢？在我看来可以有以下几种方法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;方案1，PHP 语言层面的优化。&lt;/li&gt;
  &lt;li&gt;方案2，优化 PHP 的官方实现（也就是 Zend）。&lt;/li&gt;
  &lt;li&gt;方案3，将 PHP 编译成其它语言的 bytecode（字节码），借助其它语言的虚拟机（如 JVM）来运行。&lt;/li&gt;
  &lt;li&gt;方案4，将 PHP 转成 C/C++，然后编译成本地代码。&lt;/li&gt;
  &lt;li&gt;方案5，开发更快的 PHP 虚拟机。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;PHP 语言层面的优化是最简单可行的，Facebook 当然想到了，而且还开发了   &lt;a href="http://pecl.php.net/package/xhprof"&gt;XHProf&lt;/a&gt; 这样的性能分析工具，对于定位性能瓶颈是很有帮助的。&lt;/p&gt;
 &lt;p&gt;不过 XHProf 还是没能很好解决 Facebook 的问题，所以我们继续看，接下来是方案2。简单来看，Zend 的执行过程可以分为两部分：将 PHP 编译为 opcode、执行 opcode，所以优化 Zend 可以从这两方面来考虑。&lt;/p&gt;
 &lt;p&gt;优化 opcode 是一种常见的做法，可以避免重复解析 PHP，而且还能做一些静态的编译优化，比如   &lt;a href="https://github.com/zendtech/ZendOptimizerPlus"&gt;Zend Optimizer Plus&lt;/a&gt;，但由于 PHP 语言的动态性，这种优化方法是有局限性的，乐观估计也只能提升20%的性能。另一种考虑是优化 opcode 架构本身，如基于寄存器的方式，但这种做法修改起来工作量太大，性能提升也不会特别明显（可能30%？），所以投入产出比不高。&lt;/p&gt;
 &lt;p&gt;另一个方法是优化 opcode 的执行，首先简单提一下 Zend 是如何执行的，Zend 的 interpreter（也叫解释器）在读到 opcode 后，会根据不同的 opcode 调用不同函数（其实有些是 switch，不过为了描述方便我简化了），然后在这个函数中执行各种语言相关的操作（感兴趣的话可看看  &lt;a href="http://www.php-internals.com/book/?p=chapt02/02-03-02-opcode"&gt;深入理解 PHP 内核&lt;/a&gt;这本书），所以 Zend 中并没有什么复杂封装和间接调用，作为一个解释器来说已经做得很好了。&lt;/p&gt;
 &lt;p&gt;想要提升 Zend 的执行性能，就需要对程序的底层执行有所解，比如函数调用其实是有开销的，所以能通过   &lt;a href="http://dl.acm.org/citation.cfm?id=277743"&gt;Inline threading&lt;/a&gt; 来优化掉。它的原理就像 C 语言中的 inline 关键字那样，但它是在运行时将相关的函数展开，然后依次执行（只是打个比方，实际实现不太一样），同时还避免了 CPU 流水线预测失败导致的浪费。&lt;/p&gt;
 &lt;p&gt;另外还可以像   &lt;a href="http://trac.webkit.org/browser/trunk/Source/JavaScriptCore/llint/LowLevelInterpreter.asm"&gt;JavaScriptCore&lt;/a&gt; 和   &lt;a href="http://repo.or.cz/w/luajit-2.0.git/blob_plain/HEAD:/src/vm_x86.dasc"&gt;LuaJIT&lt;/a&gt; 那样使用汇编来实现 interpreter，具体细节建议看看   &lt;a href="http://www.reddit.com/r/programming/comments/badl2/luajit_2_beta_3_is_out_support_both_x32_x64/c0lrus0"&gt;Mike 的解释&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;但这两种做法修改代价太大，甚至比重写一个还难，尤其是要保证向下兼容，后面提到 PHP 的特点时你就知道了。&lt;/p&gt;
 &lt;p&gt;开发一个高性能的虚拟机不是件简单的事情，JVM 花了10多年才达到现在的性能，那是否能直接利用这些高性能的虚拟机来优化 PHP 的性能呢？这就是方案3的思路。&lt;/p&gt;
 &lt;p&gt;其实这种方案早就有人尝试过了，比如   &lt;a href="http://quercus.caucho.com/"&gt;Quercus&lt;/a&gt; 和 IBM 的 P8，Quercus 几乎没见有人使用，而 P8   &lt;a href="https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014910522&amp;ps=25"&gt;也已经死掉了&lt;/a&gt;。Facebook 也曾经调研过这种方式，甚至还出现过不靠谱的  &lt;a href="http://nerds-central.blogspot.ie/2012/08/facebook-moving-to-jvm.html"&gt;传闻&lt;/a&gt; ，但其实 Facebook 在 2011 年就放弃了。&lt;/p&gt;
 &lt;p&gt;因为方案3看起来美好，但实际效果却不理想，按照很多大牛的说法（比如   &lt;a href="http://lambda-the-ultimate.org/node/3851#comment-57805"&gt;Mike&lt;/a&gt;），VM 总是为某个语言优化的，其它语言在上面实现会遇到很多瓶颈，比如动态的方法调用。关于这点在   &lt;a href="https://www.dartlang.org/articles/why-not-bytecode/"&gt;Dart 的文档中有过介绍&lt;/a&gt;，而且据说 Quercus 的性能与 Zend+APC 比差不了太多（[来自The HipHop Compiler for PHP]），所以没太大意义。&lt;/p&gt;
 &lt;p&gt;不过 OpenJDK 这几年也在努力，最近的   &lt;a href="http://openjdk.java.net/projects/graal/"&gt;Grall&lt;/a&gt; 项目看起来还不错，也有语言在上面取得了  &lt;a href="http://mail.openjdk.java.net/pipermail/graal-dev/2013-December/001250.html"&gt;显著的效果&lt;/a&gt;，但我还没空研究 Grall，所以这里无法判断。&lt;/p&gt;
 &lt;p&gt;接下来是方案4，它正是 HPHPc（HHVM 的前身）的做法，原理是将 PHP 代码转成 C++，然后编译为本地文件，可以认为是一种 AOT（ahead of time）的方式，关于其中代码转换的技术细节可以参考   &lt;a href="http://dl.acm.org/citation.cfm?id=2384658"&gt;The HipHop Compiler for PHP&lt;/a&gt; 这篇论文，以下是该论文中的一个截图，可以通过它来大概了解：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="172" src="http://static.codeceo.com/images/2015/02/6325de4f733e15545a5a49752ea184f3.png" width="630"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这种做法的最大优点是实现简单（相对于一个 VM 来说），而且能做很多编译优化（因为是离线的，慢点也没事），比如上面的例子就将“  &lt;code&gt;- 1”&lt;/code&gt;优化掉了。但它很难支持 PHP 中的很多动态的方法，如   &lt;code&gt;eval()&lt;/code&gt;、  &lt;code&gt;create_function()&lt;/code&gt;，因为这就得再内嵌一个 interpreter，成本不小，所以 HPHPc 干脆就直接不支持这些语法。&lt;/p&gt;
 &lt;p&gt;除了 HPHPc，还有两个类似的项目，一个是   &lt;a href="http://www.roadsend.com/"&gt;Roadsend&lt;/a&gt;，另一个是   &lt;a href="http://phpcompiler.org/"&gt;phc&lt;/a&gt; ，phc 的做法是将 PHP 转成了 C 再编译，以下是它将   &lt;code&gt;file_get_contents($f)&lt;/code&gt; 转成 C 代码的例子：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;static php_fcall_info fgc_info;
php_fcall_info_init (&amp;quot;file_get_contents&amp;quot;, &amp;amp;fgc_info);
php_hash_find (LOCAL_ST, &amp;quot;f&amp;quot;, 5863275, &amp;amp;fgc_info.params);
php_call_function (&amp;amp;fgc_info);&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;话说   &lt;a href="http://blog.paulbiggar.com/archive/a-rant-about-php-compilers-in-general-and-hiphop-in-particular/#bottom"&gt;phc 作者曾经在博客上哭诉&lt;/a&gt;，说他两年前就去 Facebook 演示过 phc 了，还和那里的工程师交流过，结果人家一发布就火了，而自己忙活了4年却默默无闻，现在前途渺茫。。。&lt;/p&gt;
 &lt;p&gt;Roadsend 也已经不维护了，对于 PHP 这样的动态语言来说，这种做法有很多的局限性，由于无法动态 include，Facebook 将所有文件都编译到了一起，上线时的文件部署居然达到了 1G，越来越不可接受了。&lt;/p&gt;
 &lt;p&gt;另外有还有一个叫   &lt;a href="https://github.com/chung-leong/qb"&gt;PHP QB&lt;/a&gt; 的项目，由于时间关系我没有看，感觉可能是类似的东东。&lt;/p&gt;
 &lt;p&gt;所以就只剩下一条路了，那就是写一个更快的 PHP 虚拟机，将一条黑路走到底。或许你和我一样，一开始听到 Facebook 要做一个虚拟机是觉得太离谱，但如果仔细分析就会发现其实也只有这样了。&lt;/p&gt;
 &lt;h2&gt;更快的虚拟机&lt;/h2&gt;
 &lt;p&gt;HHVM 为什么更快？在各种新闻报道中都提到了 JIT 这个关键技术，但其实远没有那么简单，JIT 不是什么神奇的魔法棒——用它轻轻一挥就能提升性能，而且 JIT 这个操作本身也是会耗时的，对于简单的程序没准还比 interpreter 慢，最极端的例子是   &lt;a href="http://lua-users.org/lists/lua-l/2010-03/msg00305.html"&gt;LuaJIT 2&lt;/a&gt; 的 Interpreter 就稍微比 V8 的 JIT 快。所以并不存在绝对的事情，更多还是在细节问题的处理上，HHVM 的发展历史就是不断优化的历史，你可以从下图看到它是如何一点点超过 HPHPc 的：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/02/47826f5af95a209680a600f14eaa7d74.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;值得一提的是在 Android 4.4 中新的虚拟机 ART 就采用的是 AOT 方案（还记得么？前面提到的 HPHPc 就是这种），结果比之前使用 JIT 的 Dalvik 快了一倍，所以说 JIT 也不一定比 AOT 快。&lt;/p&gt;
 &lt;p&gt;因此这个项目是有很大风险的，如果没有强大的内心和毅力，极有可能半途而废。  &lt;a href="https://code.google.com/p/unladen-swallow/"&gt;Google 就曾经想用 JIT 提升 Python 的性能&lt;/a&gt;，但  &lt;a href="http://qinsb.blogspot.jp/2011/03/unladen-swallow-retrospective.html"&gt;最终失败了&lt;/a&gt;，对于 Google 来说用到 Python 的地方其实并没什么性能问题（好吧，以前 Google 是用 Python 写过 crawl [参考 In The Plex]，但那都是1996年的事情了）。&lt;/p&gt;
 &lt;p&gt;比起 Google，Facebook 显然有更大的动力和决心，PHP 是 Facebook 最重要的语言，我们来看看 Facebook 都投入了哪些大牛到这个项目中（不全）：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Andrei Alexandrescu，『Modern C++ Design』和『C++ Coding Standards』的作者，C++ 领域无可争议的大神&lt;/li&gt;
  &lt;li&gt;Keith Adams，负责过 VMware 核心架构，当年 VMware 就派他一人去和 Intel 进行技术合作，足以证明在    &lt;a href="http://en.wikipedia.org/wiki/Virtual_Machine_Manager"&gt;VMM&lt;/a&gt; 领域他有多了解了&lt;/li&gt;
  &lt;li&gt;Drew Paroski，在微软参与过 .NET 虚拟机开发，改进了其中的 JIT。&lt;/li&gt;
  &lt;li&gt;Jason Evans，开发了 jemalloc，减少了 Firefox 一半的内存消耗。&lt;/li&gt;
  &lt;li&gt;Sara Golemon，『Extending and Embedding PHP』的作者，PHP 内核专家，这本书估计所有 PHP 高手都看过吧，或许你不知道其实她是女的&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;虽然没有像 Lars Bak、Mike Pall 这样在虚拟机领域的顶级专家，但如果这些大牛能齐心协力，写个虚拟机还是问题不大的，那么他们将面临什么样的挑战呢？接下来我们一一讨论。&lt;/p&gt;
 &lt;h3&gt;规范是什么？&lt;/h3&gt;
 &lt;p&gt;自己写 PHP 虚拟机要面临的第一个问题就是 PHP 没有语言规范，很多版本间的语法还会不兼容（甚至是小版本号，比如 5.2.1 和 5.2.3），PHP 语言规范究竟如何定义呢？来看一篇来自   &lt;a href="http://grouper.ieee.org/groups/plv/DocLog/000-099/060-thru-079/22-OWGV-N-0060/n0060.pdf"&gt;IEEE&lt;/a&gt; 的说法：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;The PHP group claim that they have the ﬁnal say in the speciﬁcation of (the language) PHP. This groups speciﬁcation is an implementation, and there is no prose speciﬁcation or agreed validation suite.&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;所以唯一的途径就是老老实实去看 Zend 的实现，好在 HPHPc 中已经痛苦过一次了，所以 HHVM 能直接利用现成，因此这个问题并不算太大。&lt;/p&gt;
 &lt;h3&gt;语言还是扩展？&lt;/h3&gt;
 &lt;p&gt;实现 PHP 语言不仅仅只是实现一个虚拟机那么简单，PHP 语言本身还包括了各种扩展，这些扩展和语言是一体的，Zend 不辞辛劳地实现了各种你可能会用到的功能。如果分析过 PHP 的代码，就会发现它的 C 代码除去空行注释后居然还有80+万行，而你猜其中 Zend 引擎部分有多少？只有不到10万行。&lt;/p&gt;
 &lt;p&gt;对于开发者来说这不是什么坏事，但对于引擎实现者来说就很悲剧了。我们可以拿 Java 来进行对比，写个 Java 的虚拟机只需实现字节码解释及一些基础的 JNI 调用，Java 绝大部分内置库都是用 Java 实现的。所以如果不考虑性能优化，单从工作量看，实现 PHP 虚拟机比 JVM 要难得多，比如就有人用8千行的 TypeScript 实现了一个   &lt;a href="https://github.com/int3/doppio"&gt;JVM Doppio&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;而对于这个问题，HHVM 的解决办法很简单，那就是只实现 Facebook 中用到的，而且同样可以先用 HPHPc 中之前写过的，所以问题也不大。&lt;/p&gt;
 &lt;h3&gt;实现 Interpreter&lt;/h3&gt;
 &lt;p&gt;接下来是 Interpreter 的实现，在解析完 PHP 后会生成 HHVM 自己设计的一种 Bytecode，存储在  &lt;code&gt;~/.hhvm.hhbc&lt;/code&gt;（SQLite 文件） 中以便重用，在执行 Bytecode 时和 Zend 类似，也是将不同的字节码放到不同的函数中去实现（这种方式在虚拟机中有个专门的称呼：  &lt;a href="http://en.wikipedia.org/wiki/Threaded_code#Subroutine_threading"&gt;Subroutine threading&lt;/a&gt;）&lt;/p&gt;
 &lt;p&gt;Interpreter 的主体实现在   &lt;a href="https://github.com/facebook/hhvm/blob/master/hphp/runtime/vm/bytecode.cpp"&gt;bytecode.cpp&lt;/a&gt; 中，比如   &lt;code&gt;VMExecutionContext::iopAdd&lt;/code&gt; 这样的方法，最终执行会根据不同类型来区分，比如 add 操作的实现是在   &lt;a href="https://github.com/facebook/hhvm/blob/master/hphp/runtime/base/tv-arith.cpp"&gt;tv-arith.cpp&lt;/a&gt; 中，下面摘抄其中的一小段：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;if (c2.m_type == KindOfInt64)  return o(c1.m_data.num, c2.m_data.num);
if (c2.m_type == KindOfDouble) return o(c1.m_data.num, c2.m_data.dbl);&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;正是因为有了 Interpreter，HHVM 在对于 PHP 语法的支持上比 HPHPc 有明显改进，理论上做到完全兼容官方 PHP。但仅这么做在性能并不会比 Zend 好多少，由于无法确定变量类型，所以需要加上类似上面的条件判断语句，但这样的代码不利于现代 CPU 的执行优化。另一个问题是数据都是 boxed 的，每次读取都需要通过类似   &lt;code&gt;m_data.num&lt;/code&gt; 和  &lt;code&gt;m_data.dbl&lt;/code&gt; 的方法来间接获取。&lt;/p&gt;
 &lt;p&gt;对于这样的问题，就得靠 JIT 来优化了。&lt;/p&gt;
 &lt;h3&gt;实现 JIT 及优化&lt;/h3&gt;
 &lt;p&gt;首先值得一提的是 PHP 的 JIT 之前并非没人尝试过：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;2008 年就有人   &lt;a href="http://llvm.org/devmtg/2008-08/Lopes_PHP-JIT-InTwoDays.pdf"&gt;用 LLVM 实验过&lt;/a&gt;，结果还比原来慢了 21 倍。。。&lt;/li&gt;
  &lt;li&gt;2010 年 IBM 日本研究院基于他们的 JVM 虚拟机代码开发了 P9，性能是官方 PHP 的 2.5 到 9.5 倍，可以看他们的论文   &lt;a href="http://dl.acm.org/citation.cfm?id=1736015"&gt;Evaluation of a just-in-time compiler retrofitted for PHP&lt;/a&gt;。&lt;/li&gt;
  &lt;li&gt;2011 年 Andrei Homescu 基于 RPython 开发过，还写了篇论文    &lt;a href="http://www.ics.uci.edu/~ahomescu/happyjit_paper.pdf"&gt;HappyJIT: a tracing JIT compiler for PHP&lt;/a&gt;，但测试结果有好有坏，并不理想。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;那么究竟什么是 JIT？如何实现一个 JIT？&lt;/p&gt;
 &lt;p&gt;在动态语言中基本上都会有个 eval 方法，可以传给它一段字符串来执行，JIT 做的就是类似的事情，只不过它要拼接不是字符串，而是不同平台下的机器码，然后进行执行，但如何用 C 来实现呢？可以参考 Eli 写的  &lt;a href="http://eli.thegreenplace.net/2013/11/05/how-to-jit-an-introduction/"&gt;这个入门例子&lt;/a&gt;，以下是文中的一段代码：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;unsigned char code[] = {
  0x48, 0x89, 0xf8,                   // mov %rdi, %rax
  0x48, 0x83, 0xc0, 0x04,             // add $4, %rax
  0xc3                                // ret
};
memcpy(m, code, sizeof(code));&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;然而手工编写机器码很容易出错，所以最好的有一个辅助的库，比如的 Mozilla 的   &lt;a href="https://developer.mozilla.org/en-US/docs/Nanojit"&gt;Nanojit&lt;/a&gt; 以及 LuaJIT 的   &lt;a href="http://luajit.org/dynasm.html"&gt;DynASM&lt;/a&gt;，但 HHVM 并没有使用这些，而是自己实现了一个只支持 x64 的（另外还在尝试用   &lt;a href="https://github.com/armvixl/vixl"&gt;VIXL&lt;/a&gt; 来生成 ARM 64 位的），通过 mprotect 的方式来让代码可执行。&lt;/p&gt;
 &lt;p&gt;但为什么 JIT 代码会更快？你可以想想其实用 C++ 编写的代码最终编译出来也是机器码，如果只是将同样的代码手动转成了机器码，那和 GCC 生成出来的有什么区别呢？虽然前面我们提到了一些针对 CPU 实现原理来优化的技巧，但在 JIT 中更重要的优化是根据类型来生成特定的指令，从而大幅减少指令数和条件判断，下面这张来自   &lt;a href="https://hacks.mozilla.org/2009/07/tracemonkey-overview/"&gt;TraceMonkey&lt;/a&gt; 的图对此进行了很直观的对比，后面我们将看到 HHVM 中的具体例子：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://static.codeceo.com/images/2015/02/5ad415949a0e6994b1693b2a796f2964.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;HHVM 首先通过 interpeter 来执行，那它会在什么时候使用 JIT 呢？常见的 JIT 触发条件有 2 种：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;trace：记录循环执行次数，如果超过一定数量就对这段代码进行 JIT。&lt;/li&gt;
  &lt;li&gt;method：记录函数执行次数，如果超过一定数量就对整个函数进行 JIT，甚至直接 inline。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;关于这两种方法哪种更好在 Lambada 上  &lt;a href="http://lambda-the-ultimate.org/node/3851"&gt;有个帖子&lt;/a&gt;引来了各路大神的讨论，尤其是 Mike Pall（LuaJIT 作者） 、Andreas Gal（Mozilla VP） 和 Brendan Eich（Mozilla CTO）都发表了很多自己的观点，推荐大家围观，我这里就不献丑了。&lt;/p&gt;
 &lt;p&gt;它们之间的区别不仅仅是编译范围，还有很多细节问题，比如对局部变量的处理，在这里就不展开了&lt;/p&gt;
 &lt;p&gt;但 HHVM 并没有采用这两种方式，而是自创了一个叫   &lt;a href="https://news.ycombinator.com/item?id=4856099"&gt;tracelet&lt;/a&gt; 的做法，它是根据类型来划分的，看下面这张图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="322" src="http://static.codeceo.com/images/2015/02/59b32b81af50757f7dde5ee16542fe5e.png" width="630"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;可以看到它将一个函数划分为了 3 部分，上面 2 部分是用于处理   &lt;code&gt;$k&lt;/code&gt; 为整数或字符串两种不同情况的，下面的部分是返回值，所以看起来它主要是根据类型的变化情况来划分 JIT 区域的，具体是如何分析和拆解 Tracelet 的细节可以查看  &lt;a href="https://github.com/facebook/hhvm/blob/master/hphp/runtime/vm/jit/translator.cpp"&gt;Translator.cpp&lt;/a&gt; 中的   &lt;code&gt;Translator::analyze&lt;/code&gt; 方法，我还没空看，这里就不讨论了。&lt;/p&gt;
 &lt;p&gt;当然，要实现高性能的 JIT 还需进行各种尝试和优化，比如最初 HHVM 新增的 tracelet 会放到前面，也就是将上图的 A 和 C 调换位置，后来尝试了一下放到后面，结果性能提示了 14%，因为测试发现这样更容易提前命中响应的类型&lt;/p&gt;
 &lt;p&gt;JIT 的执行过程是首先将 HHBC 转成 SSA (hhbc-translator.cpp)，然后对 SSA 上做优化（比如 Copy propagation），再生成本地机器码，比如在 X64 下是由   &lt;a href="https://github.com/facebook/hhvm/blob/master/hphp/runtime/vm/jit/translator-x64.cpp"&gt;translator-x64.cpp&lt;/a&gt; 实现的。&lt;/p&gt;
 &lt;p&gt;我们用一个简单的例子来看看 HHVM 最终生成的机器码是怎样的，比如下面这个 PHP 函数：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;&amp;lt;?php
function a($b){
  echo $b + 2;
}&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;编译后是这个样子：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;mov rcx,0x7200000
mov rdi,rbp
mov rsi,rbx
mov rdx,0x20
call 0x2651dfb &amp;lt;HPHP::Transl::traceCallback(HPHP::ActRec*, HPHP::TypedValue*, long, void*)&amp;gt;
cmp BYTE PTR [rbp-0x8],0xa
jne 0xae00306
; 前面是检查参数是否有效
mov rcx,QWORD PTR [rbp-0x10]           ; 这里将 %rcx 被赋值为1了
mov edi,0x2                            ; 将 %edi（也就是 %rdi 的低32位）赋值为2
add rdi,rcx                            ; 加上 %rcx
call 0x2131f1b &amp;lt;HPHP::print_int(long)&amp;gt; ; 调用 print_int 函数，这时第一个参数 %rdi 的值已经是3了
; 后面暂不讨论
mov BYTE PTR [rbp+0x28],0x8
lea rbx,[rbp+0x20]
test BYTE PTR [r12],0xff
jne 0xae0032a
push QWORD PTR [rbp+0x8]
mov rbp,QWORD PTR [rbp+0x0]
mov rdi,rbp
mov rsi,rbx
mov rdx,QWORD PTR [rsp]
call 0x236b70e &amp;lt;HPHP::JIT::traceRet(HPHP::ActRec*, HPHP::TypedValue*, void*)&amp;gt;
ret&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;而 HPHP::print_int 函数的实现是这样的：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;void print_int(int64_t i) {
  char buf[256];
  snprintf(buf, 256, &amp;quot;%&amp;quot; PRId64, i);
  echo(buf);
  TRACE(1, &amp;quot;t-x64 output(int): %&amp;quot; PRId64 &amp;quot;\n&amp;quot;, i);
}&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;可以看到 HHVM 编译出来的代码直接使用了   &lt;code&gt;int64_t&lt;/code&gt;，避免了 interpreter 中需要判断参数和间接取数据的问题，从而明显提升了性能，最终甚至做到了和 C 编译出来的代码区别不大。&lt;/p&gt;
 &lt;p&gt;需要注意：HHVM 在 server mode 下，只有超过12个请求就才会触发 JIT，启动过 HHVM 时可以通过加上如下参数来让它首次请求就使用 JIT：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;-v Eval.JitWarmupRequests=0&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;所以在测试性能时需要注意，运行一两次就拿来对比是看不出效果的。&lt;/p&gt;
 &lt;h3&gt;类型推导很麻烦，还是逼迫程序员写清楚吧&lt;/h3&gt;
 &lt;p&gt;JIT 的关键是猜测类型，因此某个变量的类型要是老变就很难优化，于是 HHVM 的工程师开始考虑在 PHP 语法上做手脚，加上类型的支持，推出了一个新语言 – Hack（吐槽一下这名字真不利于 SEO），它的样子如下：&lt;/p&gt;
 &lt;div&gt;
  &lt;pre&gt;&amp;lt;?hh
class Point2 {
  public float $x, $y;
  function __construct(float $x, float $y) {
    $this-&amp;gt;x = $x;
    $this-&amp;gt;y = $y;
  }
}
//来自：https://raw.github.com/strangeloop/StrangeLoop2013/master/slides/sessions/Adams-TakingPHPSeriously.pdf&lt;/pre&gt;
&lt;/div&gt;
 &lt;p&gt;注意到   &lt;code&gt;float&lt;/code&gt; 关键字了么？有了静态类型可以让 HHVM 更好地优化性能，但这也意味着和 PHP 语法不兼容，只能使用 HHVM。&lt;/p&gt;
 &lt;p&gt;其实我个人认为这样做最大的优点是让代码更加易懂，减少无意的犯错，就像 Dart 中的可选类型也是这个初衷，同时还方便了 IDE 识别，据说 Facebook 还在开发一个  &lt;a href="https://twitter.com/jpetazzo/status/308294205598474240"&gt;基于 Web 的 IDE&lt;/a&gt;，能协同编辑代码，可以期待一下。&lt;/p&gt;
 &lt;h2&gt;你会使用 HHVM 么？&lt;/h2&gt;
 &lt;p&gt;总的来说，比起之前的 HPHPc，我认为 HHVM 是值得一试的。它是真正的虚拟机，能够更好地支持各种 PHP 的语法，所以改动成本不会更高，而且因为能无缝切换到官方 PHP 版本，所以可以同时启动 FPM 来随时待命，HHVM 还有  &lt;a href="https://github.com/facebook/hhvm/wiki/FastCGI"&gt;FastCGI&lt;/a&gt; 接口方便调用，只要做好应急备案，风险是可控的，从长远来看是很有希望的。&lt;/p&gt;
 &lt;p&gt;性能究竟能提升多少我无法确定，需要拿自己的业务代码来进行真实测试，这样才能真正清楚 HHVM 能带来多少收益，尤其是对整体性能提升到底有多少，只有拿到这个数据才能做决策。&lt;/p&gt;
 &lt;p&gt;最后整理一下可能会遇到的问题，有计划使用的可以参考：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;扩展问题：如果用到了 PHP 扩展，肯定是要重写的，不过 HHVM 扩展写起来比 Zend 要简单的多，具体细节可以看    &lt;a href="https://github.com/facebook/hhvm/wiki/Extension-API"&gt;wiki 上的例子&lt;/a&gt;。&lt;/li&gt;
  &lt;li&gt;HHVM Server 的稳定性问题：这种多线程的架构运行一段时间可能会出现内存泄露问题，或者某个没写好的 PHP 直接导致整个进程挂掉，所以需要注意这方面的测试和容灾措施。&lt;/li&gt;
  &lt;li&gt;问题修复困难：HHVM 在出现问题时将比 Zend 难修复，尤其是 JIT 的代码，只能期望它比较稳定了。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;P.S. 其实我只了解基本的虚拟机知识，也没写过几行 PHP 代码，很多东西都是写这篇文章时临时去找资料的，由于时间仓促水平有限，必然会有不正确的地方，欢迎大家评论赐教   &lt;img alt=":)" src="http://www.codeceo.com/wp-content/themes/d-simple/img/smilies/icon_smile.gif"&gt;&lt;/img&gt; &lt;/p&gt;
 &lt;p&gt;2014年1月补充：目前 HHVM 在鄙厂的推广势头很不错，推荐大家在 2014年 尝试一下，尤其是现在兼容性测试已经达到98.58%了，修改成本进一步减小。&lt;/p&gt;
 &lt;h2&gt;　　引用&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="http://zh.reddit.com/r/IAmA/comments/1nl9at/i_am_a_member_of_facebooks_hhvm_team_a_c_and_d/?limit=500"&gt;Andrei Alexandrescu on AMA&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://news.ycombinator.com/threads?id=kmavm"&gt;Keith Adams 在 HN 上的蛛丝马迹&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.wired.com/wiredenterprise/2013/06/facebook-hhvm-saga/all/"&gt;How Three Guys Rebuilt the Foundation of Facebook&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.infoq.com/presentations/PHP-HHVM-Facebook"&gt;PHP on the Metal with HHVM&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.facebook.com/note.php?note_id=10150336948348920"&gt;Making HPHPi Faster&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.hhvm.com/blog/713/hhvm-optimization-tips"&gt;HHVM Optimization Tips&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.oscon.com/oscon2012/public/schedule/detail/25828"&gt;The HipHop Virtual Machine (hhvm) PHP Execution at the Speed of JIT&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://cufp.org/conference/sessions/2013/julien-verlaguet-facebook-analyzing-php-statically"&gt;Julien Verlaguet, Facebook: Analyzing PHP statically&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.facebook.com/notes/facebook-engineering/speeding-up-php-based-development-with-hiphop-vm/10151170460698920"&gt;Speeding up PHP-based development with HHVM&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.hhvm.com/blog/311/adding-an-opcode-to-hhbc"&gt;Adding an opcode to HHBC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>WEB开发 编程开发 HHVM PHP 高性能</category>
      <guid isPermaLink="true">https://itindex.net/detail/52808-hhvm-%E6%8F%90%E5%8D%87-php</guid>
      <pubDate>Fri, 27 Feb 2015 15:09:06 CST</pubDate>
    </item>
    <item>
      <title>Java 内存分配与垃圾回收机制</title>
      <link>https://itindex.net/detail/54776-java-%E5%86%85%E5%AD%98-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6</link>
      <description>&lt;p&gt;  &lt;img src="http://RincLiu.com/images/20150716_jvm.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;程序计数器&lt;/strong&gt;:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;用于指示当前线程执行的指令行号，字节码解释器通过改变它的值选取下一条待执行的指令；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;分支、循环、跳转、异常处理、线程恢复都需要依赖它；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;它是线程私有的；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;栈&lt;/strong&gt;:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;存储和方法执行相关的信息：栈帧(Stack Frame)；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;栈帧包含: 局部变量表(基本数据类型和引用)、操作栈、动态链接、方法出口等信息；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;每一个方法从被调用到运行结束都对应着栈帧从入栈到出栈的过程；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;根据方法类型，可分为虚拟机栈和本地方法栈；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;它也是线程私有的；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;a&gt;&lt;/a&gt;
 &lt;p&gt;  &lt;strong&gt;堆&lt;/strong&gt;:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;存储对象实例，是 GC 管理的主要区域，&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;根据对象生存周期，它被分为新生代(YoungGen)和老年代(TenuredGen)，而新生代又分为 Eden、FromSurvivor 和 ToSurvivor；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;它也是所有线程共享的；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;方法区&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;存储已被虚拟机加载的类信息、常量、静态变量、即时编译的代码等；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;HotSpot 虚拟机通常将它和”永久代”(PermanentGen)等同，该区域一般不会发生 GC；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;它也是所有线程共享的；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;直接内存&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;它与 NIO 中的 Channel 和 Buffer 有关，采用 native 方法直接分配堆外内存；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;它通过存储在 Java 堆内存中     &lt;code&gt;DirectByteBuffer&lt;/code&gt; 对象中的引用进行 I/O 操作；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;“内存溢出”与”内存泄漏”：&lt;/h3&gt; &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;“内存溢出”是指无法再分配所需的内存；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;“内存泄漏”是指已分配的内存无法释放，多次泄露会导致内存溢出；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;常见的情况是     &lt;code&gt;new&lt;/code&gt; 的对象用完后没有及时     &lt;code&gt;delete&lt;/code&gt;(回收)，造成内存无法释放；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;发生的位置：&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;     &lt;p&gt;堆(      &lt;code&gt;Xmx&lt;/code&gt;/      &lt;code&gt;Xms&lt;/code&gt;)：对象实例未及时释放或生命周期过长；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;栈(      &lt;code&gt;Xss&lt;/code&gt;)：请求的栈深度(方法调用层级或递归)超出允许最大值，或无法分配更多内存；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;常量区(      &lt;code&gt;XX:PermSize&lt;/code&gt;/      &lt;code&gt;XX:MaxPermSize&lt;/code&gt;)：通常是       &lt;code&gt;String&lt;/code&gt; 相关操作导致的，比如       &lt;code&gt;String.intern()&lt;/code&gt;，如果该对象不存在就会添加到常量池；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;直接内存(      &lt;code&gt;XX:MaxDirectMemorySize&lt;/code&gt;)：其大小默认与最大堆内存一样；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;垃圾回收算法：&lt;/h3&gt; &lt;p&gt;  &lt;strong&gt;引用计数&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;标记引用数，检查是否为 0；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;无法解决循环引用问题；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;分代&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;根据对象生存周期将内存分为：新生代(Eden/Survivor)、老年代(TenuredGen)、永久代(PermanentGen)；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;绝大部分对象存活周期很短，所以新生代一般是一个 80% 的 Eden + 两个 10% 的 Survivor；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;新生代一般采用”拷贝”算法；老年代一般采用”标记清理”或”标记整理”算法；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;拷贝&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;将可用内存分为两个部分，每次只使用其中一块，避免产生大量内存碎片；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;如果使用中的那一块内存用完，就将存活的对象拷贝到另一块未使用的内存上，并将使用过的那一块全部清理；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;实际拷贝时是将 Eden 空间和其中一个 Survivor 中存活的对象拷贝到另一个 Survivor 中；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;根搜索&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;从所有 GC Root 对象向下搜索，如果和指定对象之间没有可达的引用路径，则可被回收；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;Root 对象包括：&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;     &lt;p&gt;虚拟机栈中的引用对象；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;本地方法栈中的 JNI 引用对象；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;方法区中的静态引用对象和常量引用对象；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;标记-清理&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;搜索结束后，没有引用链的对象会被标记，一般会经历两次标记；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;如果     &lt;code&gt;finallize()&lt;/code&gt; 方法没有重写或者已经调用过，则会将该对象放到 F-Queue 队列中等待 Finallizer 线程执行清理；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;如果该对象此时要防止被回收，只需要将自己与其他存活对象建立引用关联即可；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;缺点：标记和清理过程效率低，并且会产生大量内存碎片；如果以后程序无法分配到足够的连续内存，会再次触发 GC；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;标记-整理&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;标记后并不是直接清理，而是将所有存活对象都向一端移动，然后清理掉边界以外的内存；&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;垃圾回收器：&lt;/h3&gt; &lt;p&gt;  &lt;strong&gt;Serial&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;回收时需要暂停所有工作线程；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;简单高效(没有线程交互开销)，是 Client 模式下的默认新生代收集器；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;Serial Old&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;它是 Serial 的老年代版本，同样单线程；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;ParNew&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;在 Serial 基础上添加了多线程支持，是第一款并发收集器，也是 Server 模式下的默认新生代收集器；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;除了 Serial，目前只有 ParNew 能和 CMS 配合工作；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;CMS(Concurrent Mark Sweep)&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;它追求的是最短停顿时间，适合频繁与用户交互的场景；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;分为四个步骤：&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;     &lt;p&gt;初始标记：快速标记 Root 对象能直接关联的对象(需要暂停用户线程)；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;并发标记：跟踪查找引用链；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;重新标记：修正上一步并发期间的标记(需要暂停用户线程)；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;并发清理；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;缺点：&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;     &lt;p&gt;占用 CPU 资源，影响吞吐量；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;无法处理并行过程中新产生的垃圾，只能等待下次 GC；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;由于采用“标记-清理”算法，会产生内存碎片；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;Parallel Scavenge&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;它追求的是更大的吞吐量(用户代码运行时间与总时间的比值)，适合后台任务；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;如果粗暴地减小新生代，虽然可以减小停顿时间，但会是 GC 变得频繁，并且牺牲了吞吐量；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;该收集器除了可以设置最大停顿时间和吞吐量，还可以开启自适应策略动态调整参数；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;Parallel Old&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;它是 Parallel Scavenge 的老年代版本；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;G1&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;基于“标记-整理”算法，不会产生内存碎片；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;可以精确控制某个时间段内的最大 GC 停顿时间；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;之前的回收器的收集范围都是整个新生代或老年代，而 G1 将则它们分为多个大小固定的区(Region)，并且跟踪其垃圾堆积程度，每次都优先回收垃圾最多的区域；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;内存分配与 GC 触发策略：&lt;/h3&gt; &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;一般新对象优先分配在新生代的 Eden，如果空间不够则发起一次 Minor GC；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;大对象(很长的字符串或数组)直接分配在老年代，避免 GC 时发生大量内存拷贝；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;更新对象年龄：&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;     &lt;p&gt;虚拟机给每个对象定义了 Age 计数器，Eden 中新创建的对象 Age 为 0；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;Eden 中的对象若在 Minor GC 后存活，则移入 Survivor(如果可以容纳的话)，且 Age++；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;Survivor 中的对象若经历 Minor GC 后仍然存活，则 Age++；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;当 Survivor 中某对象的Age超过阈值(默认 15)时，会被移入老年代；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;空间分配担保：&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;     &lt;p&gt;若 Minor GC后 仍有大量存活对象(Survivor 空间不够)，则需要老年代进行空间分配担保；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;检测之前移入老年代的对象平均大小，如果大于老年代剩余空间，则进行一次 Full GC 让老年代释放部分空间；&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;     &lt;p&gt;如果小于则进一步检测       &lt;code&gt;HandlePromotionFailure&lt;/code&gt; 设置是否允许担保失败，如果不允许则仍会进行 Full GC；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;参考：   &lt;a href="http://book.douban.com/subject/24722612/" rel="external" target="_blank"&gt;《深入理解 Java 虚拟机（第 2 版）：JVM 高级特性与最佳实践》&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>JVM 编程点滴</category>
      <guid isPermaLink="true">https://itindex.net/detail/54776-java-%E5%86%85%E5%AD%98-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6</guid>
      <pubDate>Thu, 16 Jul 2015 12:00:00 CST</pubDate>
    </item>
    <item>
      <title>资深工程师教你如何在股市中用量化交易看见未来</title>
      <link>https://itindex.net/detail/54490-%E5%B7%A5%E7%A8%8B%E5%B8%88-%E8%82%A1%E5%B8%82-%E9%87%8F%E5%8C%96</link>
      <description>&lt;p&gt;  &lt;em&gt;编者按：本文来自点融网旗下微信公众号点融黑帮（微信号：DianrongMafia），授权 36 氪发布。作者程司雷，现任点融贷款业务团队软件工程师。曾在美国国家仪器有限公司、兴业银行总行科技部工作多年。&lt;/em&gt;&lt;/p&gt;
 &lt;p&gt;如何能在风云变幻的金融市场获得稳定的收益，如何在追涨杀跌中克服人性的贪婪和恐惧，一直是无数个人投资者们津津乐道的话题。人工交易无疑能够创造神话，但即便如《股票大作手回忆录》中杰西·利佛莫尔这样的华尔街传奇人物，也在巅峰过后走向没落，最终向自己扣动了扳机。而大部分散户更是难逃八亏一赚一平宿命。&lt;/p&gt;
 &lt;p&gt;那么广大爱好投资的程序猿们，该怎么利用强大的技术实力，通过程序化交易来为自己创收呢？&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1. 量化交易的本质&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;交易就是通过买卖赚取差价来实现盈利。所谓量化交易（程序化交易、算法交易）就是通过某种固定的程式获取一个预期收益的概念。也许很多人都有过一卖就涨，一买就跌的切身经历，或者听信各路专家、神棍的分析，在下跌之初永远满怀” 希望 “，希望马上就要开始反弹，结果越套越深，在稍有盈利时就开始” 恐惧 “，恐惧自己的纸上富贵会化为乌有，结果早早卖出，错过了后面的几倍行情。尽管看了很多交易方面的” 心灵鸡汤 “，但还是无法克服情绪波动对交易带来的影响。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Image title" height="" src="http://www.vaikan.com" width=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;首先交易的目的是为了赚钱，而不是进进出出的乐趣，管住自己的手很重要。那么你能够计算出自己的人工交易所能达到的收益预期吗？不能，因为大部分的人工交易都是情绪化的，没有纪律可言，也就无从谈起收益预期。量化交易因为有固定的交易系统，什么时候执行什么操作指令都有着严格的规定，因此可以把自己的交易算法带到过去数年的历史交易数据中，计算出该交易算法在历史中的盈利水平（为了避免幸存者偏差，应当包含已经退市的股票数据）。&lt;/p&gt;
 &lt;p&gt;这里有一个基本的假设，那就是历史数据能够指导未来。就像经济学假设所有的经济活动参与者都是理性人一样，这是一个用于简化问题的假设。有句名言叫” 华尔街没有新鲜事物 “，该假设的现实依据是人性的不曾改变，一百年前赌徒的恐惧和贪婪，和今天市场里面的恐惧和贪婪，其实并没有什么两样。所以我们有理由相信基于历史交易数据回测出的预期收益率。另外一个假设是在完全的自由市场中，价格的走势已经反映了基本面、政策面等所有信息，我们只需要专注于价格的波动。&lt;/p&gt;
 &lt;p&gt;这个预期收益率有什么意义？它的本质是你的交易系统未来盈利水平的数学期望。数学期望意味着它是一个均值或者说期待值，它不代表你的下一次交易就能获得这个收益，你也可能亏损。数学期望意味随着你交易次数的越来越多，你的收益率就会趋向于该期望值。&lt;/p&gt;
 &lt;p&gt;需要注意的是要让你的交易系统避免” 破产风险 “。” 破产风险 “是一个赌场上的概念，指的是某一把下注太多赔光了所有，导致提前出局。我们要做的是能够在场内活下来继续交易，因为我们相信交易次数越多，收益越趋向于交易系统的期望值。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2. 一个量化交易系统是怎么创建的呢？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;首先来看一下一个交易系统必须要包括哪些基本要素：买入信号、买入仓位、止损价位、加仓价位和数量、卖出价位。这些要素缺一不可，完整的构成了一个交易系统。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Image title" height="" src="http://www.vaikan.com" width=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;买入信号——&lt;/strong&gt;用来告诉你在什么价格买入什么投资标的，常见的买入信号有：20 天均线突破信号、长短期均线交叉信号、通道突破信号、支撑与阻力线突破信号、MACD 金叉等技术突破信号。需要指出的是，不要过于迷信这些信号，它们只是一些概率统计上的指标，并不代表指标出现后市场一定按照指标描述的那样走。如果觉得固守某种价值投资的理念，如低市盈率，或者是信奉某个技术指标的买卖信号就以为能够百战百胜，那岂不是小学生也能赚钱。这些只是让你有了某种概率上的优势而已。这些信号也可以想积木一样组合进你的交易系统。当然还可以加入一些过滤器以过滤一些虚假突破的噪声信号。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;买入仓位——&lt;/strong&gt;指的是你首次建仓的仓位大小，它是资金管理的一部分。你应当仔细思量仓位的大小，避免” 破产风险 “的到来。我们常常听到的一句话 “斩断亏损，让利润飞”，就是指在仓位管理中要尽量做到小亏大赚。虽然我们拥有了概率上的优势，但是我们也无法保证每一次出手都是正确的。这就要我们在建仓时先买入比较小的头寸，一旦发生亏损便认错出局，防止小亏变成大亏。这里的亏损认定就是看是否触及了买入时所设定的止损线。割肉是很困难的事情，意味着对自己的否定，自己打脸总是让人不舒服，但我们要想想自己是来赚钱的，而不是为了证明自己。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;止损线——&lt;/strong&gt;不是一个一刀切的固定位置。简单的设定亏损 10%或者 20%作为止损线，可能会在上升趋势中因为 “震仓” 和 “洗盘” 而失去宝贵的头寸。止损线应当因时因地而异，它要根据投资标的最近的波动率和所持仓位的大小来综合计算。&lt;/p&gt;
 &lt;p&gt;任何一次的买入都是为了卖出，捂住股票上下坐电梯并不是我们想要的。类似买入信号，市场上也有一大堆的卖出指标来告诉你什么时候该卖了。挑选这些信号建立你的交易系统，并用历史数据进行回测，不断的调整参数优化预期收益率。（参数优化时不要对历史数据过度优化，那样会造成曲线的过度拟合，降低对未来数据的适应性）&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;记住：不要计较一城一池的得失，而要从概率的角度思考全局。有道是，不谋万世者，不足谋一时。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3. 交易系统的范例&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;一个特定的交易系统是在确定了上述系统要素后确定下来的。根据市场风格的不同，存在着各种各样的交易策略：趋势跟踪策略（趋势性的单边上涨或下跌的大牛市或大熊市）、套利策略（不同市场间的差价）、波动捕捉策略（震荡市场中的波段操作）等。下面以常见的趋势跟踪策略为例，简述一个交易系统的具体实现：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Image title" height="" src="http://www.vaikan.com" width=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;3.1 系统初始化&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;首次启动系统时需要把沪深两市 2800 多支股票的全量交易数据导入数据库，并计算各支股票的统计数据，包括个周期的均线值、最近 20 天的最高价与最低价、波动率 N 值等。&lt;/p&gt;
 &lt;p&gt;波动率 N 的计算方法：&lt;/p&gt;
 &lt;p&gt;TR（波动幅度）= Max (H-L, H-PDC, PDC-L); 其中 H = 当日最高价， L = 当日最低价， PDC = 前一日收盘价&lt;/p&gt;
 &lt;p&gt;N （平均波动率）= （19 * PDN + TR）/ 20; 其中 PDN = 前一日N 值， TR = 当日波动幅度&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;3.2 筛选突破信号&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;作为趋势跟踪策略，选择股价突破 20 个交易日内的最高价作为突破买入信号。为减少噪声信号，选取一个简单的均线系统作为过滤器：25 天均线大于 300 天均线的市场中只能做多，25 天均线小于 300 天均线的市场中只能做空。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;3.3 确定持仓量和加仓止损线&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;有了买入的标的和买入价格，接下来需要知道的是买多少，获利时怎么加仓，亏损时怎么止损。&lt;/p&gt;
 &lt;p&gt;买入量：为避免破产风险，必须使得买入的仓位不会造成巨大的损失。这里设定 买入量 = 一个头寸单位 = （总资金量 * 5%） / N 值，这保证了在该股票的平均波动水平下只会对你的总仓位最多造成 5%的损失；&lt;/p&gt;
 &lt;p&gt;加仓：价格每上行 1/2N， 加一个头寸单位，直到头寸上限；&lt;/p&gt;
 &lt;p&gt;止损：最新加仓的头寸单位价格 – 2N。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;3.4 退出（卖出价位）&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;主动的退出不同于止损，它是一个获利了结的概念。我们设定在一个持续获利的行情里面，你的仓位也已经达到上限，当股价跌破最近 10日 中的最低价，止盈卖出全部仓位，退出本次交易。&lt;/p&gt;
 &lt;p&gt;趋势跟踪策略意在捕捉大趋势的到来并加足筹码大赚一笔，其缺点是会存在假的突破趋势造成止损，好在止损所带来的亏损被限制在了很小的范围。另外严格的操作纪律和执行力才是交易系统取得超额收益的保证。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4. 相关技术&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;我们可以用自己喜欢的工具来实现你的交易系统，鉴于 Python 能够进行高效快速的开发和数据分析，在对实时性要求不高的数据采集、清洗加工和数据存储方面，Python 有着广泛的应用。&lt;/p&gt;
 &lt;p&gt;在数据获取方面，可以选择使用 TuShare、通联、万得等数据工具下载数据，其中 TuShare 提供 Python 接口获取历史行情数据、当日实时行情数据、基本面政策面数等，并能方便的把处理结果导入到各种数据库中。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5. 结语&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;代码就是生产力，让我们发挥自身的技术优势，告别小米加步枪的落后战力，用数据分析和机器运算来武装自己，残酷的市场中分得一杯羹。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术 股票</category>
      <guid isPermaLink="true">https://itindex.net/detail/54490-%E5%B7%A5%E7%A8%8B%E5%B8%88-%E8%82%A1%E5%B8%82-%E9%87%8F%E5%8C%96</guid>
      <pubDate>Thu, 08 Oct 2015 22:15:32 CST</pubDate>
    </item>
    <item>
      <title>5个Java9新特性你知道了吗？</title>
      <link>https://itindex.net/detail/54489-java9-%E7%9F%A5%E9%81%93</link>
      <description>&lt;p&gt;今天我们已经对Java 9中所期待的特性有了一个很清晰的图景。如果Java 8可以被描述为主要是lambdas表达式、streams和API变化的话，那么Java 9就是关于Jigsaw、额外的实用工具和内部的变化。在这篇文章中，收集了一些我们认为是Java 9中最期待的特性——除了通常的猜测之外，Jigsaw项目，承担了打破JRE并对Java核心组件模块化的使命。&lt;/p&gt;
 &lt;p&gt;这里有一些特性是Java 9中绝对必要了解的，其中的一些已经在早期的发布版本中为你捣鼓做好了准备。&lt;/p&gt;
 &lt;h3&gt;　　  &lt;strong&gt;1.Java + REPL = jshell&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;是的。之前我们怀疑Kulla项目是否会在Java 9中准时发布，但现在已得到了官方确认。下一版发布的Java将会有称为jshell的新命令行工具，它会添加本地支持和以Java方式对REPL（交互式解释器）进行推广。意思是说，如果你想只运行几行Java代码，你不必把它包装进一个单独的工程或者方法。&lt;/p&gt;
 &lt;p&gt;噢，你可以忘掉那些分号了：&lt;/p&gt;
 &lt;pre&gt;      -&amp;gt; 2 + 2
| 表达式的值是4
| 将临时变量$1的类型设为int&lt;/pre&gt;
 &lt;p&gt;还有一些像REPL加载项一样的替代品会增加到流行的IDE和解决方案中，就像Java REPL网页控制台。但目前为止，还没有官方的或者合适的方式来这么做。jshell在早期的版本中已经可以用了，等着你给它来个测试运行。&lt;/p&gt;
 &lt;h3&gt;　  &lt;strong&gt;　2、微基准测试要来了&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;由Alexey Shipilev开发的Java微基准测试套件在其进化的下一阶段，并加入Java作为官方基准解决方案。我们真的很喜欢在Takipi做基准，所以一套标准化的执行方式是我们期待的。&lt;/p&gt;
 &lt;p&gt;JHM是一组用来编译、运行和分析nano/micro/milli/macro基准的套件。当涉及到精确基准评估，对结果产生很大影响的能力将备受关注，比如预热时间和优化。当你以微秒或纳秒计时的情况下尤其如此。所以，如果你想要更加精确的结果来帮助跟踪基准以做出正确的决定，JMH是你的最佳选择——并且现在它已经成为Java 9的同义词了。&lt;/p&gt;
 &lt;h3&gt;  &lt;strong&gt;　　3、G1会成为新的默认垃圾收集器吗？&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;我们经常听说的一个误解是：Java只有一个垃圾收集器，而事实上它有4个。Java 9中，仍有一个运行提议，关于替换由Java 7引入的G1默认垃圾收集器（并行/吞吐量收集）的讨论。不同收集器之间差别精简概述，可以查看这篇里的文章。&lt;/p&gt;
 &lt;p&gt;通常来说，G1被设计来更好地支持大于4GB的堆，并且不会造成频繁的GC暂停，但当暂停发生时，往往会处理更长时间。最近我们和Outbrain的性能专家Haim Yadid讨论了关于GC的方方面面，来帮助你了解更多各收集器之间不同的权衡。同样，如果你想要深入了解相关讨论，那么hotspot-dev和jdk9-dev的邮件组是个开始学习不错的地方。&lt;/p&gt;
 &lt;h3&gt;　　  &lt;strong&gt;4、未来是HTTP 2.0&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;官方的HTTP 2.0标准是几个月之前被批准的，基于Google的SPDY算法构建。SPDY已经展示了相对HTTP 1.1巨大的速度提升，范围在11.81%到47.7%之间，并且它已经存在于大多数现代的浏览器中了。Java 9将全面支持HTTP 2.0，并且为Java配备一个全新的HTTP客户端来替代HttpURLConnection，并且同时还实现HTTP 2.0和websockets。&lt;/p&gt;
 &lt;h3&gt;　　  &lt;strong&gt;5、进程API得到了巨大的推动&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;到目前为止，通过Java来控制和管理操作系统进程能力有限。例如在早期版本的Java中，为了做一些简单的事情，像得到进程PID，要么访问本机代码，要么用某种神奇的临时解决方法。此外，还可能需要一个对于每个平台提供不同实现来保证你得到正确的结果。&lt;/p&gt;
 &lt;p&gt;在Java 9中，除了获取Linux PID的代码，现在都像这样来获取：&lt;/p&gt;
 &lt;pre&gt;    public static void main(String[] args) throws Exception {
    Process proc = Runtime.getRuntime().exec(new String[]{ &amp;quot;/bin/sh&amp;quot;, &amp;quot;-c&amp;quot;, &amp;quot;echo $PPID&amp;quot; });
    if (proc.waitFor() == 0) {
        InputStream in = proc.getInputStream();
        int available = in.available();
        byte[] outputBytes = new byte[available];
        in.read(outputBytes);
        String pid = new String(outputBytes);
        System.out.println(&amp;quot;Your pid is &amp;quot; + pid);
    }
}&lt;/pre&gt;
 &lt;p&gt;转向像这样的代码（同样也支持所有的操作系统）：&lt;/p&gt;
 &lt;pre&gt;    System.out.println(&amp;quot;Your pid is&amp;quot; + Process.getCurrentPid());&lt;/pre&gt;
 &lt;p&gt;这一更新将扩展Java与操作系统交互的能力：全新的直接操作PID、进程名和状态的方法，操作JVM线程和进程等等能力。&lt;/p&gt;
 &lt;h3&gt;　　  &lt;strong&gt;你不会在Java 9中见到什么？&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;我们以为两个有趣的特性会作为即将到来的Java发布版本中的一部分——但现在我们知道它们将不会出现在这次发布的版本。&lt;/p&gt;
 &lt;h3&gt;　　  &lt;strong&gt;1、一个标准的轻量级JSON API&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;在我们进行的一项对350名开发人员的调查中，JSON API就像Jigsaw一样被大肆宣传，但看起来它好像没在发布版本中，原因可能是资金问题。Mark Reinhold，Java平台的首席架构师，在JDK 9的邮件列表中写到：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;“这个JEP对于平台来说是个有益的补充，但长远来看，考虑到资金的因素以及Oracle资助的其它特性，它并不如其它特性一样重要。我们考虑可能在JDK 10或者之后的版本再发布这个JEP。”&lt;/p&gt;&lt;/blockquote&gt;
 &lt;h3&gt;　　  &lt;strong&gt;2、金钱和货币API&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;有一条新闻，似乎看起来金钱和货币API也缺少Oracle的支持。这是我们从Anatole Tresch那里得到的答案，这个API的产品推广师：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;@tkfxin 目前不会。从Oracle那里没得到支持。取而代之的，我们将提高Java EE支持并且spring也将支持它    &lt;img alt=":)" src="http://www.techug.com/wordpress/wp-includes/images/smilies/simple-smile.png?e3f00d"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;– Anatole Tresch (@atsticks) 2015年6月16日&lt;/p&gt;&lt;/blockquote&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程技术 java9 新特征</category>
      <guid isPermaLink="true">https://itindex.net/detail/54489-java9-%E7%9F%A5%E9%81%93</guid>
      <pubDate>Thu, 08 Oct 2015 17:45:48 CST</pubDate>
    </item>
    <item>
      <title>纯HTML5APP与原生APP的差距在哪？</title>
      <link>https://itindex.net/detail/54457-html5app-app</link>
      <description>&lt;p&gt;　　笔者写过一些纯H5的APP，虽然开发起来的确很快很舒服，但和原生比起来纯H5APP还是有很多问题，主要聚集在以下几个方面：&lt;/p&gt; &lt;p&gt;　　  &lt;strong&gt;1、动画&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;　　动画有很多种，比如侧边栏菜单的滑入滑出、元素的响应动画、页面切换之间的过场等等，在H5之下的众多实现方法都没有办法达到纯原生的性能。一般这些的话有几种不同的选择：css3动画、javascript动画、原生动画。&lt;/p&gt; &lt;p&gt;　　css3动画非常的消耗性能，如果某一个元素用到css3动画可能还看不出来，但大面积或过场使用css3动画会让app低端手机体验非常差。最好的选择一般是通过框架调用底层的动画，但不管怎么样等于在原来的代码上包上了一层，性能还是不可避免的受到影响。&lt;/p&gt; &lt;p&gt;　　比如在一个新页面的载入上，如果调用底层动画要考虑的问题有两个，一个是本身资源页面的渲染问题，另一个是远程数据的获取。即便是这些动画能够很快的响应，但大量的css页面会导致渲染卡顿，滑入时可能会有白屏/机器卡顿的现象。为了解决这些性能问题又必须要用到预加载或模拟动画。即便是这样，滑入滑出的动画在低端的安卓机器上还是有很多问题，如果获取服务端数据处理的方式不合适，卡顿白屏的现象会更严重。具体看下面的数据获取方式。&lt;/p&gt; &lt;p&gt;　　  &lt;strong&gt;2、获取服务端数据&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;　　首先要接受的是，这里的数据获取都是在资源页面上异步完成的，因为只有这样才能让这些资源页面完成预加载或者渲染。但是异步拿到的数据在填入页面中时可能会涉及DOM操作，众所周知，DOM操作非常消耗性能，如果页面小还好，页面稍大数据稍微复杂一点，频繁的DOM操作会导致明显的闪白。而且最重要的一点是，如果页面加载进来之后数据更新的速度太慢，也会让页面模板等待很长时间，对用户体验又不友好，总不能每次打开都像浏览器一样等待刷新是吧。&lt;/p&gt; &lt;p&gt;　　这个问题如果没有得到解决，H5APP是很难承担大规模数据的页面，在它们之中频繁切换更是难上加难，那么肯定有人也会想到用MVVM的方式，其实我也写过一些基于MVVM的H5APP，相对来说它们获取数据和更新数据的方式更敏捷更科学，但写的过程中又要注意很多H5独有的问题，这些问题在下面的页面切换里来讲。&lt;/p&gt; &lt;p&gt;　　  &lt;strong&gt;3、页面切换&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;　　上面我们看到了几种不错的实现方式，比如预加载和模拟动画，甚至有批量的预加载，批量的截图模拟动画等等，虽然看起来很友好解决了不少问题，但事实上如果页面足够多就会引发另一个问题——页面的生存周期。&lt;/p&gt; &lt;p&gt;　　试想一下，如果引导页或者主页面缓存了5个子页面的资源，在跳转到响应的子页面时又会缓存这些子页面的下级页面资源，如此反复肯定会占据大量内存使APP的体验下降。那么怎么知道那些页面是需要的，最多缓存多少页面，什么时候结束哪些页面的生存周期呢？在我用过的很多H5APP的框架里都没有对这些问题有一个完美的解答，因此在页面较多内容较多的APP中可能会因这些资源分配的问题降低性能。&lt;/p&gt; &lt;p&gt;　　这时候我们回过头来再看看MVVM的数据加载问题，实际上不管哪个MVVM框架，写过的人都知道管理这种新型的前端代码最重要的问题是内存的问题，你既要保证代码写的足够优雅没有任何内存泄露问题，也要考虑到在页面生存周期结束时它们的控制器/页面资源是否得到释放，这对全局有没有什么影响，在多个请求时也要合理的分配资源，甚至是复用这些父级页面传过来的缓存资源等等。较小的APP可能并不会有这些问题，如果你想用纯H5来开发大型APP，这很可能会浪费你很多时间——而且结果还不会让你满意。&lt;/p&gt; &lt;p&gt;　　  &lt;strong&gt;4、Android/iOS的区别&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;　　很多人都说纯H5APP一次编写就能编译Android/iOS两种不同的APP，大大降低了成本。实际上这个观点本身就是值得怀疑的，如果你写过这类APP就能明白我在说什么，它们既不省事，又存在很多BUG，调试时尤其繁琐。举一个很简单的例子，Android和iOS在返回上一页的处理方式上就有明显的区别，iOS的顶部bar在全屏下怎样处理，Android机器出现smart bar怎样处理页面的布局，调用底层硬件时怎样区分不同的场景等等，你需要写一个又一个机型和系统的判断，然后分别在Android和iOS下调试，最后你却发现这并没有卵用，累的要死却什么没学到，只有一堆不知道什么时候会过时的经验。&lt;/p&gt; &lt;p&gt;　　现在做H5混合APP开发的人很多，但是纯H5却很年轻，很多问题都没有很好的解决，这几个是我在做这些APP时考虑最多的问题。当然大家也不必担心，随着ES6的推行，硬件发展越来越快，纯H5APP未必没有一席之地。最后说一个很少人注意到的H5优势，大家大谈H5APP时都是快速开发、低成本、多平台等等，但我却觉得它和很多APP开发方式相比有一个不同之处——图文混合的排版。正是这些复杂多变的CSS样式消耗了性能，但是它带来了排版的多样性，能够细致到每一个字宽行高和风格的像素级处理，才是H5的优异之处。&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#31227;&amp;#21160;&amp;#24212;&amp;#29992;&amp;#35266;&amp;#23519;" src="http://download.williamlong.info/upload/2582_1.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;　　来源：维特博客投稿，  &lt;a href="http://wittsay.cc/w3cnews/800" target="_blank"&gt;原文链接&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;  &lt;a href="http://www.williamlong.info/archives/4376.html" target="_blank"&gt;评论《纯HTML5APP与原生APP的差距在哪？》的内容...&lt;/a&gt;&lt;/p&gt; &lt;h3&gt;相关文章:&lt;/h3&gt; &lt;ul&gt;  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/4235.html"&gt;手机卡恶意补卡盗刷漏洞&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/4211.html"&gt;谷歌推出虚拟运营商服务&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/4209.html"&gt;小米手机教会我们的四件事情&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/4201.html"&gt;报告称越穷的地方越爱用移动设备上网&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.williamlong.info/archives/4167.html"&gt;小米4被指预装恶意软件：回应称测试机遭篡改&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt; &lt;br /&gt;微博： &lt;a href="http://weibo.com/williamlong"&gt;新浪微博&lt;/a&gt; - 微信公众号：williamlonginfo  &lt;br /&gt;月光博客投稿信箱：williamlong.info(at)gmail.com &lt;br /&gt;Created by William Long www.williamlong.info &lt;br /&gt; &lt;img alt="&amp;#26376;&amp;#20809;&amp;#21338;&amp;#23458;" src="http://www.williamlong.info/images/qrcode.jpg"&gt;&lt;/img&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>编程开发</category>
      <guid isPermaLink="true">https://itindex.net/detail/54457-html5app-app</guid>
      <pubDate>Sat, 03 Oct 2015 23:14:41 CST</pubDate>
    </item>
    <item>
      <title>传统 Ajax 已死，Fetch 永生</title>
      <link>https://itindex.net/detail/54431-%E4%BC%A0%E7%BB%9F-ajax-fetch</link>
      <description>&lt;p&gt;  &lt;img alt="image" src="https://cloud.githubusercontent.com/assets/948896/10188666/bc9a535a-6793-11e5-9d77-f3254a210b09.png" title="image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;原谅我做一次标题党，Ajax 不会死，传统 Ajax 指的是 XMLHttpRequest（XHR），未来现在已被   &lt;a href="https://fetch.spec.whatwg.org/"&gt;Fetch&lt;/a&gt; 替代。&lt;/p&gt;
 &lt;p&gt;最近把阿里一个千万级 PV 的数据产品全部由 jQuery 的    &lt;code&gt;$.ajax&lt;/code&gt; 迁移到   &lt;code&gt;Fetch&lt;/code&gt;，上线一个多月以来运行非常稳定。结果证明，对于 IE8+ 以上浏览器，在生产环境使用 Fetch 是可行的。&lt;/p&gt;
 &lt;p&gt;由于 Fetch API 是基于 Promise 设计，有必要先学习一下 Promise，推荐阅读   &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;MDN Promise 教程&lt;/a&gt;。旧浏览器不支持 Promise，需要使用 polyfill   &lt;a href="https://github.com/jakearchibald/es6-promise"&gt;es6-promise&lt;/a&gt; 。&lt;/p&gt;
 &lt;p&gt;本文不是 Fetch API 科普贴，其实是讲异步处理和 Promise 的。Fetch API 很简单，看文档很快就学会了。推荐   &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalFetch/fetch"&gt;MDN Fetch 教程&lt;/a&gt; 和 万能的  &lt;a href="https://fetch.spec.whatwg.org/"&gt;WHATWG Fetch 规范&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;Why Fetch&lt;/h2&gt;
 &lt;p&gt;XMLHttpRequest 是一个设计粗糙的 API，不符合关注分离（Separation of Concerns）的原则，配置和调用方式非常混乱，而且基于事件的异步模型写起来也没有现代的 Promise，generator/yield，async/await 友好。&lt;/p&gt;
 &lt;p&gt;Fetch 的出现就是为了解决 XHR 的问题，拿例子说明：&lt;/p&gt;
 &lt;p&gt;使用 XHR 发送一个 json 请求一般是这样：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;var xhr = new XMLHttpRequest();
xhr.open(&amp;apos;GET&amp;apos;, url);
xhr.responseType = &amp;apos;json&amp;apos;;

xhr.onload = function() {
  console.log(xhr.response);
};

xhr.onerror = function() {
  console.log(&amp;quot;Oops, error&amp;quot;);
};

xhr.send();&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;使用 Fetch 后，顿时看起来好一点&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;fetch(url).then(function(response) {
  return response.json();
}).then(function(data) {
  console.log(data);
}).catch(function(e) {
  console.log(&amp;quot;Oops, error&amp;quot;);
});&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;使用 ES6 的   &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions"&gt;箭头函数&lt;/a&gt; 后：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;fetch(url).then(response =&amp;gt; response.json())
  .then(data =&amp;gt; console.log(data))
  .catch(e =&amp;gt; console.log(&amp;quot;Oops, error&amp;quot;, e))&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;现在看起来好很多了，但这种 Promise 的写法还是有 Callback 的影子，而且 promise 使用 catch 方法来进行错误处理的方式有点奇怪。不用急，下面使用 async/await 来做最终优化：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;注：async/await 是非常新的 API，属于 ES7，目前尚在 Stage 1(提议) 阶段，这是它的   &lt;a href="https://github.com/lukehoban/ecmascript-asyncawait"&gt;完整规范&lt;/a&gt;。使用    &lt;a href="https://babeljs.io/"&gt;Babel&lt;/a&gt; 开启    &lt;a href="https://babeljs.io/docs/usage/runtime/"&gt;runtime&lt;/a&gt; 模式后可以把 async/await 无痛编译成 ES5 代码。也可以直接使用    &lt;a href="https://github.com/facebook/regenerator"&gt;regenerator&lt;/a&gt; 来编译到 ES5。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;pre&gt;  &lt;code&gt;try {
  let response = await fetch(url);
  let data = response.json();
  console.log(data);
} catch(e) {
  console.log(&amp;quot;Oops, error&amp;quot;, e);
}
// 注：这段代码如果想运行，外面需要包一个 async function&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;duang~~ 的一声，使用   &lt;code&gt;await&lt;/code&gt;  后，  &lt;strong&gt;写异步代码就像写同步代码一样爽&lt;/strong&gt;。  &lt;code&gt;await&lt;/code&gt; 后面可以跟 Promise 对象，表示等待 Promise   &lt;code&gt;resolve()&lt;/code&gt; 才会继续向下执行，如果 Promise 被   &lt;code&gt;reject()&lt;/code&gt; 或抛出异常则会被外面的   &lt;code&gt;try...catch&lt;/code&gt; 捕获。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;await/async，generator/yield 可以粗略地认为是 Promise 的语法糖&lt;/strong&gt;（其实功能上也有增强）。因此它们可以和 Fetch 完美搭配使用。这也是使用标准 Promise 一大好处。最近也把项目中使用第三方 Promise 库的代码全部转成标准 Promise，为以后全面使用 async/await 做准备。&lt;/p&gt;
 &lt;p&gt;另外，Fetch 也很适合做现在流行的同构应用，有人基于 Fetch 的语法，在 Node 端基于 http 库实现了   &lt;a href="https://github.com/bitinn/node-fetch"&gt;node-fetch&lt;/a&gt;，又有人封装了用于同构应用的   &lt;a href="https://github.com/matthew-andrews/isomorphic-fetch"&gt;isomorphic-fetch&lt;/a&gt;。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;注：同构(isomorphic/universal)就是使   &lt;strong&gt;前后端运行同一套代码&lt;/strong&gt;的意思，后端一般是指 NodeJS 环境。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;总结一下，Fetch 优点主要有：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;   &lt;p&gt;语法简洁，更加语义化&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;基于标准 Promise 实现，支持 async/await&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;同构方便，使用     &lt;a href="https://github.com/matthew-andrews/isomorphic-fetch"&gt;isomorphic-fetch&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
 &lt;h2&gt;Fetch 启用方法&lt;/h2&gt;
 &lt;p&gt;下面是重点↓↓↓&lt;/p&gt;
 &lt;p&gt;先看一下 Fetch 原生支持率：  &lt;br /&gt;  &lt;img alt="image" src="https://cloud.githubusercontent.com/assets/948896/10188421/c6e19fc8-6791-11e5-8ac2-bfede76df6b4.png" title="image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;原生支持率并不高，幸运的是，引入下面这些 polyfill 后可以完美支持 IE8+ ：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;   &lt;p&gt;由于 IE8 是 ES3，需要引入 ES5 的 polyfill:     &lt;a href="https://github.com/es-shims/es5-shim"&gt;es5-shim, es5-sham&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;引入 Promise 的 polyfill:     &lt;a href="https://github.com/jakearchibald/es6-promise"&gt;es6-promise&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;引入 fetch 探测库：    &lt;a href="https://github.com/camsong/fetch-detector"&gt;fetch-detector&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;引入 fetch 的 polyfill:     &lt;a href="https://github.com/camsong/fetch-ie8"&gt;fetch-ie8&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;可选：如果你还使用了 jsonp，引入     &lt;a href="https://github.com/camsong/fetch-jsonp"&gt;fetch-jsonp&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;可选：开启 Babel 的 runtime 模式，现在就使用 async/await&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;Fetch polyfill 的基本原理是探测是否存在   &lt;code&gt;window.fetch&lt;/code&gt; 方法，如果没有则用 XHR 实现。这也是   &lt;a href="https://github.com/github/fetch"&gt;github/fetch&lt;/a&gt; 的做法，但是有些浏览器（Chrome 45）原生支持 Fetch，但响应中有  &lt;a href="https://lists.w3.org/Archives/Public/public-webapps-github/2015Aug/0218.html"&gt;中文时会乱码&lt;/a&gt;，老外又不太关心这种问题，所以我自己才封装了   &lt;code&gt;fetch-detector&lt;/code&gt; 和   &lt;code&gt;fetch-ie8&lt;/code&gt; 只在浏览器稳定支持 Fetch 情况下才使用原生 Fetch。这些库现在  &lt;strong&gt;每天有几千万个请求都在使用，绝对靠谱&lt;/strong&gt;！&lt;/p&gt;
 &lt;p&gt;终于，引用了这一堆 polyfill 后，可以愉快地使用 Fetch 了。但要小心，下面有坑：&lt;/p&gt;
 &lt;h2&gt;Fetch 常见坑&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;Fetch 请求默认是不带 cookie 的，需要设置     &lt;code&gt;fetch(url, {credentials: &amp;apos;include&amp;apos;})&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;服务器返回 400，500 错误码时并不会 reject，只有网络错误这些导致请求不能完成时，fetch 才会被 reject。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;竟然没有提到 IE，这实在太不科学了，现在来详细说下 IE&lt;/p&gt;
 &lt;h2&gt;IE 使用策略&lt;/h2&gt;
 &lt;p&gt;所有版本的 IE 均不支持原生 Fetch，fetch-ie8 会自动使用 XHR 做 polyfill。但在跨域时有个问题需要处理。&lt;/p&gt;
 &lt;p&gt;IE8, 9 的 XHR 不支持 CORS 跨域，虽然提供   &lt;code&gt;XDomainRequest&lt;/code&gt;，但这个东西就是玩具，不支持传 Cookie！如果接口需要权限验证，还是乖乖地使用 jsonp 吧，推荐使用   &lt;a href="https://github.com/camsong/fetch-jsonp"&gt;fetch-jsonp&lt;/a&gt;。如果有问题直接提 issue，我会第一时间解决。&lt;/p&gt;
 &lt;h2&gt;标准 Promise 的不足&lt;/h2&gt;
 &lt;p&gt;由于 Fetch 是典型的异步场景，所以大部分遇到的问题不是 Fetch 的，其实是 Promise 的。ES6 的 Promise 是基于   &lt;a href="https://promisesaplus.com/"&gt;Promises/A+&lt;/a&gt; 标准，为了保持  &lt;strong&gt;简单简洁&lt;/strong&gt;，只提供极简的几个 API。如果你用过一些牛 X 的异步库，如 jQuery(不要笑) 、Q.js 或者 RSVP.js，可能会感觉 Promise 功能太少了。&lt;/p&gt;
 &lt;h3&gt;没有 Deferred&lt;/h3&gt;
 &lt;p&gt;  &lt;a href="http://api.jquery.com/category/deferred-object/"&gt;Deferred&lt;/a&gt; 可以在创建 Promise 时可以减少一层嵌套，还有就是跨方法使用时很方便。  &lt;br /&gt;ECMAScript 11 年就有过   &lt;a href="http://wiki.ecmascript.org/doku.php?id=strawman:deferred_functions"&gt;Deferred 提案&lt;/a&gt;，但后来没被接受。其实用 Promise 不到十行代码就能实现 Deferred：  &lt;a href="https://github.com/seangenabe/es6-deferred/blob/master/deferred.js"&gt;es6-deferred&lt;/a&gt;。现在有了 async/await，generator/yield 后，deferred 就没有使用价值了。&lt;/p&gt;
 &lt;h3&gt;没有获取状态方法：isRejected，isResolved&lt;/h3&gt;
 &lt;p&gt;标准 Promise 没有提供获取当前状态 rejected 或者 resolved 的方法。只允许外部传入成功或失败后的回调。我认为这其实是优点，这是一种声明式的接口，更简单。&lt;/p&gt;
 &lt;h3&gt;缺少其它一些方法：always，progress，finally&lt;/h3&gt;
 &lt;p&gt;always 可以通过在 then 和 catch 里重复调用方法实现。finally 也类似。progress 这种进度通知的功能还没有用过，暂不知道如何替代。&lt;/p&gt;
 &lt;h2&gt;资料&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="https://fetch.spec.whatwg.org/"&gt;WHATWG Fetch 规范&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="http://bubkoo.com/2015/05/08/introduction-to-fetch/"&gt;Fetch API 简介&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="http://pouchdb.com/2015/03/05/taming-the-async-beast-with-es7.html"&gt;教你驯服 async&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;    &lt;a href="http://www.ruanyifeng.com/blog/2015/05/async.html"&gt;阮一峰介绍 async&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;最后&lt;/h2&gt;
 &lt;p&gt;Fetch 替换 XHR 只是时间问题，现在看到国外很多新的库都默认使用了 Fetch。&lt;/p&gt;
 &lt;p&gt;最后再做一个大胆预测：由于 async/await 这类新异步语法的出现，第三方的 Promise 类库会逐渐被标准 Promise 替代，使用 polyfill 是现在比较明智的做法。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;转至我的博客，原文地址：   &lt;a href="https://github.com/camsong/blog/issues/2"&gt;https://github.com/camsong/blog/issues/2&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>ajax fetch async 异步编程 javascript</category>
      <guid isPermaLink="true">https://itindex.net/detail/54431-%E4%BC%A0%E7%BB%9F-ajax-fetch</guid>
      <pubDate>Wed, 30 Sep 2015 17:29:37 CST</pubDate>
    </item>
  </channel>
</rss>

