天天看點

Hypertable源碼解讀之Hypertable.RangeServer目錄

1. Class global

維護了rangeserver程序的全局變量.其中有幾個隊列需要區分:

global::maintenance_queue:MaintenanceQueue類指針,表示 rangeserver的背景維護任務隊列;

global::work_queue:MetaLog::EntityTask類指針數組,表示一組針對rangeserver的metalog entity的任務。

這兩個隊列是有聯系的,後者可以作為前者中的一個元素,即可将針對一組metalog entity的操作作為一項任務添加到前者中,例如以下代碼(MaintenanceScheduler.schedule片段):

MaintenanceTaskWorkQueue *task = 0;

  {

    ScopedLock lock(Global::mutex);

    if (!Global::work_queue.empty())

      task = new MaintenanceTaskWorkQueue(3, 0, Global::work_queue);

  }

  if (task)

    Global::maintenance_queue->add(task);

2. Class CellStoreV6  : public CellStore

最新版本的CellStore類。

2.1. 成員函數

2.1.1. load_block_index

聲明:void  load_block_index();;

功能:對于一個特定的range,加載其在CellStore檔案中的所有索引塊。每個索引塊将記錄offset和key(該索引中的最大key),多個索引塊形成一個數組。索引快中的key對應的實際資料将被統一儲存在一個StaticBuffer中。即block_index占用的記憶體為數組記憶體+StaticBuffer記憶體。在rangeserver的記憶體統計中添加block_index的占用記憶體量。

2.1.2. open

聲明:void  open(const String &fname, const String &start_row, const String &end_row, int32_t fd, int64_t file_length, CellStoreTrailer *trailer);

功能:在檔案描述符fd對應的CellStore檔案中,擷取start_row和end_row對應的range的索引塊。在rangeserver的記憶體統計中添加一個CellStoreV6和CellStoreInfo對象的記憶體占用量。注意:該函數将置成員變量m_64bit_index為false。

3. Class  CellStoreFactory

該類隻有一個靜态函數open。該函數打開指定名稱的CellStore檔案,擷取檔案的trailer,從trailer中讀取version。針對不同的版本,建立不同的CellStoreTrailer對象和CellStore對象指針。由于目前使用的version是6,是以建立CellStoreTrailerV6對象和CellStoreV6對象指針。已經打開檔案的trailer将反序列化到CellStoreTrailer對象,也就是說該對象持有的就是目前檔案的trailer。建立一個CellStoreV6對象,并加載block_index,然後傳回其指針。

4. class AccessGroup : public CellList

一個access group的全名為:table_name + "[" + start_row + ".." + end_row + "](" + access_group_name + ")"。

該類包含3個子類:CellStoreMaintenanceData、MaintenanceData、Hints。

CellStoreMaintenanceData表示每個CellStore檔案的維護資訊,并有成員變量指向該CellStore檔案。

MaintenanceData表示該access group的維護資訊,包含其對應的CellStore檔案的維護資訊,并有成員變量指向該access group。

Hints記錄了該access group的名稱、latest_stored_revision、disk_usage和files,見AccessGroupHintsFile類。

5. class AccessGroupHintsFile

每個access group對象都有一些狀态變量,access group對象構造後會馬上需要這些變量。這些變量能夠提升系統性能,但是計算代價比較昂貴,因為它需要在METADATA表中掃描父rang對應的記錄,并且需要打開和加載組成該access group的所有CellStore檔案的塊索引。如果允許在叢集啟動時計算這些變量,然後将其保持在名為hints的磁盤檔案中,并且在access group對象構造前讀取hints檔案,這将是一種比較劃算的方案。

每個range将會有一個hints檔案,組成該range的每個access group将在檔案中對應一塊内容。每個range的hints檔案路徑如下:

/hypertable/tables/<table-id>/default/<md5-end-row>/hints

該檔案是YAML格式,包含了一個版本号、start row、end row和每個access group的概要資訊,如下所示:

Version: 2

Start Row: <start-row>

End Row: <start-row>

Access Groups: {

ag_name: {

LatestStoredRevision: <revision>,

DiskUsage: $bytes,

Files: $file_list

}

...

}

每個access group的概要資訊描述如下:

LatestStoredRevision:已經寫入到access group的CellStore檔案中的cell的最新的revision。叢集重新開機後回放commit log時,access group能安全的删除commit log中revision小于等于該值的所有的cell。

DiskUsage:access group的CellStore檔案所占據的磁盤空間,用于判斷一個range是否需要分裂。

Files:access group的CellStore檔案名稱清單,以分号分隔。access group對象構造後,該屬性暫不需要。但是如果METADATA表損壞,或者hyperspace丢失時,它能用于災難恢複。

将一個range的所有access group的概要資訊寫入一個hints檔案是為了提升啟動性能,即在啟動時,不管有多少個access group,隻對一個range讀取一次hints檔案。

