提交者
GitHub
Merge pull request #57 from crossoverJie/cim-1.0.6
cim 1.0.6
正在显示
11 个修改的文件
包含
615 行增加
和
10 行删除
| @@ -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 | } |
cim-client/src/main/java/com/crossoverjie/cim/client/service/impl/command/DelayMsgCommand.java
0 → 100644
| 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 | +} |
cim-common/src/test/java/com/crossoverjie/cim/common/data/construct/RingBufferWheelTest.java
0 → 100644
| 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 | +} |
-
请 注册 或 登录 后发表评论