作者 crossoverJie
提交者 GitHub

cim 1.0.5 (#51)

cim 1.0.5
正在显示 38 个修改的文件 包含 404 行增加789 行删除
... ... @@ -87,6 +87,12 @@
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>emoji-java</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>
<build>
... ...
... ... @@ -2,6 +2,7 @@ package com.crossoverjie.cim.client.client;
import com.crossoverjie.cim.client.config.AppConfiguration;
import com.crossoverjie.cim.client.init.CIMClientHandleInitializer;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.MsgHandle;
import com.crossoverjie.cim.client.service.RouteRequest;
import com.crossoverjie.cim.client.service.impl.ClientInfo;
... ... @@ -51,6 +52,9 @@ public class CIMClient {
private SocketChannel channel;
@Autowired
private EchoService echoService ;
@Autowired
private RouteRequest routeRequest;
@Autowired
... ... @@ -102,12 +106,13 @@ public class CIMClient {
errorCount++;
if (errorCount >= configuration.getErrorCount()) {
LOGGER.error("接失败次数达到上限[{}]次", errorCount);
LOGGER.error("接失败次数达到上限[{}]次", errorCount);
msgHandle.shutdown();
}
LOGGER.error("连接失败", e);
}
if (future.isSuccess()) {
echoService.echo("start cim client success!");
LOGGER.info("启动 cim client 成功");
}
channel = (SocketChannel) future.channel();
... ... @@ -137,7 +142,7 @@ public class CIMClient {
LOGGER.error("重连次数达到上限[{}]次", errorCount);
msgHandle.shutdown();
}
LOGGER.error("登录失败", e);
LOGGER.error("login fail", e);
}
return cimServer;
}
... ... @@ -153,7 +158,8 @@ public class CIMClient {
.build();
ChannelFuture future = channel.writeAndFlush(login);
future.addListener((ChannelFutureListener) channelFuture ->
LOGGER.info("注册成功={}", login.toString()));
echoService.echo("registry cim server success!")
);
}
/**
... ... @@ -198,9 +204,9 @@ public class CIMClient {
//首先清除路由信息,下线
routeRequest.offLine();
LOGGER.info("开始重连。。");
LOGGER.info("reconnect....");
start();
LOGGER.info("重连成功!!");
LOGGER.info("reconnect success");
}
/**
... ...
package com.crossoverjie.cim.client.constant;
/**
* Function:
*
* @author crossoverJie
* Date: 2019-08-24 22:53
* @since JDK 1.8
*/
public class Emoji {
}
... ...
... ... @@ -7,6 +7,7 @@ import com.crossoverjie.cim.common.constant.Constants;
import com.crossoverjie.cim.common.protocol.CIMRequestProto;
import com.crossoverjie.cim.common.protocol.CIMResponseProto;
import com.crossoverjie.cim.common.util.NettyAttrUtil;
import com.vdurmont.emoji.EmojiParser;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
... ... @@ -105,7 +106,9 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt
//回调消息
callBackMsg(msg.getResMsg());
LOGGER.info(msg.getResMsg());
//将消息中的 emoji 表情格式化为 Unicode 编码以便在终端可以显示
String response = EmojiParser.parseToUnicode(msg.getResMsg());
System.out.println(response);
}
... ...
package com.crossoverjie.cim.client.scanner;
import com.crossoverjie.cim.client.config.AppConfiguration;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.MsgHandle;
import com.crossoverjie.cim.client.service.MsgLogger;
import com.crossoverjie.cim.client.util.SpringBeanFactory;
import com.vdurmont.emoji.EmojiParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
... ... @@ -29,10 +31,13 @@ public class Scan implements Runnable {
private MsgLogger msgLogger ;
private EchoService echoService ;
public Scan() {
this.configuration = SpringBeanFactory.getBean(AppConfiguration.class);
this.msgHandle = SpringBeanFactory.getBean(MsgHandle.class) ;
this.msgLogger = SpringBeanFactory.getBean(MsgLogger.class) ;
this.echoService = SpringBeanFactory.getBean(EchoService.class) ;
}
@Override
... ... @@ -57,7 +62,7 @@ public class Scan implements Runnable {
//写入聊天记录
msgLogger.log(msg) ;
LOGGER.info("{}:【{}】", configuration.getUserName(), msg);
echoService.echo(EmojiParser.parseToUnicode(msg));
}
}
... ...
package com.crossoverjie.cim.client.service;
/**
* Function:
*
* @author crossoverJie
* Date: 2019-08-27 22:35
* @since JDK 1.8
*/
public interface EchoService {
/**
* echo msg to terminal
* @param msg message
* @param replace
*/
void echo(String msg, Object... replace) ;
}
... ...
... ... @@ -38,7 +38,7 @@ public class ClientInfo {
return this;
}
private class Info{
public class Info{
private String userName;
private long userId ;
private String serviceInfo ;
... ...
package com.crossoverjie.cim.client.service.impl;
import com.crossoverjie.cim.client.config.AppConfiguration;
import com.crossoverjie.cim.client.service.EchoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Function:
*
* @author crossoverJie
* Date: 2019-08-27 22:37
* @since JDK 1.8
*/
@Service
public class EchoServiceImpl implements EchoService {
private static final String PREFIX = "$";
@Autowired
private AppConfiguration appConfiguration;
@Override
public void echo(String msg,Object... replace) {
msg = "\033[31;4m" + appConfiguration.getUserName() + PREFIX + "\033[0m" + " " + msg;
String log = print(msg, replace);
System.out.println(log);
}
/**
* print msg
* @param msg
* @param place
* @return
*/
private String print(String msg, Object... place) {
StringBuilder sb = new StringBuilder();
int k = 0;
for (int i = 0; i < place.length; i++) {
int index = msg.indexOf("{}", k);
if (index == -1){
return msg;
}
if (index != 0) {
sb.append(msg, k, index);
sb.append(place[i]);
if (place.length == 1) {
sb.append(msg, index + 2, msg.length());
}
} else {
sb.append(place[i]);
if (place.length == 1) {
sb.append(msg, index + 2, msg.length());
}
}
k = index + 2;
}
if (sb.toString().equals("")){
return msg ;
}else {
return sb.toString();
}
}
}
... ...
... ... @@ -2,12 +2,13 @@ package com.crossoverjie.cim.client.service.impl;
import com.crossoverjie.cim.client.client.CIMClient;
import com.crossoverjie.cim.client.config.AppConfiguration;
import com.crossoverjie.cim.client.service.*;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.InnerCommandContext;
import com.crossoverjie.cim.client.service.MsgHandle;
import com.crossoverjie.cim.client.service.MsgLogger;
import com.crossoverjie.cim.client.service.RouteRequest;
import com.crossoverjie.cim.client.vo.req.GroupReqVO;
import com.crossoverjie.cim.client.vo.req.P2PReqVO;
import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO;
import com.crossoverjie.cim.common.data.construct.TrieTree;
import com.crossoverjie.cim.common.enums.SystemCommandEnum;
import com.crossoverjie.cim.common.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
... ... @@ -15,8 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
... ... @@ -144,63 +143,6 @@ public class MsgHandler implements MsgHandle {
}
/**
* 模糊匹配
*
* @param msg
*/
private void prefixSearch(String msg) {
try {
List<OnlineUsersResVO.DataBodyBean> onlineUsers = routeRequest.onlineUsers();
TrieTree trieTree = new TrieTree();
for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) {
trieTree.insert(onlineUser.getUserName());
}
String[] split = msg.split(" ");
String key = split[1];
List<String> list = trieTree.prefixSearch(key);
for (String res : list) {
res = res.replace(key, "\033[31;4m" + key + "\033[0m");
System.out.println(res);
}
} catch (Exception e) {
LOGGER.error("Exception", e);
}
}
/**
* 查询聊天记录
*
* @param msg
*/
private void queryChatHistory(String msg) {
String[] split = msg.split(" ");
String res = msgLogger.query(split[1]);
System.out.println(res);
}
/**
* 打印在线用户
*/
private void printOnlineUsers() {
try {
List<OnlineUsersResVO.DataBodyBean> onlineUsers = routeRequest.onlineUsers();
LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) {
LOGGER.info("userId={}=====userName={}", onlineUser.getUserId(), onlineUser.getUserName());
}
LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
} catch (Exception e) {
LOGGER.error("Exception", e);
}
}
/**
* 关闭系统
*/
... ... @@ -231,13 +173,4 @@ public class MsgHandler implements MsgHandle {
aiModel = false ;
}
private void printAllCommand(Map<String, String> allStatusCode) {
LOGGER.warn("====================================");
for (Map.Entry<String, String> stringStringEntry : allStatusCode.entrySet()) {
String key = stringStringEntry.getKey();
String value = stringStringEntry.getValue();
LOGGER.warn(key + "----->" + value);
}
LOGGER.warn("====================================");
}
}
... ...
... ... @@ -3,6 +3,7 @@ package com.crossoverjie.cim.client.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crossoverjie.cim.client.config.AppConfiguration;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.RouteRequest;
import com.crossoverjie.cim.client.vo.req.GroupReqVO;
import com.crossoverjie.cim.client.vo.req.LoginReqVO;
... ... @@ -50,6 +51,8 @@ public class RouteRequestImpl implements RouteRequest {
@Value("${cim.server.online.user.url}")
private String onlineUserUrl;
@Autowired
private EchoService echoService ;
@Autowired
... ... @@ -136,7 +139,7 @@ public class RouteRequestImpl implements RouteRequest {
//重复失败
if (!cimServerResVO.getCode().equals(StatusEnum.SUCCESS.getCode())){
LOGGER.error(appConfiguration.getUserName() + ":" + cimServerResVO.getMessage());
echoService.echo(cimServerResVO.getMessage());
System.exit(-1);
}
... ...
package com.crossoverjie.cim.client.service.impl.command;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.MsgHandle;
import org.slf4j.Logger;
... ... @@ -22,9 +23,13 @@ public class CloseAIModelCommand implements InnerCommand {
@Autowired
private MsgHandle msgHandle ;
@Autowired
private EchoService echoService ;
@Override
public void process(String msg) {
msgHandle.closeAIModel();
System.out.println("\033[31;4m" + "。゚(゚´ω`゚)゚。 AI 下线了!" + "\033[0m");
echoService.echo("\033[31;4m" + "。゚(゚´ω`゚)゚。 AI 下线了!" + "\033[0m");
}
}
... ...
package com.crossoverjie.cim.client.service.impl.command;
import com.alibaba.fastjson.JSON;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.impl.ClientInfo;
import org.slf4j.Logger;
... ... @@ -23,10 +23,13 @@ public class EchoInfoCommand implements InnerCommand {
@Autowired
private ClientInfo clientInfo;
@Autowired
private EchoService echoService ;
@Override
public void process(String msg) {
LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
LOGGER.info("client info=[{}]", JSON.toJSONString(clientInfo.get()));
LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
echoService.echo("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
echoService.echo("client info={}", clientInfo.get().getUserName());
echoService.echo("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
}
}
... ...
package com.crossoverjie.cim.client.service.impl.command;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.vdurmont.emoji.Emoji;
import com.vdurmont.emoji.EmojiManager;
import com.vdurmont.emoji.EmojiParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Function:
*
* @author crossoverJie
* Date: 2019-01-27 19:37
* @since JDK 1.8
*/
@Service
public class EmojiCommand implements InnerCommand {
private final static Logger LOGGER = LoggerFactory.getLogger(EmojiCommand.class);
@Autowired
private EchoService echoService ;
@Override
public void process(String msg) {
if (msg.split(" ").length <=1){
echoService.echo("incorrect commond, :emoji [option]") ;
return ;
}
String value = msg.split(" ")[1];
if (value != null) {
Integer index = Integer.parseInt(value);
List<Emoji> all = (List<Emoji>) EmojiManager.getAll();
all = all.subList(5 * index, 5 * index + 5);
for (Emoji emoji : all) {
echoService.echo(EmojiParser.parseToAliases(emoji.getUnicode()) + "--->" + emoji.getUnicode());
}
}
}
}
... ...
package com.crossoverjie.cim.client.service.impl.command;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.RouteRequest;
import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO;
... ... @@ -25,6 +26,8 @@ public class PrefixSearchCommand implements InnerCommand {
@Autowired
private RouteRequest routeRequest ;
@Autowired
private EchoService echoService ;
@Override
public void process(String msg) {
... ... @@ -41,7 +44,7 @@ public class PrefixSearchCommand implements InnerCommand {
for (String res : list) {
res = res.replace(key, "\033[31;4m" + key + "\033[0m");
System.out.println(res);
echoService.echo(res) ;
}
} catch (Exception e) {
... ...
package com.crossoverjie.cim.client.service.impl.command;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.common.enums.SystemCommandEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
... ... @@ -19,15 +21,18 @@ import java.util.Map;
public class PrintAllCommand implements InnerCommand {
private final static Logger LOGGER = LoggerFactory.getLogger(PrintAllCommand.class);
@Autowired
private EchoService echoService ;
@Override
public void process(String msg) {
Map<String, String> allStatusCode = SystemCommandEnum.getAllStatusCode();
LOGGER.warn("====================================");
echoService.echo("====================================");
for (Map.Entry<String, String> stringStringEntry : allStatusCode.entrySet()) {
String key = stringStringEntry.getKey();
String value = stringStringEntry.getValue();
LOGGER.warn(key + "----->" + value);
echoService.echo(key + "----->" + value);
}
LOGGER.warn("====================================");
echoService.echo("====================================");
}
}
... ...
package com.crossoverjie.cim.client.service.impl.command;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.RouteRequest;
import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO;
... ... @@ -25,16 +26,19 @@ public class PrintOnlineUsersCommand implements InnerCommand {
@Autowired
private RouteRequest routeRequest ;
@Autowired
private EchoService echoService ;
@Override
public void process(String msg) {
try {
List<OnlineUsersResVO.DataBodyBean> onlineUsers = routeRequest.onlineUsers();
LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
echoService.echo("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
for (OnlineUsersResVO.DataBodyBean onlineUser : onlineUsers) {
LOGGER.info("userId={}=====userName={}", onlineUser.getUserId(), onlineUser.getUserName());
echoService.echo("userId={}=====userName={}",onlineUser.getUserId(),onlineUser.getUserName());
}
LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
echoService.echo("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
} catch (Exception e) {
LOGGER.error("Exception", e);
... ...
package com.crossoverjie.cim.client.service.impl.command;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.MsgLogger;
import org.slf4j.Logger;
... ... @@ -22,6 +23,9 @@ public class QueryHistoryCommand implements InnerCommand {
@Autowired
private MsgLogger msgLogger ;
@Autowired
private EchoService echoService ;
@Override
public void process(String msg) {
String[] split = msg.split(" ");
... ... @@ -29,6 +33,6 @@ public class QueryHistoryCommand implements InnerCommand {
return;
}
String res = msgLogger.query(split[1]);
System.out.println(res);
echoService.echo(res);
}
}
... ...
package com.crossoverjie.cim.client.service.impl.command;
import com.crossoverjie.cim.client.client.CIMClient;
import com.crossoverjie.cim.client.service.EchoService;
import com.crossoverjie.cim.client.service.InnerCommand;
import com.crossoverjie.cim.client.service.MsgLogger;
import com.crossoverjie.cim.client.service.RouteRequest;
... ... @@ -37,25 +38,29 @@ public class ShutDownCommand implements InnerCommand {
@Resource(name = "callBackThreadPool")
private ThreadPoolExecutor executor;
@Autowired
private EchoService echoService ;
@Autowired
private ShutDownMsg shutDownMsg ;
@Override
public void process(String msg) {
LOGGER.info("系统关闭中。。。。");
echoService.echo("cim client closing...");
shutDownMsg.shutdown();
routeRequest.offLine();
msgLogger.stop();
executor.shutdown();
try {
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
LOGGER.info("线程池关闭中。。。。");
echoService.echo("thread pool closing");
}
cimClient.close();
} catch (InterruptedException e) {
LOGGER.error("InterruptedException", e);
}
echoService.echo("cim close success!");
System.exit(0);
}
}
... ...
... ... @@ -6,47 +6,47 @@ server.port=8082
# 是否打开swagger
swagger.enable = true
logging.level.root=info
logging.level.root=error
#消息记录存放路径
cim.msg.logger.path = /opt/logs/cim/
###=======生产模拟======###
# 群发消息
cim.group.route.request.url=http://45.78.28.220:8083/groupRoute
# 私聊消息
cim.p2p.route.request.url=http://45.78.28.220:8083/p2pRoute
# 登录并获取服务器ip+port
cim.server.route.request.url=http://45.78.28.220:8083/login
# 在线用户
cim.server.online.user.url=http://45.78.28.220:8083/onlineUser
# 清除路由信息
cim.clear.route.request.url=http://45.78.28.220:8083/offLine
###=======本地模拟======###
## 群发消息
#cim.group.route.request.url=http://localhost:8083/groupRoute
#cim.group.route.request.url=http://45.78.28.220:8083/groupRoute
#
## 私聊消息
#cim.p2p.route.request.url=http://localhost:8083/p2pRoute
#cim.p2p.route.request.url=http://45.78.28.220:8083/p2pRoute
#
## 登录并获取服务器ip+port
#cim.server.route.request.url=http://localhost:8083/login
#cim.server.route.request.url=http://45.78.28.220:8083/login
#
## 在线用户
#cim.server.online.user.url=http://localhost:8083/onlineUser
#cim.server.online.user.url=http://45.78.28.220:8083/onlineUser
#
## 清除路由信息
#cim.clear.route.request.url=http://45.78.28.220:8083/offLine
###=======本地模拟======###
# 群发消息
cim.group.route.request.url=http://localhost:8083/groupRoute
# 私聊消息
cim.p2p.route.request.url=http://localhost:8083/p2pRoute
# 登录并获取服务器ip+port
cim.server.route.request.url=http://localhost:8083/login
# 在线用户
cim.server.online.user.url=http://localhost:8083/onlineUser
# 清除路由信息
#cim.clear.route.request.url=http://localhost:8083/offLine
清除路由信息
cim.clear.route.request.url=http://localhost:8083/offLine
# 客户端唯一ID
cim.user.id=1551267098213
cim.user.userName=test3
cim.user.id=1566914867344
cim.user.userName=zhangsan
# 回调线程队列大小
cim.callback.thread.queue.size = 2
... ...
... ... @@ -4,13 +4,18 @@ package com.crossoverjie.cim.server.test;
import com.alibaba.fastjson.JSON;
import com.crossoverjie.cim.client.vo.res.CIMServerResVO;
import com.crossoverjie.cim.client.vo.res.OnlineUsersResVO;
import com.vdurmont.emoji.EmojiParser;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
... ... @@ -179,5 +184,24 @@ public class CommonTest {
} catch (IOException e) {
LOGGER.info("IOException", e);
}
}
@Test
public void emoji() throws Exception{
String str = "An :grinning:awesome :smiley:string &#128516;with a few :wink:emojis!";
String result = EmojiParser.parseToUnicode(str);
System.out.println(result);
result = EmojiParser.parseToAliases(str);
System.out.println(result);
//
// Collection<Emoji> all = EmojiManager.getAll();
// for (Emoji emoji : all) {
// System.out.println(EmojiParser.parseToAliases(emoji.getUnicode()) + "--->" + emoji.getUnicode() );
// }
}
}
... ...
package com.crossoverjie.cim.server.test;
import org.junit.Assert;
import org.junit.Test;
/**
* Function:
*
* @author crossoverJie
* Date: 2019-08-28 01:47
* @since JDK 1.8
*/
public class EchoTest {
@Test
public void echo() {
String msg = "{} say,you {}";
String[] place = {"zhangsan", "haha"};
String log = log(msg, place);
System.out.println(log);
Assert.assertEquals(log,"zhangsan say,you haha");
}
@Test
public void echo2() {
String msg = "{} say,you {},zhangsan say {}";
String[] place = {"zhangsan", "haha", "nihao"};
String log = log(msg, place);
System.out.println(log);
Assert.assertEquals(log,"zhangsan say,you haha,zhangsan say nihao");
}
@Test
public void echo3() {
String msg = "see you {},zhangsan say";
String[] place = {"zhangsan"};
String log = log(msg, place);
System.out.println(log);
Assert.assertEquals(log,"see you zhangsan,zhangsan say");
}
@Test
public void echo4() {
String msg = "{}see you,zhangsan say";
String[] place = {"!!!"};
String log = log(msg, place);
System.out.println(log);
Assert.assertEquals(log,"!!!see you,zhangsan say");
}
@Test
public void echo5() {
String msg = "see you,zhangsan say{}";
String[] place = {"!!!"};
String log = log(msg, place);
System.out.println(log);
Assert.assertEquals(log,"see you,zhangsan say!!!");
}
@Test
public void echo6() {
String msg = "see you,zhangsan say";
String[] place = {""};
String log = log(msg, place);
System.out.println(log);
Assert.assertEquals(log,"see you,zhangsan say");
}
private String log(String msg, String... place) {
StringBuilder sb = new StringBuilder();
int k = 0;
for (int i = 0; i < place.length; i++) {
int index = msg.indexOf("{}", k);
if (index == -1){
return msg;
}
if (index != 0) {
sb.append(msg, k, index);
sb.append(place[i]);
if (place.length == 1) {
sb.append(msg, index + 2, msg.length());
}
} else {
sb.append(place[i]);
if (place.length == 1) {
sb.append(msg, index + 2, msg.length());
}
}
k = index + 2;
}
return sb.toString();
}
}
... ...
... ... @@ -19,6 +19,7 @@ public enum SystemCommandEnum {
AI(":ai ","开启 AI 模式","OpenAIModelCommand"),
QAI(":qai ","关闭 AI 模式","CloseAIModelCommand"),
PREFIX(":pu ","模糊匹配用户","PrefixSearchCommand"),
EMOJI(":emoji ","emoji 表情列表","EmojiCommand"),
INFO(":info ","获取客户端信息","EchoInfoCommand")
;
... ...
... ... @@ -153,7 +153,7 @@ public class AccountServiceRedisImpl implements AccountService {
CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfoByUserId(sendUserId);
JSONObject jsonObject = new JSONObject();
jsonObject.put("msg", cimUserInfo.getUserName() + ":【" + groupReqVO.getMsg() + "】");
jsonObject.put("msg", cimUserInfo.getUserName() + ":" + groupReqVO.getMsg());
jsonObject.put("userId", groupReqVO.getUserId());
RequestBody requestBody = RequestBody.create(mediaType, jsonObject.toString());
... ...
... ... @@ -13,7 +13,7 @@ logging.level.root=info
management.security.enabled=false
# zk 地址
app.zk.addr=47.98.194.60:2182
app.zk.addr=ip:port
# zk 连接超时时限
app.zk.connect.timeout=15000
... ... @@ -37,10 +37,11 @@ app.route.way=com.crossoverjie.cim.common.route.algorithm.consistenthash.Consist
app.route.way.consitenthash=com.crossoverjie.cim.common.route.algorithm.consistenthash.TreeMapConsistentHash
# Redis 配置
spring.redis.host=47.98.194.60
spring.redis.host=xx
spring.redis.port=6379
spring.redis.pool.max-active=100
spring.redis.pool.max-idle=100
spring.redis.pool.max-wait=1000
spring.redis.pool.min-idle=10
spring.redis.password=xx
... ...
... ... @@ -25,7 +25,7 @@ monitor.channel.map.key=channelMap
app.zk.switch=true
# zk 地址
app.zk.addr=47.98.194.60:2182
app.zk.addr=ip:port
# zk 连接超时时限
app.zk.connect.timeout=15000
... ... @@ -36,5 +36,5 @@ app.zk.root=/route
# 清除路由信息
cim.clear.route.request.url=http://localhost:8083/offLine
# 检测多少秒没有收到客户端心跳后服务端关闭连接
# 检测多少秒没有收到客户端心跳后服务端关闭连接 单位秒
cim.heartbeat.time = 30
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cim</artifactId>
<groupId>com.crossoverjie.netty</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cim-zk</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.crossoverjie.netty</groupId>
<artifactId>cim-common</artifactId>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- spring-boot-maven-plugin (提供了直接运行项目的插件:如果是通过parent方式继承spring-boot-starter-parent则不用此插件) -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package com.crossoverjie.cim.server.zk;
import com.crossoverjie.cim.server.zk.util.AppConfiguration;
import com.crossoverjie.cim.server.zk.thread.RegistryZK;
import com.crossoverjie.cim.server.zk.util.ZKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.net.InetAddress;
/**
* @author crossoverJie
*/
@SpringBootApplication
public class Application implements CommandLineRunner{
private final static Logger LOGGER = LoggerFactory.getLogger(Application.class);
@Autowired
private AppConfiguration appConfiguration ;
@Autowired
private static ZKit zkUtil ;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
LOGGER.info("启动应用成功");
}
@Override
public void run(String... args) throws Exception {
//获得本机IP
String addr = InetAddress.getLocalHost().getHostAddress();
Thread thread = new Thread(new RegistryZK(addr, appConfiguration.getPort()));
thread.setName("registry-zk");
//thread.start() ;
}
}
\ No newline at end of file
package com.crossoverjie.cim.server.zk.cache;
import com.crossoverjie.cim.server.zk.util.ZKit;
import com.google.common.cache.LoadingCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/**
* Function: 服务器节点缓存
*
* @author crossoverJie
* Date: 2018/8/19 01:31
* @since JDK 1.8
*/
@Component
public class ServerCache {
@Autowired
private LoadingCache<String, String> cache;
@Autowired
private ZKit zkUtil;
private AtomicLong index = new AtomicLong();
public void addCache(String key) {
cache.put(key, key);
}
/**
* 更新所有缓存/先删除 再新增
*
* @param currentChilds
*/
public void updateCache(List<String> currentChilds) {
cache.invalidateAll();
for (String currentChild : currentChilds) {
String key = currentChild.split("-")[1];
addCache(key);
}
}
/**
* 获取所有的服务列表
*
* @return
*/
public List<String> getAll() {
List<String> list = new ArrayList<>();
if (cache.size() == 0) {
List<String> allNode = zkUtil.getAllNode();
for (String node : allNode) {
String key = node.split("-")[1];
addCache(key);
}
}
for (Map.Entry<String, String> entry : cache.asMap().entrySet()) {
list.add(entry.getKey());
}
return list;
}
/**
* 选取服务器
*
* @return
*/
public String selectServer() {
List<String> all = getAll();
if (all.size() == 0) {
throw new RuntimeException("路由列表为空");
}
Long position = index.incrementAndGet() % all.size();
if (position < 0) {
position = 0L;
}
return all.get(position.intValue());
}
}
package com.crossoverjie.cim.server.zk.config;
import com.crossoverjie.cim.server.zk.util.AppConfiguration;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.I0Itec.zkclient.ZkClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Function:
*
* @author crossoverJie
* Date: 2018/8/24 01:28
* @since JDK 1.8
*/
@Configuration
public class AppConfig {
@Autowired
private AppConfiguration appConfiguration ;
@Bean
public ZkClient buildZKClient(){
return new ZkClient(appConfiguration.getZkAddr(), 5000);
}
@Bean
public LoadingCache<String,String> buildCache(){
return CacheBuilder.newBuilder()
.build(new CacheLoader<String, String>() {
@Override
public String load(String s) throws Exception {
return null;
}
});
}
}
package com.crossoverjie.cim.server.zk.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
/** 是否打开swagger **/
@ConditionalOnExpression("'${swagger.enable}' == 'true'")
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.crossoverjie.netty.action.zk.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("netty-action-zk api")
.description("netty-action-zk api")
.termsOfServiceUrl("https://crossoverJie.top")
.contact("crossoverJie")
.version("1.0.0")
.build();
}
}
\ No newline at end of file
package com.crossoverjie.cim.server.zk.controller;
import com.crossoverjie.cim.common.enums.StatusEnum;
import com.crossoverjie.cim.common.res.BaseResponse;
import com.crossoverjie.cim.server.zk.cache.ServerCache;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* Function:
*
* @author crossoverJie
* Date: 22/05/2018 14:46
* @since JDK 1.8
*/
@Controller
@RequestMapping("/")
public class IndexController {
@Autowired
private ServerCache serverCache ;
/**
* 获取所有路由节点
* @return
*/
@ApiOperation("获取所有路由节点")
@RequestMapping(value = "getAllRoute",method = RequestMethod.POST)
@ResponseBody()
public BaseResponse<List<String>> getAllRoute(){
BaseResponse<List<String>> res = new BaseResponse();
List<String> all = serverCache.getAll();
res.setDataBody(all);
res.setCode(StatusEnum.SUCCESS.getCode()) ;
res.setMessage(StatusEnum.SUCCESS.getMessage()) ;
return res ;
}
/**
* 获取所有路由节点
* @return
*/
@ApiOperation("获取所有路由节点")
@RequestMapping(value = "getOneOfRoute",method = RequestMethod.POST)
@ResponseBody()
public BaseResponse<String> getOneOfRoute(){
BaseResponse<String> res = new BaseResponse();
String server = serverCache.selectServer();
res.setDataBody(server);
res.setCode(StatusEnum.SUCCESS.getCode()) ;
res.setMessage(StatusEnum.SUCCESS.getMessage()) ;
return res ;
}
}
package com.crossoverjie.cim.server.zk.thread;
import com.crossoverjie.cim.server.zk.util.AppConfiguration;
import com.crossoverjie.cim.server.zk.util.SpringBeanFactory;
import com.crossoverjie.cim.server.zk.util.ZKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Function:
*
* @author crossoverJie
* Date: 2018/8/24 01:37
* @since JDK 1.8
*/
public class RegistryZK implements Runnable {
private static Logger logger = LoggerFactory.getLogger(RegistryZK.class);
private ZKit zkUtil;
private AppConfiguration appConfiguration ;
private String ip;
private int port;
public RegistryZK(String ip, int port) {
this.ip = ip;
this.port = port;
zkUtil = SpringBeanFactory.getBean(ZKit.class) ;
appConfiguration = SpringBeanFactory.getBean(AppConfiguration.class) ;
}
@Override
public void run() {
//创建父节点
zkUtil.createRootNode();
//是否要将自己注册到 ZK
if (appConfiguration.isZkSwitch()){
String path = appConfiguration.getZkRoot() + "/ip-" + ip + ":" + port;
zkUtil.createNode(path, path);
logger.info("注册 zookeeper 成功,msg=[{}]", path);
}
//注册监听服务
zkUtil.subscribeEvent(appConfiguration.getZkRoot());
}
}
\ No newline at end of file
package com.crossoverjie.cim.server.zk.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* Function:
*
* @author crossoverJie
* Date: 2018/8/24 01:43
* @since JDK 1.8
*/
@Component
public class AppConfiguration {
@Value("${app.zk.root}")
private String zkRoot;
@Value("${app.zk.addr}")
private String zkAddr;
@Value("${app.zk.switch}")
private boolean zkSwitch;
@Value("${server.port}")
private int port;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getZkRoot() {
return zkRoot;
}
public void setZkRoot(String zkRoot) {
this.zkRoot = zkRoot;
}
public String getZkAddr() {
return zkAddr;
}
public void setZkAddr(String zkAddr) {
this.zkAddr = zkAddr;
}
public boolean isZkSwitch() {
return zkSwitch;
}
public void setZkSwitch(boolean zkSwitch) {
this.zkSwitch = zkSwitch;
}
}
package com.crossoverjie.cim.server.zk.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public final class SpringBeanFactory implements ApplicationContextAware{
private static ApplicationContext context;
public static <T> T getBean(Class<T> c){
return context.getBean(c);
}
public static <T> T getBean(String name,Class<T> clazz){
return context.getBean(name,clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
package com.crossoverjie.cim.server.zk.util;
import com.alibaba.fastjson.JSON;
import com.crossoverjie.cim.server.zk.cache.ServerCache;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* Function: Zookeeper 工具
*
* @author crossoverJie
* Date: 2018/8/19 00:33
* @since JDK 1.8
*/
@Component
public class ZKit {
private static Logger logger = LoggerFactory.getLogger(ZKit.class);
@Autowired
private ZkClient zkClient;
@Autowired
private AppConfiguration appConfiguration ;
@Autowired
private ServerCache serverCache ;
/**
* 创建父级节点
*/
public void createRootNode(){
boolean exists = zkClient.exists(appConfiguration.getZkRoot());
if (exists){
return;
}
//创建 root
zkClient.createPersistent(appConfiguration.getZkRoot()) ;
}
/**
* 写入指定节点 临时目录
*
* @param path
* @param value
*/
public void createNode(String path, String value) {
zkClient.createEphemeral(path, value);
}
/**
* 监听事件
*
* @param path
*/
public void subscribeEvent(String path) {
zkClient.subscribeChildChanges(path, new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
logger.info("清除/更新本地缓存 parentPath=【{}】,currentChilds=【{}】", parentPath,currentChilds.toString());
//更新所有缓存/先删除 再新增
serverCache.updateCache(currentChilds) ;
}
});
}
/**
* 获取所有注册节点
* @return
*/
public List<String> getAllNode(){
List<String> children = zkClient.getChildren("/route");
logger.info("查询所有节点成功=【{}】", JSON.toJSONString(children));
return children;
}
/**
* 关闭 ZK
*/
public void closeZK() {
logger.info("正在关闭 ZK");
zkClient.close();
logger.info("关闭 ZK 成功");
}
}
spring.application.name=cim-zk
# web port
server.port=9083
# 是否打开swagger
swagger.enable = true
logging.level.root=info
# 是否注册 zk
app.zk.switch=true
# zk 地址
app.zk.addr=47.98.194.60:2181
# zk 注册根节点
app.zk.root=/route
\ No newline at end of file
... ... @@ -13,7 +13,8 @@
```
spring.redis.host=47.98.194.60
spring.redis.port=6379
spring.redis.host=xx

spring.redis.port=6379
```
其实所有的配置都是通过 `SpringBoot` 来加载的,看这个配置就知道了。
... ...
... ... @@ -30,7 +30,6 @@
<module>cim-server</module>
<module>cim-client</module>
<module>cim-common</module>
<module>cim-zk</module>
<module>cim-forward-route</module>
</modules>
... ... @@ -107,4 +106,17 @@
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
... ...