库存秒杀问题-redis解决方案- 接口限流
<?php
/**
* Created by PhpStorm.
* redis 销量超卖秒杀解决方案
* redis 文档:http://doc.redisfans.com/
* ab -n 10000 -c 3000 http://localhost/demo.php 模拟并发
*/ $redis = new Redis();
$redis->connect('127.0.0.1',6379); //1. 对某一个键加锁,这个键是我们自己设置,起到监视作业
$redis->watch('sales'); //获取销量,清空sales 为0
$sales = $redis->get('sales'); //总库存
$store = 4; if($sales>=$store){
exit('已经被抢光了'); //跳转活动结束页面
} //redis事务不会回滚, 开启事务
$redis->multi();
$redis->set('sales',$sales+1); //销量加1
$res = $redis->exec(); if($res){
//减库存
include db.php; //数据库连接
//执行sql ,减库存
}
exit; =====================redis接口限流============================
- 非脚本实现
private boolean accessLimit(String ip, int limit, int time, Jedis jedis) {
boolean result = true;
String key = "rate.limit:" + ip;
if (jedis.exists(key)) {
long afterValue = jedis.incr(key);
if (afterValue > limit) {
result = false;
}
} else {
Transaction transaction = jedis.multi();
transaction.incr(key);
transaction.expire(key, time);
transaction.exec();
}
return result;
}
以上代码有两点缺陷
- 可能会出现竞态条件: 解决方法是用
WATCH监控rate.limit:$IP的变动, 但较为麻烦; - 以上代码在不使用
pipeline的情况下最多需要向Redis请求5条指令, 传输过多.
Lua脚本实现
Redis 允许将 Lua 脚本传到 Redis 服务器中执行, 脚本内可以调用大部分 Redis 命令, 且 Redis 保证脚本的原子性:
- 首先需要准备Lua代码: script.lua
--
-- Created by IntelliJ IDEA.
-- User: jifang
-- Date: 16/8/24
-- Time: 下午6:11
-- local key = "rate.limit:" .. KEYS[]
local limit = tonumber(ARGV[])
local expire_time = ARGV[] local is_exists = redis.call("EXISTS", key)
if is_exists == then
if redis.call("INCR", key) > limit then
return
else
return
end
else
redis.call("SET", key, )
redis.call("EXPIRE", key, expire_time)
return
end
private boolean accessLimit(String ip, int limit, int timeout, Jedis connection) throws IOException {
List<String> keys = Collections.singletonList(ip);
List<String> argv = Arrays.asList(String.valueOf(limit), String.valueOf(timeout));
return 1 == (long) connection.eval(loadScriptString("script.lua"), keys, argv);
}
// 加载Lua代码
private String loadScriptString(String fileName) throws IOException {
Reader reader = new InputStreamReader(Client.class.getClassLoader().getResourceAsStream(fileName));
return CharStreams.toString(reader);
}
Lua 嵌入 Redis 优势:
- 减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;
- 原子操作: Redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务;
- 复用: 脚本会永久保存 Redis 中, 其他客户端可继续使用.
库存秒杀问题-redis解决方案- 接口限流的更多相关文章
- Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流
1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...
- SpringBoot使用自定义注解+AOP+Redis实现接口限流
为什么要限流 系统在设计的时候,我们会有一个系统的预估容量,长时间超过系统能承受的TPS/QPS阈值,系统有可能会被压垮,最终导致整个服务不可用.为了避免这种情况,我们就需要对接口请求进行限流. 所以 ...
- 基于注解的接口限流+统一session认证
代码心得: 一个基本的做法:对于用户身份认证做到拦截器里,针对HandlerMethod进行统一拦截认证,根据方法上的注解标识,判别是否需要身份验证,并将查找出来的User实体存入ThreadLoca ...
- 【Dnc.Api.Throttle】适用于.Net Core WebApi接口限流框架
Dnc.Api.Throttle 适用于Dot Net Core的WebApi接口限流框架 使用Dnc.Api.Throttle可以使您轻松实现WebApi接口的限流管理.Dnc.Api.Thr ...
- 限流(三)Redis + lua分布式限流
一.简介 1)分布式限流 如果是单实例项目,我们使用Guava这样的轻便又高性能的堆缓存来处理限流.但是当项目发展为多实例了以后呢?这时候我们就需要采用分布式限流的方式,分布式限流可以以redis + ...
- 【高并发】亿级流量场景下如何为HTTP接口限流?看完我懂了!!
写在前面 在互联网应用中,高并发系统会面临一个重大的挑战,那就是大量流高并发访问,比如:天猫的双十一.京东618.秒杀.抢购促销等,这些都是典型的大流量高并发场景.关于秒杀,小伙伴们可以参见我的另一篇 ...
- Guava-RateLimiter实现令牌桶控制接口限流方案
一.前言 对于一个应用系统来说,我们有时会遇到极限并发的情况,即有一个TPS/QPS阀值,如果超了阀值可能会导致服务器崩溃宕机,因此我们最好进行过载保护,防止大量请求涌入击垮系统.对服务接口进行限流可 ...
- 服务限流 -- 自定义注解基于RateLimiter实现接口限流
1. 令牌桶限流算法 令牌桶会以一个恒定的速率向固定容量大小桶中放入令牌,当有浏览来时取走一个或者多个令牌,当发生高并发情况下拿到令牌的执行业务逻辑,没有获取到令牌的就会丢弃获取服务降级处理,提示一个 ...
- Spring Cloud Alibaba基础教程:使用Sentinel实现接口限流
最近管点闲事浪费了不少时间,感谢网友libinwalan的留言提醒.及时纠正路线,继续跟大家一起学习Spring Cloud Alibaba. Nacos作为注册中心和配置中心的基础教程,到这里先告一 ...
随机推荐
- AsyncLocal<T>与ThreadLocal<T>区别研究
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- CDQ分治入门 + 例题 Arnooks's Defensive Line [Uva live 5871]
CDQ分治入门 简介 CDQ分治是一种特别的分治方法,它由CDQ(陈丹琦)神犇于09国家集训队作业中首次提出,因此得名.CDQ分治属于分治的一种.它一般只能处理非强制在线的问题,除此之外这个算法作为某 ...
- PAT基础6-10
6-10 阶乘计算升级版 (20 分) 本题要求实现一个打印非负整数阶乘的函数. 函数接口定义: void Print_Factorial ( const int N ); 其中N是用户传入的参数,其 ...
- Chars模拟弱网测试
1.打开chars,选择[proxy]-[throttle settings] 2.勾选[enable throttling],并配置.
- 关于django的部分
新建Django工程 django-admin.exe startproject mysite 新建Django工程 新建static(静态模板文件存储路径) setting中配置static路径 S ...
- oracle函数返回结果集
一.用自定义类型实现 1.创建表对象类型. 在Oracle中想要返回表对象,必须自定义一个表类型,如下所示: create or replace type type_table is table of ...
- ruby安装方法
安装 Ruby Ruby官网下载:http://www.ruby-lang.org/en/downloads/(官网下载链接) 安装过程中,得注意,勾选上添加到环境变量 安装完成后,查看是否安装成功 ...
- GMA Round 1 YGGDRASIL
传送门 YGGDRASIL 在YGGDRASIL世界,一年有213天. Demiurge推广种植了一种植物,姑且称之为“黄金果”,它第一期生长需要140天,此后第i期生长需要的天数$a_i$满足$a_ ...
- 基于ubuntu的docker安装
系统版本:Ubuntu16.04 docker版本:18.02.0 Ubuntu 系统的内核版本>3.10(执行 uname -r 可查看内核版本) 在安装前先简单介绍一下docker,按照 ...
- pygame 笔记-5 模块化&加入敌人
上一节,已经用OOP方法,把几个类抽象出来了,但是都集中在一个.py文件中,代码显得很冗长,这一节复用模块化的思想,把这个大文件拆分成几个小文件: 先把主角Player单独放到一个文件player.p ...