Criação de um Servidor Netty

O framework Netty simplifica a implementação de serivdores de rede de alto desempenho. O processo de inicialização envolve a configuração de componentes essenciais como pools de threads, canais e pipelines de processamento.

A inicialização começa com a instanciação de um ServerBootstrap, que serve como um guia para configuração dos parâmetros do servidor. Em seguida, dois pools de threads, representados por EventLoopGroup, são criados. Um gerencia a aceitação de conexões (geralmente chamado de "boss"), e o outro manipula as operações de I/O e a lógica de negócio dos canais conectados.

ServerBootstrap bootstrap = new ServerBootstrap();
EventLoopGroup connectionAcceptor = new NioEventLoopGroup(1);
EventLoopGroup ioWorker = new NioEventLoopGroup();

O método group associa esses pools ao bootstrap. A classe do canal do servidor (NioServerSocketChannel) e um inicializador para os canais filhos (ChannelInitializer) são configurados encadeadamente.

bootstrap.group(connectionAcceptor, ioWorker)
         .channel(NioServerSocketChannel.class)
         .childHandler(new ProtocolHandlerInitializer());

O ProtocolHandlerInitializer é responsável por configurar o pipeline de cada nova conexão de cliente. O pipeline é uma cadeia de manipuladores (ChannelHandler) que processam os eventos de rede, como codificação/decodificação e lógica da aplicação.

public class ProtocolHandlerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel clientChannel) {
        ChannelPipeline pipeline = clientChannel.pipeline();
        pipeline.addLast("httpCodec", new HttpServerCodec());
        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
        pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
        pipeline.addLast("businessLogic", new WebSocketBusinessHandler());
    }
}

O WebSocketBusinessHandler implementa a interface ChannelInboundHandler para definir o comportamento da aplicação. Ele inspeciona mensagens recebidas, como requisições HTTP ou quadros WebSocket, e gera respostas adequadas. Este é o ponto onde a lógica específica da aplicação é inserida.

public class WebSocketBusinessHandler extends SimpleChannelInboundHandler<Object> {
    private WebSocketServerHandshaker handshaker;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof FullHttpRequest) {
            processHttp((FullHttpRequest) msg, ctx);
        } else if (msg instanceof WebSocketFrame) {
            processWebSocket((WebSocketFrame) msg, ctx);
        }
    }

    private void processHttp(FullHttpRequest request, ChannelHandlerContext ctx) {
        // Lógica para handshake do WebSocket
        String wsUrl = "ws://localhost:" + 8080 + "/ws-endpoint";
        WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory(wsUrl, null, true);
        this.handshaker = factory.newHandshaker(request);
        if (this.handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            this.handshaker.handshake(ctx.channel(), request);
        }
    }

    private void processWebSocket(WebSocketFrame frame, ChannelHandlerContext ctx) {
        if (frame instanceof CloseWebSocketFrame) {
            this.handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        if (frame instanceof TextWebSocketFrame) {
            String receivedText = ((TextWebSocketFrame) frame).text();
            String response = receivedText + ", bem-vindo ao servidor WebSocket!";
            ctx.channel().write(new TextWebSocketFrame(response));
        }
    }
}

Finalmente, após toda a configuração, o servidor é efetivamente iniciado, vinculando-o a uma porta de rede. O bind inicia a escuta, e o mecanismo de Selector do NIO começa a monitorar os eventos de I/O, delegando-os para as respectivas EventLoop e seus pipelines de handlers para processamento.

Channel serverChannel = bootstrap.bind(8080).sync().channel();
serverChannel.closeFuture().sync();

Tags: Netty nio WebSocket ServerBootstrap ChannelPipeline

Publicado em 6-11 00:32 por Thomas