S4:分布式流计算平台

 


S4:分布式流计算平台

前段时间考虑监控统计面临的两个问题,一是Key太多的问题。客户端输出日志的埋点程序可以通过LRU Map缓解,但是服务端就比较麻烦。对于某些以MapReduce模式实现的日志分析框架,当一个应用的key太多的时候,每个分析节点的内存中维护的 Map都会变的非常巨大,并且当一个应用的日志量太大的时候,会造成每个分析节点都在分析同一个应用的日志,而阻塞其他的应用。另一个问题是流控阀值的调 节。当新加入一个流控的时候,没人知道阀值应该设成多少比较合适,只能根据监控数据逐步调整。调整一次的周期比较漫长;即使阀值可以动态调整,每次调整仍 然需要许多人肉操作和观察,等待。。。而最近看了下S4的论文,发现S4可以完美的解决这两个问题。首先S4根据keyed attribute的取值来分发/路由事件到不同的节点、不同的处理单元,这样可以非常方便的做应用以及更小粒度的隔离,完全可以做到局部问题不影响全 局,解决了key太多的问题。其次,在S4架构之上实现的在线参数调优系统,可以通过重建线上流量,应用不同参数,比较统计结果,自动找出最优参数再反设 回线上系统,这种实现可以说是参数调优最理想的做法了。

于是翻译了S4的整篇论文和大家分享。

原文地址:http://labs.yahoo.com/files/KDCloud 2010 S4.pdf

开源地址:http://s4.io/

下面是前两节的翻译:(英文好的同学请略过)

概要

S4是一个通用的,分布式的,可扩展的,分区容错的,可插拔的平台。开发者可以很容易的在其上开发面向无界不间断流数据处理的应用。编键的数据事件 被分类路由到处理单元(Processing Elements,PEs),处理单元消费这些事件,做如下事情之一或全部:(1)发出一个或多个可能被其他PE处理的事件。(2)发布结果。这种架构类 似提供了封装和地址透明语义的Actor模式,因此允许应用在大规模并发的同时暴露简单的编程接口给应用开发者。在这篇论文里,我们将勾画S4的架构细 节,描述各种各样的应用,包括实际中的部署。我们的设计主要由大规模应用在生产环境中的数据采集和机器学习所驱动。我们展示了S4设计令人惊奇的灵活性, 使其运行在构筑于普通硬件之上的大规模集群中。

关键词:编程模型(programming model); 复杂事件处理(complex event processing);并发编程(concurrent programming); 数据处理(data processing); 分布式编程(distributed programming); map-reduce; 中间件(middleware); 并行编程(parallel programming); 实时搜索(real-time search); 软件设计(software design); 流计算(stream computing)

一、介绍

S4(简单可扩展流系统的首字母简称:Simple Scalable Streaming System)是一个受Map-Reduce模式启发的分布式流处理引擎。我们设计这个引擎是为了解决使用数据采集和机器学习算法的搜索应用环境中的现实 问题。当前的商用搜索引擎,像Google、Bing和Yahoo!,典型的做法是在用户查询响应中提供结构化的Web结果的同时插入基于流量的点击付费 模式的文本广告。为了在页面上的最佳位置展现最相关的广告,科学家开发了算法来动态估算在给定上下文中一个广告被点击的可能性。上下文可能包括用户偏好, 地理位置,之前的查询,之前的点击等等。一个主搜索引擎可能每秒钟处理成千上万次查询,每个页面都可能会包含多个广告。为了处理用户反馈,我们开发了 S4,一个低延迟,可扩展的流处理引擎。

为了便于在线实验算法,我们设想一种既适合研究又适合生产环境的架构。研究的主要需求是要具有将算法快速发布到特定领域的高度灵活性。这使得以最小 的开销和支持在实际流量中测试在线算法成为可能。生产环境的主要需求是可扩展性(以最小的代价通过增加更多的机器来提高吞吐量的能力)和高可用性(在存在 系统故障的情况下不需要人工介入仍然能持续提供服务的能力)。我们考虑过扩展开源的Hadoop平台来支持无界流计算但是我们很快认识到Hadoop平台 是为批处理做了高度优化的。MapReduce系统典型的是通过调度批量任务操作静态数据。而在流计算中的典型范式是有一个在我们无法控制的数据比率之上 的事件流流入系统中。处理系统必须赶得上事件流量,或者通过消减事件优雅的降级,这通常被称为负载分流(load shedding)。流处理的这一模式决定了要和批处理使用非常不同的架构。试图建造一个既适合流计算又适合批处理的通用平台结果可能会是一个高度复杂的 系统,并且最终可能都不是两者最理想的实现。一个作为Hadoop扩展构建的MapReduce在线架构的例子可以在[3]中找到。

MapReduce编程模型可以很容易的将多个通用批数据处理任务和操作在大规模集群上并行化,而不必担心像failover管理之类的系统问题。 MapReduce编程模型在Hadoop之类的开源软件浪潮推动下加速被采用,并且从实验室走向了Web搜索、欺诈检测、在线约会等各种各样的实际应用 中。但是通用的分布式流计算软件却没有类似的发展趋势。虽然已经有各种各样的工程和商业引擎([6] ,[7] ,[8] ,[9] ,[10]),但是它们的使用仍然局限于高度专业化的应用。Amini et. al.[7]给出了各种系统的评论。

实时搜索、高频交易、社交网络等新应用的出现将传统数据处理系统所能做的推向了极限[11]。对能够在高数据流量下操作,处理巨量数据的高可扩展流 计算解决方案有了一个清晰的需求。例如,为了个性化搜索广告,我们需要实时处理来自几百万唯一用户每秒成千上万次的查询,典型的包括分析用户最近活动如查 询、点击等。我们发现用户的会话特征可以提高广告相关性预测模型的精确度。这个性能改善用来提高显示给每个特定用户的广告的相关性[12]。S4致力于一 个通用的分布式流计算平台的需求。

值得注意的是,某些现实世界的系统实现了这样一种流处理策略:将输入数据分隔成固定大小的片段,再由MapReduce平台处理。这种方式的缺点在 于其延迟与数据片段的长度加上分隔片段、初始化处理任务的附加开销成正比。小的分段会降低延迟,增加附加开销,并且使分段间的依赖管理更加复杂(例如一个 分段可能会需要前一个分段的信息)。反之,大的分段会增加延迟。最优化的分段大小取决于具体应用。与其尝试将方形的木钉嵌入圆形的孔,我们决定探索一种简 单的可以操作实时数据流的编程模型。我们的设计目标是:

  • 提供一种简单的编程接口来处理数据流
  • 设计一个可以在普通硬件之上可扩展的高可用集群。
  • 通过在每个处理节点使用本地内存,避免磁盘I/O瓶颈达到最小化延迟
  • 使用一个去中心的,对等架构;所有节点提供相同的功能和职责。没有担负特殊责任的中心节点。这大大简化了部署和维护。
  • 使用可插拔的架构,使设计尽可能的即通用又可定制化。
  • 友好的设计理念,易于编程,具有灵活的弹性

