├── .gitignore
├── LICENSE
├── README.md
├── example
├── Makefile
└── main.cpp
└── src
├── Makefile
├── rate_limiter.cpp
├── rate_limiter.h
├── sequence.h
├── spinlock.h
├── spinlock_guard.h
└── utils.h
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.so
3 | .vscode
4 | rate_limiter_example
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 刘雨康
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 限流器
2 | 1、概述:
3 | 基于令牌桶算法实现,实现qps限制。blog: http://www.liuyukang.com/archives/cppxianliuqi
4 |
5 |
6 | 2、特点:
7 | (1)接口使用简单,无业务侵入,接入成本极低
8 | (2)线程安全,CPU友好,性能强劲
9 | (3)极轻量,核心代码150行
10 |
11 |
12 | 3、编译:
13 | 进入src,make之后即可生成libratelimiter.so。
14 |
15 |
16 | 4、使用:
17 | example中提供示例,将libratelimiter.so放入/usr/lib/中后进入example,make即可生成可执行文件。下面这段代码将会执行5s:
18 | ```
19 | int main()
20 | {
21 | RateLimiter r(100);//100qps限流器
22 |
23 | for(int i = 0; i < 500; ++i)
24 | {
25 | r.pass();//通过的速率为100/s
26 | }
27 |
28 | return 0;
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/example/Makefile:
--------------------------------------------------------------------------------
1 |
2 | TARGET =rate_limiter_example
3 | CC =g++ -L../src -g -O3 -Wall -std=c++11
4 | INCLUDE =$(shell find ./ -name "*.h")
5 | SOURCE =$(shell find ./ -name "*.cpp")
6 | OBJS =$(SOURCE:%.cpp=%.o)
7 | LIBS =-lratelimiter
8 | CFLAGS =-I../src
9 |
10 | $(TARGET):$(OBJS)
11 | $(CC) -o $(TARGET) $(OBJS) $(LIBS)
12 |
13 | %.o: %.cpp $(INCLUDE)
14 | $(CC) $(CFLAGS) -c $< -o $@ $(LIBS)
15 |
16 | all:$(TARGET)
17 |
18 | clean:
19 | rm -rf $(OBJS) $(TARGET)
20 |
--------------------------------------------------------------------------------
/example/main.cpp:
--------------------------------------------------------------------------------
1 | //@author liuyukang
2 | #include "../src/rate_limiter.h"
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | int64_t now()
9 | {
10 | struct timeval tv;
11 | ::gettimeofday(&tv, 0);
12 | int64_t seconds = tv.tv_sec;
13 | return seconds * NS_PER_SECOND + tv.tv_usec * NS_PER_USECOND;
14 | }
15 |
16 | int main()
17 | {
18 | RateLimiter r(100);//100qps限流器
19 | sleep(1);
20 |
21 | auto start = now();
22 |
23 | for(int i = 0; i < 500; ++i)
24 | {
25 | r.pass();//通过的速率为100/s
26 | }
27 |
28 | auto end = now();
29 | std::cout << (end - start) / 1000000 << "ms" << std::endl;//此时打印的时间为5000ms
30 | return 0;
31 | }
--------------------------------------------------------------------------------
/src/Makefile:
--------------------------------------------------------------------------------
1 |
2 | TARGET =libratelimiter.so
3 | CC =g++ -fPIC -g -O3 -Wall -std=c++11
4 | INCLUDE =$(shell find ./ -name "*.h")
5 | SOURCE =$(shell find ./ -name "*.cpp")
6 | OBJS =$(SOURCE:%.cpp=%.o)
7 |
8 | $(TARGET):$(OBJS)
9 | $(CC) -shared -o $(TARGET) $(OBJS) $(LIBS)
10 |
11 | %.o: %.cpp $(INCLUDE)
12 | $(CC) -c $< -o $@ $(LIBS)
13 |
14 | all:$(TARGET)
15 |
16 | clean:
17 | rm -rf $(OBJS) $(TARGET)
18 |
--------------------------------------------------------------------------------
/src/rate_limiter.cpp:
--------------------------------------------------------------------------------
1 | //@author liuyukang
2 |
3 | #include "rate_limiter.h"
4 | #include "spinlock_guard.h"
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #define RETRY_IMMEDIATELY_TIMES 100//不睡眠的最大重试获得令牌的次数
11 |
12 | //qps限制最大为十亿
13 | RateLimiter::RateLimiter(int64_t qps) :
14 | bucketSize_(1), tokenLeft_(0), supplyUnitTime_(NS_PER_SECOND / qps), lastAddTokenTime_(0)
15 | {
16 | assert(qps <= NS_PER_SECOND);
17 | assert(qps >= 0);
18 | lastAddTokenTime_ = now();
19 | }
20 |
21 | int64_t RateLimiter::now()
22 | {
23 | struct timeval tv;
24 | ::gettimeofday(&tv, 0);
25 | int64_t seconds = tv.tv_sec;
26 | return seconds * NS_PER_SECOND + tv.tv_usec * NS_PER_USECOND;
27 | }
28 |
29 | //对外接口,能返回说明流量在限定值内
30 | void RateLimiter::pass()
31 | {
32 | return mustGetToken();
33 | }
34 |
35 | //尝试获得令牌
36 | //成功获得则返回true
37 | //失败则返回false
38 | bool RateLimiter::tryGetToken()
39 | {
40 | supplyTokens();
41 |
42 | //获得一个令牌
43 | auto token = tokenLeft_.fetch_add(-1);
44 | if(token <= 0)
45 | {//已经没有令牌了,归还透支的令牌
46 | tokenLeft_.fetch_add(1);
47 | return false;
48 | }
49 |
50 | return true;
51 | }
52 |
53 | //必定成功获得令牌
54 | //其中会进行重试操作
55 | void RateLimiter::mustGetToken()
56 | {
57 | bool isGetToken = false;
58 | for(int i = 0; i < RETRY_IMMEDIATELY_TIMES; ++i)
59 | {
60 | isGetToken = tryGetToken();
61 | if(isGetToken)
62 | {
63 | return;
64 | }
65 | }
66 |
67 | while(1)
68 | {
69 | isGetToken = tryGetToken();
70 | if(isGetToken)
71 | {
72 | return;
73 | }
74 | else
75 | {
76 | //让出CPU
77 | sleep(0);
78 | }
79 | }
80 | }
81 |
82 | void RateLimiter::supplyTokens()
83 | {
84 | auto cur = now();
85 | if (cur - lastAddTokenTime_ < supplyUnitTime_)
86 | {
87 | return;
88 | }
89 |
90 | {
91 | SpinlockGuard lock(lock_);
92 | //等待自旋锁期间可能已经补充过令牌了
93 | int64_t newTokens = (cur - lastAddTokenTime_) / supplyUnitTime_;
94 | if (newTokens <= 0)
95 | {
96 | return;
97 | }
98 |
99 | //更新补充时间,不能直接=cur,否则会导致时间丢失
100 | lastAddTokenTime_ += (newTokens * supplyUnitTime_);
101 |
102 | auto freeRoom = bucketSize_ - tokenLeft_.load();
103 | if(newTokens > freeRoom || newTokens > bucketSize_)
104 | {
105 | newTokens = freeRoom > bucketSize_ ? bucketSize_ : freeRoom;
106 | }
107 |
108 | tokenLeft_.fetch_add(newTokens);
109 | }
110 |
111 | }
--------------------------------------------------------------------------------
/src/rate_limiter.h:
--------------------------------------------------------------------------------
1 | //@author liuyukang
2 | #pragma once
3 |
4 | #include "sequence.h"
5 | #include "spinlock.h"
6 |
7 | #include
8 |
9 | #define NS_PER_SECOND 1000000000//一秒的纳秒数
10 | #define NS_PER_USECOND 1000//一微秒的纳秒数
11 |
12 | //限流器
13 | //最大qps为1,000,000,000,最小为1
14 | //使用:
15 | // RateLimiter r(100);
16 | // r.pass();
17 | // 能通过r.pass()函数即可保证流速
18 | //特点:
19 | // 1、接口使用简单,无业务侵入,接入成本极低
20 | // 2、线程安全,CPU友好,性能强劲
21 | // 3、极轻量,核心代码150行
22 | //原理:
23 | // 基于令牌桶算法实现
24 | class RateLimiter
25 | {
26 | public:
27 | //qps限制最大为十亿
28 | RateLimiter(int64_t qps);
29 |
30 | DISALLOW_COPY_MOVE_AND_ASSIGN(RateLimiter);
31 |
32 | //对外接口,能返回说明流量在限定值内
33 | void pass();
34 |
35 | private:
36 |
37 | //获得当前时间,单位ns
38 | int64_t now();
39 |
40 | //更新令牌桶中的令牌
41 | void supplyTokens();
42 |
43 | //尝试获得令牌
44 | //成功获得则返回true
45 | //失败则返回false
46 | bool tryGetToken();
47 |
48 | //必定成功获得令牌
49 | //其中会进行重试操作
50 | void mustGetToken();
51 |
52 | //令牌桶大小
53 | const int64_t bucketSize_;
54 |
55 | //剩下的token数
56 | AtomicSequence tokenLeft_;
57 |
58 | //补充令牌的单位时间
59 | const int64_t supplyUnitTime_;
60 |
61 | //上次补充令牌的时间,单位纳秒
62 | int64_t lastAddTokenTime_;
63 |
64 | //自旋锁
65 | Spinlock lock_;
66 | };
--------------------------------------------------------------------------------
/src/sequence.h:
--------------------------------------------------------------------------------
1 | //@Author LiuYukang
2 | #pragma once
3 | #include
4 |
5 | #define CACHELINE_SIZE_BYTES 256
6 | #define CACHELINE_PADDING_FOR_ATOMIC_INT64_SIZE (CACHELINE_SIZE_BYTES - sizeof(std::atomic_int64_t))
7 | #define CACHELINE_PADDING_FOR_INT64_SIZE (CACHELINE_SIZE_BYTES - sizeof(int64_t))
8 |
9 | //��std::atomic_int64_t�����˷�װ���ڴ油�뱣֤_seq��һ����������
10 | class AtomicSequence
11 | {
12 | public:
13 | AtomicSequence(int64_t num = 0L) : _seq(num) {};
14 | ~AtomicSequence() {};
15 | AtomicSequence(const AtomicSequence&) = delete;
16 | AtomicSequence(const AtomicSequence&&) = delete;
17 | void operator=(const AtomicSequence&) = delete;
18 |
19 | void store(const int64_t val)//, std::memory_order _order = std::memory_order_seq_cst)
20 | {
21 | _seq.store(val);//,_order);
22 | }
23 |
24 | int64_t load()//std::memory_order _order = std::memory_order_seq_cst)
25 | {
26 | return _seq.load();// _order);
27 | }
28 |
29 | int64_t fetch_add(const int64_t increment)//, std::memory_order _order = std::memory_order_seq_cst)
30 | {
31 | return _seq.fetch_add(increment);// _order);
32 | }
33 |
34 | private:
35 | //���߶����룬�Ա�֤_seq������������������һ��������
36 | char _frontPadding[CACHELINE_PADDING_FOR_ATOMIC_INT64_SIZE];
37 | std::atomic_int64_t _seq;
38 | char _backPadding[CACHELINE_PADDING_FOR_ATOMIC_INT64_SIZE];
39 | };
40 |
41 | //��int64_t�����˷�װ���ڴ油�뱣֤_seq��һ����������
42 | class Sequence
43 | {
44 | public:
45 | Sequence(int64_t num = 0L) : _seq(num) {};
46 | ~Sequence() {};
47 | Sequence(const Sequence&) = delete;
48 | Sequence(const Sequence&&) = delete;
49 | void operator=(const Sequence&) = delete;
50 |
51 | void store(const int64_t val)
52 | {
53 | _seq = val;
54 | }
55 |
56 | int64_t load()
57 | {
58 | return _seq;
59 | }
60 |
61 | private:
62 | //���߶����룬�Ա�֤_seq������������������һ��������
63 | char _frontPadding[CACHELINE_PADDING_FOR_INT64_SIZE];
64 | volatile int64_t _seq;
65 | char _backPadding[CACHELINE_PADDING_FOR_INT64_SIZE];
66 | };
--------------------------------------------------------------------------------
/src/spinlock.h:
--------------------------------------------------------------------------------
1 | //@Author Liu Yukang
2 | #pragma once
3 | #include
4 | #include "utils.h"
5 |
6 | //���std::atomic_int����Ķ�Ԫ�ź���ʹ�ã�Ϊ1��ʾ��Դ����ʹ�ã�Ϊ0��ʾ��Դ����ʹ��
7 | class Spinlock
8 | {
9 | public:
10 | Spinlock()
11 | : sem_(1)
12 | { }
13 |
14 | ~Spinlock() { unlock(); }
15 |
16 | DISALLOW_COPY_MOVE_AND_ASSIGN(Spinlock);
17 |
18 | void lock()
19 | {
20 | int exp = 1;
21 | while (!sem_.compare_exchange_strong(exp, 0))
22 | {
23 | exp = 1;
24 | }
25 | }
26 |
27 | void unlock()
28 | {
29 | sem_.store(1);
30 | }
31 |
32 | private:
33 | std::atomic_int sem_;
34 |
35 | };
36 |
--------------------------------------------------------------------------------
/src/spinlock_guard.h:
--------------------------------------------------------------------------------
1 | //@author Liu Yukang
2 | #pragma once
3 | #include "spinlock.h"
4 | #include "utils.h"
5 |
6 | //���std::atomic_int����Ķ�Ԫ�ź���ʹ�ã�Ϊ1��ʾ��Դ����ʹ�ã�Ϊ0��ʾ��Դ����ʹ��
7 | class SpinlockGuard
8 | {
9 | public:
10 | SpinlockGuard(Spinlock& l)
11 | : lock_(l)
12 | {
13 | lock_.lock();
14 | }
15 |
16 | ~SpinlockGuard()
17 | {
18 | lock_.unlock();
19 | }
20 |
21 | DISALLOW_COPY_MOVE_AND_ASSIGN(SpinlockGuard);
22 |
23 | private:
24 | Spinlock& lock_;
25 |
26 | };
27 |
28 |
--------------------------------------------------------------------------------
/src/utils.h:
--------------------------------------------------------------------------------
1 | //@Author Liu Yukang
2 | #pragma once
3 | #define DISALLOW_COPY_MOVE_AND_ASSIGN(TypeName) TypeName(const TypeName&) = delete; TypeName(const TypeName&&) = delete; TypeName& operator=(const TypeName&) = delete
--------------------------------------------------------------------------------