﻿<?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-经验不在于年限，在于积累---专注互联网软件开发</title><link>http://www.blogjava.net/hankchen/</link><description>把工作当事业做，把项目当作品做！</description><language>zh-cn</language><lastBuildDate>Thu, 30 Apr 2026 06:24:11 GMT</lastBuildDate><pubDate>Thu, 30 Apr 2026 06:24:11 GMT</pubDate><ttl>60</ttl><item><title>利用百度分词原理设置网站首页标题</title><link>http://www.blogjava.net/hankchen/archive/2012/11/07/390976.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 07 Nov 2012 08:22:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/11/07/390976.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/390976.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/11/07/390976.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/390976.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/390976.html</trackback:ping><description><![CDATA[网站首页标题（Title）是网站SEO中最重要的部分了。百度已经多次申明Keywords和Description都不会影响网站的权重，前者是完全放弃，后者只是在搜索结果中起到展示作用。那么应该如何确定网站首页标题呢？<br /><br />总的来说，设置网站标题必须把握以下原则：<br />1、标题不能太长。原则上标题长度不能超过80个汉字，但实际上超过30个汉字，就在百度的搜索结果中显示不完整了。<br />2、标题包含的信息要足够多。信息太少不能很好地利用长尾词带来的巨大效果。<br />3、标题在长度合适的情况下，必须出现尽可能多地出现主打的推广关键词。<br /><br />以上三个条件看起来有些矛盾，又不能太长，又需要包含更多的推广关键词，这怎么可能呢？<br /><br />实际上，只要我们理解了百度分词的原理，这个目标是可以实现的。下面以一个<a title="婴儿网站" href="http://www.baobeicang.com">婴儿网站</a><a href="http://www.baobeicang.com">www.baobeicang.com</a>作为例子来分析下。<br /><br />这个网站的标题是&#8220;婴儿用品排行榜，婴儿奶粉什么牌子好，婴儿辅食大全&#8221;。这个标题看似只有三个关键词，实际上推广的关键词很多。<br /><br />下面简单列举下几个，大家可以思考下为什么会有这样的效果：<br />1、婴儿用品<br />2、婴儿用品排行榜<br />3、婴儿奶粉<br />4、婴儿奶粉排行榜<br />5、婴儿奶粉什么牌子好<br />6、婴儿用品什么牌子好<br />。。。。。。<br /><br />这就是利用百度分词原理得到的结果。有了这么多长尾关键词在首页标题里面，相信接下来的SEO工作会事半功倍的！<br /><br /><img src ="http://www.blogjava.net/hankchen/aggbug/390976.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-11-07 16:22 <a href="http://www.blogjava.net/hankchen/archive/2012/11/07/390976.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SEO阶段总结之善于向优秀的竞争对手学习</title><link>http://www.blogjava.net/hankchen/archive/2012/10/23/390074.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Tue, 23 Oct 2012 00:54:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/10/23/390074.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/390074.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/10/23/390074.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/390074.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/390074.html</trackback:ping><description><![CDATA[我是从8月底开始接触SEO的，到现在差不多两个月的时间，这段时间我成长了很多。关于SEO的水平，我只能算是刚刚入门，这段时间做了几个网站，有几个关键词进了百度首页，比如：淘宝网亲子装<a href="http://www.woshuone.com">www.woshuone.com</a>，婴儿爬行垫<a href="http://www.baobeicang.com">www.baobeicang.com</a>。但是网站的转化率不高，需要不断地学习，共勉！<br /><br />这里我只是提出一个观点：做好SEO需要善于向优秀的竞争对手学习！可以向竞争对手学习的内容包括下面几点：<br /><br />一、网站标题、描述、主要关键词是怎么确定的？<br />网站标题、描述、主要关键词都是需要在建站的时候就定下来的。一旦上线最好不要修改它，特别是新站，权重不高的时候。如果你不知道怎么定这些的话，一个方法是利用一些站长工具的关键词挖掘功能。还有一个很好的办法就是参考竞争对手的网站，看看别人是怎么定的。自己也要思考为什么别人要那样设置？那样设置有什么好处？然后再来定自己网站的。当然也不能和竞争对手的一模一样。目前比较流行的方法是利用百度的分词原理设置网站的标题，这样可以产生很多的长尾。<br /><br />二、网站是怎么样布局的？<br />网站的布局对SEO也很重要，合理的布局有利于用户和网络爬虫的浏览。可以学习对手的布局，包括：首页、栏目页、内容页、导航栏等等。另外发现好的站点也可以查看下使用的是什么模版开发的。比如我在定钻石拉链<a href="http://www.xurizipper.com">www.xurizipper.com</a>这个站布局的时候就参考了很多的竞争对手。<br /><br />三、文章是怎么写的？怎么更新？内链怎么弄？<br />网站的原创文章是提高网站排名和权重的法宝之一。你可以仔细看看对手的文章，分析都是怎么写成的。一般网站有100篇以上的文章的话，全部是原创有点难，很多是伪原创的。那么他是用什么手段做到很好的伪原创的呢？还有一点就是原创文章的更新，这也是有技巧的。原则就是需要定时定量地更新原创或深度伪原创文章！你可以观察对手每天更新多少篇文章，更新的时间点是什么时候？<br />内链大多数都是在文章内容页设置的，这时你可以看看对手是怎么弄的。现在百度对内链越来越重视，值得站长们去挖掘。<br /><br />四、外链是怎么发布的？<br />外链怎么发布也是个重要的课题。你可以分析对手的外链组成，看都是在哪些地方发的？然后你可以分析那些发外链的地方有没有价值去发，或者好不好留外链。<br /><br />总结起来，分析优秀的竞争对手是很有价值的，因为他们是活生生的案例。并且他们的排名都做得不错，说明他们的方法也是有效的！这么好的东西为什么我们不去学习呢？<br />  <img src ="http://www.blogjava.net/hankchen/aggbug/390074.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-10-23 08:54 <a href="http://www.blogjava.net/hankchen/archive/2012/10/23/390074.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HTTP返回码中30X对SEO的影响</title><link>http://www.blogjava.net/hankchen/archive/2012/09/23/388379.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Sun, 23 Sep 2012 09:33:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/09/23/388379.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/388379.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/09/23/388379.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/388379.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/388379.html</trackback:ping><description><![CDATA[在网站中由于某种原因经常会用到页面跳转。这种跳转从底层原理来看，都是利用了HTTP协议的30X返回码来实现的。<br /><br />常用的30X返回码有下面几种：<br />301 永久性跳转<br />302 暂时性跳转<br />304 Not Modified 文档未修改<br />305 Use Proxy&nbsp; 客户请求的文档应该通过Location头所指明的代理服务器提取，属于HTTP 1.1新增内容<br />其中，对SEO产生影响的是前两个，即301和302。我就先详细的介绍这两个状态码的区别。<br /><br /><strong>一、302<br /></strong>对用户而已，301，302是没有区别的。他们看到效果只是一个跳转，浏览器中旧的URL变成了新的URL。页面跳到了这个新URL指向的地方。<br />但是对于搜索引擎，302转向可能会有URL规范化及网址劫持的问题。可能被搜索引擎判为是作弊。网址规范化的内容可以参考这篇文章：<a style="color: rgb(204,0,0); text-decoration: none" href="http://www.chinamyhosting.com/seoblog/2006/04/10/url-canonicalization/" rel="nofollow">http://www.chinamyhosting.com/seoblog/2006/04/10/url-canonicalization/</a> 
<p style="margin-top: 0px; margin-bottom: 0px; color: rgb(0,0,0); padding-top: 10px">那么，网址劫持是怎么回事呢？</p>
<p style="margin-top: 0px; margin-bottom: 0px; color: rgb(0,0,0); padding-top: 10px">302重定向和网址劫持有什么关系呢？这要从搜索引擎如何处理302转向说起。从定义来说，从网址A做一个302重定向到网址B时，主机服务器的隐含意思是网址A随时有可能改主意，重新显示本身的内容或转向其他的地方。大部分的搜索引擎在大部分情况下，当收到302重定向时，一般只要去抓取目标网址就可以了，也就是说网址B。实际上如果搜索引擎在遇到302转向时，百分之百的都抓取目标网址B的话，就不用担心网址URL劫持了。<br /><br />问题就在于，有的时候搜索引擎，尤其是Google，并不能总是抓取目标网址。为什么呢？比如说，有的时候A网址很短，但是它做了一个302重定向到B网址，而B网址是一个很长的乱七八糟的URL网址，甚至还有可能包含一些问号之类的参数。很自然的，A网址更加用户友好，而B网址既难看，又不用户友好。这时Google很有可能会仍然显示网址A。</p>
<p style="margin-top: 0px; margin-bottom: 0px; color: rgb(0,0,0); padding-top: 10px">由于搜索引擎排名算法只是程序而不是人，在遇到302重定向的时候，并不能像人一样的去准确判定哪一个网址更适当，这就造成了网址URL劫持的可能性。也就是说，一个不道德的人在他自己的网址A做一个302重定向到你的网址B，出于某种原因， Google搜索结果所显示的仍然是网址A，但是所用的网页内容却是你的网址B上的内容，这种情况就叫做网址URL劫持。你辛辛苦苦所写的内容就这样被别人偷走了。<br /></p><br /><strong>二、301</strong> 
<p style="margin-top: 0px; margin-bottom: 0px; color: rgb(0,0,0); padding-top: 10px">当网页A用301重定向转到网页B时，搜索引擎可以肯定网页A永久的改变位置，或者说实际上不存在了，搜索引擎就会把网页B当作唯一有效目标。301的好处是:</p>
<p style="margin-top: 0px; margin-bottom: 0px; color: rgb(0,0,0); padding-top: 10px">（1）没有网址规范化问题。<br />（2）网页A的PR网页级别会传到网页B，这对SEO非常重要！<br /><br />在实际操作过程中，我和同事对一个站点（<a href="http://www.woshuone.com">www.woshuone.com</a>）做了实验。结果发现不管是使用301还是302，很容易被Google &#8220;K掉&#8221;，取消跳转之后过一周左右的时间就又恢复了。看来搜索引擎蜘蛛现在对页面跳转还是比较反感，认为作弊的可能性大，宁可错杀一百也不放过一人！<br /><br /><br /></p><img src ="http://www.blogjava.net/hankchen/aggbug/388379.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-09-23 17:33 <a href="http://www.blogjava.net/hankchen/archive/2012/09/23/388379.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从经理的角度看技术债务【转】</title><link>http://www.blogjava.net/hankchen/archive/2012/08/29/386485.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 29 Aug 2012 01:43:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/08/29/386485.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/386485.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/08/29/386485.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/386485.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/386485.html</trackback:ping><description><![CDATA[<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">英文原文：<a style="font-size: 15px; color: rgb(27,139,224); line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal; text-decoration: none" href="http://www.infoq.com/articles/technical-debt-levison" data-mce-href="http://www.infoq.com/articles/technical-debt-levison">Technical Debt a Perspective for Managers</a></p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">作者：Mark Levison 译者：赖勤毅 发布于 2010年11月5日</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">现在已经到第十次迭代开发周期了，你的项目开发速度开始变慢。在之前的几个迭代周期中，团队没有像以前那样完成很多的&#8220;故事场景&#8221;（stories）。此外，最近在新的故事场景和回溯中却发现更多缺陷(bug)。项目经理知道，团队成员没有变，他们也花同样的时间工作。但是，客户会发问：&#8220;发生什么事情了？这个团队还在努力工作吗？&#8221;</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">很多敏捷团队的产品改进率为150-500%，可是你们的项目看起来却貌似只有20-40%左右的改进率。这到底是怎么回事呢？在此我们找不到什么大问题；相反，只是有无数的小问题。有时，这些只是一些为了方便而使用的捷径（开发人员没有时间去清理这些修改），有时开发人员仅仅是不熟悉这中语言。还有一些问题就是，代码跟灌木丛一样凌乱，需要大幅度的修整。所有这些都属于&#8220;技术债务&#8221;。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">什么是&#8220;技术债务&#8221;？</strong></p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">它就是&#8220;那些内在的事物，现在你不去解决，遗留下来（不干完），它就会阻碍未来开发&#8221;[<a style="font-size: 15px; color: rgb(27,139,224); line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal; text-decoration: none" href="http://c2.com/cgi/wiki?TechnicalDebt" target="_blank" data-mce-href="http://c2.com/cgi/wiki?TechnicalDebt">Ward Cunningham</a>]。 表面上，应用程序看起来质量很高且状况良好，但是这些问题却隐藏在下面。 QA（质量保证部门）甚至可能告诉你说，这个应用程序真是不错，几乎找不到缺陷，但是其中仍然存在&#8220;技术债务&#8221;，如果我们没有很好地管理并设法降低这些&#8220;技术债务&#8221;，那么，程序编写和维护的代价最终将会超过它对客户的价值。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">技术债务就像信用卡一样，会有很高的利息率，就如同给团队留下了大量的帐务开销。这种情况下，开销将会体现在时间花费和解决问题所需的努力上面。开发团队拖延债务的时间越长，所积累的利息就越多（会额外增加很多工作），付出的成本也就越高。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">另外，这还增加了实际的财务支出：开发团队处理技术债务所花费的时间，可以用在对团队有价值的其它工作上。同时，这些难读的代码引起的技术债务也让我们难以找到软件的缺陷。再且，理解代码所损失的时间还可以用来做其它更有价值的事情呢。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">我们为何要累积技术债务呢？</strong></p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">项目编码初期，不整理代码，不写单元测试，也不做测试驱动开发，整个团队粗制滥造出更多的&#8220;故事场景&#8221;。 这些问题通常都不会马上暴露出来，而循规蹈矩地编写代码往往需要更多的时间，特别是在早期阶段。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">技术债务来自哪里？</strong></p>
<ul style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px 0px 1.62em 2.5em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; padding-top: 0px; white-space: normal; list-style-type: square; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">没有经验的开发人员 &#8212;&#8212; 有些项目里面，编写Java/C#/Ruby的开发人员没有接受过培训，或者没有面向对象的观念。结果呢，他们会一直编写适用于他们曾经习惯的编程语言&#8212;&#8212;像Visual Basic等&#8212;&#8212;的代码。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">项目工期的压力 &#8212;&#8212; 那些来自于经理或客户的显性压力和其它一些潜在的压力。&#8220;我们承诺在以后迭代（发布）的故事场景中做到这些&#8221;。开发团队会发现他们不会交付这些承诺的发行版本（迭代版本），而是采取便捷的手段。&#8220;我们不得不把事情做完；我们无法承担修整代码所耗费的时间。如果这不是新特性/缺陷，那么就不用去做&#8221;。 不幸的是，这些观点还会得到管理人员的支持，特别是在管理层没有意识到这些成本的时候。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">凌乱而难读的代码。当一个方法或类中存在一些难读的代码，下一个开发人员继续这些工作的时候，就觉得他也没有必要迫使自己编写清晰的代码。所以，每次有人接手这些代码的时候，一小段脏乱的代码将会变成一大堆乱七八糟的代码。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">专业领域的代码 &#8212;&#8212; 往往有这样的观念：这些代码看起来就是很差劲，但是这不属于我的领域，所以我不能或不会改变它们。另外，对于修改专业领域的代码，开发人员也会觉得力不从心。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">过度复杂 &#8212;&#8212; 开发人员经常趋向于在需求之前设计解决方案。而且，很多情况下他们的代码没有找到正确的方向，还会写一些没有用处的代码。或者，这些代码没有真正地符合需求，因为代码在问题还没有完全明白的时候就已经写完了。还有另外一种情况，过度设计花费了额外的时间，产生的代码不是不能使用就是不符合项目的需求。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">糟糕的设计 &#8212;&#8212; 有些解决方案只是设计不佳。但在设计不好的代码上面继续扩展，而不去解决这个问题，会使问题更加恶化。</li></ul>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">解决问题</strong></p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">这个问题的并不是一下子可以解决的，解决方案需要通过几个迭代周期。并且，你需要耐心，并要从多个角度寻找解决途径。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">解决方案中的要点</p>
<ol style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px 0px 1.62em 2.5em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; padding-top: 0px; white-space: normal; list-style-type: decimal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">让开发人员接受语言方面的基本培训并教授他们面向对象的原理，从而把他们的理解力提升至入门阶段。要想既成功又有效的话，这需要花几个礼拜的时间培训，还需要有精通这方面的人员来跟进和支持这一系列培训。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">告诉开发人员和管理人员，当前的这些问题都是需要花费企业资金的。这点尤其重要，因为它会使解决这些问题的意义更加明确。你要清楚地告诉他们，管理人员会重视这些问题，并且已经开始着手偿还这些技术债务了。不断支持这些工作使之成为常规的原则之一，这样整个团队就会信任这个准则。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">提供一些培训，包括代码的坏味道，重构，单元测试和测试驱动开发等。还可以结合课堂会议，基于网络的材料和书籍来进行培训。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">在工作的时候，给他们一些空余时间去研究和练习他们的技能（一个礼拜两个小时应该是达成这个目标的最小的时间量）。练习的代码应该被丢弃，这样，他们就能无拘无束地尝试和试验一些事情，不管怎么样，他们不用在基础代码库上面进行练习。花点时间练习和研究应该是最有用的建议了。假如没有为他们提供空闲的时间，就压根不会发现真正合理的敏捷开发方式。这种组织方式也被称为&#8220;道场&#8221;-Dojo（更加详细的资料可以参考&nbsp;<a style="font-size: 15px; color: rgb(27,139,224); line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal; text-decoration: none" href="http://www.notesfromatooluser.com/2008/10/tdd-randori-session.html" target="_blank" data-mce-href="http://www.notesfromatooluser.com/2008/10/tdd-randori-session.html">TDD Randori Session</a><span class="Apple-converted-space">&nbsp;</span>和&nbsp;<a style="font-size: 15px; color: rgb(27,139,224); line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal; text-decoration: none" href="http://www.notesfromatooluser.com/2008/10/tdd-randori-workshop.html" target="_blank" data-mce-href="http://www.notesfromatooluser.com/2008/10/tdd-randori-workshop.html">TDD Randori Workshop</a>）。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">使用工具（静态分析，单元测试，持续集成，自动化可接受性测试）来帮助团队发现、减少和衡量他们的技术债务量。应该在团队层面利用这些度量数据，而不能分享给管理人员。团队成员需要知道，他们并不会因为对这些技术债务的度量而接受奖励或者遭受惩罚。如果我们把这些度量数据报告给管理层，并由管理层来追踪的话，开发人员很快会找到一些窍门来规避它们，这样就失去了本来应有的价值了。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">当人们完成了他们的培训或者在技术上取得了少许提升时，应该得到奖励。奖励可以是给予一次认可的表彰或者是一些小小的礼物，但不要直接给予现金奖励。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">每两个礼拜进行一次午餐聚会和一些关于技术方面的学习型会议。利用这些会议来促进开发成员之间的讨论。提供午餐可以提高参会人数。另外比较重要的是需要管理人员每次都来出席，这样就很清楚的表明他们也一直支持大家提高技术。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">维护关于技术债务的备忘录 &#8211; 任何时候，如果开发人员发现一些无法立即应付的技术债务时，就需要填写一份技术债务登记卡。开发人员应该优先考虑这些技术债务，并花费10-15%的精力来偿还这些技术债务。大多数项目中，任何的一些小事都会使问题变得更加严重。</li></ol>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">基于这样的立场，你会发现问题，也会拥有机会。你的问题是：项目的基础代码一直在积累技术债务，但是这些债务已经开始下降了。然而，现在跟客户申请用于处理这些问题的资金会跟处理这些问题一样困难。你的机遇是：提高了开发人员的技能；清楚地表达了管理层对这项工作的支持，还有，软件的代码质量会不断改善，软件缺陷的数目也会不断减少。同时这也会很好的提高团队的整体开发能力。</p><img src ="http://www.blogjava.net/hankchen/aggbug/386485.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-08-29 09:43 <a href="http://www.blogjava.net/hankchen/archive/2012/08/29/386485.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏数据平台规划</title><link>http://www.blogjava.net/hankchen/archive/2012/08/29/386482.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 29 Aug 2012 01:40:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/08/29/386482.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/386482.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/08/29/386482.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/386482.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/386482.html</trackback:ping><description><![CDATA[<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">总的来说，游戏数据平台的工作内容就是围绕<strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">&#8220;数据&#8221;</strong><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">+</strong><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">&#8220;服务&#8221;</strong>。现在专注独立运营游戏之后，数据平台的重要性就更加突出了。但是，目前的各个系统还不能很好地支撑这次业务上的重大转变，我们需要一个全局的思维来重新规划整个数据平台，包括怎么收集和处理数据、怎么更好地对外提供服务等等。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">一、从数据的角度来看，数据包括：收集、存储、分析（挖掘）、展示、提取。</strong>兼顾目前的系统，收集、存储、挖掘、提取，这几个环节的系统需要加强。特别是收集和提取这块。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">由于历史的原因，加之没有系统规划过，之前的数据收集来源比较零散，这样导致分析数据时需要从各个地方来同步数据。当业务多了之后，这些乱七八糟的来源就够让人头疼了。这次规划的一个重点就是，建立一套游戏数据收集系统。游戏分析涉及的数据（日志类）都从这个系统中获取，不再单独分析各自的业务数据。业务系统采用数据上报的方式，按照固定的格式来上报数据。例如：登录、注册、充值、消耗等等。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">采用这套游戏数据收集系统，还有一个重要的原因。现在我们的游戏都是独立运营游戏（我们是甲方），那么我们就可以采用类似腾讯的办法，事先定义好数据规范，要求游戏方按照我们的格式上报相关数据给我们。这样我们的数据分析系统就可以做到非常通用。不管接多少游戏，分析和展示系统都统一。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">数据提取，这里主要是指临时数据。这是我们这边的一个顽疾。临时数据其实包括两个部分：数据来源和数据分析。一个常见的需求就是：分析一堆帐号（数据来源）的后续行为（数据分析）。因为临时数据的业务规则复杂，并且数据来源千奇百怪，之前采用过全手工、全自动的方式来实现，但是都失败了。现在想到的一个解决方案就是，基于游戏数据收集系统之上，再开发一个临时数据分析系统。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">游戏数据收集系统统一了数据的来源和格式，方便存储和提取原始数据。这基本解决了数据来源的问题。临时数据分析系统，可以事先实现常见的分析逻辑（例如：登录、留存、保有、付费人数、付费金额、消耗等等），然后采用过滤器模式或包装器模式来实现。这其实是一个半自动化的方案。系统的用户是开发人员和产品人员。开发完这套系统应该可以省掉60%以上的临时数据任务。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">另外，只要数据都是来自这套游戏数据收集系统，存储、分析，包括挖掘都会简单很多。存储都会在HDFS上，分析基本都使用Hive，挖掘用Mahout。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">游戏数据挖掘是需要单独发展的一块业务，特别是现在专注独立运营之后。目前这块我们还需要更多的时间来积累经验。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">二、从服务的角度来看，服务包括：服务框架、服务管理、服务监控</strong>。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">服务也是一个大范畴。服务化框架、页面登录服务器、GM接口，这些都属于服务相关的内容。兼顾目前的系统，这些内容都已经基本实现，这次只是做比较大的版本改进。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong><font color="#444444">http://www.blogjava.net/hankchen</font></strong></a>）</p><img src ="http://www.blogjava.net/hankchen/aggbug/386482.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-08-29 09:40 <a href="http://www.blogjava.net/hankchen/archive/2012/08/29/386482.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线上应用故障排查系列</title><link>http://www.blogjava.net/hankchen/archive/2012/05/09/377738.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 09 May 2012 12:35:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/05/09/377738.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/377738.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/05/09/377738.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/377738.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/377738.html</trackback:ping><description><![CDATA[<p><font style="background-color: #cce8cf"><font style="background-color: #cce8cf">线上应用的故障排查能力是一个高级软件开发人员的必修课，也最能体现一个技术人员的工作经验和能力。所以，这也是我面试别人的一个必考题目之一。</font></p> <p><font style="background-color: #cce8cf">我打算把自己在这方面的实际经验写成系列文章，主要包括下面这些内容：</font></p> <p></font></p> <p><font color="#0000ff">1、</font><a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html"><font color="#0000ff">线上应用故障排查之一：高CPU占用</font></a></p> <p><font color="#0000ff">2、</font><a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html"><font color="#0000ff">线上应用故障排查之二：高内存占用</font></a></p> <p>3、线上应用故障排查之三：高I/O占用，包括磁盘I/O、网络I/O、数据库I/O等。</p> <p>4、线上应用故障排查之四：程序僵死</p> <p>敬请期待！</p> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p> <img src ="http://www.blogjava.net/hankchen/aggbug/377738.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-05-09 20:35 <a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377738.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线上应用故障排查之二：高内存占用</title><link>http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 09 May 2012 12:21:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/377736.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/377736.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/377736.html</trackback:ping><description><![CDATA[<p>前一篇介绍了<a href=" http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html"><font color="#0000ff">线上应用故障排查之一：高CPU占用</font></a>，这篇主要分析高内存占用故障的排查。</p> <p>搞Java开发的，经常会碰到下面两种异常：</p> <p>1、java.lang.OutOfMemoryError: PermGen space <p>2、java.lang.OutOfMemoryError: Java heap space <p>要详细解释这两种异常，需要简单重提下Java内存模型。</p> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p> <p><strong>Java内存模型</strong>是描述Java程序中各变量（实例域、静态域和数组元素）之间的关系，以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节。</p> <p>在Java虚拟机中，内存分为三个代：新生代（<strong>New</strong>）、老生代（<strong>Old</strong>）、永久代（<strong>Perm</strong>）。</p> <p>（1）新生代New：新建的对象都存放这里</p> <p>（2）老生代Old：存放从新生代New中迁移过来的生命周期较久的对象。新生代New和老生代Old共同组成了堆内存。</p> <p>（3）永久代Perm：是非堆内存的组成部分。主要存放加载的Class类级对象如class本身，method，field等等。</p> <p>如果出现java.lang.OutOfMemoryError: Java heap space异常，说明Java虚拟机的堆内存不够。原因有二：</p> <p>（1）Java虚拟机的堆内存设置不够，可以通过参数-Xms、-Xmx来调整。</p> <p>（2）代码中创建了大量大对象，并且长时间不能被垃圾收集器收集（存在被引用）。</p> <p>如果出现java.lang.OutOfMemoryError: PermGen space，说明是Java虚拟机对永久代Perm内存设置不够。</p> <p>一般出现这种情况，都是程序启动需要加载大量的第三方jar包。例如：在一个Tomcat下部署了太多的应用。</p> <p>&nbsp;</p> <p>从代码的角度，软件开发人员主要关注java.lang.OutOfMemoryError: Java heap space异常，减少不必要的对象创建，同时避免内存泄漏。</p> <p>现在以一个实际的例子分析内存占用的故障排查。</p> <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/2G19(%7B7(0%7DN(FIL09LH175N_2.jpg"><img title="2G19({7(0}N(FIL09LH175N" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="228" alt="2G19({7(0}N(FIL09LH175N" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/2G19(%7B7(0%7DN(FIL09LH175N_thumb.jpg" width="535" border="0"></a> <p>通过top命令，发现PID为9004的Java进程一直占用比较高的内存不释放（24.7%），出现高内存占用的故障。 <p>想起上一篇<a href=" http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html"><font color="#0000ff">线上应用故障排查之一：高CPU占用</font></a>介绍的PS命令，能否找到具体是哪个的线程呢？ <p><font color="#ff0000">ps -mp 9004 -o THREAD,tid,time,rss,size,%mem</font> <p><font color="#ff0000"><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/1_2.jpg"><img title="1" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="219" alt="1" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/1_thumb.jpg" width="539" border="0"></a> </font></p> <p>遗憾的是，发现PS命令可以查到<strong>具体进程</strong>的CPU占用情况，但是不能查到<strong>一个进程下具体线程</strong>的内存占用情况。 <p>&nbsp; <p>只好寻求其他方法了，幸好Java提供了一个很好的内存监控工具：<font color="#ff0000">jmap命令</font> <p>jmap命令有下面几种常用的用法： <p>•jmap [pid] <p>•jmap -histo:live [pid] &gt;a.log <p>•jmap -dump:live,format=b,file=xxx.xxx [pid] <p>用得最多是后面两个。其中，jmap -histo:live [pid] 可以查看当前Java进程创建的活跃对象数目和占用内存大小。 <p>jmap -dump:live,format=b,file=xxx.xxx [pid] 则可以将当前Java进程的内存占用情况导出来，方便用专门的内存分析工具（例如：MAT）来分析。 <p>这个命令对于分析是否有内存泄漏很有帮助。具体怎么使用可以查看本博的另一篇文章：<a href="http://www.blogjava.net/hankchen/archive/2012/03/21/372389.html"><font color="#0000ff">利用Eclipse Memory Analyzer Tool（MAT）分析内存泄漏</font></a> <p><strong></strong>&nbsp; <p><strong>这里详细介绍下jmap -histo:live [pid] 命令：</strong> <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/1_4.jpg"><img title="1" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="380" alt="1" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/1_thumb_1.jpg" width="482" border="0"></a>  <p>从上图可以看出，int数组、constMethodKlass、methodKlass、constantPoolKlass都占用了大量的内存。 <p>特别是占用了大量内存的int数组，需要仔细检查相关代码。 <p>&nbsp; <p><strong>最后，总结下排查内存故障的方法和技巧有哪些：</strong> <p>1、top命令：Linux命令。可以查看实时的内存使用情况。&nbsp;&nbsp; <p>2、jmap -histo:live [pid]，然后分析具体的对象数目和占用内存大小，从而定位代码。 <p>3、jmap -dump:live,format=b,file=xxx.xxx [pid]，然后利用MAT工具分析是否存在内存泄漏等等。 <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong><img src ="http://www.blogjava.net/hankchen/aggbug/377736.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-05-09 20:21 <a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线上应用故障排查之一：高CPU占用</title><link>http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 09 May 2012 12:20:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/377735.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/377735.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/377735.html</trackback:ping><description><![CDATA[<p>一个应用占用CPU很高，除了确实是计算密集型应用之外，通常原因都是出现了死循环。</p> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p> <p>以我们最近出现的一个实际故障为例，介绍怎么定位和解决这类问题。</p> <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/clip_image002_2.jpg"><img title="clip_image002" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="216" alt="clip_image002" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/clip_image002_thumb.jpg" width="600" border="0"></a></p> <p>根据top命令，发现PID为28555的Java进程占用CPU高达200%，出现故障。</p> <p>通过ps aux | grep PID命令，可以进一步确定是tomcat进程出现了问题。但是，怎么定位到具体线程或者代码呢？</p> <p><b>首先显示线程列表</b><b>:</b>  <p><font color="#ff0000">ps -mp pid -o THREAD,tid,time</font>  <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/1.png"><img title="1" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="291" alt="1" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/1_thumb.png" width="469" border="0"></a> </p> <p>找到了耗时最高的线程28802，占用CPU时间快两个小时了！  <p><b>其次将需要的线程ID转换为16进制格式：</b>  <p><font color="#ff0000">printf "%x\n" tid</font>  <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/2.png"><img title="2" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="55" alt="2" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/2_thumb.png" width="372" border="0"></a>  <p><b>最后打印线程的堆栈信息：</b>  <p><font color="#ff0000">jstack pid |grep tid -A 30</font>  <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/3.png"><img title="3" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="401" alt="3" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/3_thumb.png" width="927" border="0"></a>  <p>找到出现问题的代码了！  <p>现在来分析下具体的代码：ShortSocketIO.readBytes(ShortSocketIO.java:106)</p> <p>ShortSocketIO是应用封装的一个用短连接Socket通信的工具类。readBytes函数的代码如下：</p> <p>public byte[] readBytes(int length) throws IOException { <p>&nbsp;&nbsp;&nbsp; if ((this.socket == null) || (!this.socket.isConnected())) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IOException("++++ attempting to read from closed socket"); <p>&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp; byte[] result = null; <p>&nbsp;&nbsp;&nbsp; ByteArrayOutputStream bos = new ByteArrayOutputStream(); <p>&nbsp;&nbsp;&nbsp; if (this.recIndex &gt;= length) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bos.write(this.recBuf, 0, length); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] newBuf = new byte[this.recBufSize]; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this.recIndex &gt; length) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.arraycopy(this.recBuf, length, newBuf, 0, this.recIndex - length); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recBuf = newBuf; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recIndex -= length; <p>&nbsp;&nbsp;&nbsp; } else { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int totalread = length; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this.recIndex &gt; 0) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; totalread -= this.recIndex; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bos.write(this.recBuf, 0, this.recIndex); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recBuf = new byte[this.recBufSize]; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recIndex = 0; <p>&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp; int readCount = 0; <p>&nbsp;&nbsp;&nbsp; <font color="#ff0000">while (totalread &gt; 0) {</font> <p><font color="#ff0000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((readCount = this.in.read(this.recBuf)) &gt; 0) {</font> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (totalread &gt; readCount) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bos.write(this.recBuf, 0, readCount); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recBuf = new byte[this.recBufSize]; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recIndex = 0; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bos.write(this.recBuf, 0, totalread); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] newBuf = new byte[this.recBufSize]; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.arraycopy(this.recBuf, totalread, newBuf, 0, readCount - totalread); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recBuf = newBuf; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recIndex = (readCount - totalread); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; totalread -= readCount; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp; } <p>} <p>问题就出在标红的代码部分。如果this.in.read()返回的数据小于等于0时，循环就一直进行下去了。而这种情况在网络拥塞的时候是可能发生的。 <p>至于具体怎么修改就看业务逻辑应该怎么对待这种特殊情况了。 <p>&nbsp; <p><strong>最后，总结下排查CPU故障的方法和技巧有哪些：</strong> <p>1、top命令：Linux命令。可以查看实时的CPU使用情况。也可以查看最近一段时间的CPU使用情况。 <p>2、PS命令：Linux命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前CPU使用情况。属于当前状态的采样数据。 <p><font color="#ff0000">3、jstack：Java提供的命令。可以查看某个进程的当前线程栈运行情况。根据这个命令的输出可以定位某个进程的所有线程的当前运行状态、运行代码，以及是否死锁等等。</font> <p>4、pstack：Linux命令。可以查看某个进程的当前线程栈运行情况。 <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p><img src ="http://www.blogjava.net/hankchen/aggbug/377735.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-05-09 20:20 <a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jstack -F 命令在Linux 64位机器报错：get_thread_regs failed for a lwp</title><link>http://www.blogjava.net/hankchen/archive/2012/04/09/373640.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Mon, 09 Apr 2012 08:04:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/04/09/373640.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/373640.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/04/09/373640.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/373640.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/373640.html</trackback:ping><description><![CDATA[<p><font style="background-color: #c7edcc" face="Verdana">最近发现一个线上项目运行一段时间之后会僵死。程序不报任何异常，占有系统资源也都正常，就是对外提供不了服务了。</font></p> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong> <p><font style="background-color: #c7edcc" face="Verdana">根据经验，应该是程序有死锁情况，于是在线上运行“jstack –F &lt;pid&gt;”命令，想把线程堆栈dump下来。</font></p> <p><font style="background-color: #c7edcc" face="Verdana">但是，发现这个命令老是报下面的错误：</font></p> <p>Thread 27316: (state = BLOCKED)<br>Error occurred during stack walking:<br>sun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.debugger.DebuggerException: <font color="#ff0000">get_thread_regs failed for a lwp<br></font></p> <p><font color="#000000">线上环境是：</font></p> <p>Linux 2.6.18-194.el5 x86_64 x86_64 x86_64 GNU/Linux</p> <p>java version "1.6.0_21"<br>Java(TM) SE Runtime Environment (build 1.6.0_21-b04)<br>Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01, mixed mode)</p> <p>经过分析发现，这是JDK6u23之前版本的一个Bug，将JDK升级到最新版本（1.6.0_31）就可以解决问题了。</p> <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/Windows-Live-Writer/jstack--F-Linux-64get_thread_regs-failed_E24F/%7BB0E1C5D8-CE33-4940-B416-2E75003A59FA%7D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="{B0E1C5D8-CE33-4940-B416-2E75003A59FA}" border="0" alt="{B0E1C5D8-CE33-4940-B416-2E75003A59FA}" src="http://www.blogjava.net/images/blogjava_net/hankchen/Windows-Live-Writer/jstack--F-Linux-64get_thread_regs-failed_E24F/%7BB0E1C5D8-CE33-4940-B416-2E75003A59FA%7D_thumb.png" width="498" height="199"></a></p><font color="#ff0000"></font> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p><img src ="http://www.blogjava.net/hankchen/aggbug/373640.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-04-09 16:04 <a href="http://www.blogjava.net/hankchen/archive/2012/04/09/373640.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Netty长连接的事件处理顺序问题</title><link>http://www.blogjava.net/hankchen/archive/2012/04/08/373572.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Sun, 08 Apr 2012 04:32:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/04/08/373572.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/373572.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/04/08/373572.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/373572.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/373572.html</trackback:ping><description><![CDATA[<p>最近的一个线上项目（认证服务器）老是出现服务延迟的情况。具体的问题描述：</p> <p>（1）客户端发送一个请求A（长连接），在服务器端的业务层需要20秒以上才能接收到。</p> <p>（2）客户端发送一个请求B（端连接），在服务器端的业务层可以迅速接收到。</p> <p>从现象大致知道问题出在服务器端的网络接收层，大量通过长连接发送过来的请求都堵塞在网络层得不到处理（在网络层排队，还没到应用层）。</p> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p> <p><strong></strong>&nbsp;</p> <p>后来经过排查，发现是Netty中的OrderedMemoryAwareThreadPoolExecutor原因。相关代码如下：</p> <p>MemoryAwareThreadPoolExecutor executor = new OrderedMemoryAwareThreadPoolExecutor(threadNums, maxChannelMemorySize,<br>&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;&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxTotalMemorySize, keepAliveTime,<br>&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;&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimeUnit.SECONDS);<br>ExecutionHandler executionHandler = new ExecutionHandler(executor); <p>public ChannelPipeline getPipeline() throws Exception<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ChannelPipeline pipeline = pipeline();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pipeline.addLast("decoder", new AuthDecoder());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pipeline.addLast("encoder", new AuthEncoder());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pipeline.addLast("executor", executionHandler);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pipeline.addLast("handler", new AuthServerHandler(commandFactory));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return pipeline;<br>} <p>&nbsp;</p> <p>先介绍下背景知识，再来分析问题。</p> <p>大家都知道，Netty是一个基于事件的NIO框架。在Netty中，一切网络动作都是通过事件来传播并处理的，例如：Channel读、Channel写等等。回忆下Netty的流处理模型：</p> <p><strong>Boss线程（一个服务器端口对于一个）---接收到客户端连接---生成Channel---交给Work线程池（多个Work线程）来处理。</strong></p> <p><strong>具体的Work线程---读完已接收的数据到ChannelBuffer---触发ChannelPipeline中的ChannelHandler链来处理业务逻辑。</strong></p> <p>注意：执行ChannelHandler链的整个过程是同步的，如果业务逻辑的耗时较长，会将导致Work线程长时间被占用得不到释放，从而影响了整个服务器的并发处理能力。</p> <p>所以，为了提高并发数，一般通过ExecutionHandler线程池来异步处理ChannelHandler链（worker线程在经过ExecutionHandler后就结束了，它会被ChannelFactory的worker线程池所回收）。在Netty中，只需要增加一行代码：</p><pre>public <a href="http://docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/ChannelPipeline.html"><code>ChannelPipeline</code></a> getPipeline() {
         return <a href="http://docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/Channels.html"><code>Channels</code></a>.pipeline(
                 new DatabaseGatewayProtocolEncoder(),
                 new DatabaseGatewayProtocolDecoder(),
                 <b><font color="#ff0000">executionHandler, // Must be shared</font></b>
                 new DatabaseQueryingHandler());</pre><pre>}</pre><pre>例如：</pre><pre><b><a href="http://docs.jboss.org/netty/3.2/api/org/jboss/netty/handler/execution/ExecutionHandler.html"><code>ExecutionHandler</code></a> executionHandler = new <a href="http://docs.jboss.org/netty/3.2/api/org/jboss/netty/handler/execution/ExecutionHandler.html"><code>ExecutionHandler</code></a>(
             new <a href="http://docs.jboss.org/netty/3.2/api/org/jboss/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.html"><code>OrderedMemoryAwareThreadPoolExecutor</code></a>(16, 1048576, 1048576))</b></pre>