为了简化S4初始的设计,我们作了如下假设:

  • 不完全的failover是可以接受的。在一个服务器故障时,处理自动的转移到稳定的服务器。存储在本地内存中的处理状态在交接中会丢失。(新的处理)状态会根据输入数据流重新生成。下游系统必须能够优雅降级。
  • 不会有节点从正在运行的集群中增加或移除。

我们发觉这些要求对于我们大部分的应用都可以接受。将来我们计划为无法接受这些限制的应用找出解决方案

允许在常规硬件之上进行分布式操作,和避免集群内使用共享内存这两个目标引导我们为S4采用Actor模式[1]。这种模式有一个简单的原语集并且 在工业级规模下的各种框架使用中被证明是有效的[13]。在S4中,通过处理单元(Processing Elements (PEs))进行计算,消息在处理单元间以数据事件的形式传送。每个PE的状态对其他PE不可访问。PE之间唯一的交互模式就是发出事件和消费事件。框架 提供了路由事件到恰当的PE和创建新PE实例的能力。这方面的设计提供了封装和地址透明的特性。

S4的设计和IBM的流处理核心(SPC)中间件有很多相同的特性。两个系统都是为了大数据量设计的。都具有使用用户定义的操作在持续数据流上采集 信息的能力。两者主要的区别在架构的设计上:SPA的设计源于一种订阅模式,而S4的设计是源于MapReduce和Actor模式的结合。我们相信因为 其对等的结构,S4的设计达到了非常高程度的简单性。集群中的所有节点都是等同的,没有中心控制。就像我们将要描述的,这得益于 ZooKeeper[14],一个简单优雅的集群管理服务,可以给数据中心的多个系统共用。

—-

全文翻译放在这里:

内网://10.13.40.158/share/Distributed Stream Computing Platform(S4)_cn.pdf

外网:百度文库:http://wenku.baidu.com/view/fb2d2c22482fb4daa58d4b7d.html

最近关注过的一些项目

最近因为空闲时间有一些,所以去看了不少开源项目,大部分东西如果看过不记录下来,其实还是相当于没看,所以想想还是有必要摘要记录一下。

首先是去了解了zookeeper 这 个项目,基于paxos算法的分布式服务组件,同事对此有非常深入的研究和介绍,具体可以看我们的团队Blog。令我感慨的是这么一个非常难以理解的算 法,却用一个简单的树状目录模型表达出来,并且在这个模型的基础上衍生出种种应用:集群感知、分布式锁、分布式队列、分布式并发原语等等,具体可以看文档 给出的recipes 。 在实现这些应用的时候,突出强调的是避免网络风暴,例如分布式锁的实现,竞争创建子节点,节点序列号最小的获取锁,其他节点等待,但是等待在什么条件上是 有讲究的,如果所有节点都等待最小节点的删除事件,那么当最小节点释放锁的时候,就需要广播消息给所有其他等待的节点;换一个思路,如果每个等待节点只是 等待比它序列号小的节点上,那么就可以避免这种广播风暴,变成一个顺序唤醒的过程。因此尽管有了zookeeper帮助实现分布式这些服务,但是要实现好 仍然有一定难度,具体可以参考官方例子。我本来萌生了基于zookeeper实现一套封装好的类似j.u.c的服务框架,后来在邮件列表发现已经有人搞了 这么一个基础类库放在github上:https://github.com/openUtility/menagerie 。不过我没有继续深入了,有兴趣的朋友可以瞧瞧。

然后又去看了我们淘宝开源的TimeTunnel 。 TimeTunnel你可以理解成一个消息中间件,它整个设计跟我们的产品相当接近,但是两者的目的完全不同,tt强调的是高吞吐量,而notify强调 的则是可靠性。TT的通讯层直接采用Facebook的thrift,并且利用zookeeper做集群管理和路由。TT的代码质量很好,有兴趣可以拉出 来看一下,并且对zookeeper的应用也是一个典型的案例。TT在高可用性上的方案也很有特色,所有的服务器节点形成一个环,两两相互主辅备份,一个 节点挂了,后续节点仍然可以提供服务直到主节点回来,有点类似一致性哈希的概念。节点的主从关系和顺序也是通过zookeeper保证。消息顺序的实现是 通过称为router的路由到固定节点做传输,router默认是策略不是固定而是RR。TT的数据存储优先放在内存,并设置了一个内存状况监视的组件, 当发现内存放不下的时候,swap到磁盘文件缓存,实现类似内存换页的功能。正常情况数据都应该在内存,当然如果可靠级别要求高的话可以先存磁盘再传输。 TT目前仍然还是比较适合传输日志这样的文本增量数据,并且提供了TailFile这样的python脚本帮你做这个事情,这个脚本可以通过 checkpoint做断点续传。在学习这个项目的时候,发现文档有很大问题,要么错误,要么遗漏,并且代码也不是最新的,我估计开源出来外面的人用的还 不太多,希望慢慢能搞的更好一些。

跟TT类似,另一个追求高吞吐量的MQ是linkedin开源的kafka 。 Kafka就跟这个名字一样,设计非常独特。首先,kafka的开发者们认为不需要在内存里缓存什么数据,操作系统的文件缓存已经足够完善和强大,只要你 不搞随机写,顺序读写的性能是非常高效的。kafka的数据只会顺序append,数据的删除策略是累积到一定程度或者超过一定时间再删除。Kafka另 一个独特的地方是将消费者信息保存在客户端而不是MQ服务器,这样服务器就不用记录消息的投递过程,每个客户端都自己知道自己下一次应该从什么地方什么位 置读取消息,消息的投递过程也是采用客户端主动pull的模型,这样大大减轻了服务器的负担。Kafka还强调减少数据的序列化和拷贝开销,它会将一些消 息组织成Message Set做批量存储和发送,并且客户端在pull数据的时候,尽量以zero-copy的方式传输,利用sendfile(对应java里的 FileChannel.transferTo/transferFrom)这样的高级IO函数来减少拷贝开销。可见,kafka是一个精心设计,特定于 某些应用的MQ系统,这种偏向特定领域的MQ系统我估计会越来越多,垂直化的产品策略值的考虑。

在此期间,我还重新去看了activemq和hornetq的存储实现,从实现上大家都大同小异,append log + data file的模式。Activemq采用异步队列写来提高吞吐量,而Hornetq干脆就直接利用JNI调用原生aio来实现高性能。在搜索Java的 aio实现的时候,碰巧发现Mina的沙箱里有个aioj的实现,源码在:https://svn.apache.org/repos/asf/mina/sandbox/mheath/aioj/ 。我测试了完全可用,也尝试改造我们的磁盘存储组件,可惜提升不多,估计不从整个设计上调整服务器,不大可能从aio上获益。

最近也重新看起了clojure的一些开源项目,clojure的开源资源在github上也非常丰富,有待挖掘,下次有机会再尝试介绍一二。

