├── images ├── cpu.png ├── perf1.png └── memory-format.jpg ├── src ├── main │ └── java │ │ └── io │ │ └── mycat │ │ └── ipc │ │ ├── util │ │ └── Util.java │ │ ├── NativeMemoryRegion.java │ │ ├── RawMessage.java │ │ ├── QueueMeta.java │ │ ├── SharedMMIPMemPool.java │ │ ├── FastestFileStorage.java │ │ ├── SharedMMRing.java │ │ └── UnsafeMemory.java └── test │ └── java │ └── io │ └── mycat │ └── ipc │ ├── TestBase.java │ ├── Writer.java │ ├── TestFastFileStorage.java │ ├── Reader.java │ └── TestSharedMMIPMemPool.java ├── pom.xml └── README.md /images/cpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyCATApache/Mycat-IPC/HEAD/images/cpu.png -------------------------------------------------------------------------------- /images/perf1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyCATApache/Mycat-IPC/HEAD/images/perf1.png -------------------------------------------------------------------------------- /images/memory-format.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyCATApache/Mycat-IPC/HEAD/images/memory-format.jpg -------------------------------------------------------------------------------- /src/main/java/io/mycat/ipc/util/Util.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc.util; 2 | 3 | /** 4 | * tools 5 | * 6 | * @author wuzhih 7 | * 8 | */ 9 | public class Util { 10 | public static long roundTo4096(long i) { 11 | return (i + 0xfffL) & ~0xfffL; 12 | } 13 | public static void main(String[] args) 14 | { 15 | System.out.println(roundTo4096(4095)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/mycat/ipc/NativeMemoryRegion.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | public class NativeMemoryRegion { 4 | 5 | private final UnsafeMemory mm; 6 | private final long addr, size; 7 | public NativeMemoryRegion(UnsafeMemory mm, long addr, long size) { 8 | super(); 9 | this.mm = mm; 10 | this.addr = addr; 11 | this.size = size; 12 | } 13 | public UnsafeMemory getMm() { 14 | return mm; 15 | } 16 | public long getAddr() { 17 | return addr; 18 | } 19 | public long getSize() { 20 | return size; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/mycat/ipc/RawMessage.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public class RawMessage { 6 | 7 | private short msgLength; 8 | private byte[] rawData; 9 | private byte dataFlag; 10 | 11 | public RawMessage() { 12 | 13 | } 14 | 15 | public byte[] getRawData() { 16 | return rawData; 17 | } 18 | 19 | public byte getDataFlag() { 20 | return this.dataFlag; 21 | } 22 | 23 | public RawMessage(byte[] rawData) { 24 | super(); 25 | this.rawData = rawData; 26 | this.msgLength = (short) rawData.length; 27 | } 28 | 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/io/mycat/ipc/TestBase.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | import org.junit.Test; 4 | 5 | public class TestBase { 6 | 7 | @Test 8 | public void testbitOp() 9 | 10 | { 11 | 12 | int size = 1024; 13 | 14 | if (Integer.bitCount(size) != 1) 15 | { 16 | throw new IllegalArgumentException("bufferSize must be a power of 2"); 17 | } 18 | int indexMask = size - 1; 19 | int index = 5; 20 | int nexIndex = (index & indexMask); 21 | System.out.println(nexIndex); 22 | index = 1024; 23 | nexIndex = (index & indexMask); 24 | System.out.println(nexIndex); 25 | index = 1025; 26 | nexIndex = (index & indexMask); 27 | System.out.println(nexIndex); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | io.mycat.ipc 6 | Mycat-IPC 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | Mycat-ipc 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 16 | 17 | 18 | junit 19 | junit 20 | 4.8.1 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mycat IPC 2 | --- 3 | Mycat java进程间超高性能通信服务框架 4 | ####特性 5 | 1. 使用共享内存映射的技术,多进程之间数据传递直接操作内存,性能极高 6 | 2. 实现了无锁多进程/多线程读写的逻辑,多个进程可以同时并发读写 7 | 3. HP ZBook笔记本测试,单一队列,两个进程通信,最高高达每秒500万的消息传输,3组进程分别对应3个队列,总和超过1000万每秒消息 8 | 9 | 10 | 11 | ####使用方式 12 | 1. 启动 io.mycat.ipc.Reader 创建一个空白队列,等待和读取消息 13 | 2. 启动 io.mycat.ipc.Writer 写入消息 14 | 15 | HP ZBook 17上 在Eclipse里启动上述两个进程的测试结果截图 16 | ![image](https://raw.githubusercontent.com/MyCATApache/Mycat-IPC/master/images/perf1.png) 17 | 18 | ![image](https://raw.githubusercontent.com/MyCATApache/Mycat-IPC/master/images/cpu.png) 19 | 注意,由于Windows任务调度的问题,经常会有Read进程得不到调度而停顿的现象,从屏幕输出可以看到此现象。 20 | ####限制 21 | 1. JDK 要求 SunJDK 1.8 22 | 23 | 24 | ####内存格式图 25 | ![image](https://github.com/huangll99/Mycat-IPC/blob/master/images/memory-format.jpg) 26 | -------------------------------------------------------------------------------- /src/test/java/io/mycat/ipc/Writer.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | public class Writer { 4 | public static void main(String[] args) { 5 | long i = 0; 6 | long start = System.currentTimeMillis(); 7 | try { 8 | SharedMMIPMemPool pool = new SharedMMIPMemPool("Coollf.dat3", 1024 * 1024 * 100L, false); 9 | SharedMMRing ring = pool.getRing((short) 1); 10 | 11 | while (true) { 12 | i++; 13 | byte[] data = ("hello :" + (i++)).getBytes("utf-8"); 14 | while (!ring.putData(data)) { 15 | Thread.yield(); 16 | } 17 | //Thread.sleep(2); 18 | // if(i==100000) 19 | // { 20 | // break; 21 | // } 22 | } 23 | } catch (Exception e) { 24 | e.printStackTrace(); 25 | } 26 | System.out.println(System.currentTimeMillis() - start); 27 | System.out.print(i); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/io/mycat/ipc/TestFastFileStorage.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | import org.junit.Test; 4 | 5 | public class TestFastFileStorage { 6 | 7 | @Test 8 | public void testWriteAndRead() throws Exception { 9 | FastestFileStorage st = new FastestFileStorage("mm.data", 100 * 1024 * 1024L, true, 1024 * 1024); 10 | for (int i = 0; i < 100000; i++) { 11 | String data = new String(" hellow " + i); 12 | int writeResult = st.writeData(data.getBytes()); 13 | if (writeResult == -1) { 14 | System.out.println("full " + i); 15 | break; 16 | } else if (writeResult == 0) { 17 | System.out.println("cant't write " + i); 18 | break; 19 | } else { 20 | if (i % 1000 == 0) 21 | System.out.println("write success " + i); 22 | } 23 | } 24 | st.close(); 25 | System.out.println(" closed "); 26 | 27 | st = new FastestFileStorage("mm.data", 100 * 1024 * 1024L, false, 1024 * 1024); 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/io/mycat/ipc/Reader.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | public class Reader { 4 | public static void main(String[] args) { 5 | try { 6 | SharedMMIPMemPool pool = new SharedMMIPMemPool("Coollf.dat3", 1024 * 1024 * 100L, true); 7 | SharedMMRing ring = pool.createNewRing((short) 1, 1024 * 1024,SharedMMRing.STORAGE_PRIMARY); 8 | long readed = 0; 9 | long curTime = System.currentTimeMillis(); 10 | while (true) { 11 | byte[] dat = ring.pullData(); 12 | if (dat != null) { 13 | readed++; 14 | if (readed % 1000000 == 0) { 15 | String data = new String(dat); 16 | System.out.println( 17 | "readed " + readed + ",speed " + readed * 1000L / (System.currentTimeMillis() - curTime) 18 | + " msgs/second, cur msg:" + data); 19 | 20 | } 21 | } else { 22 | Thread.yield(); 23 | } 24 | } 25 | } catch (Exception e) { 26 | // TODO Auto-generated catch block 27 | e.printStackTrace(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/io/mycat/ipc/TestSharedMMIPMemPool.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class TestSharedMMIPMemPool { 7 | 8 | @Test 9 | public void testInitFromFile() throws Exception { 10 | SharedMMIPMemPool pool = new SharedMMIPMemPool("testmm.dat", 1024 * 1024 * 100L, true); 11 | Assert.assertTrue(pool.getQueueCountInMM() == 0); 12 | SharedMMRing ring = pool.createNewRing((short) 1, 1024 * 1024 * 3, SharedMMRing.STORAGE_PRIMARY); 13 | 14 | for (int i = 0; i < 100; i++) { 15 | byte[] msg = ("Hellow " + i).getBytes(); 16 | while (!ring.putData(msg)) { 17 | Thread.yield(); 18 | } 19 | System.out.println("add messge " + i); 20 | } 21 | // read 22 | for (int i = 0; i < 100; i++) { 23 | 24 | byte[] msg = ring.pullData(); 25 | if (msg != null) { 26 | byte[] themsg = ("Hellow " + i).getBytes(); 27 | //System.out.println("readed messge " + new String(msg)); 28 | Assert.assertArrayEquals(themsg, msg); 29 | } 30 | 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/mycat/ipc/QueueMeta.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | public class QueueMeta { 4 | private final short groupId; 5 | private final int rawLenth; 6 | private final long addr; 7 | private final byte storageType; 8 | public QueueMeta(short groupId, int rawLenth, long addr,byte storageType) { 9 | super(); 10 | this.groupId = groupId; 11 | this.rawLenth = rawLenth; 12 | this.addr = addr; 13 | this.storageType=storageType; 14 | } 15 | 16 | public byte getStorageType() { 17 | return storageType; 18 | } 19 | 20 | public long getAddrEnd() 21 | { 22 | return addr+rawLenth; 23 | } 24 | public short getGroupId() { 25 | return groupId; 26 | } 27 | public int getRawLenth() { 28 | return rawLenth; 29 | } 30 | public long getAddr() { 31 | return addr; 32 | } 33 | 34 | @Override 35 | public int hashCode() { 36 | final int prime = 31; 37 | int result = 1; 38 | result = prime * result + (int) (addr ^ (addr >>> 32)); 39 | result = prime * result + groupId; 40 | result = prime * result + rawLenth; 41 | result = prime * result + storageType; 42 | return result; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object obj) { 47 | if (this == obj) 48 | return true; 49 | if (obj == null) 50 | return false; 51 | if (getClass() != obj.getClass()) 52 | return false; 53 | QueueMeta other = (QueueMeta) obj; 54 | if (addr != other.addr) 55 | return false; 56 | if (groupId != other.groupId) 57 | return false; 58 | if (rawLenth != other.rawLenth) 59 | return false; 60 | if (storageType != other.storageType) 61 | return false; 62 | return true; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "QueueMeta [groupId=" + groupId + ", rawLenth=" + rawLenth + ", addr=" + addr + ", storageType=" 68 | + storageType + "]"; 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/io/mycat/ipc/SharedMMIPMemPool.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * an whole shared memory buffer pool first two byte means allocated count of 8 | * queues, then each queue's define: 2 bytes: this queue's group 4 bytes: this 9 | * queque's capacity queue's memory 10 | * 11 | * @author wuzhih 12 | * 13 | */ 14 | public class SharedMMIPMemPool { 15 | private final String loc; 16 | private UnsafeMemory mm; 17 | private final int MAX_QUEUE_COUNT = 2048; 18 | private final int MM_QUEUE_START = 2; 19 | private final int MM_QUEUE_METADATA_LEN = 2 + 4 + 1; 20 | private Map allocateRings = new HashMap(); 21 | private volatile SharedMMRing lastRing; 22 | 23 | /** 24 | * Constructs a new memory mapped file. 25 | * 26 | * @param loc 27 | * the file name 28 | * @param len 29 | * the file length 30 | * @throws Exception 31 | * in case there was an error creating the memory mapped file 32 | */ 33 | public SharedMMIPMemPool(final String loc, long len, boolean createNewFile) throws Exception { 34 | 35 | this.loc = loc; 36 | this.mm = UnsafeMemory.mapAndSetOffset(loc, len, createNewFile, 0, len); 37 | init(); 38 | } 39 | 40 | public String getLoc() { 41 | return loc; 42 | } 43 | 44 | private void init() { 45 | short curQueueCount = getQueueCountInMM(); 46 | int addr = MM_QUEUE_START; 47 | SharedMMRing latest = null; 48 | long queueAddr = getFirstQueueAddr(); 49 | for (int i = 0; i < curQueueCount; i++) { 50 | short group = mm.getShortVolatile(addr); 51 | addr += 2; 52 | int rawLength = mm.getIntVolatile(addr); 53 | addr += 4; 54 | byte storageType = mm.getByteVolatile(addr); 55 | addr += 1; 56 | QueueMeta meta = new QueueMeta(group, rawLength, queueAddr, storageType); 57 | queueAddr += rawLength; 58 | SharedMMRing ring = new SharedMMRing(meta, mm.getAddr()); 59 | allocateRings.put(group, ring); 60 | latest = ring; 61 | } 62 | this.lastRing = latest; 63 | 64 | } 65 | 66 | private long getFirstQueueAddr() { 67 | return MM_QUEUE_START + MM_QUEUE_METADATA_LEN * MAX_QUEUE_COUNT; 68 | } 69 | 70 | public synchronized SharedMMRing createNewRing(short groupId, int rawLength, byte storageType) { 71 | short curQueueCount = getQueueCountInMM(); 72 | if (curQueueCount >= MAX_QUEUE_COUNT) { 73 | return null; 74 | } 75 | int metaAddr = MM_QUEUE_START + MM_QUEUE_METADATA_LEN * curQueueCount; 76 | // write queue metedata info 77 | mm.putShortVolatile(metaAddr, groupId); 78 | metaAddr += 2; 79 | mm.putIntVolatile(metaAddr, rawLength); 80 | metaAddr += 4; 81 | mm.putByteVolatile(metaAddr, storageType); 82 | // create queue 83 | SharedMMRing prevQueue = this.lastRing; 84 | long queueAddr = (prevQueue == null) ? getFirstQueueAddr() : prevQueue.getMetaData().getAddrEnd(); 85 | QueueMeta meta = new QueueMeta(groupId, rawLength, queueAddr, storageType); 86 | SharedMMRing ring = new SharedMMRing(meta,mm.getAddr()); 87 | // update header 88 | mm.putShortVolatile(0, ++curQueueCount); 89 | // put map 90 | allocateRings.put(groupId, ring); 91 | this.lastRing = ring; 92 | return ring; 93 | } 94 | 95 | public SharedMMRing getRing(short i) { 96 | return allocateRings.get(i); 97 | } 98 | 99 | public SharedMMRing getLastRing() { 100 | return this.lastRing; 101 | } 102 | 103 | public short getQueueCountInMM() { 104 | return mm.getShortVolatile(0); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/io/mycat/ipc/FastestFileStorage.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | import java.io.File; 4 | 5 | import io.mycat.ipc.util.Util; 6 | 7 | /** 8 | * use memory mapping method to speed up large file write and read 9 | * 10 | * @author wuzhih 11 | * 12 | */ 13 | public class FastestFileStorage { 14 | private static final int fileHeaderLen = 8 + 8; 15 | private final long mapBlockSize; 16 | private final String loc; 17 | private final boolean writeMode; 18 | private UnsafeMemory fileHeaderMM; 19 | private volatile UnsafeMemory curMappingMM; 20 | private final long maxFileLen; 21 | public static final int MSG_PADDING_LENGTH = 2 + 1;// 2 bytes lenth and 22 | // flag 23 | 24 | /** 25 | * Constructs a new memory mapped file. 26 | * 27 | * @param loc 28 | * the file name 29 | * @param len 30 | * the file length 31 | * @throws Exception 32 | * in case there was an error creating the memory mapped file 33 | */ 34 | public FastestFileStorage(final String loc, long maxLen, boolean writerMode, int mapBlockSize) throws Exception { 35 | if (writerMode) { 36 | new File(loc).delete(); 37 | } 38 | this.writeMode = writerMode; 39 | this.loc = loc; 40 | this.maxFileLen = Util.roundTo4096(maxLen); 41 | this.mapBlockSize = Util.roundTo4096(mapBlockSize); 42 | this.fileHeaderMM = UnsafeMemory.mapAndSetOffset(loc, maxFileLen, writerMode, 0, fileHeaderLen); 43 | init(); 44 | 45 | } 46 | 47 | public final long getNextDataFlagIndexPos() { 48 | return 0; 49 | } 50 | 51 | public final long getWriteIndexPos() { 52 | return 8; 53 | } 54 | 55 | public final long getStartPos() { 56 | return 8 * 2; 57 | } 58 | 59 | private void init() throws Exception { 60 | // next data start addr 61 | if (fileHeaderMM.compareAndSwapLong(getNextDataFlagIndexPos(), 0, this.getStartPos())) { 62 | // cur write begin addr 63 | fileHeaderMM.putLongVolatile(getWriteIndexPos(), this.getStartPos() + 1); 64 | // no next data flag 65 | fileHeaderMM.putByte(getStartPos(), SharedMMRing.FLAG_NO_NEXT); 66 | } 67 | if (writeMode) { 68 | curMappingMM = UnsafeMemory.mapAndSetOffset(loc, maxFileLen, this.writeMode, 0, this.mapBlockSize); 69 | } 70 | } 71 | 72 | public void close() throws Exception { 73 | fileHeaderMM.unmap(); 74 | curMappingMM.unmap(); 75 | } 76 | 77 | public boolean readerFarBehindWriter() { 78 | long nextDataBeginPos = this.getNextDataAddr(); 79 | long writeStartPos = getWriteStartAddr(); 80 | return (nextDataBeginPos == this.getStartPos() || (writeStartPos - nextDataBeginPos) * 2 > maxFileLen); 81 | } 82 | 83 | public int writeData(byte[] rawMsg) throws Exception { 84 | long writeStartPos = getWriteStartAddr(); 85 | int dataRealLen = getMsgTotalSpace(rawMsg); 86 | // not exceed file lenth 87 | if (writeStartPos + dataRealLen < maxFileLen - fileHeaderLen) { 88 | long endPos = writeStartPos + dataRealLen; 89 | UnsafeMemory mm = this.curMappingMM; 90 | long dataRelativePos = 0; 91 | // have space in this map block 92 | if (mm.getEndPos() >= endPos) { 93 | if (!fileHeaderMM.compareAndSwapLong(getWriteIndexPos(), writeStartPos, endPos)) { 94 | return 0; 95 | } 96 | // write data 97 | dataRelativePos = mm.getRelovePos(writeStartPos); 98 | this.writeMsg(mm, dataRelativePos, rawMsg); 99 | 100 | } else {// next mapping block; 101 | // first set writeStartPos to next block start addr 102 | long nextWritePos = mm.getEndPos() + 1; 103 | if (fileHeaderMM.compareAndSwapLong(getWriteIndexPos(), writeStartPos, nextWritePos)) { 104 | curMappingMM = UnsafeMemory.mapAndSetOffset(loc, maxFileLen, false, mm.getEndPos(), 105 | this.mapBlockSize); 106 | UnsafeMemory nextmm = curMappingMM; 107 | // set no more data flag first for the new block 108 | nextmm.putByte(0, SharedMMRing.FLAG_NO_NEXT); 109 | // set previous data's next flag to next block 110 | mm.putByteVolatile(dataRelativePos - 1, SharedMMRing.MASK_NEXT_BLOCK); 111 | // release prev map 112 | mm.unmap(); 113 | System.out.println("remap to nexBlockStartPos " +nextmm.getStartPos()+" old end pos " + mm.getEndPos()); 114 | 115 | } 116 | mm = curMappingMM; 117 | while (mm.getStartPos() < writeStartPos) { 118 | Thread.yield(); 119 | mm = curMappingMM; 120 | } 121 | dataRelativePos = mm.getRelovePos(nextWritePos); 122 | // write data 123 | this.writeMsg(mm, dataRelativePos, rawMsg); 124 | } 125 | // update prev data's next flag 126 | mm.putByteVolatile(dataRelativePos - 1, SharedMMRing.FLAG_NEXT_ADJACENT); 127 | return 1; 128 | 129 | } else { 130 | return -1; 131 | // throw new RuntimeException("file is full " + this.loc); 132 | } 133 | 134 | } 135 | 136 | private void writeMsg(UnsafeMemory mm, long pos, byte[] data) { 137 | 138 | short msgLength = (short) data.length; 139 | mm.putShort(pos, msgLength); 140 | mm.setBytes(pos + 2, data, 0, msgLength); 141 | mm.putByte(pos + 2 + msgLength, SharedMMRing.FLAG_NO_NEXT); 142 | 143 | } 144 | 145 | public long getNextDataAddr() { 146 | return fileHeaderMM.getLongVolatile(0); 147 | 148 | } 149 | 150 | public long getWriteStartAddr() { 151 | return fileHeaderMM.getLongVolatile(8); 152 | 153 | } 154 | 155 | private int getMsgTotalSpace(byte[] msg) { 156 | return msg.length + MSG_PADDING_LENGTH; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/io/mycat/ipc/SharedMMRing.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | /** 4 | * SharedMemory Ring ,Allow multi writers and readers long dataStartAddr long 5 | * dataEndAddr 6 | * 7 | * @author wuzhih 8 | * 9 | */ 10 | public class SharedMMRing { 11 | public static final byte STORAGE_PRIMARY=0; 12 | public static final byte STORAGE_EXTEND=1; 13 | public static final byte STORAGE_EXT_FILE=2; 14 | // means next data condition 15 | public static final byte FLAG_NO_NEXT = 0B0001; 16 | public static final byte FLAG_NEXT_ADJACENT = 0B0010; 17 | public static final byte MASK_NEXT_REWIND = 0B0100; 18 | public static final byte MASK_NEXT_EXTEND_RING = 0B0101; 19 | public static final byte MASK_NEXT_BLOCK = 0B0110; 20 | public static final int MSG_PADDING_LENGTH = 2 + 1;// 2 bytes lenth and 21 | // flag 22 | 23 | private final UnsafeMemory mm; 24 | private final QueueMeta metaData; 25 | 26 | public final long getStartPos() { 27 | return 8 * 2; 28 | } 29 | 30 | public final long getWriteIndexPos() { 31 | return 8; 32 | } 33 | 34 | public final long getNextDataFlagIndexPos() { 35 | return 0; 36 | } 37 | 38 | public final long getEndPos() { 39 | return mm.getSize(); 40 | } 41 | 42 | public SharedMMRing(QueueMeta metaData, long rawMemoryStartAddr) { 43 | super(); 44 | this.mm = new UnsafeMemory(metaData.getAddr() + rawMemoryStartAddr,rawMemoryStartAddr, metaData.getRawLenth()); 45 | this.metaData = metaData; 46 | // next data start addr 47 | if (mm.compareAndSwapLong(getNextDataFlagIndexPos(), 0, this.getStartPos())) { 48 | 49 | // cur write begin addr 50 | mm.putLongVolatile(getWriteIndexPos(),this.getStartPos()+1); 51 | // no next data flag 52 | mm.putByte(getStartPos(), FLAG_NO_NEXT); 53 | 54 | } 55 | 56 | } 57 | 58 | private void writeMsg(long pos, byte[] data) { 59 | short msgLength = (short) data.length; 60 | mm.putShort(pos, msgLength); 61 | mm.setBytes(pos + 2, data, 0, msgLength); 62 | mm.putByte(pos + 2 + msgLength, FLAG_NO_NEXT); 63 | 64 | } 65 | 66 | private int getMsgTotalSpace(byte[] msg) { 67 | return msg.length + MSG_PADDING_LENGTH; 68 | } 69 | 70 | public long getNextDataAddr() { 71 | return mm.getLongVolatile(0); 72 | 73 | } 74 | 75 | public long getWriteStartAddr() { 76 | return mm.getLongVolatile(8); 77 | 78 | } 79 | 80 | public UnsafeMemory getMm() { 81 | return mm; 82 | } 83 | 84 | public QueueMeta getMetaData() { 85 | return metaData; 86 | } 87 | 88 | private int tryReWindWrite(byte[] rawMsg) { 89 | int dataRealLen = getMsgTotalSpace(rawMsg); 90 | long writeStartPos = this.getWriteStartAddr(); 91 | long nextDataPos = this.getNextDataAddr(); 92 | // System.out.println("rewind write ,cur write addr :" + writeStartPos + 93 | // " nextDataPos " + nextDataPos 94 | // + " data len:" + dataRealLen); 95 | 96 | // enough space to write 97 | if (writeStartPos + dataRealLen < nextDataPos) { 98 | if (!mm.compareAndSwapLong(getWriteIndexPos(), writeStartPos, writeStartPos + dataRealLen)) { 99 | return 1; 100 | } 101 | // write data 102 | writeMsg(writeStartPos, rawMsg); 103 | // update prev data's next flag 104 | mm.putByteVolatile(writeStartPos - 1, FLAG_NEXT_ADJACENT); 105 | return 0; 106 | } else if ((writeStartPos > nextDataPos)) { 107 | return 1; 108 | } else { 109 | 110 | System.out.println("no more space cur write addr :" + writeStartPos + " nextDataPos " + nextDataPos 111 | + " data len:" + dataRealLen); 112 | // throw new java.lang.RuntimeException("no space to write "); 113 | // return -1; 114 | return -1; 115 | } 116 | } 117 | 118 | /** 119 | * return 0 ,wirte ok , 1 ,try agagin ,-1 no space 120 | * 121 | * @param rawMsg 122 | * @return 123 | */ 124 | 125 | private int tryWriteData(byte[] rawMsg) { 126 | 127 | long nextDataBeginPos = this.getNextDataAddr(); 128 | long writeStartPos = getWriteStartAddr(); 129 | // normal append write to tail 130 | if (writeStartPos > nextDataBeginPos) { 131 | int dataRealLen = getMsgTotalSpace(rawMsg); 132 | // enough space 133 | if (writeStartPos + dataRealLen <= this.getEndPos()) { 134 | // System.out.println("write start Pos " + writeStartPos); 135 | // first update writeStart pos 136 | if (!mm.compareAndSwapLong(getWriteIndexPos(), writeStartPos, writeStartPos + dataRealLen)) { 137 | return 1; 138 | } 139 | // write data 140 | this.writeMsg(writeStartPos, rawMsg); 141 | // update prev data's next flag 142 | mm.putByteVolatile(writeStartPos - 1, FLAG_NEXT_ADJACENT); 143 | return 0; 144 | } else { 145 | // System.out.println("rewind write ,before, start Pos " + 146 | // writeStartPos + " next " + nextDataBeginPos); 147 | // try rewind write 148 | 149 | // first set writeStartPos to start of queue 150 | if (mm.compareAndSwapLong(getWriteIndexPos(), writeStartPos, this.getStartPos() + 1)) { 151 | // set no more data flag 152 | mm.putByte(this.getStartPos(), FLAG_NO_NEXT); 153 | // set previous data's next flag to rewind 154 | mm.putByteVolatile(writeStartPos - 1, MASK_NEXT_REWIND); 155 | } 156 | // System.out.println("rewind write ,after, start Pos " + 157 | // this.getWriteStartAddr() + " next " + 158 | // this.getNextDataAddr()); 159 | // last writeStartPos 160 | return tryReWindWrite(rawMsg); 161 | } 162 | 163 | } else {// rewind from begin ,try wrap write 164 | // System.out.println("write rewindw start Pos " + writeStartPos); 165 | // last writeStartPos 166 | return tryReWindWrite(rawMsg); 167 | } 168 | 169 | } 170 | 171 | /** 172 | * if return false,means can't put ,ring is full 173 | * 174 | * @param rawMsg 175 | * @return 176 | */ 177 | public boolean putData(byte[] rawMsg) { 178 | 179 | for (int i = 0; i < 3; i++) { 180 | int writeResult = tryWriteData(rawMsg); 181 | switch (writeResult) { 182 | case 0: 183 | return true; 184 | case 1: 185 | continue; 186 | case -1: 187 | return false; 188 | 189 | } 190 | 191 | } 192 | return false; 193 | } 194 | 195 | private byte[] readData(long prevNextDataFlagPos, long curDataFlagPos) { 196 | int dataLength = mm.getShort(curDataFlagPos + 1); 197 | long nextDataStartAddr = curDataFlagPos + MSG_PADDING_LENGTH; 198 | if (!mm.compareAndSwapLong(getNextDataFlagIndexPos(), prevNextDataFlagPos, nextDataStartAddr + dataLength)) { 199 | return null; 200 | } 201 | byte[] msg = new byte[dataLength]; 202 | mm.getBytes(nextDataStartAddr, msg, 0, dataLength); 203 | return msg; 204 | } 205 | 206 | public byte[] pullData() { 207 | byte[] msg = null; 208 | long nextDataFlagPos = this.getNextDataAddr(); 209 | byte nextDataFlag = mm.getByteVolatile(nextDataFlagPos); 210 | switch (nextDataFlag) { 211 | case FLAG_NO_NEXT: { 212 | // System.out.println( 213 | // "no data to read ,data next pos " + nextDataFlagPos + ", write 214 | // pos " + this.getWriteStartAddr()); 215 | break; 216 | } 217 | case FLAG_NEXT_ADJACENT: { 218 | msg = readData(nextDataFlagPos, nextDataFlagPos); 219 | break; 220 | } 221 | case MASK_NEXT_REWIND: { 222 | byte newNextDataFlag = mm.getByteVolatile(this.getStartPos()); 223 | /* 224 | * System.out.println("rewind read begin,data next pos " + 225 | * nextDataFlagPos+ " next flag "+newNextDataFlag); 226 | */ 227 | switch (newNextDataFlag) { 228 | case FLAG_NO_NEXT: { 229 | mm.compareAndSwapLong(getNextDataFlagIndexPos(), nextDataFlagPos, this.getStartPos()); 230 | break; 231 | } 232 | case FLAG_NEXT_ADJACENT: { 233 | msg = readData(nextDataFlagPos, this.getStartPos()); 234 | break; 235 | } 236 | } 237 | // System.out.println("rewind read ,data next pos " + 238 | // nextDataStartAddr + ", data len: " + dataLength 239 | // + " write pos " + this.getWriteStartAddr()); 240 | } 241 | 242 | } 243 | return msg; 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/main/java/io/mycat/ipc/UnsafeMemory.java: -------------------------------------------------------------------------------- 1 | package io.mycat.ipc; 2 | 3 | import java.io.File; 4 | import java.io.RandomAccessFile; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | import java.nio.channels.FileChannel; 8 | 9 | import io.mycat.ipc.util.Util; 10 | import sun.misc.Unsafe; 11 | import sun.nio.ch.FileChannelImpl; 12 | 13 | @SuppressWarnings("restriction") 14 | public class UnsafeMemory { 15 | public static final Unsafe unsafe; 16 | public static final Method mmap; 17 | public static final Method unmmap; 18 | public static final int BYTE_ARRAY_OFFSET; 19 | 20 | private final long addr, startPos, size; 21 | 22 | static { 23 | try { 24 | Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe"); 25 | singleoneInstanceField.setAccessible(true); 26 | unsafe = (Unsafe) singleoneInstanceField.get(null); 27 | mmap = getMethod(FileChannelImpl.class, "map0", int.class, long.class, long.class); 28 | mmap.setAccessible(true); 29 | unmmap = getMethod(FileChannelImpl.class, "unmap0", long.class, long.class); 30 | unmmap.setAccessible(true); 31 | BYTE_ARRAY_OFFSET = unsafe.arrayBaseOffset(byte[].class); 32 | } catch (Exception e) { 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | 37 | public static UnsafeMemory mapAndSetOffset(final String loc, long fileSize, boolean createNewFile, long startPos, 38 | long mapSize) throws Exception { 39 | long size = 0; 40 | RandomAccessFile backingFile = new RandomAccessFile(loc, "rw"); 41 | if (createNewFile) { 42 | new File(loc).delete(); 43 | size = Util.roundTo4096(fileSize); 44 | backingFile.setLength(size); 45 | } 46 | FileChannel ch = backingFile.getChannel(); 47 | long addr = (long) mmap.invoke(ch, 1, startPos, mapSize); 48 | ch.close(); 49 | backingFile.close(); 50 | return new UnsafeMemory(addr, startPos, mapSize); 51 | 52 | } 53 | 54 | public UnsafeMemory(long addr, long startPos, long size) { 55 | this.addr = addr; 56 | this.startPos = startPos; 57 | this.size = size; 58 | } 59 | 60 | public long getAddr() { 61 | return addr; 62 | } 63 | 64 | public long getEndAddr() { 65 | return addr + size; 66 | } 67 | public long getRelovePos(long abPos) 68 | { 69 | return abPos-startPos; 70 | } 71 | 72 | public long getStartPos() 73 | { 74 | return this.startPos; 75 | } 76 | public long getEndPos() { 77 | return size + startPos; 78 | } 79 | 80 | public long getSize() { 81 | return size; 82 | } 83 | 84 | private static Method getMethod(Class cls, String name, Class... params) throws Exception { 85 | Method m = cls.getDeclaredMethod(name, params); 86 | m.setAccessible(true); 87 | return m; 88 | } 89 | 90 | protected void unmap() throws Exception { 91 | unmmap.invoke(null, addr, this.size); 92 | 93 | } 94 | 95 | /** 96 | * Reads a byte from the specified position. 97 | * 98 | * @param pos 99 | * the position in the memory mapped file 100 | * @return the value read 101 | */ 102 | public byte getByte(long pos) { 103 | return unsafe.getByte(pos + addr); 104 | } 105 | 106 | /** 107 | * Reads a byte (volatile) from the specified position. 108 | * 109 | * @param pos 110 | * the position in the memory mapped file 111 | * @return the value read 112 | */ 113 | protected byte getByteVolatile(long pos) { 114 | return unsafe.getByteVolatile(null, pos + addr); 115 | } 116 | 117 | /** 118 | * Reads a short from the specified position. 119 | * 120 | * @param pos 121 | * the position in the memory mapped file 122 | * @return the value read 123 | */ 124 | public short getShort(long pos) { 125 | return unsafe.getShort(pos + addr); 126 | } 127 | 128 | /** 129 | * Reads a short (volatile) from the specified position. 130 | * 131 | * @param pos 132 | * the position in the memory mapped file 133 | * @return the value read 134 | */ 135 | protected short getShortVolatile(long pos) { 136 | return unsafe.getShortVolatile(null, pos + addr); 137 | } 138 | 139 | /** 140 | * Reads an int from the specified position. 141 | * 142 | * @param pos 143 | * the position in the memory mapped file 144 | * @return the value read 145 | */ 146 | public int getInt(long pos) { 147 | return unsafe.getInt(pos + addr); 148 | } 149 | 150 | /** 151 | * Reads an int (volatile) from the specified position. 152 | * 153 | * @param pos 154 | * position in the memory mapped file 155 | * @return the value read 156 | */ 157 | protected int getIntVolatile(long pos) { 158 | return unsafe.getIntVolatile(null, pos + addr); 159 | } 160 | 161 | /** 162 | * Reads a long from the specified position. 163 | * 164 | * @param pos 165 | * position in the memory mapped file 166 | * @return the value read 167 | */ 168 | public long getLong(long pos) { 169 | return unsafe.getLong(pos + addr); 170 | } 171 | 172 | /** 173 | * Reads a long (volatile) from the specified position. 174 | * 175 | * @param pos 176 | * position in the memory mapped file 177 | * @return the value read 178 | */ 179 | protected long getLongVolatile(long pos) { 180 | return unsafe.getLongVolatile(null, pos + addr); 181 | } 182 | 183 | /** 184 | * Writes a byte to the specified position. 185 | * 186 | * @param pos 187 | * the position in the memory mapped file 188 | * @param val 189 | * the value to write 190 | */ 191 | public void putByte(long pos, byte val) { 192 | unsafe.putByte(pos + addr, val); 193 | } 194 | 195 | /** 196 | * Writes a byte (volatile) to the specified position. 197 | * 198 | * @param pos 199 | * the position in the memory mapped file 200 | * @param val 201 | * the value to write 202 | */ 203 | protected void putByteVolatile(long pos, byte val) { 204 | unsafe.putByteVolatile(null, pos + addr, val); 205 | } 206 | 207 | /** 208 | * Writes an int to the specified position. 209 | * 210 | * @param pos 211 | * the position in the memory mapped file 212 | * @param val 213 | * the value to write 214 | */ 215 | public void putInt(long pos, int val) { 216 | unsafe.putInt(pos + addr, val); 217 | } 218 | 219 | /** 220 | * Writes an int (volatile) to the specified position. 221 | * 222 | * @param pos 223 | * the position in the memory mapped file 224 | * @param val 225 | * the value to write 226 | */ 227 | protected void putIntVolatile(long pos, int val) { 228 | unsafe.putIntVolatile(null, pos + addr, val); 229 | } 230 | 231 | /** 232 | * Writes an short to the specified position. 233 | * 234 | * @param pos 235 | * the position in the memory mapped file 236 | * @param val 237 | * the value to write 238 | */ 239 | public void putShort(long pos, short val) { 240 | unsafe.putShort(null, pos + addr, val); 241 | } 242 | 243 | /** 244 | * Writes an short (volatile) to the specified position. 245 | * 246 | * @param pos 247 | * the position in the memory mapped file 248 | * @param val 249 | * the value to write 250 | */ 251 | protected void putShortVolatile(long pos, short val) { 252 | unsafe.putShortVolatile(null, pos + addr, val); 253 | } 254 | 255 | /** 256 | * Writes a long to the specified position. 257 | * 258 | * @param pos 259 | * the position in the memory mapped file 260 | * @param val 261 | * the value to write 262 | */ 263 | public void putLong(long pos, long val) { 264 | unsafe.putLong(pos + addr, val); 265 | } 266 | 267 | /** 268 | * Writes a long (volatile) to the specified position. 269 | * 270 | * @param pos 271 | * the position in the memory mapped file 272 | * @param val 273 | * the value to write 274 | */ 275 | protected void putLongVolatile(long pos, long val) { 276 | unsafe.putLongVolatile(null, pos + addr, val); 277 | 278 | } 279 | 280 | protected void lazySetLong(long pos, long val) { 281 | unsafe.putOrderedLong(null, pos + addr, val); 282 | 283 | } 284 | 285 | protected void lazySetInt(long pos, int val) { 286 | unsafe.putOrderedInt(null, pos + addr, val); 287 | 288 | } 289 | 290 | /** 291 | * Reads a buffer of data. 292 | * 293 | * @param pos 294 | * the position in the memory mapped file 295 | * @param data 296 | * the input buffer 297 | * @param offset 298 | * the offset in the buffer of the first byte to read data into 299 | * @param length 300 | * the length of the data 301 | */ 302 | public void getBytes(long pos, byte[] data, int offset, int length) { 303 | unsafe.copyMemory(null, pos + addr, data, BYTE_ARRAY_OFFSET + offset, length); 304 | } 305 | 306 | /** 307 | * Writes a buffer of data. 308 | * 309 | * @param pos 310 | * the position in the memory mapped file 311 | * @param data 312 | * the output buffer 313 | * @param offset 314 | * the offset in the buffer of the first byte to write 315 | * @param length 316 | * the length of the data 317 | */ 318 | public void setBytes(long pos, byte[] data, int offset, int length) { 319 | unsafe.copyMemory(data, BYTE_ARRAY_OFFSET + offset, null, pos + addr, length); 320 | } 321 | 322 | protected boolean compareAndSwapInt(long pos, int expected, int value) { 323 | return unsafe.compareAndSwapInt(null, pos + addr, expected, value); 324 | } 325 | 326 | protected boolean compareAndSwapLong(long pos, long expected, long value) { 327 | return unsafe.compareAndSwapLong(null, pos + addr, expected, value); 328 | } 329 | 330 | protected long getAndAddLong(long pos, long delta) { 331 | return unsafe.getAndAddLong(null, pos + addr, delta); 332 | } 333 | } 334 | --------------------------------------------------------------------------------