本质就是Web端不断向Server服务端发送一个HTTP请求,如果有新消息,则会将请求返回**(无数据:服务端立即返回空箱,客户端继续问)**
适用场景:
比如扫码登录
,当用户扫码时,说明用户是有意向的,所以我们客户端就不断发送请求获取消息
小OA系统
:客户量不大的情况。
缺点: 1.当用户量大,请求就会频繁发送,服务器就会频繁空响,当服务器没有新数据时也会产生较高的网络流量,浪费服务器的资源 2.无效请求很多
过程: 1.客户端发送请求,询问数据——>2.若是服务器有新的数据,将数据一起返回,否则将请求挂起(超时时间),并保持连接**(短轮询:直接返回空响了)** ,当新数据到达时才会返回给客户端
优点: 1.大幅度降低了服务端对于无用的轮询的开销 2.降低了服务器处理请求QPS
缺点: 1.没有完全解决客户端无效请求的问题
,因为有超时时间,过了超时时间后,若还没有数据达达,则会结束返回
2.服务端的压力还是比较大,因为只是降低了入口请求的QPS,但是因为要等待数据,所以并没有减少对后端资源轮询
(相当于轮询从前端移到了后端)
是一个全双工通道
1.实现方式:
1.我们这里用的netty实现websocket ,因为netty是nio
基于事件驱动的多路复用框架,即使用单线程或者少量线程能够处理大量并发连接;而Tomcat
是基于多线程
的架构,每个连接都会分配一个线程,适合于并发量比较小的适合
——>不过自T8.9之后Tomcat又引入了NIO
模型
https://blog.csdn.net/weixin_57128596/article/details/126168054?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170609584116800186537976%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=170609584116800186537976&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-1-126168054-null-null.nonecase&utm_term=nio&spm=1018.2226.3001.4450(NIO学习)
NIO与BIO的比较
2.Netty
提供了丰富的功能和组件
,可以灵活地构建自定义的网络应用。它具有强大的编解码器和处理器
,可以轻松处理复杂的协议
和数据格式
。Netty 的扩展性也非常好
,可以根据需要添加自定义的组件
。比如我们可以用netty的pipeline方便的进行前置后置的处理,可以用netty的心跳处理器
来检查连接的状态
。这些都是netty的优势**(1.丰富的组件 2.编码译码器,处理器 3.扩展良好,易于添加组件 4.心跳处理器)**
客户端依靠发起HTTP握手
,告诉服务端进行WebSocket协议通讯
,并告知WebSocket协议版本
。服务端确认协议版本,升级
为WebSocket协议。之后如果有数据需要推送
,会主动推送给客户端
。
连接开始时,客户端使用HTTP协议和服务端升级协议,升级完成后
,后续数据交换遵循WebSocket协议
。我们看看Request Headers
public void run() throws InterruptedException {
// 服务器启动引导对象
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new LoggingHandler(LogLevel.INFO)) // 为 bossGroup 添加 日志处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//30秒客户端没有向服务器发送心跳则关闭连接
pipeline.addLast(new IdleStateHandler(30, 0, 0));
// 因为使用http协议,所以需要使用http的编码器,解码器
pipeline.addLast(new HttpServerCodec());
// 以块方式写,添加 chunkedWriter 处理器
pipeline.addLast(new ChunkedWriteHandler());
/**
* 说明:
* 1. http数据在传输过程中是分段的,HttpObjectAggregator可以把多个段聚合起来;
* 2. 这就是为什么当浏览器发送大量数据时,就会发出多次 http请求的原因
*/
pipeline.addLast(new HttpObjectAggregator(8192));
//保存用户ip
pipeline.addLast(new HttpHeadersHandler());
/**
* 说明:
* 1. 对于 WebSocket,它的数据是以帧frame 的形式传递的;
* 2. 可以看到 WebSocketFrame 下面有6个子类
* 3. 浏览器发送请求时: ws://localhost:7000/hello 表示请求的uri
* 4. WebSocketServerProtocolHandler 核心功能是把 http协议升级为 ws 协议,保持长连接;
* 是通过一个状态码 101 来切换的
*/
pipeline.addLast(new WebSocketServerProtocolHandler("/"));
// 自定义handler ,处理业务逻辑
pipeline.addLast(new NettyWebSocketServerHandler());
}
});
// 启动服务器,监听端口,阻塞直到启动成功
serverBootstrap.bind(WEB_SOCKET_PORT).sync();
System.out.println("启动成功");
}
明白了websocket的升级过程,对netty的处理的就比较简单了。websocket初期是通过http请求,进行升级,建立双方的连接。
1.所以编解码器
需要用到HttpServerCodec。
2.WebSocketServerProtocolHandler
是netty进行websocket升级的处理器
(目的升级http-ws)。在这期间会抹除http相关的信息
,比如请求头啥的。如果想获取相关信息,需要在这之前获取。
里面的源代码有判断请求是否是http请求,如果是的话会组装一个握手响应类进行响应(handshake)——>如果成功就会发送成功的事件,失败的话就会返回失败饿事件
3.HttpHeadersHandler
是我们自己的处理器。赶在websocket升级之前,获取用户的ip地址
,然后保存到channel的附件里。
4.NettyWebSocketServerHandler
是我们的业务处理器,里面处理客户端的事件。
5.IdleStateHandler
实现心跳检测。