Codec


앞서 정의 했듯, 코덱은 인코딩과 디코딩 과정을 거치는 알고리즘이다.


일반적으로 동영상 압축 알고리즘인 MPEG로 알고 있다. 인코딩 과정을 통해 동영상 용량을 줄일 수 있고, 마찬가지로 디코딩 과정으로 원본파일로 변환한다. 


MPEG 확장자를 갖는 동영상 뿐 아니라 jpg 확장자를 갖는 파일도 코덱 과정을 거친 것이다.



Netty에서는 inboundHandler와 outBoundHandler가 각각 인코더와 디코더에 해당하고, 데이터 송수신시 데이터와 패킷으로 각각 변환 시킬 수 있다.





기본 정의된 코덱


- base64 Codec 

 : Base64 인코딩 데이터에 대한 코덱   

 : Base64는 8비트 이진데이터를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자로 이루어진 일련의 문자열로 바꾸는 인코딩


- bytes Codec

 : 바이트 배열에 대한 코덱


- compression Codec

 : 송수신 데이터의 압축을 지원하는 코덱 

 : 네티 4.0에서는 zlib, gzip, snappy - 4.1에서는 bzip2, castle, l24, lzf의 압축 알고리즘이 추가


- mashaslling Codec

 : 마살량 혹은 언마샬링은 객체를 네트워크를 통해 송신 가능한 형태로 변환하는 과정


- ProtoBuf Codec

 : 구글의 포로토콜 버퍼를 사용한 데이터를 송수신을 지원하는 코덱


- rtsp Codec

 : 오디오 및 비디오 같은 실시간 데이터의 전달을 위해 만들어진 애플리케이션 레벨의 프로토콜 (real time streaming protocol)


- sctp Codec

 : TCP가 아닌 sctp 전송 계층을 사용하는 코덱

 : 이 코덱을 사용하려면 부트스트랩 채널에 NioSctpChannel 혹은 NioSctpServerChannel을 설정


- http Codec

 : http 프로로콜을 지원하는 코덱


- spdy Codec

 : Spdy는 기존의 http를 보완하는 프로토콜


- HTTP/2 Codec

  : HTTP/2 Protocol을 지원하는 코덱. 구글에서 spdy 프로토콜의 지원을 중단하고 http/2에 대한 공식 지원을 하는 중이다. 

  : 네티 4.1 버전에서 제공되고 있다.


-  String Codec

  : 문자열 송수신을 지원하는 코덱. 주로 Telnet이나 채팅 서버의 프로토콜에 이용


- Serialization 코덱

  : 객체를 네트워크로 직렬화 / 역직렬화를 지원하는 코덱


- MQTT Codec

 : MQTT Protocol을 지원하는 코덱


- HaProxy

 : Load Balance와 Proxy기능 을 제공하는 오픈 솔루션을 지원하는 코덱


- STOMP

 : STOMP Protocol을 지원하는 코덱






Http/2, Http Codec example


- 아주 심플하게 코덱 구성을 해보았다.



public class Http2ClientInit extends ChannelInitializer<SocketChannel> {
private HttpToHttp2ConnectionHandler connectionHandler;
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
Http2Connection connection = new DefaultHttp2Connection(false);
httpResponseHandler responseHandler = new httpResponseHandler();
connectionHandler = new HttpToHttp2ConnectionHandlerBuilder().connection(connection).build();
socketChannel.pipeline().addLast("connection",connectionHandler);
//기본 정의 codec
socketChannel.pipeline().addLast("response",new HttpResponseDecoder());
// 내가 정의한 사용자 정의 codec
socketChannel.pipeline().addLast("response", httpResponseHandler);
}
}




=> http2/0 으로 Connection을 구성하고 응답 온 패킷에 대해 http 데이터로 변환 후, 내가 정의한 핸들러에서 데이터를 정의







참고 : 자바 네트워크 소녀 Netty 


 






 



