﻿<?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-西湖边的穷秀才-wenchu</title><link>http://www.blogjava.net/cenwenchu/</link><description /><language>zh-cn</language><lastBuildDate>Thu, 24 Jul 2008 08:09:24 GMT</lastBuildDate><pubDate>Thu, 24 Jul 2008 08:09:24 GMT</pubDate><ttl>60</ttl><item><title>理解Load Average做好压力测试</title><link>http://www.blogjava.net/cenwenchu/archive/2008/06/30/211712.html</link><dc:creator>岑文初</dc:creator><author>岑文初</author><pubDate>Mon, 30 Jun 2008 09:35:00 GMT</pubDate><guid>http://www.blogjava.net/cenwenchu/archive/2008/06/30/211712.html</guid><wfw:comment>http://www.blogjava.net/cenwenchu/comments/211712.html</wfw:comment><comments>http://www.blogjava.net/cenwenchu/archive/2008/06/30/211712.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/cenwenchu/comments/commentRss/211712.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cenwenchu/services/trackbacks/211712.html</trackback:ping><description><![CDATA[&nbsp;
<p style="text-indent: 21pt">SIP<span style="font-family: 宋体">的第四期结束了，因为控制策略的丰富，早先的的压力测试结果已经无法反映在高并发和高压力下</span>SIP<span style="font-family: 宋体">的运行状况，因此需要重新作压力测试。跟在测试人员后面做了快一周的压力测试，压力测试的报告也正式出炉，本来也就算是告一段落，但第二天测试人员说要修改报告，由于这次作压力测试的同学是第一次作，有一个指标没有注意，因此需要修改几个测试结果。那个没有注意的指标就是</span>load average<span style="font-family: 宋体">，他和我一样开始只是注意了</span>CPU<span style="font-family: 宋体">，内存的使用状况，而没有太注意这个指标，这个指标与他们通常的限制（</span>10<span style="font-family: 宋体">左右）有差别。重新测试的结果由于这个指标被要求压低，最后的报告显然不如原来的好看。自己也没有深入过压力测试，但是觉得不搞明白对将来机器配置和扩容都会有影响，因此去问了</span>DBA<span style="font-family: 宋体">和</span>SA<span style="font-family: 宋体">，得到的结果相差很大，看来不得不自己去找找问题的根本所在了。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">通过下面的几个部分的了解，可以一步一步的找出</span>Load Average<span style="font-family: 宋体">在压力测试中真正的作用。</span></p>
<p><strong><span style="font-size: 12pt">CPU</span></strong><strong><span style="font-size: 12pt; font-family: 宋体">时间片</span></strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">为了提高程序执行效率，大家在很多应用中都采用了多线程模式，这样可以将原来的序列化执行变为并行执行，任务的分解以及并行执行能够极大地提高程序的运行效率。但这都是代码级别的表现，而硬件是如何支持的呢？那就要靠</span>CPU<span style="font-family: 宋体">的时间片模式来说明这一切。程序的任何指令的执行往往都会要竞争</span>CPU<span style="font-family: 宋体">这个最宝贵的资源，不论你的程序分成了多少个线程去执行不同的任务，他们都必须排队等待获取这个资源来计算和处理命令。先看看单</span>CPU<span style="font-family: 宋体">的情况。下面两图描述了时间片模式和非时间片模式下的线程执行的情况：<br />
</span></p>
<p style="text-align: center" align="center"><span style="font-size: 9pt; font-family: 宋体"><img height="389" alt="" src="http://www.blogjava.net/images/blogjava_net/cenwenchu/CPU1.GIF" width="505" border="0" /><br />
图</span><span style="font-size: 9pt"> 1 </span><span style="font-size: 9pt; font-family: 宋体">非时间片线程执行情况<br />
</span></p>
<p style="text-align: center" align="center"><span style="font-size: 9pt; font-family: 宋体"><img height="402" alt="" src="http://www.blogjava.net/images/blogjava_net/cenwenchu/CPU2.GIF" width="487" border="0" /><br />
图</span><span style="font-size: 9pt"> 2 </span><span style="font-size: 9pt; font-family: 宋体">非时间片线程执行情况</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">在图一中可以看到，任何线程如果都排队等待</span>CPU<span style="font-family: 宋体">资源的获取，那么所谓的多线程就没有任何实际意义。图二中的</span>CPU Manager<span style="font-family: 宋体">只是我虚拟的一个角色，由它来分配和管理</span>CPU<span style="font-family: 宋体">的使用状况，此时多线程将会在运行过程中都有机会得到</span>CPU<span style="font-family: 宋体">资源，也真正实现了在单</span>CPU<span style="font-family: 宋体">的情况下实现多线程并行处理。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">多</span>CPU<span style="font-family: 宋体">的情况只是单</span>CPU<span style="font-family: 宋体">的扩展，当所有的</span>CPU<span style="font-family: 宋体">都满负荷运作的时候，就会对每一个</span>CPU<span style="font-family: 宋体">采用时间片的方式来提高效率。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">在</span>Linux<span style="font-family: 宋体">的内核处理过程中，每一个进程默认会有一个固定的时间片来执行命令（默认为</span>1/100<span style="font-family: 宋体">秒），这段时间内进程被分配到</span>CPU<span style="font-family: 宋体">，然后独占使用。如果使用完，同时未到时间片的规定时间，那么就主动放弃</span>CPU<span style="font-family: 宋体">的占用，如果到时间片尚未完成工作，那么</span>CPU<span style="font-family: 宋体">的使用权也会被收回，进程将会被中断挂起等待下一个时间片。</span></p>
<p><strong><span style="font-size: 12pt">CPU</span></strong><strong><span style="font-size: 12pt; font-family: 宋体">利用率和</span></strong><strong><span style="font-size: 12pt">Load Average</span></strong><strong><span style="font-size: 12pt; font-family: 宋体">的区别</span></strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">压力测试不仅需要对业务场景的并发用户等压力参数作模拟，同时也需要在压力测试过程中随时关注机器的性能情况，来确保压力测试的有效性。当服务器长期处于一种超负荷的情况下运行，所能接收的压力并不是我们所认为的可接受的压力。就好比项目经理在给一个人估工作量的时候，每天都让这个人工作</span>12<span style="font-family: 宋体">个小时，那么所制定的项目计划就不是一个合理的计划，那个人迟早会垮掉，而影响整体的项目进度。</span></p>
<p style="text-indent: 21pt">CPU<span style="font-family: 宋体">利用率在过去常常被我们这些外行认为是判断机器是否已经到了满负荷的一个标准，看到</span>50%-60%<span style="font-family: 宋体">的使用率就认为机器就已经压到了临界了。</span>CPU<span style="font-family: 宋体">利用率，顾名思义就是对于</span>CPU<span style="font-family: 宋体">的使用状况，这是对一个时间段内</span>CPU<span style="font-family: 宋体">使用状况的统计，通过这个指标可以看出在某一个时间段内</span>CPU<span style="font-family: 宋体">被占用的情况，如果被占用时间很高，那么就需要考虑</span>CPU<span style="font-family: 宋体">是否已经处于超负荷运作，长期超负荷运作对于机器本身来说是一种损害，因此必须将</span>CPU<span style="font-family: 宋体">的利用率控制在一定的比例下，以保证机器的正常运作。</span></p>
<p style="text-indent: 21pt">Load Average<span style="font-family: 宋体">是</span>CPU<span style="font-family: 宋体">的</span>Load<span style="font-family: 宋体">，它所包含的信息不是</span>CPU<span style="font-family: 宋体">的使用率状况，而是在一段时间内</span>CPU<span style="font-family: 宋体">正在处理以及等待</span>CPU<span style="font-family: 宋体">处理的进程数之和的统计信息，也就是</span>CPU<span style="font-family: 宋体">使用队列的长度的统计信息。为什么要统计这个信息，这个信息的对于压力测试的影响究竟是怎么样的，那就通过一个类比来解释</span>CPU<span style="font-family: 宋体">利用率和</span>Load Average<span style="font-family: 宋体">的区别以及对于压力测试的指导意义。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">我们将</span>CPU<span style="font-family: 宋体">就类比为电话亭，每一个进程都是一个需要打电话的人。现在一共有</span>4<span style="font-family: 宋体">个电话亭（就好比我们的机器有</span>4<span style="font-family: 宋体">核），有</span>10<span style="font-family: 宋体">个人需要打电话。现在使用电话的规则是管理员会按照顺序给每一个人轮流分配</span>1<span style="font-family: 宋体">分钟的使用电话时间，如果使用者在</span>1<span style="font-family: 宋体">分钟内使用完毕，那么可以立刻将电话使用权返还给管理员，如果到了</span>1<span style="font-family: 宋体">分钟电话使用者还没有使用完毕，那么需要重新排队，等待再次分配使用。</span></p>
<p style="text-indent: 21pt; text-align: center" align="center"><span style="font-size: 9pt; font-family: 宋体"><img height="489" alt="" src="http://www.blogjava.net/images/blogjava_net/cenwenchu/CPU3.GIF" width="653" border="0" /><br />
图</span><span style="font-size: 9pt"> 3 </span><span style="font-size: 9pt; font-family: 宋体">电话使用场景</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">上图中对于使用电话的用户又作了一次分类，</span>1min<span style="font-family: 宋体">的代表这些使用者占用电话时间小于等于</span>1min<span style="font-family: 宋体">，</span>2min<span style="font-family: 宋体">表示使用者占用电话时间小于等于</span>2min<span style="font-family: 宋体">，以此类推。根据电话使用规则，</span>1min<span style="font-family: 宋体">的用户只需要得到一次分配即可完成通话，而其他两类用户需要排队两次到三次。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">电话的利用率</span> =&nbsp;sum (active use cpu time)/period</p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">每一个分配到电话的使用者使用电话时间的总和去除以统计的时间段。这里需要注意的是是使用电话的时间总和</span>(sum(active use cpu time))<span style="font-family: 宋体">，这与占用时间的总和</span>(sum(occupy cpu time))<span style="font-family: 宋体">是有区别的。（例如一个用户得到了一分钟的使用权，在</span>10<span style="font-family: 宋体">秒钟内打了电话，然后去查询号码本花了</span>20<span style="font-family: 宋体">秒钟，再用剩下的</span>30<span style="font-family: 宋体">秒打了另一个电话，那么占用了电话</span>1<span style="font-family: 宋体">分钟，实际只是使用了</span>40<span style="font-family: 宋体">秒）</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">电话的</span>Average Load<span style="font-family: 宋体">体现的是在某一统计时间段内，所有使用电话的人加上等待电话分配的人一个平均统计。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">电话利用率的统计能够反映的是电话被使用的情况，当电话长期处于被使用而没有的到足够的时间休息间歇，那么对于电话硬件来说是一种超负荷的运作，需要调整使用频度。而电话</span>Average Load<span style="font-family: 宋体">却从另一个角度来展现对于电话使用状态的描述，</span>Average Load<span style="font-family: 宋体">越高说明对于电话资源的竞争越激烈，电话资源比较短缺。对于资源的申请和维护其实也是需要很大的成本，所以在这种高</span>Average Load<span style="font-family: 宋体">的情况下电话资源的长期&#8220;热竞争&#8221;也是对于硬件的一种损害。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">低利用率的情况下是否会有高</span>Load Average<span style="font-family: 宋体">的情况产生呢？理解占有时间和使用时间就可以知道，当分配时间片以后，是否使用完全取决于使用者，因此完全可能出现低利用率高</span>Load Average<span style="font-family: 宋体">的情况。由此来看，仅仅从</span>CPU<span style="font-family: 宋体">的使用率来判断</span>CPU<span style="font-family: 宋体">是否处于一种超负荷的工作状态还是不够的，必须结合</span>Load Average<span style="font-family: 宋体">来全局的看</span>CPU<span style="font-family: 宋体">的使用情况和申请情况。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">所以回过头来再看测试部对于</span>Load Average<span style="font-family: 宋体">的要求，在我们机器为</span>8<span style="font-family: 宋体">个</span>CPU<span style="font-family: 宋体">的情况下，控制在</span>10 Load<span style="font-family: 宋体">左右，也就是每一个</span>CPU<span style="font-family: 宋体">正在处理一个请求，同时还有</span>2<span style="font-family: 宋体">个在等待处理。看了看网上很多人的介绍一般来说</span>Load<span style="font-family: 宋体">简单的计算就是</span>2* CPU<span style="font-family: 宋体">个数减去</span>1-2<span style="font-family: 宋体">左右（这个只是网上看来的，未必是一个标准）。</span></p>
<p><strong><span style="font-size: 12pt; font-family: 宋体">补充几点：</span></strong></p>
<p style="text-indent: 21pt">1<span style="font-family: 宋体">．对于</span>CPU<span style="font-family: 宋体">利用率和</span>CPU Load Average<span style="font-family: 宋体">的结果来判断性能问题。首先低</span>CPU<span style="font-family: 宋体">利用率不表明</span>CPU<span style="font-family: 宋体">不是瓶颈，竞争</span>CPU<span style="font-family: 宋体">的队列长期保持较长也是</span>CPU<span style="font-family: 宋体">超负荷的一种表现。对于应用来说可能会去花时间在</span>I/O,Socket<span style="font-family: 宋体">等方面，那么可以考虑是否后这些硬件的速度影响了整体的效率。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">这里最好的样板范例就是我在测试中发现的一个现象：</span>SIP<span style="font-family: 宋体">当前在处理过程中，为了提高处理效率，将控制策略以及计数信息都放置在</span>Memcached Cache<span style="font-family: 宋体">里面，当我将</span>Memcached Cache<span style="font-family: 宋体">配置扩容一倍以后，</span>CPU<span style="font-family: 宋体">的利用率以及</span>Load<span style="font-family: 宋体">都有所下降，其实也就是在处理任务的过程中，等待</span>Socket<span style="font-family: 宋体">的返回对于</span>CPU<span style="font-family: 宋体">的竞争也产生了影响。</span></p>
<p style="text-indent: 21pt">2<span style="font-family: 宋体">．未来多</span>CPU<span style="font-family: 宋体">编程的重要性。现在服务器的</span>CPU<span style="font-family: 宋体">都是多</span>CPU<span style="font-family: 宋体">了，我们的服务器处理能力已经不再按照摩尔定律来发展。就我上面提到的电话亭场景来看，对于三种不同时间需求的用户来说，采用不同的分配顺序，我们可看到的</span>Load Average<span style="font-family: 宋体">就会有不同。假设我们统计</span>Load<span style="font-family: 宋体">的时间段为</span>2<span style="font-family: 宋体">分钟，如果将电话分配的顺序按照：</span>1min<span style="font-family: 宋体">的用户，</span>2min<span style="font-family: 宋体">的用户，</span>3min<span style="font-family: 宋体">的用户来分配，那么我们的</span>Load Average<span style="font-family: 宋体">将会最低，采用其他顺序将会有不同的结果。所以未来的多</span>CPU<span style="font-family: 宋体">编程可以更好的提高</span>CPU<span style="font-family: 宋体">的利用率，让程序跑的更快。</span></p>
<p><span style="font-family: 宋体">以上所提到的内容未必都是很准确或者正确，如果有任何的偏差也请大家指出，可以纠正一些不清楚的概念。</span></p>
<img src ="http://www.blogjava.net/cenwenchu/aggbug/211712.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cenwenchu/" target="_blank">岑文初</a> 2008-06-30 17:35 <a href="http://www.blogjava.net/cenwenchu/archive/2008/06/30/211712.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Memcached使用点滴</title><link>http://www.blogjava.net/cenwenchu/archive/2008/06/04/205942.html</link><dc:creator>岑文初</dc:creator><author>岑文初</author><pubDate>Wed, 04 Jun 2008 15:10:00 GMT</pubDate><guid>http://www.blogjava.net/cenwenchu/archive/2008/06/04/205942.html</guid><wfw:comment>http://www.blogjava.net/cenwenchu/comments/205942.html</wfw:comment><comments>http://www.blogjava.net/cenwenchu/archive/2008/06/04/205942.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/cenwenchu/comments/commentRss/205942.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cenwenchu/services/trackbacks/205942.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我对于Memcached的接触，还是在去年看了CSDN的一系列国外大型网站架构设计而开始的。最初的时候只是简单的封装了Memcached Java版的客户端，主要是对于配置的简化以及Memcached多点备份作了一些工作，然后就作为ASF的组件一部分提供给其他Team使用。其实看过Memcached Jav...&nbsp;&nbsp;<a href='http://www.blogjava.net/cenwenchu/archive/2008/06/04/205942.html'>阅读全文</a><img src ="http://www.blogjava.net/cenwenchu/aggbug/205942.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cenwenchu/" target="_blank">岑文初</a> 2008-06-04 23:10 <a href="http://www.blogjava.net/cenwenchu/archive/2008/06/04/205942.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 载入Jar内资源问题的探究(后续)</title><link>http://www.blogjava.net/cenwenchu/archive/2008/06/03/205550.html</link><dc:creator>岑文初</dc:creator><author>岑文初</author><pubDate>Tue, 03 Jun 2008 06:24:00 GMT</pubDate><guid>http://www.blogjava.net/cenwenchu/archive/2008/06/03/205550.html</guid><wfw:comment>http://www.blogjava.net/cenwenchu/comments/205550.html</wfw:comment><comments>http://www.blogjava.net/cenwenchu/archive/2008/06/03/205550.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/cenwenchu/comments/commentRss/205550.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cenwenchu/services/trackbacks/205550.html</trackback:ping><description><![CDATA[&nbsp;在《<font face="宋体">Java 载入Jar内资源问题的探究</font>》这个文档贴出来以后，有朋友给了我反馈，最终知道了问题就出现在JarOutputstream<font face="宋体">输出的时候，虽然支持直接写入目录中的文件来同时产生目录和文件，但是这样在jar中目录就不是一个有效的entry，因此在资源定位的时候就无法得到，因此必须也把目录作为entry写入，这样才会正常定位资源。这个问题作了测试以后反馈到我们的工具开发人员那边，做了修改以后一切都恢复正常，细节决定成败，那么一点细微的差异，会让各种框架都无法正常运作。<br />
<br />
代码修改如下：<br />
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt; text-align: left" align="left"><font size="3"><span style="background: silver; color: black; font-family: 'Courier New'">JarOutputStream</span><span style="color: black; font-family: 'Courier New'"> jos;</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="color: #7f0055; font-family: 'Courier New'">try</span></strong></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos = </span><strong><span style="color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="color: black; font-family: 'Courier New'"> <span style="background: silver">JarOutputStream</span>(</span><strong><span style="color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="color: black; font-family: 'Courier New'"> BufferedOutputStream(</span><strong><span style="color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="color: black; font-family: 'Courier New'"> FileOutputStream(file)));</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String f = </span><span style="color: #2a00ff; font-family: 'Courier New'">"spring/sip-analyzer-dataSource.xml"</span><span style="color: black; font-family: 'Courier New'">;</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red"><strong>String dir = "spring/";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JarEntry je1 = new JarEntry(dir);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jos.putNextEntry(je1);</strong></span><br />
&nbsp;&nbsp;&nbsp;</span></font><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JarEntry je = </span><strong><span style="color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="color: black; font-family: 'Courier New'"> JarEntry(f);</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos.putNextEntry(je);</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedInputStream bis = </span><strong><span style="color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="color: black; font-family: 'Courier New'"> BufferedInputStream(</span><strong><span style="color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="color: black; font-family: 'Courier New'"> FileInputStream(</span><span style="color: #2a00ff; font-family: 'Courier New'">"D:/work/sip3/analyzer/src/conf.test/spring/sip-analyzer-dataSource.xml"</span><span style="color: black; font-family: 'Courier New'">));</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="color: black; font-family: 'Courier New'"> i = 0;</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="color: #7f0055; font-family: 'Courier New'">while</span></strong><span style="color: black; font-family: 'Courier New'"> ((i=bis.read())!=-1) </span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos.write(i);</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bis.close();</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><font size="3"><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos.closeEntry();</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="color: black; font-family: 'Courier New'"><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos.close();</font></span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><span style="color: black; font-family: 'Courier New'">} </span><strong><span style="color: #7f0055; font-family: 'Courier New'">catch</span></strong><span style="color: black; font-family: 'Courier New'"> &nbsp;...</span></font></p>
</font>
<img src ="http://www.blogjava.net/cenwenchu/aggbug/205550.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cenwenchu/" target="_blank">岑文初</a> 2008-06-03 14:24 <a href="http://www.blogjava.net/cenwenchu/archive/2008/06/03/205550.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 载入Jar内资源问题的探究</title><link>http://www.blogjava.net/cenwenchu/archive/2008/05/28/203560.html</link><dc:creator>岑文初</dc:creator><author>岑文初</author><pubDate>Wed, 28 May 2008 08:42:00 GMT</pubDate><guid>http://www.blogjava.net/cenwenchu/archive/2008/05/28/203560.html</guid><wfw:comment>http://www.blogjava.net/cenwenchu/comments/203560.html</wfw:comment><comments>http://www.blogjava.net/cenwenchu/archive/2008/05/28/203560.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/cenwenchu/comments/commentRss/203560.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cenwenchu/services/trackbacks/203560.html</trackback:ping><description><![CDATA[&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">工作忙，有些许时间没有更新</span>Blog<span style="font-family: 宋体">了，这次在开发监控模块的时候遇到了这个问题，整个问题定位过程真是走了不少路，所以觉得有必要记录下来分享一下。在我看来很多时候结果也许就很简单一个原因，但是开发人员却要探究很久，也许在找到了其他可实现业务逻辑方法的情况下，就会放弃寻找原因，这期间我也是一样。</span></p>
<p><strong><span style="font-size: 12pt; font-family: 宋体">问题初现：</span></strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">在服务集成平台中需要新增一块写入数据库的逻辑，因此考虑最简便就是弄个</span>Spring<span style="font-family: 宋体">的</span>BeanFactory<span style="font-family: 宋体">来搞定这一切，谁知道，问题就这么出现了。很简单，通过</span>Spring<span style="font-family: 宋体">的</span>ClassPathXmlApplicationContext<span style="font-family: 宋体">来构建</span>BeanFactory<span style="font-family: 宋体">，下面的语句大家应该很熟悉：</span></p>
<p style="text-indent: 21pt"><span style="color: red">ctx = new ClassPathXmlApplicationContext("/spring/sip-*.xml");</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">通过通配符来载入</span>ClassPath<span style="font-family: 宋体">下的所有的符合规则的</span>spring<span style="font-family: 宋体">配置文件。然后在</span>Eclipse<span style="font-family: 宋体">中作完单元测试和集成测试，一切正常。然后用我们内部的打包部署，而这些配置文件都被打在</span>Jar<span style="font-family: 宋体">中作为</span>lib<span style="font-family: 宋体">库依赖。结果启动以后，在分析完日志需要写入到数据库的时候出现异常：</span></p>
<p style="text-indent: 21pt"><span style="color: red">Could not resolve bean definition resource pattern [/spring/sip-*.xml]; nested exception is java.io.FileNotFoundException: class path resource [spring/] cannot be resolved to URL because it does not exist</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">就提示来说，就是没有找到</span>spring<span style="font-family: 宋体">这个目录，也就是在</span>ClassPath<span style="font-family: 宋体">下面就没有找到资源。</span></p>
<p><strong><span style="font-size: 12pt; font-family: 宋体">第一次试图解决问题：</span></strong></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">以前调整过</span>Jboss<span style="font-family: 宋体">关于</span>ClassLoader<span style="font-family: 宋体">的配置，即自上而下搜索还是自下而上搜索，以及是否采用</span>Web<span style="font-family: 宋体">容器的</span>ClassLoader<span style="font-family: 宋体">，开始怀疑是否是这种修改造成的问题。修改了没有问题，然后就设置断点跟踪</span>Spring<span style="font-family: 宋体">的</span>ClassPathXmlApplicationContext<span style="font-family: 宋体">的构造过程，发现</span>Spring<span style="font-family: 宋体">在分析此类通配类型的过程中，首先将前面的文件目录和后面的具体通配文件分开，先定位文件目录资源，也就是在定位文件目录资源的过程中，找不到</span>spring<span style="font-family: 宋体">目录，而出现了那个异常。看了代码中也有对</span>Jar<span style="font-family: 宋体">的处理，但是在处理之前就出现了问题。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">自己做了尝试，将</span>spring<span style="font-family: 宋体">目录和其内容解压到</span>Web<span style="font-family: 宋体">的</span>Classes<span style="font-family: 宋体">目录下运行正常，或者解压到</span>war<span style="font-family: 宋体">下面也是正常的，这些地方其实都是</span>ClassPath<span style="font-family: 宋体">可以找到的，但是</span>lib<span style="font-family: 宋体">目录下的</span>jar<span style="font-family: 宋体">也应该是可以找到的。在仔细跟踪了代码中最后载入这些资源的</span>ClassLoader<span style="font-family: 宋体">内的数据，所有的</span>Jar<span style="font-family: 宋体">都是包含在内的。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">由于工作太多，因此将原有的打包模式作了修改，每次打包将这部分配置解压到</span>war<span style="font-family: 宋体">下面，这样就找到了可解决方案了，因此细致的缘由也就没有再去追究。（如果不是后面再次遇到，这个问题就会在此了结）</span></p>
<p><strong><span style="font-size: 12pt; font-family: 宋体">问题再现：</span></strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">监控模块中需要新增一块写入数据库的逻辑，在单元测试和集成测试通过的情况下出现了问题，由于此次是普通的</span>J2SE<span style="font-family: 宋体">的应用，所有的配置和依赖都打入在了</span>Jar<span style="font-family: 宋体">中，所以问题和前次一样。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">这次决定花一些时间好好找到问题所在，首先觉的</span>Spring<span style="font-family: 宋体">的资源载入应该不会不支持从</span>Jar<span style="font-family: 宋体">中载入，这是最基本的功能，因此再次打开了</span>Spring<span style="font-family: 宋体">的源码。</span></p>
<p><strong><span style="font-size: 12pt; font-family: 宋体">问题二次定位：</span></strong></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">先看看</span>ClassPathXmlApplicationContext<span style="font-family: 宋体">的类图结构：<br />
</span></p>
<span style="font-family: 宋体">
<p style="text-indent: 21pt" align="center"><img height="416" alt="" src="http://www.blogjava.net/images/blogjava_net/cenwenchu/Temp.GIF" width="654" border="0" /><br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;关键方法就是</span>getResource<span style="font-family: 宋体">方法，</span>ClassPathXmlApplicationContext<span style="font-family: 宋体">的资源定位就是采用了</span>DefaultResourceLoader<span style="font-family: 宋体">的</span>getResource<span style="font-family: 宋体">方法。内部也没有做太多的工作，其实就是如下的代码：</span></p>
<p style="margin-left: 21pt; text-indent: 21pt; text-align: left" align="left"><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">try</span></strong></p>
<p style="margin-left: 21pt; text-indent: 21pt; text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">{</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 6.5pt; color: #3f7f5f; font-family: 'Courier New'">// Try to parse the location as a URL...</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; URL url = </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> URL(location);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">return</span></strong><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> UrlResource(url);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">catch</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> (MalformedURLException ex) </span></p>
<p style="margin-left: 21pt; text-indent: 21pt; text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">{</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 6.5pt; color: #3f7f5f; font-family: 'Courier New'">// No URL -&gt; resolve as resource path.</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">return</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> getResourceByPath(location);</span></p>
<p style="text-indent: 21pt"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="text-indent: 21pt"><span style="color: black; font-family: 宋体">上面的代码都是标准的</span><span style="color: black; font-family: 'Courier New'">j2se</span><span style="color: black; font-family: 宋体">的代码</span><span style="color: black; font-family: 'Courier New'">.</span><span style="color: black; font-family: 宋体">作为</span><span style="color: black; font-family: 'Courier New'">URL</span><span style="color: black; font-family: 宋体">通过字符串来构造，通常需要能够首先获得</span><span style="color: black; font-family: 'Courier New'">URL</span><span style="color: black; font-family: 宋体">的资源全路径，而在当前情况下发现到获取资源的时候</span><span style="color: black; font-family: 'Courier New'">location</span><span style="color: black; font-family: 宋体">还是保持了</span><span style="color: black; font-family: 'Courier New'">spring/</span><span style="color: black; font-family: 宋体">的状态，而没有被替换成为所在</span><span style="color: black; font-family: 'Courier New'">jar</span><span style="color: black; font-family: 宋体">的资源全路径，那么就先作以下测试：</span></p>
<p><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; </span><span style="color: black; font-family: 宋体">新建简单的项目，然后在项目中加入包含</span><span style="color: black; font-family: 'Courier New'">spring</span><span style="color: black; font-family: 宋体">配置的</span><span style="color: black; font-family: 'Courier New'">jar</span><span style="color: black; font-family: 宋体">，然后作单元测试，测试代码如下：</span></p>
<p style="text-indent: 21pt"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">URL url = Thread.currentThread().getClass().getResource("/spring/");</span></p>
<p><span style="color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; </span><span style="color: black; font-family: 宋体">未获取到</span><span style="color: black; font-family: 'Courier New'">URL</span><span style="color: black; font-family: 宋体">，出现异常。</span></p>
<p style="text-indent: 21pt"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">URL url = Thread.currentThread().getClass().getResource("/spring/sip-analyzer-dataSource.xml");</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">正常获取到了</span>URL<span style="font-family: 宋体">。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">由此看来应该是在获取</span>Jar<span style="font-family: 宋体">中的目录资源路径的时候出现问题导致后续载入出现问题，尝试直接传入具体的文件名：</span></p>
<p style="text-indent: 21pt"><span style="color: red">ctx = new ClassPathXmlApplicationContext("/spring/sip-analyzer-dataSource.xml");</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">发现还是出现问题，在</span>new URL<span style="font-family: 宋体">的时候传入的是没有翻译过的文件名，考虑在传入的过程中就直接替换成为资源路径，因此写了一个简单的方法：</span></p>
<p style="text-indent: 21pt; text-align: left" align="left"><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">public</span></strong><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">static</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> String[] <span style="background: silver">getRealClassPath</span>(String[] locationfile)</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] result = locationfile;</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">for</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">(</span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> i = 0 ; i &lt; locationfile.</span><span style="font-size: 6.5pt; color: #0000c0; font-family: 'Courier New'">length</span><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">; i++)</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">try</span></strong></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&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; URL url = Thread.<em>currentThread</em>().getClass().getResource(locationfile[i]);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&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; String file = url.getFile();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&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; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">if</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> (file.indexOf(</span><span style="font-size: 6.5pt; color: #2a00ff; font-family: 'Courier New'">".jar!"</span><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">) &gt; 0)</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&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; result[i] = </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> StringBuffer(</span><span style="font-size: 6.5pt; color: #2a00ff; font-family: 'Courier New'">"jar:"</span><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">).append(file.substring(0,file.indexOf(</span><span style="font-size: 6.5pt; color: #2a00ff; font-family: 'Courier New'">".jar!"</span><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">)+</span><span style="font-size: 6.5pt; color: #2a00ff; font-family: 'Courier New'">".jar!"</span><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">.length()))</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&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; .append(locationfile[i]).toString();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">catch</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">(Exception ex)</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {}</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">return</span></strong><u><span style="font-size: 6.5pt; color: blue; font-family: 'Courier New'">result</span></u><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">;</span></p>
<p style="text-indent: 21pt"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">}</span></p>
<p><span style="font-family: 宋体">在将构造工厂类修改为：</span></p>
<p style="text-indent: 21pt"><span style="color: red">ctx = new ClassPathXmlApplicationContext(BaseUtil.getRealClassPath(new String[]{"/spring/sip-analyzer-dataSource.xml"}));</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">运行测试，正常启动，这也就是又变成最原始的文件罗列的模式。问题虽然找到了解决方案，但是始终觉得很别扭，同时对于无法在</span>Jar<span style="font-family: 宋体">中载入配置资源的情况我一直都还是觉得应该不是</span>Spring<span style="font-family: 宋体">的问题。</span></p>
<p><strong><span style="font-size: 14pt; font-family: 宋体">峰回路转：</span></strong></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">晚上到家还是有点不死心，就直接建了个项目作单元测试，然后将一个自己建立的</span>Jar<span style="font-family: 宋体">加入到</span>Classpath<span style="font-family: 宋体">下面，作单元测试，结果大吃一惊。</span></p>
<p style="text-indent: 21pt"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">URL url = Thread.currentThread().getClass().getResource("/test/");</span></p>
<p style="text-indent: 21pt"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">URL url = Thread.currentThread().getClass().getResource("/test/test.txt");</span></p>
<p style="text-indent: 21pt"><span style="color: black; font-family: 宋体">都正常获取到了资源，这完全推翻了我早先认为在</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">中无法获得目录作为资源的问题。然后把公司里面的项目重新打包然后加入到</span><span style="color: black; font-family: 'Courier New'">ClassPath</span><span style="color: black; font-family: 宋体">下，验证</span><span style="color: black; font-family: 'Courier New'">spring</span><span style="color: black; font-family: 宋体">的目录，出错，目录无法获取，此时我确定看来应该不是应用的问题，而是环境问题。检查了两个</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">，看似没有什么区别，将公司项目的</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">中的</span><span style="color: black; font-family: 'Courier New'">spring</span><span style="color: black; font-family: 宋体">目录拷贝到测试的</span><span style="color: black; font-family: 'Courier New'">jar</span><span style="color: black; font-family: 宋体">中，然后作测试，可以找到目录。那么问题完全定位到了</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">本身。通过</span><span style="color: black; font-family: 'Courier New'">RAR</span><span style="color: black; font-family: 宋体">的压缩工具看了一下两个</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">的信息，除了显示所谓的压缩平台不同（一个是</span><span style="color: black; font-family: 'Courier New'">DOS,</span><span style="color: black; font-family: 宋体">一个是</span><span style="color: black; font-family: 'Courier New'">Unix</span><span style="color: black; font-family: 宋体">）其他没有任何区别。然后自己用</span><span style="color: black; font-family: 'Courier New'">RAR</span><span style="color: black; font-family: 宋体">打了一个</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">以及在</span><span style="color: black; font-family: 'Courier New'">linux</span><span style="color: black; font-family: 宋体">下打了一个</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">做了测试，两个</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">内的目录都是正常可以被获取。</span></p>
<p style="text-indent: 21pt"><span style="color: black; font-family: 宋体">无意中我换了一下需要获取的目录名称，也就是说在公司项目中有多个目录在</span><span style="color: black; font-family: 'Courier New'">jar</span><span style="color: black; font-family: 宋体">中，这次换成为</span><span style="color: black; font-family: 'Courier New'">ibatis</span><span style="color: black; font-family: 宋体">目录，正常获取，看来不是</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">的格式。回想了一下，公司的打包工具是自己人写的，其中提供了一个特性，如果一个项目内部的一些配置信息是需要让调用它的第三方在编译期配置，那么可以通过在第三方项目构建的过程中，动态的生成配置文件然后植入到被依赖的</span><span style="color: black; font-family: 'Courier New'">jar</span><span style="color: black; font-family: 宋体">中。而</span><span style="color: black; font-family: 'Courier New'">spring</span><span style="color: black; font-family: 宋体">这个目录中由于那些数据库的配置都是需要动态配置的，因此</span><span style="color: black; font-family: 'Courier New'">spring</span><span style="color: black; font-family: 宋体">的那个目录是后期被写入的，而</span><span style="color: black; font-family: 'Courier New'">ibatis</span><span style="color: black; font-family: 宋体">是早先就固化在项目中的。</span></p>
<p style="text-indent: 21pt"><span style="color: black; font-family: 宋体">由于我们的</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">在</span><span style="color: black; font-family: 'Courier New'">META-INF</span><span style="color: black; font-family: 宋体">中都有</span><span style="color: black; font-family: 'Courier New'">INDEX.LIST</span><span style="color: black; font-family: 宋体">文件，过去遇到过在</span><span style="color: black; font-family: 'Courier New'">JAR</span><span style="color: black; font-family: 宋体">中自己手工放入一些文件由于没有修改</span><span style="color: black; font-family: 'Courier New'">INDEX.LIST</span><span style="color: black; font-family: 宋体">而导致虽然文件已经存在但是不会被发现，于是打开公司项目中的</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">，果然</span><span style="color: black; font-family: 'Courier New'">INDEX.LIST</span><span style="color: black; font-family: 宋体">中只有</span><span style="color: black; font-family: 'Courier New'">ibatis</span><span style="color: black; font-family: 宋体">，而没有</span><span style="color: black; font-family: 'Courier New'">spring</span><span style="color: black; font-family: 宋体">，看来是我的同事在写入的时候没有将</span><span style="color: black; font-family: 'Courier New'">INDEX.LIST</span><span style="color: black; font-family: 宋体">更新。立刻将</span><span style="color: black; font-family: 'Courier New'">INDEX.LIST</span><span style="color: black; font-family: 宋体">作了更新，测试</span><span style="color: black; font-family: 'Courier New'">spring</span><span style="color: black; font-family: 宋体">目录，结果依然出错。看来这还不是问题的根本。</span></p>
<p style="text-indent: 21pt"><span style="color: black; font-family: 宋体">立刻问了我们开发打包工具的同事，向他们要写入</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">的代码，对方的回答是就是采用简单的</span><span style="color: black; font-family: 'Courier New'">JarOutputStream</span><span style="color: black; font-family: 宋体">来写入，没有什么特殊的。那我就开始怀疑是否是因为采用这种方式写入到</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">中的目录在被资源定位的时候会出现问题。于是写了下面的代码：</span></p>
<p style="text-indent: 21pt; text-align: left" align="left"><span style="font-size: 6.5pt; background: silver; color: black; font-family: 'Courier New'">JarOutputStream</span><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> jos;</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">try</span></strong></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos = </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> <span style="background: silver">JarOutputStream</span>(</span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> BufferedOutputStream(</span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> FileOutputStream(file)));</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String f = </span><span style="font-size: 6.5pt; color: #2a00ff; font-family: 'Courier New'">"spring/sip-analyzer-dataSource.xml"</span><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">;</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; File source = </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> File(f);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JarEntry je = </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> JarEntry(f);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos.putNextEntry(je);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedInputStream bis = </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> BufferedInputStream(</span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">new</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> FileInputStream(</span><span style="font-size: 6.5pt; color: #2a00ff; font-family: 'Courier New'">"D:/work/sip3/analyzer/src/conf.test/spring/sip-analyzer-dataSource.xml"</span><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">));</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> i = 0;</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">while</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> ((i=bis.read())!=-1) </span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos.write(i);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bis.close();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos.closeEntry();</span></p>
<p style="text-indent: 21pt"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jos.close();</span></p>
<p style="text-indent: 21pt"><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'">} </span><strong><span style="font-size: 6.5pt; color: #7f0055; font-family: 'Courier New'">catch</span></strong><span style="font-size: 6.5pt; color: black; font-family: 'Courier New'"> &nbsp;...</span></p>
<p style="text-indent: 21pt"><span style="color: black; font-family: 宋体">结果创建出来的</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">中的</span><span style="color: black; font-family: 'Courier New'">spring</span><span style="color: black; font-family: 宋体">目录无法被资源定位，同样在这个</span><span style="color: black; font-family: 'Courier New'">Jar</span><span style="color: black; font-family: 宋体">中直接拖入一个目录</span><span style="color: black; font-family: 'Courier New'">test</span><span style="color: black; font-family: 宋体">，然后刷新测试，</span><span style="color: black; font-family: 'Courier New'">test</span><span style="color: black; font-family: 宋体">目录可以被定位。</span></p>
<p><strong><span style="font-size: 14pt; font-family: 宋体">后续</span></strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">就到了这个阶段来看如果以上面这种方式写入，对于目录资源定位的却存在问题。这个问题还没有最终的肯定的结论，在我现在所有的试验来看，不论是否有</span>INDEX.LIST<span style="font-family: 宋体">，或者</span>INDEX.LIST<span style="font-family: 宋体">，如果用程序写入到</span>Jar<span style="font-family: 宋体">中，目录作为资源定位都会出现问题（起码是上面那种普通写入方式）。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">这种情况可能是由于这种写法还有一些其他需要配置的，例如写入到</span>META-INF/INDEX.LIST<span style="font-family: 宋体">中，或者就是</span>J2SE<span style="font-family: 宋体">现在</span>API<span style="font-family: 宋体">存在的一个问题。不过不管是什么问题，起码值得引起重视，特别是现在类似于</span>Spring<span style="font-family: 宋体">框架载入</span>Jar<span style="font-family: 宋体">目录下的配置。</span></p>
<img src ="http://www.blogjava.net/cenwenchu/aggbug/203560.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cenwenchu/" target="_blank">岑文初</a> 2008-05-28 16:42 <a href="http://www.blogjava.net/cenwenchu/archive/2008/05/28/203560.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Tiger Concurrent Practice --日志分析并行分解设计与实现</title><link>http://www.blogjava.net/cenwenchu/archive/2008/04/23/194923.html</link><dc:creator>岑文初</dc:creator><author>岑文初</author><pubDate>Wed, 23 Apr 2008 00:41:00 GMT</pubDate><guid>http://www.blogjava.net/cenwenchu/archive/2008/04/23/194923.html</guid><wfw:comment>http://www.blogjava.net/cenwenchu/comments/194923.html</wfw:comment><comments>http://www.blogjava.net/cenwenchu/archive/2008/04/23/194923.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/cenwenchu/comments/commentRss/194923.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cenwenchu/services/trackbacks/194923.html</trackback:ping><description><![CDATA[&nbsp;
<p class="MsoNormal"><span lang="EN-US"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在完成</span><span lang="EN-US">ASF</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">集成</span><span lang="EN-US">REST</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">以后，接到的任务就是要完成一个日志分析应用。需求没有很明确，只是要有这么一个东西能够满足分析收集后的日志，将分析后的原始数据入库，作为后期分析和统计使用。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在动手做之前，我还是给这个应用作了最基本的需求定义：灵活配置（输入源，输出目标，分析器的实现等），高效（并行任务分解）。就这两点能够做到，那么将来需求如何变化都可以适应。</span><span lang="EN-US">Tiger</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span><span lang="EN-US">Concurrent</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">包是满足后面那项最好的实现，因此打算好好的实践一把，也就这部分</span><span lang="EN-US">Tiger</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的特性还没有充分使用过，里面的线程池，异步服务调用，并发控制都能够极好的完成并行任务分解的工作。也就是在这个过程中，看到了</span><span lang="EN-US">IBM</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">开发者论坛上的一片文章，讲关于《应用</span><span lang="EN-US">fork-join</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">框架》，谈到了在</span><span lang="EN-US">J2SE 7 </span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span><span lang="EN-US">Concurrent</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">包中将会增加</span><span lang="EN-US">fork-join</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">风格的并行分解库，其实这个是更细粒度的任务分解，同时能够在当前多</span><span lang="EN-US">CPU</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的情况下提高执行效率，充分利用</span><span lang="EN-US">CPU</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的一种实现。无关的话不多说了，就写一下整个设计和实现的过程以及中间的一些细节知识。</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-size: 14pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">背景：</span><span lang="EN-US" style="font-size: 14pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">由于服务路由应用访问量十分大，即时的将访问记录入库对于路由应用本身以及数据库来说无疑都会产生很大的压力和影响。因此考虑首先将访问信息通过</span><span lang="EN-US">log4j</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">记录在本地（当然自己需要定制一下</span><span lang="EN-US">Log4j</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span><span lang="EN-US">Appender</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span><span lang="EN-US">Filter</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），然后通过服务器的定时任务脚本来将日志集中到日志分析应用所在的机器上（这里通过配置可以决定日志是根据什么时间间隔来产生新文件）。日志分析应用就比较单纯的读取日志，分析日志，输出分析结果（包括写入数据库或者是将即时统计信息存入到集中式缓存</span><span lang="EN-US">Memcached</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中）。网络结构图如下：</span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US"><v:shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></v:path><o:lock aspectratio="t" v:ext="edit"><img height="583" alt="" src="http://www.blogjava.net/images/blogjava_net/cenwenchu/siplog.jpg" width="572" border="0" /></o:lock></v:shapetype></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-size: 9pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">图</span><span lang="EN-US" style="font-size: 9pt">1 </span><span style="font-size: 9pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">网络结构图</span><span lang="EN-US" style="font-size: 9pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-size: 14pt">Concurrent</span><span style="font-size: 14pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">概述：</span><span lang="EN-US" style="font-size: 14pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Tiger</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">出来也有些年头了，但是每一个新的特性是否都在实际的工作中使用过，起码我自己是没有作到的，包括对于</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Concurrent</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">包也只是看过，写了几个</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Test case</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">玩一下，但具体使用到实际开发中还是比较少的。在这个工作之前，如果考虑要使用对象池或者线程池，那么一定会去采用</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">apache</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">的</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">common pool</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">，不过在现在</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">jdk</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">日益&#8220;强大&#8221;的基础下，能够通过</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">jdk</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">自己搞定的，就尽量不再引入第三方包了。看</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Java </span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">的</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Doc</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">很容易就理解了</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Concurrent</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">，这里我只是大致的说一下几个自己在应用中使用的接口：</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">BlockingQueue&lt;E&gt;</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">：看看名字就知道了，阻塞式队列，可以设置大小。适合于生产者和消费者模式，生产者在队列满时阻塞，消费者在队列空时阻塞。在日志分析应用开发中被用于分析任务（生产者）和输出任务（消费者）之间的分析结果存储通道。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Callable&lt;V&gt;</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">：任何需要执行的任务都可以定义成</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Callable</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">，类似于线程的</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Runnable</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">接口，可以被</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Service Executor</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">指派给内部的线程异步执行，并且返回对象或者抛出异常。在日志分析应用开发中，非定时性的任务都定义成为此类型。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">ConcurrentMap&lt;K,V&gt;</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">：这个以前常常使用，因为效率要远远高于</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Collections.synchronizedCollection</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">和</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">synchronized</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">。后面还会提到实践中的几个实用的技巧来防止在高并发的情况下出现问题。在日志分析应用中，此类型的</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Map</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">作为保存日志文件分析状态的缓存（日志文件分为两种状态：分析中，分析结束。如果不存在于</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Map</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">中就认为尚未分析，那么将其纳入</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Map</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">然后启动分析处理线程工作，如果存在于</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Map</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">中标示为分析中，那么将不会再分析此文件，如果分析结束并且被输出，将会标示此文件分析结束，异步清理线程将会定时根据策略删除或移动文件）。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">ExecutorService</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">：内置线程池，异步执行指派任务，并可以根据返回的</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Future</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">来跟踪执行情况。在日志分析应用开发中，被用于非定时性任务执行。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">ScheduledExecutorService</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">：内置线程池，定时异步执行指派任务，并可以根据返回的</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">Future</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">来跟踪执行情况。在日志分析应用开发中，被用于定时性任务执行。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">以上就是被使用到的接口，具体实现策略配置就不在此赘述了。</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-size: 14pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">整体结构设计：</span><span lang="EN-US" style="font-size: 14pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">整体设计还是基于开始设定的两个原则</span><span lang="EN-US">:</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">灵活配置</span><span lang="EN-US">,</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">高效性</span><span lang="EN-US">(</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">任务分解</span><span lang="EN-US">,</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">并行流水线执行</span><span lang="EN-US">)</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。说到任务分解又会想起读书时候的离散数学中关键路径等等。任务分解还是要根据具体情况来分析和设计，不然并行不但不会提高效率，反而还降低了处理效率。</span></p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">就日志分析来看，主要的处理过程可以分成这么几个任务：</span></p>
<p class="MsoNormal" style="margin-left: 56.25pt; text-indent: -35.25pt; mso-list: l1 level1 lfo1; tab-stops: list 56.25pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">1．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">检查日志来源目录，锁定需要分析的文件。（执行需要时间很短，可通过定时间隔执行）。</span></p>
<p class="MsoNormal" style="margin-left: 56.25pt; text-indent: -35.25pt; mso-list: l1 level1 lfo1; tab-stops: list 56.25pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">2．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">分析已经被锁定的日志文件，产生分析结果。（执行需要时间根据日志文件大小来决定，因此需要线程异步执行，结果根据设定拆分成细粒度包，降低输出线程等待时间）。</span></p>
<p class="MsoNormal" style="margin-left: 56.25pt; text-indent: -35.25pt; mso-list: l1 level1 lfo1; tab-stops: list 56.25pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">3．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">检查分析结果队列。（执行需要时间很短，当前是配置了</span><span lang="EN-US">SingleThreadExecutor</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来执行检查阻塞队列的工作，同时获取到分析结果包以后立刻创建线程来执行输出任务）</span></p>
<p class="MsoNormal" style="margin-left: 56.25pt; text-indent: -35.25pt; mso-list: l1 level1 lfo1; tab-stops: list 56.25pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">4．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">输出分析结果，如果输出成功，将分析过的日志文件在日志文件状态缓存中的状态更新为已分析。（执行时间根据输出情况来定，当前实现的是批量输出到数据库中，根据配置来批量提交入库，后续还会考虑实时统计到集中式</span><span lang="EN-US">Cache</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">作为监控使用）。</span></p>
<p class="MsoNormal" style="margin-left: 56.25pt; text-indent: -35.25pt; mso-list: l1 level1 lfo1; tab-stops: list 56.25pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">5．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">清理分析日志文件。（执行时间较短，设定了定时线程池执行清理任务，根据策略配置来执行清理和移动文件任务，并且清除在日志文件状态缓存中的信息）</span></p>
<p class="MsoNormal" style="margin-left: 21pt"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin-left: 21pt; text-indent: 21pt"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">根据上面的分解可以看到，其实在单线程工作的过程中，容易造成阻塞而影响性能的主要是读取，分析和写出这三个过程的协调，一个一个读取分析和写出，性能一定低于读取和分析并行工作，而分析完毕才写出，性能一定低于分析部分，写出部分。</span></p>
<p class="MsoNormal" style="margin-left: 21pt; text-indent: 21pt"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">同时由于细分各个任务，因此任务与任务之间的耦合度降低，可以运行期获取具体的任务实现配置，达到灵活配置的目的。</span></p>
<p class="MsoNormal" style="margin-left: 21pt; text-indent: 21pt"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">下面就具体的看看整个流程，以及其中的一些细节的说明，这里根据下图中的序号来逐一描述：</span></p>
<p class="MsoNormal" style="margin-left: 78.75pt; text-indent: -36.75pt; mso-list: l0 level1 lfo2; tab-stops: list 78.75pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">1．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">配置了</span><span lang="EN-US">Schedule Executor</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来检查日志所属目录中的日志文件，</span><span lang="EN-US">Executor</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的线程池大小以及检查时间间隔都根据配置来设定。</span></p>
<p class="MsoNormal" style="margin-left: 78.75pt"><span lang="EN-US" style="color: red">Tip:</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">定时任务可以设置</span><span lang="EN-US" style="color: red">delay</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">时间，那么可以根据你的任务数量以及时间间隔来设定每一个任务的</span><span lang="EN-US" style="color: red">delay</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">时间，均匀的将这些任务分布，提高效率。</span><span lang="EN-US" style="color: red"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin-left: 78.75pt"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin-left: 78.75pt; text-indent: -36.75pt; mso-list: l0 level1 lfo2; tab-stops: list 78.75pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">2．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">当</span><span lang="EN-US">Read Schedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">被执行时，将会去检查</span><span lang="EN-US">Analysis Log File State Concurrent Cache</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（也就是上面提到的</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">ConcurrentMap</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）中是否存在此文件，如果不存在证明尚未分析，需要将其置入</span><span lang="EN-US">Cache</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，如果已经存在就去查询其他文件。</span><span lang="EN-US" style="color: red">Tip:</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这里用了一点小技巧，通常我们对于此类操作应该做两部分工作，</span><span lang="EN-US" style="color: red">get</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">然后再</span><span lang="EN-US" style="color: red">put</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，但是这样可能就会在高并发的情况下出现问题，因为这两个操作不是一个原子操作。</span><span lang="EN-US" style="color: red">ConcurrentMap</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">提供了</span><span lang="EN-US" style="color: red">putIfAbsent</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">操作，这个操作意思就是说如果需要</span><span lang="EN-US" style="color: red">put</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span><span lang="EN-US" style="color: red">key</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">没有存在于</span><span lang="EN-US" style="color: red">Map</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中，那么将会把</span><span lang="EN-US" style="color: red">key,value</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">存入，并且返回</span><span lang="EN-US" style="color: red">null</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，如果已经存在了</span><span lang="EN-US" style="color: red">key</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">那么就返回</span><span lang="EN-US" style="color: red">key</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在</span><span lang="EN-US" style="color: red">map</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">已经对应的值。通过</span><span lang="EN-US" style="color: red">if (resources.putIfAbsent(filename, Constants.FILE_STATUS_ANALYSISING) == null)</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">就可以把两个操作合并成为一个操作。</span></p>
<p class="MsoNormal" style="margin-left: 78.75pt; text-indent: -36.75pt; mso-list: l0 level1 lfo2; tab-stops: list 78.75pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">3．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">日志读取的工作线程完成锁定文件以后，就将后续的工作交给</span><span lang="EN-US">Log Analysis Service Executor</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来创建分析任务异步执行分析操作，日志读取工作线程任务就此完成。</span></p>
<p class="MsoNormal" style="margin-left: 78.75pt; text-indent: -36.75pt; mso-list: l0 level1 lfo2; tab-stops: list 78.75pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">4．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">Log Analysis Schedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是运行期装载具体的接口实现类（采用的就是类似于</span><span lang="EN-US">JAXP</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">等框架使用的</span><span lang="EN-US">META-INF/services</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来读取工厂类，载入接口实现）。</span><span lang="EN-US">Analysis Schedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">执行的主要任务就是分析文件，并且根据配置将分析结果拆分并串行的置入到</span><span lang="EN-US">Block Queue</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中，提供给输出线程使用。</span></p>
<p class="MsoNormal" style="margin-left: 78.75pt; text-indent: -36.75pt; mso-list: l0 level1 lfo2; tab-stops: list 78.75pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">5．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">Receiver</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">主要工作就是守候着</span><span lang="EN-US">Block Queue</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，当有数据结果产生就创建</span><span lang="EN-US">Write Schedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来异步执行输出。</span></p>
<p class="MsoNormal" style="margin-left: 78.75pt; text-indent: -36.75pt; mso-list: l0 level1 lfo2; tab-stops: list 78.75pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">6．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">Log Writer Service Executor</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">根据配置来决定内置线程池大小，同时在</span><span lang="EN-US">Receiver</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">获取到数据包时产生</span><span lang="EN-US">Write Schedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来异步执行输出工作。</span></p>
<p class="MsoNormal" style="margin-left: 78.75pt; text-indent: -36.75pt; mso-list: l0 level1 lfo2; tab-stops: list 78.75pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">7．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">Write Schedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span><span lang="EN-US">Analysis Schedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">一样可以运行期装载接口实现类，这样提供了灵活的输出策略配置。</span></p>
<p class="MsoNormal" style="margin-left: 78.75pt"><span lang="EN-US" style="color: red">Tips:</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在数据库输出的时候需要配置批量提交记录最大数，分批提交提高性能，也防止过大结果集批量提交问题。</span><span lang="EN-US" style="color: red"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin-left: 78.75pt; text-indent: -36.75pt; mso-list: l0 level1 lfo2; tab-stops: list 78.75pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">8．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">写出完成以后需要更新锁定文件的状态，标示成为已经分析成功。这里还遗留一点问题，在一个日志文件分包的过程中每一个包都回记录隶属于哪一个分析文件，文件的最后一个数据包将会被标示。在输出成功以后会去检查哪些包是文件最后数据包，更新此文件为已分析成功，如果出现异常，那么将会把这些文件状态清除，接受下一次的重新分析。这里一个文件部分包提交暂时没有做到事务一致，如果出现部分成功可能会重复分析和记录。</span></p>
<p class="MsoNormal" style="margin-left: 78.75pt; text-indent: -36.75pt; mso-list: l0 level1 lfo2; tab-stops: list 78.75pt"><span lang="EN-US" style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">9．<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">最后就是</span><span lang="EN-US">Clean Schedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">被定时执行，根据策略来删除或者移动已经被分析过的文件。</span></p>
<p class="MsoNormal" style="margin-left: 42pt"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin-left: 42pt"><span lang="EN-US" style="color: red">Tips:<o:p></o:p></span></p>
<p class="MsoNormal" style="margin-left: 63pt; text-indent: 21pt"><span lang="EN-US" style="color: red; mso-bidi-font-size: 10.5pt">ScheduledExecutorService</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">内部可以配置线程池，当执行定时任务比较耗时，线程池中的线程都被占用的情况下，定时任务将不会准确的按时执行，因此设计过程中需要注意的是，定时任务一般是简短的工作任务，如果比较耗时，那么应该结合</span><span lang="EN-US" style="color: red; mso-bidi-font-size: 10.5pt">ScheduledExecutorService</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">和</span><span lang="EN-US" style="color: red; mso-bidi-font-size: 10.5pt">ExecutorService</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">，定时任务完成必要工作以后将耗时工作转交给</span><span lang="EN-US" style="color: red; mso-bidi-font-size: 10.5pt">ExecutorService</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">创建的即时执行异步线程去处理，保证</span><span lang="EN-US" style="color: red; mso-bidi-font-size: 10.5pt">Schedule Executor</span><span style="color: red; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">正常工作。</span><span lang="EN-US" style="color: red; mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US" style="color: red; mso-bidi-font-size: 10.5pt"><img height="841" alt="" src="http://www.blogjava.net/images/blogjava_net/cenwenchu/analyzertt.jpg" width="789" border="0" /><o:p></o:p></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-size: 9pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">图</span><span lang="EN-US" style="font-size: 9pt">2 </span><span style="font-size: 9pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">流程结构设计</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-size: 14pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类图：</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" align="center"><span lang="EN-US"><img height="563" alt="" src="http://www.blogjava.net/images/blogjava_net/cenwenchu/analyzer1.GIF" width="660" border="0" /></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-size: 9pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">图</span><span lang="EN-US" style="font-size: 9pt">3 </span><span style="font-size: 9pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类图</span><span lang="EN-US" style="font-size: 9pt">1<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">上面的类图中主要描述的就是日志分析应用的三个主类：类似于控制台的</span><span lang="EN-US">LogAnalyzer</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，具体内部资源管理类，配置类。（</span><span lang="EN-US">T</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示采用泛型）</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" align="center"><span lang="EN-US"><img height="971" alt="" src="http://www.blogjava.net/images/blogjava_net/cenwenchu/Schedule.GIF" width="905" border="0" /></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-size: 9pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">图</span><span lang="EN-US" style="font-size: 9pt"> 4 </span><span style="font-size: 9pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类图</span><span lang="EN-US" style="font-size: 9pt">2<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">类图</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">2</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">主要就是描述了在整个应用中所有的被分解可并行的任务定义。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">ClearSchedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">是用来在控制台输入</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">stop</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">停止日志分析的时候，做后续资源回收工作的任务。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">CleanSchedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">是用来清除被分析后的日志文件任务。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt">ConsumerSchedule</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">是阻塞队列消费者任务。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt">其他还有一些辅助工具类以及工厂类和定义类就不画了。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-size: 14pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt">后话：</span><span lang="EN-US" style="font-size: 14pt; mso-bidi-font-size: 12.0pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">做这个设计和开发的过程中又好好的实践了一些编程细节方面的内容，作为架构设计来说，需要多一些全局观和业务观，作为一个良好的开发者来说需要多实践，多了解一些细节，在不断学习和掌握各种大方向技术框架的同时，适当的了解一些细节也是一种很好的补充，同时也可以衍生思考。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>REST</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">风格的服务结合云计算的思想，会被使用的更为广泛，而云计算其实就是一个问题分解和组合处理的过程，可以说是一种宏观的问题解决策略。高效解决问题，提供服务，通过组合体现业务最大价值，就是互联服务的最重要目的。<br />
<br />
更多文章请访问：http://blog.csdn.net/cenwenchu79/</span></p>
<img src ="http://www.blogjava.net/cenwenchu/aggbug/194923.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cenwenchu/" target="_blank">岑文初</a> 2008-04-23 08:41 <a href="http://www.blogjava.net/cenwenchu/archive/2008/04/23/194923.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Xml解析校验引起的依赖问题</title><link>http://www.blogjava.net/cenwenchu/archive/2008/04/07/191151.html</link><dc:creator>岑文初</dc:creator><author>岑文初</author><pubDate>Mon, 07 Apr 2008 00:22:00 GMT</pubDate><guid>http://www.blogjava.net/cenwenchu/archive/2008/04/07/191151.html</guid><wfw:comment>http://www.blogjava.net/cenwenchu/comments/191151.html</wfw:comment><comments>http://www.blogjava.net/cenwenchu/archive/2008/04/07/191151.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cenwenchu/comments/commentRss/191151.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cenwenchu/services/trackbacks/191151.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 假期结束，开始收心回来继续工作。晚上有一个项目要发布，公司的同事突然打手机给我，说ASF的文件解析又出了上次的问题，希望尽快解决。<br />
&nbsp;<br />
<span style="font-size: 18pt">问题描述：<br />
</span>&nbsp;<br />
上一次问题：<br />
多台机器运行同一个分支的应用，但是有些机器正常，有一台机器始终在启动的时候报文件解析错误，从提示看来，主要是因为解析配置文件的时候校验dtd失效，这台机器无法连接外网。最后降低了我们内部的核心解析包，问题解决（或者让这台机器连接到外网）。（当时由于自己手头工作比较多，也没有在意，既然解决了就随之过去了）<br />
&nbsp;<br />
此次问题：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 问题的提示和上次的类似，不过这次的机器时连接外网的。<br />
&nbsp;<br />
<span style="font-size: 18pt">问题查找：</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解析出错的文件是ASF（SCA的服务框架）的组件配置文件（composite文件），格式为xml的格式，解析方式是通过StAX标准来实现的。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 按照上一次的解决方法，我将内部的tuscany0.998降级到tuscany0.997，解析正常。看了一下我对于这两个版本升级作的修改，主要是支持了SCA框架中的Spring配置文件能够使用import的标签，内签多个标准的spring文件。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 跟踪代码内部发现，果然是在解析某几个spring的配置文件时出现了问题，比较了一下ASF的Spring（正常解析）和标准的Spring配置文件，差别主要是在关于Xml的校验申明的区别。ASF的Spring配置文件是由ASF Spring插件来自己解析的（采用Schema申明（固定的Target namingspace），因此早先所有的ASF的Spring我都要求大家采用Schema的校验申明），而对于原来不是ASF的spring都是采用dtd的校验方式申明（互相拷贝导致都是这样）。下面就是两种申明：<br />
&nbsp;<br />
Schema：<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"<br />
&nbsp;&nbsp;&nbsp; xmlns:sca="http://www.springframework.org/schema/sca"<br />
&nbsp;&nbsp;&nbsp; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
&nbsp;&nbsp;&nbsp; xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd<br />
&nbsp;&nbsp;&nbsp; http://www.springframework.org/schema/sca http://www.springframework.org/schema/sca/spring-sca.xsd"&gt;<br />
&nbsp;<br />
Dtd：<br />
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"&gt;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 早先由于在0.997版本中没有支持import，因此也就不会去解析那些不是ASF的Spring文件，而现在因为需求支持了import所以需要解析那些原来不属于ASF的Spring的配置文件。因此降低版本不是解决问题的办法。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 进一步跟进问题，发现是在解析Dtd的申明时候出现问题，抛出异常说连接超时。通过IE访问了一下dtd的地址，的却也是有问题，无法连接。看来是Spring的dtd的服务器出现了问题，导致了我们解析文件时候校验无法正常，最终无法正常启动。<br />
&nbsp;<br />
<span style="font-size: 18pt">问题解决：</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里先说一下最后解决的几个方案，后面会有一些详细的解释和说明。<br />
&nbsp;<br />
1． 升级ASF的Spring插件包，去除对于Xml的格式校验。<br />
XMLInputFactory xmlFactory = XMLInputFactory.newInstance();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
//add by wenchu.cenwc cancel support dtd check<br />
xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, "false");<br />
&nbsp;<br />
2． 将Dtd的校验申明修改成为Schema的校验申明。<br />
3． 建立公司的Xml校验服务器，控制管理dtd或者schema，将所有的xml Schema或者dtd申明指向该服务器。<br />
&nbsp;<br />
问题延伸开来的思考：<br />
就问题的解决方案来看这个问题的一些值得注意和思考的地方。<br />
&nbsp;<br />
方案1：<br />
当前对于XML解析来说，各种框架都已经统一的实现了StAX的标准，同时在jdk6得rt.jar中都已经将StAX API作为基础框架API纳入其内。而通常情况下，如果不配置是否校验Xml，那么都将默认会主动校验Xml，此时就会出现上面我所遇到的问题，如果当你依赖的Xml DTD 或者 Schema服务器出现问题，就会导致你本地应用可能受到影响。在Dtd的申明说明中，&lt;!DOCTYPE rootElement PUBLIC "PublicIdentifier" "URIreference"&gt;红色部分说可以在网络出现问题或者网络速度很慢的时候被部分xml解析器替代URIreference使用，不过我这边没有成功过。我通过自己屏蔽网络连接来模拟环境的情况，都是无法通过的。（Schema比较奇怪，就算无法连接网络还是可以正常的，这个后续需要继续研究看看，或者有朋友对这个问题有了解请告知一下）<br />
&nbsp;<br />
方案2：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实早在2002年就已经有将dtd替换成为schema的趋势了，两者的区别和优劣网上的文章介绍了很多了，这儿不再罗列，其实最更本一点就是schema就是用xml来校验xml，而dtd却采用了另一套规则来校验xml，其本身也是xml，扩展性，可读性，学习曲线等等都次与schema。除了遗留系统，我个人看不出还有什么必要去使用老的dtd来校验xml的格式。Spring的dtd服务的出现问题，也说明了其实对于dtd这种方式的校验，spring也已经不会保证几个9的稳定性。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Xml常常会被作为数据承载中介，使双方能够在跨平台跨语言的情况下松耦合的交互信息，也是现在的SOA的实施基础。那么双方势必需要有协议和数据格式规范来约束，schema作为dtd的新一代替代者已经广为使用。另一方面，xsd也早已独立于wsdl作为数据描述和可重用的数据描述说明被采用到各种互联网应用。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看看国外的Facebook,亚马逊,ebay等公司的REST风格的API，就可以清楚地了解到xsd十分适合作为轻量级的数据交互协议。在后续ASF中融入REST配置的实现中，也需要采用XSD这种Schema描述来实现数据交互解析。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因此替换掉Dtd的配置是迟早要做的一件事情，所以迟作不如早作，更避免拷贝引起的问题放大效果（不过这个问题由于要考虑QA和业务组的项目经理的顾虑，因此我只能做到的是建议）。<br />
&nbsp;<br />
方案3：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看看Maven这些年这么火，其实在我们自己公司内部的antx同样都是在做一件事情，就是对于第三方的依赖包的版本控制。对于开源项目依赖的管理其实很重要，作的好项目能够很好的利用已有的成果，管理的不好就会被一些不太稳定的开源项目搞得头破血流。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 记得在上次三亚的聚会上谈到了对于Tuscany的依赖，其实对于这个项目来说，如果要作为成熟的产品来说，那么势必要获取一个版本然后就作为稳定的依赖，而不是一味的升级更新，由于我们产品的特殊性以及早期的Tuscany的不成熟，因此我们仅仅只是使用了Tuscany的最核心解析文件框架部分，其他的插件都采取自己设计或者在原有设计上优化和更新的做法。当然这不是说对于所有的第三方依赖都是采取这样的策略，其实如果不是基础框架设计，仅仅只是应用级别的使用，只需要拿来主义就完全可以了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回过头来看，大家现在对于类库的管理已经都很重视了，但是对于配置性的或者数据格式类的文件还没有引起足够的重视，不过看到很多朋友已经在本地作了这样的工作，不过就我来看，如果能够对dtd,schema作版本控制和服务器搭建，在长远来看还是有一定的好处的，只是说根据各自的需求来做这样的工作。<br />
&nbsp;<br />
&nbsp;<br />
<span style="font-size: 18pt">后续</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 问题出现的当天，我其实晚上就一直比较担心，因为如果问题不解决，那么将会影响到很多项目组（框架被使用的越广泛，自己所要承担的责任越重大）。其实也是由于自己上次对于这个问题的不上心，导致了问题的再次出现。刨根问底是件好事，做我们这行的还是需要多问一些为什么，这样就会少不少危急时刻的怎么办了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很多时候为什么程序员自己喜欢什么都自己做，因为掌握在自己手中的事情总是能够解决，但是现在项目中对于第三方的依赖越来越多，在选择和控制上必须慎之又慎，有时候依赖也是双刃剑。<br />
&nbsp;
<img src ="http://www.blogjava.net/cenwenchu/aggbug/191151.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cenwenchu/" target="_blank">岑文初</a> 2008-04-07 08:22 <a href="http://www.blogjava.net/cenwenchu/archive/2008/04/07/191151.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SOA论剑三亚江湖会</title><link>http://www.blogjava.net/cenwenchu/archive/2008/03/03/183341.html</link><dc:creator>岑文初</dc:creator><author>岑文初</author><pubDate>Sun, 02 Mar 2008 19:36:00 GMT</pubDate><guid>http://www.blogjava.net/cenwenchu/archive/2008/03/03/183341.html</guid><wfw:comment>http://www.blogjava.net/cenwenchu/comments/183341.html</wfw:comment><comments>http://www.blogjava.net/cenwenchu/archive/2008/03/03/183341.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.blogjava.net/cenwenchu/comments/commentRss/183341.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cenwenchu/services/trackbacks/183341.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">两个半小时之后，我和</span>BlueDavy<span style="font-family: 宋体">大叔回到了杭州，一个忙碌而充实的周末就这么过去了。回到家已经</span>1<span style="font-family: 宋体">点多了，洗了个澡，精神又来了，想把这个充实的周末写下来，做个纪念。</span>
<p><span style="font-family: 宋体">缘起</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">几周前，受到了普元的邀请，去三亚参加一个</span>SOA<span style="font-family: 宋体">的技术交流会，原本以为是因为在他们的论坛同时更新了一些文章所以被邀请一起去参加产品部活动，开始不是很想去，毕竟厂商出钱多半就是让你去听听他们的产品，一个周末飞来飞去的也累，但淘宝的</span>BlueDavy<span style="font-family: 宋体">大叔要去，顺便也叫我一起去热闹热闹，那么就决定去凑这个热闹。周五下班以后背了个包就直奔机场了，结果到了机场才发现办护照的时候身份证放在家里了，幸好机场还有个临时身份证明办理的便民业务，总算顺利的上了飞机。</span></p>
<p><span style="font-family: 宋体">人物</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">第一天到场，第一个环节就是自我介绍，江湖中人就悉数登场了。</span>Robbin,<span style="font-family: 宋体">白衣，俞黎敏，小刚，杨戈，</span>InfoQ<span style="font-family: 宋体">中文站的主编霍泰稳和其他几个编辑，灰狐的主创：银狐</span>999<span style="font-family: 宋体">，程勇，普元的老大</span>Chris<span style="font-family: 宋体">以及普元的很多技术架构师牛人。陈勇的一句话就把我们这批人概括了一下，其实大家都是江湖中人，平时混各个堂口的，今天在普元召集的这么一次活动，有机会能够汇聚一起，谈天说地。</span></p>
<p><span style="font-family: 宋体">活动</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">第一天上午有两个</span>Topic<span style="font-family: 宋体">关于</span>SOA<span style="font-family: 宋体">的市场价值，下午也有两个</span>Topic<span style="font-family: 宋体">关于</span>SOA<span style="font-family: 宋体">的企业架构，主讲的都是我们这些外部请来的人。上午下午有很多时间是自由讨论，每个人都有机会发表自己的意见，应该是算圆桌会议，所以你要发言随时随地都可以直接说，不需要顾忌什么，只要觉得要说，该说，就能说。这一天碰撞出不少火花。晚上边吃烧烤边聊，后面去沙滩上继续讨论技术圈的构建（不过这个到了最后也没有一个很好的解决方案）。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">第二天主要是普元分享了他们对于</span>SOA<span style="font-family: 宋体">技术的理解和具体的实现，从实际角度去展示了一个</span>SOA<span style="font-family: 宋体">的实实在在的成果。</span></p>
<p><span style="font-family: 宋体">收获</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">其实这部分才是我要写得重点。一部分一部分来说吧。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">首先是识人，这次在会上看到了很多都是自己曾经蛮佩服的国内开源技术论坛或者社区的创办者，其实从他们身上除了看到技术人员本身所特有的对于技术的追求更多的是对于新事物的理性的学习和分析。其实我一直觉得有一点对于一个架构师或者一个优秀的开发人员来说很重要的特质就是开放的去学习和接受新事物，有一个包容的心态去看待每一个陌生的新生事物，那么才能不断地进步。而排斥和固步自封，只能最后束缚了自己的脚步，慢慢的落后与他人。</span>SOA<span style="font-family: 宋体">这个概念提出来很多年了，炒作到实质性的转变也就在这一两年，具体的实践还需要后面几年的实施和验