7. 데이터 버퍼와 코덱 by ks

Java NIO는 ByteBuffer많은 라이브러리가 자신의 바이트 버퍼 API를 빌드합니다. 특히 네트워크 작업에서 버퍼를 재사용하거나 직접 버퍼를 사용하면 성능에 도움이됩니다. 예를 들어, Netty는 ByteBuf계층 구조를 가지고 있고 , Undertow는 XNIO를 사용하고, Jetty는 콜백이있는 풀링 된 바이트 버퍼를 사용합니다. spring-core모듈은 다음과 같이 다양한 바이트 버퍼 API로 작업 할 추상화 집합을 제공합니다.

  • DataBufferFactory 데이터 버퍼 생성을 추상화합니다.

  • DataBuffer 될 수있는 바이트 버퍼를 나타냅니다 .

  • DataBufferUtils 데이터 버퍼 용 유틸리티 메소드를 제공합니다.

  • 코덱은 스트림 데이터 버퍼 스트림을 상위 수준의 객체로 디코딩하거나 인코딩합니다.

nio란?? 자바의 능이 상대적으로 좋지 않은 부분은 스윙과 I/O인데, 이를 메모리 직접 접근하는 것처럼 보이게 해서 개선하려고 했는데, 그게 nio 패키지이다!

참조 : https://jeong-pro.tistory.com/145

Netty의 데이터 컨테이너 - ByteBuf

  • 별도의 Read Index / Write Index가 있다.

  • 위의 이유로 flip() 메소드를 사용하지 않아도 된다.

  • 가변 바이트 버퍼를 사용할 수 있다.

  • 바이트 버퍼 풀 기능을 제공한다.

  • 복합 버퍼 사용이 가능하다.(Heap + Direct)

  • Java의 ByteBuffer와 Netty의 ByteBuf의 상호 변환이 가능하다.

참조 : https://koda93.github.io/Netty%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88(ByteBuf)/

Undertow XNIO

JBoss에서 사용하는 WEB/Servlet 컨테이너. 자바 언어로 작성된 웹서버. Undertow I/O는 JBoss 의 XNIO를 이용하여 처리하고, blocking/non-blocking API 를 제공한다.

참조 : http://www.opennaru.com/jboss/undertow-is-web-server-in-wildfly-and-jboss-eap7/

Jetty

서블릿과 JSP 를 지원하는 자바기반의 WAS

7.1 DataBufferFactory

DataBufferFactory 는 다음 두 가지 방법 중 하나로 데이터 버퍼를 만드는 데 사용됩니다.

(org.springframework.core.io.buffer Interface DataBufferFactory)

  1. 데이터 버퍼를 할당 해, 필요에 따라서 용량을 사전에 지정합니다. 필요에 따라서 DataBuffer의 구현이 확장 및 축소 할 수 있어, 보다 효율적입니다.

  2. 기존의 byte [] 또는 java.nio.ByteBuffer를 wrap합니다. 이것은, DataBuffer 구현으로 지정된 데이터를 장식 해, 할당을 포함하지 않습니다.

WebFlux 응용 프로그램은 DataBufferFactory직접 만들지 않고 대신 클라이언트 측 ServerHttpResponse또는 ClientHttpRequest에서 엑세스합니다. 팩토리 유형은 기본 클라이언트 또는 서버 (예 : NettyDataBufferFactoryReactor Netty) DefaultDataBufferFactory에 따라 달라집니다 .

WebFlux : reactive 기반의 웹 프레임워크. 5.0부터 Spring framework에 추가되었고 non-blocking reactive(비동기) stream 프로세스를 지원하고 Netty, Undertow, Servlet 3.1+ 컨테이너 같은 방식으로 서버에서 동작하는 웹 프레임워크이다.

참조 : https://ztkmkoo.github.io/2017/12/13/spring-webflux/

7.2 DataBuffer

(org.springframework.core.io.buffer Intaerface DataBuffer)

DataBuffer 인터페이스는 java.nio.ByteBuffer와 비슷한 작업을 제공하지만 Netty ByteBuf에서 영감을 얻은 몇 가지 추가 이점을 제공합니다. 다음은 혜택의 일부 목록입니다.

  • 읽기와 쓰기를 번갈아 수행하기 위해 flip ()을 호출 할 필요가없는 독립적 인 위치에서 읽고 쓰기.

  • 용량은 java.lang.StringBuilder 에서처럼 필요에 따라 확장

  • 풀링된 버퍼 및 PooledDataBuffer를 통한 참조 계산

  • 버퍼를 java.nio.ByteBuffer, InputStream, OutputStream으로 봄

  • 지정된 바이트에 대한 인덱스 또는 마지막 인덱스를 결정

7.3 PooledDataBuffer

(org.springframework.core.io.buffer Interface PooledDataBuffer)

ByteBuffer 용의 Javadoc로 설명했듯이 , 바이트 버퍼는 직접 또는 간접 일 수 있습니다. 직접 버퍼는 Java 힙 외부에 상주 할 수 있으므로 원시 I / O 작업을 복사 할 필요가 없습니다. 따라서 직접 버퍼는 소켓을 통해 데이터를주고받는 데 특히 유용하지만 버퍼를 풀링한다는 아이디어로 이어지기 때문에 생성 및 릴리스 비용도 높아집니다.

