swoole如何對ip限制訪問頻率
- 2019 年 12 月 15 日
- 筆記
swoole如何對ip限制訪問頻率
在我們開發api的過程中,有的時候我們還需要考慮單個用戶(ip)訪問頻率控制,避免被惡意調用。
歸根到底也就只有兩個步驟:
- 用戶訪問要統計次數
- 執行操作邏輯之前要判斷次數頻率是否過高,過高則不執行
easyswoole中實現Ip訪問頻率限制
本文章舉例的是在easyswoole框架中實現的程式碼,在swoole原生中實現方式是一樣的。
只要在對應的回調事件做判斷攔截處理即可。
- 使用swooleTable,儲存用戶訪問情況(也可以使用其他組件、方式儲存)
- 使用定時器,將前一周期的訪問情況清空,統計下一周期
如以下IpList類,實現了初始化Table、統計IP訪問次數、獲取一個周期內次數超過一定值的記錄
<?php /** * Ip訪問次數統計 * User: Siam * Date: 2019/7/8 0008 * Time: 下午 9:53 */ namespace App; use EasySwooleComponentSingleton; use EasySwooleComponentTableManager; use SwooleTable; class IpList { use Singleton; /** @var Table */ protected $table; public function __construct() { TableManager::getInstance()->add('ipList', [ 'ip' => [ 'type' => Table::TYPE_STRING, 'size' => 16 ], 'count' => [ 'type' => Table::TYPE_INT, 'size' => 8 ], 'lastAccessTime' => [ 'type' => Table::TYPE_INT, 'size' => 8 ] ], 1024*128); $this->table = TableManager::getInstance()->get('ipList'); } function access(string $ip):int { $key = substr(md5($ip), 8,16); $info = $this->table->get($key); if ($info) { $this->table->set($key, [ 'lastAccessTime' => time(), 'count' => $info['count'] + 1, ]); return $info['count'] + 1; }else{ $this->table->set($key, [ 'ip' => $ip, 'lastAccessTime' => time(), 'count' => $info['count'] + 1, ]); return 1; } } function clear() { foreach ($this->table as $key => $item){ $this->table->del($key); } } function accessList($count = 10):array { $ret = []; foreach ($this->table as $key => $item){ if ($item['count'] >= $count){ $ret[] = $item; } } return $ret; } }
封裝完IP統計的操作之後
我們可以在EasySwooleEvent.php
的mainServerCreate回調事件中初始化IpList和定時器
<?php public static function mainServerCreate(EventRegister $register) { // 開啟IP限流 IpList::getInstance(); $class = new class('IpAccessCount') extends AbstractProcess{ protected function run($arg) { $this->addTick(5*1000, function (){ /** * 正常用戶不會有一秒超過6次的api請求 * 做列表記錄並清空 */ $list = IpList::getInstance()->accessList(30); // var_dump($list); IpList::getInstance()->clear(); }); } }; }
接著我們在OnRequest回調中,判斷和統計Ip的訪問
<?php public static function onRequest(Request $request, Response $response): bool { $fd = $request->getSwooleRequest()->fd; $ip = ServerManager::getInstance()->getSwooleServer()->getClientInfo($fd)['remote_ip']; // 如果當前周期的訪問頻率已經超過設置的值,則攔截 // 測試的時候可以將30改小,比如3 if (IpList::getInstance()->access($ip) > 30) { /** * 直接強制關閉連接 */ ServerManager::getInstance()->getSwooleServer()->close($fd); // 調試輸出 可以做邏輯處理 echo '被攔截'.PHP_EOL; return false; } // 調試輸出 可以做邏輯處理 echo '正常訪問'.PHP_EOL; }
以上就實現了對同一IP訪問頻率的限制操作。
具體還可以根據自身需求進行擴展,如對具體的某個介面再進行限流。