6. class Range  :  public CellList

一個range的全名為: table_name + "[" + start_row + ".." + end_row + "]"。

rang對象構造時主要完成的任務:

1)計算METADATA和使用者表的range分裂大小,前者如果沒有配置,則預設和後者一緻;

2)計算split_off為hight還是low;

3)讀取hints檔案;

4)建立每個access group對象,并将其記錄到m_access_group_map、m_access_group_vector和m_column_family_vector中。m_access_group_map的key為access group名稱,value為access group指針,m_column_family_vector的下标為cf的編号,值為access group指針。

7. Class  RangeSet

抽象類,用于在一個range集合中删除一個range,或者改變range的startrow/endrow。

8. Class  TableInfo :  public RangeSet

管理一個表中處于激活狀态的所有range。

8.1. 成員函數

8.1.1. find_containing_range

聲明:bool find_containing_range(const String &row, RangePtr &range, String &start_row, String &end_row);

功能:尋找給定的row屬于的range。

參數:row給定的row。後三個都是輸出參數,分别表示找到的range,以及range的start row和end row。

傳回值:如果找到傳回true,否則傳回false。

9. Class  LoadStatistics

統計應用程式的負載,包括:scan、update和sync。該類用一個時間周期初始化,即表示統計的間隔周期。大部分最新收集的統計資訊存儲在m_computed成員變量中,目前周期正在收集的統計資訊存儲在m_running成員變量中。

10. Class  StatsRangeServer

RangeServer統計類,用于RangeServer的監控功能。

11. Class MaintenancePrioritizer

設定range維護任務的優先級。其包含了一個嵌套類MemoryState,表示目前的記憶體狀态。MemoryState有三個成員變量:limit表示rangeserver的記憶體限制;balance表示rangeserver目前使用的記憶體量;needed表示rangeserver目前需求的記憶體量。MemoryState有兩個成員函數:decrement_needed表示減少仍然需要的記憶體量;need_more表示是否仍然需要更多的記憶體量。

下述幾個成員函數在操作range時,都是針對非busy狀态的range。

11.1. 成員函數

11.1.1. Schedule_initialization_operation

聲明:void schedule_initialization_operations(std::vector<RangeData> &range_data,                                            int32_t &priority);

功能:

修改未初始化的range的優先級。

周遊range_data。如果range未初始化,置成員變量m_uninitialized_ranges_seen為true;如果range處于busy狀态,或者range的優先級不為0,則忽略本range,開始處理下一個range;如果range未初始化,則置其優先級為priority,并将priority加1.

參數:

Range_data:range集合;

Priority:初始優先級。

11.1.2. Schedule_inprogress_operation

聲明:bool schedule_inprogress_operations(std::vector<RangeData> &range_data, MemoryState &memory_state, int32_t &priority, String *trace);

功能:對指定的一組range中,維護資訊中:字段state為RELINQUISH_LOG_INSTALLED或者SPLIT_LOG_INSTALLED的range,釋放其占用的記憶體,并且改變其維護資訊中的維護标記和優先級。

周遊range_data,對不處于busy狀态的range執行下列操作之一:

a)如果range狀态(state)為RELINQUISH_LOG_INSTALLED,則在range的維護标記(maintenance_flags)上追加RELINQUISH;

b)如果range狀态為SPLIT_LOG_INSTALLED或者SPLIT_SHRUNK,則在range的維護标記上追加SPLIT。

上述兩個操作都會将in_progress置為true。如果in_progress為true,執行下列操作:

a)置range的優先級為priority,并将priority加1;

b)如果range狀态為RELINQUISH_LOG_INSTALLED或者SPLIT_LOG_INSTALLED,從rangeserver目前需求的記憶體量中減去該range的記憶體占用量(包括每個ag的記憶體配置設定量,以及ag中每個CellStore檔案的bloom_filter和block_index的記憶體占用量)。

參數:

Range_data:range集合;

memory_state:目前rangeserver的記憶體狀态;

Priority:初始優先級。

傳回值:true表示rangeserver目前需求的記憶體量沒有滿足,反之為false。

11.1.3. Schedule_split_ and_relinquishes

聲明:bool schedule_split_and_relinquishes(std::vector<RangeData> &range_data, MemoryState &memory_state, int32_t &priority, String *trace);

功能:對指定的一組range中,維護資訊中:優先級為0, relinquish或者needs_split字段為true的range,釋放其占用的記憶體,并且改變其維護資訊中的維護标記和優先級。

周遊range_data,統計不處于busy狀态或優先級為0的range的磁盤和記憶體占用量。前者為range中所有ag的磁盤預估量(disk_estimate)總和,後者不但包含每個ag的記憶體配置設定量(mem_allocated),還包含每個ag中每個CellStore檔案的bloom_filter和block_index的記憶體占用量。

