2016-12-26 93 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次请求后,您的浏览器必须拒绝创建新的连接。

我注意到的另一件事是,您正在为服务器中的每个请求创建一个新线程,并且永远不会让它死亡。当您尝试缩放时,这会导致问题。如果你真的想让代码在不同的线程中运行,那么我建议你查看重载的方法来向管道添加处理程序;那些应该让你指定一个线程池来运行它们。

相关问题