ConcurrentHaspLRUHashMap实现初探

ConcurrentHaspLRUHashMap实现初探

一、      关于LRU。

LRU 即 Least  Rencetly  Used(最近最少使用)缓存替换策略。在任何LRU算法中,它必定有以下两个策略组成:

1、  退化 策略。根据访问情况,对节点按热度进行排序(hot->cold),以便决定哪些节点是热节点(hot)的,哪些节点是冷节点(cold)的。这个退化的策略,一般按以下两种方式去处理:

l  非集中式。即每命中一次就进行退化操作。

非集中式的退化操作,往往由双向链表的方式去实现。每次命中之后就移动命中节点在链表中的位置。(位置靠前的就是hot的数据)。当然,复杂的策略中,有用queue数组进行hot分级等。

l  集中式。定期去进行退化操作。

在集中式的退化操作,常用的策略是:每次命中之后,记录一个时间戳、定时器时间点等等参数。由一个线程去扫描,定期清除老数据。

2、  清除 策略。即去掉那些cold的数据。

l  替换。这个在操作系统缓存中应该是一个常用的做法。

l  删除。删除掉数据,以腾出空间放新的数据。(因为内存是有限的)

二、      ConcurrentHashMap与LinkedHashMap

在JAVA中,LRU的原生实现是JDK中LinkedHashMap。LinkedHashMap继承自HashMap

【实现原理】 简单说就是HashMap的每个节点做一个双向链表。每次访问这个节点,就把该节点移动到双向链表的头部。满了以后,就从链表的尾部删除。但是LinkedHashMap并是非线程安全(其实现中,双向链表的操作是没有任何线程安全的措施的)。

对于线程安全的HashMap,在JDK中有ConcurrentHashMap原生支持。

【实现原理】采用锁分离机制,把一个HashMap分成多个segement,对每个segement的写操作上锁。同时,他的get()操作是没 有锁的,具体思想就是把每个hash槽中的链表的头节点置成final的。对hash槽中链表操作,只能从头部去处理。这样就不会有读不一致的情况出现。 这个原理,最好还是看源码,比较清晰。

三、      ConcurrentLRUHashMap的实现方式一:直接包装LinkedHashMap。

即,在LinkedHashMap外层全部加锁。

典型代码:

public V get(Object key) {
lock.lock();
try {
return super.get(key);
}
finally {
lock.unlock();
}
}

对LinkedHashMap做包装,所有访问都是带锁委托给LinkedHashMap。这样虽然解决了多线程安全问题。但是,是以严重的性能消耗为代价代价。

四、      ConcurrentLRUHashMap实现方式二:直接改造ConcurrentHashMap

该方案主要是重写ConcurrentHashMap。

1、  给每个Entry加一个timestamp。

2、  每次get命中的话,修改时间戳。

3、  定时统计整个map的总量,如果总量大于某个阈值,则deadline往后推。同时,在put的时候,检查hash槽里面每个节点的时间戳,如果已经过期,就删除掉过期节点。

上述做法,删除操作分布在每次put操作中。所以,删除效率比较高。但是,由于时间片不可控,最终将导致内存爆炸的情况出现。

请看下面一种场景:

横坐标表示一个时间片。面积表示这个时间片里面节点数量。

假定节点命中率为50%(命中后,更新到命中时刻的时间片),每个时间片写入10条新数据。

我们可以在运行过程中,每个时间片定义一个更新一次deadline。在put数据的时候,我们可以检查hash槽中Entry是否过期,如果已经过期,则删掉过期数据。

对于deadline的计算,我们可以设置三个阈值(a

a)         totalCount

b)         a

c)         b

d)         totalCount>c     deadline=currentTime

上述看似非常优雅的方案,却隐藏几个严重的问题:

1、  时间片的选择问题。

这个方案中,时间片的选择是一个比较困难的问题。因为,如果系统在一个时间片之内爆掉内存的话,系统将直接崩溃。

当然,这个问题,我们可以加外部限制得方式去控制

2、  deadline 之前的数据,不能很快删除。导致deaddata滞留,浪费大量的内存

假定 deadline之前的数据,约为总数据量的10%。因为删数据操作,只在put的时候。假定每个时间点的put操作,能覆盖20%的hash槽。这个 10%*20%=2%,每个时间点,只能删除2%的过期数据。然后,随着时间的推移。这个过程必将趋于稳定。而这个趋于稳定后,内存消耗,至少是 capacity的4-5倍。这样的消耗和浪费。是难以承受的。

这个方案,从实际测试来看,情况非常糟糕。所以最终还是放弃了。

五、      ConcurrentLRUHashMap实现方式三:分段实现锁分离+每个段内维护一份退化链表

【实现策略】:

1、锁分离机制。内部分成了多个segement,每个segement是独立加锁,相互不干扰。

2、每个segement内部维护一个双向链表(退化链表)。每次命中/添加,就把节点移动到退化链表头部。

3、每次put操作,通过hash,散到每个segement中,判断segment的容量是否到达阈值。 如果到达阈值,则删除退化链表中最末尾的节点。

【实现】

1、重新定义HashEntry


static class HashEntry {

/**

* 键

*/

final K key;

/**

* hash值

*/

final int hash;

/**

* 值

*/

volatile V value;

/**

* hash链指针

*/

final HashEntry next;

/**

* 双向链表的下一个节点

*/

HashEntry linknext;

/**

* 双向链表的下一个节点

*/

HashEntry linkpref;

/**

* 死亡标记

*/

AtomicBoolean dead;


}

2、定义segment

static final class Segment extends ReentrantLock implements

Serializable {

private static final long serialVersionUID = 1L;

transient int threshold;

transient volatile int count;

transient int modCount;

transient volatile HashEntry[] table;

transient final HashEntry header;// 头节点

}

3、  put操作

代码太长了,见附件吧

