﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-庄周梦蝶-随笔分类-工作随笔</title><link>http://www.blogjava.net/killme2008/category/45690.html</link><description>生活、程序、未来</description><language>zh-cn</language><lastBuildDate>Tue, 17 Apr 2012 21:17:38 GMT</lastBuildDate><pubDate>Tue, 17 Apr 2012 21:17:38 GMT</pubDate><ttl>60</ttl><item><title>Java程序员常用工具集</title><link>http://www.blogjava.net/killme2008/archive/2012/04/17/374936.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Tue, 17 Apr 2012 09:05:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2012/04/17/374936.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/374936.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2012/04/17/374936.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/374936.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/374936.html</trackback:ping><description><![CDATA[<br />&nbsp; &nbsp; 我发现很多人没办法高效地解决问题的关键原因是不熟悉工具，不熟悉工具也还罢了，甚至还不知道怎么去找工具，这个问题就大条了。我想列下我能想到的一个Java程序员会用到的常用工具。<br />
<br />
一、编码工具<br />
<br />
1.IDE：<a href="http://www.eclipse.org/">Eclipse</a>或者<a href="http://www.jetbrains.com/idea/">IDEA</a>，熟悉尽可能多的快捷键，《<a href="https://www.google.com/#hl=zh-CN&amp;site=&amp;source=hp&amp;q=Eclipse+%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE&amp;oq=Eclipse+%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE&amp;aq=f&amp;aqi=g-l1&amp;aql=&amp;gs_l=hp.3..0i13.449l7739l0l7948l53l49l9l4l4l9l278l4511l14j15j7l36l0.&amp;bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&amp;fp=eef1a08d3fa904e4&amp;biw=1594&amp;bih=858">Eclipse常见快捷键列表</a>》<br />
2.插件：&nbsp;<br />
(1) <a href="http://findbugs.sourceforge.net/">Findbugs</a>，在release之前进行一次静态代码检查是必须的<br />
(2) <a href="http://www.atlassian.com/software/clover/overview">Clover</a>，关心你的单元测试覆盖率<br />
(3)&nbsp;<a href="http://checkstyle.sourceforge.net/">Checkstyle</a>&nbsp;代码风格检查<br />
<br />
3.构建和部署工具:<a href="http://ant.apache.org/">ant</a>或者<a href="http://maven.apache.org/">maven</a>，现在主流都是maven了吧，<a href="http://www.sonatype.org/nexus/">使用nexus搭建maven私服</a>，再加上持续集成<a href="http://jenkins-ci.org/">jenkins</a>。代码质量不用愁。<br />
<br />
4.版本管理工具： <a href="http://subversion.tigris.org/">svn</a>或者<a href="http://git-scm.com/">git</a><br />
<br />
5.<a href="http://www.linuxsky.org/doc/admin/200712/213.html">diff和patch</a><br />
<br />
6.设置你的eclipse或者IDEA，如formatter,<a href="http://mestreota.blogspot.jp/2007/11/save-action-on-eclipse.html">save actions</a>以及code template等。代码风格，直接用google的也可以啊。《<a href="http://code.google.com/p/google-styleguide/">Google style guide</a>》<br />
<br />
7.掌握一个文本编辑器，Emacs或者VIM，熟悉常用快捷键。这在你需要在线编辑代码，或者编写其他语言代码时候特别有用。《<a href="http://linuxtoy.org/archives/why-emacs-vim-good.html">神器圣战</a>》<br />
<br />
二、JDK相关<br />
<br />
1.jstat : 观察GC情况，如：<br />
<br />
<div style="font-size: 13px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; border-left-color: #cccccc; border-image: initial; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; background-color: #eeeeee; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->jstat&nbsp;-gcutil&nbsp;pid&nbsp;2000</div>
<br />
2.jmap，查看heap情况，如查看存活对象列表：<br />
<div style="font-size: 13px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; border-left-color: #cccccc; border-image: initial; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; background-color: #eeeeee; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->jmap&nbsp;-histo:live&nbsp;pid&nbsp;|grep&nbsp;com.company&nbsp;|less&nbsp;</div>
<br />
或者dump内存用来分析：<br />
<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->jmap&nbsp;-dump:file=test.bin&nbsp;pid</div>
<br />
3.分析dump的堆文件，可以用jhat:<br />
<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->jhat&nbsp;test.bin</div>
<br />
&nbsp; 分析完成后可以用浏览器查看堆的情况。这个工具的分析结果还比较原始，你还可以用<a href="http://www.eclipse.org/mat/">Eclipse MAT</a>插件进行图形化分析，或者IBM的<a href="https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=4544bafe-c7a2-455f-9d43-eb866ea60091">Heap Analyzer</a>.<br />
<br />
4.jvisualvm和jconsole： JVM自带的性能分析和监控工具，怎么用？<a href="http://docs.oracle.com/javase/6/docs/technotes/guides/visualvm/index.html">请自己看文档。</a><br />
<br />
5.jstack：分析线程堆栈，如<br />
<br />
<div style="font-size: 13px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; border-left-color: #cccccc; border-image: initial; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; background-color: #eeeeee; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->jstack&nbsp;pid&nbsp;&gt;&nbsp;thread_dump</div>
<br />
&nbsp; &nbsp; 查看CPU最高的线程在干什么的方法结合top和jstack：<a href="http://www.iteye.com/topic/1114219">http://www.iteye.com/topic/1114219</a><br />
<br />
6.更多JVM工具，参见官方文档：<a href="http://docs.oracle.com/javase/6/docs/technotes/tools/">http://docs.oracle.com/javase/6/docs/technotes/tools/<br />
<br />
</a>7.学习使用btrace分析java运行时问题。《<a href="http://rdc.taobao.com/team/jm/archives/509">Btrace使用简介</a>》<br />
<br />
8.GC日志分析工具：<a href="http://www.tagtraum.com/gcviewer.html">GC viewer</a>、<a href="http://code.google.com/p/gc-console/">GC-console</a>或者<a href="http://stackoverflow.com/questions/1839599/analyze-gc-logs-for-sun-hotspots-jvm-6">自己挑吧。</a><br />
<br />
9.性能分析工具，除了自带的jvisualvm外，还可以用商业的<a href="http://www.ej-technologies.com/products/jprofiler/overview.html">jprofiler</a>。<br />
<br />
<a href="http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm">10.JVM参数大全<br />
<br />
</a>11.《<a href="http://hllvm.group.iteye.com/group/topic/27945">JVM调优标准参数陷阱</a>》，iteye神贴。<br />
<br />
三、Linux工具<br />
<br />
1.<a href="http://www.commandlinefu.com/commands/browse/sort-by-votes">熟悉常用的shell命令，</a><br />
<div style="display: inline-block; "><br />
2.<a href="http://paulkeck.com/ssh/">设置ssh免登陆<br />
</a></div>
<br />
<br />
3.使用<a href="http://htop.sourceforge.net/">htop</a>替换top。<br />
<br />
4.熟悉下<a href="http://www.ibm.com/developerworks/aix/library/au-unix-strace.html">strace,gdb</a>甚至<a href="http://sourceware.org/systemtap/" title="systemtap"></a><a href="http://sourceware.org/systemtap/"></a><a href="http://sourceware.org/systemtap/" title="systemtap">systemta</a>p来分析问题。<br />
<br />
<a href="http://techgurulive.com/2009/01/29/performance-tuning-tools-ps-top-sar-iostat-and-vmstat/">5.熟悉vmstat,iostat,sar等性能统计工具。</a><br />
<br />
5.自动化部署脚本，<a href="http://docs.fabfile.org/en/1.4.1/index.html">py-fabric</a>或者自荐下我的<a href="https://github.com/killme2008/clojure-control">clojure-control</a>。<br />
<br />
四、其他<br />
<br />
1.掌握一门脚本语言，<a href="http://python.org">Python</a>或者<a href="http://www.ruby-lang.org">Ruby</a>，高效解决一些需要quick and dirty的任务：比如读写文件、导入导出数据库、网页爬虫等。注意不是python.com，咔咔。<br />
<br />
2.使用Linux或者Mac os系统作为你的开发环境。<br />
<br />
3.升级你的&#8220;硬件工具&#8221;，双屏大屏显示器、SSD、8G内存甚至更多。<br /><br />4.你懂的：<a href="https://code.google.com/p/goagent/">https://code.google.com/p/goagent/</a><br />
<br />
五、如何查找工具？<br />
<br />
1.搜索引擎，google或者baidu，《<a href="http://www.williamlong.info/archives/728.html">搜索技巧</a>》<br />
<br />
2.万能的stack overflow：<a href="http://stackoverflow.com/">http://stackoverflow.com/<br />
</a>
<br />
3.虚心问牛人。<br /><br />六、最重要的是&#8943;&#8943;<br /><br />一颗永不停止学习的心。<img src ="http://www.blogjava.net/killme2008/aggbug/374936.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2012-04-17 17:05 <a href="http://www.blogjava.net/killme2008/archive/2012/04/17/374936.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最近做的一些事情</title><link>http://www.blogjava.net/killme2008/archive/2012/01/15/368548.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sun, 15 Jan 2012 04:50:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2012/01/15/368548.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/368548.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2012/01/15/368548.html#Feedback</comments><slash:comments>18</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/368548.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/368548.html</trackback:ping><description><![CDATA[<br />&nbsp;&nbsp;&nbsp; 很久没写blog了，写写最近做的一些工作，给感兴趣的朋友做参考。<br />&nbsp;&nbsp;&nbsp; 首先是我们的<a href="http://sna-projects.com/kafka/projects.php">kafka</a>的&#8220;复制品&#8221;metamorphosis做了1.4版本，实现了同步复制方案，broker本身也做了很多优化，总体而言meta是一个非常成熟可用的产品了。甚至可以说是我在淘宝做的最好的一个产品。有些朋友总是问我们为什么不直接用kafka，而要另写一个？这里做个统一的解答。<br />（1）kafka是scala写的，我对scala不熟悉，也不待见，考虑到维护和语言熟悉程度，用java重写仍然是最好的选择。<br />（2）其次，kafka的整个社区非常不活跃，发展太慢，而我又不愿意去学习scala来参与社区发展，那么唯一的出路就是自己写。<br />（3）kafka的一些工作不能满足我们的要求，比如一开始它连producer的负载均衡都没有，它的消费者API设计还是比较蛋疼的。它也不支持事务，没有考虑作为一个通用的MQ系统来使用。并且它也没有高可用和数据高可靠的方案。<br />（4）我们做了什么呢？<br />a.用java彻底重写整个系统，除了原理一致，整个实现是彻底重新实现的。<br />b.我们提供了生产者的负载均衡（仍然是基于zk），重新设计了消费者API，更符合 JMS的使用习惯。<br />c.我们提供了事务实现，包括producer和consumer端的，包括本地事务和符合XA规范的分布式事务实现。<br />d.我们提供了两种数据高可靠方案：类似mysql的异步复制和同步复制方案。通过将消息复制到多个节点上来保证数据的高可靠。<br />e.我们提供了http协议的实现，并且本身使用协议也是类似memcached的文本协议，内部也增加了很多统计项目，可以以memcached的stats协议的方式来获取纯文本的统计信息。整个系统运维很方便。<br />f.提供了很多扩展应用：广播消费者的实现，多种offset存储的实现（默认的zookeeper，还有文件和mysql)，tail4j用于作为agent发送日志，log4j appender扩展用于透明地使用log4j发送消息，hdfs writer用于将消息写入hdfs，storm spout用于将消息接入storm做实时分析，基本上形成一套完整的工具链和扩展。<br />g.一些其他功能点：group commit提升数据可靠性和吞吐量，连接复用，集群下的顺序消息发送，消息数据的无痛迁移和水平扩展，web管理平台等。<br /><br />&nbsp;&nbsp;&nbsp; meta未来会走开源的路子，不过不会是我来推动的，估计是在今年会有进展。<br /><br />&nbsp;&nbsp;&nbsp; 我最近还写了一些小项目值得一提，首先是<a href="http://code.google.com/p/aviator/">aviator</a>这个轻量级的表达式执行引擎发布了2.2.1版本，主要是这么几个改进：<br />（1）支持多维数组变量的访问，如a[0][0]<br />（2）添加Expression#getVariableNames()用于返回表达式的变量列表<br />（3）添加AviatorEvaluator#exec方法来简化调用<br />（4）bug修正等。<br />&nbsp;&nbsp;&nbsp; maven直接升级：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">dependency</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">groupId</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; ">com.googlecode.aviator</span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">groupId</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">artifactId</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; ">aviator</span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">artifactId</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">version</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; ">2.2.1</span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">version</span><span style="color: #0000FF; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">dependency</span><span style="color: #0000FF; ">&gt;</span></div><br />&nbsp;&nbsp;&nbsp; 其次，<a href="https://github.com/killme2008/hs4j">hs4j</a>这个handler socket的客户端，由新浪微博的@赵鹏城实现了inc/dec协议，添加了incr和decr方法用于更新计数，感谢他的贡献，如果你需要这两个功能可以自己从github拉取源码并构建打包，暂时不准备发布到maven。<br /><br />&nbsp;&nbsp;&nbsp; 第三，关注高可用的Transaction Manager实现的可以关注下我的<a href="https://github.com/killme2008/ewok">ewok</a>项目，这是一个基于<a href="http://docs.codehaus.org/display/BTM/Home">BTM</a>这个开源JTA实现，提供基于bookkeeper的高可用的TM项目。将事务日志写到高可用的bookkeeper上，并利用zookeeper来做到故障的透明迁移，某个TM挂了，可以在其他机器上从bookkeeper拉取日志并恢复。代码已经稳定并做了性能测试，没有做进一步的破坏性测试。<a href="http://docs.codehaus.org/display/BTM/Home">BTM</a>是一个比JOTM和atomikos更靠谱的开源JTA实现，并且性能也好上很多，代码质量更不用说，建议有兴趣的可以看一下。我也为它贡献了<a href="https://jira.codehaus.org/browse/BTM-113">一个事务日志写入优化的patch</a>，日志写入性能提升了近一倍。<br /><br />&nbsp;&nbsp;&nbsp; 最后，我在clojure上做了一些事情，首先是为<a href="https://github.com/nathanmarz/storm">storm</a>项目贡献了两个patch：利用curator做zookeeper交互和添加storm.ui.context.path选项，前者被作者接受，后者暂时只对我们有用。前者让storm跟zk的交互更可用，后者是为storm ui添加了可选的相对路径。你都可以在<a href="https://github.com/killme2008/storm">我fork的分支上</a>尝试，curator的patch在storm 0.6.2上发布，现在还是snapshot状态。昨天晚上牙痛睡不着，半夜写了个<a href="https://github.com/killme2008/stm-profiler">clojure STM profiler</a>，用于统计分析clojure STM运行状况，诸如事务运行次数和时间，事务的重试原因和次数等，可以针对每个dosync的form做统计，有兴趣也可以看下。不过我其实更想将这个功能加入到clojure核心，会尝试提交下pull request。<br /><br />&nbsp;&nbsp; 还有个工作上的变迁，我将在2月1号正式从呆了近三年的淘宝离职，加入一支充满活力的创业团队。从稳定的大公司出来，去加入一家初创公司，不能说没有风险，但是我还是想去接受新的挑战，愿意更新我的知识结构，愿意向牛人们学习。我在某个blog上说我今年遇到了人生中最大的挑战和转折，并不是说这个事情，而是我的儿子今年患了一场重病，庆幸在很多人的帮助和关心下，他勇敢地挺了过来，度过最困难的一关，现在还在继续治疗。我要感谢很多人，感谢淘宝，感谢我的TL华黎和锋寒，感谢我的同事和朋友林轩，感谢我们的HR，感谢三年后打交道的很多同事。没有他们，我今年真的过不了关，没有他们，我也不能进入淘宝并呆上快三年。<br /><br />&nbsp;&nbsp; 最后的最后，我要特别感谢我的儿子，谢谢你的降生，谢谢你今年的勇敢，谢谢你给我们全家带来的快乐，谢谢你继续陪着我们 ，也希望你新年继续勇敢地坚持下去，我们必将战胜一切。&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;<img src ="http://www.blogjava.net/killme2008/aggbug/368548.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2012-01-15 12:50 <a href="http://www.blogjava.net/killme2008/archive/2012/01/15/368548.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>找bug记(2)</title><link>http://www.blogjava.net/killme2008/archive/2011/09/02/357774.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 01 Sep 2011 16:02:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2011/09/02/357774.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/357774.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2011/09/02/357774.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/357774.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/357774.html</trackback:ping><description><![CDATA[<br />&nbsp;&nbsp;&nbsp; 这篇blog迟到了很久，本来是想写另一个跟网络相关bug的查找过程，偷偷懒，写下最近印象比较深刻的bug。这个bug是我的同事水寒最终定位到的。<br />&nbsp;&nbsp;&nbsp; 前几个月同事报告称有一个线上MQ集群会同一时间抛出ArrayIndexOutOfBoundsException这个异常，也就是数组越界。查看源码，除去一些无关紧要的细节大概是这样子：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;ConnectionSelector{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;AtomicInteger&nbsp;sets</span><span style="color: #000000; ">=</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;AtomicInteger(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br /><br />&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;selectConnection(List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Connection</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;connList){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(connList</span><span style="color: #000000; ">==</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)｛<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000;">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ｝<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;size&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;connList.size();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(size&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000;">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: red;">return</span><span style="color: red;">&nbsp;connList.get(sets.incrementAndGet()&nbsp;</span><span style="color: red;">%</span><span style="color: red;">&nbsp;size);<br /></span><span>}</span><span style="color: #000000;"><br /><br />&nbsp;&nbsp;&nbsp;}</span></div><br />&nbsp;&nbsp;&nbsp; 很显然，这里的本意是实现一个轮询的连接选择器，返回一个选中的连接。使用AtomicInteger递增并对链表大小取模，返回结果索引位置的连接。异常抛出的位置就是我代码中标红的位置。<br /><br />&nbsp;&nbsp;&nbsp; 显然，这里有两种可能，一种情况下是说在执行那一行代码的时候，connList的大小缩小了（也就是说连接可能被其他线程移出），那么导致取模的结果越界。另一种可能是取模的结果本身确实超过了列表范围。<br /><br />&nbsp;&nbsp;&nbsp; 第一种情况是完全可能的，因为服务器的连接可能随时断开或者重连，但是这种情况相对非常少见，因此我们这里并没有对这个选择过程做同步，主要是从性能的角度出发，偶尔的失败可以接受。很遗憾的是，我被我的思维惯性误导了，从来没有怀疑过第二种情况，总是认为是不是真的连接恰巧断开导致这个异常，但是却无法解释这个异常发生后就一直错误下去，无法自行恢复。<br />&nbsp;&nbsp;&nbsp; 为什么说思维惯性误导呢？这里的问题其实是负数取模的问题，对一个负数进行取模，结果会是正数还是负数？答案是结果因语言而异。<br />&nbsp;&nbsp;&nbsp; 我很早以前在使用Ruby的时候做过测试，负数取模结果为正数，例如在irb里尝试下：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1000</span><span style="color: #000000; ">%</span><span style="color: #000000; ">3</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">=&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2001</span><span style="color: #000000; ">%</span><span style="color: #000000; ">4</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">=&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">3</span></div><br />&nbsp;&nbsp;&nbsp; 这个印象持续至今，在clojure里结果也是这样子：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Clojure&nbsp;</span><span style="color: #000000; ">1.2</span><span style="color: #000000; ">.</span><span style="color: #000000; ">1</span><span style="color: #000000; "><br />user</span><span style="color: #000000; ">=&gt;</span><span style="color: #000000; ">&nbsp;(mod&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1000</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">)<br /></span><span style="color: #000000; ">2</span><span style="color: #000000; "><br />user</span><span style="color: #000000; ">=&gt;</span><span style="color: #000000; ">&nbsp;(mod&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2001</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">)<br /></span><span style="color: #000000; ">3</span></div><br />&nbsp;&nbsp;&nbsp; 可以再试试python:<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Python&nbsp;</span><span style="color: #000000; ">2.7</span><span style="color: #000000; ">.</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;(r271:</span><span style="color: #000000; ">86832</span><span style="color: #000000; ">,&nbsp;Jun&nbsp;</span><span style="color: #000000; ">16</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">2011</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">16</span><span style="color: #000000; ">:</span><span style="color: #000000; ">59</span><span style="color: #000000; ">:</span><span style="color: #000000; ">05</span><span style="color: #000000; ">)&nbsp;<br />[GCC&nbsp;</span><span style="color: #000000; ">4.2</span><span style="color: #000000; ">.</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;(Based&nbsp;on&nbsp;Apple&nbsp;Inc.&nbsp;build&nbsp;</span><span style="color: #000000; ">5658</span><span style="color: #000000; ">)&nbsp;(LLVM&nbsp;build&nbsp;</span><span style="color: #000000; ">2335.15</span><span style="color: #000000; ">.</span><span style="color: #000000; ">00</span><span style="color: #000000; ">)]&nbsp;on&nbsp;darwin<br />Type&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">help</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">copyright</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">credits</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;or&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">license</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">&nbsp;more&nbsp;information.<br /></span><span style="color: #000000; ">&gt;&gt;&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">10000</span><span style="color: #000000; ">%</span><span style="color: #000000; ">3</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">2</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">&gt;&gt;&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2001</span><span style="color: #000000; ">%</span><span style="color: #000000; ">4</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">3</span></div><br />&nbsp;&nbsp;&nbsp; 这三种语言的结果完全一致，结果都为正数。这个惯性思维延续到java却不成立了，可惜我根本没做测试，让我们试下：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1000</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">%</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2001</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">%</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><br />打印结果为：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span></div><br />&nbsp;&nbsp;&nbsp; 果然，在java里负数取模的结果为负数，而不是我习惯性地认为是正数。因此最终的定位到的原因就是sets这个变量递增超过Integer.MAX_VALUE后越界变成负数了，取模的结果为负数，导致抛出数组越界的异常，这也解释了为什么同一个集群都在同一时间出问题，因为这个集群内的机器启动时间相邻并且调用这个方法次数相对平均。<strike>修正问题很简单，加个Math.abs就好。</strike><br /><br />&nbsp;&nbsp;&nbsp; Update:加个abs是不够的，因为Math.abs的javadoc提醒了：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">Note&nbsp;that&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;the&nbsp;argument&nbsp;is&nbsp;equal&nbsp;to&nbsp;the&nbsp;value&nbsp;of&nbsp;Integer.MIN_VALUE,&nbsp;the&nbsp;most&nbsp;negative&nbsp;representable&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;value,&nbsp;the&nbsp;result&nbsp;is&nbsp;that&nbsp;same&nbsp;value,&nbsp;which&nbsp;is&nbsp;negative. <br /></span></div><br />&nbsp;&nbsp;&nbsp; 也就是说对Integer.MIN_VALUE做abs结果仍然是负数。尽管在这个场景中失败一次可以接受，但是最好的办法还是回复中steven提到的抵消符号位的做法：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">(sets.incrementAndGet()&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0x7FFFFFFF</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #000000; ">%</span><span style="color: #000000; ">&nbsp;size</span></div>&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; 这个问题更详细的讨论后来我找到<a href="http://ceeji.net/blog/mod-in-real/">这篇博客</a>，作者讨论几种语言和计算器的这个问题的结果，给出了一些结论。不过我觉的这个结论可能也不是那么可靠，特别是对c/c++来说，很大程度上应该还是依赖于实现，最可靠的办法还是强制结果为正。<br /><br />&nbsp;&nbsp;&nbsp; 这个bug的几个教训：<br />1、首先是第一次出现的时候没有引起足够重视，重启解决问题后没有深究。有句玩笑话：99％的程序问题都可以通过重启解决。但是事实上问题仍然存在，该发生的终究还会发生。不管你信不信，它就是发生了，这是一个奇迹。<br />2、注意大脑的思维惯性，经验主义和教条主义都不可取。最近在读一本好书《<a href="http://book.douban.com/subject/6709809/">暗时间</a>》，大脑误导我们的手段可是多种多样。<br />3、最后就是这个负数取模的结果因语言而异，不要依赖于特定实现。<br />&nbsp;&nbsp;&nbsp;<img src ="http://www.blogjava.net/killme2008/aggbug/357774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2011-09-02 00:02 <a href="http://www.blogjava.net/killme2008/archive/2011/09/02/357774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>写DSL的步骤</title><link>http://www.blogjava.net/killme2008/archive/2011/07/25/355010.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Mon, 25 Jul 2011 11:30:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2011/07/25/355010.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/355010.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2011/07/25/355010.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/355010.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/355010.html</trackback:ping><description><![CDATA[<br />1.选定宿主语言，最好选用元编程能力强悍的语言作为宿主语言。<br />2.确定DSL的样子，<strong>让脑袋空白，不去考虑任何实现问题</strong>，纯粹思考你想要实现的dsl是什么样子<br />3.用你想要的DSL写一个<strong>最基本的例子，只包括最基本的功能</strong>。<br />4.开始实现DSL，尽快让你的DSL例子以<strong>dirty and quick</strong>的方式跑起来。<br />5.写更多DSL的例子，慢慢包括你想要的所有功能，并一一实现，在这个过程中你可能改变DSL的样子，原来模糊的东西渐渐清楚。<br />6.大功告成，review你的代码并添加<strong>自动化测试</strong>，将代码中dirty和bad smell的部分一一剔除。<br />7.让你的DSL接受实际应用的考验吧。<br /><img src ="http://www.blogjava.net/killme2008/aggbug/355010.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2011-07-25 19:30 <a href="http://www.blogjava.net/killme2008/archive/2011/07/25/355010.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最近对kafka的移植工作</title><link>http://www.blogjava.net/killme2008/archive/2011/05/07/349731.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sat, 07 May 2011 02:46:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2011/05/07/349731.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/349731.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2011/05/07/349731.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/349731.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/349731.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; <a href="http://sna-projects.com/kafka/">Kafka</a>这个linkedin开源的MQ，我在过去的<a href="http://www.blogjava.net/killme2008/archive/2011/01/20/343284.html">blog</a>简单介绍过。最近3周来，我的工作就是做它的一个Java移植版本，kafka是用scala写的，基于维护和定制的角度，这个拷贝的版本还是用Java。说拷贝，也不尽然，原理相同，但实现完全换过，从数据结构到通讯框架、通讯协议、程序组织，乃至一些重要功能点上都做了改进和更新。我将这个Java版本取名为metamorphosis,也就是卡夫卡的代表作《变形记》的英文名。<br />
<br />
&nbsp;&nbsp;&nbsp; 在原版本上，目前做了如下改进：<br />
1、协议替换为文本协议，整个协议类似memcached，文本协议的优点自不必说。通讯框架也是采用内部使用的通讯框架，减少工作量。<br />
<br />
2、存储结构上也采用自定义结构，更简洁紧凑。<br />
<br />
3、kafka原来只支持consumer和broker之间的服务查找和负载均衡，meta加入了producer和broker之间的服务查找和负载均衡。<br />
<br />
4、Consumer API没有采用kafka的stream方式，而是同时实现同步获取和异步订阅两种方式，更接近JMS和Notify。<br />
<br />
5、改进了服务器端文件recover的性能，采用并发多线程recover的方式（可选）。<br />
<br />
6、添加了实时统计功能和协议，类似memcached的stats协议,响应透明号召。<br />
<br />
7、客户端的连接复用。<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 以后要做的事情，可能包括：<br />
1、实现类似Mysql的master/slave方案，可能还要分为同步和异步两种模式。<br />
<br />
2、分区扩展时候的数据自动迁移功能，做到无痛水平扩展。<br />
<br />
3、高可用方案的另一个实现。<br />
<br />
4、嵌入Http server做web管理。<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 工作在本周初步告一段落，接下来是要做集成测试和压测等，我在两台8核16G的机器上分别部署服务器和客户端（订阅者发布者同在一台），做的一个简单压测数据如下：并发100个线程发送5000万消息并同时消费，1K大小的消息TPS可以达到3.8万，4K大小的消息TPS可以达到1.8万，服务器load都维持在一个较低的水平。从这个数据来看，超过我一开始的预期。后续可能做下kakfa的测试对比下。<br />
<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/349731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2011-05-07 10:46 <a href="http://www.blogjava.net/killme2008/archive/2011/05/07/349731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>工作随笔——2010-07-24</title><link>http://www.blogjava.net/killme2008/archive/2010/07/24/327027.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sat, 24 Jul 2010 12:51:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2010/07/24/327027.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/327027.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2010/07/24/327027.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/327027.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/327027.html</trackback:ping><description><![CDATA[<br />
&nbsp;&nbsp;&nbsp; 转眼间快到8月，已经过去了两个季度，是时候稍微总结下干了什么，以后想干什么。从春节到现在，我仍然是做淘宝的消息中间件Notify的开发，中间额外去做了一个多月的另一个项目的开发，重拾了web开发的一些东西。<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 这半年来Notify的改进集中在通讯层的改造，引入AMQP的订阅模型，以及将重要消息从oracle迁移到mysql做HA方案，这一过程是一个慢慢稳定系统的过程。新版本刚出来的时候有不少BUG，有些BUG很严重，幸运的是没有造成严重的后果，再一次提醒我小心，小心，再小心；小心是一方面，工作有没有做到位是另一个方面，暴露出来的问题还是单元测试不全面，以及麻痹大意心态下的不警惕，对关键代码认识不清，code review也没有做到位等等。<br />
<br />
&nbsp;&nbsp;&nbsp; Notify做到现在，剩下的问题是一些很难解决，又非常关键性的问题，如消息的去重，消息的顺序性，以及消息的可靠存储。我说的这些问题都是在分布式集群环境下需要解决的问题，如果仅仅是单台服务器，这些问题的解决还算不上特别困难。消息的去重，基本的思路是在客户端和服务器之间各自维持一个状态，用于保存当前消息的处理情况，依据这个情况来做消息的去重，但是状态的保存对服务器和客户端来说都是一个额外的负担，并且很难做到可靠的存储，如果状态丢了，去重的目的就没办法做到。ActiveMQ里是在服务器和客户端都维持了一个bitmap做重复的检测，但是这个bitmap大小必然是受限的，并且无法持久保存的。消息的在集群环境下的顺序性，涉及到集群环境下的事件的时间顺序问题，除了使用分布式锁来保证一致性之外，暂时也没有好的思路去解决。消息的可靠存储，今年我们的目标至少是脱离oracle，目前实现的HA mysql双写的实现已经开始部署到交易这样的核心系统上，第三个季度将慢慢地全部切换过去。下一步的目标是将消息存储到key-value系统上，但是需要解决的是索引的问题，因为消息的恢复涉及到查询，并且需要根据一些特殊条件做查询以应付诸如尽快恢复重要消息这样的功能，因此目前的一些key-value系统要么在索引功能上太弱，要么在集群功能上太弱，要么在大数据量存储上有局限，因此不是特别切合我们的场景，因此一个可行的方案是将消息的header继续存储在关系数据库，方便做查询，而将数据较大的body存储在key-value上，减轻数据库的负担。今年，我们还是希望能在以上3个方向某个方向做出突破。<br />
<br />
&nbsp;&nbsp; 这半年来技术上的收获，第一个季度业余时间都去打游戏了，没方什么心思在学习和工作上，后来去学习了下ASM，总算对java的byte code，以及jvm执行byte code过程有了个理解，然后利用ASM去搞了aviator这个项目。接下来开始做服务器的SEDA改造，这个过程完成了部分，但是不满意，SEDA的模型过于理论化，模型是好的，但是在stage controller的实现上目前没有可供参考的经验，做到资源的自动控制更需要实际的测试和实践，基本的指导原则只能作为参考。另外，最近下决心去重构整个项目，从一个一个类看起，看到不爽的地方马上去做重构，这个过程，我又去重看了下《重构》中的原则，在谈论诸如分布式、海量数据存储、云计算这样的大词之前，我需要的做的仍然是将代码写好，写的漂亮。也许是时候<span style="color: red;">回到本源</span>，再去重读下《设计模式》，重读下《重构》，既然我还在写java代码，那还是希望写的更好点。<br />
&nbsp;&nbsp; 另外，我现在喜欢上了clojure语言，并且正儿八经地找了本书好好学习，从源码和bytecode入手去理解它的实现。我为什么喜欢clojure?<br />
&nbsp;&nbsp; 首先，它是LISP的方言之一，LISP的优点它全有，LISP的缺点它能想办法避免。Clojure也有宏，也有quote，也有将procedure as data的能力，Clojure的数据结构都是immutable，此外还是persistence，避免了immutable数据大量拷贝带来的开销。Clojure的数据结构还天生是lazy的，表达能力上一个台阶。Clojure在语法上更多变化，某些程度上降低了括号的使用频率，这一点有利有弊。Clojure的内在原则是一致的，核心语法非常简单，它没有多种范式可供选择，因此没有scala那样复杂的类型系统，没有为了包容java程序员引入的OO模型（有替代品），使用clojure最好的方式是函数式地，但是它的扩展能力允许你去尝试各种范式。<br />
<br />
&nbsp;&nbsp; 其次，Clojure对并发的支持更符合一般程序员的理解，它只是将锁替换成了事务，利用STM去保存可变状态，但是却避免锁带来的缺点——死锁、活锁、竞争条件。它没有引入新的模型，这对习惯于用锁编程的同学来说，STM没有很大差异，你可以将它理解成内存型数据库。<br />
<br />
&nbsp;&nbsp; 第三，最重要的一点，Clojure是实现于JVM之上的，Java上的任何东西它都能直接利用，并且利用type hint之类的手段可以做到性能上没有损失。尽管Java语言有千般不是，但是寄生于整个平台之上的开源生态系统是任何其他社区都很难比拟的，放弃Java平台这个宝库，暂时还做不到。过去学习scheme，学习common lisp，更多的目的是开阔眼界，现在能实际地使用，还能有比这更幸福的事情吗？<br />
<br />
&nbsp;&nbsp; 下半年技术上想学习什么呢？除了clojure之外，我想去看下haskell，了解下什么是mond，除此之外，就是收收心将《算法导论》读完吧。另外，收起对awk和shell编程的偏见，好好熟悉下这两个工具，dirty and quickly的干活有时候还是很重要的。<br />
<br />
&nbsp;&nbsp;&nbsp; 我还是个典型的码农，喜欢写代码，喜欢尝试新东西，至少热情和好奇心还残存一些，那么就继续当好码农吧。<br />
&nbsp;<br /><img src ="http://www.blogjava.net/killme2008/aggbug/327027.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2010-07-24 20:51 <a href="http://www.blogjava.net/killme2008/archive/2010/07/24/327027.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>写代码与情绪</title><link>http://www.blogjava.net/killme2008/archive/2010/01/09/308808.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sat, 09 Jan 2010 03:09:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2010/01/09/308808.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/308808.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2010/01/09/308808.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/308808.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/308808.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 你不得不承认，写代码的效率跟周期性的情绪相关。以我为例，总存在着周期性的情绪波动，那段时间内基本不想写代码，上班就是收收邮件，看看网页，遗憾的是每个月都有那么几天。事实上，我认为在一天8小时的工作中，能有2、3个小时能达到忘我状态的工作，那已经是非常不错的事情。如果你是程序员，你肯定知道我说的忘我状态是什么。我在这里说的局限了，其实任何工作都可能进入这种忘我状态，这种状态下你的思维非常活跃，全神贯注，哪怕有人跟你说话你也会听而不闻，这种状态在你读一本非常有趣的小说的时候也会出现。这种状态下的你效率会非常高，例如我前段时间内就在一周内写了13000多行代码，600多个测试用例，为我们的系统重新实现了一个通信层。<br />
<br />
&nbsp;&nbsp; 看过很多讨论程序员工作效率的文章，据称研究表明要进入这种状态是至少要15分钟的时间，因此频繁地打断工作会阻碍你的工作效率，毕竟酝酿情绪也是需要时间的嘛。我有思考过怎么去尽量多地保持这种状态，排除那种对工作厌烦的情绪，毕竟拿着工资不干活心里还是会不安，况且看到周围那么多高效率的人，压力是难免的，让人担心的不是每天只有两个小时的高效工作，而是那段什么都不想干的时间。最后让我发现一个方法，说起来很简单，就是在出现这种低效状态的时候，强迫自己打开eclipse，而不是 firefox，强迫自己去写几行代码，如果这段时间内没有被其他事情打断，那么你还是容易进入一种不那么高效和愉悦的工作状态，至少能做到专心致志。当然，跟自己的情绪对抗可能不是世界上最困难的事情，也是其中之一，不过请你相信，只要你打开eclipse开始写代码并进入思考状态，那么你至少是可以暂时遗忘那些负面情绪的，甚至你的情绪可能因为解决了某个难题而高昂起来。<br />
&nbsp;&nbsp; 这个方法肯定不是什么新发现，我估计很多人会有同样的经验，今早在看《joel说软件》其中一篇文章《开火与运动》也谈到了同样的问题，joel也提到相同的经验：开了头就好。你不知道要费多少劲才能将一辆带齿轮的山地车运转起来，不过一旦它转起来之后，一切都跟骑一辆没有齿轮的自行车没什么两样。Joel还延伸了更多，开火的策略不仅仅是工作效率的问题，也是竞争策略，当你向敌人开火的时候，同时向敌人靠过去，活力会迫使敌人低下头而不能向你开火。竞争也是如此，压迫性的不断推出新东西让你的竞争对手疲于奔命，反而遗忘产品的根本性的目的，这些新东西可能只是为了替换过去不易用的东西，为什么不易用的东西在过去也会被推出来？那只是了为每天进步不断开火，让敌人忘记开火。<br />
&nbsp;&nbsp;&nbsp; <br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/308808.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2010-01-09 11:09 <a href="http://www.blogjava.net/killme2008/archive/2010/01/09/308808.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2009年小结</title><link>http://www.blogjava.net/killme2008/archive/2009/12/24/307191.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 24 Dec 2009 09:29:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2009/12/24/307191.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/307191.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2009/12/24/307191.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/307191.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/307191.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 趁着下班前的半小时，回顾下2009年我都干了什么，有什么收获，有什么不足。<br />
&nbsp;&nbsp;&nbsp; 09年最重要的事情是我的儿子出生了，小家伙的到来带给全家很多欢乐，烦恼也不少，比如半夜总要被吵醒，晚上的读书也没办法那么专心读了。此外，我还在学习怎么当爸爸，写过这篇《<a href="http://www.blogjava.net/killme2008/archive/2009/08/10/290592.html">新爸爸指南</a>》，记录下新生儿遇到的种种问题，逐渐经历自己生命的又一个阶段，这个历程很美好。<br />
&nbsp;&nbsp;&nbsp; 年初从广州公司辞职后，到了厦门一家创业公司，这不是一次很愉快的经历，回想起来我的问题不小。首先不该贸然地想去转换一个语言平台，写C++实在不是很好的编程的体验，乃至于我根本提不起工作热情；其次，心态不成熟，遇到问题和困扰的时候还是比较被动地解决，事实上完全没必要搞成这样，主动提出并且离开公司并不是什么丢人的事情。这次经历告诉我做决定的时候最好再慎重一点，毕竟自己不是一个人了，养家糊口是实实在在的责任。<br />
&nbsp;&nbsp; 在厦门的失败经历后，我投简历到了淘宝，尽管对于待遇并不是很满意，出于对淘宝的向往和有点理想主义的小情怀还是来到了杭州。刚来的时候，工作很顺利，生活比较糟糕，老婆孩子接到杭州后才好了点，生活比较有规律了。在淘宝，我所做的仍然是开发，写代码还是我的最爱，不过做的离业务的比较远，这正符合我的期望。负责的是一个消息中间件的开发，这个产品本身已经成型，并且应用在了淘宝的核心系统当中，现在每天通过这个MQ发送的消息量已经接近两亿，整个系统拥有数个集群，近30台机器。工作不单纯是开发，包括一些方案的设计和日常的维护工作，总体来讲还是很愉悦的体验。不足的地方，我仍然还是将自己视为一个纯粹的技术人员，对淘宝本身的业务、对其他系统的架构设计的了解都比较少，甚至于认识的人还是很局限，不过这个跟我的性格有关了。<br />
&nbsp;&nbsp;&nbsp; 技术上，这一年自我感觉没多大进步，除了将<a href="http://www.douban.com/subject/1148282/">sicp</a>读完之外(我准备再度几遍)，一些零零散散的技术书籍也看了不少，很少留下深刻的印象，比较有价值的是《<a href="http://www.douban.com/subject/3558788/">卓有成效的程序员</a>》和《<a href="http://www.douban.com/subject/1165791/">C++网络编</a>程》上下两卷。前者使我开始有意识地将自己一些重复性的工作自动化，提高自己的工作效率，后者让我对网络框架的设计模式有了相对全面的认识，也促进了我对Java网络编程的认识。今年也开发了个Java Memcached Client——<a href="http://code.google.com/p/xmemcached/">Xmemcached</a>,并在大家的鼓励下持续地在改进，总算有不少用户在用，没有枉费精力和时间，也算今年的一个小小自得的地方。这里要特别感谢下曹晓刚，没有他的鼓励和他们公司的使用，<a href="http://code.google.com/p/xmemcached/">xmc</a>还只是个人玩具。09年下半年又将不少精力放在了Erlang，过去学习是跟风，这次总算在项目中了有了个小应用，并且将《Erlang程序设计》和OTP设计原则来回读了几遍，对Erlang的兴趣越来越大，甚至于想是不是该去找份专职做Erlang的工作。对技术的学习，我还是没有一个明确的规划，任凭兴趣在几个领域里转来转去，这不是好现象，明年希望能更有计划和针对性地去学习，能跟自己的工作契合得更紧密一些。明年也希望能将《算法导论》读完，今年读了1/4，发现我的数学都已经抛到了Java国了，算法复杂度的推导总是看不懂，因此又去搞了几本数学书，从头再看看。<br />
<br />
&nbsp;&nbsp; 回顾完了，说说明年的愿望：<br />
技术上：读完《算法导论》，继续深入Erlang，探索Erlang在工作中的实际应用，加强对其他系统的了解以及大型网站构建方面的学习<br />
生活上：希望能全家一起去旅游一次，希望能将老爸老妈接过来玩一段时间。<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/307191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2009-12-24 17:29 <a href="http://www.blogjava.net/killme2008/archive/2009/12/24/307191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个优秀的MQ产品的特点。</title><link>http://www.blogjava.net/killme2008/archive/2009/09/18/295517.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 17 Sep 2009 16:09:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2009/09/18/295517.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/295517.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2009/09/18/295517.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/295517.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/295517.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; MQ在分布式系统中扮演着重要角色，异步的消息通信全要靠它，而异步通信正是提高系统伸缩性的不二良方。说说我认为的一个优秀的MQ产品需要具备的特征。<br />
<br />
&nbsp;&nbsp;&nbsp; 首先显然是高可用性，我们当然希望MQ能支撑7x24小时应用，而不是三天两头当机，我们要追求的是99.9%的可靠服务时间。要做到高可用性，显然我们需要做MQ的集群，一台当了，不影响整个集群的服务能力，这里涉及到告警、流控、消息的负载均衡、数据库的使用、测试的完备程度等等。<br />
<br />
&nbsp;&nbsp;&nbsp; 其次是消息存储的高可靠性。我们要保证100%不丢消息。要做到消息存储的高可靠性，不仅仅是MQ的责任，更涉及到硬件、操作系统、语言平台和数据库的一整套方案。许多号称可靠存储的MQ产品其实都不可靠，要知道，硬件错误是常态，如果在硬件错误的情况下还能保证消息的可靠存储这才是难题。这里可能需要用到特殊的存储硬件，特殊的数据库，分布式的数据存储，数据库的分库分表和同步等等。你要考虑消息存储在哪里，是文件系统，还是数据库，是本地文件，还是分布式文件，是搞主辅备份呢还是多主机写入等等。<br />
&nbsp;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; 第三是高可扩展性，MQ集群能很好地支持水平扩展，这就要求我们的节点之间最好不要有通信和数据同步。<br />
<br />
&nbsp;&nbsp;&nbsp; 第四是性能，性能是实现高可用性的前提，很难想象单机性能极差的MQ组成的集群能在高负载下幸免于难。性能因素跟采用的平台、语言、操作系统、代码质量、数据库、网络息息相关。MQ产品的核心其实是消息的存储，在保证存储安全的前提下如何保证和提高消息入队的效率是性能的关键因素。这里需要开发人员建立起性能观念，不需要你对一行行代码斤斤计较，但是你需要知道这样做会造成什么后果，有没有更好更快的方式，你怎么证明它更好更快。软件实现的性能是一方面，另一方面就是平台相关了，因为MQ本质上是IO密集型的系统，瓶颈在IO，如何优化网络IO、文件IO这需要专门的知识。性能另一个相关因素是消息的调度上，引入消息顺序和消息优先级，允许消息的延迟发送，都将增大消息发送调度的复杂性，如何保证高负载下的调度也是要特别注意的地方。<br />
<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; 第五，高可配置性和监控工具的完整，这是一个MQ产品容易忽略的地方。异步通信造成了查找问题的难度，不像同步调用那样有相对明确的时序关系。因此查找异步通信的异常是很困难的，这就需要MQ提供方便的DEBUG工具，查找分析日志的工具，查看消息生命周期的工具，查看系统间依赖关系的工具等等。可定制也是MQ产品非常重要的一方面，可方便地配置各类参数并在集群中同步，并且可动态调整各类参数，这将大大降低维护难度。<br />
<br />
&nbsp;&nbsp; 一些不成熟的想法，瞎侃。<br />
<br />
<br />
&nbsp;&nbsp;&nbsp; <br />
<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/295517.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2009-09-18 00:09 <a href="http://www.blogjava.net/killme2008/archive/2009/09/18/295517.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>