Netty는 현재 제가 개발하고 있는 채팅형 시스템에도 사용되고있고 수십년간 이벤트 기반 애플리케이션에 꾸준하게 사용되어왔습니다. 웬만한 대규모 분산 시스템 및 클라우드 애플리케이션에 사용되고 있습니다. 오늘은 Netty에 대해서 정리해보겠습니다.
[목차]
- 서버 프레임워크 netty 란?
- 서버 프레임워크 netty 설치
- 서버 프레임워크 netty Server 구현
- 서버 프레임워크 netty Client 구현
1. 서버 프레임워크 Netty란?
Netty는 Java기반의 네트워크 프레임워크입니다. HTTP, WebSocket, TCP 같은 프로토콜은 안정성과 확장성, 고성능으로 작동시키며 대규모 분산 시스템에도 자주 쓰입니다. TCP를 쓰면서 Netty를 쓰지 않는 곳은 별로 못봤습니다.
Netty의 구조는 크게 3가지 구조로 되어있습니다.
첫째는 응용프로그램 레이어로서 실제 비즈니스로직이 여기 포함되며 네트워크로 넘어오는 데이터들을 처리합니다. 여기서는 받아오는 데이터를 암호화 또는 복호화시키고 메시지를 프레임으로 나누는 메시지프레이밍 등 실제 이 데이터를 가지고 응용하는 로직을 구성합니다.
둘째는 전송 레이어로 Low level의 네트워크 프로토콜을 추상화한 후 API를 제공하여 단순하게 송수신합니다. 여기서 TCP, UDP, HTTP, WebSockets 등 데이터를 송수신하는 API를 마련합니다.
셋째는 네트워크 레이어로 네트워크 세부정보를 파악하여 입출력 관리를 하고 연결이 잘 되었는지 확인합니다. 이 과정을 Netty에서는 Eventloop를 확인하여 관리합니다. 업무를 효율적이고 확장 가능한 방식으로 연결을 처리합니다.
각각 모듈화되어 있어서 상호호환하며 네트워크 레이어에서 연결에 대한 부분을 담당하고 전송 레이어에서는 어떻게 데이터를 담을 것인지 판단하고 데이터의 모습을 그립니다. 마지막으로 응용프로그램 레이어에서는 받아올 데이터에 대한 가공 및 보안처리를 합니다.
Netty는 네트워크 시스템을 쉽게 구축하도록 사용자를 도와주도록 설계되었으며 보면 볼수록 잘 만들었다고 느껴집니다. 계속 Netty를 파보고 있는데 어려운 점도 있지만 조금만 더 보면 이해하는데 무리가 없을 것입니다.
2. 서버 프레임워크 Netty 설치
보통 Netty를 사용하면 Java를 씁니다. Java에 많이 사용하는 IDE는 이클립스나 Intellij 입니다. 저는 무료인 이클립스를 사용해서 그 기준으로 설명하겠습니다.
Netty를 설치하는 방법에는 2가지가 있습니다. Netty를 다운로드 받아서 jar확장자로 추가하거나 라이브러리를 추가하는 방법입니다. 라이브러리를 추가하는 방식은 Maven, Gradle을 이용합니다.
Netty jar파일은 https://netty.io/downloads.html 에서 다운받을 수 있습니다. Netty에는 버전이 많은데 요즘은 0.4.0을 잘 사용하지 않습니다. http2 방식을 지원하지 않기 때문입니다.
Eclipse에서 Project를 우클릭하면 메뉴가 나오는데 Build Path -> Configure Build Path를 선택해줍니다. 그 다음 Libraries 탭을 선택한 후 Add JARs 버튼을 클릭해줍니다. 다운받은 jar파일을 선택하고 Open 한 후 Apply and Close로 닫아줍니다.
다음으로 버전관리 툴인 Maven과 Gradle을 통한 방식입니다.
Maven을 이용할 경우 pom.xml 파일에 추가해줍니다.
// pom.xml 파일에 추가
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.65.Final</version>
</dependency>
</dependencies>
gradle을 사용할 경우 build.gradle에다 추가해줍니다. gradle은 Android studio에도 자주쓰니 알아두면 좋습니다.
dependencies {
implementation 'io.netty:netty-all:4.1.65.Final'
}
3. 서버 프레임워크 Netty Server 구현
Netty로 Server를 만드는 일은 아주 쉽고 안정성 있습니다. 에러에 대한 함수처리, 비동기처리, 채널 관련 함수가 있기 때문에 개발하는 것은 간단합니다.
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
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;
public class NettyServer {
private final int port;
public NettyServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture future = bootstrap.bind(port).sync();
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8000;
NettyServer server = new NettyServer(port);
server.run();
}
}
8000번 포트로 Netty Server를 만드는 코드입니다.
ChannelFuture를 통해서 비동기적인 방식으로 입출력처리를 관리해서 전송 작업이 성공했는지 실패했는지 알 수 있습니다. 비동기식 작업에서 높은 성능을 발휘합니다.
SeverBootStrap으로 서버를 구성하기 위한 설정을 하고 EventLoopGroup으로 클라이언트의 연결 요청을 수신합니다. 또한 클라이언트마다 Channel을 생성해주고 Channel이 초기화되었다면 위와 같은 initChannel을 handler로 받아 초기화되었다는 것을 알립니다.
ChildOption의 KeepAlive는 연결이 여전히 살아있다는 Heartbeat 메시지를 TCP/IP에 간헐적으로 보낼 수 있게 해주는 SocketChannel의 옵션입니다.
4. 서버 프레임워크 Netty Client 구현
위에서 만들었던 Netty 서버에 대한 클라이언트를 구현할 것입니다. 클라이언트 또한 Netty Server처럼 간단하고 안정성 있게 구현할 수 있으며 쉽게 만들 수 있습니다.
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class NettyClient {
private final String host;
private final int port;
public NettyClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler());
}
});
ChannelFuture f = b.connect(host, port).sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
NettyClient client = new NettyClient("localhost", 8000);
client.run();
}
}
위의 서버를 실행한 후 클라이언트도 실행한다면 서버에 접속이 될 것입니다. netty의 진가는 실시간으로 데이터를 주고받는 시스템에서 나옵니다.
우선 BootStrap을 이용해서 클라이언트를 설정하고 NioEventLoopGroup을 통해 이벤트루프를 만듭니다. NioSocketChannel을 통해 클라이언트 소켓 채널을 설정하고 channel을 초기화시킵니다. 마지막으로 ChannelFuture을 통해서 서버에 연결 성공했는지 따져줍니다.
채널에서 데이터를 주고 받을 때에는 io.netty.channel.ChannelInboundHandlerAdapter를 사용하여 수신 받는 모든 데이터를 처리합니다. 주고 받을 때에는 ByteBuf를 사용합니다. ByteBuf는 ByteBuffer와 유사하지만 읽기 및 쓰기에 용이한 특징을 가지고 있고 다양한 API를 제공해서 데이터를 조작할 수 있습니다. 메모리 할당 제어도 가능하여 프로토콜을 배분하고 주고 받을 때 용이합니다.
데이터를 전송할 때 정해야할 프로토콜은 API 명세서와 비슷하게 나름의 숫자로 작성합니다. Netty Server에서도 동일하게
io.netty.channel.ChannelInboundHandlerAdapter를 상속받아 ByteBuf를 통해서 데이터 값을 주고 받습니다.
[함께 읽으면 좋은 글]
2023.02.08 - [취미로 코딩하기] - 사이드 프로젝트 플랫폼, 코딩 사이트 추천
2023.01.30 - [취미로 코딩하기] - Nodejs이란, Nodejs 설치, 웹서버 정리
'프로그래밍_' 카테고리의 다른 글
Azure 가상머신 Mysql 설치, workbench 사용하기 (0) | 2023.02.24 |
---|---|
클라우드 서버, AWS vs AZURE 무엇을 사용해야 할까? (0) | 2023.02.21 |
Nodejs typscript 사용해야 할까?,사용법 (0) | 2023.02.06 |
iOS 개발 NotificationCenter 사용법, 데이터전달 (2) | 2023.02.03 |
Node.js APNs 서버 개발 (0) | 2023.01.30 |
댓글