- 场景
创建SocketIOServer时利用 isAuthorized 做了身份验证,当验证失败时返回false,虽然服务并没有建立起来,但是前端会不断进行轮询,从而造成一定的资源浪费。
@Bean public SocketIOServer socketIOServer() { Configuration config = new Configuration(); config.setHostname(host); config.setPort(port); config.setAuthorizationListener(new AuthorizationListener() { @Override public boolean isAuthorized(HandshakeData data) { //TODO 该处可以用来进行身份验证 log.info("id【"+data.getSingleUrlParam("id")+"】正在进行连接"); return ("1").equals(data.getSingleUrlParam("id"))? false : true; } }); return new SocketIOServer(config); }
17:20:46.676 INFO 22704 --- [ntLoopGroup-5-6] c.e.socketdemo.SocketdemoApplication : id【1】正在进行连接 17:20:47.940 INFO 22704 --- [ntLoopGroup-5-7] c.e.socketdemo.SocketdemoApplication : id【1】正在进行连接 17:20:49.774 INFO 22704 --- [ntLoopGroup-5-8] c.e.socketdemo.SocketdemoApplication : id【1】正在进行连接 17:20:54.783 INFO 22704 --- [ntLoopGroup-5-9] c.e.socketdemo.SocketdemoApplication : id【1】正在进行连接 17:21:00.098 INFO 22704 --- [tLoopGroup-5-10] c.e.socketdemo.SocketdemoApplication : id【1】正在进行连接 17:21:05.108 INFO 22704 --- [tLoopGroup-5-11] c.e.socketdemo.SocketdemoApplication : id【1】正在进行连接 17:21:10.419 INFO 22704 --- [tLoopGroup-5-12] c.e.socketdemo.SocketdemoApplication : id【1】正在进行连接 17:21:15.429 INFO 22704 --- [tLoopGroup-5-13] c.e.socketdemo.SocketdemoApplication : id【1】正在进行连接
- 解决思路
从问题的表象看,造成这个情况的原因是前端没有关闭socket连接服务,由于socket服务并没有建立,JAVA不存在可操作的对象,因此只能限定前端只进行一次握手尝试,或者被拒绝后关闭socket服务。
- 解决方案
后来在https://www.w3cschool.cn/socket/socket-k49j2eia.html里找到了这样一段代码
socket.on('connect_error', (error) => { // ... });
完善后
socket.on('connect_error', (error) => {
socket.close();
});
或者
socket.on('connect_error', function(error) {
socket.close();
});
实测问题得到解决:
17:43:32.339 INFO 22704 --- [tLoopGroup-5-11] c.e.socketdemo.SocketdemoApplication : id【1】正在进行连接
17:43:47.625 INFO 22704 --- [tLoopGroup-5-13] c.e.socketdemo.SocketdemoApplication : id【2】正在进行连接
可以看到 "1" 并没有下像之前一样每隔几秒就进行一次尝试。
- 拓展
随着握手失败的测试增加,socket每次心跳的时间也会延长,但即使是延长 依然是在10秒内进行频繁请求。大部分项目的身份权限验证都是要连接数据库进行的,所以除了从前端进行努力, 也可以使用中间件进行非法用户的记录,然后直接拒绝。
- 后续更正
上文提到反复链接是心跳原因,后来观察很可能是因为socket.io的握手机制导致的。socket.io在进行握手的时候默认采用的是polling轮询机制进行的,当失败时会持续发送握手请求。
当然,也有一种新的解决方案,在socket.io官网中有这样的代码:
By default, a long-polling connection is established first, then upgraded to “better” transports (like WebSocket). If you like to live dangerously, this part can be skipped:
const socket = io({ |
const socket = io({ transports: ['websocket'] });
这一段代码是申明使用websocket进行首次创建,建议如果想要放弃无法使用socket服务的用户,就使用这种方式。下方的代码是在js中,创建时声明连接方式的代码:
var socket = io.connect('http://localhost:9010?clientid='+clientid,{transports:['websocket']});
而下方的重连声明又将polling设为了首个创建方式,如果再次遇到polling创建失败的用户,服务器将会持续受到长轮询的请求轰炸。
socket.on('reconnect_attempt', () => {
socket.io.opts.transports = ['polling', 'websocket'];
});
如果有更好的方式,请联系交流, qq465824201, 转载请标明出处。