Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 635|回复: 0

为什么Actor模型是高并发事务的终极解决方案?

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-6-17 15:13:43 | 显示全部楼层 |阅读模式

    首先看看一个问题:

    用户甲的操作 1.开始事务 2.访问表A 3.访问表B 4.提交事务

    乙用户在操作 1.开始事务 2.访问表B 3.访问表A 4.提交事务

    如果甲用户和乙用户的两个事务同时发生,甲事务锁住了表A未释放(因为整个事务未完成),正在准备访问B表,而乙事务锁住了表B未释放(因为整个事务未完成),正在准备访问A表,可是A表被甲事务锁住了,等甲事务释放,而甲事务真正等待乙事务释放B表,陷入了无限等待,也就是死锁Dead Lock。

    也有道友使用多线程来模拟存储过程:http://www.jdon.com/45727,每个线程里开启一个事务,类似上述问题也会出现死锁。

    问题出在哪里?

    是我们的思路方向出现问题:

    其实无论是使用数据库锁 还是多线程,这里有一个共同思路,就是将数据喂给线程,就如同计算机是一套加工流水线,数据作为原材料投入这个流水线的开始,流水线出来后就是成品,这套模式的前提是数据是被动的,自身不复杂,没有自身业务逻辑要求。适合大数据处理或互联网网站应用等等。

    但是如果数据自身要求有严格的一致性,也就是事务机制,数据就不能被动被加工,要让数据自己有行为能力保护实现自己的一致性,就像孩子小的时候可以任由爸妈怎么照顾关心都可以,但是如果孩子长大有自己的思想和要求,他就可能不喜欢被爸妈照顾,他要求自己通过行动实现自己的要求。

    数据也是如此。

    只有我们改变思路,让数据自己有行为维护自己的一致性,才能真正安全实现真正的事务。

    数据+行为=对象,有人问了,对象不是也要被线程调用吗?

    例如下述代码,因为对象的行为要被线程调用,我们要使用同步锁synchronized :

    public class A { 
            private volatile int lower, upper; //两个状态值
            public int getLower() { return lower; } 
            public int getUpper() { return upper; }
            public synchronized void setAUpper(int value){
                 if (value < a.getUpper()) 
                        a.setLower(value);
            }
    
            public asynchronization void setALower(int value){
                if (value > a.getLower()) 
                       a.setUpper(value);
             }
       }

    上面这段代码业务逻辑是想实现lower<upper:

    1. lower和upper的初始值是(0, 5), 2.一个客户端请求线程A: setLower(4) 一个客户端请求线程B: setUpper(3) 3. lower和upper是 (4, 3)

    这个结果破坏了lower<upper这个逻辑一致性,所以,用锁并不能保证逻辑一致性,而且还带来了堵塞。锁用错了地方,不但没有得到想要的,而且还失去更多。

    从历史上看,锁的问题如鬼魂一直伴随着我们:

    1.用数据表一个字段来表示状态,比如1表示已付款未发货,2表示已付款已发货,然后用户来一个请求用SQL或存储过程修改,这时使用的数据库锁。

    2.用ORM实现,比如Hibernate JPA来修改状态,虽然不用SQL了,但是Hibernate的悲观锁和乐观锁也让人抓狂。

    3.彻底抛弃数据库,直接在内存缓存中进行修改,使用Java的同步锁,性能还是不够,吞吐量上不去。如上图提示,只能一个厕所蹲位一个人用,其他人必须排队。

    4.Actor模型。

    Actor模型原理 Actor模型=数据+行为+消息。

    Actor模型内部的状态由自己的行为维护,外部线程不能直接调用对象的行为,必须通过消息才能激发行为,这样就保证Actor内部数据只有被自己修改。

    Actor模型如何实现?

    Scala或ErLang的进程信箱都是一种Actor模型,也有Java的专门的Actor模型,这里是几种Actor模型比较

    明白了Actor模型原理,使用Disruptor这样无锁队列也可以自己实现Actor模型,让一个普通对象与外界的交互调用通过Disruptor消息队列实现,比如LMAX架构就是这样实现高频交易,从2009年成功运行至今,被Martin Fowler推崇。

    回到本帖最初问题,如何使用Actor模型解决高并发事务呢?

    转账是典型的符合该问题的案例,转账是将A帐号到B帐号转账,使用Actor模型解决如下:

    发出是否可转出消息--->消息队列--->A

    A作为一个对象,注意不是数据表,对象是有行为的,检查自己余额是否可转账,如果可以,冻结这部分金额,比如转账100元,冻结100元,从余额中扣除。因为外部命令是通过消息顺序进来的,所以下一个消息如果也是扣除,再次检查余额是否足够......

    具体详细流程可见:REST和DDD

    那么,既然Actor模型如此巧妙,而解决方向与我们习惯的数据喂机器的方式如此不同,那么如何在实战中能明显发现某个数据修改应该用Actor模型解决呢?因为我们习惯将数据喂机器的思路啊?

    使用DDD领域驱动设计CQRS架构就能明显发现这些特殊情况,CQRS是读写分离,其中写操作是应领域专家要求编写的功能,在这类方向,我们都有必要使用Actor模型实现,因为在这个方向上,领域专家的要求都表达为聚合根实体,聚合根就是用Actor模型实现最合适不过了。而读方向,比如大数据处理,报表查询,OLTP等等都是数据喂机器的方式。

    有的道友会疑问,我们经常使用SSH,也就是Spring + Hibernate架构,这个默认是哪种方向呢?很显然,默认是数据喂机器的方向,所以在实现写操作时,特别警惕高并发发生死锁等影响性能问题,当然也包括EJB架构。

    有一种togaf架构,将企业软件架构分为数据架构和应用架构等,实际是EJB或SSH的变相描述,这种架构的问题我们已经一目了然了,特别这样的系统如果从面向内部管理转向到SaaS模型时,这类高并发死锁问题就特别容易发生,几乎不具备可用性。前期12306火车票系统是这类问题的典型体现。

     

    我们可以将目前企业软件分为两大类:license软件和云计算SaaS。

    卖license的软件要过渡到Saas几乎不可能,需要重新编写,因为过去 企业软件架构存在致命问题:高并发事务处理。

    卖license的软件一般是SOA架构,我认为由于SOA太强调服务,而服务是一种机器处理,如果在数据写方向使用SOA,这是一种数据喂机器的方式,必然会遭遇高并发事务和死锁问题。在这个写方向使用REST要比SOA更合适一些,因为REST侧重资源与状态,与DDD强调的实体与行为是一脉相承的。

    函数式编程前段时间很火,但是纯FP的语言逐渐被对象和函数混合式的语言如Scala赶超,关键还是可变的状态是现实中无法回避的问题,可变的使用对象式的Actror是最合适的,而不变性使用函数这种机器处理方式也是最合适的。可变性对应写操作,不变性对应读操作,如大数据处理,报表BI等。

    有人说,为什么大多数互联网软件都是数据喂机器,或函数式即可,比如twitter或facebook,因为他们没有事务要求,一般涉及到钱等重要交易都需要事务,而发个言,写段话等等都是非高事务要求,也可以这么说,非结构化的数据一般都没有事务要求,结构化聚合的数据才有事务要求。

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-25 23:52 , Processed in 0.068024 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表