2016-12-26 1 views
0

Netty를 처음 사용하고 브라우저 연결 클라이언트로 서버 전송 이벤트를 보내기 위해 http 연결을 열어 두는 Netty http 서버를 찾은 예제를 기반으로 작성했습니다.열린 http 연결 제한이있는 Netty 서버

문제는 최대 약 5 개의 연결 만 허용하고 이후에는 새 연결을 차단한다는 것입니다. 내가 봤 거든 대부분의 답변은 SO_LOGBACK 높은 가치를 설정했다. 다른 값을 시도하면서 나는 아무런 차이도 보지 못했습니다. MAX_INTEGER 값으로 설정하고 여전히 5 개의 연결 만 있습니다.

서버 코드 (사용의 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. 당신은 그 중 하나를 수행하지 않는 당신이

을 완료하면

  • 그것이
  • 닫기 연결을 청크 보내기 콘텐츠 길이를 설정합니다. 귀하의 브라우저가 귀하가 보내는 각 요청에 대한 완전한 응답을 아직 기다리고 있으며 귀하의 테스트에서 각 요청에 대해 새로운 연결을 사용하고있는 것으로 의심됩니다. 5 개의 요청 후에 브라우저가 새로운 연결을 거부해야합니다.

    내가 알아챈 또 다른 사실은 서버의 각 요청에 대해 새 스레드를 만들고 그것을 절대 버리지 않도록한다는 것입니다. 그러면 규모를 조정할 때 문제가 발생할 수 있습니다. 이 코드를 다른 스레드에서 실행하려면 파이프 라인에 핸들러를 추가하는 오버로드 된 메서드를 살펴 보는 것이 좋습니다. 스레드 풀을 지정하면 스레드 풀을 실행할 수 있습니다.