Netty框架
2021-02-01 17:14
标签:dex tty 区别 switch 字符串 组成 性能 ssg 土豆 netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。 channel代表的一个连接,每个client请求都会对应到具体的一个channel。在Netty里,Channel是通讯的载体,而ChannelHandler负责Channel中的逻辑处理。它是Netty网络通信的主体,由它负责同对端进行网络通信、注册和数据操作等功能。 状态主要包括:打开、关闭、连接 Channel对应有几个重要的组件,分别是ChannelHandler、ChannelHandlerContext、ChannelPipeline 以下是它们的关系图: 是业务逻辑的核心处理类,用于处理Channel对应的事件。 ChannelHandler接口里面只定义了三个生命周期方法,我们主要实现它的子接口ChannelInboundHandler和ChannelOutboundHandler,为了便利,框架提供了ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter和ChannelDuplexHandler这三个适配类,在使用的时候只需要实现你关注的方法即可。 *以下是ChannelHandler类图: *ChannelHandler下主要是两个子接口: ChannelInboundHandler(入站): 处理输入数据和Channel状态类型改变。 适配器: ChannelInboundHandlerAdapter(适配器设计模式) 常用的: SimpleChannelInboundHandler ChannelOutboundHandler(出站): 处理输出数据 适配器: ChannelOutboundHandlerAdapter 每一个Handler都一定会处理出站或者入站(可能两者都处理),区别在于SimpleChannelInboundHandler会对没有外界引用的资源进行一定的清理,并且入站的消息可以通过泛型来规定。 这里为什么有设配器模式呢? 我们在写自定义Handel时候,很少会直接实现上面两个接口,因为接口中有很多默认方法需要实现,所以这里就采用了设配器模式,ChannelInboundHandlerAdapter和 ChannelInboundHandlerAdapter就是设配器模式的产物,让它去实现上面接口,实现它所有方法。那么你自己写自定义Handel时,只要继承它,就无须重写上面接口的所有方法了。 *ChannelHandler 生命周期: handlerAdded: 当 ChannelHandler 添加到 ChannelPipeline 调用 handlerRemoved: 当 ChannelHandler 从 ChannelPipeline 移除时调用 exceptionCaught: 当 ChannelPipeline 执行抛出异常时调用 是维护ChannelHandler的上下文内容(1. channelHandler的一些状态信息的存储;2. channelHandler之间互相通讯的桥梁,如本文第一张图便可知道ChannelHandlerContext的重要性了。(通过channelHandlerContext,我们可用把上一个channelHandler的处理结果传递给下一个channelHandler))。 在Channel创建的时候,会同时创建ChannelPipeline。可以理解为ChannelPipeline是ChannelHandler的容器,所有ChannelHandler都会注册到ChannelPipeline中,并按顺序组织起来。channel事件消息在ChannelPipeline中流动和传播,相应的事件能够被ChannelHandler拦截处理、传递、忽略或者终止。Pipeline把channelHandle和Context给连接起来。 以下是channelPipeline内的关系图: 由上图可以看出,ChannelPipeline包含两条线路:Upstream和Downstream。Upstream对应上行,接收到的消息、被动的状态改变,都属于Upstream。Downstream则对应下行,发送的消息、主动的状态改变,都属于Downstream。 由上图和代码可以看出,对于DefaultChinnelPipeline它的Handel头部和尾部的Handel是固定的,我们所添加的Handel是添加在这个头和尾之前的Handel。(下面这个图更加清晰) 1>channelRegistered: channel注册到一个EventLoop。 Netty 主要基于主从 Reactors 多线程模型(如下图)做了一定的修改,其中主从 Reactor 多线程模型有多个 Reactor: MainReactor 负责客户端的连接请求,并将请求转交给 SubReactor。 SubReactor 负责相应通道的 IO 读写请求。 非 IO 请求(具体逻辑处理)的任务则会直接写入队列,等待 worker threads 进行处理。 这里引用 Doug Lee 大神的 Reactor 介绍:Scalable IO in Java 里面关于主从 Reactor 多线程模型的图: 特别说明的是:虽然 Netty 的线程模型基于主从 Reactor 多线程,借用了 MainReactor 和 SubReactor 的结构。但是实际实现上 SubReactor 和 Worker 线程在同一个线程池中: 上面代码中的 bossGroup 和 workerGroup 是 Bootstrap 构造方法中传入的两个对象,这两个 group 均是线程池: bossGroup 线程池则只是在 Bind 某个端口后,获得其中一个线程作为 MainReactor,专门处理端口的 Accept 事件,每个端口对应一个 Boss 线程。 workerGroup 线程池会被各个 SubReactor 和 Worker 线程充分利用。 1.定义两个线程组 也叫做事件循环组 2.定义一个的启动服务类 2. 通过ChannelPipeline初始化处理器,类似于拦截器Chain,当客户端首次连接后即调用initChannel方法完成初始化动作。 初始化器,服务端启动后会自动调用这个方法,它是一个回调方法 3. Client的消息处理类Handler,通常继承SimpleChannelInboundHandler 4.心跳机制(基本步骤只要前3步即可)(这个心跳机制还没有细细研究,等研究完了再来补上相关知识点) 以上只是部分代码 代码学习的git地址: 后端项目地址: https://github.com/haoxiaoyong1014/chat-software 前端项目地址: https://github.com/haoxiaoyong1014/chat-view Netty的传输快其实也是依赖了NIO的这个。我们知道,Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。 Netty针对这种情况,使用了NIO中的另一大特性——零拷贝,当他需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。 传统数据拷贝 零拷贝 1.Netty的使用:Server和Client通信 2.SpringBoot+Netty开发IM即时通讯系列(一) 3.(七)分布式通信----Netty实现NIO通信 4.【Netty】(8)---理解ChannelPipeline 5. Netty学习笔记之ChannelHandler 6.这可能是目前最透彻的Netty原理架构解析 Netty框架 标签:dex tty 区别 switch 字符串 组成 性能 ssg 土豆 原文地址:https://www.cnblogs.com/chensisi/p/13164402.html简介
提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。特点:并发高、传输快、封装好。核心组件
一、Channel:
1.概念
主要的IO操作,读(read)、写(write)、连接(connect)、绑定(bind)。
所有的IO操作都是异步的,调用诸如read,write方法后,并不保证IO操作完成,但会返回一个凭证,在IO操作成功,取消或失败后会记录在该凭证中。2.组件
1>ChannelHandler
2>channelHandlerContex
3>channelPipeline
1 /**
2 * 可以看到,DefaultChinnelPipeline 内部使用了两个特殊的Hander 来表示Handel链的头和尾。
3 */
4 public DefaultChannelPipeline(AbstractChannel channel) {
5 if (channel == null) {
6 throw new NullPointerException("channel");
7 }
8 this.channel = channel;
9
10 TailHandler tailHandler = new TailHandler();
11 tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);
12
13 HeadHandler headHandler = new HeadHandler(channel.unsafe());
14 head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);
15
16 head.next = tail;
17 tail.prev = head;
18 }
3.Channel 生命周期
2>channelActive: 变为活跃状态(连接到了远程主机),可以接受和发送数据
3>channelInactive: channel处于非活跃状态,没有连接到远程主机
4>channelUnregistered: channel已经创建,但是未注册到一个EventLoop里面,也就是没有和Selector绑定 二、Netty线程模型
1 EventLoopGroup bossGroup = new NioEventLoopGroup();
2 EventLoopGroup workerGroup = new NioEventLoopGroup();
3 ServerBootstrap server = new ServerBootstrap();
4 server.group(bossGroup, workerGroup)
5 .channel(NioServerSocketChannel.class)
三、基于netty构建服务的基本步骤
1 package cn.haoxiaoyong.netty.netty;
2
3 import io.netty.bootstrap.ServerBootstrap;
4 import io.netty.channel.ChannelFuture;
5 import io.netty.channel.EventLoopGroup;
6 import io.netty.channel.nio.NioEventLoopGroup;
7 import io.netty.channel.socket.nio.NioServerSocketChannel;
8 import org.springframework.stereotype.Component;
9
10 import javax.annotation.PostConstruct;
11
12 /**
13 * @author chensisi
14 * @Date 2020/6/22
15 * @Description 1.实现客户端发送请求
16 */
17 @Component
18 public class WebSocketServer {
19
20
21 /**
22 *主线程组,用于接收客户端的链接,但不做任何处理
23 */
24 private EventLoopGroup bossGroup;
25 /**
26 *定义从线程组,主线程组会把任务转给从线程组进行处理
27 */
28 private EventLoopGroup workerGroup;
29 /**
30 *服务器
31 */
32 private ServerBootstrap server;
33 /**
34 *回调
35 */
36 private ChannelFuture future;
37
38 /**
39 * 服务启动类,任务分配自动处理
40 *
41 */
42 @PostConstruct
43 public void start() {
44 //启动
45 //绑定监听端口,调用sync同步阻塞方法等待绑定操作完
46 // ChannelFuture future = server.bind(port).sync();
47 try {
48 future = server.bind(9001);
49 System.out.println("netty server - 启动成功");
50
51 //获取某个客户端所对应的chanel,关闭并设置同步方式
52 //future.channel().closeFuture().sync();
53 } catch (Exception e) {
54 e.printStackTrace();
55 } finally {
56 //使用一种优雅的方式进行关闭
57 bossGroup.shutdownGracefully();
58 workerGroup.shutdownGracefully();
59 }
60 }
61
62 /**
63 * 构造方法,方便调用
64 */
65 public WebSocketServer() {
66 bossGroup = new NioEventLoopGroup();
67 workerGroup = new NioEventLoopGroup();
68 server = new ServerBootstrap();
69 //需要去针对一个之前的线程模型(上面定义的是主从线程)
70 server.group(bossGroup,workerGroup)
71 //设置NIO的双向通道
72 .channel(NioServerSocketChannel.class)
73 //子处理器,用于处理workerGroup
74 /*设置chanel初始化器
75 每一个chanel由多个handler共同组成管道(pipeline)*/
76
77 .childHandler(new WebSocketInitializer());
78 }
79
80 }
1 public class WebSocketInitializer extends ChannelInitializer
1 public class ChatHandler extends SimpleChannelInboundHandler
1 public class HearBeatHandler extends ChannelInboundHandlerAdapter {
2
3 /**
4 * 触发用户事件
5 */
6 @Override
7 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
8 //evt是否为一个IdleStateEvent类的实例
9 if (evt instanceof IdleStateEvent) {
10 IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
11 if (idleStateEvent.state() == IdleState.READER_IDLE) {
12 //读空闲
13 //检测到读空闲不做任何的操作
14 System.out.println("读空闲事件触发...");
15 } else if (idleStateEvent.state() == IdleState.WRITER_IDLE) {
16 //写空闲
17 //检测到写空闲不做任何的操作
18 System.out.println("写空闲事件触发...");
19 } else if (idleStateEvent.state() == IdleState.ALL_IDLE) {
20 //读写空闲
21 System.out.println("--------------");
22 System.out.println("读写空闲事件触发");
23 System.out.println("关闭通道资源");
24 ctx.channel().close();
25 }
26 }
27 }
28 }
netty的优点
零拷贝
抄录地址:
Netty学习笔记之ChannelHandler
上一篇:js进行数字图像处理:亮度、对比度、马赛克画笔、放大缩小、镜像、贴纸、旋转、颜色值显示
下一篇:npm 安装依赖报错解决:npm ERR! Unexpected end of JSON input while parsing near