20.LMAX——DDD的极致性能架构
学习视频来源:DDD独家秘籍视频合集 https://space.bilibili.com/24690212/channel/collectiondetail?sid=1940048&ctype=0
文章目录
- 历史
- 起源
- 架构目标
- 架构要素
- 时序对比
- 传统时序
- 事件溯源时序
- LMAX时序
- 单线程非阻塞异步IO(reactor)
- 多线程
- 单线程
- 事件溯源
- 高可用方案
- 代价
- 总结
- 优点
- 缺点
- 适用范围
历史
LMAX是由Martin Fowler 2011年提出,原文地址: https://martinfowler.com/articles/lmax.html
起源
- 金融零售平台,要求高性能
- 达到单线程600万TPS,一个线程一秒钟能处理600万个交易请求
- LMAX架构名称就是以这个交易平台的名字命名的
架构目标
超高TPS。这里的TPS不是指只把数据读出来,是要有写入操作的,有变更的。
架构要素
1. 聚合常驻内存
在领域驱动设计中,不管以什么方式设计,最后要得到的就是“聚合”。在LMAX中,聚合是一直在内存中存在,不像传统的架构,用的时候把聚合从数据库中加载到内存,修改后保存到数据库 ,内存中的聚合就被回收掉了。
2. 事件溯源
需要高可用写入领域事件,宕机重启后可以从领域事件重建聚合。
3. 单线程异步非阻塞IO(串行,无锁)
外部系统发过来的请求会先写入环形队列Input Disruptor,Business Logic Processor会串行化地从队列中一个一个取出来请求处理,并将结果写入到OutDisruptor中,这个结果就是领域事件。
它没有给客户端直接返回结果,它是完全异步化的过程,客户端发过来请求后不会等待,直接退出。客户端会监听相应的事件,当收到相应的事件,才知道操作是否成功。Disruptor是一个高性能的队列框架,当初就是为了LMAX架构而设计的。
时序对比
传统时序
事件溯源时序
LMAX时序
单线程非阻塞异步IO(reactor)
多线程
- CPU在多线程间切换开销高 (相对于reactor)
- 阻塞
每个请求一线程模式,并发受线程数限制。像传统的tomcat这种方式,一个请求对应一个线程。
- 非阻塞
聚合在内存中只有一个实例,多线程情况下就会出现并发问题,所以就需要对实例加锁,加锁情况下每个聚合实际并发1。
单线程
- 串行执行,无需加锁,一个CPU执行一个线程。CPU不会频繁在用户态和内核态切换。
- 阻塞
如果用阻塞的方式,并发能力1,性能暴跌,所以只能用非阻塞方式。 - 非阻塞 (超高性能的唯一选择)
事件溯源
为什么要使用事件溯源,主要有两点:
- 只持久化领域事件,带来高性能写
- 确保重启服务器时,可以领域事件中,重建聚合。
高可用方案
聚合放在内存中,如果宕机怎么办呢,所以需要设计高可用方案。
所有的高可用方案都是通过冗余来实现的。main(主机器) 会发出所有的事件,follower(从机器) 会不断的重播这些事件,并重建聚合。如果main和follower之间没有高延迟,二者就可以在短时间内保持一致性。supervisor会监测main和follower, 一旦main出现宕机,就会在follower中选择一个作为新的主机器main,并告诉网关,将请求发到新的主机器上去。
代价
1. 无数据库事务
无先操作后回滚,必须严格前置完成所有校验,才能变更聚合内的状态数据,才能发布领域事件。
2. 运行时外部调用变复杂
因为异步的原因,导致它和外边系统交互很复杂。需要写代码处理异步IO,给编写代码带来负担。需要减少主动外部调用: 监听事件提前组装数据 (数据量小)
显式事件驱动方式
3. 受内存大小限制
因为聚合全部在内存中,如果聚合数量太大就需要用多台服务器去做数据分区,架构更复杂。
总结
优点
- 极致性能
全内存化模型设计,更灵活自由,可以大量使用对象和对象之间的引用。不像之前传统设计聚合的时候,是尽量减少聚合和聚合之间的引用。
缺点
- 异步编程带来的编程难度
- 高可用架构更复杂。传统的架构,只需要将服务复制很多份,服务是无状态的,它们共享同一个数据库。当我们需要高可用的,只需要多部署几台服务器就可以了,不存在主从切换问题、数据不一致问题。
适用范围
聚合数据量适合内存化,需要极致TPS场景,比如:
- 某些游戏服务器
频繁的执行操作,服务端不断的响应。 - 火车订票