web集成mpush开发

转载 https://blog.csdn.net/qq_16758997/article/details/84561537

服务部署方式参照:https://github.com/mywiki/mpush-doc/blob/master/SUMMARY.md 完成
mpush官方详细开发文档:http://mpush.mydoc.io/?t=134820(ps:不太适合编程初学者查看)

redis默认只能本机访问,需要修改配置文件,请参考https://blog.csdn.net/weiyangdong/article/details/79916445进行修改

完整web项目demol连接:https://download.csdn.net/download/qq_16758997/10943141

 

一、新建一个普通的maven web工程或新建一个web工程再转换为maven工程(文章采用后一种方式,jdk1.8,tomcat 9.0)

二、修改pom.xml文件如下:

 
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  2. <modelVersion>4.0.0</modelVersion>

  3. <groupId>webmpush</groupId>

  4. <artifactId>webmpush</artifactId>

  5. <version>0.0.1-SNAPSHOT</version>

  6. <packaging>war</packaging>

  7. <dependencies>

  8. <dependency>

  9. <groupId>com.github.mpusher</groupId>

  10. <artifactId>mpush-client</artifactId>

  11. <version>0.8.0</version>

  12. </dependency>

  13. </dependencies>

  14. <build>

  15. <sourceDirectory>src</sourceDirectory>

  16. <resources>

  17. <resource>

  18. <directory>src</directory>

  19. <excludes>

  20. <exclude>**/*.java</exclude>

  21. </excludes>

  22. </resource>

  23. </resources>

  24. <plugins>

  25. <plugin>

  26. <artifactId>maven-compiler-plugin</artifactId>

  27. <version>3.7.0</version>

  28. <configuration>

  29. <source>1.8</source>

  30. <target>1.8</target>

  31. </configuration>

  32. </plugin>

  33. <plugin>

  34. <artifactId>maven-war-plugin</artifactId>

  35. <version>3.2.1</version>

  36. <configuration>

  37. <warSourceDirectory>WebContent</warSourceDirectory>

  38. </configuration>

  39. </plugin>

  40. </plugins>

  41. </build>

  42. </project>

新加标签部分,导入mpush相关的jar包

<dependencies>
    <dependency>
        <groupId>com.github.mpusher</groupId>
        <artifactId>mpush-client</artifactId>
        <version>0.8.0</version>
    </dependency>
  </dependencies>

在src目录下新建application.conf文件,文件内容如下

 
  1. ##################################################################################################################

  2. #

  3. # NOTICE:

  4. #

  5. # 系统配置文件,所有列出的项是系统所支持全部配置项

  6. # 如果要覆盖某项的值可以添加到mpush.conf中。

  7. #

  8. # 配置文件格式采用HOCON格式。解析库由https://github.com/typesafehub/config提供。

  9. # 具体可参照说明文档,比如含有特殊字符的字符串必须用双引号包起来。

  10. #

  11. ##################################################################################################################

  12.  
  13. mp {

  14. #基础配置

  15. home=${user.dir} //程序工作目录

  16.  
  17. #日志配置

  18. log-level=warn

  19. log-dir=${mp.home}/logs

  20. log-conf-path=${mp.home}/conf/logback.xml

  21.  
  22. #核心配置

  23. core {

  24. max-packet-size=10k //系统允许传输的最大包的大小

  25. compress-threshold=10k //数据包启用压缩的临界值,超过该值后对数据进行压缩

  26. min-heartbeat=3m //最小心跳间隔

  27. max-heartbeat=3m //最大心跳间隔

  28. max-hb-timeout-times=2 //允许的心跳连续超时的最大次数

  29. session-expired-time=1d //用于快速重连的session 过期时间默认1天

  30. epoll-provider=netty //nio:jdk自带,netty:由netty实现

  31. }

  32.  
  33. #安全配置

  34. security {

  35. #rsa 私钥、公钥key长度为1024;可以使用脚本bin/rsa.sh生成, @see com.mpush.tools.crypto.RSAUtils#main

  36. private-key="MIIBNgIBADANBgkqhkiG9w0BAQEFAASCASAwggEcAgEAAoGBAKCE8JYKhsbydMPbiO7BJVq1pbuJWJHFxOR7L8Hv3ZVkSG4eNC8DdwAmDHYu/wadfw0ihKFm2gKDcLHp5yz5UQ8PZ8FyDYvgkrvGV0ak4nc40QDJWws621dm01e/INlGKOIStAAsxOityCLv0zm5Vf3+My/YaBvZcB5mGUsPbx8fAgEAAoGAAy0+WanRqwRHXUzt89OsupPXuNNqBlCEqgTqGAt4Nimq6Ur9u2R1KXKXUotxjp71Ubw6JbuUWvJg+5Rmd9RjT0HOUEQF3rvzEepKtaraPhV5ejEIrB+nJWNfGye4yzLdfEXJBGUQzrG+wNe13izfRNXI4dN/6Q5npzqaqv0E1CkCAQACAQACAQACAQACAQA="

  37. public-key="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCghPCWCobG8nTD24juwSVataW7iViRxcTkey/B792VZEhuHjQvA3cAJgx2Lv8GnX8NIoShZtoCg3Cx6ecs+VEPD2fBcg2L4JK7xldGpOJ3ONEAyVsLOttXZtNXvyDZRijiErQALMTorcgi79M5uVX9/jMv2Ggb2XAeZhlLD28fHwIDAQAB"

  38. aes-key-length=16 //AES key 长度

  39. }

  40.  
  41. #网络配置

  42. net {

  43. local-ip="127.0.0.1" //本地ip, 默认取第一个网卡的本地IP

  44. public-ip="127.0.0.1" //外网ip, 默认取第一个网卡的外网IP

  45.  
  46. connect-server-bind-ip="" //connSrv 绑定的本地ip (默认anyLocalAddress 0.0.0.0 or ::0)

  47. connect-server-register-ip=${mp.net.public-ip} //公网ip, 注册到zk中的ip, 默认是public-ip

  48. connect-server-port=3000 //长链接服务对外端口, 公网端口

  49. connect-server-register-attr { //注册到zk里的额外属性,比如配置权重,可在alloc里排序

  50. weight:1

  51. }

  52.  
  53. gateway-server-bind-ip="" //gatewaySrv 绑定的本地ip (默认anyLocalAddress 0.0.0.0 or ::0)

  54. gateway-server-register-ip=${mp.net.local-ip} //本地ip, 注册到zk中的ip, 默认是local-ip

  55. gateway-server-port=3001 //网关服务端口, 内部端口

  56. gateway-server-net=tcp //网关服务使用的网络类型tcp/udp/sctp/udt

  57.  
  58. gateway-client-port=4000 //UDP 客户端端口

  59. gateway-server-multicast="239.239.239.88" //239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效

  60. gateway-client-multicast="239.239.239.99" //239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效

  61. gateway-client-num=1 //网关客户端连接数

  62.  
  63. admin-server-port=3002 //控制台服务端口, 内部端口

  64. ws-server-port=0 //websocket对外端口, 公网端口, 0表示禁用websocket

  65. ws-path="/" //websocket path

  66.  
  67. public-host-mapping { //本机局域网IP和公网IP的映射关系, 该配置后续会被废弃

  68. //"10.0.10.156":"111.1.32.137"

  69. //"10.0.10.166":"111.1.33.138"

  70. }

  71.  
  72. snd_buf { //tcp/udp 发送缓冲区大小

  73. connect-server=32k

  74. gateway-server=0

  75. gateway-client=0 //0表示使用操作系统默认值

  76. }

  77.  
  78. rcv_buf { //tcp/udp 接收缓冲区大小

  79. connect-server=32k

  80. gateway-server=0

  81. gateway-client=0 //0表示使用操作系统默认值

  82. }

  83.  
  84. write-buffer-water-mark { //netty 写保护

  85. connect-server-low=32k

  86. connect-server-high=64k

  87. gateway-server-low=10m

  88. gateway-server-high=20m

  89. }

  90.  
  91. traffic-shaping { //流量整形配置

  92. gateway-client {

  93. enabled:false

  94. check-interval:100ms

  95. write-global-limit:30k

  96. read-global-limit:0

  97. write-channel-limit:3k

  98. read-channel-limit:0

  99. }

  100.  
  101. gateway-server {

  102. enabled:false

  103. check-interval:100ms

  104. write-global-limit:0

  105. read-global-limit:30k

  106. write-channel-limit:0

  107. read-channel-limit:3k

  108. }

  109.  
  110. connect-server {

  111. enabled:false

  112. check-interval:100ms

  113. write-global-limit:0

  114. read-global-limit:100k

  115. write-channel-limit:3k

  116. read-channel-limit:3k

  117. }

  118. }

  119. }

  120.  
  121. #Zookeeper配置

  122. zk {

  123. server-address="127.0.0.1:2181" //多台机器使用","分隔如:"10.0.10.44:2181,10.0.10.49:2181" @see org.apache.zookeeper.ZooKeeper#ZooKeeper()

  124. namespace=mpush

  125. digest=mpush //zkCli.sh acl 命令 addauth digest mpush

  126. watch-path=/

  127. retry {

  128. #initial amount of time to wait between retries

  129. baseSleepTimeMs=3s

  130. #max number of times to retry

  131. maxRetries=3

  132. #max time in ms to sleep on each retry

  133. maxSleepMs=5s

  134. }

  135. connectionTimeoutMs=5s

  136. sessionTimeoutMs=5s

  137. }

  138.  
  139. #Redis集群配置

  140. redis {

  141. cluster-model=single //single,cluster,sentinel

  142. sentinel-master:"",

  143. nodes:["127.0.0.1:6379"] //["127.0.0.1:6379"]格式ip:port

  144. password="" //your password

  145. config {

  146. maxTotal:8,

  147. maxIdle:4,

  148. minIdle:1,

  149. lifo:true,

  150. fairness:false,

  151. maxWaitMillis:5000,

  152. minEvictableIdleTimeMillis:300000,

  153. softMinEvictableIdleTimeMillis:1800000,

  154. numTestsPerEvictionRun:3,

  155. testOnCreate:false,

  156. testOnBorrow:false,

  157. testOnReturn:false,

  158. testWhileIdle:false,

  159. timeBetweenEvictionRunsMillis:60000,

  160. blockWhenExhausted:true,

  161. jmxEnabled:false,

  162. jmxNamePrefix:pool,

  163. jmxNameBase:pool

  164. }

  165. }

  166.  
  167. #HTTP代理配置

  168. http {

  169. proxy-enabled=true//启用Http代理

  170. max-conn-per-host=5 //每个域名的最大链接数, 建议web服务nginx超时时间设长一点, 以便保持长链接

  171. default-read-timeout=10s //请求超时时间

  172. max-content-length=5m //response body 最大大小

  173. dns-mapping { //域名映射外网地址转内部IP, 域名部分不包含端口号

  174. //"mpush.com":["127.0.0.1:8080", "127.0.0.1:8081"]

  175. }

  176. }

  177.  
  178. #线程池配置

  179. thread {

  180. pool {

  181. conn-work:0 //接入服务线程池大小,0表示线程数根据cpu核数动态调整(2*cpu)

  182. gateway-server-work:0 //网关服务线程池大小,0表示线程数根据cpu核数动态调整(2*cpu)

  183. http-work:0 //http proxy netty client work pool size,0表示线程数根据cpu核数动态调整(2*cpu)

  184. ack-timer:1 //处理ACK消息超时

  185. push-task:0 //消息推送中心,推送任务线程池大小, 如果为0表示使用Gateway Server的work线程池,tcp下推荐0

  186. gateway-client-work:0 //网关客户端线程池大小,0表示线程数根据cpu核数动态调整(2*cpu),该线程池在客户端运行

  187. push-client:2 //消息推送回调处理,该线程池在客户端运行

  188.  
  189. event-bus { //用户处理内部事件分发

  190. min:1

  191. max:16

  192. queue-size:10000 //大量的online,offline

  193. }

  194.  
  195. mq { //用户上下线消息, 踢人等

  196. min:1

  197. max:4

  198. queue-size:10000

  199. }

  200. }

  201. }

  202.  
  203. #推送消息流控

  204. push {

  205. flow-control { //qps = limit/(duration)

  206. global:{ //针对非广播推送的流控,全局有效

  207. limit:5000 //qps = 5000

  208. max:0 //UN limit

  209. duration:1s //1s

  210. }

  211.  
  212. broadcast:{ //针对广播消息的流控,单次任务有效

  213. limit:3000 //qps = 3000

  214. max:100000 //10w

  215. duration:1s //1s

  216. }

  217. }

  218. }

  219.  
  220. #系统监控配置

  221. monitor {

  222. dump-dir=${mp.home}/tmp

  223. dump-stack=false //是否定时dump堆栈

  224. dump-period=1m //多久监控一次

  225. print-log=true //是否打印监控日志

  226. profile-enabled=false //开启性能监控

  227. profile-slowly-duration=10ms //耗时超过10ms打印日志

  228. }

  229.  
  230. #SPI扩展配置

  231. spi {

  232. thread-pool-factory:"com.mpush.tools.thread.pool.DefaultThreadPoolFactory"

  233. dns-mapping-manager:"com.mpush.common.net.HttpProxyDnsMappingManager"

  234. }

  235. }

三、新建一个普通的类,需要实现两个接口 PushSender(mpush启动是需要使用), ServletContextListener(web项目的监听器需要继承的类)

 
  1. import java.util.concurrent.CompletableFuture;

  2. import java.util.concurrent.FutureTask;

  3.  
  4. import javax.servlet.ServletContextEvent;

  5. import javax.servlet.ServletContextListener;

  6.  
  7. import com.mpush.api.push.PushContext;

  8. import com.mpush.api.push.PushResult;

  9. import com.mpush.api.push.PushSender;

  10. import com.mpush.api.service.Listener;

  11.  
  12. public class ServiceManager implements PushSender, ServletContextListener {

  13. public static PushSender pushSender = null;

  14.  
  15. // 在tomcat启动是启动消息发送服务

  16. // 启动一个定时器

  17. @Override

  18. public void contextInitialized(ServletContextEvent arg0) {

  19. // PushClient PushClient=new PushClient();

  20. if (pushSender == null)

  21. pushSender = PushSender.create();

  22. pushSender.start().join();

  23. }

  24.  
  25. public static PushSender getPushSender() {

  26. if (pushSender == null)

  27. pushSender = PushSender.create();

  28. return pushSender;

  29. }

  30.  
  31. @Override

  32. public void contextDestroyed(ServletContextEvent arg0) {

  33. pushSender.stop();

  34. }

  35.  
  36. @Override

  37. public void start(Listener listener) {

  38. // TODO Auto-generated method stub

  39.  
  40. }

  41.  
  42. @Override

  43. public void stop(Listener listener) {

  44. // TODO Auto-generated method stub

  45.  
  46. }

  47.  
  48. @Override

  49. public CompletableFuture<Boolean> start() {

  50. // TODO Auto-generated method stub

  51. return null;

  52. }

  53.  
  54. @Override

  55. public CompletableFuture<Boolean> stop() {

  56. // TODO Auto-generated method stub

  57. return null;

  58. }

  59.  
  60. @Override

  61. public boolean syncStart() {

  62. // TODO Auto-generated method stub

  63. return false;

  64. }

  65.  
  66. @Override

  67. public boolean syncStop() {

  68. // TODO Auto-generated method stub

  69. return false;

  70. }

  71.  
  72. @Override

  73. public void init() {

  74. // TODO Auto-generated method stub

  75.  
  76. }

  77.  
  78. @Override

  79. public boolean isRunning() {

  80. // TODO Auto-generated method stub

  81. return false;

  82. }

  83.  
  84. @Override

  85. public FutureTask<PushResult> send(PushContext context) {

  86. // TODO Auto-generated method stub

  87. return null;

  88. }

  89. }

在tomcat启动是就启动mpush消息发送服务,采用单例模式,增加静态公用get方法获取详细推送服务实例,共其它类调用

// 在tomcat启动是启动消息发送服务
    // 启动一个定时器
    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        // PushClient PushClient=new PushClient();
        if (pushSender == null)
            pushSender = PushSender.create();
        pushSender.start().join();
    }

    public static PushSender getPushSender() {
        if (pushSender == null)
            pushSender = PushSender.create();
        return pushSender;
    }

四、增加web项目监听器配置,修改web.xml文件

 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">

  3. <display-name>webpush</display-name>

  4. <welcome-file-list>

  5. <welcome-file>index.html</welcome-file>

  6. <welcome-file>index.htm</welcome-file>

  7. <welcome-file>index.jsp</welcome-file>

  8. <welcome-file>default.html</welcome-file>

  9. <welcome-file>default.htm</welcome-file>

  10. <welcome-file>default.jsp</welcome-file>

  11. </welcome-file-list>

  12. <listener>

  13. <!-- 这里的类路径是第三步新建的监听器类路径 -->

  14. <listener-class>com.webpush.service.ServiceManager</listener-class>

  15. </listener>

  16. </web-app>

新增自定义web项目的监听器(这里的类路径是第三步新建的监听器类路径)

<listener>
    <listener-class>com.webpush.service.ServiceManager</listener-class>
  </listener>

到这里就已经完成mpush继承环境的开发搭建了,以下的内容是增加测试的servlet

五、新建两个servlet类并添加web.xml文件配置:

第一个servlet类:

 
  1. package com.webpush.pushmessage;

  2.  
  3. import java.io.IOException;

  4. import java.util.ArrayList;

  5. import java.util.List;

  6. import java.util.concurrent.TimeUnit;

  7. import java.util.concurrent.locks.LockSupport;

  8.  
  9. import javax.servlet.ServletException;

  10. import javax.servlet.annotation.WebServlet;

  11. import javax.servlet.http.HttpServlet;

  12. import javax.servlet.http.HttpServletRequest;

  13. import javax.servlet.http.HttpServletResponse;

  14.  
  15. import com.mpush.api.push.AckModel;

  16. import com.mpush.api.push.MsgType;

  17. import com.mpush.api.push.PushCallback;

  18. import com.mpush.api.push.PushContext;

  19. import com.mpush.api.push.PushMsg;

  20. import com.mpush.api.push.PushResult;

  21. import com.mpush.api.push.PushSender;

  22. import com.webpush.service.ServiceManager;//刚才第三步新建servlet监听器的类(修改为自己的类路径)

  23.  
  24. /**

  25. * Servlet implementation class Htmlpushmesg

  26. */

  27. @WebServlet("/htmlpushmesg")

  28. public class Htmlpushmesg extends HttpServlet {

  29. private static final long serialVersionUID = 1L;

  30. PushResult pushResult=null;//消息推送结果

  31.  
  32. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  33. request.setCharacterEncoding("UTF-8");

  34. int msgType=Integer.valueOf(request.getParameter("msgtype"));

  35. String pushMsg=request.getParameter("pushMsg");

  36. String broadcast=request.getParameter("broadcast");

  37. String[] userIds=request.getParameterValues("userId");

  38.  
  39. List<String> users=new ArrayList<String>();

  40. if(userIds!=null)

  41. for(int i=0,len=userIds.length;i<len;i++) users.add(userIds[i]);

  42.  
  43. PushSender sender = ServiceManager.getPushSender();//刚才第三步新建servlet监听器的类,过去消息发送服务的实例

  44. PushMsg msg = PushMsg.build(msgType==1?MsgType.NOTIFICATION:msgType==2?MsgType.MESSAGE:MsgType.NOTIFICATION_AND_MESSAGE, pushMsg);

  45. //msg.setMsgId(msgId);

  46. msg.setContent(pushMsg);

  47. PushContext context = PushContext.build(msg)

  48. .setAckModel(AckModel.BIZ_ACK)

  49. .setBroadcast("true".equals(broadcast))//是否进行广播

  50. .setUserIds(users)//多用户推送

  51. .setTimeout(3000)

  52. .setCallback(new PushCallback() {

  53. @Override

  54. public void onResult(PushResult result) {

  55. pushResult=result;

  56. }

  57. }).setTaskId("123456");

  58. /*FutureTask<PushResult> future = */sender.send(context);

  59. LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(10));

  60.  
  61. request.setAttribute("pushResult", pushResult);

  62. request.getRequestDispatcher("/push.jsp").forward(request, response);

  63. }

  64. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  65. doGet(request, response);

  66. }

  67. }

