<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

       一張截圖,Java雖然號稱跨平臺,然而涉及到跟網絡相關時,還是依賴于各個平臺的實現。對寫java網絡編程的朋友有點價值。


    posted @ 2009-10-12 17:52 dennis 閱讀(2101) | 評論 (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 閱讀(1641) | 評論 (0)編輯 收藏

        喜歡奇幻的朋友可以瞧瞧,據說是煙大推薦的,同樣在17K上,國人寫的西方奇幻,還未寫完,但是長度已經足夠你好好享受。味道呢?戰斗類似《博德之門》,語言比較繞,沒讀過此類作品的可能不習慣,文筆沒得說,寫作水準暫未發現下降趨勢,值得長期追。名字比較奇怪,《昆古尼爾》,是北歐神話中大神奧丁的所有物——永恒之槍。故事就不劇透了,前兩章可能比較暈,但是堅持看下去就能搞明白啦。如果你實在著急呢,可以看看這個背景介紹。

    posted @ 2009-10-10 00:44 dennis 閱讀(553) | 評論 (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 閱讀(3130) | 評論 (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 閱讀(3514) | 評論 (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 閱讀(5845) | 評論 (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 閱讀(12796) | 評論 (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 閱讀(3389) | 評論 (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 閱讀(1172) | 評論 (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 閱讀(2099) | 評論 (1)編輯 收藏

    僅列出標題
    共56頁: First 上一頁 13 14 15 16 17 18 19 20 21 下一頁 Last 
    主站蜘蛛池模板: 免费大片av手机看片高清| 久久精品国产精品亚洲精品| 最近最新中文字幕完整版免费高清| 久久国产乱子伦精品免费不卡| aa在线免费观看| fc2成年免费共享视频18| 免费精品国产自产拍在线观看| 精品无码专区亚洲| 亚洲AV成人片无码网站| 亚洲乱码无人区卡1卡2卡3| 亚洲日韩AV一区二区三区四区| 一区二区亚洲精品精华液 | 国产亚洲精品a在线观看| 国产成人毛片亚洲精品| 夜夜春亚洲嫩草影院| 亚洲免费人成在线视频观看| 久久精品7亚洲午夜a| 久久亚洲sm情趣捆绑调教 | 免费吃奶摸下激烈视频| 亚洲国产黄在线观看| 中文亚洲成a人片在线观看| 亚洲精品乱码久久久久久久久久久久| 亚洲情综合五月天| 亚洲综合在线观看视频| 亚洲国产精品午夜电影| 亚洲欧美自偷自拍另类视| 国产亚洲精品精品精品| EEUSS影院WWW在线观看免费| 青青草原1769久久免费播放| 亚洲高清视频免费| 香蕉高清免费永久在线视频| 成人亚洲网站www在线观看| 欧洲亚洲综合一区二区三区| 美女视频免费看一区二区| 在线免费视频你懂的| 最近免费最新高清中文字幕韩国| 无码中文在线二区免费| 亚洲成a人片在线观看久| 亚洲AV无码1区2区久久| 亚洲熟女精品中文字幕| 一级毛片**免费看试看20分钟|