1.订购调用下游的Dubbo请求,为了防止击溃下游的服务,需要在上游设置订购限流。防止下游服务崩溃、
直接上代码:
private void sendFluxLimitingCtrl() {
Integer tps;
tps = Integer.parseInt(CommonHelper.getParamByMask(SmartConstants.OFFER_MANAGEMENT_FLUX_LIMIT, "600"));
if (tps == null) {
// 没配置
tps = -1;
}
if (tps <= 0) {
logger.info("{} not configed flowsize", SmartConstants.OFFER_MANAGEMENT_FLUX_LIMIT);
return;
}
Calendar c = Calendar.getInstance();
int milliSec = c.get(Calendar.MILLISECOND);
String key = SmartConstants.OFFER_MANAGEMENT_FLUX_LIMIT + "_" + getCurrentStamp();
// 原子型的操作
int times = RedisUtil.incr(RedisUtil.FUNTYPE0, key, 1, 2L);
logger.info("key: 0$MCCM_MCCM$1000${},times: {}", key, times);
if (times <= tps) {
logger.debug("OFFER_MANAGEMENT_FLUX_LIMIT not limit...");
}
else {
logger.debug("OFFER_MANAGEMENT_FLUX_LIMITlimit...");
try {
//等待直到1s之后。
Thread.sleep(1001 - milliSec);
sendFluxLimitingCtrl();
}
catch (InterruptedException e) {
logger.error(SmartConstants.OFFER_MANAGEMENT_FLUX_LIMIT, e);
}
}
}
/**
* Description:获取当前秒数 <br/>
*
* @author Li.xiaolong<br/>
* @taskId <br/>
* @return <br/>
*/
private String getCurrentStamp() {
return String.valueOf(System.currentTimeMillis() / 1000);
}
2.对象锁实现限流(使用线程安全的ConcurrentMap实现)
(1)首先创建一个工厂 限流工厂 用来区别不同类型的订购限流方式。返回对应的实例。new一个新的实例。
public final class FlowControlFactory {
/**
* flowCtrlMap
*/
private static ConcurrentMap<String, IFlowController> flowCtrlMap = new ConcurrentHashMap<>();
/**
* FlowControlMgr
*/
private FlowControlFactory() {
}
/**
* Description: 获取实例 接触渠道的 <br>
*
* @author Li.xiaolong <br>
* @taskId <br>
* @param triggerType 接触渠道
* @param scene 批量/事件
* @return <br>
*/
public static IFlowController getControllerInstance(String triggerType, String scene) {
String key = triggerType + "_" + scene;
IFlowController instance = flowCtrlMap.get(key);
if (null == instance) {
instance = createCtrlInstance(triggerType, scene);
flowCtrlMap.put(key, instance);
}
return instance;
}
/**
* Description: 创建实例
*
* @author wei.min<br>
* @taskId 926230 <br>
* @param triggerType 触发类型
* @param scene 触发方式
* @return <br>
*/
private static IFlowController createCtrlInstance(String triggerType, String scene) {
IFlowController instance = null;
if (CmsTypeDef.TRIGGER_TYPE_OFFER.equals(triggerType)) {
if (CmsTypeDef.TRIGGER_SCENE_BATCH.equals(scene)) {.
//OFFER类型
instance = new FlowControllerOrder(scene);
}
else if (CmsTypeDef.TRIGGER_TYPE_PRESENT.equals(scene)) {
//PRESENT类型 礼物
instance = new FlowControllerOfferManagement();
}
else {
instance = new FlowControllerOrder(scene);
}
}
else if (CmsTypeDef.RECOMMEND_TYPE.equals(triggerType)) {
if (CmsTypeDef.RECOMMEND_TYPE_SCENE.equals(scene)) {
//RECOMMEND 推荐商品
instance = new FlowControllerRecommend();
}
}
else {
instance = new FlowControllerAdvice(triggerType, scene);
}
return instance;
}
public void sendFluxLimiting(String type) {
if (tps == -1) {
return;
}
synchronized (fluxMutex) {
Calendar c = Calendar.getInstance();
int milliSec = c.get(Calendar.MILLISECOND);
String currentDate = getCurrentStamp(c);
int currCount = 0;
if (fluxCounter.get(currentDate) != null) {
currCount = fluxCounter.get(currentDate).intValue();
}
currCount++;
if (currCount > tps) {
try {
logger.info(
"Flow Control, Waiting for the number of milliseconds : {}. "
+ " Current send speed : {}, Max Speed :{}. Current time :{}.",
(1001 - milliSec), currCount, tps, c.getTime().toString());
fluxMutex.wait(1001 - milliSec);
logger.info("Flow Control. Sleeped {} milliSec", 1001 - milliSec);
currCount = 1;
c = Calendar.getInstance();
currentDate = getCurrentStamp(c);
}
catch (InterruptedException e) {
logger.error(ErrorValues.SMART_ERROR, e);
}
}
else {
// donothing
}
fluxCounter.clear();
fluxCounter.put(currentDate, Integer.valueOf(currCount));
logger.debug("trigger_type = {}, currCount={}", controllerType, currCount);
}
}
/**
* Description:获取当前秒数 <br/>
*
* @author Li.xiaolong<br/>
* @taskId <br/>
* @param c <br/>
* @return <br/>
*/
private String getCurrentStamp(Calendar c) {
int hour = c.get(Calendar.HOUR);
int minute = c.get(Calendar.MINUTE);
int second = c.get(Calendar.SECOND);
int cachePos = hour * 3600 + minute * 60 + second;
return String.valueOf(cachePos);
}
限流接口
public interface IFlowController {
/**
* Description: 发送限制<br>
*
* @author wei.min<br>
* @taskId 926230<br>
* @param type <br>
*/
void sendFluxLimiting(String type);
}
下面看每个限流接口实现类。针对不同场景实现不同。
FlowControllerOrder OFFER类型
public class FlowControllerOrder implements IFlowController {
/**
* ZSmartLogger
*/
ZSmartLogger logger = ZSmartLogger.getLogger(FlowControllerOrder.class);
/**
* 流量控制,保存每种类型的限制发送量
*/
private Map<String, Integer> tps = new HashMap<String, Integer>();
/**
* 流量控制缓存,保存每种类型的发送速度
*/
private Map<String, CurrentDateLastValuePair> fluxCounter = new ConcurrentHashMap<String, CurrentDateLastValuePair>();
/**
* 流量控制器类型
*/
private String controllerType;
/** 流量控制的offer分类 */
private List<String> offerTypeList = new ArrayList<String>();
/**
* hashCode
*/
private int hashCode;
/**
* Description: 私有构造函数,根据类型和场景实例化流量控制器对象 <br>
*
* @param scene <br>
*/
public FlowControllerOrder(String scene) {
try {
tps = getFluxLimit();
controllerType = scene + "_" + CmsTypeDef.TRIGGER_TYPE_OFFER;
hashCode = this.hashCode();
}
catch (Exception e) {
logger.error(ErrorValues.SMART_ERROR, e);
}
}
/**
* Description: <br>
*
* @author wei.min <br>
* @taskId 926230<br>
* @param scene 使用场景 BATCH/EVENT
* @return <br>
*/
private Map<String, Integer> getFluxLimit() {
String offerFluxLimit = "10000"; // 修改 锁并发 订购接口 100
// offer的流量分类型限制,配置例子 :200|2_4_3_5:100|B_C:500 表示2,3,4,5四种类型共用100,B和C类型共用500,其他类型的用200
/*
* if (CmsTypeDef.TRIGGER_SCENE_BATCH.equals(scene)) { offerFluxLimit =
* CommonHelper.getParamByMask("OFFER_FLUX_LIMIT_BATCH", "200|2_4:100|B_C:500|3:100"); } else { offerFluxLimit =
* CommonHelper.getParamByMask("OFFER_FLUX_LIMIT_EVENT", "100|2_4:100|B_C:300|3_5:100"); }
*/
// offer的 根据type区分一下
String[] arrOffer = offerFluxLimit.split("\\|");
for (String offerLimit : arrOffer) {
String[] limit = offerLimit.split(":");
if (limit.length == 2) {
tps.put(limit[0], Integer.valueOf(limit[1]));
offerTypeList.add(limit[0]);
}
else {
tps.put("NO_TYPE", Integer.valueOf(limit[0]));
}
}
return tps;
}
@Override
public void sendFluxLimiting(String offerType) {
// 根据offerType 取流量控制归属的TYPE
String offerFluxType = getFluxType(offerType);
CurrentDateLastValuePair currentDatelastValue = fluxCounter.get(offerFluxType);
if (currentDatelastValue == null) {
currentDatelastValue = syncPutNewCurrentDatelastValue(offerFluxType);
}
synchronized (currentDatelastValue) {
Calendar c = Calendar.getInstance();
int milliSec = c.get(Calendar.MILLISECOND);
String currentDate = getCurrentStamp(c);
int currCount = 0;
if (currentDate.equals(currentDatelastValue.getCurrentDate())) {
// 时间未变,取
currCount = currentDatelastValue.getLastValue();
}
currCount++;
if (currCount > tps.get(offerFluxType)) {
try {
logger.info(
"Flow Control, Waiting for the number of milliseconds : {}. "
+ " Current send speed : {}, Max Speed :{}. Current time :{}.",
(1001 - milliSec), currCount, tps.get(offerFluxType), c.getTime().toString());
currentDatelastValue.wait(1001 - milliSec);
logger.info("Flow Control. Sleeped {} milliSec", 1001 - milliSec);
currCount = 1;
c = Calendar.getInstance();
currentDate = getCurrentStamp(c);
}
catch (InterruptedException e) {
logger.error(ErrorValues.SMART_ERROR, e);
}
}
currentDatelastValue.setLastValue(currCount);
currentDatelastValue.setCurrentDate(currentDate);
logger.debug("offer_type = {} in {}, currCount={}", offerType, offerFluxType, currCount);
}
}
/**
* Description: <br>
*
* @author zou.hang <br>
* @taskId <br>
* @param offerFluxType <br>
* @return <br>
*/
private synchronized CurrentDateLastValuePair syncPutNewCurrentDatelastValue(String offerFluxType) {
CurrentDateLastValuePair currentDatelastValue = fluxCounter.get(offerFluxType);
if (currentDatelastValue == null) {
currentDatelastValue = new CurrentDateLastValuePair();
Calendar c = Calendar.getInstance();
String currentDate = getCurrentStamp(c);
currentDatelastValue.setCurrentDate(currentDate);
currentDatelastValue.setLastValue(0);
fluxCounter.put(offerFluxType, currentDatelastValue);
}
return currentDatelastValue;
}
/**
* Description: offerType 取流量控制归属的TYPE <br>
*
* @author wei.min<br>
* @taskId <br>
* @param offerType offer 类型
* @return <br>
*/
private String getFluxType(String offerType) {
String fluxType = "NO_TYPE";
String typeStr = "_" + offerType + "_";
for (String types : offerTypeList) {
String fluxTypeStr = "_" + types + "_";
if (fluxTypeStr.indexOf(typeStr) >= 0) {
fluxType = types;
break;
}
}
return fluxType;
}
/**
* Description:获取当前秒数 <br>
*
* @author Li.xiaolong<br>
* @taskId <br>
* @param c <br>
* @return <br>
*/
private String getCurrentStamp(Calendar c) {
int hour = c.get(Calendar.HOUR);
int minute = c.get(Calendar.MINUTE);
int second = c.get(Calendar.SECOND);
int cachePos = hour * 3600 + minute * 60 + second;
return String.valueOf(cachePos);
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("FluxController type = ").append(controllerType).append(" \n");
sb.append("Tps = ").append(tps).append(" \n");
sb.append("HashCode = ").append(hashCode).append(" \n");
sb.append("Detail = ").append(fluxCounter.toString()).append(" \n");
return sb.toString();
}
}
public class FlowControllerRecommend implements IFlowController {
/**
* ZSmartLogger
*/
ZSmartLogger logger = ZSmartLogger.getLogger(FlowControllerRecommend.class);
@Override
public void sendFluxLimiting(String channelId) {
sendFluxLimitingCtrl();
}
private void sendFluxLimitingCtrl() {
Integer tps;
tps = Integer.parseInt(CommonHelper.getParamByMask(SmartConstants.IRE_RECOMMEND_FLUX_LIMIT, "600"));
if (tps == null) {
// 没配置
tps = -1;
}
if (tps <= 0) {
logger.info("{} not configed flowsize", SmartConstants.IRE_RECOMMEND_FLUX_LIMIT);
return;
}
Calendar c = Calendar.getInstance();
int milliSec = c.get(Calendar.MILLISECOND);
String key = SmartConstants.IRE_RECOMMEND_FLUX_LIMIT + "_" + getCurrentStamp();
logger.info("key:0$MCCM_MCCM$1000${}", key);
int times = RedisUtil.incr(RedisUtil.FUNTYPE0, key, 1, 2L);
if (times <= tps) {
// success
logger.debug("sendFluxLimiting not limit...");
}
else {
logger.debug("sendFluxLimiting limit...");
try {
Thread.sleep(1001 - milliSec);
sendFluxLimitingCtrl();
}
catch (InterruptedException e) {
logger.error(SmartConstants.IRE_RECOMMEND_FLUX_LIMIT, e);
}
}
}
/**
* Description:获取当前秒数 <br/>
*
* @author Li.xiaolong<br/>
* @taskId <br/>
* @return <br/>
*/
private String getCurrentStamp() {
return String.valueOf(System.currentTimeMillis() / 1000);
}
}
public class FlowControllerAdvice implements IFlowController {
/**
* ZSmartLogger
*/
ZSmartLogger logger = ZSmartLogger.getLogger(FlowControllerAdvice.class);
/**
* 流量控制,限制发送量
*/
private int tps = 0;
/**
* 流量控制缓存,保存每秒的发送速度
*/
private Map<String, Integer> fluxCounter;
/**
* 对象锁
*/
private Object fluxMutex;
/**
* 流量控制器类型
*/
private String controllerType;
/**
* hashCode
*/
private int hashCode;
/**
* Description: 私有构造函数,根据类型和场景实例化流量控制器对象 <br/>
*
* @param triggerType <br/>
* @param scene <br/>
*/
public FlowControllerAdvice(String triggerType, String scene) {
try {
fluxCounter = new HashMap<String, Integer>();
fluxMutex = new Object();
tps = getFluxLimit(triggerType, scene);
controllerType = scene + "_" + triggerType;
hashCode = this.hashCode();
}
catch (Exception e) {
logger.error(ErrorValues.SMART_ERROR, e);
}
}
/**
* Description: <br/>
*
* @author wei.min<br/>
* @taskId 926230<br/>
* @param triggerType 业务类型
* @param scene 批量/事件
* @return int
*/
private int getFluxLimit(String triggerType, String scene) {
tps = -1;
if (CmsTypeDef.TRIGGER_SCENE_BATCH.equals(scene)) {
String batchFluxLimitStr = CommonHelper.getParamByMask("SEND_FLUX_LIMIT_BATCH", "1000|300|300|100|300|1000");
String[] arrBatch = batchFluxLimitStr.split("\\|");
Map<String, Integer> limitMap = getThirdLimitMap();
if (CmsTypeDef.TRIGGER_TYPE_SMS.equals(triggerType)) {
tps = Integer.parseInt(arrBatch[0]);
}
else if (CmsTypeDef.TRIGGER_TYPE_MMS.equals(triggerType)) {
tps = Integer.parseInt(arrBatch[1]);
}
else if (CmsTypeDef.TRIGGER_TYPE_EMAIL.equals(triggerType)) {
tps = Integer.parseInt(arrBatch[2]);
}
// taskId:485441
else if (CmsTypeDef.TRIGGER_TYPE_TWITTER.equals(triggerType)) {
tps = Integer.parseInt(arrBatch[3]);
}
// else if (CmsTypeDef.TRIGGER_TYPE_USSD.equals(triggerType)) {
// tps = Integer.parseInt(arrBatch[4]);
// }
else if (CmsTypeDef.TRIGGER_TYPE_ENOTIFICATION.equals(triggerType)) {
tps = Integer.parseInt(arrBatch[5]);
}
else if (limitMap.containsKey(triggerType)) {
tps = limitMap.get(triggerType);
}
}
else if (CmsTypeDef.TRIGGER_SCENE_EVENT.equals(scene)) {
String eventFluxLimitStr = CommonHelper.getParamByMask("SEND_FLUX_LIMIT_EVENT", "1000");
String[] arrEvent = eventFluxLimitStr.split("\\|");
if (CmsTypeDef.TRIGGER_TYPE_SMS.equals(triggerType)) {
tps = Integer.parseInt(arrEvent[0]);
}
}
return tps;
}
/**
* Description: <br/>
*
* @author chenmengshi<br/>
* @taskId <br/>
*/
private Map<String, Integer> getThirdLimitMap() {
Map<String, Integer> limitMap = new HashMap<String, Integer>();
String batchFluxLimistStrForThird = CommonHelper.getParamByMask("SEND_FLUX_LIMIT_BATCH_FOR_THIRDCHANNEL", "");
if (StringUtil.isEmpty(batchFluxLimistStrForThird.trim())) {
return limitMap;
}
String[] arrBatchForThird = batchFluxLimistStrForThird.split("\\|");
String[] keyValueArr = null;
String key = null;
String value = null;
for (String advice : arrBatchForThird) {
keyValueArr = advice.split("=");
key = keyValueArr[0].trim();
value = keyValueArr[1].trim();
if (StringUtil.isNumeric(value)) {
limitMap.put(key, Integer.parseInt(value));
}
}
return limitMap;
}
@Override
public void sendFluxLimiting(String type) {
if (tps == -1) {
return;
}
synchronized (fluxMutex) {
Calendar c = Calendar.getInstance();
int milliSec = c.get(Calendar.MILLISECOND);
String currentDate = getCurrentStamp(c);
int currCount = 0;
if (fluxCounter.get(currentDate) != null) {
currCount = fluxCounter.get(currentDate).intValue();
}
currCount++;
if (currCount > tps) {
try {
logger.info(
"Flow Control, Waiting for the number of milliseconds : {}. "
+ " Current send speed : {}, Max Speed :{}. Current time :{}.",
(1001 - milliSec), currCount, tps, c.getTime().toString());
fluxMutex.wait(1001 - milliSec);
logger.info("Flow Control. Sleeped {} milliSec", 1001 - milliSec);
currCount = 1;
c = Calendar.getInstance();
currentDate = getCurrentStamp(c);
}
catch (InterruptedException e) {
logger.error(ErrorValues.SMART_ERROR, e);
}
}
else {
// donothing
}
fluxCounter.clear();
fluxCounter.put(currentDate, Integer.valueOf(currCount));
logger.debug("trigger_type = {}, currCount={}", controllerType, currCount);
}
}
/**
* Description:获取当前秒数 <br/>
*
* @author Li.xiaolong<br/>
* @taskId <br/>
* @param c <br/>
* @return <br/>
*/
private String getCurrentStamp(Calendar c) {
int hour = c.get(Calendar.HOUR);
int minute = c.get(Calendar.MINUTE);
int second = c.get(Calendar.SECOND);
int cachePos = hour * 3600 + minute * 60 + second;
return String.valueOf(cachePos);
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("FluxController type = ").append(controllerType).append(" \n");
sb.append("Tps = ").append(tps).append(" \n");
sb.append("HashCode = ").append(hashCode).append(" \n");
sb.append("Detail = ");
// mod by zheng.ran task:861276 多线程时sendFluxLimiting中修改hashmap,读取hashmap会报错
synchronized (fluxMutex) {
sb.append(fluxCounter.toString()).append(" \n");
}
return sb.toString();
}
}
具体业务场景如果调用呢?
IFlowController flow = FlowControlFactory.getControllerInstance(RECOMMEND,
CmsTypeDef.RECOMMEND_TYPE_SCENE);