Structure

Netty: ๊ตฌ์„ฑ ์š”์†Œ ๋ฐ ์„ค๊ณ„

์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” Netty์˜ ์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ์™€ ์„ค๊ณ„ ์›๋ฆฌ์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

3.1 ์ฑ„๋„, ์ด๋ฒคํŠธ ๋ฃจํ”„ ๋ฐ ์ฑ„๋„ ํ“จ์ฒ˜

Netty์˜ ์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ๋Š” Channel, EventLoop, ๊ทธ๋ฆฌ๊ณ  ChannelFuture์ž…๋‹ˆ๋‹ค. ์ด๋“ค ๊ฐ๊ฐ์ด ๋ฌด์—‡์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

3.1.1 ์ฑ„๋„(Channel)

์ฑ„๋„์€ ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์„ ์ถ”์ƒํ™”ํ•œ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. Netty์—์„œ๋Š” ๋‹ค์–‘ํ•œ ์œ ํ˜•์˜ ์ฑ„๋„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. NioSocketChannel์€ ๋น„์ฐจ๋‹จํ˜• I/O ์†Œ์ผ“ ์ฑ„๋„์ž…๋‹ˆ๋‹ค. ์ฑ„๋„์€ ๋ฐ์ดํ„ฐ๋ฅผ ์†ก์ˆ˜์‹ ํ•˜๊ธฐ ์œ„ํ•œ ์ฃผ์š” ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค.

import io.netty.channel.Channel

// ์ฑ„๋„ ์ธํ„ฐํŽ˜์ด์Šค
interface MyChannel : Channel {
    // ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹  ๋ฉ”์„œ๋“œ ์ •์˜
}

3.1.2 ์ด๋ฒคํŠธ ๋ฃจํ”„(EventLoop)

์ด๋ฒคํŠธ ๋ฃจํ”„๋Š” ์ฑ„๋„์˜ ๋ชจ๋“  ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋‹จ์ผ ์Šค๋ ˆ๋“œ์ž…๋‹ˆ๋‹ค. Netty๋Š” ์ฑ„๋„๋‹น ํ•˜๋‚˜์˜ ์ด๋ฒคํŠธ ๋ฃจํ”„๋ฅผ ํ• ๋‹นํ•˜์—ฌ, ํ•ด๋‹น ์ฑ„๋„์— ๋Œ€ํ•œ ๋ชจ๋“  I/O ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

import io.netty.channel.EventLoop

// ์ด๋ฒคํŠธ ๋ฃจํ”„ ์ธํ„ฐํŽ˜์ด์Šค
interface MyEventLoop : EventLoop {
    // ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋ฉ”์„œ๋“œ ์ •์˜
}

3.1.3 ์ฑ„๋„ ํ“จ์ฒ˜(ChannelFuture)

์ฑ„๋„ ํ“จ์ฒ˜๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ์„ ๋•Œ ์•Œ๋ฆผ์„ ๋ฐ›๊ณ , ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Netty์˜ ChannelFuture ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์˜ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด ๋ฆฌ์Šค๋„ˆ๋ฅผ ํ†ตํ•ด ์•Œ๋ฆผ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

import io.netty.channel.ChannelFuture

// ์ฑ„๋„ ํ“จ์ฒ˜ ์ธํ„ฐํŽ˜์ด์Šค
interface MyChannelFuture : ChannelFuture {
    // ๋น„๋™๊ธฐ ์ž‘์—… ์ƒํƒœ ํ™•์ธ ๋ฉ”์„œ๋“œ ์ •์˜
}

3.1.4 ๊ตฌ์„ฑ ์š”์†Œ ๋‹ค์ด์–ด๊ทธ๋žจ

3.2 ์ฑ„๋„ ํ•ธ๋“ค๋Ÿฌ ๋ฐ ์ฑ„๋„ ํŒŒ์ดํ”„๋ผ์ธ

Netty์˜ ์ฃผ์š” ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜๋Š” ChannelHandler์™€ ChannelPipeline์ž…๋‹ˆ๋‹ค.

