如果有一个需求,让你构建一个网络的聊天室,你会怎么解决?
首先,对于请求来说,端总是处于被动的一方,即只能由发送请求,才能够被动回应。
也就是说,如果没有发送请求,则就不能回应。
并且具有无状态的特点,即使有长链接(Connection请求头)的支持,但受限于的被动特性,要有更好的解决思路才行。
根据上面的需求,最简单的解决方案就是不断的朝端发送请求,获取最新的消息。
对于前端来说一般都是基于来做,但是轮询的缺点非常明显:
- Server需要不断的处理请求,压力非常大
- 前端数据刷新不及时,setInterval间隔时间越长,数据刷新越慢,setInterval间隔时间越短,Server端的压力越大
以下是用和做的简单实例。
每个用户打开该页面后都会生成一个随机名字,前端采用轮询的方式更新记录。
后端用一个列表存储最近的聊天记录,最多存储100条,超过一百条截取最近十条。
总体流程就是前端发送过来的消息都放进列表中,然后前端轮询时就将整个聊天记录列表获取到后在页面进行渲染。
缺点非常明显,仅仅有两个用户在线时,后端的请求就非常频繁了:
后端代码:
前端代码:
前端代码:
轮询是不断的发送请求,端显然受不了。
这时候就可以使用长轮询的机制,即为每一个进入聊天室的用户(与端建立连接的用户)创建一个队列,每个用户轮询时都去询问自己的队列,如果没有新消息就等待,如果后端一旦接收到新消息就将消息放入所有的等待队列中返回本次请求。
长轮询是在轮询基础上做的,也是不断的访问服务器,但是服务器不会即刻返回,而是等有新消息到来时再返回,或者等到超时时间到了再返回。
- Server端采用队列,为每一个请求创建一个专属队列
- Server端有新消息进来,放入每一个请求的队列中进行返回,或者等待超时时间结束捕获异常后再返回
使用长轮询实现聊天室是最佳的解决方案。
前端页面打开后的流程依旧是生成随机名字,后端立马为这个随机名字拼接上uuid后创建一个专属的队列。
然后每次发送消息时都将消息装到每个用户的队列中,如果有队列消息大于1的说明该用户已经下线,将该队列删除即可。
获取最新消息的时候就从自己的队列中获取,获取不到就阻塞,获取到就立刻返回。
后端代码:
前端代码:
前端代码: