本文试图基于计数器实现一个简单服务限流程序,这个计数器的值表示服务器尚可可以接待的请求数量。假设服务器设定的 qps 为 500,那么我们每隔 2 ms(即 1 / qps)检查计数器的值,如果小于设定的最大请求数,就给计数器 + 1,否则什么都不做。当请求进来的时候,如果计数器大于 0,表示可以提供服务,这时计数器 - 1,执行服务。如果计数器等于0,则无法提供服务。
代码如下:
$http = new Swoole\Http\Server('0.0.0.0', 9501);
// 限流控制对象
$traffic = new Traffic();
$traffic->start();
$http->on('request', function ($request, $response) use ($traffic) {
// 无法提供服务,直接返回
if (! $traffic->canServe()) {
$response->header("Content-Type", "text/html; charset=utf-8");
$response->end("<h1>The Server is busy...</h1>");
return;
}
// 执行主逻辑代码
$app = new App($request, $response);
$app->run();
});
$http->start();
/**
* 主逻辑代码
* Class App
*/
class App {
private $request;
private $response;
public function __construct($request, $response)
{
$this->request = $request;
$this->response = $response;
}
public function run()
{
/**
* do something
*/
$this->response->header("Content-Type", "text/html; charset=utf-8");
$this->response->end("<h1>I am in service</h1>");
}
}
/**
* 限流控制类
*/
class Traffic {
/**
* @var Redis redis 客户端
*/
private $redis;
/**
* @var int 每秒可处理的请求数
*/
private $qps = 2;
/**
* @var int 待处理请求的最大容量
*/
private $maximum_requests = 5;
public function __construct()
{
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$this->redis = $redis;
}
/**
* 开启计时器
*/
public function start()
{
$redis = $this->redis;
$time_interval = floor(1000 / $this->qps);
$maximum_requests = $this->maximum_requests;
// 计时器
Swoole\Timer::tick($time_interval, function() use ($redis, $maximum_requests) {
$num = $redis->get('num');
var_dump($num);
if ($num < $maximum_requests) {
$redis->incr('num');
}
});
}
/**
* 是否可以为 1 个请求提供服务
* @return bool
*/
public function canServe(): bool
{
$num = $this->redis->get('num');
if ($num > 0) {
// 计数器 - 1
$this->redis->decr('num');
return true;
} else {
return false;
}
}
}