小延迟大吞吐:LMAX架构(1)(3)
输出disruptors也类似于此,但是只有两个有顺序的消费者,转换和输出。输出事件被组织进入几个topics, 这样消息能够被发送到只有感兴趣的topic中,每个topic有自己的disruptor.
disruptor不但适合一个生产者多个消费者,也适合多个生产者。
disruptor设计的好处是能够容易让消费者快速抓取,如果发生问题,比如在15号位置有一个转换问题,而接受者在31号,它能够从16-30号一次性批量抓取,这种数据批读取能力加快消费者处理,降低整体延迟性。
ring buffer是巨大的: 输入2千万号槽;4百万输出. 次序计算器是一个64bit long 整数型,平滑增长(banq注:大概这里发现了JVM的伪共享),象其他系统一样disruptors过一个晚上将被清除,主要是擦除内存,以便不会产生代价昂贵的垃圾回收机制启动(我认为重启是一个好的习惯,以便你应付不时之需。)
日志工作是将事件存储到持久化介质上,以便出错是重放,但是他们没有使用数据库来实现,而是文件系统,他们将事件流写到磁盘上,在现代概念看来,磁盘对于随机访问是非常慢,但是对于流操作却很快,也就是说,磁盘是一种新式的磁带。
之前我提到LMAX运行在集群多个系统拷贝能够支持失败回复,复制工作负责这些节点的同步,所有节点联系是IP广播, 这样客户端能够不需要知道主节点的IP地址. 只有主节点直接听取输入事件,然后运行一个复制工作者,复制工作者将把输入事件广播到其他次要节点. 如果主节点当机,心跳机制将会发现, 另外一个节点就成为主节点,开始处理输入事件,启动复制工作者,每个节点都有自己的输入disruptor这样它有自己的日志处理和格式转换。
即使有IP广播,复制还是需要的,因为IP消息是以不同顺序到达不同节点,主节点提供为其他处理提供一个确定顺序。
格式转换unmarshaler是将事件从其消息格式转换到Java对象,这样才能在业务逻辑处理器中使用,不同于其他消费者,它需要修改ring buffer中的数据以便能够存入这个被转换好的Java对象,这里有一个规则:并发地每次只有一个消费者能够运行写入,这实际上也符合单一写入者原则。
disruptor组件可以用在LMAX系统以外,通常金融财务公司对他们的系统都保持隐秘,但是LMAX能够开源,我很高兴,这将允许其他组织使用disruptor,它也将允许其他人对其进行并发性能测试。
(banq注:disruptor看来是一种特殊的消息组件类似JMS东西)。
队列和机制偏爱的缺乏
LMAX架构引起了人们的关注,因为它是一个非常不同的方式接近的高性能系统。到目前为止,我已经谈到了它是如何工作的,但没有太多深入探讨了为什么它是这样。这个故事本身是有趣的,意识到他们是有缺陷的。
许多商业系统都有自己的核心架构师,通过事务性数据库实现多个会话事务(banq注:如EJB或Spring JTA等等),LMAX团队也熟悉这些知识,但是确信这些不合适他们的系统。这个经验是建立在LMAX母公司Betfair上 – 这是一家体育博彩公司,它处理很多人的体育投赌事件,这是一个相当大的并发访问,传统数据库机制几乎无法应付,这些让他们相信必须寻找另外一个途径来突破,他们现在接近目标了。
他们最初的想法为获得高性能是使用现在流行的并发。这意味着允许多线程并行处理多个订单。然而,在这种情况下是很难实现的,因为这些线程必须互相沟通。处理订单变化的市场条件等都需要相互沟通。
早期他们探索了Actor模型和近亲SEDA. Actor模型依赖于独立,活跃的对象有其自己的线程,彼此之间是通过queue同学,很多人认为这种并发模型比基于原始锁的方式易于处理。
这团队就建立了一个actor模型原型,进行性能测试,他们发现的是处理器会花费更多时间在管理队列,而不是去做真正应用逻辑,队列访问成了真正瓶颈(banq注:Scala的Actor模型很有名,不知这是否算Scala致命问题,怪不得很少人谈Scala的Actor模型了).
当追求性能达到这种程度,现代硬件构造原理成为很重要的必须了解的知识了,马丁汤普森喜欢用的一句话是“机制偏爱”,这词来自赛车驾驶,它反映的是赛车手对汽车有一种与生俱来的感觉,使他们能够感受到如何发挥它到最好状态。许多程序员包括我承认我也陷入这样一个阵营:不会认为编程如何与硬件等底层机制交互是值得研究的。
现代的CPU延迟是影响性能的主导因素之一,在CPU如何与内存交互,CPU具有多层次的缓存一级二级),每级速度都明显加快。因此,如果要提高速度,将您的代码和数据加载到这些缓存中。
在某个层次, actor模型能够帮助你,你能认为actor可以作为集群节点,是缓存的自然单元。但是actors需要相互联系, 这是通过队列的- 而LMAX团队发现队列会干扰缓存(banq注:JVM伪分享的问题)。
为什么队列干扰了缓存呢?解释是这样的: 为了将数据放入队列,你需要写入队列,类似地,为了从队列取出数据,你需要移除队列也是一种写,客户端也许不只一次写入同样数据结构,处理写通常需要锁,但是如果锁使用了,会引起切换到底层系统的场景, 当这个发生后,处理器会丢失它的缓存中的数据。
他们得出的结论能够获得最好的缓存性能, 你需要设计一个CPU核写任何内存,多个读是好的,处理器会非常快,而队列失败在one-writer原则。
这样的分析导致LMAX团队得出一系列结论,导致他们设计出disruptor, 能够遵循single-writer约束. 其次它导向留澳单个线程处理业务逻辑的新的目标, 问题是:一个线程如果从并发管理结构中脱离出来(没有锁机制),它到底能跑多快?
单个线程的本质是:确保你每个CPU核运行一个线程,缓存配合,尽可能的高速缓存访问甚于主内存。这就意味着代码和数据需要尽可能的一致,. 保持小的代码对象和数据在一起,以便允许他们能够调入到一个高速缓存单位中或者轮换,简化高速缓存管理就是提高性能。
LMAX架构的路径的一个重要组成部分是使用了性能测试。基于actor模型的放弃也是来自于测试原型的性能。同时也为改善的各个组成部分的性能步骤,启用了性能测试。机械同情是非常宝贵的的 – 它有助于形成假设什么可以改进,并指导你前进 -最终测试提供了有说服力的证据。
两段关于性能测试重要性,忽略…
你应当使用者架构吗?
这个架构是适合非常小小众,必须有很低的延迟获得复杂大量的交易,大多数应用并不需要6百万TPS。
但是我对这个架构着迷的原因是它的设计,它移除了很多其他大多数编程系统的复杂性,传统围绕事务性的关系数据库会话并发模型是不是免费的麻烦? (banq注:因为都掌握都知道也就是免费的) 通常与数据库打交道都有不寻常的付出和努力,对象/关系数据库映射ORM工具Object/relational mapping tools能够帮助减轻不少这种痛苦,但是它不能解决全部问题,大多数企业性能微调还是要纠结于SQL.
现在你能得到服务器更多的主内存,比我们过去这些老家伙得到的磁盘还要多,越来越多应用能够将他们的工作全部置于内存中,这样消除了复杂性和性能低问题. 事件源驱动Event Sourcing提供了一种内存in-memory系统的解决方案, 在单个线程运行业务解决了并发. LMAX 经验建议只要你需要少于几百万TPS,你就有足够的性能提升余地。
这里也是相似于CQRS. 一种事件驱动, in-memory风格自然的命令系统(尽管LMAX当前没有使用CQRS.)
那么表示你是不是不应该走上这条道路呢?始终存在这样鲜为人知的棘手的技术问题,这个行业需要更多的时间去探索它的边界(注:老子思想的缴啊)。基本出发点是鼓励有自己特点的架构。
一个重要特点是处理一个交易总是潜在地影响其后面的处理方式,因为交易总是相互独立的, 因为很少相互协调,那么使用分离单独处理器分别处理并发运行也许更加有吸引力。
LMAX指出了“事件”概念是如何改变世界(banq注:hold住事件,而不是hold住数据,你就上了一个新层次,摆脱低级趣味的数据库癖好)。 许多网站使用原有的信息存储系统,然后渲染各种能够吸引眼球的效果. 他们的架构挑战就是如何正确使用缓存。
LMAX另外一个特点是这是一个后台系统,有理由考虑如何在一个交互模型中应用它,比如日益增长的Web应用,当异步通讯在WEB应用越来越多时,这将改变我们的编程模型。
这个改变会影响很多团队,大多数人倾向于认为同步编程,不习惯于异步处理。异步通讯是必不可少的响应工具,在javascript世界已经广泛使用,如 AJAX 和 node.js, 这些鼓励人们研究这些风格. LMAX团队发现虽然要花费一定时间来适应异步编程模型,但是一旦习惯就成为自然,特别是错误处理上容易得多。
LMAX团队当然感觉到花力气协调事务性关系数据库的日子已经屈指可数(banq老泪纵横啊,05年喊出了数据库时代的终结,08年我就喊出数据库已死,被国内很多大牛讥笑为疯子) 。你可以使用一种更加容易方式编写程序而且比传统集中式中央数据库运行得更快,为什么视而不见呢?
原文链接:http://martinfowler.com/articles/lmax.html
译文链接:http://www.jdon.com/42452
相关文章
- 暂无相关文章
用户点评