分布式异步消息框架构建笔记5——如何避开并行编程中的数据共享陷阱

来源:互联网 发布:店铺宝贝优化 编辑:程序博客网 时间:2024/05/17 11:34

任何多线程/并行/分布式都会面临一个问题,“数据状态共享"。

有经验的开发者会说,要想正确有效的避开避开状态共享,那么就应该别用任何状态共享

虽然不得不说,这是一个不错的建议,但是没有状态共享,你需要如何才能知道非本地数据的状态?

也许你会说使用消息,使用消息来处理,那么我们丑陋的回调金字塔应该叠的更高了。

不得不说这是一个解决办法,但是为了保持状态不被修改,那么我们还得在远程申请一个写入锁,防止数据被别的任务所修改。

那么流程就是 申请锁-》请求某个消息状态-》释放锁


假设我们一个简单的交易任务,服务A上的玩家1准备和服务器B上的玩家2准备交易。

玩家A需要付出100个金币,玩家B需要把背包里的一把刀给玩家A。


非分布式简单非常简单


if(玩家A.金币>100 && 玩家B.拥有(刀)){

玩家A.金币-=100;

玩家A.获得(刀);

        玩家B.失去(刀);

}


防止逻辑失败,如果有个事务出错后回滚一下,这个逻辑就完美了,简单有效.


那么如果是分布式这就显得比较麻烦了, 试着写一个同步办法,远程调用比较费时间,但是至少能正常工作.


sv1.锁定(玩家A)

sv2.锁定(玩家B)

if(sv1.玩家A.金币>100 && sv2.玩家B.拥有(刀)){

sv1.玩家A.金币-=100;

sv2.玩家A.获得(刀);

        sv3.玩家B.失去(刀);

}

sv1.释放(玩家A)

sv2.释放(玩家B)


这个时候应该请出异步来优化这次调用了..


sv1.锁定(玩家A,(){

sv2.锁定(玩家B,(){

if(sv1.玩家A.金币>100 && sv2.玩家B.拥有(刀)){

sv1.玩家A.金币(-100,(){

sv2.玩家A.获得(刀,(){

sv3.玩家B.失去(刀,(){

sv1.释放(玩家A,(){

sv2.释放(玩家B,(){


});

});

);

});

});

}

})

});

我好想确定自己是不是来搞笑的..


如果保持之前设想的逻辑:

1.所有对单个对象执行都是单线程的.(数据访问唯一)

2.当对象不在本地,提交到远程执行,并返回结果.

3.获得结果后,继续在本地执行


交易发起会在玩家A这方发起,我们克隆一个玩家B的镜像,并且标记一个有效时间.
所有的修改都有操作标记.然后在玩家A和玩家B的逻辑都在玩家A线程上执行.

但是,因为我们操作的镜像,并不影响原有数据.

操作完逻辑后,并不算成功,我们需要获得操作完成后玩家A/B与交易之前的差异数据

如何获得参考之前的一篇文章,然后到了我们真正要写入的时候.

玩家A置等待锁(也就是暂时不执行别的请求),远程请求玩家B的写入.

完成后,玩家A写入. 释放等待锁.


写入瞬间有一个防误操作的锁,其他时间都不影响其他任何操作.


如果远程服务器在交易发起的瞬间被,修改,那么写入失败,只要丢弃双方差异数据并释放锁即可.


从以上逻辑我们可以看出

1.数据访问唯一让我们无需担心执行过程中被修改,也就是我们可以推算出任何非多步的操作都是成功的.(方法中不包含远程方法,必然会执行成功)
2.线程切换机制,保证我们在编写代码时运行位置的正确性,因为你无需判断是否需要远程执行,仅仅都通过代理调用即可.

3.除非事务,保证双方的操作必须成功,否则,无需任何锁,也就是安全的跨服操作.






0 0
原创粉丝点击