超详细Netty入门,看这篇就够了

本文主要讲述Netty框架的一些特性以及重要组件,希望看完之后能对Netty框架有一个比较直观的感受,希望能帮助读者快速入门Netty,减少一些弯路。

官方的介绍:

Netty is an asynchronous event-driven network application frameworkfor rapid development of maintainable high performance protocol servers & clients.

Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端

从官网上介绍,Netty是一个网络应用程序框架,开发服务器和客户端。也就是用于网络编程的一个框架。既然是网络编程,Socket就不谈了,为什么不用NIO呢?

2.1 NIO的缺点

对于这个问题,之前我写了一篇文章《NIO入门》对NIO有比较详细的介绍,NIO的主要问题是:

  • NIO的类库和API繁杂,学习成本高,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
  • 需要熟悉Java多线程编程。这是因为NIO编程涉及到Reactor模式,你必须对多线程和网络编程非常熟悉,才能写出高质量的NIO程序。
  • 臭名昭著的epoll bug。它会导致Selector空轮询,最终导致CPU 100%。直到JDK1.7版本依然没得到根本性的解决。

2.2 Netty的优点

相对地,Netty的优点有很多:

  • API使用简单,学习成本低。
  • 功能强大,内置了多种解码编码器,支持多种协议。
  • 性能高,对比其他主流的NIO框架,Netty的性能最优。
  • 社区活跃,发现BUG会及时修复,迭代版本周期短,不断加入新的功能。
  • Dubbo、Elasticsearch都采用了Netty,质量得到验证。

上面这张图就是在官网首页的架构图,我们从上到下分析一下。

绿色的部分Core核心模块,包括零拷贝、API库、可扩展的事件模型。

橙色部分Protocol Support协议支持,包括Http协议、webSocket、SSL(安全套接字协议)、谷歌Protobuf协议、zlib/gzip压缩与解压缩、Large File Transfer大文件传输等等。

红色的部分Transport Services传输服务,包括Socket、Datagram、Http Tunnel等等。

以上可看出Netty的功能、协议、传输方式都比较全,比较强大。

首先搭建一个HelloWord工程,先熟悉一下API,还有为后面的学习做铺垫。以下面这张图为依据:

4.1 引入Maven依赖

使用的版本是4.1.20,相对比较稳定的一个版本。

4.2 创建服务端启动类

4.3 创建服务端处理器

4.4 创建客户端启动类

4.5 创建客户端处理器

4.6 测试

先启动服务端,再启动客户端,就可以看到结果:

MyServer打印结果:

MyClient打印结果:

5.1 taskQueue任务队列

如果Handler处理器有一些长时间的业务处理,可以交给taskQueue异步处理。怎么用呢,请看代码演示:

我们打一个debug调试,是可以看到添加进去的taskQueue有一个任务。

5.2 scheduleTaskQueue延时任务队列

延时任务队列和上面介绍的任务队列非常相似,只是多了一个可延迟一定时间再执行的设置,请看代码演示:

依然打开debug进行调试查看,我们可以有一个scheduleTaskQueue任务待执行中

5.3 Future异步机制

在搭建HelloWord工程的时候,我们看到有一行这样的代码:

很多操作都返回这个ChannelFuture对象,究竟这个ChannelFuture对象是用来做什么的呢?

ChannelFuture提供操作完成时一种异步通知的方式。一般在Socket编程中,等待响应结果都是同步阻塞的,而Netty则不会造成阻塞,因为ChannelFuture是采取类似观察者模式的形式进行获取结果。请看一段代码演示:

5.4 Bootstrap与ServerBootStrap

Bootstrap和ServerBootStrap是Netty提供的一个创建客户端和服务端启动器的工厂类,使用这个工厂类非常便利地创建启动类,根据上面的一些例子,其实也看得出来能大大地减少了开发的难度。首先看一个类图:

可以看出都是继承于AbstractBootStrap抽象类,所以大致上的配置方法都相同。