Java ByteBuffer



자바 NIO ByteBuffer는 바이트 데이터를 저장하고 읽는 저장소




내부 배열을 3가지 속성으로 관리한다.

(1)Capacity : 버퍼에 저장하는 최대 크기

(2)Position : 읽기 또는 쓰기가 작업 중인 위치

(3)Limit : 읽고 쓰는 버퍼의 최대 공간


 

생성에는 3가지 특징 : allocate , allocateDirect, wrap

(1)Allocate : jvm heap 영역에 생성.

(2)AllocateDirect : 운영체제의 커널 영역에 생성. ByteBuffer로만 생성할 수 있음.

(3)Wrap : 입력된 바이트 배열을 사용하여 바이트 버퍼를 생성.







Netty ByteBuf



Pure Java에서 Buffer와 그 하위 클래스들을 제공함에도 불구하고 네티에서는 자체적인 바이트 버퍼 API를 사용

 

특징

• 별도의 읽기 인덱스와 쓰기 인덱스

• flip 메서드 없이 읽기 쓰기 가능

• 가변 바이트 버퍼

• 프레임워크 레벨의 바이트 버퍼 풀 제공

• Java ByteBuffer와 호환됨

• 자료형에 따른 클래스를 제공하지 않고 별도의 method로 제공





Byte Buffer Pool


네티의 바이트버퍼의 특징은 바이트버퍼풀을 제공한다는 것이다. 이를 통해 생성된 바이트 버퍼를 재사용 할 수 있다.


•  다음은 풀링을 할 수 있는 버퍼를 생성하는 방법이다.


        PooledByteBufAllocator.DEFAULT.heapBuffer() 

         PooledByteBufAllocator.DEFAULT.directBuffer()


 

• 풀링을 하지 않는 버퍼 생성 방법.

        

        Unpooled.buffer(); 

         Unpooled.directBuffer();

 



바이트 버퍼 풀을 통해 매번 ByteBuf 메모리의 할당과 해제를 하지 않아도 됨.


 






















ChannelFutureListener 


.....


ChannelFutureListener future = ctx.write(msg);


//write가 완료됐을 때 이벤트를 받을 수 있는 리스너 추가

future.addListener(listener);


//io작업이 완료됐는지

future.isDone();


//io작업 대기

future.sync();






네티가 제공하는 기본 ChannelFutureListener 


- ChannelFutureListener.CLOSE;

- ChannelFutureListener.CLOSE_ON_FAILURE;

- ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE;



.....


ChannelFutureListener future = ctx.write(msg);



future.addlistener(ChannerFutureListener.CLOSE);







참고자료: https://www.slideshare.net/krisjeong/going-asynchronous-with-netty-soscon-2015









이벤트 핸들러

- 이벤트 처리를 다루는 객체


코덱

- 인코딩과 디코딩의 합성어, 이벤트 핸들러를 상속받아 구현된 구현체



인코더: 전송할 데이터를 전송 프로토콜에 맞춰 변환, ChannelOutboundHandler 인터페이스 상속

디코더: 수신한 데이터를 전송 프로토콜에 맞춰 변환, ChannelInboundHandler 인터페이스 상속



네티에는 수많은 코덱이 있다.


ex)loggingHandler, Mqtt, Base64, Https 등등..








파이프라인 

- 발생된 이벤트가 이동하는 통로






- 다음은 서버소켓으로부터 이벤트들이 채널로 들어온 상황을 가정한 그림이다.

클라이언트 부트스트랩에 등록한 채널에 이벤트가 들어온다면, 가장 먼저 등록한 핸들러로부터 이벤트를 처리할 것이다.







 

 






Netty Framework는 Multi Thread를 지원하고 Event loop 기반 위에서 수행됩니다. 









특징

- 네티의 이벤트는 채널에서 발생

- 채널에 event handler를 여러개 추가할 수 있다.

- multi core CPU 효율적으로 사용

