Provided ChannelHandlers and codecs

Netty๋ฅผ ์ด์šฉํ•œ ๋‹ค์–‘ํ•œ ํ”„๋กœํ† ์ฝœ ์ฒ˜๋ฆฌ

Netty๋Š” ๋‹ค์–‘ํ•œ ์ผ๋ฐ˜์ ์ธ ํ”„๋กœํ† ์ฝœ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋ฑ๊ณผ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋„๊ตฌ๋“ค์„ ์‚ฌ์šฉํ•˜๋ฉด SSL/TLS ๋ฐ WebSocket ์ง€์›๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ์†Œ์š”๋˜๋Š” ์‹œ๊ฐ„๊ณผ ๋…ธ๋ ฅ์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

11.1 SSL/TLS๋กœ Netty ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณดํ˜ธํ•˜๊ธฐ

์˜ค๋Š˜๋‚  ๋ฐ์ดํ„ฐ ํ”„๋ผ์ด๋ฒ„์‹œ๋Š” ์ค‘์š”ํ•œ ๋ฌธ์ œ์ด๋ฉฐ, ๊ฐœ๋ฐœ์ž๋กœ์„œ ์šฐ๋ฆฌ๋Š” ์ด๋ฅผ ํ•ด๊ฒฐํ•  ์ค€๋น„๊ฐ€ ๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์†Œํ•œ SSL๊ณผ TLS์™€ ๊ฐ™์€ ์•”ํ˜ธํ™” ํ”„๋กœํ† ์ฝœ์— ๋Œ€ํ•ด ์ต์ˆ™ํ•ด์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Netty๋Š” ์ด๋Ÿฌํ•œ ํ”„๋กœํ† ์ฝœ์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด javax.net.ssl ํŒจํ‚ค์ง€๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์•”ํ˜ธํ™” ๋ฐ ๋ณตํ˜ธํ™”๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Netty๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ SSLEngine์„ ์‚ฌ์šฉํ•˜๋Š” SslHandler๋ฅผ ์ œ๊ณตํ•˜์—ฌ ์ด๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

Netty์˜ OpenSSL/SSLEngine ๊ตฌํ˜„

Netty๋Š” OpenSSL ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” OpenSslEngine ๊ตฌํ˜„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” JDK์˜ SSLEngine ๊ตฌํ˜„๋ณด๋‹ค ๋” ๋‚˜์€ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. OpenSSL ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•  ๊ฒฝ์šฐ Netty ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๊ธฐ๋ณธ์ ์œผ๋กœ OpenSslEngine์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด JDK ๊ตฌํ˜„์œผ๋กœ ๋Œ€์ฒด๋ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ํ๋ฆ„

SSL/TLS ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•œ ๋ฐ์ดํ„ฐ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • ์•”ํ˜ธํ™”๋œ ์ˆ˜์‹  ๋ฐ์ดํ„ฐ๋Š” SslHandler์— ์˜ํ•ด ๊ฐ€๋กœ์ฑ„์–ด์ ธ ๋ณตํ˜ธํ™”๋œ ํ›„ ๋‚ด๋ถ€๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

  • ๋ฐœ์‹  ๋ฐ์ดํ„ฐ๋Š” SslHandler๋ฅผ ํ†ตํ•ด ์•”ํ˜ธํ™”๋œ ํ›„ ์™ธ๋ถ€๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

SSL/TLS ์ง€์› ์ถ”๊ฐ€ํ•˜๊ธฐ

๋‹ค์Œ ์ฝ”๋“œ๋Š” SslHandler๋ฅผ ChannelPipeline์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ChannelInitializer๋Š” Channel์ด ๋“ฑ๋ก๋  ๋•Œ ChannelPipeline์„ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

public class SslChannelInitializer extends ChannelInitializer<Channel> {
    private final SslContext context;
    private final boolean startTls;

    public SslChannelInitializer(SslContext context, boolean startTls) {
        this.context = context;
        this.startTls = startTls;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        SSLEngine engine = context.newEngine(ch.alloc());
        ch.pipeline().addFirst("ssl", new SslHandler(engine, startTls));
    }
}

11.2 Netty HTTP/HTTPS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ•

HTTP/HTTPS๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ํ”„๋กœํ† ์ฝœ ์ค‘ ํ•˜๋‚˜์ด๋ฉฐ, Netty๋Š” ์ด ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ์ธ์ฝ”๋”์™€ ๋””์ฝ”๋”๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

HTTP ๋””์ฝ”๋”, ์ธ์ฝ”๋” ๋ฐ ์ฝ”๋ฑ

public class HttpPipelineInitializer extends ChannelInitializer<Channel> {
    private final boolean client;

    public HttpPipelineInitializer(boolean client) {
        this.client = client;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (client) {
            pipeline.addLast("decoder", new HttpResponseDecoder());
            pipeline.addLast("encoder", new HttpRequestEncoder());
        } else {
            pipeline.addLast("decoder", new HttpRequestDecoder());
            pipeline.addLast("encoder", new HttpResponseEncoder());
        }
    }
}

HTTP ๋ฉ”์‹œ์ง€ ์ง‘๊ณ„

