Netty之WebSocket应用

1. 什么是Netty?

  Netty是一个高性能事件驱动,异步非阻塞的IO Java开源框架,由Jboss提供,用于建立Tcp等底层的链接,基于Netty可以建立高性能的Http服务器,快速开发高性能、高可靠的网络服务器和客户端程序。它支持Http,websocket,tcp,udp等协议。同时Netty又是基于NIO的客户端,服务器端编程框架,使用Netty可以确保快速和简单的开发出一个网络应用。例如实现了某种协议的客户端服务端应用。Netty简化了网络应用的开发过程,比如tcp,udp的socket服务开发。同时Netty提供了非常可靠的稳定性和良好的伸缩性。

  2. Netty使用场景

  高性能领域:比如游戏,大数据分布式计算得以广泛应用

  多线程并发领域:多路复用模型,多线程模型,主从多线程模型

  异步通信领域等。

  3. Java IO通信

  在介绍WebSocket之前,我们先简单说一下Java IO通信中的BIO,伪异步IO,NIO以及AIO等。

  (1)BIO通信

  BIO通信:采用BIO模型通信的服务端,通常由一个独立的线程负责监听客户端的连接,它接收到客户端连接请求之后,会对每个客户端创建一个新的线程,进行链路处理,处理完成之后通过输出流返回应答给客户端,此时线程销毁,这就是典型的”一请求一应答“的通信模型。

  该模型的最大缺点就是:缺乏弹性伸缩能力,当客户端并发访问量增加以后,服务端的线程个数和客户端的并发访问数会成1比1的正比关系,由于线程是Java虚拟机的宝贵系统资源,当线程数膨胀之后,系统的性能就会快速下降。随着并发访问量的继续增大,系统将会发生线程堆栈异常或创建线程失败等问题。最终导致进程宕机或将死,不能对外提供服务。

  BIO通信模型如下图:

  

https://img4.mukewang.com/5d348dc00001d25409450347.jpg

 

  客户端的个数与服务端的线程呈1比1的正比关系

  (2)伪异步IO通信

  伪异步IO通信:当有新的客户端接入的时候,将客户端的socket封装成一个task投递到后端的线程池进行处理,线程池维护一个消息队列和N个活跃的线程,对消息队列中的任务进行相关的处理。也就是说当有M个客户端接入的时候,服务端将会创建一个具有N个线程的线程池来对客户端的请求进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此它的资源占用是可控的,无论多少个客户端对它访问,都不会导致资源的耗尽和宕机。

  缺点:当有大量的客户端进入的时候,随着并发访问量的不断增加,伪异步IO通信可能会出现线程池阻塞问题。

  伪异步IO的通信模型如下图:

  

https://img3.mukewang.com/5d348e4c00016fe709320345.jpg

 

  当有N个新的客户端接入的时候,伪异步IO通信将客户端的socket封装成一个Task投递到后端的线程池中进行处理,JDK的线程池负责维护一个消息队列和N个活跃的线程,它与BIO通信模型最大的不同点是:伪异步IO通信的服务端不再针对每一个客户端都创建一个独立的线程,它是由一个线程池来统一处理所有客户端的接入请求。当有大量客户端接入的时候,并发量不断上涨,将会出现线程池阻塞问题。

  (3)NIO通信

  缓存区Buffer: 它是一个对象,它包含一些要写入或要读出的数据,在NIO中加入Buffer对象,体现了新库与原IO的重要区别。在面向流的IO中,可以将数据直接写入或者将数据直接读到Strem对象中。在NIO中,所有数据都是用缓冲区进行处理的,在读取数据时,直接取读缓冲区中的数据,在写入数据时,直接写入缓冲区中。任何时候处理NIO中的数据时都是通过缓冲区进行操作。

  通道Channel: 网络数据通过通道Channel读取和写入,通道与流的不同之处在于通道是双向的,而流只是在一个方向上移动,一个流必须是InputStream或OutputStream的子类,而通道可以用于读写或二者同时进行。

  多路复用器selector: selector提供了选择已经就绪的任务的能力,会通过不断轮询在其上的Channel,在某个Channel上面发生读或者写事件,则该Channel处于就绪状态,会被Selector轮询出来,然后获取Channel的集合进行后续IO的操作。

  NIO通信并没有最大连接数的限制,可以接入成千上万的客户端。

  (4)AIO通信

  AIO通信:它是连接注册读写事件和回调函数,读写方法异步,同时它是主动通知程序。AIO异步通信提供了两种方式获取操作结果:第一种方式是通过java.util.concurrent的Future类来表示异步操作的结果;第二种方式是在执行异步操作的时候传入一个java.nio.channels.CompletionHandler接口的实现类作为操作完成回调。NIO的异步套接字回调,是真正的异步非阻塞IO,对应于Unix网络编程中的事件驱动IO,不需要通过多路复用器对被注册的通道进行轮询操作即可实现异步读写,从而简化NIO的编程模型。

  BIO,伪异步IO,NIO, AIO之间的区别如下表:

  

