PHP+laravel 高并发下接口限流方案

9 篇文章 0 订阅
3 篇文章 1 订阅

一、什么是接口限流
那么什么是限流呢?顾名思义,限流就是限制流量,包括并发的流量和一定时间内的总流量,就像你宽带包了1个G的流量,用完了就没了,所以控制你的使用频率和单次使用的总消耗。

通过限流,我们可以很好地控制系统的qps,从而达到保护系统或者接口服务器稳定的目的。

二、常用的接口限流算法
1、计数器法
2、漏桶算法
3、令牌桶算法

今天主要使用令牌桶算法进行接口限流(基于laravel框架)

首先,我们有一个固定容量的桶,桶里存放着令牌(token)。桶一开始是空的(可用token数为0),token以一个固定的速率r往桶里填充,直到达到桶的容量,多余的令牌将会被丢弃。每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。
在这里插入图片描述
令牌桶的另外一个好处是可以方便的改变速度. 一旦需要提高速率,则按需提高放入桶中的令牌的速率. 一般会定时(比如100毫秒)往桶中增加一定数量的令牌, 有些变种算法则实时的计算应该增加的令牌的数量.

代码实现:
1、通过定时任务,匀速的向令牌桶中添加令牌

<?php

namespace App\Console\Commands;

use App\Http\Controllers\Api\V1\TokenBucketController;
use Illuminate\Console\Command;


class CrontabAddTokens extends Command
{
    /**
     * 定时往令牌桶里添加令牌
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'CrontabTokens:add';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '定时自动增加令牌数';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {

        // 令牌桶容器
        $queue = 'mycontainer';

        // 最大令牌数
        $max = 100;

        // 每次时间间隔加入的令牌数
        $token_num = 10;

        // 时间间隔,最好是能被60整除的数,保证覆盖每一分钟内所有的时间
        $time_step = 1;

        // 执行次数
        $exec_num = (int)(60/$time_step);

        // 创建TokenBucketController对象
        $tokenBucket = new TokenBucketController($queue, $max);

        for($i = 0; $i < $exec_num; $i++){
            $add_num = $tokenBucket->add($token_num);
            echo '['.date('Y-m-d H:i:s').'] add token num:'.$add_num.PHP_EOL;
            sleep($time_step);
        }

    }


}

2、创建一个TokenBucketController对象,用户添加和消费令牌以及一些初始化设置

<?php
namespace App\Http\Controllers\Api\V1;


use Illuminate\Support\Facades\Redis;


class TokenBucketController extends BaseController
{

    private $_queue;  // 令牌桶
    private $_max;    // 最大令牌数

    /**
     * 初始化
     * @param Array $config redis连接设定
     */
    public function __construct($queue, $max = 10){

        $this->_queue = $queue;
        $this->_max = $max;
    }

    /**
     * 加入令牌
     * @param  Int $num 加入的令牌数量
     * @return Int 加入的数量
     */
    public function add($num=0)
    {

        // 当前剩余令牌数
        $curnum = intval(Redis::Llen($this->_queue));


        // 最大令牌数
        $maxnum = intval($this->_max);

        // 计算最大可加入的令牌数量,不能超过最大令牌数
        $num = $maxnum >= $curnum + $num ? $num : $maxnum - $curnum;

        // 加入令牌
        if($num>0){
            $token = array_fill(0, $num, 1);
            Redis::lPush($this->_queue, ...$token);
            return $num;
        }

        return 0;

    }

    /**
     * 获取令牌
     * @return Boolean
     */
    public function get(){
        return Redis::rPop($this->_queue)? true : false;
    }

    /**
     * 重设令牌桶,填满令牌
     */
    public function reset(){
        Redis::del($this->_queue);
        $this->add($this->_max);
    }

}

3、模拟用户请求消费令牌

<?php
namespace App\Http\Controllers\Api\V1;


use App\Component\ApiReturn;
use App\Component\Logic\GoodsLogic;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Response;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;


class TokenConsumeController extends BaseController
{