一般来说,使用Bootstrap创建启动器的步骤可分为以下几步:

5.4.1 group()

在上一篇文章《Reactor模式》中,我们就讲过服务端要使用两个线程组:

  • bossGroup 用于监听客户端连接,专门负责与客户端创建连接,并把连接注册到workerGroup的Selector中。
  • workerGroup用于处理每一个连接发生的读写事件。

一般创建线程组直接使用以下new就完事了:

有点好奇的是,既然是线程组,那线程数默认是多少呢?深入源码:

通过源码可以看到,默认的线程数是cpu核数的两倍。假设想自定义线程数,可以使用有参构造器:

5.4.2 channel()

这个方法用于设置通道类型,当建立连接后,会根据这个设置创建对应的Channel实例。

使用debug模式可以看到

通道类型有以下:

NioSocketChannel: 异步非阻塞的客户端 TCP Socket 连接。

NioServerSocketChannel: 异步非阻塞的服务器端 TCP Socket 连接。

常用的就是这两个通道类型,因为是异步非阻塞的。所以是首选。

OioSocketChannel: 同步阻塞的客户端 TCP Socket 连接。

OioServerSocketChannel: 同步阻塞的服务器端 TCP Socket 连接。

稍微在本地调试过,用起来和Nio有一些不同,是阻塞的,所以API调用也不一样。因为是阻塞的IO,几乎没什么人会选择使用Oio,所以也很难找到例子。我稍微琢磨了一下,经过几次报错之后,总算调通了。代码如下:

NioSctpChannel: 异步的客户端 Sctp(Stream Control Transmission Protocol,流控制传输协议)连接。

NioSctpServerChannel: 异步的 Sctp 服务器端连接。

本地没启动成功,网上看了一些网友的评论,说是只能在linux环境下才可以启动。从报错信息看:SCTP not supported on this platform,不支持这个平台。因为我电脑是window系统,所以网友说的有点道理。

5.4.3 option()与childOption()

首先说一下这两个的区别。

option()设置的是服务端用于接收进来的连接,也就是boosGroup线程。

childOption()是提供给父管道接收到的连接,也就是workerGroup线程。

搞清楚了之后,我们看一下常用的一些设置有哪些:

SocketChannel参数,也就是childOption()常用的参数:

SO_RCVBUF Socket参数,TCP数据接收缓冲区大小。TCP_NODELAY TCP参数,立即发送数据,默认值为Ture。SO_KEEPALIVE Socket参数,连接保活,默认值为False。启用该功能时,TCP会主动探测空闲连接的有效性。

ServerSocketChannel参数,也就是option()常用参数:

SO_BACKLOG Socket参数,服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝。默认值,Windows为200,其他为128。

由于篇幅限制,其他就不列举了,大家可以去网上找资料看看,了解一下。

5.4.4 设置流水线(重点)

ChannelPipeline是Netty处理请求的责任链,ChannelHandler则是具体处理请求的处理器。实际上每一个channel都有一个处理器的流水线。

在Bootstrap中childHandler()方法需要初始化通道,实例化一个ChannelInitializer,这时候需要重写initChannel()初始化通道的方法,装配流水线就是在这个地方进行。代码演示如下:

处理器Handler主要分为两种:

ChannelInboundHandlerAdapter(入站处理器)、ChannelOutboundHandler(出站处理器)

入站指的是数据从底层java NIO Channel到Netty的Channel。

出站指的是通过Netty的Channel来操作底层的java NIO Channel。

ChannelInboundHandlerAdapter处理器常用的事件有

  1. 注册事件 fireChannelRegistered。
  2. 连接建立事件 fireChannelActive。
  3. 读事件和读完成事件 fireChannelRead、fireChannelReadComplete。
  4. 异常通知事件 fireExceptionCaught。
  5. 用户自定义事件 fireUserEventTriggered。
  6. Channel 可写状态变化事件 fireChannelWritabilityChanged。
  7. 连接关闭事件 fireChannelInactive。