- 이벤트 루프 객체는 이벤트 큐를 가지고 이벤트가 큐에 쌓임

- 네티의 채널은 하나의 이벤트 루프에 등록됨, 다중 이벤트루프 또한 지원

- 네티의 이벤트는 발생순서와 처리순서가 항상 일치한다.

- 이벤트 처리를 위해 SingleThreadEventExecutor 사용







=> 간단하게 요약하면, 클라이언트와 서버간의 입출력 이벤트는 채널에서 read/write 이벤트 루프객체 전달을 통해 처리된다,   






  


[아래 그림은 클라이언트에서 write 이벤트를 발생시키는 처리 과정이다.]





<Process>


1) 기본적으로 여러개의 쓰레드는 channelcontext 에 write 할 수 있다.


2) InboundHandlerAdapter는 채널파이프라인으로 전달된다. (파이프라인은 다음장에서.)



3) 채널에서 발생된 이벤트는 이벤트루프그룹 큐에 쌓이고 서버와 연결된 socket의 outputstream을 통해 write한다.












참고자료 : https://www.slideshare.net/krisjeong/going-asynchronous-with-netty-soscon-2015

(1) Netty Bootstrap이란?


- 애플리케이션의 동작 및 설정을 지정해주는 헬퍼클래스






(2)  Bootstrap의 종류


- ServerBootstrap

- bootstrap


=> bootstrap은 클라이언트에서 사용하며, 구조적으로 ServerBootstrap과 동일








(3)  Bootstrap의 논리적구조






(4)  Bootstrap의 예제코드


public EchoServer() {


   public static void main(String[] args){

        bossGroup = new NioEventLoopGroup(1);  // 생성자에 1이므로 단일스레드로 동작하는 객체

        workerGroup = new NioEventLoopGroup(); // 생성자 인수가 없으면 cpu 코어 수에 따라 설정된다.


    bootstrap = new ServerBootstrap();

    bootstrap.group(bossGroup, workerGroup) // boss – 연결, worker - 입출력

        .channel(NioServerSocketChannel.class) // 부모쓰레드(boss) 입출력모드 설정 NIO모드

        .childHandler(new ChannelInitializer<SocketChannel>() { //worker쓰레드 자식 채널의 초기화

          @Override

          protected void initChannel(SocketChannel socketChannel) throws Exception {

            ChannelPipeline pipeline = socketChannel.pipeline();

            // 핸들러 추가

            pipeline.addLast(new EchoServerHandler());

          }

        });


     }

 bootstrap.bind(host,port).sync();  //socket 주소와 port binding

  }






(5)  Bootstrap의 구체적인 옵션


group: 이벤트 루프 설정

클라이언트와 달리 이벤트 루프가 2개 필요


channel: 소켓 입출력 모드 설정

LocalServerChannel: 로컬 가상 통신을 위한 소켓 채널

OioServerSocketChannel: Blocking 소켓 채널

NioServerSocketChannel: Nonblocking 소켓 채널

EpollServerSocketChannel: epoll 소켓 채널 (리눅스에서만적용)

OioSctpServerChannel: Blocking sctp 소켓 채널

NioSctpServerChannel: Nonblocking sctp 소켓 채널

SCTP(Stream Control Transmission Protocol): Layer 4 protocol

NioUdtByteAcceptiorChannel: Nonblocking udt 소켓 채널

NioUdtMessageAcceptorChannel: blocking udt 소켓 채널

UDT(UDP-Based Data Transfer): Layer 7 protocol


channelFactory: 소켓 입출력 모드 설정

Netty에서 기본 제공하는 channel class보다 더 복잡한 로직이 필요할 때 사용


handler: 서버 소켓 채널의 이벤트 핸들러 설정

부모 쓰레드에서 발생한 이벤트만 처리


childHandler: 클라이언트 소켓 채널의 이벤트 핸들러 설정

자식 쓰레드에서 발생한 이벤트만 처리


