作者 crossoverJie

:sparkles: Introducing new features.新增拒绝重复登录

@@ -50,7 +50,9 @@ public class CIMClient { @@ -50,7 +50,9 @@ public class CIMClient {
50 private RouteRequest routeRequest ; 50 private RouteRequest routeRequest ;
51 51
52 @PostConstruct 52 @PostConstruct
53 - public void start() throws Exception { 53 + public void start() {
  54 +
  55 + try {
54 //登录 + 获取可以使用的服务器 ip+port 56 //登录 + 获取可以使用的服务器 ip+port
55 CIMServerResVO.ServerInfo cimServer = userLogin(); 57 CIMServerResVO.ServerInfo cimServer = userLogin();
56 58
@@ -59,6 +61,11 @@ public class CIMClient { @@ -59,6 +61,11 @@ public class CIMClient {
59 61
60 //向服务端注册 62 //向服务端注册
61 loginCIMServer(); 63 loginCIMServer();
  64 +
  65 + }catch (Exception e){
  66 + LOGGER.error("Exception",e);
  67 + }
  68 +
62 } 69 }
63 70
64 /** 71 /**
@@ -7,6 +7,7 @@ import com.crossoverjie.cim.client.vo.req.GroupReqVO; @@ -7,6 +7,7 @@ import com.crossoverjie.cim.client.vo.req.GroupReqVO;
7 import com.crossoverjie.cim.client.vo.req.LoginReqVO; 7 import com.crossoverjie.cim.client.vo.req.LoginReqVO;
8 import com.crossoverjie.cim.client.vo.res.CIMServerResVO; 8 import com.crossoverjie.cim.client.vo.res.CIMServerResVO;
9 import com.crossoverjie.cim.common.enums.StatusEnum; 9 import com.crossoverjie.cim.common.enums.StatusEnum;
  10 +import com.crossoverjie.cim.common.exception.CIMException;
10 import okhttp3.*; 11 import okhttp3.*;
11 import org.slf4j.Logger; 12 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory; 13 import org.slf4j.LoggerFactory;
@@ -78,6 +79,12 @@ public class RouteRequestImpl implements RouteRequest { @@ -78,6 +79,12 @@ public class RouteRequestImpl implements RouteRequest {
78 79
79 String json = response.body().string(); 80 String json = response.body().string();
80 CIMServerResVO cimServerResVO = JSON.parseObject(json, CIMServerResVO.class); 81 CIMServerResVO cimServerResVO = JSON.parseObject(json, CIMServerResVO.class);
  82 +
  83 + //重复登录
  84 + if (cimServerResVO.getCode().equals(StatusEnum.REPEAT_LOGIN.getCode())){
  85 + throw new CIMException(StatusEnum.REPEAT_LOGIN) ;
  86 + }
  87 +
81 if (!cimServerResVO.getCode().equals(StatusEnum.SUCCESS.getCode())){ 88 if (!cimServerResVO.getCode().equals(StatusEnum.SUCCESS.getCode())){
82 throw new RuntimeException("route server exception code=" + cimServerResVO.getCode()) ; 89 throw new RuntimeException("route server exception code=" + cimServerResVO.getCode()) ;
83 } 90 }
@@ -11,7 +11,7 @@ logging.level.root=info @@ -11,7 +11,7 @@ logging.level.root=info
11 # 群发消息 11 # 群发消息
12 cim.group.route.request.url=http://45.78.28.220:8083/groupRoute 12 cim.group.route.request.url=http://45.78.28.220:8083/groupRoute
13 13
14 -# 获取服务器ip+port 14 +# 登录并获取服务器ip+port
15 cim.server.route.request.url=http://45.78.28.220:8083/login 15 cim.server.route.request.url=http://45.78.28.220:8083/login
16 16
17 # 客户端唯一ID 17 # 客户端唯一ID
@@ -14,8 +14,8 @@ public enum StatusEnum { @@ -14,8 +14,8 @@ public enum StatusEnum {
14 /** 失败 */ 14 /** 失败 */
15 FAIL("4000", "失败"), 15 FAIL("4000", "失败"),
16 16
17 - /** 重复请求 */  
18 - REPEAT_REQUEST("5000", "重复请求"), 17 + /** 重复登录 */
  18 + REPEAT_LOGIN("5000", "重复登录"),
19 19
20 /** 请求限流 */ 20 /** 请求限流 */
21 REQUEST_LIMIT("6000", "请求限流"), 21 REQUEST_LIMIT("6000", "请求限流"),
  1 +package com.crossoverjie.cim.common.exception;
  2 +
  3 +
  4 +import com.crossoverjie.cim.common.enums.StatusEnum;
  5 +
  6 +/**
  7 + * Function:
  8 + *
  9 + * @author crossoverJie
  10 + * Date: 2018/8/25 15:26
  11 + * @since JDK 1.8
  12 + */
  13 +public class CIMException extends GenericException {
  14 +
  15 +
  16 + public CIMException(String errorCode, String errorMessage) {
  17 + super(errorMessage);
  18 + this.errorCode = errorCode;
  19 + this.errorMessage = errorMessage;
  20 + }
  21 +
  22 + public CIMException(Exception e, String errorCode, String errorMessage) {
  23 + super(e, errorMessage);
  24 + this.errorCode = errorCode;
  25 + this.errorMessage = errorMessage;
  26 + }
  27 +
  28 + public CIMException(String message) {
  29 + super(message);
  30 + this.errorMessage = message;
  31 + }
  32 +
  33 + public CIMException(StatusEnum statusEnum) {
  34 + super(statusEnum.getMessage());
  35 + this.errorMessage = statusEnum.message();
  36 + this.errorCode = statusEnum.getCode();
  37 + }
  38 +
  39 + public CIMException(StatusEnum statusEnum, String message) {
  40 + super(message);
  41 + this.errorMessage = message;
  42 + this.errorCode = statusEnum.getCode();
  43 + }
  44 +
  45 + public CIMException(Exception oriEx) {
  46 + super(oriEx);
  47 + }
  48 +
  49 + public CIMException(Throwable oriEx) {
  50 + super(oriEx);
  51 + }
  52 +
  53 + public CIMException(String message, Exception oriEx) {
  54 + super(message, oriEx);
  55 + this.errorMessage = message;
  56 + }
  57 +
  58 + public CIMException(String message, Throwable oriEx) {
  59 + super(message, oriEx);
  60 + this.errorMessage = message;
  61 + }
  62 +
  63 +
  64 + public static boolean isResetByPeer(String msg) {
  65 + if ("Connection reset by peer".equals(msg)) {
  66 + return true;
  67 + }
  68 + return false;
  69 + }
  70 +
  71 +}
  1 +package com.crossoverjie.cim.common.exception;
  2 +
  3 +import java.io.Serializable;
  4 +
  5 +/**
  6 + * Function:
  7 + *
  8 + * @author crossoverJie
  9 + * Date: 2018/8/25 15:27
  10 + * @since JDK 1.8
  11 + */
  12 +public class GenericException extends RuntimeException implements Serializable {
  13 + private static final long serialVersionUID = 1L;
  14 + String errorCode;
  15 + String errorMessage;
  16 +
  17 + public GenericException() {
  18 + }
  19 +
  20 + public GenericException(String message) {
  21 + super(message);
  22 + }
  23 +
  24 + public GenericException(Exception oriEx) {
  25 + super(oriEx);
  26 + }
  27 +
  28 + public GenericException(Exception oriEx, String message) {
  29 + super(message, oriEx);
  30 + }
  31 +
  32 + public GenericException(Throwable oriEx) {
  33 + super(oriEx);
  34 + }
  35 +
  36 + public GenericException(String message, Exception oriEx) {
  37 + super(message, oriEx);
  38 + }
  39 +
  40 + public GenericException(String message, Throwable oriEx) {
  41 + super(message, oriEx);
  42 + }
  43 +
  44 + public String getErrorCode() {
  45 + return this.errorCode;
  46 + }
  47 +
  48 + public void setErrorCode(String errorCode) {
  49 + this.errorCode = errorCode;
  50 + }
  51 +
  52 + public String getErrorMessage() {
  53 + return this.errorMessage;
  54 + }
  55 +
  56 + public void setErrorMessage(String errorMessage) {
  57 + this.errorMessage = errorMessage;
  58 + }
  59 +}
1 package com.crossoverjie.cim.common.pojo; 1 package com.crossoverjie.cim.common.pojo;
2 2
3 /** 3 /**
4 - * Function: 4 + * Function: 用户信息
5 * 5 *
6 * @author crossoverJie 6 * @author crossoverJie
7 * Date: 2018/12/24 02:33 7 * Date: 2018/12/24 02:33
@@ -61,7 +61,7 @@ public class RouteController { @@ -61,7 +61,7 @@ public class RouteController {
61 CIMServerResVO value = cimServerResVOEntry.getValue(); 61 CIMServerResVO value = cimServerResVOEntry.getValue();
62 if (userId.equals(groupReqVO.getUserId())){ 62 if (userId.equals(groupReqVO.getUserId())){
63 //过滤掉自己 63 //过滤掉自己
64 - CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfo(groupReqVO.getUserId()); 64 + CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfoByUserId(groupReqVO.getUserId());
65 LOGGER.warn("过滤掉了发送者 userId={}",cimUserInfo.toString()); 65 LOGGER.warn("过滤掉了发送者 userId={}",cimUserInfo.toString());
66 continue; 66 continue;
67 } 67 }
@@ -85,7 +85,7 @@ public class RouteController { @@ -85,7 +85,7 @@ public class RouteController {
85 public BaseResponse<NULLBody> offLine(@RequestBody ChatReqVO groupReqVO) throws Exception { 85 public BaseResponse<NULLBody> offLine(@RequestBody ChatReqVO groupReqVO) throws Exception {
86 BaseResponse<NULLBody> res = new BaseResponse(); 86 BaseResponse<NULLBody> res = new BaseResponse();
87 87
88 - CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfo(groupReqVO.getUserId()); 88 + CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfoByUserId(groupReqVO.getUserId());
89 89
90 LOGGER.info("下线用户[{}]", cimUserInfo.toString()); 90 LOGGER.info("下线用户[{}]", cimUserInfo.toString());
91 accountService.offLine(groupReqVO.getUserId()); 91 accountService.offLine(groupReqVO.getUserId());
@@ -138,8 +138,8 @@ public class RouteController { @@ -138,8 +138,8 @@ public class RouteController {
138 res.setCode(StatusEnum.SUCCESS.getCode()); 138 res.setCode(StatusEnum.SUCCESS.getCode());
139 res.setMessage(StatusEnum.SUCCESS.getMessage()); 139 res.setMessage(StatusEnum.SUCCESS.getMessage());
140 } else { 140 } else {
141 - res.setCode(StatusEnum.FAIL.getCode());  
142 - res.setMessage(StatusEnum.FAIL.getMessage()); 141 + res.setCode(StatusEnum.REPEAT_LOGIN.getCode());
  142 + res.setMessage(StatusEnum.REPEAT_LOGIN.getMessage());
143 } 143 }
144 144
145 return res; 145 return res;
@@ -41,7 +41,7 @@ public interface AccountService { @@ -41,7 +41,7 @@ public interface AccountService {
41 void saveRouteInfo(LoginReqVO loginReqVO ,String msg) throws Exception ; 41 void saveRouteInfo(LoginReqVO loginReqVO ,String msg) throws Exception ;
42 42
43 /** 43 /**
44 - * 加载所有的路有关系 44 + * 加载所有用户的路有关系
45 * @return 所有的路由关系 45 * @return 所有的路由关系
46 */ 46 */
47 Map<Long,CIMServerResVO> loadRouteRelated() ; 47 Map<Long,CIMServerResVO> loadRouteRelated() ;
@@ -13,9 +13,9 @@ public interface UserInfoCacheService { @@ -13,9 +13,9 @@ public interface UserInfoCacheService {
13 13
14 /** 14 /**
15 * 通过 userID 获取用户信息 15 * 通过 userID 获取用户信息
16 - * @param userId 16 + * @param userId 用户唯一 ID
17 * @return 17 * @return
18 * @throws Exception 18 * @throws Exception
19 */ 19 */
20 - CIMUserInfo loadUserInfo(long userId) throws Exception ; 20 + CIMUserInfo loadUserInfoByUserId(long userId) throws Exception ;
21 } 21 }
@@ -68,6 +68,15 @@ public class AccountServiceRedisImpl implements AccountService { @@ -68,6 +68,15 @@ public class AccountServiceRedisImpl implements AccountService {
68 68
69 @Override 69 @Override
70 public boolean login(LoginReqVO loginReqVO) throws Exception { 70 public boolean login(LoginReqVO loginReqVO) throws Exception {
  71 +
  72 + //先判断是否已经登录,第一次登录 cimUserInfo 为空
  73 + CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfoByUserId(loginReqVO.getUserId());
  74 + if (cimUserInfo != null){
  75 + //重复登录
  76 + return false ;
  77 + }
  78 +
  79 + //再去Redis里查询
71 String key = ACCOUNT_PREFIX + loginReqVO.getUserId(); 80 String key = ACCOUNT_PREFIX + loginReqVO.getUserId();
72 String userName = redisTemplate.opsForValue().get(key); 81 String userName = redisTemplate.opsForValue().get(key);
73 if (null == userName) { 82 if (null == userName) {
@@ -119,7 +128,7 @@ public class AccountServiceRedisImpl implements AccountService { @@ -119,7 +128,7 @@ public class AccountServiceRedisImpl implements AccountService {
119 128
120 @Override 129 @Override
121 public void pushMsg(String url, long sendUserId, ChatReqVO groupReqVO) throws Exception { 130 public void pushMsg(String url, long sendUserId, ChatReqVO groupReqVO) throws Exception {
122 - CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfo(sendUserId); 131 + CIMUserInfo cimUserInfo = userInfoCacheService.loadUserInfoByUserId(sendUserId);
123 132
124 JSONObject jsonObject = new JSONObject(); 133 JSONObject jsonObject = new JSONObject();
125 jsonObject.put("msg", cimUserInfo.getUserName() + ":【" + groupReqVO.getMsg() + "】"); 134 jsonObject.put("msg", cimUserInfo.getUserName() + ":【" + groupReqVO.getMsg() + "】");
@@ -22,7 +22,7 @@ import static com.crossoverjie.cim.route.constant.Constant.ACCOUNT_PREFIX; @@ -22,7 +22,7 @@ import static com.crossoverjie.cim.route.constant.Constant.ACCOUNT_PREFIX;
22 public class UserInfoCacheServiceImpl implements UserInfoCacheService { 22 public class UserInfoCacheServiceImpl implements UserInfoCacheService {
23 23
24 /** 24 /**
25 - * 本地缓存,后期可换为 LRU 25 + * 本地缓存,为了防止内存撑爆,后期可换为 LRU。
26 */ 26 */
27 private final static Map<Long,CIMUserInfo> USER_INFO_MAP = new ConcurrentHashMap<>(64) ; 27 private final static Map<Long,CIMUserInfo> USER_INFO_MAP = new ConcurrentHashMap<>(64) ;
28 28
@@ -30,7 +30,7 @@ public class UserInfoCacheServiceImpl implements UserInfoCacheService { @@ -30,7 +30,7 @@ public class UserInfoCacheServiceImpl implements UserInfoCacheService {
30 private RedisTemplate<String,String> redisTemplate ; 30 private RedisTemplate<String,String> redisTemplate ;
31 31
32 @Override 32 @Override
33 - public CIMUserInfo loadUserInfo(long userId) throws Exception { 33 + public CIMUserInfo loadUserInfoByUserId(long userId) throws Exception {
34 34
35 //优先从本地缓存获取 35 //优先从本地缓存获取
36 CIMUserInfo cimUserInfo = USER_INFO_MAP.get(userId); 36 CIMUserInfo cimUserInfo = USER_INFO_MAP.get(userId);
@@ -2,6 +2,7 @@ package com.crossoverjie.cim.server.handle; @@ -2,6 +2,7 @@ package com.crossoverjie.cim.server.handle;
2 2
3 import com.alibaba.fastjson.JSONObject; 3 import com.alibaba.fastjson.JSONObject;
4 import com.crossoverjie.cim.common.constant.Constants; 4 import com.crossoverjie.cim.common.constant.Constants;
  5 +import com.crossoverjie.cim.common.exception.CIMException;
5 import com.crossoverjie.cim.common.pojo.CIMUserInfo; 6 import com.crossoverjie.cim.common.pojo.CIMUserInfo;
6 import com.crossoverjie.cim.common.protocol.CIMRequestProto; 7 import com.crossoverjie.cim.common.protocol.CIMRequestProto;
7 import com.crossoverjie.cim.server.config.AppConfiguration; 8 import com.crossoverjie.cim.server.config.AppConfiguration;
@@ -92,7 +93,7 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto @@ -92,7 +93,7 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto
92 93
93 @Override 94 @Override
94 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 95 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
95 - if (isResetByPeer(cause.getMessage())){ 96 + if (CIMException.isResetByPeer(cause.getMessage())){
96 return; 97 return;
97 } 98 }
98 99
@@ -100,11 +101,4 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto @@ -100,11 +101,4 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto
100 101
101 } 102 }
102 103
103 -  
104 - private boolean isResetByPeer(String msg) {  
105 - if ("Connection reset by peer".equals(msg)) {  
106 - return true;  
107 - }  
108 - return false;  
109 - }  
110 } 104 }