HTTP ์š”์ฒญ ๋ฐ ์‘๋‹ต์€ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Netty๋Š” ๋ฉ”์‹œ์ง€ ๋ถ€๋ถ„์„ FullHttpRequest ๋ฐ FullHttpResponse ๋ฉ”์‹œ์ง€๋กœ ๋ณ‘ํ•ฉํ•˜๋Š” ์ง‘๊ณ„๊ธฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

public class HttpAggregatorInitializer extends ChannelInitializer<Channel> {
    private final boolean isClient;

    public HttpAggregatorInitializer(boolean isClient) {
        this.isClient = isClient;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (isClient) {
            pipeline.addLast("codec", new HttpClientCodec());
        } else {
            pipeline.addLast("codec", new HttpServerCodec());
        }
        pipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024));
    }
}

HTTP ์••์ถ•

Netty๋Š” ๋ฐ์ดํ„ฐ ์••์ถ•์„ ์ง€์›ํ•˜๋Š” ChannelHandler ๊ตฌํ˜„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

public class HttpCompressionInitializer extends ChannelInitializer<Channel> {
    private final boolean isClient;

    public HttpCompressionInitializer(boolean isClient) {
        this.isClient = isClient;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (isClient) {
            pipeline.addLast("codec", new HttpClientCodec());
            pipeline.addLast("decompressor", new HttpContentDecompressor());
        } else {
            pipeline.addLast("codec", new HttpServerCodec());
            pipeline.addLast("compressor", new HttpContentCompressor());
        }
    }
}

11.3 WebSocket ์ง€์›

Netty๋Š” WebSocket ์ง€์›์„ ์œ„ํ•œ ๊ด‘๋ฒ”์œ„ํ•œ ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

public class WebSocketServerInitializer extends ChannelInitializer<Channel> {
    @Override
    protected void initChannel(Channel ch) throws Exception {
        ch.pipeline().addLast(
            new HttpServerCodec(),
            new HttpObjectAggregator(65536),
            new WebSocketServerProtocolHandler("/websocket"),
            new TextFrameHandler(),
            new BinaryFrameHandler(),
            new ContinuationFrameHandler()
        );
    }
}

11.4 ์œ ํœด ์—ฐ๊ฒฐ ๋ฐ ํƒ€์ž„์•„์›ƒ

Netty๋Š” ์œ ํœด ์—ฐ๊ฒฐ ๋ฐ ํƒ€์ž„์•„์›ƒ์„ ๊ฐ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์—ฌ๋Ÿฌ ChannelHandler ๊ตฌํ˜„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

IdleStateHandler ์˜ˆ์ œ

public class IdleStateHandlerInitializer extends ChannelInitializer<Channel> {
    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new IdleStateHandler(0, 0, 60, TimeUnit.SECONDS));
        pipeline.addLast(new HeartbeatHandler());
    }

    public static final class HeartbeatHandler extends ChannelInboundHandlerAdapter {
        private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(
            Unpooled.copiedBuffer("HEARTBEAT", CharsetUtil.ISO_8859_1));

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (evt instanceof IdleStateEvent) {
                ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            } else {
                super.userEventTriggered(ctx, evt);
            }
        }
    }
}

11.5 ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์“ฐ๊ธฐ

Netty๋Š” NIO์˜ ์ œ๋กœ ๋ณต์‚ฌ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜์—ฌ ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ๋„คํŠธ์›Œํฌ๋กœ ์ „์†กํ•  ๋•Œ ๋ณต์‚ฌ ๋‹จ๊ณ„๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

FileInputStream in = new FileInputStream(file);
FileRegion region = new DefaultFileRegion(in.getChannel(), 0, file.length());
channel.writeAndFlush(region).addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        if (!future.isSuccess()) {
            Throwable cause = future.cause();
            // ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
        }
    }
});

11.6 ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”

Netty๋Š” ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™” ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

JDK ์ง๋ ฌํ™”

public class JdkSerializationInitializer extends ChannelInitializer<Channel> {
    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new ObjectEncoder());
        pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
    }
}

JBoss Marshalling

public class MarshallingInitializer extends ChannelInitializer<Channel> {
    private final MarshallerProvider marshallerProvider;
    private final UnmarshallerProvider unmarshallerProvider;

    public MarshallingInitializer(UnmarshallerProvider unmarshallerProvider, MarshallerProvider marshallerProvider) {
        this.unmarshallerProvider = unmarshallerProvider;
        this.marshallerProvider = marshallerProvider;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new MarshallingDecoder(unmarshallerProvider));
        pipeline.addLast(new MarshallingEncoder(marshallerProvider));
        pipeline.addLast(new ObjectHandler());
    }
}

ํ”„๋กœํ† ์ฝœ ๋ฒ„ํผ

public class ProtoBufInitializer extends ChannelInitializer<Channel> {
    private final MessageLite lite;

    public ProtoBufInitializer(MessageLite lite) {
        this.lite = lite;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new ProtobufVarint32FrameDecoder());
        pipeline.addLast(new ProtobufEncoder());
        pipeline.addLast(new ProtobufDecoder(lite));
        pipeline.addLast(new ObjectHandler());
    }
}

Last updated

Was this helpful?