類(lèi):BccSoaHelper
public startLockOnlyOne() throws Exception{
String billCacheKey = createCacheKey(billId);
try {
// 第一層控制:手機(jī)號(hào)碼
addKeyAndValue2AllServer(billCacheKey, billCacheKey, expireSeconds);
} catch (BusinessConcurrentControlException e) {
ExceptionUtil.throwBusinessException(BsI18nResource.SOS11120180, billId);
}
}
這個(gè)方法在下面的一個(gè)場(chǎng)景里面執(zhí)行不會(huì)有問(wèn)題,都可以很好的處理兩個(gè)業(yè)務(wù)對(duì)同一個(gè)號(hào)碼互斥執(zhí)行的需求,如果兩個(gè)方法并發(fā)執(zhí)行,都進(jìn)入到了臨界區(qū),那么會(huì)爭(zhēng)用該全局鎖,最后只有一個(gè)方法會(huì)執(zhí)行成功;另外一個(gè)方法會(huì)直接拋異常,事務(wù)回滾,告知調(diào)用方此時(shí)有另外一個(gè)業(yè)務(wù)在執(zhí)行,暫時(shí)不能執(zhí)行當(dāng)前業(yè)務(wù)
public method1OnSv1() throws Exception{
...臨界區(qū)
BccSoaHelper.startLockOnlyOne();
...
}
public method2OnSv2() throws Exception{
...臨界區(qū)
BccSoaHelper.startLockOnlyOne();
...
}
假如某個(gè)SV層同時(shí)調(diào)用了兩次這個(gè)方法,這個(gè)SV的代碼會(huì)因?yàn)閽伋霎惓;貪L,此時(shí)我們可以說(shuō)這個(gè)鎖是不可重入
public method1OnSv1() throws Exception{
...臨界區(qū)
...假如某個(gè)邏輯里面執(zhí)行了第一次訂單受理
//
BccSoaHelper.startLockOnlyOne();
...假如某個(gè)邏輯里面執(zhí)行了第二次訂單受理,必拋異常
//
BccSoaHelper.startLockOnlyOne();
...
}
所以要對(duì)其再進(jìn)行再一步的加深區(qū)分,使其可以重入;一般來(lái)說(shuō),出現(xiàn)上面所說(shuō)的不可重入的情況的,都應(yīng)該是在“同一個(gè)方法”執(zhí)行過(guò)程中調(diào)用了兩次該鎖,而我們的SOA里面的SV的調(diào)用應(yīng)該就是與該“同一個(gè)方法”類(lèi)似,在這個(gè)SOA調(diào)用里面因?yàn)榭赡苷{(diào)用不同的中心不同的業(yè)務(wù)SV,而這個(gè)業(yè)務(wù)SV是否調(diào)用了該全局鎖是不清楚的,所以有可能會(huì)因?yàn)樵撴i在同一個(gè)SOA的SV里面不可重入導(dǎo)致事務(wù)回滾,所以要實(shí)現(xiàn)同一個(gè)SOA調(diào)用里面的可重入。
然后巧妙的使用session管理器實(shí)現(xiàn)“同一個(gè)SOA”里面的可重入,因?yàn)閟ession的生命周期剛好就貫穿整個(gè)SOA調(diào)用,使用其保存一些額外信息來(lái)區(qū)分是否是同一個(gè)SOA里面的調(diào)用正好不過(guò),如果判斷是同一個(gè)SOA第二次或者更多的對(duì)鎖的調(diào)用,那么就不拋出異常,這樣就可以實(shí)現(xiàn)可重入了,下面是完整的代碼
public static void beginBusiCtrlForJX(String billId, long mainBusinessId, short expireSeconds) throws Exception {
String billCacheKey = createCacheKey(billId);
String soaInvokeCacheKey = createCacheKey(getSoaInvokeBccUniqueIndex(billId));
try {
// 第一層控制:手機(jī)號(hào)碼
addKeyAndValue2AllServer(billCacheKey, billCacheKey, expireSeconds);
} catch (BusinessConcurrentControlException e) {
// 會(huì)進(jìn)入此分支,代表已經(jīng)通過(guò)了BillId進(jìn)行了并發(fā)控制
try {
addKeyAndValue2AllServer(soaInvokeCacheKey, soaInvokeCacheKey, expireSeconds);
} catch (BusinessConcurrentControlException e2) {// 如果此時(shí)拋異常,說(shuō)明是單一SOA服務(wù)調(diào)用,同一session的同一個(gè)的第二次SOA調(diào)用也會(huì)在這里
log.info("<<<<<<<<<<<<<<<<<<<<<單一SOA服務(wù)調(diào)用, 屬于正常場(chǎng)景>>>>>>>>>>>>>>>>>>>>>");
return;
}
try {
BccUtil.delete(soaInvokeCacheKey);
} catch (Exception e2) {
log.error("<<<<<<<<<<<<<<<<<<<<<用戶(hù)正在受理其他訂單類(lèi)業(yè)務(wù),請(qǐng)稍后再試!>>>>>>>>>>>>>>>>>>>>>");
}
ExceptionUtil.throwBusinessException(BsI18nResource.SOS11120180, billId);
} catch (Exception e) {
log.error(ExceptUtil.getExceptionDtl(e));
}
try {
addKeyAndValue2AllServer(soaInvokeCacheKey, soaInvokeCacheKey, expireSeconds);
} catch (Exception e) {
log.error("<<<<<<<<<<<<<<<<<<<<<并發(fā)控制失敗>>>>>>>>>>>>>>>>>>>>>");
}
}
/**
* 獲取Soa服務(wù)調(diào)用對(duì)應(yīng)的BCC并發(fā)控制唯一索引
*
* @param billId
* @return
* @throws Exception
*/
private static String getSoaInvokeBccUniqueIndex(String billId) throws Exception {
Object bccUniqueIndexObject = SessionManager.getUser().get(BCC_UNIQUE_INDEX);
String bccUniqueIndex = StringUtils.EMPTY;
if (null == bccUniqueIndexObject) {
bccUniqueIndex = generateBccUniqueIndex(billId);// 緩存中沒(méi)有,則生成一條唯一索引值
SessionManager.getUser().set(BCC_UNIQUE_INDEX, bccUniqueIndex);
} else {
bccUniqueIndex = (String) bccUniqueIndexObject;
}
return bccUniqueIndex;
}
備注:session必須是全局共享的或者是每次都可以通過(guò)負(fù)載均衡到同一個(gè)web容器的,同時(shí),如果memcached的異常則此鎖會(huì)直接拋異常