第二个servlet类:

在新建第二个servlet类之前先建一个redis的配置文件,文件名:redis.properties,放在src目录下

 
  1. jedis.pool.maxActive=1024

  2. jedis.pool.maxIdle=200

  3. jedis.pool.maxWait=10000

  4. jedis.pool.testOnBorrow=true

  5. jedis.pool.testOnReturn=true

  6. jedis.pool.timeout=10000

  7. # ip地址必须和文件application.conf中的redis的IP地址相同

  8. redisReadURL=127.0.0.1

  9. redisReadPort=6379

  10. # ip地址必须和文件application.conf中的redis的IP地址相同

  11. redisWriteURL=127.0.0.1

  12. redisWritePort=6379

  13. # 你的redis密码,同文件application.conf中的redis中的密码

  14. password=

servlet类

 
  1. package com.webpush.pushmessage;

  2.  
  3. import java.io.File;

  4. import java.io.IOException;

  5. import java.util.ArrayList;

  6. import java.util.HashMap;

  7. import java.util.List;

  8. import java.util.Map;

  9. import java.util.stream.Collectors;

  10.  
  11. import javax.servlet.ServletException;

  12. import javax.servlet.annotation.WebServlet;

  13. import javax.servlet.http.HttpServlet;

  14. import javax.servlet.http.HttpServletRequest;

  15. import javax.servlet.http.HttpServletResponse;

  16.  
  17. import com.mpush.tools.config.CC;

  18. import com.mpush.tools.config.data.RedisNode;

  19. import com.typesafe.config.Config;

  20. import com.typesafe.config.ConfigFactory;

  21.  
  22. import redis.clients.jedis.Jedis;

  23. import redis.clients.jedis.JedisPool;

  24. import redis.clients.jedis.JedisPoolConfig;

  25.  
  26. /**

  27. * 获取所有在线用户的信息

  28. */

  29. @WebServlet("/userStutas")

  30. public class UserStutas extends HttpServlet {

  31. private static final long serialVersionUID = 1L;

  32.  
  33. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  34.  
  35. request.setAttribute("users", getRedisdata());

  36. request.getRequestDispatcher("/userlist.jsp").forward(request, response);

  37. }

  38.  
  39. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  40. doGet(request, response);

  41. }

  42.  
  43.  
  44. /**

  45. * redis操作部分

  46. */

  47. // 连接实例的最大连接数

  48. private static int MAX_ACTIVE = 1024;

  49. // 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。

  50. private static int MAX_IDLE = 200;

  51. // 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException

  52. private static int MAX_WAIT = 10000;

  53. // 连接超时的时间

  54. private static int TIMEOUT = 10000;

  55. // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;

  56. private static boolean TEST_ON_BORROW = true;

  57. private static JedisPool jedisPool = null;

  58.  
  59. //加载配置文件

  60. static void load() {

  61. Config config = ConfigFactory.load();//扫描加载所有可用的配置文件

  62. String custom_conf = "mp.conf";//加载自定义配置, 值来自jvm启动参数指定-Dmp.conf

  63. if (config.hasPath(custom_conf)) {

  64. File file = new File(config.getString(custom_conf));

  65. if (file.exists()) {

  66. Config custom = ConfigFactory.parseFile(file);

  67. config = custom.withFallback(config);

  68. }

  69. }

  70. Config cfg = CC.cfg.getObject("mp").toConfig().getObject("redis").toConfig();

  71. try {

  72. JedisPoolConfig redisconf = new JedisPoolConfig();

  73. redisconf.setMaxTotal(MAX_ACTIVE);

  74. redisconf.setMaxIdle(MAX_IDLE);

  75. redisconf.setMaxWaitMillis(MAX_WAIT);

  76. redisconf.setTestOnBorrow(TEST_ON_BORROW);

  77. RedisNode redisnode=cfg.getList("nodes")

  78. .stream()//第一纬度数组

  79. .map(v -> RedisNode.from(v.unwrapped().toString()))

  80. .collect(Collectors.toCollection(ArrayList::new)).get(0);

  81. jedisPool = new JedisPool(redisconf, redisnode.getHost(), redisnode.getPort(), TIMEOUT, cfg.getString("password"));

  82. } catch (Exception e) {

  83. e.printStackTrace();

  84. }

  85. }

  86. public Map<String, Map<String,List<String>>> getRedisdata() {

  87. Jedis jedis = getJedis();

  88. Map<String, Map<String,List<String>>> map = new HashMap<String, Map<String,List<String>>>();

  89. for(String key : jedis.keys("mp:ur:*")) {

  90. //System.err.println(key+"\t");

  91. Map<String,List<String>> maplist=new HashMap<String,List<String>>();

  92. for(String ke : jedis.hkeys(key)) {

  93. maplist.put(ke, jedis.hmget(key,ke));

  94. //System.out.print(ke+"\t");

  95. //System.out.println(jedis.hmget(key,ke));

  96. }

  97. map.put(key, maplist);

  98. }

  99. returnResource(jedis);

  100. return map;

  101. }

  102.  
  103.  
  104. /**

  105. * 初始化Redis连接池

  106. */

  107. static {

  108. load();

  109. }

  110.  
  111. /**

  112. * 获取Jedis实例

  113. */

  114. public synchronized static Jedis getJedis() {

  115. try {

  116. if (jedisPool != null) {

  117. Jedis resource = jedisPool.getResource();

  118. return resource;

  119. } else {

  120. return null;

  121. }

  122. } catch (Exception e) {

  123. e.printStackTrace();

  124. return null;

  125. }

  126. }

  127.  
  128. /***

  129. *

  130. * 释放资源

  131. */

  132. public static void returnResource(final Jedis jedis) {

  133. if (jedis != null) {

  134. jedisPool.returnResource(jedis);

  135. }

  136. }

  137. }

