Java开源:traffic-shm-异步无锁 IPC 类库

LarhondaQui 7年前
   <p style="text-align:center"><img src="https://simg.open-open.com/show/d02f31a448784120a6e1ef34723740c4.png"></p>    <p>traffic-shmanna是Java版的异步无锁IPC类库,支持多生产者-单消费者模式,可配合Bson等序列化协议,作为同一服务器中Java进程间的轻量级通讯组件使用,吞吐量可达到百万级TPS。 <strong>traffic-shm</strong> anna具有良好的跨平台移植性,支持在主流的操作系统如:Linux、macOS、Windows、AIX和HP-UX中运行。本文主要介绍 <strong>traffic-shm</strong> anna的实现细节。</p>    <h2>1. 内存映射文件</h2>    <p>操作系统使用虚拟内存来进行内存管理。虚拟内存是一个抽象的概念,它为每个为应用进程提供了使用虚拟地址取代物理内存地址进行内存访问的功能。</p>    <p>操作系统通过将一个虚拟内存区域与一个磁盘上的普通文件关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射。Java语言中,可通过FileChannel类的map()方法进行操作,实现过程由操作系统的mmap()实现。</p>    <p>内存映射使用文件系统建立从用户空间直到可用文件系统页的虚拟内存映射。这样做有几个好处:</p>    <ol>     <li>用户进程把文件数据当作内存,所以无需发布read()或write()系统调用。</li>     <li>当用户进程碰触到映射内存空间,缺页错误会自动产生,从而将文件数据从磁盘读进内存。如果用户修改了映射内存空间,相关页会自动标记为脏,随后刷新到磁盘,文件得到更新。</li>     <li>操作系统的虚拟内存子系统会对页进行智能高速缓存,自动根据系统负载进行内存管理。</li>     <li>数据总是按页对齐的,无需执行缓冲区拷贝。</li>     <li>大型文件使用映射,无需耗费大量内存,即可进行数据拷贝。</li>    </ol>    <p><img src="https://simg.open-open.com/show/f30175a9952f6d121e46db39c5e73485.png"></p>    <h2>2. 内存对齐</h2>    <ul>     <li><strong>页对齐</strong> 操作系统对内存采用页式管理机制,对于用mmap()映射的普通文件来,进程会在自己的地址空间新增一块空间,空间大小由mmap()的len参数指定。进程能够访问的有效地址大小取决于文件被映射部分的大小。超过这个空间大小,内核会根据超过的严重程度返回发送不同的信号给进程。示例如下:</li>    </ul>    <p><img src="https://simg.open-open.com/show/94fe9fde3672ed80e303cc4b0c01af2b.png"></p>    <p>因此,考虑到不同操作系统及处理器架构,内存映射文件的大小及mmap()映射的内存空间 大小均需要按页大小进行对齐。</p>    <ul>     <li><strong>数据类型对齐</strong> 简单来讲,数据类型对齐需要满足以下条件:数据所在的内存虚拟地址要能被这个数据的长度所整除。数据域单个数据块以4字节进行对齐。</li>    </ul>    <h2>3. 字节顺序</h2>    <p>不同的处理器架构使用不同的字节顺序存储数据,目前常见有大端小端两种存储方式。一个多位的整数将按照其存储地址的最低或最高字节排列,如果低位字节存放在内存的低地址端,高位字节存放在内存的高地址端,称为小端模式,反之为大端模式。以下以0x0A0B0C0D为例,分别解释小端模式及大端模式。</p>    <ul>     <li> <p>小端模式</p>      <table>       <thead>        <tr>         <th>1</th>         <th>2</th>         <th>3</th>         <th>4</th>        </tr>       </thead>       <tbody>        <tr>         <td>0x0D</td>         <td>0x0C</td>         <td>0x0B</td>         <td>0x0A</td>        </tr>       </tbody>      </table> </li>     <li> <p>大端模式</p>      <table>       <thead>        <tr>         <th>1</th>         <th>2</th>         <th>3</th>         <th>4</th>        </tr>       </thead>       <tbody>        <tr>         <td>0x0A</td>         <td>0x0B</td>         <td>0x0C</td>         <td>0x0D</td>        </tr>       </tbody>      </table> </li>    </ul>    <p>为了保证在不同操作系统及处理器架构下的可移植性, <strong>traffic-shm</strong> anna约定按照大端模式存储数据。</p>    <h2>4. 数据结构</h2>    <p>如下图所示,队列由元数据域及数据域两部分组成,公共信息及读写位置指针在元数据域维护,数据块为数据域的最小单位。</p>    <p><img src="https://simg.open-open.com/show/dcb369514ae45741239b48be5c0055e9.png"></p>    <p>同时为了节约内存空间,避免频繁映射造成的内存浪费,针对数据域采用了RingBuffer数据结构。</p>    <p><img src="https://simg.open-open.com/show/52190266a1123f5f52e69ae572df0e83.png"></p>    <h2>5. 并发控制</h2>    <ul>     <li><strong>可见性</strong> 为保证可见性,使用Unsafe的volatile方法对读写位置指针及ACK域进行操作。</li>     <li><strong>原子性</strong> 为了保证在多生产者模式下的原子性,对ACK域的一个int类型先后进行了两次无符号short型填充。</li>    </ul>    <p>参考:</p>    <ul>     <li>《深入理解计算机系统》</li>     <li>《Java NIO》</li>    </ul>    <p>项目主页:<a href="http://www.open-open.com/lib/view/home/1495805565723">http://www.open-open.com/lib/view/home/1495805565723</a></p>    <p> </p>    <p> </p>    <p> </p>