如果range沒有出現RANGESERVER_ROW_OVERFLOW類型的錯誤,執行下列兩種操作之一:

a)如果range需要relinquish,則在range的維護标記上追加RELINQUISH;

b)如果range需要split,并且不是root表的range,則在range的維護标記上追加SPLIT。

上述兩個操作都會置range的優先級為priority,并将priority加1。還會從rangeserver目前需求的記憶體量中減去該range的記憶體占用量。

參數:

Range_data:range集合;

memory_state:目前rangeserver的記憶體狀态;

Priority:初始優先級。

傳回值:true表示rangeserver目前需求的記憶體量沒有滿足,反之為false。

11.1.4. Schedule_ necessary_compactions

聲明:bool schedule_necessary_compactions(std::vector<RangeData> &range_data, CommitLog *log, int64_t prune_threshold, MemoryState &memory_state, int32_t &priority, String *trace);

功能:在指定的一組range中,對滿足commit log清理條件的range,以及有compaction需求的range,追加compac維護标記,并根據不同的compaction類型,為其ag追加不同的compaction維護标記,還改變其維護資訊中的優先級。如果目前rangeserver有記憶體需求,釋放其ag占用的記憶體。

擷取commit log的frament的統計資訊到comulative_size_map。

周遊range_data,對不處于busy狀态的range周遊其ag集合。如果能在comulative_size_map中發現大于等于ag的earliest_cached_revision的元素,則表明commit log中有資料能compact到該ag。如果comulative_size_map中該元素(也就是revision)累積的fragment大小超過了commit log清理門檻值,并且ag已使用的記憶體量也大于0,則在range維護标記上追加COMPACT,在ag維護标記上追加COMPACT_MINOR。如果目前rangeserver還需要更多的記憶體,則從rangeserver目前需求的記憶體量中減去已經配置設定給該ag的記憶體量,并分别在range和ag的維護标記上追加MEMORY_PURGE和MEMORY_PURGE_SHADOW_CACHE。如果range的優先級為0,則置其為priority,并将priority加1。

周遊range_data,對不處于busy狀态的range周遊其ag集合。如果ag的維護标記為0,執行下列操作之一:

a)如果range需要compaction(compaction_type_needed),則在ag的維護标記上追加range的compaction類型。如果目前rangeserver還需要更多的記憶體,則從rangeserver目前需求的記憶體量中減去已經配置設定給該ag的記憶體量;

b)如果ag需要進行gc(gc_needed),則在ag的維護标記上追加COMPACT_GC;

c)如果ag不是in_memory模式,并且ag已使用的記憶體大于Global::access_group_max_mem(預設1G),則在ag的維護标記上追加COMPACT_MINOR。如果目前rangeserver還需要更多的記憶體,則從rangeserver目前需求的記憶體量中減去已經配置設定給該ag的記憶體量,并分别給range和ag的維護标記追加MEMORY_PURGE和MEMORY_PURGE_SHADOW_CACHE;

d)如果ag需要進行merge操作(needs_merging),則在ag的維護标記上追加COMPACT_MERGING。

上述操作執行時,都會在range的維護标記上追加COMPACT,并且如果range的優先級為0,則置其為priority,并将priority加1.

參數:

Range_data:range集合;

memory_state:目前rangeserver的記憶體狀态;

Priority:初始優先級。

Log:commit log

傳回值:true表示rangeserver目前需求的記憶體量沒有滿足,反之為false。

11.1.5. purge_cellstore_indexes

聲明:bool purge_cellstore_indexes(std::vector<RangeData> &range_data, MemoryState &memory_state, int32_t &priority, String *trace);

功能:對指定的一組range中,處于非split狀态,并且其bloom_filter或者block_index已經占用了記憶體的range,釋放其bloom_filter和block_index占用的記憶體,并且改變其維護資訊中的維護标記和優先級。

周遊range_data,判斷其中的每個range是否同時滿足下列條件:a)不處于busy狀态或split狀态;b)range包含的任一CellStore檔案的bloom_filter或者block_index占用的記憶體量大于0。如果滿足,則執行下列操作:

a)在range和ag的維護标記上追加MEMORY_PURGE,在CellStore的維護标記上追加MEMORY_PURGE_CELLSTORE;

b)從rangeserver目前需求的記憶體量中減去CellStore檔案的bloom_filter和block_index占用的記憶體量。

C)如果range的優先級為0,則置其為priority,并将priority加1。

參數:

Range_data:range集合;

memory_state:目前rangeserver的記憶體狀态;

Priority:初始優先級。

傳回值:true表示rangeserver目前需求的記憶體量沒有滿足,反之為false。

11.1.6. compact_cellcaches

聲明:bool compact_cellcaches(std::vector<RangeData> &range_data, MemoryState &memory_state, int32_t &priority, String *trace);

