今年在阅读某个项目源码的时候看到DelayQueue的使用,
xmemcached 1.2.6.1的重连任务也是采用DelayQueue管理,ReconnectRequest实现Delayed接口,我突然想起去review下xmc的源码,发现一个严重的BUG,原始代码如下:
public
final
class
ReconnectRequest
implements
Delayed {
public
long
getDelay(TimeUnit unit) {
return
nextReconnectTimestamp
-
System.currentTimeMillis(); } }
getDelay返回该任务还剩下多少时间可以被执行,将下次执行的时间戳减去当前时间即可,问题在于这里返回的是毫秒,而没有调用getDelay传入的TimeUnit做转换,在DelayQueue内部其实是用纳秒做单位交给Condition对象去等待
for
(;;) { E first
=
q.peek();
if
(first
==
null
) { available.await(); }
else
{
long
delay
=
first.getDelay(TimeUnit.NANOSECONDS);
if
(delay
>
0
) {
long
tl
=
available.awaitNanos(delay); }
else
{ E x
=
q.poll();
assert
x
!=
null
;
if
(q.size()
!=
0
) available.signalAll();
//
wake up other takers
return
x; } } }
最终导致的问题是,
awaitNanos很快返回(
awaitNanos接受的是纳秒,这里却传入毫秒)
,循环执行发现重新计算的delay仍然大于0,循环等到getDelay返回的越来越小直到0才执行相应的Task,,造成的现象是在重连的时候cpu占用率很高。 单元测试的时候没有发现这个问题,主要是因为功能正常,没有关注资源消耗情况,因此惭愧地忽略了。 解决的办法很简单,修改getDelay方法即可:
public
long
getDelay(TimeUnit unit) {
return
unit.convert( nextReconnectTimestamp
-
System.currentTimeMillis(), TimeUnit.MILLISECONDS); }
这个BUG比较严重,已经升级1.2.6.1的朋友建议马上升级到1.2.6.2,使用maven的朋友只要修改版本即可,没有使用maven的请到这里下载。
文章转自庄周梦蝶 ,原文发布时间 2010-10-22
相关资源:xmemcached-1.2.6.2