修改web.xml文件

 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">

  3. <display-name>webpush</display-name>

  4. <welcome-file-list>

  5. <welcome-file>index.html</welcome-file>

  6. <welcome-file>index.htm</welcome-file>

  7. <welcome-file>index.jsp</welcome-file>

  8. <welcome-file>default.html</welcome-file>

  9. <welcome-file>default.htm</welcome-file>

  10. <welcome-file>default.jsp</welcome-file>

  11. </welcome-file-list>

  12. <listener>

  13. <listener-class>com.webpush.service.ServiceManager</listener-class>

  14. </listener>

  15. <servlet>

  16. <servlet-name>htmlpushmesg</servlet-name>

  17. <servlet-class>com.webpush.pushmessage.Htmlpushmesg</servlet-class>

  18. </servlet>

  19. <servlet-mapping>

  20. <servlet-name>htmlpushmesg</servlet-name>

  21. <url-pattern>/htmlpushmesg.do</url-pattern>

  22. </servlet-mapping>

  23. <servlet>

  24. <servlet-name>userStutas</servlet-name>

  25. <servlet-class>com.webpush.pushmessage.UserStutas</servlet-class>

  26. </servlet>

  27. <servlet-mapping>

  28. <servlet-name>userStutas</servlet-name>

  29. <url-pattern>/userStutas.do</url-pattern>

  30. </servlet-mapping>

  31. </web-app>

