netty入门

闲的无聊想学习一下netty

写个netty入门吧

什么是netty

 

有了Netty,你可以实现自己的HTTP服务器,FTP服务器,UDP服务器,RPC服务器,WebSocket服务器,Redis的Proxy服务器,MySQL的Proxy服务器等等。

我们回顾一下传统的HTTP服务器的原理

1、创建一个ServerSocket,监听并绑定一个端口

2、一系列客户端来请求这个端口

3、服务器使用Accept,获得一个来自客户端的Socket连接对象

4、启动一个新线程处理连接

4.1、读Socket,得到字节流

4.2、解码协议,得到Http请求对象

4.3、处理Http请求,得到一个结果,封装成一个HttpResponse对象

4.4、编码协议,将结果序列化字节流 写Socket,将字节流发给客户端

5、继续循环步骤3

 

IO多路复用。它是由操作系统提供的系统调用,早期这个操作系统调用的名字是select,但是性能低下,后来渐渐演化成了Linux下的epoll和Mac里的kqueue。

 

客户端监听(Listen)时,Accept是阻塞的,只有新连接来了,Accept才会返回,主线程才能继

读写socket时,Read是阻塞的,只有请求消息来了,Read才能返回,子线程才能继续处理

读写socket时,Write是阻塞的,只有客户端把消息收了,Write才能返回,子线程才能继续读取下一个请求

传统的BIO模式下,从头到尾的所有线程都是阻塞的,这些线程就干等着,占用系统的资源,什么事也不干。

 

NIO是怎么做到非阻塞的呢。它用的是事件机制。它可以用一个线程把Accept,读写操作,请求处理的逻辑全干了。如果什么事都没得做,它也不会死循环,它会将线程休眠起来,直到下一个事件来了再继续干活,这样的一个线程称之为NIO线程。

 

官方那个给出的介绍是:

Netty是由JBOSS提供的一个java开源框架。

Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

  

然后我们简单理解一下,这玩意就是个程序,干什么的?netty是封装java socket noi的。

类似的功能是 apache的mina。

 

相对于Tomcat这种Web Server(顾名思义主要是提供Web协议相关的服务的),

Netty是一个Network Server,是处于Web Server更下层的网络框架,也就是说你可以使用Netty模仿Tomcat做一个提供HTTP服务的Web容器。

  

说白了,就是一个好使的处理Socket的东西。要是想了解详细点,可以去看看官方的介绍。

 

另外Netty框架是基于事件机制的,简单说,就是发生什么事,就找相关处理方法。就跟着火了找119,抢劫了找110一个道理。所以,这里,我们处理的是当客户端和服务端完成连接以后的这个事件。什么时候完成的连接,Netty知道,他告诉我了,我就负责处理。这就是框架的作用。Netty,提供的事件还有很多,以后会慢慢的接触和介绍。

 

 

Netty原理和使用

 

  Netty是一个高性能 事件驱动的异步的非堵塞的IO(NIO)框架,用于建立TCP等底层的连接,基于Netty可以建立高性能的Http服务器。支持HTTP、 WebSocket 、Protobuf、 Binary TCP |和UDP,Netty已经被很多高性能项目作为其Socket底层基础,如HornetQ Infinispan Vert.x 
Play Framework Finangle和 Cassandra。其竞争对手是:Apache MINA和 Grizzly。

   传统堵塞的IO读取如下:

InputStream is = new FileInputStream(“input.bin”); 
int byte = is.read(); // 当前线程等待结果到达直至错误

 

而使用NIO如下:

