﻿<?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-上善若水-随笔分类-Logging</title><link>http://www.blogjava.net/DLevin/category/54907.html</link><description>In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation.
To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra</description><language>zh-cn</language><lastBuildDate>Thu, 13 Aug 2015 08:47:19 GMT</lastBuildDate><pubDate>Thu, 13 Aug 2015 08:47:19 GMT</pubDate><ttl>60</ttl><item><title>Log4J引起的程序“装死”</title><link>http://www.blogjava.net/DLevin/archive/2015/08/13/426751.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Thu, 13 Aug 2015 08:28:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2015/08/13/426751.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/426751.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2015/08/13/426751.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/426751.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/426751.html</trackback:ping><description><![CDATA[<h2>问题起因</h2>
依然是在使用GemFire的集群中，我们发现偶尔会出现一些GemFire的Function执行特别慢，并且超过了两分钟（为了保证数据的一致性，我们在写之前需要先拿一个Lock，因为不能每个Key都对应一个Lock，因而我们使用了Guava的Stripe Lock（关于Stripe Lock可以参考<a href="http://www.blogjava.net/DLevin/archive/2013/12/25/407990.html">这里</a>），而且这个Lock本身我们指定了2分钟的超时时间，因而如果写超过两分钟，我们就会收到Exception）。这个问题其实已经困扰了我们好几年了，刚前段时间，我们发现长时间的Stop-The-World GC会引起这个问题，而且这种时候很多时候会引起那个节点从集群中退出，并不是所有的这种错误都有GC的问题，我特地查了GC的日志，有些这种写超过两分钟的情况下，GC一直处于非常健康的状态，而且查了GemFire的日志和我们自己的日志，也没有发现任何异常。由于我们每个数据保留两分份拷贝，也就是说每次数据写都要写两个节点，两分钟对CPU来说可以做太多的事情，因而只有IO才能在某些时候产生这种问题，在问题发生的时候也没有任何overflow数据，而且本地操作，即使对IO来说2分钟也是一个非常长的时间了，因而我们只能怀疑这是写另一个节点引起的，对另一个节点，它是在同一个Data Center中，而且基本是在同一个Chasis内部，因而它们之间小于1M的数据量通信也不太可能花去2分钟的时间，所以剩下的我们就只能怀疑网络的问题了，比如数据丢包、网络抖动、网络流量太大一起传输变慢等，但是我们没有找到任何相关的问题。所以我们很长一段时间素手无策，只能怪GemFire闭源，我们不知道这两分钟是不是GemFire自己内部在做一些不为人知的事情，因而太忙了而每来得及处理我们的写请求。虽然我一直觉得不管在处理什么炒作，两分钟都没有响应根本无法解释的通，更何况GemFire节点之间并没有报告有任何异常，或者像以前发现的一个节点向Locator举报另一个节点没有响应的问题，Locator自己也能很正常的向那个节点发送新的成员信息（View），因而看起来向是这个节点虽然花了两分钟多来写一个数据，但是它还是有响应的，有点&#8220;假死&#8221;的赶脚。<br />
<br />
<h2>问题发现</h2>
这个问题这么几年以来时不时的就会发生，而且因为以前花的时间太多了，而且也没有找到任何出错的地方，现在索性不去花太多时间在上面了，更何况这个它很长时间才发生一次，并且今年以来就一直没发生过，直到前几周出现一次，我有点不信邪的重新去看这个问题，依然没有找到任何可疑的地方，GC日志、应用程序日志、GemFire自己的日志、网络、CPU使用情况等所有的都是正常的，除了问题发生的那个时刻，应用程序没有任何日志，另外在问题发生之前出现过Log4J日志文件的Rolling（我们使用RollingFileAppender，并且只保留20个日志文件），但是Log4J日志文件Roll的日志出现了断结，在开始要Roll到真正完成Roll中间还有几行GemFire自身的日志，此时我并没有觉得这个是有很大问题的，因为我始终觉得Log4J除了它自己提到平均对性能有10%的影响以外，它就是一个简单的把日志写到文件的过程，不会影响的整个应用程序本身，因为它太简单了，直到今天这个问题再次出现，依然没有任何其他方面的收获，所有的地方都显示正常状态，甚至我们之前发现的网卡问题今天也没有发生，然而同样是出问题的两分钟没有出现应用程序日志，日志文件Roll的日志和上次类似，开始Roll到结束出现GemFire日志的交叉。
<div><fieldset><legend>最近一次发生的日志</legend>
<div>[info 2015/08/12 01:56:07.736 BST &#8230;] ClientHealthMonitor: Registering client with member id &#8230;</div>
<div>log4j: rolling over count=20971801</div>
<div>log4j: maxBackupIndex=20</div>
<div>[info 2015/08/12 01:56:12.265 BST &#8230;] ClientHealthMonitor: Unregistering client with member id &#8230;</div>
<div>&#8230;&#8230;</div>
<div>[info 2015/08/12 01:56:23.773 BST &#8230;] ClientHealthMonitor: Registering client with member id &#8230;</div>
<div>log4j: Renaming file logs/&#8230;.log.19 to logs/&#8230;.log.20</div>
</fieldset></div>
<div><fieldset><legend>一周前发生的日志</legend>
<div>[info 2015/08/04 01:43:45.761 BST &#8230;] ClientHealthMonitor: Registering client with member id &#8230;</div>
<div>log4j: rolling over count=20971665</div>
<div>log4j: maxBackupIndex=20</div>
<div>&#8230;&#8230;</div>
<div>[info 2015/08/04 01:45:25.506 BST &#8230;] ClientHealthMonitor: Registering client with member id &#8230;</div>
<div>log4j: Renaming file logs/&#8230;.log.19 to logs/&#8230;.log.20</div>
</fieldset></div>
<div>看似这个是一个规律（套用同事的一句话：一次发生时偶然，两次发生就是科学了）。然而此时我其实依然不太相信Log4J是&#8220;凶手&#8221;，因为我一直觉得Log4J是一个简单的日志输出框架，它要是出问题也只是它自己的问题，是局部的，而这个问题的出现明显是全局的，直到我突然脑子一闪而过，<strong>日志打印的操作是synchronized，也就是说在日志文件Roll的时候，所有其它需要打日志的线程都要等待直到Roll完成，如果这个Roll过程超过了2分钟，那么就会发生我们看到的Stripe Lock超时，也就是发生了程序&#8220;假死&#8221;的状态。</strong>重新查看Log4J打印日志的方法调用栈，它会在两个地方用synchronized，即同一个Category（Logger）类实例：<br />
<div style="font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all; background-color: #eeeeee;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;callAppenders(LoggingEvent&nbsp;event)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;writes&nbsp;=&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">for</span>(Category&nbsp;c&nbsp;=&nbsp;<span style="color: #0000FF; ">this</span>;&nbsp;c&nbsp;!=&nbsp;<span style="color: #0000FF; ">null</span>;&nbsp;c=c.parent)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000;">&nbsp;Protected&nbsp;against&nbsp;simultaneous&nbsp;call&nbsp;to&nbsp;addAppender,&nbsp;removeAppender,<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">synchronized</span>(c)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(c.aai&nbsp;!=&nbsp;<span style="color: #0000FF; ">null</span>)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writes&nbsp;+=&nbsp;c.aai.appendLoopOnAppenders(event);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(!c.additive)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">break</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
。。。<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div>
</div>以及同一个Appender在doApppend时：<br /><div style="font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all; background-color: #eeeeee;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">synchronized</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;doAppend(LoggingEvent&nbsp;event)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;。。。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.append(event);<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div><div>而Roll的过程就是在append方法中，进一步分析，在下面两句话之间，他们分别花费了超过100s和超过11s的时间：</div><div>log4j: maxBackupIndex=20<br />。。。</div><div>log4j: Renaming file logs/&#8230;.log.19 to logs/&#8230;.log.20</div><div>而这两句之间只包含了两个File.exists()，一个File.delete()，一个File.rename()操作：</div><div style="font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all; background-color: #eeeeee;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;rollOver()&nbsp;{<br />&nbsp; &nbsp; &nbsp; 。。。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(maxBackupIndex&nbsp;&gt;&nbsp;0)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000;">&nbsp;Delete&nbsp;the&nbsp;oldest&nbsp;file,&nbsp;to&nbsp;keep&nbsp;Windows&nbsp;happy.</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;File(fileName&nbsp;+&nbsp;'.'&nbsp;+&nbsp;maxBackupIndex);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(file.exists())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;renameSucceeded&nbsp;=&nbsp;file.delete();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">for</span>&nbsp;(<span style="color: #0000FF; ">int</span>&nbsp;i&nbsp;=&nbsp;maxBackupIndex&nbsp;-&nbsp;1;&nbsp;i&nbsp;&gt;=&nbsp;1&nbsp;&amp;&amp;&nbsp;renameSucceeded;&nbsp;i--)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;File(fileName&nbsp;+&nbsp;"."&nbsp;+&nbsp;i);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(file.exists())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;target&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;File(fileName&nbsp;+&nbsp;'.'&nbsp;+&nbsp;(i&nbsp;+&nbsp;1));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LogLog.debug("Renaming&nbsp;file&nbsp;"&nbsp;+&nbsp;file&nbsp;+&nbsp;"&nbsp;to&nbsp;"&nbsp;+&nbsp;target);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;renameSucceeded&nbsp;=&nbsp;file.renameTo(target);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp; &nbsp; &nbsp; 。。。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div><div><h2>NFS简单性能测试和分析</h2>因而我对NFS的性能作了一些简单测试：</div><div>只有一个线程时，在NFS下rename性能：</div><div>1 file: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;3ms</div><div>10 files: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;48ms</div><div>20 files: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;114ms</div><div>相比较，在本地磁盘rename的性能：</div><div>1 file: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1ms</div><div>3 files: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1ms</div><div>10 files: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;3ms</div><div>对NFS和本地磁盘写的性能（模拟日志，每行都会flush）：</div><table border="0" cellspacing="0" cellpadding="0" style="border-collapse:collapse;">  <tbody><tr style="height:13.4pt">   <td width="139" valign="top" style="width: 1.45in; border: 1pt solid windowtext; padding: 0in 5.4pt; height: 13.4pt;">   <p>&nbsp;</p>   </td>   <td width="78" valign="top" style="width: 58.5pt; border-style: solid solid solid none; border-top-color: windowtext; border-right-color: windowtext; border-bottom-color: windowtext; border-top-width: 1pt; border-right-width: 1pt; border-bottom-width: 1pt; padding: 0in 5.4pt; height: 13.4pt;">   <p><span style="color:#1F497D">NFS</span></p>   </td>   <td width="78" valign="top" style="width: 58.5pt; border-style: solid solid solid none; border-top-color: windowtext; border-right-color: windowtext; border-bottom-color: windowtext; border-top-width: 1pt; border-right-width: 1pt; border-bottom-width: 1pt; padding: 0in 5.4pt; height: 13.4pt;">   <p><span style="color:#1F497D">LOCAL</span></p>   </td>  </tr>  <tr>   <td width="139" valign="top" style="width: 1.45in; border-style: none solid solid; border-right-color: windowtext; border-bottom-color: windowtext; border-left-color: windowtext; border-right-width: 1pt; border-bottom-width: 1pt; border-left-width: 1pt; padding: 0in 5.4pt;">   <p><span style="color:#1F497D">1 writer, 11M</span></p>   </td>   <td width="78" valign="top" style="width:58.5pt;border-top:none;border-left:none;   border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;   padding:0in 5.4pt 0in 5.4pt">   <p><span style="color:#1F497D">443ms</span></p>   </td>   <td width="78" valign="top" style="width:58.5pt;border-top:none;border-left:none;   border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;   padding:0in 5.4pt 0in 5.4pt">   <p><span style="color:#1F497D">238ms</span></p>   </td>  </tr>  <tr>   <td width="139" valign="top" style="width: 1.45in; border-style: none solid solid; border-right-color: windowtext; border-bottom-color: windowtext; border-left-color: windowtext; border-right-width: 1pt; border-bottom-width: 1pt; border-left-width: 1pt; padding: 0in 5.4pt;">   <p><span style="color:#1F497D">1 writer, 101M</span></p>   </td>   <td width="78" valign="top" style="width:58.5pt;border-top:none;border-left:none;   border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;   padding:0in 5.4pt 0in 5.4pt">   <p><span style="color:#1F497D">2793ms</span></p>   </td>   <td width="78" valign="top" style="width:58.5pt;border-top:none;border-left:none;   border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;   padding:0in 5.4pt 0in 5.4pt">   <p><span style="color:#1F497D">992ms</span></p>   </td>  </tr>  <tr>   <td width="139" valign="top" style="width: 1.45in; border-style: none solid solid; border-right-color: windowtext; border-bottom-color: windowtext; border-left-color: windowtext; border-right-width: 1pt; border-bottom-width: 1pt; border-left-width: 1pt; padding: 0in 5.4pt;">   <p><span style="color:#1F497D">10 writers, 11M</span></p>   </td>   <td width="78" valign="top" style="width:58.5pt;border-top:none;border-left:none;   border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;   padding:0in 5.4pt 0in 5.4pt">   <p><span style="color:#1F497D">~4400ms</span></p>   </td>   <td width="78" valign="top" style="width:58.5pt;border-top:none;border-left:none;   border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;   padding:0in 5.4pt 0in 5.4pt">   <p><span style="color:#1F497D">~950ms</span></p>   </td>  </tr>  <tr>   <td width="139" valign="top" style="width: 1.45in; border-style: none solid solid; border-right-color: windowtext; border-bottom-color: windowtext; border-left-color: windowtext; border-right-width: 1pt; border-bottom-width: 1pt; border-left-width: 1pt; padding: 0in 5.4pt;">   <p><span style="color:#1F497D">10 writers, 101M</span></p>   </td>   <td width="78" valign="top" style="width:58.5pt;border-top:none;border-left:none;   border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;   padding:0in 5.4pt 0in 5.4pt">   <p><span style="color:#1F497D">~30157ms</span></p>   </td>   <td width="78" valign="top" style="width:58.5pt;border-top:none;border-left:none;   border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;   padding:0in 5.4pt 0in 5.4pt">   <p><span style="color:#1F497D">~5500ms</span></p>   </td>  </tr></tbody></table><div><br />一些其他的统计：</div><div><strong>100同时写:</strong></div><div>Create 20 files spend: 301ms</div><div>Renaming 20 files spends: 333ms</div><div>Delete 20 files spends: 329ms</div><div></div><div><strong>1000同时写:</strong></div><div>Create 20 files spend: 40145ms</div><div>Renaming 20 files spends: 39273ms<br /></div><div><strong>而在1000个同时写的过程中，重命名：</strong></div><div>Rename file: LogTest1.50 take: <strong>36434ms</strong></div><div>Rename file: LogTest1.51 take: <strong>39ms</strong></div><div>Rename file: LogTest1.52 take: <strong>34ms</strong><br /></div><div>也就是说在这个模拟过程中，一个文件的rename超过36s，而向我们有十几台机器同时使用相同的NFS，并且每台机器上都跑二三十个程序，如果那段时间同时有上万个的日志写，可以预计达到100s情况是可能发生的。</div><div>关于NFS性能的问题，在《构建高性能WEB站点》的书（330页）中也有涉及。简单的介绍，NFS由Sun在1984年开发，是主流异构平台实现文件共享的首选方案。它并没有自己的传输协议，而是使用RPC（Remote Procedure Call）协议（应用层），RPC协议默认底层基于UDP传输，但是自己实现在丢包时的重传机制，而且NFS服务器采用多进程模型，默认进程为4，但是一般都会调优增加服务进程数，然而&#8220;不管怎么对NFS进行性能优化，NFS注定不适合作为I/O密集型文件共享方案，但可以作为一般用途，比如提供站点内部的资源共享，它的优势在于容易搭建，而且可以减少不必要的数据冗余。&#8221;</div><div>可以使用命令：&#8220;nfsstat -c&#8221;获取对NFS服务器的操作的简单统计，具体可以参考《构建高性能WEB站点》的相关章节，里面还有更详细的对NFS服务器性能的测试。</div><div><br /><h2>总结</h2><strong>从这个事件我总结了两件事情：</strong></div><div>1. 日志的影响可能是全局性的，因而要非常小心，一个耗时的操作可能引起程序的&#8220;假死&#8221;，因而要非常小心。</div><div>2. 虽然把日志打印在NFS上，对大量的日志文件查找会方便很多，但是这是一个很耗性能的设计，特别是当大量的程序共享这个NFS的时候，因而要尽量避免。</div><img src ="http://www.blogjava.net/DLevin/aggbug/426751.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2015-08-13 16:28 <a href="http://www.blogjava.net/DLevin/archive/2015/08/13/426751.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Commons Logging存在的ClassLoader问题详解</title><link>http://www.blogjava.net/DLevin/archive/2012/11/10/391122.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Fri, 09 Nov 2012 16:51:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/11/10/391122.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/391122.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/11/10/391122.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/391122.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/391122.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在看Java Logging相关的框架、代码、资料的时候，一直有听说Commons Logging存在ClassLoader相关的问题，但是看它的实现代码（1.1.1版本），对ClassLoader做了非常详细的查找：用了Thread Context ClassLoader、System ClassLoader、以及LogFactoryImpl本身的ClassLoader，感觉上已经很全面了。上周...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2012/11/10/391122.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/391122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-11-10 00:51 <a href="http://www.blogjava.net/DLevin/archive/2012/11/10/391122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入源码之JDK Logging</title><link>http://www.blogjava.net/DLevin/archive/2012/11/08/390992.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Wed, 07 Nov 2012 16:49:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/11/08/390992.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/390992.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/11/08/390992.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/390992.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/390992.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: JDK从1.4开始提供Logging实现，据说当初JDK打算采用Log4J的，后来因为某些原因谈判没谈拢，然后就自己开发了一套，不知道是为了报复而故意不沿用Log4J的命名方式和抽象方式，还是开发这个模块的人水平不够，或没用心，亦或是我用Commons Logging和Log4J习惯了，看JDK的Logging实现怎么看怎么不爽~~~吐个槽额~~~~  JDK Logging将日志打印抽象成以下几...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2012/11/08/390992.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/390992.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-11-08 00:49 <a href="http://www.blogjava.net/DLevin/archive/2012/11/08/390992.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入源码之SLF4J</title><link>http://www.blogjava.net/DLevin/archive/2012/11/08/390991.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Wed, 07 Nov 2012 16:44:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/11/08/390991.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/390991.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/11/08/390991.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/390991.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/390991.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Commons Logging+Log4J一直是Java日志的经典组合，以至于很多服务器都使用了类似的配置，像WebSphere、以前的Tomcat都使用Commons Logging作为日志输出框架，而据说JBoss则直接Commons Logging和Log4J一起使用了（这个估计是为了解决Commons Logging中经常在这类服务器上遇到的ClassLoader问题）。然而Log4J的开...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2012/11/08/390991.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/390991.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-11-08 00:44 <a href="http://www.blogjava.net/DLevin/archive/2012/11/08/390991.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入源码之Commons Logging </title><link>http://www.blogjava.net/DLevin/archive/2012/11/04/390755.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 03 Nov 2012 17:20:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/11/04/390755.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/390755.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/11/04/390755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/390755.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/390755.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 自从七月份去走川藏后，已经好几个月没有更新博客了。其实八月底从拉萨回来后一直在Spring的代码，也想写几篇关于Spring源码的解读，可惜Spring实在是太复杂了，花了我一个多月的时间，框架大体流程是有头绪了，但是具体实现和各个模块的具体细节还都不是很清楚，迟迟不敢动笔。其实原本我不想回到Logging这一块，我知道光看完Log4J的代码还不够，也感觉Log在系统中其实占据了蛮重要的位置（虽然...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2012/11/04/390755.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/390755.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-11-04 01:20 <a href="http://www.blogjava.net/DLevin/archive/2012/11/04/390755.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】Log4j/common log和各种服务器集成的问题</title><link>http://www.blogjava.net/DLevin/archive/2012/11/02/390639.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Thu, 01 Nov 2012 18:08:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/11/02/390639.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/390639.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/11/02/390639.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/390639.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/390639.html</trackback:ping><description><![CDATA[转自：<a href="http://xinyanfei.blog.sohu.com/72361504.html">http://xinyanfei.blog.sohu.com/72361504.html</a><br />我从转到Java开发后，一直在开发Application Server，所以基本没有和大型的Web Server打过交道，所以关于common logging在这些server上部署时遇到的问题也只能是从理论上去解释。感觉这篇文章介绍的不错，所以转载过来。<br /><br /><span class="Apple-style-span" style="color: #62613b; font-family: Verdana, Arial, Helvetica, 宋体, sans-serif; line-height: 25px; ">&nbsp; 目前的很多商业和非商业的服务器中间件都默认集成了common-log甚至是log4j.因此当我门把我们的应用发布在上面的时候,都会遇到关于log方面的问题.<br style="line-height: 25px; " />&nbsp; 1.webshpere下面集成log4j.<br style="line-height: 25px; " />&nbsp; "WebSphere的类装入器方式有两种方式：PARENT_FIRST和PARENT_LAST。默认值是PARENT_FIRST，这种方式在载入当前classpath的类之前先载入其上一级classloader能够装入的类。这是标准的JVM classloader的默认策略。如果采用PARENT_LAST，则过程正好相反，即先载入当前classpath的类，再载入其上一级classloader能够装入的类，这样可以用当前classpath中更新的类覆盖其上一级classloader的相同类。受类装入器方式影响的classloader包括application classloader、WAR classloader以及共享类库的classloader。"<br style="line-height: 25px; " />&nbsp;因为websphere在共享类库的classloader中有一套common logging,但是确没有合适配置文件.如果我们把配置正确的log4j.properties文件放在共享类库下,我们会发现log4j可以运行.但还有另外一个很通用的方式--改变webshpere的类库加载顺序.我们让他先加载我们web应用所需的类库.即我们把web应用的加栽方式改为PARENT_LAST.<br style="line-height: 25px; " />&nbsp; 哎,尽管我小心的提防,今天还是中招了,在我的配置里,log4j的配置文件只能读取一次,不能一个应用一个配置文件.为了让它加载自己的配置,可以自己写(或者用spring的)servlet/listener去手动加载这个配置文件.<br style="line-height: 25px; " />&nbsp;2.jboss下面的集成log4j<br style="line-height: 25px; " />&nbsp; 大家可能都曾在为jboss下面配置log4j郁闷过.jboss比webshpere走的还远.无论你的项目是否使用了log4j,jboss在自己启动的时候就已经运行他了.也就是说在jboss加载自己共享类库的时候,已经读取了自己log4j.xml文件配置.这个文件在conf中可以找到.如果你需要为你的应用单独配置一个catagory,你需要直接在这里配置．<br style="line-height: 25px; " />&nbsp;&nbsp;&nbsp;在webloader装载应用的时候,如果应用中有log4j的包,似乎总出现appender已被占用的问题.笔者把log4j的包连带应用中的log4j配置文件一并移去,世界清净了.<br style="line-height: 25px; " />关于为了让应用自带的log4j配置文件生效,有人建议修改<br style="line-height: 25px; " />&nbsp;&lt;attribute name="Java2ClassLoadingCompliance"&gt;false&lt;/attribute&gt;<br style="line-height: 25px; " />和<br style="line-height: 25px; " />&nbsp;&lt;attribute name="UseJBossWebLoader"&gt;false&lt;/attribute&gt;<br style="line-height: 25px; " />这两个属性．<br style="line-height: 25px; " />　３．sunone下面集成log4j<br style="line-height: 25px; " />&nbsp;&nbsp;&nbsp; 距离上次用SunOne服务器已经好长时间了,似乎sunOne的log有些类似jboss,也是一个服务器的log集中管理.由于使用的不是很多,暂且在这里站个位子.<br style="line-height: 25px; " />随手贴点关于log的信息:<br style="line-height: 25px; " /><a href="http://wiki.apache.org/jakarta-commons/Logging/FrequentlyAskedQuestions" style="line-height: 21px; color: #62613b; font: normal normal normal 12px/normal Verdana, Arial, Helvetica, 宋体, sans-serif; text-decoration: underline; ">http://wiki.apache.org/jakarta-commons/Logging/FrequentlyAskedQuestions</a><br style="line-height: 25px; " /><a href="http://www-128.ibm.com/developerworks/cn/websphere/library/techarticles/0408_baigang/part3.html" style="line-height: 21px; color: #62613b; font: normal normal normal 12px/normal Verdana, Arial, Helvetica, 宋体, sans-serif; text-decoration: underline; ">http://www-128.ibm.com/developerworks/cn/websphere/library/techarticles/0408_baigang/part3.html</a></span><br /><img src ="http://www.blogjava.net/DLevin/aggbug/390639.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-11-02 02:08 <a href="http://www.blogjava.net/DLevin/archive/2012/11/02/390639.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Log4J源码之LoggerRepository和Configurator</title><link>http://www.blogjava.net/DLevin/archive/2012/07/10/382678.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 09 Jul 2012 18:38:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/07/10/382678.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/382678.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/07/10/382678.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/382678.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/382678.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: LoggerRepository从字面上理解，它是一个Logger的容器，它会创建并缓存Logger实例，从而具有相同名字的Logger实例不会多次创建，以提高性能。它的这种特性有点类似Spring的IOC概念。Log4J支持两种配置文件：properties文件和xml文件。Configurator解析配置文件，并将解析后的信息添加到LoggerRepository中。LogManager最终将...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2012/07/10/382678.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/382678.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-07-10 02:38 <a href="http://www.blogjava.net/DLevin/archive/2012/07/10/382678.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Log4J源码之Appender</title><link>http://www.blogjava.net/DLevin/archive/2012/07/10/382676.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 09 Jul 2012 18:23:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/07/10/382676.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/382676.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/07/10/382676.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/382676.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/382676.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Appender负责定义日志输出的目的地，它可以是控制台（ConsoleAppender）、文件（FileAppender）、JMS服务器（JmsLogAppender）、以Email的形式发送出去（SMTPAppender）等。Appender是一个命名的实体，另外它还包含了对Layout、ErrorHandler、Filter等引用：Code highlighting produced by ...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2012/07/10/382676.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/382676.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-07-10 02:23 <a href="http://www.blogjava.net/DLevin/archive/2012/07/10/382676.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Log4J源码之Layout</title><link>http://www.blogjava.net/DLevin/archive/2012/07/04/382131.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Tue, 03 Jul 2012 17:05:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/07/04/382131.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/382131.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/07/04/382131.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/382131.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/382131.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Layout负责将LoggingEvent中的信息格式化成一行日志信息。对不同格式的日志可能还需要提供头和尾等信息。另外有些Layout不会处理异常信息，此时ignoresThrowable()方法返回false，并且异常信息需要Appender来处理，如PatternLayout。  Log4J自身实现了7个Layout类，我们可以通过继承自Layout类以实现用户自定义的日志消息格式。Log4...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2012/07/04/382131.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/382131.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-07-04 01:05 <a href="http://www.blogjava.net/DLevin/archive/2012/07/04/382131.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Log4J源码之Log4J Core</title><link>http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Wed, 27 Jun 2012 18:09:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/381667.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/381667.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/381667.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 毕业又赶上本科的同学会，还去骑车环了趟崇明岛，六月貌似就没消停过，不过终于这些事情基本上都结束了，我也可以好好的看些书、读些源码、写点博客了。Log4J将写日志功能抽象成七个核心类/接口：Logger、LoggerRepository、Level、LoggingEvent、Appender、Layout、ObjectRender。其类图如下：更详细的，实现Log4J主要功能相关...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/381667.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-06-28 02:09 <a href="http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Log4J源码之SimpleLog</title><link>http://www.blogjava.net/DLevin/archive/2012/06/12/380647.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Tue, 12 Jun 2012 15:38:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2012/06/12/380647.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/380647.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2012/06/12/380647.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/380647.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/380647.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一个月前因为某些事情被困宁波三天，在酒店里闲着无事，然后开始看Log4J的源码。原本之后的一个星期就应该开始写了，无奈又遇到一些事情，迟迟没有动笔。感觉工作后要做好一件额外的事情总是很难，每天下班后才能看代码、写文章，而如果中途遇到一些没有预料到的事情就很容易不了了之了，所以现在如果出现能静下心来看代码、写文章的时间，我都是特别珍惜。我一直不知道如何开场一篇文章，所以先用一些废话做引子&#8230...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2012/06/12/380647.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/380647.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2012-06-12 23:38 <a href="http://www.blogjava.net/DLevin/archive/2012/06/12/380647.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>