# 限流 **Repository Path**: fengmingcong/current-limiting ## Basic Information - **Project Name**: 限流 - **Description**: 限流方案 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-08-28 - **Last Updated**: 2022-08-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 某电商业务场景1 业务背景:电商实际业务中,不同的商品,适合在不同的区域售卖,比如空气净化器,优先适合在空气质量不好的城市售卖,雪地靴,优先适合在北方天气冷的地区售卖,即每个城市适合按当地用户喜好,优先展示不同的商品。 现有表关系 ![image-20220827150345236](image-20220827150345236.png) 1.如何让返回给前端的商品列表,区域(即城市)动态变化。 ``` 明确需求:前端输入参数:区域(即城市);后端返回值:每个城市按照用户喜欢排序后的商品列表。 新增接口:根据城市返回用户喜欢排序的商品列表接口。 新增表格:现有表格无法体现城市、商品、用户喜欢关系,因此需要新增表。 实现方式:城市信息(前端获取客户端所在的城市信息,或者后端新增一个根据用户ip地址返回城市信息的接口)作为参数传递到接口。 ``` 2.假定上述效果已经完成实现,如何扩展现有系统,在不影响现有版本(无区域化差异)的情况下,做对比测试? 给出大致设计思路和实现? ``` 大致思路如下。 1.新增一个城市喜好表:ID-国家-省份-城市-商品ID-喜好排序-创建时间-更新时间。(如果城市要使用编号,可以再加一张对应关系表) 2.初始化一份用户喜好数据。(参考产品买家地址及销量等,后续添加手工维护页面或者根据销量自动调整) 3.前端调用客户端位置信息或者调用后端接口,获取城市信息(某国-某省-某市),然后调用查询接口 ``` ``` select 商品信息,喜好排序 from 商品表 left join 喜好表 on 商品ID=商品ID where 城市信息=输入的城市信息 order by 喜好排序,原先排序方式 limit xx offset xx ``` ## 某电商业务场景2 运营推广部门某次策划上线秒杀或者优惠活动,经测试人员估算压力测试,大约在一个小时内进来100万+用户访问,系统吞吐量固定的情况下,为保障Java服务端正常运行不崩溃,需要对正常访问用户进行限流处理,大约每秒响应1000个请求。 请问限流的系统如何设计,给出具体的实现?(服务端框架采用spring boot+mybatis+redis) ### 一、单个接口响应限流。 如果指的是每个服务springboot能够处理的请求为1000个。可以在接口执行前加上限流处理。 可以引入开源限流工具类。Google开源工具包Guava提供了限流工具类RateLimiter ``` com.google.guava guava 30.1-jre ``` ``` @Slf4j @Aspect @Component public class RateLimiterAop { /** * 限流策略 : 1秒钟1000个请求 */ private final RateLimiter limiter = RateLimiter.create(1000); // 定义切点Pointcut。(如果是针对特点的秒杀接口,可以通过自定义注解,在对应的接口上添加注解) @Pointcut("execution(* com.fmc.web..*.*(..))") public void executeController() { } @Around("executeController()") public Object around(ProceedingJoinPoint pjp) throws Throwable { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); //在进入接口前,先获取令牌。500毫秒内,没拿到令牌,就直接进入服务降级 if (limiter.tryAcquire(500, TimeUnit.MILLISECONDS)) { log.info("获取令牌成功,时间{}", LocalDateTime.now().format(dtf)); //执行原逻辑 return pjp.proceed(); } else { log.warn("进入服务降级,时间{}", LocalDateTime.now().format(dtf)); return "当前排队人数较多,请稍后再试!"; } } } ``` ### 二、集群部署限流 如果使用nginx,可以使用nginx限流 ``` limit_req_zone $binary_remote_addr zone=one:10m rate=1000r/s; 第一个参数:$binary_remote_addr 表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址。 第二个参数:zone=one:10m表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息。 第三个参数:rate=10r/s表示允许同一个客户端的访问频次是每秒10次,还可以有比如30r/m的 ``` 如果项目使用k8s部署,也可以通过配置k8s的属性进行限流。 如果升级采用springcloud框架,也可以通过使用cloud的组件限流 ## 某电商业务场景3 同上,运营推广部门策划活动后,曾遇到恶意刷单或者系统被注水的情况。为保护开放接口不被恶意调用,请设计一个方案,对恶意接口请求流量做区分,同时不能影响对正常访问用户。 ``` 1.调用接口前加一层判断,通过ip+登陆用户的信息两个判断是否段时间内多次调用。判断是否恶意调用,返回不同的逻辑。 2.在网关层进行黑白名单过滤。比如ngigx+OpenResty+Lua+redis 可以实现在nginx层进行动态黑白名单过滤,不影响原有代码 ```