Netty(네티)는 네트워크 응용 프로그램을 개발하기 위한 고성능, 이벤트 기반의 네트워크 프레임워크입니다. Netty는 Java로 작성되었으며, TCP, UDP, HTTP 및 기타 다양한 프로토콜을 지원하는 다목적 네트워크 프로그래밍을 위해 설계되었습니다. 여기에는 다양한 기능이 포함되어 있어 대규모의 분산 시스템 및 서버 개발에 적합합니다.
Maven 프로젝트에서 pom.xml 파일에 의존성 dependency 추가
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.86.Final</version>
</dependency>
Netty 구조
application.yml
netty:
server:
use: true
port: 2xxx
client:
use: true
host: 1xx.xxx.xx.xx
port: 3xxx
requestTime: 10
application.yml 파일에 서버와 클라이언트에 대한 정보를 입력해줍니다.
Netty의 구조
- Channel 및 ChannelPipeline: 네트워크 통신은 Channel을 통해 이루어지며, 각 Channel에는 연결된 파이프라인(ChannelPipeline)이 있습니다. ChannelPipeline은 이벤트 핸들러의 체인으로 구성되어 있습니다.
- EventLoop 및 EventLoopGroup: EventLoop는 비동기 이벤트 처리를 담당하는 스레드입니다. EventLoopGroup은 여러 EventLoop를 관리하며, 주로 서버와 클라이언트의 이벤트 처리를 담당합니다.
- ChannelHandler: 이벤트 핸들러는 데이터 수신, 쓰기, 예외 처리 등의 이벤트를 처리하는 데 사용됩니다. ChannelInboundHandler 및 ChannelOutboundHandler는 각각 입력 및 출력 이벤트를 처리합니다.
- ChannelFuture: 비동기 작업의 결과를 나타냅니다. ChannelFuture는 비동기 작업이 완료되었을 때 애플리케이션에 알리기 위해 사용됩니다.
Netty는 이러한 구조를 통해 성능, 확장성, 유연성을 제공하며, 다양한 네트워크 애플리케이션을 쉽게 개발할 수 있도록 도와줍니다.
NettyClientManager
@Component
public class NettyClientManager {
private static final Logger logger = LoggerFactory.getLogger(NettyClientManager.class);
private static NettyClientManager instance = null;
@Value("${netty.client.host}")
private String host;
@Value("${netty.client.port}")
private int port;
@Value("${netty.client.requestTime}")
private int connectionRequestTime = 0;
@Value("${netty.client.use}")
private boolean useFlag;
private boolean nettyConnectionStatus = false;
private ChannelHandlerContext channelHandlerContext = null;
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
private Bootstrap bootstrap = new Bootstrap();
private final ClientConnectionProcess clientConnectionProcess;
private final SendDataProcess sendDataProcess;
private final ReceiveDataProcess receiveDataProcess;
private Thread connectionThread;
private Thread receiveDataThread;
private Thread sendDataThread;
public NettyClientManager(@Qualifier("NettyClientConnectionProcess") ClientConnectionProcess clientConnectionProcess,
@Qualifier("NettyClientSendDataProcess") SendDataProcess sendDataProcess,
@Qualifier("NettyClientReceiveDataProcess") ReceiveDataProcess receiveDataProcess) {
this.clientConnectionProcess = clientConnectionProcess;
this.sendDataProcess = sendDataProcess;
this.receiveDataProcess = receiveDataProcess;
}
public static NettyClientManager getInstance() {
return instance;
}
public int getConnectionRequestTime() {
return connectionRequestTime;
}
public void setNettyConnectionStatus(boolean nettyConnectionStatus) {
this.nettyConnectionStatus = nettyConnectionStatus;
}
public boolean isNettyConnectionStatus() {
return nettyConnectionStatus;
}
public ChannelHandlerContext getChannelHandlerContext() {
return channelHandlerContext;
}
public void setChannelHandlerContext(ChannelHandlerContext channelHandlerContext) {
this.channelHandlerContext = channelHandlerContext;
}
@PostConstruct
void initialize() {
instance = this;
clientConfigSet();
if (useFlag) {
this.connectionThread = new Thread(clientConnectionProcess);
this.sendDataThread = new Thread(sendDataProcess);
this.receiveDataThread = new Thread(receiveDataProcess);
this.connectionThread.start();
this.sendDataThread.start();
this.receiveDataThread.start();
}
}
@PreDestroy
void destroy() {
this.connectionThread.interrupt();
this.receiveDataThread.interrupt();
this.sendDataThread.interrupt();
this.workerGroup.shutdownGracefully();
}
private void clientConfigSet() {
bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ByteArrayDecoder());
pipeline.addLast(new ByteArrayEncoder());
ch.pipeline().addLast(new SocketClientHandler());
}
});
}
public void connect() throws InterruptedException {
ChannelFuture f = bootstrap.connect(host, port).sync();
f.channel().closeFuture().sync();
}
public void receiveDataEnqueue(NettyClientDataContext data) {
this.receiveDataProcess.receiveDataEnqueue(data);
}
public void sendDataEnqueue(byte[] sendData) throws NettyClientException {
NettyClientDataContext nettyClientDataContext = new NettyClientDataContext();
nettyClientDataContext.setOriginalData(sendData);
this.sendDataProcess.sendDataEnqueue(nettyClientDataContext);
}
}
- 이 코드는 Netty를 사용하여 클라이언트 측에서 네트워크 통신을 관리하는 NettyClientManager 클래스입니다. @Value 어노테이션으로 스프링의 설정 파일(application.yml 혹은 application.properties)을 읽어 옵니다.
필드 및 속성
- host, port: 서버에 연결할 호스트와 포트 정보를 저장하는 필드입니다.
- connectionRequestTime: 연결 요청 시도 간격을 지정하는데 사용되는 시간입니다.
- useFlag: Netty를 사용할지 여부를 나타내는 플래그입니다.
- nettyConnectionStatus: Netty 연결 상태를 나타내는 플래그입니다.
- channelHandlerContext: Netty Channel의 컨텍스트를 저장합니다.
- workerGroup: Netty의 이벤트 루프 그룹으로, 클라이언트의 이벤트 처리를 담당합니다.
- bootstrap: 클라이언트를 설정하고 부트스트랩하는 데 사용되는 Netty의 부트스트랩 객체입니다.
- clientConnectionProcess, sendDataProcess, receiveDataProcess: 각각 연결, 데이터 전송, 데이터 수신을 처리하는 프로세스입니다.
- connectionThread, receiveDataThread, sendDataThread: 각각 연결, 데이터 수신, 데이터 전송을 처리하는 스레드입니다.
생성자
- 생성자에서는 연결, 데이터 전송, 데이터 수신을 처리하는 프로세스를 주입받습니다.
메서드
- initialize: 객체 초기화를 위한 메서드로, 스레드를 생성하고 시작합니다.
- destroy: 객체 소멸을 위한 메서드로, 스레드를 종료하고 Netty의 이벤트 루프 그룹을 정리합니다.
- clientConfigSet: Netty 클라이언트의 구성을 설정합니다. Bootstrap 객체를 초기화하고, 채널 초기화, 디코더 및 인코더를 설정합니다.
- connect: 서버에 연결하는 메서드로, 지정된 호스트와 포트로 연결을 시도하고 대기합니다.
- receiveDataEnqueue: 데이터를 수신하는 메서드로, 수신된 데이터를 처리하는 프로세스에 큐에 넣습니다.
- sendDataEnqueue: 데이터를 전송하는 메서드로, 전송할 데이터를 처리하는 프로세스에 큐에 넣습니다.
@PostConstruct 및 @PreDestroy 어노테이션
- @PostConstruct: 해당 메서드는 객체가 생성된 후에 호출되며, 초기화 작업을 수행합니다.
- @PreDestroy: 해당 메서드는 객체가 소멸되기 전에 호출되며, 정리 작업을 수행합니다.
Bootstrap 및 Channel 설정
- Bootstrap 객체를 초기화하고, NioSocketChannel을 사용하여 채널을 설정합니다.
- SO_KEEPALIVE 옵션을 사용하여 TCP keep-alive를 활성화합니다.
- ChannelInitializer를 사용하여 채널 파이프라인을 초기화하고, 바이트 배열을 인코딩 및 디코딩하는 핸들러를 추가합니다.
'Spring & Spring Boot' 카테고리의 다른 글
Spring & Spring Boot - @Accessors (0) | 2024.05.09 |
---|---|
WAR 빌드와 배포 - intelliJ Tomcat(톰캣) 설정 (0) | 2024.02.06 |
Springboot - 생성자 주입 , 필드 주입 방식, 수정자 주입 방식의 차이점 & 생성자 주입 방식을 권하는 이유? (0) | 2023.02.20 |
Springboot - ResponseEntity란? (0) | 2023.02.15 |
Springboot - Lombok 라이브러리에서 자주 사용하는 애노테이션 (0) | 2023.02.14 |
댓글