Swoole+Redis实现异步队列,处理高并发场景

6 篇文章 1 订阅
4 篇文章 0 订阅

之前做过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,到生产环境部署,并发能力会更加强劲!

此外,我还测试了不同服务器代理情况,主要瓶颈还是网络,包括四种:

  1. 直接Nginx(nginx+fpm)
  2. 直接swoole(swoole)
  3. Nginx反向代理到swoole服务端(nginx+swoole)
  4. 通过Nginx,PHP再调curl请求到swoole服务端(nginx+fpm+swoole)

处理速度情况如下

2>3>>1>>4

可以看出,没有php-fpm处理的是最快的,这是因为fpm的worker进程有限,一个请求就要一个进程处理,进程用完了,其他请求则在排队。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现这个功能需要用到两个接口,一个是根据经纬度查询城市的接口,另一个是根据城市名查询经纬度的接口。 首先,我们需要获取用户的经纬度信息。可以使用浏览器的 Geolocation API 或者通过 IP 地址查询服务来获取。获取到经纬度后,可以调用根据经纬度查询城市的接口。 以下是使用 Laravel+Swoole 实现的代码示例: ```php use Swoole\Coroutine\Http\Client; function getCityByLatLng($lat, $lng) { // 调用根据经纬度查询城市的接口 $client = new Client('api.map.baidu.com', 443, true); $client->set(['timeout' => 10]); $client->get('/geocoder/v2/', [ 'location' => "$lat,$lng", 'output' => 'json', 'ak' => 'your_ak', // 填写你的百度地图开发者密钥 ]); $response = json_decode($client->body, true); if ($response['status'] == 0) { return $response['result']['addressComponent']['city']; } return null; } function getLatLngByCity($city) { // 调用根据城市名查询经纬度的接口 $client = new Client('api.map.baidu.com', 443, true); $client->set(['timeout' => 10]); $client->get('/geocoder/v2/', [ 'address' => $city, 'output' => 'json', 'ak' => 'your_ak', // 填写你的百度地图开发者密钥 ]); $response = json_decode($client->body, true); if ($response['status'] == 0) { $location = $response['result']['location']; return [$location['lat'], $location['lng']]; } return null; } // 示例:根据经纬度返回对应城市 $lat = 39.915168; $lng = 116.403875; $city = getCityByLatLng($lat, $lng); echo $city; // 北京市 // 示例:根据城市返回对应经纬度 $city = '北京市'; $latLng = getLatLngByCity($city); print_r($latLng); // Array ( [0] => 39.90469 [1] => 116.40717 ) ``` 另外,如果你需要根据城市查询经纬度的功能,推荐使用第三方库如 `geocoder-php` 或 `googlemaps/google-maps-services-php`,这些库已经封装好了各种地图服务的 API,使用起来更加方便。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值