ChannelOutboundHandler处理器常用的事件有

  1. 端口绑定 bind。
  2. 连接服务端 connect。
  3. 写事件 write。
  4. 刷新时间 flush。
  5. 读事件 read。
  6. 主动断开连接 disconnect。
  7. 关闭 channel 事件 close。

还有一个类似的handler(),主要用于装配parent通道,也就是bossGroup线程。一般情况下,都用不上这个方法。

5.4.5 bind()

提供用于服务端或者客户端绑定服务器地址和端口号,默认是异步启动。如果加上sync()方法则是同步。

有五个同名的重载方法,作用都是用于绑定地址端口号。不一一介绍了。

5.4.6 优雅地关闭EventLoopGroup

会关闭所有的child Channel。关闭之后,释放掉底层的资源。

5.5 Channel

Channel是什么?不妨看一下官方文档的说明:

A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind

翻译大意:一种连接到网络套接字或能进行读、写、连接和绑定等I/O操作的组件。

如果上面这段说明比较抽象,下面还有一段说明:

A channel provides a user:

the current state of the channel (e.g. is it open? is it connected?),the configuration parameters of the channel (e.g. receive buffer size),the I/O operations that the channel supports (e.g. read, write, connect, and bind), andthe ChannelPipeline which handles all I/O events and requests associated with the channel.

翻译大意:

channel为用户提供:

  1. 通道当前的状态(例如它是打开?还是已连接?)
  2. channel的配置参数(例如接收缓冲区的大小)
  3. channel支持的IO操作(例如读、写、连接和绑定),以及处理与channel相关联的所有IO事件和请求的ChannelPipeline。

5.5.1 获取channel的状态

以上就是获取channel的四种状态的方法。

5.5.2 获取channel的配置参数

获取单条配置信息,使用getOption(),代码演示:

获取多条配置信息,使用getOptions(),代码演示:

5.5.3 channel支持的IO操作

写操作,这里演示从服务端写消息发送到客户端:

客户端控制台:

连接操作,代码演示:

通过channel获取ChannelPipeline,并做相关的处理:

5.6 Selector

在NioEventLoop中,有一个成员变量selector,这是nio包的Selector,在之前《NIO入门》中,我已经讲过Selector了。

Netty中的Selector也和NIO的Selector是一样的,就是用于监听事件,管理注册到Selector中的channel,实现多路复用器。

5.7 PiPeline与ChannelPipeline

在前面介绍Channel时,我们知道可以在channel中装配ChannelHandler流水线处理器,那一个channel不可能只有一个channelHandler处理器,肯定是有很多的,既然是很多channelHandler在一个流水线工作,肯定是有顺序的。

于是pipeline就出现了,pipeline相当于处理器的容器。初始化channel时,把channelHandler按顺序装在pipeline中,就可以实现按序执行channelHandler了。

在一个Channel中,只有一个ChannelPipeline。该pipeline在Channel被创建的时候创建。ChannelPipeline包含了一个ChannelHander形成的列表,且所有ChannelHandler都会注册到ChannelPipeline中。

5.8 ChannelHandlerContext

在Netty中,Handler处理器是有我们定义的,上面讲过通过集成入站处理器或者出站处理器实现。这时如果我们想在Handler中获取pipeline对象,或者channel对象,怎么获取呢。

于是Netty设计了这个ChannelHandlerContext上下文对象,就可以拿到channel、pipeline等对象,就可以进行读写等操作。

通过类图,ChannelHandlerContext是一个接口,下面有三个实现类。

实际上ChannelHandlerContext在pipeline中是一个链表的形式。看一段源码就明白了:

下面我用一张图来表示,会更加清晰一点:

5.9 EventLoopGroup

我们先看一下EventLoopGroup的类图:

其中包括了常用的实现类NioEventLoopGroup。OioEventLoopGroup在前面的例子中也有使用过。