<p>&nbsp;</p>
<p>对于ExecutionHandler需要的线程池模型，Netty提供了两种可选：
<p>1） MemoryAwareThreadPoolExecutor 通过对线程池内存的使用控制，可控制Executor中待处理任务的上限（超过上限时，后续进来的任务将被阻塞），并可控制单个Channel待处理任务的上限，防止内存溢出错误；
<p>2） OrderedMemoryAwareThreadPoolExecutor 是 MemoryAwareThreadPoolExecutor 的子类。除了MemoryAwareThreadPoolExecutor 的功能之外，它还可以保证同一Channel中处理的事件流的顺序性，这主要是控制事件在异步处理模式下可能出现的错误的事件顺序，但它并不保证同一Channel中的事件都在一个线程中执行（通常也没必要）。
<p>例如：<pre>Thread X: --- Channel A (Event A1) --.   .-- Channel B (Event B2) --- Channel B (Event B3) ---&gt;
                                      \ /
                                       X
                                      / \
Thread Y: --- Channel B (Event B1) --'   '-- Channel A (Event A2) --- Channel A (Event A3) ---&gt;</pre>
<p>上图表达的意思有几个：
<p>（1）对整个线程池而言，处理同一个Channel的事件，必须是按照顺序来处理的。例如，必须先处理完Channel A (Event A1) ，再处理Channel A (Event A2)、Channel A (Event A3)
<p>（2）同一个Channel的多个事件，会分布到线程池的多个线程中去处理。
<p>（3）不同Channel的事件可以同时处理（分担到多个线程），互不影响。&nbsp;&nbsp; <p>OrderedMemoryAwareThreadPoolExecutor 的这种事件处理有序性是有意义的，因为通常情况下，请求发送端希望服务器能够按照顺序处理自己的请求，特别是需要多次握手的应用层协议。例如：XMPP协议。
<p>&nbsp; <p>现在回到具体业务上来，我们这里的认证服务也使用了OrderedMemoryAwareThreadPoolExecutor。认证服务的其中一个环节是使用长连接，不断处理来自另外一个服务器的认证请求。通信的数据包都很小，一般都是200个字节以内。一般情况下，处理这个过程很快，所以没有什么问题。但是，由于认证服务需要调用第三方的接口，如果第三方接口出现延迟，将导致这个过程变慢。一旦一个事件处理不完，由于要保持事件处理的有序性，其他事件就全部堵塞了！而短连接之所以没有问题，是因为短连接一个Channel就一个请求数据包，处理完Channel就关闭了，根本不存在顺序的问题，所以在业务层可以迅速收到请求，只是由于同样的原因（第三方接口），处理时间会比较长。
<p>其实，认证过程都是独立的请求数据包（单个帐号），每个请求数据包之间是没有任何关系的，保持这样的顺序没有意义！
<p>&nbsp; <p>最后的改进措施：
<p>1、去掉OrderedMemoryAwareThreadPoolExecutor，改用MemoryAwareThreadPoolExecutor。
<p>2、减少调用第三方接口的超时时间，让处理线程尽早回归线程池。
<p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p> <img src ="http://www.blogjava.net/hankchen/aggbug/373572.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-04-08 12:32 <a href="http://www.blogjava.net/hankchen/archive/2012/04/08/373572.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>