본문 바로가기
IT/JAVA

Netty(3) - Byte array 로 받기 - ByteArrayDecoder is not a @Sharable handler

by 최고영회 2021. 2. 5.
728x90
반응형
SMALL

진행해야 하는 프로젝트 중에 TCP 로 전문을 받아 정해진 프로토콜에 의해 전문을 Parsing 하고

전문에 포함된 File 을 저장하여 분석해야 하는 작업이 필요했다.

이전까지는 Spring integration 을 이용하거나 간단한 경우 직접 native 한 code 를 작성해서 통신작업을 개발했다.

netty를 이용하면 multithread 등 고민해야 할 부분들이 아주 많이 사라지고

실제로 처리해야 하는 business logic 에만 집중할 수 있기 때문에 적용해 보았다.

childHandler 를 만들고 등록할 때 이 channel을 초기화 해 줘야 한다.

그리고 초기화 시 필요에 따라 encoder(outbound), decoder(inbound) 를 pipeline에 등록한다.

<아래 링크에서 예제 소스 코드 참고>

kimyhcj.tistory.com/426

 

Netty(2) - Bootstrap

Bootstrap 이란 Application의 동작 및 설정을 지정해주는 helper class 이다. 사전적 의미를 찾아보면 "그 자체의 동작에 의해서 어떤 소정의 상태로 이행하도록 설정되어 있는 방법" 이다. 같은 맥락이라

kimyhcj.tistory.com

 

 

일반적으로 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); 
  } 
}

잘된다.

728x90
반응형
LIST

'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