庄周梦蝶

生活、程序、未来
   :: 首页 ::  ::  :: 聚合  :: 管理

   一张截图,Java虽然号称跨平台,然而涉及到跟网络相关时,还是依赖于各个平台的实现。对写java网络编程的朋友有点价值。


posted @ 2009-10-12 17:52 dennis 阅读(2100) | 评论 (0)编辑 收藏

    基于java nio的java memcached client——xmemcached正式发布1.2.0-stable版本,这是一个稳定的版本,在1.2.0-RC2的基础上做了性能改进和BUG修复。在用户的反馈下,发现了数个比较严重的BUG,因此这个版本建议升级以规避这些可能出现的BUG。相比于1.2.0-RC2,主要的改进如下:

1、添加心跳检测,默认开启这个特性,你可以通过

memcachedClient.setEnableHeartBeat(false);  

 来关闭。心跳检测出于兼容性考虑是基于version协议实现的。

2.添加新的incr/decr方法,允许传入初始值,如果指定的key不存在的时候,就将该值add到memcached。具体参见API文档。

3.修复数个BUG,如Issue 55,Issue 57,Issue 58,Issue ,Issue 60。具体请看这里

 

总结1.2相比于1.1版本的主要新增特性列表如下:

1、支持完整的memcached二进制协议

2、支持java nio连接池

3、支持kestrel

4、支持与hibernate-memcached的集成

5、日志从common-logging迁移到slf4j

6、简化构建等。

7、兼容JDK5。

 

欢迎试用并反馈,我的email: killme2008@gmail.com

posted @ 2009-10-10 10:59 dennis 阅读(1638) | 评论 (0)编辑 收藏

    喜欢奇幻的朋友可以瞧瞧,据说是烟大推荐的,同样在17K上,国人写的西方奇幻,还未写完,但是长度已经足够你好好享受。味道呢?战斗类似《博德之门》,语言比较绕,没读过此类作品的可能不习惯,文笔没得说,写作水准暂未发现下降趋势,值得长期追。名字比较奇怪,《昆古尼尔》,是北欧神话中大神奥丁的所有物——永恒之枪。故事就不剧透了,前两章可能比较晕,但是坚持看下去就能搞明白啦。如果你实在着急呢,可以看看这个背景介绍

posted @ 2009-10-10 00:44 dennis 阅读(550) | 评论 (0)编辑 收藏

    采用的是jboss netty的benchmark,环境是两台linux机器,都是4核16G内存以及2.6内核,网络环境是公司内网,带宽是1Gbps,JDK1.6.0_07。对比的是mina 2.0M6yanf4j 1.0-stable,两者都在压到16K,5000并发的时候客户端退出,因此后面给出的图有个16K的在5000并发为0,事实上只是几个连接失败,但是benchmark client就忽略了这个数据。实际过程还测试了1万并发连接的情况,但是由于测试客户端很容易退出,因此最后还是选定最大并发5000。注意,并非mina和yanf4j无法支撑1万个连接,而是benchmark client本身的处理,再加上内核tcp参数没有调整造成的。

首先看源码,mina的Echo Server:
package org.jboss.netty.benchmark.echo.server;

import java.net.InetSocketAddress;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.jboss.netty.benchmark.echo.Constant;

/**
 * 
@author The Netty Project (netty-dev@lists.jboss.org)
 * 
@author Trustin Lee (tlee@redhat.com)
 *
 * 
@version $Rev: 394 $, $Date: 2008-10-03 12:55:27 +0800 (星期五, 03 十月 2008) $
 *
 
*/
public class MINA {

    
public static void main(String[] args) throws Exception {
        
boolean threadPoolDisabled = args.length > 0 && args[0].equals("nothreadpool");

        SocketAcceptor acceptor 
= new NioSocketAcceptor(Runtime.getRuntime().availableProcessors());
        acceptor.getSessionConfig().setMinReadBufferSize(Constant.MIN_READ_BUFFER_SIZE);
        acceptor.getSessionConfig().setReadBufferSize(Constant.INITIAL_READ_BUFFER_SIZE);
        acceptor.getSessionConfig().setMaxReadBufferSize(Constant.MAX_READ_BUFFER_SIZE);
        acceptor.getSessionConfig().setThroughputCalculationInterval(
0);
        acceptor.getSessionConfig().setTcpNoDelay(
true);
        acceptor.setDefaultLocalAddress(
new InetSocketAddress(Constant.PORT));

        
if (!threadPoolDisabled) {
            
// Throttling has been disabled because it causes a dead lock.
            
// Also, it doesn't have per-channel memory limit.
            acceptor.getFilterChain().addLast(
                    
"executor",
                    
new ExecutorFilter(
                            Constant.THREAD_POOL_SIZE, Constant.THREAD_POOL_SIZE));
        }

        acceptor.setHandler(
new EchoHandler());
        acceptor.bind();

        System.out.println(
"MINA EchoServer is ready to serve at port " + Constant.PORT + ".");
        System.out.println(
"Enter 'ant benchmark' on the client side to begin.");
        System.out.println(
"Thread pool: " + (threadPoolDisabled? "DISABLED" : "ENABLED"));
    }

    
private static class EchoHandler extends IoHandlerAdapter {

