load_factor參數算法詳解
server_info的load_factor
//src\ripple\app\misc\NetworkOPs.cpp : getServerInfo
info[jss::load_factor] = static_cast<double> (loadFactor) / loadBaseServer;
參數說明:
- loadBaseServer爲load_factor的基礎初始值:lftNormalFee = 256;
- loadFactor由以下參數決定:
auto const escalationMetrics = app_.getTxQ().getMetrics(*app_.openLedger().current());
auto const loadFactorServer = app_.getFeeTrack().getLoadFactor();
auto const loadBaseServer = app_.getFeeTrack().getLoadBase();
auto const loadFactorFeeEscalation = escalationMetrics ? escalationMetrics->expFeeLevel : 1;
auto const loadBaseFeeEscalation = escalationMetrics ? escalationMetrics->referenceFeeLevel : 1;
auto const loadFactor = std::max(static_cast<std::uint64_t>(loadFactorServer),
mulDiv(loadFactorFeeEscalation, loadBaseServer, loadBaseFeeEscalation).second);
2.1. mulDiv函數定義如下:
給定三個參數A,B,C,結果result=A*B/C; 當result超過uint64_t的最大值limit64時,返回limit64,否則返回result。
2.2. loadFactorServer實際爲{ clusterTxnLoadFee_, localTxnLoadFee_, remoteTxnLoadFee_ }三個值取最大值。
這部分涉及本地及遠程負載費用計算,在loadFactorServer部分詳細介紹;
2.3. loadFactorFeeEscalation和loadBaseFeeEscalation都是由getMetrics返回,這涉及到ripple的FeeEscalation特性。
loadFactorServer
loadFactorServer爲{ clusterTxnLoadFee_, localTxnLoadFee_, remoteTxnLoadFee_ }三個值取最大值,cluster模式
目前沒有使用,暫時不討論。主要介紹localTxnLoadFee_和remoteTxnLoadFee_ ;
1. localTxnLoadFee_
主要由LoadManger類來負責管理:
//src\ripple\app\main\LoadManager.cpp : run ()
bool change = false;
if (app_.getJobQueue ().isOverloaded ()) {
JLOG(journal_.info()) << app_.getJobQueue ().getJson (0);
change = app_.getFeeTrack ().raiseLocalFee ();
}
else {
change = app_.getFeeTrack ().lowerLocalFee ();
}
if (change) {
app_.getOPs ().reportFeeChange ();
}
從上述代碼中,確定此處有四部分因素:
- job是否過載;
- raiseLocalFee;
- lowerLocalFee;
- reportFeeChange;
整體過程概述爲:
首先判斷當前job是否過載,即isOverloaded函數。該函數遍歷當前每一個執行的job,如果有任意一個job被判斷爲過載,
則返回true,觸發raiseLocalFee函數。否則觸發lowerLocalFee。只有連續兩次觸發raiseLocalFee,纔會調整fee。無論fee
是增大或者減小,只要有變化就會調用reportFeeChange向peer廣播本地fee。其他節點收到廣播後將其設置爲remoteTxnLoadFee_。
1.1. isOverloaded()
該函數遍歷每個當前job。進行過載判斷,只要有一個過載,就返回ture,觸發raiseLocalFee。
每個job都會有一個過載樣本庫,每次當jobEvent停止時,運行時間加上等待時間被記爲延遲時間,超過2ms,就會被加入過載樣本庫。
樣本庫計數mLatencyEvents加一,延遲均值和延遲峯值都會增加當前樣本延遲量。
當mLatencyEvents值不等於零,就將延遲均值和延遲峯值和該job設定的預期延遲均值mTargetLatencyAvg和預期延遲峯值mTargetLatencyPk進行比較:(預期延遲值在job的定義中給出)
isOverTarget (mLatencyMSAvg / (mLatencyEvents * 4), mLatencyMSPeak / (mLatencyEvents * 4));
bool LoadMonitor::isOverTarget (std::uint64_t avg, std::uint64_t peak)
{
return (mTargetLatencyPk && (peak > mTargetLatencyPk)) ||
(mTargetLatencyAvg && (avg > mTargetLatencyAvg));
}
根據比較結果判斷是否過載。
1.2. raiseLocalFee()
該函數通過一個計數器控制,每次調用該函數,計數器加一,只有該計數器大於2時,纔會調整,否則直接返回,而每次調用lowerLocalFee都會將該計數器減一,最少爲0。所以需要連續兩次觸發reaiseLocalFee,纔會有效。
函數內部首先將localTxnLoadFee_與remoteTxnLoadFee_比較,取最大值,然後增加該值的1/4,並與上限lftFeeMax比較,超過上限返回上限,否則返回計算結果。
1.3. lowerLocalFee()
每次調用首先將計數器減一,然後減少localTxnLoadFee_的1/4,並與基礎值lftNormalFee比較,小於基礎值,返回基礎值,否則返回計算結果。
1.4. reportFeeChange()
無論費用是增加或者減少,只有有變動,就會通過該函數,廣播給peer節點。其他節點收到後設置爲該節點的remoteTxnLoadFee_
2. remoteTxnLoadFee_
即其他信任節點廣播的localTxnLoadFee_的值。
FeeEscalation特性
getMetrics函數
//src\ripple\app\misc\impl\TxQ.cpp : getMetrics
在getMetrics函數中,有三個決定因素,分別是當前交易量,期望交易量和倍乘器。
1. 期望交易量算法:
鏈啓動後,對於每個區塊設定一個交易數量最小值,期望值和最大值。除了程序預置的以下值,
還可以在配置文件中的[transaction_queue]下手動配置。
StandAlone | Normal | |
---|---|---|
最小值 | 1000 | 5 |
期望值 | 1000 | 50 |
最大值 | NULL | NULL |
期望交易量就是
上一區塊的實際交易量與預置最小值和預置期望值比較,小於最小取最小,大於期望取期望,否則取原本值。
(這是一種情況的取值,另一種情況算法詳見chainsqld\src\ripple\app\misc\impl\TxQ.cpp : TxQ::FeeMetrics::update)。
2. 倍乘器算法:
鏈啓動後,設置一個初始最小倍乘器:minimumEscalationMultiplier = 256 * 500。
倍乘器實際算法:(feeLevels是一個vector,按順序存儲當前區塊所有交易的手續費)
if (feeLevels.empty())
{
escalationMultiplier_ = minimumMultiplier_;
}
else
{
escalationMultiplier_ = (feeLevels[size / 2] +
feeLevels[(size - 1) / 2] + 1) / 2;
escalationMultiplier_ = std::max(escalationMultiplier_,
minimumMultiplier_);
}
3. 計算最後expFeeLevel
FeeEscalation特性就是根據當前openledger的交易量和由上一區塊的交易量計算出的期望交易量進行比較:
if (current > target)
{
return mulDiv(multiplier, current * current, target * target).second;
}
return baseLevel;
最後計算
至此,將server_info中計算load_factor的所有參數準備完畢,並依據文章開頭的公式:
auto const loadFactor = std::max(static_cast<std::uint64_t>(loadFactorServer),
mulDiv(loadFactorFeeEscalation, loadBaseServer, loadBaseFeeEscalation).second);
計算出load_factor。