Authored by hugufei

个性化降级代码优化

package com.yoho.search.common.downgrade.persional;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yoho.search.common.cache.impls.SearchRedis;
@Service
public class AutoDownGradePersionalRateLimitService {
@Autowired
private SearchRedis searchRedis;
public boolean isRateLimit(String key, int rateLimitInSecond, int rateLimit) {
if (!searchRedis.searchRedisTemplate.hasKey(key)) {
// 如果已经被初始化过, 则不用再初始化, 因为其他线程可能已经进行了 increase, 所以必须使用 setIfAbsent
searchRedis.searchValueOperations.setIfAbsent(key, "0");
// 设置有效期, redis所有的key, 必须设置有效期
searchRedis.searchRedisTemplate.longExpire(key, rateLimitInSecond, TimeUnit.SECONDS);
}
// 原子性对值修改delta数值
return searchRedis.searchValueOperations.increment(key, 1).intValue() > rateLimit;
}
}
... ... @@ -7,10 +7,10 @@ import java.lang.annotation.Target;
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoDownGradePersional {
public @interface PersionalRateLimit {
public int rateLimitInSecond();
public int limit() default 100;// 总次数
public int rateLimit();
public int second() default 10;// 总时间
}
... ...
... ... @@ -17,33 +17,33 @@ import com.yoho.search.common.filter.YohoHttpServletRequestWrapper;
@Component
@Aspect
public class AutoDownGradePersionalAspect {
public class PersionalRateLimitAspect {
private static final Logger logger = LoggerFactory.getLogger("DOWNGRADE");
private static final Logger DOWNGRADE = LoggerFactory.getLogger("DOWNGRADE");
@Autowired
private AutoDownGradePersionalRateLimitService autoDownGradePersionalRateLimitService;
private PersionalRateLimitService persionalRateLimitService;
@Around("@annotation(com.yoho.search.common.downgrade.persional.AutoDownGradePersional)")
public Object downGrade(ProceedingJoinPoint pjp) throws Throwable {
@Around("@annotation(com.yoho.search.common.downgrade.persional.PersionalRateLimit)")
public Object persionalRateLimit(ProceedingJoinPoint pjp) throws Throwable {
try {
String autoDownGradeKey = this.getAutoDownGradeKey(pjp);
String persionalRateLimitKey = this.getPersionalRateLimitKey(pjp);
MethodSignature signature = (MethodSignature) pjp.getSignature();
AutoDownGradePersional autoDownGrade = signature.getMethod().getAnnotation(AutoDownGradePersional.class);
PersionalRateLimit rersionalRateLimit = signature.getMethod().getAnnotation(PersionalRateLimit.class);
Object[] arges = pjp.getArgs();
for (Object object : arges) {
if (this.dealPersional(object, autoDownGrade, autoDownGradeKey)) {
if (this.doPersionalRateLimit(object, rersionalRateLimit, persionalRateLimitKey)) {
break;
}
}
return pjp.proceed();
} catch (Exception e) {
logger.error(e.getMessage());
DOWNGRADE.error(e.getMessage());
throw e;
}
}
private boolean dealPersional(Object object, AutoDownGradePersional autoDownGrade, String autoDownGradeKey) {
private boolean doPersionalRateLimit(Object object, PersionalRateLimit rateLimit, String rateLimitKey) {
if (!(object instanceof YohoHttpServletRequestWrapper)) {
return false;
}
... ... @@ -55,17 +55,27 @@ public class AutoDownGradePersionalAspect {
if (StringUtils.isNotBlank(yohoHttpServletRequestWrapper.getParameter("order"))) {
return false;
}
int rateLimitInSecond = autoDownGrade.rateLimitInSecond();
int rateLimit = autoDownGrade.rateLimit();
if (autoDownGradePersionalRateLimitService.isRateLimit(autoDownGradeKey, rateLimitInSecond, rateLimit)) {
logger.error("AutoDownGradePersionalAspect,autoDownGradeKey is [{}],rateLimitInSecond is [{}],rateLimit is [{}]", autoDownGradeKey, rateLimitInSecond, rateLimit);
if (persionalRateLimitService.isPersionalRateLimit(rateLimitKey, rateLimit)) {
DOWNGRADE.error("PersionalRateLimit happen ,rateLimitKey is [{}],rateLimit is [{}]", rateLimitKey, this.getRateLimitInfo(rateLimit));
yohoHttpServletRequestWrapper.addParams("uid", new String[] { "0" });
return true;
}
return false;
}
private String getAutoDownGradeKey(ProceedingJoinPoint pjp) {
private String getRateLimitInfo(PersionalRateLimit rateLimit) {
StringBuilder sb = new StringBuilder();
sb.append(rateLimit.limit()).append("c/").append(rateLimit.second()).append("s");
return sb.toString();
}
/**
* @controller层则获取url名称
* @service层则获取方法名称
* @param pjp
* @return
*/
private String getPersionalRateLimitKey(ProceedingJoinPoint pjp) {
// 1、获取method和class
Method targetMethod = ((MethodSignature) (pjp.getSignature())).getMethod();
Class<?> clazz = targetMethod.getDeclaringClass();
... ...
package com.yoho.search.common.downgrade.persional;
public class PersionalRateLimitConfig {
private int limit;
private int second;
public PersionalRateLimitConfig(){
}
public PersionalRateLimitConfig(PersionalRateLimit rateLimit) {
super();
this.limit = rateLimit.limit();
this.second = rateLimit.second();
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
public int getSecond() {
return second;
}
public void setSecond(int second) {
this.second = second;
}
}
... ...
package com.yoho.search.common.downgrade.persional;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.yoho.core.config.ConfigReader;
import com.yoho.search.common.cache.impls.SearchRedis;
@Service
public class PersionalRateLimitService {
@Autowired
private SearchRedis searchRedis;
@Autowired
private ConfigReader configReader;
public boolean isPersionalRateLimit(String rateLimitName, PersionalRateLimit persionalRateLimit) {
String rateLimitKey = this.getRateLimitKey(rateLimitName);
PersionalRateLimitConfig limitConfig = this.getPersionalRateLimitConfig(rateLimitKey, persionalRateLimit);
return this.isRateLimit(rateLimitKey, limitConfig);
}
private String getRateLimitKey(String rateLimitName) {
return "yohosearch.ratelimit:" + rateLimitName.replaceAll("/", "_");
}
private PersionalRateLimitConfig getPersionalRateLimitConfig(String rateLimitKey, PersionalRateLimit rateLimit) {
PersionalRateLimitConfig defaultConfig = new PersionalRateLimitConfig(rateLimit);
String configJsonString = configReader.getString(rateLimitKey, JSON.toJSONString(defaultConfig));
return JSON.toJavaObject(JSON.parseObject(configJsonString), PersionalRateLimitConfig.class);
}
private boolean isRateLimit(String rateLimitKey, PersionalRateLimitConfig limitConfig) {
if (!searchRedis.searchRedisTemplate.hasKey(rateLimitKey)) {
// 如果已经被初始化过, 则不用再初始化, 因为其他线程可能已经进行了 increase, 所以必须使用 setIfAbsent
searchRedis.searchValueOperations.setIfAbsent(rateLimitKey, "0");
// 设置有效期, redis所有的key, 必须设置有效期
searchRedis.searchRedisTemplate.longExpire(rateLimitKey, limitConfig.getSecond(), TimeUnit.SECONDS);
}
// 原子性对值修改delta数值
return searchRedis.searchValueOperations.increment(rateLimitKey, 1).intValue() > limitConfig.getLimit();
}
}
... ...
... ... @@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSONObject;
import com.yoho.search.common.downgrade.persional.AutoDownGradePersional;
import com.yoho.search.common.downgrade.persional.PersionalRateLimit;
import com.yoho.search.common.utils.HttpServletRequestUtils;
import com.yoho.search.models.SearchApiResult;
import com.yoho.search.service.service.IProductListService;
... ... @@ -29,7 +29,7 @@ public class ProductListController {
*
* @return
*/
@AutoDownGradePersional(rateLimitInSecond = 10, rateLimit = 100)
@PersionalRateLimit(limit = 100, second = 10)
@RequestMapping(method = RequestMethod.GET, value = "/productindex/productList")
@ResponseBody
public SearchApiResult productList(HttpServletRequest request) {
... ... @@ -38,10 +38,11 @@ public class ProductListController {
}
/**
* 获取商品列表,返回product_skn
* 获取商品列表[返回phrase字段]
*
* @return
*/
@PersionalRateLimit(limit = 100, second = 10)
@RequestMapping(method = RequestMethod.GET, value = "/productindex/productListWithPhrase")
@ResponseBody
public SearchApiResult productListWithPhrase(HttpServletRequest request) {
... ... @@ -54,6 +55,7 @@ public class ProductListController {
*
* @return
*/
@PersionalRateLimit(limit = 100, second = 10)
@RequestMapping(method = RequestMethod.POST, value = "/productindex/productListByPost")
@ResponseBody
public SearchApiResult productListByPost(@RequestBody JSONObject param) {
... ...