项目要求前端要做到自动刷新功能,low 的办法就是由前端做轮询。但是随着后面产品要求的实时通知越来越多,轮询已经渐渐不满足需求了,只能选择使用 socket 长链接了。
但是百度,Google 之后,发现大多数都是使用的 SocketJS 来实现的 socket,而前端使用的 React Native 只能使用 websocket 实现。没有办法,只能自己查阅资料实现了。
引入 Maven 依赖
1 | <dependency> |
websocket 配置
- 项目中有使用 Spring Security 作为登录权限验证,所以要先将 security 对 socket 的路径忽略掉,自己去做权限
1 | @Override |
- 以
socket作为访问路径,允许所有远端访问
1 | @Configuration |
开始实现 websocket 的接入
websocket 的握手
项目中使用了 security 做了登录用户的管理。所以限制只有登录的用户才能和服务端进行 websocket 链接,因此之前放在 http 请求头中的 token 信息要带到 websocket 中来,但是 websocket 中没有请求头参数的设置,因此只能将 token 信息放在链接地址中,类似与 get 请求。如ws://localhost:8080/socket?token={token}这样。所以,要在 websocket 和服务器建立连接的握手中去校验 token 的合法性。
1 | package com.gugu.boy.spring.websocket.interceptor; |
服务端针对前端通过 websocket 长链接发送的消息处理
1 | package com.gugu.boy.spring.websocket.handler; |
因为项目中需要有业务需要对固定的用户发送消息的需求,所以我针对 WebSocketSession 进行了集中管理。
首先创建WebSocketContainer组件,将所有的 WebSocketSession 集中存储到ConcurrentHashMap中,以 userId 作为 Map 的 key,WebSocketSession 作为 value,这样就可以通过 Map 的特性可以快速找到对应 userId 的 WebSocketSession,从而将消息发送出去。
当有客户端连接上来或者断开链接,都要增加 session 或者移除对应的 session。
同时,启动一个只有 1 个线程的线程池,定时对 map 进行监控,可以定期展示当前链接人员。
1 | package com.gugu.boy.spring.websocket; |
最后再增加一个工具类WebSocketTemplate进行统一发送封装,目前功能比较简单,都是同步发送,后面可以追加线程池管理进行异步发送。
1 | package com.gugu.boy.spring.websocket; |