swoole+redis模拟秒杀抢购

swoole版本

FengMacBookPro:miaosha feng$ php --ri swoole

swoole

Swoole => enabled
Author => Swoole Team 
Version => 4.4.16
Built => Feb 27 2020 14:45:23
coroutine => enabled
kqueue => enabled
rwlock => enabled
openssl => OpenSSL 1.0.2j  26 Sep 2016
pcre => enabled
zlib => 1.2.8
async_redis => enabled

Directive => Local Value => Master Value
swoole.enable_coroutine => On => On
swoole.enable_library => On => On
swoole.enable_preemptive_scheduler => Off => Off
swoole.display_errors => On => On
swoole.use_shortname => On => On
swoole.unixsock_buffer_size => 262144 => 262144

php版本

FengMacBookPro:miaosha feng$ php -v
PHP 7.2.10 (cli) (built: Oct  9 2018 14:56:43) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

数据库

CREATE TABLE `orders` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `goods_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

客户端

client.php

//模拟用户抢购,模拟1500个客户端
ini_set('display_errors',1);
error_reporting(-1);
for($i=1;$i<=1500;$i++)
{
    go(function () use ($i){
        $client = new Swoole\Coroutine\Http\Client('127.0.0.1',9501);
        $client->set(["timeout"=>5]);
        $client->get("/goods/index?goods_id=1&user_id={$i}");
        if($client->body == 'ok')
        {
            echo $client->body."\n";
        }
        $client->close();
    });

}

http服务端

httpserver.php

//http抢购服务器
ini_set('display_errors',1);
error_reporting(-1);
Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]);
$http = new swoole_http_server("127.0.0.1", 9501);
$http->set([
    'enable_coroutine' => true,
    'reactor_num' => 4,
    'worker_num' => 4,    //worker process num
    'task_worker_num' => 4,
    'max_request' => 1000,
    'dispatch_mode' => 2,
    'max_connection' => 5000,

]);
$http->on("start", function ($server) {
    echo "Http server start http://127.0.0.1:9501/\n";
    //初始化一些操作
    $redis = new Redis();
    $redis->connect('127.0.0.1',6379);
    $redis->set("kucun",10);
    $redis->del("order_member");
    $redis->del("order_goods");
    $redis->close();
    $mysql = mysqli_connect('127.0.0.1','root','root','orders','3306');
    $mysql->query("TRUNCATE table orders");
    $mysql->close();

});


$http->on("request",function($request, $response) use ($http){
    if ($request->server['path_info'] == '/favicon.ico' || $request->server['request_uri'] == '/favicon.ico') {
        return $response->end();
    }
    $response->header("Content-Type", "text/html; charset=utf-8");
    $route = explode('/',trim($request->server['request_uri'],'/'));
    $controller = ucfirst($route[0]);
    $action = $route[1];
    $data = (new $controller())->$action($request->get,$http);
    $response->end($data);
});

$http->on('task',function ($http,$task_id,$from_id,$data){
    //echo "New AsyncTask[id=$task_id]".PHP_EOL;
    $mysql = mysqli_connect('127.0.0.1','root','root','orders','3306');
    $mysql->query("set names utf8");
    if(!$mysql)
    {
        echo "Error: Unable to connect to MySQL." . PHP_EOL;
        echo "Debugging errno: " . mysqli_connect_errno() . PHP_EOL;
        echo "Debugging error: " . mysqli_connect_error() . PHP_EOL;
        return ;
    }
    $data = json_decode($data,true);
    $sql = " insert into orders (user_id,goods_id) values ({$data['user_id']},{$data['goods_id']}) ";
    $mysql->query($sql);
    $data = json_encode($data);
    //返回任务执行的结果
    $http->finish("$data -> OK");
    $mysql->close();
});

//处理异步任务的结果(此回调函数在worker进程中执行)
$http->on('finish', function ($http, $task_id, $data) {
    echo "AsyncTask[$task_id] Finish: $data".PHP_EOL;
});

class Goods {

    public $redis;
    function  __construct()
    {
        $redis = new Redis();
        $redis->connect('127.0.0.1',6379);
        $this->redis = $redis;
    }

    function index($params,$http){
        if(!$params['goods_id'] || !$params['user_id']) {
            return '';
        }
        //判断是否抢了
        $is_exist = $this->redis->sIsMember("order_member",$params['user_id']);
        if(!$is_exist)
        {
            #此出不能使用$redis->get/set
            #减库存
            $kucun = $this->redis->decr("kucun");
            if($kucun >= 0 ) {
                //抢到号的入队列
                $arr['user_id'] = $params['user_id'];
                $arr['goods_id'] = $params['goods_id'];
                $json = json_encode($arr);
                //队列形式
                //$this->redis->lPush("order_goods",$json);
                //采用异步任务形式
                //$task_id = $http->task($json);
                //直接入库
                $this->insert($json);
                //记录已经抢到的用户
                $this->redis->sAdd("order_member",$params['user_id']);
                return 'ok';

            }else {
                $this->redis->set("kucun",0);
                return '';
            }
        }else{
            return '';
        }

    }

    public function insert($data){
        $mysql = mysqli_connect('127.0.0.1','root','root','orders','3306');
        $mysql->query("set names utf8");
        if(!$mysql)
        {
            echo "Error: Unable to connect to MySQL." . PHP_EOL;
            echo "Debugging errno: " . mysqli_connect_errno() . PHP_EOL;
            echo "Debugging error: " . mysqli_connect_error() . PHP_EOL;
            return ;
        }
        $data = json_decode($data,true);
        $sql = " insert into orders (user_id,goods_id) values ({$data['user_id']},{$data['goods_id']}) ";
        $mysql->query($sql);
        echo json_encode($data)."\n";
        $mysql->close();
    }

    function __destruct()
    {
        // TODO: Implement __destruct() method.
        $this->redis->close();
    }
}

$http->start();


消费者端,负责把redis队列的数据入库

consumer.php

//消费抢购的订单
ini_set('display_errors',1);
error_reporting(-1);
//其四个进程处理队列
$workerNum = 4;
$pool = new Swoole\Process\Pool($workerNum);
$pool->on("WorkerStart", function (Swoole\Process\Pool $pool, int $workerId) {
    $process = $pool->getProcess();
    $redis = new Redis();
    $redis->pconnect('127.0.0.1', 6379);
    $key = "order_goods";
    $mysql = mysqli_connect('127.0.0.1','root','root','orders','3306');
    $mysql->query("set names utf8");
    if(!$mysql)
    {
        echo "Error: Unable to connect to MySQL." . PHP_EOL;
        echo "Debugging errno: " . mysqli_connect_errno() . PHP_EOL;
        echo "Debugging error: " . mysqli_connect_error() . PHP_EOL;
        return;
    }
    while (true) {
        $d = $redis->rPop($key);
        $d = json_decode($d,true);
        $user_id = $d["user_id"];
        $goods_id = $d["goods_id"];
        if($user_id && $goods_id)
        {
            $sql = " insert into orders (user_id,goods_id) values ({$user_id},{$goods_id}) ";
            $mysql->query($sql);
            echo "ok\n";
        }else{
              //没数据的时候进行休眠,不然循环$redis->rpop会导致redis-server消耗很高
              sleep(1);
        }
    }
});

$pool->on("WorkerStop", function ($pool, $workerId) {
    echo "Worker#{$workerId} is stopped\n";
});

$pool->start();

你可能感兴趣的:(swoole+redis模拟秒杀抢购)