《分布式Java应用:基础与实践》样章、代码、纠错、补充

《分布式Java应用:基础与实践》一书中会存在一些错误的地方,以及一些尚未深入讲解的部分,在这篇文章里会提供纠错的信息以及补充的内容的文章的链接,关于书中错误的部分,还请各位海涵和帮助指正。

样章请从此下载:
http://bluedavy.me/book/booksample.pdf

随书的代码请从此处下载:
http://bluedavy.me/book/source.zip

《分布式Java应用:基础与实践》一书中会存在一些错误的地方,以及一些尚未深入讲解的部分,在这篇文章里会提供纠错的信息以及补充的内容的文章的链接,关于书中错误的部分,还请各位海涵和帮助指正。

样章请从此下载:
http://bluedavy.me/book/booksample.pdf

随书的代码请从此处下载:
http://bluedavy.me/book/source.zip

===============纠错===============

  • 第257页 — 感谢@偶系阿萌 的反馈
    “当节点较少时,有可能会出现这些机器节点是均匀分布的现象”
    应修改为:
    “当节点较少时,有可能会出现这些机器节点是不均匀分布的现象”

  • 第41页 — 感谢dongtalk的反馈
    “各厂商在实现JDK时通常会将符合Java语言规范的源代码编译为class文件的编译器”
    应修改为:
    “各厂商在实现JDK时通常会提供将符合Java语言规范的源代码编译为class文件的编译器”

  • 第46页 — 感谢yeshucheng的反馈
    图3.4中的“Booksrap ClassLoader”应为“BootStrap ClassLoader”

  • 第68页
    图3.13用来说明Sun hotspot中可用的GC,其中旧生代可用的GC的图有错误的地方,准确来说,在Sun Hotspot V 1.6.0中并行GC应该有两种:Parallel Mark Sweep和Parallel Compacting。

  • 第71页
    “只有经历过几次Minor GC仍然存活的对象,才放入旧生代中,这个在Minor GC中存活的次数在串行和ParNew方式时可通过-XX:MaxTenuringThreshold来设置,在Parallel Scavenge时则由Hotspot根据运行状况来决定。”
    这句话准确的应为:
    “只有经历过几次Minor GC仍然存活的对象,才放入旧生代中,这个在Minor GC中存活的最大次数在串行和ParNew方式时可通过-XX:MaxTenuringThreshold来设置,但并不代表对象一定会存活MaxTenuringThreshold次才会晋升到旧生代,串行和ParNew采用一个规则在每次Minor GC后计算可存活的次数,规则为累积每个age的对象所占的内存,一直计算到占用大小超过survivor space一半的age,如计算了所有的age,均未超过则以MaxTenuringThreshold为准,否则则以age为准,在Parallel Scavenge时默认情况下由Hotspot根据运行状况来决定。”。

  • 第73页
    “并行GC在基于SurvivorRatio值划分eden space和两块survivor space的方式上和串行GC一样。”
    修正为
    “默认情况下并行GC在基于SurvivorRatio值划分eden space和两块survivor space的方式上和串行GC一样,在开启-XX:+UseAdaptiveSizePolicy后则为每次Minor GC后动态计算eden、to的大小。”

  • 第73页
    “当在Eden Space上分配内存时Eden Space空间不足,JVM即触发Minor GC的执行,也可在程序中通过System.gc的方式(可通过在启动参数中增加-XX:+DisableExplicitGC来避免程序中调用System.gc触发GC)来触发。”
    修正为
    “当在Eden Space上分配内存时Eden Space空间不足,JVM即触发Minor GC的执行,当旧生代采用并行GC时,也可在程序中通过System.gc的方式(可通过在启动参数中增加-XX:+DisableExplicitGC来避免程序中调用System.gc触发GC)来触发。”

  • 第77页 — 感谢dongtalk的反馈
    “首先将代空间划分为并行线程个数的区域(regions)”
    应修改为
    “首先将旧生代空间划分为并行线程个数的区域(regions)”

  • 第77页
    并行应为并行Compacting,书中没有介绍Parallel Mark Sweep,在后续的blog中会进行完善;

  • 第78页
    这句话是错误的:“并行是server级别机器(非32位Windows)上默认采用的GC方式,也可通过-XX:+UseParallelGC或-XX:+UseParallelOldGC来强制指定。”
    修正为
    “并行是server级别机器(非32位Windows)上默认采用的GC方式,可通过-XX:+UseParallelGC来指定使用Parallel Mark Sweep,通过-XX:+UseParallelOldGC来指定使用Parallel Compacting。”;

  • 第80页
    “CMS GC触发的条件为旧生代已使用的空间达到设定的CMSInitiatingOccupancyFraction百分比,例如默认CMSInitiatingOccupancyFraction为68%,如旧生代空间为1 000MB,那么当旧生代已使用的空间达到680MB时,CMS GC即开始执行;”
    修正为
    ”CMS GC触发的条件为旧生代已使用的空间达到设定的CMSInitiatingOccupancyFraction百分比或持久代已使用的空间达到设定的CMSInitiatingPermOccupancyFraction百分比,例如在JDK 6.0中默认值为92,如旧生代空间为1000MB,那么当旧生代已使用的空间达到920MB时,CMS GC即开始执行;“

  • 第80页
    ”持久代的GC也可采用CMS方式,方式为设置以下参数:-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled。“
    修正为
    ”持久代的GC也可采用CMS方式,方式为设置此参数:-XX:+CMSClassUnloadingEnabled。“;

  • 第87页
    表3.1中:Server模式下旧生代和持久代GC方式准确的说应为Parallel Mark Sweep;

  • 第87页
    表3.2中:”-XX:+UseParallelGC 并行回收GC 并行GC“准确的说应为:”-XX:+UseParallelGC 并行回收GC Parallel Mark Sweep GC“;

  • 第87页
    表3.2中:“-XX:+UseParallelOldGC 并行回收GC 并行GC”准确的说应为:“-XX:+UseParallelOldGC 并行回收GC 并行Compacting GC”;

  • 第99页 — 感谢liang xie的反馈
    “如果要分析jvm堆dumap文件”
    应为:
    “如果要分析jvm堆dump文件”

  • 第112页 — 感谢liang xie的反馈
    “JDK常用package中的常用类进行分析……….,对根据需求合理地选择类会有一定的帮助”产生了重复。

  • 第113页 — 感谢libai的反馈
    ”super调用的为AbstractList的默认构造器方法….ArrayList采用的是数组的方式来存放对象“
    这段话出现了重复。

  • 第183页 — 感谢liang xie的反馈
    “在没有安装pidstat或内核版本为2.6.20以后”
    应为
    “在没有安装pidstat或内核版本为2.6.20之前”

    ===============内容补充===============
    1、说说MaxTenuringThreshold这个参数
    2、Sun JDK OOM

  • 《分布式Java应用:基础与实践》序

    分布式Java应用需要开发人员掌握较多的知识点,通常分布式Java应用的场景还会对性能、可用性以及可伸缩有较高的要求,而这也就意味着开发人员需要掌握更多的知识点。我刚进淘宝的时候,曾经一直苦恼对于一个这样的分布式Java应用,我到底需要学习些什么。

    随着在淘宝工作的不断开展,我的眼前终于慢慢呈现了高性能、高可用以及可伸缩的Java应用所需知识点的全景,这张知识点的全景图现在已经演变成了本书的目录。当看到自己整理出的知识点的全景图时,很惊讶地发现其中有些知识点其实是我之前已经学习过的,但到了真正需要使用的时候有些是完全遗忘了,有些则是在使用时碰到了很多的问题,从这里我看到,当学习到的知识不去经过实践检验时,这些知识就不算真正属于自己。

    分布式Java应用需要开发人员掌握较多的知识点,通常分布式Java应用的场景还会对性能、可用性以及可伸缩有较高的要求,而这也就意味着开发人员需要掌握更多的知识点。我刚进淘宝的时候,曾经一直苦恼对于一个这样的分布式Java应用,我到底需要学习些什么。

    随着在淘宝工作的不断开展,我的眼前终于慢慢呈现了高性能、高可用以及可伸缩的Java应用所需知识点的全景,这张知识点的全景图现在已经演变成了本书的目录。当看到自己整理出的知识点的全景图时,很惊讶地发现其中有些知识点其实是我之前已经学习过的,但到了真正需要使用的时候有些是完全遗忘了,有些则是在使用时碰到了很多的问题,从这里我看到,当学习到的知识不去经过实践检验时,这些知识就不算真正属于自己。

    幸运的是,在淘宝我得到了分布式Java应用的绝佳实践机会,于是所学习到的网络通信、高性能、高并发、高可用以及可伸缩的一些知识,有机会在实践中得到验证。正是这样的机会,让我对这些知识点有了更深刻本质的理解,并能将其中的一些知识真正吸收,变为自己的经验,所以我一个很真切的体会就是:实践是最好的成长。

    经历了这段艰难的成长,自己也希望不要忘却在这个过程中的收获,胡适先生曾说:“发表是最好的记忆”(这句话也长期放在台湾技术作家侯捷老师的网站上),于是萌生了写这本书的想法,一方面想梳理自己通过实践所获得的成长,另一方面也希望与正在从事分布式Java应用的技术人员分享一些实践的心得,同时给将要或打算从事分布式Java应用的技术人员提供一些参考。

    从2008年11月确认要写这本书,到2010年5月完成这本书,历时一年半,过程可谓波折不断,前3个月的写作一帆风顺,顺利完成了第一章和第三章的撰写。

    到了2009年3月底后,由于投入到了《OSGi原理与最佳实践》一书的编写中,停顿了将近3个月的时间。

    2009年8月重新开始了这本书的撰写,在2009年10月下旬前按计划完成了第二章、第四章和第五章,但随后由于项目进入冲刺阶段、忙于校园招聘以及迁居等事情,再度停顿了本书的撰写。

    记得在刚开始写这本书的时候,周筠老师就告诉我,要坚持写,就算每天只写一点也是好的,千万不要停顿!确实如此,在停顿了两次后,就很难再找到继续写这本书的动力和激情了,得感谢徐定翔编辑在之后给我的鼓励和督促,终于在2010年3月我又开始了写作。待完成了第六章的编写后,由于剩下的第七章中的部分知识点和自己的工作联系不是非常紧密,导致了不断的拖延,这时周筠老师和徐定翔编辑给我的一个建议起到了关键作用,就是先放下第七章,先做之前完成的六章的定稿。

    回到自己熟悉的前6章,终于再次有了写作的动力,随着工作中对自己书中所涉及的知识点的不断实践,此时再回头看自己半年前甚至一年前写的初稿时,发现其中有不少的错误以及条理不够清晰的地方,于是进行了大刀阔斧的改动,实践所获得的积累此时起了巨大的推动作用,这6章定稿除了第三章以外的章节,完成得较为顺利。

    6章定稿提交后,由白爱萍编辑带领的编辑小组再次对书稿进行细致的“田间管理”,给出了非常多的修改建议,印象中几乎平均每页都至少有两到三处需要修改的地方,正是他们的认真和专业,使得定稿中很多语言上以及技术上的错误得以纠正,感谢武汉博文的编辑们。

    最后,在第三章定稿的修改过程中,得到了同事莫枢(http://rednaxelafx.javaeye.com)非常多的建议和帮助,在此非常感谢他的支持。

    全书在编写的过程中,初稿、定稿也提交给了一些业内专家帮忙评审,主要有:郑晖、霍炬、曹祺、刘力祥,他们给出的很多意见一方面纠正了书中一些技术上的错误,另一方面也让书的条理性更加顺畅,衷心感谢他们的辛勤付出。尤其郑晖老师,在承诺为本书写推荐序时,又花时间把全稿通读一遍,他的耐心和专业精神,让我感佩不已。

    书的撰写过程是如此漫长,每天晚上下班后、周末、假期,甚至过年期间,都成了写书的时间,感谢家人给我的包容、支持和理解,最要感谢的是我明年春节就将迎娶的准老婆:宗伟,感谢你忍受了我不断忘记买家里需要的各种东西,感谢你独立完成了新家的装修,更要感谢你允许我这么多的周末、假日都无法陪伴你,没有你的支持和鼓励,这本书是无法完成的。

    回顾整个编写过程,从开始编写,到提交完全部终稿,经历了15个月的时间,写作时间大概为11个月,经过这11个月对这些知识点的不断实践、回顾和总结,它们在我的脑海中刻下了深深的烙印,的确,发表是最好的记忆。

    林昊

    2010年5月20日晚于杭州家中

    《分布式Java应用:基础与实践》的封面和目录

    封面:

    目录请见全文。

    封面:

    目录如下:
    第1章 分布式Java应用 1
    1.1 基于消息方式实现系统间的通信 3
    1.1.1 基于Java自身技术实现消息方式的系统间通信 3
    1.1.2 基于开源框架实现消息方式的系统间通信 10
    1.2 基于远程调用方式实现系统间的通信 14
    1.2.1 基于Java自身技术实现远程调用方式的系统间通信 14
    1.2.2 基于开源框架实现远程调用方式的系统间通信 17
    第2章 大型分布式Java应用与SOA 23
    2.1 基于SCA实现SOA平台 26
    2.2 基于ESB实现SOA平台 29
    2.3 基于Tuscany实现SOA平台 30
    2.4 基于Mule实现SOA平台 34
    第3章 深入理解JVM 39
    3.1 Java代码的执行机制 40
    3.1.1 Java源码编译机制 41
    3.1.2 类加载机制 44
    3.1.3 类执行机制 49
    3.2 JVM内存管理 63
    3.2.1 内存空间 63
    3.2.2 内存分配 65
    3.2.3 内存回收 66
    3.2.4 JVM内存状况查看方法和分析工具 92
    3.3 JVM线程资源同步及交互机制 100
    3.3.1 线程资源同步机制 100
    3.3.2 线程交互机制 104
    3.3.3 线程状态及分析 105
    第4章 分布式应用与Sun JDK类库 111
    4.1 集合包 112
    4.1.1 ArrayList 113
    4.1.2 LinkedList 116
    4.1.3 Vector 117
    4.1.4 Stack 118
    4.1.5 HashSet 119
    4.1.6 TreeSet 120
    4.1.7 HashMap 120
    4.1.8 TreeMap 123
    4.1.9 性能测试 124
    4.1.10 小结 138
    4.2 并发包(java.util.concurrent) 138
    4.2.1 ConcurrentHashMap 139
    4.2.2 CopyOnWriteArrayList 145
    4.2.3 CopyOnWriteArraySet 149
    4.2.4 ArrayBlockingQueue 149
    4.2.5 AtomicInteger 151
    4.2.6 ThreadPoolExecutor 153
    4.2.7 Executors 157
    4.2.8 FutureTask 158
    4.2.9 Semaphore 161
    4.2.10 CountDownLatch 162
    4.2.11 CyclicBarrier 163
    4.2.12 ReentrantLock 164
    4.2.13 Condition 164
    4.2.14 ReentrantReadWriteLock 165
    4.3 序列化/反序列化 167
    4.3.1 序列化 167
    4.3.2 反序列化 170
    第5章 性能调优 173
    5.1 寻找性能瓶颈 175
    5.1.1 CPU消耗分析 175
    5.1.2 文件IO消耗分析 182
    5.1.3 网络IO消耗分析 186
    5.1.4 内存消耗分析 187
    5.1.5 程序执行慢原因分析 191
    5.2 调优 192
    5.2.1 JVM调优 192
    5.2.2 程序调优 202
    5.2.5 对于资源消耗不多,但程序执行慢的情况 214
    第6章 构建高可用的系统 227
    6.1 避免系统中出现单点 228
    6.1.1 负载均衡技术 228
    6.1.2 热备 236
    6.2 提高应用自身的可用性 238
    6.2.1 尽可能地避免故障 239
    6.2.2 及时发现故障 246
    6.2.3 及时处理故障 248
    6.2.4 访问量及数据量不断上涨的应对策略 249
    第7章 构建可伸缩的系统 251
    7.1 垂直伸缩 252
    7.1.1 支撑高访问量 252
    7.1.2 支撑大数据量 254
    7.1.3 提升计算能力 254
    7.2 水平伸缩 254
    7.2.1 支撑高访问量 254
    7.2.2 支撑大数据量 264
    7.2.3 提升计算能力 266

    大型应用与SOA

    摘自我那本6月份要上市的,但目前名字还没完全确定的书,由于书中涵盖的更多的为构建高性能分布式Java应用所需要的基础知识,也许改成了《通往高性能分布式Java应用之路》,摘取的这段内容主要是阐述下为什么大型应用需要SOA,以及eBay的例子。

    摘自我那本6月份要上市的,但目前名字还没完全确定的书,由于书中涵盖的更多的为构建高性能分布式Java应用所需要的基础知识,也许改成了《通往高性能分布式Java应用之路》,摘取的这段内容主要是阐述下为什么大型应用需要SOA,以及eBay的例子。

    当应用获得用户的认可后,会不断的发展,以豆瓣 为例,早期豆瓣只有书评的功能,随着大家对豆瓣的认可以及使用的用户越来越多,豆瓣发展到了今天的豆瓣社区、豆瓣读书、豆瓣电影和豆瓣音乐等功能,这四个大块的功能有各自的特色,但又有很多可公用的业务逻辑,例如用户信息、评价等,如这四个系统都维护自己的用户信息读写或评价业务逻辑,此时会造成的问题一方面是当需要修改评价逻辑时,所有系统有可能都需要修改,当需要修改用户信息的读取方式时,也是所有系统都需要修改,会相当的复杂;另一方面是每个系统上都有很多种类的业务逻辑,这就像在一个小超市中,一个人负责收银、清洁、摆货、咨询等各种各样的事情,当来超市买东西的人多了后,这个人就没有办法再负责这么多的事情了,系统也同样如此。

    第一个现象为系统多元化后带来的问题,可采用的方法为对共用逻辑的部分进行抽象,形成多个按领域划分的共用业务逻辑系统;第二个现象为系统访问量、数据量上涨后带来的典型问题,当超市的顾客不断增加时,通常超市会采取分工的方式来更好的服务顾客,同样,对于系统而言,也会采取拆分系统的方式来解决。

    在构建了共用业务逻辑系统和拆分系统后,最明显的问题就是系统之间如何进行交互,如不做控制,会出现的现象是多个系统之间出现多种多样的交互方式,Http、TCP/IP+NIO、Hessian、RMI、Webservice等;同步、异步等方式可能都会出现,这会导致开发人员在每访问一个共用业务逻辑系统或拆分出来的系统后,都有可能要学习不同的交互方式;同时也会造成各开发团队重复造轮子,提供不同交互方式用的框架,这对于应用的性能、可用性而言也带来了极大的挑战。

    对于上面的问题,很容易想到的一种解决方法就是统一交互的方式,SOA无疑是实现这种方式的首选指导思想。
    SOA全称为面向服务架构,其强调系统之间以标准的服务方式进行交互,各系统可采用不同的语言、不同的框架进行实现,需要交互时则全部通过服务的方式来进行。

    eBay也实现了一个SOA平台来支撑其业务的多元化发展 ,eBay认为SOA能给其带来的最大好处为提升业务的重用以及灵敏性,SOA平台除了要实现统一的交互外,还会碰到其他多方面的挑战,来看看eBay眼中SOA平台会带来哪些挑战:
     服务多级调用带来的延时;
    当出现一个功能需要调用服务A,服务A又需要调用服务B,服务B又需要调用服务C时,这个时候会带来大幅度的延时,解决延时需要做到的是高性能的服务交互,以及完善的服务调用过程控制,例如当调用在服务B执行时已超时,那么则没必要继续调用服务C,而应该直接抛出超时异常给调用端。
     调试/跟踪困难
    例如当调用服务A,服务A报错时,调用者通常会去找服务A的开发人员来解决,服务A的开发人员去查问题,有可能会发现是由于服务B报错,服务B的开发人员去查,有可能发现是网络有问题,在这样的情况下,就会导致出现问题时调试/跟踪非常困难。
     更高的安全/监测的要求
    在未拆分系统时,对系统的安全和监测都只需要在一个系统上做即可,当拆分后,就必须在每个系统上都有相应的安全控制以及监测。
     现有应用移植
    这一点是SOA平台在推广时的大挑战,需要移植的应用上的功能越多,就需要花费越多的时间来完成移植。
     QoS的支持
    每个服务提供者能够支撑的访问量必然是有限的,因此设定服务的QoS非常重要,并且应尽可能采取一系列的措施来保障QoS,例如流量控制、机器资源分配等。
     高可用和高度可伸缩
    这是互联网应用必须做到的两点,SOA平台承担了所有服务的交互,因此其可用性以及伸缩性就更会影响巨大。
     多版本和依赖管理
    随着服务不断发展,以及使用面不断扩大,服务多版本的存在会成为一个大的需求,同时由于服务众多,这些服务的依赖关系也需要管理起来,以便在升级时能进行相应的安排。

    eBay根据这些挑战自行实现了一个SOA平台,这个SOA平台主要包含了以下几点:
     高性能、可扩展的轻量级框架;
     监测、安全控制、流量控制的支持;
     服务注册和服务仓库;
     开发工具;

    在推行SOA平台时,eBay采取的为按领域驱动的方式划分服务,同时不断的培训开发人员,以便让开发人员明确服务化后和之前单系统的方式的区别,例如可能会产生网络通信错误、超时等。

    综合上面的内容来看,对于一个大型应用中的SOA平台,至少应包含以下几点功能:
     统一的服务交互方式,并可实现和现有应用的无缝集成;
     提供调试/跟踪的支持;
     依赖管理;
     高性能以及高可用。

    在实现SOA时,可参考的标准或概念有SCA、ESB,同时业界也有一些实现了SCA、ESB的框架,下面就来具体的看看SCA、ESB、SCA框架以及ESB框架。