美文网首页ZStack博客集
ZStack源码剖析之核心库鉴赏——Defer

ZStack源码剖析之核心库鉴赏——Defer

作者: 泊浮目 | 来源:发表于2017-10-03 20:41 被阅读27次

    本文首发于泊浮目的专栏: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)在接收到创建计算规格的请求后的处理逻辑,大致的逻辑即:

    1. 在DB中创建一条记录
    2. 添加当前账户与该计算规格的关联
    3. 创建相应的系统标签
    4. 回复该消息,并打印一行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的放入来自系统启动时利用反射所做的一个行为。因此并不会影响使用时的性能。

    相关文章

      网友评论

        本文标题:ZStack源码剖析之核心库鉴赏——Defer

        本文链接:https://www.haomeiwen.com/subject/kfrcextx.html