├── bullet-comet
├── bullet-comet-core
│ ├── src
│ │ └── main
│ │ │ ├── resources
│ │ │ ├── bootstrap.yml
│ │ │ └── application.yml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── lyqf
│ │ │ └── bullet
│ │ │ └── comet
│ │ │ ├── package-info.java
│ │ │ ├── netty
│ │ │ ├── package-info.java
│ │ │ └── handler
│ │ │ │ ├── MessageHandler.java
│ │ │ │ └── AuthHandler.java
│ │ │ ├── util
│ │ │ ├── ThreadPoolExecutorService.java
│ │ │ ├── SpringContextBeanUtil.java
│ │ │ ├── UrlUtil.java
│ │ │ └── IpUtil.java
│ │ │ ├── biz
│ │ │ └── PushBiz.java
│ │ │ ├── constant
│ │ │ ├── ExceptionCodeConstant.java
│ │ │ └── CommonConstant.java
│ │ │ ├── vo
│ │ │ ├── LoginReq.java
│ │ │ └── PushMsg.java
│ │ │ ├── dto
│ │ │ └── UserRoomDTO.java
│ │ │ ├── exception
│ │ │ ├── ParamsException.java
│ │ │ └── ServiceBizException.java
│ │ │ ├── CometApp.java
│ │ │ ├── web
│ │ │ ├── CloseUserConnectionController.java
│ │ │ └── PushController.java
│ │ │ ├── manager
│ │ │ └── UserClearOnlineManager.java
│ │ │ ├── context
│ │ │ └── UserContextHolder.java
│ │ │ └── BulletChatServer.java
│ └── pom.xml
├── bullet-comet-facade
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── lyqf
│ │ │ └── bullet
│ │ │ └── comet
│ │ │ └── client
│ │ │ ├── PushClient.java
│ │ │ ├── CloseUserConnectionClient.java
│ │ │ └── vo
│ │ │ └── PushReq.java
│ └── pom.xml
└── pom.xml
├── bullet-logic
├── bullet-logic-core
│ ├── src
│ │ └── main
│ │ │ ├── resources
│ │ │ ├── bootstrap.yml
│ │ │ └── application.yml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── lyqf
│ │ │ └── bullet
│ │ │ └── logic
│ │ │ ├── enums
│ │ │ └── package-info.java
│ │ │ ├── acl
│ │ │ ├── package-info.java
│ │ │ └── CloseUserConnectionFacadeAcl.java
│ │ │ ├── dto
│ │ │ ├── UserLiveDTO.java
│ │ │ ├── UserInfoDTO.java
│ │ │ ├── RoomDTO.java
│ │ │ └── MsgDTO.java
│ │ │ ├── constant
│ │ │ ├── KafkaConstant.java
│ │ │ └── RedisConstant.java
│ │ │ ├── util
│ │ │ └── BulletDateUtil.java
│ │ │ ├── LogicApp.java
│ │ │ ├── config
│ │ │ ├── CustomerLoadBalancerConfig.java
│ │ │ └── RedisConfig.java
│ │ │ ├── web
│ │ │ ├── UserAndRoomSimpleManagerController.java
│ │ │ ├── UserMsgController.java
│ │ │ └── UserAuthController.java
│ │ │ ├── KafkaProducerConfig.java
│ │ │ └── manager
│ │ │ ├── KafkaManger.java
│ │ │ └── UserAuthManager.java
│ └── pom.xml
├── bullet-logic-facade
│ ├── src
│ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── lyqf
│ │ │ └── bullet
│ │ │ └── logic
│ │ │ └── client
│ │ │ ├── package-info.java
│ │ │ ├── vo
│ │ │ ├── ClearOnlineReq.java
│ │ │ ├── AuthUserReq.java
│ │ │ ├── UserMsgResp.java
│ │ │ ├── AuthUserResp.java
│ │ │ └── UserMsgReq.java
│ │ │ ├── UserMsgClient.java
│ │ │ └── UserAuthClient.java
│ └── pom.xml
└── pom.xml
├── bullet-push
├── bullet-push-core
│ ├── src
│ │ └── main
│ │ │ ├── resources
│ │ │ ├── bootstrap.yml
│ │ │ └── application.yml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── lyqf
│ │ │ └── bullet
│ │ │ └── push
│ │ │ ├── acl
│ │ │ └── package-info.java
│ │ │ ├── consumer
│ │ │ ├── SysMsgConsumer.java
│ │ │ ├── CommonMsgConsumer.java
│ │ │ └── ExtMsgConsumer.java
│ │ │ ├── constant
│ │ │ └── KafkaConstant.java
│ │ │ ├── PushApp.java
│ │ │ ├── web
│ │ │ └── TestContoller.java
│ │ │ ├── manager
│ │ │ └── LiveNodeManager.java
│ │ │ ├── config
│ │ │ ├── CustomerLoadBalancerConfig.java
│ │ │ ├── KafkaConsumerConfig.java
│ │ │ └── RedisConfig.java
│ │ │ └── biz
│ │ │ ├── ExtMsgPushBiz.java
│ │ │ └── CommonMsgPushBiz.java
│ └── pom.xml
├── bullet-push-facade
│ └── pom.xml
└── pom.xml
├── bullet-common
├── src
│ └── main
│ │ └── java
│ │ └── com
│ │ └── lyqf
│ │ └── bullet
│ │ └── common
│ │ ├── util
│ │ └── package-info.java
│ │ ├── constant
│ │ ├── CommonConstant.java
│ │ └── RedisConstant.java
│ │ ├── enums
│ │ ├── LevelEnum.java
│ │ └── OperateTypeEnum.java
│ │ ├── model
│ │ ├── Request.java
│ │ ├── Response.java
│ │ └── ApiResponse.java
│ │ ├── exception
│ │ ├── ProviderNotFoundException.java
│ │ └── BizException.java
│ │ ├── holder
│ │ └── RpcContextHolder.java
│ │ └── loadbanlance
│ │ └── CustomerLoadBalance.java
└── pom.xml
├── .gitignore
├── README.md
├── bullet-client
├── src
│ └── main
│ │ └── java
│ │ └── com
│ │ └── lyqf
│ │ └── bullet
│ │ └── client
│ │ ├── BulletChatClient.java
│ │ └── handler
│ │ └── WebSocketClientHandler.java
└── pom.xml
└── pom.xml
/bullet-comet/bullet-comet-core/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/util/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * @author chenlang
3 | * @date 2022/5/12 4:20 下午
4 | */
5 | package com.lyqf.bullet.common.util;
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * @author chenlang
3 | * @date 2022/5/25 3:51 下午
4 | */
5 | package com.lyqf.bullet.comet;
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/netty/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * @author chenlang
3 | * @date 2022/5/11 2:58 下午
4 | */
5 | package com.lyqf.bullet.comet.netty;
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/enums/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * @author chenlang
3 | * @date 2022/5/26 5:18 下午
4 | */
5 | package com.lyqf.bullet.logic.enums;
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-facade/src/main/java/com/lyqf/bullet/logic/client/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * @author chenlang
3 | * @date 2022/5/25 11:03 下午
4 | */
5 | package com.lyqf.bullet.logic.client;
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/acl/package-info.java:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 防腐层代码(限流,功能转化,类试adapter作用)
4 | * todo 调用其他服务的入口统一放在这里
5 | * @author chenlang
6 | * @date 2022/5/30 1:25 下午
7 | */
8 | package com.lyqf.bullet.logic.acl;
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/acl/package-info.java:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 防腐层代码(限流,功能转化,类试adapter作用)
4 | * todo 调用其他服务的入口统一放在这里
5 | *
6 | * @author chenlang
7 | * @date 2022/5/30 1:25 下午
8 | */
9 | package com.lyqf.bullet.push.acl;
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/consumer/SysMsgConsumer.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.consumer;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/17 3:55 下午
6 | */
7 |
8 | public class SysMsgConsumer {
9 | }
10 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/util/ThreadPoolExecutorService.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.util;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/11 2:58 下午
6 | */
7 | public class ThreadPoolExecutorService {
8 | // todo
9 | }
10 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/biz/PushBiz.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.biz;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | /**
6 | * @author chenlang
7 | * @date 2022/5/17 4:45 下午
8 | */
9 | @Component
10 | public class PushBiz {
11 |
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: bullet-comet
4 | main:
5 | allow-bean-definition-overriding: true
6 | cloud:
7 | nacos:
8 | discovery:
9 | # server-addr: 127.0.0.1:8848
10 | server-addr: 39.105.34.3:8848
11 |
12 | server:
13 | port: 8895
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/dto/UserLiveDTO.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.dto;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @author chenlang
7 | * @date 2022/5/12 4:36 下午
8 | */
9 |
10 | @Data
11 | public class UserLiveDTO {
12 | private Long id;
13 | private Long roomId;
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/constant/ExceptionCodeConstant.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.constant;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/11 4:29 下午
6 | */
7 | public class ExceptionCodeConstant {
8 |
9 | /**
10 | *
11 | */
12 | public static final Integer PARAMS_ERROR_CODE = 40001;
13 | }
14 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/constant/CommonConstant.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.constant;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/11 4:00 下午
6 | */
7 | public class CommonConstant {
8 |
9 | /**
10 | *
11 | */
12 | public static final String WEBSOCKET_URL = "ws://localhost:8099/websocket";
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/constant/CommonConstant.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.constant;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/24 4:04 下午
6 | */
7 | public class CommonConstant {
8 |
9 | /**
10 | *
11 | */
12 | public static final String CUSTOMER_NODE_ID = "customer_node_id";
13 |
14 | public static final Integer NO_PROVIDER_ERROR_CODE = 4444;
15 | }
16 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/constant/KafkaConstant.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.constant;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/17 4:06 下午
6 | */
7 | public class KafkaConstant {
8 |
9 | public static final String SYS_MSG_TOPIC = "sys_msg";
10 |
11 | public static final String EXT_MSG_TOPIC = "ext_msg";
12 |
13 | public static final String COMMON_MSG_TOPIC = "common_msg";
14 | }
15 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/enums/LevelEnum.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.enums;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/16 4:14 下午
6 | */
7 | public enum LevelEnum {
8 |
9 | SYSTEM(1), EXT(2), COMMON(3), HEART(4);
10 |
11 | int code;
12 |
13 | LevelEnum(int code) {
14 | this.code = code;
15 | }
16 |
17 | public Integer getCode() {
18 | return code;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/constant/KafkaConstant.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.constant;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/16 4:28 下午
6 | */
7 | public class KafkaConstant {
8 |
9 | public static final String SYS_MSG_TOPIC = "sys_msg";
10 |
11 | public static final String EXT_MSG_TOPIC = "ext_msg";
12 |
13 | public static final String COMMON_MSG_TOPIC = "common_msg";
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/vo/LoginReq.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.vo;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/11 4:19 下午
10 | */
11 | @AllArgsConstructor
12 | @NoArgsConstructor
13 | @Data
14 | public class LoginReq {
15 |
16 | private Long roomId;
17 |
18 | private String token;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/constant/RedisConstant.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.constant;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/12 4:18 下午
6 | */
7 | public class RedisConstant {
8 |
9 | /**
10 | * 用户在哪个节点上
11 | */
12 | public static final String USER_NODE = "user_node:%s";
13 |
14 |
15 | /**
16 | * 房间在哪些节点上
17 | */
18 | public static final String ROOM_NODE = "room_nodes:%s";
19 |
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: bullet-logic
4 | main:
5 | allow-bean-definition-overriding: true
6 | cloud:
7 | nacos:
8 | discovery:
9 | # server-addr: 127.0.0.1:8848
10 | server-addr: 192.168.22.129:8848
11 |
12 | server:
13 | port: 8086
14 |
15 | spring.redis.cluster.nodes: 82.157.68.174:7001,82.157.68.174:7002,82.157.68.174:7003
16 | spring.kafka.nodes: 192.168.22.129:9092
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/dto/UserRoomDTO.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.dto;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/17 2:10 下午
10 | */
11 | @Data
12 | public class UserRoomDTO implements Serializable {
13 |
14 | private static final long serialVersionUID = 169849378576190669L;
15 |
16 | private Long userId;
17 |
18 | private Long roomId;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-facade/src/main/java/com/lyqf/bullet/logic/client/vo/ClearOnlineReq.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.client.vo;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/17 1:57 下午
10 | */
11 |
12 | @Data
13 | public class ClearOnlineReq implements Serializable {
14 |
15 | private static final long serialVersionUID = 1760383264305020478L;
16 |
17 | private Long userId;
18 |
19 | private Long roomId;
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/exception/ParamsException.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.exception;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @author chenlang
7 | * @date 2022/5/11 4:26 下午
8 | */
9 |
10 | @Data
11 | public class ParamsException extends RuntimeException {
12 |
13 | private Integer code;
14 | private String msg;
15 |
16 | public ParamsException(Integer code, String msg) {
17 | this.code = code;
18 | this.msg = msg;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/dto/UserInfoDTO.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/12 4:07 下午
10 | */
11 | @Data
12 | @AllArgsConstructor
13 | @NoArgsConstructor
14 | public class UserInfoDTO {
15 | private Long id;
16 | private String userIdCardName;
17 | private String nickName;
18 | private String headUrl;
19 | }
20 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/exception/ServiceBizException.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.exception;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @author chenlang
7 | * @date 2022/5/11 4:26 下午
8 | */
9 |
10 | @Data
11 | public class ServiceBizException extends RuntimeException {
12 |
13 | private Integer code;
14 | private String msg;
15 |
16 | public ServiceBizException(Integer code, String msg) {
17 | this.code = code;
18 | this.msg = msg;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/util/BulletDateUtil.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.util;
2 |
3 | import java.util.Date;
4 |
5 | import org.apache.commons.lang3.time.DateUtils;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/17 2:03 下午
10 | */
11 | public class BulletDateUtil {
12 |
13 | /**
14 | * 获取当前时间的下一天这个时间
15 | * @return
16 | */
17 | public static Date genNextDateFromCurrentDate() {
18 | return DateUtils.addDays(new Date(), 1);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/model/Request.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.model;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/11 7:00 下午
10 | */
11 | @Data
12 | public class Request implements Serializable {
13 |
14 | private static final long serialVersionUID = 4809210224829105997L;
15 |
16 | private Long userId;
17 |
18 | private Long roomId;
19 |
20 | private Integer operation;
21 |
22 | private String data;
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/model/Response.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.model;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/11 7:00 下午
10 | */
11 | @Data
12 | public class Response implements Serializable {
13 |
14 | private static final long serialVersionUID = 4809210224829105997L;
15 |
16 | private Long userId;
17 |
18 | private Long roomId;
19 |
20 | private Integer operation;
21 |
22 | private String data;
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/exception/ProviderNotFoundException.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.exception;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/30 2:36 下午
6 | */
7 | public class ProviderNotFoundException extends RuntimeException {
8 | private Integer code;
9 | private String ms;
10 |
11 | /**
12 | *
13 | * @param code
14 | * @param message
15 | */
16 | public ProviderNotFoundException(Integer code, String message) {
17 | this.code = code;
18 | this.ms = message;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: bullet-agg
4 | main:
5 | allow-bean-definition-overriding: true
6 | cloud:
7 | nacos:
8 | discovery:
9 | # server-addr: 127.0.0.1:8848
10 | server-addr: 192.168.22.129:8848
11 |
12 | server:
13 | port: 8087
14 |
15 | spring.kafka.consumer.group-id: bullet-agg
16 | spring.kafka.nodes: 192.168.22.129:9092
17 | spring.kafka.consumer.auto-offset-reset: earliest
18 |
19 | spring.redis.cluster.nodes: 82.157.68.174:7001,82.157.68.174:7002,82.157.68.174:7003
20 |
21 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/constant/RedisConstant.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.constant;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/12 4:18 下午
6 | */
7 | public class RedisConstant {
8 |
9 | public static final String TOKEN_KEY = "user_token:%s";
10 |
11 | public static final String USER_INFO_KEY = "user_info:%s";
12 |
13 | public static final String ROOM_KEY = "room:%s";
14 |
15 | /**
16 | * roomId
17 | */
18 | public static final String ANONYMOUS_USER_ID_START_KEY = "user_anonymous_start";
19 |
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-facade/src/main/java/com/lyqf/bullet/logic/client/vo/AuthUserReq.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.client.vo;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/12 3:48 下午
10 | */
11 |
12 | @Data
13 | public class AuthUserReq implements Serializable {
14 |
15 | private static final long serialVersionUID = 7090117243937573161L;
16 |
17 | private String token;
18 |
19 | private String userName;
20 |
21 | private Long roomId;
22 |
23 | private String node;
24 |
25 |
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/exception/BizException.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.exception;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @author chenlang
7 | * @date 2022/5/23 11:40 上午
8 | */
9 |
10 | @Data
11 | public class BizException extends RuntimeException {
12 |
13 | private Integer code;
14 | private String ms;
15 |
16 |
17 | /**
18 | *
19 | * @param code
20 | * @param message
21 | */
22 | public BizException(Integer code, String message) {
23 | this.code = code;
24 | this.ms = message;
25 | }
26 |
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/enums/OperateTypeEnum.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.enums;
2 |
3 | /**
4 | * @author chenlang
5 | * @date 2022/5/23 11:22 上午
6 | */
7 | public enum OperateTypeEnum {
8 |
9 | /**
10 | *
11 | */
12 | TXT_MSG(1), IMAG_MSG(2), LIKE_MSG(3), ONLINE_EXT_MSG(11), LOTTERY__SYS_MSG(12), RED_ENVELOPE_SYS_MSG(13),
13 | PRODUCT_SYS_MSG(14), HERART_PING(100);
14 |
15 | int code;
16 |
17 | OperateTypeEnum(int code) {
18 | this.code = code;
19 | }
20 |
21 | public Integer getCode() {
22 | return code;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/vo/PushMsg.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.vo;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @author chenlang
7 | * @date 2022/5/17 5:17 下午
8 | */
9 | @Data
10 | public class PushMsg {
11 |
12 | /**
13 | * 操作类型 1 文本聊天 2 图片聊天 3 点赞 10以上为扩展功能 (抽奖,发红包,推产品,禁言,踢人等)
14 | */
15 | private Integer operateType;
16 |
17 | /**
18 | * 具体内容
19 | */
20 | private String content;
21 |
22 | /**
23 | *
24 | */
25 | private Long userId;
26 |
27 | /**
28 | *
29 | */
30 | private String userNickName;
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-facade/src/main/java/com/lyqf/bullet/logic/client/vo/UserMsgResp.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.client.vo;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/16 2:49 下午
10 | */
11 |
12 | @Data
13 | public class UserMsgResp implements Serializable {
14 |
15 | private static final long serialVersionUID = 2869741037236201052L;
16 |
17 | private String reqId;
18 |
19 | /**
20 | * ture 成功
21 | */
22 | private boolean result;
23 |
24 | /**
25 | * 返回其他内容信息
26 | */
27 | private String data;
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/dto/RoomDTO.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.dto;
2 |
3 | import java.util.Date;
4 |
5 | import com.fasterxml.jackson.annotation.JsonFormat;
6 |
7 | import lombok.AllArgsConstructor;
8 | import lombok.Data;
9 | import lombok.NoArgsConstructor;
10 |
11 | /**
12 | * @author chenlang
13 | * @date 2022/5/12 4:07 下午
14 | */
15 | @Data
16 | @NoArgsConstructor
17 | @AllArgsConstructor
18 | public class RoomDTO {
19 | private Long id;
20 | private String name;
21 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
22 | private Date startTime;
23 | private String imgUrl;
24 | private String desc;
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bullet-chat
2 | # 直播弹幕系统
3 | 该弹幕系统主要四大组件组成:
4 | comet 接入层:该模块主要负责与client端(web浏览器)建立websocket长链接并维持长链接稳定性;
5 | logic 逻辑处理层:该模块主要提供:消息转发(mq)、登录逻辑处理、记录client与comet实例之间的关系、为client端提供可用的comet实例、为外部
6 | 提供http直接发送消息的能力;
7 | push 消息路由推送层:用户发送消到到logic,logic将消息发送到Mq中,push从Mq获取消息进行处理,然后通过logic获取消息的目的地(具体的comet实例),
8 | 并获取对应实例(rpc路由)并消息推送至comet,再由comet长链接推送到client端;
9 | client 客户端:与comet进行建立链接,接收消息和发送消息;
10 |
11 | 该项目只提供了基本的收发消息能力,比如像消息的敏感词、消息过滤及限流、client询址(server)等能力并没有提供。启动该项目还需要依赖
12 | kafka,redis,nacos 这几个中间件。本项目原则上支持水平扩展,但这个到了一定量级后要考虑的是db的路由链接等能力。
13 |
14 | ## 架构图:
15 |
16 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-facade/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | bullet-push
7 | com.lyqf
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | bullet-push-facade
13 |
14 |
15 | 8
16 | 8
17 |
18 |
19 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-facade/src/main/java/com/lyqf/bullet/logic/client/vo/AuthUserResp.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.client.vo;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/12 4:39 下午
10 | */
11 | @Data
12 | public class AuthUserResp implements Serializable {
13 | private static final long serialVersionUID = -1185223096517555110L;
14 |
15 | /**
16 | * 是否是重复登录 true是
17 | */
18 | private Boolean repeatLogin = false;
19 |
20 | /**
21 | * 登录结果 true 为成功
22 | */
23 | private Boolean loginResult = true;
24 |
25 | /**
26 | *
27 | */
28 | private String msg;
29 |
30 | private Long userId;
31 |
32 | private Long roomId;
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/holder/RpcContextHolder.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.holder;
2 |
3 | import com.alibaba.ttl.TransmittableThreadLocal;
4 |
5 | /**
6 | * @author chenlang
7 | * @date 2022/5/30 11:13 上午
8 | */
9 | public class RpcContextHolder {
10 |
11 | private static TransmittableThreadLocal context = new TransmittableThreadLocal<>();
12 |
13 | /**
14 | *
15 | * @return
16 | */
17 | public static String getStringValue() {
18 | return context.get();
19 | }
20 |
21 | public static void setStringParameter(String data) {
22 | context.set(data);
23 | }
24 |
25 | /**
26 | *
27 | */
28 | public static void remove() {
29 | context.remove();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-facade/src/main/java/com/lyqf/bullet/comet/client/PushClient.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.client;
2 |
3 | import org.springframework.cloud.openfeign.FeignClient;
4 | import org.springframework.web.bind.annotation.RequestBody;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RequestMethod;
7 |
8 | import com.lyqf.bullet.comet.client.vo.PushReq;
9 | import com.lyqf.bullet.common.model.ApiResponse;
10 |
11 | /**
12 | * @author chenlang
13 | * @date 2022/5/17 4:31 下午
14 | */
15 |
16 | @FeignClient("bullet-comet")
17 | public interface PushClient {
18 |
19 | /**
20 | *
21 | * @param pushReq
22 | * @return
23 | */
24 | @RequestMapping(value = "/push", method = RequestMethod.POST)
25 | ApiResponse push(@RequestBody PushReq pushReq);
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/bullet-push/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | bullet-chat-new
7 | com.lyqf
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | bullet-push
13 | pom
14 |
15 | bullet-push-core
16 | bullet-push-facade
17 |
18 |
19 |
20 | 8
21 | 8
22 |
23 |
24 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-facade/src/main/java/com/lyqf/bullet/logic/client/UserMsgClient.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.client;
2 |
3 | import org.springframework.cloud.openfeign.FeignClient;
4 | import org.springframework.web.bind.annotation.RequestBody;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RequestMethod;
7 |
8 | import com.lyqf.bullet.logic.client.vo.UserMsgReq;
9 | import com.lyqf.bullet.logic.client.vo.UserMsgResp;
10 |
11 | /**
12 | * @author chenlang
13 | * @date 2022/5/16 Re6 下午
14 | */
15 | @FeignClient("bullet-logic")
16 | public interface UserMsgClient {
17 |
18 | /**
19 | * 用户发送消息
20 | *
21 | * @param userMsgReq
22 | * @return
23 | */
24 | @RequestMapping(value = "/send-msg", method = RequestMethod.POST)
25 | UserMsgResp sendMsg(@RequestBody UserMsgReq userMsgReq);
26 | }
27 |
--------------------------------------------------------------------------------
/bullet-logic/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | bullet-chat-new
7 | com.lyqf
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | bullet-logic
13 | pom
14 |
15 | bullet-logic-core
16 | bullet-logic-facade
17 |
18 |
19 |
20 | 8
21 | 8
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-facade/src/main/java/com/lyqf/bullet/comet/client/CloseUserConnectionClient.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.client;
2 |
3 | import org.springframework.cloud.openfeign.FeignClient;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.RequestMethod;
6 |
7 | import com.lyqf.bullet.common.model.ApiResponse;
8 | import org.springframework.web.bind.annotation.RequestParam;
9 |
10 | /**
11 | * @author chenlang
12 | * @date 2022/5/23 11:33 上午
13 | */
14 | @FeignClient("bullet-comet")
15 | public interface CloseUserConnectionClient {
16 |
17 | /**
18 | * 断开用户老连接
19 | *
20 | * @param userId
21 | * @param roomId
22 | * @return
23 | */
24 | @RequestMapping(value = "/close", method = RequestMethod.GET)
25 | ApiResponse close(@RequestParam Long userId, @RequestParam Long roomId);
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-facade/src/main/java/com/lyqf/bullet/comet/client/vo/PushReq.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.client.vo;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/17 4:31 下午
10 | */
11 |
12 | @Data
13 | public class PushReq implements Serializable {
14 |
15 | private static final long serialVersionUID = -1043305543639856086L;
16 |
17 | /**
18 | * 系统用户id
19 | */
20 | private Long sysUserId;
21 |
22 | private String sysUserName;
23 |
24 | private Long userId;
25 |
26 | private String userNickName;
27 |
28 | private Long roomId;
29 |
30 | /**
31 | * 单聊的userId,该值为null代表群聊
32 | */
33 | private Long toUserId;
34 |
35 | /**
36 | *
37 | */
38 | private Integer operateType;
39 |
40 | /**
41 | * 具体内容
42 | */
43 | private String content;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/PushApp.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
6 | import org.springframework.cloud.openfeign.EnableFeignClients;
7 |
8 | import lombok.extern.slf4j.Slf4j;
9 |
10 | /**
11 | * @author chenlang
12 | * @date 2022/5/17 9:01 上午
13 | */
14 |
15 | @Slf4j
16 | @EnableFeignClients(basePackages = {"com.lyqf.bullet.*"})
17 | @EnableDiscoveryClient
18 | @SpringBootApplication
19 | public class PushApp {
20 |
21 | public static void main(String[] args) {
22 | try {
23 | SpringApplication app = new SpringApplication(PushApp.class);
24 | app.run(args);
25 | } catch (Exception e) {
26 | log.warn("main方法启动失败,", e);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/bullet-comet/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | bullet-chat-new
7 | com.lyqf
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | bullet-comet
13 | pom
14 |
15 | bullet-comet-core
16 | bullet-comet-facade
17 |
18 |
19 |
20 |
21 | org.springframework.cloud
22 | spring-cloud-starter-openfeign
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/consumer/CommonMsgConsumer.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.consumer;
2 |
3 | import org.apache.kafka.clients.consumer.ConsumerRecord;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.kafka.annotation.KafkaListener;
6 | import org.springframework.stereotype.Component;
7 |
8 | import com.lyqf.bullet.push.biz.CommonMsgPushBiz;
9 | import com.lyqf.bullet.push.constant.KafkaConstant;
10 |
11 | import lombok.extern.slf4j.Slf4j;
12 |
13 | /**
14 | * @author chenlang
15 | * @date 2022/5/17 3:55 下午
16 | */
17 | @Slf4j
18 | @Component
19 | public class CommonMsgConsumer {
20 |
21 | @Autowired
22 | private CommonMsgPushBiz commonMsgPushBiz;
23 |
24 | @KafkaListener(topics = KafkaConstant.COMMON_MSG_TOPIC)
25 | public void listen(ConsumerRecord record) {
26 | String data = record.value().toString();
27 | log.info("receive data :{}", data);
28 | commonMsgPushBiz.pushCommonMsg(data);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/util/SpringContextBeanUtil.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.util;
2 |
3 | import java.util.Locale;
4 |
5 | import org.springframework.beans.BeansException;
6 | import org.springframework.context.ApplicationContext;
7 | import org.springframework.context.ApplicationContextAware;
8 | import org.springframework.stereotype.Component;
9 |
10 | /**
11 | * @author chenlang
12 | * @date 2022/5/17 11:01 上午
13 | */
14 | @Component
15 | public class SpringContextBeanUtil implements ApplicationContextAware {
16 |
17 | private static ApplicationContext context;
18 |
19 | @Override
20 | public void setApplicationContext(ApplicationContext context) throws BeansException {
21 | SpringContextBeanUtil.context = context;
22 | }
23 |
24 | public static T getBean(Class beanClass) {
25 | return context.getBean(beanClass);
26 | }
27 |
28 | public static String getMessage(String key) {
29 | return context.getMessage(key, null, Locale.getDefault());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/acl/CloseUserConnectionFacadeAcl.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.acl;
2 |
3 | import com.lyqf.bullet.comet.client.CloseUserConnectionClient;
4 | import com.lyqf.bullet.common.exception.ProviderNotFoundException;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Component;
8 |
9 | /**
10 | * @author chenlang
11 | * @date 2022/5/30 2:41 下午
12 | */
13 |
14 | @Slf4j
15 | @Component
16 | public class CloseUserConnectionFacadeAcl {
17 |
18 | @Autowired
19 | private CloseUserConnectionClient closeUserConnectionClient;
20 |
21 | /**
22 | *
23 | * @param userId
24 | * @param roomId
25 | * @return
26 | */
27 | public boolean close(Long userId, Long roomId) {
28 | try {
29 | closeUserConnectionClient.close(userId, roomId);
30 | } catch (ProviderNotFoundException e) {
31 | log.info("e", e);
32 | }
33 | return true;
34 | }
35 |
36 |
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-facade/src/main/java/com/lyqf/bullet/logic/client/vo/UserMsgReq.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.client.vo;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | /**
8 | * @author chenlang
9 | * @date 2022/5/16 2:49 下午
10 | */
11 |
12 | @Data
13 | public class UserMsgReq implements Serializable {
14 |
15 | private static final long serialVersionUID = 2869741037236201052L;
16 |
17 | /**
18 | * 唯一id
19 | */
20 | private String reqId;
21 |
22 | private Long userId;
23 |
24 | /**
25 | * 用户昵称
26 | */
27 | private String userNickName;
28 |
29 | /**
30 | * 用户身证证姓名
31 | */
32 | private String userIdCardName;
33 |
34 | /**
35 | * 头像
36 | */
37 | private String headUrl;
38 |
39 | /**
40 | *
41 | */
42 | private Long roomId;
43 |
44 | /**
45 | * 单聊的userId,该值为null代表群聊
46 | */
47 | private Long toUserId;
48 |
49 | /**
50 | *
51 | */
52 | private Integer operateType;
53 |
54 | /**
55 | * 具体内容
56 | */
57 | private String content;
58 | }
59 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/CometApp.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
6 | import org.springframework.cloud.openfeign.EnableFeignClients;
7 |
8 | import lombok.extern.slf4j.Slf4j;
9 |
10 | /**
11 | * 前端使用:https://github.com/joewalnes/reconnecting-websocket
12 | * @author chenlang
13 | * @date 2022/5/10 5:32 下午
14 | */
15 |
16 | @Slf4j
17 | @EnableDiscoveryClient
18 | @EnableFeignClients(basePackages = {"com.lyqf.bullet.*"})
19 | @SpringBootApplication
20 | public class CometApp {
21 |
22 | public static void main(String[] args) {
23 | try {
24 | SpringApplication app = new SpringApplication(CometApp.class);
25 | app.run(args);
26 |
27 | BulletChatServer server = new BulletChatServer(8889);
28 | server.start();
29 | } catch (Exception e) {
30 | log.warn("main方法启动失败,", e);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/consumer/ExtMsgConsumer.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.consumer;
2 |
3 | import org.apache.kafka.clients.consumer.ConsumerRecord;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.kafka.annotation.KafkaListener;
6 | import org.springframework.stereotype.Component;
7 |
8 | import com.lyqf.bullet.push.biz.ExtMsgPushBiz;
9 | import com.lyqf.bullet.push.constant.KafkaConstant;
10 |
11 | import lombok.extern.slf4j.Slf4j;
12 |
13 | /**
14 | * @author chenlang
15 | * @date 2022/5/17 3:55 下午
16 | */
17 |
18 | @Slf4j
19 | @Component
20 | public class ExtMsgConsumer {
21 |
22 | @Autowired
23 | private ExtMsgPushBiz extMsgPushBiz;
24 |
25 |
26 | /**
27 | *
28 | * @param record
29 | */
30 | @KafkaListener(topics = KafkaConstant.EXT_MSG_TOPIC)
31 | public void listen(ConsumerRecord record) {
32 | String data = record.value().toString();
33 | log.info("ExtMsgConsumer receive data :{}", data);
34 | extMsgPushBiz.pushExtMsg(data);
35 | }
36 |
37 |
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-facade/src/main/java/com/lyqf/bullet/logic/client/UserAuthClient.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.client;
2 |
3 | import org.springframework.cloud.openfeign.FeignClient;
4 | import org.springframework.web.bind.annotation.RequestBody;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RequestMethod;
7 |
8 | import com.lyqf.bullet.common.model.ApiResponse;
9 | import com.lyqf.bullet.logic.client.vo.AuthUserReq;
10 | import com.lyqf.bullet.logic.client.vo.AuthUserResp;
11 | import com.lyqf.bullet.logic.client.vo.ClearOnlineReq;
12 |
13 | /**
14 | * @author chenlang
15 | * @date 2022/5/12 3:10 下午
16 | */
17 |
18 | @FeignClient("bullet-logic")
19 | public interface UserAuthClient {
20 |
21 | @RequestMapping(value = "/auth", method = RequestMethod.POST)
22 | ApiResponse authUser(@RequestBody AuthUserReq req);
23 |
24 | /**
25 | *
26 | * @return
27 | */
28 | @RequestMapping(value = "/clear-online", method = RequestMethod.POST)
29 | ApiResponse clearOnlineByUserId(@RequestBody ClearOnlineReq req);
30 | }
31 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/web/CloseUserConnectionController.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.web;
2 |
3 | import com.lyqf.bullet.comet.context.UserContextHolder;
4 | import com.lyqf.bullet.common.model.ApiResponse;
5 | import io.netty.channel.Channel;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.springframework.web.bind.annotation.GetMapping;
8 | import org.springframework.web.bind.annotation.RequestParam;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | /**
12 | * @author chenlang
13 | * @date 2022/5/24 1:39 下午
14 | */
15 |
16 | @Slf4j
17 | @RestController
18 | public class CloseUserConnectionController {
19 |
20 | @GetMapping("/close")
21 | public ApiResponse close(@RequestParam Long userId, @RequestParam Long roomId) {
22 | Channel channel = UserContextHolder.userWithChannelMap.get(userId);
23 | UserContextHolder.clear(channel, userId, roomId);
24 |
25 | if (channel != null) {
26 | channel.close();
27 | }
28 | log.info("重复登录,clear上一条链接信息,userId:{},roomId:{}", userId, roomId);
29 | return ApiResponse.genSuccessData(true);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/LogicApp.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic;
2 |
3 | import com.lyqf.bullet.logic.config.CustomerLoadBalancerConfig;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
8 | import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
9 | import org.springframework.cloud.openfeign.EnableFeignClients;
10 |
11 | /**
12 | * @author chenlang
13 | * @date 2022/5/17 9:01 上午
14 | */
15 |
16 | @Slf4j
17 | @EnableFeignClients(basePackages = {"com.lyqf.bullet.comet.client"})
18 | @EnableDiscoveryClient
19 | @SpringBootApplication
20 | @LoadBalancerClient(name = "myServer", configuration = CustomerLoadBalancerConfig.class)
21 | public class LogicApp {
22 |
23 | public static void main(String[] args) {
24 | try {
25 | SpringApplication app = new SpringApplication(LogicApp.class);
26 | app.run(args);
27 | } catch (Exception e) {
28 | log.warn("main方法启动失败,", e);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-facade/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | bullet-comet
7 | com.lyqf
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | bullet-comet-facade
13 |
14 |
15 | 8
16 | 8
17 |
18 |
19 |
20 |
21 | com.lyqf
22 | bullet-common
23 | 1.0-SNAPSHOT
24 |
25 |
26 |
27 | org.projectlombok
28 | lombok
29 | true
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/dto/MsgDTO.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.dto;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @author chenlang
7 | * @date 2022/5/16 3:02 下午
8 | */
9 | @Data
10 | public class MsgDTO {
11 |
12 | /**
13 | *
14 | */
15 | private Long sysUserId;
16 |
17 | private Long userId;
18 |
19 | /**
20 | * 用户昵称
21 | */
22 | private String userNickName;
23 |
24 | /**
25 | * 用户身证证姓名
26 | */
27 | private String userIdCardName;
28 |
29 | /**
30 | * 头像
31 | */
32 | private String headUrl;
33 |
34 | private Long roomId;
35 |
36 | /**
37 | * 消息优先级: 1 系统级别(12 抽奖 ,13 发红包,14 推产品,15 禁言,16 踢人 ) 2.扩展自定义消息 (20 用户上线) 3.普通消息 (1 文本聊天 2 图片聊天 3 点赞) 4.心跳消息
38 | */
39 | private Integer level;
40 |
41 | /**
42 | * 单聊的userId,该值为null代表群聊
43 | */
44 | private Long toUserId;
45 |
46 | /**
47 | * 操作类型 1 文本聊天 2 图片聊天 3 点赞 20 以上为扩展功能 11 用户上线,12 抽奖 ,13 发红包,14 推产品,
48 | */
49 | private Integer operateType;
50 |
51 | /**
52 | * 具体内容
53 | */
54 | private String content;
55 |
56 | /**
57 | * 扩展字段
58 | */
59 | private String ext;
60 | }
61 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/web/TestContoller.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.web;
2 |
3 | import org.springframework.web.bind.annotation.GetMapping;
4 | import org.springframework.web.bind.annotation.RequestParam;
5 | import org.springframework.web.bind.annotation.RestController;
6 |
7 | import com.alibaba.fastjson.JSON;
8 | import com.lyqf.bullet.comet.client.PushClient;
9 | import com.lyqf.bullet.comet.client.vo.PushReq;
10 | import com.lyqf.bullet.common.model.ApiResponse;
11 |
12 | import lombok.extern.slf4j.Slf4j;
13 |
14 | import javax.annotation.Resource;
15 |
16 | /**
17 | * @author chenlang
18 | * @date 2022/5/19 7:45 下午
19 | */
20 |
21 | @Slf4j
22 | @RestController
23 | public class TestContoller {
24 |
25 | @Resource
26 | private PushClient pushClient;
27 |
28 | @GetMapping("/a")
29 | public ApiResponse pushCommonMsg(@RequestParam String data) {
30 | // todo check biz logic and rate limit
31 | PushReq pushReq = JSON.parseObject(data, PushReq.class);
32 | // todo find roomid对应的的node id
33 |
34 | pushClient.push(pushReq);
35 | log.info("push success:{}", data);
36 |
37 | return ApiResponse.genSuccessData(true);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/manager/LiveNodeManager.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.manager;
2 |
3 | import com.lyqf.bullet.common.constant.RedisConstant;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.data.redis.core.RedisTemplate;
6 | import org.springframework.stereotype.Component;
7 |
8 | import java.util.Set;
9 |
10 | /**
11 | * @author chenlang
12 | * @date 2022/5/24 5:28 下午
13 | */
14 |
15 | @Component
16 | public class LiveNodeManager {
17 |
18 | @Autowired
19 | private RedisTemplate redisTemplate;
20 |
21 | /**
22 | * todo 后面改走rpc调用logic服务获取节点信息(redis集群做业务分离)
23 | *
24 | * @param roomId
25 | * @return
26 | */
27 | public Set getNodeIdsByRoomId(Long roomId) {
28 | String roomNodesKey = String.format(RedisConstant.ROOM_NODE, roomId);
29 | return redisTemplate.opsForSet().members(roomNodesKey);
30 | }
31 |
32 | /**
33 | * todo 后面改走rpc调用logic服务获取节点信息(redis集群做业务分离)
34 | *
35 | * @param userId
36 | * @return
37 | */
38 | public String getNodeIdsByUserId(Long userId) {
39 | String roomNodesKey = String.format(RedisConstant.USER_NODE, userId);
40 | return (String) redisTemplate.opsForValue().get(roomNodesKey);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/config/CustomerLoadBalancerConfig.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.config;
2 |
3 | import org.springframework.cloud.client.ServiceInstance;
4 | import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
5 | import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
6 | import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
7 | import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.core.env.Environment;
10 |
11 | import com.lyqf.bullet.common.loadbanlance.CustomerLoadBalance;
12 |
13 | /**
14 | * @author chenlang
15 | * @date 2022/5/28 11:50 下午
16 | */
17 | @LoadBalancerClients(defaultConfiguration = CustomerLoadBalancerConfig.class)
18 | public class CustomerLoadBalancerConfig {
19 |
20 | /**
21 | *
22 | * @param
23 | * @return
24 | */
25 | @Bean
26 | public ReactorLoadBalancer reactorServiceInstanceLoadBalancer(Environment environment,
27 | LoadBalancerClientFactory loadBalancerClientFactory) {
28 | String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
29 | return new CustomerLoadBalance(
30 | loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/config/CustomerLoadBalancerConfig.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.config;
2 |
3 | import org.springframework.cloud.client.ServiceInstance;
4 | import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
5 | import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
6 | import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
7 | import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.core.env.Environment;
10 |
11 | import com.lyqf.bullet.common.loadbanlance.CustomerLoadBalance;
12 |
13 | /**
14 | * @author chenlang
15 | * @date 2022/5/28 11:50 下午
16 | */
17 | @LoadBalancerClients(defaultConfiguration = CustomerLoadBalancerConfig.class)
18 | public class CustomerLoadBalancerConfig {
19 |
20 | /**
21 | *
22 | * @param
23 | * @return
24 | */
25 | @Bean
26 | public ReactorLoadBalancer reactorServiceInstanceLoadBalancer(Environment environment,
27 | LoadBalancerClientFactory loadBalancerClientFactory) {
28 | String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
29 | return new CustomerLoadBalance(
30 | loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-facade/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | bullet-logic
7 | com.lyqf
8 | 1.0-SNAPSHOT
9 |
10 |
11 | 4.0.0
12 | bullet-logic-facade
13 |
14 |
15 | 8
16 | 8
17 |
18 |
19 |
20 |
21 | com.lyqf
22 | bullet-common
23 | 1.0-SNAPSHOT
24 | compile
25 |
26 |
27 |
28 | org.projectlombok
29 | lombok
30 | true
31 |
32 |
33 |
34 | org.springframework.cloud
35 | spring-cloud-starter-openfeign
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/web/UserAndRoomSimpleManagerController.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.web;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.data.redis.core.RedisTemplate;
5 | import org.springframework.web.bind.annotation.PostMapping;
6 | import org.springframework.web.bind.annotation.RequestBody;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | import com.lyqf.bullet.logic.constant.RedisConstant;
11 | import com.lyqf.bullet.logic.dto.RoomDTO;
12 | import com.lyqf.bullet.logic.dto.UserInfoDTO;
13 |
14 | /**
15 | * @author chenlang
16 | * @date 2022/5/19 1:21 下午
17 | */
18 | @RestController
19 | @RequestMapping
20 | public class UserAndRoomSimpleManagerController {
21 |
22 | @Autowired
23 | private RedisTemplate redisTemplate;
24 |
25 |
26 |
27 | @PostMapping("/room/add")
28 | public String addRoom(@RequestBody RoomDTO roomDTO) {
29 | String roomKey = String.format(RedisConstant.ROOM_KEY, roomDTO.getId());
30 | redisTemplate.opsForValue().set(roomKey, roomDTO);
31 | return "success";
32 | }
33 |
34 |
35 |
36 | /**
37 | *
38 | * @param userInfoDTO
39 | * @return
40 | */
41 | @PostMapping("/user/add")
42 | public String addUser(@RequestBody UserInfoDTO userInfoDTO) {
43 | String tokenKey = String.format(RedisConstant.TOKEN_KEY, userInfoDTO.getId());
44 | redisTemplate.opsForValue().set(tokenKey, userInfoDTO);
45 | return "success";
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/util/UrlUtil.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.util;
2 |
3 | import java.util.Map;
4 |
5 | import org.apache.commons.lang3.StringUtils;
6 |
7 | import com.lyqf.bullet.comet.constant.ExceptionCodeConstant;
8 | import com.lyqf.bullet.comet.exception.ParamsException;
9 | import com.lyqf.bullet.comet.vo.LoginReq;
10 |
11 | import io.github.ljwlgl.util.UrlParamsUtil;
12 | import lombok.extern.slf4j.Slf4j;
13 |
14 | /**
15 | * @author chenlang
16 | * @date 2022/5/11 4:15 下午
17 | */
18 | @Slf4j
19 | public class UrlUtil {
20 |
21 | /**
22 | *
23 | * @param url
24 | * @return
25 | */
26 | public static LoginReq getLoginInfo(String url) {
27 | log.info("getUrlParams url:{}", url);
28 | Map data = UrlParamsUtil.split(url);
29 |
30 | LoginReq loginReq = new LoginReq();
31 | String roomId = data.get("roomId");
32 | if (StringUtils.isBlank(roomId)) {
33 | throw new ParamsException(ExceptionCodeConstant.PARAMS_ERROR_CODE, "房间号不能为空");
34 | }
35 |
36 | String token = data.get("token");
37 | if (StringUtils.isBlank(token)) {
38 | throw new ParamsException(ExceptionCodeConstant.PARAMS_ERROR_CODE, "没有认证信息!");
39 | }
40 |
41 | try {
42 | loginReq.setRoomId(Long.parseLong(roomId));
43 | loginReq.setToken(token);
44 | } catch (Exception e) {
45 | log.warn("e", e);
46 | throw new ParamsException(ExceptionCodeConstant.PARAMS_ERROR_CODE, "请求参数不正确");
47 | }
48 |
49 | return loginReq;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/manager/UserClearOnlineManager.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.manager;
2 |
3 | import com.lyqf.bullet.comet.context.UserContextHolder;
4 | import com.lyqf.bullet.comet.dto.UserRoomDTO;
5 | import com.lyqf.bullet.comet.util.SpringContextBeanUtil;
6 | import com.lyqf.bullet.logic.client.UserAuthClient;
7 | import com.lyqf.bullet.logic.client.vo.ClearOnlineReq;
8 | import io.netty.channel.ChannelHandlerContext;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | /**
12 | * @author chenlang
13 | * @date 2022/5/26 4:30 下午
14 | */
15 |
16 | @Slf4j
17 | public class UserClearOnlineManager {
18 |
19 | private static UserAuthClient userAuthClient = SpringContextBeanUtil.getBean(UserAuthClient.class);
20 |
21 |
22 | /**
23 | *
24 | * @param ctx
25 | * @param cause
26 | */
27 | public static void clearOnlineInfo(ChannelHandlerContext ctx, Throwable cause) {
28 | UserRoomDTO userRoomDTO = UserContextHolder.channelWithUserMap.get(ctx.channel());
29 | log.error(" authHandler error and close the channel,userId:{}",
30 | userRoomDTO == null ? -1 : userRoomDTO.getUserId(), cause);
31 |
32 | if (userRoomDTO == null) {
33 | ctx.channel().close();
34 | return;
35 | }
36 |
37 | ClearOnlineReq req = new ClearOnlineReq();
38 | req.setUserId(userRoomDTO.getUserId());
39 | req.setRoomId(userRoomDTO.getRoomId());
40 | userAuthClient.clearOnlineByUserId(req);
41 |
42 | UserContextHolder.clear(ctx.channel(), userRoomDTO.getUserId(), userRoomDTO.getRoomId());
43 | ctx.channel().close();
44 |
45 | log.info("clear userId online status finish,userId:{}", userRoomDTO.getUserId());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/model/ApiResponse.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.model;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 |
10 | /**
11 | * @author chenlang
12 | * @date 2022/5/12 3:13 下午
13 | */
14 |
15 | @Data
16 | @AllArgsConstructor
17 | @NoArgsConstructor
18 | @Builder
19 | @SuppressWarnings("all")
20 | public class ApiResponse implements Serializable {
21 |
22 | private static final long serialVersionUID = 7090117243937573164L;
23 |
24 |
25 | public static final String ERROR_MES = "error";
26 | public static final String SUCCESS_MES = "success";
27 |
28 | public static final Integer SUCCESS_CODE = 200;
29 | public static final Integer ERROR_CODE = 400;
30 |
31 | private Integer code;
32 | private String msg;
33 | private T data;
34 |
35 | /**
36 | *
37 | * @return
38 | */
39 | public boolean isSuccess() {
40 | return SUCCESS_CODE.equals(code);
41 | }
42 |
43 | /**
44 | *
45 | */
46 | public static ApiResponse genSuccessData(Object data) {
47 | return ApiResponse.builder().code(SUCCESS_CODE).msg(SUCCESS_MES).data(data).build();
48 | }
49 |
50 | public static ApiResponse genDefaultErrorData() {
51 | return ApiResponse.builder().code(ERROR_CODE).msg(ERROR_MES).build();
52 | }
53 |
54 | public static ApiResponse genErrorData(Integer code, String msg) {
55 | return ApiResponse.builder().code(code).msg(msg).build();
56 | }
57 |
58 | public static ApiResponse genErrorData(Integer code, String msg, Object data) {
59 | return ApiResponse.builder().code(code).msg(msg).data(data).build();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/bullet-common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | bullet-chat-new
7 | com.lyqf
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | bullet-common
13 |
14 |
15 | 8
16 | 8
17 |
18 |
19 |
20 |
21 | org.projectlombok
22 | lombok
23 | true
24 |
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-web
29 | true
30 |
31 |
32 |
33 | org.springframework.cloud
34 | spring-cloud-loadbalancer
35 | true
36 |
37 |
38 |
39 | org.apache.commons
40 | commons-lang3
41 | true
42 |
43 |
44 |
45 | com.alibaba
46 | transmittable-thread-local
47 |
48 |
49 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/KafkaProducerConfig.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.apache.kafka.clients.producer.ProducerConfig;
7 | import org.apache.kafka.common.serialization.StringSerializer;
8 | import org.springframework.beans.factory.annotation.Value;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.kafka.core.DefaultKafkaProducerFactory;
12 | import org.springframework.kafka.core.KafkaTemplate;
13 | import org.springframework.kafka.core.ProducerFactory;
14 |
15 | /**
16 | * @author chenlang
17 | * @date 2022/5/17 3:21 下午
18 | */
19 | @Configuration
20 | public class KafkaProducerConfig {
21 |
22 | @Value("${spring.kafka.nodes}")
23 | private String kafkaServer;
24 |
25 | @Bean
26 | public ProducerFactory producerFactory() {
27 | return new DefaultKafkaProducerFactory<>(producerConfigs());
28 | }
29 |
30 | @Bean
31 | public Map producerConfigs() {
32 | Map props = new HashMap<>();
33 | props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaServer);
34 | props.put(ProducerConfig.RETRIES_CONFIG, 0);
35 | props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
36 | props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
37 | props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
38 | props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
39 | props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
40 | return props;
41 | }
42 |
43 | @Bean
44 | public KafkaTemplate kafkaTemplate() {
45 | return new KafkaTemplate(producerFactory());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/manager/KafkaManger.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.manager;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.kafka.core.KafkaTemplate;
5 | import org.springframework.kafka.support.SendResult;
6 | import org.springframework.stereotype.Component;
7 | import org.springframework.util.concurrent.ListenableFuture;
8 | import org.springframework.util.concurrent.ListenableFutureCallback;
9 |
10 | import com.alibaba.fastjson.JSON;
11 | import com.lyqf.bullet.common.enums.LevelEnum;
12 | import com.lyqf.bullet.logic.constant.KafkaConstant;
13 | import com.lyqf.bullet.logic.dto.MsgDTO;
14 |
15 | import lombok.extern.slf4j.Slf4j;
16 |
17 | /**
18 | * @author chenlang
19 | * @date 2022/5/16 4:16 下午
20 | */
21 | @Component
22 | @Slf4j
23 | public class KafkaManger {
24 |
25 | @Autowired
26 | KafkaTemplate kafkaTemplate;
27 |
28 | /**
29 | * todo 同一用户发到同一个partition中
30 | * 相同hashcode一样的roomId走同一个topic
31 | */
32 | public void sendMsg(MsgDTO msgDTO) {
33 | ListenableFuture> future = null;
34 | if (LevelEnum.SYSTEM.getCode().equals(msgDTO.getLevel())) {
35 | future = kafkaTemplate.send(KafkaConstant.SYS_MSG_TOPIC, JSON.toJSONString(msgDTO));
36 | } else if (LevelEnum.EXT.getCode().equals(msgDTO.getLevel())) {
37 | future = kafkaTemplate.send(KafkaConstant.EXT_MSG_TOPIC, JSON.toJSONString(msgDTO));
38 | } else {
39 | future = kafkaTemplate.send(KafkaConstant.COMMON_MSG_TOPIC, JSON.toJSONString(msgDTO));
40 | }
41 |
42 | future.addCallback(new ListenableFutureCallback>() {
43 | @Override
44 | public void onSuccess(SendResult result) {
45 | log.info("kafka 发送消息成功,result :{}", result.toString());
46 | }
47 |
48 | @Override
49 | public void onFailure(Throwable ex) {
50 | log.warn("kafka 发送消息失败", ex);
51 | }
52 | });
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/biz/ExtMsgPushBiz.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.biz;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.lyqf.bullet.comet.client.PushClient;
5 | import com.lyqf.bullet.comet.client.vo.PushReq;
6 | import com.lyqf.bullet.push.manager.LiveNodeManager;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.apache.commons.lang3.ObjectUtils;
9 | import org.apache.commons.lang3.StringUtils;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.stereotype.Component;
12 |
13 | import javax.annotation.Resource;
14 | import java.util.Set;
15 |
16 | /**
17 | * @author chenlang
18 | * @date 2022/5/18 4:25 下午
19 | */
20 | @Slf4j
21 | @Component
22 | public class ExtMsgPushBiz {
23 |
24 | @Resource
25 | private PushClient pushClient;
26 |
27 | @Autowired
28 | private LiveNodeManager liveNodeManager;
29 |
30 | /**
31 | *
32 | * @param data
33 | */
34 | public void pushExtMsg(String data) {
35 | // todo check biz logic and rate limit
36 | PushReq pushReq = JSON.parseObject(data, PushReq.class);
37 | // todo find roomid对应的的node id
38 |
39 | if (pushReq == null) {
40 | return;
41 | }
42 |
43 | if (pushReq.getToUserId() != null) {
44 | String nodeId = liveNodeManager.getNodeIdsByUserId(pushReq.getToUserId());
45 | if (StringUtils.isNotBlank(nodeId)) {
46 | pushClient.push(pushReq);
47 | } else {
48 | log.info("单聊消息,没有找到用户:{}的连接信息", pushReq.getToUserId());
49 | }
50 |
51 | } else { // broadcast
52 | Set nodeIds = liveNodeManager.getNodeIdsByRoomId(pushReq.getRoomId());
53 | if (!ObjectUtils.isEmpty(nodeIds)) {
54 | nodeIds.forEach(k -> {
55 | pushClient.push(pushReq);
56 | });
57 | } else {
58 | log.info("群聊消息,没有找到room:{}的连接信息", pushReq.getRoomId());
59 | }
60 |
61 | }
62 |
63 | log.info("pushExtMsg push success:{}", data);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/config/KafkaConsumerConfig.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.config;
2 |
3 | import org.apache.kafka.clients.consumer.ConsumerConfig;
4 | import org.apache.kafka.common.serialization.StringDeserializer;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
9 | import org.springframework.kafka.core.ConsumerFactory;
10 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | /**
16 | * @author chenlang
17 | * @date 2022/5/18 5:13 下午
18 | */
19 |
20 | @Configuration
21 | public class KafkaConsumerConfig {
22 |
23 | @Value("${spring.kafka.nodes}")
24 | private String nodes;
25 |
26 | @Value("${spring.kafka.consumer.group-id}")
27 | private String groupId;
28 |
29 |
30 | @Bean
31 | ConcurrentKafkaListenerContainerFactory
32 | kafkaListenerContainerFactory() {
33 | ConcurrentKafkaListenerContainerFactory factory =
34 | new ConcurrentKafkaListenerContainerFactory<>();
35 | factory.setConsumerFactory(consumerFactory());
36 | return factory;
37 | }
38 |
39 |
40 | @Bean
41 | public ConsumerFactory consumerFactory() {
42 | return new DefaultKafkaConsumerFactory<>(consumerProps());
43 | }
44 |
45 | @Bean
46 | public Map consumerProps() {
47 | Map props = new HashMap<>();
48 | props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, nodes);
49 | props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
50 | props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
51 | props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "100");
52 | props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "15000");
53 | props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
54 | props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
55 | return props;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/context/UserContextHolder.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.context;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Map;
6 | import java.util.concurrent.ConcurrentHashMap;
7 |
8 | import org.apache.commons.lang3.ObjectUtils;
9 |
10 | import com.lyqf.bullet.comet.dto.UserRoomDTO;
11 |
12 | import io.netty.channel.Channel;
13 |
14 | /**
15 | * @author chenlang
16 | * @date 2022/5/11 6:41 下午
17 | */
18 |
19 | @SuppressWarnings("all")
20 | public class UserContextHolder {
21 |
22 | /**
23 | * 用户 -> 连接 map
24 | */
25 | public static Map userWithChannelMap = new ConcurrentHashMap<>();
26 |
27 | /**
28 | * 连接 -> 用户 map
29 | */
30 | public static Map channelWithUserMap = new ConcurrentHashMap<>();
31 |
32 | /**
33 | * 一个房间记录了哪些用户链接
34 | */
35 | public static Map> roomWithChannelsMap = new ConcurrentHashMap<>();
36 |
37 | /**
38 | *
39 | * @param channel
40 | * @param userRoomDTO
41 | */
42 | public static void add(Channel channel, UserRoomDTO userRoomDTO) {
43 | userWithChannelMap.put(userRoomDTO.getUserId(), channel);
44 | channelWithUserMap.put(channel, userRoomDTO);
45 | List channels = roomWithChannelsMap.get(userRoomDTO.getRoomId());
46 | if (ObjectUtils.isEmpty(channels)) {
47 | channels = new ArrayList();
48 | }
49 | channels.add(channel);
50 |
51 | roomWithChannelsMap.put(userRoomDTO.getRoomId(), channels);
52 | }
53 |
54 | /**
55 | *
56 | * @param channel
57 | * @param userId
58 | */
59 | public static void clear(Channel channel, Long userId, Long roomId) {
60 | if (channel != null) {
61 | channelWithUserMap.remove(channel);
62 | }
63 |
64 | if (userId != null) {
65 | userWithChannelMap.remove(userId);
66 | }
67 |
68 | if (roomId == null) {
69 | return;
70 | }
71 |
72 | List channels = roomWithChannelsMap.get(roomId);
73 | if (!ObjectUtils.isEmpty(channels) && channels.contains(channels)) {
74 | channels.remove(channel);
75 | }
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/bullet-push/bullet-push-core/src/main/java/com/lyqf/bullet/push/biz/CommonMsgPushBiz.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.push.biz;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.lyqf.bullet.comet.client.PushClient;
5 | import com.lyqf.bullet.comet.client.vo.PushReq;
6 | import com.lyqf.bullet.common.holder.RpcContextHolder;
7 | import com.lyqf.bullet.push.manager.LiveNodeManager;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.apache.commons.lang3.ObjectUtils;
10 | import org.apache.commons.lang3.StringUtils;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.stereotype.Component;
13 |
14 | import javax.annotation.Resource;
15 | import java.util.Set;
16 |
17 | /**
18 | * @author chenlang
19 | * @date 2022/5/18 4:25 下午
20 | */
21 | @Slf4j
22 | @Component
23 | public class CommonMsgPushBiz {
24 |
25 | @Resource
26 | private PushClient pushClient;
27 |
28 | @Autowired
29 | private LiveNodeManager liveNodeManager;
30 |
31 | /**
32 | *
33 | * @param data
34 | */
35 | public void pushCommonMsg(String data) {
36 | // todo check biz logic and rate limit
37 | PushReq pushReq = JSON.parseObject(data, PushReq.class);
38 | // todo find roomid对应的的node id
39 |
40 | if (pushReq == null) {
41 | return;
42 | }
43 |
44 | if (pushReq.getToUserId() != null) {
45 | String nodeId = liveNodeManager.getNodeIdsByUserId(pushReq.getToUserId());
46 | if (StringUtils.isNotBlank(nodeId)) {
47 | RpcContextHolder.setStringParameter(nodeId);
48 | pushClient.push(pushReq);
49 | } else {
50 | log.info("单聊消息,没有找到用户:{}的连接信息", pushReq.getToUserId());
51 | }
52 |
53 | } else { // broadcast
54 | Set nodeIds = liveNodeManager.getNodeIdsByRoomId(pushReq.getRoomId());
55 | if (!ObjectUtils.isEmpty(nodeIds)) {
56 | nodeIds.forEach(k -> {
57 | RpcContextHolder.setStringParameter(k);
58 | pushClient.push(pushReq);
59 | });
60 | } else {
61 | log.info("群聊消息,没有找到room:{}的连接信息", pushReq.getRoomId());
62 | }
63 | }
64 |
65 | log.info("push success:{}", data);
66 |
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/netty/handler/MessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.netty.handler;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.lyqf.bullet.comet.context.UserContextHolder;
5 | import com.lyqf.bullet.comet.dto.UserRoomDTO;
6 | import com.lyqf.bullet.comet.manager.UserClearOnlineManager;
7 | import com.lyqf.bullet.comet.util.SpringContextBeanUtil;
8 | import com.lyqf.bullet.common.enums.OperateTypeEnum;
9 | import com.lyqf.bullet.common.exception.BizException;
10 | import com.lyqf.bullet.logic.client.UserMsgClient;
11 | import com.lyqf.bullet.logic.client.vo.UserMsgReq;
12 | import io.netty.channel.ChannelHandlerContext;
13 | import io.netty.channel.SimpleChannelInboundHandler;
14 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
15 | import lombok.extern.slf4j.Slf4j;
16 | import org.apache.commons.lang3.StringUtils;
17 |
18 | /**
19 | * @author chenlang
20 | * @date 2022/5/11 3:48 下午
21 | */
22 |
23 | @Slf4j
24 | public class MessageHandler extends SimpleChannelInboundHandler {
25 |
26 | private static final String PING = "PING@!2#4%6&8";
27 |
28 | private static final String PONG = "PONG@!2#4%6&8";
29 |
30 | private UserMsgClient userMsgClient = SpringContextBeanUtil.getBean(UserMsgClient.class);
31 |
32 | @Override
33 | protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
34 | UserRoomDTO userRoomDTO = UserContextHolder.channelWithUserMap.get(ctx.channel());
35 | if (userRoomDTO == null) {
36 | log.error("没找到用户数据。。。。");
37 | throw new BizException(4000, "用户数据没找到");
38 | }
39 |
40 | String msgData = msg.text();
41 | log.info("receive userId msg ,userId:{},msg:{}", userRoomDTO.getUserId(), msgData);
42 | if (StringUtils.isBlank(msgData)) {
43 | return;
44 | }
45 |
46 | UserMsgReq userMsgReq;
47 | //heart msg
48 | if (msgData.toUpperCase().contains(PING)) {
49 | userMsgReq = new UserMsgReq();
50 | userMsgReq.setUserId(userRoomDTO.getUserId());
51 | userMsgReq.setToUserId(userRoomDTO.getUserId());
52 | userMsgReq.setOperateType(OperateTypeEnum.HERART_PING.getCode());
53 | userMsgReq.setContent(PONG);
54 | } else {
55 | userMsgReq = JSONObject.parseObject(msgData, UserMsgReq.class);
56 | userMsgReq.setUserId(userRoomDTO.getUserId());
57 | }
58 |
59 | userMsgClient.sendMsg(userMsgReq);
60 | }
61 |
62 | @Override
63 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
64 | UserClearOnlineManager.clearOnlineInfo(ctx, cause);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/bullet-comet/bullet-comet-core/src/main/java/com/lyqf/bullet/comet/web/PushController.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.comet.web;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.lyqf.bullet.comet.context.UserContextHolder;
5 | import com.lyqf.bullet.comet.dto.UserRoomDTO;
6 | import com.lyqf.bullet.comet.client.vo.PushReq;
7 | import com.lyqf.bullet.comet.vo.PushMsg;
8 | import com.lyqf.bullet.common.model.ApiResponse;
9 | import io.netty.channel.Channel;
10 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
11 | import lombok.extern.slf4j.Slf4j;
12 | import org.apache.commons.lang3.ObjectUtils;
13 | import org.springframework.web.bind.annotation.PostMapping;
14 | import org.springframework.web.bind.annotation.RequestBody;
15 | import org.springframework.web.bind.annotation.RestController;
16 |
17 | import java.util.List;
18 |
19 | /**
20 | * @author chenlang
21 | * @date 2022/5/17 4:27 下午
22 | */
23 | @Slf4j
24 | @RestController
25 | public class PushController {
26 |
27 | @PostMapping("/push")
28 | public ApiResponse push(@RequestBody PushReq pushReq) {
29 | log.info("接到收消息:{}", pushReq);
30 |
31 | PushMsg msg = new PushMsg();
32 | msg.setContent(pushReq.getContent());
33 | msg.setOperateType(pushReq.getOperateType());
34 | msg.setUserNickName(pushReq.getUserNickName());
35 | msg.setUserId(pushReq.getUserId());
36 | String outMsg = JSONObject.toJSONString(msg);
37 |
38 | // s to s
39 | if (pushReq.getToUserId() != null) {
40 | log.info("to user msg,to userId:{}", pushReq.getToUserId());
41 | Channel channel = UserContextHolder.userWithChannelMap.get(pushReq.getToUserId());
42 | if (channel != null) {
43 | channel.writeAndFlush(new TextWebSocketFrame(outMsg));
44 | }
45 | } else { // broadcast
46 | List channelList = UserContextHolder.roomWithChannelsMap.get(pushReq.getRoomId());
47 | if (ObjectUtils.isNotEmpty(channelList)) {
48 | channelList.forEach(k -> {
49 | UserRoomDTO userRoomDTO = UserContextHolder.channelWithUserMap.get(k);
50 | if (userRoomDTO != null) {
51 | Long channelUserId = userRoomDTO.getUserId();
52 | // 不能自己发消息给自己
53 | if (!pushReq.getUserId().equals(channelUserId)) {
54 | k.writeAndFlush(new TextWebSocketFrame(outMsg));
55 | log.info("向userid 为:{} 推送消息成功.", userRoomDTO.getUserId());
56 | }
57 | }
58 | });
59 | }
60 | }
61 |
62 | return ApiResponse.genSuccessData(true);
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/bullet-common/src/main/java/com/lyqf/bullet/common/loadbanlance/CustomerLoadBalance.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.common.loadbanlance;
2 |
3 | import com.lyqf.bullet.common.constant.CommonConstant;
4 | import com.lyqf.bullet.common.exception.ProviderNotFoundException;
5 | import com.lyqf.bullet.common.holder.RpcContextHolder;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.apache.commons.lang3.StringUtils;
8 | import org.springframework.beans.factory.ObjectProvider;
9 | import org.springframework.cloud.client.ServiceInstance;
10 | import org.springframework.cloud.client.loadbalancer.DefaultResponse;
11 | import org.springframework.cloud.client.loadbalancer.EmptyResponse;
12 | import org.springframework.cloud.client.loadbalancer.Request;
13 | import org.springframework.cloud.client.loadbalancer.Response;
14 | import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
15 | import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
16 | import reactor.core.publisher.Mono;
17 |
18 | import java.util.List;
19 | import java.util.Random;
20 |
21 | /**
22 | * @author chenlang
23 | * @date 2022/5/19 2:40 下午
24 | */
25 | @SuppressWarnings("all")
26 | @Slf4j
27 | public class CustomerLoadBalance implements ReactorServiceInstanceLoadBalancer {
28 |
29 | // 服务列表
30 | private ObjectProvider serviceInstanceListSupplierProvider;
31 |
32 | public CustomerLoadBalance(ObjectProvider serviceInstanceListSupplierProvider,
33 | String name) {
34 | this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
35 | }
36 |
37 | @Override
38 | public Mono> choose(Request request) {
39 | try {
40 | String customerNodeId = RpcContextHolder.getStringValue();
41 | log.info("choose customerNodeId:{}", customerNodeId);
42 | ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable();
43 | return supplier.get().next().map(serviceInstances -> chooseService(serviceInstances, customerNodeId));
44 | } finally {
45 | RpcContextHolder.remove();
46 | }
47 | }
48 |
49 | /**
50 | * 使用随机数获取服务
51 | *
52 | * @param instances
53 | * @return
54 | */
55 | private Response chooseService(List instances, String customerNodeId) {
56 | if (instances.isEmpty()) {
57 | if (log.isWarnEnabled()) {
58 | log.warn("customer loadbance. No servers available for service.........");
59 | }
60 |
61 | return new EmptyResponse();
62 | }
63 |
64 | if (StringUtils.isNotBlank(customerNodeId)) {
65 | for (ServiceInstance instance : instances) {
66 | String host = instance.getHost();
67 | if (customerNodeId.equalsIgnoreCase(host)) {
68 | log.info("load banlance choose host:{} node", host);
69 | return new DefaultResponse(instance);
70 | }
71 | }
72 | throw new ProviderNotFoundException(CommonConstant.NO_PROVIDER_ERROR_CODE,"can't find host:" + customerNodeId + " provider");
73 | }
74 |
75 | // 随机算法
76 | int size = instances.size();
77 | Random random = new Random();
78 | ServiceInstance instance = instances.get(random.nextInt(size));
79 | return new DefaultResponse(instance);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/bullet-logic/bullet-logic-core/src/main/java/com/lyqf/bullet/logic/manager/UserAuthManager.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.logic.manager;
2 |
3 | import com.lyqf.bullet.comet.client.CloseUserConnectionClient;
4 | import com.lyqf.bullet.common.exception.BizException;
5 | import com.lyqf.bullet.common.holder.RpcContextHolder;
6 | import com.lyqf.bullet.logic.acl.CloseUserConnectionFacadeAcl;
7 | import com.lyqf.bullet.logic.client.vo.AuthUserReq;
8 | import com.lyqf.bullet.logic.constant.RedisConstant;
9 | import com.lyqf.bullet.logic.dto.RoomDTO;
10 | import com.lyqf.bullet.logic.dto.UserInfoDTO;
11 | import lombok.extern.slf4j.Slf4j;
12 | import org.apache.commons.lang3.StringUtils;
13 | import org.springframework.data.redis.core.RedisTemplate;
14 | import org.springframework.stereotype.Component;
15 |
16 | import javax.annotation.Resource;
17 | import javax.servlet.http.HttpServletRequest;
18 | import java.util.concurrent.TimeUnit;
19 |
20 | /**
21 | * @author chenlang
22 | * @date 2022/5/23 11:37 上午
23 | */
24 |
25 | @Slf4j
26 | @SuppressWarnings("all")
27 | @Component
28 | public class UserAuthManager {
29 |
30 | @Resource
31 | private RedisTemplate redisTemplate;
32 |
33 |
34 | @Resource
35 | private CloseUserConnectionClient closeUserConnectionFacade;
36 |
37 | @Resource
38 | private CloseUserConnectionFacadeAcl closeUserConnectionFacadeAcl;
39 |
40 | /**
41 | *
42 | */
43 | public UserInfoDTO checkUserInfo(AuthUserReq req, HttpServletRequest request) {
44 | String tokenKey = String.format(RedisConstant.TOKEN_KEY, req.getToken());
45 |
46 | UserInfoDTO userInfoDTO = (UserInfoDTO)redisTemplate.opsForValue().get(tokenKey);
47 | if (userInfoDTO == null) {
48 | return genAnonymousAccount();
49 | }
50 |
51 | String roomKey = String.format(RedisConstant.ROOM_KEY, req.getRoomId());
52 | RoomDTO room = (RoomDTO)redisTemplate.opsForValue().get(roomKey);
53 | if (room == null) {
54 | throw new BizException(4002, "房间号信息不正确");
55 | }
56 |
57 | checkUserHasBanLog(userInfoDTO.getId());
58 |
59 | String userNodeKey =
60 | String.format(com.lyqf.bullet.common.constant.RedisConstant.USER_NODE, userInfoDTO.getId());
61 | String userNodeInfo = (String)redisTemplate.opsForValue().get(userNodeKey);
62 | if (StringUtils.isNotBlank(userNodeInfo)) {
63 | RpcContextHolder.setStringParameter(req.getNode());
64 | closeUserConnectionFacadeAcl.close(userInfoDTO.getId(), req.getRoomId());
65 | log.info("用户重复链接,剔除老连接,userId:{}", userInfoDTO.getId());
66 | }
67 | return userInfoDTO;
68 | }
69 |
70 | /**
71 | *
72 | * @return
73 | */
74 | private UserInfoDTO genAnonymousAccount() {
75 | String startIdKey = String.format(RedisConstant.ANONYMOUS_USER_ID_START_KEY);
76 | UserInfoDTO userInfoDTO = new UserInfoDTO();
77 | userInfoDTO.setId(redisTemplate.opsForValue().decrement(startIdKey));
78 | userInfoDTO.setNickName("匿名用户");
79 |
80 | String userInfoKey = String.format(RedisConstant.USER_INFO_KEY, userInfoDTO.getId());
81 | redisTemplate.opsForValue().set(userInfoKey, userInfoDTO, 3, TimeUnit.HOURS);
82 | log.info("create AnonymousAccount,userId:{}", userInfoDTO.getId());
83 | return userInfoDTO;
84 | }
85 |
86 | /**
87 | * 禁止登录
88 | */
89 | private void checkUserHasBanLog(Long userId) {
90 | log.info("该用户id没有被禁言");
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/bullet-client/src/main/java/com/lyqf/bullet/client/BulletChatClient.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.client;
2 |
3 | import com.lyqf.bullet.client.handler.WebSocketClientHandler;
4 | import io.netty.bootstrap.Bootstrap;
5 | import io.netty.channel.ChannelInitializer;
6 | import io.netty.channel.ChannelPipeline;
7 | import io.netty.channel.EventLoopGroup;
8 | import io.netty.channel.nio.NioEventLoopGroup;
9 | import io.netty.channel.socket.SocketChannel;
10 | import io.netty.channel.socket.nio.NioSocketChannel;
11 | import io.netty.handler.codec.http.HttpClientCodec;
12 | import io.netty.handler.codec.http.HttpHeaders;
13 | import io.netty.handler.codec.http.HttpObjectAggregator;
14 | import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
15 | import io.netty.handler.codec.http.websocketx.WebSocketVersion;
16 | import lombok.extern.slf4j.Slf4j;
17 |
18 | import java.net.URI;
19 | import java.util.concurrent.ExecutorService;
20 | import java.util.concurrent.Executors;
21 | import java.util.concurrent.atomic.AtomicInteger;
22 |
23 | /**
24 | * @author
25 | */
26 |
27 | @Slf4j
28 | @SuppressWarnings("all")
29 | public class BulletChatClient {
30 |
31 | private static final EventLoopGroup group = new NioEventLoopGroup();
32 |
33 | private static ExecutorService executorService = Executors.newFixedThreadPool(50);
34 |
35 | private static AtomicInteger atomicInteger = new AtomicInteger(1);
36 |
37 | public static void main(String[] args) throws Exception {
38 | for (int i = 0; i < 10000; i++) {
39 | executorService.submit(() -> {
40 | final String url = "ws://127.0.0.1:8889?roomId=1&token=" + atomicInteger.incrementAndGet();
41 | final BulletChatClient client = new BulletChatClient();
42 | URI uri = URI.create(url);
43 | try {
44 | client.open(uri);
45 | Thread.sleep(100);
46 | } catch (Exception e) {
47 | e.printStackTrace();
48 | }
49 | });
50 | }
51 | }
52 |
53 | public static void open(URI uri) throws Exception {
54 | Bootstrap b = new Bootstrap();
55 | String protocol = uri.getScheme();
56 | if (!"ws".equals(protocol)) {
57 | throw new IllegalArgumentException("Unsupported protocol: " + protocol);
58 | }
59 |
60 | // Connect with V13 (RFC 6455 aka HyBi-17). You can change it to V08 or V00.
61 | // If you change it to V00, ping is not supported and remember to change
62 | // HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline.
63 | final WebSocketClientHandler handler = new WebSocketClientHandler(WebSocketClientHandshakerFactory
64 | .newHandshaker(uri, WebSocketVersion.V13, null, false, HttpHeaders.EMPTY_HEADERS, 1280000));
65 |
66 | b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer() {
67 | @Override
68 | public void initChannel(SocketChannel ch) throws Exception {
69 | ChannelPipeline pipeline = ch.pipeline();
70 | pipeline.addLast("http-codec", new HttpClientCodec());
71 | pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
72 | pipeline.addLast("ws-handler", handler);
73 | }
74 | });
75 |
76 | // System.out.println("WebSocket Client connecting");
77 | b.connect(uri.getHost(), uri.getPort()).sync().channel();
78 | log.info("conncet success");
79 | handler.handshakeFuture().sync();
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/bullet-client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | bullet-chat-new
7 | com.lyqf
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | bullet-client
13 |
14 |
15 | 8
16 | 8
17 |
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-web
23 |
24 |
25 |
26 | com.alibaba.cloud
27 | spring-cloud-starter-alibaba-nacos-discovery
28 |
29 |
30 | springframework.cloud
31 | spring-cloud-starter-netflix-ribbon
32 |
33 |
34 |
35 |
36 |
37 | org.springframework.cloud
38 | spring-cloud-loadbalancer
39 |
40 |
41 |
42 | org.springframework.cloud
43 | spring-cloud-starter-openfeign
44 |
45 |
46 |
47 | io.netty
48 | netty-all
49 |
50 |
51 |
52 | io.github.ljwlgl
53 | common-util
54 |
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-starter-actuator
59 |
60 |
61 |
62 |
63 | org.projectlombok
64 | lombok
65 |
66 |
67 |
68 | com.lyqf
69 | bullet-logic-facade
70 | 1.0-SNAPSHOT
71 |
72 |
73 |
74 | com.lyqf
75 | bullet-common
76 | 1.0-SNAPSHOT
77 |
78 |
79 |
80 | com.lyqf
81 | bullet-comet-facade
82 | 1.0-SNAPSHOT
83 |
84 |
85 |
86 |
87 | com.alibaba
88 | fastjson
89 |
90 |
91 |
92 | org.projectreactor
93 | reactor-spring
94 |
95 |
96 |
97 | org.apache.commons
98 | commons-lang3
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/bullet-client/src/main/java/com/lyqf/bullet/client/handler/WebSocketClientHandler.java:
--------------------------------------------------------------------------------
1 | package com.lyqf.bullet.client.handler;
2 |
3 | import io.netty.channel.Channel;
4 | import io.netty.channel.ChannelFuture;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.channel.ChannelPromise;
7 | import io.netty.channel.SimpleChannelInboundHandler;
8 | import io.netty.handler.codec.http.FullHttpResponse;
9 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
10 | import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
11 | import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
12 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
13 | import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
14 | import io.netty.handler.codec.http.websocketx.WebSocketFrame;
15 | import io.netty.util.CharsetUtil;
16 | import lombok.extern.slf4j.Slf4j;
17 |
18 | /**
19 | * @author chenlang
20 | * @date 2022/6/6 5:56 下午
21 | */
22 |
23 | @SuppressWarnings("all")
24 | @Slf4j
25 | public class WebSocketClientHandler extends SimpleChannelInboundHandler