本文首發于泊浮目的專欄:https://segmentfault.com/blog/camile
在Go語言中,有一個關鍵字叫做defer
——其作用是在函數return前執行。在ZStack中也有類似的工具類,讓我們來看看吧。
演示代碼
private void handle(APICreateInstanceOfferingMsg msg) {
APICreateInstanceOfferingEvent evt = new APICreateInstanceOfferingEvent(msg.getId());
String type = msg.getType() == null ? UserVmInstanceOfferingFactory.type.toString() : msg.getType();
InstanceOfferingFactory f = getInstanceOfferingFactory(type);
InstanceOfferingVO vo = new InstanceOfferingVO();
if (msg.getResourceUuid() != null) {
vo.setUuid(msg.getResourceUuid());
} else {
vo.setUuid(Platform.getUuid());
}
HostAllocatorStrategyType allocType = msg.getAllocatorStrategy() == null ? HostAllocatorStrategyType
.valueOf(HostAllocatorConstant.LEAST_VM_PREFERRED_HOST_ALLOCATOR_STRATEGY_TYPE) : HostAllocatorStrategyType.valueOf(msg.getAllocatorStrategy());
vo.setAllocatorStrategy(allocType.toString());
vo.setName(msg.getName());
vo.setCpuNum(msg.getCpuNum());
vo.setCpuSpeed(msg.getCpuSpeed());
vo.setDescription(msg.getDescription());
vo.setState(InstanceOfferingState.Enabled);
vo.setMemorySize(msg.getMemorySize());
vo.setDuration(InstanceOfferingDuration.Permanent);
vo.setType(type);
InstanceOfferingInventory inv = new SQLBatchWithReturn<InstanceOfferingInventory>() {
@Override
@Deferred
protected InstanceOfferingInventory scripts() {
Defer.guard(() -> dbf.remove(vo));
InstanceOfferingInventory inv = f.createInstanceOffering(vo, msg);
acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), vo.getUuid(), InstanceOfferingVO.class);
tagMgr.createTagsFromAPICreateMessage(msg, vo.getUuid(), InstanceOfferingVO.class.getSimpleName());
return inv;
}
}.execute();
evt.setInventory(inv);
bus.publish(evt);
logger.debug("Successfully added instance offering: " + printer.print(inv));
}
這段代碼是ZStack ManageNode(簡稱MN)在接收到創建計算規格
的請求后的處理邏輯,大致的邏輯即:
- 在DB中創建一條記錄
- 添加
當前賬戶
與該計算規格
的關聯 - 創建相應的
系統標簽
- 回復該消息,并打印一行log
在這段代碼中,我們可以看到在執行邏輯2、3時,這里做了一個Defer.guard(() -> dbf.remove(vo));
,其作用是在執行下面的邏輯發生異常時執行,這樣就移除了臟數據。
如何使用
Defer的使用方法也很簡單,如果你要在某個函數使用它,在上面添加一個 @Deferred
的Annotation,然后就可以在該函數內使用它了。
一般有兩種用法:
- Defer.guard:在該函數拋出異常時執行Runable。
- Defer.defer:在該函數返回前執行。我們可以使用其釋放局部鎖。
為了避免不熟悉ZStack讀者理解起來生澀,建議參考其Case在這里,我們將展現一個較為簡單的case:
public class TestDefer1 {
CLogger logger = Utils.getLogger(TestDefer1.class);
int count = 0;
@Before
public void setUp() throws Exception {
}
@Deferred
private void case1() {
count++;
Defer.guard(new Runnable() { //當捕捉到異常時,執行其中的匿名Runable語句
@Override
public void run() {
count--;
}
});
//拋出一個異常
throw new CloudRuntimeException("Roll back count");
}
@Test(expected = CloudRuntimeException.class)
public void test() {
case1();
Assert.assertEquals(0, count);
}
}
實現
Defer的庫非常的小。其本質通過對Spring提供的AOP和Java提供的ThreadLocal以及一個Stack數據結構進行封裝:對于執行函數的當前線程存入一個Stack數據結構,每一個填寫在Defer中的Runable都會被放入,之后根據調用的Defer的函數來決定其行為。
這里Runable的放入來自系統啟動時利用反射所做的一個行為。因此并不會影響使用時的性能。