1. 程式人生 > >問題解決系列: 後臺服務流量控制- 控制訪問別的服務的速度

問題解決系列: 後臺服務流量控制- 控制訪問別的服務的速度

發送 template 個人 exce 保護 rms 這一 ole 每分鐘

互聯網的後臺提倡大系統小做,微服務化。所以後臺服務之間相互依賴,我依賴別人的,別人也依賴我的,這很正常。

但是後臺服務講穩定性。只有一切可控,才能談穩定性。

為了不沖垮下遊的服務,我們有兩種做法:一種是下遊服務做一個自我保護(具體實現方法下次再寫),一種是上遊保護下遊。

比如A服務向B服務發送消息,B給A分配了每分鐘3000條消息的訪問量。那麽A如何控制自己每分鐘的訪問量在3000次以內呢?

基本思路:

這是個分布式的問題,A服務可能包含了墮胎機器,所有的機器共享一個設定的配額 3000次/每分。

借助redis來管理配額。

每次A向B發起請求時,先檢查一下配額夠不夠。不夠就延時等待,使用下一分鐘的配額。

既然使用了redis, 還要考慮到redis服務掛了的情況,也要能正常發送消息。

如果Redis掛了,作為應急方案,可以使用簡單睡眠的方法來控制速度。

/**

* 以分鐘為單位,每分鐘發送的消息數控制在指定範圍內, 缺省 每分鐘 3000條消息/分鐘。 * 如果返回false, 對於調用者來說,需要睡眠一段時間再次檢查是否可以發送消息。 * */ private boolean allowSendMsg(int msgNum) { boolean allowSendMsgRet = true; long curMinute = new Date().getTime()/60000;

//在redis中查找當前這一分鐘的配額

String key = "msg_flowctl_" + String.valueOf(curMinute); boolean redisException = false; try { if (redis.exists(key)) { long remainingMsgNum = redis.decrBy(key, msgNum); if (remainingMsgNum <= 0) { /**當前這分鐘的額度用完了*/ allowSendMsgRet = false; } } else {

//這一分鐘的配額在redis還沒有,那麽進行初次設置。如果一次要求的已經大於3000。 這裏采取了比較寬松的策略,就是允許它過。

//其實還可以要求調用方對請求進行拆分。

long remainingMsgNum = msgNum > 3000? 0 : 3000 - msgNum; redisTemplate.getJedisCmd().set(key, String.valueOf(remainingMsgNum)); redisTemplate.getJedisCmd().expire(key, 60); } } catch (Exception e) {

//redis不可靠要記下來

redisException = true; } if (redisException) { /**redis 失效以後的流控應急方案。 * * 通過睡眠線程來限制發往企信後臺的速度. * 3000個人(消息)/分鐘,算出來每條消息要睡眠20ms. * 假設當前A服務總共有兩條服務器,因此每條消息需要睡眠40ms. * * 這種方法簡單粗暴而有效,只能保證單臺服務器 發送1500條消息到B服務,時間段肯定是超過一分鐘的。 * 缺點:(1) 時間粒度太粗。 * (2) 不易運維。需要代碼結合部署的服務器數量。 * 比如代碼轉手幾次以後,擴容機器。擴容的同學沒有意識到,發往B服務的速度變快了。 * */ try { Thread.sleep(msgNum * sleepMiniSecPerMsg); } catch (Exception e) {} } return allowSendMsgRet; }

問題解決系列: 後臺服務流量控制- 控制訪問別的服務的速度