从Netty的架构图中,可以知道服务器是需要两个线程组进行配合工作的,而这个线程组的接口就是EventLoopGroup。

每个EventLoopGroup里包括一个或多个EventLoop,每个EventLoop中维护一个Selector实例。

5.9.1 轮询机制的实现原理

我们不妨看一段DefaultEventExecutorChooserFactory的源码:

这段代码可以确定执行的方式是轮询机制,接下来debug调试一下:

它这里还有一个判断,如果线程数不是2的N次方,则采用取模算法实现。

能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!

本文为阿里云原创内容,未经允许不得转载。

推荐 33 个 IDEA 最牛配置,写代码太爽了

1.设置maven

1.在File->settings->搜索maven

2.Mavan home directory–设置maven安装包的bin文件夹所在的位置

3.User settings file–设置setting文件所在的位置

4.Local repository–设置本地仓库

2.IDEA 设置代码行宽度

1.在File->settings->Editor->Code Style

2.有人会问,如果输入的代码超出宽度界线时,如何让IDE自动将代码换行?有两种方式!

3.第一种,在上述的“Right margin (columns)”的下方,有“Wrap when typing reaches right margin”选项,选中它,是什么效果呢?

4.随着输入的字符的增加,当代码宽度到达界线时,IDEA会自动将代码换行。

5.第一种方式是在输入代码时触发,还有第二种方式,在File->settings->Code Style->Java中,选中“Wrapping and Braces”选项卡,

6.在“Keep when reformatting”中有一个“Ensure rigth margin is not exceeded”,选中它,是什么效果呢?

7.从配置项的字面意思很容易理解,在格式化Java代码时,确保代码没有超过宽度界线。

8.即输入的代码超出界线后,

3.IDEA 提示不区分大小写

1.首先打开File—–>setting

2.然后,输入:sensitive

3.将右侧的 case sensitive completion 修改为NONE

4.IntelliJ强制更新Maven Dependencies

1.Intellj 自动载入Mave依赖的功能很好用,但有时候会碰到问题,导致pom文2.件修改却没有触发自动重新载入的动作,此时需要手动强制更新依赖。

如下:

1.手动删除Project Settings里面的Libraries内容;

2.在Maven Project的试图里clean一下,删除之前编译过的文件;

3.项目右键-》Maven-》Reimport

4.Ok, 此时发现依赖已经建立!

5.idea的环境配置默认保存位置

1.idea的环境配置默认保存位置:C:\\Users\\xxxxxxxxx\\.IntelliJIdea14 ,xxxxxx代表用户目录,

2.可以对该目录进行备份,一但环境出问题恢复此配置即可.

3.可以在%IDEA_HOME%/bin/idea.properties中修改该配置路径.

6.隐藏不想看到的文件或者文件夹(类似eclipse的filter功能)

intellij idea 隐藏不想看到的文件或者文件夹(类似eclipse的filter功能)

打开intellij –>:>File –>>Settings–>>搜索File Type

7.修改为Eclipse快捷键

File -> Settings -> Keymap => Keymaps改为 Eclipse copy

8.修改默认设置–default setting

修改默认设置–default setting

9.修改智能提示快捷键

1.File -> Settings -> Keymap -> Main menu -> Code -> Completion -> Basic=>修改为Ctrl+Alt+Enter

2.保存时把冲突的Remove掉。

3.File -> Settings -> Keymap -> Editor Actions -> Complete Current Statement=>修改为Ctrl+

10.查找快捷键冲突问题处理

1.File -> Settings -> Keymap -> Main menu -> Edit ->Find =>修改Find…和Replace…分别改为Ctrl+F 和Ctrl+R

11.显示行号

1.File -> Settings ->Editor ->General -> Appearance =>Show line numbers选中

12.代码智能提示,忽略大小写

File -> Settings -> Editor -> Code Completion里把Case sensitive completion设置为None就可以了

13.用*标识编辑过的文件

1.Editor–>General –> Editor Tabs

