├── .gitignore
├── README.md
├── README
├── pic
│ ├── cloudpush-framework.png
│ ├── cloudpush-logo.png
│ ├── httpSend.gif
│ ├── send.gif
│ ├── websocketSend.gif
│ ├── wx+.jpg
│ └── wx_money.png
└── wiki
│ ├── nacos-import.jpg
│ ├── nacos-login.jpg
│ ├── nacos-start.jpg
│ └── nacos-update.jpg
├── cloudpush-api
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── huangliang
│ └── api
│ ├── annotation
│ └── LogOperate.java
│ ├── config
│ ├── RedisComConfig.java
│ └── RocketMQConfig.java
│ ├── constants
│ ├── CommonConsts.java
│ ├── Constants.java
│ └── RedisPrefix.java
│ ├── entity
│ ├── Client.java
│ ├── WebsocketMessage.java
│ ├── request
│ │ ├── AbstractRequest.java
│ │ └── SendRequest.java
│ └── response
│ │ └── Response.java
│ ├── exception
│ ├── NetException.java
│ └── ServiceException.java
│ └── util
│ ├── DateUtils.java
│ ├── NetUtils.java
│ ├── ObjUtils.java
│ ├── RedisUtils.java
│ └── UUIDUtils.java
├── cloudpush-eureka
├── .gitignore
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── huangliang
│ │ │ └── eureka
│ │ │ ├── CloudpushEurekaApplication.java
│ │ │ ├── config
│ │ │ └── RedisConfig.java
│ │ │ └── listener
│ │ │ └── EurekaStateChangeListener.java
│ └── resources
│ │ ├── application.yml
│ │ ├── bootstrap.properties
│ │ └── logback.xml_bak
│ └── test
│ └── java
│ └── com
│ └── huangliang
│ └── eureka
│ └── CloudpushEurekaApplicationTests.java
├── cloudpush-gateway
├── .gitignore
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── huangliang
│ │ │ └── cloudpushgateway
│ │ │ ├── CloudpushGatewayApplication.java
│ │ │ ├── config
│ │ │ └── RedisConfig.java
│ │ │ ├── route
│ │ │ └── RedisRouteDefinitionRepository.java
│ │ │ └── service
│ │ │ └── RouteService.java
│ └── resources
│ │ ├── application-config.yml
│ │ ├── application.yml
│ │ └── bootstrap.properties
│ └── test
│ └── java
│ └── com
│ └── huangliang
│ └── cloudpushgateway
│ └── CloudpushGatewayApplicationTests.java
├── cloudpush-portal
├── .gitignore
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── huangliang
│ │ │ └── cloudpushportal
│ │ │ ├── CloudpushPortalApplication.java
│ │ │ ├── aspect
│ │ │ └── LogAdvice.java
│ │ │ ├── config
│ │ │ ├── GlobalExceptionHandler.java
│ │ │ └── Swagger2.java
│ │ │ ├── controller
│ │ │ ├── MessageController.java
│ │ │ ├── NacosTest.java
│ │ │ └── ServerController.java
│ │ │ ├── entity
│ │ │ └── res
│ │ │ │ └── WebsocketServer.java
│ │ │ ├── service
│ │ │ ├── MessageService.java
│ │ │ └── messagedispatch
│ │ │ │ ├── HttpDispatchServiceImpl.java
│ │ │ │ ├── MQDispatchServiceImpl.java
│ │ │ │ └── MessageDispatchService.java
│ │ │ └── templet
│ │ │ └── Redis.java
│ └── resources
│ │ ├── application.yml
│ │ └── bootstrap.properties
│ └── test
│ └── java
│ └── com
│ └── huangliang
│ └── cloudpushportal
│ └── CloudpushPortalApplicationTests.java
├── cloudpush-task
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── huangliang
│ │ └── cloudpushtask
│ │ ├── CloudpushTaskApplication.java
│ │ ├── task
│ │ └── CountWebsocketServerWeight.java
│ │ └── templet
│ │ └── Redis.java
│ └── resources
│ ├── application.yml
│ └── bootstrap.properties
├── cloudpush-websocket
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── huangliang
│ │ │ └── cloudpushwebsocket
│ │ │ ├── CloudpushWebsocketApplication.java
│ │ │ ├── config
│ │ │ └── ComConfig.java
│ │ │ ├── constants
│ │ │ ├── AttrConstants.java
│ │ │ └── MessageConstants.java
│ │ │ ├── controller
│ │ │ ├── ChannelController.java
│ │ │ └── MessageController.java
│ │ │ ├── netty
│ │ │ ├── HttpRequestHandler.java
│ │ │ ├── Server.java
│ │ │ ├── ServerInitializer.java
│ │ │ └── WebsocketRequestHandler.java
│ │ │ ├── service
│ │ │ ├── HttpResponseService.java
│ │ │ ├── MessageHttpService.java
│ │ │ ├── channel
│ │ │ │ └── ChannelService.java
│ │ │ ├── message
│ │ │ │ ├── MessageSendService.java
│ │ │ │ └── MqConsumerService.java
│ │ │ └── websocket
│ │ │ │ ├── IWebSocketService.java
│ │ │ │ ├── WebsocketRequestService.java
│ │ │ │ ├── WebsocketServiceStrategy.java
│ │ │ │ ├── handlerClose
│ │ │ │ └── CloseWebSocketService.java
│ │ │ │ ├── handlerPing
│ │ │ │ └── PingWebSocketService.java
│ │ │ │ └── handlerText
│ │ │ │ ├── BussinessService.java
│ │ │ │ ├── HeartBeatService.java
│ │ │ │ ├── IMessageService.java
│ │ │ │ ├── MessageServiceStrategy.java
│ │ │ │ └── TextWebSocketService.java
│ │ │ ├── task
│ │ │ ├── ScanClientsNotOnline.java
│ │ │ └── UpdateRedisChannelActiveTimeTask.java
│ │ │ ├── templet
│ │ │ └── Redis.java
│ │ │ └── util
│ │ │ ├── NettyUtil.java
│ │ │ └── WebsocketMessageGenerateUtils.java
│ └── resources
│ │ ├── application-config.yml
│ │ ├── application.yml
│ │ ├── bootstrap.properties
│ │ └── logback.xml
│ └── test
│ └── java
│ └── com
│ └── huangliang
│ └── cloudpushwebsocket
│ └── CloudpushWebsocketApplicationTests.java
├── pom.xml
└── tools
├── apache-jmeter-5.0.zip
├── client.html
├── nacos-config.zip
└── start.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | #package files
4 |
5 | *.war
6 | *.ear
7 |
8 | #kdiff3 ignore
9 | target/
10 |
11 | #eclipse ignore
12 | .settings/
13 | .project
14 | .classpath
15 |
16 | #idea
17 | .idea/
18 | /idea/
19 | *.ipr
20 | *.iml
21 | *.iws
22 |
23 | # temp file
24 |
25 | *.log
26 | *.cache
27 | *.diff
28 | *.patch
29 | *.tmp
30 |
31 | #system ignore
32 | .DS_Store
33 | Thumbs.db
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # cloudpush- 分布式推送产品
4 | cloudpush是我对于springcloud微服务框架的一个学习实践,旨在学习微服务架构的同时,实现一个基于websocket协议,可平滑水平拓展的分布式推送产品。
5 |
6 | [项目相关博客](https://juejin.im/post/5da457eef265da5b7326e9eb)
7 |
8 | [启动教程](https://github.com/liangQAQ/cloudpush/wiki)
9 |
10 | # 应用架构图
11 | 
12 |
13 | # 项目用到的技术
14 | 目前核心的技术栈采用的是SpringBoot2.0.1.RELEASE
15 |
16 | ## 前端使用的技术
17 | 管理台页面待开发。。
18 |
19 | ## 后端使用的技术
20 | 后端的主要架构是基于springboot.
21 |
22 | * SpringCloud
23 | * Netty
24 | * Nacos
25 | * Redis
26 | * RocketMQ
27 |
28 | # 项目模块说明
29 |
30 | | tools 工具包 | 压缩包 | 项目,或者测试中需要用的到中间件包,直接用docker更方便。。 |
31 | | ------------------------------------------------------------ | --------- | ---------------------------------------------------- |
32 | | cloudpush-api 公共组件部分 | jar | 公共组件,很多地方都有引用 |
33 | | cloudpush-eureka 注册中心 | web项目 | 为啥有了nacos还要eureka:通过@EventListener额外增加对websocket服务状态的监听,从而动态地将websocket服务中额外启动的netty服务的ip、port维护进redis(用zk来维护这个地址也一样) |
34 | | cloudpush-portal 接口服务 | web项目 | 提供http接口服务 |
35 | | cloudpush-task 后台计算服务 | web项目 | 现仅仅根据各个websocket的连接数计算路由权重存进redis,不是运行系统的必要条件|
36 | | cloudpush-websocket 推送服务 | web项目 | 由springboot启动的一个netty服务,启动的netty端口提供websocket的握手,消息的收发服务 |
37 | | cloudpush-gateway 统一网关 | web项目 | 统一入口,便于未来鉴权,现在用作根据redis中的websocket路由权重,生成ws连接的动态路由规则(优先分配给连接数较少的实例)|
38 |
39 |
40 | # 项目开发进度
41 | ## 注意事项
42 |
43 | * 项目在批量推送的功能中,出于性能考虑,用到了redis的管道特性(一次查询出多个客户端所属的websocket节点),遂无法使用cluster分片集群,所以。。要么哨兵一个master处理写操作,要么使用Codis作代理,实现redis集群多个master处理写操作。
44 |
45 | ## 后台实现的功能有
46 |
47 | * 推送功能(根据客户端的连接时的标识参数,以http请求或者websocket消息的形式,给一个或者多个客户端推送指定内容的websocket消息)
48 | * 踢出过期客户端功能(客户端上报心跳,ping消息或者封装心跳格式的websocket消息,会刷新所在的websocket节点所维护的客户端的活跃时间)
49 |
50 | ## 前台项目整体的规划有
51 |
52 | * 提供管理后台页面
53 |
54 | ## 后台规划
55 |
56 | * 接入监控组件对应用所在服务器的cpu、网卡、内存等健康指标的监控
57 | * 结合客户端收到消息的回执和在推送流程中的埋点,接入ELK组件实现对消息送达率,客户端会话时长的统计
58 |
59 | # 效果图
60 | ## 以http形式触发的推送
61 | 
62 | ## 以websocket形式触发的推送
63 | 
64 |
65 | # 消息解析
66 | ### http
67 | {
68 | "to": ["1607080309668","1607071389121"],
69 | "msg": {
70 | "key1": "value",
71 | "key2": "value2"
72 | },
73 | "sendToAll": false
74 | }
75 | ### websocket
76 | {"activeTime":1607080644685,"from":"system","messageId":"725cf41a5798474fb31a1258bed2d5d8","msg":{"key1":"value","key2":"value2"},"requestId":"f231012a-b2ed-40b1-841a-45538dc48ee1","sessionId":"172.31.236.11:9000_1607080309668_20201204191140","to":"1607080309668","trigger":1,"msgType":1}
77 | ## 客户端收到的websocket消息详解
78 | | 值 | 意义 |
79 | | -- | ---- |
80 | | activeTime | 发生时间 |
81 | | from | 消息来源 |
82 | | messageId | 唯一消息id,用于回执重发保证送达率(暂未实现) |
83 | | requestId | 请求id,以http形式触发的时候会存在,用于写es统计 |
84 | | sessionId | 会话id,在一次连接中保持一致,用于写es统计 |
85 | | msg | 具体推送的消息内容 |
86 | | to | 推送的目的地(客户端标识) |
87 | | trigger | 消息触发方式(http或者websocket) |
88 | | msgType | //错误代码ERROR(-1,"error"),//连接类型消息CONNECTION(0,"connection"),//发送的业务类型消息BUSSINESS(1,"bussiness"),//发送的业务类型消息的回执BUSSINESS_ACK(2,"bussiness_ack"),//心跳类型HEARTBEAT(3,"heartbeat"),//心跳类型回执HEARTBEAT_ACK(4,"heartbeat_ack");|
89 |
90 | # 如何贡献
91 |
92 | 项目对你有帮助或者启发的话
93 |
94 | * 老哥点个star
95 | * 提个issue或者pr参与开发
96 | * 甚至打赏瓶水也ok O(∩_∩)O
97 | 
98 |
99 |
100 | # 技术交流及问题解答
101 | * 微信号:
102 | 
103 | 申请表明来意
104 |
105 |
106 |
--------------------------------------------------------------------------------
/README/pic/cloudpush-framework.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/pic/cloudpush-framework.png
--------------------------------------------------------------------------------
/README/pic/cloudpush-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/pic/cloudpush-logo.png
--------------------------------------------------------------------------------
/README/pic/httpSend.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/pic/httpSend.gif
--------------------------------------------------------------------------------
/README/pic/send.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/pic/send.gif
--------------------------------------------------------------------------------
/README/pic/websocketSend.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/pic/websocketSend.gif
--------------------------------------------------------------------------------
/README/pic/wx+.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/pic/wx+.jpg
--------------------------------------------------------------------------------
/README/pic/wx_money.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/pic/wx_money.png
--------------------------------------------------------------------------------
/README/wiki/nacos-import.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/wiki/nacos-import.jpg
--------------------------------------------------------------------------------
/README/wiki/nacos-login.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/wiki/nacos-login.jpg
--------------------------------------------------------------------------------
/README/wiki/nacos-start.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/wiki/nacos-start.jpg
--------------------------------------------------------------------------------
/README/wiki/nacos-update.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangQAQ/cloudpush/c4c7e278b701c47de4476cd94935e6384b872f4a/README/wiki/nacos-update.jpg
--------------------------------------------------------------------------------
/cloudpush-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | cloudpush
7 | com.huangliang
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | cloudpush-api
13 |
14 |
15 |
16 | org.projectlombok
17 | lombok
18 |
19 |
20 | org.springframework.boot
21 | spring-boot-starter-data-redis
22 |
23 |
24 | commons-collections
25 | commons-collections
26 | 3.2.2
27 |
28 |
29 | org.springframework.cloud
30 | spring-cloud-starter-alibaba-nacos-config
31 | 0.2.1.RELEASE
32 |
33 |
34 | org.yaml
35 | snakeyaml
36 | 1.23
37 |
38 |
39 |
40 |
41 | io.springfox
42 | springfox-swagger2
43 | 2.6.1
44 |
45 |
46 | io.springfox
47 | springfox-swagger-ui
48 | 2.6.1
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/annotation/LogOperate.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.annotation;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Inherited
6 | @Documented
7 | @Retention(RetentionPolicy.RUNTIME)
8 | @Target(ElementType.METHOD)
9 | public @interface LogOperate {
10 | String module() default "";
11 | }
12 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/config/RedisComConfig.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.config;
2 |
3 | import org.springframework.data.redis.connection.RedisConnectionFactory;
4 | import org.springframework.data.redis.core.RedisTemplate;
5 | import org.springframework.data.redis.serializer.StringRedisSerializer;
6 |
7 | /**
8 | * redis全局配置
9 | */
10 | public class RedisComConfig {
11 |
12 | public static RedisTemplate getTemplate(RedisConnectionFactory factory){
13 | RedisTemplate template = new RedisTemplate();
14 | template.setConnectionFactory(factory);
15 | // Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
16 | // ObjectMapper mapper = new ObjectMapper();
17 | // mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
18 | // mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
19 | // jackson2JsonRedisSerializer.setObjectMapper(mapper);
20 | StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
21 | // key采用String的序列化方式
22 | template.setKeySerializer(stringRedisSerializer);
23 | // hash的key也采用String的序列化方式
24 | template.setHashKeySerializer(stringRedisSerializer);
25 | // value序列化方式采用jackson
26 | template.setValueSerializer(stringRedisSerializer);
27 | // hash的value序列化方式采用String
28 | template.setHashValueSerializer(stringRedisSerializer);
29 | template.afterPropertiesSet();
30 | return template;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/config/RocketMQConfig.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.config;
2 |
3 | import com.huangliang.api.constants.Constants;
4 |
5 | public class RocketMQConfig {
6 |
7 | /**
8 | * 根据具体的实例名得到对应websocket服务的MQ topic地址
9 | * @param instantceId
10 | * @return
11 | */
12 | public static String getWebsocketTopic(String instantceId){
13 | return Constants.ROCKETMQ_TOPIC_PREFIX + getMqInstance(instantceId);
14 | }
15 |
16 | /**
17 | * 根据具体的实例名得到对应websocket服务的MQ topic地址
18 | * @param instantceId
19 | * @return
20 | */
21 | public static String getWebsocketGroup(String instantceId){
22 | return Constants.ROCKETMQ_GROUP_PREFIX + getMqInstance(instantceId);
23 | }
24 |
25 | /**
26 | * mq群组中不允许出现实例中的":",故替换成"-"
27 | * @param instanceId
28 | * @return
29 | */
30 | private static String getMqInstance(String instanceId){
31 | return instanceId.replace(":","-").replace(".","-");
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/constants/CommonConsts.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.constants;
2 |
3 | public interface CommonConsts
4 | {
5 | String REQUST_SUC = "success";
6 |
7 | String REQUST_FAIL = "failure";
8 |
9 | Boolean TRUE = true;
10 |
11 | Boolean FALSE = false;
12 |
13 | Integer LIMI_TALL = -1;
14 |
15 | Integer LIMI_MAX = 10;
16 |
17 | String SUCCESS = "0";
18 |
19 | String ERROR = "-1";
20 |
21 | String MD5_KEY = "qqq_live";
22 |
23 | Integer AUTO_CHECK_HOSTER = 1;
24 |
25 | // 编码格式
26 | String DECODE = "UTF-8";
27 |
28 | // 高清标识
29 | String HD = "1";
30 |
31 | // 标清标识
32 | String SD = "0";
33 |
34 | String YES = "1";
35 |
36 | String NO = "0";
37 |
38 | // 10进制常量
39 | String TEN = "0";
40 |
41 | // 16进制常量
42 | String SIXTEN = "1";
43 |
44 | // 单位为秒
45 | String SECEND = "1";
46 |
47 | // 单位为豪秒
48 | String MILLISSECEND = "0";
49 |
50 | // 逗号分隔符
51 | String COMMA_FLAG = ",";
52 |
53 | // 句号分隔符
54 | String PERIOD_FLAG = ".";
55 |
56 | // 分号分隔符
57 | String SEMICOLON_FLAG = ";";
58 |
59 | // 分号分隔符
60 | String COLON_FLAG = ":";
61 |
62 | // &分隔符
63 | String AND_SPLIT = "&";
64 |
65 | // /分隔符
66 | String FILE_SEPARATOR = "/";
67 |
68 | // String FILE_SEPARATOR = File.separator;
69 |
70 | // M3U8后缀
71 | String M3U8 = ".m3u8";
72 |
73 | String UPPER_M3U8 = ".M3U8";
74 |
75 | // 等号分隔
76 | String EQUAL_SIGN = "=";
77 |
78 | // 生成9位随机数
79 | Integer NINE_RANDOM = 999999999;
80 |
81 | String STRING_HTTP = "http://";
82 |
83 | String STRING_WS = "ws://";
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/constants/Constants.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.constants;
2 |
3 | public interface Constants {
4 |
5 | String WEBSOCKET_SERVER = "websocket";
6 |
7 | String ROCKETMQ_TOPIC_PREFIX = "websocket";
8 |
9 | String ROCKETMQ_GROUP_PREFIX = "group";
10 |
11 | String UPGRADE = "Upgrade";
12 |
13 | String SYSTEM = "system";
14 |
15 | String POST = "POST";
16 |
17 | String GET = "GET";
18 |
19 | String CHANNELID = "channelId";
20 |
21 | String SESSIONID = "sessionId";
22 |
23 | String Trigger = "Trigger";
24 |
25 | String MSG = "msg";
26 | }
27 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/constants/RedisPrefix.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.constants;
2 |
3 | public interface RedisPrefix {
4 | //websocket的服务地址,类型-hash,key为websocket节点的实例名(ip:port),value为websocket端口
5 | public static String WEBSOCKETSERVER = "websocket_server";
6 |
7 | //websocket实例与对应连接的权重关系
8 | public static String WEBSOCKETWEIGHT = "websocket_weight";
9 |
10 | //客户端连接前缀,后缀为客户端标识-hash
11 | public static String PREFIX_CLIENT = "client_";
12 |
13 | //websocket服务所连接的客户端id集合前缀-list
14 | public static String PREFIX_SERVERCLIENTS = "serverclients_";
15 | }
16 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/entity/Client.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.entity;
2 |
3 | import ch.qos.logback.core.util.DatePatternToRegexUtil;
4 | import com.huangliang.api.util.DateUtils;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 | import org.springframework.format.annotation.DateTimeFormat;
9 |
10 | import java.io.Serializable;
11 | import java.util.Date;
12 |
13 | /**
14 | * 客户端websocket连接对象属性描述
15 | */
16 | @Data
17 | @NoArgsConstructor
18 | public class Client implements Serializable {
19 |
20 | //唯一标识
21 | private String channelId;
22 |
23 | //所连接的主机
24 | private String host;
25 |
26 | //上次活跃时间
27 | private String lastActiveTime;
28 |
29 | //创建时间
30 | private String createTime;
31 |
32 | public Client(String channelId, String host) {
33 | this.channelId = channelId;
34 | this.host = host;
35 | this.lastActiveTime = DateUtils.getCurrentDateTime();
36 | this.createTime = this.lastActiveTime;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/entity/WebsocketMessage.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.entity;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.fasterxml.jackson.annotation.JsonFormat;
5 | import com.fasterxml.jackson.annotation.JsonIgnore;
6 | import com.fasterxml.jackson.annotation.JsonInclude;
7 | import lombok.AllArgsConstructor;
8 | import lombok.Data;
9 | import lombok.NoArgsConstructor;
10 |
11 | import java.io.Serializable;
12 | import java.util.Date;
13 |
14 | @Data
15 | @AllArgsConstructor
16 | @NoArgsConstructor
17 | public class WebsocketMessage implements Serializable {
18 |
19 | //消息所属的请求id
20 | private String requestId;
21 | //会话id,在同一次中保持一致
22 | private String sessionId;
23 | //推送消息的id,推送和回执保持一致
24 | @JsonInclude(value=JsonInclude.Include.NON_NULL)
25 | private String messageId;
26 | //消息的类型
27 | @JsonInclude(value=JsonInclude.Include.NON_NULL)
28 | private Integer msgType;
29 | //目标客户端标识
30 | @JsonInclude(value=JsonInclude.Include.NON_NULL)
31 | private String[] to;
32 | //消息内容
33 | @JsonInclude(value=JsonInclude.Include.NON_NULL)
34 | private JSONObject msg;
35 | //来源
36 | @JsonIgnore
37 | private String from = "system";
38 | //触发类型 1.接口调用触发 2.websocket通信触发
39 | private Integer trigger;
40 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
41 | private Date activeTime = new Date();
42 | //错误信息
43 | private String resultMsg;
44 |
45 | public WebsocketMessage(String messageId, Integer type, String[] to, JSONObject msg, String from, Integer trigger) {
46 | this.messageId = messageId;
47 | this.msgType = type;
48 | this.to = to;
49 | this.msg = msg;
50 | this.from = from;
51 | this.trigger = trigger;
52 | }
53 |
54 | public WebsocketMessage(String requestId, String sessionId, String messageId, Integer type, String[] to, JSONObject msg, String from, Integer trigger) {
55 | this.requestId = requestId;
56 | this.sessionId = sessionId;
57 | this.messageId = messageId;
58 | this.msgType = type;
59 | this.to = to;
60 | this.msg = msg;
61 | this.from = from;
62 | this.trigger = trigger;
63 | }
64 |
65 | //消息类型标识
66 | public enum MsgType {
67 | //错误代码
68 | ERROR(-1,"error"),
69 | //发送的业务类型消息
70 | CONNECTION(0,"connection"),
71 | //发送的业务类型消息
72 | BUSSINESS(1,"bussiness"),
73 | //发送的业务类型消息的回执
74 | BUSSINESS_ACK(2,"bussiness_ack"),
75 | //心跳类型
76 | HEARTBEAT(3,"heartbeat"),
77 | //心跳类型回执
78 | HEARTBEAT_ACK(4,"heartbeat_ack");
79 |
80 | public Integer code;
81 | public String info;
82 |
83 | MsgType(Integer code, String info){
84 | this.code = code;
85 | this.info = info;
86 | }
87 | public Integer getCode() {
88 | return code;
89 | }
90 | public String getInfo() {
91 | return info;
92 | }
93 | }
94 |
95 | public enum Trigger {
96 | //触发推送的方式
97 | //1.接口请求的方式
98 | HTTP(1,"HTTP"),
99 | //2.websocket消息触发
100 | WEBSOCKET(2,"WEBSOCKET");
101 |
102 | public Integer code;
103 | public String info;
104 |
105 | Trigger(Integer code, String info) {
106 | this.code = code;
107 | this.info = info;
108 | }
109 | public Integer getCode() {
110 | return code;
111 | }
112 | public String getInfo() {
113 | return info;
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/entity/request/AbstractRequest.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.entity.request;
2 |
3 | import io.swagger.annotations.ApiModelProperty;
4 | import lombok.Data;
5 |
6 | import java.util.UUID;
7 |
8 | @Data
9 | public abstract class AbstractRequest {
10 | @ApiModelProperty(value="请求的唯一id" ,required=false)
11 | private String requestId = UUID.randomUUID().toString();
12 | }
13 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/entity/request/SendRequest.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.entity.request;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibaba.fastjson.JSONObject;
5 | import io.swagger.annotations.ApiModel;
6 | import io.swagger.annotations.ApiModelProperty;
7 | import lombok.Data;
8 |
9 | import java.io.Serializable;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | @Data
14 | @ApiModel(value="推送请求对象模型")
15 | public class SendRequest extends AbstractRequest implements Serializable {
16 |
17 | /**
18 | * 发送目的地(客户端)
19 | */
20 | @ApiModelProperty(value="推送目的地的数组" ,required=true)
21 | private List to = new ArrayList<>();
22 | /**
23 | * 发送内容
24 | */
25 | @ApiModelProperty(value="推送消息的内容" ,required=true)
26 | private JSONObject msg;
27 |
28 | /**
29 | * 推送发起者默认系统
30 | */
31 | private String from = "system";
32 |
33 | @ApiModelProperty(value="延时推送(单位秒,暂未实现)" ,required=false)
34 | private Integer delay;
35 |
36 | @ApiModelProperty(value="是否发送给所有人" ,required=false)
37 | private Boolean sendToAll = false;
38 | }
39 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/entity/response/Response.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.entity.response;
2 |
3 | import com.huangliang.api.constants.CommonConsts;
4 |
5 | public class Response
6 | {
7 | /**
8 | * code = 0 时返回true
9 | * code != 0 时返回false
10 | */
11 | private boolean success;
12 |
13 | /**
14 | * 消息状态码
15 | * 0 表示成功
16 | * 其它表示失败
17 | */
18 | private String code;
19 |
20 | private String msg;
21 |
22 | /**
23 | * 返回数据
24 | */
25 | private T data;
26 |
27 | public Response()
28 | {
29 | this.code = CommonConsts.SUCCESS;
30 | this.success = CommonConsts.TRUE;
31 | this.msg = CommonConsts.REQUST_SUC;
32 | }
33 |
34 | public Response(String msg,T data)
35 | {
36 | this.code = CommonConsts.SUCCESS;
37 | this.success = CommonConsts.TRUE;
38 | this.data = data;
39 | this.msg = msg;
40 | }
41 |
42 | public Response(String code, String retInfo)
43 | {
44 | this.code = code;
45 | this.msg = retInfo;
46 | if (CommonConsts.SUCCESS.equals(code))
47 | {
48 | success = true;
49 | }
50 | else
51 | {
52 | success = false;
53 | }
54 | }
55 |
56 | public Response(String code, String msg, T data)
57 | {
58 | this.code = code;
59 | this.msg = msg;
60 | this.data = data;
61 | if (CommonConsts.SUCCESS.equals(code))
62 | {
63 | success = true;
64 | }
65 | else
66 | {
67 | success = false;
68 | }
69 | }
70 |
71 | public String getCode() {
72 | return code;
73 | }
74 |
75 | public void setCode(String code) {
76 | this.code = code;
77 | }
78 |
79 | public String getMsg()
80 | {
81 | return msg;
82 | }
83 |
84 | public void setMsg(String msg)
85 | {
86 | this.msg = msg;
87 | }
88 |
89 | public T getData()
90 | {
91 | return data;
92 | }
93 |
94 | public void setData(T data)
95 | {
96 | this.data = data;
97 | }
98 |
99 | public boolean isSuccess()
100 | {
101 | return success;
102 | }
103 |
104 | public void setSuccess(boolean success)
105 | {
106 | this.success = success;
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/exception/NetException.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.exception;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class NetException extends Exception{
7 |
8 | private Integer code;
9 |
10 | private String message;
11 |
12 | private Exception exception;
13 |
14 | public NetException(String message) {
15 | this.message = message;
16 | }
17 |
18 | public NetException(Integer code, String message) {
19 | super();
20 | this.code = code;
21 | this.message = message;
22 | }
23 | public NetException(Integer code, Exception exception) {
24 | super();
25 | this.code = code;
26 | this.exception = exception;
27 | }
28 | public NetException(String message, Exception exception) {
29 | super();
30 | this.message = message;
31 | this.exception = exception;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/exception/ServiceException.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.exception;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class ServiceException extends RuntimeException{
7 |
8 | private Integer code;
9 |
10 | private String message;
11 |
12 | public ServiceException(String message) {
13 | this.message = message;
14 | }
15 |
16 | public ServiceException(Integer code, String message) {
17 | super();
18 | this.code = code;
19 | this.message = message;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/util/DateUtils.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.util;
2 |
3 | import java.text.ParseException;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Date;
6 |
7 | public class DateUtils {
8 | public static String DATE_FORMAT = "yyyy-MM-dd";
9 |
10 | public static String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
11 | public static String DATE_TIME_FORMAT_II = "yyyyMMddHHmmss";
12 |
13 | public static String DATE_FORMAT_CHINESE = "yyyy年M月d日";
14 |
15 | /**
16 | * 获取当前日期
17 | *
18 | *
19 | * @return
20 | *
21 | */
22 | public static String getCurrentDate() {
23 | String datestr = null;
24 | SimpleDateFormat df = new SimpleDateFormat(DateUtils.DATE_FORMAT);
25 | datestr = df.format(new Date());
26 | return datestr;
27 | }
28 |
29 | /**
30 | * 获取当前日期时间
31 | *
32 | *
33 | * @return
34 | *
35 | */
36 | public static String getCurrentDateTime() {
37 | String datestr = null;
38 | SimpleDateFormat df = new SimpleDateFormat(DateUtils.DATE_TIME_FORMAT);
39 | datestr = df.format(new Date());
40 | return datestr;
41 | }
42 | public static String getCurrentDateTimeFormat() {
43 | String datestr = null;
44 | SimpleDateFormat df = new SimpleDateFormat(DateUtils.DATE_TIME_FORMAT_II);
45 | datestr = df.format(new Date());
46 | return datestr;
47 | }
48 |
49 | /**
50 | * 获取当前日期时间
51 | *
52 | *
53 | * @return
54 | *
55 | */
56 | public static String getCurrentDateTime(String Dateformat) {
57 | String datestr = null;
58 | SimpleDateFormat df = new SimpleDateFormat(Dateformat);
59 | datestr = df.format(new Date());
60 | return datestr;
61 | }
62 |
63 | public static String dateToDateTime(Date date) {
64 | SimpleDateFormat df = new SimpleDateFormat(DateUtils.DATE_TIME_FORMAT);
65 | return df.format(date);
66 | }
67 | /**
68 | * 将字符串日期转换为日期格式
69 | *
70 | *
71 | * @param datestr
72 | * @return
73 | *
74 | */
75 | public static Date stringToDate(String datestr) {
76 |
77 | if(datestr ==null ||datestr.equals("")){
78 | return null;
79 | }
80 | Date date = new Date();
81 | SimpleDateFormat df = new SimpleDateFormat(DateUtils.DATE_FORMAT);
82 | try {
83 | date = df.parse(datestr);
84 | } catch (ParseException e) {
85 | date=DateUtils.stringToDate(datestr,"yyyyMMdd");
86 | }
87 | return date;
88 | }
89 |
90 | /**
91 | * 将字符串日期转换为日期格式
92 | * 自定義格式
93 | *
94 | * @param datestr
95 | * @return
96 | *
97 | */
98 | public static Date stringToDate(String datestr, String dateformat) {
99 | Date date = new Date();
100 | SimpleDateFormat df = new SimpleDateFormat(dateformat);
101 | try {
102 | date = df.parse(datestr);
103 | } catch (ParseException e) {
104 | e.printStackTrace();
105 | }
106 | return date;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/util/ObjUtils.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.util;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 |
5 | import java.lang.reflect.Field;
6 | import java.lang.reflect.Modifier;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | @Slf4j
11 | public class ObjUtils {
12 | public static Map ObjToMap(Object obj){
13 | Map map=new HashMap();
14 | Field[] fields = obj.getClass().getDeclaredFields();
15 | for(Field field:fields){
16 | if(field.getName().equalsIgnoreCase("class")){
17 | continue;
18 | }
19 | field.setAccessible(true);
20 | try {
21 | map.put(field.getName(), field.get(obj));
22 | } catch (IllegalAccessException e) {
23 | log.error("属性获取异常",e);
24 | }
25 | }
26 | return map;
27 | }
28 |
29 | public static Map ObjToByteMap(Object obj){
30 | Map map=new HashMap();
31 | Field[] fields = obj.getClass().getDeclaredFields();
32 | for(Field field:fields){
33 | if(field.getName().equalsIgnoreCase("class")){
34 | continue;
35 | }
36 | field.setAccessible(true);
37 | try {
38 | map.put(field.getName().getBytes(), field.get(obj).toString().getBytes());
39 | } catch (IllegalAccessException e) {
40 | log.error("属性获取异常",e);
41 | }
42 | }
43 | return map;
44 | }
45 | public Object mapToObj(Map map,Class> clz) throws Exception{
46 | Object obj = clz.newInstance();
47 | Field[] declaredFields = obj.getClass().getDeclaredFields();
48 | for(Field field:declaredFields){
49 | int mod = field.getModifiers();
50 | if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){
51 | continue;
52 | }
53 | field.setAccessible(true);
54 | field.set(obj, map.get(field.getName()));
55 | }
56 | return obj;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/cloudpush-api/src/main/java/com/huangliang/api/util/RedisUtils.java:
--------------------------------------------------------------------------------
1 | package com.huangliang.api.util;
2 |
3 | import com.huangliang.api.constants.RedisPrefix;
4 | import org.springframework.dao.DataAccessException;
5 | import org.springframework.data.redis.connection.RedisConnection;
6 | import org.springframework.data.redis.core.RedisCallback;
7 |
8 | import java.util.List;
9 |
10 | public class RedisUtils {
11 |
12 | public static RedisCallback> getClientHostByClientFromRedis(List requestClients){
13 | return new RedisCallback