2015-12-13 117 views
3

我正在編寫一個基於Netty的本地HTTP服務器。當我進行壓力測試時,我受限於400個請求/秒。Netty應用程序優化

爲了優化我的服務器,我編寫了一個基於Netty的簡單服務器,它只是向客戶端發送「Hello World」,並且我使用Gatling 2啓動了一個壓力測試,並且使用此服務器,相同的結果(限於400次/秒)。

我使用Yourkit進行分析,沒有額外的GC活動,並且我的開放/關閉套接字被限制爲480個套接字/秒。

我使用MacBook Pro,有4個內核,16 GB的RAM,我使用Netty 4.1。

由於其他基準測試的結果顯示> 20 000請求/秒或更多,我很驚訝被限制在400請求/秒。我知道有硬件限制,但是對於在4核+16 GB Ram上發送「hello World」的請求數是400 req/s非常低。

非常感謝您的幫助,我不知道從哪裏開始優化我的Netty代碼。

有沒有優化Netty的具體指導方針?

這裏我的Hello World服務器的源代碼,然後我連接的處理程序:

public class TestServer { 

    private static final Logger logger = LogManager.getLogger("TestServer"); 

    int nbSockets = 0 ; 

    EventLoopGroup pool = new NioEventLoopGroup() ; 

    private void init(int port) { 

     EventLoopGroup bossGroup = new NioEventLoopGroup(100) ; 


     try { 
      long t1 = System.currentTimeMillis() ; 
      ServerBootstrap b = new ServerBootstrap().group(bossGroup); 

      b.channel(NioServerSocketChannel.class) 
        .childHandler(new ChannelInitializer<SocketChannel>() { 
         @Override 
         public void initChannel(SocketChannel ch) throws Exception { 
          ch.pipeline().addLast("decoder", new HttpRequestDecoder(8192, 8192 * 2, 
            8192 * 2)); 
          ch.pipeline().addLast("encoder", new HttpResponseEncoder()); 

          ch.pipeline().addLast(new TestServerHandler(TestServer.this)); 
         } 

         @Override 
         public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
          System.err.println("Error"); 
          super.exceptionCaught(ctx,cause); 
         } 


        }) 
        .option(ChannelOption.SO_BACKLOG, 100000) 
        .option(ChannelOption.SO_KEEPALIVE,false) 
        .option(ChannelOption.TCP_NODELAY,false) 
        .option(ChannelOption.SO_REUSEADDR,true) 
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS,10000) 
        .childOption(ChannelOption.SO_KEEPALIVE, true); 

      ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);; 

      scheduler.scheduleAtFixedRate(new Runnable() { 
       @Override 
       public void run() { 
        System.err.println(nbSockets); 
        nbSockets = 0 ; 
       } 
      },1, 1,TimeUnit.SECONDS) ; 


      // Bind and start to accept incoming connections. 
      ChannelFuture f = b.bind(port).sync(); 

      f.channel().closeFuture().sync(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } finally { 
      bossGroup.shutdownGracefully(); 
      System.err.println("Coucou"); 
     } 
    } 

    public static void main(String[] args) { 
     TestServer testServer = new TestServer() ; 
     testServer.init(8888); 
    } 
} 

,這裏是我的處理程序的源代碼:

public class TestServerHandler extends ChannelInboundHandlerAdapter { 
    private final TestServer testServer; 

    public TestServerHandler(TestServer testServer) { 
     this.testServer = testServer ; 
    } 


    @Override 
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
     try { 
      process(ctx, msg); 
     } catch (Throwable e) { 
      e.printStackTrace(); 
     } 


    } 

    public void process(ChannelHandlerContext ctx, Object msg) throws Exception { 
     ctx.channel().writeAndFlush(buildHttpResponse()).addListener(new GenericFutureListener<Future<? super Void>>() { 
      @Override 
      public void operationComplete(Future<? super Void> future) throws Exception { 
       ctx.channel().close() ; 
       testServer.nbSockets ++ ; 
      } 
     }) ; 
    } 

    public DefaultFullHttpResponse buildHttpResponse() { 

     String body = "hello world" ; 
     byte[] bytes = body.getBytes(Charset.forName("UTF-8")); 
     ByteBuf byteContent = Unpooled.copiedBuffer(bytes); 
     HttpResponseStatus httpResponseStatus =HttpResponseStatus.OK; 

     DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, 
       httpResponseStatus, byteContent); 


     return httpResponse; 
    } 

} 
+0

我在你的代碼上本地運行apache bench:'ab -n 100000 -c 100 http:// localhost:8888 /'。它報告了超過16000個請求/秒 –

+0

謝謝你,你是對的。我會檢查一下爲什麼我沒有和gatling一樣的結果。 – bsh

+0

@四十二你是如何設法得到這麼高的數字的?在我的筆記本電腦(OSX)上運行這個相同的ab會導致連接在〜16.500連接後被拒絕。你的機器是什麼樣的?你是如何設置TCP堆棧的? –

回答

5

您已停用保持活動狀態,並在每個請求中關閉連接,所以我懷疑你大部分時間都是打開和關閉HTTP連接。

由於其他基準測試結果表明> 20 000 REQ /秒,或者更

你指的是哪一個其他基準?他們很有可能將連接池連接起來,並使用HTTP流水線技術,因此與您的使用情況截然不同。

回到你原來的問題(如何優化Netty的),有兩大類的東西,你可以這樣做:

  • 微優化分配:使用集中的ByteBuffers,甚至更好的計算他們只有一次
  • 切換到native epoll transport(僅Linux)

但是,所有這些改進可能不會比連接處理多得多。

+0

謝謝Stephane的回答。我更改了keep-alive參數並將其設置爲true,但它不會改變我的測試結果。當我從不同的客戶端獲得連接時,保持活着是否會改變現實生活中的應用?我的猜測是,當我有來自同一客戶端的連接時,保持活躍狀態​​是適用的?無論如何,是一個java服務器可以處理的限制是400個req​​/s嗎?我改變了java.nio.channels.spi.SelectorProvider參數,將它放到「sun.nio.ch.KQueueSelectorProvider」(Mac)中,我沒有任何改變。 – bsh

+0

最後一句話,當我切換回舊io(阻止io)時,我得到了相同的結果。我的代碼中有一些錯誤。 – bsh

+0

您正在手動關閉連接:ctx.channel()。close(); –