功能:指定的一組range中,非split狀态的range,如果其任一ag處于非major_compaction狀态,并且已經占用了記憶體,則釋放其ag占用的記憶體,并且改變其維護資訊中的維護标記和優先級。

周遊range_data,判斷其中的每個range是否同時滿足下列條件:a)不處于busy狀态或split狀态;b)range包含的任一ag不處于major compaction狀态,并且占用的記憶體量大于0,還要不是in_memory狀态。如果滿足,則執行下列操作:

a)在range的維護标記上追加COMPACT和MEMORY_PURGE,在ag的維護标記上追加COMPACT_MINOR和MEMORY_PURGE_SHADOW_CACHE;

b)從rangeserver目前需求的記憶體量中減去ag占用的記憶體量;

c)如果range的優先級為0,則置其為priority,并将priority加1。

參數:

Range_data:range集合;

memory_state:目前rangeserver的記憶體狀态;

Priority:初始優先級。

傳回值:true表示rangeserver目前需求的記憶體量沒有滿足,反之為false。

12. class MaintenancePrioritizerLowMemory : public MaintenancePrioritizer 

低記憶體模式下,維護任務将通過此類釋放記憶體。

該類主函數為prioritize。該函數聲明為void  prioritize(std::vector<RangeData> &range_data, MemoryState &memory_state, int32_t &priority, String *trace)。主體思路為:

将集合range_data分為四個子集合:range_data_root、range_data_metadata、range_data_system、range_data_user。

對range_data_root、range_data_metadata、range_data_system集合分别執行下列操作。

1)對每個處于RELINQUISH_LOG_INSTALLED或者SPLIT_LOG_INSTALLED狀态的的range,釋放其占用的記憶體(配置設定給每個ag的,以及每個CellStore的bloom_filter和block_index記憶體),并且在其維護标記中追加RELINQUISH或者SPLIT,并增加range優先級。如果rangeserver目前還需要記憶體,則轉步驟2;

2)對每個處于relinquish或者need_split狀态的的range,釋放其占用的記憶體(配置設定給每個ag的,以及每個CellStore的bloom_filter和block_index記憶體),并且在其維護标記中追加RELINQUISH或者SPLIT,并增加range優先級。如果rangeserver目前還需要記憶體,則轉步驟3;

3)對每個滿足commit log清理條件的range,以及有compaction需求的range,追加compac維護标記,并根據不同的compaction類型,為其ag追加不同的compaction維護标記,還改變其維護資訊中的優先級,并增加range優先級。如果目前rangeserver有記憶體需求,釋放其占用的記憶體(配置設定給每個ag的記憶體);

4)增加未初始化的range的優先級。

對range_data_user集合執行下列操作。

1)對每個處于RELINQUISH_LOG_INSTALLED或者SPLIT_LOG_INSTALLED狀态的的range,釋放其占用的記憶體(配置設定給每個ag的,以及每個CellStore的bloom_filter和block_index記憶體),并且在其維護标記中追加RELINQUISH或者SPLIT,并增加range優先級。如果rangeserver目前還需要記憶體,則轉步驟2,否則轉步驟3;

2)對每個處于relinquish或者need_split狀态的的range,釋放其占用的記憶體(配置設定給每個ag的,以及每個CellStore的bloom_filter和block_index記憶體),并且在其維護标記中追加RELINQUISH或者SPLIT,并增加range優先級;

3)如果目前讀負載高,則首先緊縮每個range的CellCache(compact_cellcache)。如果rangeserver目前還需要記憶體,再緊縮每個range的block_cache。如果rangeserver目前還需要記憶體,最後清理每個range的CellStore的索引所占的記憶體(purge_cellstore_index)。轉步驟5;

4)如果目前寫負載高,則首先緊縮每個range的block_cache。如果rangeserver目前還需要記憶體,再清理每個range的CellStore的索引所占的記憶體(purge_cellstore_index)。如果rangeserver目前還需要記憶體,最後緊縮每個range的CellCache(compact_cellcache);

5)對每個滿足commit log清理條件的range,以及有compaction需求的range,追加compac維護标記,并根據不同的compaction類型,為其ag追加不同的compaction維護标記,還改變其維護資訊中的優先級,并增加range優先級。如果目前rangeserver有記憶體需求,釋放其占用的記憶體(配置設定給每個ag的記憶體);

6)修改未初始化的range的優先級。

13. Class  MaintenanceQueue

存放周期性維護任務的一個隊列,每個維護任務是類MaintenanceTask或其子類的一個對象指針。維護任務依次按其級别(level)、優先級(priority)和開始時間(start_time)進行排序,級别越高,優先級越高,開始時間越早的任務越靠前。

維護線程的數量可配置,預設在CPU核數和磁盤個數*3/2之間選擇最大值。

