-
緣起
業(yè)務(wù)邏輯為M服務(wù)器向中央服務(wù)器C上報(bào)數(shù)據(jù),每條數(shù)據(jù)使用uid標(biāo)識(shí)磕昼,即M服務(wù)器-->uid數(shù)據(jù)-->C服務(wù)器£蹋基于超時(shí)重發(fā)機(jī)制沐批,M服務(wù)器可能會(huì)向C服務(wù)器發(fā)送重復(fù)數(shù)據(jù)蝎亚,故C服務(wù)器需要做去重處理。 -
問(wèn)題分析
脫離業(yè)務(wù)場(chǎng)景发框,這就是一個(gè)并發(fā)訪問(wèn)的問(wèn)題,這也是解決此問(wèn)題的出發(fā)點(diǎn)梅惯。解決并發(fā)問(wèn)題的核心思想就是將異步控制為同步,想要對(duì)代碼邏輯做同步控制就要使用同步機(jī)制她君,通常使用的就是鎖機(jī)制了。而鎖機(jī)制的本質(zhì)就是保證代碼邏輯執(zhí)行的原子性缔刹,即對(duì)于同一條數(shù)據(jù)的入庫(kù)請(qǐng)求判斷劣针,要做重復(fù)判斷,進(jìn)而解決并發(fā)請(qǐng)求的問(wèn)題捺典。具體到此業(yè)務(wù),對(duì)于同一個(gè)uid標(biāo)識(shí)的數(shù)據(jù)襟己,只入庫(kù)一條。故問(wèn)題轉(zhuǎn)換過(guò)程為:
重復(fù)數(shù)據(jù)上傳 --> 并發(fā)訪問(wèn)去重 --> 同步機(jī)制控制代碼訪問(wèn)邏輯 --> 鎖機(jī)制/同步塊/redis計(jì)數(shù)器 - 問(wèn)題解決
- 基于redis計(jì)數(shù)器并發(fā)控制
- 利用rdis的原子性操作煌张,就是當(dāng)一個(gè)請(qǐng)求在操作某個(gè)key時(shí)呐赡,其它操作同一個(gè)key的請(qǐng)求必須等待,即利用原子性實(shí)現(xiàn)了代碼控制
- 每次請(qǐng)求進(jìn)來(lái)都會(huì)在對(duì)應(yīng)kye上加1链嘀,只有第一次請(qǐng)求是1,故一分鐘以?xún)?nèi)的其它重復(fù)請(qǐng)求怀泊,不做業(yè)務(wù)處理
public boolean filterRepeatMessage(MqttMessage message) {
boolean needContinueHandle = false;
String key = "MSG_" + message.getData().getString("uid");
long count = redisTemplate.opsForValue().increment(key, 1);
if (count == 1) {
redisTemplate.expire(key, 60, TimeUnit.SECONDS);
needContinueHandle = true;
}
//日志記錄
return needContinueHandle;
}
- 基于同步方法的并發(fā)控制
- 所有請(qǐng)求同步,效率低
public synchronized boolean filterRepeatMessage(MqttMessage message) {
boolean needContinueHandle = false;
//查詢(xún)數(shù)據(jù)庫(kù)中是否有重復(fù)記錄
if (has) {
needContinueHandle = true;
}
//日志記錄
return needContinueHandle;
}
- 基于uid分段鎖的并發(fā)控制
- 所有請(qǐng)求同步务傲,效率低
private static ConcurrentHashMap<Long, Byte[]> lockerStore = new ConcurrentHashMap<Long, Byte[]>();
private static Object getLock(String uid) {
lockerStore.putIfAbsent(uid, new Byte[]{});
Byte[] ret = lockerStore.get(phone);
return ret;
}
public boolean filterRepeatMessage(MqttMessage message) {
boolean needContinueHandle = false;
synchronized(getLock( message.getData().getString("uid"))){
//查詢(xún)數(shù)據(jù)庫(kù)中是否有重復(fù)記錄
if (has) {
needContinueHandlae = true;
}
}
//日志記錄
return needContinueHandle;
}
PS:
實(shí)際中,可根據(jù)業(yè)務(wù)需求使用緩存來(lái)替代數(shù)據(jù)庫(kù)查詢(xún)看杭。綜合比對(duì),使用redis計(jì)數(shù)器解決并發(fā)請(qǐng)求去重的方案效率更高楼雹,使用更方便,推薦使用贮缅。