Loading...
Spring Framework Reference Documentation 7.0.2의 Data Buffers and Codecs의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
Java NIO는 ByteBuffer를 제공하지만, 특히 버퍼 재사용 및/또는 direct 버퍼 사용이
성능에 유리한 네트워크 작업의 경우, 많은 라이브러리들이 그 위에 자신들만의 바이트 버퍼 API를
구현합니다. 예를 들어 Netty에는 ByteBuf hierarchy가 있고,
Jetty는 콜백을 통해 반환되는 풀링된 바이트 버퍼를 사용합니다.
spring-core 모듈은 다음과 같이 다양한 바이트 버퍼 API를 다루기 위한 일련의 추상화를 제공합니다:
DataBufferFactory는 데이터 버퍼 생성에 대한 추상화를 제공합니다.
DataBuffer는
풀링될 수 있는 바이트 버퍼를 나타냅니다.
DataBufferUtils는 데이터 버퍼를 위한 유틸리티 메서드들을 제공합니다.
Codecs는 데이터 버퍼 스트림을 상위 수준 객체로 디코드 또는 인코드합니다.
DataBufferFactoryDataBufferFactory는 다음 두 가지 방식 중 하나로 데이터 버퍼를 생성하는 데 사용됩니다:
새로운 데이터 버퍼를 할당합니다. 이때 미리 알고 있다면 capacity를 선택적으로 지정할 수 있으며,
이는 DataBuffer 구현이 필요에 따라 증가 및 축소될 수 있음에도 불구하고 더 효율적입니다.
기존 byte[] 또는 java.nio.ByteBuffer를 래핑하여, 주어진 데이터를 allocation 없이
DataBuffer 구현으로 데코레이트합니다.
WebFlux 애플리케이션은 DataBufferFactory를 직접 생성하지 않고,
대신 클라이언트 측에서 ServerHttpResponse 또는 ClientHttpRequest를 통해 접근한다는 점에 유의하십시오.
팩토리의 타입은 기반이 되는 클라이언트 또는 서버에 따라 달라지며, 예를 들어
Reactor Netty의 경우 NettyDataBufferFactory, 그 외의 경우 DefaultDataBufferFactory입니다.
DataBufferDataBuffer 인터페이스는 java.nio.ByteBuffer와 유사한 연산을 제공하지만,
Netty ByteBuf에서 영감을 받은 몇 가지 추가적인 이점을 제공합니다.
아래는 그 이점의 일부 목록입니다:
읽기와 쓰기에 대해 독립적인 position을 가지므로, 읽기와 쓰기를 번갈아 하기 위해
flip() 호출이 필요하지 않습니다.
java.lang.StringBuilder와 같이 필요에 따라 capacity가 확장됩니다.
PooledDataBuffer를 통한 풀링된 버퍼 및 reference counting.
버퍼를 java.nio.ByteBuffer, InputStream, 또는 OutputStream으로 view할 수 있습니다.
주어진 바이트에 대한 index 또는 마지막 index를 결정할 수 있습니다.
PooledDataBufferByteBuffer에 대한 Javadoc에서 설명한 것처럼, 바이트 버퍼는 direct일 수도 있고 non-direct일 수도 있습니다. direct 버퍼는 Java 힙 외부에 위치할 수 있으며, 이는 네이티브 I/O 연산을 위한 copying 필요성을 제거합니다. 이로 인해 direct 버퍼는 소켓을 통한 데이터 송수신에 특히 유용하지만, 생성과 해제가 더 비용이 많이 들기 때문에 버퍼 풀링이라는 개념으로 이어집니다.
PooledDataBuffer는 바이트 버퍼 풀링에 필수적인 reference counting을 돕는
DataBuffer의 확장입니다. 어떻게 동작할까요? PooledDataBuffer가 할당되면
reference count는 1입니다. retain() 호출은 count를 증가시키고,
release() 호출은 count를 감소시킵니다. count가 0보다 큰 동안에는
버퍼가 해제되지 않을 것이 보장됩니다. count가 0으로 감소되면, 풀링된 버퍼는
해제될 수 있으며, 실제로는 버퍼를 위해 예약된 메모리가 메모리 풀로 반환된다는 것을 의미할 수 있습니다.
PooledDataBuffer를 직접 다루는 대신, 대부분의 경우 DataBufferUtils의 편의 메서드를 사용하는 것이 더 좋다는 점에 유의하십시오. 이 메서드들은
DataBuffer가 PooledDataBuffer의 인스턴스인 경우에만 release 또는 retain을 적용합니다.
DataBufferUtilsDataBufferUtils는 데이터 버퍼를 다루기 위한 여러 유틸리티 메서드를 제공합니다:
예를 들어, 기반 바이트 버퍼 API가 composite 버퍼를 지원하는 경우, zero copy로 데이터 버퍼 스트림을 하나의 버퍼로 join합니다.
InputStream 또는 NIO Channel을 Flux<DataBuffer>로 변환하고,
반대로 Publisher<DataBuffer>를 OutputStream 또는 NIO Channel로 변환합니다.
버퍼가 PooledDataBuffer의 인스턴스인 경우, DataBuffer를 release 또는 retain하는 메서드.
특정 바이트 count에 도달할 때까지 바이트 스트림을 skip 또는 take합니다.
org.springframework.core.codec 패키지는 다음 strategy 인터페이스를 제공합니다:
Encoder는 Publisher<T>를 데이터 버퍼 스트림으로 인코드합니다.
Decoder는 Publisher<DataBuffer>를 상위 수준 객체 스트림으로 디코드합니다.
spring-core 모듈은 byte[], ByteBuffer, DataBuffer, Resource, 그리고
String encoder 및 decoder 구현을 제공합니다. spring-web 모듈은 Jackson JSON,
Jackson Smile, JAXB2, Protocol Buffers 및 기타 encoder와 decoder를 추가합니다.
WebFlux section의 Codecs를 참조하십시오.
DataBuffer데이터 버퍼를 사용할 때, 버퍼가 풀링될 수 있으므로 버퍼가 release되도록 특별한 주의를 기울여야 합니다. 여기서는 codec을 사용하여 그 동작 방식을 설명하지만, 이 개념은 보다 일반적으로 적용됩니다. 데이터 버퍼를 관리하기 위해 codec 내부에서 수행해야 하는 작업을 살펴보겠습니다.
Decoder는 상위 수준 객체를 생성하기 전에 input 데이터 버퍼를 마지막으로 읽는 주체이므로,
다음과 같이 이를 release해야 합니다:
Decoder가 각 input 버퍼를 단순히 읽고 즉시
release할 준비가 되어 있는 경우, DataBufferUtils.release(dataBuffer)를 통해 그렇게 할 수 있습니다.
Decoder가 flatMap, reduce 등과 같이 내부적으로 데이터 item을 prefetch 및 cache하는
Flux 또는 Mono operator를 사용하거나, filter, skip 등과 같이 item을 생략하는
operator를 사용하는 경우, 이러한 버퍼가 discard되기 전에 release되도록 보장하기 위해,
error 또는 cancellation signal의 결과로 discard되는 경우를 포함하여,
composition chain에 doOnDiscard(DataBuffer.class, DataBufferUtils::release)를 추가해야 합니다.
Decoder가 그 밖의 어떤 방식으로든 하나 이상의 데이터 버퍼를 유지하는 경우,
완전히 읽혔을 때, 또는 cache된 데이터 버퍼가 읽히고 release되기 전에 발생하는
error 또는 cancellation signal의 경우에 release되도록 보장해야 합니다.
DataBufferUtils#join은 데이터 버퍼 스트림을 하나의 데이터 버퍼로 aggregate하기 위한
안전하고 효율적인 방법을 제공한다는 점에 유의하십시오.
마찬가지로 skipUntilByteCount와 takeUntilByteCount는 decoder가 사용할 수 있는
추가적인 안전한 메서드입니다.
Encoder는 다른 주체가 읽고 (그리고 release해야 하는) 데이터 버퍼를 할당합니다.
따라서 Encoder가 할 일은 많지 않습니다. 그러나 Encoder는 버퍼에 데이터를 채우는 동안
serialization error가 발생할 경우 데이터 버퍼를 release하도록 주의해야 합니다. 예를 들어:
1DataBuffer buffer = factory.allocateBuffer(); 2boolean release = true; 3try { 4 // serialize and populate buffer.. 5 release = false; 6} 7finally { 8 if (release) { 9 DataBufferUtils.release(buffer); 10 } 11} 12return buffer;
1val buffer = factory.allocateBuffer() 2var release = true 3try { 4 // serialize and populate buffer.. 5 release = false 6} finally { 7 if (release) { 8 DataBufferUtils.release(buffer) 9 } 10} 11return buffer
Encoder의 consumer는 자신이 받는 데이터 버퍼를 release할 책임이 있습니다.
WebFlux 애플리케이션에서는 Encoder의 output이 HTTP 서버 response 또는 클라이언트 HTTP request에
write하는 데 사용되며, 이 경우 데이터 버퍼를 release하는 책임은
서버 response 또는 클라이언트 request에 write하는 코드에 있습니다.
Netty에서 실행할 때는 troubleshooting buffer leaks를 위한 디버깅 옵션이 있다는 점에 유의하십시오.
Null-safety
Ahead of Time Optimizations