1. 程式人生 > 其它 >laravel redis秒殺功能

laravel redis秒殺功能

1.初始化:
秒殺商品,將商品以list資料型別存入redis(每個數量為一個元素);

2.購買:
1)購買使用者入佇列,如果使用者佇列長度超過指定的排隊長度,則返回排隊數過多
2)如果使用者佇列長度小於指定的排隊長度,然後生成訂單,減去庫存。下單完成

<?php

namespace App\Http\Controllers;

use App\Models\Goods;
use App\Models\Orders;
use App\Services\RedisLock;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

class MiaoshaController extends Controller
{
	//排隊人數
    private $listNumber = 50;

    /**
     * 實現秒殺
     */
    public function secondsKill(Request $request)
    {
        try {
            $user_data = $request->only(['user_id', 'goods_id']);
            if ( !$user_data ) {
                return $this->failed('資料不能為空');
            }
            $goodsId = $user_data['goods_id'];
            $userId = $user_data['user_id'];
            #訪問使用者入隊介面
            $user_list = $this->requestUser($userId);

            if ( !$user_list ) {
                return $this->failed('排隊數大於商品總數:'.Redis::llen('user_list'));
            }
            #消費商品,從佇列中取出商品
            $count = Redis::lpop('goods_store:' . $goodsId);
            if(!$count) {
                return $this->failed('商品搶光了');
            }
            #進入redis鎖
            $redisLock = new RedisLock($userId);
            $lock = $redisLock->lock();
            if ($lock) {
                #最後進入資料庫操作(每次固定消費1個)
                $mysql_data = $this->storeOrder($userId, $count, '1');
                if ( !$mysql_data ) {
                    $redisLock->unlock();
                    return $this->failed('生成訂單失敗');
                } else {
                    #關閉鎖
                    $redisLock->unlock();
                    return $this->success('搶購成功');
                }
            }
            return $this->success('生成訂單失敗');

        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * 將商品加入redis
     */
    public function storageGoods(Request $request)
    {
        try {
            #查詢商品
            $resutl = Goods::where('id', $request->goods_id)->select(['store','id'])->first();

            $store = $resutl->store;
            $res = Redis::llen('goods_store:' . $resutl->id);
            $count = $store - $res;
            for($i = 0;$i < $count; $i++){
                Redis::lpush('goods_store:' . $resutl->id, $resutl->id);
            }
            return $this->success('加入成功'.$count);
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * 將使用者也存入佇列中(就是將訪問請求資料)(此處沒有進行使用者過濾,同一個使用者進行多次請求也會進入佇列)
     */
    private function requestUser($userId)
    {
        $res = Redis::llen('user_list');
        #判斷排隊數
        if ($res = Redis::llen('user_list') > $this->listNumber) {
            // return '排隊數大於商品總數';
            return false;
        }
        #新增資料
        Redis::lpush('user_list', $userId);
        return true;
    }



    /*
     *下單
     */
    private function storeOrder($user_id, $goods_id, $number)
    {
        try {
            #開啟事務
            DB::beginTransaction();
            #查詢庫存sharedLock()共享鎖,可以讀取到資料,事務未提交不能修改,直到事務提交
            #lockForUpdate()不能讀取到資料
            $resutl = Goods::where(['id'=>$goods_id])->lockForUpdate()->first();
            #新增訂單
            if ( $resutl ) {
                $resutl_order = Orders::create([
                    'user_id' => $user_id,
                    'goods_id' => $goods_id,
                    'goods_number' => $number,
                    'ordersn' => $this->buildOrderNo(),
                    'price' => $resutl->price,
                ]);
                #減少庫存
                $resutl_update = Goods::where('id',$goods_id)->where('store', '>', 0)->decrement('store');
                #將使用者從佇列裡面彈出,允許下一個使用者進來
                Redis::rpop('user_list');
                if ($resutl_order->id > 0 && $resutl_update > 0) {
                    DB::commit();
                    return true;
                }
            }

            DB::rollBack();
            return false;

        } catch(Exception $e) {
            throw $e;
        }
    }

    /**
     * 生成唯一訂單號
     */
    private function buildOrderNo(){
        return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
    }
}

  轉載:https://blog.csdn.net/xiayu204575/article/details/106719845

搜尋

複製