之前做过redis处理并发的,处理效率要比直接操作mysql快1一倍左右,但其实效率还是不高,虽然redis是很快,但因为要经过nginx,单个nginx处理并发的能力也是有限的,所以这一块的瓶颈是nginx的并发能力。
既然如此,我们就玩点花的,用PHP处理并发的最强工具,Swoole
Swoole是什么?
Swoole的实际就是一个底层使用C实现的PHP协程框架,他使 PHP 开发人员可以编写高性能高并发的 TCP、UDP、Unix Socket、HTTP、 WebSocket 等服务,而这次,我们用到的就是http服务器。
下面这个是我封装好的一个Swoole类,只要装好swoole和redis扩展就能用
<?php
class Swoole
{
function __construct()
{
$http = new swoole_http_server("0.0.0.0", 8888);
$http->on("start", function ($server)
{
echo "Swoole http server is started at http://wrsndm:8888\n";
});
$http->on("request", function ($request, $response)
{
$this->response = $response;
//防止Chrome请求2次的问题
if ($request->server['path_info'] == '/favicon.ico' || $request->server['request_uri'] == '/favicon.ico') {
$response->end();
return;
}
$response->header("Content-Type", "text/plain; charset=utf-8");//中文编码
$method = $request->get['method'] ?? 'index';
$this->$method($request);
// $response->end("Hello World Rand Number is:" . rand(0, 100) . "\n");
});
//实例化Redis
$this->redis = new Redis();
$this->redis->connect('redis', '6379');//redis可改成自己的IP
//启动swoole_http服务
$http->start();
}
public function index($request)
{
$this->response->end('剩余库存' . $this->redis->lLen('goods_list'));
}
public function redis_qianghuo($request){
//查询库存
if($this->redis->lLen('goods_list') == 0) {
//记录失败次数
while (true){
if(!$this->redis->set('fail_lock',1,['EX'=>5,'NX']))//拿锁的权限,没拿到则continue
continue;
$num = $this->redis->get('fail_num');
$this->redis->set('fail_num', $num+1);
$this->redis->del('fail_lock');//解锁
break;
}
$this->ajaxReturn('商品已售完...');
}
$uid = $request->server['remote_port'];
//查询是否重复购买
// if($this->redis->sIsMember('bought_list',$uid)) {
// //记录重复购买次数
while (true){
if(!$this->redis->set('bought_lock',1,['EX'=>5,'NX']))//拿锁的权限,没拿到则continue
continue;
$num = $this->redis->get('bought_num');
$this->redis->set('bought_num', $num+1);
$this->redis->del('bought_lock');//解锁
break;
}
// $this->ajaxReturn('你已经购买过了!');
// echo "已经购买过了\n";
// return;
// }
//商品出队
$goods_id = $this->redis->rpop('goods_list');
//uid添加到已抢购名单,防止重复抢购
$this->redis->sAdd('bought_list',$uid);
$value = array(
'uid' => $uid,
'goods_id' => $goods_id,
'time' => time(),
);
//保存订单信息
$this->redis->hSet('order_info',$uid,json_encode($value));
$this->ajaxReturn('购买成功。');
echo "购买成功\n";
}
private function ajaxReturn($info){
$this->response->end($info);
}
}
$swoole = new Swoole();
首先要启动swoole_http服务器,只能使用cli模式启动,成功会输出下面这段文字
然后我们用ab进行1W次的并发测试
ab -c10 -n10000 localhost:8999/?method=redis_qianghuo
然后是Nginx服务器的并发测试
可以看到,swoole处理是6.9秒,且全部成功,nginx处理是18秒,而且有276个失败请求(我之前测的,错误率还挺高),效率提升了差不多2倍,我用的最低配的阿里云,所以处理能力不是很强,rps只有1400,到生产环境部署,并发能力会更加强劲!
此外,我还测试了不同服务器代理情况,主要瓶颈还是网络,包括四种:
- 直接Nginx(nginx+fpm)
- 直接swoole(swoole)
- Nginx反向代理到swoole服务端(nginx+swoole)
- 通过Nginx,PHP再调curl请求到swoole服务端(nginx+fpm+swoole)
处理速度情况如下
2>3>>1>>4
可以看出,没有php-fpm处理的是最快的,这是因为fpm的worker进程有限,一个请求就要一个进程处理,进程用完了,其他请求则在排队。