    public function consume()
    {
		$mycontainer = 'mycontainer';
        //获取当前令牌桶状态,是否获取到令牌
        $status = $this->getMycontainerStatus($mycontainer );
        if($status){
            //取到令牌,则查询数据
            $query = (new GoodsLogic())->getInfo(['id' => 1]);
            return ApiReturn::success($query);
        }else{
            //未取到令牌,则返回服务繁忙
            return Response::json([
                'msg' => '服务器繁忙,请稍后再试'
            ], 500); // Status code here
        }


    }
    
    /**
     * 获取令牌桶令牌状态
     * @param string $queue
     * @return bool
     */
    public function getMycontainerStatus($queue = 'mycontainer')
    {
        // 令牌桶容器
//        $queue = 'mycontainer';
        // 创建TokenBucketController对象
        $tokenBucket = new TokenBucketController($queue);

        $status = $tokenBucket->get();
        return $status;
    }


}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,你需要创建一个用户表来存储用户的信息,包括用户名、密码和邮箱等。可以使用Laravel自带的迁移工具来创建表。在命令行中输入`php artisan make:migration create_users_table --create=users`,然后打开创建的迁移文件,添加需要的字段。 ``` Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('email')->unique(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); ``` 接下来,需要创建一个用户模型,并将其关联到用户表。在命令行中输入`php artisan make:model User`,然后打开创建的用户模型文件,在类中添加以下代码: ``` class User extends Authenticatable { use Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; public function getEmailForPasswordReset() { return $this->email; } } ``` 然后,你需要创建一个控制器来处理注册和登录逻辑。在命令行中输入`php artisan make:controller AuthController`,然后打开创建的控制器文件,在类中添加以下代码: ``` class AuthController extends Controller { public function showRegistrationForm() { return view('auth.register'); } public function register(Request $request) { $this->validate($request, [ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6|confirmed', ]); $user = User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($request->password), ]); Auth::login($user); return redirect('/home'); } public function showLoginForm() { return view('auth.login'); } public function login(Request $request) { $this->validate($request, [ 'email' => 'required|string|email', 'password' => 'required|string', ]); if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) { return redirect()->intended('/home'); } return back()->withErrors(['email' => 'Email or password is incorrect.']); } public function logout() { Auth::logout(); return redirect('/login'); } } ``` 在这个控制器中,我们定义了四个方法:`showRegistrationForm()`用于显示注册表单,`register()`用于处理注册逻辑,`showLoginForm()`用于显示登录表单,`login()`用于处理登录逻辑,`logout()`用于退出登录。 最后,你需要创建两个视图文件,一个用于显示注册表单,一个用于显示登录表单。在`resources/views/auth`目录下分别创建`register.blade.php`和`login.blade.php`视图文件。在这两个文件中,你可以使用Laravel自带的表单生成器来创建表单。 注册表单: ``` <form method="POST" action="{{ route('register') }}"> @csrf <div> <label for="name">Name</label> <div> <input id="name" type="text" name="name" value="{{ old('name') }}" required autofocus> </div> @if ($errors->has('name')) <span> <strong>{{ $errors->first('name') }}</strong> </span> @endif </div> <div> <label for="email">Email</label> <div> <input id="email" type="email" name="email" value="{{ old('email') }}" required> </div> @if ($errors->has('email')) <span> <strong>{{ $errors->first('email') }}</strong> </span> @endif </div> <div> <label for="password">Password</label> <div> <input id="password" type="password" name="password" required> </div> @if ($errors->has('password')) <span> <strong>{{ $errors->first('password') }}</strong> </span> @endif </div> <div> <label for="password-confirm">Confirm Password</label> <div> <input id="password-confirm" type="password" name="password_confirmation" required> </div> </div> <div> <div> <button type="submit"> Register </button> </div> </div> </form> ``` 登录表单: ``` <form method="POST" action="{{ route('login') }}"> @csrf <div> <label for="email">Email</label> <div> <input id="email" type="email" name="email" value="{{ old('email') }}" required autofocus> </div> @if ($errors->has('email')) <span> <strong>{{ $errors->first('email') }}</strong> </span> @endif </div> <div> <label for="password">Password</label> <div> <input id="password" type="password" name="password" required> </div> @if ($errors->has('password')) <span> <strong>{{ $errors->first('password') }}</strong> </span> @endif </div> <div> <div> <button type="submit"> Login </button> </div> </div> </form> ``` 这样,你就可以在Laravel中实现用户Email注册和登录了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值