首页
学习
活动
专区
工具
TVP
发布

喵 朕的第一个领地诞生了

本方案是为了解决前后台及时通信设计的,例如后台代码触发一个事件,可以及时的传递给前台,也就是消息的推送功能.关于消息的推送,方案1是使用定时任务,Cron表达式设置每分钟处理一下后台逻辑进行事件的判断.方案2是使用webSocket建立消息通信通道,挂起一个线程进行时间的判断和消息推送.虽然都能实现消息推送的功能,但是方案二明显效率更高,对服务器造成的压力相对于方案1来说也更小,这里就简单介绍下使用第二种方案进行消息的及时推送实现方法.首先说下使用webSocket进行访问是路径格式:ws:// path + "wsMy?jspCode=" + jspCode以WS开头,后面可以追加参数,上文路径是中的wsMy是为了接下来的过滤器过滤,防止恶意访问.首先添加webSocket依赖:```org.springframeworkspring-websocket4.0.1.RELEASE```接下来就是前台页面上的触发访问webSocket:```function getUserId() {$.ajax({type : "post",url : "$/memo/getCurrentUserId",beforeSend: function () {//加载中waitload();},success : function(data) {currentUserId = data;closewait();//动态获取当前系统服务器IP地址//若执行成功的话,则隐藏进度条提示//$为定义的项目名称,此处不展示,因地制宜var path = addrAndPort+"$/"console.log("当前系统的IP及端口号为:"+path);var userId = currentUserId;if(userId==-1){window.location.href="$/memo/testWebSocket";}var jspCode = userId;var websocket;if ('WebSocket' in window) {//alert("webSocket"+"ws://" + path + "wsMy?jspCode=" + jspCode);websocket = new WebSocket("ws://" + path + "wsMy?jspCode=" + jspCode);} else if ('MozWebSocket' in window) {websocket = new MozWebSocket("ws://" + path + "wsMy?jspCode=" + jspCode);} else {websocket = new SockJS("http://" + path + "wsMy/sockjs?jspCode=" + jspCode);}websocket.onopen = function(event) {console.log("WebSocket:已连接");console.log(event);};websocket.onmessage = function(event) {if (event.data =="木有消息啊"){//alert("木有消息啊,测试webSocket是否死亡")}else {alert("您有新的提醒,请进入系统首页进行查看")window.flushParent();var aaa = event.data.split("*");var id = aaa[0];var content = aaa[1];window.layer.alert(content, {skin: 'layui-layer-molv' //样式类名 自定义样式,closeBtn: 1 // 是否显示关闭按钮,anim: 1 //动画类型,btn: ['取消提醒','确定'] //按钮,icon: 6 // icon,yes:function(){$.ajax({type : "post",data :{id :id},url : "$/memo/cancleNotepadByMap",success : function(data) {window.location.reload();//若执行成功的话,则隐藏进度条提示if (data != null && data != 'undefined'&& data == 1) {layer.msg('该提醒取消成功!', );parent.flushParent();layer_close();window.location.reload();}else if (data == -1) {layer.msg('记事本名称已经存在!', );}else if (data == 0) {layer.msg('很抱歉!添加失败!', );}else{layer.msg('系统异常!请与系统管理员联系!', );}}});},btn2:function(){layer.msg('按钮2')}});}};websocket.onerror = function(event) {console.log("WebSocket:发生错误 ");console.log(event);};websocket.onclose = function(event) {console.log("WebSocket:已关闭");console.log(event);}}})}```因为本案例中访问的是当前服务器的路径,所以要对当前服务器的IP以及端口号进行获取,然后赋值给webSocket的访问路径:```/*获取当前用户id*/var addrAndPort = "";function getAddrAndport() {$.ajax({type : "get",url : "$/getAddrAndport",beforeSend: function () {//加载中waitload();},success : function(data) {addrAndPort = data;}})}后台获取IP以及端口号返回到前台:@RequestMapping(value = "/getAddrAndport", method = { RequestMethod.GET,RequestMethod.POST })@ResponseBodypublic Object getAddrAndport(HttpServletRequest request,HttpServletResponse response) {String addrAndPort = "";addrAndPort = request.getLocalAddr()+":"+request.getLocalPort();return addrAndPort;}之前查找资料,获取当前系统的IP以及端口号的方法,杂乱,还是直接从HttpServletRequest 中直接获取最为直接和准确.```当前台页面触发访问的时候,后台服务器接收到访问请求并且进行处理,这时候我把后台关于webSocket的请求分为三个文件:1:HandShake.java```package com.yyx.webSocket;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.http.server.ServletServerHttpRequest;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.server.HandshakeInterceptor;import java.util.*;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;/*** Created by zhangrui on 2018/1/22.*/public class HandShake implements HandshakeInterceptor{@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception {// TODO Auto-generated method stubString jspCode = ((ServletServerHttpRequest) request).getServletRequest().getParameter("jspCode");// 标记用户// String userId = (String) ((ServletServerHttpRequest) request).getServletRequest().getSession().getAttribute("userId");if(jspCode!=null){attributes.put("jspCode", jspCode);}else{return false;}return true;}@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {// TODO Auto-generated method stub}}```2.MyWebSocketConfig.java```package com.yyx.webSocket;import org.springframework.stereotype.Component;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.socket.config.annotation.EnableWebSocket;import org.springframework.web.socket.config.annotation.WebSocketConfigurer;import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;import javax.annotation.Resource;/*** Created by zhangrui on 2018/1/22.*/@Component@EnableWebMvc@EnableWebSocketpublic class MyWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {@ResourceMyWebSocketHandler handler;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// TODO Auto-generated method stubregistry.addHandler(handler, "/wsMy");registry.addHandler(handler, "/wsMy/sockjs").withSockJS();}}```3:MyWebSockethandler.java访问成功后的处理逻辑,本文中是使用线程写的,因人而异.```package com.yyx.webSocket;import com.google.gson.GsonBuilder;import com.yyx.zbhr.controller.MemoController;import com.yyx.zbhr.entity.MemoEntity;import com.yyx.zbhr.entity.NotepadEntity;import com.yyx.zbhr.entity.User;import com.yyx.zbhr.service.MMemoService;import com.yyx.zbhr.service.MPersonInfoService;import com.yyx.zbhr.utils.UserUtil;import org.apache.commons.collections.map.HashedMap;import org.apache.shiro.authz.annotation.RequiresAuthentication;import org.apache.shiro.authz.annotation.RequiresPermissions;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.socket.*;import javax.servlet.http.HttpSession;import java.io.IOException;import java.net.URI;import java.text.SimpleDateFormat;import java.util.*;import java.util.Map.*;/*** Created by zhangrui on 2018/1/22.*/@Componentpublic class MyWebSocketHandler implements WebSocketHandler {public static final Map userSocketSessionMap;private static Logger logger = LoggerFactory.getLogger(MyWebSocketHandler.class);private static Calendar fromCal=Calendar.getInstance();@Autowired(required = false)private MPersonInfoService mPersonInfoService;@Autowired(required = false)private MMemoService mMemoService;static {userSocketSessionMap = new HashMap();}public static WebSocketSession session1 ;@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// TODO Auto-generated method stub//HttpSession httpSession = (HttpSession) session;SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");//调用接口获取ID//Long userId = mMemoService.getCurrentUserId();Map map = new HashedMap();/*//wsMy?jspCode=23*/String uri = session.getUri().toString();String[] split = uri.split("=");int currentUserId = Integer.parseInt(split[1]);// TODO 自动shiro获取id失败map.put("userId",currentUserId);String jspCode = (String) session.getHandshakeAttributes().get("jspCode");if (userSocketSessionMap.get(jspCode) == null) {userSocketSessionMap.put(jspCode, session);}new Thread(new Runnable() {public void run() {while(true){Date date = new Date();String format = simpleDateFormat.format(date);List notepadEntities = mMemoService.selectNotepadForNotice(map);for (int i = 0; i < notepadEntities.size(); i++) {String format1 = simpleDateFormat.format(notepadEntities.get(i).getStarTime());String format2 = simpleDateFormat.format(notepadEntities.get(i).getEndTime());StringBuffer message = new StringBuffer();int flag1 = date.compareTo(notepadEntities.get(i).getEndTime());int flag2 = date.compareTo(notepadEntities.get(i).getStarTime());if (flag1 < 0 && flag2 >= 0) {try {String noteType = "";if (notepadEntities.get(i).getNoteType() == 1){noteType = "工作";}else if (notepadEntities.get(i).getNoteType() == 2){noteType = "生活";}else if (notepadEntities.get(i).getNoteType() == 3){noteType = "家庭";}else if (notepadEntities.get(i).getNoteType() == 4){noteType = "私密";}else {noteType = "其他";}message.append(notepadEntities.get(i).getId()+"*");message.append("日程类型:"+noteType+"");message.append("起始时间:"+format1+"");message.append("终止时间:"+format2+"");message.append("主题:"+notepadEntities.get(i).getTitle()+"");message.append("内容:"+notepadEntities.get(i).getContent()+"");session.sendMessage(new TextMessage(message,true));//session.sendMessage(new TextMessage(new GsonBuilder().create().toJson("\"日程类型\":\"" + noteType + "\";\"日程时间\":\"" + simpleDateFormat.format(notepadEntities.get(i).getStarTime())+"--"+simpleDateFormat.format(notepadEntities.get(i).getEndTime()) + "\";\"标题:\":\"" + notepadEntities.get(i).getTitle() + "\";\"内容:\":\"" + notepadEntities.get(i).getContent() + "\"")));//session.sendMessage(new TextMessage("日程提醒\n日程事件:"+format1+"--"+format2+"\n"+"标题:"+notepadEntities.get(i).getTitle()+"\n"+"内容:"+notepadEntities.get(i).getContent()+"类型:"+noteType+"\n"+""));} catch (IOException e) {e.printStackTrace();}}else if(flag1>0){try {String noteType = "";if (notepadEntities.get(i).getNoteType() == 1){noteType = "工作";}else if (notepadEntities.get(i).getNoteType() == 2){noteType = "生活";}else if (notepadEntities.get(i).getNoteType() == 3){noteType = "家庭";}else if (notepadEntities.get(i).getNoteType() == 4){noteType = "私密";}else {noteType = "其他";}message.append(notepadEntities.get(i).getId()+"*");message.append("日程过期提醒");message.append("日程类型:"+noteType+"");message.append("起始时间:"+format1+"");message.append("终止时间:"+format2+"");message.append("主题:"+notepadEntities.get(i).getTitle()+"");message.append("内容:"+notepadEntities.get(i).getContent()+"");session.sendMessage(new TextMessage(message,true));//session.sendMessage(new TextMessage(new GsonBuilder().create().toJson("\"日程类型\":\"" + noteType + "\";\"日程时间\":\"" + simpleDateFormat.format(notepadEntities.get(i).getStarTime())+"--"+simpleDateFormat.format(notepadEntities.get(i).getEndTime()) + "\";\"标题:\":\"" + notepadEntities.get(i).getTitle() + "\";\"内容:\":\"" + notepadEntities.get(i).getContent() + "\"")));//session.sendMessage(new TextMessage("日程提醒\n日程事件:"+format1+"--"+format2+"\n"+"标题:"+notepadEntities.get(i).getTitle()+"\n"+"内容:"+notepadEntities.get(i).getContent()+"类型:"+noteType+"\n"+""));} catch (IOException e) {e.printStackTrace();}}else {String noteType = "";if (notepadEntities.get(i).getNoteType() == 1){noteType = "工作";}else if (notepadEntities.get(i).getNoteType() == 2){noteType = "生活";}else if (notepadEntities.get(i).getNoteType() == 3){noteType = "家庭";}else if (notepadEntities.get(i).getNoteType() == 4){noteType = "私密";}else {noteType = "其他";}//session.sendMessage(new TextMessage(new GsonBuilder().create().toJson("\"日程类型 \":\" " + noteType + "\";\"日程时间\":\"" + simpleDateFormat.format(notepadEntities.get(i).getStarTime())+"--"+simpleDateFormat.format(notepadEntities.get(i).getEndTime()) + "\";\"标题\":\"" + notepadEntities.get(i).getTitle() + "\";\"内容 \":\"" + notepadEntities.get(i).getContent() + "\"")));//session.sendMessage(new TextMessage(new GsonBuilder().create().toJson("\"number\":\"" + i + "\";\"deptId\":\"" + format + "\";\"事件:\":\"" + "这只是哥哥拿来测试的事件" + i + "\"")));/* message.append("日程类型:"+noteType+"");message.append("起始时间:"+format1+"");message.append("终止时间:"+format2+"");message.append("主题:"+notepadEntities.get(i).getTitle()+"");message.append("内容:"+notepadEntities.get(i).getContent()+"");session.sendMessage(new TextMessage(message,true));*/try {session.sendMessage(new TextMessage("木有消息啊",true));} catch (IOException e) {e.printStackTrace();}}}try {Thread.sleep(1000*60);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}@Overridepublic void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception {// TODO Auto-generated method stub//Message msg=new Gson().fromJson(message.getPayload().toString(),Message.class);//msg.setDate(new Date());// sendMessageToUser(msg.getTo(), new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));session.sendMessage(message);}@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {// TODO Auto-generated method stubif (session.isOpen()) {session.close();}Iterator it = userSocketSessionMap.entrySet().iterator();// 移除Socket会话while (it.hasNext()) {Map.Entry entry = it.next();if (entry.getValue().getId().equals(session.getId())) {userSocketSessionMap.remove(entry.getKey());System.out.println("Socket会话已经移除:用户ID" + entry.getKey());break;}}}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {// TODO Auto-generated method stubSystem.out.println("Websocket:" + session.getId() + "已经关闭");Iterator it = userSocketSessionMap.entrySet().iterator();// 移除Socket会话while (it.hasNext()) {Entry entry = it.next();if (entry.getValue().getId().equals(session.getId())) {userSocketSessionMap.remove(entry.getKey());//TODO 对webSocket资源进行回收System.gc();System.out.println("Socket会话已经移除:用户ID" + entry.getKey());break;}}}@Overridepublic boolean supportsPartialMessages() {return false;}public void broadcast(final TextMessage message) throws IOException {Iterator it = userSocketSessionMap.entrySet().iterator();// 多线程群发while (it.hasNext()) {final Entry entry = it.next();if (entry.getValue().isOpen()) {new Thread(new Runnable() {public void run() {try {if (entry.getValue().isOpen()) {entry.getValue().sendMessage(message);}} catch (IOException e) {e.printStackTrace();}}}).start();}}}public void sendMessageToJsp(final TextMessage message,String type) throws IOException {Iterator it = userSocketSessionMap.entrySet().iterator();// 多线程群发while (it.hasNext()) {final Entry entry = it.next();if (entry.getValue().isOpen() && entry.getKey().contains(type)) {new Thread(new Runnable() {public void run() {try {if (entry.getValue().isOpen()) {entry.getValue().sendMessage(message);}} catch (IOException e) {e.printStackTrace();}}}).start();}}}}```第三个文件是核心,具体的代码是实验代码,有一部分是冗余代码,粘贴出来仅供参考,值得一提的是,在webSocket进行sendMessage的时候,可选的message格式有多种,当时博主想传回类json,但是传到前台发现处理比较麻烦,所以就传回了StringBuffer类型的,至于StringBuffer和StringBuidder之间的选择,看情况决定,这里不多言.再展示下消息传递后的效果图:![这里写图片描述](http://img.blog.csdn.net/20180228095519400?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbTBfMzcxOTA0OTU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)对返回到前台的消息处理使用的layer进行弹出,比较好用,样式多.可以直接从layer官网查询样式进行使用,网址....百度.最后一点,本文中使用的web容器及版本是Tomcat8.0.48,虽然有人说在Tomcat8.0之前,不支持webSocket消息通讯,说的是那时候webSocket协议未制定,但是....博主试了下使用Tomcat7.9可行,所以这里就不再多说废话啦,博主也只是个门外汉,更多更细的webSocket知识点还是留待大家自行发掘吧!最后一点,请不要诟病博主的访问路径:getAddrAndport,虽然不规范,但是....这破习惯一下子难改啊...逐步改正.完.

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180228G0FO1B00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券