redis限制请求频率及资源隔离


声明:本文转载自https://my.oschina.net/qixiaobo025/blog/1503394,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

背景

由于导入及导出服务的使用,可能过多占用业务系统的请求。

为此在db层次做了切分(资源隔离)使用不同的db连接池。

同时针对导入导出服务增加请求频率限制,避免占用过多资源

解决方案

db连接资源分离

比较简单的利用spring做dataSource路由

/**  * Created by qixiaobo on 2016/12/2.  */ public class DataSourceRouter extends AbstractRoutingDataSource {     @Override     protected Object determineCurrentLookupKey() {         return WxbStatic.getDataSourceRouting();     } }
import com.air.tqb.annoate.DataSource; import com.air.tqb.annoate.DataSourceType; import com.air.tqb.utils.WxbStatic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component;   /**  * Created by qixiaobo on 2016/12/2.  */ @Aspect @Component @Order(Ordered.HIGHEST_PRECEDENCE + 2) public class DataSourceRoutingAspect {       private static final Log logger = LogFactory.getLog(DataSourceRoutingAspect.class);         @Around("execution(public * com.air.tqb.service..*.*(..)) && @annotation(com.air.tqb.annoate.DataSource) && @annotation(dataSource)")     public Object setDataSource(ProceedingJoinPoint joinPoint, DataSource dataSource) throws Throwable {         DataSourceType dataSourceType = dataSource.value();         try {             WxbStatic.setDataSourceRouting(dataSourceType.getValue());             if (logger.isDebugEnabled()) {                 logger.debug("DataSourceType[" + dataSourceType.getValue() + "] set.");             }             return joinPoint.proceed();         } finally {             WxbStatic.clearDataSourceRouting();             if (logger.isDebugEnabled()) {                 logger.debug("DataSourceType[" + dataSourceType.getValue() + "] remove.");             }         }       }         @Around("execution(public * com.air.tqb.service.report..*.*(..))")     public Object setDataSource(ProceedingJoinPoint joinPoint) throws Throwable {         DataSourceType dataSourceType = DataSourceType.SLOW;         try {             WxbStatic.setDataSourceRouting(dataSourceType.getValue());             if (logger.isDebugEnabled()) {                 logger.debug("report DataSourceType[" + dataSourceType.getValue() + "] set.");             }             return joinPoint.proceed();         } finally {             WxbStatic.clearDataSourceRouting();             if (logger.isDebugEnabled()) {                 logger.debug("report DataSourceType[" + dataSourceType.getValue() + "] remove.");             }         }       } }

这样我们可以吧report和正常请求区分开来。使用不同的db连接

/**  * Created by qixiaobo on 2016/12/2.  */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface DataSource {     DataSourceType value() default DataSourceType.NORMAL; }
package com.air.tqb.annoate;   public enum DataSourceType {       NORMAL("normal"), SLOW("slow"), REPORT("report");       DataSourceType(String value) {         this.value = value;     }       private String value;       public String getValue() {         return value;     } }

在不同的方法上加上指定的db标签可以完成db的选择。从而完成资源隔离。

方法调用限制

对于指定的占用资源方法采用开发按照key做配置,对于指定次数的限制

@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface OperationLimit {     int  value() default 5;     String key() default "import"; }
@Aspect @Component @Order(Ordered.HIGHEST_PRECEDENCE + 4) public class OperationLimitAspect {     @Autowired     @Qualifier(value = "stringRedisTemplate")     private StringRedisTemplate template;       @Around("@annotation(com.air.tqb.annoate.OperationLimit) && @annotation(operationLimit)")     public void operationLimit(ProceedingJoinPoint joinPoint, OperationLimit operationLimit) {         ValueOperations<String, String> stringStringValueOperations = template.opsForValue();         Long incremented = stringStringValueOperations.increment(operationLimit.key(), 1);         //暂时不考虑事务了,简单策略过期即可         stringStringValueOperations.getOperations().expire(operationLimit.key(), 180, TimeUnit.SECONDS);         boolean limitReached = checkLimit(incremented, operationLimit);         if (limitReached) {             stringStringValueOperations.increment(operationLimit.key(), -1);             throw new LimitOperationException("当前操作" + operationLimit.key() + "已经超过最大操作数" + operationLimit.value() + "限制,请稍后再试!");         } else {             try {                 joinPoint.proceed();                 stringStringValueOperations.increment(operationLimit.key(), -1);             } catch (Throwable throwable) {                 Throwables.propagate(throwable);             }         }         }       private boolean checkLimit(Long incremented, OperationLimit operationLimit) {         if (operationLimit.value() < 0 || operationLimit.value() < incremented) {             return false;         }         return true;     }   }

 

本文发表于2017年08月06日 12:36
(c)注:本文转载自https://my.oschina.net/qixiaobo025/blog/1503394,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2033 讨论 1 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1