- 場景
創建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, 轉載請標明出處。