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。