web Socket客户端,接受消息测试(app请在官网下载,如果Android7.0及以上版本点击绑定退出,请换成Android6.0及以上版本安装测试)

 
  1. <!DOCTYPE html>

  2. <html lang="en">

  3. <head>

  4. <meta charset="UTF-8">

  5. <title>MPush WebSocket Client</title>

  6. </head>

  7. <body>

  8.  
  9. <script type="text/javascript">

  10. (function (window) {

  11. let socket, session = {}, ID_SEQ = 1;

  12. let config = {listener: null, log: console};

  13. let listener = {

  14. onOpened: function (event) {

  15. if (config.listener != null) {

  16. config.listener.onOpened(event);

  17. }

  18. handshake();

  19. },

  20. onClosed: function (event) {

  21. if (config.listener != null) {

  22. config.listener.onClosed(event);

  23. }

  24. session = {};

  25. ID_SEQ = 1;

  26. socket = null;

  27. },

  28. onHandshake: function () {

  29. session.handshakeOk = true;

  30. if (config.listener != null) {

  31. config.listener.onHandshake();

  32. }

  33. if (config.userId) {

  34. bindUser(config.userId, config.tags);

  35. }

  36. },

  37. onBindUser: function (success) {

  38. if (config.listener != null) {

  39. config.listener.onBindUser(success);

  40. }

  41. },

  42. onReceivePush: function (message, messageId) {

  43. if (config.listener != null) {

  44. config.listener.onReceivePush(message, messageId);

  45. }

  46. },

  47. onKickUser: function (userId, deviceId) {

  48. if (config.listener != null) {

  49. config.listener.onKickUser(userId, deviceId);

  50. }

  51. doClose(-1, "kick user");

  52. }

  53. };

  54. const Command = {

  55. HANDSHAKE: 2,

  56. BIND: 5,

  57. UNBIND: 6,

  58. ERROR: 10,

  59. OK: 11,

  60. KICK: 13,

  61. PUSH: 15,

  62. ACK: 23,

  63. UNKNOWN: -1

  64. };

  65. function Packet(cmd, body, sessionId) {

  66. return {

  67. cmd: cmd,

  68. flags: 16,

  69. sessionId: sessionId || ID_SEQ++,

  70. body: body

  71. }

  72. }

  73. function handshake() {

  74. send(Packet(Command.HANDSHAKE, {

  75. deviceId: config.deviceId,

  76. osName: config.osName,

  77. osVersion: config.osVersion,

  78. clientVersion: config.clientVersion

  79. })

  80. );

  81. }

  82. function bindUser(userId, tags) {

  83. if (userId && userId != session.userId) {

  84. session.userId = userId;

  85. session.tags = tags;

  86. send(Packet(Command.BIND, {userId: userId, tags: tags}));

  87. }

  88. }

  89. function ack(sessionId) {

  90. send(Packet(Command.ACK, null, sessionId));

  91. }

  92. function send(message) {

  93. if (!socket) {

  94. return;

  95. }

  96. if (socket.readyState == WebSocket.OPEN) {

  97. socket.send(JSON.stringify(message));

  98. } else {

  99. config.log.error("The socket is not open.");

  100. }

  101. }

  102. function dispatch(packet) {

  103. switch (packet.cmd) {

  104. case Command.HANDSHAKE: {

  105. config.log.debug(">>> handshake ok.");

  106. listener.onHandshake();

  107. break;

  108. }

  109. case Command.OK: {

  110. if (packet.body.cmd == Command.BIND) {

  111. config.log.debug(">>> bind user ok.");

  112. listener.onBindUser(true);

  113. }

  114. break;

  115. }

  116. case Command.ERROR: {

  117. if (packet.body.cmd == Command.BIND) {

  118. config.log.debug(">>> bind user failure.");

  119. listener.onBindUser(false);

  120. }

  121. break;

  122. }

  123. case Command.KICK: {

  124. if (session.userId == packet.body.userId && config.deviceId == packet.body.deviceId) {

  125. config.log.debug(">>> receive kick user.");

  126. listener.onKickUser(packet.body.userId, packet.body.deviceId);

  127. }

  128. break;

  129. }

  130. case Command.PUSH: {

  131. config.log.debug(">>> receive push, content=" + packet.body.content);

  132. let sessionId;

  133. if ((packet.flags & 8) != 0) {

  134. ack(packet.sessionId);

  135. } else {

  136. sessionId = packet.sessionId

  137. }

  138. listener.onReceivePush(packet.body.content, sessionId);

  139. break;

  140. }

  141. }

  142. }

  143. function onReceive(event) {

  144. config.log.debug(">>> receive packet=" + event.data);

  145. dispatch(JSON.parse(event.data))

  146. }

  147. function onOpen(event) {

  148. config.log.info("Web Socket opened!");

  149. listener.onOpened(event);

  150. }

  151. function onClose(event) {

  152. config.log.info("Web Socket closed!");

  153. listener.onClosed(event);

  154. }

  155. function onError(event) {

  156. config.log.info("Web Socket receive, error");

  157. doClose();

  158. }

  159. function doClose(code, reason) {

  160. if (socket) socket.close();

  161. config.log.info("try close web socket client, reason=" + reason);

  162. }

  163. function doConnect(cfg) {

  164. config = copy(cfg);

  165. socket = new WebSocket(config.url);

  166. socket.onmessage = onReceive;

  167. socket.onopen = onOpen;

  168. socket.onclose = onClose;

  169. socket.onerror = onError;

  170. config.log.debug("try connect to web socket server, url=" + config.url);

  171. }

  172. function copy(cfg) {

  173. for (let p in cfg) {

  174. if (cfg.hasOwnProperty(p)) {

  175. config[p] = cfg[p];

  176. }

  177. }

  178. return config;

  179. }

  180. window.mpush = {

  181. connect: doConnect,

  182. close: doClose,

  183. bindUser: bindUser

  184. }

  185. })(window);

  186. function $(id) {

  187. return document.getElementById(id);

  188. }

  189. let log = {

  190. log: function () {

  191. $("responseText").value += (Array.prototype.join.call(arguments, "") + "\r\n");

  192. }

  193. };

  194. log.debug = log.info = log.warn = log.error = log.log;

  195. function connect() {

  196. mpush.connect({

  197. url: $("url").value,

  198. userId: $("userId").value,

  199. deviceId: "test-1001",

  200. osName: "web " + navigator.userAgent,

  201. osVersion: "55.2",

  202. clientVersion: "1.0",

  203. log: log

  204. });

  205. }

  206. function bind() {

  207. mpush.bindUser($("userId").value)

  208. }

  209. </script>

  210. <form οnsubmit="return false;">

  211. <label> 推送服务地址:

  212. <input type="text" id="url" readonly="readonly" value="ws://192.168.10.24:8100/">

  213. </label>

  214. <input type="button" value="连接服务" οnclick="connect()">

  215. <br>

  216. <label> 绑定用户:

  217. <input type="text" id="userId" value="ttkx">

  218. </label>

  219. <input type="button" value="绑定" οnclick="bind()">

  220. <h3><label for="responseText">接收到的消息</label></h3>

  221. <textarea id="responseText" style="width:100%;height:500px;"></textarea>

  222. </form>

  223.  
  224. </body>

  225. </html>

