2016-12-26 94 views
0

我是Netty的新手,我基於一個示例編寫了一個Netty http服務器,它使http連接保持打開狀態,以便向瀏覽器客戶端發送服務器發送的事件。打開http連接的Netty服務器限制

問題是它只接受約5個連接,之後阻塞新的連接。我GOOGLE了,發現大多數答案說SO_LOGBACK設置爲一個更高的值。試過不同的價值觀,雖然我沒有看到任何區別。我甚至將它設置爲MAX_INTEGER值,並且仍然只有5個連接。

Server代碼(使用了Netty版本4.1.6.Final):

package server; 

import static io.netty.buffer.Unpooled.copiedBuffer; 

import io.netty.bootstrap.ServerBootstrap; 
import io.netty.channel.ChannelFuture; 
import io.netty.channel.ChannelHandlerContext; 
import io.netty.channel.ChannelInboundHandlerAdapter; 
import io.netty.channel.ChannelInitializer; 
import io.netty.channel.ChannelOption; 
import io.netty.channel.EventLoopGroup; 
import io.netty.channel.nio.NioEventLoopGroup; 
import io.netty.channel.socket.SocketChannel; 
import io.netty.channel.socket.nio.NioServerSocketChannel; 
import io.netty.handler.codec.http.DefaultFullHttpResponse; 
import io.netty.handler.codec.http.FullHttpResponse; 
import io.netty.handler.codec.http.HttpHeaders; 
import io.netty.handler.codec.http.HttpObjectAggregator; 
import io.netty.handler.codec.http.HttpResponseStatus; 
import io.netty.handler.codec.http.HttpServerCodec; 
import io.netty.handler.codec.http.HttpVersion; 

public class NettyHttpServer { 
private ChannelFuture channel; 
private final EventLoopGroup masterGroup; 

public NettyHttpServer() { 
    masterGroup = new NioEventLoopGroup(100); 
} 

public void start() { 
    try { 
    final ServerBootstrap bootstrap = new ServerBootstrap().group(masterGroup) 
    .channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer <SocketChannel>() { 
    @Override 
    public void initChannel(final SocketChannel ch) throws Exception { 
     ch.pipeline().addLast("codec", new HttpServerCodec()); 
     ch.pipeline().addLast("aggregator", new HttpObjectAggregator(512 * 1024)); 
     ch.pipeline().addLast("request", new ChannelInboundHandlerAdapter() { 
     @Override 
     public void channelRead(final ChannelHandlerContext ctx, final Object msg) 
     throws Exception { 
     System.out.println(msg); 
     registerToPubSub(ctx, msg); 
     } 

     @Override 
     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 
     ctx.flush(); 
     } 

     @Override 
     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
     ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, 
     HttpResponseStatus.INTERNAL_SERVER_ERROR, 
     copiedBuffer(cause.getMessage().getBytes()))); 
     } 
     }); 
    } 
    }).option(ChannelOption.SO_BACKLOG, Integer.MAX_VALUE) 
    .childOption(ChannelOption.SO_KEEPALIVE, true); 
    channel = bootstrap.bind(8081).sync(); 
    // channels.add(bootstrap.bind(8080).sync()); 
    } catch (final InterruptedException e) {} 
} 

public void shutdown() { 
    masterGroup.shutdownGracefully(); 

    try { 
    channel.channel().closeFuture().sync(); 
    } catch (InterruptedException e) {} 
} 

private void registerToPubSub(final ChannelHandlerContext ctx, Object msg) { 
    new Thread() { 
    @Override 
    public void run() { 
    while (true) { 
    final String responseMessage = "data:abcdef\n\n"; 
    FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, 
     copiedBuffer(responseMessage.getBytes())); 

    response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); 
    response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/event-stream"); 
    response.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, "*"); 
    response.headers().set("Cache-Control", "no-cache"); 

    ctx.writeAndFlush(response); 

    try { 
     Thread.sleep(1000); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
    } 
    }; 
    }.start(); 
} 

public static void main(String[] args) { 
    new NettyHttpServer().start(); 
} 
} 

客戶端的js代碼(我運行它從我的瀏覽器超過5次在不同的標籤,而不是所有的人得到:

var source = new EventSource("http://localhost:8081"); 
source.onmessage = function(event) { 
    console.log(event.data); 
}; 
source.onerror= function(err){console.log(err); source.close()}; 
source.onopen = function(event){console.log('open'); console.log(event)} 

回答

1

你需要讓瀏覽器知道你完成發送響應,和你有三種選擇。

  1. 設置內容長度
  2. 把它分塊
  3. 關閉連接,當你完成

你沒有做任何的那些。我懷疑您的瀏覽器仍在等待您發送的每個請求的完整響應,並且正在測試中爲每個請求使用新的連接。 5次請求後,您的瀏覽器必須拒絕創建新的連接。

我注意到的另一件事是,您正在爲服務器中的每個請求創建一個新線程,並且永遠不會讓它死亡。當您嘗試縮放時,這會導致問題。如果你真的想讓代碼在不同的線程中運行,那麼我建議你查看重載的方法來向管道添加處理程序;那些應該讓你指定一個線程池來運行它們。

相關問題