Java nio的一个严重BUG,导致cpu 100% - 代码之美 - 博客频道 - CSDN.NET
这个BUG会在linux上导致cpu 100%,使得nio server/client不可用,具体的详情可以看这里http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933 。令人失望的是这个BUG直到jdk 6u4才解决,sun的拖沓让人难以相信。这个BUG在server端容易出现,因为server端有频繁地接入断开连接。
使用jdk 6u4之前版本的nio框架都有这个隐患,除非你的框架很好地处理了这个可能的隐患。Grizzly的处理方式比较简单,也就是BUG报告里面提到的方式,在SelectionKey.cancel()之后马上进行了一次select调用将fd从poll(epoll)中移除:
Java代码:
this.selectionKey.cancel();
try {
// cancel key,then select now to remove file descriptor
this.selector.selectNow();
} catch (IOException e) {
onException(e);
log.error("Selector selectNow fail", e);
}
实际上这样的解决方式还是留有隐患的,因为key的取消和这个selectNow操作很可能跟Selector.select操作并发地在进行,在两个操作之间仍然留有一个极小的时间窗口可能发生这个BUG。因此,你需要更安全地方式处理这个问题,jetty的处理方式是这样,连续的 select(timeout)操作没有阻塞并返回0,并且次数超过了一个指定阀值,那么就遍历整个key set,将key仍然有效并且interestOps等于0的所有key主动取消掉;如果在这次修正后,仍然继续出现select(timeout)不阻塞并且返回0的情况,那么就重新创建一个新的Selector,并将Old Selector的有效channel和对应的key转移到新的Selector上。
long before=now;
int selected=selector.select(wait);
now = System.currentTimeMillis();
_idleTimeout.setNow(now);
_timeout.setNow(now);
// Look for JVM bugs
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933
if (__JVMBUG_THRESHHOLD>0 && selected==0 && wait>__JVMBUG_THRESHHOLD && (now-before)<(wait/2) )
{
_jvmBug++;
if (_jvmBug>=(__JVMBUG_THRESHHOLD2))
{
synchronized (this)
{
_lastJVMBug=now;
// BLOODY SUN BUG !!! Try refreshing the entire selector.
final Selector new_selector = Selector.open();
for (SelectionKey k: selector.keys())
{
if (!k.isValid() || k.interestOps()==0)
continue;
final SelectableChannel channel = k.channel();
final Object attachment = k.attachment();
if (attachment==null)
addChange(channel);
else
addChange(channel,attachment);
}
_selector.close();
_selector=new_selector;
_jvmBug=0;
return;
}
}
else if (_jvmBug==__JVMBUG_THRESHHOLD || _jvmBug==__JVMBUG_THRESHHOLD1)
{
// Cancel keys with 0 interested ops
for (SelectionKey k: selector.keys())
{
if (k.isValid()&&k.interestOps()==0)
{
k.cancel();
}
}
return;
}
}
else
_jvmBug=0;
这个方案能比较好的在jdk 6u4之前的版本上解决这个BUG可能导致的问题。Mina和Netty没有看到有处理这个BUG的代码,如果我看错了,请留言告诉我。Yanf4j一直采用的是grizzly的方式,准备加上jetty的处理方案。当然,最简单的方案就是升级你的JDK。
俺实际上被这个BUG害的很惨, 因为降到了jdk 6u4 CPU仍然会时不时的增高, 原因是低版本的Jetty同样在这方面处理的不是很合理.
Jetty关于这个BUG的描述:http://jira.codehaus.org/browse/JETTY-937, 用JETTY做为应用, JETTY1.6.22 + JDK 6U4俺现在测下来, CPU基本正常.
另外nio还有几个严重的bug,在实际运行中碰到了,sun在u18才修复,而现在u18还没正式发布。
http://download.java.net/jdk6/6u18/promoted/b01/changes/JDK6u18.b01.list.html
例如:http://bugs.sun.com/view_bug.do?bug_id=6693490
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6670302 这个BUG跟我这里描述的相似,解决的方法也是一样。
http://bugs.sun.com/view_bug.do?bug_id=6693490 这个BUG,一个间接影响也是关闭的channel一直有ready事件,select不阻塞并立即返回0,也就是导致CPU 100%,同样也是可以通过这里描述的方案解决的。
参考:
http://www.infoq.com/cn/articles/practice-of-java-nio-communication-framework
How To: Make Sure /etc/resolv.conf Never Get Updated By DHCP Client
Option # 3: Configure dhclient.conf
/etc/dhclient.conf or /etc/dhcp/dhclient.conf file contains configuration information for dhclient. You can turn on or off DNS update and other options for specific interface or all interface using this file. The man pages for DHCLIENT.CONF and DHCP-OPTIONS point out that in dhclient.conf, you should add this:
supersede domain-name-servers 202.54.1.2, 199.2.3.4;
OR
prepend domain-name-servers 1.2.3.4, 1.2.3.5;
Here is a sample config for you:
timeout 60; retry 60; reboot 10; select-timeout 5; initial-interval 2; reject 192.33.137.209; interface "eth0" { send host-name "laptop-area51.nixcraft.net.in.home"; send dhcp-client-identifier 00:30:48:33:BC:32; send dhcp-lease-time 3600; supersede domain-search "net.in.home", "cyberciti.biz", "vpx.nixcraft.net.in"; prepend domain-name-servers 8.8.8.8, 127.0.0.1; request subnet-mask, broadcast-address, time-offset, routers, domain-search, domain-name, domain-name-servers, host-name; require subnet-mask, domain-name-servers; }
Parent: Internet and Networking
The default setup of Ubuntu does not make it easy to use static DNS servers while using DHCP. If you use the standard ubuntu way of networking (ifupdown), you can edit /etc/network/interfaces
In that file you find the entry for your interface. If your interface is eth0, then look for the following lines:
auto eth0 iface eth0 inet dhcp
Add one line, so it looks like:
auto eth0 iface eth0 inet dhcp dns-nameservers ip.address.of.nameserver
Run
sudo invoke-rc.d networking restart
To make the changes effective
If you do not use ifupdown, you need to edit /etc/dhcp3/dhclient.conf
Find the lines
#prepend domain-name-servers 127.0.0.1; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, host-name, netbios-name-servers, netbios-scope;
And change them to
prepend domain-name-servers 1.2.3.4, 1.2.3.5; request subnet-mask, broadcast-address, time-offset, routers, domain-name, host-name, netbios-name-servers, netbios-scope;
Replace 1.2.3.4 and 1.2.3.5 with the addresses of your DNS servers.
Run
sudo invoke-rc.d networking restart
To make the changes effective:
netty/MemcacheClient.java at master · netty/netty · GitHub
/* | |
* Copyright 2014 The Netty Project | |
* | |
* The Netty Project licenses this file to you under the Apache License, | |
* version 2.0 (the "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at: | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
* License for the specific language governing permissions and limitations | |
* under the License. | |
*/ | |
package io.netty.example.memcache.binary; | |
import io.netty.bootstrap.Bootstrap; | |
import io.netty.channel.Channel; | |
import io.netty.channel.ChannelFuture; | |
import io.netty.channel.ChannelInitializer; | |
import io.netty.channel.ChannelPipeline; | |
import io.netty.channel.EventLoopGroup; | |
import io.netty.channel.nio.NioEventLoopGroup; | |
import io.netty.channel.socket.SocketChannel; | |
import io.netty.channel.socket.nio.NioSocketChannel; | |
import io.netty.handler.codec.memcache.binary.BinaryMemcacheClientCodec; | |
import io.netty.handler.codec.memcache.binary.BinaryMemcacheObjectAggregator; | |
import io.netty.handler.ssl.SslContext; | |
import io.netty.handler.ssl.SslContextBuilder; | |
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; | |
import java.io.BufferedReader; | |
import java.io.InputStreamReader; | |
/** | |
* Simple memcache client that demonstrates get and set commands against a memcache server. | |
*/ | |
public final class MemcacheClient { | |
static final boolean SSL = System.getProperty("ssl") != null; | |
static final String HOST = System.getProperty("host", "127.0.0.1"); | |
static final int PORT = Integer.parseInt(System.getProperty("port", "11211")); | |
public static void main(String[] args) throws Exception { | |
// Configure SSL. | |
final SslContext sslCtx; | |
if (SSL) { | |
sslCtx = SslContextBuilder.forClient() | |
.trustManager(InsecureTrustManagerFactory.INSTANCE).build(); | |
} else { | |
sslCtx = null; | |
} | |
EventLoopGroup group = new NioEventLoopGroup(); | |
try { | |
Bootstrap b = new Bootstrap(); | |
b.group(group) | |
.channel(NioSocketChannel.class) | |
.handler(new ChannelInitializer<SocketChannel>() { | |
@Override | |
protected void initChannel(SocketChannel ch) throws Exception { | |
ChannelPipeline p = ch.pipeline(); | |
if (sslCtx != null) { | |
p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); | |
} | |
p.addLast(new BinaryMemcacheClientCodec()); | |
p.addLast(new BinaryMemcacheObjectAggregator(Integer.MAX_VALUE)); | |
p.addLast(new MemcacheClientHandler()); | |
} | |
}); | |
// Start the connection attempt. | |
Channel ch = b.connect(HOST, PORT).sync().channel(); | |
// Read commands from the stdin. | |
System.out.println("Enter commands (quit to end)"); | |
System.out.println("get <key>"); | |
System.out.println("set <key> <value>"); | |
ChannelFuture lastWriteFuture = null; | |
BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); | |
for (;;) { | |
String line = in.readLine(); | |
if (line == null) { | |
break; | |
} | |
if ("quit".equals(line.toLowerCase())) { | |
ch.close().sync(); | |
break; | |
} | |
// Sends the received line to the server. | |
lastWriteFuture = ch.writeAndFlush(line); | |
} | |
// Wait until all messages are flushed before closing the channel. | |
if (lastWriteFuture != null) { | |
lastWriteFuture.sync(); | |
} | |
} finally { | |
group.shutdownGracefully(); | |
} | |
} | |
} |