├── 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 | 
17 |
18 | 
19 | 注意,由于Windows任务调度的问题,经常会有Read进程得不到调度而停顿的现象,从屏幕输出可以看到此现象。
20 | ####限制
21 | 1. JDK 要求 SunJDK 1.8
22 |
23 |
24 | ####内存格式图
25 | 
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 |
--------------------------------------------------------------------------------