4、  get操作

		V get(Object key, int hash) {
HashEntry e = getFirst(hash);
// 遍历查找
while (e != null) {
if (e.hash == hash && key.equals(e.key)) {
V v = e.value;
// 把节点移动到头部。
moveNodeToHeader(e);
if (v != null)
return v;
// 在锁的情况读,必定能读到。
// tab[index] = new HashEntry(key, hash, first, value),
// value赋值和tab[index]赋值可能会重新排序,重新排序之后,可能会读空值
// 读到空值的话,在有锁的情况在再读一遍,一定能读!
return readValueUnderLock(e); // recheck
}
e = e.next;
}
return null;

六、      ConcurrentLRUHashMap实现方式四:

具体的做法是:

1、  对concurrentHashMap 每个节点加时间戳,每次命中只修改该节点的时间戳。

2、  集中式退化操作,每次命中并不进行退化操作。而是集中式进行退化操作(满的时候,或者时间到了)。

代码:

private static class CountableKey implements Comparable> {

public CountableKey(K key,V value) {

if (value == null) {

throw new NullPointerException("should not be null");

}

this.value = value;

this.key = key;

refreshTimeStamp();

}



public void refreshTimeStamp(){

timestamp.set(System.currentTimeMillis());

}

final V value;

final K key;

AtomicLong timestamp = new AtomicLong();



@Override

public int compareTo(CountableKey o) {

long thisval = this.timestamp.get();

long anotherVal = o.timestamp.get();

return (thisval < anotherVal?-1:(thisval == anotherVal?0:1));

}

}

该方案的好处:

1、  快速执行get操作。get操作的时间是“concurrentHashMap的get时间+更新时间戳”的时间。

2、  put操作,一般的put操作的时间是“concurrentHashMap的put时间”,只要还未到达容量限制。而到达容量限制以后的,需要进行“退化,清理操作”+put的时间

该方案的 可能存在的问题:

1、  命中率,该算法的命中率同linkedHashMap

2、  清除 策略:

l  满了,执行清楚。缺点:1、会出现某个时刻,写操作卡死(如果正在等待清理的话)

l  定时执行。缺点:1、性能耗费。2、读不一致仍然无法避免。

七、      ConcurrentLRUHashMap实现方式的比较

本文只是抛砖引玉,希望能看到更多好多ConcurrentLRUHashMap的实现方式。由于能力有限。上文提到的第二种实现方式,在实际实现中并不能很好的退化,最终可能导致内存溢出。具体分析如下表

方式 方式一 方式二 方式三 方式四
性能
线程安全 绝对安全 安全 安全 安全
内存消耗 一般 很多 一般 一般
稳定性 稳定 不稳定 稳定 不稳定

总体来说,第三者性较好。

比较方式一和方式三:

两个OOM Cases排查过程的分享

分享一下两个OOM Cases的查找过程,一个应用是Native OOM;另外一个应用其实没有OOM,只是每隔一段时间就会出现频繁FGC的现象,OOM的查找已经具备了不错的工具,但有些时候还是会出现很难查的现 象,希望这两个排查过程的分享能给需要的同学带来一些帮助。

Native OOM的排查Case
之前的几个PPT里我都说到了,目前查找Native OOM最好的方法就是用google perftools了,于是挂上google perftools,等待应用再次native oom,很幸运,两天后,应用就再次native oom了,于是分析crash之前那段时间谁在不断的分配堆外的内存,pprof看到的结果主要是java.util.Inflater造成的,由于之前 已经碰到过类似的case,知道如果使用了Inflater,但不显式的调用Inflater.end的话,确实会造成这个现象。
于是剩下的问题就是找出代码里什么地方调用了Inflater,这种时候btrace这个神器就可以发挥作用了,一个简单的btrace脚本:

import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class Trace{
    @OnMethod (
       clazz= "java.util.zip.Inflater" ,
       method= "/.*/"
    )
    public static void traceExecute( @ProbeMethodName String methodName){
      println(concat( "who call Inflater." ,methodName));
      jstack();
    }
}

执行后很快就找到了代码什么地方调用了Inflater,于是加上了显式的调用Inflater.end,搞定收工。

偶尔频繁FGC的排查Case
这个Case就没上面的那个那么顺利了,查了有接近一个月才查出最终的原因。
当这个应用出现频繁FGC时,dump了内存,用MAT分析后,看到内存被消耗完的原因是由于几个线程内的ThreadLocalMap中有大量的数据,ThreadLocal中消耗最多内存的主要是一个HashMap,这里面有大量的数据。
于是当时想到的第一个方法就是查查应用里面什么地方往ThreadLocal里放了HashMap,杯具的是,当查找代码后发现应用本身的代码并没有往 ThreadLocal里放HashMap,那就只能是应用依赖的其他jar包做了这样的事了,但不可能去抓出这个应用依赖的所有的jar的源码来扫描, 于是继续借助BTrace,写了个脚本来跟踪这类型的线程中谁调用了ThreadLocal.set,并且放的是HashMap,btrace脚本如下:

import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class Trace{
    @OnMethod (
       clazz= "java.lang.ThreadLocal" ,
       method= "set"
    )
    public static void traceExecute(Object value){
       if (startsWith(name(currentThread()), "xxx" ) && startsWith( "java.util.HashMap" ,name(classOf(value))) ){
            println( "-------------------------" );
            jstack();
            println();
       }
    }
}

OK,开始运行上面的脚本,发现竟然一直都没打印出什么内容,只能一直等了,杯具的是一直到了一周后再次出现频繁FGC时,这个脚本都没输出任何的东西,于是只好转换思路。

既然是HashMap里put了大量的某种类型的数据,那干脆用btrace来看看是谁在往HashMap里put这些数据,于是又写了一个 btrace脚本,执行后,很快就看到了是代码中什么地方在put这些数据,但是从抓到的调用者来看,不仅仅是目前有大量数据的这类型的线程会调,其他类 型的线程也会调用,如果这个地方有问题的话,应该就全部有问题了,于是跳过这里。

回到MAT看到的现象,会不会是因为代码什么地方用ThreadLocal的方式不对,又或是什么地方往ThreadLocal里放了东西,又忘了 清除呢,因此要做的就是找出这个应用中所有属性为ThreadLocal的地方,来人肉分析了,于是写了一个jsp,扫描所有的classloader中 的所有class,找出属性类型为ThreadLocal的,扫描后找到了一些,还真发现有一个和现在HashMap中放的数据一样的private ThreadLocal,这种用法在线程复用的情况下,如果是每次new ThreadLocal的话,会导致ThreadLocal放的东西一直不释放,兴奋的以为已经发现原因了,可惜和业务方一确认,这个类借助Spring 保证了singleton的,因此不会有问题。
好吧,到这一步,只能猜想是由于某种参数请求的时候造成业务上会获得大量的数据了,于是想着要找业务方来分析代码了,这个非常麻烦,于是到此就几乎停滞不前了。

今天静下心来,重新仔细的看了下MAT分析的结果,决定仍然用btrace跟踪下之前往HashMap中put数据的那个业务代码,突然发现,在 web类型的处理线程中它借助的是filter去clear数据的,而杯具的是出问题的这种类型线程的处理机制是没有filter机制的,因此猜测问题估 计出在这里了,继续btrace,看看这种类型的线程中是不是只有人调put,没人调clear,btrace脚本运行,很快就验证了这个猜测,于是相应 的解决掉了这个case,搞定收工。

在这第二个case中,可见在频繁FGC或者OOM时,很有可能MAT只能告诉你初步的原因,但要对应到代码上到底是什么地方造成的,还得花很大精力分析了,这个时候BTrace通常能帮上很大的忙。

高质量软件,从点点滴滴做起

写这篇文章的想法产生在昨天晚上读《面向对象分析与设计》的时候,我渐渐发现我们这个小组不知不觉地贯彻了很多非常有价值的实践经验,这些点点滴滴都对我 们的最终的产品质量产生了或大或小的影响,保证我们的系统不会出现重大的故障。我想有必要将这些“隐性知识”稍微总结一下,以供参考和记录。

从过程的连续光谱来看,我们大概处于中间位置偏左的位置,更偏向一个轻量级团队的敏捷过程,但是也包含计划驱动过程中的因素。我们的小组是自管理的,没有 专门的QA和SA,我们自己去想出最好的工作方法,但是在执行中我们的计划还是相对确定的,每个季度做什么都会有一个比较明确的计划和里程碑,并且对问题 领域都相对熟悉;我们的过程是迭代式,一般一个季度至少会交付一个稳定可执行的新版本,我们在文档上做的不是特别好,很多都依赖于团队成员之间的“隐性知 识”;同时我们对问题的改进基本还是有一个流程和机制,会持续的跟踪问题并改进。

下面分阶段总结下我们的一些实践经验。

一、分析和设计阶段

1、在这个阶段,我们会明确准备做什么,界定问题的边界,对功能进行一个取舍。一般在一个版本完成之后会马上开始这个过程。大家都想一想接下来做什么,经过几轮PK后确定重要紧急的事情优先做,定义下一个版本的功能列表

2、功能列表出来之后,我们会针对每个功能提出各种方案做比较,在此期间,我们会邀请更大团队范围内的专家参与方案和设计的评审 ,剔除不切实际以及明显有缺陷的方案,针对一些风险点提出改进建议和防范措施。

3、在设计方案出来之后,我们会分配功能的开发任务,根据每个开发人员熟悉的领域,自主领取或者被动分配任务 。这个过程不是一成不变的,考虑到团队内部知识交流的必要性,也可能让不熟悉某个领域的人去做他不熟悉的事情。

二、构造阶段

1、整个系统已经有一个关键的抽象机制 ,针对我们的服务器有一个核心的pipeline机制,针对我们的客户端,有一个核心的发送消息流程。将所有的功能模块组织在这个关键机制周围,形成一个强有力的整体。

2、开发完成不仅仅意味着功能代码的完成,还包括测试代码 :单元测试和集成测试。如果你没办法做到全面的覆盖,那就要求必须覆盖运行的关键路径和极端场景。

3、单元测试我们使用JUnit,适当使用Mock可以简化测试。但是Mock对象如果太多,也许会失去测试的价值,这里有一个权衡。

4、在整个构造过程中,我们贯彻每日构建、持续集成的原则。使用hudson做持续集成,时刻关注测试状况 ,有问题及时反馈给开发者。

5、有一个功能强大的集成测试框架,模拟实际环境做各种测试,它的目的是尽量在接近真实状况下去执行系统并尽早暴露问题。

6、每个功能完成之后,立即发起review,请同事和你一起复审代码。复审代码的作用不仅是发现bug,改良设计,也是一个知识交流的最佳途径。我们经常能通过代码审查 发现一些设计上的缺陷,以及功能实现上的BUG。我们团队应该说是非常看重代码审查的作用。

7、使用findbugs和clover等工具 ,分析代码质量并改进。

8、在发布之前,做一次集中的代码review ,每个人介绍下自己的功能实现代码和设计,一般我们会申请一个会议室和投影仪,并邀请团队之外的人加入review。

9、在发布之前,有一个系统的压测流程,针对每个版本更新压测方案,并预留一到两周的时间做性能压测。压测不仅能尽早暴露性能隐患,还可以发现系统在特殊情况下的一些BUG。压测除了关注系统的吞吐量、GC情况之外,还应该关注硬件的性能指标。

三、发布和总结
1、发布之前,最好让使用我们系统的用户使用新版本做一个回归测试 ,一方面是测试兼容性,一方面也可以及早发现BUG。

2、我们的发布流程:线下、beta、线上。每个阶段通常都持续一到两周,才会进行到下一阶段。并且是从相对不重要的系统,到关键系统的顺序进行发布。

3、发布之后,通过日志、运行时监控、用户反馈等方式收集系统运行状况,发现BUG,修正BUG,补充测试,测试通过,重新发布。

4、每个版本发布后,需要总结下本次发布过程中遇到的所有BUG以及经验教训,并提出可能的改进建议。

5、需要一个跟踪线上问题的BUG跟踪系统,可以用JIRA之类的trace软件。跟踪不仅是记录,最好列出解决的时间点,在哪个版本确定解决,甚至确定交给谁去解决,并持续跟进。

hotswap 用户手册

关于 hotswap(该补丁的网址http://ssw.jku.at/dcevm/ )

Hotswap  是一个允许在运行状态下无限制的修改加载类文件的Java虚拟机补丁。当前java虚拟机的动态加载机制只允许修改类的方法体,而打了hotswap补丁以后,可以增加,删除类属性,方法,甚至可以改变一个类的父类。

Hotswap补丁是基于GPL v2.0开源协议的。你可以通过windows,linux,mac os下载hotswap 补丁的源代码或者可执行文件。

安装 hotswap

警告 : 该补丁目前还处于试验阶段. 当该补丁用于调试java程序使用是,是相当稳定的。但我们不提倡在生产环境中使用该补丁。

现在提供了 32 , 64 Windows 虚拟机32 Mac OS (从这里获得), 和32 Linux 虚拟机 的补丁 . 所有的修改基于 JDK7-b102版本。

安装程序

  • dcevm-0.2-win.jar (5.6 MB)
  • dcevm-0.2-mac.jar (6.0 MB)
  • dcevm-0.2-linux.jar (5.8 MB)

该补丁不仅能打在java7上,且打到java 6上,也一样正常工作。

在windows 启动安装程序,在控制台输入:
> java -jar dcevm-0.2-win.jar

在Mac OS启动安装程序,终端输入:
$ sudo java -jar dcevm-0.2-mac.jar

在Mac OS启动安装程序,终端输入:
$ sudo java -jar dcevm-0.2-linux.jar

安装程序会替换掉java下 bin/client/jvm.dll 和 bin/server/jvm.dll ,并将以后的jvm.dll备份到相应目录下。还会将dcevm.jar 加到lib/ext/ 目录.

图一:hotswap补丁安装界面。

执行上述命令后,就会出现图一界面,选择将要安装该补丁的java目录,单击安装就可以了。

Ps:如果你的Linux没有图形界面,您可以从这里下载 已经打好补丁的java。

使用 hotswap 调试 java 程序

  1. 首先用修改后的java以debug模式启动 java程序。
  2. 使用eclipse连接到该java进程(也可以直接在eclipse中以debug方式启动)
  3. 现在在eclipse 工程下面针对class文件的任何修改将会直接反映到java程序中去。

hotswap 在淘宝

令我们高兴的是,淘宝开发人员对该技术有着强烈的兴趣,目前已有如下团队使用的该补丁:Mytaobao开发团队,TDDL(Rtools)开发团队,HSF开发团队,交易中心等团队。

我们期待你的加入。

Ps:如果你使用HSF_JETTY插件,你只要通过升级就hsf_jetty,不用手动安装,就可以使用该patch。我们并会在接下来实现spring,webx配置文件的不重启动态替换。尽情期待。

zookeeper使用和原理探究(一)

zookeeper介绍
zookeeper是一个为分布式应用提供一致性服务的软件,它是开源的Hadoop项目中的一个子项目,并且根据google发表的论文来实现的,接下来我们首先来安装使用下这个软件,然后再来探索下其中比较重要一致性算法。

zookeeper安装和使用
zookeeper的安装基本上可以按照 http://hadoop.apache.org/zookeeper/docs/current/ zookeeperStarted.html 这个页面上的步骤完成安装,这里主要介绍下部署一个集群的步骤,因为这个官方页面似乎讲得并不是非常详细(Running Replicated Zookeeper)。

由于手头机器不足,所以在一台机器上部署了3个server,如果你手头也比较紧,也可以这么做。那么我建了3个文件夹,如下
server1 server2 server3

然后每个文件夹里面解压一个zookeeper的下载包,并且还建了几个文件夹,总体结构如下,最后那个是下载过来压缩包的解压文件
data dataLog logs zookeeper-3.3.2

那么首先进入data目录,创建一个myid的文件,里面写入一个数字,比如我这个是server1,那么就写一个1,server2对应myid文件就写入2,server3对应myid文件就写个3

然后进入zookeeper-3.3.2/conf目录,那么如果是刚下过来,会有3个文件,configuration.xml, log4j.properties,zoo_sample.cfg,这3个文件我们首先要做的就是在这个目录创建一个zoo.cfg的配置文件,当然你可 以把zoo_sample.cfg文件改成zoo.cfg,配置的内容如下所示:

tickTime=2000
initLimit=5
syncLimit=2
dataDir=xxxx/zookeeper/server1/data
dataLogDir=xxx/zookeeper/server1/dataLog
clientPort=2181

server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890

标红的几个配置应该官网讲得很清楚了,只是需要注意的是clientPort这个端口如果你是在1台机器上部署多个server,那么每台机器都要 不同的clientPort,比如我server1是2181,server2是2182,server3是2183,dataDir和 dataLogDir也需要区分下。

最后几行唯一需要注意的地方就是 server.X 这个数字就是对应 data/myid中的数字。你在3个server的myid文件中分别写入了1,2,3,那么每个server中的zoo.cfg都配 server.1,server.2,server.3就OK了。因为在同一台机器上,后面连着的2个端口3个server都不要一样,否则端口冲突,其 中第一个端口用来集群成员的信息交换,第二个端口是在leader挂掉时专门用来进行选举leader所用。

进入zookeeper-3.3.2/bin 目录中,./zkServer.sh start启动一个server,这时会报大量错误?其实没什么关系,因为现在集群只起了1台server,zookeeper服务器端起来会根据 zoo.cfg的服务器列表发起选举leader的请求,因为连不上其他机器而报错,那么当我们起第二个zookeeper实例后,leader将会被选 出,从而一致性服务开始可以使用,这是因为3台机器只要有2台可用就可以选出leader并且对外提供服务(2n+1台机器,可以容n台机器挂掉)。

接下来就可以使用了,我们可以先通过 zookeeper自带的客户端交互程序来简单感受下zookeeper到底做一些什么事情。进入zookeeper-3.3.2/bin(3个 server中任意一个)下,./zkCli.sh –server 127.0.0.1:2182,我连的是开着2182端口的机器。

那么,首先我们随便打个命令,因为zookeeper不认识,他会给出命令的help,如下图

ls(查看当前节点数据),
ls2(查看当前节点数据并能看到更新次数等数据) ,
create(创建一个节点) ,
get(得到一个节点,包含数据和更新次数等数据),
set(修改节点)
delete(删除一个节点)

通过上述命令实践,我们可以发现,zookeeper使用了一个类似文件系统的树结构,数据可以挂在某个节点上,可以对这个节点进行删改。另外我们还发现,当改动一个节点的时候,集群中活着的机器都会更新到一致的数据。

zookeeper的数据模型
在简单使用了zookeeper之后,我们发现其数据模型有些像操作系统的文件结构,结构如下图所示

(1) 每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识,如/SERVER2节点的标识就为/APP3/SERVER2
(2) Znode可以有子znode,并且znode里可以存数据,但是EPHEMERAL类型的节点不能有子节点
(3) Znode中的数据可以有多个版本,比如某一个路径下存有多个数据版本,那么查询这个路径下的数据就需要带上版本。
(4) znode 可以是临时节点,一旦创建这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除,Zookeeper 的客户端和服务器通信采用长连接方式,每个客户端和 服务器通过心跳来保持连接,这个连接状态称为 session,如果 znode 是临时节点,这个 session 失效,znode 也就删除了
(5) znode 的目录名可以自动编号,如 App1 已经存在,再创建的话,将会自动命名为 App2
(6) znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的集中管理,集群管理,分布式锁等等。

通过java代码使用zookeeper
Zookeeper的使用主要是通过创建其jar包下的Zookeeper实例,并且调用其接口方法进行的,主要的操作就是对znode的增删改操作,监听znode的变化以及处理。

以下为主要的API使用和解释

//创建一个Zookeeper实例,第一个参数为目标服务器地址和端口,第二个参数为Session超时时间,第三个为节点变化时的回调方法
ZooKeeper zk = new ZooKeeper( "127.0.0.1:2181" , 500000 , new Watcher() {
            // 监控所有被触发的事件
              public void process(WatchedEvent event) {
            //dosomething
            }
       });
//创建一个节点root,数据是mydata,不进行ACL权限控制,节点为永久性的(即客户端shutdown了也不会消失)
zk.create( "/root" , "mydata" .getBytes(),Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
 
//在root下面创建一个childone znode,数据为childone,不进行ACL权限控制,节点为永久性的
zk.create( "/root/childone" , "childone" .getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
 
//取得/root节点下的子节点名称,返回List
zk.getChildren( "/root" , true );
 
//取得/root/childone节点下的数据,返回byte[]
zk.getData( "/root/childone" , true , null );
 
//修改节点/root/childone下的数据,第三个参数为版本,如果是-1,那会无视被修改的数据版本,直接改掉
zk.setData( "/root/childone" , "childonemodify" .getBytes(), - 1 );
 
//删除/root/childone这个节点,第二个参数为版本,-1的话直接删除,无视版本
zk.delete( "/root/childone" , - 1 );
 
//关闭session
zk.close();

Zookeeper的主流应用场景实现思路 除去官方示例)

(1)配置管理
集中式的配置管理在应用集群中是非常常见的,一般商业公司内部都会实现一套集中的配置管理中心,应对不同的应用集群对于共享各自配置的需求,并且在配置变更时能够通知到集群中的每一个机器。

Zookeeper很容易实现这种集中式的配置管理,比如将APP1的所有配置配置到/APP1 znode下,APP1所有机器一启动就对/APP1这个节点进行监控(zk.exist(“/APP1″,true)),并且实现回调方法 Watcher,那么在zookeeper上/APP1 znode节点下数据发生变化的时候,每个机器都会收到通知,Watcher方法将会被执行,那么应用再取下数据即可 (zk.getData(“/APP1″,false,null));

以上这个例子只是简单的粗颗粒度配置监控,细颗粒度的数据可以进行分层级监控,这一切都是可以设计和控制的。
(2)集群管理
应用集群中,我们常常需要让每一个机器知道集群中(或依赖的其他某一个集群)哪些机器是活着的,并且在集群机器因为宕机,网络断链等原因能够不在人工介入的情况下迅速通知到每一个机器。

Zookeeper同样很容易实现这个功能,比如我在zookeeper服务器端有一个znode叫/APP1SERVERS,那么集群中每一个机 器启动的时候都去这个节点下创建一个EPHEMERAL类型的节点,比如server1创建/APP1SERVERS/SERVER1(可以使用ip,保 证不重复),server2创建/APP1SERVERS/SERVER2,然后SERVER1和SERVER2都watch /APP1SERVERS这个父节点,那么也就是这个父节点下数据或者子节点变化都会通知对该节点进行watch的客户端。因为EPHEMERAL类型节 点有一个很重要的特性,就是客户端和服务器端连接断掉或者session过期就会使节点消失,那么在某一个机器挂掉或者断链的时候,其对应的节点就会消 失,然后集群中所有对/APP1SERVERS进行watch的客户端都会收到通知,然后取得最新列表即可。

另外有一个应用场景就是集群选master,一旦master挂掉能够马上能从slave中选出一个master,实现步骤和前者一样,只是机器在 启动的时候在APP1SERVERS创建的节点类型变为EPHEMERAL_SEQUENTIAL类型,这样每个节点会自动被编号,例如

zk.create( "/testRootPath/testChildPath1" , "1" .getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
 
zk.create( "/testRootPath/testChildPath2" , "2" .getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
 
zk.create( "/testRootPath/testChildPath3" , "3" .getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
 
// 创建一个子目录节点
zk.create( "/testRootPath/testChildPath4" , "4" .getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
 
System.out.println(zk.getChildren( "/testRootPath" , false ));

打印结果:[testChildPath10000000000, testChildPath20000000001, testChildPath40000000003, testChildPath30000000002]

zk.create( "/testRootPath" , "testRootData" .getBytes(),Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
 
// 创建一个子目录节点
zk.create( "/testRootPath/testChildPath1" , "1" .getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
 
zk.create( "/testRootPath/testChildPath2" , "2" .getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
 
zk.create( "/testRootPath/testChildPath3" , "3" .getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
 
// 创建一个子目录节点
zk.create( "/testRootPath/testChildPath4" , "4" .getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
 
System.out.println(zk.getChildren( "/testRootPath" , false ));

打印结果:[testChildPath2, testChildPath1, testChildPath4, testChildPath3]

我们默认规定编号最小的为master,所以当我们对/APP1SERVERS节点做监控的时候,得到服务器列表,只要所有集群机器逻辑认为最小编 号节点为master,那么master就被选出,而这个master宕机的时候,相应的znode会消失,然后新的服务器列表就被推送到客户端,然后每 个节点逻辑认为最小编号节点为master,这样就做到动态master选举。

总结

我们初步使用了一下zookeeper并且尝试着描述了几种应用场景的具体实现思路,接下来的文章,我们会尝试着去探究一下zookeeper的高可用性与leaderElection算法。

参考:http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/

http://hadoop.apache.org/zookeeper/docs/current/

http://rdc.taobao.com/team/jm/archives/448

Hotspot & hotswap, who and who are best freinds

Hotspot & hotswap, who and who are best freinds (点击这里查看pp t)

java 动态替换的ppt。

  1. hotspot & hotswapwho and who are best friends
    [email protected]
  2. why we need hotswap?
    主管: 你在偷懒?
    汇编开发人员:我在编译。
    C++开发人员: 我在编译打包部署。
    java 开发人员: 我在打包部署。
    Jsp开发人员:我在部署。
    Python开发人员:………
    Ruby开发人员:………
  3. why we need hotswap?
    线上紧急修改。(方法体的修改、增加和减少方法,字段,类,接口,更改类继承拓扑结构)
  4. JVM 1.2 以后支持了hotswap功能
    JVMTM Tool Interface
    typedefstruct {
    jclassklass;
    jintclass_byte_count;
    const unsigned char* class_bytes;
    } jvmtiClassDefinition;
    jvmtiError
    RedefineClasses(jvmtiEnv* env,
    jintclass_count,
    const jvmtiClassDefinition* class_definitions)
  5. JVM 1.2 以后支持了hotswap功能
    JavaTM Virtual Machine Debug Interface Reference
    typedefstruct {
    jclassclazz; /* Class to be redefined */
    jintclass_byte_count; /* number of bytes defining class (below) */
    jbyte *class_bytes; /* bytes defining class */
    /* (in Class File Format of JVM spec) */
    } JVMDI_class_definition;jvmdiError
    RedefineClasses(jintclassCount, JVMDI_class_definition *classDefs)
  6. Java dev除打包部署外还干嘛?
    用IDE(eclipse)调试
  7. Sounds good,what’s the problem?
    Unfortunately, this redefinition is limited only to changing method bodies — it cannot either add methods or fields or otherwise change anything else, except for the method bodies.
  8. So, Here we go!
    Discarding classloaders
    java.lang.instrument
    Hacking changes into the JVM itself feels a fragile approach
  9. Discarding classloaders
  10. Discarding classloaders can help, but
    Not particularly granular
    Instance state is lost
    Can run into memory problems
    Demo(discarding.classloaders.hotswap) by [email protected]
  11. java.lang.instrument
    a JRuby object is not much more than a Map from method names to their implementations and from field names to their values.
    byte[] transform(ClassLoader loader,
    String className,
    Class classBeingRedefined,
    ProtectionDomainprotectionDomain,
    byte[] classfileBuffer)
    throws IllegalClassFormatException
  12. java.lang.instrument
    java.lang.instrument.ClassFileTransformer. This class can do two things:
    replace method bodies of existing classes (but nothing else, it is essentially the same as hotswap).
    modify classes before they get loaded into the java virtual machine
    class Class1
    { protected static int field;
    }
  13. java.lang.instrument
    class Class1{  protected static int field;    public static Object __REDEFINED_STATIC_METHOD_(intmethodNo, Object[] parameters)  {    if(methodNo == 0)    {      return field;    }    if(methodNo == 1)    {      Integer p0 = (Integer)parameters[0];      int unboxedP0 = p0.intValue();      field = unboxedP0;      return null;    }    return null;  }}
  14. java.lang.instrument
    public class Proxy1{  public void setField(intparam)  {    Object[] params = new Object[1];    params[0] = new Integer(param);    Class1.__REDEFINED_STATIC_METHOD_(1,params);  }}
  15. Sounds well,but…
    Perfomance. subject to indirection & Pem memory
    Java SDK classes. The native code
    Compatibility.  A.class.getMethods()
  16. fakereplace
    Google Code project
    Usual structure changes supported
    Methods, fields, constructors
    Annotations
    Reflection instrumentation
    Integrates support directly for some frameworks
  17. Fakereplace demo
  18. Jrebel(not free)
    one master class + several anonymous
    Solve problem
    direction and lightweight
    Avoids instrumenting the Java
    Tweaks the results of the Reflection API, so that we can correctly include the added/removed members in these results
  19. Jrebel Demo
  20. Sounds perfect
  21. 国家队( nation team )
    Hotswap.patch
  22. hotspot and hotswap
    Arbitrary changes possible (including changes to subtype relationships)
    No performance penalty before or after the change
    No introduced indirections
    Continued execution of old active methods
    Only simple, comprehensible strategies for field matching or method transitions
    Change possible at any point when the VM is suspended
    Graceful handling of accesses to deleted fields or calls of deleted methods
  23. HotswapStatus
  24. Transformer Methods
    class A
    {
    int x;
    intdoubleX;
    static void $staticTransformer() //class transformer
    {
    System.out.println(“Class A has a new version”);
    }
    void $transformer() { doubleX = 2 * x; } //instance’s
    }
  25. Hotswap.patch demo
  26. Hotswap.patchAlgorithm
    • Loading the New Classes
    • Updating the Data Structures and Pointers
  27. Loading the New Classes
    step1:Find all affected classes.
    (VM_RedefineClasses::FindAffectedKlassesClosure)
    Redefining D and B
    Find all affected classes
  28. Loading the New Classes
    step2:Sort the classes topologically.
    (VM_RedefineClasses::TopologicalClassSorting)
    Topological order.
  29. Updating the Data Structures and Pointers
    step1:Flush dependent code .
    Currently all compiled methods are deoptimized
    (Classes::flush_dependent_code)
  30. Updating the Data Structures and Pointers
    step2:Update constant pool cache entries.
    (VM_RedefineClasses::adjust_cpool_cache)
  31. Updating the Data Structures and Pointers
    step3: Swap pointers.
    利用gc的特性 ,gc 复制一个对象的后 会调整指向该对象的pointer
    (MarkSweep::adjust_pointer)
  32. Updating the Data Structures and Pointers
    step4: Update instance fields.
    all new fields are initialized to zero.(新建)
    instanceKlass::do_fields_evolution(标记)
    MarkSweep::update_fields(拷贝)
  33. Fantastic,but…
  34. [email protected]
    • HSF_JETTY plugin自动集成hotswap.patch。
    • 你也可以自己打patch。
  35. Question time
  36. Party time谢谢[email protected]

2010年北京JavaOne大会总结

4天,20场sessions,这是我2010年北京JavaOne大会的行程。

第一天
大会开幕式是下午3点,早晨我7点就起床了,这是我半年以来为数不多的几次早起。早起的目的是去逛北京天安门,这也是我生平第一次见老毛。
我的出行方式选择了坐地铁。

由于我是吃完早饭才出的门,到达地铁站时,差不多8点半这样子,发现人流没有想象中的那么多(晚上回来时,发现早晨太乐观了)
于是买了票,换乘了3条线后才到天安门东站(北京的地铁还是挺实惠的,一票可以随便坐。)。走出地铁站时,赫然发现一个白色的亭子。一开始以为是大学生志愿者搞什么活动。走近一看,原来是安检。
当时心里就想:“北京上访的人有这么多嘛,为啥天安门也要安检。”

不过心里不爽也没办法,还得过安检。走过安检,往前步行没多久,我和同事就到了宏伟的天安门广场。
站在伟大的烈士纪念碑前,我发出了一份来自内心的感叹,“好冷啊!”
当时的气温是零下3度,刮着4-5级大风。任何一个生物站在天安门广场的中央,我想都会跟一根风干的冰棍相差不多。

为了防止冻僵,我们几位结伴出行的同事都是匆匆拍了几张照片,就打道回府了。
冬天的北京,看来非常不适合旅行。

下午,达到会场。会场位于北京国家会议中心,紧邻鸟巢和水立方。进入会场时,我震惊了。又是安检?是的,天子脚下莫非黄土,我深深地被雷到了。

开幕式开始,按照会议的老规矩,几位Oracle的高管轮番向我们灌输思想,顺带推销。对于已经看过美国JavaOne照片和视频的我来说,差不多可以免疫。
开幕式么,还是一如既往的无聊。

第二,三天
我主要的sessions都安排在这两天。我参加的session包括,gc调优,jdk7战略,project coin,jrockit等。
关于这两天的sessions,我唯一想表达的就是,slides很好,主讲者很水。

在听session的过程中,主讲者的语速很快,翻ppt和跳章的速度更快,并且每个知识点都不做任何延伸或发散,80%都是ppt上的内容照着念。
这种session,还不如自己回去看slides更好。

虽然session讲不怎么样,不过sessions的内容还是跟美国JavaOne对接,但也属于炒冷饭的范畴。
如果你看过美国JavaOne的slides,基本上,这里80%的sessions是几乎相同的内容。
如果你没看过美国JavaOne的slides,那么这些slides值得回去好好消化一下。

第四天
由于第二,三天,sessions听得很悲剧。加上今天的sessions对我来说都是些不太感兴趣的sessions。所以,旷了好几场没去听。
下午吃完午饭,决定跟几位同行的同事去王府井买点礼品,带回给团队的同学们。算是给这次的行程画个句号。

来到王府井,给我的第一印象是很像杭州的延安路。
往前走没几分钟,就发现了一条小吃街。不过里面的小吃几乎都是什么香港小笼包,韩国炒年糕,土耳其烤肉等,没什么本地特色。
唯一一个本地产的小吃就是老北京酸奶。当然,这也不算是什么特产了,至少有当地的特色。

再往前走,我们就到了王府井百货,百货大楼保留了七八时年代的造型。进入地下一层的食品柜台,我买了一些稻香村的糕点。
走出百货大楼时,天色已晚,我们按原路返回酒店了。

第五天
早晨8点半起床,退房,离开酒店,赶往首都机场。
下午5点半回到了杭州的住处。

Sun JDK 1.6内存管理

分为使用篇、调优篇和实现篇三个部分,使用篇为填鸭式,调优篇为pattern式,实现篇为启发式,三个PPT的目标为:
1.掌握Sun JDK的内存区域的划分;
2.掌握Sun JDK垃圾收集器的使用方法和触发时机;
3.掌握OOM的解决方法;
4.掌握一些基本的GC调优的方法;
5.了解自动内存管理的常见实现方法,以及Sun JDK所做的优化。
感兴趣的同学可以看下,:)

先行放上使用篇和调优篇,实现篇以及slides中的cases后续放上。

使用篇:
Sun JDK 1.6内存管理 -使用篇

调优篇:
Sun JDK 1.6内存管理 -调优篇

  • Page 1 of 5
  • 1
  • 2
  • 3
  • 4
  • 5
  • >

你可能感兴趣的:(分布式)