歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

redis限制請(qǐng)求頻率及資源隔離

發(fā)布時(shí)間:2017-08-13 11:40  回復(fù):0  查看:2371   最后回復(fù):2017-08-13 11:40  

本文和大家分享的主要是redis限制請(qǐng)求頻率及資源隔離相關(guān)內(nèi)容,一起來看看吧,希望對(duì)大家學(xué)習(xí)redis有所幫助。

 背景

  由于導(dǎo)入及導(dǎo)出服務(wù)的使用,可能過多占用業(yè)務(wù)系統(tǒng)的請(qǐng)求。

  為此在db層次做了切分(資源隔離)使用不同的db連接池。

  同時(shí)針對(duì)導(dǎo)入導(dǎo)出服務(wù)增加請(qǐng)求頻率限制,避免占用過多資源

  解決方案

  db連接資源分離

  比較簡(jiǎn)單的利用springdataSource路由

  /**

  * 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和正常請(qǐng)求區(qū)分開來。使用不同的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標(biāo)簽可以完成db的選擇。從而完成資源隔離。

  方法調(diào)用限制

  對(duì)于指定的占用資源方法采用開發(fā)按照key做配置,對(duì)于指定次數(shù)的限制

  @Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface OperationLimit {

  int  value() default 5;

  String key() default "import";

  }

  package com.air.tqb.aop;

  import com.air.tqb.Exception.LimitOperationException;import com.air.tqb.annoate.OperationLimit;import com.google.common.base.Throwables;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.stereotype.Component;

  import java.util.concurrent.TimeUnit;

  @Aspect@Component@Order(Ordered.HIGHEST_PRECEDENCE + 4)public class OperationLimitAspect {

  @Autowired

  @Qualifier(value = "stringRedisTemplate")

  private StringRedisTemplate template;

  private final static String LIMIT_KEY_PREFIX = "limit:";

  @Around("@annotation(com.air.tqb.annoate.OperationLimit) && @annotation(operationLimit)")

  public Object operationLimit(ProceedingJoinPoint joinPoint, OperationLimit operationLimit) {

  ValueOperations<STRING, string="">stringStringValueOperations = template.opsForValue();

  final String key = getLimitKey(operationLimit);

  Long incremented = stringStringValueOperations.increment(key, 1);

  //暫時(shí)不考慮事務(wù)了,簡(jiǎn)單策略過期即可

  stringStringValueOperations.getOperations().expire(key, 180, TimeUnit.SECONDS);

  boolean limitReached = checkLimit(incremented, operationLimit);

  if (limitReached) {

  stringStringValueOperations.increment(key, -1);

  throw new LimitOperationException("當(dāng)前操作" + operationLimit.key() + "已經(jīng)超過最大操作數(shù)" + operationLimit.value() + "限制,請(qǐng)稍后再試!");

  } else {

  try {

  return joinPoint.proceed();

  } catch (Throwable throwable) {

  return Throwables.propagate(throwable);

  } finally {

  stringStringValueOperations.increment(key, -1);

  }

  }

  }

  private boolean checkLimit(Long incremented, OperationLimit operationLimit) {

  if (operationLimit.value() < 0 || incremented <= operationLimit.value()) {

  return false;

  }

  return true;

  }

  private String getLimitKey(OperationLimit operationLimit) {

  return LIMIT_KEY_PREFIX + operationLimit.key();

  }

  }

 

來源:極客頭條

您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?