开原网站制作公司,深圳企业画册印刷,永春网站设计,我国网络营销现状分析生命无罪#xff0c;健康万岁#xff0c;我是laity。
我曾七次鄙视自己的灵魂#xff1a;
第一次#xff0c;当它本可进取时#xff0c;却故作谦卑#xff1b;
第二次#xff0c;当它在空虚时#xff0c;用爱欲来填充#xff1b;
第三次#xff0c;在困难和容易之…生命无罪健康万岁我是laity。
我曾七次鄙视自己的灵魂
第一次当它本可进取时却故作谦卑
第二次当它在空虚时用爱欲来填充
第三次在困难和容易之间它选择了容易
第四次它犯了错却借由别人也会犯错来宽慰自己
第五次它自由软弱却把它认为是生命的坚韧
第六次当它鄙夷一张丑恶的嘴脸时却不知那正是自己面具中的一副
第七次它侧身于生活的污泥中虽不甘心却又畏首畏尾。 Spring定时任务 webSocket实现定时给指定用户发送消息类似于消息中心 相信有需求的小伙伴读此文章可以有一定的帮助或者思路 逻辑思路
在做这个业务的时候也遇到了很多的坑但是现在我帮你踩完了。
使用Spring定时任务框架(Scheduled)和websocket网络通信协议框架来进行定时向指定用户的客户端推送消息编写并设置webSocketConfig配置文件(百度上的常规配置即可)基于ServerEndpoint(value “/websocket/{userId}”)注解标记路径并让前端进行连接服务器端SocketServer中都是使用ConcurrentHashMap集合来进行接收和存储 它是Java 5中引入了ConcurrentHashMap这个并发集合类ConcurrentHashMap通过将一个大的数据结构分解为多个小的数据结构然后对每个小的数据结构加锁来实现线程安全。这种方式称为分段锁。分段锁我记得是有问题的性能不好(什么原因导致的有时间去查:查询热点数据)在JDK8时弃用后采用CAS和synchronized组合的方式来保证并发安全。Java 8中的ConcurrentHashMap在内部实现上采用了数组链表红黑树的数据结构也使得其具有较好的查找和遍历性能有兴趣的小伙伴可以去阅读下一文看懂 jdk8 中的 ConcurrentHashMap个人感觉写的很不错。 集合clients用于记录连接的客户端(session)、key为生成的uuid集合conn用于存放clients数据key为用户唯一标识(id)。同时也充分使用了websocket的生命周期进行资源释放等操作。自定义方法sendMessageByUserId(Integer userId, String message)基于userId获取conn中的set集合基于iterator进行集合迭代使用hasNext()返回的Boolean进行判断是否继续进行循环遍历.next()获取其中uuid数据(指针移动)通过uuid取出存在clients集合中的session进行发送数据以上是WebSocket实现思路基于EnableScheduling注解开启定时任务基于Scheduled(cron “……”)注解和cron表达式进行任务执行。业务逻辑不用的人业务不同所以这里我省略掉了我的业务逻辑有需求的小伙伴可以私信我进一步沟通调用websocket的sendMessageUserById()方法来进行返回数据
代码实现
依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-websocket/artifactIdversion2.7.0/version
/dependencywebSocketServer.java
/*** Project: JavaLaity* Description: 服务端*/
Slf4j
Component
ServerEndpoint(value /websocket/{userId})
public class WebSocketServer {/*** concurrent包的线程安全Set用来存放每个客户端对应的MyWebSocket对象若要实现服务器端与单一客户端通信的话可以使用Map来存放其中key可以为用户标识*/private static final CopyOnWriteArraySetWebSocketServer webSocketSet new CopyOnWriteArraySetWebSocketServer();// public static ThreadLocalSysUser clientUser new ThreadLocal();/*** 用于记录连接的客户端 (sid,session)*/public static MapString, Session clients new ConcurrentHashMap();/*** 基于userId关联sid(用于解决同一用户id在多个web端连接的问题)*/public static MapInteger, SetString conn new ConcurrentHashMap();/*** 与某个客户端的连接会话需要通过它来给客户端发送数据*/private Session session;private String sid null;// 当WebSocket连接建立时会调用标注有OnOpen的方法OnOpenpublic void onOpen(Session session, PathParam(userId) Integer userId) {System.out.println(WebSocket opened: session.getId());this.session session;this.sid UUID.randomUUID().toString();clients.put(this.sid, session);SetString clientSet conn.get(userId);if (clientSet null) {clientSet new HashSet();conn.put(userId, clientSet);}clientSet.add(this.sid);// 加入set中webSocketSet.add(this);}// 给所有当前连接的用户发送消息public void sendMessageAll(String message) {try {if (webSocketSet.size() ! 0) {for (WebSocketServer item : webSocketSet) {if (item ! null) {// 同步锁解决多线程下发送消息异常关闭// 避免高并发下多处频繁调用sendMessage()方法发送消息而导致的webSocket挂掉我们在sendMessage这个方法里面加入同步锁// 锁住session这样就能保障webSocket有条不絮的向前端推送消息synchronized (item.session) {item.session.getBasicRemote().sendText(message);}}}}} catch (IOException e) {e.printStackTrace();}}/*** 根据用户ID发送给某一个用户*/public void sendMessageByUserId(Integer userId, String message) {if (userId ! null) {SetString clientSet conn.get(userId);if (clientSet ! null) {IteratorString iterator clientSet.iterator();while (iterator.hasNext()) {String sid iterator.next();Session session clients.get(sid);if (session ! null) {try {session.getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();}}}}}}// TODO 当浏览器关闭触发该事件OnClosepublic void OnClose(Session session) {log.info(this.sid 断开连接);clients.remove(this.sid);webSocketSet.remove(this);}// 当收到客户端前端发送的消息时会调用OnMessage方法OnMessagepublic void OnMessage(String message, Session session) {System.out.println(Received message: message);}// TODO 发生错误时调用OnErrorpublic void OnError(Session session, Throwable error) {System.out.println(发生错误);error.printStackTrace();}
}AideScheduleTask.java
/*** Project: JavaLaity* Description: 实现业务逻辑的位置*/
Configuration
public class AideScheduleTask {AutowiredWebSocketServer webSocketServer;AutowiredRedisCache redisCache;// 测试Scheduled(cron 0/20 * * * * ?)private void testTask() throws Exception {// 使用ThreadLocal - 哈哈哈弃用了行不通。// TODO: 你需要的业务逻辑SysUser sysUser new SysUser().setId(1)if (sysUser.getId() ! null) {webSocketServer.sendMessageByUserId(sysUser.getId(), 这个是老大可以发role );}}
}注意问题 在写Corn表达式时需要注意的点 外国对于周几的定义和中国是不一样的中国1-7对应周一到周日外国的1-7对应的是周日到周六国内1-7 MON,TUE,WED,THU,FRI,SAT,SUN国外1-7 SUN,MON,TUE,WED,THU,FRI,SAT √ 个人认为有性能问题以及数据库压力的大问题这里本人就只是简单的分享下设计思路不同业务需求实现策略不同。也有肯定公司不在乎这种性能问题。
愿每个人都能遵循自己的时钟做不后悔的选择。我是Laity正在前行的Laity。