https://img2.mukewang.com/5d3492ba000169fa09480848.jpg

 

  注:客户端个数中的比值是客户端的个数与服务端的IO线程数的个数比

  4. WebSocket

  (1) 什么是WebSocket?

  WebSocket是一种H5协议规范,通过握手机制客户端与服务器之间就能够建立一个类似Tcp的连接,从而方便客户端与服务器之间的通信。

  它是一种解决客户端与服务端实时通信而产生的技术:WebSocket本质是一种基于TCP协议,先通过Http/Https发一个特殊的Http请求进行握手,握手后会创建一个用于交换数据的TCP链接,之后客户端和服务端使用该TCP链接进行实时通信。当WebSocket的客户端和服务端握手后 , 也就是建立通信后,就不再需要之前的http请求参与。

  (2) WebSocket的优点:

  a. 节省通信开销,之前WebServer实现通信,都使用轮询(每隔特定时间间隔浏览器自动发送Http请求,去获取服务端的响应)该情况下,需要不停的向服务器发送请求,而HttpRequest的handler很长,请求包含真正的数据可能很小,会占用很多额外的带宽和服务器资源。

  b. 服务器主动传送数据给客户端,在给定时间,服务器和客户端在任意时刻相互推送信息,浏览器(客户端)和服务器只需要做一个握手的动作。建立连接后,服务器可主动传数据给客户端,客户端也可以随意向服务端传数据。交换数据时所携带的头信息很小。

  c. 实时通信:WebSocket不仅限于Ajax方式通信。ajax方式需要浏览器发起请求。而WebSocket技术 服务端和客户端可以彼此相互推送信息,从而实现实时通信。

  (3) WebSocket建立连接过程步骤如下:

  a、客户端发起握手请求。

  b、服务端响应请求。

  c、建立连接。

  详细流程:

  建立一个WebSocket连接,客户端或浏览器首先向服务器发送一个特殊的Http请求(携带一些附加头信息)Upgrade:websocket,服务端解析附加头信息,产生应答消息,然后响应给客户端,之后客户端就与服务端建立响应的链接。

  (4) WebSocket生命周期:

  1、打开事件:端点上建立新链接时,该事件是先于其他任何事件发生之前。该事件发生会产生三部分信息。

  1.1、创建WebSocket Session对象:用于表示已经建立好的链接 。

  1.2、配置对象:包含配置端点的信息。

  1.3、一组路径参数,用于打开节点握手时,WebSocket端入栈匹配的URI 。

  2、消息事件:主要是接收WebSocket对话中,另一端发送的消息。链接上的消息将会有三种形式抵达客户端。

  2.1、文本消息: 用String处理

  2.2、二进制消息: 用byteBuffer或者byte[]处理

  2.3、pong消息: 用Java WebSocket API中的pong.message接口的实例来处理

  3、错误事件:WebSocket链接或者端点发生错误时产生。可以处理入栈消息时发生的各种异常。入栈消息可能产生的三种异常。

  3.1、WebSocket建立链接时发生错误:SessionException类型。

  3.2、WebSocket试图将入栈消息解码成开发人员使用的对象时 EncodeException类型。

  3.3、WebSocket端点的其他方法运行时产生的错误,WebSocket实现将记录端点操作过程中产生的任何异常 。

  4、关闭事件:WebSocket链接端点关闭,做一些清理工作,可以由参与连接的任意一个端点发出。

  (5) WebSocket关闭链接:

  1、服务器关闭底层TCP链接

  2、客户端发起TCP Close

  底层的TCP ,正常情况下应该首先由服务器关闭 ,在异常情况下客户端可以发起TCP Close。

  流程:当服务器被指示关闭WebSocket链接时,服务端会发起一个TCP Close操作, 客户端应该等待服务器的TCP Close。

  5. WebSocket使用实例demo

  (1)创建一个SpringBoot工程,工程结构如下:

  

https://img.mukewang.com/5d34958e0001ab4107151100.jpg

 

  (2)启动SpringBoot服务,访问:http://localhost:8080/websocket

  (3)启动MainApp主程序,然后刷新页面,就可以进行发送数据了。

  效果图如下:

  

https://img1.mukewang.com/5d3496ac000147e416450745.jpg

热门文章

暂无图片
编程学习 ·

命令模式

菜鸟教程中代理模式总结)1.定义:将请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令 的合适的对象,并把该命令传给相应的对象,该对象执行命令。 2.主要解决:行为请求者与行为实现者通常是一种紧耦合的关系,为了消除这种耦合关系 3.何时使用:命…
暂无图片
编程学习 ·

系统整理springCloud (一),搭建父项目,管理jar包

一,应用springCloud 有一段时间了,Boot由1到2,springCloud 也到了H版本,alibaba 也已孵化维护自己版本,在这里对springCloud做一个系统的整理,版本为boot2版本。首先建立父工程cloud-parent加入jar包<!-- 统一管理jar包版本 --> <properties><project.bui…
暂无图片
编程学习 ·

