聊聊Disruptor 和 Aeron 这两个开源库
聊聊Disruptor 和 Aeron 这两个开源库
Disruptor
The best way to understand what the Disruptor is, is to compare it to something well understood and quite similar in purpose. In the case of the Disruptor this would be Java's BlockingQueue. Like a queue the purpose of the Disruptor is to move data (e.g. messages or events) between threads within the same process. However there are some key features that the Disruptor provides that distinguish it from a queue. They are:
•Multicast events to consumers, with consumer dependency graph.
•Pre-allocate memory for events.
•Optionally lock-free.
Core Concepts
Before we can understand how the Disruptor works, it is worthwhile defining a number of terms that will be used throughout the documentation and the code. For those with a DDD bent, think of this as the ubiquitous language of the Disruptor domain.
•Ring Buffer: The Ring Buffer is often considered the main aspect of the Disruptor, however from 3.0 onwards the Ring Buffer is only responsible for the storing and updating of the data (Events) that move through the Disruptor. And for some advanced use cases can be completely replaced by the user.
•Sequence: The Disruptor uses Sequences as a means to identify where a particular component is up to. Each consumer (EventProcessor) maintains a Sequence as does the Disruptor itself. The majority of the concurrent code relies on the the movement of these Sequence values, hence the Sequence supports many of the current features of an AtomicLong. In fact the only real difference between the 2 is that the Sequence contains additional functionality to prevent false sharing between Sequences and other values.
•Sequencer: The Sequencer is the real core of the Disruptor. The 2 implementations (single producer, multi producer) of this interface implement all of the concurrent algorithms use for fast, correct passing of data between producers and consumers.
•Sequence Barrier: The Sequence Barrier is produced by the Sequencer and contains references to the main published Sequence from the Sequencer and the Sequences of any dependent consumer. It contains the logic to determine if there are any events available for the consumer to process.
•Wait Strategy: The Wait Strategy determines how a consumer will wait for events to be placed into the Disruptor by a producer. More details are available in the section about being optionally lock-free.
•Event: The unit of data passed from producer to consumer. There is no specific code representation of the Event as it defined entirely by the user.
•EventProcessor: The main event loop for handling events from the Disruptor and has ownership of consumer's Sequence. There is a single representation called BatchEventProcessor that contains an efficient implementation of the event loop and will call back onto a used supplied implementation of the EventHandler interface.
•EventHandler: An interface that is implemented by the user and represents a consumer for the Disruptor.
•Producer: This is the user code that calls the Disruptor to enqueue Events. This concept also has no representation in the code.
Aeron
Aeron is an OSI layer 4 Transport for message-oriented streams. It works over UDP or IPC, and supports both unicast and multicast. The main goal is to provide an efficient and reliable connection with a low and predictable latency. Aeron has Java, C++ and .NET clients.
When to use?
A high and predictable performance is a main advantage of Aeron, it’s most useful in application which requires low-latency, high throughput (e.g. sending large files) or both (akka remoting uses Aeron).
If it can work over UDP, why not to use UDP?
The main goal of Aeron is high performance. That is why it makes sense why it can work over UDP but doesn’t support TCP. But someone can ask what features Aeron provides on top of UDP?
Aeron is OSI Layer 4 (Transport) Services. It supports next features:
1. Connection Oriented Communication
2. Reliability
3. Flow Control
4. Congestion Avoidance/Control
5. Multiplexing
Architecture
Aeron uses unidirectional connections. If you need to send requests and receive responses, you should use two connections.Publisher and Media Driver (see later) are used to send a message, Subscriber and Media Driver — to receive. Client talks to Media Driver via shared memory.
为什么将这两个库在一起聊呢,通常Disruptor框架的核心数据结构circular buffer是Java实现的,主要原因是Java对并发的支持比较友好,而且比较早的支持了内存模型,但是 C++ 11以后,C++ 同样在并发方面有了长足的进步, Aeron就是在这个背景下产生的,虽然核心代码依旧是Java,但是在对C++客户端支持的设计中也实现了比较多的,有价值的数据结构,比如OneToOneRingBuffer, ManyToOneRingBuffer。
下面就以OneToOneRingBuffer为例进行说明,
/*
* Copyright 2014-2020 Real Logic Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ #ifndef AERON_RING_BUFFER_ONE_TO_ONE_H
#define AERON_RING_BUFFER_ONE_TO_ONE_H #include <climits>
#include <functional>
#include <algorithm>
#include "util/Index.h"
#include "util/LangUtil.h"
#include "AtomicBuffer.h"
#include "Atomic64.h"
#include "RingBufferDescriptor.h"
#include "RecordDescriptor.h" namespace aeron { namespace concurrent { namespace ringbuffer { class OneToOneRingBuffer
{
public:
explicit OneToOneRingBuffer(concurrent::AtomicBuffer &buffer) :
m_buffer(buffer)
{
m_capacity = buffer.capacity() - RingBufferDescriptor::TRAILER_LENGTH; RingBufferDescriptor::checkCapacity(m_capacity); m_maxMsgLength = m_capacity / ; m_tailPositionIndex = m_capacity + RingBufferDescriptor::TAIL_POSITION_OFFSET;
m_headCachePositionIndex = m_capacity + RingBufferDescriptor::HEAD_CACHE_POSITION_OFFSET;
m_headPositionIndex = m_capacity + RingBufferDescriptor::HEAD_POSITION_OFFSET;
m_correlationIdCounterIndex = m_capacity + RingBufferDescriptor::CORRELATION_COUNTER_OFFSET;
m_consumerHeartbeatIndex = m_capacity + RingBufferDescriptor::CONSUMER_HEARTBEAT_OFFSET;
} OneToOneRingBuffer(const OneToOneRingBuffer &) = delete;
OneToOneRingBuffer &operator=(const OneToOneRingBuffer &) = delete; inline util::index_t capacity() const
{
return m_capacity;
} bool write(std::int32_t msgTypeId, concurrent::AtomicBuffer &srcBuffer, util::index_t srcIndex, util::index_t length)
{
RecordDescriptor::checkMsgTypeId(msgTypeId);
checkMsgLength(length); const util::index_t recordLength = length + RecordDescriptor::HEADER_LENGTH;
const util::index_t requiredCapacity = util::BitUtil::align(recordLength, RecordDescriptor::ALIGNMENT);
const util::index_t mask = m_capacity - ; std::int64_t head = m_buffer.getInt64(m_headCachePositionIndex);
std::int64_t tail = m_buffer.getInt64(m_tailPositionIndex);
const util::index_t availableCapacity = m_capacity - (util::index_t)(tail - head); if (requiredCapacity > availableCapacity)
{
head = m_buffer.getInt64Volatile(m_headPositionIndex); if (requiredCapacity > (m_capacity - (util::index_t)(tail - head)))
{
return false;
} m_buffer.putInt64(m_headCachePositionIndex, head);
} util::index_t padding = ;
auto recordIndex = static_cast<util::index_t>(tail & mask);
const util::index_t toBufferEndLength = m_capacity - recordIndex; if (requiredCapacity > toBufferEndLength)
{
auto headIndex = static_cast<std::int32_t>(head & mask); if (requiredCapacity > headIndex)
{
head = m_buffer.getInt64Volatile(m_headPositionIndex);
headIndex = static_cast<std::int32_t>(head & mask); if (requiredCapacity > headIndex)
{
return false;
} m_buffer.putInt64Ordered(m_headCachePositionIndex, head);
} padding = toBufferEndLength;
} if ( != padding)
{
m_buffer.putInt64Ordered(
recordIndex, RecordDescriptor::makeHeader(padding, RecordDescriptor::PADDING_MSG_TYPE_ID));
recordIndex = ;
} m_buffer.putBytes(RecordDescriptor::encodedMsgOffset(recordIndex), srcBuffer, srcIndex, length);
m_buffer.putInt64Ordered(recordIndex, RecordDescriptor::makeHeader(recordLength, msgTypeId));
m_buffer.putInt64Ordered(m_tailPositionIndex, tail + requiredCapacity + padding); return true;
} int read(const handler_t &handler, int messageCountLimit)
{
const std::int64_t head = m_buffer.getInt64(m_headPositionIndex);
const auto headIndex = static_cast<std::int32_t>(head & (m_capacity - ));
const std::int32_t contiguousBlockLength = m_capacity - headIndex;
int messagesRead = ;
int bytesRead = ; auto cleanup = util::InvokeOnScopeExit {
[&]()
{
if (bytesRead != )
{
m_buffer.setMemory(headIndex, bytesRead, );
m_buffer.putInt64Ordered(m_headPositionIndex, head + bytesRead);
}
}}; while ((bytesRead < contiguousBlockLength) && (messagesRead < messageCountLimit))
{
const std::int32_t recordIndex = headIndex + bytesRead;
const std::int64_t header = m_buffer.getInt64Volatile(recordIndex);
const std::int32_t recordLength = RecordDescriptor::recordLength(header); if (recordLength <= )
{
break;
} bytesRead += util::BitUtil::align(recordLength, RecordDescriptor::ALIGNMENT); const std::int32_t msgTypeId = RecordDescriptor::messageTypeId(header);
if (RecordDescriptor::PADDING_MSG_TYPE_ID == msgTypeId)
{
continue;
} ++messagesRead;
handler(
msgTypeId, m_buffer, RecordDescriptor::encodedMsgOffset(recordIndex),
recordLength - RecordDescriptor::HEADER_LENGTH);
} return messagesRead;
} inline int read(const handler_t &handler)
{
return read(handler, INT_MAX);
} inline util::index_t maxMsgLength() const
{
return m_maxMsgLength;
} inline std::int64_t nextCorrelationId()
{
return m_buffer.getAndAddInt64(m_correlationIdCounterIndex, );
} inline void consumerHeartbeatTime(std::int64_t time)
{
m_buffer.putInt64Ordered(m_consumerHeartbeatIndex, time);
} inline std::int64_t consumerHeartbeatTime() const
{
return m_buffer.getInt64Volatile(m_consumerHeartbeatIndex);
} inline std::int64_t producerPosition() const
{
return m_buffer.getInt64Volatile(m_tailPositionIndex);
} inline std::int64_t consumerPosition() const
{
return m_buffer.getInt64Volatile(m_headPositionIndex);
} inline std::int32_t size() const
{
std::int64_t headBefore;
std::int64_t tail;
std::int64_t headAfter = m_buffer.getInt64Volatile(m_headPositionIndex); do
{
headBefore = headAfter;
tail = m_buffer.getInt64Volatile(m_tailPositionIndex);
headAfter = m_buffer.getInt64Volatile(m_headPositionIndex);
}
while (headAfter != headBefore); return static_cast<std::int32_t>(tail - headAfter);
} bool unblock()
{
return false;
} private:
concurrent::AtomicBuffer &m_buffer;
util::index_t m_capacity;
util::index_t m_maxMsgLength;
util::index_t m_headPositionIndex;
util::index_t m_headCachePositionIndex;
util::index_t m_tailPositionIndex;
util::index_t m_correlationIdCounterIndex;
util::index_t m_consumerHeartbeatIndex; inline void checkMsgLength(util::index_t length) const
{
if (length > m_maxMsgLength)
{
throw util::IllegalArgumentException(
"encoded message exceeds maxMsgLength of " + std::to_string(m_maxMsgLength) +
" length=" + std::to_string(length),
SOURCEINFO);
}
}
}; }}} #endif
使用示例
AtomicBuffer ab(&buff[], buff.size());
OneToOneRingBuffer ringBuffer(ab); util::index_t tail = ;
util::index_t head = ; ab.putInt64(HEAD_COUNTER_INDEX, head);
ab.putInt64(TAIL_COUNTER_INDEX, tail); std::cout <<"circular buffer capacity is : "<<ringBuffer.capacity() << std::endl;
util::index_t length = ;
util::index_t recordLength = length + RecordDescriptor::HEADER_LENGTH; AtomicBuffer srcAb(&srcBuff[], srcBuff.size());
srcAb.putInt64(, );
srcAb.putStringWithoutLength(, ""); while (ringBuffer.write(MSG_TYPE, srcAb, , length))
{
} int timesCalled = ;
const int messagesRead = ringBuffer.read(
[&](std::int32_t msgTypeId, concurrent::AtomicBuffer& internalBuf, util::index_t valPosition, util::index_t length)
{
timesCalled++;
std::cout << "circular buffer read value is : " << internalBuf.getInt64(valPosition)
<<" string value: " << internalBuf.getStringWithoutLength(valPosition+, length - )
<<" called times: "<< timesCalled <<std::endl;
});
运行结果
circular buffer capacity is :
circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times: circular buffer read value is : string value: called times:
参考资料
https://github.com/LMAX-Exchange/disruptor
https://github.com/real-logic/aeron
https://medium.com/@pirogov.alexey/aeron-low-latency-transport-protocol-9493f8d504e8
https://github.com/LMAX-Exchange/disruptor/wiki/Getting-Started
聊聊Disruptor 和 Aeron 这两个开源库的更多相关文章
- DICOM医学图像处理:开源库mDCM与DCMTK的比較分析(一),JPEG无损压缩DCM图像
背景介绍: 近期项目需求,须要使用C#进行最新的UI和相关DICOM3.0医学图像模块的开发.在C++语言下,我使用的是应用最广泛的DCMTK开源库,在本专栏的起初阶段的大多数博文都是对DCMTK开源 ...
- 使用 Golang 代码生成图表的开源库对比
本文的目标读者 对用 Golang 代码生成折线图.扇形图等图表有兴趣的朋友. 本文摘要 主要介绍 Go 中用以绘图的开源库,分别是: GitHub - wcharczuk/go-chart: go ...
- 喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了
折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...
- 两个开源的 Spring Boot + Vue 前后端分离项目
折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...
- 学习Spring Boot看这两个开源项目就够了!非得值得收藏的资源
Spring Boot我就不做介绍了,大家都懂得它是一个多么值得我们程序员兴奋的框架. 为什么要介绍这两个开源项目呢? 1.提供了丰富的学习实践案例 2.整合了非常多优质的学习资源 不多说了,直接上链 ...
- 腾讯两大开源项目Tars、TSeer
6月25日,在LC3(LinuxCon + ContainerCon + CloudOpen)中国2018大会上,腾讯宣布其两大开源项目——RPC开发框架Tars.轻量化名字服务方案TSeer,加入L ...
- Java下好用的开源库推荐
作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文想介绍下自己在Java下做开发使用到的一些开源的优秀编程库,会不定 ...
- libCURL开源库在VS2010环境下编译安装,配置详解
libCURL开源库在VS2010环境下编译安装,配置详解 转自:http://my.oschina.net/u/1420791/blog/198247 http://blog.csdn.net/su ...
- GitHub上那些值得一试的JAVA开源库--转
原文地址:http://www.jianshu.com/p/ad40e6dd3789 作为一名程序员,你几乎每天都会使用到GitHub上的那些著名Java第三方库,比如Apache Commons,S ...
随机推荐
- Python-操作XML文件
一.python对XML文件的操作 目录 1.xml 创建 2.xml 数据查询 3.xml 数据修改 4.xml 数据删除 二. 1.xml 创建 import xml.etree.ElementT ...
- Oracle 中文日期转换
中文日期转换 select to_char(to_date('07-5月-17'),'yyyy-MM-dd HH24:mi:ss') from dual
- 如何从零基础开始学习LoadRunner12(一)
如何从零基础开始学习LoadRunner12(一) 上一篇文章讲到了如何安装LR12的教程,这一篇文章来讲一下怎么利用LoadRunner自带的Sample来学习LoadRunner的基本使用. 首先 ...
- 企业级自动化部署方案——ansible实现tomcat自动安装和配置
共耗时10多个小时 思路一 总体设计 ansible-playbook目录结构 [root@ansible ~]# tree /etc/ansible/roles/tomcat /etc/ansibl ...
- Oracle 11g Windows下安装出现INS-30131错误
1.错误信息 2.解决方法 问题分析:访问Temp文件夹缺少权限 方案1: 使用net use查看C盘是否共享,否则进行设置,这种方法网上有很多帖子,不详坠 方案2:打开cmd,到相应的解压后的set ...
- sql-lib闯关11-20关
从第11关开始,我们就进入到了POST注入的世界了. POSTpost是一种数据提交方式,它主要是指数据从客户端提交到服务器端,例如,我们常常使用的用户登录模块.网站的留言板模块等,在这些功能模块中我 ...
- python绘图设置标题、标签,无法显示中文
先说解决办法:在程序开始之前,引入使用的模块之后,添加如下代码: plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['axes.unico ...
- 用Python简单批量处理数据
近期碰到一个问题,两套系统之间数据同步出了差错,事后才发现的,又不能将业务流程倒退,但是这么多数据手工处理量也太大了,于是决定用Python偷个小懒. 1.首先分析数据. 两边数据库字段的值都是一样, ...
- Python python 函数参数:必选参数,默认参数
import math # 函数的必选参数 '''函数的必选参数,指的是函数调用的时候必须传入的参数 ''' def cal (n): return n * n var = cal(2) '''上面的 ...
- 那些年,我们追过的java8
9月份java9就要发布了,在8月的最后一天决定回顾一下java8那些惊天动地的变化,加深理解,共同进步. 我们都知道java与c++,c不同是一个为面向对象而生的语言,面向对象思想贯彻了java发展 ...