消息推送页面

 
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="com.webpush.redis.RedisUtils,java.util.*"%>

  2. <!DOCTYPE html>

  3. <html>

  4. <head>

  5. <meta charset="UTF-8">

  6. <title>Insert title here</title>

  7. </head>

  8. <body>

  9. <h1>消息推送</h1>

  10. <div>推送结果:<%=request.getAttribute("pushResult")%></div>

  11. <form action="<%=request.getContextPath() %>/htmlpushmesg.do" method="post">

  12. 消息类型:<select name="msgtype">

  13. <option value="1">提醒(会在通知栏显示)</option>

  14. <option value="2">消息(不会在通知栏显示,业务自定义消息)</option>

  15. <option value="3">提醒+消息</option>

  16. </select><br/>

  17. 是否广播:<input type="radio" value="true" name="broadcast">广播

  18. <input type="radio" value="false" name="broadcast" checked="checked">指定用户推送<br/>

  19. 推送用户:<%Map<String, Map<String,List<String>>> users=new RedisUtils().getRedisdata();

  20. for(String user : users.keySet()){%>

  21. <input type="checkbox" name="userId" value="<%=user.split("mp:ur:")[1]%>"><%=user.split("mp:ur:")[1]%>

  22. <%}%>

  23. </select><br/>

  24. 消息内容:<textArea name="pushMsg">魔推mPush魔推mPush是由移石创想(北京)科技有限公司(mRocker)推出的一款针对企业移动应用程序推送市场的一款集消息类推送与数据服务的开发者服务产品。</textArea>

  25. <br/><input type="submit" value="确定" />

  26. </form>

  27. </body>

  28. </html>

