使用Redis和对象锁实现限流。(高并发场景下订购)

10 篇文章 0 订阅
6 篇文章 0 订阅
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);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值