        EchoHandler() {
            
super();
        }

        @Override
        
public void messageReceived(IoSession session, Object message)
                
throws Exception {
            session.write(((IoBuffer) message).duplicate());
        }

        @Override
        
public void exceptionCaught(IoSession session, Throwable cause)
                
throws Exception {
            session.close();
        }
    }
}


再看Yanf4j的Echo Server,没有多大区别:
package org.jboss.netty.benchmark.echo.server;

import java.nio.ByteBuffer;
import org.jboss.netty.benchmark.echo.Constant;

import com.google.code.yanf4j.config.Configuration;
import com.google.code.yanf4j.core.Session;
import com.google.code.yanf4j.core.impl.HandlerAdapter;
import com.google.code.yanf4j.core.impl.StandardSocketOption;
import com.google.code.yanf4j.nio.TCPController;

public class Yanf4j {

    
public static void main(String[] args) throws Exception {
        
boolean threadPoolDisabled = args.length > 0
                
&& args[0].equals("nothreadpool");
        Configuration configuration 
= new Configuration();
        configuration.setCheckSessionTimeoutInterval(
0);
        configuration.setSessionIdleTimeout(
0);
        configuration
                .setSessionReadBufferSize(Constant.INITIAL_READ_BUFFER_SIZE);
        TCPController controller 
= new TCPController(configuration);
        controller.setSocketOption(StandardSocketOption.SO_REUSEADDR, 
true);
        controller.setSocketOption(StandardSocketOption.TCP_NODELAY, 
true);
        controller.setHandler(
new EchoHandler());
        
if (!threadPoolDisabled) {
            controller.setReadThreadCount(Constant.THREAD_POOL_SIZE);
        }
        controller.bind(Constant.PORT);
        System.out.println(
"Yanf4j EchoServer is ready to serve at port "
                
+ Constant.PORT + ".");
        System.out
                .println(
"Enter 'ant benchmark' on the client side to begin.");
        System.out.println(
"Thread pool: "
                
+ (threadPoolDisabled ? "DISABLED" : "ENABLED"));
    }

    
static class EchoHandler extends HandlerAdapter {
        @Override
        
public void onMessageReceived(final Session session, final Object msg) {
            session.write(((ByteBuffer) msg).duplicate());
        }

        @Override
        
public void onExceptionCaught(Session session, Throwable t) {
            session.close();
        }

    }
}

    两者都启用线程池(16个线程),开启TCP_NODELAY选项,Client采用SYNC模式,压测结果如下(仅供参考),分别是数据大小为128、1K、4K和16K情况下,随着并发client上升吞吐量的对比图:














系统的资源消耗来看,Mina的load相对偏高。