option - 서버 소켓 채널의 소켓 옵션 설정

기본적으로 자바에서 설정할 수 있는 모든 소켓 옵션 설정 가능

TCP_NODELAY: Nagle 알고리즘 비활성화 여부 설정

SO_KEEPALIVE: 정해진 시간마다 keepalive packet 전송

SO_SNDBUF: 커널 송신 버퍼 크기

SO_RCVBUF: 커널 수신 버퍼 크기

SO_REUSEADDR: TIME_WAIT 상태의 포트에도 bind 가능해짐

SO_LINGER: 소켓을 닫을 때 송신 버퍼에 남은 데이터 전송 대기 시간

SO_BACKLOG: 동시에 수용 가능한 소켓 연결 요청 수


childOption: 클라이언트 소켓 채널의 소켓 옵션 설정









Netty Framework


네티는 전 세계에서 많은 개발자가 사용하는 범용 자바 네트워크 애플리케이션 프레임워크입니다.


다양한 오픈소스 프레임워크 내부에서 사용되고 있으며, 카카오/라인/애플/트위터 등 서비스 제공 업체에서도 사용됩니다.


네티는 Non-block 비동기 처리가 가능하기에 고성능으로 시스템을 유지할 수 있습니다.


또한, 상당 부분 프로그래머의 귀찮은 작업을 네티가 알아서 처리해줍니다.. ( 멀티 쓰레드 처리와 같은..)


훌륭하게도, 최소 10만 이상의 클라이언트의 접속이 가능합니다. 


Netty의 구조와 주요특징을 정확하게 알고 사용법을 익힌다면 , 효과적으로 네티 프레임워크를 사용할 수 있을 것입니다.




https://netty.io/


네티 공식 사이트에서 네티를 다음과 같이 정의하고 있습니다.



Netty는 비동기 이벤트 기반 네트워크 응용 프로그램 프레임 워크입니다.

유지 보수가 가능한 고성능 프로토콜 서버 및 클라이언트를 신속하게 개발할 수 있습니다.


Netty는 프로토콜 서버 및 클라이언트와 같은 네트워크 응용 프로그램을 빠르고 쉽게 개발할 수있는 NIO 클라이언트 서버 프레임 워크입니다.

 TCP 및 UDP 소켓 서버와 같은 네트워크 프로그래밍을 크게 간소화하고 간소화합니다.








주요 특징



네티 공식 홈페이지에선, 네티의 주요 특징을 첫줄에 정의하고 있습니다.

1. 비동기 이고 , 2. block&non-block 이 가능한, 3. 이벤트 기반 네트워크 프레임워크이다.





(1) 동기 / 비동기 처리 

- 동기: 특정 서비스를 호출하면 처리가 완료될 때까지 기다렸다가 결과를 받는 방식

-  비동기: 서비스를 호출하여 즉시 응답을 받고, 다른 작업을 하다가 처리가 완료되었는지 확인하여 결과를 받는 방식. NettryReactor 패턴 사용하고 있습니다.



(2)블로킹/논블로킹 소켓

블로킹 : read, write, accept 등의 메서드가 호출되면 완료될 때까지 쓰레드가 멈춤

- 논블로킹: 하나의 스레드로 여러 클라이언트 대응 가능




(3)이벤트 기반 프로그래밍

- 네트워크 이벤트의 주체는 소켓

- 데이터를 소켓에 전달하기 위해 데이터 핸들러 이용

- 로직 분리, 코드 재사용성 증가, 에러 처리 부담 완화


이벤트의 예) 연결 요청 , 데이터 전송, 데이터 수신







=> 다음과 같은 3가지 특징과 구조를 생각하며 , 네티를 사용한다면 조금 더 구조적으로 혹은 단계적으로 손쉽게 접근이 가능 할 것 같습니다.




참고

- https://netty.io/

- 자바 네트워크 소녀 Netty 

















+ Recent posts