참고
Netty Server
NOTE
모든 네티 서버에는 다음 항목이 필요하다
•
하나 이상의 ChannelHandler
◦
이 컴포넌트는 클라이언트로부터 받은 데이터를 서버측에서 처리하는 비즈니스 논리를 구현한다.
•
부트스트랩
◦
서버를 구성하는 시동 코드를 의미한다.
◦
최소한 서버가 연결 요청을 수신하는 포트 서버와 바인딩하는 코드가 있어야 한다.
•
Netty는 다음과 같은 Componet를 통해 데이터를 처리한다.
◦
Channel, EventLoop, ChannelFuture
◦
ChannelHandler, ChannelPipeline
◦
Bootstrap
Channel
NOTE
자바 네트워크
Socket을 사용해서 bind, accept, read를 진행한다.
•
기본 입출력 작업(bind, connect, read, write)은 네트워크 전송에서 제공하는 기본형을 이용한다.
•
자바 기반 네트워크 기본 구조는 Socket클래스이다.
Netty 네트워크
•
Netty의 Channel 인터페이스는 Socket으로 직접 작업할 때의 복잡성을 크게 완화하는 API를 제공한다.
•
Netty는 Channel 인터페이스를 구현한 몇가지 특수한 구현체를 제공한다 (NioSocketChannel만 알면된다)
클래스 | 기능 |
EmbeddedChannel | 실제 연결없이 ChannelHandlers 의 테스트를 할 수 있도록 구현한 채널 클래스 |
EpollChannel | 최대 성능을 위해 EPOLL Edge-Triggered Mode 를 사용 하는 Linux 용으로 최적화된 채널 클래스 |
KQueueChannel | jni 라이브러리를 사용 하는 채널 클래스 |
LocalChannel | 로컬 채널 클래스 |
NioServerChannel | NIO 채널 클래스 |
NioSocketChannel | NIO 소켓 클래스 |
EventLoop
NOTE
EventLoop, Thread, EvnetLoopGroup 관계
•
EventLoop는 연결의 수명주기 중 발생하는 이벤트를 처리하는 Netty의 핵심 추상화를 정의한다.
•
Netty의 스레드 처리 모델은 내용이 많으므로 별도의 글을 통해 설명하겠다.
ChannelFuture
NOTE
// ChannelFuture: I/O operation의 결과나 상태를 제공하는 객체
// 지정한 host, port로 소켓을 바인딩하고 incoming connections을 받도록 준비함
ChannelFuture serverChannelFuture = serverBootstrap.bind(tcpPort).sync();
Java
복사
•
네티의 모든 I/O 처리는 비동기식이다.
•
비동기식은 I/O 작업의 호출이 완료 여부와 상관없이 즉시 반환되며, 이때 ChannelFuture 인터페이스를 통해서 I/O처리가 완료 되었는지 확인 하고 결과를 검색할 수 있다.
•
ChannelFuture 함수
◦
addListener()
▪
작업 리스너 등록
◦
removeListener()
▪
작업 리스너 제거
◦
await()
▪
I/O 작업 완료 대기
◦
sync()
▪
I/O 작업 대기중 실패 하면 실패 이유 반환
ChannelHandler
NOTE
ChannelInobundHandler
•
Channel의 입력 데이터 처리
•
channelRead()
◦
메시지가 들어올때 마다 호출된다.
•
channelReadComplete()
◦
channelRead()의 마지막 호출에서 현재 일관 처리의 마지막 메시지를 처리했음을 통보
•
exceptCaught()
◦
읽기 작업중 예와 발생시 나온다.
ChannelOutboundHandler
•
Channel의 출력 데이터를 처리
•
bind()
•
connect()
•
read()
•
flush()
ChannelPipeline, ChannelHandlerContext, ChannelInitializer
NOTE
ChannelHandler 체인을 위한 컨테이너를 제공하며, 체인 상에서 인바운드 아웃바운드 이벤트를 전파하는 API를 정의한다!
•
Channel이 생성되면, 여기에 자동으로 자체적인 ChannelPipeline이 할당된다.
1.
ChannelInitializer구현체를 ServerBootstrap에 등록한다.
2.
ChannelInitializer.initChannel()이 호출되면 ChannelInitializer가 ChannelHandler의 커스텀 집합을 파이프라인에 설치한다.
3.
ChannelInitializer는 ChannelPipeline에서 자신을 제거한다.
ChannelInboundHandler, ChannelOutboundHandler의 구현을 구분해서, 핸들러간 데이터 전달이 동일한 방향으로 수행되도록한다.
@Component
@RequiredArgsConstructor
public class NettyChannelInitializer extends ChannelInitializer<SocketChannel> {
private final NettyHandler nettyHandler;
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(nettyHandler);
}
}
Java
복사
ChannelHandlerContext
•
ChannelHandler와 ChannlerPipeline간 연결을 나타낸다.
•
ChannelHandler를 ChannelPipeline에 추가시 할당
ChannelInitializer
•
여러 ChannelHandler를 CahnnelPipeline에 할당하기 위한 클래스
•
Channel 생성 시 호출
Bootstrap
NOTE
@Bean
public ServerBootstrap serverBootstrap(NettyChannelInitializer nettyChannelInitializer) {
// boss: incoming connection을 수락하고, 수락한 connection을 worker에게 등록(register)
// worker: boss가 수락한 연결의 트래픽 관리
NioEventLoopGroup bossGroup = new NioEventLoopGroup(bossCount);
NioEventLoopGroup workerGroup = new NioEventLoopGroup(workerCount);
// ServerBootstrap: 서버 설정을 도와주는 class
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.DEBUG))
.childHandler(nettyChannelInitializer);
b.option(ChannelOption.SO_BACKLOG, backlog);
return b;
}
Java
복사
부트스트랩 설정
진행순서
NOTE
// 1. 부트스트랩에서 채널생성
@Bean
public ServerBootstrap serverBootstrap(NettyChannelInitializer nettyChannelInitializer) {
// boss: incoming connection을 수락하고, 수락한 connection을 worker에게 등록(register)
// worker: boss가 수락한 연결의 트래픽 관리
NioEventLoopGroup bossGroup = new NioEventLoopGroup(bossCount);
NioEventLoopGroup workerGroup = new NioEventLoopGroup(workerCount);
// ServerBootstrap: 서버 설정을 도와주는 class
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // NioServerSocket 사용
.handler(new LoggingHandler(LogLevel.DEBUG)) //
.childHandler(nettyChannelInitializer);
// SO_BACKLOG: 동시에 수용 가능한 최대 incoming connections 개수
b.option(ChannelOption.SO_BACKLOG, backlog);
return b;
}
// 2. 생성된 채널을 사용해 ChannelFuture 만든다.
// 지정된 TcpPort로 서버소켓을 바인딩하고 준비
ChannelFuture serverChannelFuture = serverBootstrap.bind(tcpPort).sync();
// 서버 소켓이 닫힐 때까지 기다림
serverChannel = serverChannelFuture.channel().closeFuture().sync().channel();
Java
복사