« Skynet 1.0.0 发布 | 返回首页 | 群星的汉化及其它 »

在 skynet 中如何实现多 actor 协作的事务

今天在 qq 群中,有个同学问,在 skynet 中,如果多个 actor 需要协作时,能否有事务来保证一系列操作的过程中,状态不会被破坏。

他举了个例子:

如果有一段业务逻辑是:

local a = skynet.call(A, ...)
local b = skynet.call(B, ...)
if a and b then
  dosomething()
end

如何保证 a and b 这个条件有效呢?也就是说,在第一行从 A 处获取状态后,还要向 B 查询一个状态;如果希望两者查询完毕,一直到 dosomething() 结束前,A 和 B 的状态都不要改变。期间,如果有请求发到了 A 或者 B ,最后都暂时挂起。

后来,又有同学补充了一个更实际一点的案例:

如果我需要买点东西,先从银行查询余额,然后从商店获取一个物品,然后去银行扣掉钱,再把物品加到背包里。这个过程会涉及多个 actor ,整个过程又需要是一个事务,不要在执行这个事务过程中,有任何涉及的 actor 状态改变。

固然,我们肯定可以从设计上找到一个更好的方法处理上面这类交易事件,而不需要涉及复杂的多个 actor 之间的交互。但这也是一个典型的案例说明本文一开始想表达的需求。

如果一系列的操作只涉及一个 actor ,那么在这个 actor 中使用 skynet.queue 就可以避免在处理消息过程中的外部干扰了。但若涉及多个 actor 就会麻烦一些。

我简单思考了一下,似乎还是可以用 skynet.queue 这个模块来解决这个问题。简单做了个实现,供参考。

这里是 github 上的仓库地址

这个事务的模拟有很多限制,其中之一是,任何时候系统中只能有一个事务在处理。(这个限制应该可以去掉,但加上这个限制,可以让这个 example 代码更简单一些)

如果你的 actor 要针对事务处理来响应消息,那么就应该使用 transaction.dispatch 来处理消息,而不是直接用 skynet.dispatch 分发。transaction.dispatch 会回传一个 transaction 对象,如果需要对外请求,应该使用 transaction:call 而不能再使用 skynet.call 。

transaction:call 会把事务的 session 号传递给对方,而每个能处理事务的 actor 每次收到请求后,都会根据 session 号把这个请求加到对应的队列中(由 skynet.queue 管理)。

而每个执行队列的最前面,都会向 transactiond 这个中心服务请求锁。如果当前没有事务在运行,那么回直接返回,从而拿到执行权;如果当前有其它事务在运行,则这个请求会被挂起,继而让整个运行队列都挂起。


这个 example 还有一些缺陷,如果要实际使用需要进一步完善。

比如 transaction 的创建和销毁都是显式的 api 调用(transaction.create 和 transaction:release),如果漏掉了 release 调用,或是中间过程有 error 没有捕获,或是还没等到 release 环节,服务自己退出了,跳过了 release ,都会导致整个事务锁不会被解开。

改进方法是用 pcall 封装事务的 create/release 过程,并在 transactiond 中增加一个监视创建者退出的方法。这个这里就不展开了。

Comments

最近才学习skynet,其实这种需求每个游戏普遍存在,但是游戏跟银行系统又不太一样,我们游戏的简单粗暴的处理是,直接先扣钱,或者商店先扣商品,如果到一方的处理失败,回退操作,这种异步代码看上去会比正常的奇怪一点点,但是相对好用,框架层不支持,要业务逻辑自己写。

最后你只有一个actor,那还异步个啥

不适合用‘我’这个自然锁来处理,这样你的扣钱,加物品的逻辑自然要写在我这个actor上,那么银行和商店的actor还有啥存在的必要吗,他们就只剩下数据了。

云风,请问下lua中我发现lua占用内存在不停增加,那lua 的collectgarbage机制是怎么样。

不错

很有意思,很多复杂的问题,或者说自然表象复杂的问题 都可以通过抽取出一层“本质层”出来予以解决。
transaction 就是“层”的解决。
文中的例子:“如果我需要买点东西,先从银行查询余额,然后从商店获取一个物品,然后去银行扣掉钱,再把物品加到背包里”,看似涉及多个actor,其实都是以“我”为中心,是不是可以换个角度,通过把”我“看作唯一的一个actor设计呢。因为系统中,我是唯一的,我是自然锁。

很多基于异步的服务都有这个问题,一直也没想到什么好办法

这个确切名字应该是分布式事务吧。

在需要多次往返Actor之间进行通信的时候,感觉非常痛苦

说个题外话,就你这网站,运营商也会劫持,弹出各种广告窗口,似乎他们开发了一种自动针对所有网站都能弹窗的程序。不想免费替运营商打广告的话,建议还是尽快升级Tls+http2,还能提速和降低被墙概率。没有证书可去letsencrypt申请,免费的。

-------------------------------
"固然,我们肯定可以从设计上找到一个更好的方法处理上面这类交易事件"
-------------------------------

能讲一下这个更好的方法的总体思路吗。

Post a comment

非这个主题相关的留言请到:留言本