需求线上出现的问题是 , 一些非核心的查询数据业务 , 在请求超时或者错误的时候 , 用户会越查询 , 导致数据库cup飙升 , 拖垮核心的业务 。
领导让我做三件事 , 一是把这些接口做一个限流 , 这些限流参数是可配的 , 第二是这些接口可以设置开关 , 当发现问题时 , 可以手动关闭这些接口 , 不至于数据库压力过大影响核心业务的服务 。第三是做接口的熔断 , 熔断设置可以配置 。
经过确定 , 前两个实现用redis来实现 , 第三个因为熔断讨论觉得比较复杂 , 决定采用我提出的用Hystrix,目前项目不能热加载生效配置中心的最新的配置 , 所以后期推荐使用Archaius , 这些网上查到的 , 具体为啥不选其他的 , 原因就是其他的比较复杂 , 上手感觉这个最快 。
这篇文章说实现 , 其他问题不涉及 , 请多多指教 。
思路接口的屏蔽:通过AOP实现 , 每次访问接口的时候 , 通过接口的Key值 , 在Redis取到接口设置开关值 , 如果打开继续 , 否在拒绝 。接口限流也是基于AOP , 根据接口的Key值 , 取到这个接口的限流值 , 表示多长时间 , 限流几次 , 每次访问都会请求加一 , 通过比较 , 如果超过限制再返回 , 否在继续 。
代码
文章插图
AccessLimiter接口 , 主要有两类方法 , 是否开启限流 , 取Redis中的限流值 。
package com.hcfc.auto.util.limit;import JAVA.util.concurrent.TimeUnit;/** * @创建人 peng.wang * @描述 访问限制器 */public interface AccessLimiter {/*** 检查指定的key是否收到访问限制* @param key限制接口的标识* @param times 访问次数* @param per一段时间* @param unit时间单位* @return*/public boolean isLimited(String key, long times, long per, TimeUnit unit);/*** 移除访问限制* @param key*/public void refreshLimited(String key);/*** 接口是否打开* @return*/public boolean isStatus(String redisKey);/*** 接口的限流大小* @param redisKeyTimes* @return*/public long getTimes(String redisKeyTimes);/*** 接口限流时间段* @param redisKeyPer* @return*/public long getPer(String redisKeyPer);/*** 接口的限流时间单位* @param redisKeyUnit* @return*/public TimeUnit getUnit(String redisKeyUnit);/*** 是否删除接口限流* @param redisKeyIsRefresh* @return*/public boolean getIsRefresh(String redisKeyIsRefresh);}
RedisAccessLimiter是AccessLimiter接口的实现类package com.hcfc.auto.util.limit; import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /** * @创建人 peng.wang * @描述 基于Redis的实现 */@Componentpublic class RedisAccessLimiter implements AccessLimiter {private static final Logger LOGGER = LoggerFactory.getLogger(RedisAccessLimiter.class);@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic boolean isLimited(String key, long times, long per, TimeUnit unit) {Long curTimes = redisTemplate.boundValueOps(key).increment(1);LOGGER.info("curTimes {}",curTimes);if(curTimes > times) {LOGGER.debug("超频访问:[{}]",key);return true;} else {if(curTimes == 1) {LOGGER.info(" set expire ");redisTemplate.boundValueOps(key).expire(per, unit);return false;} else {return false;}}}@Overridepublic void refreshLimited(String key) {redisTemplate.delete(key);}@Overridepublic boolean isStatus(String redisKey) {try {return (boolean)redisTemplate.opsForValue().get(redisKey+"IsOn");}catch (Exception e){LOGGER.info("redisKey is not find or type mismatch, key: ", redisKey);return false;}}@Overridepublic long getTimes(String redisKeyTimes) {try {return (long)redisTemplate.opsForValue().get(redisKeyTimes+"Times");}catch (Exception e){LOGGER.info("redisKey is not find or type mismatch, key: ", redisKeyTimes);return 0;}}@Overridepublic long getPer(String redisKeyPer) {try {return (long)redisTemplate.opsForValue().get(redisKeyPer+"Per");}catch (Exception e){LOGGER.info("redisKey is not find or type mismatch, key: ", redisKeyPer);return 0;}}@Overridepublic TimeUnit getUnit(String redisKeyUnit) {try {return (TimeUnit) redisTemplate.opsForValue().get(redisKeyUnit+"Unit");}catch (Exception e){LOGGER.info("redisKey is not find or type mismatch, key: ", redisKeyUnit);return TimeUnit.SECONDS;}}@Overridepublic boolean getIsRefresh(String redisKeyIsRefresh) {try {return (boolean)redisTemplate.opsForValue().get(redisKeyIsRefresh+"IsRefresh");}catch (Exception e){LOGGER.info("redisKey is not find or type mismatch, key: ", redisKeyIsRefresh);return false;}}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 一次关于架构的“嘴炮”
- 羡慕BI软件的可视化?Python终于可以做出联动可视化报告
- Nginx反向代理Grafana服务的配置步骤
- 12 个优化 Docker 镜像安全性的技巧
- 免费开源的视频直播/录制 OBS Studio 上架 Steam
- 24 张图搞定 ICMP:最常用的网络命令 ping 和 tracert
- 泡沫红茶用什么茶,泡沫红茶饮料
- 泡红茶需要多少度的水,绿茶多少度的水
- 红茶功夫茶的泡法,红茶包的泡法
- 红茶滇红作用,红茶的泡多长时间