作者 crossoverJie
提交者 GitHub

Merge pull request #57 from crossoverJie/cim-1.0.6

cim 1.0.6
@@ -50,8 +50,9 @@ @@ -50,8 +50,9 @@
50 * [x] 路由(`cim-forward-route`)服务自身是无状态,可用 `Nginx` 代理支持高可用。 50 * [x] 路由(`cim-forward-route`)服务自身是无状态,可用 `Nginx` 代理支持高可用。
51 * [x] 服务端自动剔除离线客户端。 51 * [x] 服务端自动剔除离线客户端。
52 * [x] 客户端自动重连。 52 * [x] 客户端自动重连。
  53 +* [x] 延时消息
53 * [ ] 分组群聊。 54 * [ ] 分组群聊。
54 -* [ ] Android SDK 55 +* [ ] SDK 开发包
55 * [ ] 离线消息。 56 * [ ] 离线消息。
56 * [ ] 协议支持消息加密。 57 * [ ] 协议支持消息加密。
57 * [ ] 更多的客户端路由策略。 58 * [ ] 更多的客户端路由策略。
@@ -3,6 +3,7 @@ package com.crossoverjie.cim.client.config; @@ -3,6 +3,7 @@ package com.crossoverjie.cim.client.config;
3 import com.crossoverjie.cim.client.handle.MsgHandleCaller; 3 import com.crossoverjie.cim.client.handle.MsgHandleCaller;
4 import com.crossoverjie.cim.client.service.impl.MsgCallBackListener; 4 import com.crossoverjie.cim.client.service.impl.MsgCallBackListener;
5 import com.crossoverjie.cim.common.constant.Constants; 5 import com.crossoverjie.cim.common.constant.Constants;
  6 +import com.crossoverjie.cim.common.data.construct.RingBufferWheel;
6 import com.crossoverjie.cim.common.protocol.CIMRequestProto; 7 import com.crossoverjie.cim.common.protocol.CIMRequestProto;
7 import com.google.common.util.concurrent.ThreadFactoryBuilder; 8 import com.google.common.util.concurrent.ThreadFactoryBuilder;
8 import okhttp3.OkHttpClient; 9 import okhttp3.OkHttpClient;
@@ -104,4 +105,11 @@ public class BeanConfig { @@ -104,4 +105,11 @@ public class BeanConfig {
104 return caller ; 105 return caller ;
105 } 106 }
106 107
  108 +
  109 + @Bean
  110 + public RingBufferWheel bufferWheel(){
  111 + ExecutorService executorService = Executors.newFixedThreadPool(2) ;
  112 + return new RingBufferWheel(executorService) ;
  113 + }
  114 +
107 } 115 }
1 package com.crossoverjie.cim.client.handle; 1 package com.crossoverjie.cim.client.handle;
2 2
  3 +import com.crossoverjie.cim.client.service.EchoService;
3 import com.crossoverjie.cim.client.service.ShutDownMsg; 4 import com.crossoverjie.cim.client.service.ShutDownMsg;
  5 +import com.crossoverjie.cim.client.service.impl.EchoServiceImpl;
4 import com.crossoverjie.cim.client.thread.ReConnectJob; 6 import com.crossoverjie.cim.client.thread.ReConnectJob;
5 import com.crossoverjie.cim.client.util.SpringBeanFactory; 7 import com.crossoverjie.cim.client.util.SpringBeanFactory;
6 import com.crossoverjie.cim.common.constant.Constants; 8 import com.crossoverjie.cim.common.constant.Constants;
@@ -41,6 +43,8 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt @@ -41,6 +43,8 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt
41 43
42 private ShutDownMsg shutDownMsg ; 44 private ShutDownMsg shutDownMsg ;
43 45
  46 + private EchoService echoService ;
  47 +