posted @ 2009-10-09 14:08 dennis 阅读(3126) | 评论 (2)编辑 收藏

    上篇文章我谈到了java nio的一个严重BUG,并且介绍了jetty是如何规避这个BUG的。我在将这部分代码整合进yanf4j的过程中发现了不少误判的情况,让我们看看误判是怎么发生的。jetty的解决方案是通过在select返回为0的情况下,计量Selector.select(timeout)执行的时间是否与传入的timeout参数相差太大(小于timeout的一半),如果相差太大,那么认为发生一次bug,如果发生的次数超过设定值,依据严重程度进行处理:第一尝试取消任何有效并且interestOps等于0的SelectionKey;第二次就是重新创建一个Selector,并将有效的Channel注册到新的Selector。误判的发生就产生于这个时间的计量上,看javadoc可以发现它是这样描述这个方法的:

    This method performs a blocking selection operation. It returns only after at least one channel is selected, this selector's wakeup method is invoked, or the current thread is interrupted, whichever comes first

    意思就是说这个方法将阻塞select调用,直到下列三种情况之一发生才返回:至少一个channel被选中;同一个Selector的wakeup方法被调用;或者调用所处的当前线程被中断。这三种情况无论谁先发生,都将导致select(timeout)返回。因此为了减少误判,你需要将这三种情况加入判断条件。Jetty的方案已经将select返回为0的情况考虑了,但是却没有考虑线程被中断或者Selector被wakeup的情况,在jetty的运行时也许不会有这两种情况的发生,不过我在windows上用jdk 6u7跑jetty的时候就发现了误判的日志产生。除了wakeup和线程中断这两种情形外,为了进一步提高判断效率,应该将操作系统版本和jdk版本考虑进来,如果是非linux系统直接不进行后续的判断,如果是jdk6u4以后版本也直接忽略判断,因此yanf4j里的实现大致如下:
            boolean seeing = false;
            
/**
             * 非linux系统或者超过java6u4版本,直接返回
             
*/
            
if (!SystemUtils.isLinuxPlatform()
                    
|| SystemUtils.isAfterJava6u4Version()) {
                
return seeing;
            }
            
/**
             * 判断是否发生BUG的要素:
             * (1)select返回为0
             * (2)wait时间大于0
             * (3)select耗时小于一定值
             * (4)非wakeup唤醒
             *  (5)非线程中断引起
             
*/
            