該類有三個嵌套類:LtMaintenanceTask、MaintenanceQueueState、Worker。LtMaintenanceTask類描述MaintenanceTask在隊列中的排序規則。MaintenanceQueueState類維護隊列狀态,其内部維護了一個任務隊列,并且維護了一個range集合。Worker類是一個函數對象,表示維護任務隊列的工作線程,用于從隊列中排程維護任務。

該類執行個體化時将會建立指定數量的線程,多個線程共享一個MaintenanceQueueState對象m_state。工作線程将從m_state.queue中擷取一個維護任務(MaintenanceTask)對象指針,然後調用其execute函數去執行實際的操作。

14. class MaintenanceTaskWorkQueue : public MaintenanceTask

此類本身不是一個隊列,也是一項維護任務,但是由于其内部有一個需要維護的MetaLog::EntityTaskPtr集合,是以名稱中就有了隊列的稱号。該類用于metalog的維護。

15. MaintenanceScheduler

背景維護任務排程類。該類的主函數為schedule,其主體邏輯如下:

1)重新計算并擷取rangeserver記憶體的各項度量名額;

2)删除Global::maintenance_queue中與range相關的維護任務;

3)擷取rangeserver上所有處于激活狀态的range和可以安全删除的transfer log,分别放入ranges和remove_ok_logs,并擷取ranges中每個range最新的維護資訊;

4)周遊ranges,得到的每種range中最小的revision(earlist_cached_revision),并清理各種類型的commit log;

5)周遊ranges,統計并列印rangeserver的記憶體統計結果和配置設定比例;

6)根據統計得到的記憶體資訊,為ranges中的每個range配置設定維護任務标記和優先級,即設定range維護資訊的maintenance_flags和priority字段;

7)周遊ranges,根據每個range的狀态,建立相應的維護任務,将其加入到Global::maintenance_queue。例如:range狀态為RangeState::RELINQUISH_LOG_INSTALLED時,将建立MaintenanceTaskRelinquish對象。如果是非初次執行本函數,将會首先基于優先級對ranges進行排序,然後才會進行周遊;

8)如果存在還未完成的MaintenanceTaskWorkQueue,即metalog處理任務,也将其加入到Global::maintenance_queue。

16. Class  TableInfoMap

管理一個rangeserver中處于激活狀态的range集合和可被安全删除的commit log集合。

range集合其實是一個map,表示tableid到tableinfo的映射, tableinfo包含了一組處于激活狀态的range。

維護任務排程器(maintenance scheduler)負責删除commit log,以及連結的transfer log。删除前,需要擷取激活狀态的range集合,計算每個range的統計資訊。如果一個commit log不再包含能被任何range compact的資料,則其可以安全删除。

然而,加載range時需要兩步操作:1)連結range的transfer log到commit log;2)持久化range到metalog。如果維護任務排程器恰好在這兩步之間執行commit log的删除操作,則可能在range成功加載之前,誤删一個新加入的transfer log。

為了避免這個問題,引入了一個新類:MetaLogEntityRemoveOkLogs。該類包含了一組雖然已經連結到commit log,但是可以被安全删除的transfer log。通過TableInfoMap::add_staged_range方法,可将該類的一個實體(entity)持久化到metalog中作為一個新的range實體。通過TableInfoMap::get_ranges方法,不但可以擷取目前處于激活狀态的range集合的一緻性的快照,還可擷取transfer log集合。

17. Class  ConnectionHandler

負責分發針對rangeserver的網絡通信事件,即将針對rangeserver的不同請求交由對應的ApplicationHandler子類進行處理。例如:當rangeserver接收到compact請求時,将由此類派發請求到RequestHandlerCompact類進行處理。

該類持有一個rangeserver指針、一個應用程式隊列(ApplicationQueue)指針。

18. class HandlerFactory : public ConnectionHandlerFactory

rangeserver的分發器構造類,即為rangeserver構造ConnectionHandler對象。

19. Class  TimerHandler:public  DispatchHandler

目前主要用于定時的處理維護隊列排程。每次定時觸發時,它會執行handle方法,其中會添加一個RequestHandlerDoMaintenance對象到rangeserver的應用程式隊列。

它也會執行低記憶體檢測,如果發現低記憶體狀态,将觸發維護優先級算法去積極的釋放記憶體。它也會暫停rangeserver的應用程式隊列,以使落後的維護線程趕上。

19.1. 成員函數

19.1.1. handle

聲明:void TimerHandler::handle(Hypertable::EventPtr &event);

功能:

1)檢測rangeserver的應用程式隊列是否處于暫停狀态,如果是,執行步驟2,否則執行步驟3;

2)如果滿足以下3個條件之一,則重新開機rangeserver的應用程式隊列:a、之前已經處于低記憶體模式,但是現在已經不處于低記憶體模式;b、暫停時間已經超過最大的等待時間(預設兩分鐘);c、m_restart_generation<=Global::maintenance_queue->generation()。轉向步驟6;