while (true) { 
 selector.select(); // 从多个通道请求事件 
 Iterator it = selector.selectedKeys().iterator(); 
 while (it.hasNext()) { 
  SelectorKey key = (SelectionKey) it.next(); 
  handleKey(key); 
  it.remove(); 
 } 

堵塞与非堵塞原理

  传统硬件的堵塞如下,从内存中读取数据,然后写到磁盘,而CPU一直等到磁盘写完成,磁盘的写操作是慢的,这段时间CPU被堵塞不能发挥效率。 

 使用非堵塞的DMA如下图:CPU只是发出写操作这样的指令,做一些初始化工作,DMA具体执行,从内存中读取数据,然后写到磁盘,当完成写后发出一个中断事件给CPU。这段时间CPU是空闲的,可以做别的事情。这个原理称为Zero.copy零拷贝。 

 

  Netty底层基于上述Java NIO的零拷贝原理实现: 

 

 

基础知识是nio

 

关于NIO基础的知识:https://my.oschina.net/andylucc/blog/614295

            http://www.cnblogs.com/dolphin0520/p/3919162.html 

          http://blog.csdn.net/wuxianglong/article/details/6604817  

netty 官方API: http://netty.io/4.1/api/index.html

netty 中文指南:https://waylau.com/netty-4-user-guide/   (来自个人)

 

Reactor线程模型

一个NIO线程+一个accept线程:

https://segmentfault.com/img/bVbj1ZH?w=550&h=211

使用原生JDK

API复杂

高可用的话:需要出路断连重连、半包读写、失败缓存等问题

JDK NIO的bug

空轮询的bug

 

 

 

netty版本:netty-5.0.0.Alpha2 http://files.cnblogs.com/files/applerosa/netty-5.0.0.Alpha2.7z

maven依赖:

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>5.0.0.Alpha2</version>
</dependency>

第一步:首先创建一个工程、引入包依赖。这都不会还是看看java入门吧

 

第二步

关于netty ,首先要搞清楚,这是建立在客户端和服务端之间的。

我们先说服务端,服务端建立相应的规则,然后运行起来,等待客户端访问或者发送”消息“。好了,我们先建立服务端代码:

代码后面再补上

 

bootStrap,通过添加hanlder,我们可以监听Channel的各种动作以及状态的改变,包括连接,绑定,接收消息等。

 

在基类AbstractBootstrap有handler方法,目的是添加一个handler,监听Bootstrap的动作,客户端的Bootstrap中,继承了这一点。

在服务端的ServerBootstrap中增加了一个方法childHandler,它的目的是添加handler,用来监听已经连接的客户端的Channel的动作和状态。

handler在初始化时就会执行,而childHandler会在客户端成功connect后才执行,这是两者的区别。

pipeline是伴随Channel的存在而存在的,交互信息通过它进行传递

 

们通常要绑定一个handler(Netty:Bootstrap的handler和childHandler)进行通道的监听,当收到数据时就会触发某个事件,从而进行进一步的处理。

目前我们用的比较多的就是ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter。

ChannelInboundHandlerAdapter,看名字中的 IN,就是进入的意思,一般就是事件(event),比如当有数据到来时,channel被激活时或者不可用时,下面介绍几个最常用的。

 

 

 

 

channelActive

通道激活时触发,当客户端connect成功后,服务端就会接收到这个事件,从而可以把客户端的Channel记录下来,供后面复用

 

 

channelRead

这个必须用啊,当收到对方发来的数据后,就会触发,参数msg就是发来的信息,可以是基础类型,也可以是序列化的复杂对象。

 

 

 

channelReadComplete

channelRead执行后触发

 

exceptionCaught

出错是会触发,做一些错误处理

 

 

 

 

ChannelOutboundHandlerAdapter,看到了out,表示出去的动作,监听自己的IO操作,比如connect,bind等,在重写这个Adapter的方法时,记得执行super.xxxx,否则动作无法执行。

 

bind

服务端执行bind时,会进入到这里,我们可以在bind前及bind后做一些操作

 

connect

客户端执行connect连接服务端时进入

 

 

 

 

Group:群组,Loop:循环,Event:事件,这几个东西联在一起,相比大家也大概明白它的用途了。

 

Netty内部都是通过线程在处理各种数据,EventLoopGroup就是用来管理调度他们的,注册Channel,管理他们的生命周期,下面就来看看EventLoopGroup是怎样工作的。

 

Netty框架初探,当我们启动客户端或者服务端时,都要声明一个Group对象

 

  1. EventLoopGroup group = new NioEventLoopGroup();    

 

 

 

  1. NioEventLoopGroup extends MultithreadEventLoopGroup extends MultithreadEventExecutorGroup
构造函数
  1. public NioEventLoopGroup() {  
  2.     this(0);  
  3. }  
  4. //他会连续调用内部的构造函数,直到用下面的构造去执行父类的构造  
  5. //nThreads此时为0,马上就会提到这个参数的用处  
  6. public NioEventLoopGroup(  
  7.         int nThreads, Executor executor, final SelectorProvider selectorProvider) {  
  8.     super(nThreads, executor, selectorProvider);  
  9. }  
 
  1.  
基类MultithreadEventLoopGroup的构造
  1. //这里会根据nThreads创建执行者数组  
  2. private final EventExecutor[] children;  
  3.   
  4. protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {  
  5.     if (nThreads <= 0) {  
  6.         throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));  
  7.     }  
  8.   
  9.     if (executor == null) {  
  10.         executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());  
  11.     }  
  12. //这里创建EventExecutor数组对象  
  13.     children = new EventExecutor[nThreads];  
  14.     if (isPowerOfTwo(children.length)) {  
  15.         chooser = new PowerOfTwoEventExecutorChooser();  
  16.     } else {  
  17.         chooser = new GenericEventExecutorChooser();  
  18.     }  
  19. //此处循环children数组,来创建内部的NioEventLoop对象  
  20.     for (int i = 0; i < nThreads; i ++) {  
  21.         boolean success = false;  
  22.         try {  
  23.             //newChild是abstract方法,运行期会执行具体的实例对象的重载  
  24.             children[i] = newChild(executor, args);  
  25.             success = true;  
  26.         } catch (Exception e) {  
  27.             // TODO: Think about if this is a good exception type  
  28.             throw new IllegalStateException("failed to create a child event loop", e);  
  29.         } finally {  
  30.             //如果没有成功,做关闭处理  
  31.             if (!success) {  
  32.                 for (int j = 0; j < i; j ++) {  
  33.                     children[j].shutdownGracefully();  
  34.                 }  
  35.   
  36.                 for (int j = 0; j < i; j ++) {  
  37.                     EventExecutor e = children[j];  
  38.                     try {  
  39.                         while (!e.isTerminated()) {  
  40.                             e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);  
  41.                         }  
  42.                     } catch (InterruptedException interrupted) {  
  43.                         // Let the caller handle the interruption.  
  44.                         Thread.currentThread().interrupt();  
  45.                         break;  
  46.                     }  
  47.                 }  
  48.             }  
  49.         }  
  50.     }  
  51.   
  52.     ......  
  53. }  
 
  1. NioEventLoop extends SingleThreadEventLoop extends SingleThreadEventExecutor  
NioEventLoop通过自己的构造行数,一直调用到SingleThreadEventExecutor的构造
 
至此,Group和内部的Loop对象以及Executor就创建完毕,那么他们是什么时候被调用的呢?就是在服务端bind和客户端connect时。

 

服务端的bind方法执行的是AbstractBootstrap的bind方法,bind方法中先会调用validate()方法检查是否有group

所以,假如初始时,没有设置 Bootstrap的group的话,就会报错。

 

最终bind调用doBind,然后调用doBind0,启动一个Runnable线程

我们这里,channel.eventLoop()得到的是NioEventLoop对象,所以执行NioEventLoop的run方法,开始线程运行。

 

我们在创建Bootstrap初期,会调用它的group方法,绑定一个group,这样,一个循环就开始运行了。

 

 

后面在写channel    channelpipeline   options  configs

 

 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值