2.在IDEA中,你需要做以下设置, 这样被修改的文件会以*号标识出来,你可以及时保存相关的文件。

3.“Mark modifyied tabs with asterisk

14.关闭自动代码提示

1.Preferences => IDE Settings => Editor => Code Completion => Autopopup documentation in (ms)

15.常用快捷键

1.Ø Top #10切来切去:Ctrl+Tab

2.Ø Top #9选你所想【选中上下文相关联代码】:Ctrl+W

3.Ø Top #8代码生成:Template/Postfix +Tab

4.Ø Top #7发号施令:Ctrl+Shift+A

5.Ø Top #6无处藏身:Shift+Shift

6.Ø Top #5自动完成:Ctrl+Shift+Enter

7.Ø Top #4创造万物:Alt+Insert

使用前三名!

1.Ø Top #1智能补全:Ctrl+Shift+Space

2.Ø Top #1自我修复:Alt+Enter

3.Ø Top #1重构一切:Ctrl+Shift+Alt+T

其他辅助

1.以上这些神键配上一些辅助快捷键,即可让你的双手90%以上的时间摆脱鼠2标,专注于键盘仿佛在进行钢琴表演。这些不起眼却是至关重要的最后一块拼图有:

2.Ø 命令:Ctrl+Shift+A可以查找所有Intellij的命令,并且每个命令后面还有其快捷键。所以它不仅是一大神键,也是查找学习快捷键的工具。

3.Ø 新建:Alt+Insert可以新建类、方法等任何东西。

4.Ø 格式化代码:格式化import列表Ctrl+Alt+O,格式化代码Ctrl+Alt+L。

5.Ø 切换窗口:Alt+Num,常用的有1-项目结构,3-搜索结果,4/5-运行调试。Ctrl+Tab切换标签页,Ctrl+E/Ctrl+Shift+E打开最近打开过的或编辑过的文件。

6.Ø 单元测试:Ctrl+Alt+T创建单元测试用例。

7.Ø 运行:Alt+Shift+F10运行程序,Shift+F9启动调试,Ctrl+F2停止。

8.Ø 调试:F7/F8/F9分别对应Step into,Step over,Continue。

此外还有些我自定义的,例如水平分屏Ctrl+|等,和一些神奇的小功能9.Ctrl+Shift+V粘贴 很早以前拷贝过的,Alt+Shift+Insert(块选)进入到列模式进行按列选中

16.svn 不能同步代码问题修正

File -> Settings ->Subversion ->General => Use command line client 选中

1.使用command line方式需要指定svn.exe的路径,例如:D:\\tools\\TortoiseSVN\\bin\\svn.exe

2.注意,安装TortoiseSVN时路径中不要带空格,例如:C:\\Program Files\\TortoiseSVN\\bin\\svn.exe就会报错.

3.安装TortoiseSVN选择全部安装组件,否则可能没有svn.exe

17.设置idea的SVN忽略掉*.iml文件

1.Editor->File Types=>Ignore files and folders增加*.iml;

2.在lgnore files and folesrs中输入.idea;注意要\”;\”结尾。你就可以隐藏.idea文件夹

18.改变编辑文本字体大小

File -> settings -> EDITOR COLORS & FONTS -> FONT -> SIZ

19.IDEA编码设置

1.FILE -> SETTINGS -> FILE ENCODINGS => IDE ENCODING

2.FILE -> SETTINGS -> FILE ENCODINGS => Project Encoding

3.FILE -> SETTINGS -> FILE ENCODINGS => Default encoding for properties files

4.FILE -> SETTINGS -> FILE ENCODINGS => Transparent native-to-ascii conversion

20.Live Templates

System.out.println 快捷输出

“abc”.sout => System.out.println(\”abc\”);

在eclipse中使用方式为:sysout=> System.out.println();

for循环

List<String> list = new ArrayList<String>();

输入: list.for 即可输出

for(String s:list){}

《Intellij IDEA 撸码最头大的问题》这篇

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。