3)檢測目前是否處于低記憶體狀态,如果是,執行步驟4,否則執行步驟5;

4)如果m_low_memory_mode為true,則暫停rangeserver的應用程式隊列,否則置m_low_memory_mode為true。轉向步驟6;

5)置m_low_memory_mode為false。檢測commit log大小如果超過門檻值,則暫停rangeserver的應用程式隊列。

6)如果要處理的事件類型為定時器事件(TIMER),則判斷是否要進行背景維護任務,如果是,則添加一個RequestHandlerDoMaintenance對象到rangeserver的應用程式隊列,并置m_schedule_outstanding标記為true,否則添加一個定時器事件。

20. Class  DefinitionRangeServer : public Metalog::Definition

Rangeserver的metalog類。

21. class MetaLogEntityRange : public MetaLog::Entity

rangeserver的metalog由一組entity組成。該類的每個對象記錄一個range的狀态。

22. class MetaLogEntityRemoveOkLogs: public MetaLog::Entity

rangeserver的metalog由一組entity組成。該類的每個對象用于追蹤可被安全删除的transfer log。

23. class MetaLog::EntityTask : public MetaLog::Entity

在rangeserver的metalog entity上執行的任務。

24. Class RangeServer

Rangeserver程序的主類,将在程序的main函數中被執行個體化。Main函數中首先建立一個ConnectionManage對象,用于維護與rangeserver通訊的TCP連接配接。再建立一個預設大小為50的應用程式隊列(ApplicationQueue),用于存放針對rangeserver的請求事件。然後連接配接hyperspace,建立HyperSpace::Session對象。最後用ConnectionManage對象指針、應用程式隊列指針和HyperSpace::Session對象為參數執行個體化rangeserver。

注意:main函數在建立應用程式隊列時,已經預設建立了50個線程。這些線程值守在應用程式隊列上,一旦發現隊列中有請求事件,将會馬上予以處理。

Rangeserver對象構造時,會擷取相關的配置參數,請留意以下配置項:

Hypertable.RangeServer.MaintenanceThreads:背景維護線程數量。預設在CPU核數和磁盤個數*3/2之間選擇最大值;

Maintenance.Interval:背景維護任務的排程間隔,預設30秒;

Hypertable.RangeServer.AccessGroup.CellCache.ScannerCacheSize:該值最小需要為10000;

Hypertable.RangeServer.MemoryLimit和Hypertable.RangeServer.MemoryLimit.Percentage:rangeserver程序的記憶體限制。兩個效果類似,但是第一個優先級高,即如果配置了第一個,則會忽略第二個;

Hypertable.RangeServer.QueryCache.MaxMemory:該值最大隻能為程序記憶體限制的五分之一;

DfsBroker.Timeout:rangeserver通路DfsBroker的逾時時間。如果預設,則為Hypertable.Request.Timeout(預設6分鐘);

Hypertable.RangeServer.CommitLog.DfsBroker.Host:rangeserver操作commit log時所需的DfsBroker機器。如果預設,則等同于DfsBroker.Host(預設值localhost);

Hypertable.RangeServer.CommitLog.PruneThreshold.Max/Min/Max.MemoryPercentage:commit log需要被清理的最大和最小門檻值。即commit log小于最小值時,背景維護線程将不考慮清理,但是大于最大值時,必須予以清理。Max和Max.MemoryPercentage作用類似,但是前者優先級高于後者。後者參照的基數是實體記憶體。

Rangeserver對象構造時,會建立以下重要的對象:

Global::load_statistics:LoadStatistics類執行個體,用于統計rangeserver程序的scan、update和sync資訊,預設統計間隔時間為30秒,即為背景維護任務排程的間隔時間;

m_stats:StatsRangeServer類執行個體,用于統計rangeserver程序的監控資訊;

Global::memory_tracker:MemoryTracker類執行個體,用于統計rangeserver程序的記憶體使用資訊;

Global::log_dfs和Global::dfs:DfsBroker::Client類執行個體,rangeserver通路DfsBroker的用戶端對象;

Global::maintenance_queue:MaintenanceQueue類執行個體,rangeserver的背景維護任務隊列。該對象建立後,也會建立多個線程值守在該隊列上,一旦發現隊列中有維護任務,将會馬上予以執行;

m_live_map:TableInfoMap類執行個體,用于管理該rangeserver中處于激活狀态的range集合和可被安全删除的commit log集合;

Global::master_client和m_master_client:MasterClient類執行個體,兩者是同一類對象,通路master的用戶端;

m_maintenance_scheduler:MaintenanceScheduler類執行個體,背景維護隊列排程器,負責排程rangeserver的背景維護隊列(Global::maintenance_queue);