if (JVMBUG_THRESHHOLD > 0 && selected == 0
                    
&& wait > JVMBUG_THRESHHOLD && now - before < wait / 4
                    
&& !this.wakenUp.get() /* waken up */
                    
&& !Thread.currentThread().isInterrupted()/* Interrupted */) {
                
this.jvmBug.incrementAndGet();

    其中判断是否是线程中断引起的是通过Thread.currentThread().isInterrupted(),判断是否是wakeup是通过一个原子变量wakenUp,当调调用Selector.wakeup时候,这个原子变量更新为true。判断操作系统和jdk版本是通过System.getProperty得到系统属性做字符串处理即可。类似的代码示例:
public static final String OS_NAME = System.getProperty("os.name");

    
private static boolean isLinuxPlatform = false;

    
static {
        
if (OS_NAME != null && OS_NAME.toLowerCase().indexOf("linux">= 0) {
            isLinuxPlatform 
= true;
        }
    }
    
public static final String JAVA_VERSION = System
            .getProperty(
"java.version");
    
private static boolean isAfterJava6u4Version = false;
    
static {
        
if (JAVA_VERSION != null) {
            
// java4 or java5
            if (JAVA_VERSION.indexOf("1.4.">= 0
                    
|| JAVA_VERSION.indexOf("1.5.">= 0)
                isAfterJava6u4Version 
= false;
            
// if it is java6,check sub version
            else if (JAVA_VERSION.indexOf("1.6.">= 0) {
                
int index = JAVA_VERSION.indexOf("_");
                
if (index > 0) {
                    String subVersionStr 
= JAVA_VERSION.substring(index + 1);
                    
if (subVersionStr != null && subVersionStr.length() > 0) {
                        
try {
                            
int subVersion = Integer.parseInt(subVersionStr);
                            
if (subVersion >= 4)
                                isAfterJava6u4Version 
= true;
                        } 
catch (NumberFormatException e) {

                        }
                    }
                }
                
// after java6
            } else
                isAfterJava6u4Version 
= true;
        }
    }


posted @ 2009-10-06 23:56 dennis 阅读(3512) | 评论 (0)编辑 收藏

    随着并发数量的提高,传统nio框架采用一个Selector来支撑大量连接事件的管理和触发已经遇到瓶颈,因此现在各种nio框架的新版本都采用多个Selector并存的结构,由多个Selector均衡地去管理大量连接。这里以Mina和Grizzly的实现为例。

   在Mina 2.0中,Selector的管理是由org.apache.mina.transport.socket.nio.NioProcessor来处理,每个NioProcessor对象保存一个Selector,负责具体的select、wakeup、channel的注册和取消、读写事件的注册和判断、实际的IO读写操作等等,核心代码如下:
   public NioProcessor(Executor executor) {
        
super(executor);
        
try {
            
// Open a new selector
            selector = Selector.open();
        } 
catch (IOException e) {
            
throw new RuntimeIoException("Failed to open a selector.", e);
        }
    }


    
protected int select(long timeout) throws Exception {
        
return selector.select(timeout);
    }

 
    
protected boolean isInterestedInRead(NioSession session) {
        SelectionKey key 
= session.getSelectionKey();
        
return key.isValid() && (key.interestOps() & SelectionKey.OP_READ) != 0;
    }


    
protected boolean isInterestedInWrite(NioSession session) {
        SelectionKey key 
= session.getSelectionKey();
        
return key.isValid() && (key.interestOps() & SelectionKey.OP_WRITE) != 0;
    }

    
protected int read(NioSession session, IoBuffer buf) throws Exception {
        
return session.getChannel().read(buf.buf());
    }


    
protected int write(NioSession session, IoBuffer buf, int length) throws Exception {
        
if (buf.remaining() <= length) {
            
return session.getChannel().write(buf.buf());
        } 
else {
            
int oldLimit = buf.limit();
            buf.limit(buf.position() 
+ length);
            
try {
                
return session.getChannel().write(buf.buf());
            } 
finally {
                buf.limit(oldLimit);
            }
        }
    }

   这些方法的调用都是通过AbstractPollingIoProcessor来处理,这个类里可以看到一个nio框架的核心逻辑,注册、select、派发,具体因为与本文主题不合,不再展开。NioProcessor的初始化是在NioSocketAcceptor的构造方法中调用的:

 public NioSocketAcceptor() {
        
super(new DefaultSocketSessionConfig(), NioProcessor.class);
        ((DefaultSocketSessionConfig) getSessionConfig()).init(
this);
    }

   直接调用了父类AbstractPollingIoAcceptor的构造函数,在其中我们可以看到,默认是启动了一个SimpleIoProcessorPool来包装NioProcessor:
protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
            Class
<? extends IoProcessor<T>> processorClass) {
        
this(sessionConfig, nullnew SimpleIoProcessorPool<T>(processorClass),
                
true);
    }

   这里其实是一个组合模式,SimpleIoProcessorPool和NioProcessor都实现了Processor接口,一个是组合形成的Processor池,而另一个是单独的类。调用的SimpleIoProcessorPool的构造函数是这样:

    private static final int DEFAULT_SIZE = Runtime.getRuntime().availableProcessors() + 1
    public SimpleIoProcessorPool(Class<? extends IoProcessor<T>> processorType) {
        
this(processorType, null, DEFAULT_SIZE);
    }
    可以看到,默认的池大小是cpu个数+1,也就是创建了cpu+1个的Selector对象。它的重载构造函数里是创建了一个数组,启动一个CachedThreadPool来运行NioProcessor,通过反射创建具体的Processor对象,这里就不再列出了。

    Mina当有一个新连接建立的时候,就创建一个NioSocketSession,并且传入上面的SimpleIoProcessorPool,当连接初始化的时候将Session加入SimpleIoProcessorPool:
    protected NioSession accept(IoProcessor<NioSession> processor,
            ServerSocketChannel handle) 
throws Exception {

        SelectionKey key 
= handle.keyFor(selector);
        
        
if ((key == null|| (!key.isValid()) || (!key.isAcceptable()) ) {
            
return null;
        }

        
// accept the connection from the client
        SocketChannel ch = handle.accept();
        
        
if (ch == null) {
            
return null;
        }

        
return new NioSocketSession(this, processor, ch);
    }

       
        
private void processHandles(Iterator<H> handles) throws Exception {
            
while (handles.hasNext()) {
                H handle 
= handles.next();
                handles.remove();

                
// Associates a new created connection to a processor,
                
// and get back a session
                T session = accept(processor, handle);
                
                
if (session == null) {
                    
break;
                }

                initSession(session, 
nullnull);

                
// add the session to the SocketIoProcessor
                session.getProcessor().add(session);
            }
        }

    加入的操作是递增一个整型变量并且模数组大小后对应的NioProcessor注册到session里:

    
private IoProcessor<T> nextProcessor() {
        checkDisposal();
        
return pool[Math.abs(processorDistributor.getAndIncrement()) % pool.length];
    }

 
   if (p == null) {
            p 
= nextProcessor();
            IoProcessor
<T> oldp =
                (IoProcessor
<T>) session.setAttributeIfAbsent(PROCESSOR, p);
            
if (oldp != null) {
                p 
= oldp;
            }
    }


    这样一来,每个连接都关联一个NioProcessor,也就是关联一个Selector对象,避免了所有连接共用一个Selector负载过高导致server响应变慢的后果。但是注意到NioSocketAcceptor也有一个Selector,这个Selector用来干什么的呢?那就是集中处理OP_ACCEPT事件的Selector,主要用于连接的接入,不跟处理读写事件的Selector混在一起,因此Mina的默认open的Selector是cpu+2个。

    看完mina2.0之后,我们来看看Grizzly2.0是怎么处理的,Grizzly还是比较保守,它默认就是启动两个Selector,其中一个专门负责accept,另一个负责连接的IO读写事件的管理。Grizzly 2.0中Selector的管理是通过SelectorRunner类,这个类封装了Selector对象以及核心的分发注册逻辑,你可以将他理解成Mina中的NioProcessor,核心的代码如下:

protected boolean doSelect() {
        selectorHandler 
= transport.getSelectorHandler();
        selectionKeyHandler 
= transport.getSelectionKeyHandler();
        strategy 
= transport.getStrategy();
        
        
try {

            
if (isResume) {
                
// If resume SelectorRunner - finish postponed keys
                isResume = false;
                
if (keyReadyOps != 0) {
                    
if (!iterateKeyEvents()) return false;
                }
                
                
if (!iterateKeys()) return false;
            }

            lastSelectedKeysCount 
= 0;
            
            selectorHandler.preSelect(
this);
            
            readyKeys 
= selectorHandler.select(this);

            
if (stateHolder.getState(false== State.STOPPING) return false;
            
            lastSelectedKeysCount 
= readyKeys.size();
            
            
if (lastSelectedKeysCount != 0) {
                iterator 
= readyKeys.iterator();
                
if (!iterateKeys()) return false;
            }

            selectorHandler.postSelect(
this);
        } 
catch (ClosedSelectorException e) {
            notifyConnectionException(key,
                    
"Selector was unexpectedly closed", e,
                    Severity.TRANSPORT, Level.SEVERE, Level.FINE);
        } 
catch (Exception e) {
            notifyConnectionException(key,
                    
"doSelect exception", e,
                    Severity.UNKNOWN, Level.SEVERE, Level.FINE);
        } 
catch (Throwable t) {
            logger.log(Level.SEVERE,
"doSelect exception", t);
            transport.notifyException(Severity.FATAL, t);
        }

        
return true;
    }

    基本上是一个reactor实现的样子,在AbstractNIOTransport类维护了一个SelectorRunner的数组,而Grizzly用于创建tcp server的类TCPNIOTransport正是继承于AbstractNIOTransport类,在它的start方法中调用了startSelectorRunners来创建并启动SelectorRunner数组:
  private static final int DEFAULT_SELECTOR_RUNNERS_COUNT = 2;
 @Override
  
public void start() throws IOException {

  
if (selectorRunnersCount <= 0) {
                selectorRunnersCount 
= DEFAULT_SELECTOR_RUNNERS_COUNT;
            }
  startSelectorRunners();

}

 protected void startSelectorRunners() throws IOException {
        selectorRunners 
= new SelectorRunner[selectorRunnersCount];
        
        
synchronized(selectorRunners) {
            
for (int i = 0; i < selectorRunnersCount; i++) {
                SelectorRunner runner 
=
                        
new SelectorRunner(this, SelectorFactory.instance().create());
                runner.start();
                selectorRunners[i] 
= runner;
            }
        }
    }

  可见Grizzly并没有采用一个单独的池对象来管理SelectorRunner,而是直接采用数组管理,默认数组大小是2。SelectorRunner实现了Runnable接口,它的start方法调用了一个线程池来运行自身。刚才我提到了说Grizzly的Accept是单独一个Selector来管理的,那么是如何表现的呢?答案在RoundRobinConnectionDistributor类,这个类是用于派发注册事件到相应的SelectorRunner上,它的派发方式是这样:

 public Future<RegisterChannelResult> registerChannelAsync(
            SelectableChannel channel, 
int interestOps, Object attachment,
            CompletionHandler completionHandler) 
            
throws IOException {
        SelectorRunner runner 
= getSelectorRunner(interestOps);
        
        
return transport.getSelectorHandler().registerChannelAsync(
                runner, channel, interestOps, attachment, completionHandler);
    }
    
    
private SelectorRunner getSelectorRunner(int interestOps) {
        SelectorRunner[] runners 
= getTransportSelectorRunners();
        
int index;
        
if (interestOps == SelectionKey.OP_ACCEPT || runners.length == 1) {
            index 
= 0;
        } 
else {
            index 
= (counter.incrementAndGet() % (runners.length - 1)) + 1;
        }
        
        
return runners[index];
    }

    getSelectorRunner这个方法道出了秘密,如果是OP_ACCEPT,那么都使用数组中的第一个SelectorRunner,如果不是,那么就通过取模运算的结果+1从后面的SelectorRunner中取一个来注册。

    分析完mina2.0和grizzly2.0对Selector的管理后我们可以得到几个启示:

1、在处理大量连接的情况下,多个Selector比单个Selector好
2、多个Selector的情况下,处理OP_READ和OP_WRITE的Selector要与处理OP_ACCEPT的Selector分离,也就是说处理接入应该要一个单独的Selector对象来处理,避免IO读写事件影响接入速度。
3、Selector的数目问题,mina默认是cpu+2,而grizzly总共就2个,我更倾向于mina的策略,但是我认为应该对cpu个数做一个判断,如果CPU个数超过8个,那么更多的Selector线程可能带来比较大的线程切换的开销,mina默认的策略并非合适,幸好可以设置这个数值。

   
   


    

posted @ 2009-10-06 16:10 dennis 阅读(5837) | 评论 (2)编辑 收藏

    这个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)中移除:
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 :D

posted @ 2009-09-28 19:27 dennis 阅读(12755) | 评论 (9)编辑 收藏

    泰山在线的周利朋友对xmemcached做了很多测试,他发现了一个比较严重的BUG,在linux平台的重连机制有时候会失效。表现的现象是这样,正常连接上memcached之后,kill掉其中的一台memcched server,xmemcached会开始自动重连这台server直到连接成功,然而事情没有像预想的那样,现象是有时候可以重连成功,有时候却没有,如果设置了connectionPoolSize,有时候建立的连接数达到connectionPoolSize,有时候却没有。他还向我描述了那时候的netstat观察到的网络情况,有比较多CLOSE_WAIT存在,这个显然是由于memcached主动断开,xmemcached被动进入CLOSE_WAIT,但是没有发送FIN的情况,如果有发送FIN那应该进入LAST_ACK而不是停留在CLOSE_WAIT。因此反应的第一个问题是xmemcached没有在接到memcached断开之后主动关闭socket发送FIN。检查代码发现其实是有这个逻辑,但是nio的channel关闭有个隐蔽的问题,就是在SelectionKey.cancel之后还需要调用select才能真正地关闭socket,这里会有个延迟,另外,为了防止CLOSE_WAIT现象的再次发生,设置SO_LINGER选项强制关闭也是必须的。做了这两个修改后,build了一个临时版本请周利朋友帮忙测试,重连失败的情况有所减轻,但是仍然会发生。因此根本的问题不在于CLOSE_WAIT的处理上,通过检查代码发现了下面这段代码:
if(!future.isDone()&&!future.get(DEFAULT_CONNECTION_TIMEOUT,TimeUnit.MILLISECONDS){
  
}
else{
   connected
=true;
}

   可能你已经发现问题在哪。这段代码的意图是通过future.get阻塞等待连接成功或者失败,如果失败做一些处理,如果成功将connected设置为true。这里判断失败有两个条件,future.isDone为false,并且future.get也返回false才认为失败,问题恰恰出在这里,因为future.isDone可能在连接的失败的情况下返回true,而这段逻辑将这种情况误判为连接成功,导致重试的请求被取消。修改很简单,将future.isDone这个条件去掉即可。
    回想起来,我也忘了当初为什么加上这个条件,这里感谢下周利的帮助,并且向使用xmemcached的朋友们提个醒。这个问题在win32平台上不会出现(比较诡异,估计跟并发有关),在linux平台出现的几率比较大,预计在10月份发布的1.2.0-stable中修正,这个stable版主要工作是修复BUG。欢迎更多朋友反馈问题和BUG,我将及时修复和反馈。

posted @ 2009-09-27 23:29 dennis 阅读(3367) | 评论 (6)编辑 收藏

    By starting at the top of the triangle below and moving to adjacent numbers on the row below, the maximum total from top to bottom is 23.

3
7 4
4 6
8 5 9 3

That is, 3 + 7 + 4 + 9 = 23.

Find the maximum total from top to bottom of the triangle below:

75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23

NOTE: As there are only 16384 routes, it is possible to solve this problem by trying every route. However, Problem 67, is the same challenge with a triangle containing one-hundred rows; it cannot be solved by brute force, and requires a clever method! ;o)

    最简单的方法就是穷举,从根节点出发,每个节点都有两个分叉,到达底部的路径有估计有2的指数级的数目(有会算的朋友请留言,我的组合数学都还给老师了),不过这道题显然是符合动态规划的特征,往下递增一层的某个节点的最佳结果f[i][j]肯定是上一层两个入口节点对应的最佳结果的最大值,也就是f[i-1][j]或者f[i-1][j+1],递归的边界就是定点f[0][0]=75。因此我的解答如下,考虑了金字塔边界的情况,数据按照金字塔型存储在numbers.txt中,

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Euler18Problem {

    
public static void maxSun(int[][] a, int rows, int cols) {
        
// 结果列表
        int[][] f = new int[15][15];
        
// 路径,用于输出计算路径
        int[][] path = new int[15][15];
        
// 递归边界
        f[0][0= a[0][0];
        path[
0][0= 0;
        
// 递推
        for (int i = 1; i < rows; i++) {
            
int col = i + 1;
            
// 决策
            for (int j = 0; j < col; j++) {
                
// 左边界
                if (j - 1 < 0) {
                    f[i][j] 
= f[i - 1][j] + a[i][j];
                    path[i][j] 
= j;
                } 
else if (j + 1 > col) { // 右边界
                    f[i][j] = f[i - 1][j - 1+ a[i][j];
                    path[i][j] 
= j - 1;
                } 
else {
                    
// 处于中间位置
                    if (f[i - 1][j] <= f[i - 1][j - 1]) {
                        f[i][j] 
= f[i - 1][j - 1+ a[i][j];
                        path[i][j] 
= j - 1;
                    } 
else {
                        f[i][j] 
= f[i - 1][j] + a[i][j];
                        path[i][j] 
= j;
                    }
                }
            }
        }
        
// 求出结果
        int result = 0, col = 0;
        
for (int i = 0; i < cols; i++) {
            
if (f[14][i] > result) {
                result 
= f[14][i];
                col 
= i;
            }
        }
        
// 输出路径
        System.out.println("row=14,col=" + col + ",value=" + a[14][col]);
        
for (int i = rows - 2; i >= 0; i--) {
            col 
= path[i][col];
            System.out.println(
"row=" + i + ",col=" + col + ",value="
                    
+ a[i][col]);
        }

        System.out.println(result);

    }

    
public static void main(String[] args) throws Exception {
        
int rows = 15;
        
int cols = 15;
        
int[][] a = new int[rows][cols];

        BufferedReader reader 
= new BufferedReader(new InputStreamReader(
                Euler18Problem.
class.getResourceAsStream("/numbers.txt")));
        String line 
= null;
        
int row = 0;
        
while ((line = reader.readLine()) != null && !line.trim().equals("")) {
            String[] numbers 
= line.split(" ");
            
for (int i = 0; i < numbers.length; i++) {
                a[row][i] 
= Integer.parseInt(numbers[i]);
            }
            row
++;
        }
        reader.close();

        maxSun(a, rows, cols);

    }
}
     执行结果如下,包括了路径输出:
row=14,col=9,value=93
row
=13,col=8,value=73
row
=12,col=7,value=43
row
=11,col=6,value=17
row
=10,col=5,value=43
row
=9,col=4,value=47
row
=8,col=3,value=56
row
=7,col=3,value=28
row
=6,col=3,value=73
row
=5,col=2,value=23
row
=4,col=2,value=82
row
=3,col=2,value=87
row
=2,col=1,value=47
row
=1,col=0,value=95
row
=0,col=0,value=75
1074

    ps.并非我闲的蛋疼在半夜做题,只是被我儿子折腾的无法睡觉了,崩溃。

posted @ 2009-09-27 02:52 dennis 阅读(1161) | 评论 (4)编辑 收藏

    本以为在上篇定稿的参数后应该能有比较好的表现,然后实际的表现大出我的意料,cms回收触发非常频繁,虽然每次都只是10-50毫秒,但是次数12个小时内能达到180多次,这显然不正常。通过gc日志和jstat可以看到,每次old区还在5%左右就开始进行CMS,此时的perm区也才30%,这两个数字有浮动并且CMS触发的时间上也没有规律,在测试环境和生产环境中都是如此。

    那么最后是怎么解决的呢?其实没有解决。我只是替换了一个参数就没再发生这个现象,上文提到为了避免System.gc()调用引起的full gc,使用了jdk6引入的新参数-XX:+ExplicitGCInvokesConcurrent来让System.gc()并发执行,但是测试表明恰恰是这个参数引起了CMS的频繁发生,去掉这个参数就没有那个奇特的现象。重复检查了代码,并且再次查看了GC日志,没有再发现有System.gc()的调用,我暂时将原因归结于使用了ExplicitGCInvokesConcurrent参数后其他方法触发了CMS,如果有知晓的朋友请留言告知,最后的方案还是彻底禁掉了显式GC调用。最终定稿的参数:

-server -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m
-XX:PermSize=64m -XX:MaxPermSize=64m -XX:+UseConcMarkSweepGC
-XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70
-XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0
-XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC
-XX:SurvivorRatio=8

    删除了+CMSPermGenSweepingEnabled,这个参数在jdk6上跟
-XX:+CMSClassUnloadingEnabled作用重叠了,如果你还跑在jdk5上面,那么应该使用这个参数。救助空间设置为NewSize的1/10,也就是25M左右,让年轻代尽量回收,防止年轻对象跑到年老代过早触发CMS甚至full gc。CMS的触发阀值下降到70%,因为年老代增长较慢,宁愿回收次数多一点,降低长暂停的可能。

    24小时内的某台生产机器的表现,通过jstat观察:
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
 
39.70   0.00   5.59  15.15  28.99  20260  326.041    14    0.592  326.633
 
39.70   0.00  65.49  15.15  28.99  20260  326.041    14    0.592  326.633
  
0.00  36.93  19.37  15.16  29.01  20261  326.059    14    0.592  326.650
  
0.00  36.93  93.23  15.16  29.01  20261  326.059    14    0.592  326.650
 
34.04   0.00  59.62  15.17  29.01  20262  326.076    14    0.592  326.668
  
0.00  38.55  12.76  15.19  29.01  20263  326.094    14    0.592  326.686
  
0.00  38.55  65.48  15.19  29.01  20263  326.094    14    0.592  326.686

    CMS两次暂停时间总和在100ms以下,minor gc平均一次执行花了16ms,平均3-4秒发生一次。暂时来看还不错,也许还可以适当调小一下NewSize,加快以下minor gc。

    此次调整总共花了大概一周多的时间,由于经验不足,还是走了不少弯路,幸好最终的结果还可以,也让自己对cms gc有比较深入的了解。我们的系统在周4晚上已经全部更新上线,从内部测试、压测、日常测试、beta测试以来,每个阶段都发现几个隐蔽的问题,在上线后暂时没有再发现问题,证明这个流程还是很有意义的,我过去对流程充满偏见,现在看来是可笑的。总结我在淘宝5个月越来学习到的东西,几个关键词:认真、负责、细心、快乐。

posted @ 2009-09-26 18:29 dennis 阅读(2094) | 评论 (1)编辑 收藏

仅列出标题
共56页: First 上一页 13 14 15 16 17 18 19 20 21 下一页 Last