44 48
45 @Override 49 @Override
46 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 50 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
@@ -68,7 +72,6 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt @@ -68,7 +72,6 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt
68 72
69 @Override 73 @Override
70 public void channelActive(ChannelHandlerContext ctx) throws Exception { 74 public void channelActive(ChannelHandlerContext ctx) throws Exception {
71 -  
72 //客户端和服务端建立连接时调用 75 //客户端和服务端建立连接时调用
73 LOGGER.info("cim server connect success!"); 76 LOGGER.info("cim server connect success!");
74 } 77 }
@@ -95,6 +98,10 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt @@ -95,6 +98,10 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt
95 98
96 @Override 99 @Override
97 protected void channelRead0(ChannelHandlerContext ctx, CIMResponseProto.CIMResProtocol msg) throws Exception { 100 protected void channelRead0(ChannelHandlerContext ctx, CIMResponseProto.CIMResProtocol msg) throws Exception {
  101 + if (echoService == null){
  102 + echoService = SpringBeanFactory.getBean(EchoServiceImpl.class) ;
  103 + }
  104 +
98 105
99 //心跳更新时间 106 //心跳更新时间
100 if (msg.getType() == Constants.CommandType.PING){ 107 if (msg.getType() == Constants.CommandType.PING){
@@ -108,7 +115,7 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt @@ -108,7 +115,7 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt
108 115
109 //将消息中的 emoji 表情格式化为 Unicode 编码以便在终端可以显示 116 //将消息中的 emoji 表情格式化为 Unicode 编码以便在终端可以显示
110 String response = EmojiParser.parseToUnicode(msg.getResMsg()); 117 String response = EmojiParser.parseToUnicode(msg.getResMsg());
111 - System.out.println(response); 118 + echoService.echo(response);
112 } 119 }
113 120
114 121
@@ -5,6 +5,9 @@ import com.crossoverjie.cim.client.service.EchoService; @@ -5,6 +5,9 @@ import com.crossoverjie.cim.client.service.EchoService;
5 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.stereotype.Service; 6 import org.springframework.stereotype.Service;
7 7
  8 +import java.time.LocalDate;
  9 +import java.time.LocalTime;
  10 +
8 /** 11 /**
9 * Function: 12 * Function:
10 * 13 *
@@ -21,8 +24,10 @@ public class EchoServiceImpl implements EchoService { @@ -21,8 +24,10 @@ public class EchoServiceImpl implements EchoService {
21 private AppConfiguration appConfiguration; 24 private AppConfiguration appConfiguration;
22 25
23 @Override 26 @Override
24 - public void echo(String msg,Object... replace) {  
25 - msg = "\033[31;4m" + appConfiguration.getUserName() + PREFIX + "\033[0m" + " " + msg; 27 + public void echo(String msg, Object... replace) {
  28 + String date = LocalDate.now().toString() + " " + LocalTime.now().withNano(0).toString();
  29 +
  30 + msg = "[" + date + "] \033[31;4m" + appConfiguration.getUserName() + PREFIX + "\033[0m" + " " + msg;
26 31
27 String log = print(msg, replace); 32 String log = print(msg, replace);
28 33
@@ -32,6 +37,7 @@ public class EchoServiceImpl implements EchoService { @@ -32,6 +37,7 @@ public class EchoServiceImpl implements EchoService {
32 37
33 /** 38 /**
34 * print msg 39 * print msg
  40 + *
35 * @param msg 41 * @param msg
36 * @param place 42 * @param place
37 * @return 43 * @return
@@ -42,7 +48,7 @@ public class EchoServiceImpl implements EchoService { @@ -42,7 +48,7 @@ public class EchoServiceImpl implements EchoService {
42 for (int i = 0; i < place.length; i++) { 48 for (int i = 0; i < place.length; i++) {
43 int index = msg.indexOf("{}", k); 49 int index = msg.indexOf("{}", k);
44 50
45 - if (index == -1){ 51 + if (index == -1) {
46 return msg; 52 return msg;
47 } 53 }
48 54
@@ -63,9 +69,9 @@ public class EchoServiceImpl implements EchoService { @@ -63,9 +69,9 @@ public class EchoServiceImpl implements EchoService {
63 69
64 k = index + 2; 70 k = index + 2;
65 } 71 }
66 - if (sb.toString().equals("")){  
67 - return msg ;  
68 - }else { 72 + if (sb.toString().equals("")) {
  73 + return msg;
  74 + } else {
69 return sb.toString(); 75 return sb.toString();
70 } 76 }
71 } 77 }
  1 +package com.crossoverjie.cim.client.service.impl.command;
  2 +
  3 +import com.crossoverjie.cim.client.service.EchoService;
  4 +import com.crossoverjie.cim.client.service.InnerCommand;
  5 +import com.crossoverjie.cim.client.service.MsgHandle;
  6 +import com.crossoverjie.cim.common.data.construct.RingBufferWheel;
  7 +import com.vdurmont.emoji.EmojiParser;
  8 +import org.springframework.beans.factory.annotation.Autowired;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +/**
  12 + * Function:
  13 + *
  14 + * @author crossoverJie
  15 + * Date: 2019-09-25 00:37
  16 + * @since JDK 1.8
  17 + */
  18 +@Service
  19 +public class DelayMsgCommand implements InnerCommand {
  20 +
  21 + @Autowired
  22 + private EchoService echoService ;
  23 +
  24 + @Autowired
  25 + private MsgHandle msgHandle ;
  26 +
  27 + @Autowired
  28 + private RingBufferWheel ringBufferWheel ;
  29 +
  30 + @Override
  31 + public void process(String msg) {
  32 + if (msg.split(" ").length <=2){
  33 + echoService.echo("incorrect commond, :delay [msg] [delayTime]") ;
  34 + return ;
  35 + }
  36 +
  37 + String message = msg.split(" ")[1] ;
  38 + Integer delayTime = Integer.valueOf(msg.split(" ")[2]);
  39 +
  40 + RingBufferWheel.Task task = new DelayMsgJob(message) ;
  41 + task.setKey(delayTime);
  42 + ringBufferWheel.addTask(task);
  43 + ringBufferWheel.start();
  44 + echoService.echo(EmojiParser.parseToUnicode(msg));
  45 + }
  46 +
  47 +
  48 +
  49 + private class DelayMsgJob extends RingBufferWheel.Task{
  50 +
  51 + private String msg ;
  52 +
  53 + public DelayMsgJob(String msg) {
  54 + this.msg = msg;
  55 + }
  56 +
  57 + @Override
  58 + public void run() {
  59 + msgHandle.sendMsg(msg);
  60 + }
  61 + }
  62 +}
@@ -6,6 +6,7 @@ import com.crossoverjie.cim.client.service.InnerCommand; @@ -6,6 +6,7 @@ import com.crossoverjie.cim.client.service.InnerCommand;
6 import com.crossoverjie.cim.client.service.MsgLogger; 6 import com.crossoverjie.cim.client.service.MsgLogger;
7 import com.crossoverjie.cim.client.service.RouteRequest; 7 import com.crossoverjie.cim.client.service.RouteRequest;
8 import com.crossoverjie.cim.client.service.ShutDownMsg; 8 import com.crossoverjie.cim.client.service.ShutDownMsg;
  9 +import com.crossoverjie.cim.common.data.construct.RingBufferWheel;
9 import org.slf4j.Logger; 10 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory; 11 import org.slf4j.LoggerFactory;
11 import org.springframework.beans.factory.annotation.Autowired; 12 import org.springframework.beans.factory.annotation.Autowired;
@@ -45,6 +46,9 @@ public class ShutDownCommand implements InnerCommand { @@ -45,6 +46,9 @@ public class ShutDownCommand implements InnerCommand {
45 @Autowired 46 @Autowired
46 private ShutDownMsg shutDownMsg ; 47 private ShutDownMsg shutDownMsg ;
47 48
  49 + @Autowired
  50 + private RingBufferWheel ringBufferWheel ;
  51 +
48 @Override 52 @Override
49 public void process(String msg) { 53 public void process(String msg) {
50 echoService.echo("cim client closing..."); 54 echoService.echo("cim client closing...");
@@ -52,6 +56,7 @@ public class ShutDownCommand implements InnerCommand { @@ -52,6 +56,7 @@ public class ShutDownCommand implements InnerCommand {
52 routeRequest.offLine(); 56 routeRequest.offLine();
53 msgLogger.stop(); 57 msgLogger.stop();
54 executor.shutdown(); 58 executor.shutdown();
  59 + ringBufferWheel.stop(false);
55 try { 60 try {
56 while (!executor.awaitTermination(1, TimeUnit.SECONDS)) { 61 while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
57 echoService.echo("thread pool closing"); 62 echoService.echo("thread pool closing");
@@ -15,6 +15,11 @@ @@ -15,6 +15,11 @@
15 15
16 <dependencies> 16 <dependencies>
17 <dependency> 17 <dependency>
  18 + <groupId>ch.qos.logback</groupId>
  19 + <artifactId>logback-classic</artifactId>
  20 + </dependency>
  21 +
  22 + <dependency>
18 <groupId>com.google.protobuf</groupId> 23 <groupId>com.google.protobuf</groupId>
19 <artifactId>protobuf-java</artifactId> 24 <artifactId>protobuf-java</artifactId>
20 </dependency> 25 </dependency>
  1 +package com.crossoverjie.cim.common.data.construct;
  2 +
  3 +import org.slf4j.Logger;
  4 +import org.slf4j.LoggerFactory;
  5 +
  6 +import java.util.HashSet;
  7 +import java.util.Set;
  8 +import java.util.concurrent.ExecutorService;
  9 +import java.util.concurrent.TimeUnit;
  10 +import java.util.concurrent.atomic.AtomicInteger;
  11 +import java.util.concurrent.locks.Condition;
  12 +import java.util.concurrent.locks.Lock;
  13 +import java.util.concurrent.locks.ReentrantLock;
  14 +
  15 +/**
  16 + * Function:Ring Queue, it can be used to delay task.
  17 + *
  18 + * @author crossoverJie
  19 + * Date: 2019-09-20 14:46
  20 + * @since JDK 1.8
  21 + */
  22 +public final class RingBufferWheel {
  23 +
  24 + private Logger logger = LoggerFactory.getLogger(RingBufferWheel.class);
  25 +
  26 +
  27 + /**
  28 + * default ring buffer size
  29 + */
  30 + private static final int STATIC_RING_SIZE = 64;
  31 +
  32 + private Object[] ringBuffer;
  33 +
  34 + private int bufferSize;
  35 +
  36 + /**
  37 + * business thread pool
  38 + */
  39 + private ExecutorService executorService;
  40 +
  41 + private AtomicInteger taskSize = new AtomicInteger();
  42 +
  43 + /***
  44 + * task running sign
  45 + */
  46 + private volatile boolean stop = false;
  47 +
  48 + private Lock lock = new ReentrantLock();
  49 + private Condition condition = lock.newCondition();
  50 +
  51 + /**
  52 + * Create a new delay task ring buffer by default size
  53 + * @param executorService the business thread pool
  54 + */
  55 + public RingBufferWheel(ExecutorService executorService) {
  56 + this.executorService = executorService;
  57 + this.bufferSize = STATIC_RING_SIZE;
  58 + this.ringBuffer = new Object[bufferSize];
  59 + }
  60 +
  61 +
  62 + /**
  63 + * Create a new delay task ring buffer by custom buffer size
  64 + * @param executorService the business thread pool
  65 + * @param bufferSize custom buffer size
  66 + */
  67 + public RingBufferWheel(ExecutorService executorService, int bufferSize) {
  68 + this(executorService);
  69 +
  70 + if (!powerOf2(bufferSize)) {
  71 + throw new RuntimeException("bufferSize=[" + bufferSize + "] must be a power of 2");
  72 + }
  73 + this.bufferSize = bufferSize;
  74 + this.ringBuffer = new Object[bufferSize];
  75 + }
  76 +
  77 + /**
  78 + * Add a task into the ring buffer
  79 + * @param task business task extends RingBufferWheel.Task
  80 + */
  81 + public void addTask(Task task) {
  82 + int key = task.getKey();
  83 + Set<Task> tasks = get(key);
  84 +
  85 + if (tasks != null) {
  86 + int cycleNum = cycleNum(key, bufferSize);
  87 + task.setCycleNum(cycleNum);
  88 + tasks.add(task);
  89 + } else {
  90 + int index = mod(key, bufferSize);
  91 + int cycleNum = cycleNum(key, bufferSize);
  92 + task.setCycleNum(index);
  93 + task.setCycleNum(cycleNum);
  94 + Set<Task> sets = new HashSet<>();
  95 + sets.add(task);
  96 + put(key, sets);
  97 + }
  98 +
  99 + taskSize.incrementAndGet();
  100 +
  101 + }
  102 +
  103 + /**
  104 + * thread safe
  105 + * @return the size of ring buffer
  106 + */
  107 + public int taskSize() {
  108 + return taskSize.get();
  109 + }
  110 +
  111 + /**
  112 + * Start background thread to consumer wheel timer, it will run until you call method {@link #stop}
  113 + */
  114 + public void start() {
  115 + logger.info("delay task is starting");
  116 + Thread job = new Thread(new TriggerJob());
  117 + job.setName("consumer RingBuffer thread");
  118 + job.start();
  119 + }
  120 +
  121 + /**
  122 + * Stop consumer ring buffer thread
  123 + * @param force True will force close consumer thread and discard all pending tasks
  124 + * otherwise the consumer thread waits for all tasks to completes before closing.
  125 + */
  126 + public void stop(boolean force) {
  127 + if (force) {
  128 + logger.info("delay task is forced stop");
  129 + stop = true;
  130 + executorService.shutdownNow();
  131 + } else {
  132 + logger.info("delay task is stopping");
  133 + if (taskSize() > 0){
  134 + try {
  135 + lock.lock();
  136 + condition.await();
  137 + stop = true;
  138 + } catch (InterruptedException e) {
  139 + logger.error("InterruptedException", e);
  140 + } finally {
  141 + lock.unlock();
  142 + }
  143 + }
  144 + executorService.shutdown();
  145 + }
  146 +
  147 +
  148 + }
  149 +
  150 +
  151 + private Set<Task> get(int key) {
  152 + int index = mod(key, bufferSize);
  153 + return (Set<Task>) ringBuffer[index];
  154 + }
  155 +
  156 + private void put(int key, Set<Task> tasks) {
  157 + int index = mod(key, bufferSize);
  158 + ringBuffer[index] = tasks;
  159 + }
  160 +
  161 + private Set<Task> remove(int key) {
  162 + Set<Task> tempTask = new HashSet<>();
  163 + Set<Task> result = new HashSet<>();
  164 +
  165 + Set<Task> tasks = (Set<Task>) ringBuffer[key];
  166 + if (tasks == null) {
  167 + return result;
  168 + }
  169 +
  170 + for (Task task : tasks) {
  171 + if (task.getCycleNum() == 0) {
  172 + result.add(task);
  173 +
  174 + size2Notify();
  175 + } else {
  176 + // decrement 1 cycle number and update origin data
  177 + task.setCycleNum(task.getCycleNum() - 1);
  178 + tempTask.add(task);
  179 + }
  180 + }
  181 +
  182 + //update origin data
  183 + ringBuffer[key] = tempTask;
  184 +
  185 + return result;
  186 + }
  187 +
  188 + private void size2Notify() {
  189 + lock.lock();
  190 + int size = taskSize.decrementAndGet();
  191 + if (size == 0) {
  192 + condition.signal();
  193 + }
  194 + lock.unlock();
  195 + }
  196 +
  197 + private boolean powerOf2(int target) {
  198 + if (target < 0) {
  199 + return false;
  200 + }
  201 + int value = target & (target - 1);
  202 + if (value != 0) {
  203 + return false;
  204 + }
  205 +
  206 + return true;
  207 + }
  208 +
  209 + private int mod(int target, int mod) {
  210 + // equals target % mod
  211 + return target & (mod - 1);
  212 + }
  213 +
  214 + private int cycleNum(int target, int mod) {
  215 + //equals target/mod
  216 + return target >> Integer.bitCount(mod - 1);
  217 + }
  218 +
  219 + /**
  220 + * An abstract class used to implement business.
  221 + */
  222 + public abstract static class Task extends Thread {
  223 +
  224 +
  225 + private int cycleNum;
  226 +
  227 + private int key;
  228 +
  229 + @Override
  230 + public void run() {
  231 + }
  232 +
  233 + public int getKey() {
  234 + return key;
  235 + }
  236 +
  237 + public void setKey(int key) {
  238 + this.key = key;
  239 + }
  240 +
  241 + public int getCycleNum() {
  242 + return cycleNum;
  243 + }
  244 +
  245 + private void setCycleNum(int cycleNum) {
  246 + this.cycleNum = cycleNum;
  247 + }
  248 + }
  249 +
  250 +
  251 + private class TriggerJob implements Runnable {
  252 +
  253 + @Override
  254 + public void run() {
  255 + int index = 0;
  256 + while (!stop) {
  257 +
  258 + Set<Task> tasks = remove(index);
  259 + for (Task task : tasks) {
  260 + executorService.submit(task);
  261 + }
  262 +
  263 + if (++index > bufferSize - 1) {
  264 + index = 0;
  265 + }
  266 +
  267 + try {
  268 + TimeUnit.SECONDS.sleep(1);
  269 + } catch (InterruptedException e) {
  270 + logger.error("InterruptedException", e);
  271 + }
  272 + }
  273 +
  274 + logger.info("delay task is stopped");
  275 + }
  276 + }
  277 +}
@@ -20,7 +20,8 @@ public enum SystemCommandEnum { @@ -20,7 +20,8 @@ public enum SystemCommandEnum {
20 QAI(":qai ","关闭 AI 模式","CloseAIModelCommand"), 20 QAI(":qai ","关闭 AI 模式","CloseAIModelCommand"),
21 PREFIX(":pu ","模糊匹配用户","PrefixSearchCommand"), 21 PREFIX(":pu ","模糊匹配用户","PrefixSearchCommand"),
22 EMOJI(":emoji ","emoji 表情列表","EmojiCommand"), 22 EMOJI(":emoji ","emoji 表情列表","EmojiCommand"),
23 - INFO(":info ","获取客户端信息","EchoInfoCommand") 23 + INFO(":info ","获取客户端信息","EchoInfoCommand"),
  24 + DELAY_MSG(":delay ","delay message, :delay [msg] [delayTime]","DelayMsgCommand")
24 25
25 ; 26 ;
26 27
  1 +package com.crossoverjie.cim.common;
  2 +
  3 +import org.junit.Test;
  4 +
  5 +import java.time.LocalDate;
  6 +import java.time.LocalTime;
  7 +import java.util.concurrent.TimeUnit;
  8 +
  9 +/**
  10 + * Function:
  11 + *
  12 + * @author crossoverJie
  13 + * Date: 2019-09-23 14:21
  14 + * @since JDK 1.8
  15 + */
  16 +public class CommonTest {
  17 +
  18 +
  19 + @Test
  20 + public void test2(){
  21 + System.out.println(LocalDate.now().toString());
  22 + System.out.println(LocalTime.now().withNano(0).toString());
  23 + }
  24 +
  25 + @Test
  26 + public void test() throws InterruptedException {
  27 +
  28 +
  29 + System.out.println(is2(9));
  30 +
  31 + System.out.println(Integer.bitCount(64-1));
  32 +
  33 + int target = 1569312600 ;
  34 + int mod = 64 ;
  35 + System.out.println(target % mod);
  36 +
  37 + System.out.println(mod(target,mod));
  38 + System.out.println("============");
  39 +
  40 + System.out.println(cycleNum(256,64)) ;
  41 +
  42 + cycle();
  43 + }
  44 +
  45 +
  46 + private int mod(int target, int mod){
  47 + // equals target % mod
  48 + return target & (mod -1) ;
  49 + }
  50 +
  51 + private int cycleNum(int target,int mod){
  52 + //equals target/mod
  53 + return target >> Integer.bitCount(mod-1) ;
  54 + }
  55 +
  56 + private boolean is2(int target){
  57 + if (target < 0){
  58 + return false ;
  59 + }
  60 +
  61 + int value = target & (target - 1) ;
  62 + if (value != 0){
  63 + return false ;
  64 + }
  65 +
  66 + return true ;
  67 + }
  68 +
  69 +
  70 + private void cycle() throws InterruptedException {
  71 + int index = 0 ;
  72 + while (true){
  73 + System.out.println("=======" + index);
  74 +
  75 + if (++index > 63){
  76 + index = 0 ;
  77 + }
  78 + TimeUnit.MILLISECONDS.sleep(200);
  79 + }
  80 + }
  81 +
  82 +}
  1 +package com.crossoverjie.cim.common.data.construct;
  2 +
  3 +import org.junit.Test;
  4 +import org.slf4j.Logger;
  5 +import org.slf4j.LoggerFactory;
  6 +
  7 +import java.util.concurrent.ExecutorService;
  8 +import java.util.concurrent.Executors;
  9 +import java.util.concurrent.TimeUnit;
  10 +
  11 +public class RingBufferWheelTest {
  12 +
  13 + private static Logger logger = LoggerFactory.getLogger(RingBufferWheelTest.class) ;
  14 +
  15 + public static void main(String[] args) throws InterruptedException {
  16 + test5();
  17 +
  18 + return;
  19 + }
  20 +
  21 + private static void test1() throws InterruptedException {
  22 + ExecutorService executorService = Executors.newFixedThreadPool(2) ;
  23 +
  24 + Task task = new Task() ;
  25 + task.setKey(10);
  26 + RingBufferWheel wheel = new RingBufferWheel(executorService) ;
  27 + wheel.addTask(task) ;
  28 +
  29 + task = new Task() ;
  30 + task.setKey(74);
  31 + wheel.addTask(task) ;
  32 +
  33 + wheel.start();
  34 +
  35 + while (true){
  36 + logger.info("task size={}" , wheel.taskSize());
  37 + TimeUnit.SECONDS.sleep(1);
  38 + }
  39 + }
  40 + private static void test2() throws InterruptedException {
  41 + ExecutorService executorService = Executors.newFixedThreadPool(2) ;
  42 +
  43 + Task task = new Task() ;
  44 + task.setKey(10);
  45 + RingBufferWheel wheel = new RingBufferWheel(executorService) ;
  46 + wheel.addTask(task) ;
  47 +
  48 + task = new Task() ;
  49 + task.setKey(74);
  50 + wheel.addTask(task) ;
  51 +
  52 + wheel.start();
  53 +
  54 +// new Thread(() -> {
  55 +// while (true){
  56 +// logger.info("task size={}" , wheel.taskSize());
  57 +// try {
  58 +// TimeUnit.SECONDS.sleep(1);
  59 +// } catch (InterruptedException e) {
  60 +// e.printStackTrace();
  61 +// }
  62 +// }
  63 +// }).start();
  64 +
  65 + TimeUnit.SECONDS.sleep(12);
  66 + wheel.stop(true);
  67 +
  68 +
  69 + }
  70 + private static void test3() throws InterruptedException {
  71 + ExecutorService executorService = Executors.newFixedThreadPool(2) ;
  72 +
  73 + Task task = new Task() ;
  74 + task.setKey(10);
  75 + RingBufferWheel wheel = new RingBufferWheel(executorService) ;
  76 + wheel.addTask(task) ;
  77 +
  78 + task = new Task() ;
  79 + task.setKey(74);
  80 + wheel.addTask(task) ;
  81 +
  82 + wheel.start();
  83 +
  84 +
  85 + TimeUnit.SECONDS.sleep(2);
  86 + wheel.stop(false);
  87 +
  88 +
  89 + }
  90 + private static void test4() throws InterruptedException {
  91 + ExecutorService executorService = Executors.newFixedThreadPool(2) ;
  92 +
  93 + RingBufferWheel wheel = new RingBufferWheel(executorService) ;
  94 +
  95 + for (int i = 0; i < 65; i++) {
  96 + Job task = new Job(i) ;
  97 + task.setKey(i);
  98 + wheel.addTask(task);
  99 + }
  100 +
  101 + wheel.start();
  102 +
  103 + logger.info("task size={}",wheel.taskSize());
  104 +
  105 + wheel.stop(false);
  106 +
  107 +
  108 + }
  109 + private static void test5() throws InterruptedException {
  110 + ExecutorService executorService = Executors.newFixedThreadPool(2) ;
  111 +
  112 + RingBufferWheel wheel = new RingBufferWheel(executorService,512) ;
  113 +
  114 + for (int i = 0; i < 65; i++) {
  115 + Job task = new Job(i) ;
  116 + task.setKey(i);
  117 + wheel.addTask(task);
  118 + }
  119 +
  120 + wheel.start();
  121 +
  122 + logger.info("task size={}",wheel.taskSize());
  123 +
  124 + wheel.stop(false);
  125 +
  126 +
  127 + }
  128 +
  129 +
  130 + private static class Task extends RingBufferWheel.Task{
  131 +
  132 + @Override
  133 + public void run() {
  134 + logger.info("================");
  135 + }
  136 + }
  137 +
  138 + private static class Job extends RingBufferWheel.Task{
  139 +
  140 + private int num ;
  141 +
  142 + public Job(int num) {
  143 + this.num = num;
  144 + }
  145 +
  146 + @Override
  147 + public void run() {
  148 + logger.info("number={}" , num);
  149 + }
  150 + }
  151 +}