m_timer_handler:TimerHandler類執行個體,用于定時的啟動背景維護隊列排程器。

兩個事件分發器(ConnectionHandler對象):分别用于分發來自master的事件和非master的事件。所有針對rangeserer的事件都将被分發器添加到rangeserer的應用程式隊列中。

注意,此時rangeser已經擁有了兩個隊列:一個應用程式隊列(m_app_queue),一個背景維護任務隊列(Global::maintenance_queue)。

當Global::master_client和m_master_client對象建立後,相當于rangeserver已經向master進行了報到。Master将周期性的采集rangeserver的狀态資訊用于監控顯示,預設周期30秒,通過Hypertable.Monitoring.Interval配置項設定。來自master的采集請求會由rangeserver的事件分發器加入到應用程式隊列(m_app_queue),應用程式隊列的值守線程擷取到該請求後,會調用RequestHandlerGetStatistics對象的run函數,該函數中最終會調用rangeserver的get_statistics函數,是以rangeserver日志中會周期性的看到“Entering get_statistics()”和“Exiting get_statistics()”字樣。

接下來,構造函數會調用local_recover成員函數加載所有的range,并回放(replay)commit log。然後調用m_timer_handler.start設定一個即刻觸發的定時器,觸發對象即為自身。觸發後會調用m_timer_handler.handle函數,其會在rangeserver的應用程式隊列中添加一個RequestHandlerDoMaintenance對象。應用程式隊列的值守線程擷取到該對象後,會調用該對象的run函數,函數中實際隻調用了rangeserver對象的do_maintenance函數。該函數執行完畢前會再設定一個定時器,觸發時最終又會回到本函數,進而實作了反複的背景維護任務排程。

在local_recover和do_maintenance中,都有可能建立維護任務,并添加其到背景維護任務隊列(Global::maintenance_queue)中。此時,隊列上的值守線程将會執行具體的維護任務。local_recover隻在rangeserver程序啟動時執行一次,但是do_maintenance通過定時器觸發将會周期性的執行。

do_maintenance中主要執行的是m_maintenance_scheduler的schedule函數。當第二次觸發do_maintenance時,schedule函數會為每個未初始化的range建立一個MaintenanceTaskDeferredInitialization對象,并添加對象指針到背景維護任務隊列(Global::maintenance_queue)。此時rangeserver對象應該已經構造完畢。Global::maintenance_queue上的值守線程拿到MaintenanceTaskDeferredInitialization時,會調用其execute函數,繼而轉調range的deferred_initialization函數。

range的deferred_initialization函數會調用load_cell_stores函數,是以在rangeserver日志中會看到“Loading cellstores for range_name”字樣。load_cell_stores函數從METADATA中擷取該range包含的所有ag,以及ag中的files和nextcsid。對于每個file将會打開一個CellStore對象(CellStoreFactory::open),并将其加入ag的m_stores集合中,此時在rangeserver日志中會看到“Loading  CellStore filename”字樣。當一個range的所有檔案加載完畢後,rangeserver日志中會看到“Finished Loading  cellstores  for  range_name”字樣。

每個CellStore檔案加載時,都會記錄其占用的記憶體和磁盤大小。這些值後續會追加到ag和range的維護資訊中,進而決定後續對range的維護任務。

注意:deferred_initialization函數結束前會置m_initialized為true,這會引起後續的連鎖反應。因為range維護資訊中的字段initialized源自m_initialized,是以下一輪在do_maintenance中擷取range的維護資料時,會得到為true的initialized字段。而initialized字段值會影響後續的針對range的維護任務的配置設定以及優先級的設定。

低記憶體模式處理

m_timer_handler每次處理定時器事件時,會檢查rangeserveer目前是否處于低記憶體狀态(已使用的記憶體大于記憶體限制)。

假設m_timer_handler在第M次時,首次檢測到低記憶體狀态,則置狀态變量m_low_memory_mode為true,并在應用程式隊列中添加一個RequestHandlerDoMaintenance對象。如前所述,應用程式隊列的值守線程獲最終會調用rangeserver對象的do_maintenance函數。該函數會從m_timer_handler中獲知到低記憶體狀态,并将此狀态傳遞到rangeserver的維護任務排程器對象m_maintenance_scheduler。該對象在緊跟其後的schedule函數中計算目前需要釋放的記憶體量(超限記憶體+記憶體限制*10%,10%是通過LowMemoryLimit.Percentage配置項指定的),并将其儲存到memory_state.needed中。如果此時維護任務隊列已滿,就結束本次維護任務,否則會通過m_prioritizer->prioritize調整目前range的優先級。

