序文
前の記事のオゾンデータの書き込み処理の分析は、我々はオゾン分析にデータを書き込む処理を共有しています。、別のプロセス、リードデータ解析処理に対応する共有する著者。一般的に共通の部分と、プロセスのオゾンデータの読み取りと書き込み、話す、ブロック、チャンク、バッファの概念に関連しています。複雑さの点では、読み取り処理は、理解しやすいの数書き込み処理よりも簡単です。
オゾン読み取りプロセスデータ:ブロックによる読み出したデータは、チャンクのオフセット
あなたがプロセスを書いオゾンデータ上の記事の著者を熟読されている場合は、オゾンキーデータがブロックに応じて分割し、各ブロックは、チャンク単位に基づいて、さらに書き込みデータであることを知っている必要があります。チャンクチャンクは、ファイルに対応しています。ブロックは、内部仮想概念であるが、データノードのコンテナブロックは、それぞれのチャンクリストの下の情報を保持します。
主に、ブロックの複数に分割されたデータセグメントによれば、各データのブロック開始位置は、グローバルモードでオフセット当然異なっています。ブロックは、第2実施例の値が、ブロックの長さに等しいオフセット。ブロックデータ編成の下のチャンクは、同じ理由です。
別に依存読み取りデータからのオフセットが、追加のニーズはクライアントが事前にそのような情報、主に以下の3つの操作を知らないの後に、他のサービスにブロック、チャンク情報を読み取ることがあります。
- クライアントはOzoneManagerにクエリー要求キー情報を開始し、キー情報が返されたブロックの下にあるすべての情報が含まれています
- 内部ブロックのストリームデータコンテナDBはデータノードでブロックを照会し、ブロック情報は、彼らがチャンクを持っ属する情報が含まれています
- 次いで、チャンクストリームデータノードの実際のチャンクデータファイル情報を問い合わせ、それ自体の中に外部の読み取りのためにバッファにロード
要約すると、図の全体的なプロセスは次のとおりです。
関連するコードデータの解析は、オゾンを読みます
コードは、キーの読み取りのいくつかを達成するための分析方法に関連してみましょう。
OMキー操作情報に最初のクライアントサービスのクエリ、
public OzoneInputStream readFile(String volumeName, String bucketName,
String keyName) throws IOException {
OmKeyArgs keyArgs = new OmKeyArgs.Builder()
.setVolumeName(volumeName)
.setBucketName(bucketName)
.setKeyName(keyName)
.setSortDatanodesInPipeline(topologyAwareReadEnabled)
.build();
// 1.client向OM查询给你key的metadata信息,里面包含有key下的block信息
// 然后client用查询得到的key信息构造输入流对象.
OmKeyInfo keyInfo = ozoneManagerClient.lookupFile(keyArgs);
return createInputStream(keyInfo);
}
その後、複数のブロックストリームオブジェクトを作成するKeyInputStream背後に初期化方法を実行します
private synchronized void initialize(String keyName,
List<OmKeyLocationInfo> blockInfos,
XceiverClientManager xceiverClientManager,
boolean verifyChecksum) {
this.key = keyName;
this.blockOffsets = new long[blockInfos.size()];
long keyLength = 0;
// 2.KeyInputStream根据查询得到的key block信息构造对应BlockOutputStream对象
for (int i = 0; i < blockInfos.size(); i++) {
OmKeyLocationInfo omKeyLocationInfo = blockInfos.get(i);
if (LOG.isDebugEnabled()) {
LOG.debug("Adding stream for accessing {}. The stream will be " +
"initialized later.", omKeyLocationInfo);
}
// 3.构造BlockOutputStream并加入到block stream列表中
addStream(omKeyLocationInfo, xceiverClientManager,
verifyChecksum);
// 4.更新当前创建的BlockOutputStream在全局key文件下的偏移量值
this.blockOffsets[i] = keyLength;
// 5.更新当前的key len,此值将成为下一个BlockOutputStream的初始偏移量
keyLength += omKeyLocationInfo.getLength();
}
this.length = keyLength;
}
ブロックは、そのデータに基づいてオフセット操作を読んで、
public synchronized int read(byte[] b, int off, int len) throws IOException {
checkOpen();
if (b == null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
return 0;
}
int totalReadLen = 0;
// 当还有剩余需要读取的数据时,继续进行block的数据读取
while (len > 0) {
// 当当前的block下标已经是最后一个block stream,并且最后一个block
// stream的未读数据长度为0时,说明key文件数据已全部读完,操作返回.
if (blockStreams.size() == 0 ||
(blockStreams.size() - 1 <= blockIndex &&
blockStreams.get(blockIndex)
.getRemaining() == 0)) {
return totalReadLen == 0 ? EOF : totalReadLen;
}
// 1.获取当前准备读取的BlockInputStream对象
BlockInputStream current = blockStreams.get(blockIndex);
// 2.计算后面需要读取的数据长度,取剩余需要读取的数据长度和当前
// BlockInputStream未读的数据长度间的较小值.
int numBytesToRead = Math.min(len, (int)current.getRemaining());
// 3.从BlockInputStream中读取数据到字节数组中
int numBytesRead = current.read(b, off, numBytesToRead);
if (numBytesRead != numBytesToRead) {
// This implies that there is either data loss or corruption in the
// chunk entries. Even EOF in the current stream would be covered in
// this case.
throw new IOException(String.format("Inconsistent read for blockID=%s "
+ "length=%d numBytesToRead=%d numBytesRead=%d",
current.getBlockID(), current.getLength(), numBytesToRead,
numBytesRead));
}
// 4.更新相关指标,offset偏移量,剩余需要读取的数据长度更新
totalReadLen += numBytesRead;
off += numBytesRead;
len -= numBytesRead;
// 5.如果当前的Block数据读完了,则block下标移向下一个block
if (current.getRemaining() <= 0 &&
((blockIndex + 1) < blockStreams.size())) {
blockIndex += 1;
}
}
return totalReadLen;
}
ブロックストリームは、上記再度実際の読み取り操作のチャンクストリームを含む読み出し動作を起動し、上記方法は、基本的には同じ論理です。
さらにデータは、操作方法は、方法を模索読み取ります
public synchronized void seek(long pos) throws IOException {
checkOpen();
if (pos == 0 && length == 0) {
// It is possible for length and pos to be zero in which case
// seek should return instead of throwing exception
return;
}
if (pos < 0 || pos > length) {
throw new EOFException(
"EOF encountered at pos: " + pos + " for key: " + key);
}
// 1. 更新Block的索引位置
if (blockIndex >= blockStreams.size()) {
// 如果Index超过最大值,则从blockOffsets中进行二分查找Index值
blockIndex = Arrays.binarySearch(blockOffsets, pos);
} else if (pos < blockOffsets[blockIndex]) {
// 如果目标位置小于当前block的offset,则缩小范围到[0, blockOffsets[blockIndex]]
// 进行查找
blockIndex =
Arrays.binarySearch(blockOffsets, 0, blockIndex, pos);
} else if (pos >= blockOffsets[blockIndex] + blockStreams
.get(blockIndex).getLength()) {
// 否则进行剩余部分[blockOffsets[blockIndex+1], blockOffsets[blockStreams.size() - 1]]
blockIndex = Arrays
.binarySearch(blockOffsets, blockIndex + 1,
blockStreams.size(), pos);
}
if (blockIndex < 0) {
// Binary search returns -insertionPoint - 1 if element is not present
// in the array. insertionPoint is the point at which element would be
// inserted in the sorted array. We need to adjust the blockIndex
// accordingly so that blockIndex = insertionPoint - 1
blockIndex = -blockIndex - 2;
}
// 2.重置上次BlockOutputStream seek的位置
blockStreams.get(blockIndexOfPrevPosition).resetPosition();
// 3.重置当前Block下标后的block的位置
for (int index = blockIndex + 1; index < blockStreams.size(); index++) {
blockStreams.get(index).seek(0);
}
// 4. 调整当前Block到目标给定的位置=给定位置-此block的全局偏移量
blockStreams.get(blockIndex).seek(pos - blockOffsets[blockIndex]);
blockIndexOfPrevPosition = blockIndex;
}
読み取りロジックおよびキーストリームブロックストリームの内部は実質的に均一に達成するので、ここではスキップ。私たちは、チャンクストリームは、バッファデータ読み出し処理を指示見えます。
チャンクストリームザ・は次のように操作があるお読みください。
public synchronized int read(byte[] b, int off, int len) throws IOException {
// According to the JavaDocs for InputStream, it is recommended that
// subclasses provide an override of bulk read if possible for performance
// reasons. In addition to performance, we need to do it for correctness
// reasons. The Ozone REST service uses PipedInputStream and
// PipedOutputStream to relay HTTP response data between a Jersey thread and
// a Netty thread. It turns out that PipedInputStream/PipedOutputStream
// have a subtle dependency (bug?) on the wrapped stream providing separate
// implementations of single-byte read and bulk read. Without this, get key
// responses might close the connection before writing all of the bytes
// advertised in the Content-Length.
if (b == null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
return 0;
}
checkOpen();
int total = 0;
while (len > 0) {
// 1.准备读取len长度数据到Buffer中
int available = prepareRead(len);
if (available == EOF) {
// There is no more data in the chunk stream. The buffers should have
// been released by now
Preconditions.checkState(buffers == null);
return total != 0 ? total : EOF;
}
// 2.从buffer读数据到输入数组中,此过程buffer的position会往后移动available长度
buffers.get(bufferIndex).get(b, off + total, available);
// 3.更新剩余长度
len -= available;
total += available;
}
// 4.如果已经读到Chunk尾部了,则释放buffer空间
if (chunkStreamEOF()) {
// smart consumers determine EOF by calling getPos()
// so we release buffers when serving the final bytes of data
releaseBuffers();
}
return total;
}
PrepareRead操作は、データノードがバッファにロードされたチャンクからデータを読み込みます
private synchronized int prepareRead(int len) throws IOException {
for (;;) {
if (chunkPosition >= 0) {
if (buffersHavePosition(chunkPosition)) {
// The current buffers have the seeked position. Adjust the buffer
// index and position to point to the chunkPosition.
adjustBufferPosition(chunkPosition - bufferOffset);
} else {
// Read a required chunk data to fill the buffers with seeked
// position data
readChunkFromContainer(len);
}
}
// 如果Chunk之前没有seek到某个位置,则获取当前buffer,判断是否包含数据
if (buffersHaveData()) {
// Data is available from buffers
ByteBuffer bb = buffers.get(bufferIndex);
return len > bb.remaining() ? bb.remaining() : len;
} else if (dataRemainingInChunk()) {
// 如果当前buffer不包含数据并且chunk有剩余数据需要被读,
// 则读取chunk数据到buffer中
readChunkFromContainer(len);
} else {
// All available input from this chunk stream has been consumed.
return EOF;
}
}
}
ループのそれぞれの端部において、上記方法は、chunkStreamEOFチェック読取位置となり
/**
* 检查是否已经抵达Chunk尾部.
*/
private boolean chunkStreamEOF() {
if (!allocated) {
// Chunk data has not been read yet
return false;
}
// 判断读取的位置是否已经达到Chunk末尾的2个条件:
// 1)buffer中是否还有数据
// 2)是否已经达到chunk的length长度
if (buffersHaveData() || dataRemainingInChunk()) {
return false;
} else {
Preconditions.checkState(bufferOffset + bufferLength == length,
"EOF detected, but not at the last byte of the chunk");
return true;
}
}
頻繁に使用するのByteBuffer IOを削減するチャンクストリームは、効率を改善するために、操作をお読みください。
OKが、上記のプロセスは、オゾン読み出しデータ分析で、コア・ポイントは、ブロック間でデータを読み取ることで、チャンクは、データに基づいてオフセット。