令牌桶代码介绍
token_bucket主要是令牌桶相关处理,本篇博客主要是分析一下令牌桶代码,理论知识见下篇博客。
令牌桶理论介绍
先从头文件开始
头文件是相关结构体的申明,我们先看最重要的结构体TokenBucket,这个就是最基础的令牌桶。主要有下面几个属性
- rate:这个参数是指令牌生成速度的指标
- burst:我们可以看作是桶的大小
- last:是指消息传送时候最后一个消息的时间
- used:当前正在使用的桶
#define TOKEN_PRE_MSG 1000
typedef struct TokenBucket TokenBucket;
struct TokenBucket {
int rate; // 每毫秒生成令牌的速率。现在,我们改用TPS。
int burst; // The burst size of the token bucket
uint64 last; // 最后一个消息的时间
int used; // 使用的桶
};
桶状态结构体BucketStatus
主要只有三个状态:busy、Idle、Invalid。
其中busy指的是该令牌桶正在被使用中,他已经完成了初始化等等前期步骤,已经是可以使用了。
Idle状态指的是闲置状态,就是完成初始化了,但是还没有使用这个桶,这时候可以理解为这个桶是一个可用状态。
invalid状态是指不可用,可能是在使用过程中:堆栈溢出、不合法调用等等错误操作导致桶处于无效状态。
这一步常常伴随初始化过程.
typedef enum BucketStatus {
BUCKET_BUSY,
BUCKET_IDLE,
BUCKET_INVALID,
} BucketStatus;
下面是一个初始化函数TB_InitBucket
这个函数用来初始化令牌桶,我们之前分析可以知道令牌桶有四个属性,但是这里初始化时候只用了两个变量。下面我们来看具体函数。
从参数开始:
- bucket:要操作的桶指针
- rate:桶生成速度
- burst:桶的size大小
其他两个参数这里全部置为了0,一个是上一条消息的时间,另外一个是当前使用的桶。这时候如果rate变量是TPS的化,这个used对应的变量就是TOKEN_PRE_MSG------宏定义为1000。
inline static void TB_InitBucket(TokenBucket *bucket, int rate, int burst)
{
bucket->last = 0;
bucket->rate = rate;
bucket->burst = burst;
bucket->used = 0;
}
源文件中的参数分析
令牌桶的.c文件中只有一个函数,TB_CheckMessage。该函数顾名思义是令牌桶在检查消息是否可以处理,核心部分只有下面几行
全部代码如下:
int TB_CheckMessage(TokenBucket *bucket)
{
if (bucket == NULL) {
return BUCKET_INVALID;
}
uint64 now = SAMGR_GetProcessTime();
//这里获取一次当前内核时间
uint64 generated = (now > bucket->last) ? ((now - bucket->last) * bucket->rate) : 0;
//这时候当前时间如果大于桶内last属性,generated变量就是(当前时间减去last)*TPS,否则generate就是0
//这里的意义就是生成令牌的数量
int used = bucket->used + TOKEN_PRE_MSG;
//当前使用令牌+1000
used = (generated > used) ? 0 : (used - (int)generated);
//生成得到令牌>used时候,就是令牌不够用了,这时候讲used置为0,否则当前令牌就是之前的used前去生成的generated
if (used >= bucket->burst * TOKEN_PRE_MSG) {
//当前使用令牌多于桶大小和消息乘积,说明桶正在处理数据,处于busy状态
return BUCKET_BUSY;
}
bucket->used = used;
bucket->last = now;
//如果令牌数量少于消息所用令牌,说明无法处理,这时候桶处于闲置状态,可用
return BUCKET_IDLE;
}
这个函数主要就是检查消息是否可以通过令牌桶处理或者发送,因为令牌桶内令牌的数量决定当前消息是否可以处理,如果可以处理就将桶的状态置为可用,否则就是busy。