Prioritize函數中,會調整記憶體可以被釋放的range的維護辨別和優先級,并會從memory_state.needed中預先減去該range占用的記憶體,注意此時記憶體還沒有真正的釋放。随後,針對調整後優先級大于0的range,根據其維護辨別添加相應的維護任務到Global::maintenance_queue,該隊列的值守線程馬上啟動第M輪的維護任務。此時,do_maintenance函數已經準備結束了,目前結束前它不會忘記設定下一輪的定時器……

下一輪(M+1)的m_timer_handler定時器處理事件中,如果檢測到rangeserveer仍處于低記憶體狀态,則會暫停rangeserver的應用程式隊列,日志中會看到“Application queuePAUSED due to low memory”字樣,随後也會在應用程式隊列中添加一個RequestHandlerDoMaintenance對象。雖然應用程式隊列已經暫停,但是由于RequestHandlerDoMaintenance屬于緊急任務,是以該對象可被值守線程擷取。如果此時rangeserveer仍處于低記憶體狀态,則rangeserver的do_maintenance函數如上次處理邏輯。

再下一輪(M+2)的m_timer_handler定時器處理事件中,如果檢測到rangeserveer應用程式隊列已暫停,則會試圖重新開機它,此時日志中會看到“Restarting application queue”字樣。重新開機條件為三者之一:1)雖然之前是低記憶體模式,但是現在檢測已經不是低記憶體模式;2)暫停時間已經超過了最長等待時間(Hypertable.RangeServer.Maintenance.MaxAppQueuePause,預設兩分鐘);3)。本輪一般情況下不會再添加RequestHandlerDoMaintenance對象,而是直接設定下一輪的定時器。 

後面的定時器處理事件,将類似于M+1和M+2輪的邏輯。

24.1. 成員函數

24.1.1. Local_recover

聲明:void local_recover();

功能:

1)讀取rangeserver所有的metalog entity到entities集合。如果集合不為空,則執行步驟2-4,否則執行步驟5;

2)将集合中屬于MetaLog::EntityTask 的entity添加到global::work_queue,并從集合中去除RangeState::PHANTOM狀态的entity;

3)如果entities集合包含root表的entity,則對每個entity執行回放和加載range操作(replay_load_range函數)。建立一個CommitLogReader對象root_log_reader,其将對應root表的所有fragment。使用root_log_reader回放fragment(replay_log函數),即将fragment中的cell添加到對應的range,并擷取fragment對應的transfer log。擷取root表處于激活狀态的所有range,判斷每個range的狀态,如果處于RangeState::SPLIT_LOG_INSTALLED、RangeState::SPLIT_SHRUNK、RangeState::RELINQUISH_LOG_INSTALLED狀态之一,則建構相應的維護任務對象,并将其添加到維護任務數組(maintenace_tasks)中。将root表對應的TableInfoMap對象合并到m_live_map。建立root表對應的commit log對象Global::root_log,用于接收之後對于該表的寫入。将數組maintenace_tasks中的維護任務添加到Global::maintenance_queue中,發出回放完成的通知。注意:這裡的root_log_reader用于處理已有的root類型的commit log檔案,Global::root_log将用于處理之後新的root類型的commit log檔案;

4)對METADATA表、SYSTEM表和USER表執行類似于root表的操作。然後轉向6;

5)建立ROOT表、METADATA表、SYSTEM表和USER表對應的commit log對象Global::root_log、Global::metadata_log、Global::system_log、Global::user_log,并且通知這四種log已經回放完成;

6)如果Global::remove_ok_logs為空,則建立它,并将第三步和第四步中産生的可以安全移除的transfer log插入其中,并持久化其到metalog中。

參數: 

24.1.2. replay_load_range

聲明:void replay_load_range(TableInfoMap &replay_map, MetaLogEntityRange *range_entity);

功能:回放和加載range_entity對應的range,并在replay_map中記錄range_entity對應表的TableInfoMap;

參數:

24.1.3. replay_update

聲明:void replay_update(TableInfoMap &replay_map, const uint8_t *data, size_t len);

功能:将data指向的cell block解析為一個個的cell,然後添加到對應表的range中。

參數: 

24.1.4. replay_log

聲明:void replay_update(TableInfoMap &replay_map, CommitLogReaderPtr &log_reader);

功能:将rangeserver的commit log逐塊的添加到對應表的range中,每塊的添加将調用replay_update函數。

參數: replay_map:需要回放的table,如果發現回放的塊所屬表不在其中,則本塊資料不進行回放;log_reader:用來讀取需要回放的commit log塊。

24.1.5. do_maintenance

聲明:void  do_maintenance()

功能:執行rangeserver的背景維護任務。清理過期的scanner(預設過期時間30分鐘),判斷目前rangeserver是否處于低記憶體狀态,然後調用m_maintenace_scheduler.schedule函數進行背景維護。該函數最後将調用m_timer_handler->maintenance_scheduled_notify設定定時器,該定時器觸發時最終又會回到本函數,進而實作了反複的背景維護任務排程。

繼續閱讀