WebSocket想必大家都不陌生,当我们的程序需要实时高效的获取后端的返回结果时,除了早期大家用到的前端轮循的机制之外,当前比较简单好用的莫过于WebSocket了。
当然,这篇文章不是WebSocket的科普文,按照惯例:强哥出品,必属精品的原则(哈哈,自吹一波)。我们就不在这里介绍WebSocket是什么以及如何在Springboot上使用了,大家有兴趣的可以自行百度。
当我们在后端项目中加入WebSocket之后,如何测试是否配置成功呢?无疑编写一个前端html5的demo页面,试着建立WebSocket连接并由后端主动推送消息查看前端是否收到是最简单的验证方式了。
截取部分后端代码:
@OnOpenpublic void onOpen(Session session,@PathParam("winNum") String fromWinNum) throws IOException { this.session = session; if(StringUtils.isEmpty(fromWinNum)){ log.error("请输入窗口号!!!!!!!!!!!!!!!!"); return; }else{ try { if(websocketList.get(fromWinNum) == null){ this.winNum = fromWinNum; websocketList.put(fromWinNum,this); addOnlineCount(); //在线数加1 log.info("有新窗口开始监听:{},当前窗口数为{}",fromWinNum,getOnlineCount()); sendMessage("一条来自后端的消息"); }else{ session.getBasicRemote().sendText("已有相同窗口,请重新输入不同窗口号"); CloseReason closeReason = new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE,"相同窗口"); session.close(closeReason); } }catch (IOException e){ e.printStackTrace(); } } if(session.isOpen()){ log.info("connect success"); }}
下面是一个简单的前端脚本:
<!DOCTYPE HTML><html> <head> <meta charset="utf-8"> <script type="text/javascript"> var success = 0; function WebSocketTest(i){ if ("WebSocket" in window){ var x = 100;//上限 var y = 0; //下限 // 打开一个 web socket var ws = new WebSocket("ws://localhost:8080/websocket/" + i); ws.onopen = function(){ // Web Socket 已连接上,使用 send() 方法发送数据 ws.send("客户端来了"); success++; console.log("成功连接个数:"+success); }; ws.onmessage = function (evt) { var received_msg = evt.data; }; }else{ // 浏览器不支持 WebSocket alert("您的浏览器不支持 WebSocket!"); } } WebSocketTest(1);</script> </head> <body> </body></html>
可以看到,我们在js代码中,调用WebSocketTest()方法创建了前端Html5的WebSocket客户端并连接到服务器端。我们打开Chrome浏览器的开发者工具查看:
确实成功创建了WebSocket连接,同时收到了来自后端推送的消息。由此可见,我们的项目中引入WebSocket其实还是相对比较简单的,同时因为其能够在前端主动收到来自后端推送的消息,实时性上也更优于前端轮询的机制。
当我们完成了上面的内容,想必许多人就急急忙忙的开始在WebSocket服务中加入自己的业务逻辑了。强哥当初也是这样,可是代码敲着敲着心里就暗暗发虚。
不知道大家有没有思考过一个问题:WebSocket是长连接,当用户量上来后,对我们服务端的压力不是就会比传统的Http无状态连接的压力大吗?要是因为引入了WebSocket后,用户量一上来就导致系统无法访问那可不行,那不是还不如原来的轮询了吗?
没错,如果能够在项目中引入新功能时,我们能够多想想,我相信你已经是一个较负责的开发人员了。尽量让问题发生在开发甚至预研阶段,而不是在项目真正的部署上线后往往是比较的,上线后遇到问题,只会让我们焦头烂额并且备受打击。
既然担心引入WebSocket会对性能造成影响,那最直接方式无疑就是进行压测看看实际效果了。那么要怎么压测呢?哈哈,我们不是已经有了前端连接WebSocket的代码了吗,我们进行改造一下:
<!DOCTYPE HTML><html> <head> <meta charset="utf-8"> <script type="text/javascript"> var success = 0; function WebSocketTest(i){ if ("WebSocket" in window) { var x = 100;//上限 var y = 0; //下限 // 打开一个 web socket var ws = new WebSocket("ws://localhost:8080/websocket/" + i); ws.onopen = function(){ // Web Socket 已连接上,使用 send() 方法发送数据 ws.send("客户端来了"); success++; console.log("成功连接个数:"+success); }; ws.onmessage = function (evt) { var received_msg = evt.data; }; } else { // 浏览器不支持 WebSocket alert("您的浏览器不支持 WebSocket!"); } } for(let i=0; i<50; i++){ WebSocketTest(i) }</script> </head> <body> </body></html>
没错,我们将WebSocketTest()方法加入到一个for循环里面,先循环50次练练手。前后端输出如下:
可见没有问题,加大压力150再试试:
同样没有问题,那就翻个倍300,嘿嘿,真是越来越自信:
纳尼!!!怎么只有255个,有45个失败了:
这尼玛,不到300个就翻车了,那还搞什么,难不成引入WebSocket后,我的项目只能不到300人访问,那还不如轮训呢。不!我不信,再来几次:280次、310次、400次。结果居然都和上面一样,成功建立256个连接之后,其他的就都失败了。
256这么诡异的数字,里面一定隐含着惊天的秘密,第一个想法就是:难不成是后端限制了websocket的连接个数?
结果一番搜索,在配置文件中找到了如下配置:
server.tomcat.max-connections=10000
最大连接数为10000???我压测的结果是256。10000???256???这差的也太多了吧!!
难道是没有生效,把10000改成10试试,前端还是一下请求300次看看:
确实连上10个之后,其他的就都失败了,由此可见,这个配置还是生效的。那到底是因为什么?
连接到了256个就失败了,后端配置最大连接数又是生效的且远大于256,那么这个诡异的256究竟是为什么呢?后端配置是对的……啊!前端是不是有什么问题?难道浏览器限制了WebSocket的连接数?赶紧百度一下:
确认过眼神,就是这个答案了,Chrome浏览器限制WebSocket的最大连接数就是256。可能在试验中存在一点点偏差,可是是这个答案无疑了。既然是因为浏览器的限制,那我们在搞个别的浏览器(这里用360浏览器)一起试试,后端连接改回10000,前端代码还是300,当然因为后端代码有限制map的key不能重复,所以换浏览器时,for循环代码需要改成从300开始,结果如下:
成功了!!后端接收了255+256=511个请求,可见后端还是能够顶得住压力的。由此,我们便弄清了为什么在测试的时候,WebSocket最多只能连接256个连接的问题:原来各个浏览器有对WebSocket的连接数进行了限制。
看到这里你可能会觉得真是一顿操作猛如虎,结果竟然是因为浏览器的问题。这不是很简单吗?可是,当你真正的遇到问题的时候,其实,解决问题的过程才是比较关键的。
既然解决了这个问题,可是接下来是不是就一帆风顺了呢?强哥又想再多问几个问题:
- 后端既然可以接受10000个连接,可是,是不是每一个请求过来,都需要一个线程来处理呢?
- 我们的后端代码中,session作为成员变量,难道是每个请求都有独自的类处理吗,Springboot不是默认单例吗?
- 前后端WebSoket有没有可能出现断连的问题,要如何解决呢?
这些问题,强哥在之后的推文中将会继续展开讲解,好了,今天的篇幅有点太长了,就到这里吧。
对了,今天的源码稍后也会发布出来,公众号后台留言:websocket可以获取到相应源码