3.2.1 ์ฑ„๋„ ํ•ธ๋“ค๋Ÿฌ(ChannelHandler)

์ฑ„๋„ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋„คํŠธ์›Œํฌ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ˆ˜์‹ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜, ์†ก์‹ ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์ค€๋น„ํ•ฉ๋‹ˆ๋‹ค. ChannelInboundHandler๋Š” ์ˆ˜์‹ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ChannelOutboundHandler๋Š” ์†ก์‹ ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter

class MyChannelHandler : ChannelInboundHandlerAdapter() {
    override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
        // ์ˆ˜์‹ ๋œ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
        println("Received message: $msg")
        ctx.writeAndFlush(msg)
    }

    override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
        cause.printStackTrace()
        ctx.close()
    }
}

3.2.2 ์ฑ„๋„ ํŒŒ์ดํ”„๋ผ์ธ(ChannelPipeline)

์ฑ„๋„ ํŒŒ์ดํ”„๋ผ์ธ์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ฑ„๋„ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฒด์ธ์œผ๋กœ ์—ฐ๊ฒฐํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ์ฑ„๋„์„ ํ†ตํ•ด ์ „์†ก๋  ๋•Œ, ๊ฐ ํ•ธ๋“ค๋Ÿฌ๋Š” ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ดํ”„๋ผ์ธ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜, ๋กœ๊น…ํ•˜๊ฑฐ๋‚˜, ์ธ์ฆ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

import io.netty.channel.ChannelInitializer
import io.netty.channel.socket.SocketChannel

class MyChannelInitializer : ChannelInitializer<SocketChannel>() {
    override fun initChannel(ch: SocketChannel) {
        ch.pipeline().addLast(MyChannelHandler())
    }
}

3.3 ๋ถ€ํŠธ์ŠคํŠธ๋ž˜ํ•‘(Bootstrapping)

๋ถ€ํŠธ์ŠคํŠธ๋ž˜ํ•‘์€ Netty ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ค์ •ํ•˜๊ณ  ์‹œ์ž‘ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๋ชจ๋‘ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ํด๋ž˜์Šค๊ฐ€ ์žˆ์œผ๋ฉฐ, ์ด๋“ค์€ ๋„คํŠธ์›Œํฌ ์„ค์ •์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

3.3.1 ์„œ๋ฒ„ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ(ServerBootstrap)

์„œ๋ฒ„ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์€ ์„œ๋ฒ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ค์ •ํ•˜๊ณ  ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„ ์†Œ์ผ“์„ ๋ฐ”์ธ๋”ฉํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.

import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioServerSocketChannel

fun startServer(port: Int) {
    val bossGroup = NioEventLoopGroup()
    val workerGroup = NioEventLoopGroup()
    try {
        val b = ServerBootstrap()
        b.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel::class.java)
            .childHandler(MyChannelInitializer())
            .option(ChannelOption.SO_BACKLOG, 128)
            .childOption(ChannelOption.SO_KEEPALIVE, true)

        val f = b.bind(port).sync()
        f.channel().closeFuture().sync()
    } finally {
        workerGroup.shutdownGracefully()
        bossGroup.shutdownGracefully()
    }
}

3.3.2 ํด๋ผ์ด์–ธํŠธ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ(Bootstrap)

ํด๋ผ์ด์–ธํŠธ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์€ ํด๋ผ์ด์–ธํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ค์ •ํ•˜๊ณ  ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ์†ก์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.

import io.netty.bootstrap.Bootstrap
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioSocketChannel

fun startClient(host: String, port: Int) {
    val group = NioEventLoopGroup()
    try {
        val b = Bootstrap()
        b.group(group)
            .channel(NioSocketChannel::class.java)
            .handler(MyChannelInitializer())

        val f = b.connect(host, port).sync()
        f.channel().closeFuture().sync()
    } finally {
        group.shutdownGracefully()
    }
}

Last updated

Was this helpful?