진행해야 하는 프로젝트 중에 TCP 로 전문을 받아 정해진 프로토콜에 의해 전문을 Parsing 하고
전문에 포함된 File 을 저장하여 분석해야 하는 작업이 필요했다.
이전까지는 Spring integration 을 이용하거나 간단한 경우 직접 native 한 code 를 작성해서 통신작업을 개발했다.
netty를 이용하면 multithread 등 고민해야 할 부분들이 아주 많이 사라지고
실제로 처리해야 하는 business logic 에만 집중할 수 있기 때문에 적용해 보았다.
childHandler 를 만들고 등록할 때 이 channel을 초기화 해 줘야 한다.
그리고 초기화 시 필요에 따라 encoder(outbound), decoder(inbound) 를 pipeline에 등록한다.
<아래 링크에서 예제 소스 코드 참고>
일반적으로 netty를 처음 공부하면서 구글링해 보면 대부분 echoserver를 sample code로 많이 볼 수 있다.
echoserver에 대한 handler를 만들때에는 childChannel pipeline에 decoder로 StringDecoder를 사용한다.
간단한 echo 나 채팅에서는 String으로 주고 받기 때문에 이와 같은 encoder/decoder를 이용하는데
전문 통신은 보통 byte[] 로 하는 경우가 많다. (기존 legacy module 들이 c, c++인 경우가 많아서...)
이런 경우 decoder로 ByteArrayDecoder 를 사용할 수 있다.
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ByteArrayDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new FileSaveHandler());
이런식이 될 것이다.
그리고 Handler 에서는 아래 코드 처럼 msg를 byte[] 로 casting 할 수 있다.
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("receive file");
byte[] bytes = (byte[])msg;
// parsing and saved file
log.info("save file complete");
}
channel 을 초기화 할 때 encoder를 매번 new 하는것이 맞을까?
encoder, decoder에 대해서 한번만 생성해서 해당 instance를 재사용하는 것이 좋을것 같다.
private final StringEncoder stringEncoder = new StringEncoder();
private final ByteArrayDecoder byteArrayDecoder = new ByteArrayDecoder();
이렇게 encoder, decoder를 선언하고 test를 해 봤다.
첫번째 통신에서는 문제 없이 잘 동작했는데
두번째 통신부터는 ByteArrayDecoer 가 @Sharable handler 가 아니라서 여러번 add 하거나 remove 하지 말라는 예외 메시지를 내뱉는다. is not a @Sharable handler, so can't be added or removed multiple times.
함께 사용한 StringEncoder는 문제가 없을까?
StringEncoder의 코드를 보면 @Sharable 로 선언되어 있다.
그럼 ByteArrayDecoder는?? sharable 이 아니네.....
그럼 ByteArayDecoder 는 매번 새로운 객체를 만들어서 초기화 해줘야 하는걸까?
간단하게 이렇게 하면 된다.
ByteArrayDecoder를 상속받는 MyByteArrayDecoder class 를 만들고 sharable 하게 선언한다.
@ChannelHandler.Sharable
public class MyByteArrayDecoder extends ByteArrayDecoder {}
그리고 channel 초기화 시 이렇게 사용한다.
@Slf4j
@Component
@RequiredArgsConstructor
public class FileSaveChannelInitializer extends ChannelInitializer<SocketChannel>{
private final MyByteArrayDecoder byteArrayDecoder = new MyByteArrayDecoder();
private final StringEncoder stringEncoder = new StringEncoder();
private final FileSaveHandler fileSaveHandler;
@Override
protected void initChannel(SocketChannel ch) throws Exception {
log.info("initChannel...");
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(byteArrayDecoder);
pipeline.addLast(stringEncoder);
pipeline.addLast(fileSaveHandler);
}
}
잘된다.
'IT > JAVA' 카테고리의 다른 글
Netty(5) - TCP/IP 파일 송수신 (0) | 2021.02.09 |
---|---|
Netty(4) - encoder/decoder 기본 (0) | 2021.02.09 |
Netty(2) - Bootstrap (0) | 2021.02.04 |
Netty 란 (0) | 2021.02.02 |
jmap 오류 발생할 경우 - Unable to open socket file, HotSpot VM not loaded (0) | 2020.12.01 |