﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-xiaomage234-随笔分类-performance</title><link>http://www.blogjava.net/xiaomage234/category/40627.html</link><description>生命本就是一次凄美的漂流，记忆中放不下的，永远是孩提时代的那一份浪漫与纯真！</description><language>zh-cn</language><lastBuildDate>Fri, 19 Apr 2019 11:36:32 GMT</lastBuildDate><pubDate>Fri, 19 Apr 2019 11:36:32 GMT</pubDate><ttl>60</ttl><item><title>性能问题定位：工具使用【转】</title><link>http://www.blogjava.net/xiaomage234/archive/2019/04/19/433725.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 19 Apr 2019 03:04:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2019/04/19/433725.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/433725.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2019/04/19/433725.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/433725.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/433725.html</trackback:ping><description><![CDATA[from:<a href="http://zhulongchao.com/blog/performance-trace.html">http://zhulongchao.com/blog/performance-trace.html<br /><br /><h2>1.网速测试</h2><h3>安装iperf</h3><div style="margin: 0px; box-sizing: border-box; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><div style="margin: 0px; box-sizing: border-box; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><pre style="margin-top: 0px; margin-bottom: 10px; box-sizing: border-box; padding: 9.5px; background: #f5f5f5; font-size: 0.8rem; line-height: 1.42857; border-radius: 4px; border: 1px solid #cccccc; color: #333333; word-break: break-all; overflow-wrap: break-word; white-space: pre-wrap; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace;"><code style="margin: 0px auto; box-sizing: border-box; padding: 0px 0.2em; max-width: 760px; width: 739px; display: block; font-size: 0.8rem; background-color: #f4f4f4; border: none; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; line-height: 21.76px;">yum install epel-release 从epel源中安装 yum install -y  iperf </code></pre></div></div><h3>带宽检测</h3><div style="margin: 0px; box-sizing: border-box; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><div style="margin: 0px; box-sizing: border-box; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><pre style="margin-top: 0px; margin-bottom: 10px; box-sizing: border-box; padding: 9.5px; background: #f5f5f5; font-size: 0.8rem; line-height: 1.42857; border-radius: 4px; border: 1px solid #cccccc; color: #333333; word-break: break-all; overflow-wrap: break-word; white-space: pre-wrap; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace;"><code style="margin: 0px auto; box-sizing: border-box; padding: 0px 0.2em; max-width: 760px; width: 739px; display: block; font-size: 0.8rem; background-color: #f4f4f4; border: none; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; line-height: 21.76px;">iperf -s 开启服务端  iperf -c ip </code></pre></div></div><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;"><img src="http://images.cnblogs.com/cnblogs_com/zhulongchao/771893/o_iperf.png" alt="" style="margin: 1.5em auto 1.6em; box-sizing: border-box; padding: 0px; max-width: 100%; display: block; height: auto;" /></p><h3>丢包问题</h3><div style="margin: 0px; box-sizing: border-box; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><div style="margin: 0px; box-sizing: border-box; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><pre style="margin-top: 0px; margin-bottom: 10px; box-sizing: border-box; padding: 9.5px; background: #f5f5f5; font-size: 0.8rem; line-height: 1.42857; border-radius: 4px; border: 1px solid #cccccc; color: #333333; word-break: break-all; overflow-wrap: break-word; white-space: pre-wrap; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace;"><code style="margin: 0px auto; box-sizing: border-box; padding: 0px 0.2em; max-width: 760px; width: 739px; display: block; font-size: 0.8rem; background-color: #f4f4f4; border: none; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; line-height: 21.76px;">tcpdump进行抓包  tcpdump -i eth0 -s 3000 port 8080 -w /home/tomcat.pcap  对于抓包文件采用wireshark进行分析  丢包(TCP DUP ACK) 重传(retransmission)，超时重传， </code></pre></div></div><h2>2.cdn性能测试</h2><div style="margin: 0px; box-sizing: border-box; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><div style="margin: 0px; box-sizing: border-box; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><pre style="margin-top: 0px; margin-bottom: 10px; box-sizing: border-box; padding: 9.5px; background: #f5f5f5; font-size: 0.8rem; line-height: 1.42857; border-radius: 4px; border: 1px solid #cccccc; color: #333333; word-break: break-all; overflow-wrap: break-word; white-space: pre-wrap; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace;"><code style="margin: 0px auto; box-sizing: border-box; padding: 0px 0.2em; max-width: 760px; width: 739px; display: block; font-size: 0.8rem; background-color: #f4f4f4; border: none; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; line-height: 21.76px;">cdn 缓存，回源问题    304请求，浏览器是否使用本地缓存。比较last_modified 和if_modified_since  通过实践戳来判断，浏览器缓存和cdn缓存 </code></pre></div></div><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;"><img src="http://img4.tbcdn.cn/L1/461/1/2118052340.jpg" alt="" style="margin: 1.5em auto 1.6em; box-sizing: border-box; padding: 0px; max-width: 100%; display: block; height: auto;" /></p><h2>3.DNS基础</h2><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">路由解析</p><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">泛域名解析</p><h2>4.分布式服务链路追踪</h2><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;"><img src="http://images.cnblogs.com/cnblogs_com/zhulongchao/771893/o_traceid.png" alt="" style="margin: 1.5em auto 1.6em; box-sizing: border-box; padding: 0px; max-width: 100%; display: block; height: auto;" /></p><div style="margin: 0px; box-sizing: border-box; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><div style="margin: 0px; box-sizing: border-box; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><pre style="margin-top: 0px; margin-bottom: 10px; box-sizing: border-box; padding: 9.5px; background: #f5f5f5; font-size: 0.8rem; line-height: 1.42857; border-radius: 4px; border: 1px solid #cccccc; color: #333333; word-break: break-all; overflow-wrap: break-word; white-space: pre-wrap; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace;"><code style="margin: 0px auto; box-sizing: border-box; padding: 0px 0.2em; max-width: 760px; width: 739px; display: block; font-size: 0.8rem; background-color: #f4f4f4; border: none; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; line-height: 21.76px;">http入口产生一个traceId  分发到rpc调用，cache，db，jms调用链路中  google的著名论文dapper和zipkin  日志聚合，绑定链路日志和业务日志  采样采集，慢请求，异常服务。  日志量大。日志异步写入，环状数组，日志组件自研  共享信息放在ThreadLocal中。比如traceId </code></pre></div></div><h2>5.网卡性能问题定位</h2><div style="margin: 0px; box-sizing: border-box; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><div style="margin: 0px; box-sizing: border-box; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><pre style="margin-top: 0px; margin-bottom: 10px; box-sizing: border-box; padding: 9.5px; background: #f5f5f5; font-size: 0.8rem; line-height: 1.42857; border-radius: 4px; border: 1px solid #cccccc; color: #333333; word-break: break-all; overflow-wrap: break-word; white-space: pre-wrap; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace;"><code style="margin: 0px auto; box-sizing: border-box; padding: 0px 0.2em; max-width: 760px; width: 739px; display: block; font-size: 0.8rem; background-color: #f4f4f4; border: none; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; line-height: 21.76px;">tsar -l  -i 1 --traffic 查看网卡的进出流量 </code></pre></div></div><h2>6.CPU性能问题定位</h2><div style="margin: 0px; box-sizing: border-box; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><div style="margin: 0px; box-sizing: border-box; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><pre style="margin-top: 0px; margin-bottom: 10px; box-sizing: border-box; padding: 9.5px; background: #f5f5f5; font-size: 0.8rem; line-height: 1.42857; border-radius: 4px; border: 1px solid #cccccc; color: #333333; word-break: break-all; overflow-wrap: break-word; white-space: pre-wrap; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace;"><code style="margin: 0px auto; box-sizing: border-box; padding: 0px 0.2em; max-width: 760px; width: 739px; display: block; font-size: 0.8rem; background-color: #f4f4f4; border: none; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; line-height: 21.76px;">tsar -l  -i 1 --cpu  软件问题定位，perf 采样所有进程数据  perf record -F 99 -a -g -- sleep 30  java进程的函数map：java -cp attach-main.jar:$JAVA_HOME/lib/tools.jar net.virtualvoid.perf.AttachOnce PID  输出函数和地址的map  输出火焰图 perf script | stackcollapse-perf.pl | flamegraph.pl --color=java --hash &gt; flamegraph.svg </code></pre></div></div><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;"><img src="http://images.cnblogs.com/cnblogs_com/zhulongchao/771893/o_QQ%e6%88%aa%e5%9b%be20170724162019.png" alt="" style="margin: 1.5em auto 1.6em; box-sizing: border-box; padding: 0px; max-width: 100%; display: block; height: auto;" /></p><h2>7.内存性能问题定位</h2><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">-堆内内存问题，</p><div style="margin: 0px; box-sizing: border-box; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><div style="margin: 0px; box-sizing: border-box; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><pre style="margin-top: 0px; margin-bottom: 10px; box-sizing: border-box; padding: 9.5px; background: #f5f5f5; font-size: 0.8rem; line-height: 1.42857; border-radius: 4px; border: 1px solid #cccccc; color: #333333; word-break: break-all; overflow-wrap: break-word; white-space: pre-wrap; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace;"><code style="margin: 0px auto; box-sizing: border-box; padding: 0px 0.2em; max-width: 760px; width: 739px; display: block; font-size: 0.8rem; background-color: #f4f4f4; border: none; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; line-height: 21.76px;">采用jmap dump内存，采用离线工具分析  jprofile、mat </code></pre></div></div><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">-堆外内存问题</p><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">a.google-perftools</p><div style="margin: 0px; box-sizing: border-box; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><div style="margin: 0px; box-sizing: border-box; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;"><pre style="margin-top: 0px; margin-bottom: 10px; box-sizing: border-box; padding: 9.5px; background: #f5f5f5; font-size: 0.8rem; line-height: 1.42857; border-radius: 4px; border: 1px solid #cccccc; color: #333333; word-break: break-all; overflow-wrap: break-word; white-space: pre-wrap; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace;"><code style="margin: 0px auto; box-sizing: border-box; padding: 0px 0.2em; max-width: 760px; width: 739px; display: block; font-size: 0.8rem; background-color: #f4f4f4; border: none; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; line-height: 21.76px;">yum install -y google-perftools graphviz  export LD_PRELOAD=/usr/lib64/libtcmalloc.so.4  export HEAPPROFILE=/home/testGperf.prof  执行程序，结束程序，生成prof  分析prof  生成svg, pdf,text pprof --svg $JAVA_HOME/bin/java testGperf.prof.0001.heap &gt; test.svg  pprof --pdf $JAVA_HOME/bin/java testGperf.prof.0001.heap &gt; test.pdf  pprof --text $JAVA_HOME/bin/java testGperf.prof.0001.heap &gt; test.txt </code></pre></div></div><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">b.jemalloc定位（优势，适合长时间trace）</p><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;"><img src="http://images.cnblogs.com/cnblogs_com/zhulongchao/771893/o_QQ%e6%88%aa%e5%9b%be20170724161232.png" alt="" style="margin: 1.5em auto 1.6em; box-sizing: border-box; padding: 0px; max-width: 100%; display: block; height: auto;" /></p><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">sudo apt-get install graphviz 编译安装 ./configure &#8211;enable-prof &#8211;enable-stats &#8211;enable-debug &#8211;enable-fill make make install</p><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">运行配置 export MALLOC_CONF=&#8221;prof:true,prof_gdump:true,prof_prefix:/home/jedump/jez,lg_prof_interval:30,lg_prof_sample:17&#8221;</p><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">export LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 运行 java -jar target/spring-boot-jemalloc-example-0.0.1-SNAPSHOT.jar</p><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">jeprof &#8211;show_bytes &#8211;svg jez.*.heap &gt; app-profiling.svg</p><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">注明：如果在docker容器中，推荐用pprof，jemalloc只显示函数地址，不显示函数名</p><h2>8.机器资源配额问题</h2><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">/etc/security/limits.conf</p><ul style="margin: 0px 0px 0px 20px; box-sizing: border-box; padding: 0px; font-size: 0.9rem; text-align: justify; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;"><li style="margin: 0px; box-sizing: border-box; padding: 0px;">soft nofile 65536</li><li style="margin: 0px; box-sizing: border-box; padding: 0px;">hard nofile 65536</li></ul><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">控制该用户文件句柄数</p><h2>9.磁盘性能问题定位</h2><p style="margin: 20px 0px; box-sizing: border-box; padding: 0px; text-align: justify; font-size: 0.9rem; color: #222222; font-family: Georgia, &quot;Linux Libertine&quot;, -apple-system, &quot;Helvetica Neue&quot;, Helvetica, &quot;Nimbus Sans L&quot;, Arial, &quot;Liberation Sans&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Source Han Sans CN&quot;, &quot;Source Han Sans SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Wenquanyi Micro Hei&quot;, &quot;WenQuanYi Zen Hei&quot;, &quot;ST Heiti&quot;, SimHei, &quot;WenQuanYi Zen Hei Sharp&quot;, sans-serif; background-color: #ffffff;">tsar -l -i 1 &#8211;io</p></a><img src ="http://www.blogjava.net/xiaomage234/aggbug/433725.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2019-04-19 11:04 <a href="http://www.blogjava.net/xiaomage234/archive/2019/04/19/433725.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java堆外内存排查小结【转】</title><link>http://www.blogjava.net/xiaomage234/archive/2019/03/30/433695.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Sat, 30 Mar 2019 03:44:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2019/03/30/433695.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/433695.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2019/03/30/433695.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/433695.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/433695.html</trackback:ping><description><![CDATA[from:<a href="https://blog.csdn.net/lycyingO/article/details/80854669">https://blog.csdn.net/lycyingO/article/details/80854669<br /><br /><br /><div>&nbsp;版权声明：微信公众号《小姐姐味道》，转载注明出处<span style="white-space:pre">	</span>https://blog.csdn.net/lycyingO/article/details/80854669</div><div></div><div></div><div>简介</div><div>JVM堆外内存难排查但经常会出现问题，这可能是目前最全的JVM堆外内存排查思路。</div><div></div><div>通过本文，你应该了解：</div><div></div><div>pmap 命令</div><div>gdb 命令</div><div>perf 命令</div><div>内存 RSS、VSZ的区别</div><div>java NMT</div><div>起因</div><div>这几天遇到一个比较奇怪的问题，觉得有必要和大家分享一下。我们的一个服务，运行在docker上，在某个版本之后，占用的内存开始增长，直到docker分配的内存上限，但是并不会OOM。版本的更改如下：</div><div></div><div>升级了基础软件的版本</div><div>将docker的内存上限由4GB扩展到8GB</div><div>上上个版本的一项变动是使用了EhCache的Heap缓存</div><div>没有读文件，也没有mmap操作</div><div>使用jps 查看启动参数，发现分配了大约3GB的堆内存</div><div></div><div>[root]$ jps -v</div><div>75 Bootstrap -Xmx3000m -Xms3000m&nbsp; -verbose:gc -Xloggc:/home/logs/gc.log -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSCompactAtFullCollection -XX:MaxTenuringThreshold=10 -XX:MaxPermSize=128M -XX:SurvivorRatio=3 -XX:NewRatio=2 -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+UseParNewGC -XX:+UseConcMarkSweepGC</div><div>使用ps查看进程使用的内存和虚拟内存 ( Linux内存管理 )。除了虚拟内存比较高达到17GB以外，实际使用的内存RSS也夸张的达到了7GB，远远超过了-Xmx的设定。</div><div></div><div>[root]$ ps -p 75 -o rss,vsz&nbsp;&nbsp;</div><div>&nbsp;</div><div>RSS&nbsp; &nbsp; VSZ 7152568 17485844</div><div>原创文章，转载注明出处 (http://sayhiai.com)</div><div>排查过程</div><div>明显的，是有堆外内存的使用，不太可能是由于EhCache引起的（因为我们使用了heap方式）。了解到基础软件的升级涉及到netty版本升级，netty会用到一些DirectByteBuffer，第一轮排查我们采用如下方式：</div><div></div><div>jmap -dump:format=b,file=75.dump 75 通过分析堆内存找到DirectByteBuffer的引用和大小</div><div>部署一个升级基础软件之前的版本，持续观察</div><div>部署另一个版本，更改EhCache限制其大小到1024M</div><div>考虑到可能由Docker的内存分配机制引起，部署一实例到实体机</div><div>结果4个环境中的服务，无一例外的都出现了内存超用的问题。问题很奇怪，宝宝睡不着觉。</div><div></div><div>pmap</div><div>为了进一步分析问题，我们使用pmap查看进程的内存分配，通过RSS升序序排列。结果发现除了地址000000073c800000上分配的3GB堆以外，还有数量非常多的64M一块的内存段，还有巨量小的物理内存块映射到不同的虚拟内存段上。但到现在为止，我们不知道里面的内容是什么，是通过什么产生的。</div><div></div><div>[root]$ pmap -x 75&nbsp; | sort -n -k3</div><div>&nbsp;</div><div>.....省略N行</div><div>&nbsp;</div><div>0000000040626000&nbsp; &nbsp;55488&nbsp; &nbsp;55484&nbsp; &nbsp;55484 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa07c000000&nbsp; &nbsp;65536&nbsp; &nbsp;55820&nbsp; &nbsp;55820 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa044000000&nbsp; &nbsp;65536&nbsp; &nbsp;55896&nbsp; &nbsp;55896 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa0c0000000&nbsp; &nbsp;65536&nbsp; &nbsp;56304&nbsp; &nbsp;56304 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9db8000000&nbsp; &nbsp;65536&nbsp; &nbsp;56360&nbsp; &nbsp;56360 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa0b8000000&nbsp; &nbsp;65536&nbsp; &nbsp;56836&nbsp; &nbsp;56836 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa084000000&nbsp; &nbsp;65536&nbsp; &nbsp;57916&nbsp; &nbsp;57916 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9ec4000000&nbsp; &nbsp;65532&nbsp; &nbsp;59752&nbsp; &nbsp;59752 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa008000000&nbsp; &nbsp;65536&nbsp; &nbsp;60012&nbsp; &nbsp;60012 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9e58000000&nbsp; &nbsp;65536&nbsp; &nbsp;61608&nbsp; &nbsp;61608 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9f18000000&nbsp; &nbsp;65532&nbsp; &nbsp;61732&nbsp; &nbsp;61732 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa018000000&nbsp; &nbsp;65532&nbsp; &nbsp;61928&nbsp; &nbsp;61928 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa088000000&nbsp; &nbsp;65536&nbsp; &nbsp;62336&nbsp; &nbsp;62336 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa020000000&nbsp; &nbsp;65536&nbsp; &nbsp;62428&nbsp; &nbsp;62428 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9e44000000&nbsp; &nbsp;65536&nbsp; &nbsp;64352&nbsp; &nbsp;64352 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9ec0000000&nbsp; &nbsp;65528&nbsp; &nbsp;64928&nbsp; &nbsp;64928 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa050000000&nbsp; &nbsp;65532&nbsp; &nbsp;65424&nbsp; &nbsp;65424 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9e08000000&nbsp; &nbsp;65512&nbsp; &nbsp;65472&nbsp; &nbsp;65472 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9de0000000&nbsp; &nbsp;65524&nbsp; &nbsp;65512&nbsp; &nbsp;65512 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9dec000000&nbsp; &nbsp;65532&nbsp; &nbsp;65532&nbsp; &nbsp;65532 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9dac000000&nbsp; &nbsp;65536&nbsp; &nbsp;65536&nbsp; &nbsp;65536 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9dc8000000&nbsp; &nbsp;65536&nbsp; &nbsp;65536&nbsp; &nbsp;65536 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9e30000000&nbsp; &nbsp;65536&nbsp; &nbsp;65536&nbsp; &nbsp;65536 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007f9eb4000000&nbsp; &nbsp;65536&nbsp; &nbsp;65536&nbsp; &nbsp;65536 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa030000000&nbsp; &nbsp;65536&nbsp; &nbsp;65536&nbsp; &nbsp;65536 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>00007fa0b0000000&nbsp; &nbsp;65536&nbsp; &nbsp;65536&nbsp; &nbsp;65536 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>000000073c800000 3119140 2488596 2487228 rwx--&nbsp; &nbsp; [ anon ]</div><div>&nbsp;</div><div>total kB&nbsp; &nbsp; &nbsp; &nbsp; 17629516 7384476 7377520</div><div>通过google，找到以下资料 Linux glibc &gt;= 2.10 (RHEL 6) malloc may show excessive virtual memory usage)</div><div></div><div>文章指出造成应用程序大量申请64M大内存块的原因是由Glibc的一个版本升级引起的，通过export MALLOC_ARENA_MAX=4可以解决VSZ占用过高的问题。虽然这也是一个问题，但却不是我们想要的，因为我们增长的是物理内存，而不是虚拟内存。</div><div></div><div>NMT</div><div>幸运的是 JDK1.8有Native Memory Tracker可以帮助定位。通过在启动参数上加入-XX:NativeMemoryTracking=detail就可以启用。在命令行执行jcmd可查看内存分配。</div><div></div><div>#jcmd 75 VM.native_memory summary</div><div>&nbsp;</div><div>Native Memory Tracking: Total: reserved=5074027KB, committed=3798707KB -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Java Heap (reserved=3072000KB, committed=3072000KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (mmap: reserved=3072000KB, committed=3072000KB) -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Class (reserved=1075949KB, committed=28973KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (classes #4819)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (malloc=749KB #13158)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (mmap: reserved=1075200KB, committed=28224KB) -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Thread (reserved=484222KB, committed=484222KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (thread #470)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (stack: reserved=482132KB, committed=482132KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (malloc=1541KB #2371)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (arena=550KB #938) -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Code (reserved=253414KB, committed=25070KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (malloc=3814KB #5593)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (mmap: reserved=249600KB, committed=21256KB) -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GC (reserved=64102KB, committed=64102KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (malloc=54094KB #255)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (mmap: reserved=10008KB, committed=10008KB) -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Compiler (reserved=542KB, committed=542KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (malloc=411KB #543)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (arena=131KB #3) -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Internal (reserved=50582KB, committed=50582KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (malloc=50550KB #13713)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (mmap: reserved=32KB, committed=32KB) -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Symbol (reserved=6384KB, committed=6384KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (malloc=4266KB #31727)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (arena=2118KB #1) -&nbsp; &nbsp; Native Memory Tracking (reserved=1325KB, committed=1325KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (malloc=208KB #3083)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (tracking overhead=1117KB) -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Arena Chunk (reserved=231KB, committed=231KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (malloc=231KB) -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Unknown (reserved=65276KB, committed=65276KB)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (mmap: reserved=65276KB, committed=65276KB)</div><div>虽然pmap得到的内存地址和NMT大体能对的上，但仍然有不少内存去向成谜。虽然是个好工具但问题并不能解决。</div><div></div><div>gdb</div><div>非常好奇64M或者其他小内存块中是什么内容，接下来通过gdbdump出来。读取/proc目录下的maps文件，能精准的知晓目前进程的内存分布。</div><div></div><div>以下脚本通过传入进程id，能够将所关联的内存全部dump到文件中（会影响服务，慎用）。</div><div></div><div>grep rw-p /proc/$1/maps | sed -n 's/^\([0-9a-f]*\)-\([0-9a-f]*\) .*$/\1 \2/p' | while read start stop; do gdb --batch --pid $1 -ex "dump memory $1-$start-$stop.dump 0x$start 0x$stop"; done</div><div>更多时候，推荐之dump一部分内存。(再次提醒操作会影响服务，注意dump的内存块大小，慎用)。</div><div></div><div>gdb --batch --pid 75 -ex "dump memory a.dump 0x7f2bceda1000 0x7f2bcef2b000</div><div>[root]$ du -h *</div><div>dump 4.0K</div><div>55-00600000-00601000.dump 400K</div><div>55-00eb7000-00f1b000.dump 0</div><div>55-704800000-7c0352000.dump 47M</div><div>55-7f2840000000-7f2842eb8000.dump 53M</div><div>55-7f2848000000-7f284b467000.dump 64M</div><div>55-7f284c000000-7f284fffa000.dump 64M</div><div>55-7f2854000000-7f2857fff000.dump 64M</div><div>55-7f285c000000-7f2860000000.dump 64M</div><div>55-7f2864000000-7f2867ffd000.dump 1016K</div><div>55-7f286a024000-7f286a122000.dump 1016K</div><div>55-7f286a62a000-7f286a728000.dump 1016K</div><div>55-7f286d559000-7f286d657000.dump</div><div>是时候查看里面的内容了</div><div></div><div>[root]$ view 55-7f284c000000-7f284fffa000.dump</div><div>^@^@X+^?^@^@^@^@^@d(^?^@^@^@ &#255;^C^@^@^@^@^@ &#255;^C^@^@^@^@^@^@^@^@^@^@^@^@&#177;&lt;97&gt;p^C^@^@^@^@ 8^^Z+^?^@^@ ^@^@d(^?^@^@ 8^^Z+^?^@^@ ^@^@d(^?^@^@</div><div>achine":524993642,"timeSecond":1460272569,"inc":2145712868,"new":false},"device":{"client":"android","uid":"xxxxx","version":881},"</div><div>device_android":{"BootSerialno":"xxxxx","CpuInfo":"0-7","MacInfo":"2c:5b:b8:b0:d5:10","RAMSize":"4027212","SdcardInfo":"xxxx","Serialno":"xxxx",</div><div>"android_id":"488aedba19097476","buildnumber":"KTU84P/1416486236","device_ip":"0.0.0.0","mac":"2c:5b:b8:b0:d5:10","market_source":"12","model":"OPPO ...more</div><div>纳尼？这些内容不应该在堆里面么？为何还会使用额外的内存进行分配？上面已经排查netty申请directbuffer的原因了，那么还有什么地方在分配堆外内存呢？</div><div></div><div>perf</div><div>传统工具失灵，快到了黔驴技穷的时候了，是时候祭出神器perf了。</div><div></div><div>使用 perf record -g -p 55 开启监控栈函数调用。运行一段时间后Ctrl+C结束，会生成一个文件perf.data。</div><div></div><div>执行perf report -i perf.data查看报告。</div><div></div><div></div><div>如图，进程大量执行bzip相关函数。搜索zip，结果如下：</div><div></div><div></div><div></div><div>-.-!</div><div></div><div>进程调用了Java_java_util_zip_Inflater_inflatBytes() 申请了内存，仅有一小部分调用Deflater释放内存。与pmap内存地址相比对，确实是bzip在搞鬼。</div><div></div><div>原创文章，转载注明出处 (http://sayhiai.com)</div><div>解决</div><div>java项目搜索zip定位到代码，发现确实有相关bzip压缩解压操作，而且GZIPInputStream有个地方没有close。</div><div></div><div>GZIPInputStream使用Inflater申请堆外内存，Deflater释放内存，调用close()方法来主动释放。如果忘记关闭，Inflater对象的生命会延续到下一次GC。在此过程中，堆外内存会一直增长。</div><div></div><div>原代码：</div><div></div><div>public byte[] decompress ( byte[] input) throws IOException {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ByteArrayOutputStream out = new ByteArrayOutputStream();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; IOUtils.copy(new GZIPInputStream(new ByteArrayInputStream(input)), out);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return out.toByteArray();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>修改后：</div><div></div><div>&nbsp;public byte[] decompress(byte[] input) throws IOException {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ByteArrayOutputStream out = new ByteArrayOutputStream();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(input));</div><div>&nbsp; &nbsp; &nbsp; &nbsp; IOUtils.copy(gzip, out);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; gzip.close();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return out.toByteArray();</div><div>&nbsp; &nbsp; }</div><div>经观察，问题解决。</div><div>---------------------&nbsp;</div><div>作者：lycyingO&nbsp;</div><div>来源：CSDN&nbsp;</div><div>原文：https://blog.csdn.net/lycyingO/article/details/80854669&nbsp;</div><div>版权声明：本文为博主原创文章，转载请附上博文链接！</div></a><img src ="http://www.blogjava.net/xiaomage234/aggbug/433695.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2019-03-30 11:44 <a href="http://www.blogjava.net/xiaomage234/archive/2019/03/30/433695.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入浅出 JIT 编译器</title><link>http://www.blogjava.net/xiaomage234/archive/2017/06/08/432592.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 08 Jun 2017 09:27:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2017/06/08/432592.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/432592.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2017/06/08/432592.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/432592.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/432592.html</trackback:ping><description><![CDATA[from:http://blog.csdn.net/liaodehong/article/details/51605457<br /><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">本文将深入浅出地讲解 JIT 编译器在 JVM 中的运作原理，使读者能够更好的理解&nbsp;<a href="http://lib.csdn.net/base/java" title="Java 知识库" target="_blank" style="text-decoration: none; color: #df3434; font-weight: bold;">Java</a>&nbsp;底层机制并且为读者在 Java 性能优化领域打开更广的视野。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"></p><h2><a name="t0" target="_blank" style="color: rgb(12, 137, 207);"></a>JIT 简介</h2><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"></p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">JIT 是 just in time 的缩写, 也就是即时编译编译器。使用即时编译器技术，能够加速 Java 程序的执行速度。下面，就对该编译器技术做个简单的讲解。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">首先，我们大家都知道，通常通过 javac 将程序源代码编译，转换成 java 字节码，JVM 通过解释字节码将其翻译成对应的机器指令，逐条读入，逐条解释翻译。很显然，经过解释执行，其执行速度必然会比可执行的二进制字节码程序慢很多。为了提高执行速度，引入了 JIT 技术。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">在运行时 JIT 会把翻译过的机器码保存起来，以备下次使用，因此从理论上来说，采用该 JIT 技术可以接近以前纯编译技术。下面我们看看，JIT 的工作过程。</p><h3><a name="t1" target="_blank" style="color: rgb(12, 137, 207);"></a>JIT 编译过程</h3><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">当 JIT 编译启用时（默认是启用的），JVM 读入.class 文件解释后，将其发给 JIT 编译器。JIT 编译器将字节码编译成本机机器代码，下图展示了该过程。</p><h5><a name="t2" target="_blank" style="color: rgb(12, 137, 207);"></a>图 1. JIT 工作原理图</h5><img alt="图 1. JIT 工作原理图" src="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/img001.png" width="576" style="border: none; max-width: 100%; max-height: 100%; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;" /><p ibm-back-to-top"="" style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><a target="_blank" href="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/#ibm-pcon" style="text-decoration: none; color: #745285; margin: 0px; padding: 0px 0px 0px 16px; border-width: 0px; border-bottom-style: none; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; display: inline; line-height: 1.065em; font-weight: bold;">回页首</a></p><h2><a name="t3" target="_blank" style="color: rgb(12, 137, 207);"></a>Hot Spot 编译</h2><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">当 JVM 执行代码时，它并不立即开始编译代码。这主要有两个原因：</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">首先，如果这段代码本身在将来只会被执行一次，那么从本质上看，编译就是在浪费精力。因为将代码翻译成 java 字节码相对于编译这段代码并执行代码来说，要快很多。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">当然，如果一段代码频繁的调用方法，或是一个循环，也就是这段代码被多次执行，那么编译就非常值得了。因此，编译器具有的这种权衡能力会首先执行解释后的代码，然后再去分辨哪些方法会被频繁调用来保证其本身的编译。其实说简单点，就是 JIT 在起作用，我们知道，对于 Java 代码，刚开始都是被编译器编译成字节码文件，然后字节码文件会被交由 JVM 解释执行，所以可以说 Java 本身是一种半编译半解释执行的语言。Hot Spot VM 采用了 JIT compile 技术，将运行频率很高的字节码直接编译为机器指令执行以提高性能，所以当字节码被 JIT 编译为机器码的时候，要说它是编译执行的也可以。也就是说，运行时，部分代码可能由 JIT 翻译为目标机器指令（以 method 为翻译单位，还会保存起来，第二次执行就不用翻译了）直接执行。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">第二个原因是最优化，当 JVM 执行某一方法或遍历循环的次数越多，就会更加了解代码结构，那么 JVM 在编译代码的时候就做出相应的优化。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">我们将在后面讲解这些优化策略，这里，先举一个简单的例子：我们知道 equals() 这个方法存在于每一个&nbsp;<a href="http://lib.csdn.net/base/java" title="Java 知识库" target="_blank" style="text-decoration: none; color: #df3434; font-weight: bold;">Java&nbsp;</a>Object 中（因为是从 Object class 继承而来）而且经常被覆写。当解释器遇到 b = obj1.equals(obj2) 这样一句代码，它则会查询 obj1 的类型从而得知到底运行哪一个 equals() 方法。而这个动态查询的过程从某种程度上说是很耗时的。</p><h3><a name="t4" target="_blank" style="color: rgb(12, 137, 207);"></a>寄存器和主存</h3><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">其中一个最重要的优化策略是编译器可以决定何时从主存取值，何时向寄存器存值。考虑下面这段代码：</p><h5><a name="t5" target="_blank" style="color: rgb(12, 137, 207);"></a>清单 1. 主存 or 寄存器测试代码</h5><div style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><pre name="code" style="white-space: pre-wrap; word-wrap: break-word; margin-top: 0px; border: 1px solid #cccccc; outline: 0px; font-size: 11px; vertical-align: baseline; width: 780px; font-family: &quot;Andale Mono&quot;, &quot;Lucida Console&quot;, Monaco, Liberation, fixed, monospace; overflow: auto; color: #000000; clear: right; margin-bottom: 6px !important; padding: 5px 10px 5px 3px !important; background-color: #f7f7f7 !important;">public class RegisterTest {  private int sum;   public void calculateSum(int n) {  for (int i = 0; i &lt; n; ++i) {  sum += i;  }  } }</pre></div><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">在某些时刻，sum 变量居于主存之中，但是从主存中检索值是开销很大的操作，需要多次循环才可以完成操作。正如上面的例子，如果循环的每一次都是从主存取值，性能是非常低的。相反，编译器加载一个寄存器给 sum 并赋予其初始值，利用寄存器里的值来执行循环，并将最终的结果从寄存器返回给主存。这样的优化策略则是非常高效的。但是线程的同步对于这种操作来说是至关重要的，因为一个线程无法得知另一个线程所使用的寄存器里变量的值，线程同步可以很好的解决这一问题，有关于线程同步的知识，我们将在后续文章中进行讲解。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">寄存器的使用是编译器的一个非常普遍的优化。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">回到之前的例子，JVM 注意到每次运行代码时，obj1 都是 java.lang.String 这种类型，那么 JVM 生成的被编译后的代码则是直接调用 String.equals() 方法。这样代码的执行将变得非常快，因为不仅它是被编译过的，而且它会跳过查找该调用哪个方法的步骤。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">当然过程并不是上面所述这样简单，如果下次执行代码时，obj1 不再是 String 类型了，JVM 将不得不再生成新的字节码。尽管如此，之后执行的过程中，还是会变的更快，因为同样会跳过查找该调用哪个方法的步骤。这种优化只会在代码被运行和观察一段时间之后发生。这也就是为什么 JIT 编译器不会理解编译代码而是选择等待然后再去编译某些代码片段的第二个原因。</p><p ibm-back-to-top"="" style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><a target="_blank" href="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/#ibm-pcon" style="text-decoration: none; color: #745285; margin: 0px; padding: 0px 0px 0px 16px; border-width: 0px; border-bottom-style: none; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; display: inline; line-height: 1.065em; font-weight: bold;">回页首</a></p><h2><a name="t6" target="_blank" style="color: rgb(12, 137, 207);"></a>初级调优：客户模式或服务器模式</h2><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">JIT 编译器在运行程序时有两种编译模式可以选择，并且其会在运行时决定使用哪一种以达到最优性能。这两种编译模式的命名源自于命令行参数（eg: -client 或者 -server）。JVM Server 模式与 client 模式启动，最主要的差别在于：-server 模式启动时，速度较慢，但是一旦运行起来后，性能将会有很大的提升。原因是：当虚拟机运行在-client 模式的时候，使用的是一个代号为 C1 的轻量级编译器，而-server 模式启动的虚拟机采用相对重量级代号为 C2 的编译器。C2 比 C1 编译器编译的相对彻底，服务起来之后，性能更高。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">通过 java -version 命令行可以直接查看当前系统使用的是 client 还是 server 模式。例如：</p><h5><a name="t7" target="_blank" style="color: rgb(12, 137, 207);"></a>图 2. 查看编译模式</h5><img alt="图 2. 查看编译模式" src="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/img002.png" width="624" style="border: none; max-width: 100%; max-height: 100%; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;" /><p ibm-back-to-top"="" style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><a target="_blank" href="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/#ibm-pcon" style="text-decoration: none; color: #745285; margin: 0px; padding: 0px 0px 0px 16px; border-width: 0px; border-bottom-style: none; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; display: inline; line-height: 1.065em; font-weight: bold;">回页首</a></p><h2><a name="t8" target="_blank" style="color: rgb(12, 137, 207);"></a>中级编译器调优</h2><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">大多数情况下，优化编译器其实只是选择合适的 JVM 以及为目标主机选择合适的编译器（-cient，-server 或是-xx:+TieredCompilation）。多层编译经常是长时运行应用程序的最佳选择，短暂应用程序则选择毫秒级性能的 client 编译器。</p><h3><a name="t9" target="_blank" style="color: rgb(12, 137, 207);"></a>优化代码缓存</h3><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">当 JVM 编译代码时，它会将汇编指令集保存在代码缓存。代码缓存具有固定的大小，并且一旦它被填满，JVM 则不能再编译更多的代码。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">我们可以很容易地看到如果代码缓存很小所具有的潜在问题。有些热点代码将会被编译，而其他的则不会被编译，这个应用程序将会以运行大量的解释代码来结束。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">这是当使用 client 编译器模式或分层编译时很频繁的一个问题。当使用普通 server 编译器模式时，编译合格的类的数量将被填入代码缓存，通常只有少量的类会被编译。但是当使用 client 编译器模式时，编译合格的类的数量将会高很多。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">在 Java 7 版本，分层编译默认的代码缓存大小经常是不够的，需要经常提高代码缓存大小。大型项目若使用 client 编译器模式，则也需要提高代码缓存大小。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">现在并没有一个好的机制可以确定一个特定的应用到底需要多大的代码缓存。因此，当需要提高代码缓存时，这将是一种凑巧的操作，一个通常的做法是将代码缓存变成默认大小的两倍或四倍。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">可以通过 &#8211;XX:ReservedCodeCacheSize=Nflag（N 就是之前提到的默认大小）来最大化代码缓存大小。代码缓存的管理类似于 JVM 中的内存管理：有一个初始大小（用-XX:InitialCodeCacheSize=N 来声明）。代码缓存的大小从初始大小开始，随着缓存被填满而逐渐扩大。代码缓存的初始大小是基于芯片<a href="http://lib.csdn.net/base/architecture" title="大型网站架构知识库" target="_blank" style="text-decoration: none; color: #df3434; font-weight: bold;">架构</a>（例如 Intel 系列机器，client 编译器模式下代码缓存大小起始于 160KB，server 编译器模式下代码缓存大小则起始于 2496KB）以及使用的编译器的。重定义代码缓存的大小并不会真正影响性能，所以设置 ReservedCodeCacheSize 的大小一般是必要的。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">再者，如果 JVM 是 32 位的，那么运行过程大小不能超过 4GB。这包括了 Java 堆，JVM 自身所有的代码空间（包括其本身的库和线程栈），应用程序分配的任何的本地内存，当然还有代码缓存。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">所以说代码缓存并不是无限的，很多时候需要为大型应用程序来调优（或者甚至是使用分层编译的中型应用程序）。比如 64 位机器，为代码缓存设置一个很大的值并不会对应用程序本身造成影响，应用程序并不会内存溢出，这些额外的内存预定一般都是被<a href="http://lib.csdn.net/base/operatingsystem" title="操作系统知识库" target="_blank" style="text-decoration: none; color: #df3434; font-weight: bold;">操作系统</a>所接受的。</p><h3><a name="t10" target="_blank" style="color: rgb(12, 137, 207);"></a>编译阈值</h3><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">在 JVM 中，编译是基于两个计数器的：一个是方法被调用的次数，另一个是方法中循环被回弹执行的次数。回弹可以有效的被认为是循环被执行完成的次数，不仅因为它是循环的结尾，也可能是因为它执行到了一个分支语句，例如 continue。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">当 JVM 执行一个 Java 方法，它会检查这两个计数器的总和以决定这个方法是否有资格被编译。如果有，则这个方法将排队等待编译。这种编译形式并没有一个官方的名字，但是一般被叫做标准编译。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">但是如果方法里有一个很长的循环或者是一个永远都不会退出并提供了所有逻辑的程序会怎么样呢？这种情况下，JVM 需要编译循环而并不等待方法被调用。所以每执行完一次循环，分支计数器都会自增和自检。如果分支计数器计数超出其自身阈值，那么这个循环（并不是整个方法）将具有被编译资格。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">这种编译叫做栈上替换（OSR），因为即使循环被编译了，这也是不够的：JVM 必须有能力当循环正在运行时，开始执行此循环已被编译的版本。换句话说，当循环的代码被编译完成，若 JVM 替换了代码（前栈），那么循环的下个迭代执行最新的被编译版本则会更加快。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">标准编译是被-XX:CompileThreshold=Nflag 的值所触发。Client 编译器模式下，N 默认的值 1500，而 Server 编译器模式下，N 默认的值则是 10000。改变 CompileThreshold 标志的值将会使编译器相对正常情况下提前（或推迟）编译代码。在性能领域，改变 CompileThreshold 标志是很被推荐且流行的方法。事实上，您可能知道 Java 基准经常使用此标志（比如：对于很多 server 编译器来说，经常在经过 8000 次迭代后改变次标志）。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">我们已经知道 client 编译器和 server 编译器在最终的性能上有很大的差别，很大程度上是因为编译器在编译一个特定的方法时，对于两种编译器可用的信息并不一样。降低编译阈值，尤其是对于 server 编译器，承担着不能使应用程序运行达到最佳性能的风险，但是经过<a href="http://lib.csdn.net/base/softwaretest" title="软件测试知识库" target="_blank" style="text-decoration: none; color: #df3434; font-weight: bold;">测试</a>应用程序我们也发现，将阈值从 8000 变成 10000，其实有着非常小的区别和影响。</p><h3><a name="t11" target="_blank" style="color: rgb(12, 137, 207);"></a>检查编译过程</h3><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">中级优化的最后一点其实并不是优化本身，而是它们并不能提高应用程序的性能。它们是 JVM（以及其他工具）的各个标志，并可以给出编译工作的可见性。它们中最重要的就是--XX:+PrintCompilation（默认状态下是 false）。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">如果 PrintCompilation 被启用，每次一个方法（或循环）被编译，JVM 都会打印出刚刚编译过的相关信息。不同的 Java 版本输出形式不一样，我们这里所说的是基于 Java 7 版本的。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">编译日志中大部分的行信息都是下面的形式：</p><h5><a name="t12" target="_blank" style="color: rgb(12, 137, 207);"></a>清单 2. 日志形式</h5><div style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><pre name="code" style="white-space: pre-wrap; word-wrap: break-word; margin-top: 0px; border: 1px solid #cccccc; outline: 0px; font-size: 11px; vertical-align: baseline; width: 780px; font-family: &quot;Andale Mono&quot;, &quot;Lucida Console&quot;, Monaco, Liberation, fixed, monospace; overflow: auto; color: #000000; clear: right; margin-bottom: 6px !important; padding: 5px 10px 5px 3px !important; background-color: #f7f7f7 !important;">timestamp compilation_id attributes (tiered_level) method_name size depot</pre></div><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">这里 timestamp 是编译完成时的时间戳，compilation_id 是一个内部的任务 ID，且通常情况下这个数字是单调递增的，但有时候对于 server 编译器（或任何增加编译阈值的时候），您可能会看到失序的编译 ID。这表明编译线程之间有些快有些慢，但请不要随意推断认为是某个编译器任务莫名其妙的非常慢。</p><h3><a name="t13" target="_blank" style="color: rgb(12, 137, 207);"></a>用 jstat 命令检查编译</h3><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">要想看到编译日志，则需要程序以-XX:+PrintCompilation flag 启动。如果程序启动时没有 flag，您可以通过 jstat 命令得到有限的可见性信息。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">Jstat 有两个选项可以提供编译器信息。其中，-compile 选项提供总共有多少方法被编译的总结信息（下面 6006 是要被检查的程序的进程 ID）：</p><h5><a name="t14" target="_blank" style="color: rgb(12, 137, 207);"></a>清单 3 进程详情</h5><div style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><pre name="code" style="white-space: pre-wrap; word-wrap: break-word; margin-top: 0px; border: 1px solid #cccccc; outline: 0px; font-size: 11px; vertical-align: baseline; width: 780px; font-family: &quot;Andale Mono&quot;, &quot;Lucida Console&quot;, Monaco, Liberation, fixed, monospace; overflow: auto; color: #000000; clear: right; margin-bottom: 6px !important; padding: 5px 10px 5px 3px !important; background-color: #f7f7f7 !important;">% jstat -compiler 6006 CompiledFailedInvalid TimeFailedTypeFailedMethod 206 0 0 1.97 0</pre></div><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">注意，这里也列出了编译失败的方法的个数信息，以及编译失败的最后一个方法的名称。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">另一种选择，您可以使用-printcompilation 选项得到最后一个被编译的方法的编译信息。因为 jstat 命令有一个参数选项用来重复其操作，您可以观察每一次方法被编译的情况。举个例子：</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">Jstat 对 6006 号 ID 进程每 1000 毫秒执行一次： %jstat &#8211;printcompilation 6006 1000，具体的输出信息在此不再描述。</p><p ibm-back-to-top"="" style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><a target="_blank" href="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/#ibm-pcon" style="text-decoration: none; color: #745285; margin: 0px; padding: 0px 0px 0px 16px; border-width: 0px; border-bottom-style: none; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; display: inline; line-height: 1.065em; font-weight: bold;">回页首</a></p><h2><a name="t15" target="_blank" style="color: rgb(12, 137, 207);"></a>高级编译器调优</h2><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">这一节我们将介绍编译工作剩下的细节，并且过程中我们会探讨一些额外的调优策略。调优的存在很大程度上帮助了 JVM 工程师诊断 JVM 自身的行为。如果您对编译器的工作原理很感兴趣，这一节您一定会喜欢。</p><h3><a name="t16" target="_blank" style="color: rgb(12, 137, 207);"></a>编译线程</h3><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">从前文中我们知道，当一个方法（或循环）拥有编译资格时，它就会排队并等待编译。这个队列是由一个或很多个后台线程组成。这也就是说编译是一个异步的过程。它允许程序在代码正在编译时被继续执行。如果一个方法被标准编译方式所编译，那么下一个方法调用则会执行已编译的方法。如果一个循环被栈上替换方式所编译，那么下一次循环迭代则会执行新编译的代码。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">这些队列并不会严格的遵守先进先出原则：哪一个方法的调用计数器计数更高，哪一个就拥有优先权。所以即使当一个程序开始执行，并且有大量的代码需要编译，这个优先权顺序将帮助并保证最重要的代码被优先编译（这也是为什么编译 ID 在 PrintComilation 的输出结果中有时会失序的另一个原因）。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">当使用 client 编译器时，JVM 启动一个编译线程，而 server 编译器有两个这样的线程。当分层编译生效时，JVM 会基于某些复杂方程式默认启动多个 client 和 server 线程，涉及双日志在目标平台上的 CPU 数量。如下图所示：</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">分层编译下 C1 和 C2 编译器线程默认数量：</p><h5><a name="t17" target="_blank" style="color: rgb(12, 137, 207);"></a>图 3. C1 和 C2 编译器默认数量</h5><img alt="图 3. C1 C2 编译器默认数量" src="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/img003.png" width="435" style="border: none; max-width: 100%; max-height: 100%; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;" /><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">编译器线程的数量可以通过-XX:CICompilerCount=N flag 进行调节设置。这个数量是 JVM 将要执行队列所用的线程总数。对于分层编译，三分之一的（至少一个）线程被用于执行 client 编译器队列，剩下的（也是至少一个）被用来执行 server 编译器队列。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">在何时我们应该考虑调整这个值呢？如果一个程序被运行在单 CPU 机器上，那么只有一个编译线程会更好一些：因为对于某个线程来说，其对 CPU 的使用是有限的，并且在很多情况下越少的线程竞争资源会使其运行性能更高。然而，这个优势仅仅局限于初始预热阶段，之后，这些具有编译资格的方法并不会真的引起 CPU 争用。当一个股票批处理应用程序运行在单 CPU 机器上并且编译器线程被限制成只有一个，那么最初的计算过程将比一般情况下快 10%（因为它没有被其他线程进行 CPU 争用）。迭代运行的次数越多，最初的性能收益就相对越少，直到所有的热点方法被编译完性能收益也随之终止。</p><p ibm-back-to-top"="" style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><a target="_blank" href="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/#ibm-pcon" style="text-decoration: none; color: #745285; margin: 0px; padding: 0px 0px 0px 16px; border-width: 0px; border-bottom-style: none; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; display: inline; line-height: 1.065em; font-weight: bold;">回页首</a></p><h2><a name="t18" target="_blank" style="color: rgb(12, 137, 207);"></a>结束语</h2><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">本文详细介绍了 JIT 编译器的工作原理。从优化的角度讲，最简单的选择就是使用 server 编译器的分层编译技术，这将解决大约 90%左右的与编译器直接相关的性能问题。最后，请保证代码缓存的大小设置的足够大，这样编译器将会提供最高的编译性能。</p><p style="margin: 0px; padding: 0px; color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">转载自<a target="_blank" href="https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/" style="text-decoration: none; color: #0c89cf;">点击打开链接</a></p><img src ="http://www.blogjava.net/xiaomage234/aggbug/432592.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2017-06-08 17:27 <a href="http://www.blogjava.net/xiaomage234/archive/2017/06/08/432592.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈对JIT编译器的理解</title><link>http://www.blogjava.net/xiaomage234/archive/2017/06/08/432591.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 08 Jun 2017 09:26:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2017/06/08/432591.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/432591.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2017/06/08/432591.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/432591.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/432591.html</trackback:ping><description><![CDATA[<h1>from:http://www.cnblogs.com/insistence/p/5901457.html<br /><br />1. 什么是Just In Time编译器?</h1><h2>Hot Spot 编译</h2><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">当 JVM 执行代码时，它并不立即开始编译代码。这主要有两个原因：</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">首先，如果这段代码本身在将来只会被执行一次，那么从本质上看，编译就是在浪费精力。因为将代码翻译成 java 字节码相对于编译这段代码并执行代码来说，要快很多。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">当 然，如果一段代码频繁的调用方法，或是一个循环，也就是这段代码被多次执行，那么编译就非常值得了。因此，编译器具有的这种权衡能力会首先执行解释后的代 码，然后再去分辨哪些方法会被频繁调用来保证其本身的编译。其实说简单点，就是 JIT 在起作用，我们知道，对于 Java 代码，刚开始都是被编译器编译成字节码文件，然后字节码文件会被交由 JVM 解释执行，所以可以说 Java 本身是一种半编译半解释执行的语言。Hot Spot VM 采用了 JIT compile 技术，将运行频率很高的字节码直接编译为机器指令执行以提高性能，所以当字节码被 JIT 编译为机器码的时候，要说它是编译执行的也可以。也就是说，运行时，部分代码可能由 JIT 翻译为目标机器指令（以 method 为翻译单位，还会保存起来，第二次执行就不用翻译了）直接执行。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">第二个原因是最优化，当 JVM 执行某一方法或遍历循环的次数越多，就会更加了解代码结构，那么 JVM 在编译代码的时候就做出相应的优化。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">我 们将在后面讲解这些优化策略，这里，先举一个简单的例子：我们知道 equals() 这个方法存在于每一个 Java Object 中（因为是从 Object class 继承而来）而且经常被覆写。当解释器遇到 b = obj1.equals(obj2) 这样一句代码，它则会查询 obj1 的类型从而得知到底运行哪一个 equals() 方法。而这个动态查询的过程从某种程度上说是很耗时的。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">在主流商用JVM（HotSpot、J9）中，Java程序一开始是通过<strong style="margin: 0px; padding: 0px;">解释器（Interpreter）</strong>进行<strong style="margin: 0px; padding: 0px;">解释执行</strong>的。当JVM发现某个方法或代码块运行特别频繁时，就会把这些代码认定为&#8220;<strong style="margin: 0px; padding: 0px;">热点代码（Hot Spot Code）</strong>&#8221;，然后JVM会把这些代码编译成与本地平台相关的机器码，并进行各种层次的优化，完成这个任务的编译器称为：即时编译器（Just In Time Compiler，JIT）</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">JIT编译器是&#8220;动态编译器&#8221;的一种，相对的&#8220;静态编译器&#8221;则是指的比如：C/C++的编译器</p><blockquote style="margin: 0px; background-color: #ffffff; border-top: none; border-right: none; border-bottom: none; border-left-width: 5px; border-left-color: #dddddd; font-family: Verdana, Arial, Helvetica, sans-serif;"><p style="margin: 0px; padding: 0px; color: #777777;">JIT并不是JVM的必须部分，JVM规范并没有规定JIT必须存在，更没有限定和指导JIT。但是，JIT性能的好坏、代码优化程度的高低却是衡量一款JVM是否优秀的最关键指标之一，也是虚拟机中最核心且最能体现虚拟机技术水平的部分。</p></blockquote><hr style="margin: 0px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;" /><h1>2. 编译器与解释器</h1><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">首先，不是所有JVM都采用编译器和解释器并存的架构，但主流商用虚拟机，都同时包含这两部分。</p><h2>2.1 配合过程</h2><ol style="margin: 0px 0px 1em 40px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">当程序需要迅速启动然后执行的时候，<strong style="margin: 0px; padding: 0px;">解释器可以首先发挥作用</strong>，编译器不运行从而省去编译时间，立即执行程序</p></li><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">在程序运行后，随着时间的推移，<strong style="margin: 0px; padding: 0px;">编译器逐渐发挥作用</strong>，把越来越多的代码编译成本地代码之后，可以获得更高的执行效率</p></li><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">当程序运行环境中内存资源限制较大（如部分嵌入式系统中），可以使用解释执行来节约内存；反之，则可以使用编译执行来提升效率。</p></li><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">同时，解释器还可以作为编译器（C2才会激进优化）激进优化时的一个&#8220;<strong style="margin: 0px; padding: 0px;">逃生门</strong>&#8221;，让编译器根据概率选择一些大多数时候都能提升运行速度的优化手段，当激进优化假设不成立。如：加载了新类后，类型继承结构出现变化，出现&#8220;罕见陷阱（Uncommon Trap）&#8221;时，可以通过逆优化（Deoptimization）<strong style="margin: 0px; padding: 0px;">退回到解释状态</strong>继续执行&nbsp;<br style="margin: 0px; padding: 0px;" /><em style="margin: 0px; padding: 0px;">(部分没有解释器的虚拟机，也会采用不进行激进优化的C1编译器担任&#8220;逃生门&#8221;的角色)</em></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><img title="" src="http://img.blog.csdn.net/20160324193842966" alt="这里写图片描述" style="margin: 0px; padding: 0px; border: none; max-width: 800px;" /></p></li></ol><h2>2.2 解释器 - Interpreter</h2><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">Interpreter解释执行class文件，好像JavaScript执行引擎一样</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">特殊的例子：</p><ul style="margin: 0px 0px 1em 40px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 0px; list-style-type: disc;">最早的Sun Classic VM只有Interpreter</li><li style="margin: 0px; padding: 0px; list-style-type: disc;">BEA JRockit VM则只有Compiler，但它主要面向服务端应用，部署在其上的应用不重点关注启动时间</li></ul><h2>2.3 编译器 - Compiler</h2><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">只说HotSpot JVM</p><h3>1. C1和C2：</h3><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">HotSpot虚拟机内置了两个即时编译器，分别称为Client Compiler和Server Compiler，习惯上将前者称为C1，后者称为C2</p><h3>2. 使用C1还是C2？</h3><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">HotSpot默认采用解释器和其中<strong style="margin: 0px; padding: 0px;">一个</strong>编译器直接配合的方式工作，使用那个编译器取决于虚拟机<strong style="margin: 0px; padding: 0px;">运行的模式</strong>，HotSpot会根据自身版本和宿主机器硬件性能自动选择模式，用户也可以使用&#8220;-client&#8221;或&#8221;-server&#8221;参数去指定</p><ol style="margin: 0px 0px 1em 40px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">混合模式（Mixed Mode）&nbsp;<br style="margin: 0px; padding: 0px;" /><strong style="margin: 0px; padding: 0px;">默认的模式</strong>，如上面描述的这种方式就是mixed mode</p></li><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">解释模式（Interpreted Mode）&nbsp;<br style="margin: 0px; padding: 0px;" />可以使用参数&#8220;-Xint&#8221;，在此模式下<strong style="margin: 0px; padding: 0px;">全部代码解释执行</strong></p></li><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">编译模式（Compiled Mode）&nbsp;<br style="margin: 0px; padding: 0px;" />参数&#8220;-Xcomp&#8221;，此模式<strong style="margin: 0px; padding: 0px;">优先</strong>采用编译，但是<strong style="margin: 0px; padding: 0px;">无法编译时也会解释</strong>（在最新的HotSpot中此参数被取消）</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">可以看到，我的JVM现在是mixed mode&nbsp;<br style="margin: 0px; padding: 0px;" /><img title="" src="http://img.blog.csdn.net/20160324194757071" alt="这里写图片描述" style="margin: 0px; padding: 0px; border: none; max-width: 800px;" /></p></li></ol><h3><strong style="margin: 0px; padding: 0px;">重要：&#8595;</strong></h3><blockquote style="margin: 0px; background-color: #ffffff; border-top: none; border-right: none; border-bottom: none; border-left-width: 5px; border-left-color: #dddddd; font-family: Verdana, Arial, Helvetica, sans-serif;"><p style="margin: 0px; padding: 0px; color: #777777;">在JDK1.7（1.7仅包括Server模式）之后，HotSpot就不是默认&#8220;采用解释器和其中<strong style="margin: 0px; padding: 0px;">一个</strong>编译器&#8221;配合的方式了，而是采用了分层编译，分层编译时C1和C2有可能同时工作</p></blockquote><hr style="margin: 0px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;" /><h1>3. 分层编译</h1><h2>3.1 为什么要分层编译？</h2><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">由于编译器compile本地代码需要占用程序时间，要编译出优化程度更高的代码所花费的时间可能更长，且此时解释器还要替编译器收集性能监控信息，这对解释执行的速度也有影响</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">所以，为了在程序启动响应时间与运行效率之间达到最佳平衡，HotSpot在JDK1.6中出现了分层编译（Tiered Compilation）的概念并在JDK1.7的Server模式JVM中作为默认策略被开启</p><h2>3.2 编译层 tier（或者叫级别）</h2><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">分层编译根据编译器编译、优化的规模与耗时，划分了不同的编译层次（不只以下3种），包括：</p><ul style="margin: 0px 0px 1em 40px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">第0层，程序解释执行（<strong style="margin: 0px; padding: 0px;">没有编译</strong>），解释器不开启性能监控功能，可触发第1层编译。</p></li><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">第1层，也称<strong style="margin: 0px; padding: 0px;">C1编译</strong>，将字节码编译为本地代码，进行简单、可靠的优化，如有必要将加入性能监控的逻辑</p></li><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">第2层（或2层以上），也称为<strong style="margin: 0px; padding: 0px;">C2编译</strong>，也是将字节码编译为本地代码，但是会启用一些编译耗时较长的优化，<strong style="margin: 0px; padding: 0px;">甚至会</strong>根据性能监控信息进行一些不可靠的<strong style="margin: 0px; padding: 0px;">激进优化</strong></p></li></ul><blockquote style="margin: 0px; background-color: #ffffff; border-top: none; border-right: none; border-bottom: none; border-left-width: 5px; border-left-color: #dddddd; font-family: Verdana, Arial, Helvetica, sans-serif;"><p style="margin: 0px; padding: 0px; color: #777777;">实施分层编译后，C1和C2将会同时工作，许多代码会被<strong style="margin: 0px; padding: 0px;">多次编译</strong>，用C1获取更高的编译速度，用C2来获取更好的编译质量，且在解释执行的时候解释器也无须再承担收集性能监控信息的任务</p></blockquote><hr style="margin: 0px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;" /><h1>4. 编译对象与触发条件</h1><h2>1. 谁被编译了？</h2><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">编译对象就是之前说的&#8220;热点代码&#8221;，它有两类：</p><ol style="margin: 0px 0px 1em 40px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 0px; list-style-type: decimal;">被多次调用的<strong style="margin: 0px; padding: 0px;">方法</strong>&nbsp;<br style="margin: 0px; padding: 0px;" /><ul style="margin: 0px 0px 1em 40px; padding: 0px;"><li style="margin: 0px; padding: 0px; list-style-type: disc;">一个方法被多次调用，理应称为热点代码，这种编译也是虚拟机中标准的JIT编译方式</li></ul></li><li style="margin: 0px; padding: 0px; list-style-type: decimal;">被多次执行的<strong style="margin: 0px; padding: 0px;">循环体</strong>&nbsp;<br style="margin: 0px; padding: 0px;" /><ul style="margin: 0px 0px 1em 40px; padding: 0px;"><li style="margin: 0px; padding: 0px; list-style-type: disc;">编译动作由循环体出发，但编译对象<strong style="margin: 0px; padding: 0px;">依然会以整个方法为对象</strong>；</li><li style="margin: 0px; padding: 0px; list-style-type: disc;">这种编译方式由于<strong style="margin: 0px; padding: 0px;">编译发生在方法执行过程中</strong>，因此形象的称为：栈上替换（On Stack Replacement-&nbsp;<strong style="margin: 0px; padding: 0px;">OSR编译</strong>，即方法栈帧还在栈上，方法就被替换了）</li></ul></li></ol><h2>2. 触发条件</h2><h3>1. 综述</h3><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">上面的方法和循环体都说&#8220;多次&#8221;，那么多少算多？换个说法就是编译的触发条件。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">判断一段代码是不是热点代码，是不是需要触发JIT编译，这样的行为称为：热点探测（Hot Spot Detection），有几种主流的探测方式：</p><ol style="margin: 0px 0px 1em 40px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">基于计数器的热点探测（Counter Based Hot Spot Detection）&nbsp;<br style="margin: 0px; padding: 0px;" />虚拟机会为每个方法（或每个代码块）建立计数器，统计执行次数，如果超过阀值那么就是热点代码。缺点是维护计数器开销。</p></li><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">基于采样的热点探测（Sample Based Hot Spot Detection）&nbsp;<br style="margin: 0px; padding: 0px;" />虚拟机会周期性检查各个线程的栈顶，如果某个方法经常出现在栈顶，那么就是热点代码。缺点是不精确。</p></li><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">基于踪迹的热点探测（Trace Based Hot Spot Detection）&nbsp;<br style="margin: 0px; padding: 0px;" />Dalvik中的JIT编译器使用这种方式</p></li></ol><h3>2. HotSpot</h3><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">HotSpot使用的是第1种，因此它为每个方法准备了两类计数器：方法调用计数器（Invocation Counter）和回边计数器（Back Edge Counter）</p><ol style="margin: 0px 0px 1em 40px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">方法计数器</p><ul style="margin: 0px 0px 1em 40px; padding: 0px;"><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">默认阀值，在Client模式下是1500次，Server是10000次，可以通过参数&#8220;-XX:CompileThreshold&#8221;来设定</p></li><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">当一个方法被调用时会首先检查是否存在被JIT编译过得版本，如果存在则使用此本地代码来执行；如果不存在，则将方法计数器+1，然后判断&#8220;方法计数器和回边计数器之和&#8221;是否超过阀值，如果是则会向编译器提交一个方法编译请求</p></li><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">默认情况下，执行引擎并不会同步等待上面的编译完成，而是会继续解释执行。当编译完成后，此方法的调用入口地址会被系统自动改写为新的本地代码地址</p></li><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">还有一点，热度是会衰减的，也就是说不是仅仅+，也会-，热度衰减动作是在虚拟机的GC执行时顺便进行的</p></li></ul></li><li style="margin: 0px; padding: 0px; list-style-type: decimal;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">回边计数器</p><ul style="margin: 0px 0px 1em 40px; padding: 0px;"><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">回边，顾名思义，只有执行到大括号&#8221;}&#8221;时才算+1</p></li><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">默认阀值，Client下13995，Server下10700</p></li><li style="margin: 0px; padding: 0px; list-style-type: disc;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">它的调用逻辑和方法计数器差不多，只不过遇到回边指令时+1、超过阀值时会提交OSR编译请求以及这里没有热度衰减</p></li></ul></li></ol><hr style="margin: 0px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;" /><h1>5. 编译过程</h1><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">编译过程是在后台线程（daemon）中完成的，可以通过参数&#8220;-XX:-BackgroundCompilation&#8221;来禁止后台编译，但此时执行线程就会同步等待编译完成才会执行程序</p><ol style="margin: 0px 0px 1em 40px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 0px; list-style-type: decimal;">Client Compiler&nbsp;<br style="margin: 0px; padding: 0px;" />C1编译器是一个简单快速的三段式编译器，主要关注&#8220;局部性能优化&#8221;，放弃许多耗时较长的全局优化手段&nbsp;<br style="margin: 0px; padding: 0px;" />过程：class -&gt; 1. 高级中间代码 -&gt; 2. 低级中间代码 -&gt; 3. 机器代码</li><li style="margin: 0px; padding: 0px; list-style-type: decimal;">Server Compiler&nbsp;<br style="margin: 0px; padding: 0px;" />C2是专门面向服务器应用的编译器，是一个充分优化过的高级编译器，几乎能达到GNU C++编译器使用-O2参数时的优化强度。</li></ol><blockquote style="margin: 0px; background-color: #ffffff; border-top: none; border-right: none; border-bottom: none; border-left-width: 5px; border-left-color: #dddddd; font-family: Verdana, Arial, Helvetica, sans-serif;"><p style="margin: 0px; padding: 0px; color: #777777;">使用参数&#8220;-XX:+PrintCompilation&#8221;会让虚拟机在JIT时把方法名称打印出来，如图：&nbsp;<br style="margin: 0px; padding: 0px;" /><img title="" src="http://img.blog.csdn.net/20160325165430857" alt="这里写图片描述" style="margin: 0px; padding: 0px; border: none; max-width: 800px;" /></p></blockquote><hr style="margin: 0px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;" /><h1>6. Java和C/C++的编译器对比</h1><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">这里不是比Java和C/C++谁快这种大坑问题，只是比较编译器（我认为开发效率上Java快，执行效率上C/C++快）</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px; color: #333333; font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff;">这种对比代表了经典的即时编译器与静态编译期的对比，其实总体来说Java编译器有优有劣。主要就是动态编译时间压力大能做的优化少，还要做一些动态校验。而静态编译器无法实现一些开发上很有用的动态特性</p><img src ="http://www.blogjava.net/xiaomage234/aggbug/432591.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2017-06-08 17:26 <a href="http://www.blogjava.net/xiaomage234/archive/2017/06/08/432591.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java JIT性能调优【转】</title><link>http://www.blogjava.net/xiaomage234/archive/2017/06/08/432590.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 08 Jun 2017 08:48:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2017/06/08/432590.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/432590.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2017/06/08/432590.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/432590.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/432590.html</trackback:ping><description><![CDATA[from:http://www.tuicool.com/articles/r6Z7vaj<br /><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">JVM自动监控这所有方法的执行，如果某个方法是热点方法，JVM就计划把该方法的字节码代码编译成本地机器代码，编译成机器代码的过程是在独立线程中执行的，不会影响程序的执行，这个过程就是JIT（just in time）。</p><h2>JIT针对下面的几种方式进行优化</h2><ul style="padding: 0px; margin: 0px 0px 0.75em 25px; list-style-type: none; font-size: 16px; line-height: 1.7em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;"><li style="line-height: 1.7em; list-style-type: disc;">把bytecode编译成本地代码</li><li style="line-height: 1.7em; list-style-type: disc;">单态调度（monomorphic dispatch），当个对象的类和其父类间有方法重写时，JVM调用对象的方法可以通过对象的类型路径来判断应该调用父类的方法还是子类的方法，对此JIT进行优化，这种优化是C++所不具备的，C++中需要查找虚函数表。</li><li style="line-height: 1.7em; list-style-type: disc;">循环展开（loop unrolling）</li><li style="line-height: 1.7em; list-style-type: disc;">类型锐化</li><li style="line-height: 1.7em; list-style-type: disc;">逃逸分析（escape analysis）</li><li style="line-height: 1.7em; list-style-type: disc;">移除无用代码（这个现在IDE会提示我们的，比如：intellij idea）</li><li style="line-height: 1.7em; list-style-type: disc;">Intrinsics</li><li style="line-height: 1.7em; list-style-type: disc;">分支预测</li><li style="line-height: 1.7em; list-style-type: disc;">方法内联（inlining，对性能的提升很大），默认情况，&lt;= 35字节码的方法可以进行内联，通过这个来修改内联方法的最大值：-XX:MaxInlineSize=，通过-XX:FreqInlineSize=来设置频繁调用方法的临界值</li></ul><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">这些优化方法通常是层层依赖的，所以当JIT优化后的代码被JVM应用，就会开始尝试进行更上一层次的优化。因此我们写代码的时候，应该尽量往这些优化方式上面靠。</p><h2>输出JIT编译过的方法</h2><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">在JVM启动参数中添加如下的启动参数：</p><pre less"="" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: #444444; border-radius: 4px; margin-top: 0px; margin-bottom: 0.75em; line-height: 1.5em; word-break: break-all; word-wrap: break-word; background-color: #f6f6f6; border: none; overflow-x: auto;"><span style="color: #333333; font-weight: 700;">-XX</span>:+PrintCompilation </pre><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">输出内容类似这样：</p><pre css"="" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: #444444; border-radius: 4px; margin-top: 0px; margin-bottom: 0.75em; line-height: 1.5em; word-break: break-all; word-wrap: break-word; background-color: #f6f6f6; border: none; overflow-x: auto;">31&nbsp;&nbsp;&nbsp;&nbsp;23 <span style="color: #333333; font-weight: 700;">s</span>!&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #333333; font-weight: 700;">sun</span><span style="color: #880000;">.misc</span><span style="color: #880000;">.URLClassPath</span><span style="color: #bc6060;">::getLoader</span> (136 <span style="color: #333333; font-weight: 700;">bytes</span>) </pre><ul style="padding: 0px; margin: 0px 0px 0.75em 25px; list-style-type: none; font-size: 16px; line-height: 1.7em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;"><li style="line-height: 1.7em; list-style-type: disc;">第1列 &nbsp;31：为JVM启动后到该方法被编译相隔的时间，单位为毫秒</li><li style="line-height: 1.7em; list-style-type: disc;">第2列 &nbsp;23：编译ID，用来跟踪一个方法的编译、优化、深度优化</li><li style="line-height: 1.7em; list-style-type: disc;">第3列 &nbsp;s!：s是指该方法是synchronized，感叹号是指该方法有对异常的处理</li><li style="line-height: 1.7em; list-style-type: disc;">第4列 &nbsp;sun.misc.URLClassPath::getLoader：被编译的方法</li><li style="line-height: 1.7em; list-style-type: disc;">第5列 &nbsp;(136 bytes)：方法的字节大小</li></ul><h2>输出JIT编译的细节信息</h2><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">通过添加参数-XX:+PrintCompilation，可以看到的信息其实并不具体，比如：那些方法进行了内联，内联后的二进制代码是怎么样的都没有。而要输出JIT编译的细节信息，就需要在JVM启动参数中添加这个参数：</p><pre hljs=""  less"="" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: #444444; border-radius: 4px; margin-top: 0px; margin-bottom: 1.5em; line-height: 1.5em; word-break: break-all; word-wrap: break-word; background-color: #f6f6f6; border: none; overflow-x: auto;"><span style="color: #333333; font-weight: 700;">-XX</span>:+LogCompilation <span style="color: #333333; font-weight: 700;">-XX</span>:+UnlockDiagnosticVMOptions <span style="color: #333333; font-weight: 700;">-XX</span>:+TraceClassLoading <span style="color: #333333; font-weight: 700;">-XX</span>:+PrintAssembly </pre><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">输出的编译信息，默认情况是在启动JVM的目录下一个名为：hotspot_pid&lt;PID&gt;.log的文件</p><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">如果想指定文件路径和文件名的话，可以再添加一个启动参数：</p><pre yaml"="" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: #444444; border-radius: 4px; margin-top: 0px; margin-bottom: 0.75em; line-height: 1.5em; word-break: break-all; word-wrap: break-word; background-color: #f6f6f6; border: none; overflow-x: auto;">-XX:LogFile=&lt;pathto file<span style="color: #880000;">&gt; </span></pre><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">输出的是一个很大的xml文件，可能有几百兆，内容大致如下：</p><pre hljs=""  vim"="" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: #444444; border-radius: 4px; margin-top: 0px; margin-bottom: 1.5em; line-height: 1.5em; word-break: break-all; word-wrap: break-word; background-color: #f6f6f6; border: none; overflow-x: auto;">&lt;nmethodcompile_id=<span style="color: #880000;">'2'</span> <span style="color: #333333; font-weight: 700;">compiler</span>=<span style="color: #880000;">'C1'</span> level=<span style="color: #880000;">'3'</span>  entry=<span style="color: #880000;">'0x00000001023fe240'</span> size=<span style="color: #880000;">'1224'</span>  address=<span style="color: #880000;">'0x00000001023fe0d0'</span> relocation_offset=<span style="color: #880000;">'288'</span>  insts_offset=<span style="color: #880000;">'368'</span> stub_offset=<span style="color: #880000;">'880'</span> scopes_data_offset=<span style="color: #880000;">'1032'</span>  scopes_pcs_offset=<span style="color: #880000;">'1104'</span> dependencies_offset=<span style="color: #880000;">'1200'</span>  nul_chk_table_offset=<span style="color: #880000;">'1208'</span>  method=<span style="color: #880000;">'java/lang/String hashCode ()I'</span> bytes=<span style="color: #880000;">'55'</span> <span style="color: #397300;">count</span>=<span style="color: #880000;">'512'</span>  backedge_count=<span style="color: #880000;">'8218'</span> iicount=<span style="color: #880000;">'512'</span> stamp=<span style="color: #880000;">'0.350'</span>/&gt; </pre><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">而且内容很难读懂，建议使用JITWatch（&nbsp;<a href="https://github.com/AdoptOpenJDK/jitwatch/" rel="nofollow,noindex" target="_blank" style="color: #949494; text-decoration: none; transition: 0.25s; outline: none 0px; border-bottom: 1px dashed #949494; font-style: italic; font-weight: bold;">https://github.com/AdoptOpenJDK/jitwatch/</a>&nbsp;）的可视化界面来查看JIT编译的细节信息。同时JITWatch还可以给出很多优化建议，给我们有效的优化代码提供参考，详见下文。</p><h2>JIT编译模式</h2><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">C1: 通常用于那种快速启动的GUI应用，对应启动参数：－client</p><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">C2: 通常用于长时间允许的服务端应用，对应启动参数：－server</p><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">分层编译模式（tiered compilation）：这是自从Java SE 7以后的新特性，可通过添加启动参数来开启：</p><pre less"="" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: #444444; border-radius: 4px; margin-top: 0px; margin-bottom: 0.75em; line-height: 1.5em; word-break: break-all; word-wrap: break-word; background-color: #f6f6f6; border: none; overflow-x: auto;"><span style="color: #333333; font-weight: 700;">-XX</span>:+TieredCompilation </pre><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">这个特性在应用启动阶段使用C1模式以达到快速启动的效果，一旦应用程序运行起来以后，C2模式将取代C1模式，以进行更深度的优化。在Java SE 8中，这个特性是默认的。</p><h2>JITWatch</h2><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">前面也提到了，JITWatch可以通过可视化界面来帮助我们分析JVM输出的JIT编译输出日志，还可以帮助我们静态分析jar中的代码是否符合JIT编译优化的条件，还可以以曲线图形的方式展示JIT编译的整个过程中的一些指标，非常好用的工具。</p><h3>下载</h3><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">JITWatch需要在github上把代码clone下来，然后用maven来运行，地址为：&nbsp;<a href="https://github.com/AdoptOpenJDK/jitwatch/" rel="nofollow,noindex" target="_blank" style="color: #949494; text-decoration: none; transition: 0.25s; outline: none 0px; border-bottom: 1px dashed #949494; font-style: italic; font-weight: bold;">https://github.com/AdoptOpenJDK/jitwatch/</a></p><h3>运行JITWwatch</h3><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">在代码根目录下执行&nbsp;<code style="padding: 2px 4px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 3px; background-color: #f7f7f9; border: none; font-weight: 600; font-size: 14px;">launchUI.sh（</code>&nbsp;Linux/Mac）或则&nbsp;<code style="padding: 2px 4px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; border-radius: 3px; background-color: #f7f7f9; border: none; font-weight: 600; font-size: 14px;">launchUI.bat（windows）</code></p><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">如果你使用maven，也可以在代码根目录下这样运行（其他运行方式，请参考JITWatch的github首页）</p><pre less"="" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: #444444; border-radius: 4px; margin-top: 0px; margin-bottom: 0.75em; line-height: 1.5em; word-break: break-all; word-wrap: break-word; background-color: #f6f6f6; border: none; overflow-x: auto;"><span style="color: #333333; font-weight: 700;">mvncleancompileexec</span>:java </pre><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">如果你使用的是mac，而且idk版本是jdk7，且运行mvn clean compile exec:java时出现下面的错误和异常时：&nbsp;</p><pre hljs=""  css"="" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: #444444; border-radius: 4px; margin-top: 0px; margin-bottom: 1.5em; line-height: 1.5em; word-break: break-all; word-wrap: break-word; background-color: #f6f6f6; border: none; overflow-x: auto;"><span style="color: #333333; font-weight: 700;">Causedby</span>: <span style="color: #333333; font-weight: 700;">java</span><span style="color: #880000;">.lang</span><span style="color: #880000;">.NullPointerException</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.t2k</span><span style="color: #880000;">.MacFontFinder</span><span style="color: #880000;">.initPSFontNameToPathMap</span>(<span style="color: #333333; font-weight: 700;">MacFontFinder</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:339)</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.t2k</span><span style="color: #880000;">.MacFontFinder</span><span style="color: #880000;">.getFontNamesOfFontFamily</span>(<span style="color: #333333; font-weight: 700;">MacFontFinder</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:390)</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.t2k</span><span style="color: #880000;">.T2KFontFactory</span><span style="color: #880000;">.getFontResource</span>(<span style="color: #333333; font-weight: 700;">T2KFontFactory</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:233)</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.t2k</span><span style="color: #880000;">.LogicalFont</span><span style="color: #880000;">.getSlot0Resource</span>(<span style="color: #333333; font-weight: 700;">LogicalFont</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:184)</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.t2k</span><span style="color: #880000;">.LogicalFont</span><span style="color: #880000;">.getSlotResource</span>(<span style="color: #333333; font-weight: 700;">LogicalFont</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:228)</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.t2k</span><span style="color: #880000;">.CompositeStrike</span><span style="color: #880000;">.getStrikeSlot</span>(<span style="color: #333333; font-weight: 700;">CompositeStrike</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:86)</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.t2k</span><span style="color: #880000;">.CompositeStrike</span><span style="color: #880000;">.getMetrics</span>(<span style="color: #333333; font-weight: 700;">CompositeStrike</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:132)</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.javafx</span><span style="color: #880000;">.font</span><span style="color: #880000;">.PrismFontUtils</span><span style="color: #880000;">.getFontMetrics</span>(<span style="color: #333333; font-weight: 700;">PrismFontUtils</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:31)</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.javafx</span><span style="color: #880000;">.font</span><span style="color: #880000;">.PrismFontLoader</span><span style="color: #880000;">.getFontMetrics</span>(<span style="color: #333333; font-weight: 700;">PrismFontLoader</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:466)</span>  <span style="color: #333333; font-weight: 700;">atjavafx</span><span style="color: #880000;">.scene</span><span style="color: #880000;">.text</span><span style="color: #880000;">.Text</span>.&lt;<span style="color: #333333; font-weight: 700;">init</span>&gt;(<span style="color: #333333; font-weight: 700;">Text</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:153)</span>  <span style="color: #333333; font-weight: 700;">atcom</span><span style="color: #880000;">.sun</span><span style="color: #880000;">.javafx</span><span style="color: #880000;">.scene</span><span style="color: #880000;">.control</span><span style="color: #880000;">.skin</span><span style="color: #880000;">.Utils</span>.&lt;<span style="color: #333333; font-weight: 700;">clinit</span>&gt;(<span style="color: #333333; font-weight: 700;">Utils</span><span style="color: #880000;">.java</span><span style="color: #bc6060;">:52)</span>  ... 13 <span style="color: #333333; font-weight: 700;">more</span> &nbsp; <span style="color: #bc6060;">[ERROR]</span> <span style="color: #333333; font-weight: 700;">Failedto</span> <span style="color: #333333; font-weight: 700;">executegoalorg</span><span style="color: #880000;">.codehaus</span><span style="color: #880000;">.mojo</span><span style="color: #bc6060;">:exec-maven-plugin</span><span style="color: #bc6060;">:1.5.0</span><span style="color: #bc6060;">:java</span> (<span style="color: #333333; font-weight: 700;">default-cli</span>) <span style="color: #333333; font-weight: 700;">onprojectjitwatch-ui</span>: <span style="color: #333333; font-weight: 700;">Anexceptionoccuredwhile</span> <span style="color: #333333; font-weight: 700;">executingtheJavaclass</span>. <span style="color: #333333; font-weight: 700;">null</span>: <span style="color: #333333; font-weight: 700;">InvocationTargetException</span>: <span style="color: #333333; font-weight: 700;">Exceptionin</span> <span style="color: #333333; font-weight: 700;">Applicationstartmethod</span>: <span style="color: #333333; font-weight: 700;">ExceptionInInitializerError</span>: <span style="color: #333333; font-weight: 700;">NullPointerException</span> <span style="color: #333333; font-weight: 700;">-</span>&gt; <span style="color: #bc6060;">[Help 1]</span> </pre><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">请在org.adoptopenjdk.jitwatch.launch.LaunchUI类的main函数开头处添加下面的代码（或者直接使用我fork修改好的&nbsp;<a href="https://github.com/dongritengfei/jitwatch" target="_blank" rel="nofollow,noindex" style="color: #949494; text-decoration: none; transition: 0.25s; outline: none 0px; border-bottom: 1px dashed #949494; font-style: italic; font-weight: bold;">JITWatch</a>&nbsp;）：</p><pre hljs=""  dart"="" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: #444444; border-radius: 4px; margin-top: 0px; margin-bottom: 1.5em; line-height: 1.5em; word-break: break-all; word-wrap: break-word; background-color: #f6f6f6; border: none; overflow-x: auto;"><span style="color: #333333; font-weight: 700;">final</span> Class&lt;?&gt; macFontFinderClass = Class.forName(<span style="color: #880000;">"com.sun.t2k.MacFontFinder"</span>); <span style="color: #333333; font-weight: 700;">final</span> java.lang.reflect.FieldpsNameToPathMap = macFontFinderClass.getDeclaredField(<span style="color: #880000;">"psNameToPathMap"</span>); psNameToPathMap.setAccessible(<span style="color: #333333; font-weight: 700;">true</span>); <span style="color: #333333; font-weight: 700;">if</span> (psNameToPathMap.<span style="color: #333333; font-weight: 700;">get</span>(<span style="color: #333333; font-weight: 700;">null</span>) == <span style="color: #333333; font-weight: 700;">null</span>) { &nbsp;&nbsp;&nbsp;&nbsp;psNameToPathMap.<span style="color: #333333; font-weight: 700;">set</span>( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #333333; font-weight: 700;">null</span>, <span style="color: #333333; font-weight: 700;">new</span> java.util.HashMap&lt;<span style="color: #397300;">String</span>, <span style="color: #397300;">String</span>&gt;()); } <span style="color: #333333; font-weight: 700;">final</span> java.lang.reflect.FieldallAvailableFontFamilies = macFontFinderClass.getDeclaredField(<span style="color: #880000;">"allAvailableFontFamilies"</span>); allAvailableFontFamilies.setAccessible(<span style="color: #333333; font-weight: 700;">true</span>); <span style="color: #333333; font-weight: 700;">if</span> (allAvailableFontFamilies.<span style="color: #333333; font-weight: 700;">get</span>(<span style="color: #333333; font-weight: 700;">null</span>) == <span style="color: #333333; font-weight: 700;">null</span>) { &nbsp;&nbsp;&nbsp;&nbsp;allAvailableFontFamilies.<span style="color: #333333; font-weight: 700;">set</span>( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #333333; font-weight: 700;">null</span>, <span style="color: #333333; font-weight: 700;">new</span> <span style="color: #397300;">String</span>[] {}); } </pre><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">然后重新运行即可看到JITWatch的界面。</p><h2><strong>Reference</strong></h2><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;"><a href="http://www.oracle.com/technetwork/articles/java/architect-evans-pt1-2266278.html" target="_blank" rel="nofollow,noindex" style="color: #949494; text-decoration: none; transition: 0.25s; outline: none 0px; border-bottom: 1px dashed #949494; font-style: italic; font-weight: bold;">http://www.oracle.com/technetwork/articles/java/architect-evans-pt1-2266278.html</a></p><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 1.7em; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;"><a href="https://www.chrisnewland.com/images/jitwatch/HotSpot_Profiling_Using_JITWatch.pdf" target="_blank" rel="nofollow,noindex" style="color: #949494; text-decoration: none; transition: 0.25s; outline: none 0px; outline-offset: -2px; border-bottom: 1px dashed #949494; font-style: italic; font-weight: bold;">https://www.chrisnewland.com/images/jitwatch/HotSpot_Profiling_Using_JITWatch.pdf</a></p><img src ="http://www.blogjava.net/xiaomage234/aggbug/432590.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2017-06-08 16:48 <a href="http://www.blogjava.net/xiaomage234/archive/2017/06/08/432590.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在 Linux/UNIX 终端下使用 nload 实时监控网络流量和带宽使用</title><link>http://www.blogjava.net/xiaomage234/archive/2016/09/08/431780.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 08 Sep 2016 07:04:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/09/08/431780.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/431780.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/09/08/431780.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/431780.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/431780.html</trackback:ping><description><![CDATA[from:https://linux.cn/article-2871-1.html<br /><br /><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">如果你想在命令行界面监控网络吞吐量，nload 应用程序是个不错的选择。它是一个实时监控网络流量和带宽使用的控制台应用程序，使用两个图表可视化地展示接收和发送的流量，并提供诸如数据交换总量、最小/最大网络带宽使用量等附加信息。</p><a id="3_334"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h3>安装</h3><a id="4_351"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h4>在 CentOS/RHEL/Red Hat/Fedora Linux 上安装 nload</h4><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">首先在 CentOS 或者基于 RHEL 的操作系统上<a href="http://www.cyberciti.biz/faq/fedora-sl-centos-redhat6-enable-epel-repo/" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8;">启用 EPEL 仓库</a>，然后键入&nbsp;<a href="http://www.cyberciti.biz/faq/rhel-centos-fedora-linux-yum-command-howto/" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8;">yum 命令</a>安装 nload：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; font-style: italic;">#</span> <span style="word-wrap: break-word; color: #e28964;">yum</span><span style="word-wrap: break-word; color: #b8ffb8;"> install nload</span></li></ol></pre><a id="4_771"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h4>在 Debian 或者 Ubuntu Linux 上安装 nload</h4><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">键入&nbsp;<a href="http://www.cyberciti.biz/tips/linux-debian-package-management-cheat-sheet.html" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8;">apt-get 命令</a>：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ </span><span style="word-wrap: break-word; color: #e28964;">sudo</span> <span style="word-wrap: break-word; color: #e28964;">apt-get</span><span style="word-wrap: break-word; color: #b8ffb8;"> install nload</span></li></ol></pre><a id="4_1026"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h4>在 FreeBSD 操作系统上安装 nload</h4><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">通过 port 安装 nload，键入：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; font-style: italic;">#</span> <span style="word-wrap: break-word; color: #e28964;">cd</span> <span style="word-wrap: break-word; color: #b8ffb8;">/</span><span style="word-wrap: break-word; color: #b8ffb8;">usr</span><span style="word-wrap: break-word; color: #b8ffb8;">/</span><span style="word-wrap: break-word; color: #b8ffb8;">ports</span><span style="word-wrap: break-word; color: #b8ffb8;">/</span><span style="word-wrap: break-word; color: #b8ffb8;">net</span><span style="word-wrap: break-word; color: #b8ffb8;">/</span><span style="word-wrap: break-word; color: #b8ffb8;">nload</span><span style="word-wrap: break-word; color: #b8ffb8;">/</span> <span style="word-wrap: break-word; color: #b8ffb8;">&amp;</span><span style="word-wrap: break-word; color: #b8ffb8;">amp</span><span style="word-wrap: break-word; color: #b8ffb8;">;&amp;</span><span style="word-wrap: break-word; color: #b8ffb8;">amp</span><span style="word-wrap: break-word; color: #b8ffb8;">;</span> <span style="word-wrap: break-word; color: #e28964;">make</span><span style="word-wrap: break-word; color: #b8ffb8;"> install clean</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">或者添加包</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; font-style: italic;">#</span><span style="word-wrap: break-word; color: #b8ffb8;"> pkg install net</span><span style="word-wrap: break-word; color: #b8ffb8;">/</span><span style="word-wrap: break-word; color: #b8ffb8;">nload</span></li></ol></pre><a id="4_1320"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h4>在 OpenBSD 操作系统上安装 nload</h4><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">键入下列命令：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ </span><span style="word-wrap: break-word; color: #e28964;">sudo</span><span style="word-wrap: break-word; color: #b8ffb8;"> pkg_add </span><span style="word-wrap: break-word; color: #b8ffb8;">-</span><span style="word-wrap: break-word; color: #b8ffb8;">i nload</span></li></ol></pre><a id="4_1467"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h4>在类 Unix 操作系统上从源代码安装 nload</h4><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">首先，使用 wget 或者 curl 命令获取源代码：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ </span><span style="word-wrap: break-word; color: #e28964;">cd</span> <span style="word-wrap: break-word; color: #b8ffb8;">/</span><span style="word-wrap: break-word; color: #b8ffb8;">tmp</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ </span><span style="word-wrap: break-word; color: #e28964;">wget</span><span style="word-wrap: break-word; color: #b8ffb8;"> http</span><span style="word-wrap: break-word; color: #b8ffb8;">:</span><span style="word-wrap: break-word; font-style: italic;">//www.roland-riegel.de/nload/nload-0.7.4.tar.gz</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;"><a href="http://www.cyberciti.biz/faq/tar-extract-linux/" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8;">使用 tar 命令解压缩名为 nload-0.7.4.tar.gz 的 tar 包</a>，键入：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ </span><span style="word-wrap: break-word; color: #e28964;">tar</span><span style="word-wrap: break-word; color: #b8ffb8;"> xvf nload</span><span style="word-wrap: break-word; color: #b8ffb8;">-</span><span style="word-wrap: break-word; color: #3387cc;">0.7</span><span style="word-wrap: break-word; color: #b8ffb8;">.</span><span style="word-wrap: break-word; color: #3387cc;">4.tar</span><span style="word-wrap: break-word; color: #b8ffb8;">.</span><span style="word-wrap: break-word; color: #b8ffb8;">gz</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">使用 cd 命令进入 nload 源代码所在目录：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ </span><span style="word-wrap: break-word; color: #e28964;">cd</span><span style="word-wrap: break-word; color: #b8ffb8;"> nload</span><span style="word-wrap: break-word; color: #b8ffb8;">*</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">然后键入 ./configure 为你的操作系统配置安装包：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ sh </span><span style="word-wrap: break-word; color: #b8ffb8;">./</span><span style="word-wrap: break-word; color: #b8ffb8;">configure</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">或者</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ </span><span style="word-wrap: break-word; color: #b8ffb8;">./</span><span style="word-wrap: break-word; color: #b8ffb8;">configure</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">运行 configure 命令需要一点时间。完成后，使用 make 命令编译 nload：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ </span><span style="word-wrap: break-word; color: #e28964;">make</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">最后，键入 make install 命令以 root 用户身份安装 nload 应用程序和相关文件：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ </span><span style="word-wrap: break-word; color: #e28964;">sudo</span> <span style="word-wrap: break-word; color: #e28964;">make</span><span style="word-wrap: break-word; color: #b8ffb8;"> install</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">或者</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; font-style: italic;">#</span> <span style="word-wrap: break-word; color: #e28964;">make</span><span style="word-wrap: break-word; color: #b8ffb8;"> install</span></li></ol></pre><a id="3_2641"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h3>使用</h3><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">如何使用 nload 显示当前网络使用量呢？</p><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">基本语法是：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">nload</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">nload device</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">nload </span><span style="word-wrap: break-word; color: #b8ffb8;">[</span><span style="word-wrap: break-word; color: #b8ffb8;">options</span><span style="word-wrap: break-word; color: #b8ffb8;">]</span><span style="word-wrap: break-word; color: #b8ffb8;"> device1 device2</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">键入下列命令：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload eth0</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload em0 em2</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">会得到输出：</p><p style="word-wrap: break-word; margin: 0px; padding: 0px; line-height: 2em; text-align: center; font-family: Tahoma, Helvetica, SimSun, sans-serif;"><img src="https://dn-linuxcn.qbox.me/data/attachment/album/201404/18/133553oc7ggu5ho5j75goj.jpg" alt="图01: 使用 nload 命令" title="图01: 使用 nload 命令" style="word-wrap: break-word; max-width: 95%;" /></p><p style="word-wrap: break-word; margin: 0px; padding: 0px; line-height: 2em; text-align: center; font-family: Tahoma, Helvetica, SimSun, sans-serif;"><em style="word-wrap: break-word; color: #999999; min-width: 20%; min-height: 22px; display: inline-block; padding: 10px; margin: 0px auto 10px; border-bottom: 1px solid #d9d9d9; font-size: 13px; line-height: 1.7;">图01: 使用 nload 命令</em></p><a id="4_3169"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h4>操控 nload 应用程序</h4><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">nload 命令一旦执行就会开始监控网络设备，你可以使用下列快捷键操控 nload 应用程序。</p><ol style="word-wrap: break-word; margin: 1em 2em; padding: 0px; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"><li style="word-wrap: break-word; margin: 0px; padding: 0px; list-style-type: decimal; line-height: 2em;">你可以按键盘上的 &#8592; &#8594; 或者 Enter/Tab 键在设备间切换。</li><li style="word-wrap: break-word; margin: 0px; padding: 0px; list-style-type: decimal; line-height: 2em;">按 F2 显示选项窗口。</li><li style="word-wrap: break-word; margin: 0px; padding: 0px; list-style-type: decimal; line-height: 2em;">按 F5 将当前设置保存到用户配置文件。</li><li style="word-wrap: break-word; margin: 0px; padding: 0px; list-style-type: decimal; line-height: 2em;">按 F6 从配置文件重新加载设置。</li><li style="word-wrap: break-word; margin: 0px; padding: 0px; list-style-type: decimal; line-height: 2em;">按 q 或者 Ctrl+C 退出 nload。</li></ol><a id="4_3635"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h4>设置显示刷新间隔</h4><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">默认每 100 毫秒刷新一次显示数值，下面的例子将时间间隔设置成 500 毫秒：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload </span><span style="word-wrap: break-word; color: #b8ffb8;">-</span><span style="word-wrap: break-word; color: #b8ffb8;">t </span><span style="word-wrap: break-word; color: #b8ffb8;">{</span><span style="word-wrap: break-word; color: #b8ffb8;">interval_number_in_millisec</span><span style="word-wrap: break-word; color: #b8ffb8;">}</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload </span><span style="word-wrap: break-word; color: #b8ffb8;">-</span><span style="word-wrap: break-word; color: #b8ffb8;">t </span><span style="word-wrap: break-word; color: #3387cc;">500</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">输出：</p><p style="word-wrap: break-word; margin: 0px; padding: 0px; line-height: 2em; text-align: center; font-family: Tahoma, Helvetica, SimSun, sans-serif;"><img src="https://dn-linuxcn.qbox.me/data/attachment/album/201404/18/133554o1q91yym98zpmamd.gif" alt="Animated gif 01 - nload command in action" style="word-wrap: break-word; max-width: 95%;" /></p><p style="word-wrap: break-word; margin: 0px; padding: 0px; line-height: 2em; text-align: center; font-family: Tahoma, Helvetica, SimSun, sans-serif;"><em style="word-wrap: break-word; color: #999999; min-width: 20%; min-height: 22px; display: inline-block; padding: 10px; margin: 0px auto 10px; border-bottom: 1px solid #d9d9d9; font-size: 13px; line-height: 1.7;">Animated gif 01 - nload command in action</em></p><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">GIF 动画 01 - 使用 nload 命令</p><a id="4_4094"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h4>设置流量数值显示的单位</h4><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">语法如下：</p><pre linenums=""  prettyprinted"="" style="word-wrap: break-word; box-shadow: rgba(0, 0, 0, 0.498039) 0px 0px 5px; text-shadow: #000000 0px 1px 1px; border-radius: 6px; color: #b8ffb8; margin: 10px; padding: 1em 1em 1em 2em; white-space: pre-wrap; border: 1px solid #000000; line-height: 25.2px; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;); background-color: #161b20;"><ol style="word-wrap: break-word; margin: 1em 2em; padding: 1px; border-left: 2px solid #009900; color: #aeaeae; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAYAAAA4GpVBAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90GBgMqHcZ0EYUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAF0lEQVQI12NgYGAwZsAECDEWBgYGBgYACpwAazfG694AAAAASUVORK5CYII=&quot;);"><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload </span><span style="word-wrap: break-word; color: #b8ffb8;">-</span><span style="word-wrap: break-word; color: #b8ffb8;">u h</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">H</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">b</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">B</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">k</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">K</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">m</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">M</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">g</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">G</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload </span><span style="word-wrap: break-word; color: #b8ffb8;">-</span><span style="word-wrap: break-word; color: #b8ffb8;">U h</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">H</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">b</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">B</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">k</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">K</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">m</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">M</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">g</span><span style="word-wrap: break-word; color: #b8ffb8;">|</span><span style="word-wrap: break-word; color: #b8ffb8;">G</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload </span><span style="word-wrap: break-word; color: #b8ffb8;">-</span><span style="word-wrap: break-word; color: #b8ffb8;">u h</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload </span><span style="word-wrap: break-word; color: #b8ffb8;">-</span><span style="word-wrap: break-word; color: #b8ffb8;">u G</span></li><li style="word-wrap: break-word; margin: 0px; padding: 0px 0px 0px 28px; text-indent: -28px; line-height: 2em; list-style-type: decimal;"><span style="word-wrap: break-word; color: #b8ffb8;">$ nload </span><span style="word-wrap: break-word; color: #b8ffb8;">-</span><span style="word-wrap: break-word; color: #b8ffb8;">U G</span></li></ol></pre><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">释义：</p><ul style="word-wrap: break-word; margin: 1em 2em; padding: 0px; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"><li style="word-wrap: break-word; margin: 0px; padding: 0px; list-style: disc; line-height: 2em;">小写选项 -u: h 意为自动格式化为人类易读的单位，b 意为 Bit/s，k 意为 kBit/s，m 意为 MBit/s，g 意为 GBit/s。大写字母意为使用 Byte 替代 Bit。默认为 k。</li><li style="word-wrap: break-word; margin: 0px; padding: 0px; list-style: disc; line-height: 2em;">大写选项 -U 与小写选项 -u 非常相似，不同之处在于它展示的是数据量，比如 Bit, kByte, GBit 等等。（没有 "/s"）。默认值是 M。</li></ul><a id="3_4723"  ext"="" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8; text-decoration: underline; position: relative; top: -40px; display: block; height: 0px; overflow: hidden; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;"></a><h3>结论</h3><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">我觉得 nload 是一个稳定可靠的应用程序，如果你喜欢 nload，你可能也想试试 Linux 和其他类 Unix 操作系统环境下的 vnstat 与 iftop 工具。</p><hr style="word-wrap: break-word; clear: both; margin: 0px 10px; font-family: Tahoma, Helvetica, SimSun, sans-serif; line-height: 25.2px;" /><p style="word-wrap: break-word; margin: 10px 0px; padding: 0px; line-height: 2em; font-family: Tahoma, Helvetica, SimSun, sans-serif;">译自:&nbsp;<a href="http://www.cyberciti.biz/networking/nload-linux-command-to-monitor-network-traffic-bandwidth-usage/" rel="external nofollow" target="_blank" style="word-wrap: break-word; color: #4d8ad8;">http://www.cyberciti.biz/networking/nload-linux-command-to-monitor-network-traffic-bandwidth-usage/</a></p><img src ="http://www.blogjava.net/xiaomage234/aggbug/431780.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-09-08 15:04 <a href="http://www.blogjava.net/xiaomage234/archive/2016/09/08/431780.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>作为大数据工程师，你必须熟练运用的性能优化技术</title><link>http://www.blogjava.net/xiaomage234/archive/2016/09/08/431779.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 08 Sep 2016 07:03:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/09/08/431779.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/431779.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/09/08/431779.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/431779.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/431779.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: from:http://chuansong.me/n/474670251169作者介绍吴朱华：国内资深云计算和大数据专家，之前曾在IBM中国研究院和上海云人信息科技有限公司参与过多款云计算产和大数据产品的开发工作，同济本科，并曾在北京大学读过硕士。2011年中，发表业界最好的两本云计算书之一《云计算核心技术剖析》。2016年和上海华东理工大学的阮彤教授等合著了《大数据技术前沿》一书。最近几年一直参...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2016/09/08/431779.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/431779.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-09-08 15:03 <a href="http://www.blogjava.net/xiaomage234/archive/2016/09/08/431779.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java性能分析之火焰图</title><link>http://www.blogjava.net/xiaomage234/archive/2016/08/31/431715.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Wed, 31 Aug 2016 08:22:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/08/31/431715.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/431715.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/08/31/431715.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/431715.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/431715.html</trackback:ping><description><![CDATA[from:http://www.tuicool.com/articles/fMf2quA<br /><br /><br /><h2>FlameGraph</h2><p style="margin: 0px 0px 0.75em; font-size: 16px; line-height: 27.2px; text-indent: 1em; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;">火焰图&nbsp;，简单通过x轴横条宽度来度量时间指标，y轴代表线程栈的层次，简单明了， 容易找出具体的可有化点，非常方便，当然前提是我们通过profiler工具获取到profiler 数据。</p><div style="font-size: 16px; line-height: 27.2px; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;"><h3>java profiler</h3><div style="line-height: 1.7em;"><p style="margin: 0px 0px 0.75em; line-height: 1.7em; text-indent: 1em;">java性能调优时，我们经常会用到profiler工具，但是很多时候你可能不知道，大部分的 profiler工具都是有问题的&nbsp;<span style="position: relative; font-size: 12px; line-height: 0; vertical-align: baseline; top: -0.5em;">,</span>&nbsp;，简单来说，profiler：增加开销；修改了你的 代码，导致java编译器的优化行为不确定；同时影响了代码的层次，层次越深自然也影响 执行效率。</p><p style="margin: 0px 0px 0.75em; line-height: 1.7em; text-indent: 1em;">当然如果你不是通过上面方式实现，二是通过获取on-cpu线程的线程栈方式，这又会带来 一个麻烦的问题：获取系统范围的线程栈，jvm必须处于safepoint&nbsp;状态，只有当线 程处于safepoint状态的时候，别的线程才能去获取它的线程栈，而这个safepoint是由jvm 控制的，这对于profiler非常不利，有可能一个很热的代码块，jvm不会在该代码块中间放 置safepoint，导致profiler无法获得该线程栈，导致错误的profiler结果。</p><p style="margin: 0px 0px 0.75em; line-height: 1.7em; text-indent: 1em;">上面的问题几个商用的profiler工具都存在，Oracle Solaris studio利用的是jvmti的一 个非标准接口AsyncGetCallTrace来实现，不存在上面问题，Jeremy Manson也利用该接口 实现了一个简单的profiler工具：&nbsp;<a href="https://code.google.com/p/lightweight-java-profiler/wiki/GettingStarted" rel="nofollow,noindex" style="color: #949494; text-decoration: none; transition: 0.25s; outline: none 0px; border-bottom: 1px dashed #949494; font-style: italic; font-weight: bold;">Lightweight Asynchronous Sampling Profiler</a>&nbsp;，我们 的火焰图的数据来源就是通过它来获取的。</p></div></div><div style="font-size: 16px; line-height: 27.2px; color: #333333; font-family: &quot;Helvetica Neue&quot;, Helvetica, Tahoma, Arial, STXihei, &quot;Microsoft YaHei&quot;, 微软雅黑, sans-serif; background-color: #fefefe;"><h3>lightweight-java-profiler</h3><div style="line-height: 1.7em;"><p style="margin: 0px 0px 0.75em; line-height: 1.7em; text-indent: 1em;">当然，这个工具只支持hotspot的vm，需要你自己编译，有些问题需要注意：</p><ul style="padding: 0px; margin: 0px 0px 0.75em 25px; list-style-type: none; line-height: 1.7em;"><li style="line-height: 1.7em; list-style-type: disc;">如果你需要在rhel上编译，需要安装4.6以上版本gcc&nbsp;，4.4版本不支持。</li><li style="line-height: 1.7em; list-style-type: disc;">如果你需要在ubunt上编译，可能会碰到编译错误&nbsp;。</li></ul><p style="margin: 0px 0px 0.75em; line-height: 1.7em; text-indent: 1em;">编译的时候，需要主要修改BITS参数，如果你要编译64Bit，使用命令：</p><pre undefined"="" style="padding: 0.3em; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; margin-top: 0px; margin-bottom: 1.5em; font-size: 12px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; border: 1px solid rgba(0, 0, 0, 0.14902); overflow-y: auto; background-color: #f6f6f6;">make BITS=64 all</pre><p style="margin: 0px 0px 0.75em; line-height: 1.7em; text-indent: 1em;">使用方法很简单，直接在你的启动命令上添加如下参数：</p><pre ruby"="" style="padding: 0.3em; font-family: Monaco, Menlo, Consolas, &quot;Courier New&quot;, monospace; border-radius: 4px; margin-top: 0px; margin-bottom: 1.5em; font-size: 12px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; border: 1px solid rgba(0, 0, 0, 0.14902); overflow-y: auto; background-color: #f6f6f6;">-<span style="color: #990073;">agentpath:</span>path/to/liblagent.so[<span style="color: #990073;">:file=name</span>]</pre><p style="margin: 0px 0px 0.75em; line-height: 1.7em; text-indent: 1em;">启动之后，会在启动目录下生成trace.txt文件（缺省），该文件就是我们需要的采样数据。</p><p style="margin: 0px 0px 0.75em; line-height: 1.7em; text-indent: 1em;">另外有几个参数可在编译时修改，都在global.h文件中。首先是采样的频率，缺省是100次 每秒；另外是最大采样的线程栈，缺省3000，超过3000就忽略（对于复杂的应用明显不够） ；最后是栈的深度，缺省是128（对于调用层次深的应用调大）。当然你记录的东西越多， 也会有性能损耗，我调成30000+256，一刻钟生成200M文件。</p><p style="margin: 0px 0px 0.75em; line-height: 1.7em; text-indent: 1em;">另外特别需要注意，trace不是实时写入，而是在应用shutdown的时候才写入的，别kill应 用，否则trace里面什么都没有。</p></div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/431715.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-08-31 16:22 <a href="http://www.blogjava.net/xiaomage234/archive/2016/08/31/431715.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>构建C1000K的服务器(2) – 实现百万连接的comet服务器</title><link>http://www.blogjava.net/xiaomage234/archive/2016/05/26/430658.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 26 May 2016 03:17:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/05/26/430658.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/430658.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/05/26/430658.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/430658.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/430658.html</trackback:ping><description><![CDATA[<p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">这是关于&nbsp;<a href="http://www.ideawu.net/blog/tag/c1000k" style="color: #6666cc; text-decoration: none;">C1000K</a>&nbsp;序列文章的第二篇, 在前一篇文章&nbsp;<a href="http://www.ideawu.net/blog/archives/740.html" style="color: #6666cc; text-decoration: none;">构建C1000K的服务器(1) &#8211; 基础</a>&nbsp;中, 介绍了支持 C1000K 的 Linux 系统的内核参数调整和系统设置. 在本篇文章中, 将对一个真正的应用服务器做 C1000K 测试.</p><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">Comet 服务器是一类逻辑相对简单, 需要高并发连接的服务器. Comet 在网站系统中的应用非常广泛, 可以见这篇日志的介绍:&nbsp;<a href="http://www.ideawu.net/blog/archives/737.html" style="color: #6666cc; text-decoration: none;">http://www.ideawu.net/blog/archives/737.html</a>.</p><h3>HTTP 协议处理</h3><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">要开发一个支持百万并发连接的 Comet 服务器, 我选择 C/C++ 语言, 当然还有其它的选择如 Erlang, Java 等. 对于一个只支持 long-polling Comet 服务器, 首先要具备解析 HTTP 协议的能力, 我选择 libevent 来处理 HTTP 协议.</p><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;"></p><h3>通道和订阅者管理</h3><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">服务器在启动的时候, 就预先分配了 100 万个通道对象的空间, 但订阅者对象是按需分配的, 通过内存池方式. 100 万个通道对象和程序的其它数据占用了 24MB 的内存.</p><h3>Benchmark</h3><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">启动 icomet 服务器:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; color: #555555; background: #f3f3f3;">./icomet </pre><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">服务器监听了 100 个端口, 是为了测试方便, 原因见前一篇文章的分析:&nbsp;<a href="http://www.ideawu.net/blog/archives/740.html" style="color: #6666cc; text-decoration: none;">构建C1000K的服务器(1) &#8211; 基础</a>.</p><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">然后启动 benchmark 客户端:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; color: #555555; background: #f3f3f3;">./tools/benchmark 127.0.0.1 8100 </pre><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">benchmark 程序每创建十万个连接, 就会暂停, 按回车后继续. 通过 top/ps 查看 icomet 进程的内存占用. 最终, 得出如下数据.</p><table style="border-collapse: collapse; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;"><tbody><tr><th style="padding: 4px; border: 1px solid #aaaaaa; background: #dddddd;">连接数</th><th style="padding: 4px; border: 1px solid #aaaaaa; background: #dddddd;">进程VIRT</th><th style="padding: 4px; border: 1px solid #aaaaaa; background: #dddddd;">进程RES</th></tr><tr><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">0</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">39m</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">24m</td></tr><tr><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">100000</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">302m</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">288m</td></tr><tr><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">200000</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">579m</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">565m</td></tr><tr><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">500000</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">1441m</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">1427m</td></tr><tr><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">1000000</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">2734m</td><td style="padding: 4px; border-style: solid; border-color: #aaaaaa;">2720m</td></tr></tbody></table><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">可以看到, 每一个 Comet 连接大约占用了 2.7KB 的内存. 此时, 服务器空闲, 进程占用 CPU 为 0%.</p><p style="margin: 18px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">项目的代码在:&nbsp;<a href="https://github.com/ideawu/icomet" style="color: #6666cc; text-decoration: none;">https://github.com/ideawu/icomet</a>, 欢迎大家试用, 并反馈你的测试数据.</p><h3>Related posts:</h3><ol style="margin: 8px 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;"><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/821.html" rel="bookmark" title="Permanent Link: HTTP 长连接技术 Comet" style="color: #6666cc; text-decoration: none;">HTTP 长连接技术 Comet</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/740.html" rel="bookmark" title="Permanent Link: 构建C1000K的服务器(1) &#8211; 基础" style="color: #6666cc; text-decoration: none;">构建C1000K的服务器(1) &#8211; 基础</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/797.html" rel="bookmark" title="Permanent Link: iComet 的一个应用场景" style="color: #6666cc; text-decoration: none;">iComet 的一个应用场景</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/781.html" rel="bookmark" title="Permanent Link: 长连接技术的应用" style="color: #6666cc; text-decoration: none;">长连接技术的应用</a></li></ol><img src ="http://www.blogjava.net/xiaomage234/aggbug/430658.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-05-26 11:17 <a href="http://www.blogjava.net/xiaomage234/archive/2016/05/26/430658.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>构建C1000K的服务器(1) – 基础</title><link>http://www.blogjava.net/xiaomage234/archive/2016/05/26/430657.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 26 May 2016 03:16:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/05/26/430657.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/430657.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/05/26/430657.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/430657.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/430657.html</trackback:ping><description><![CDATA[from:http://www.ideawu.net/blog/archives/740.html?cp=2#comments<br /><br /><div style="float: left; width: 640px; margin-top: 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;"><p style="margin: 18px 0px;">著名的&nbsp;<a href="http://www.kegel.com/c10k.html" style="color: #6666cc; text-decoration: none;">C10K 问题</a>提出的时候, 正是 2001 年, 到如今 12 年后的 2013 年, C10K 已经不是问题了, 任何一个普通的程序员, 都能利用手边的语言和库, 轻松地写出 C10K 的服务器. 这既得益于软件的进步, 也得益于硬件性能的提高.</p><p style="margin: 18px 0px;">现在, 该是考虑&nbsp;<a href="http://www.ideawu.net/blog/tag/c100k" style="color: #6666cc; text-decoration: none;">C1000K</a>, 也就是百万连接的问题的时候了. 像 Twitter, weibo, Facebook 这些网站, 它们的同时在线用户有上千万, 同时又希望消息能接近实时地推送给用户, 这就需要服务器能维持和上千万用户的 TCP 网络连接, 虽然可以使用成百上千台服务器来支撑这么多用户, 但如果每台服务器能支持一百万连接(C1000K), 那么只需要十台服务器.</p><p style="margin: 18px 0px;">有很多技术声称能解决&nbsp;<a href="http://www.ideawu.net/blog/tag/c100k" style="color: #6666cc; text-decoration: none;">C1000K</a>&nbsp;问题, 例如 Erlang, Java NIO 等等, 不过, 我们应该首先弄明白, 什么因素限制了 C1000K 问题的解决. 主要是这几点:</p><ol style="margin: 8px 0px;"><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/740.html#q1" style="color: #6666cc; text-decoration: none;">操作系统能否支持百万连接?</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/740.html#q2" style="color: #6666cc; text-decoration: none;">操作系统维持百万连接需要多少内存?</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/740.html#q3" style="color: #6666cc; text-decoration: none;">应用程序维持百万连接需要多少内存?</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/740.html#q4" style="color: #6666cc; text-decoration: none;">百万连接的吞吐量是否超过了网络限制?</a></li></ol><p style="margin: 18px 0px;">下面来分别对这几个问题进行分析.</p><h3><a href="http://www.ideawu.net/blog/archives/740.html?cp=2#q1" name="q1" style="color: #6666cc; text-decoration: none;">1. 操作系统能否支持百万连接?</a></h3><p style="margin: 18px 0px;">对于绝大部分&nbsp;<a href="http://www.ideawu.net/blog/category/linux" style="color: #6666cc; text-decoration: none;">Linux</a>&nbsp;操作系统, 默认情况下确实不支持 C1000K! 因为操作系统包含最大打开文件数(Max Open Files)限制, 分为系统全局的, 和进程级的限制.</p><h4>全局限制</h4><p style="margin: 18px 0px;">在 Linux 下执行:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">cat /proc/sys/fs/file-nr </pre><p style="margin: 18px 0px;">会打印出类似下面的一行输出:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">5100	0	101747 </pre><p style="margin: 18px 0px;">第三个数字&nbsp;<code>101747</code>&nbsp;就是当前系统的全局最大打开文件数(Max Open Files), 可以看到, 只有 10 万, 所以, 在这台服务器上无法支持 C1000K. 很多系统的这个数值更小, 为了修改这个数值, 用 root 权限修改 /etc/sysctl.conf 文件:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">fs.file-max = 1020000 net.ipv4.ip_conntrack_max = 1020000 net.ipv4.netfilter.ip_conntrack_max = 1020000 </pre><p style="margin: 18px 0px;">需要重启系统服务生效:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;"># Linux $ sudo sysctl -p /etc/sysctl.conf  # BSD $ sudo /etc/rc.d/sysctl reload </pre><h3>进程限制</h3><p style="margin: 18px 0px;">执行:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">ulimit -n </pre><p style="margin: 18px 0px;">输出:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">1024 </pre><p style="margin: 18px 0px;">说明当前 Linux 系统的每一个进程只能最多打开 1024 个文件. 为了支持 C1000K, 你同样需要修改这个限制.</p><p style="margin: 18px 0px;"><strong>临时修改</strong></p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">ulimit -n 1020000 </pre><p style="margin: 18px 0px;">不过, 如果你不是 root, 可能不能修改超过 1024, 会报错:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">-bash: ulimit: open files: cannot modify limit: Operation not permitted </pre><p style="margin: 18px 0px;"><strong>永久修改</strong></p><p style="margin: 18px 0px;">编辑 /etc/security/limits.conf 文件, 加入如下行:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;"># /etc/security/limits.conf work         hard    nofile      1020000 work         soft    nofile      1020000 </pre><p style="margin: 18px 0px;">第一列的&nbsp;<code>work</code>&nbsp;表示 work 用户, 你可以填&nbsp;<code>*</code>, 或者&nbsp;<code>root</code>. 然后保存退出, 重新登录服务器.</p><p style="margin: 18px 0px;">注意: Linux 内核源码中有一个常量(NR_OPEN in /usr/include/linux/fs.h), 限制了最大打开文件数, 如 RHEL 5 是 1048576(2^20), 所以, 要想支持&nbsp;<a href="http://www.ideawu.net/blog/tag/c1000k" style="color: #6666cc; text-decoration: none;">C1000K</a>, 你可能还需要重新编译内核.</p><h3><a href="http://www.ideawu.net/blog/archives/740.html?cp=2#q2" name="q2" style="color: #6666cc; text-decoration: none;">2. 操作系统维持百万连接需要多少内存?</a></h3><p style="margin: 18px 0px;">解决了操作系统的参数限制, 接下来就要看看内存的占用情况. 首先, 是操作系统本身维护这些连接的内存占用. 对于 Linux 操作系统, socket(fd) 是一个整数, 所以, 猜想操作系统管理一百万个连接所占用的内存应该是 4M/8M, 再包括一些管理信息, 应该会是 100M 左右. 不过, 还有 socket 发送和接收缓冲区所占用的内存没有分析. 为此, 我写了最原始的 C 网络程序来验证:</p><h4>服务器</h4><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;unistd.h&gt; #include &lt;errno.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;netinet/tcp.h&gt; #include &lt;sys/select.h&gt;  #define MAX_PORTS 10  int main(int argc, char **argv){     struct sockaddr_in addr;     const char *ip = "0.0.0.0";     int opt = 1;     int bufsize;     socklen_t optlen;     int connections = 0;     int base_port = 7000;     if(argc &gt; 2){         base_port = atoi(argv[1]);     }      int server_socks[MAX_PORTS];      for(int i=0; i&lt;MAX_PORTS; i++){         int port = base_port + i;         bzero(&amp;addr, sizeof(addr));         addr.sin_family = AF_INET;         addr.sin_port = htons((short)port);         inet_pton(AF_INET, ip, &amp;addr.sin_addr);          int serv_sock;         if((serv_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1){             goto sock_err;         }         if(setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &amp;opt, sizeof(opt)) == -1){             goto sock_err;         }         if(bind(serv_sock, (struct sockaddr *)&amp;addr, sizeof(addr)) == -1){             goto sock_err;         }         if(listen(serv_sock, 1024) == -1){             goto sock_err;         }          server_socks[i] = serv_sock;         printf("server listen on port: %d\n", port);     }      //optlen = sizeof(bufsize);     //getsockopt(serv_sock, SOL_SOCKET, SO_RCVBUF, &amp;bufsize, &amp;optlen);     //printf("default send/recv buf size: %d\n", bufsize);      while(1){         fd_set readset;         FD_ZERO(&amp;readset);         int maxfd = 0;         for(int i=0; i&lt;MAX_PORTS; i++){             FD_SET(server_socks[i], &amp;readset);             if(server_socks[i] &gt; maxfd){                 maxfd = server_socks[i];             }         }         int ret = select(maxfd + 1, &amp;readset, NULL, NULL, NULL);         if(ret &lt; 0){             if(errno == EINTR){                 continue;             }else{                 printf("select error! %s\n", strerror(errno));                 exit(0);             }         }          if(ret &gt; 0){             for(int i=0; i&lt;MAX_PORTS; i++){                 if(!FD_ISSET(server_socks[i], &amp;readset)){                     continue;                 }                 socklen_t addrlen = sizeof(addr);                 int sock = accept(server_socks[i], (struct sockaddr *)&amp;addr, &amp;addrlen);                 if(sock == -1){                     goto sock_err;                 }                 connections ++;                 printf("connections: %d, fd: %d\n", connections, sock);             }         }     }      return 0; sock_err:     printf("error: %s\n", strerror(errno));     return 0; } </pre><p style="margin: 18px 0px;">注意, 服务器监听了 10 个端口, 这是为了测试方便. 因为只有一台客户端测试机, 最多只能跟同一个 IP 端口创建 30000 多个连接, 所以服务器监听了 10 个端口, 这样一台测试机就可以和服务器之间创建 30 万个连接了.</p><h4>客户端</h4><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;unistd.h&gt; #include &lt;errno.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;netinet/tcp.h&gt;  int main(int argc, char **argv){     if(argc &lt;=  2){         printf("Usage: %s ip port\n", argv[0]);         exit(0);     }      struct sockaddr_in addr;     const char *ip = argv[1];     int base_port = atoi(argv[2]);     int opt = 1;     int bufsize;     socklen_t optlen;     int connections = 0;      bzero(&amp;addr, sizeof(addr));     addr.sin_family = AF_INET;     inet_pton(AF_INET, ip, &amp;addr.sin_addr);      char tmp_data[10];     int index = 0;     while(1){         if(++index &gt;= 10){             index = 0;         }         int port = base_port + index;         printf("connect to %s:%d\n", ip, port);          addr.sin_port = htons((short)port);          int sock;         if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1){             goto sock_err;         }         if(connect(sock, (struct sockaddr *)&amp;addr, sizeof(addr)) == -1){             goto sock_err;         }          connections ++;         printf("connections: %d, fd: %d\n", connections, sock);          if(connections % 10000 == 9999){             printf("press Enter to continue: ");             getchar();         }         usleep(1 * 1000);         /*            bufsize = 5000;            setsockopt(serv_sock, SOL_SOCKET, SO_SNDBUF, &amp;bufsize, sizeof(bufsize));            setsockopt(serv_sock, SOL_SOCKET, SO_RCVBUF, &amp;bufsize, sizeof(bufsize));          */     }      return 0; sock_err:     printf("error: %s\n", strerror(errno));     return 0; } </pre><p style="margin: 18px 0px;">我测试 10 万个连接, 这些连接是空闲的, 什么数据也不发送也不接收. 这时, 进程只占用了不到 1MB 的内存. 但是, 通过程序退出前后的 free 命令对比, 发现操作系统用了 200M(大致)内存来维护这 10 万个连接! 如果是百万连接的话, 操作系统本身就要占用 2GB 的内存! 也即 2KB 每连接.</p><p style="margin: 18px 0px;">可以修改</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">/proc/sys/net/ipv4/tcp_wmem /proc/sys/net/ipv4/tcp_rmem </pre><p style="margin: 18px 0px;">来控制 TCP 连接的发送和接收缓冲的大小(多谢 @<a href="http://www.cnblogs.com/egmkang" style="color: #6666cc; text-decoration: none;">egmkang</a>).</p><h3><a href="http://www.ideawu.net/blog/archives/740.html?cp=2#q3" name="q3" style="color: #6666cc; text-decoration: none;">3. 应用程序维持百万连接需要多少内存?</a></h3><p style="margin: 18px 0px;">通过上面的测试代码, 可以发现, 应用程序维持百万个空闲的连接, 只会占用操作系统的内存, 通过 ps 命令查看可知, 应用程序本身几乎不占用内存.</p><h3><a href="http://www.ideawu.net/blog/archives/740.html?cp=2#q4" name="q4" style="color: #6666cc; text-decoration: none;">4. 百万连接的吞吐量是否超过了网络限制?</a></h3><p style="margin: 18px 0px;">假设百万连接中有 20% 是活跃的, 每个连接每秒传输 1KB 的数据, 那么需要的网络带宽是 0.2M x 1KB/s x 8 = 1.6Gbps, 要求服务器至少是万兆网卡(10Gbps).</p><h3>总结</h3><p style="margin: 18px 0px;">Linux 系统需要修改内核参数和系统配置, 才能支持 C1000K. C1000K 的应用要求服务器至少需要 2GB 内存, 如果应用本身还需要内存, 这个要求应该是至少 10GB 内存. 同时, 网卡应该至少是万兆网卡.</p><p style="margin: 18px 0px;">当然, 这仅仅是理论分析, 实际的应用需要更多的内存和 CPU 资源来处理业务数据.</p><p style="margin: 18px 0px;">参考:</p><p style="margin: 18px 0px;">*&nbsp;<a href="http://www.cyberciti.biz/faq/linux-increase-the-maximum-number-of-open-files/" style="color: #6666cc; text-decoration: none;">http://www.cyberciti.biz/faq/linux-increase-the-maximum-number-of-open-files/</a><br />*&nbsp;<a href="http://www.lognormal.com/blog/2012/09/27/linux-tcpip-tuning/" style="color: #6666cc; text-decoration: none;">http://www.lognormal.com/blog/2012/09/27/linux-tcpip-tuning/</a></p><p style="margin: 18px 0px;">下一篇:&nbsp;<a href="http://www.ideawu.net/blog/archives/742.html" style="color: #6666cc; text-decoration: none;">构建C1000K的服务器(2) &#8211; 实现</a></p><h3>Related posts:</h3><ol style="margin: 8px 0px;"><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/706.html" rel="bookmark" title="Permanent Link: 要记得清除 sockaddr_in" style="color: #6666cc; text-decoration: none;">要记得清除 sockaddr_in</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/742.html" rel="bookmark" title="Permanent Link: 构建C1000K的服务器(2) &#8211; 实现百万连接的comet服务器" style="color: #6666cc; text-decoration: none;">构建C1000K的服务器(2) &#8211; 实现百万连接的comet服务器</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/244.html" rel="bookmark" title="Permanent Link: 数据传输中的停止等待机制的实现" style="color: #6666cc; text-decoration: none;">数据传输中的停止等待机制的实现</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/739.html" rel="bookmark" title="Permanent Link: Libevent 2 HTTP 客户端示例" style="color: #6666cc; text-decoration: none;">Libevent 2 HTTP 客户端示例</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/789.html" rel="bookmark" title="Permanent Link: 有趣的 main 函数参数" style="color: #6666cc; text-decoration: none;">有趣的 main 函数参数</a></li></ol></div><div style="float: left; width: 640px; margin-top: 4px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">Posted by&nbsp;<strong>ideawu</strong>&nbsp;at 2013-09-16 22:01:16	Tags:&nbsp;<a href="http://www.ideawu.net/blog/tag/c1000k" rel="tag" style="color: #6666cc; text-decoration: none;">C1000K</a>,&nbsp;<a href="http://www.ideawu.net/blog/tag/%e9%ab%98%e5%b9%b6%e5%8f%91" rel="tag" style="color: #6666cc; text-decoration: none;">高并发</a></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/430657.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-05-26 11:16 <a href="http://www.blogjava.net/xiaomage234/archive/2016/05/26/430657.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>经典的”服务器最多65536个连接”误解</title><link>http://www.blogjava.net/xiaomage234/archive/2016/05/26/430656.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 26 May 2016 03:15:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/05/26/430656.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/430656.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/05/26/430656.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/430656.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/430656.html</trackback:ping><description><![CDATA[from:http://www.ideawu.net/blog/archives/533.html<br /><br /><div style="float: left; width: 640px; margin-top: 0px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;"><p style="margin: 18px 0px;">"因为TCP端口号是16位无符号整数, 最大65535, 所以一台服务器最多支持65536个TCP socket连接." - 一个非常经典的误解! 即使是有多年网络编程经验的人, 也会持有这个错误结论.</p><p style="margin: 18px 0px;">要戳破这个错误结论, 可以从理论和实践两方面来.</p><h3>理论</h3><p style="margin: 18px 0px;">系统通过一个四元组来唯一标识一条TCP连接. 这个四元组的结构是{local_ip, local_port, remote_ip, remote_port}, 对于IPv4, 系统理论上最多可以管理2^(32+16+32+16), 2的96次方个连接.</p><p style="margin: 18px 0px;"><strong>因为对于同一台服务器来说, 一般只有一个 local_ip, 那么, 同一台服务器可以管理 2^(16+32+16) 个连接. 而一个服务(进程, 如 Nginx 进程)一般只监听一个 local_port, 那么, 同一台服务就可以管理 2^(32+16) 个连接. 而如果从一台远端机器(所谓的 client)来连接这台服务器上的一个服务, 那么 local_ip, local_port, remote_ip 这3个变量是固定的, 那么, 就只能建立 2^16=65536 个连接了. 这就是经典的误解的来源!</strong></p><p style="margin: 18px 0px;">如果不仅仅考虑TCP, 则是一个五元组, 加上协议号(TCP, UDP或者其它).</p><p style="margin: 18px 0px;"></p><h3>实践</h3><p style="margin: 18px 0px;">服务器绑定一个ip:port, 然后accept连接, 所有accept的连接使用的本地地址也是同样的ip:port.</p><h3>扩展内容</h3><p style="margin: 18px 0px;">如果某个客户端向同一个TCP端点(ip:port)发起主动连接, 那么每一条连接都必须使用不同的本地TCP端点, 如果客户端只有一个IP则是使用不同的本地端口, 该端口的范围在*nix系统上的一个例子是32768到61000, 可以通过如下命令查看:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">[root@<a href="http://www.benegg.com/" style="color: #6666cc; text-decoration: none;">benegg.com</a> ~]# cat /proc/sys/net/ipv4/ip_local_port_range  32768   61000 </pre><p style="margin: 18px 0px;">也就是说, 一个客户端连接同一个服务器的同一个ip:port(比如进行压力测试), 最多可以发起30000个左右的连接.</p><p style="margin: 18px 0px;">TCP客户端(TCP的主动发起者)可以在同一ip:port上向不同的服务器发起主动连接, 只需在bind之前对socket设置SO_REUSEADDR选项.</p><p style="margin: 18px 0px;">系统支持的最大打开文件描述符数(包括socket连接):</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">[root@<a href="http://www.benegg.com/" style="color: #6666cc; text-decoration: none;">benegg.com</a> ~]# cat /proc/sys/fs/file-max 580382 </pre><p style="margin: 18px 0px;">单个进程所能打开的最大文件描述符数:</p><pre style="overflow: auto; font-size: 12px; line-height: 14.4px; margin-left: 20px; padding: 4px 6px; border-left-width: 5px; border-left-style: solid; border-left-color: #88dd88; background: #f3f3f3;">[root@<a href="http://www.benegg.com/" style="color: #6666cc; text-decoration: none;">benegg.com</a> ~]# ulimit -n 1024 </pre><h3>结论</h3><p style="margin: 18px 0px;">无论是对于服务器还是客户端, 认为"一台机器最多建立65536个TCP连接"是没有根据的, 理论上远远超过这个值.</p><p style="margin: 18px 0px;">另外, 对于client端, 操作系统会自动根据不同的远端 ip:port, 决定是否重用本地端口.</p><h3>Related posts:</h3><ol style="margin: 8px 0px;"><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/516.html" rel="bookmark" title="Permanent Link: 连连看游戏开发实践(1) &#8211; 算法" style="color: #6666cc; text-decoration: none;">连连看游戏开发实践(1) &#8211; 算法</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/246.html" rel="bookmark" title="Permanent Link: 对P2P应用不友好的NAT" style="color: #6666cc; text-decoration: none;">对P2P应用不友好的NAT</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/169.html" rel="bookmark" title="Permanent Link: Tomcat网站server.xml设置" style="color: #6666cc; text-decoration: none;">Tomcat网站server.xml设置</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/229.html" rel="bookmark" title="Permanent Link: P2P穿透NAT的思路" style="color: #6666cc; text-decoration: none;">P2P穿透NAT的思路</a></li><li style="margin: 2px 0px;"><a href="http://www.ideawu.net/blog/archives/770.html" rel="bookmark" title="Permanent Link: SSDB 的双主和多主配置" style="color: #6666cc; text-decoration: none;">SSDB 的双主和多主配置</a></li></ol></div><div style="float: left; width: 640px; margin-top: 4px; color: #555555; font-family: 微软雅黑, arial; font-size: 15px; line-height: 22.5px; background-color: #ffffff;">Posted by&nbsp;<strong>ideawu</strong>&nbsp;at 2010-07-16 16:44:50</div><img src ="http://www.blogjava.net/xiaomage234/aggbug/430656.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-05-26 11:15 <a href="http://www.blogjava.net/xiaomage234/archive/2016/05/26/430656.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JMH简介</title><link>http://www.blogjava.net/xiaomage234/archive/2016/04/06/429986.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Wed, 06 Apr 2016 06:19:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/04/06/429986.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/429986.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/04/06/429986.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/429986.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/429986.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 本文由&nbsp;ImportNew&nbsp;-&nbsp;hejiani&nbsp;翻译自&nbsp;java-performance。欢迎加入翻译小组。转载请见文末要求。JMH是新的microbenchmark（微基准测试）框架（2013年首次发布）。与其他众多框架相比它的特色优势在于，它是由Oracle实现JIT的相同人员开发的。特别是我想提一下Aleksey Shipilev和他优秀的博...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2016/04/06/429986.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/429986.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-04-06 14:19 <a href="http://www.blogjava.net/xiaomage234/archive/2016/04/06/429986.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Monadic futures in Java 8: How to organize your data flow and avoid callback hell</title><link>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429970.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 05 Apr 2016 09:51:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429970.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/429970.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429970.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/429970.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/429970.html</trackback:ping><description><![CDATA[<div>http://zeroturnaround.com/rebellabs/monadic-futures-in-java8/<br /><br /><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Few people will argue that asynchronous computation is cool and useful. In fact, the whole<a href="http://www.reactivemanifesto.org/" style="color: #5da6ce;">reactive programming</a>&nbsp;idea is based on asynchronous computations being possible. Well, there&#8217;s more than that, but the core idea is to allow data and events to flow through your system and do something with the results when they become available.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">So let&#8217;s look at an example of asynchronous function that everyone has seen and many have written themselves.</p><pre javascript"="" style="overflow-x: auto; color: #5f5e4e; padding: 0.8em; line-height: 1.5em; background: #f3f3f3;">$(<span style="color: #7d9726;">"#book"</span>).fadeIn(<span style="color: #7d9726;">"slow"</span>,    <span style="color: #5f9182;">function() </span>{    <span style="color: #ae7313;">console</span>.log(&#8220;hurray&#8221;);   }); </pre><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">This piece of JavaScript code takes a book element and fades it in. When fading is complete a callback function is called and &#8220;hurray&#8221; string appears in the console. All is well and good in this trivial case, but once your system grows you can find yourself writing more and more of these nested callbacks.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Callbacks are a common way of dealing with asynchronous or delayed actions. They are not the best option though; the problem with callbacks is that they tend to chain forever, callbacks for callbacks for callbacks, until you find yourself in a complete mess and every change in the code becomes extremely painful and slow.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Maybe there are other ways to organize asynchronous code? In fact, there are: all you need to do is just tweak the perspective a bit. Imagine, if you had a type to represent a result of an async computation. It would be awesome, and your code would pass it around like every other value and be flat, fluid and readable.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Well, why don&#8217;t we build it!</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">When we&#8217;re done, we&#8217;ll have a monadic type Promise written in Java 8 that will make our asynchronous code wonderful. It&#8217;s not like it wasn&#8217;t ever done before, but I want to lead you through the process and help you understand what&#8217;s happening and why. If you are lazy or just prefer starting from code, check out the github&nbsp;<a href="https://github.com/shelajev/promises" style="color: #5da6ce;">repo</a>.</p><h2>Getting to love monads in 9.5 minutes</h2><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Oh, monads! Every programmer worth their morning coffee has written about them. Monads are what functional programming adepts love, use and praise. And there are thousands of tutorials and posts describing the concept.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">So if you know everything there is to know about monads and want to get a closer look onto more interesting things, scroll down to the code below. Otherwise, bear with me just ten minutes, maybe this will become your go-to explanation about what a monad is.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;"><strong>A monad is a type</strong>, that represents a context of computation. I bet you&#8217;ve heard that before, but have you thought about what it means?</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">First of all, a monad doesn&#8217;t specify&nbsp;<strong>what is happening</strong>, that&#8217;s the responsibility of the computation within the context. A monad says&nbsp;<strong>what surrounds the computation</strong>&nbsp;that is happening.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Now, if you want an image reference to help you out, you can think of a monad as a bubble. Some people prefer a box, but a box is something concrete so a bubble works better for me.<br /><img src="http://zeroturnaround.com/wp-content/uploads/2014/02/monad-bubble-320x320.jpg" alt="A lovely bubble with a cure dragon-ish creature inside" width="320" height="320" size-medium=""  wp-image-40993"="" srcset="/wp-content/uploads/2014/02/monad-bubble-160x160.jpg 160w, /wp-content/uploads/2014/02/monad-bubble-320x320.jpg 320w, /wp-content/uploads/2014/02/monad-bubble-200x200.jpg 200w, /wp-content/uploads/2014/02/monad-bubble-32x32.jpg 32w, /wp-content/uploads/2014/02/monad-bubble-64x64.jpg 64w, /wp-content/uploads/2014/02/monad-bubble-96x96.jpg 96w, /wp-content/uploads/2014/02/monad-bubble-128x128.jpg 128w, /wp-content/uploads/2014/02/monad-bubble.jpg 494w" sizes="(max-width: 320px) 100vw, 320px" style="border: 0px; vertical-align: top; display: block; margin: 0.5em auto;" /><br />These monad-bubbles have two properties:</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;"></p><ul style="list-style: none; padding: 0px; margin: 0px 0px 0px 1.2em; color: #5f5f5f; font-family: 'Open Sans', Verdana, sans-serif; line-height: normal; background-color: #ffffff;"><li style="margin: 0.5em 0px;">a bubble can surround something</li><li style="margin: 0.5em 0px;">a bubble can receive instructions about what should it do with a surrounded thing</li></ul><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;"></p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">The surrounding part is easy to model in a programming language. Just take something and return a bubble! A constructor or a factory method comes to mind immediately here. Let&#8217;s look at how it is formalized. I&#8217;m assuming that you have some knowledge of Haskell notation (which you probably should have anyway). So the function that takes something and returns a monad is usually called pure or return:</p><pre java"="" style="overflow-x: auto; color: #5f5e4e; padding: 0.8em; line-height: 1.5em; background: #f3f3f3;"><span style="color: #5f9182;">return</span> :: a -&gt; m a </pre><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Or in Java, if we can have some Monad class already.</p><pre java"="" style="overflow-x: auto; color: #5f5e4e; padding: 0.8em; line-height: 1.5em; background: #f3f3f3;"><span style="color: #5f9182;">public</span> <span style="color: #5f9182;">class <span style="color: #36a166;">Monad</span>&lt;<span style="color: #36a166;">T</span>&gt; </span>{    <span style="color: #5f9182;">public <span style="color: #36a166;">Monad</span><span style="color: #ae7313;">(T t)</span> </span>{     &#8230;   } }</pre><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">See that was easy. In fact, we&#8217;re halfway there. Another thing we must add is the ability to receive instructions for working with this value T eaten by our bubble.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">What will help us is a bind function, which takes some form of an action and returns a different monad bubble that wraps this action executed on whatever was previously in the bubble.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">For the sake of completeness, here is how it looks in Haskell.</p><pre erlang-repl"="" style="overflow-x: auto; color: #5f5e4e; padding: 0.8em; line-height: 1.5em; background: #f3f3f3;">(&gt;&gt;=)  :: m a -&gt; (a -&gt; m b) -&gt; m b </pre><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">So this&nbsp;<em>bind</em>&nbsp;function takes a monad over&nbsp;<em>a</em>,&nbsp;<em>(m a)&nbsp;</em>and a function from&nbsp;<em>a</em>, and returns a different monad. In Java, we&#8217;ll have this definition as follows.</p><pre php"="" style="overflow-x: auto; color: #5f5e4e; padding: 0.8em; line-height: 1.5em; background: #f3f3f3;"><span style="color: #5f9182;">public</span> <span style="color: #5f9182;">class <span style="color: #36a166;">Monad</span>&lt;<span style="color: #36a166;">T</span>&gt; </span>{    <span style="color: #5f9182;">public</span> <span style="color: #5f9182;">abstract</span> &lt;V&gt; Monad&lt;V&gt; bind(<span style="color: #5f9182;">Function&lt;<span style="color: #36a166;">T</span>, <span style="color: #36a166;">Monad</span>&lt;<span style="color: #36a166;">V</span>&gt;&gt; <span style="color: #36a166;">f</span>)</span>; }</pre><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">That will complete our generic definition of monads so we can proceed with an implementation.</p><h2>Wait, what? I can have my monads in Java?</h2><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">First of all, there are many different types of monads. In that sense, a monad is more like an interface in Java terms. There is a List monad, a Maybe monad, an IO monad (for languages that are very pure and cannot allow themselves to have normal IO), etc.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">We will focus on creating a specific monad in Java, more specifically in Java 8. There is a good reason as to why we chose Java 8, since previously we found out that&nbsp;<strong>a monad will have to manipulate functions, which is really not that enjoyable in pre-lambda versions of Java</strong>. &nbsp;However, Java 8 introduces lambdas and first-class methods, so it will be much more pleasant to work with them.</p><h2>Your homemade Promise implementation</h2><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Here we go, now we&#8217;ve established our goal to have a monadic type to represent async computations. We&#8217;ve got our tools, namely Java 8, and we are ready to hack.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">What we want to have is a Promise class that represents a result of asynchronous computation, either successful or erroneous.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Let&#8217;s pretend that we already have some Promise class that accepts callbacks to execute when the main computation is finished. Luckily, we don&#8217;t have to pretend very much, there are many implementations of that available:&nbsp;<a href="http://doc.akka.io/docs/akka/snapshot/java/futures.html" style="color: #5da6ce;">Akka&#8217;s Future</a>,&nbsp;<a href="https://github.com/playframework/play1/blob/master/framework/src/play/libs/F.java" style="color: #5da6ce;">Play&#8217;s promise</a>&nbsp;and so forth.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">For this post I&#8217;m using the one from&nbsp;<a href="https://github.com/playframework/play1/blob/master/framework/src/play/libs/F.java" style="color: #5da6ce;">Play Framework</a>, in which case instances of Promise get redeemed when some thread calls&nbsp;<em>invoke()</em>&nbsp;or&nbsp;<em>invokeWithException()</em>&nbsp;methods. It also accepts callbacks in a form of Play&#8217;s Promise specific Action class arguments. Obviously,&nbsp;<em>Promise</em>&nbsp;has constructors already, but not only do I want to create new instances of&nbsp;<em>Promise</em>, I also want to mark them completed with a value immediately. Here is how I can do it.</p><pre java"="" style="overflow-x: auto; color: #5f5e4e; padding: 0.8em; line-height: 1.5em; background: #f3f3f3;"><span style="color: #5f9182;">public</span> <span style="color: #5f9182;">static</span> &lt;V&gt; Promise&lt;V&gt; <span style="color: #36a166;">pure<span style="color: #ae7313;">(<span style="color: #5f9182;">final</span> V v)</span> </span>{     Promise&lt;V&gt; p = <span style="color: #5f9182;">new</span> Promise&lt;&gt;();     p.invoke(v);     <span style="color: #5f9182;">return</span> p;   }</pre><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">The returned&nbsp;<em>Promise</em>&nbsp;is already redeemed and is ready to provide us with a result of the computation, which is precisely the given value.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">The bind implementation will look like something below. It takes a function and adds that as a callback to this instance. A callback will get a result of&nbsp;<em>this</em>&nbsp;computation and apply given function to it. Whatever that function application returns or throws is used to redeem the resulting<em>Promise</em>.</p><pre javascript"="" style="overflow-x: auto; color: #5f5e4e; padding: 0.8em; line-height: 1.5em; background: #f3f3f3;">public &lt;R&gt; <span style="color: #ae7313;">Promise</span>&lt;R&gt; bind(final <span style="color: #ae7313;">Function</span>&lt;V, <span style="color: #ae7313;">Promise</span>&lt;R&gt;&gt; <span style="color: #5f9182;">function) </span>{     <span style="color: #ae7313;">Promise</span>&lt;R&gt; result = <span style="color: #5f9182;">new</span> <span style="color: #ae7313;">Promise</span>&lt;&gt;();      <span style="color: #5f9182;">this</span>.onRedeem(callback -&gt; {       <span style="color: #5f9182;">try</span> {         V v = callback.get();         <span style="color: #ae7313;">Promise</span>&lt;R&gt; applicationResult = <span style="color: #5f9182;">function.<span style="color: #36a166;">apply</span>(<span style="color: #ae7313;">v</span>);         <span style="color: #36a166;">applicationResult</span>.<span style="color: #36a166;">onRedeem</span>(<span style="color: #ae7313;">applicationCallback -&gt; {           try {             R r = applicationCallback.get(</span>);             <span style="color: #36a166;">result</span>.<span style="color: #36a166;">invoke</span>(<span style="color: #ae7313;">r</span>);           }           <span style="color: #36a166;">catch</span> (<span style="color: #ae7313;">Throwable e</span>) </span>{             result.invokeWithException(e);           }         });       }       <span style="color: #5f9182;">catch</span> (Throwable e) {         result.invokeWithException(e);       }     });     <span style="color: #5f9182;">return</span> result;   } </pre><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Both applying the given function and getting a result from&nbsp;<em>this</em>&nbsp;are wrapped into the try-catch blocks, so exceptions are propagated to the resulting instance of&nbsp;<em>Promise</em>, just as one might expect.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">With these two constructs, it&#8217;s very easy to chain asynchronous computations while avoiding going deeper and deeper into the callback hell. In the following synthetic example, we do exactly that.</p><pre gradle"="" style="overflow-x: auto; color: #5f5e4e; padding: 0.8em; line-height: 1.5em; background: #f3f3f3;"><span style="color: #5f9182;">public</span> <span style="color: #5f9182;">static</span> <span style="color: #5f9182;">void</span> example1()                      <span style="color: #5f9182;">throws</span> ExecutionException, InterruptedException {     Promise&lt;String&gt; promise = Async.submit(() -&gt; {       String helloWorld = <span style="color: #7d9726;">"hello world"</span>;       <span style="color: #5f9182;">long</span> n = <span style="color: #ae7313;">500</span>;       System.out.<span style="color: #5f9182;">println</span>(<span style="color: #7d9726;">"Sleeping "</span> + n + <span style="color: #7d9726;">" ms example1"</span>);       Thread.sleep(n);       <span style="color: #5f9182;">return</span> helloWorld;     });     Promise&lt;Integer&gt; promise2 = promise.bind(string -&gt;               Promise.pure(Integer.valueOf(string.hashCode())));     System.out.<span style="color: #5f9182;">println</span>(<span style="color: #7d9726;">"Main thread example2"</span>);     <span style="color: #5f9182;">int</span> hashCode = promise2.get();     System.out.<span style="color: #5f9182;">println</span>(<span style="color: #7d9726;">"HashCode = "</span> + hashCode);   }</pre><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">That is basically it. We&#8217;ve implemented a monadic type&nbsp;<em>Promise</em>&nbsp;to represent a result of an async action.</p><h2>Production-ready completable future</h2><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">For those of you who have beared with me this far, I just want to say some final words about the quality of this implementation. Naturally, the above-mentioned GitHub repository has some tests that are proving that in some contexts, this might all work. However, I wouldn&#8217;t recommend using those Promises in production.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">One reason for that is that Java 8 already contains a class that represents a result of async computation and is monadic&#8230;, welcome,&nbsp;<a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html" style="color: #5da6ce;">CompletableFuture</a>!</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">It does exactly what we want it to do and features several methods that allow you to bind a function to the result of an existing computation. Moreover, it provides methods to apply a function or a consumer, which is a void function by the way, or a plain old Runnable.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;"></p><ul style="list-style: none; padding: 0px; margin: 0px 0px 0px 1.2em; color: #5f5f5f; font-family: 'Open Sans', Verdana, sans-serif; line-height: normal; background-color: #ffffff;"><li style="margin: 0.5em 0px;"><a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html#thenApply(java.util.function.Function)" style="color: #5da6ce;">thenApply</a>(<a href="http://download.java.net/lambda/b88/docs/api/java/util/function/Function.html" style="color: #5da6ce;">Function</a>&lt;? super&nbsp;<a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html" style="color: #5da6ce;">T</a>,? extends U&gt; fn)</li><li style="margin: 0.5em 0px;"><a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html#thenApplyAsync(java.util.function.Function)" style="color: #5da6ce;">thenApplyAsync</a>(<a href="http://download.java.net/lambda/b88/docs/api/java/util/function/Function.html" style="color: #5da6ce;">Function</a>&lt;? super&nbsp;<a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html" style="color: #5da6ce;">T</a>,? extends U&gt; fn)</li><li style="margin: 0.5em 0px;"><a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html#thenAccept(java.util.function.Consumer)" style="color: #5da6ce;">thenAccept</a>(<a href="http://download.java.net/lambda/b88/docs/api/java/util/function/Consumer.html" style="color: #5da6ce;">Consumer</a>&lt;? super&nbsp;<a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html" style="color: #5da6ce;">T</a>&gt; block)</li><li style="margin: 0.5em 0px;"><a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html#thenAcceptAsync(java.util.function.Consumer)" style="color: #5da6ce;">thenAcceptAsync</a>(<a href="http://download.java.net/lambda/b88/docs/api/java/util/function/Consumer.html" style="color: #5da6ce;">Consumer</a>&lt;? super&nbsp;<a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html" style="color: #5da6ce;">T</a>&gt; block)</li><li style="margin: 0.5em 0px;"><a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html#thenRun(java.lang.Runnable)" style="color: #5da6ce;">thenRun</a>(<a href="http://download.java.net/lambda/b88/docs/api/java/lang/Runnable.html" style="color: #5da6ce;">Runnable</a>&nbsp;action)</li><li style="margin: 0.5em 0px;"><a href="http://download.java.net/lambda/b88/docs/api/java/util/concurrent/CompletableFuture.html#thenRunAsync(java.lang.Runnable)" style="color: #5da6ce;">thenRunAsync</a>(<a href="http://download.java.net/lambda/b88/docs/api/java/lang/Runnable.html" style="color: #5da6ce;">Runnable</a>&nbsp;action)</li></ul><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;"></p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">On top of that, methods that end on&nbsp;<em>*Async</em>&nbsp;will execute this function asynchronously using a common&nbsp;<em>ForkJoin</em>&nbsp;executor. Otherwise, you can also supply an executor of your own liking.</p><h2>Conclusion</h2><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Hopefully, this post shed some light onto what a monad is and next time you are about write a callback, you might want to consider a different approach.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">In the post above we&#8217;ve looked at what monads are and how one can implement monadic classes in Java 8. Monads are great help in organizing data flow through your code and we&#8217;ve shown it with an example of&nbsp;<em>Promise</em>&nbsp;monad that represents results of an asynchronous computation. All the code from this blogpost is available for pondering in the Github&nbsp;<a href="https://github.com/shelajev/promises" style="color: #5da6ce;">repo</a>.</p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;">Stay tuned for my next post, in which I plan to cover how to use the&nbsp;<a href="http://commons.apache.org/sandbox/commons-javaflow/" style="color: #5da6ce;">javaflow</a>&nbsp;library to implement asynchronous awaiting for the promise to return a result. So you can get even more reactive :-)</p><hr style="border: 0px; height: 1px; color: #5f5f5f; font-family: 'Open Sans', Verdana, sans-serif; line-height: normal; margin: 20px 0px !important; background: #ededed;" /><em style="color: #5f5f5f; font-family: 'Open Sans', Verdana, sans-serif; line-height: normal; background-color: #ffffff;">Want to learn more about what rocks in Java 8? Check out&nbsp;<a href="http://zeroturnaround.com/rebellabs/java-8-revealed-lambdas-default-methods-and-bulk-data-operations/" style="color: #5da6ce;">Java 8 Revealed: Lambdas, Default methods and Bulk Data Operations</a>&nbsp;by Anton Arhipov</em><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;"></p><p style="line-height: 1.65; word-spacing: 0.15em; font-family: 'Open Sans', Verdana, sans-serif; background-color: #ffffff;"><a button"="" href="http://pages.zeroturnaround.com/RebelLabs-AllReportLanders_Java8Revealed.html" style="color: #ffffff; white-space: nowrap; -webkit-font-smoothing: antialiased; transition: all 0.3s; text-align: center; border: 0px; border-radius: 3px; display: inline-block; font-size: 0.9em; font-weight: 600; line-height: 1; margin-top: -0.5em; padding: 0.5em 2em; background: #f2693d !important;">Get the PDF</a></p></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/429970.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-04-05 17:51 <a href="http://www.blogjava.net/xiaomage234/archive/2016/04/05/429970.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Play Framework: async I/O without the thread pool and callback hell</title><link>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429969.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 05 Apr 2016 09:48:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429969.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/429969.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429969.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/429969.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/429969.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: from:https://engineering.linkedin.com/play/play-framework-async-io-without-thread-pool-and-callback-hellUnder the hood, LinkedIn consists of hundreds of services that can be evolved and scaled indepen...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2016/04/05/429969.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/429969.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-04-05 17:48 <a href="http://www.blogjava.net/xiaomage234/archive/2016/04/05/429969.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Latency Numbers Every Programmer Should Know</title><link>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429967.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 05 Apr 2016 09:47:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429967.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/429967.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429967.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/429967.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/429967.html</trackback:ping><description><![CDATA[<div>http://www.eecs.berkeley.edu/~rcs/research/interactive_latency.html</div><img src ="http://www.blogjava.net/xiaomage234/aggbug/429967.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-04-05 17:47 <a href="http://www.blogjava.net/xiaomage234/archive/2016/04/05/429967.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Threaded vs Evented Servers</title><link>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429968.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 05 Apr 2016 09:47:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429968.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/429968.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429968.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/429968.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/429968.html</trackback:ping><description><![CDATA[from:http://mmcgrana.github.io/2010/07/threaded-vs-evented-servers.html<br /><br /><div style="margin: 0px 0px 2em; color: #aa0000; font-weight: bold; font-family: helvetica, arial, clean, sans-serif; font-size: 14.674px; line-height: 22.011px; background-color: #ffffff;"><a href="http://mmcgrana.github.io/" style="margin: 0px; padding: 0px; color: #aa0000; text-decoration: none;">Mark McGranaghan</a></div><div id="post" style="margin: 0px; font-family: helvetica, arial, clean, sans-serif; font-size: 14.674px; line-height: 22.011px; background-color: #ffffff;"><h1>Threaded vs Evented Servers</h1><p style="margin: 1em 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #aaaaaa;">July 24 2010</span></p><p style="margin: 1em 0px; padding: 0px;">Broadly speaking, there are two ways to handle concurrent requests to a server.&nbsp;<em style="margin: 0px; padding: 0px;">Threaded</em>servers use multiple concurrently-executing threads that each handle one client request, while&nbsp;<em style="margin: 0px; padding: 0px;">evented</em>&nbsp;servers run a single event loop that handles events for all connected clients.</p><p style="margin: 1em 0px; padding: 0px;">To chose between the threaded and evented approaches you need to consider the load profile of the server. This post describes a simple mathematical model for reasoning about these load profiles and their implications for server design.</p><p style="margin: 1em 0px; padding: 0px;">Suppose that requests to a server take&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">c</code>&nbsp;CPU milliseconds and&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">w</code>&nbsp;wall clock milliseconds to execute. The CPU time is spent actively computing on behalf of the request, while the wall clock time is the total time including that time spent waiting for calls to external resources. For example, a web application request might take 5 ms of CPU time&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">c</code>&nbsp;and 95 ms waiting for a database call for a total wall time&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">w</code>&nbsp;of 100 ms. Let&#8217;s also say that a threaded version of the server can maintain up to&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">t</code>&nbsp;threads before performance degrades because of scheduling and context-switching overhead. Finally, we&#8217;ll assume single-core servers.</p><p style="margin: 1em 0px; padding: 0px;">If a server is CPU bound then it will be able to respond to at most</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #0086b3;">/ </span><span style="margin: 0px; padding: 0px; color: #009999;">1000</span> <span style="margin: 0px; padding: 0px; color: #008080;">c</span><span style="margin: 0px; padding: 0px;">)</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">requests per second. For example, if each requests takes 2 ms of CPU time then the CPU can only handle</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #0086b3;">/ </span><span style="margin: 0px; padding: 0px; color: #009999;">1000</span> <span style="margin: 0px; padding: 0px; color: #009999;">2</span><span style="margin: 0px; padding: 0px;">)</span> <span style="margin: 0px; padding: 0px; color: #008080;">=&gt;</span> <span style="margin: 0px; padding: 0px; color: #009999;">500</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">requests per second.</p><p style="margin: 1em 0px; padding: 0px;">If the server is thread bound then it can handle at most</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #0086b3;">* </span><span style="margin: 0px; padding: 0px; color: #008080;">t</span> <span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #0086b3;">/ </span><span style="margin: 0px; padding: 0px; color: #009999;">1000</span> <span style="margin: 0px; padding: 0px; color: #008080;">w</span><span style="margin: 0px; padding: 0px;">))</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">requests per second. This expression is similar to the one for CPU time, but here we multiply the result by&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">t</code>&nbsp;to account for the&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">t</code>&nbsp;concurrent threads.</p><p style="margin: 1em 0px; padding: 0px;">The throughput of a threaded server is the minimum of the CPU and thread bounds since it is subject to both constraints. An evented server is not subject to the thread constraint since it only uses one thread; its throughput is given by the CPU bound. We can express this as follows:</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; font-weight: bold;">defn </span><span style="margin: 0px; padding: 0px; color: #008080;">max-request-rate</span> <span style="margin: 0px; padding: 0px;">[</span><span style="margin: 0px; padding: 0px; color: #008080;">t</span> <span style="margin: 0px; padding: 0px; color: #008080;">c</span> <span style="margin: 0px; padding: 0px; color: #008080;">w</span><span style="margin: 0px; padding: 0px;">]</span>   <span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; font-weight: bold;">let </span><span style="margin: 0px; padding: 0px;">[</span><span style="margin: 0px; padding: 0px; color: #008080;">cpu-bound</span>    <span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #0086b3;">/ </span><span style="margin: 0px; padding: 0px; color: #009999;">1000</span> <span style="margin: 0px; padding: 0px; color: #008080;">c</span><span style="margin: 0px; padding: 0px;">)</span>         <span style="margin: 0px; padding: 0px; color: #008080;">thread-bound</span> <span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #0086b3;">* </span><span style="margin: 0px; padding: 0px; color: #008080;">t</span> <span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #0086b3;">/ </span><span style="margin: 0px; padding: 0px; color: #009999;">1000</span> <span style="margin: 0px; padding: 0px; color: #008080;">w</span><span style="margin: 0px; padding: 0px;">))]</span>     <span style="margin: 0px; padding: 0px;">{</span><span style="margin: 0px; padding: 0px; color: #990073;">:threaded</span> <span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #0086b3;">min </span><span style="margin: 0px; padding: 0px; color: #008080;">cpu-bound</span> <span style="margin: 0px; padding: 0px; color: #008080;">thread-bound</span><span style="margin: 0px; padding: 0px;">)</span>      <span style="margin: 0px; padding: 0px; color: #990073;">:evented</span>  <span style="margin: 0px; padding: 0px; color: #008080;">cpu-bound</span><span style="margin: 0px; padding: 0px;">}))</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">Now we&#8217;ll consider some different types of servers and see how they might perform with threaded and evented implementations.</p><p style="margin: 1em 0px; padding: 0px;">For the examples below I&#8217;ll use a&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">t</code>&nbsp;value of 25. This is a modest number of threads that most threading implementations can handle.</p><p style="margin: 1em 0px; padding: 0px;">Let&#8217;s start with a classic example: an HTTP proxy server. These servers require very little CPU time, so say&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">c</code>&nbsp;is 0.1 ms. Suppose that the downstream servers can receive the relay within milliseconds for a wall time&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">w</code>&nbsp;of, say, 10 ms. Then we have</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #990000; font-weight: bold;">max-request-rate</span> <span style="margin: 0px; padding: 0px; color: #009999;">25</span> <span style="margin: 0px; padding: 0px; color: #009999;">0.1</span> <span style="margin: 0px; padding: 0px; color: #009999;">10</span><span style="margin: 0px; padding: 0px;">)</span> <span style="margin: 0px; padding: 0px; color: #008080;">=&gt;</span> <span style="margin: 0px; padding: 0px;">{</span><span style="margin: 0px; padding: 0px; color: #990073;">:threaded</span> <span style="margin: 0px; padding: 0px; color: #009999;">2500</span>, <span style="margin: 0px; padding: 0px; color: #990073;">:evented</span> <span style="margin: 0px; padding: 0px; color: #009999;">10000</span><span style="margin: 0px; padding: 0px;">}</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">In this case we expect a threaded server to be able to handle 2500 requests per second and an evented server 10000 requests per second. The higher performance of the evented server implies that the thread bound is limiting for the threaded server.</p><p style="margin: 1em 0px; padding: 0px;">Another familiar example is the web application server. Let&#8217;s first consider the case where we have a lightweight app that does not access any external resources. In this case the request parsing and response generation might take a few milliseconds; say&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">c</code>&nbsp;is 2 ms. Since no blocking calls are made this is the value of&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">w</code>&nbsp;as well. Then</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #990000; font-weight: bold;">max-request-rate</span> <span style="margin: 0px; padding: 0px; color: #009999;">25</span> <span style="margin: 0px; padding: 0px; color: #009999;">2</span> <span style="margin: 0px; padding: 0px; color: #009999;">2</span><span style="margin: 0px; padding: 0px;">)</span> <span style="margin: 0px; padding: 0px; color: #008080;">=&gt;</span> <span style="margin: 0px; padding: 0px;">{</span><span style="margin: 0px; padding: 0px; color: #990073;">:threaded</span> <span style="margin: 0px; padding: 0px; color: #009999;">500</span>, <span style="margin: 0px; padding: 0px; color: #990073;">:evented</span> <span style="margin: 0px; padding: 0px; color: #009999;">500</span><span style="margin: 0px; padding: 0px;">}</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">Here the threaded server performs as well as the evented server because the workload is CPU bound.</p><p style="margin: 1em 0px; padding: 0px;">Suppose we have a more heavyweight app that is making calls to external resources like the filesystem and database. In this case the amount of CPU time will be somewhat larger that the previous case but still modest; say&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">c</code>&nbsp;is 5 ms. But now that we are waiting on external resources we should expect a&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">w</code>&nbsp;value of, say, 100 ms. Then we have</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #990000; font-weight: bold;">max-request-rate</span> <span style="margin: 0px; padding: 0px; color: #009999;">25</span> <span style="margin: 0px; padding: 0px; color: #009999;">5</span> <span style="margin: 0px; padding: 0px; color: #009999;">100</span><span style="margin: 0px; padding: 0px;">)</span> <span style="margin: 0px; padding: 0px; color: #008080;">=&gt;</span> <span style="margin: 0px; padding: 0px;">{</span><span style="margin: 0px; padding: 0px; color: #990073;">:threaded</span> <span style="margin: 0px; padding: 0px; color: #009999;">200</span>, <span style="margin: 0px; padding: 0px; color: #990073;">:evented</span> <span style="margin: 0px; padding: 0px; color: #009999;">200</span><span style="margin: 0px; padding: 0px;">}</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">Even though we are making a lot of blocking calls, the workload is still CPU bound and the threaded and evented servers will therefore perform comparably.</p><p style="margin: 1em 0px; padding: 0px;">Suppose now that we are implementing a background service such as an RSS feed fetcher that makes high-latency requests to external services and then performs minimal processing of the results. In this case&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">c</code>&nbsp;may be quite low, say 2 ms, but&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">w</code>&nbsp;will be high, say 250 ms. Then</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #990000; font-weight: bold;">max-request-rate</span> <span style="margin: 0px; padding: 0px; color: #009999;">25</span> <span style="margin: 0px; padding: 0px; color: #009999;">2</span> <span style="margin: 0px; padding: 0px; color: #009999;">250</span><span style="margin: 0px; padding: 0px;">)</span> <span style="margin: 0px; padding: 0px; color: #008080;">=&gt;</span> <span style="margin: 0px; padding: 0px;">{</span><span style="margin: 0px; padding: 0px; color: #990073;">:threaded</span> <span style="margin: 0px; padding: 0px; color: #009999;">100</span>, <span style="margin: 0px; padding: 0px; color: #990073;">:evented</span> <span style="margin: 0px; padding: 0px; color: #009999;">500</span><span style="margin: 0px; padding: 0px;">}</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">Here an evented server will perform better. The CPU load is sufficiently low and the external resource latency sufficiently high that the blocking external calls limit the threaded implementation.</p><p style="margin: 1em 0px; padding: 0px;">Finally, consider the case of long polling clients. Here clients establish a connection to the server and the server responds only when it has a message it wants to send to the client. Suppose that we have a lightweight app such that&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">c</code>&nbsp;is 1 ms, but that response messages are sent to the client after 10 seconds such that the&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">w</code>&nbsp;value is 10000 ms. Then</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #990000; font-weight: bold;">max-request-rate</span> <span style="margin: 0px; padding: 0px; color: #009999;">25</span> <span style="margin: 0px; padding: 0px; color: #009999;">1</span> <span style="margin: 0px; padding: 0px; color: #009999;">10000</span><span style="margin: 0px; padding: 0px;">)</span> <span style="margin: 0px; padding: 0px; color: #008080;">=&gt;</span> <span style="margin: 0px; padding: 0px;">{</span><span style="margin: 0px; padding: 0px; color: #990073;">:threaded</span> <span style="margin: 0px; padding: 0px; color: #009999;">2.5</span>, <span style="margin: 0px; padding: 0px; color: #990073;">:evented</span> <span style="margin: 0px; padding: 0px; color: #009999;">1000</span><span style="margin: 0px; padding: 0px;">}</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">If the server were really limited to 25 threads and each client required its own thread, we could only allow 2.5 new connections per second if we wanted to avoid exceeding the thread allocation. An evented server on the other hand could saturate the CPU by accepting 1000 requests per second.</p><p style="margin: 1em 0px; padding: 0px;">Even if we increase the maximum number of threads&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">t</code>&nbsp;by an order of magnitude to 250, the evented approach still fares better:</p><div style="margin: 0px; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px 0.4em; border: 1px solid #dddddd; background-color: #eeeeff;"><code style="margin: 0px; padding: 0px 0.2em; border: none; font-size: 13.9403px;"><span style="margin: 0px; padding: 0px;">(</span><span style="margin: 0px; padding: 0px; color: #990000; font-weight: bold;">max-request-rate</span> <span style="margin: 0px; padding: 0px; color: #009999;">250</span> <span style="margin: 0px; padding: 0px; color: #009999;">1</span> <span style="margin: 0px; padding: 0px; color: #009999;">10000</span><span style="margin: 0px; padding: 0px;">)</span> <span style="margin: 0px; padding: 0px; color: #008080;">=&gt;</span> <span style="margin: 0px; padding: 0px;">{</span><span style="margin: 0px; padding: 0px; color: #990073;">:threaded</span> <span style="margin: 0px; padding: 0px; color: #009999;">25</span>, <span style="margin: 0px; padding: 0px; color: #990073;">:evented</span> <span style="margin: 0px; padding: 0px; color: #009999;">1000</span><span style="margin: 0px; padding: 0px;">}</span> </code></pre></div><p style="margin: 1em 0px; padding: 0px;">Indeed, a threaded server would need to maintain 10000 threads in order to be able to accept requests at the rate of the evented server.</p><p style="margin: 1em 0px; padding: 0px;">Now that we have seen some specific examples of the model we should step back and note the patterns. In general, an evented architecture becomes more favorable as the ratio of wall time&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">w</code>&nbsp;to CPU time&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">c</code>&nbsp;increases, i.e. as proportionally more time is spent waiting on external resources. Also, the viability of a threaded architecture depends on the strength of the underlying threading implementation; the higher the thread threshold&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">t</code>, the more wait time can be tolerated before eventing becomes necessary.</p><p style="margin: 1em 0px; padding: 0px;">In addition to the quantitative performance implications captured by this model, there are several qualitative factors that influence the suitability of threaded and evented architectures for particular servers.</p><p style="margin: 1em 0px; padding: 0px;">One factor is the fit of the server architecture to the work that the server is doing internally. For example, proxying is well suited to evented architectures because the work being done is fundamentally evented: upon receiving an input chunk from the client the chunk is relayed to a downstream server. In contrast, the business logic implemented by web applications is more naturally described in a synchronous style. The callbacks required by an evented architecture become unwieldy in complex application code.</p><p style="margin: 1em 0px; padding: 0px;">Another consideration is memory coordination and consistency. Evented servers executing in a single event loop do not need to worry about the correctness and performance implications of maintaining consistent shared memory, but this may be a problem for threaded servers. Threaded servers therefore attempt to minimize memory shared among threads. This approach works well for the servers that we discussed above - proxies, web applications, background workers, and long poll endpoints - as none of them need to share state internally across client sessions. But fundamentally stateful servers like caches and databases cannot avoid this problem.</p><p style="margin: 1em 0px; padding: 0px;">The threaded approach can be a non-starter if the underlying platform does not support proper threading. In these cases blocking calls to external resources prevent the process from using the CPU in other threads, even if the blocker is not itself using the CPU. C Ruby falls into this category. In these cases&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">t</code>&nbsp;is effectively 1, making evented architectures relatively more appealing.</p><p style="margin: 1em 0px; padding: 0px;">In the other extreme, the assumption of&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">t</code>&nbsp;being 25 or even 250 may be too modest for some platforms. These low&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">t</code>&nbsp;values are an&nbsp;<a href="http://www.usenix.org/events/hotos03/tech/full_papers/vonbehren/vonbehren_html/index.html" style="margin: 0px; padding: 0px; color: #aa00aa;">an artifact of threading implementations</a>&nbsp;and not intrinsic to the threading model itself. More scalable threading implementations make threaded servers viable for higher&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">w</code>&nbsp;to&nbsp;<code style="margin: 0px; padding: 0px 0.2em; border: 1px solid #dddddd; font-size: 13.9403px; background-color: #eeeeff;">c</code>&nbsp;ratios.</p><p style="margin: 1em 0px; padding: 0px;">An evented approach can be compromised by a lack of evented libraries for the platform. For evented servers to perform optimally, all external resources must be accessed through nonblocking libraries. Such libraries are not always available, especially on platforms that have typically used threaded/blocking models like the JVM and C Ruby. Fortunately this situation is improving as developers&nbsp;<a href="http://jboss.org/netty" style="margin: 0px; padding: 0px; color: #aa00aa;">publish</a>&nbsp;<a href="https://github.com/ning/async-http-client" style="margin: 0px; padding: 0px; color: #aa00aa;">more</a>&nbsp;<a href="http://rubyeventmachine.com/" style="margin: 0px; padding: 0px; color: #aa00aa;">nonblocking</a>&nbsp;<a href="https://github.com/igrigorik/em-http-request" style="margin: 0px; padding: 0px; color: #aa00aa;">libraries</a>&nbsp;in response to the demand from implementors of evented servers. Indeed, the requirement of pervasive evented libraries for optimal performance is one reason that&nbsp;<a href="http://nodejs.org/" style="margin: 0px; padding: 0px; color: #aa00aa;">node.js</a>&nbsp;is so compelling for building evented servers.</p></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/429968.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-04-05 17:47 <a href="http://www.blogjava.net/xiaomage234/archive/2016/04/05/429968.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Finagle</title><link>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429966.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 05 Apr 2016 09:46:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429966.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/429966.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2016/04/05/429966.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/429966.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/429966.html</trackback:ping><description><![CDATA[<p style="line-height: 1.4em; color: #3e4349; font-family: Georgia, serif; font-size: 17px;">Finagle is an extensible RPC system for the JVM, used to construct high-concurrency servers. Finagle implements uniform client and server APIs for several protocols, and is designed for high performance and concurrency. Most of Finagle&#8217;s code is protocol agnostic, simplifying the implementation of new&nbsp;<a internal"="" href="http://twitter.github.io/finagle/guide/Protocols.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;"><em>protocols</em></a>.</p><p style="line-height: 1.4em; color: #3e4349; font-family: Georgia, serif; font-size: 17px;">Finagle uses a&nbsp;<em>clean</em>,&nbsp;<em>simple</em>, and&nbsp;<em>safe</em>&nbsp;concurrent programming model, based on<a internal"="" href="http://twitter.github.io/finagle/guide/Futures.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;"><em>Futures</em></a>. This leads to safe and modular programs that are also simple to reason about.</p><p style="line-height: 1.4em; color: #3e4349; font-family: Georgia, serif; font-size: 17px;">Finagle clients and servers expose statistics for monitoring and diagnostics. They are also traceable through a mechanism similar to&nbsp;<a external"="" href="http://research.google.com/pubs/pub36356.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Dapper</a>&#8216;s (another Twitter open source project,&nbsp;<a external"="" href="http://zipkin.io/" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Zipkin</a>, provides trace aggregation and visualization).</p><p style="line-height: 1.4em; color: #3e4349; font-family: Georgia, serif; font-size: 17px;">The&nbsp;<a internal"="" href="http://twitter.github.io/finagle/guide/Quickstart.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;"><em>quickstart</em></a>&nbsp;has an overview of the most important concepts, walking you through the setup of a simple HTTP server and client.</p><p style="line-height: 1.4em; color: #3e4349; font-family: Georgia, serif; font-size: 17px;">A section on&nbsp;<a internal"="" href="http://twitter.github.io/finagle/guide/Futures.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;"><em>Futures</em></a>&nbsp;follows, motivating and explaining the important ideas behind the concurrent programming model used in Finagle. The next section documents&nbsp;<a internal"="" href="http://twitter.github.io/finagle/guide/ServicesAndFilters.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;"><em>Services &amp; Filters</em></a>&nbsp;which are the core abstractions used to represent clients and servers and modify their behavior.</p><p style="line-height: 1.4em; color: #3e4349; font-family: Georgia, serif; font-size: 17px;">Other useful resources include:</p><ul style="margin: 10px 0px 10px 30px; padding: 0px; list-style: none; line-height: 1.4; color: #3e4349; font-family: Georgia, serif; font-size: 17px;"><li style="list-style: none; line-height: 1.4em;">&#8220;<a external"="" href="http://monkey.org/~marius/funsrv.pdf" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Your Server as a Function</a>&#8221; a paper motivating the core abstractions behind finagle (<a external"="" href="http://sigops.org/sosp/sosp13/plos.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">PLOS&#8217;13</a>).</li><li style="list-style: none; line-height: 1.4em;"><a external"="" href="https://blog.twitter.com/2011/finagle-a-protocol-agnostic-rpc-system" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Twitter engineering blog entry introducing Finagle</a></li><li style="list-style: none; line-height: 1.4em;"><a external"="" href="http://days2011.scala-lang.org/node/138/286" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">ScalaDays 2011 presentation on Finagle</a></li><li style="list-style: none; line-height: 1.4em;">Twitter&#8217;s&nbsp;<a external"="" href="http://twitter.github.com/scala_school/" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Scala School</a>&nbsp;has a section&nbsp;<a external"="" href="http://twitter.github.com/scala_school/finagle.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">introducing Finagle</a>&nbsp;and another&nbsp;<a external"="" href="http://twitter.github.com/scala_school/searchbird.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">constructing a distributed search engine using Finagle</a></li><li style="list-style: none; line-height: 1.4em;">Alex Martins&#8217;&nbsp;<a external"="" href="http://alexmartins.me/2015/12/30/building-fault-tolerant-clients-with-finagle-part-1.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">post</a>&nbsp;on configuring fault tolerant Finagle clients (Part 1)</li></ul><div id="user-s-guide" style="color: #3e4349; font-family: Georgia, serif; font-size: 17px; line-height: normal;"><h1>User&#8217;s guide<a href="http://twitter.github.io/finagle/guide/#user-s-guide" title="Permalink to this headline" style="color: #dddddd; text-decoration: none; visibility: hidden; padding: 0px 4px;"></a></h1><div compound"=""><ul style="margin: 10px 0px 10px 30px; padding: 0px; list-style: none; line-height: 1.4;"><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Quickstart.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Quickstart</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Futures.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Concurrent Programming with Futures</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/ServicesAndFilters.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Services &amp; Filters</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Configuration.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Configuration</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Servers.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Servers</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Clients.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Clients</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Contexts.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Contexts</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Names.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Names and Naming in Finagle</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Protocols.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Protocols</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Extending.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Extending Finagle</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/Metrics.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Metrics</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/FAQ.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">FAQ</a></li></ul></div></div><div id="notes" style="color: #3e4349; font-family: Georgia, serif; font-size: 17px; line-height: normal;"><h1>Notes<a href="http://twitter.github.io/finagle/guide/#notes" title="Permalink to this headline" style="color: #dddddd; text-decoration: none; visibility: hidden; padding: 0px 4px;"></a></h1><div compound"=""><ul style="margin: 10px 0px 10px 30px; padding: 0px; list-style: none; line-height: 1.4;"><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/changelog.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">Changelog</a></li><li style="list-style: none; line-height: 1.4em;"><a internal"="" href="http://twitter.github.io/finagle/guide/license.html" style="color: #004b6b; text-decoration: none; border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #004b6b;">License</a></li></ul></div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/429966.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2016-04-05 17:46 <a href="http://www.blogjava.net/xiaomage234/archive/2016/04/05/429966.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用十条命令在一分钟内检查Linux服务器性能</title><link>http://www.blogjava.net/xiaomage234/archive/2015/12/25/428827.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 25 Dec 2015 08:30:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/12/25/428827.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/428827.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/12/25/428827.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/428827.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/428827.html</trackback:ping><description><![CDATA[<p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">如果你的Linux服务器突然负载暴增，告警短信快发爆你的手机，如何在最短时间内找出Linux性能问题所在？来看Netflix性能工程团队的这篇<a href="http://techblog.netflix.com/2015/11/linux-performance-analysis-in-60s.html" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">博文</a>，看它们通过十条命令在一分钟内对机器性能问题进行诊断。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">概述</span></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">通过执行以下命令，可以在1分钟内对系统资源使用情况有个大致的了解。</p><ul style="margin: 0px 0px 15px 10px; padding: 0px; border: 0px; clear: left; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">uptime</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">dmesg | tail</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">vmstat 1</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">mpstat -P ALL 1</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">pidstat 1</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">iostat -xz 1</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">free -m</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">sar -n DEV 1</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">sar -n TCP,ETCP 1</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">top</li></ul><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">其中一些命令需要安装sysstat包，有一些由procps包提供。这些命令的输出，有助于快速定位性能瓶颈，检查出所有资源（CPU、内存、磁盘IO等）的利用率（utilization）、饱和度（saturation）和错误（error）度量，也就是所谓的<a href="http://www.brendangregg.com/usemethod.html" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">USE方法</a>。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">下面我们来逐一介绍下这些命令，有关这些命令更多的参数和说明，请参照命令的手册。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">uptime</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ uptime 23:51:26 up 21:31,  1 user,  load average: 30.02, 26.43, 19.02 </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">这个命令可以快速查看机器的负载情况。在Linux系统中，这些数据表示等待CPU资源的进程和阻塞在不可中断IO进程（进程状态为D）的数量。这些数据可以让我们对系统资源使用有一个宏观的了解。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">命令的输出分别表示1分钟、5分钟、15分钟的平均负载情况。通过这三个数据，可以了解服务器负载是在趋于紧张还是区域缓解。如果1分钟平均负载很高，而15分钟平均负载很低，说明服务器正在命令高负载情况，需要进一步排查CPU资源都消耗在了哪里。反之，如果15分钟平均负载很高，1分钟平均负载较低，则有可能是CPU资源紧张时刻已经过去。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">上面例子中的输出，可以看见最近1分钟的平均负载非常高，且远高于最近15分钟负载，因此我们需要继续排查当前系统中有什么进程消耗了大量的资源。可以通过下文将会介绍的vmstat、mpstat等命令进一步排查。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">dmesg | tail</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ dmesg | tail [1880957.563150] perl invoked oom-killer: gfp_mask=0x280da, order=0, oom_score_adj=0 [...] [1880957.563400] Out of memory: Kill process 18694 (perl) score 246 or sacrifice child [1880957.563408] Killed process 18694 (perl) total-vm:1972392kB, anon-rss:1953348kB, file-rss:0kB [2320864.954447] TCP: Possible SYN flooding on port 7001. Dropping request.  Check SNMP counters. </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">该命令会输出系统日志的最后10行。示例中的输出，可以看见一次内核的oom kill和一次TCP丢包。这些日志可以帮助排查性能问题。千万不要忘了这一步。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">vmstat 1</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ vmstat 1 procs ---------memory---------- ---swap-- -----io---- -system-- ------cpu-----  r  b swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st 34  0    0 200889792  73708 591828    0    0     0     5    6   10 96  1  3  0  0 32  0    0 200889920  73708 591860    0    0     0   592 13284 4282 98  1  1  0  0 32  0    0 200890112  73708 591860    0    0     0     0 9501 2154 99  1  0  0  0 32  0    0 200889568  73712 591856    0    0     0    48 11900 2459 99  0  0  0  0 32  0    0 200890208  73712 591860    0    0     0     0 15898 4840 98  1  1  0  0 ^C </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">vmstat(8) 命令，每行会输出一些系统核心指标，这些指标可以让我们更详细的了解系统状态。后面跟的参数1，表示每秒输出一次统计信息，表头提示了每一列的含义，这几介绍一些和性能调优相关的列：</p><ul style="margin: 0px 0px 15px 10px; padding: 0px; border: 0px; clear: left; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">r：等待在CPU资源的进程数。这个数据比平均负载更加能够体现CPU负载情况，数据中不包含等待IO的进程。如果这个数值大于机器CPU核数，那么机器的CPU资源已经饱和。</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">free：系统可用内存数（以千字节为单位），如果剩余内存不足，也会导致系统性能问题。下文介绍到的free命令，可以更详细的了解系统内存的使用情况。</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">si, so：交换区写入和读取的数量。如果这个数据不为0，说明系统已经在使用交换区（swap），机器物理内存已经不足。</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">us, sy, id, wa, st：这些都代表了CPU时间的消耗，它们分别表示用户时间（user）、系统（内核）时间（sys）、空闲时间（idle）、IO等待时间（wait）和被偷走的时间（stolen，一般被其他虚拟机消耗）。</li></ul><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">上述这些CPU时间，可以让我们很快了解CPU是否出于繁忙状态。一般情况下，如果用户时间和系统时间相加非常大，CPU出于忙于执行指令。如果IO等待时间很长，那么系统的瓶颈可能在磁盘IO。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">示例命令的输出可以看见，大量CPU时间消耗在用户态，也就是用户应用程序消耗了CPU时间。这不一定是性能问题，需要结合r队列，一起分析。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">mpstat -P ALL 1</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ mpstat -P ALL 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015  _x86_64_ (32 CPU) 07:38:49 PM  CPU   %usr  %nice   %sys %iowait   %irq  %soft  %steal  %guest  %gnice  %idle 07:38:50 PM  all  98.47   0.00   0.75    0.00   0.00   0.00    0.00    0.00    0.00   0.78 07:38:50 PM    0  96.04   0.00   2.97    0.00   0.00   0.00    0.00    0.00    0.00   0.99 07:38:50 PM    1  97.00   0.00   1.00    0.00   0.00   0.00    0.00    0.00    0.00   2.00 07:38:50 PM    2  98.00   0.00   1.00    0.00   0.00   0.00    0.00    0.00    0.00   1.00 07:38:50 PM    3  96.97   0.00   0.00    0.00   0.00   0.00    0.00    0.00    0.00   3.03 [...] </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">该命令可以显示每个CPU的占用情况，如果有一个CPU占用率特别高，那么有可能是一个单线程应用程序引起的。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">pidstat 1</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ pidstat 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015    _x86_64_    (32 CPU) 07:41:02 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command 07:41:03 PM     0         9    0.00    0.94    0.00    0.94     1  rcuos/0 07:41:03 PM     0      4214    5.66    5.66    0.00   11.32    15  mesos-slave 07:41:03 PM     0      4354    0.94    0.94    0.00    1.89     8  java 07:41:03 PM     0      6521 1596.23    1.89    0.00 1598.11    27  java 07:41:03 PM     0      6564 1571.70    7.55    0.00 1579.25    28  java 07:41:03 PM 60004     60154    0.94    4.72    0.00    5.66     9  pidstat 07:41:03 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command 07:41:04 PM     0      4214    6.00    2.00    0.00    8.00    15  mesos-slave 07:41:04 PM     0      6521 1590.00    1.00    0.00 1591.00    27  java 07:41:04 PM     0      6564 1573.00   10.00    0.00 1583.00    28  java 07:41:04 PM   108      6718    1.00    0.00    0.00    1.00     0  snmp-pass 07:41:04 PM 60004     60154    1.00    4.00    0.00    5.00     9  pidstat ^C </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">pidstat命令输出进程的CPU占用率，该命令会持续输出，并且不会覆盖之前的数据，可以方便观察系统动态。如上的输出，可以看见两个JAVA进程占用了将近1600%的CPU时间，既消耗了大约16个CPU核心的运算资源。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">iostat -xz 1</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ iostat -xz 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015  _x86_64_ (32 CPU) avg-cpu:  %user   %nice %system %iowait  %steal   %idle           73.96    0.00    3.73    0.03    0.06   22.21 Device:   rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util xvda        0.00     0.23    0.21    0.18     4.52     2.08    34.37     0.00    9.98   13.80    5.42   2.44   0.09 xvdb        0.01     0.00    1.02    8.94   127.97   598.53   145.79     0.00    0.43    1.78    0.28   0.25   0.25 xvdc        0.01     0.00    1.02    8.86   127.79   595.94   146.50     0.00    0.45    1.82    0.30   0.27   0.26 dm-0        0.00     0.00    0.69    2.32    10.47    31.69    28.01     0.01    3.23    0.71    3.98   0.13   0.04 dm-1        0.00     0.00    0.00    0.94     0.01     3.78     8.00     0.33  345.84    0.04  346.81   0.01   0.00 dm-2        0.00     0.00    0.09    0.07     1.35     0.36    22.50     0.00    2.55    0.23    5.62   1.78   0.03 [...] ^C </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">iostat命令主要用于查看机器磁盘IO情况。该命令输出的列，主要含义是：</p><ul style="margin: 0px 0px 15px 10px; padding: 0px; border: 0px; clear: left; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">r/s, w/s, rkB/s, wkB/s：分别表示每秒读写次数和每秒读写数据量（千字节）。读写量过大，可能会引起性能问题。</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">await：IO操作的平均等待时间，单位是毫秒。这是应用程序在和磁盘交互时，需要消耗的时间，包括IO等待和实际操作的耗时。如果这个数值过大，可能是硬件设备遇到了瓶颈或者出现故障。</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">avgqu-sz：向设备发出的请求平均数量。如果这个数值大于1，可能是硬件设备已经饱和（部分前端硬件设备支持并行写入）。</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">%util：设备利用率。这个数值表示设备的繁忙程度，经验值是如果超过60，可能会影响IO性能（可以参照IO操作平均等待时间）。如果到达100%，说明硬件设备已经饱和。</li></ul><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">如果显示的是逻辑设备的数据，那么设备利用率不代表后端实际的硬件设备已经饱和。值得注意的是，即使IO性能不理想，也不一定意味这应用程序性能会不好，可以利用诸如预读取、写缓存等策略提升应用性能。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">free &#8211;m</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ free -m              total       used       free     shared    buffers     cached Mem:        245998      24545     221453         83         59        541 -/+ buffers/cache:      23944     222053 Swap:            0          0          0 </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">free命令可以查看系统内存的使用情况，-m参数表示按照兆字节展示。最后两列分别表示用于IO缓存的内存数，和用于文件系统页缓存的内存数。需要注意的是，第二行-/+ buffers/cache，看上去缓存占用了大量内存空间。这是Linux系统的内存使用策略，尽可能的利用内存，如果应用程序需要内存，这部分内存会立即被回收并分配给应用程序。因此，这部分内存一般也被当成是可用内存。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">如果可用内存非常少，系统可能会动用交换区（如果配置了的话），这样会增加IO开销（可以在iostat命令中提现），降低系统性能。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">sar -n DEV 1</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ sar -n DEV 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015     _x86_64_    (32 CPU) 12:16:48 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil 12:16:49 AM      eth0  18763.00   5032.00  20686.42    478.30      0.00      0.00      0.00      0.00 12:16:49 AM        lo     14.00     14.00      1.36      1.36      0.00      0.00      0.00      0.00 12:16:49 AM   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00 12:16:49 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil 12:16:50 AM      eth0  19763.00   5101.00  21999.10    482.56      0.00      0.00      0.00      0.00 12:16:50 AM        lo     20.00     20.00      3.25      3.25      0.00      0.00      0.00      0.00 12:16:50 AM   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00 ^C </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">sar命令在这里可以查看网络设备的吞吐率。在排查性能问题时，可以通过网络设备的吞吐量，判断网络设备是否已经饱和。如示例输出中，eth0网卡设备，吞吐率大概在22 Mbytes/s，既176 Mbits/sec，没有达到1Gbit/sec的硬件上限。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">sar -n TCP,ETCP 1</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ sar -n TCP,ETCP 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015    _x86_64_    (32 CPU) 12:17:19 AM  active/s passive/s    iseg/s    oseg/s 12:17:20 AM      1.00      0.00  10233.00  18846.00 12:17:19 AM  atmptf/s  estres/s retrans/s isegerr/s   orsts/s 12:17:20 AM      0.00      0.00      0.00      0.00      0.00 12:17:20 AM  active/s passive/s    iseg/s    oseg/s 12:17:21 AM      1.00      0.00   8359.00   6039.00 12:17:20 AM  atmptf/s  estres/s retrans/s isegerr/s   orsts/s 12:17:21 AM      0.00      0.00      0.00      0.00      0.00 ^C </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">sar命令在这里用于查看TCP连接状态，其中包括：</p><ul style="margin: 0px 0px 15px 10px; padding: 0px; border: 0px; clear: left; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">active/s：每秒本地发起的TCP连接数，既通过connect调用创建的TCP连接；</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">passive/s：每秒远程发起的TCP连接数，即通过accept调用创建的TCP连接；</li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;">retrans/s：每秒TCP重传数量；</li></ul><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">TCP连接数可以用来判断性能问题是否由于建立了过多的连接，进一步可以判断是主动发起的连接，还是被动接受的连接。TCP重传可能是因为网络环境恶劣，或者服务器压力过大导致丢包。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">top</span></p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">$ top top - 00:15:40 up 21:56,  1 user,  load average: 31.09, 29.87, 29.92 Tasks: 871 total,   1 running, 868 sleeping,   0 stopped,   2 zombie %Cpu(s): 96.8 us,  0.4 sy,  0.0 ni,  2.7 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st KiB Mem:  25190241+total, 24921688 used, 22698073+free,    60448 buffers KiB Swap:        0 total,        0 used,        0 free.   554208 cached Mem    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND  20248 root      20   0  0.227t 0.012t  18748 S  3090  5.2  29812:58 java   4213 root      20   0 2722544  64640  44232 S  23.5  0.0 233:35.37 mesos-slave  66128 titancl+  20   0   24344   2332   1172 R   1.0  0.0   0:00.07 top   5235 root      20   0 38.227g 547004  49996 S   0.7  0.2   2:02.74 java   4299 root      20   0 20.015g 2.682g  16836 S   0.3  1.1  33:14.42 java      1 root      20   0   33620   2920   1496 S   0.0  0.0   0:03.82 init      2 root      20   0       0      0      0 S   0.0  0.0   0:00.02 kthreadd      3 root      20   0       0      0      0 S   0.0  0.0   0:05.35 ksoftirqd/0      5 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H      6 root      20   0       0      0      0 S   0.0  0.0   0:06.94 kworker/u256:0      8 root      20   0       0      0      0 S   0.0  0.0   2:38.05 rcu_sched </pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">top命令包含了前面好几个命令的检查的内容。比如系统负载情况（uptime）、系统内存使用情况（free）、系统CPU使用情况（vmstat）等。因此通过这个命令，可以相对全面的查看系统负载的来源。同时，top命令支持排序，可以按照不同的列排序，方便查找出诸如内存占用最多的进程、CPU占用率最高的进程等。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">但是，top命令相对于前面一些命令，输出是一个瞬间值，如果不持续盯着，可能会错过一些线索。这时可能需要暂停top命令刷新，来记录和比对数据。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;"><span style="font-weight: 600; margin: 0px; border: 0px; padding: 0px;">总结</span></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">排查Linux服务器性能问题还有很多工具，上面介绍的一些命令，可以帮助我们快速的定位问题。例如前面的示例输出，多个证据证明有JAVA进程占用了大量CPU资源，之后的性能调优就可以针对应用程序进行。</p><hr style="margin: 0px; border: 0px; padding: 0px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;" /><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">感谢<a href="http://www.infoq.com/cn/author/%E5%BE%90%E5%B7%9D" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">徐川</a>对本文的审校。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; line-height: 1.8; clear: both; width: 610px; font-family: 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 微软雅黑, STHeiti, 'WenQuanYi Micro Hei', SimSun, Helvetica, sans-serif; background-color: #ffffff;">给InfoQ中文站投稿或者参与内容翻译工作，请邮件至<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#100;&#105;&#116;&#111;&#114;&#115;&#64;&#99;&#110;&#46;&#105;&#110;&#102;&#111;&#113;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">editors@cn.infoq.com</a>。也欢迎大家通过新浪微博（<a href="http://www.weibo.com/infoqchina" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">@InfoQ</a>，<a href="http://weibo.com/u/1451714913" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">@丁晓昀</a>），微信（微信号：<a href="http://weixin.sogou.com/gzh?openid=oIWsFt0HnZ93MfLi3pW2ggVJFRxY" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">InfoQChina</a>）关注我们，并与我们的编辑和其他读者朋友交流（欢迎加入InfoQ读者交流群<a href="http://shang.qq.com/wpa/qunwpa?idkey=cc82a73d7522f0090aa3cbb6a8f4bdafa8b82177f481014c976a8740d927997a" target="_blank" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;"><img _href="img://group.png" _p="true" align="middle" alt="InfoQ好读者" border="0" src="http://pub.idqqimg.com/wpa/images/group.png" title="InfoQ好读者" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px; max-width: 100%;" /></a>（已满），InfoQ读者交流群（#2）</p><img src ="http://www.blogjava.net/xiaomage234/aggbug/428827.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-12-25 16:30 <a href="http://www.blogjava.net/xiaomage234/archive/2015/12/25/428827.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>btrace一些你不知道的事(源码入手)【转】</title><link>http://www.blogjava.net/xiaomage234/archive/2015/12/18/428731.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 18 Dec 2015 10:08:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/12/18/428731.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/428731.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/12/18/428731.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/428731.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/428731.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: from:http://agapple.iteye.com/blog/1005918背景&nbsp;&nbsp;&nbsp; 周五下班回家，在公司班车上觉得无聊，看了下btrace的源码(自己反编译)。 一些关于btrace的基本内容，可以看下我早起的一篇记录：btrace记忆&nbsp;&nbsp;&nbsp; 上一篇主要介绍的是btrace的一些基本使用以及api，这里我想从btrace源码本...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2015/12/18/428731.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/428731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-12-18 18:08 <a href="http://www.blogjava.net/xiaomage234/archive/2015/12/18/428731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BTrace使用简介</title><link>http://www.blogjava.net/xiaomage234/archive/2015/12/18/428725.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 18 Dec 2015 06:52:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/12/18/428725.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/428725.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/12/18/428725.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/428725.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/428725.html</trackback:ping><description><![CDATA[<p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">很多时候在online的应用出现问题时，很多时候我们需要知道更多的程序的运行细节，但又不可能在开发的时候就把程序中所有的运行细节都打印到日志上，通常这个时候能采取的就是修改代码，重新部署，然后再观察，但这种方法对于online应用来说不是很好，另外一方面如果碰到不好改的代码，例如引用的其他的外部的包什么的，就很麻烦了，BTrace就是一个可以在不改代码、不重启应用的情况下，动态的查看程序运行细节的工具，其官方网站在此：http://kenai.com/projects/btrace/ ，在这篇blog中，就来看看如何用BTrace来动态的监测方法的一些运行细节状况。<br />BTrace通过动态的挂接用java写的代码到运行时上来获取到一些运行细节，例如典型的使用btrace的方法为：<br />btrace -cp [btrace的jar所在的路径，默认为btrace/build下] [pid] [需要运行的java代码]<br />例如一段这样的代码：<br />[java]<br />import java.util.Random;<br />public class Case1{</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">public static void main(String[] args) throws Exception{<br />Random random=new Random();<br />CaseObject object=new CaseObject();<br />boolean result=true;<br />while(result){<br />result=object.execute(random.nextInt(1000));<br />Thread.sleep(1000);<br />}<br />}</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">}<br />public class CaseObject{</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">private static int sleepTotalTime=0;</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">public boolean execute(int sleepTime) throws Exception{<br />System.out.println("sleep: "+sleepTime);<br />sleepTotalTime+=sleepTime;<br />Thread.sleep(sleepTime);<br />return true;<br />}</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">}<br />[/java]<br />如在程序运行的情况下，想知道调用CaseObject的execute方法的以下几种情况，在BTrace中可以这么做：<br />1、调用此方法时传入的是什么参数，返回的是什么值，当时sleepTotalTime是多少？<br />BTrace脚本如下：<br />[java]<br />import static com.sun.btrace.BTraceUtils.*;<br />import com.sun.btrace.annotations.*;</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">@BTrace public class TraceMethodArgsAndReturn{<br />@OnMethod(<br />clazz="CaseObject",<br />method="execute",<br />location=@Location(Kind.RETURN)<br />)<br />public static void traceExecute(@Self CaseObject instance,int sleepTime,@Return boolean result){<br />println("call CaseObject.execute");<br />println(strcat("sleepTime is:",str(sleepTime)));<br />println(strcat("sleepTotalTime is:",str(get(field("CaseObject","sleepTotalTime"),instance))));<br />println(strcat("return value is:",str(result)));<br />}<br />}<br />[/java]<br />然后直接执行btrace -cp btrace/build [pid] TraceMethodArgsAndReturn.java就可以了。<br />当程序中调用到caseobject的execute方法时，就会在btrace的console中输出相应的信息。<br />2、execute方法执行耗时是多久？<br />BTrace脚本如下：<br />[java]<br />import static com.sun.btrace.BTraceUtils.*;<br />import com.sun.btrace.annotations.*;</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">@BTrace public class TraceMethodExecuteTime{</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">@TLS static long beginTime;</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">@OnMethod(<br />clazz="CaseObject",<br />method="execute"<br />)<br />public static void traceExecuteBegin(){<br />beginTime=timeMillis();<br />}</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">@OnMethod(<br />clazz="CaseObject",<br />method="execute",<br />location=@Location(Kind.RETURN)<br />)<br />public static void traceExecute(int sleepTime,@Return boolean result){<br />println(strcat(strcat("CaseObject.execute time is:",str(timeMillis()-beginTime)),"ms"));<br />}<br />}<br />[/java]<br />3、谁调用了execute方法？<br />BTrace脚本如下：<br />[java]<br />import static com.sun.btrace.BTraceUtils.*;<br />import com.sun.btrace.annotations.*;</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">@BTrace public class TraceMethodCallee{<br />@OnMethod(<br />clazz="CaseObject",<br />method="execute"<br />)<br />public static void traceExecute(){<br />println("who call CaseObject.execute :");<br />jstack();<br />}<br />}<br />[/java]<br />4、有没有人调用CaseObject中的哪一行代码？<br />BTrace脚本如下：<br />[java]<br />import static com.sun.btrace.BTraceUtils.*;<br />import com.sun.btrace.annotations.*;</p><p style="margin: 0.25em 0px 0.75em; padding: 0px; line-height: 19.5px; color: #333333; font-family: 'Segoe UI', Calibri, 'Myriad Pro', Myriad, 'Trebuchet MS', Helvetica, Arial, sans-serif; font-size: 13px; background-color: #ffffff;">@BTrace public class TraceMethodLine{<br />@OnMethod(<br />clazz="CaseObject",<br />location=@Location(value=Kind.LINE,line=5)<br />)<br />public static void traceExecute(@ProbeClassName String pcn,@ProbeMethodName String pmn,int line){<br />println(strcat(strcat(strcat("call ",pcn),"."),pmn));<br />}<br />}<br />[/java]<br />从上面可看出，在有了BTrace后，要动态的跟踪代码的运行细节还是非常爽的，更多的细节的操作请大家查看BTrace的<a href="http://kenai.com/projects/btrace/pages/UserGuide" style="text-decoration: none; outline: none; color: #2d8ac7;">User Guide</a>。</p><img src ="http://www.blogjava.net/xiaomage234/aggbug/428725.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-12-18 14:52 <a href="http://www.blogjava.net/xiaomage234/archive/2015/12/18/428725.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>性能和可维护性的话题</title><link>http://www.blogjava.net/xiaomage234/archive/2015/09/25/427498.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 25 Sep 2015 09:00:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/09/25/427498.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/427498.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/09/25/427498.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/427498.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/427498.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 作者：自由飞网址：http://www.cnblogs.com/freeflying/p/4788494.html我们在上一篇《架构之路（1）：目标》中设定了架构的目标，只有一个，就是可维护性。完全没有提性能，这是故意的。似乎程序员都是急性子，或许是被windows冗长的开机时间折磨够了，有可能是因为提升性能的效果是最显而易见的&#8230;&#8230;总之，我发现，绝大部分程序员对性能的关注和...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2015/09/25/427498.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/427498.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-09-25 17:00 <a href="http://www.blogjava.net/xiaomage234/archive/2015/09/25/427498.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高并发 Web 服务的演变：节约系统内存和 CPU</title><link>http://www.blogjava.net/xiaomage234/archive/2015/09/25/427496.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 25 Sep 2015 08:48:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/09/25/427496.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/427496.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/09/25/427496.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/427496.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/427496.html</trackback:ping><description><![CDATA[from:<span style="color: #00b050; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif; line-height: 22.4px; white-space: pre-wrap; background-color: #ffffff;">http://blog.jobbole.com/91749/<br /><br /></span><h3 style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 20px; font-stretch: normal; line-height: 30px; font-family: 'Microsoft YaHei', 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #2e2e2e; background-color: #ffffff;">一、越来越多的并发连接数</h3><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">现在的Web系统面对的并发连接数在近几年呈现指数增长，高并发成为了一种常态，给Web系统带来不小的挑战。以最简单粗暴的方式解决，就是增加Web系统的机器和升级硬件配置。虽然现在的硬件越来越便宜，但是一味地通过增加机器来解决并发量的增长，成本是非常高昂的。结合技术优化方案，才是更有效的解决方法。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">并发连接数为什么呈指数增长？实际上，从这几年的用户基数上看，这个数量并没有出现指数增长，因此它并非主要原因。主要原因，还是web变得更复杂，交互更丰富所导致的。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">1. 页面元素增多，交互复杂</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">Web页面元素越来越多，更为丰富。更多的资源元素，意味着更多的下载请求。Web系统的交互越来越复杂，交互场景和次数也大幅增加。以&#8220;www.qq.com&#8221;的首页为例子，刷新一次，大概会有244个请求。并且，在页面打开完成之后，还会有一些定时的查询或者上报请求持续运作。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww4.sinaimg.cn/mw690/5e4d414cgw1ewa0vduimsj20b401y0t8.jpg" width="400" height="70" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">目前的Http请求，为了减少反复的创建和销毁连接行为，通常都建立长连接（Connection keep-alive）。一经建立，这个连接会被保持住一段时间，被后续请求复用。然而，它也带来了另一个新的问题，连接的保持是会占用Web系统服务端资源的，如果不充分使用这个连接，会导致资源浪费。长连接被创建后，首批资源传输完毕，之后几乎没有数据交互，一直到超时时间，才会自动释放长连接占据的系统资源。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww3.sinaimg.cn/mw690/5e4d414cgw1ewa0ve7u3ej20dc0c83yu.jpg" width="480" height="440" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">除此之外，还有一些Web需求本身就需要长期保持连接的，例如Web socket。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">2. 主流的本浏览器的连接数在增加</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">面对越来越丰富的Web资源，主流浏览器并发连接数也在增加，同一个域下，早期的浏览器一般只有1-2个下载连接，而目前的主流浏览器通常在2-6个。增加浏览器并发连接数目，在需要下载资源比较多的场景下，可以加快页面的加载速度。更多的连接对浏览器加载页面元素是有好处的，在某些连接遭遇&#8220;网络阻塞&#8221;的情况下，其他正常的下载连接可以继续工作。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">这样自然无形增加了Web系统后端的压力，更多的下载连接意味着占据了更多的Web服务器的资源。而在用户访问高峰期，自热而然就形成了&#8220;高并发&#8221;场景。这些连接和请求，占据了服务器的大量CPU和内存等资源。尤其在资源数目超过100+的网站页面中，使用更多的下载连接，非常有必要。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">&nbsp;</p><h3 style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 20px; font-stretch: normal; line-height: 30px; font-family: 'Microsoft YaHei', 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #2e2e2e; background-color: #ffffff;">二、Web前端优化，降低服务端压力</h3><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">在缓解&#8220;高并发&#8221;的压力，需要前端和后端的共同配合优化，才能达到最大效果。在用户第一线的Web前端，可以起到减少或者减轻Http请求的效果。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">1. 减少Web请求</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">常用的实现方法是通过Http协议头中的expire或max-age来控制，将静态内容放入浏览器的本地缓存，在之后的一段时间里，不再请求Web服务器，直接使用本地资源。还有HTML5中的本地存储技术（LocalStorage），也被作为一个强大的数据本地缓存。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww1.sinaimg.cn/mw690/5e4d414cgw1ewa0vek7k8j20cs079t8v.jpg" width="460" height="261" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">这种方案缓存后，根本不发送请求到Web服务器，大幅降低服务器压力，也带来了良好的用户体验。但是，这种方案，对首次访问的用户无效，同时，也影响部分Web资源的实时性。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">2. 减轻Web请求</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">浏览器的本地缓存是存在过期时间的，一旦过期，就必须重新向服务器请求。这个时候，会有两种情形：</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（1）服务器的资源内容没有更新，浏览器请求Web资源，服务器回复&#8220;可以继续使用本地缓存&#8221;。（发生通信，但是Web服务器只需要做简单&#8220;回复&#8221;）</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（2）服务器的文件或者内容已经更新，浏览器请求Web资源，Web服务器通过网络传输新的资源内容。（发生通信，Web服务器需要完成复杂的传输工作）</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">这里的协商方式是通过Http协议的Last-Modified或Etag来控制，这个时候请求服务器，如果是内容没有发生变更的情况，服务器会返回304 Not Modified。这样的话，就不需要每次请求Web服务器都做复杂的传输完整数据文件的工作，只要简单的http应答就可以达到相同的效果。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww1.sinaimg.cn/mw690/5e4d414cgw1ewa0vezaqrj20cn07sweu.jpg" width="455" height="280" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">虽然上述请求，起到&#8220;减轻&#8221;Web服务器的压力，但是连接仍然被建立，请求也发生了。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">3. 合并页面请求</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">如果是比较老一些的Web开发者，应该会更有印象，在ajax盛行之前。页面大部分都是直接输出的，并没有这么多的ajax请求，Web后端将页面内容完全拼凑好了，再返回给前端。那个时候，页面静态化，是一个挺广泛的优化方式。后来，被交互更友好的ajax渐渐替代了，一个页面的请求也变得越来越多。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">由于移动端的网络（2G/3G）比起PC宽带差很多，并且部分手机配置比较低，面对一个超过100个请求的网页，加载的速度会缓慢很多。于是，优化的方向又重新回到合并页面元素，减少请求数量：</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（1）合并HTML展示内容。将CSS和JS直接嵌入到HTML页面内，不通过连接的方式引入。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（2）Ajax动态内容合并请求。对于动态内容，将10次Ajax请求合并为1次的批量信息查询。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（3）小图片合并，通过CSS的偏移量技术Sprites，将很多小图片合并为一张。这个优化方式，在PC端的Web优化中，也非常常见。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww3.sinaimg.cn/mw690/5e4d414cgw1ewa0vfpnuxj20b40j6aak.jpg" width="400" height="690" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">合并请求，减少了传输数据的次数，也就是相当于将它们从一个一个地请求，变为一次的&#8220;批量&#8221;请求。上述优化方法，到达&#8220;减轻&#8221;Web服务器压力的目的，减少了需要建立的连接。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">&nbsp;</p><h3 style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 20px; font-stretch: normal; line-height: 30px; font-family: 'Microsoft YaHei', 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #2e2e2e; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">三、 节约Web服务端的内存</strong></h3><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">前端的优化完成，我们就需要着眼于Web服务端本身。内存是Web服务器非常重要的资源，更多的内存通常意味着可以同时放入更多的工作任务。就Web服务占用内存而言，可以粗略划分：</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（1）用来维持连接的基本内存，进程初始化时，会载入一些基础模块到内存。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（2）被传输的数据内容载入到各个缓冲区，占据的内存。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（3）程序执行过程中，申请和使用的内存。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">如果维持一个连接，能够尽可能少占用内存，那么我们就可以维持更多的并发连接，从而让Web服务器支持更多的并发连接数。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">Apache（httpd）是一个成熟并且古老的Web服务，而Apache的发展和演变，一直在追求做到这一点，它试图不断减少服务占据的内存，以支持更大的并发量。以Apache的工作模式的演变为视角，我们一起来看看，它们是如何优化内存的问题的。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">1. prefork MPM，多进程工作模式</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">prefork是Apache最成熟和稳定的工作模式，即使是现在，仍然被广泛使用。主进程生成后，它先完成基础的初始化工作，然后，通过fork预先产生一批的子进程（子进程会复制父进程的内存空间，不需要再做基础的初始化工作）。然后等待服务，之所以预先生成，是为了减少频繁创建和销毁进程的开销。多进程的好处，是进程之间的内存数据不会相互干扰，同时，某个进程异常终止也不会影响其他进程。但是，就内存而言，每个httpd子进程占用了很多的内存，因为子进程的内存数据是复制父进程的。我们可以粗略认为，这里存在大量的&#8220;重复数据&#8221;被放在内存中。最终，导致我们能够生成的子进程最大数量是很有限。在面对高并发时，因为有不少Keep-alive的长连接，将这些子进程&#8220;霸占&#8221;住，很可能导致可用子进程耗尽。因此，prefork并不太适合高并发场景。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww1.sinaimg.cn/mw690/5e4d414cgw1ewa0vgh7amj20ev01y0sq.jpg" width="535" height="70" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><ul style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; list-style-position: outside; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><li style="border: 0px; margin: 0px 0px 5px 30px; padding: 0px;">优点：成熟稳定，兼容所有新老模块。同时，不需要担心线程安全的问题。（例如，我们常用的mod_php，将PHP编译为Apache的子模块，就不需要支持线程安全）</li><li style="border: 0px; margin: 0px 0px 5px 30px; padding: 0px;">缺点：一个服务进程占用很多内存。</li></ul><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">2. worker MPM，多进程和多线程的混合模式</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">worker模式比起prefork，是使用了多进程和多线程的混合模式。它也预先fork了几个子进程（数量很少），然后每个子进程创建一些线程（其中包括一个监听线程）。每个请求过来，会被分配到1个线程来服务。线程比起进程会更轻量，因为线程通常会共享父进程的内存空间，因此，内存的占用会减少一些。在高并发的场景下，因为比起prefork更省内存，因此会有更多的可用线程。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww1.sinaimg.cn/mw690/5e4d414cgw1ewa0vgyqmnj20fm0bwq39.jpg" width="562" height="428" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">但是，它并没有解决Keep-alive的长连接&#8220;霸占&#8221;线程的问题，只是对象变成了比较轻量的线程。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">有些人会觉得奇怪，那么这里为什么不完全使用多线程呢，还要引入多进程？因为还需要考虑稳定性，如果一个线程挂了，会导致同一个进程下其他正常的子线程都挂了。如果全部采用多线程，某个线程挂掉，就导致整个Apache服务&#8220;全军覆没&#8221;。而目前的工作模式，受影响的只是Apache的一部分服务，而不是整个服务。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">线程共享父进程的内存空间，减少了内存的占用，却又引起了新的问题。就是&#8220;线程安全&#8221;，多个线程修改共享资源导致的&#8220;竞争行为&#8221;，又强迫我们所使用的模块必须支持&#8220;线程安全&#8221;。因此，它有一定程度上增加Web服务的不稳定性。例如，mod_php所使用的PHP拓展，也同样需要支持&#8220;线程安全&#8221;，否则，不能在该模式下使用。</p><ul style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; list-style-position: outside; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><li style="border: 0px; margin: 0px 0px 5px 30px; padding: 0px;">优点：占据更少的内存，高并发下表现更优秀。</li><li style="border: 0px; margin: 0px 0px 5px 30px; padding: 0px;">缺点：必须考虑线程安全的问题，同时锁的引入又增加了CPU的开销。</li></ul><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">3. event MPM，多进程和多线程的混合模式，引入Epoll</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">这个是Apache中比较新的模式，在现在的版本（Apache 2.4.10）已经是稳定可用的模式。它和worker模式很像，最大的区别在于，它解决了keep-alive场景下，长期被占用的线程的资源浪费问题。event MPM中，会有一个专门的线程来管理这些keep-alive类型的线程，当有真实请求过来的时候，将请求传递给服务线程，执行完毕后，又允许它释放。它减少了&#8220;占据&#8221;连接而又不使用的资源浪费，增强了高并发场景下的请求处理能力。因为减少了&#8220;闲等&#8221;的线程，线程的数量减少，同等场景下，内存占用会下降一些。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww1.sinaimg.cn/mw690/5e4d414cgw1ewa0vhfc7yj20dm03w0st.jpg" width="490" height="140" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">event MPM在遇到某些不兼容的模块时，会失效，将会回退到worker模式，一个工作线程处理一个请求。新版Apache官方自带的模块，全部是支持event MPM的。注意一点，event MPM需要Linux系统（Linux 2.6+）对EPoll的支持，才能启用。Apache的三种模式中在真实应用场景中，event MPM是最节约内存的。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">4. 使用比较轻量的Nginx作为Web服务器</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">虽然Apache的不断优化，减少了内存占用，从而增加了处理高并发的能力。但是，正如前面所说，Apache是一个古老而成熟的Web服务，同时，集成很多稳定的模块，是一个比较重的Web服务。Nginx是个比较轻量的Web服务，占据的内存天然就少于Apache。而且，Nginx通过一个进程来服务于N个连接。所使用的方式，并不是Apache的增加进程/线程来支持更多的连接。对于Nginx来说，它少创建了大量的进程/线程，减少了很多内存的开销。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww4.sinaimg.cn/mw690/5e4d414cgw1ewa0vi4ol2j20d208cdfx.jpg" width="470" height="300" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">静态文件的QPS性能压测结果，Nginx性能大概3倍于Apache对静态文件的处理。PHP等动态文件的QPS，Nginx的做法通常是通过FastCGI的方式和PHP-FPM通信的方式完成，PHP作为一个与之无关的外部服务存在。而Apache通常将PHP编译为自己的字模块（新版的Apache也支持FastCGI）。PHP动态文件，Nginx的表现略逊于Apache。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">5. sendfile节约内存</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">Apache、Nginx等不少Web服务，都带有sendfile支持的。sendfile可以减少数据到&#8220;用户态内存空间&#8221;（用户缓冲区）的拷贝，进而减少内存的占用。当然，很多同学第一个反应当然是问Why？为了尽可能清楚讲述这个原理，我们就先回Linux内核态和用户态的存储空间的交互。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">一般情况下，用户态（也就是我们的程序所在的内存空间）是不会直接读写或者操作各种设备（磁盘、网络、终端等），中间通常用内核作为&#8220;中间人&#8221;，来完成对设备的操作或者读写。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">以最简单的磁盘读写例子，从磁盘中读取A文件，写入到B文件。A文件数据是从磁盘开始，然后载入到&#8220;内核缓冲区&#8221;，然后再拷贝到&#8220;用户缓冲区&#8221;，我们才可以对数据进行处理。写入的时候，也同理，从&#8220;用户态缓冲区&#8221;载入到&#8220;内核缓冲区&#8221;，最后写入到磁盘B文件。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww2.sinaimg.cn/mw690/5e4d414cgw1ewa0vibkrhj20e606ymxl.jpg" width="510" height="250" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">这样写文件很累吧，于是有人觉得这里可以跳过&#8220;用户缓冲区&#8221;的拷贝。其实，这就是MMP（Memory-Mapping，内存映射）的实现，建立一个磁盘空间和内存的直接映射，数据不再复制到&#8220;用户态缓冲区&#8221;，而是返回一个指向内存空间的指针。于是，我们之前的读写文件例子，就会变成，A文件数据从磁盘载入到&#8220;内核缓冲区&#8221;，然后从&#8220;内核缓冲区&#8221;复制到B文件的&#8220;内核缓冲区&#8221;，B文件再从&#8221;内核缓冲区&#8220;写回到磁盘中。这个过程，减少了一次内存拷贝，同时也少内存占用。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww2.sinaimg.cn/mw690/5e4d414cgw1ewa0viu3gaj208w08c3ys.jpg" width="320" height="300" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">好了，回到sendfile的话题上来，简单的说，sendfile的做法和MMP类似，就是减少数据从&#8221;内核态缓冲区&#8220;到&#8221;用户态缓冲区&#8220;的内存拷贝。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">默认的磁盘文件读取，到传输给socket，流程（不使用sendfile）是：</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww1.sinaimg.cn/mw690/5e4d414cgw1ewa0vjdjqvj20cn07sq36.jpg" width="455" height="280" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">使用sendfile之后：</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww4.sinaimg.cn/mw690/5e4d414cgw1ewa0vjqw0cj208707sjrj.jpg" width="295" height="280" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">这种方式，不仅节省了内存，而且还有CPU的开销。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">&nbsp;</p><h3 style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 20px; font-stretch: normal; line-height: 30px; font-family: 'Microsoft YaHei', 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #2e2e2e; background-color: #ffffff;">四、节约Web服务器的CPU</h3><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">对Web服务器而言，CPU是另一个非常核心的系统资源。虽然一般情况下，我们认为业务程序的执行消耗了我们主要CPU。但是，就Web服务程序而言，多线程/多进程的上下文切换，也是比较消耗CPU资源的。一个进程/线程通常不能长期占有CPU，当发生阻塞或者时间片用完，就无法继续占用CPU，这个时候，就会发生上下文切换，CPU时间片从老进程/线程切换到新的。除此之外，在并发连接数目很高的场景下，对这些用户建立的连接（socket文件描述符）状态的轮询和检测，也是比较消耗CPU的。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">而Apache和Nginx的发展和演变，也在努力减少CPU开销。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">1. Select/Poll（Apache早期版本的I/O多路复用）</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">通常，Web服务都要维护很多个和用户通信的socket文件描述符，I/O多路复用，其实就是为了方便对这些文件描述符的管理和检测。Apache早期版本，是使用select的模式，简单的说，就是将这些我们关注的socket文件描述符交给内核，让内核告诉我们，那些描述符可操作。Poll与select原理基本相同，因此放在一起，它们之间的区别，就不赘叙了哈。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">select/poll返回的是一个我们之前提交的文件描述符集合（内核将其中可读、可写或者异常状态的socket文件描述符的标识位修改了），我们需要通过轮询检查才能获得我们可以操作的文件描述符。在这个过程中，不断重复执行。在实际应用场景中，大部分被我们监控的socket文件描述符，都是&#8221;空闲的&#8220;，也就是说，不能操作。我们对整个集合轮询，就是为了找了少部分我们可以操作的socket文件描述符。于是，当我们监控的socket文件描述符越多（用户并发连接数越来越多），这个轮询工作，也就越来越沉重，进而导致增大了CPU的开销。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww2.sinaimg.cn/mw690/5e4d414cgw1ewa0vk9ajfj20ev0d2mxr.jpg" width="535" height="470" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">如果我们监控的socket文件描述符，几乎都是&#8221;活跃的&#8220;，反而使用这种模式更合适一点。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">2. Epoll（新版的Apache的event MPM，Nginx等支持）</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">Epoll是Linux2.6开始正式支持的I/O多路复用，我们可以理解为它是对select/poll的改进。首先，我们同样将我们关注的socket文件描述符集合告诉给内核，同时，给它们注册&#8221;回调函数&#8220;，如果某个socket文件准备好了，就通过回调函数通知我们。于是，我们就不需要专门去轮询整个全量的socket文件描述符集合，直接可以得到已经可操作的socket文件描述符。那么，那些大部分&#8221;空闲&#8220;的描述符，我们就不遍历了。即使我们监控的socket文件描述越来越多，我们轮询的也只是&#8221;活跃可操作&#8220;的socket文件描述符。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww3.sinaimg.cn/mw690/5e4d414cgw1ewa0vkmr7bj20go09smxn.jpg" width="600" height="352" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">其实，有一种极端点的场景，就是我们全部文件描述符几乎都是&#8221;活跃&#8220;的，这样反而导致了大量回调函数的执行，又增加了CPU的开销。但是，就Web服务的真实场景，绝大部分时候，都是连接集合中都存在很多&#8221;空闲&#8220;连接。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">3. 线程/进程的创建销毁和上下文切换</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">通常，Apache某一个时间内，是一个进程/线程服务于一个连接。于是，Apache就有很多的进程/线程，服务于很多的连接。Web服务在高峰期，会建立很多的进程/线程，也就带来很多的上下文切换开销。而Nginx，它通常只有1个master主进程和几个worker子进程，然后，1个worker进程服务很多个连接，进而节省了CPU的上下文切换开销。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww2.sinaimg.cn/mw690/5e4d414cgw1ewa0vl0xuyj20aq01sq3d.jpg" width="386" height="64" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">两种模式虽然不同，但实际上不能直接出分好坏，综合来说，各有各自的优势，就不妄议了哈。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">4. 多线程下的锁对CPU的开销</strong></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">Apache中的worker和event模式，都有采用多线程。多线程因为共享父进程的内存空间，在访问共享数据的时候，就会产生竞争，也就是线程安全问题。因此通常会引入锁（Linux下比较常用的线程相关的锁有互斥量metux，读写锁rwlock等），成功获取锁的线程可以继续执行，获取失败的通常选择阻塞等待。引入锁的机制，程序的复杂度往往增加不少，同时还有线程&#8220;死锁&#8221;或者&#8220;饿死&#8221;的风险（多进程在访问进程间共享资源的时候，也有同样的问题）。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">死锁现象（两个线程彼此锁住对方想要获取的资源，相互阻塞等待，永远无法达不到满足条件）：</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww2.sinaimg.cn/mw690/5e4d414cgw1ewa0vlds0sj20go04qjrj.jpg" width="600" height="170" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">饿死现象（某个线程，一直获取不到它想要锁资源，永远无法执行下一步）：</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww2.sinaimg.cn/mw690/5e4d414cgw1ewa0vly4rwj20b407874d.jpg" width="400" height="260" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">为了避免这些锁导致的问题，就不得不加大程序的复杂度，解决方案一般有：</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（1）对资源的加锁，根据约定好的顺序，大家都先对共享资源X加锁，加锁成功之后才能加锁共享资源Y。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">（2）如果线程占有资源X，却加锁资源Y失败，则放弃加锁，同时也释放掉之前占有的资源X。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">在使用PHP的时候，在Apache的worker和event模式下，也必须兼容线程安全。通常，新版本的PHP官方库是没有线程安全方面的问题，需要关注的是第三方扩展。PHP实现线程安全，不是通过锁的方式实现的。而是为每个线程独立申请一份全局变量的副本，相当于线程的私人内存空间，但是这样做相对消耗多一些内存。不过，这样的好处，是不需要引入复杂的锁机制实现，也避免了锁机制对CPU的开销。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">这里顺便提到一下，经常和Nginx搭配工作的PHP-FPM（FastCGI）使用的是多进程，因此不会有线程安全的问题。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><img class="alignnone" alt="" src="http://ww4.sinaimg.cn/mw690/5e4d414cgw1ewa0vm7demj207506eaa1.jpg" width="257" height="230" style="border: 0px; margin: auto; padding: 0px; font-size: 0px; color: transparent; vertical-align: middle; max-width: 100%; height: auto; display: block; clear: both;" /></p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">&nbsp;</p><h3 style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 20px; font-stretch: normal; line-height: 30px; font-family: 'Microsoft YaHei', 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #2e2e2e; background-color: #ffffff;"><strong style="border: 0px; margin: 0px; padding: 0px;">五、</strong>&nbsp;小结</h3><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">可能有些同学看完之后，会得出结论，Nginx+PHP-FPM的工作方式，似乎是最节省系统资源的Web系统工作方式。某种程度上说，的确是可以这么说的，但是Web系统的搭建，需要从实际业务应用的角度出发，具体问题需要具体分析，寻求最合适的技术方案。</p><p style="border: 0px; margin: 0px 0px 20px; padding: 0px; font-size: 15px; color: #2e2e2e; font-family: 'Microsoft YaHei', 宋体, 'Myriad Pro', Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">Web服务的不断演变和发展，努力地追求用尽可能少的系统资源，来支撑更多的用户请求，这是一条波澜壮阔的前进之路。这些技术方案，汇聚了很多值得学习和借鉴的解决问题的思路。</p><div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/427496.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-09-25 16:48 <a href="http://www.blogjava.net/xiaomage234/archive/2015/09/25/427496.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Thrift对多接口服务的支持  </title><link>http://www.blogjava.net/xiaomage234/archive/2015/03/18/423585.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Wed, 18 Mar 2015 09:22:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/03/18/423585.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/423585.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/03/18/423585.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/423585.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/423585.html</trackback:ping><description><![CDATA[<p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">Thrift在0.9.1版本之前，一直只提交了对单一接口服务的支持，即一个RPC服务器（对应一个端口）支持一个服务接口的实现。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">但是很多时候，我们的服务不能实现在一个接口里，一是接口里的方法越来越多，不好管理和使用；二是根据职责的单一要求，不能类型的方法，不能放在同一接口里。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">在 Thrift-0.9.1之前，我们要解决上面的问题，一是通过多个RPC服务器来实现，这个方法必然导致了我们RPC服务器越来越多，管理上麻烦；二是通过其他的一些途径，如使用netty作为RPC服务器等，这个方法实现上相对麻烦一些，需要去了解netty的知识。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">这些方法在这里就不再详述了。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">从Thrift-0.9.1开始，thrift开始提供对多接口服务的支持，使得我们开发多接口RPC服务相对简单多了。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">这里还是给出例子来说明。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">首先，我们做了两个接口定义文件来定义两个接口：</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><u>namespace</u>&nbsp;javacom.eli.test.service</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><u>struct</u>&nbsp;Topic</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">{</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; 1: i32&nbsp;<u>uid</u>,</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; 2: string name,</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; 3: string content</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">service TopicService</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">{</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; void store(1: Topic topic),</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; Topic retrieve(1: i32&nbsp;<u>uid</u>)</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><u>namespace</u>&nbsp;javacom.eli.test.service</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><u>struct</u>&nbsp;User</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">{</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; 1: i32&nbsp;<u>uid</u>,</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; 2: string name,</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; 3: string blurb</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">service UserService</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">{</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; void store1(1: User user),</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; User retrieve1(1: i32&nbsp;<u>uid</u>)</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">然后使用Thrift-0.9.1.exe生成相应的JavaBean及接口：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">然后，我们写两个接口的实现类：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">import org.apache.thrift.TException;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">import com.eli.test.service.Topic;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">import com.eli.test.service.TopicService;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">public class TopicImpl implements TopicService.Iface{</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">public void store(Topic topic)throws TException</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">{</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("theinput topic: ");</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("id:"+topic.getUid());</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("name:"+topic.getName());</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("content:"+topic.getContent());</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">public Topic retrieve(int uid)throws TException</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">{</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("theinput uid: "+uid);</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return newTopic(uid,"test","test");</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">import org.apache.thrift.TException;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">import com.eli.test.service.User;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">import com.eli.test.service.UserService;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">public class UserImpl implements UserService.Iface{</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">public void store1(User user)throws TException</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">{</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("theinput user: ");</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("uid:"+user.getUid());</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("name:"+user.getName());</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("blur:"+user.getBlurb());</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">public User retrieve1(int uid)throws TException</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">{</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("theinput uid: "+uid);</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return newUser(uid,"tom","123");</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">}</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">上述工作完成以后，就可以写服务器代码了。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">服务器代码与单一接口的服务器代码最大的不同是使用了&#8220;TMultiplexedProcessor&#8221;类，通过该类，可以注册多个接口的服务实现类：</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">TMultiplexedProcessor processor =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;TMultiplexedProcessor();</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; processor.registerProcessor(<span style="color: #2a00ff;">"TopicService"</span>,&nbsp;<strong><span style="color: #7f0055;">new</span></strong>TopicService.Processor&lt;TopicService.Iface&gt;(<strong><span style="color: #7f0055;">new</span></strong>TopicImpl()));</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; processor.registerProcessor(<span style="color: #2a00ff;">"UserService"</span>,&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;UserService.Processor&lt;UserService.Iface&gt;(<strong><span style="color: #7f0055;">new</span></strong>&nbsp;UserImpl()));</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">其他代码就跟以前的服务器代码一样了。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">完整的服务器代码如下：</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">TMultiplexedProcessor processor =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>TMultiplexedProcessor();</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TServerTransport t =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;TServerSocket(9090);</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TServer server =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;TThreadPoolServer(<strong><span style="color: #7f0055;">new</span></strong>TThreadPoolServer.Args(t).processor(processor));</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; processor.registerProcessor(<span style="color: #2a00ff;">"TopicService"</span>,&nbsp;<strong><span style="color: #7f0055;">new</span></strong>TopicService.Processor&lt;TopicService.Iface&gt;(<strong><span style="color: #7f0055;">new</span></strong>TopicImpl()));</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; processor.registerProcessor(<span style="color: #2a00ff;">"UserService"</span>,&nbsp;<strong><span style="color: #7f0055;">new</span></strong>UserService.Processor&lt;UserService.Iface&gt;(<strong><span style="color: #7f0055;">new</span></strong>UserImpl()));</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="color: #3f7f5f;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TSimpleServer server = new TSimpleServer(new&nbsp;<u>Args</u>(t).processor(processor));</span></p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.<em><span style="color: #0000c0;">out</span></em>.println(<span style="color: #2a00ff;">"the serveris started and is listening at 9090..."</span>);</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.serve();</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">最后是客户端代码了，客户端代码的不同之处是引入了&#8220;TMultiplexedProtocol&#8221;类，它来帮助客户端区别调用哪个接口：</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">TMultiplexedProtocol mp1 =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;<span style="background: silver;">TMultiplexedProtocol</span>(protocol,<span style="color: #2a00ff;">"TopicService"</span>);</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp; TopicService.Clientservice1 =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;TopicService.Client(mp1);</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">完整的客户端测试代码如下：</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TSocket transport =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;TSocket(<span style="color: #2a00ff;">"localhost"</span>,9090);</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TBinaryProtocol protocol =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;TBinaryProtocol(transport);</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background: silver;">TMultiplexedProtocol</span>mp1 =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;<span style="background: silver;">TMultiplexedProtocol</span>(protocol,<span style="color: #2a00ff;">"TopicService"</span>);</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TopicService.Client service1 =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>TopicService.Client(mp1);</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background: silver;">TMultiplexedProtocol</span>mp2 =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>&nbsp;<span style="background: silver;">TMultiplexedProtocol</span>(protocol,<span style="color: #2a00ff;">"UserService"</span>);</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UserService.Client service2 =&nbsp;<strong><span style="color: #7f0055;">new</span></strong>UserService.Client(mp2);</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transport.open();</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; service1.store(<strong><span style="color: #7f0055;">new</span></strong>&nbsp;Topic(668,<span style="color: #2a00ff;">"test topic"</span>,<span style="color: #2a00ff;">"just a test!"</span>));</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; service2.store1(<strong><span style="color: #7f0055;">new</span></strong>&nbsp;User(888,<span style="color: #2a00ff;">"tom"</span>,<span style="color: #2a00ff;">"haha"</span>));</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.<em><span style="color: #0000c0;">out</span></em>.println(service1.retrieve(168));</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.<em><span style="color: #0000c0;">out</span></em>.println(service2.retrieve1(999));</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transport.close();</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">运行服务器代码和客户端代码，则客户端会打印如下结果：</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">Topic(uid:168, name:test, content:test)</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">User(uid:999, name:tom, blurb:123)</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">服务器端会打印如下的信息：</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">the server is started and is listening at 9090...</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">the input topic:</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">id: 668</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">name: test topic</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">content: just a test!</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">the input user:</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">uid: 888</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">name: tom</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">blur: haha</p><p align="left" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">the input uid: 168</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">the input uid: 999</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">通过上面的例子，可以看到，Thrift-0.9.1版本很简单的解决了多接口服务的问题，随便说一下，该版本并没有解决所有语言的多接口实现问题，只是解决了Java和其他几个语言。请大家在使用的时候，查清楚您使用的语言是否解决了这个问题。</p><img src ="http://www.blogjava.net/xiaomage234/aggbug/423585.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-03-18 17:22 <a href="http://www.blogjava.net/xiaomage234/archive/2015/03/18/423585.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>nginx+Tomcat性能监控[转]</title><link>http://www.blogjava.net/xiaomage234/archive/2015/03/17/423514.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 17 Mar 2015 02:00:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/03/17/423514.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/423514.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/03/17/423514.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/423514.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/423514.html</trackback:ping><description><![CDATA[<div id="cnblogs_post_body" style="margin: 0px 0px 20px;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><strong>一、开启nginx的监控</strong></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">1)、nginx简单状态监控</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">在nginx.conf中添加如下代码即可监控nginx当前的状态，然后访问<a href="http://serverip/status" style="color: #8e1a10;">http://serverip/status</a>即可访问</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><span style="color: #ff0000;">location /status {</span><br /><span style="color: #ff0000;">stub_status on;</span><br /><span style="color: #ff0000;">access_log off;</span><br /><span style="color: #ff0000;">}</span></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">一般显示为</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><span style="color: #ff0000;">Active connections: 16&nbsp;</span><br /><span style="color: #ff0000;">server accepts handled requests</span><br /><span style="color: #ff0000;">191226 191226 305915&nbsp;</span><br /><span style="color: #ff0000;">Reading: 0 Writing: 1 Waiting: 15</span></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><span style="font-size: 13px;">ctive connections: 对后端发起的活动连接数.</span></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><span style="font-size: 13px;"><span style="font-family: Tahoma;">Server accepts handled requests: Nginx总共处理了24个连接,成功创建24次握手(证明中间没有失败的),总共处理了129个请求.</span></span></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><span style="font-size: 13px;"><span style="font-family: Tahoma;">Reading: Nginx 读取到客户端的Header信息数.</span></span></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><span style="font-size: 13px;"><span style="font-family: Tahoma;">Writing: Nginx 返回给客户端的Header信息数.</span></span></p><div style="margin: 0px;"><span style="font-size: 13px;"><span style="font-family: Tahoma;">Waiting: 开启keep-alive的情况下,这个值等于 active &#8211; (reading + writing),意思就是Nginx已经处理完成,正在等候下一次请求指令的驻留连接.</span></span></div><div style="margin: 0px;"><span style="color: #ff0000;"><span style="font-size: 13px;"><span style="font-family: Tahoma;">注意的，</span></span><span style="font-size: 12px;"><span style="font-family: Tahoma;">本模块默认是不会编译进Nginx的,如果你要使用该模块,则要在编译安装Nginx时指定:</span></span></span></div><div style="margin: 0px;"><span style="font-family: 宋体; font-size: 13px;">&nbsp;./configure &#8211;with-http_stub_status_module&nbsp;</span></div><div style="margin: 0px;"><span style="font-family: 宋体; font-size: 13px;">&nbsp;查看已安装的 Nginx 是否包含 stub_status 模块</span></div><div style="margin: 0px;"><span style="font-family: 宋体; font-size: 13px;">&nbsp;#/usr/local/nginx/sbin/nginx -V&nbsp;</span></div><div style="margin: 0px;"><span style="font-family: 宋体; font-size: 13px;">&nbsp;TLS SNI support disabled</span></div><div style="margin: 0px;"><span style="font-family: 宋体; font-size: 13px;">configure arguments: --prefix=/usr/local/nginx --user=www --group=www --with-http_stub_status_module --with-file-aio --with-http_ssl_module&nbsp;</span></div><div style="margin: 0px;">&nbsp;</div><div style="margin: 0px;"><span style="font-family: Tahoma;">2)、nginx的图形化监控-nginx-RRD stats</span></div><div style="margin: 0px;">&nbsp;</div><div style="margin: 0px;"><div style="margin: 0px;"><div style="margin: 0px;"><a href="http://www.nginx.eu/nginx-rrd.html" rel="nofollow" target="_blank" style="color: #8e1a10;">nginx-rrd</a>是<a href="http://nginx.net/" rel="nofollow" target="_blank" style="color: #8e1a10;">nginx</a>官方推荐的一款Nginx监控工具，利用<a href="http://www.nginx.eu/nginx-rrd.html" rel="nofollow" target="_blank" style="color: #8e1a10;">nginx-rrd</a>可以很方便的生成图表，便于我们查看。</div><div style="margin: 0px;"><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">1、运行环境(centos)：</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">在安装前需要安装好rrdtool这个画图工具和相应的perl模块，可以先运行：</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">yum&nbsp;install rrdtool libhtml-parser-perl libwww-perl librrds-perl librrd2-dev</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">确保rrdtool和相应的perl被安装上。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">2、安装配置</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">下载：wget&nbsp;<a href="http://soft.vpser.net/status/nginx-rrd/nginx-rrd-0.1.4.tgz" rel="nofollow" style="color: #8e1a10;">http://soft.vpser.net/status/nginx-rrd/nginx-rrd-0.1.4.tgz</a></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">解压：tar zxvf nginx-rrd-0.1.4.tgz</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">进入nginx-rrd目录，cd nginx-rrd-0.1.4/</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">复制主程序：cp usr/sbin/* /usr/sbin</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">复制配置文件cp etc/nginx-rrd.conf /etc</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">复制定时执行文件：cp etc/cron.d/nginx-rrd.cron /etc/cron.d</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">创建nginx-rrd生成目录：mkdir /home/wwwroot/nginx &amp;&amp; mkdir /home/wwwroot/nginx/rrd</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">cp html/index.php /home/wwwroot/nginx</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">编辑/home/wwwroot/nginx/index.php修改登录密码</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">&lt;?php<br />header("Content-Type: text/html; charset=utf-8");</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><span style="color: #ff0000;">$password = "admin";&nbsp;</span></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">.........</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">编辑配置文件nginx-rrd.conf，修改完成后如下：</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">#####################################################<br />#<br /># dir where rrd databases are stored<br />RRD_DIR="/home/wwwroot/nginx-rrd/";<br /># dir where png images are presented<br />WWW_DIR="/home/wwwroot/nginx/";<br /># process nice level<br />NICE_LEVEL="-19";<br /># bin dir<br />BIN_DIR="/usr/sbin";<br /># servers to test<br /># server_utl;server_name<br />SERVERS_URL="<a href="http://127.0.0.1/status;127.0.0.1" style="color: #8e1a10;">http://127.0.0.1/status;127.0.0.1</a>"</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">多个server，可以SERVERS_URL中空格分开，前部分为nginx_status的地址，后面为被监控主机的域名。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">SEVERS_URL 格式</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">注意，nginx.conf虚拟主机server{}中，需要已经加入：</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">location /status {<br />stub_status on;<br />access_log off;<br />}</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">以上设置就完成，可以自行运行一下：/usr/sbin/nginx-collect ，启动收集程序。cron会15分钟生成一次数据。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">如果没有定时执行生成数据，可以在/etc/crontab最后面加上：</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">* * * * * root /usr/sbin/nginx-collect<br />*/15 * * * * root /usr/sbin/nginx-graph</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">然后输入然后访问<a href="http://serverip/status" style="color: #8e1a10;">http://serverip/nginx/</a>即可访问。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">&nbsp;<img src="http://images.cnitblog.com/blog/397900/201302/04173932-30ea4f23621c4ef6b9fc2f8e12516ae5.x-png" alt="" style="border: 0px; width: 693px;" /></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><strong>二、开启tomcat的监控</strong></p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">1)、tomcat6的配置</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">修改tomcat/conftomcat-users.xml文件中&lt;/tomcat-users&gt;节点之前添加如下代码即可。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">&nbsp;&lt;user username="admin" password="admin" roles="manager"/&gt;</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">然后输入然后访问<a href="http://serverip/status" style="color: #8e1a10;">http://serverip:8080/manager/status</a>即可访问。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">2)tomcat7的配置</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">修改tomcat/conftomcat-users.xml文件中&lt;/tomcat-users&gt;节点之前添加如下代码即可。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">&lt;role rolename="manager-gui"/&gt;<br />&lt;user username="tomcat" admin="admin" roles="manager-gui"/&gt;</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;">然后输入然后访问<a href="http://serverip/status" style="color: #8e1a10;">http://serverip:8080/manager/status</a>即可访问。</p><p style="margin-top: 10px; margin-bottom: 10px; padding: 0px;"><img src="http://images.cnitblog.com/blog/397900/201302/04173919-dd668188ed3f46d597c23c7a62209407.x-png" alt="" style="border: 0px; width: 693px;" /></p></div></div></div></div><div style="margin: 0px; clear: both;"></div><div id="blog_post_info_block" style="margin: 20px 0px 0px;"><div id="BlogPostCategory" style="margin: 0px 0px 10px; font-family: Verdana; line-height: 25.2000007629395px; background-color: #ffffff;"></div><div id="EntryTag" style="margin: 20px 0px 0px; font-size: 9pt; color: gray; font-family: Verdana; background-color: #ffffff;">标签:&nbsp;<a href="http://www.cnblogs.com/wanghaosoft/tag/nginx%2BTomcat%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7/" style="color: #8e1a10;">nginx+Tomcat性能监控</a>,&nbsp;<a href="http://www.cnblogs.com/wanghaosoft/tag/nginx%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7/" style="color: #8e1a10;">nginx性能监控</a>,&nbsp;<a href="http://www.cnblogs.com/wanghaosoft/tag/tomcat%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7/" style="color: #8e1a10;">tomcat性能监控</a></div><div id="blog_post_info" style="margin: 0px; font-family: Verdana; line-height: 25.2000007629395px; background-color: #ffffff;"></div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/423514.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-03-17 10:00 <a href="http://www.blogjava.net/xiaomage234/archive/2015/03/17/423514.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Tomcat 并发优化[转]</title><link>http://www.blogjava.net/xiaomage234/archive/2015/03/17/423513.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 17 Mar 2015 01:59:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/03/17/423513.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/423513.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/03/17/423513.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/423513.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/423513.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 首先先介绍一款知名的网站压力测试工具:webbench.Webbench能测试处在相同硬件上，不同服务的性能以及不同硬件上同一个服务的运行状况。webbench的标准测试可以向我们展示服务器的两项内容：每分钟相应请求数和每秒钟传输数据量。webbench不但能具有便准静态页面的测试能力，还能对动态页面（ASP,PHP,JAVA,CGI）进 行测试的能力。还有就是他支持对含有SSL的安全网站例如电子...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2015/03/17/423513.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/423513.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-03-17 09:59 <a href="http://www.blogjava.net/xiaomage234/archive/2015/03/17/423513.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tomcat使用APR提高性能[转]</title><link>http://www.blogjava.net/xiaomage234/archive/2015/03/16/423496.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 16 Mar 2015 10:59:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/03/16/423496.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/423496.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/03/16/423496.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/423496.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/423496.html</trackback:ping><description><![CDATA[from： http://nll.im/post/tomcat-apr.html<br /><br /><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">操作系统:Centos6.3<br style="padding: 0px; margin: 0px;" />Tomcat:7.0.42<br style="padding: 0px; margin: 0px;" />JDK:1.6.0_45<br style="padding: 0px; margin: 0px;" />配置:8G,4核.</p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">最近tomcat负载比较高,默认的配置和bio的处理方式已经无力支撑.据说APR能提升50%~60%的性能,所以尝试下APR优化.<br style="padding: 0px; margin: 0px;" />APR介绍:</p><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; color: #34495e; background: none #ecf0f1;"><code style="padding: 3px 5px; margin: -3px 0px 0px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 14px; border-radius: 5px; color: #666666;">Tomcat可以使用APR来提供超强的可伸缩性和性能，更好地集成本地服务器技术。  APR(Apache Portable Runtime)是一个高可移植库，它是Apache HTTP Server 2.x的核心。  APR有很多用途，包括访问高级IO功能(例如sendfile,epoll和OpenSSL)，OS级别功能(随机数生成，系统状态等等)，本地进程管理(共享内存，NT管道和UNIX sockets)。这些功能可以使Tomcat作为一个通常的前台WEB服务器，能更好地和其它本地web技术集成，总体上让Java更有效率作为一个高性能web服务器平台而不是简单作为后台容器。  在产品环境中，特别是直接使用Tomcat做WEB服务器的时候，应该使用Tomcat Native来提高其性能。</code></pre><h2>1. 服务器安装GCC</h2><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">客户服务器连不上外网,服务器也没有gcc,所以先使用代理连接外网,修改<code style="padding: 3px 5px; margin: -3px 0px 0px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 14px; border-radius: 5px; color: #666666;">/etc/yum.conf</code>,加入代理配置:</p><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; color: #34495e; background: none #ecf0f1;"><code style="padding: 3px 5px; margin: -3px 0px 0px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 14px; border-radius: 5px; color: #666666;">proxy=http://10.103.0.46:808</code></pre><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">如果需要验证加入用户名密码:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;"><span style="padding: 0px; margin: 0px; color: #bb60d5;">proxy_username</span><span style="padding: 0px; margin: 0px; color: #666666;">=</span>代理服务器用户名 <span style="padding: 0px; margin: 0px; color: #bb60d5;">proxy_password</span><span style="padding: 0px; margin: 0px; color: #666666;">=</span>代理服务器密码 </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">yum使用163的Centos源:参考<a href="http://mirrors.163.com/.help/centos.html" style="padding: 0px; margin: 0px; color: #9c2525; text-decoration: none; transition: all 0.4s linear; -webkit-transition: all 0.4s linear; font-size: 13px;">http://mirrors.163.com/.help/centos.html</a><br style="padding: 0px; margin: 0px;" />先备份</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">然后下载<a href="http://mirrors.163.com/.help/CentOS6-Base-163.repo" style="padding: 0px; margin: 0px; color: #9c2525; text-decoration: none; transition: all 0.4s linear; -webkit-transition: all 0.4s linear; font-size: 13px;">CentOS6</a>&nbsp;放入到<code style="padding: 3px 5px; margin: -3px 0px 0px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 14px; border-radius: 5px; color: #666666;">/etc/yum.repos.d/</code>(操作前请做好相应备份)</p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">运行以下命令生成缓存:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">yum clean all yum makecache </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">然后安装<code style="padding: 3px 5px; margin: -3px 0px 0px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 14px; border-radius: 5px; color: #666666;">gcc</code>:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">yum -y install gcc </pre></div><hr style="padding: 0px; margin: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;" /><h2>2. 安装APR</h2><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">从<a href="http://apr.apache.org/download.cgi" style="padding: 0px; margin: 0px; color: #9c2525; text-decoration: none; transition: all 0.4s linear; -webkit-transition: all 0.4s linear; font-size: 13px;">http://apr.apache.org/download.cgi</a>下载apr,apr-util,apr-iconv. 因为服务器没有配置全局的http代理,只是yum代理,所以下载之后传到服务器即可.</p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">传输完安装apr:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">tar zxvf apr-1.5.1.tar.gz <span style="padding: 0px; margin: 0px; color: #007020;">cd </span>apr-1.5.1 ./configure --prefix<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr make make install </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">安装apr-iconv:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">tar zxvf apr-iconv-1.2.1.tar.gz <span style="padding: 0px; margin: 0px; color: #007020;">cd </span>apr-iconv-1.2.1 ./configure --prefix<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr-iconv --with-apr<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr make make install </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">安装apr-util:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">tar zxvf apr-util-1.5.3.tar.gz <span style="padding: 0px; margin: 0px; color: #007020;">cd </span>apr-util-1.5.3 ./configure --prefix<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr-util --with-apr<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr --with-apr-iconv<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr-iconv/bin/apriconv make   make install </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">安装tomcat-native:首先到tomcat/bin目录下,找到对应的tar文件.</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">tar zxvf tomcat-native.tar.gz <span style="padding: 0px; margin: 0px; color: #007020;">cd </span>tomcat-native-1.1.27-src/jni/native/ ./configure --with-apr<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr --with-java-home<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/lib/jdk1.6.0_45 make make install </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">安装完成之后 会出现如下提示信息</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">Libraries have been installed in: /usr/local/apr/lib </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">添加环境变量:&nbsp;<code style="padding: 3px 5px; margin: -3px 0px 0px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 14px; border-radius: 5px; color: #666666;">vi /etc/profile</code>在文件末尾处添加下面的变量</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;"><span style="padding: 0px; margin: 0px; color: #007020;">export </span><span style="padding: 0px; margin: 0px; color: #bb60d5;">LD_LIBRARY_PATH</span><span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr/lib </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">然后执行下面命令，使环境变量即时生效</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;"><span style="padding: 0px; margin: 0px; color: #007020;">source</span> /etc/profile </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">以下为完整安装脚本:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;"><span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">#setup apr</span> tar zxvf apr-1.5.1.tar.gz <span style="padding: 0px; margin: 0px; color: #007020;">cd </span>apr-1.5.1 ./configure --prefix<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr make <span style="padding: 0px; margin: 0px; color: #666666;">&amp;&amp;</span> make install <span style="padding: 0px; margin: 0px; color: #007020;">cd</span> ../ <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">#setup apr-iconv</span> tar zxvf apr-iconv-1.2.1.tar.gz <span style="padding: 0px; margin: 0px; color: #007020;">cd </span>apr-iconv-1.2.1/ ./configure --prefix<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr-iconv --with-apr<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr make <span style="padding: 0px; margin: 0px; color: #666666;">&amp;&amp;</span> make install <span style="padding: 0px; margin: 0px; color: #007020;">cd</span> ../ <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">#setup apr-util</span> tar zxvf apr-util-1.5.3.tar.gz <span style="padding: 0px; margin: 0px; color: #007020;">cd </span>apr-util-1.5.3 ./configure --prefix<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr-util --with-apr<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr --with-apr-iconv<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr-iconv/bin/apriconv make <span style="padding: 0px; margin: 0px; color: #666666;">&amp;&amp;</span> make install <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">#setup tomcat-native</span> <span style="padding: 0px; margin: 0px; color: #007020;">cd</span> /app/apache-tomcat-7.0.42/bin tar zxvf tomcat-native.tar.gz <span style="padding: 0px; margin: 0px; color: #007020;">cd </span>tomcat-native-1.1.27-src/jni/native ./configure --with-apr<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr --with-java-home<span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/lib/jdk1.6.0_45 make <span style="padding: 0px; margin: 0px; color: #666666;">&amp;&amp;</span> make install <span style="padding: 0px; margin: 0px; color: #007020;">cd</span> / <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">#LD_LIBRARY_PATH</span> <span style="padding: 0px; margin: 0px; color: #007020;">echo</span> -e <span style="padding: 0px; margin: 0px; color: #4070a0;">'export LD_LIBRARY_PATH=/usr/local/apr/lib'</span> &gt;&gt; /etc/profile <span style="padding: 0px; margin: 0px; color: #007020;">export </span><span style="padding: 0px; margin: 0px; color: #bb60d5;">LD_LIBRARY_PATH</span><span style="padding: 0px; margin: 0px; color: #666666;">=</span>/usr/local/apr/lib <span style="padding: 0px; margin: 0px; color: #007020;">source</span> /etc/profile </pre></div><hr style="padding: 0px; margin: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;" /><h2>3. 配置Tomcat</h2><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">修改tomcat配置<code style="padding: 3px 5px; margin: -3px 0px 0px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 14px; border-radius: 5px; color: #666666;">conf/server.xml</code>:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;"><span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">&lt;!--The connectors can use a shared executor, you can define one or more named thread pools--&gt;</span>     <span style="padding: 0px; margin: 0px; color: #062873; font-weight: bold;">&lt;Executor</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">name=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"tomcatThreadPool"</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">namePrefix=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"catalina-exec-"</span>         <span style="padding: 0px; margin: 0px; color: #4070a0;">maxThreads=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"800"</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">minSpareThreads=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"400"</span><span style="padding: 0px; margin: 0px; color: #062873; font-weight: bold;">/&gt;</span>        <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">&lt;!-- A "Connector" represents an endpoint by which requests are received</span> <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">         and responses are returned. Documentation at :</span> <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">         Java HTTP Connector: /docs/config/http.html (blocking &amp; non-blocking)</span> <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">         Java AJP  Connector: /docs/config/ajp.html</span> <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">         APR (HTTP/AJP) Connector: /docs/apr.html</span> <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">         Define a non-SSL HTTP/1.1 Connector on port 8080</span> <span style="padding: 0px; margin: 0px; color: #60a0b0; font-style: italic;">    --&gt;</span>     <span style="padding: 0px; margin: 0px; color: #062873; font-weight: bold;">&lt;Connector</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">port=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"80"</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">executor=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"tomcatThreadPool"</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">protocol=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"org.apache.coyote.http11.Http11AprProtocol"</span>                <span style="padding: 0px; margin: 0px; color: #4070a0;">connectionTimeout=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"20000"</span>                <span style="padding: 0px; margin: 0px; color: #4070a0;">redirectPort=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"8443"</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">enableLookups=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"false"</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">acceptCount=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"1000"</span><span style="padding: 0px; margin: 0px; color: #062873; font-weight: bold;">/&gt;</span> </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">修改为<code style="padding: 3px 5px; margin: -3px 0px 0px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 14px; border-radius: 5px; color: #666666;">Http11AprProtocol</code>&nbsp;协议.</p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">之后启动tomcat即可.</p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">遇到问题:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">SEVERE: Failed to initialize the SSLEngine. org.apache.tomcat.jni.Error: 70023: This <span style="padding: 0px; margin: 0px; color: #007020; font-weight: bold;">function</span> has not been implemented on this platform </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">请关闭SSL侦听，除非你有使用SSL,修改conf/server.xml</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;"><span style="padding: 0px; margin: 0px; color: #062873; font-weight: bold;">&lt;Listener</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">className=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"org.apache.catalina.core.AprLifecycleListener"</span> <span style="padding: 0px; margin: 0px; color: #4070a0;">SSLEngine=</span><span style="padding: 0px; margin: 0px; color: #4070a0;">"off"</span> <span style="padding: 0px; margin: 0px; color: #062873; font-weight: bold;">/&gt;</span> </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">压测结果:</p><div style="margin: 0px; border: 0px; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><pre style="padding: 15px 20px; margin: 1.2em -2px; font-family: Menlo, monospace; border: 1px solid #e0e0e0; border-radius: 5px; font-size: 11px; overflow: auto; max-width: 900px; background: none;">webbench -c <span style="padding: 0px; margin: 0px; color: #40a070;">4000</span> -t <span style="padding: 0px; margin: 0px; color: #40a070;">30</span> http://10.103.10.140/workbench/index.jsp Webbench - Simple Web Benchmark 1.5 Copyright <span style="padding: 0px; margin: 0px; color: #666666;">(</span>c<span style="padding: 0px; margin: 0px; color: #666666;">)</span> Radim Kolar 1997-2004, GPL Open Source Software.  Benchmarking: GET http://10.103.10.140/workbench/index.jsp <span style="padding: 0px; margin: 0px; color: #40a070;">4000</span> clients, running <span style="padding: 0px; margin: 0px; color: #40a070;">30</span> sec.  <span style="padding: 0px; margin: 0px; color: #bb60d5;">Speed</span><span style="padding: 0px; margin: 0px; color: #666666;">=</span><span style="padding: 0px; margin: 0px; color: #40a070;">484340</span> pages/min, <span style="padding: 0px; margin: 0px; color: #40a070;">2441573</span> bytes/sec. Requests: <span style="padding: 0px; margin: 0px; color: #40a070;">242170</span> susceed, <span style="padding: 0px; margin: 0px; color: #40a070;">0</span> failed. </pre></div><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;">参考:</p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><a href="http://www.chepoo.com/tomcat-performance-three-times-is-not-a-dream.html" style="padding: 0px; margin: 0px; color: #9c2525; text-decoration: none; transition: all 0.4s linear; -webkit-transition: all 0.4s linear; font-size: 13px;">http://www.chepoo.com/tomcat-performance-three-times-is-not-a-dream.html</a></p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><a href="http://rhomobi.com/topics/36" style="padding: 0px; margin: 0px; color: #9c2525; text-decoration: none; transition: all 0.4s linear; -webkit-transition: all 0.4s linear; font-size: 13px;">http://rhomobi.com/topics/36</a></p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><a href="https://gitsea.com/2013/07/02/tomcat-%E5%B9%B6%E5%8F%91%E4%BC%98%E5%8C%96/" style="padding: 0px; margin: 0px; color: #9c2525; text-decoration: none; transition: all 0.4s linear; -webkit-transition: all 0.4s linear; font-size: 13px;">https://gitsea.com/2013/07/02/tomcat-%E5%B9%B6%E5%8F%91%E4%BC%98%E5%8C%96/</a></p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><a href="http://pengranxiang.iteye.com/blog/1128905" style="padding: 0px; margin: 0px; color: #9c2525; text-decoration: none; transition: all 0.4s linear; -webkit-transition: all 0.4s linear; font-size: 13px;">http://pengranxiang.iteye.com/blog/1128905</a></p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><a href="http://tomcat.apache.org/tomcat-7.0-doc/apr.html#Linux" style="padding: 0px; margin: 0px; color: #9c2525; text-decoration: none; transition: all 0.4s linear; -webkit-transition: all 0.4s linear; font-size: 13px;">http://tomcat.apache.org/tomcat-7.0-doc/apr.html#Linux</a></p><p style="padding: 0px; margin: 1em 0px; border: 0px; text-align: justify; color: #34495e; font-family: Montserrat, Georgia, 'Microsoft YaHei'; font-size: 17.6000003814697px; line-height: 28.1599998474121px; background-color: #ecf0f1;"><a href="http://www.cnblogs.com/wanghaosoft/archive/2013/02/04/2892099.html" style="padding: 0px; margin: 0px; color: #9c2525; text-decoration: none; transition: all 0.4s linear; -webkit-transition: all 0.4s linear; font-size: 13px;">http://www.cnblogs.com/wanghaosoft/archive/2013/02/04/2892099.html</a></p><img src ="http://www.blogjava.net/xiaomage234/aggbug/423496.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-03-16 18:59 <a href="http://www.blogjava.net/xiaomage234/archive/2015/03/16/423496.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Netty版本升级血泪史之线程篇</title><link>http://www.blogjava.net/xiaomage234/archive/2015/02/10/422845.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 10 Feb 2015 04:03:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/02/10/422845.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/422845.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/02/10/422845.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/422845.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/422845.html</trackback:ping><description><![CDATA[from：http://www.infoq.com/cn/articles/netty-version-upgrade-history-thread-part<br /><br /><h2>1. 背景</h2><h3>1.1. Netty 3.X系列版本现状</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">根据对Netty社区部分用户的调查，结合Netty在其它开源项目中的使用情况，我们可以看出目前Netty商用的主流版本集中在3.X和4.X上，其中以Netty 3.X系列版本使用最为广泛。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty社区非常活跃，3.X系列版本从2011年2月7日发布的netty-3.2.4 Final版本到2014年12月17日发布的netty-3.10.0 Final版本，版本跨度达3年多，期间共推出了61个Final版本。</p><h3>1.2. 升级还是坚守老版本</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">相比于其它开源项目，Netty用户的版本升级之路更加艰辛，最根本的原因就是Netty 4对Netty 3没有做到很好的前向兼容。</p><div style="margin: 0px; border: 0px; height: 0px; clear: both; font-size: 0px; font-family: Arial, sans-serif; background-color: #ffffff;"></div><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">由于版本不兼容，大多数老版本使用者的想法就是既然升级这么麻烦，我暂时又不需要使用到Netty 4的新特性，当前版本还挺稳定，就暂时先不升级，以后看看再说。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">坚守老版本还有很多其它的理由，例如考虑到线上系统的稳定性、对新版本的熟悉程度等。无论如何升级Netty都是一件大事，特别是对Netty有直接强依赖的产品。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">从上面的分析可以看出，坚守老版本似乎是个不错的选择；但是，&#8220;理想是美好的,现实却是残酷的&#8221;，坚守老版本并非总是那么容易，下面我们就看下被迫升级的案例。</p><div id="lowerFullwidthVCR" style="margin: 0px; border: 0px; font-family: Arial, sans-serif; background-color: #ffffff;"></div><h3>1.3. &#8220;被迫&#8221;升级到Netty 4.X</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">除了为了使用新特性而主动进行的版本升级，大多数升级都是&#8220;被迫的&#8221;。下面我们对这些升级原因进行分析。</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">公司的开源软件管理策略：对于那些大厂，不同部门和产品线依赖的开源软件版本经常不同，为了对开源依赖进行统一管理，降低安全、维护和管理成本，往往会指定优选的软件版本。由于Netty 4.X 系列版本已经非常成熟，因为，很多公司都优选Netty 4.X版本。</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">维护成本：无论是依赖Netty 3.X，还是Netty4.X，往往需要在原框架之上做定制。例如，客户端的短连重连、心跳检测、流控等。分别对Netty 4.X和3.X版本实现两套定制框架，开发和维护成本都非常高。根据开源软件的使用策略，当存在版本冲突的时候，往往会选择升级到更高的版本。对于Netty，依然遵循这个规则。</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">新特性：Netty 4.X相比于Netty 3.X,提供了很多新的特性，例如优化的内存管理池、对MQTT协议的支持等。如果用户需要使用这些新特性，最简便的做法就是升级Netty到4.X系列版本。</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">更优异的性能：Netty 4.X版本相比于3.X老版本，优化了内存池，减少了GC的频率、降低了内存消耗；通过优化Rector线程池模型，用户的开发更加简单，线程调度也更加高效。</li></ol><h3>1.4. 升级不当付出的代价</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">表面上看，类库包路径的修改、API的重构等似乎是升级的重头戏，大家往往把注意力放到这些&#8220;明枪&#8221;上，但真正隐藏和致命的却是&#8220;暗箭&#8221;。如果对Netty底层的事件调度机制和线程模型不熟悉，往往就会&#8220;中枪&#8221;。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">本文以几个比较典型的真实案例为例，通过问题描述、问题定位和问题总结，让这些隐藏的&#8220;暗箭&#8221;不再伤人。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">由于Netty 4线程模型改变导致的升级事故还有很多，限于篇幅，本文不一一枚举，这些问题万变不离其宗，只要抓住线程模型这个关键点，所谓的疑难杂症都将迎刃而解。</p><h2>2. Netty升级之后遭遇内存泄露</h2><h3>2.1. 问题描述</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">随着JVM虚拟机和JIT即时编译技术的发展，对象的分配和回收是个非常轻量级的工作。但是对于缓冲区Buffer，情况却稍有不同，特别是对于堆外直接内存的分配和回收，是一件耗时的操作。为了尽量重用缓冲区，Netty4.X提供了基于内存池的缓冲区重用机制。性能测试表明，采用内存池的ByteBuf相比于朝生夕灭的ByteBuf，性能高23倍左右（性能数据与使用场景强相关）。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务应用的特点是高并发、短流程，大多数对象都是朝生夕灭的短生命周期对象。为了减少内存的拷贝，用户期望在序列化的时候直接将对象编码到PooledByteBuf里，这样就不需要为每个业务消息都重新申请和释放内存。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务的相关代码示例如下：</p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">//在业务线程中初始化内存池分配器，分配非堆内存  ByteBufAllocator allocator = new PooledByteBufAllocator(true);  ByteBuf buffer = allocator.ioBuffer(1024); //构造订购请求消息并赋值，业务逻辑省略 SubInfoReq infoReq = new SubInfoReq (); infoReq.setXXX(......); //将对象编码到ByteBuf中 codec.encode(buffer, info); //调用ChannelHandlerContext进行消息发送 ctx.writeAndFlush(buffer);</pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务代码升级Netty版本并重构之后，运行一段时间，Java进程就会宕机，查看系统运行日志发现系统发生了内存泄露（示例堆栈）：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205000.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图2-1 OOM内存溢出堆栈</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">对内存进行监控（切换使用堆内存池，方便对内存进行监控），发现堆内存一直飙升，如下所示（示例堆内存监控）：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205001.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图2-2 堆内存监控</p><h3>2.2. 问题定位</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">使用jmap -dump:format=b,file=netty.bin PID 将堆内存dump出来，通过IBM的HeapAnalyzer工具进行分析，发现ByteBuf发生了泄露。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">因为使用了内存池，所以首先怀疑是不是申请的ByteBuf没有被释放导致？查看代码，发现消息发送完成之后，Netty底层已经调用ReferenceCountUtil.release(message)对内存进行了释放。这是怎么回事呢？难道Netty 4.X的内存池有Bug，调用release操作释放内存失败？</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">考虑到Netty 内存池自身Bug的可能性不大，首先从业务的使用方式入手分析：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">内存的分配是在业务代码中进行，由于使用到了业务线程池做I/O操作和业务操作的隔离，实际上内存是在业务线程中分配的；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">内存的释放操作是在outbound中进行，按照Netty 3的线程模型，downstream（对应Netty 4的outbound，Netty 4取消了upstream和downstream）的handler也是由业务调用者线程执行的，也就是说释放跟分配在同一个业务线程中进行。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">初次排查并没有发现导致内存泄露的根因，一筹莫展之际开始查看Netty的内存池分配器PooledByteBufAllocator的Doc和源码实现，发现内存池实际是基于线程上下文实现的，相关代码如下：</p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">final ThreadLocal&lt;PoolThreadCache&gt; threadCache = new ThreadLocal&lt;PoolThreadCache&gt;() {         private final AtomicInteger index = new AtomicInteger();         @Override         protected PoolThreadCache initialValue() {             final int idx = index.getAndIncrement();             final PoolArena&lt;byte[]&gt; heapArena;             final PoolArena&lt;ByteBuffer&gt; directArena;             if (heapArenas != null) {                 heapArena = heapArenas[Math.abs(idx % heapArenas.length)];             } else {                 heapArena = null;             }             if (directArenas != null) {                 directArena = directArenas[Math.abs(idx % directArenas.length)];             } else {                 directArena = null;             }             return new PoolThreadCache(heapArena, directArena);         }</pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">也就是说内存的申请和释放必须在同一线程上下文中，不能跨线程。跨线程之后实际操作的就不是同一块内存区域，这会导致很多严重的问题，内存泄露便是其中之一。内存在A线程申请，切换到B线程释放，实际是无法正确回收的。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过对Netty内存池的源码分析，问题基本锁定。保险起见进行简单验证，通过对单条业务消息进行Debug，发现执行释放的果然不是业务线程，而是Netty的NioEventLoop线程：当某个消息被完全发送成功之后，会通过ReferenceCountUtil.release(message)方法释放已经发送成功的ByteBuf。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">问题定位出来之后，继续溯源，发现Netty 4修改了Netty 3的线程模型：在Netty 3的时候，upstream是在I/O线程里执行的，而downstream是在业务线程里执行。当Netty从网络读取一个数据报投递给业务handler的时候，handler是在I/O线程里执行；而当我们在业务线程中调用write和writeAndFlush向网络发送消息的时候,handler是在业务线程里执行，直到最后一个Header handler将消息写入到发送队列中，业务线程才返回。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty4修改了这一模型，在Netty 4里inbound(对应Netty 3的upstream)和outbound(对应Netty 3的downstream)都是在NioEventLoop(I/O线程)中执行。当我们在业务线程里通过ChannelHandlerContext.write发送消息的时候，Netty 4在将消息发送事件调度到ChannelPipeline的时候，首先将待发送的消息封装成一个Task，然后放到NioEventLoop的任务队列中，由NioEventLoop线程异步执行。后续所有handler的调度和执行，包括消息的发送、I/O事件的通知，都由NioEventLoop线程负责处理。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">下面我们分别通过对比Netty 3和Netty 4的消息接收和发送流程，来理解两个版本线程模型的差异：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 3的I/O事件处理流程：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205003.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图2-3 Netty 3 I/O事件处理线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 4的I/O消息处理流程：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205004.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图2-4 Netty 4 I/O事件处理线程模型</p><h3>2.3. 问题总结</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 4.X版本新增的内存池确实非常高效，但是如果使用不当则会导致各种严重的问题。诸如内存泄露这类问题，功能测试并没有异常，如果相关接口没有进行压测或者稳定性测试而直接上线，则会导致严重的线上问题。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">内存池PooledByteBuf的使用建议：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">申请之后一定要记得释放，Netty自身Socket读取和发送的ByteBuf系统会自动释放，用户不需要做二次释放；如果用户使用Netty的内存池在应用中做ByteBuf的对象池使用，则需要自己主动释放；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">避免错误的释放：跨线程释放、重复释放等都是非法操作，要避免。特别是跨线程申请和释放，往往具有隐蔽性，问题定位难度较大；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">防止隐式的申请和分配：之前曾经发生过一个案例，为了解决内存池跨线程申请和释放问题，有用户对内存池做了二次包装，以实现多线程操作时，内存始终由包装的管理线程申请和释放，这样可以屏蔽用户业务线程模型和访问方式的差异。谁知运行一段时间之后再次发生了内存泄露，最后发现原来调用ByteBuf的write操作时，如果内存容量不足，会自动进行容量扩展。扩展操作由业务线程执行，这就绕过了内存池管理线程，发生了&#8220;引用逃逸&#8221;。该Bug只有在ByteBuf容量动态扩展的时候才发生，因此，上线很长一段时间没有发生，直到某一天......因此，大家在使用Netty 4.X的内存池时要格外当心，特别是做二次封装时，一定要对内存池的实现细节有深刻的理解。</li></ol><h2>3. Netty升级之后遭遇数据被篡改</h2><h3>3.1. 问题描述</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">某业务产品，Netty3.X升级到4.X之后，系统运行过程中，偶现服务端发送给客户端的应答数据被莫名&#8220;篡改&#8221;。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务服务端的处理流程如下：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">将解码后的业务消息封装成Task，投递到后端的业务线程池中执行；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务线程处理业务逻辑，完成之后构造应答消息发送给客户端；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务应答消息的编码通过继承Netty的CodeC框架实现，即Encoder ChannelHandler;</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">调用Netty的消息发送接口之后，流程继续，根据业务场景，可能会继续操作原发送的业务对象。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务相关代码示例如下：</p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">//构造订购应答消息 SubInfoResp infoResp = new SubInfoResp(); //根据业务逻辑，对应答消息赋值 infoResp.setResultCode(0); infoResp.setXXX()； 后续赋值操作省略...... //调用ChannelHandlerContext进行消息发送 ctx.writeAndFlush(infoResp); //消息发送完成之后，后续根据业务流程进行分支处理，修改infoResp对象 infoResp.setXXX(); 后续代码省略......</pre><h3>3.2. 问题定位</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">首先对应答消息被非法&#8220;篡改&#8221;的原因进行分析，经过定位发现当发生问题时，被&#8220;篡改&#8221;的内容是调用<strong style="margin: 0px; border: 0px; padding: 0px;">writeAndFlush</strong>接口之后，由后续业务分支代码修改应答消息导致的。由于修改操作发生在<strong style="margin: 0px; border: 0px; padding: 0px;">writeAndFlush</strong>操作之后，按照Netty 3.X的线程模型不应该出现该问题。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在Netty3中，downstream是在业务线程里执行的，也就是说对<span style="margin: 0px; border: 0px; padding: 0px;">SubInfoResp</span>的编码操作是在业务线程中执行的，当编码后的ByteBuf对象被投递到消息发送队列之后，业务线程才会返回并继续执行后续的业务逻辑，此时修改应答消息是不会改变已完成编码的ByteBuf对象的，所以肯定不会出现应答消息被篡改的问题。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">初步分析应该是由于线程模型发生变更导致的问题，随后查验了Netty 4的线程模型，果然发生了变化：当调用outbound向外发送消息的时候，Netty会将发送事件封装成Task，投递到NioEventLoop的任务队列中异步执行，相关代码如下：</p><pre style="margin-top: 10px; margin-bottom: 10px; padding: 10px 10px 10px 5px; border: 1px solid #e8e8e8; font-family: Consolas, Monaco, 'Andale Mono', monospace; color: #314e64; width: 597.796875px; overflow: auto; clear: none; float: none !important; background-color: #f5f2f0; background-position: -29px 0px;">@Override  public void invokeWrite(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {         if (msg == null) {             throw new NullPointerException("msg");         }         validatePromise(ctx, promise, true);         if (executor.inEventLoop()) {             invokeWriteNow(ctx, msg, promise);         } else {             AbstractChannel channel = (AbstractChannel) ctx.channel();             int size = channel.estimatorHandle().size(msg);             if (size &gt; 0) {                 ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();                 // Check for null as it may be set to null if the channel is closed already                 if (buffer != null) {                     buffer.incrementPendingOutboundBytes(size);                 }             }             safeExecuteOutbound(WriteTask.newInstance(ctx, msg, size, promise), promise, msg);         }     }</pre><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过上述代码可以看出，Netty首先对当前的操作的线程进行判断，如果操作本身就是由NioEventLoop线程执行，则调用写操作；否则，执行线程安全的写操作，即将写事件封装成Task，放入到任务队列中由Netty的I/O线程执行，业务调用返回，流程继续执行。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过源码分析，问题根源已经很清楚：系统升级到Netty 4之后，线程模型发生变化，响应消息的编码由NioEventLoop线程异步执行，业务线程返回。这时存在两种可能：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">如果编码操作先于修改应答消息的业务逻辑执行，则运行结果正确；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">如果编码操作在修改应答消息的业务逻辑之后执行，则运行结果错误。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">由于线程的执行先后顺序无法预测，因此该问题隐藏的相当深。如果对Netty 4和Netty3的线程模型不了解，就会掉入陷阱。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 3版本业务逻辑没有问题，流程如下：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205005.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图3-1 升级之前的业务流程线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">升级到Netty 4版本之后，业务流程由于Netty线程模型的变更而发生改变，导致业务逻辑发生问题：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205006.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图3-2 升级之后的业务处理流程发生改变</p><h3>3.3. 问题总结</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">很多读者在进行Netty 版本升级的时候，只关注到了包路径、类和API的变更，并没有注意到隐藏在背后的&#8220;暗箭&#8221;- 线程模型变更。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">升级到Netty 4的用户需要根据新的线程模型对已有的系统进行评估，重点需要关注outbound的ChannelHandler，如果它的正确性依赖于Netty 3的线程模型，则很可能在新的线程模型中出问题，可能是功能问题或者其它问题。</p><h2>4. Netty升级之后性能严重下降</h2><h3>4.1. 问题描述</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">相信很多Netty用户都看过如下相关报告：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在Twitter，Netty 4 GC开销降为五分之一：Netty 3使用Java对象表示I/O事件，这样简单，但会产生大量的<a href="http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">垃圾</a>，尤其是在我们这样的规模下。Netty 4在新版本中对此做出了更改，取代生存周期短的事件对象，而以定义在生存周期长的通道对象上的方法处理I/O事件。它还有一个使用池的专用缓冲区分配器。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">每当收到新信息或者用户发送信息到远程端，Netty 3均会创建一个新的堆缓冲区。这意味着，对应每一个新的缓冲区，都会有一个&#8216;new byte[capacity]&#8217;。这些缓冲区会导致GC压力，并消耗内存带宽：为了安全起见，新的字节数组分配时会用零填充，这会消耗内存带宽。然而，用零填充的数组很可能会再次用实际的数据填充，这又会消耗同样的内存带宽。如果Java虚拟机（JVM）提供了创建新字节数组而又无需用零填充的方式，那么我们本来就可以将内存带宽消耗减少50%，但是目前没有那样一种方式。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在Netty 4中，代码定义了粒度更细的API，用来处理不同的事件类型，而不是创建事件对象。它还实现了一个新缓冲池，那是一个纯Java版本的&nbsp;<a href="https://www.facebook.com/notes/facebook-engineering/scalable-memory-allocation-using-jemalloc/480222803919" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">jemalloc</a>&nbsp;（Facebook也在用）。现在，Netty不会再因为用零填充缓冲区而浪费内存带宽了。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">我们比较了两个分别建立在Netty 3和4基础上<a href="http://en.wikipedia.org/wiki/Echo_Protocol" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">echo</a>协议服务器。（Echo非常简单，这样，任何垃圾的产生都是Netty的原因，而不是协议的原因）。我使它们服务于相同的分布式echo协议客户端，来自这些客户端的16384个并发连接重复发送256字节的随机负载，几乎使千兆以太网饱和。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">根据测试结果，Netty 4：</p><ul style="margin: 0px 0px 15px 10px; padding: 0px; list-style-position: initial; list-style-image: initial; border: 0px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;"><strong style="margin: 0px; border: 0px; padding: 0px;">GC中断频率是原来的1/5： 45.5 vs. 9.2次/分钟</strong></li><li style="margin: 0px 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none;"><strong style="margin: 0px; border: 0px; padding: 0px;">垃圾生成速度是原来的1/5： 207.11 vs 41.81 MiB/秒</strong></li></ul><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">正是看到了相关的Netty 4性能提升报告，很多用户选择了升级。事后一些用户反馈Netty 4并没有跟产品带来预期的性能提升，有些甚至还发生了非常严重的性能下降，下面我们就以某业务产品的失败升级经历为案例，详细分析下导致性能下降的原因。</p><h3>4.2. 问题定位</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">首先通过JMC等性能分析工具对性能热点进行分析，示例如下（信息安全等原因，只给出分析过程示例截图）：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205007.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图4-1 JMC性能监控分析</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过对热点方法的分析，发现在消息发送过程中，有两处热点：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">消息发送性能统计相关Handler;</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">编码Handler。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">对使用Netty 3版本的业务产品进行性能对比测试，发现上述两个Handler也是热点方法。既然都是热点，为啥切换到Netty4之后性能下降这么厉害呢？</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过方法的调用树分析发现了两个版本的差异：在Netty 3中，上述两个热点方法都是由业务线程负责执行；而在Netty 4中，则是由NioEventLoop(I/O)线程执行。对于某个链路，业务是拥有多个线程的线程池，而NioEventLoop只有一个，所以执行效率更低，返回给客户端的应答时延就大。时延增大之后，自然导致系统并发量降低，性能下降。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">找出问题根因之后，针对Netty 4的线程模型对业务进行专项优化，性能达到预期，远超过了Netty 3老版本的性能。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 3的业务线程调度模型图如下所示：充分利用了业务多线程并行编码和Handler处理的优势，周期T内可以处理N条业务消息。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205008.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图4-2 Netty 3业务调度性能模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">切换到Netty 4之后，业务耗时Handler被I/O线程串行执行，因此性能发生比较大的下降：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205009.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图4-3 Netty 4业务调度性能模型</p><h3>4.3. 问题总结</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">该问题的根因还是由于Netty 4的线程模型变更引起，线程模型变更之后，不仅影响业务的功能，甚至对性能也会造成很大的影响。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">对Netty的升级需要从功能、兼容性和性能等多个角度进行综合考虑，切不可只盯着API变更这个芝麻，而丢掉了性能这个西瓜。API的变更会导致编译错误，但是性能下降却隐藏于无形之中，稍不留意就会中招。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">对于讲究快速交付、敏捷开发和灰度发布的互联网应用，升级的时候更应该要当心。</p><h2>5. Netty升级之后上下文丢失</h2><h3>5.1. 问题描述</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">为了提升业务的二次定制能力，降低对接口的侵入性，业务使用线程变量进行消息上下文的传递。例如消息发送源地址信息、消息Id、会话Id等。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务同时使用到了一些第三方开源容器，也提供了线程级变量上下文的能力。业务通过容器上下文获取第三方容器的系统变量信息。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">升级到Netty 4之后，业务继承自Netty的ChannelHandler发生了空指针异常，无论是业务自定义的线程上下文、还是第三方容器的线程上下文，都获取不到传递的变量值。</p><h3>5.2. 问题定位</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">首先检查代码，看业务是否传递了相关变量，确认业务传递之后怀疑跟Netty 版本升级相关，调试发现，业务ChannelHandler获取的线程上下文对象和之前业务传递的上下文不是同一个。这就说明执行ChannelHandler的线程跟处理业务的线程不是同一个线程！</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">查看Netty 4线程模型的相关Doc发现，Netty修改了outbound的线程模型，正好影响了业务消息发送时的线程上下文传递，最终导致线程变量丢失。</p><h3>5.3. 问题总结</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通常业务的线程模型有如下几种：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务自定义线程池/线程组处理业务，例如使用JDK 1.5提供的ExecutorService；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">使用J2EE Web容器自带的线程模型，常见的如JBoss和Tomcat的HTTP接入线程等；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">隐式的使用其它第三方框架的线程模型，例如使用NIO框架进行协议处理，业务代码隐式使用的就是NIO框架的线程模型，除非业务明确的实现自定义线程模型。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在实践中我们发现很多业务使用了第三方框架，但是只熟悉API和功能，对线程模型并不清楚。某个类库由哪个线程调用，糊里糊涂。为了方便变量传递，又随意的使用线程变量，实际对背后第三方类库的线程模型产生了强依赖。当容器或者第三方类库升级之后，如果线程模型发生了变更，则原有功能就会发生问题。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">鉴于此，在实际工作中，尽量不要强依赖第三方类库的线程模型，如果确实无法避免，则必须对它的线程模型有深入和清晰的了解。当第三方类库升级之后，需要检查线程模型是否发生变更，如果发生变化，相关的代码也需要考虑同步升级。</p><h2>6. Netty3.X VS Netty4.X 之线程模型</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过对三个具有典型性的升级失败案例进行分析和总结，我们发现有个共性：都是线程模型改变惹的祸!</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">下面小节我们就详细得对Netty3和Netty4版本的I/O线程模型进行对比，以方便大家掌握两者的差异，在升级和使用中尽量少踩雷。</p><h3>6.1 Netty 3.X 版本线程模型</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty 3.X的I/O操作线程模型比较复杂，它的处理模型包括两部分：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Inbound：主要包括链路建立事件、链路激活事件、读事件、I/O异常事件、链路关闭事件等；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Outbound：主要包括写事件、连接事件、监听绑定事件、刷新事件等。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">我们首先分析下Inbound操作的线程模型：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205010.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图6-1 Netty 3 Inbound操作线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">从上图可以看出，Inbound操作的主要处理流程如下：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程（Work线程）将消息从TCP缓冲区读取到SocketChannel的接收缓冲区中；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">由I/O线程负责生成相应的事件，触发事件向上执行，调度到ChannelPipeline中；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程调度执行ChannelPipeline中Handler链的对应方法，直到业务实现的Last Handler;</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Last Handler将消息封装成Runnable，放入到业务线程池中执行，I/O线程返回，继续读/写等I/O操作；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务线程池从任务队列中弹出消息，并发执行业务逻辑。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过对Netty 3的Inbound操作进行分析我们可以看出，Inbound的Handler都是由Netty的I/O Work线程负责执行。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">下面我们继续分析Outbound操作的线程模型：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205011.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图6-2 Netty 3 Outbound操作线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">从上图可以看出，Outbound操作的主要处理流程如下：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">业务线程发起Channel Write操作，发送消息；</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Netty将写操作封装成写事件，触发事件向下传播；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">写事件被调度到ChannelPipeline中，由业务线程按照Handler Chain串行调用支持Downstream事件的Channel Handler;</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">执行到系统最后一个ChannelHandler，将编码后的消息Push到发送队列中，业务线程返回；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Netty的I/O线程从发送消息队列中取出消息，调用SocketChannel的write方法进行消息发送。</li></ol><h3>6.2 Netty 4.X 版本线程模型</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">相比于Netty 3.X系列版本，Netty 4.X的I/O操作线程模型比较简答，它的原理图如下所示：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205012.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图6-3 Netty 4 Inbound和Outbound操作线程模型</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">从上图可以看出，Outbound操作的主要处理流程如下：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程NioEventLoop从SocketChannel中读取数据报，将ByteBuf投递到ChannelPipeline，触发ChannelRead事件；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程NioEventLoop调用ChannelHandler链，直到将消息投递到业务线程，然后I/O线程返回，继续后续的读写操作；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">业务线程调用ChannelHandlerContext.write(Object msg)方法进行消息发送；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">如果是由业务线程发起的写操作，ChannelHandlerInvoker将发送消息封装成Task，放入到I/O线程NioEventLoop的任务队列中，由NioEventLoop在循环中统一调度和执行。放入任务队列之后，业务线程返回；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">I/O线程NioEventLoop调用ChannelHandler链，进行消息发送，处理Outbound事件，直到将消息放入发送队列，然后唤醒Selector，进而执行写操作。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">通过流程分析，我们发现Netty 4修改了线程模型，无论是Inbound还是Outbound操作，统一由I/O线程NioEventLoop调度执行。</p><h3>6.3. 线程模型对比</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">在进行新老版本线程模型PK之前，首先还是要熟悉下串行化设计的理念：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">我们知道当系统在运行过程中，如果频繁的进行线程上下文切换，会带来额外的性能损耗。多线程并发执行某个业务流程，业务开发者还需要时刻对线程安全保持警惕，哪些数据可能会被并发修改，如何保护？这不仅降低了开发效率，也会带来额外的性能损耗。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">为了解决上述问题，Netty 4采用了串行化设计理念，从消息的读取、编码以及后续Handler的执行，始终都由I/O线程NioEventLoop负责，这就意外着整个流程不会进行线程上下文的切换，数据也不会面临被并发修改的风险，对于用户而言，甚至不需要了解Netty的线程细节，这确实是个非常好的设计理念，它的工作原理图如下：</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;"><img _href="img://null" _p="true" src="http://cdn3.infoqstatic.com/resource/articles/netty-version-upgrade-history-thread-part/zh/resources/0205014.png" width="500" style="border: 0px; margin: 0px 10px 10px 0px; padding: 0px;"  alt="" /></p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">图6-4 Netty 4的串行化设计理念</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">一个NioEventLoop聚合了一个多路复用器Selector，因此可以处理成百上千的客户端连接，Netty的处理策略是每当有一个新的客户端接入，则从NioEventLoop线程组中顺序获取一个可用的NioEventLoop，当到达数组上限之后，重新返回到0，通过这种方式，可以基本保证各个NioEventLoop的负载均衡。一个客户端连接只注册到一个NioEventLoop上，这样就避免了多个I/O线程去并发操作它。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">Netty通过串行化设计理念降低了用户的开发难度，提升了处理性能。利用线程组实现了多个串行化线程水平并行执行，线程之间并没有交集，这样既可以充分利用多核提升并行处理能力，同时避免了线程上下文的切换和并发保护带来的额外性能损耗。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">了解完了Netty 4的串行化设计理念之后，我们继续看Netty 3线程模型存在的问题，总结起来，它的主要问题如下：</p><ol style="margin: 10px 0px 10px 10px; padding: 0px 0px 0px 20px; border: 0px; width: 549px; clear: left; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Inbound和Outbound实质都是I/O相关的操作，它们的线程模型竟然不统一，这给用户带来了更多的学习和使用成本；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Outbound操作由业务线程执行，通常业务会使用线程池并行处理业务消息，这就意味着在某一个时刻会有多个业务线程同时操作ChannelHandler，我们需要对ChannelHandler进行并发保护，通常需要加锁。如果同步块的范围不当，可能会导致严重的性能瓶颈，这对开发者的技能要求非常高，降低了开发效率；</li><li style="margin: 4px 0px; padding: 0px 0px 0px 10px; border: none; float: none; clear: none;">Outbound操作过程中，例如消息编码异常，会产生Exception，它会被转换成Inbound的Exception并通知到ChannelPipeline，这就意味着业务线程发起了Inbound操作！它打破了Inbound操作由I/O线程操作的模型，如果开发者按照Inbound操作只会由一个I/O线程执行的约束进行设计，则会发生线程并发访问安全问题。由于该场景只在特定异常时发生，因此错误非常隐蔽！一旦在生产环境中发生此类线程并发问题，定位难度和成本都非常大。</li></ol><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">讲了这么多，似乎Netty 4 完胜 Netty 3的线程模型，其实并不尽然。在特定的场景下，Netty 3的性能可能更高，就如本文第4章节所讲，如果编码和其它Outbound操作非常耗时，由多个业务线程并发执行，性能肯定高于单个NioEventLoop线程。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">但是，这种性能优势不是不可逆转的，如果我们修改业务代码，将耗时的Handler操作前置，Outbound操作不做复杂业务逻辑处理，性能同样不输于Netty 3，但是考虑内存池优化、不会反复创建Event、不需要对Handler加锁等Netty 4的优化，整体性能Netty 4版本肯定会更高。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">总而言之，如果用户真正熟悉并掌握了Netty 4的线程模型和功能类库，相信不仅仅开发会更加简单，性能也会更优！</p><h3>6.4. 思考</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">就Netty 而言，掌握线程模型的重要性不亚于熟悉它的API和功能。很多时候我遇到的功能、性能等问题，都是由于缺乏对它线程模型和原理的理解导致的，结果我们就以讹传讹，认为Netty 4版本不如3好用等。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">不能说所有开源软件的版本升级一定都胜过老版本，就Netty而言，我认为Netty 4版本相比于老的Netty 3，确实是历史的一大进步。</p><h2>7. 作者简介</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">李林锋，2007年毕业于东北大学，2008年进入华为公司从事高性能通信软件的设计和开发工作，有7年NIO设计和开发经验，精通Netty、Mina等NIO框架和平台中间件，现任华为软件平台架构部架构师，《Netty权威指南》作者。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">联系方式：新浪微博 Nettying 微信：Nettying 微信公众号：Netty之家</p><hr style="margin: 0px; border: 0px; padding: 0px; font-family: Arial, sans-serif; background-color: #ffffff;" /><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">感谢<a href="http://www.infoq.com/cn/author/%E9%83%AD%E8%95%BE" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">郭蕾</a>对本文的策划和审校。</p><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; float: none; clear: none; width: 610px; font-family: Arial, sans-serif; background-color: #ffffff;">给InfoQ中文站投稿或者参与内容翻译工作，请邮件至<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#100;&#105;&#116;&#111;&#114;&#115;&#64;&#99;&#110;&#46;&#105;&#110;&#102;&#111;&#113;&#46;&#99;&#111;&#109;" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">editors@cn.infoq.com</a>。也欢迎大家通过新浪微博（<a href="http://www.weibo.com/infoqchina" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">@InfoQ</a>）或者腾讯微博（<a href="http://t.qq.com/infoqchina" style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">@InfoQ</a>）关注我们，并与我们的编辑和其他读者朋友交流。</p><img src ="http://www.blogjava.net/xiaomage234/aggbug/422845.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-02-10 12:03 <a href="http://www.blogjava.net/xiaomage234/archive/2015/02/10/422845.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Java线程(九)：Condition-线程通信更高效的方式【转】</title><link>http://www.blogjava.net/xiaomage234/archive/2015/01/15/422232.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 15 Jan 2015 01:36:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/01/15/422232.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/422232.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/01/15/422232.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/422232.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/422232.html</trackback:ping><description><![CDATA[<div id="article_content" style="margin: 35px 0px; font-size: 15px; color: #555555; line-height: 35px; font-family: 'microsoft yahei';"><p style="margin: 0px; padding: 0px;">接近一周没更新《Java线程》专栏了，主要是这周工作上比较忙，生活上也比较忙，呵呵，进入正题，上一篇讲述了并发包下的Lock，Lock可以更好的解决线程同步问题，使之更面向对象，并且ReadWriteLock在处理同步时更强大，那么同样，线程间仅仅互斥是不够的，还需要通信，本篇的内容是基于上篇之上，使用Lock如何处理线程通信。</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 那么引入本篇的主角，Condition，Condition 将 Object 监视器方法（wait、notify 和 notifyAll）分解成截然不同的对象，以便通过将这些对象与任意 Lock 实现组合使用，为每个对象提供多个等待 set （wait-set）。其中，Lock 替代了 synchronized 方法和语句的使用，Condition 替代了 Object 监视器方法的使用。下面将之前写过的一个线程通信的例子替换成用Condition实现(Java线程(三))，代码如下：</p><p style="margin: 0px; padding: 0px;"></p><div bg_java"="" style="width: 938.515625px; overflow: hidden; border-color: #cccccc;"><div><div style="border-left-color: #999999;"><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/ghsau/article/details/7481142#" title="view plain" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;">view plain</a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" title="copy" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;">copy</a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" title="print" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;">print</a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" title="?" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;">?</a><a href="https://code.csdn.net/snippets/129603" target="_blank" title="在CODE上查看代码片" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;"><img src="https://code.csdn.net/assets/CODE_ico.png" width="12" height="12" alt="在CODE上查看代码片" style="border: none; position: relative; top: 1px; left: 2px;" /></a><a href="https://code.csdn.net/snippets/129603/fork" target="_blank" title="派生到我的代码片" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;"><img src="https://code.csdn.net/assets/ico_fork.svg" width="12" height="12" alt="派生到我的代码片" style="border: none; position: relative; top: 2px; left: 2px;" /></a><div style="position: absolute; left: 537px; top: 825px; width: 29px; height: 15px; z-index: 99;"></div></div></div><ol start="1" style="border-width: 0px;"><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;"><span style="border-width: 0px; color: blue;">public</span>&nbsp;<span style="border-width: 0px; color: blue;">class</span><span style="border-width: 0px;">&nbsp;ThreadTest2&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">public</span>&nbsp;<span style="border-width: 0px; color: blue;">static</span>&nbsp;<span style="border-width: 0px; color: blue;">void</span><span style="border-width: 0px;">&nbsp;main(String[]&nbsp;args)&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">final</span><span style="border-width: 0px;">&nbsp;Business&nbsp;business&nbsp;=&nbsp;</span><span style="border-width: 0px; color: blue;">new</span><span style="border-width: 0px;">&nbsp;Business();&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">new</span><span style="border-width: 0px;">&nbsp;Thread(</span><span style="border-width: 0px; color: blue;">new</span><span style="border-width: 0px;">&nbsp;Runnable()&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: #646464;">@Override</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">public</span>&nbsp;<span style="border-width: 0px; color: blue;">void</span><span style="border-width: 0px;">&nbsp;run()&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;threadExecute(business,&nbsp;<span style="border-width: 0px; color: red;">"sub"</span><span style="border-width: 0px;">);&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}).start();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;threadExecute(business,&nbsp;<span style="border-width: 0px; color: red;">"main"</span><span style="border-width: 0px;">);&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">public</span>&nbsp;<span style="border-width: 0px; color: blue;">static</span>&nbsp;<span style="border-width: 0px; color: blue;">void</span><span style="border-width: 0px;">&nbsp;threadExecute(Business&nbsp;business,&nbsp;String&nbsp;threadType)&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">for</span><span style="border-width: 0px;">(</span><span style="border-width: 0px; color: blue;">int</span><span style="border-width: 0px;">&nbsp;i&nbsp;=&nbsp;</span><span style="border-width: 0px; color: #c00000;">0</span><span style="border-width: 0px;">;&nbsp;i&nbsp;&lt;&nbsp;</span><span style="border-width: 0px; color: #c00000;">100</span><span style="border-width: 0px;">;&nbsp;i++)&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">try</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">if</span><span style="border-width: 0px;">(</span><span style="border-width: 0px; color: red;">"main"</span><span style="border-width: 0px;">.equals(threadType))&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;business.main(i);&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="border-width: 0px; color: blue;">else</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;business.sub(i);&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="border-width: 0px; color: blue;">catch</span><span style="border-width: 0px;">&nbsp;(InterruptedException&nbsp;e)&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;"><span style="border-width: 0px; color: blue;">class</span><span style="border-width: 0px;">&nbsp;Business&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">private</span>&nbsp;<span style="border-width: 0px; color: blue;">boolean</span><span style="border-width: 0px;">&nbsp;bool&nbsp;=&nbsp;</span><span style="border-width: 0px; color: blue;">true</span><span style="border-width: 0px;">;&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">private</span><span style="border-width: 0px;">&nbsp;Lock&nbsp;lock&nbsp;=&nbsp;</span><span style="border-width: 0px; color: blue;">new</span><span style="border-width: 0px;">&nbsp;ReentrantLock();&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">private</span><span style="border-width: 0px;">&nbsp;Condition&nbsp;condition&nbsp;=&nbsp;lock.newCondition();&nbsp;&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">public</span>&nbsp;<span style="border-width: 0px;">/*synchronized*/</span>&nbsp;<span style="border-width: 0px; color: blue;">void</span><span style="border-width: 0px;">&nbsp;main(</span><span style="border-width: 0px; color: blue;">int</span><span style="border-width: 0px;">&nbsp;loop)&nbsp;</span><span style="border-width: 0px; color: blue;">throws</span><span style="border-width: 0px;">&nbsp;InterruptedException&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lock.lock();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">try</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">while</span><span style="border-width: 0px;">(bool)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;condition.await();<span style="border-width: 0px;">//this.wait();</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">for</span><span style="border-width: 0px;">(</span><span style="border-width: 0px; color: blue;">int</span><span style="border-width: 0px;">&nbsp;i&nbsp;=&nbsp;</span><span style="border-width: 0px; color: #c00000;">0</span><span style="border-width: 0px;">;&nbsp;i&nbsp;&lt;&nbsp;</span><span style="border-width: 0px; color: #c00000;">100</span><span style="border-width: 0px;">;&nbsp;i++)&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span style="border-width: 0px; color: red;">"main&nbsp;thread&nbsp;seq&nbsp;of&nbsp;"</span><span style="border-width: 0px;">&nbsp;+&nbsp;i&nbsp;+&nbsp;</span><span style="border-width: 0px; color: red;">",&nbsp;loop&nbsp;of&nbsp;"</span><span style="border-width: 0px;">&nbsp;+&nbsp;loop);&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bool&nbsp;=&nbsp;<span style="border-width: 0px; color: blue;">true</span><span style="border-width: 0px;">;&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;condition.signal();<span style="border-width: 0px;">//this.notify();</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="border-width: 0px; color: blue;">finally</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lock.unlock();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">public</span>&nbsp;<span style="border-width: 0px;">/*synchronized*/</span>&nbsp;<span style="border-width: 0px; color: blue;">void</span><span style="border-width: 0px;">&nbsp;sub(</span><span style="border-width: 0px; color: blue;">int</span><span style="border-width: 0px;">&nbsp;loop)&nbsp;</span><span style="border-width: 0px; color: blue;">throws</span><span style="border-width: 0px;">&nbsp;InterruptedException&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lock.lock();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">try</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">while</span><span style="border-width: 0px;">(!bool)&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;condition.await();<span style="border-width: 0px;">//this.wait();</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">for</span><span style="border-width: 0px;">(</span><span style="border-width: 0px; color: blue;">int</span><span style="border-width: 0px;">&nbsp;i&nbsp;=&nbsp;</span><span style="border-width: 0px; color: #c00000;">0</span><span style="border-width: 0px;">;&nbsp;i&nbsp;&lt;&nbsp;</span><span style="border-width: 0px; color: #c00000;">10</span><span style="border-width: 0px;">;&nbsp;i++)&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span style="border-width: 0px; color: red;">"sub&nbsp;thread&nbsp;seq&nbsp;of&nbsp;"</span><span style="border-width: 0px;">&nbsp;+&nbsp;i&nbsp;+&nbsp;</span><span style="border-width: 0px; color: red;">",&nbsp;loop&nbsp;of&nbsp;"</span><span style="border-width: 0px;">&nbsp;+&nbsp;loop);&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bool&nbsp;=&nbsp;<span style="border-width: 0px; color: blue;">false</span><span style="border-width: 0px;">;&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;condition.signal();<span style="border-width: 0px;">//this.notify();</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="border-width: 0px; color: blue;">finally</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lock.unlock();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">}&nbsp;&nbsp;</span></li></ol></div>&nbsp; &nbsp; &nbsp; &nbsp; 在Condition中，用await()替换wait()，用signal()替换notify()，用signalAll()替换notifyAll()，传统线程的通信方式，Condition都可以实现，这里注意，Condition是被绑定到Lock上的，要创建一个Lock的Condition必须用newCondition()方法。<p style="margin: 0px; padding: 0px;"></p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 这样看来，Condition和传统的线程通信没什么区别，Condition的强大之处在于它可以为多个线程间建立不同的Condition，下面引入API中的一段代码，加以说明。</p><p style="margin: 0px; padding: 0px;"></p><div bg_java"="" style="width: 938.515625px; overflow: hidden; border-color: #cccccc;"><div><div style="border-left-color: #999999;"><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/ghsau/article/details/7481142#" title="view plain" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;">view plain</a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" title="copy" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;">copy</a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" title="print" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;">print</a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" title="?" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;">?</a><a href="https://code.csdn.net/snippets/129603" target="_blank" title="在CODE上查看代码片" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;"><img src="https://code.csdn.net/assets/CODE_ico.png" width="12" height="12" alt="在CODE上查看代码片" style="border: none; position: relative; top: 1px; left: 2px;" /></a><a href="https://code.csdn.net/snippets/129603/fork" target="_blank" title="派生到我的代码片" style="border-width: 0px; color: #0c89cf; background-image: initial; background-position: 0px 50%;"><img src="https://code.csdn.net/assets/ico_fork.svg" width="12" height="12" alt="派生到我的代码片" style="border: none; position: relative; top: 2px; left: 2px;" /></a><div style="position: absolute; left: 537px; top: 2111px; width: 29px; height: 15px; z-index: 99;"></div></div></div><ol start="1" style="border-width: 0px;"><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;"><span style="border-width: 0px; color: blue;">class</span><span style="border-width: 0px;">&nbsp;BoundedBuffer&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">final</span><span style="border-width: 0px;">&nbsp;Lock&nbsp;lock&nbsp;=&nbsp;</span><span style="border-width: 0px; color: blue;">new</span><span style="border-width: 0px;">&nbsp;ReentrantLock();</span><span style="border-width: 0px;">//锁对象</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">final</span><span style="border-width: 0px;">&nbsp;Condition&nbsp;notFull&nbsp;&nbsp;=&nbsp;lock.newCondition();</span><span style="border-width: 0px;">//写线程条件&nbsp;</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">final</span><span style="border-width: 0px;">&nbsp;Condition&nbsp;notEmpty&nbsp;=&nbsp;lock.newCondition();</span><span style="border-width: 0px;">//读线程条件&nbsp;</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">final</span><span style="border-width: 0px;">&nbsp;Object[]&nbsp;items&nbsp;=&nbsp;</span><span style="border-width: 0px; color: blue;">new</span><span style="border-width: 0px;">&nbsp;Object[</span><span style="border-width: 0px; color: #c00000;">100</span><span style="border-width: 0px;">];</span><span style="border-width: 0px;">//缓存队列</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">int</span><span style="border-width: 0px;">&nbsp;putptr</span><span style="border-width: 0px;">/*写索引*/</span><span style="border-width: 0px;">,&nbsp;takeptr</span><span style="border-width: 0px;">/*读索引*/</span><span style="border-width: 0px;">,&nbsp;count</span><span style="border-width: 0px;">/*队列中存在的数据个数*/</span><span style="border-width: 0px;">;&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">public</span>&nbsp;<span style="border-width: 0px; color: blue;">void</span><span style="border-width: 0px;">&nbsp;put(Object&nbsp;x)&nbsp;</span><span style="border-width: 0px; color: blue;">throws</span><span style="border-width: 0px;">&nbsp;InterruptedException&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lock.lock();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">try</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">while</span><span style="border-width: 0px;">&nbsp;(count&nbsp;==&nbsp;items.length)</span><span style="border-width: 0px;">//如果队列满了&nbsp;</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notFull.await();<span style="border-width: 0px;">//阻塞写线程</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;items[putptr]&nbsp;=&nbsp;x;<span style="border-width: 0px;">//赋值&nbsp;</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">if</span><span style="border-width: 0px;">&nbsp;(++putptr&nbsp;==&nbsp;items.length)&nbsp;putptr&nbsp;=&nbsp;</span><span style="border-width: 0px; color: #c00000;">0</span><span style="border-width: 0px;">;</span><span style="border-width: 0px;">//如果写索引写到队列的最后一个位置了，那么置为0</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++count;<span style="border-width: 0px;">//个数++</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notEmpty.signal();<span style="border-width: 0px;">//唤醒读线程</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="border-width: 0px; color: blue;">finally</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lock.unlock();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">public</span><span style="border-width: 0px;">&nbsp;Object&nbsp;take()&nbsp;</span><span style="border-width: 0px; color: blue;">throws</span><span style="border-width: 0px;">&nbsp;InterruptedException&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lock.lock();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">try</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">while</span><span style="border-width: 0px;">&nbsp;(count&nbsp;==&nbsp;</span><span style="border-width: 0px; color: #c00000;">0</span><span style="border-width: 0px;">)</span><span style="border-width: 0px;">//如果队列为空</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notEmpty.await();<span style="border-width: 0px;">//阻塞读线程</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;x&nbsp;=&nbsp;items[takeptr];<span style="border-width: 0px;">//取值&nbsp;</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">if</span><span style="border-width: 0px;">&nbsp;(++takeptr&nbsp;==&nbsp;items.length)&nbsp;takeptr&nbsp;=&nbsp;</span><span style="border-width: 0px; color: #c00000;">0</span><span style="border-width: 0px;">;</span><span style="border-width: 0px;">//如果读索引读到队列的最后一个位置了，那么置为0</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--count;<span style="border-width: 0px;">//个数--</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notFull.signal();<span style="border-width: 0px;">//唤醒写线程</span><span style="border-width: 0px;">&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="border-width: 0px; color: blue;">return</span><span style="border-width: 0px;">&nbsp;x;&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="border-width: 0px; color: blue;">finally</span><span style="border-width: 0px;">&nbsp;{&nbsp;&nbsp;</span></span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lock.unlock();&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;</span></li><li style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-color: #999999; line-height: 18px; background-color: #f5fae2;"><span style="border-width: 0px;">&nbsp;}&nbsp;&nbsp;</span></li></ol></div>&nbsp; &nbsp; &nbsp; &nbsp; 这是一个处于多线程工作环境下的缓存区，缓存区提供了两个方法，put和take，put是存数据，take是取数据，内部有个缓存队列，具体变量和方法说明见代码，这个缓存区类实现的功能：有多个线程往里面存数据和从里面取数据，其缓存队列(先进先出后进后出)能缓存的最大数值是100，多个线程间是互斥的，当缓存队列中存储的值达到100时，将写线程阻塞，并唤醒读线程，当缓存队列中存储的值为0时，将读线程阻塞，并唤醒写线程，下面分析一下代码的执行过程：<p style="margin: 0px; padding: 0px;"></p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 1. 一个写线程执行，调用put方法；</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 2. 判断count是否为100，显然没有100；</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 3. 继续执行，存入值；</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 4. 判断当前写入的索引位置++后，是否和100相等，相等将写入索引值变为0，并将count+1；</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 5. 仅唤醒<strong>读线程阻塞队列</strong>中的一个；</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 6. 一个读线程执行，调用take方法；</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 7. &#8230;&#8230;</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 8. 仅唤醒<strong>写线程阻塞队列</strong>中的一个。</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 这就是多个Condition的强大之处，假设缓存队列中已经存满，那么阻塞的肯定是写线程，唤醒的肯定是读线程，相反，阻塞的肯定是读线程，唤醒的肯定是写线程，那么假设只有一个Condition会有什么效果呢，缓存队列中已经存满，这个Lock不知道唤醒的是读线程还是写线程了，如果唤醒的是读线程，皆大欢喜，如果唤醒的是写线程，那么线程刚被唤醒，又被阻塞了，这时又去唤醒，这样就浪费了很多时间。</p><p style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp; &nbsp; 本文来自：<a target="_blank" href="http://blog.csdn.net/ghsau" style="text-decoration: none; color: #0c89cf;">高爽<span style="background-color: white;">|Coder</span></a>，原文地址：<a target="_blank" href="http://blog.csdn.net/ghsau/article/details/7436458" style="text-decoration: none; color: #0c89cf;"></a><a target="_blank" href="http://blog.csdn.net/ghsau/article/details/7451464" style="text-decoration: none; color: #0c89cf;"></a><a target="_blank" href="http://blog.csdn.net/ghsau/article/details/7481142" style="text-decoration: none; color: #0c89cf;">http://blog.csdn.net/ghsau/article/details/7481142</a>，转载请注明。<br /></p><p style="margin: 0px; padding: 0px;"></p></div><div bdshare-button-style0-16"="" data-bd-bind="1421221078540" style="zoom: 1; font-family: 'microsoft yahei'; font-size: 12px; line-height: normal; float: right;"><a href="http://blog.csdn.net/ghsau/article/details/7481142#" data-cmd="more" style="text-decoration: none; float: left; padding-left: 17px; line-height: 16px; height: 16px; cursor: pointer; margin: 6px 6px 6px 0px; color: #333333; background-image: url(http://bdimg.share.baidu.com/static/api/img/share/icons_0_16.png?v=d754dcc0.png) !important; background-position: 0px 0px !important; background-repeat: no-repeat;"></a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" data-cmd="qzone" title="分享到QQ空间" style="text-decoration: none; float: left; padding-left: 17px; line-height: 16px; height: 16px; cursor: pointer; margin: 6px 6px 6px 0px; background-image: url(http://bdimg.share.baidu.com/static/api/img/share/icons_0_16.png?v=d754dcc0.png); background-position: 0px -52px !important; background-repeat: no-repeat;"></a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" data-cmd="tsina" title="分享到新浪微博" style="text-decoration: none; float: left; padding-left: 17px; line-height: 16px; height: 16px; cursor: pointer; margin: 6px 6px 6px 0px; background-image: url(http://bdimg.share.baidu.com/static/api/img/share/icons_0_16.png?v=d754dcc0.png); background-position: 0px -104px !important; background-repeat: no-repeat;"></a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" data-cmd="tqq" title="分享到腾讯微博" style="text-decoration: none; float: left; padding-left: 17px; line-height: 16px; height: 16px; cursor: pointer; margin: 6px 6px 6px 0px; background-image: url(http://bdimg.share.baidu.com/static/api/img/share/icons_0_16.png?v=d754dcc0.png); background-position: 0px -260px !important; background-repeat: no-repeat;"></a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" data-cmd="renren" title="分享到人人网" style="text-decoration: none; float: left; padding-left: 17px; line-height: 16px; height: 16px; cursor: pointer; margin: 6px 6px 6px 0px; background-image: url(http://bdimg.share.baidu.com/static/api/img/share/icons_0_16.png?v=d754dcc0.png); background-position: 0px -208px !important; background-repeat: no-repeat;"></a><a href="http://blog.csdn.net/ghsau/article/details/7481142#" data-cmd="weixin" title="分享到微信" style="text-decoration: none; float: left; padding-left: 17px; line-height: 16px; height: 16px; cursor: pointer; margin: 6px 6px 6px 0px; background-image: url(http://bdimg.share.baidu.com/static/api/img/share/icons_0_16.png?v=d754dcc0.png); background-position: 0px -1612px !important; background-repeat: no-repeat;"></a></div><ul style="margin-top: 30px; font-family: 'microsoft yahei'; font-size: 12px; line-height: normal;"></ul><img src ="http://www.blogjava.net/xiaomage234/aggbug/422232.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-01-15 09:36 <a href="http://www.blogjava.net/xiaomage234/archive/2015/01/15/422232.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Redis学习手册(Set数据类型)</title><link>http://www.blogjava.net/xiaomage234/archive/2014/12/01/420889.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 01 Dec 2014 11:45:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2014/12/01/420889.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/420889.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2014/12/01/420889.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/420889.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/420889.html</trackback:ping><description><![CDATA[<div id="cnblogs_post_body" style="margin-bottom: 20px; font-family: Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif; font-size: 12px; line-height: 18px; background-color: #ffffff;"><p style="margin-top: 10px; margin-bottom: 10px;"><span style="font-size: 14pt;"><strong><span style="color: #ff0000;">一、概述：</span></strong></span><br /><br />&nbsp;&nbsp; &nbsp;&nbsp; 在Redis中，我们可以将Set类型看作为没有排序的字符集合，和List类型一样，我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要说明的是，这些操作的时间复杂度为O(1)，即常量时间内完成次操作。Set可包含的最大元素数量是4294967295。<br />&nbsp;&nbsp; &nbsp;&nbsp; 和List类型不同的是，Set集合中不允许出现重复的元素，这一点和C++标准库中的set容器是完全相同的。换句话说，如果多次添加相同元素，Set中将仅保留该元素的一份拷贝。和List类型相比，Set类型在功能上还存在着一个非常重要的特性，即在服务器端完成多个Sets之间的聚合计算操作，如unions、intersections和differences。由于这些操作均在服务端完成，因此效率极高，而且也节省了大量的网络IO开销。<br /><br /><span style="font-size: 14pt;"><strong><span style="color: #ff0000;">二、相关命令列表：</span></strong></span></p><table border="0" align="center" style="border-style: solid; border-color: silver; border-collapse: collapse; width: 680px;"><tbody><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><strong><span style="color: #0000ff;">命令原型</span></strong></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><strong><span style="color: #0000ff;">时间复杂度</span></strong></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><strong><span style="color: #0000ff;">命令描述</span></strong></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><strong><span style="color: #0000ff;">返回值</span></strong></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SADD</strong>&nbsp;key member [member ...]</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示操作的成员数量。如果在插入的过程用，参数中有的成员在Set中已经存在，该成员将被忽略，而其它成员仍将会被正常插入。如果执行该命令之前，该Key并不存在，该命令将会创建一个新的Set，此后再将参数中的成员陆续插入。如果该Key的Value不是Set类型，该命令将返回相关的错误信息。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">本次操作实际插入的成员数量。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SCARD</strong>&nbsp;key</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">获取Set中成员的数量。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回Set中成员的数量，如果该Key并不存在，返回0。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SISMEMBER</strong>key member</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">判断参数中指定成员是否已经存在于与Key相关联的Set集合中。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">1表示已经存在，0表示不存在，或该Key本身并不存在。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SMEMBERS</strong>&nbsp;key</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示Set中已经存在的成员数量。获取与该Key关联的Set中所有的成员。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><p style="margin-top: 10px; margin-bottom: 10px;"><span style="color: #0000ff;">返回Set中所有的成员。</span></p></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SPOP</strong>&nbsp;key&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">随机的移除并返回Set中的某一成员。 由于Set中元素的布局不受外部控制，因此无法像List那样确定哪个元素位于Set的头部或者尾部。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回移除的成员，如果该Key并不存在，则返回nil。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SREM</strong>&nbsp;key member [member ...]</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示被删除的成员数量。从与Key关联的Set中删除参数中指定的成员，不存在的参数成员将被忽略，如果该Key并不存在，将视为空Set处理。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">从Set中实际移除的成员数量，如果没有则返回0。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SRANDMEMBER</strong>key&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">和SPOP一样，随机的返回Set中的一个成员，不同的是该命令并不会删除返回的成员。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回随机位置的成员，如果Key不存在则返回nil。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SMOVE</strong>&nbsp;source destination member</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">原子性的将参数中的成员从source键移入到destination键所关联的Set中。因此在某一时刻，该成员或者出现在source中，或者出现在destination中。如果该成员在source中并不存在，该命令将不会再执行任何操作并返回0，否则，该成员将从source移入到destination。如果此时该成员已经在destination中存在，那么该命令仅是将该成员从source中移出。如果和Key关联的Value不是Set，将返回相关的错误信息。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">1表示正常移动，0表示source中并不包含参数成员。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SDIFF</strong>&nbsp;key [key ...]</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示所有Sets中成员的总数量。返回参数中第一个Key所关联的Set和其后所有Keys所关联的Sets中成员的差异。如果Key不存在，则视为空Set。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">差异结果成员的集合。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SDIFFSTORE</strong>destination key [key ...]&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">该命令和SDIFF命令在功能上完全相同，两者之间唯一的差别是SDIFF返回差异的结果成员，而该命令将差异成员存储在destination关联的Set中。如果destination键已经存在，该操作将覆盖它的成员。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回差异成员的数量。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SINTER</strong>&nbsp;key [key ...]&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N*M)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示最小Set中元素的数量，M则表示参数中Sets的数量。该命令将返回参数中所有Keys关联的Sets中成员的交集。因此如果参数中任何一个Key关联的Set为空，或某一Key不存在，那么该命令的结果将为空集。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">交集结果成员的集合。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SINTERSTORE</strong>destination key [key ...]</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N*M)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">该命令和SINTER命令在功能上完全相同，两者之间唯一的差别是SINTER返回交集的结果成员，而该命令将交集成员存储在destination关联的Set中。如果destination键已经存在，该操作将覆盖它的成员。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回交集成员的数量。&nbsp;</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SUNION</strong>&nbsp;key [key ...]&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示所有Sets中成员的总数量。该命令将返回参数中所有Keys关联的Sets中成员的并集。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">并集结果成员的集合。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>SUNIONSTORE</strong>destination key [key ...]&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">该命令和SUNION命令在功能上完全相同，两者之间唯一的差别是SUNION返回并集的结果成员，而该命令将并集成员存储在destination关联的Set中。如果destination键已经存在，该操作将覆盖它的成员。&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回并集成员的数量。</span></td></tr></tbody></table><p style="margin-top: 10px; margin-bottom: 10px;"><span style="font-size: 14pt;"><strong><span style="color: #ff0000;">三、命令示例：</span></strong></span><br /><br /><span style="color: #800080; font-size: 16px;">&nbsp;&nbsp; 1. SADD/SMEMBERS/SCARD/SISMEMBER:</span><br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#在Shell命令行下启动Redis的客户端程序。</span></em><br />&nbsp;&nbsp; &nbsp;/&gt; redis-cli<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#插入测试数据，由于该键myset之前并不存在，因此参数中的三个成员都被正常插入。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sadd myset a b c</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 3<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#由于参数中的a在myset中已经存在，因此本次操作仅仅插入了d和e两个新成员。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sadd myset a d e</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 2<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#判断a是否已经存在，返回值为1表示存在。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sismember myset a</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#判断f是否已经存在，返回值为0表示不存在。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sismember myset f</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 0<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#通过smembers命令查看插入的结果，从结果可以，输出的顺序和插入顺序无关。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;<em><span style="color: #0000ff;">&nbsp;smembers myset</span></em><br />&nbsp;&nbsp; &nbsp;1) "c"<br />&nbsp;&nbsp; &nbsp;2) "d"<br />&nbsp;&nbsp; &nbsp;3) "a"<br />&nbsp;&nbsp; &nbsp;4) "b"<br />&nbsp;&nbsp; &nbsp;5) "e"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#获取Set集合中元素的数量。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">scard myset</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 5<br /><br /><span style="color: #800080; font-size: 16px;">&nbsp;&nbsp; &nbsp;2. SPOP/SREM/SRANDMEMBER/SMOVE:</span><br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#删除该键，便于后面的测试。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">del myset</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#为后面的示例准备测试数据。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sadd myset a b c d</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 4<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#查看Set中成员的位置。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;<em><span style="color: #0000ff;">&nbsp;smembers myset</span></em><br />&nbsp;&nbsp; &nbsp;1) "c"<br />&nbsp;&nbsp; &nbsp;2) "d"<br />&nbsp;&nbsp; &nbsp;3) "a"<br />&nbsp;&nbsp; &nbsp;4) "b"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#从结果可以看出，该命令确实是随机的返回了某一成员。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">srandmember myset</span></em><br />&nbsp;&nbsp; &nbsp;"c"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#Set中尾部的成员b被移出并返回，事实上b并不是之前插入的第一个或最后一个成员。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">spop myset</span></em><br />&nbsp;&nbsp; &nbsp;"b"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#查看移出后Set的成员信息。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;<em><span style="color: #0000ff;">&nbsp;smembers myset</span></em><br />&nbsp;&nbsp; &nbsp;1) "c"<br />&nbsp;&nbsp; &nbsp;2) "d"<br />&nbsp;&nbsp; &nbsp;3) "a"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#从Set中移出a、d和f三个成员，其中f并不存在，因此只有a和d两个成员被移出，返回为2。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">srem myset a d f</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 2<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#查看移出后的输出结果。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">smembers myset</span></em><br />&nbsp;&nbsp; &nbsp;1) "c"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#为后面的smove命令准备数据。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sadd myset a b</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 2<br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sadd myset2 c d</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 2<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#将a从myset移到myset2，从结果可以看出移动成功。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">smove myset myset2 a</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#再次将a从myset移到myset2，由于此时a已经不是myset的成员了，因此移动失败并返回0。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">smove myset myset2 a</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 0<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#分别查看myset和myset2的成员，确认移动是否真的成功。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">smembers myset</span></em><br />&nbsp;&nbsp; &nbsp;1) "b"<br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;<em><span style="color: #0000ff;">&nbsp;smembers myset2</span></em><br />&nbsp;&nbsp; &nbsp;1) "c"<br />&nbsp;&nbsp; &nbsp;2) "d"<br />&nbsp;&nbsp; &nbsp;3) "a"<br /><br /><span style="color: #800080; font-size: 16px;">&nbsp;&nbsp; 3. SDIFF/SDIFFSTORE/SINTER/SINTERSTORE:</span><br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#为后面的命令准备测试数据。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sadd myset a b c d</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 4<br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;<em><span style="color: #0000ff;">&nbsp;sadd myset2 c</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sadd myset3 a c e</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 3<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#myset和myset2相比，a、b和d三个成员是两者之间的差异成员。再用这个结果继续和myset3进行差异比较，b和d是myset3不存在的成员。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sdiff myset myset2 myset3</span></em><br />&nbsp;&nbsp; &nbsp;1) "d"<br />&nbsp;&nbsp; &nbsp;2) "b"<br />&nbsp;<em><span style="color: #008000;">&nbsp; &nbsp;#将3个集合的差异成员存在在diffkey关联的Set中，并返回插入的成员数量。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sdiffstore diffkey myset myset2 myset3</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 2<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#查看一下sdiffstore的操作结果。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">smembers diffkey</span></em><br />&nbsp;&nbsp; &nbsp;1) "d"<br />&nbsp;&nbsp; &nbsp;2) "b"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#从之前准备的数据就可以看出，这三个Set的成员交集只有c。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sinter myset myset2 myset3</span></em><br />&nbsp;&nbsp; &nbsp;1) "c"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#将3个集合中的交集成员存储到与interkey关联的Set中，并返回交集成员的数量。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;<em><span style="color: #0000ff;">&nbsp;sinterstore interkey myset myset2 myset3</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#查看一下sinterstore的操作结果。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">smembers interkey</span></em><br />&nbsp;&nbsp; &nbsp;1) "c"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#获取3个集合中的成员的并集。&nbsp;&nbsp; &nbsp;</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sunion myset myset2 myset3</span></em><br />&nbsp;&nbsp; &nbsp;1) "b"<br />&nbsp;&nbsp; &nbsp;2) "c"<br />&nbsp;&nbsp; &nbsp;3) "d"<br />&nbsp;&nbsp; &nbsp;4) "e"<br />&nbsp;&nbsp; &nbsp;5) "a"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#将3个集合中成员的并集存储到unionkey关联的set中，并返回并集成员的数量。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">sunionstore unionkey myset myset2 myset3</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 5<br />&nbsp;<em><span style="color: #008000;">&nbsp; &nbsp;#查看一下suiionstore的操作结果。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">smembers unionkey</span></em><br />&nbsp;&nbsp; &nbsp;1) "b"<br />&nbsp;&nbsp; &nbsp;2) "c"<br />&nbsp;&nbsp; &nbsp;3) "d"<br />&nbsp;&nbsp; &nbsp;4) "e"<br />&nbsp;&nbsp; &nbsp;5) "a"<br /><br /><span style="font-size: 14pt;"><strong><span style="color: #ff0000;">四、应用范围：</span></strong></span><br /><br />&nbsp;&nbsp; &nbsp;&nbsp; 1). 可以使用Redis的Set数据类型跟踪一些唯一性数据，比如访问某一博客的唯一IP地址信息。对于此场景，我们仅需在每次访问该博客时将访问者的IP存入Redis中，Set数据类型会自动保证IP地址的唯一性。<br />&nbsp;&nbsp; &nbsp;&nbsp; 2). 充分利用Set类型的服务端聚合操作方便、高效的特性，可以用于维护数据对象之间的关联关系。比如所有购买某一电子设备的客户ID被存储在一个指定的Set中，而购买另外一种电子产品的客户ID被存储在另外一个Set中，如果此时我们想获取有哪些客户同时购买了这两种商品时，Set的intersections命令就可以充分发挥它的方便和效率的优势了。</p></div><div style="clear: both; font-family: Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif; font-size: 12px; line-height: 18px; background-color: #ffffff;"></div><div id="blog_post_info_block" style="margin-top: 20px; font-family: Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif; font-size: 12px; line-height: 18px; background-color: #ffffff;"><div id="BlogPostCategory" style="margin-bottom: 10px;">分类:&nbsp;<a href="http://www.cnblogs.com/stephen-liu74/category/354125.html" style="color: #1d58d1; text-decoration: none;">Redis</a></div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/420889.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2014-12-01 19:45 <a href="http://www.blogjava.net/xiaomage234/archive/2014/12/01/420889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Redis学习手册(Hashes数据类型)</title><link>http://www.blogjava.net/xiaomage234/archive/2014/12/01/420888.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 01 Dec 2014 11:44:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2014/12/01/420888.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/420888.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2014/12/01/420888.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/420888.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/420888.html</trackback:ping><description><![CDATA[<div id="cnblogs_post_body" style="margin-bottom: 20px; font-family: Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif; font-size: 12px; line-height: 18px; background-color: #ffffff;"><p style="margin-top: 10px; margin-bottom: 10px;"><span style="font-size: 14pt;"><strong><span style="color: #ff0000;">一、概述：</span></strong></span><br /><br />&nbsp;&nbsp; &nbsp;&nbsp; 我们可以将Redis中的Hashes类型看成具有String Key和String Value的map容器。所以该类型非常适合于存储值对象的信息。如Username、Password和Age等。如果Hash中包含很少的字段，那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash可以存储4294967295个键值对。<br /><br /><span style="font-size: 14pt;"><strong><span style="color: #ff0000;">二、相关命令列表：</span></strong></span></p><table border="0" align="center" style="border-style: solid; border-color: silver; border-collapse: collapse; width: 680px;"><tbody><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><strong><span style="color: #0000ff;">命令原型</span></strong></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><strong><span style="color: #0000ff;">时间复杂度</span></strong></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><strong><span style="color: #0000ff;">命令描述</span></strong></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><strong><span style="color: #0000ff;">返回值</span></strong></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HSET</strong>&nbsp;key field value</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">为指定的Key设定Field/Value对，如果Key不存在，该命令将创建新Key以参数中的Field/Value对，如果参数中的Field在该Key中已经存在，则用新值覆盖其原有值。&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">1表示新的Field被设置了新值，0表示Field已经存在，用新值覆盖原有值。&nbsp;</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HGET</strong>&nbsp;key field&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回指定Key中指定Field的关联值。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回参数中Field的关联值，如果参数中的Key或Field不存，返回nil。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HEXISTS</strong>key field&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">判断指定Key中的指定Field是否存在。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">1表示存在，0表示参数中的Field或Key不存在。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HLEN</strong>&nbsp;key&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">获取该Key所包含的Field的数量。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回Key包含的Field数量，如果Key不存在，返回0。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HDEL</strong>&nbsp;key field [field ...]&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示参数中待删除的字段数量。从指定Key的Hashes Value中删除参数中指定的多个字段，如果不存在的字段将被忽略。如果Key不存在，则将其视为空Hashes，并返回0.</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">实际删除的Field数量。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HSETNX</strong>key field value</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">只有当参数中的Key或Field不存在的情况下，为指定的Key设定Field/Value对，否则该命令不会进行任何操作。&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">1表示新的Field被设置了新值，0表示Key或Field已经存在，该命令没有进行任何操作。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HINCRBY</strong>key field increment&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(1)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">增加指定Key中指定Field关联的Value的值。如果Key或Field不存在，该命令将会创建一个新Key或新Field，并将其关联的Value初始化为0，之后再指定数字增加的操作。该命令支持的数字是64位有符号整型，即increment可以负数。&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回运算后的值。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HGETALL</strong>key</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示Key包含的Field数量。获取该键包含的所有Field/Value。其返回格式为一个Field、一个Value，并以此类推。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">Field/Value的列表。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HKEYS</strong>key&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示Key包含的Field数量。返回指定Key的所有Fields名。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">Field的列表。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HVALS</strong>key&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示Key包含的Field数量。返回指定Key的所有Values名。&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">Value的列表。&nbsp;</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HMGET</strong>key field [field ...]&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)&nbsp;</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示请求的Field数量。获取和参数中指定Fields关联的一组Values。如果请求的Field不存在，其值返回nil。如果Key不存在，该命令将其视为空Hash，因此返回一组nil。</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">返回和请求Fields关联的一组Values，其返回顺序等同于Fields的请求顺序。</span></td></tr><tr><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;"><strong>HMSET</strong>&nbsp;key field value [field value ...]</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">O(N)</span></td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><span style="color: #0000ff;">时间复杂度中的N表示被设置的Field数量。逐对依次设置参数中给出的Field/Value对。如果其中某个Field已经存在，则用新值覆盖原有值。如果Key不存在，则创建新Key，同时设定参数中的Field/Value。&nbsp;</span>&nbsp;</td><td style="font-size: 12px; border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;">&nbsp;</td></tr></tbody></table><p style="margin-top: 10px; margin-bottom: 10px;"><span style="font-size: 14pt;"><strong><span style="color: #ff0000;">三、命令示例：</span></strong></span><br /><br />&nbsp;&nbsp; &nbsp;<span style="color: #800080; font-size: 16px;">1. HSET/HGET/HDEL/HEXISTS/HLEN/HSETNX:</span><br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#在Shell命令行启动Redis客户端程序</span></em><br />&nbsp;&nbsp; &nbsp;/&gt;&nbsp;<em><span style="color: #0000ff;">redis-cli</span></em><br />&nbsp;<em><span style="color: #008000;">&nbsp; &nbsp;#给键值为myhash的键设置字段为field1，值为stephen。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hset myhash field1 "stephen"</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#获取键值为myhash，字段为field1的值。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hget myhash field1</span></em><br />&nbsp;&nbsp; &nbsp;"stephen"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#myhash键中不存在field2字段，因此返回nil。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hget myhash field2</span></em><br />&nbsp;&nbsp; &nbsp;(nil)<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#给myhash关联的Hashes值添加一个新的字段field2，其值为liu。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hset myhash field2 "liu"</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#获取myhash键的字段数量。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hlen myhash</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 2<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#判断myhash键中是否存在字段名为field1的字段，由于存在，返回值为1。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hexists myhash field1</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#删除myhash键中字段名为field1的字段，删除成功返回1。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hdel myhash field1</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#再次删除myhash键中字段名为field1的字段，由于上一条命令已经将其删除，因为没有删除，返回0。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hdel myhash field1</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 0<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#判断myhash键中是否存在field1字段，由于上一条命令已经将其删除，因为返回0。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hexists myhash field1</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 0<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#通过hsetnx命令给myhash添加新字段field1，其值为stephen，因为该字段已经被删除，所以该命令添加成功并返回1。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hsetnx myhash field1 stephen</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#由于myhash的field1字段已经通过上一条命令添加成功，因为本条命令不做任何操作后返回0。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hsetnx myhash field1 stephen</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 0<br /><br /><span style="color: #800080; font-size: 16px;">&nbsp;&nbsp; 2. HINCRBY：</span><br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#删除该键，便于后面示例的测试。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">del myhash</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#准备测试数据，该myhash的field字段设定值1。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hset myhash field 5</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#给myhash的field字段的值加1，返回加后的结果。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hincrby myhash field 1</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 6<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#给myhash的field字段的值加-1，返回加后的结果。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hincrby myhash field -1</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 5<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#给myhash的field字段的值加-10，返回加后的结果。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hincrby myhash field -10</span></em><br />&nbsp;&nbsp; &nbsp;(integer) -5&nbsp;&nbsp;&nbsp;<br /><br /><span style="color: #800080; font-size: 16px;">&nbsp;&nbsp; &nbsp;3. HGETALL/HKEYS/HVALS/HMGET/HMSET:</span><br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#删除该键，便于后面示例测试。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">del myhash</span></em><br />&nbsp;&nbsp; &nbsp;(integer) 1<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#为该键myhash，一次性设置多个字段，分别是field1 = "hello", field2 = "world"。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hmset myhash field1 "hello" field2 "world"</span></em><br />&nbsp;&nbsp; &nbsp;OK<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#获取myhash键的多个字段，其中field3并不存在，因为在返回结果中与该字段对应的值为nil。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hmget myhash field1 field2 field3</span></em><br />&nbsp;&nbsp; &nbsp;1) "hello"<br />&nbsp;&nbsp; &nbsp;2) "world"<br />&nbsp;&nbsp; &nbsp;3) (nil)<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#返回myhash键的所有字段及其值，从结果中可以看出，他们是逐对列出的。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hgetall myhash</span></em><br />&nbsp;&nbsp; &nbsp;1) "field1"<br />&nbsp;&nbsp; &nbsp;2) "hello"<br />&nbsp;&nbsp; &nbsp;3) "field2"<br />&nbsp;&nbsp; &nbsp;4) "world"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#仅获取myhash键中所有字段的名字。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;&nbsp;<em><span style="color: #0000ff;">hkeys myhash</span></em><br />&nbsp;&nbsp; &nbsp;1) "field1"<br />&nbsp;&nbsp; &nbsp;2) "field2"<br /><em><span style="color: #008000;">&nbsp;&nbsp; &nbsp;#仅获取myhash键中所有字段的值。</span></em><br />&nbsp;&nbsp; &nbsp;redis 127.0.0.1:6379&gt;<em><span style="color: #0000ff;">&nbsp;hvals myhash</span></em><br />&nbsp;&nbsp; &nbsp;1) "hello"<br />&nbsp;&nbsp; &nbsp;2) "world"&nbsp;</p></div><div style="clear: both; font-family: Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif; font-size: 12px; line-height: 18px; background-color: #ffffff;"></div><div id="blog_post_info_block" style="margin-top: 20px; font-family: Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif; font-size: 12px; line-height: 18px; background-color: #ffffff;"><div id="BlogPostCategory" style="margin-bottom: 10px;">分类:&nbsp;<a href="http://www.cnblogs.com/stephen-liu74/category/354125.html" style="color: #1d58d1; text-decoration: none;">Redis</a></div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/420888.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2014-12-01 19:44 <a href="http://www.blogjava.net/xiaomage234/archive/2014/12/01/420888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>