客户端所有用户列表

 
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*,com.alibaba.fastjson.JSON"%>

  2. <!DOCTYPE html>

  3. <html>

  4. <head>

  5. <meta charset="UTF-8">

  6. <title>Insert title here</title>

  7. <style type="text/css">

  8. th,td{border:solid 1px #999}

  9. </style>

  10. </head>

  11. <body>

  12. <h1 style='text-align: center'>所有用户列表数据</h1>

  13. <table border="0" cellspacing="0" width="100%">

  14. <tr>

  15. <th style="width: 100px;">用户名</th>

  16. <th style="width: 70px;">版本号</th>

  17. <th style="width: 155px;">主机</th>

  18. <th style="width: 70px;">在线情况</th>

  19. <th>客户端类型</th>

  20. </tr>

  21. <%

  22. Map<String, Map<String, List<String>>> map = (Map<String, Map<String, List<String>>>) request.getAttribute("users");

  23. Set<String> set = map.keySet();

  24. for (String user : set) {

  25. Set<String> se = map.get(user).keySet();

  26. Iterator<String> it = se.iterator();

  27. %>

  28. <tr>

  29. <td rowspan="<%=se.size()%>"><%=user.split("mp:ur:")[1]%></td>

  30. <%

  31. String list = map.get(user).get(it.next()).get(0);

  32. Map<String, Object> m = JSON.parseObject(list, Map.class);

  33. %>

  34. <td><%=m.get("clientVersion")%></td>

  35. <td><%=m.get("hostAndPort")%></td>

  36. <td><%="true".equals(m.get("online").toString()) ? "<text style=\"color:#0f0\">在线</text>" : "<text style=\"color:#999\">离线</text>"%></td>

  37. <td><%=m.get("osName")%></td>

  38. </tr>

  39. <%

  40. while (it.hasNext()) {

  41. %>

  42. <tr>

  43. <%

  44. list = map.get(user).get(it.next()).get(0);

  45. m = JSON.parseObject(list, Map.class);

  46. %>

  47. <td><%=m.get("clientVersion")%></td>

  48. <td><%=m.get("hostAndPort")%></td>

  49. <td><%="true".equals(m.get("online").toString()) ? "<text style=\"color:#0f0\">在线</text>" : "<text style=\"color:#999\">离线</text>"%></td>

  50. <td><%=m.get("osName")%></td>

  51. </tr>

  52. <%

  53. }

  54. }

  55. %>

  56. </table>

  57. </body>

  58. </html>

然后运行web项目,查看效果

ps:欢迎加群:517413713 讨论

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值