HBase之Table.put客戶端流程(續)
上篇博文中已經談到,有兩個流程沒有講到。一個是MetaTableAccessor.getRegionLocations,另外一個是ConnectionImplementation.cacheLocation。這一節,就讓我們單獨來介紹這兩個流程。
首先讓我們來到MetaTableAccessor.getRegionLocations。
1.呼叫MetaTableAccessor.getRegionInfo,獲取返回結果集中指定的列資訊(info:regioninfo)的值。在這個方法的呼叫過程中,有一個知識點需要大家關注——Result.binarySearch。我將放在後面講解。
2.然後呼叫了Result.getNoVersionMap。在這裡,完成了對返回結果集的含version版本資訊的封裝與不含version版本資訊的封裝,同樣,我將放在後面講解。
首先讓我們來到Result.binarySearch。大家可以看到這裡使用的kvs[0]的rowKey,然後使用了傳入的family(info)與qualifier(regioninfo)。大家可能比較迷惑,為什麼這裡的邏輯是這樣的。原因很簡單,因為這裡傳入的Cell陣列的rowKey都是一樣的,要利用Arrays.binarySearch搜尋指定family:qualifier。因此首先使用這些資訊構造了一個封裝了以上資訊的FirstOnRowColCell。這裡需要注意的是,新建的cell.getTimestamp返回值為HConstants.LATEST_TIMESTAMP = Long.MAX_VALUE。這裡,大家可能會對Arrays.binarySearch的返回值比較新奇,為什麼結果是負值包括後面為什麼有表示式(pos = (pos+1) * -1)。大家感興趣的可以追一下原始碼,我只簡單說一下結論。在呼叫Arrays.binarySearch方法時,如果所要搜尋的陣列中包含鍵,則返回鍵在該陣列的位置,然而,如果陣列中不包含鍵,那麼就返回-(insertion point) - 1。這裡的insertion point就是該陣列中第一個元素大於鍵的索引位置(the index of the first element greater than the key)。如果大家還是不懂,在網上搜一下就明白了,我在這裡就不詳述了。後面通過表示式(pos = (pos+1) * -1)也就獲取的Arrays.binarySearch後的insertion point。看到這裡大家可能還有點迷惑,不過,相信我在介紹完CellComparatorImpl後,大家可能就恍然大悟了。
接下來讓我們來到CellComparatorImpl.compare方法。這裡主要呼叫了compareRows與compareWithoutRow。compareRows比較簡單,就是比較傳入Cell的rowKey。真正重要的是compareWithoutRow。
接下來讓我們來到CellComparatorImpl.compareWithoutRow方法。這裡比較容易誤會的是compareTimestamps。
接下來讓我們來到CellComparatorImpl.compareTimestamps。正如截圖中註釋所說,交換順序以實現將相同的family:qualifier按照時間戳的降序來排列(family與qualifier都是按照升序來排列的)。看到這裡,相信大家就能夠明白為什麼構建的Cell時間戳為Long.MAX_VALUE。
不過,我還是在這裡再簡單介紹一下。上面我已經提到Arrays.binarySearch中insertion point是該陣列中第一個元素大於鍵的索引位置(the index of the first element greater than the key)。假如,如果說這裡的CellComparatorImpl.compareTimestamps為升序排列,那麼,上面構造的key的insertion point為陣列中相同family:qualifier的index + 1。而這裡改為降序之後,構造的key的insertion point為陣列中相同family:qualifier的index。而這個結果正是我們需要的。
到這裡,大家可能就明白了Result.getColumnLatestCell方法的含義——獲取指定family:qualifier中時間戳最接近Long.MAX_VALUE的cell。
接下來我插入一個知識點——Result.getMap與Result.getNoVersionMap。這裡獲取的是含version資訊的列。通過其中的versionMap.put方法我們就可以知道,這裡將不同version的value值儲存在map中了。
然後來到Result.getNoVersionMap。在這裡獲取的是不含version的列。由於上面在構造versionMap時傳入的Comparator為倒序排序,因此,這裡通過qualifierEntry.getValue().firstKey()獲得的是最新版本的value。
接下來,讓我們來到本節中另外一個也是最後一個重要的方法ConnectionImplementation.cacheLocation。由於其主要呼叫了MetaCache.getCachedLocation,因此,我在這裡貼出MetaCache.getCachedLocation原始碼,如下圖所示。其中比較重要的方法是MetaCache.getTableLocations。
接下來讓我們來到MetaCache.getTableLocations,如下圖所示。如果看過我的上篇博文《HBase之Table.put客戶端流程》,大家可能知道,我埋了一個伏筆,也就是這裡的最後一個入參。上一篇中的與這裡的入參型別不同,但是方法的呼叫流程是一樣的,我就在這裡詳細講解。
上圖中最後一個入參是java.util.function.Supplier。如下圖所示。
上圖中的最後一個入參型別是Runnable。看到這裡,大家可能就明白了。如果在MetaCache.cachedRegionLocations中並沒有相應的key,value對,那麼就會呼叫supplier.get方法,也就是getTableLocations的最後一個入參,重新構建一個CopyOnWriteArrayMap,並且將內部的比較器設定為Bytes.BYTES_COMPARATOR。然後將其放到MetaCache.cachedRegionLocations。
到此為止,完整的《HBase之Table.put客戶端流程》就結束了。大家如果有什麼疑問或者大資料相關的問題可以傳送至我的郵箱15935152719@163. com。
從下一節起,也就是本週末,我將為大家帶來HBase的第二章內容——Hbase之Client協議。屆時,Client協議中的服務端與客戶端的完整流程將為大家一一奉上。如果比較關注其中的內容可以關注我,或者成為我的粉絲,都是就可以及時收到更新啦。