收藏量4w+的Web开发框架,你还没学?点击收藏!

Python的Web开发,也是工作岗位比较多的领域。如果你对Python的Web开发有兴趣,正打算开始学习使用Python做Web开发等,那么学习一门基于Python的Web开发框架是必修课。Python作为当前最热门,也是最主要的Web开发语言之一,在其二十多年的历史中出现了数十种Web框架,比如Djan…
暂无图片
编程学习 ·

英语四六级必备软件

分享今天的软件的时候,突然想到了十几年前初中高中学英语的时候,那时候为了学会英语,让家人买过mp3,结果里面最后装的全部都是流行歌,还买过一个步步高学习机,结果最后这个学习机变成的游戏机,想想如果当初学习英语的有这个软件会怎么样今天分享的这个英语软件面向所有爱…
暂无图片
编程学习 ·

你(真的)编写异常安全代码吗? [关闭]

本文翻译自:Do you (really) write exception safe code? [closed] Exception handling (EH) seems to be the current standard, and by searching the web, I can not find any novel ideas or methods that try to improve or replace it (well, some variations exist, b…
暂无图片
编程学习 ·

docker方式部署ELK

1.拉取原始镜像: docker pull sebp/elk:6602.启动下镜像方便进入,进行自定义配置修改:docker run -dit --name elk \-p 5601:5601 \-p 9200:9200 \-p 5044:5044 \-v /data/elasticsearch:/var/lib/elasticsearch \-v /etc/localtime:/etc/localtime \sebp/elk:660这里说明下560…
暂无图片
编程学习 ·

安装JDK

安装JDK 前提 要准备好jdk的包:jdk-8uxxx-linux-x64.tar.gz //JAVA 8 版本都可以 笔者这里使用的171 下载地址:Java SE 8 存档下载. 步骤 一、 解压jdk:tar -zxvf jdk-8u171-linux-x64.tar.gz二、 设置环境变量,编辑文件添加如下: vi /etc/profile export JAVA_HOME=/us…
暂无图片
编程学习 ·

spring @Primary-@Qualifier在spring中的使用

在spring 中使用注解,常使用@Autowired, 默认是根据类型Type来自动注入的。但有些特殊情况,对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下 @Primary 的作用就出来了。下面是个简单的使用例子。 有如下一个接口 public interface Singer {String …
暂无图片
编程学习 ·

mybati中动态标签「if」没有生效的原因

一、问题: <if test="carrier != null and carrier != and carrier !=0">AND CARRIER = #{carrier} </if>我们在接口设置传入的字段类型为String,要在carrier字段不为null,空字符串,和”0“的时候增加以上条件,但是以上当carrier等于"0"时…
暂无图片
编程学习 ·

近三位数增长,苏宁银行金融科技之花结出普惠金融之果

文|曾响铃来源|科技向令说(xiangling0815)美联储无限QE,2020年中国不设GDP目标,2万亿直达基层扶危纾困……国内疫情已经基本控制,经济基本面迎来全面复苏阶段,作为市场中最活跃的存在之一,小微企业在复苏过程中,面临的融资难等问题也被热议。在中国有一群喊着帮助小微企…
暂无图片
编程学习 ·

datawhale-综合练习题目

这里写自定斜体样式义目录标题这两天考试多,休息的时间也没有,等我考完再重新做回来,等我!功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建…
暂无图片
编程学习 ·

Python之闭包的学习

什么是闭包?内部函数对外部函数作用域内变量的引用,则内部函数称为闭包。闭包的条件:必须有内嵌函数(函数里面的函数)。内嵌函数必须引用一个定义在外部函数里面的变量。外部函数必须返回内嵌函数。 列子:def funcOut(a):def funcIn(b):return a + breturn funcInf = fun…
暂无图片
编程学习 ·

数据结构-字典

字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对 哈希表 哈希表由dict.h/dictht结构定义 typedef struct dictht {// 哈希表数组dictEntry **table;// 哈希表大小unsigned long size;// 哈希表大小掩码,用于计算…
暂无图片
编程学习 ·

Python科学计算系列12—积分变换

1.拉普拉斯变换及逆变换拉普拉斯变换公式拉普拉斯逆变换公式例子:代码如下:from sympy import * from sympy.integrals import laplace_transformt, s, a = symbols(t s a) # 拉普拉斯变换 F1 = laplace_transform(sin(a * t), t, s) F2 = laplace_transform(exp(a * t), t, …
暂无图片
编程学习 ·

oss图片处理

https://help.aliyun.com/document_detail/44688.html?spm=a2c4g.11186623.6.1402.200f1729JSctVl
暂无图片
编程学习 ·

币小秘:如何才能减少被套,降低风险!

币小秘:如何才能减少被套,降低风险! 说买卖中遇到的问题之前,先说另一个问题,在资本商场玩,每个人,入市必须要学会面临自己,供认自己的问题。 这是一个放大镜商场,把你的所有瑕疵都摊开在显微镜下,假如你无法正视自己的过错——永远都是把问题归因到本身以外的当地,…