Je suis récemment obsédé par le développement commercial et je n'ai pas mis à jour mon blog depuis un certain temps. Je prévois d'inclure des solutions pratiques dans des scénarios commerciaux ou de meilleures idées de conception dans les prochaines années. Partager des articles de blog, ce n'est pas comme passer beaucoup de temps à trier du contenu lié à un sujet auparavant (retenir l'astuce ultime). Le contenu de l'article suivant n'est peut-être pas aussi riche, mais il peut être plus détaillé sur un point. autant que possible, ou Analyse plus approfondie, grâce au partage continu et à l'auto-évaluation, à l'accumulation d'expériences, tout en augmentant la fréquence de partage de blogs
Scénario
Scénario 1
留言功能限制,30秒 内只能评论 10次,超出次数不让能再评论,并提示:过于频繁
Scénario 2
点赞功能限制,10秒 内只能点赞 10次,超出次数后不能再点赞,并禁止操作 1个小时,提示:过于频繁,被禁止操作1小时
Scénario 3
上传记录功能,限制一天只能上传 100次,超出次数不让能再上传,并提示:超出今日上线
Résumer de l'essence
Dans le processus de développement commercial, nous sommes constamment impliqués dans la conception de divers scénarios commerciaux, et il est souvent facile de rencontrer des scénarios de solutions très similaires, mais les modules métier actuels auxquels ils appartiennent sont différents. En fait, l'essence de ces exigences est de résoudre le même problème. Lorsque nous rencontrons ce scénario, nous devons en extraire les problèmes essentiels. les exigences basées sur notre propre expérience, analysent et mettent en œuvre des solutions universelles, rendent vos solutions plus précieuses, cela peut faire la différence entre vous êtes un ingénieur avec une âme ou le roi du cp (copier-coller) le plus fort.
En analysant les trois scénarios commerciaux ci-dessus, nous pouvons constater qu'ils contiennent une logique similaire, les appelant des problèmes similaires. Nous allons maintenant résumer ce problème, concevoir une solution générale et décrire le même organigramme logique. :
En analysant les scénarios de demande ci-dessus, nous pouvons extraire les conditions dont ils ont tous besoin :
Objets restreints : opérations restreintes par les utilisateurs (commentaires, likes), record, ...) Le nombre d'opérations dans la plage de temps
(最小时间单位用秒:天/小时/分钟都可换算成秒,用秒可以解决更多的场景)
<?php/** * 频率限制 * @param string $action 操作动作 * @param int $userId 发起操作的用户ID * @param int $time 时间范围X秒内 * @param int $number 限制操作数Y次 * @param array $expire 超出封印时间Z ['type'=>1,'ttl'=>过期时间/秒] ['type'=>2,'ttl'=>具体过期时间戳] 二选一 * @return bool * @throws \Exception */public static function frequencyLimit(string $action, int $userId, int $time, int $number, $expire = []){ // todo 根据用户操作动作时间范围,进行频率的控制和失效释放}
La fonction doit effectuer les opérations et le temps initiés par l'utilisateur, ainsi que le nombre cumulé de stockage, et doit nettoyer l'expiration si nous comptons sur MySQL pour le stockage à ce moment-là, ce sera assez pénible. pour y réfléchir.Le protagoniste ici : redis apparaît enfin. Sur la base des caractéristiques de redis, le fonctionnement atomique de incr et key prend en charge le mécanisme d'expiration et le stockage en mémoire. L'avantage en termes d'efficacité peut atteindre l'objectif de manière relativement simple, flexible et efficace.
Voici un code qui implémente simplement une fonction commune :
<?php/** * 频率限制 * @param string $action 操作动作 * @param int $userId 发起操作的用户ID * @param int $time 时间范围X秒内 * @param int $number 限制操作数Y次 * @param array $expire 超出封印时间Z ['type'=>1,'ttl'=>过期时间/秒] ['type'=>2,'ttl'=>具体过期时间戳] 二选一 * @return bool * @throws \Exception */public function frequencyLimit(string $action, int $userId, int $time, int $number, $expire = []){ if (empty($action) || $userId <= 0 || $time <= 0 || $number <= 0) { throw new \Exception('非法参数'); } $key = 'act:limit:' . $action . ':' . $userId; $r = RedisClient::connect(); //获取当前累计次数 $current = intval($r->get($key)); if ($current >= $number) return false; //累计并返回最新值 $current = $r->incr($key); //第一次累加,设置控制操作频率的有效时间 if ($current === 1) $r->expire($key, $time); //未超出限制次数先放过 if ($current < $number) return true; //超出后根据需要重新设置过期失效时间 $current === $number 判断保证只重新设置一次 $type = empty($expire['type']) ? 0 : intval($expire['type']); $ttl = empty($expire['ttl']) ? 0 : intval($expire['ttl']); if ($current === $number && $ttl > 0 && in_array($type, [1, 2])) { if ($type === 1) $r->expire($key, $ttl); if ($type === 2) $r->expireAt($key, $ttl); } return false; }//场景1/** * 评论限制 * @param int $userId * @return bool|string */public function doComment(int $userId){ try { $pass = FrequencyLimit::doHandle('comment', $userId, 30, 10); if (!$pass) return '过于频繁'; // todo 评论逻辑 return true; } catch (\Exception $e) { return $e->getMessage(); } }//场景2/** * 点赞限制 * @param int $userId * @return bool|string */public function doLike(int $userId){ try { $pass = FrequencyLimit::doHandle('like', $userId, 10, 10, ['type' => 1, 'ttl' => 1 * 60 * 60]); if (!$pass) return '过于频繁,被禁止操作1小时'; // todo 点赞逻辑 return true; } catch (\Exception $e) { return $e->getMessage(); } }//场景3/** * 上传限制 * @param int $userId * @return bool|string */public function doUpload(int $userId){ try { $expire = strtotime(date('Y-m-d', strtotime(+1 . 'days'))); $pass = FrequencyLimit::doHandle('upload', $userId, 1 * 24 * 60 * 60, 100, ['type' => 2, 'ttl' => $expire]); if (!$pass) return '超出今日上线'; // todo 上传逻辑 return true; } catch (\Exception $e) { return $e->getMessage(); } }//场景N
编码上可以根据你设计这个通用方案的复杂度进行进一步抽象,如抽象成频率限制的功能类等
Résumé
Analyser des scénarios commerciaux similaires, découvrir des problèmes essentiels et concevoir des solutions générales
Faire des solutions plus précieuses et soyez un développeur avec une âme
Maîtrisez Redis et utilisez pleinement ses fonctionnalités et avantages
Pour plus d'articles techniques liés à Redis, veuillez visiter
Tutoriel Rediscolonne pour apprendre !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!