네티의 데이터 컨테이너 - ByteBuf 알아보기


자바 NIO 의 ByteBuffer 의 사용을 편리하게 사용 할 수 있도록 기능을 제공하는 API 가 바로 네티의 데이터 컨테이너인 ByteBuf 입니다.
ByteBuf 의 기능을 살펴 보고 사용하는 방법에 대해서 살펴 보겠습니다.

  1. ByteBuf 의 장점은?

    • ByteBuf 는 순차적인 두가지 포인트 변수(readerIndex, WriterIndex)를 제공 하여 읽기 쓰기 전환 없이 사용이 가능 합니다.
      ※ ByteBuffer 는 Flip() 을 사용 하여 읽기 , 쓰기 전환이 필요합니다.

      • 인덱스 다이어그램

          +-------------------+------------------+------------------+
          | discardable bytes |  readable bytes  |  writable bytes  |
          |                   |     (CONTENT)    |                  |
          +-------------------+------------------+------------------+
          |                   |                  |                  |
          0      <=      readerIndex   <=   writerIndex    <=    capacity
        
    • 사용자 정의 버퍼 형식으로 확장이 가능

    • 내장 복합 버퍼 형식을 통해 투명한 제로 카피

    • 용량을 필요에 따라 확장 가능

        public abstract ByteBuf capacity(int newCapacity);
      
    • 메서드 체인 지원

    • 참조 카운팅 지원

      • 참조 카운팅은 다른 객체에서 더이상 참조 하지 않는 객체가 보유한 리소스를 해제해 메모리 사용량과 성능을 최적화 하는 기법.
        ByteBuf heapBuff = Unpooled.buffer(128);        
        System.out.println(heapBuff.refCnt() ==  1);	//true
        boolean release = heapBuff.release();	//참조 해제
        System.out.println(release);				//true
        System.out.println(heapBuff.refCnt() ==  1);	//false
      
    • ByteBuffer Pool 지원

  2. ByteBuf 인스턴스 생성 (Pool & UnPool)

    네티의 ByteBuf 는 풀링을 하는냐 하지 않느냐로 구분이 됩니다.

     //풀링 byteBuf
     ByteBuf pooledHeapBuf = PooledByteBufAllocator.DEFAULT.buffer(128);
     pooledHeapBuf.writeBytes("test Pooled Heap Buffer".getBytes());
     for(int i = 0; i < pooledHeapBuf.capacity(); i++) {
         byte b = pooledHeapBuf.getByte(i);
         System.out.print((char) b);
     }
        
     //언풀링 byteBuf
     ByteBuf unpooledHeapBuf = Unpooled.buffer(128);
     unpooledHeapBuf.writeBytes("test UnPooled Heap Buffer".getBytes());
     for(int i = 0; i < unpooledHeapBuf.capacity(); i++) {
         byte b = unpooledHeapBuf.getByte(i);
         System.out.print((char) b);
     }
    

    1) ByteBufAllocator 인터페이스

    메모리 할당과 해제시 발생하는 오버헤드를 줄이기 위해 ByteBufAllocator 인터페이스를 통해 ByteBuf 인스턴스를 할당하는데
    이용 할 수 있는 풀링을 구현 합니다.

    • ByteBufAllocator 인터페이스 제공 메소드

      메소드 설명
      buffer() 힙 기반 저장소 또는 다이렉트 데이터 저장소 ByteBuf 를 반환
      ioBuffer() 소켓 IO 작업에 사용될 ByteBuf 를 반환
      heapBuffer() 힙 기반 저장소 ByteBuf 를 반환
      directBuffer() 다이렉트 저장소 ByteBuf 를 반환
      compositeBuffer() 지정된 컴포넌트 수만큼 힙 기반 또는 다이렉트 버퍼를 추가해 확장 할 수 있는 복합 ByteBuf 를 반환

    2) UnPooled 클래스

    풀링 되지 않는 ByteBuf 인스턴스를 생성하는 클래스

    • UnPooled 클래스 제공 메소드

      메소드 설명
      buffer() 폴링 되지 않는 힙 기반 저장소 ByteBuf 를 반환
      directBuffer() 폴링 되지 않는 다이렉트 저장소 ByteBuf 를 반환
      wrappedBuffer() 지정된 데이터를 래핑 하는 ByteBuf 를 반환
      copiedBuffer() 지정된 데이터를 복자 하는 ByteBuf 를 반환
  3. 자주 사용 되는 메소드 정리

    1) 읽기/쓰기 작업

    • get(), set() 작업은 지정한 인덱스에서 시작하며 인덱스를 변경 하지 않음.
    • read(), write() 작업은 지정한 인덱스에서 시작하며 접근한 바이트 수 만큼 인덱스를 증가시킴.

    • get()

      메소드 설명
      getBoolean(int) 지정한 인덱스의 boolean 값 반환
      getByte(int) 지정한 인덱스의 byte 반환
      getUnsignedByte(int) 지정한 인덱스의 부호 없는 바이트 값을 short 로 반환
      getInt(int) 지정한 인덱스의 int 반환
      getUnsignedInt(int) 지정한 인덱스의 부호없는 int 값 반환
      getMedium(int) 지정한 인덱스의 24비트 미디엄 int 값 반환
      getUnsignedMedium(int) 지정한 인덱스의 부호없는 24비트 미디엄 int 값 반환
      getLong(int) 지정한 인덱스의 long 값을 반환
      getShort(int) 지정한 인덱스의 short 값을 반환
      getUnsignedShort(int) 지정한 인덱스의 부호 없는 short 값을 반환
      getBytes(int, …) 버퍼의 데이터를 지정된 대상의 지정된 인덱스부터 전송
    • set()

      메소드 설명
      setBoolean(int, boolean) 지정한 인덱스에 boolean 값 설정
      setByte(int index, int value) 지정한 인덱스에 Byte 값 설정
      setMedium(int index, int value) 지정한 인덱스에 24비트 미디엄 값 설정
      setInt(int index, int value) 지정한 인덱스에 int 값 설정
      setLong(int index, int value) 지정한 인덱스에 long 값 설정
      setShort(int index, int value) 지정한 인덱스에 short 값 설정
    • read()

      메소드 설명
      readBoolean() 현재 readerIndex 위치의 boolean 값을 반환 하고 readerIndex를 증가 시킨다.
      readByte() 현재 readerIndex 위치의 byte 값을 반환 하고 readerIndex를 증가 시킨다.
      readShort() 현재 readerIndex 위치의 short 값을 반환 하고 readerIndex를 증가 시킨다.
      readMedium() 현재 readerIndex 위치의 24비트 미디엄 값을 반환 하고 readerIndex를 증가 시킨다.
      readInt() 현재 readerIndex 위치의 int 값을 반환 하고 readerIndex를 증가 시킨다.
      readLong() 현재 readerIndex 위치의 long 값을 반환 하고 readerIndex를 증가 시킨다.
      readChar() 현재 readerIndex 위치의 char 값을 반환 하고 readerIndex를 증가 시킨다.
      readFloat() 현재 readerIndex 위치의 float 값을 반환 하고 readerIndex를 증가 시킨다.
      readDouble() 현재 readerIndex 위치의 double 값을 반환 하고 readerIndex를 증가 시킨다.
      readBytes(ByteBuf dst, int dstIndex, int length) 현재 byteBuf의 readerIndex 부터 시작하는 바이트를 dst 로 지정된 dstIndex 부터 length 만큼 전송함.
    • write()

      메소드 설명
      writeBoolean() 현재 writerIndex 위치에 boolean 값을 기록 하고 writerIndex를 값만큼 증가 시킨다.
      writeByte() 현재 writerIndex 위치에 byte 값을 기록 하고 writerIndex를 값만큼 증가 시킨다.
      writeShort() 현재 writerIndex 위치에 short 값을 기록 하고 writerIndex를 값만큼 증가 시킨다.
      writeMedium() 현재 writerIndex 위치에 24비트 미디엄 값을 기록 하고 writerIndex를 값만큼 증가 시킨다.
      writeInt() 현재 writerIndex 위치에 int 값을 기록 하고 writerIndex를 값만큼 증가 시킨다.
      writeLong() 현재 writerIndex 위치에 long 값을 기록 하고 writerIndex를 값만큼 증가 시킨다.
      writeChar() 현재 writerIndex 위치에 char 값을 기록 하고 writerIndex를 값만큼 증가 시킨다.
      writeFloat() 현재 writerIndex 위치에 float 값을 기록 하고 writerIndex를 값만큼 증가 시킨다.
      writeDouble() 현재 writerIndex 위치에 double 값을 기록 하고 writerIndex를 값만큼 증가 시킨다.
      writeBytes(ByteBuf src, int srcIndex, int length) 지정된 src 의 데이터를 지정된 길이만큼 기록 하고 기록한 만큼 writerIndex 를 증가 시킨다.
    • 추가 유용한 작업

      메소드 설명
      isReadable() 읽을수 있는 바이트가 있으면 true 반환
      isWritable() 기록 할 수 있는 바이트가 있으면 true 반환
      readableBytes() 읽을수 있는 바이트 수를 반환
      writableBytes() 기록할 수 있는 바이트 수를 반환
      capacity() ByteBuf 가 기록 할 수 있는 바이트 수를 반환
      maxCapacity() ByteBuf 가 기록 할 수 있는 최대 바이트 수를 반환
      hasArray() ByteBuf 에 보조 배열이 있는 경우 true 를 반환
      array() ByteBuf 에 보조 배열이 있는 경우 그 배열을 반환

Back to blog