thinkphp6:根据ip和时间用redis做限流(thinkphp v6.0.12LTS)
一,创建路由 / 配置中间件/配置redis
创建controller:
liuhongdi@lhdpc:/data/php/imgtouch$ php think make:controller Serve Controller:app\controller\Serve created successfully.
创建中间件:
liuhongdi@lhdpc:/data/php/imgtouch$ php think make:middleware ServeLimit Middleware:app\middleware\ServeLimit created successfully.
查看创建的middleware
liuhongdi@lhdpc:/data/php/imgtouch$ ls app/middleware CheckJwt.php ServeLimit.php
查看创建的controller
liuhongdi@lhdpc:/data/php/imgtouch$ ls app/controller/ Auth.php Home.php Image.php Index.php Serve.php
配置路由和中间件
route/app.php:
<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st <liu21st@gmail.com> // +---------------------------------------------------------------------- use think\facade\Route; Route::group('home', function () { Route::get('home', 'home/home'); Route::post('postdata', 'home/postdata'); }); Route::group('serve', function () { Route::get('image', 'serve/image'); })->middleware(\app\middleware\ServeLimit::class);
配置redis:
.env
[REDIS0] TYPE = redis HOST = 127.0.0.1 PORT = 6379 PASSWORD =
说明:刘宏缔的架构森林是一个专注架构的博客,
网站:https://blog.imgtouch.com
本文: https://blog.imgtouch.com/index.php/2023/06/03/thinkphp6-gen-ju-ip-he-shi-jian-yong-redis-zuo-xian-liu/
对应的源码可以访问这里获取: https://github.com/liuhongdi/
或: https://gitee.com/liuhongdi
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,php代码:
1,ServeLimit.php
<?php declare (strict_types = 1); namespace app\middleware; use app\result\Result; use log\BusinessLog; class ServeLimit { /** * 处理请求 * * @param \think\Request $request * @param \Closure $next * @return \think\response\Json */ public function handle($request, \Closure $next){ //得到当前IP $ip = $request->ip(); //判断是否可访问,1:可访问 0:不可访问 $isCan = $this->isCanAccess($ip); if ($isCan == "1"){ return $next($request); } else { return Result::ErrorCode(1,"访问超出次数限制"); } } //判断是否允许访问 //参数:ip地址 //返回:1:可访问 0:不可访问 private function isCanAccess($ip) { $day = date("Ymd"); $key = $day."_".$ip; //访问redis,用lua脚本判断是否可访问 $lua = <<<SCRIPT local key = KEYS[1]; local limit = tonumber(KEYS[2]) local duration = tonumber(KEYS[3]) --redis.log(redis.LOG_NOTICE,' duration: '..duration) local current = redis.call('GET', key) if current == false then --redis.log(redis.LOG_NOTICE,key..' is nil ') redis.call('SET', key,1) redis.call('EXPIRE',key,duration) --redis.log(redis.LOG_NOTICE,' set expire end') return '1' else --redis.log(redis.LOG_NOTICE,key..' value: '..current) local num_current = tonumber(current) if num_current+1 > limit then return '0' else redis.call('INCRBY',key,1) return '1' end end SCRIPT; $redis = new \Redis(); $redisHost = env('redis0.host', '127.0.0.1'); $redisIp = env('redis0.port', '6379'); $redis->connect($redisHost,(int)$redisIp); $redis->select(0); $duration = 3600 * 24; //时长,以秒为单位 $number = 3; //允许访问的次数 $isSucc = $redis->eval($lua, [$key,$number,$duration], 3); //记录 $log = new BusinessLog('test'); $log->log("isSucc:".$isSucc.":"); return $isSucc; } }
2,Serve.php
<?php declare (strict_types = 1); namespace app\controller; use app\result\Result; use think\Request; class Serve { //测试一个get方式的访问 public function image() { return Result::Success(["msg"=>'this is serve image']); } }
3,BusinessLog.php
<?php namespace log; class BusinessLog{ var $baseDir; var $subDir; var $fileName; //构造 function __construct($type){ if ($type == "test") { $this->baseDir = "/data/businesslog/testlog"; $this->subDir = date("Ym"); $this->fileName = "test_".date("Ymd") . ".txt"; } } //记录 function log($content){ if (is_array($content)) { $contentStr = var_export($content, true); } else { $contentStr = $content; } $fileDir = $this->baseDir."/".$this->subDir; if (!is_dir($fileDir)) { mkdir($fileDir, 0777, true); } $filePath = $fileDir . "/" . $this->fileName; $ip = request()->ip(); $logContent = date("Y-m-d H:i:s") . "--" . $ip . "--" . $contentStr . "\r\n"; error_log($logContent, 3, $filePath); } }
4,Result.php
<?php namespace app\result; use think\response\Json; class Result { //success,返回数据 static public function Success($data):Json { $rs = [ 'code'=>0, 'msg'=>"success", 'data'=>$data, ]; return json($rs); } //error需要code/msg参数 static public function ErrorCode($code,$msg):Json { $rs = [ 'code'=>$code, 'msg'=>$msg, 'data'=>"", ]; return json($rs); } //error,传入定义的数组常量 static public function Error($arr):Json { $rs = [ 'code'=>$arr['code'], 'msg'=>$arr['msg'], 'data'=>"", ]; return json($rs); } }
三,测试效果
1,浏览器访问:页面返回的内容:
2,redis中查看效果:
liuhongdi@lhdpc:/data/phplog/202210$ redis-cli 127.0.0.1:6379> get 20221026_127.0.0.1 "3" 127.0.0.1:6379> ttl 20221026_127.0.0.1 (integer) 85885
3,通过并发测试效果:
ab10并发
liuhongdi@lhdpc:/data/php/admapi/app$ ab -c 10 -n 10 http://127.0.0.1:8002/serve/image This is ApacheBench, Version 2.3 <$Revision: 1879490 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: nginx/1.18.0 Server Hostname: 127.0.0.1 Server Port: 8002 Document Path: /serve/image Document Length: 63 bytes Concurrency Level: 10 Time taken for tests: 0.022 seconds Complete requests: 10 Failed requests: 7 (Connect: 0, Receive: 0, Length: 7, Exceptions: 0) Total transferred: 2340 bytes HTML transferred: 560 bytes Requests per second: 447.85 [#/sec] (mean) Time per request: 22.329 [ms] (mean) Time per request: 2.233 [ms] (mean, across all concurrent requests) Transfer rate: 102.34 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.2 0 1 Processing: 3 9 4.3 10 15 Waiting: 3 9 4.4 10 15 Total: 4 9 4.2 10 15 Percentage of the requests served within a certain time (ms) 50% 10 66% 11 75% 14 80% 15 90% 15 95% 15 98% 15 99% 15 100% 15 (longest request)
查看日志:
liuhongdi@lhdpc:/data/php/admapi/app$ more /data/businesslog/testlog/202210/test_20221026.txt 2022-10-26 18:13:20--127.0.0.1--isSucc:1: 2022-10-26 18:13:20--127.0.0.1--isSucc:1: 2022-10-26 18:13:20--127.0.0.1--isSucc:1: 2022-10-26 18:13:20--127.0.0.1--isSucc:0: 2022-10-26 18:13:20--127.0.0.1--isSucc:0: 2022-10-26 18:13:20--127.0.0.1--isSucc:0: 2022-10-26 18:13:20--127.0.0.1--isSucc:0: 2022-10-26 18:13:20--127.0.0.1--isSucc:0: 2022-10-26 18:13:20--127.0.0.1--isSucc:0: 2022-10-26 18:13:20--127.0.0.1--isSucc:0:
四,查看thinkphp的版本:
liuhongdi@lhdpc:/data/php/imgtouch$ php think version v6.0.12LTS