赞
踩
什么是消息推送?
正常情况下一个前后端的交互过程是这样的:
大部分情况以上流程就可以解决,但有一些特殊情况下例外,比如服务器无法马上处理一个请求或服务器通过定时任务自动处理任务,并且需要在以后某个时间处理完成后通知用户处理结果(比如转账、抽奖、核对订单等)。这种时候需要服务端主动向用户推送一些内容,一般有两种做法:
一般来讲,第二种会更加优雅一些,并且减少了大量的请求消耗。市场上也有很多的消息推送技术,比如websocket、socketio、netty等。本文介绍下Java springboot下的netty-socketio的实现方式。
一、后端
1.添加maven依赖
com.corundumstudio.socketio netty-socketio 1.7.18
2.添加config配置类
主要是配置下后端socket的地址和端口。
import com.corundumstudio.socketio.SocketIOServer;import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration@Slf4jpublic class SocketServerConfig { private static int port; @Value("${socket.port}") public void setPort(int port) { SocketServerConfig.port = port; } @Bean public SocketIOServer socketIOServer() { log.info("SocketIoServer启动 端口"+port); com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();// config.setHostname("localhost"); config.setPort(port); SocketIOServer server = new SocketIOServer(config); return server; } /** * 用于扫描netty-socketio的注解,比如 @OnConnect、@OnEvent */ @Bean public SpringAnnotationScanner springAnnotationScanner() { return new SpringAnnotationScanner(socketIOServer()); }}
3.添加消息处理类
主要是监听客户端的连接、断开、客户端发送消息等事件。在连接时存储下活动客户端以备后面推送时使用,在断开时在活动库中删除客户端。
import cn.hutool.json.JSONObject;import com.corundumstudio.socketio.AckRequest;import com.corundumstudio.socketio.SocketIOClient;import com.corundumstudio.socketio.SocketIOServer;import com.corundumstudio.socketio.annotation.OnConnect;import com.corundumstudio.socketio.annotation.OnDisconnect;import com.corundumstudio.socketio.annotation.OnEvent;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;/*** @Description websocket处理器**/@Component@Slf4jpublic class MessageEventHandler { @Autowired private SocketIOServer server; public static ConcurrentMap clientMap = new ConcurrentHashMap<>(); /** * 客户端连接的时候触发 * * @param client */ @OnConnect public void onConnect(SocketIOClient client) { //存储SocketIOClient,用于发送消息 clientMap.put(client.getSessionId().toString(), client); //回发消息 client.sendEvent("message", "welcome"); log.info("客户端["+client.getRemoteAddress()+"]连接成功"); } /** * 客户端关闭连接时触发 * * @param client */ @OnDisconnect public void onDisconnect(SocketIOClient client) { log.info("客户端["+client.getRemoteAddress()+"]断开连接"); clientMap.remove(client.getSessionId()); } /** * 客户端发来的事件 * * @param client 客户端信息 * @param request 请求信息 * @param data 客户端发送数据 */ @OnEvent(value = "xxxEvent") public void onEvent(SocketIOClient client, AckRequest request, JSONObject data) { log.info("客户端发来xxxEvent消息:" + data); }}
4.服务端给客户端发消息工具类
封装了一个服务端发送给前端的方法,注意事件是可以自定义的,但需要前端用相同事件名称接收。
import cn.hutool.json.JSONObject;import com.corundumstudio.socketio.SocketIOClient;/*** @Description websocket工具类**/public class SocketUtils { /** * 不同的业务自己与前端协定好,然后增加事件类型 */ public enum EventEnum { message,//普通消息 xxx,//自定义消息 } /** * 发送广播消息 * @param event * @param obj */ public static void sendMsg(EventEnum event, JSONObject obj){ for (SocketIOClient client : MessageEventHandler.clientMap.values()) { if (client.isChannelOpen()) { client.sendEvent(event.name(), obj); } } }}
二、前端(VUE)
1.添加封装的websocket.js(github上的一个开源项目icebob/vue-websocket)
提供一个在vue环境封装的全局调用,引入了socket.io-client依赖,将业务的vue文件中定义的监听方法统一管理。
import IO from "socket.io-client";//文档地址 https://github.com/icebob/vue-websocketexport default { install(Vue, connection, opts) { let socket; if (connection != null && typeof connection === "object") socket = connection; else socket = IO(connection || "", opts); Vue.prototype.$socket = socket; let addListeners = function() { // console.log("addListeners xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); if (this.$options["socket"]) { let conf = this.$options.socket; if (conf.namespace) { this.$socket = IO(conf.namespace, conf.options); } // console.log('conf.events ', conf.events); if (conf.events) { let prefix = conf.prefix || ""; Object.keys(conf.events).forEach((key) => { let func = conf.events[key].bind(this); this.$socket.on(prefix + key, func); conf.events[key].__binded = func; }); } } }; let removeListeners = function() { if (this.$options["socket"]) { let conf = this.$options.socket; if (conf.namespace) { this.$socket.disconnect(); } if (conf.events) { let prefix = conf.prefix || ""; Object.keys(conf.events).forEach((key) => { this.$socket.off(prefix + key, conf.events[key].__binded); }); } } }; Vue.mixin({ [Vue.version.indexOf("2") === 0 ? "beforeCreate" : "beforeCompile"]: addListeners, beforeDestroy: removeListeners }); }};
2.在main.js中增加全局配置
引入封装的js,设置后端socket服务的地址和端口。
import VueWebsocket from "./utils/websocket.js";Vue.use(VueWebsocket, process.'http://localhost:8889');
3.在使用的业务模块vue文件中添加监听方法(跟created、mounted、methods同级)
socket: { events: { event1(msg) { console.log('接收到服务端event1事件信息', msg); }, event2(msg) { console.log('接收到服务端event2事件信息', msg); } } }
注意这里的event1和event2 是可以自定义的,但需要后端与前端保持一致,只需要在方法里处理推送过来的消息即可。
总结一下,主要分为两部分:
1.后端配置服务的地址端口和推送的业务事件
2.前端配置需要连接的服务端地址端口和监听需要接收的业务事件
希望本文对你有所帮助,关注我,不定时更新实用技术干货。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。