PooledDataBuffer는 바이트 버퍼 풀링에 필수적인 참조 카운팅에 도움이되는 DataBuffer의 확장입니다. 어떻게 작동합니까? PooledDataBuffer가 할당 될 때, 참조 카운트는 1입니다. retain () 호출은 count를 증가시키고 release () 호출은 감소시킵니다. 카운트가 0 이상이면, 버퍼는 해제되지 않는 것이 보증됩니다. 카운트가 0으로 감소하면 풀링 된 버퍼가 해제 될 수 있습니다. 실제로 버퍼의 예약 된 메모리가 메모리 풀로 반환 될 수 있습니다.

PooledDataBuffer로 직접 조작하는 대신, 대부분의 경우, PoolBuffer의 인스턴스 인 경우에 한해 DataBuffer에 release 또는 retain을 적용하는 DataBufferUtils의 편리한 메소드를 사용하는 것이 더 좋습니다.

7.4 DataBufferUtils

(org.springframework.core.io.buffer Class DataBufferUtils)

DataBufferUtils 는 데이터 버퍼에서 작동하는 여러 가지 유틸리티 메소드를 제공합니다.

  • 데이터 버퍼의 트릠을 단일 복사 (예 : 0복사) 에 결합합니다. 예를 들어서 기본 바이트 버퍼 API에서 지원하는 경우 복합 버퍼를 통해서 결합합니다.

  • InputStream 또는 NIO 채널을 Flux 로 바꾸고, Publisher 를 OutputStream 또는 NIO 채널로 바꿉니다.

  • 버퍼가 PooledDataBuffer의 인스턴스의 경우, DataBuffer를 릴리즈 또는 보관 유지하기위한 메소드.

  • 특정 바이트 수가 될 때까지 바이트 스트림을 건너 뛰거나 가져옵니다.

7.5 코덱

org.springframework.core.codec패키지는 다음과 같은 전략 인터페이스를 제공합니다.

  • EncoderPublisher<T>데이터 버퍼의 스트림 으로 인코딩 합니다.

  • DecoderPublisher<DataBuffer>더 높은 레벨의 객체들의 스트림 으로 디코딩 한다.

spring-core모듈을 제공하고 byte[], ByteBuffer, DataBuffer, Resource, 및 String인코더와 디코더 구현. 이 spring-web모듈에는 Jackson JSON, Jackson Smile, JAXB2, 프로토콜 버퍼 및 기타 인코더 및 디코더가 추가되었습니다. WebFlux 섹션의 코덱 을 참조하십시오 .

7.6 DataBuffer 사용

데이터 버퍼로 작업 할 때, 버퍼가 풀 (pool) 될 수 있기 때문에 버퍼가 풀어 지도록 특별한주의를 기울여야한다 . 코덱을 사용하여 작동 방식을 설명하지만 개념이 더 일반적으로 적용됩니다. 데이터 버퍼를 관리하기 위해 코덱이 내부적으로 수행해야하는 작업을 살펴 ​​보겠습니다.

Decoder는 상위 수준의 객체를 만들기 전에 입력 데이터 버퍼를 마지막으로 읽으므로 다음과 같이 해제해야합니다.

  1. 디코더가 각 입력 버퍼를 읽어 들여 즉시 풀 준비가되어있는 경우는, DataBufferUtils.release (dataBuffer)를 개입시켜 디코더가 그렇게 할 수 있습니다.

  2. 디코더가 flatMap, reduce 등의 Flux 또는 Mono 연산자를 사용하여 데이터 항목을 내부적으로 프리 페치 및 캐시하거나 필터, 건너 뛰기 및 항목을 제외하는 연산자를 사용하는 경우 doOnDiscard (PooledDataBuffer.class, DataBufferUtils :: release)를 삭제해야 구성 요소 체인에 버퍼를 추가해야 폐기 될 수 있으며, 결과적으로 오류 또는 취소 신호가 발생할 수 있습니다.

  3. 디코더가 다른 방법으로 하나 이상의 데이터 버퍼를 유지하는 경우 완전히 읽혀 지거나 캐시 된 데이터 버퍼를 읽고 해제하기 전에 오류나 취소 신호가 발생하는 경우 디코더가 해제되는지 확인해야합니다.

DataBufferUtils # join은 데이터 버퍼 스트림을 단일 데이터 버퍼로 모으는 안전하고 효율적인 방법을 제공합니다. 마찬가지로 skipUntilByteCount 및 takeUntilByteCount는 디코더가 사용할 수있는 안전한 추가 메서드입니다.

인코더는 다른 사람들이 읽고 받아야하는 데이터 버퍼를 할당합니다. 따라서 인코더는 할 일이 많지 않습니다. 그러나 데이터를 버퍼에 채우는 동안 직렬화 오류가 발생하면 인코더는 데이터 버퍼를 해제해야합니다. 예 :

DataBuffer buffer = factory.allocateBuffer();
boolean release = true;
try {
    // serialize and populate buffer..
    release = false;
}
finally {
    if (release) {
        DataBufferUtils.release(buffer);
    }
}
return buffer;

소비자는 소비자가 Encoder받는 데이터 버퍼를 공개해야합니다. WebFlux 응용 프로그램에서의 출력은 EncoderHTTP 서버 응답 또는 클라이언트 HTTP 요청에 쓰는 데 사용됩니다.이 경우 데이터 버퍼를 해제하는 것은 서버 응답 또는 클라이언트 요청에 대한 코드 작성의 책임입니다 .

Netty에서 실행할 때 버퍼 누수 문제해결 하기위한 디버깅 옵션이 있습니다 .

Last updated