Netty in action study
2015-11-05 14:01
288 查看
第一章 Netty介绍
Netty提供了简单易用的api从网络处理代码中解耦业务逻辑David John Wheeler, 计算机科学中的所有问题都可以通过间接的方法解决。计算机科学中的任何问题都可以通过加上一层逻辑层来解决
1.2.1 Callback (回调)
public interface Fetcher { void fetchData(FetcherCallback callback); } public interface FetcherCallback { void onData(Data data) throws Exception; void onError(Throwable cause); }
1.2.2 Futures (将来。。。要发生的)
Furures是一个抽象概念,它表示一个值,该值可能在某个点变得可用。1.4.2 扩展的ByteBuffer
ByteBuffer允许包装一个byte[]来获得一个实例。如果希望减少内存拷贝,这种方式是非常有利的。第二章 第一个Netty程序
2.3 编写一个应答服务器
端口,线程模式,事件循环,业务逻辑,通道类型port, thread pool, envent loop,
(leo:异步多是消息驱动,所以有事件循环)
2.3.1 启动服务器
ServerBootstrapNioEventLoopGroup
语法 bootstrap( event loop group )( nio server socket channel )( localaddress & port )( channel initializer( channel handler ))
2.3.2 实现服务器业务逻辑
Netty使用多个Channel Handler来达到对事件处理的分离Handler所有方法中,只有channel read方法是必须要重写的。
2.3.3 捕获异常
2.4 编写应答程序客户端
语法 bootstrap( event loop group )( nio server socket channel )(remote address & port )( channel initializer( channel handler ))
ServerBootstrap
NioEventLoopGroup 可以理解为线程池,用来处理连接,接收数据,发送数据
疑问:为何 server & client 例子中,部分步骤调用了 sync()
可能你会问为什么在这里使用的是SimpleChannelInboundHandler而不使用ChannelInboundHandlerAdapter?
主要原因是ChannelInboundHandlerAdapter在处理完消息后需要负责释放资源。在这里将调用ByteBuf.release()来释放资源。
SimpleChannelInboundHandler会在完成channelRead0后释放消息,这是通过Netty处理所有消息的ChannelHandler实现了ReferenceCounted接口达到的
为什么在服务器中不使用SimpleChannelInboundHandler呢?因为服务器要返回相同的消息给客户端,
在服务器执行完成写操作之前不能释放调用读取到的消息,因为写操作是异步的,一旦写操作完成,Netty中会自动释放消息
第三章 Netty核心概念
十个核心类Bootstrap or ServerBootstrap
EventLoop
EventLoopGroup
ChannelPipeline
Channel
Future or ChannelFuture
ChannelInitializer
ChannelHandler
3.1 Netty Crash Course
一个Netty程序开始于Bootstrap类,Bootstrap类是Netty提供的一个可以通过简单配置来设置或"引导"程序的一个很重要的类。Netty中设计了Handlers来处理特定的"event"和设置Netty中的事件,从而来处理多个协议和数据。事件可以描述成一个非常通用的方法,因为你可以自定义一个handler,用来将Object转成byte[]或将byte[]转成Object;也可以定义个handler处理抛出的异常。(leo:网络中传输的都是字节,不要忘了网络字节序)
你会经常编写一个实现ChannelInboundHandler的类,ChannelInboundHandler是用来接收消息,当有消息过来时,你可以决定如何处理。当程序需要返回消息时可以在ChannelInboundHandler里write/flush数据。可以认为应用程序的业务逻辑都是在ChannelInboundHandler中来处理的,业务罗的生命周期在ChannelInboundHandler中。
Netty连接客户端端或绑定服务器需要知道如何发送或接收消息,这是通过不同类型的handlers来做的,多个Handlers是怎么配置的?Netty提供了ChannelInitializer类用来配置Handlers。ChannelInitializer是通过ChannelPipeline来添加ChannelHandler的,如发送和接收消息,这些Handlers将确定发的是什么消息。ChannelInitializer自身也是一个ChannelHandler,在添加完其他的handlers之后会自动从ChannelPipeline中删除自己。
所有的Netty程序都是基于ChannelPipeline。ChannelPipeline和EventLoop和EventLoopGroup密切相关,因为它们三个都和事件处理相关,所以这就是为什么它们处理IO的工作由EventLoop管理的原因。
Netty中所有的IO操作都是异步执行的,例如你连接一个主机默认是异步完成的;写入/发送消息也是同样是异步。也就是说操作不会直接执行,而是会等一会执行,因为你不知道返回的操作结果是成功还是失败,但是需要有检查是否成功的方法或者是注册监听来通知;Netty使用Futures和ChannelFutures来达到这种目的。Future注册一个监听,当操作成功或失败时会通知。ChannelFuture封装的是一个操作的相关信息,操作被执行时会立刻返回ChannelFuture。
3.2 Channels, Events and Input/Output(IO)
Netty中的EventLoopGroup包含一个或多个EventLoop,而EventLoop就是一个Channel执行实际工作的线程。EventLoop总是绑定一个单一的线程,在其生命周期内不会改变leo:channel会被线程run()调用
3.3 什么是Bootstrap?为什么使用它?
“引导”是Netty中配置程序的过程Bootstrap和ServerBootstrap之间的差异:
Bootstrap用来连接远程主机,有1个EventLoopGroup
ServerBootstrap用来绑定本地端口,有2个EventLoopGroup
leo:Bootstrap用一个EventLoopGroup处理连接和处理业务。ServerBootstrap用一个EventLoopGroup处理连接,用一个EventLoopGroup处理业务。
3.4 Channel Handlers and Data Flow(通道处理和数据流)
Handers依赖于ChannelPipeline来决定他们执行的顺序ChannelHandler是一段执行业务逻辑处理数据的代码,他们来来往往的通过ChannelPipeline,ChannelPipeline可以理解为用来管理ChannelHandler的一个容器。
ChannelHandler会在程序引导阶段被添加到ChannelPipeline中,并且被添加的顺序将决定处理数据的顺序。
一个事件能传递到下一个ChannelInBoundHandler或者上一个ChannelOutBoundHandler, 在ChannelPipeline中通过使用ChannelHandlerContext调用每一个方法。
Netty提供了抽象的事件基类称为ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter,每个都提供了在ChannelPipeline中通过调用相应的方法将事件传递给下一个Handler的方法的实现。
Netty中发送消息有两种方法:直接写入通道( leo: Channel ???) 或写入ChannelHandlerContext对象。区别如下:
直接写入通道导致处理消息从ChannelPipeline的尾部开始
写入ChannelHandlerContext对象导致处理消息从ChannelPipeline的下一个handler开始
3.5 编码器·解码器·和业务逻辑:细看Handlers
有不同类型的handlers,每个handler都依赖于他们的基类。Netty提供了一系列的“Adapter”类。每个handler负责转发事件消息到ChannelPipeline的下一个handler。(leo: 那handler没有调用相应方法,那下一个、上一个不就不会处理了?)在Adapter类及子类是自动完成,我们只需要在感兴趣的Adapter中重写方法。
有几个Adapter允许自定义ChannelHandler,一般自定义ChannelHandler需要继承 编码/解码Adapter类中一个:
ChannelHandlerAdapter
ChannelInboundHandlerAdapter
ChannelOutboundHandlerAdapter
3.5.1 Encoders, Decoders
leo:Encoder,Decoder也是ChannelHandler (符合OO 抽象---> 统一)3.5.2 业务逻辑(Domain Logic)
第四章 Transport
网络应用程序都以字节码传输。leo:channel的概念是java.no引入的 java.nio.channel, 其实就是包装了建立了连接的socket
4.2 Transport API
传输API的核心是channel接口,它用于所有出站的操作。每个Channel都会分配一个ChannelPipeline和ChannelConfig
ChannelPipeline实现了拦截过滤器模式
Channel提供了很多方法,如下列表:
eventLoop(),返回分配给Channel的EventLoop
pipeline(),返回分配给Channel的ChannelPipeline
isActive(),返回Channel是否激活,已激活说明与远程连接对等
localAddress(),返回已绑定的本地SocketAddress
remoteAddress(),返回已绑定的远程SocketAddress
write(),写数据到远程客户端,数据通过ChannelPipeline传输过去
Channel channel = ... //Create ByteBuf that holds data to write ByteBuf buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8); //Write data ChannelFuture cf = channel.write(buf); //Add ChannelFutureListener to get notified after write completes cf.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { //Write operation completes without error if (future.isSuccess()) { System.out.println(.Write successful.); } else { //Write operation completed but because of error System.err.println(.Write error.); future.cause().printStacktrace();
Channel是线程安全(thread-safe)的,它可以被多个不同的线程安全的操作,在多线程环境下,所有的方法都是安全的。正因为Channel是安全的,我们存储对Channel的引用,并在学习的时候使用它写入数据到远程已连接的客户端,使用多线程也是如此。下面的代码是一个简单的多线程例子
final Channel channel = ... //Create ByteBuf that holds data to write final ByteBuf buf = Unpooled.copiedBuffer("your data",CharsetUtil.UTF_8); //Create Runnable which writes data to channel Runnable writer = new Runnable() { @Override public void run() { channel.write(buf.duplicate()); } }; //Obtain reference to the Executor which uses threads to execute tasks Executor executor = Executors.newChachedThreadPool(); // write in one thread //Hand over write task to executor for execution in thread executor.execute(writer); // write in another thread //Hand over another write task to executor for execution in thread executor.execute(writer);
4.3 Netty包含的传输实现
Netty自带了一些传输协议的实现,虽然没有支持所有的传输协议,但是其自带的已足够我们来使用。
Netty应用程序的传输协议依赖于底层协议,本节我们将学习Netty中的传输协议。Netty中的传输方式有如下几种:
NIO,io.netty.channel.socket.nio,基于java.nio.channels的工具包,使用选择器作为基础的方法。
OIO,io.netty.channel.socket.oio,基于java.net的工具包,使用阻塞流。
Local,io.netty.channel.local,用来在虚拟机之间本地通信。
Embedded,io.netty.channel.embedded,嵌入传输,它允许在没有真正网络的运输中使用ChannelHandler,可以非常有用的来测试ChannelHandler的实现。
4.3.1 NIO - Nonblocking I/O
NIO传输是目前最常用的方式,它通过使用选择器提供了完全异步的方式操作所有的I/O,NIO从Java1.4才被提供。NIO中,我们可以注册一个通道或获得某个通道的改变的状态,通道状态有下面几种改变:
一个新的Channel被接受并已准备好
Channel连接完成
Channel中有数据并已准备好读取
Channel发送数据出去
处理完改变的状态后需重新设置他们的状态,用一个线程来检查是否有已准备好的Channel,如果有则执行相关事件。
在这里可能只同时一个注册的事件而忽略其他的。选择器所支持的操作在SelectionKey中定义,具体如下:
OP_ACCEPT,有新连接时得到通知
OP_CONNECT,连接完成后得到通知
OP_READ,准备好读取数据时得到通知
OP_W RITE,写入数据到通道时得到通知
Netty中的NIO传输就是基于这样的模型来接收和发送数据
Netty中的NIO传输是“zero-file-copy”,也就是零文件复制,这种机制可以让程序速度更快,更高效的从文件系统中传输内容,零复制就是我们的应用程序不会将发送的数据先复制到JVM堆栈在进行处理,而是直接从内核空间操作。
4.3.3 Local - In VM transport
4.3.4 Embedded transport
一般用来测试特定的ChannelHandler的实现4.4每种传输方式在什么时候使用?
不多加赘述,看下面列表:OIO,在低连接数、需要低延迟时、阻塞时使用
NIO,在高连接数时使用
Local,在同一个JVM内通信时使用
Embedded,测试ChannelHandler时使用
第五章 Buffers
第六章 ChannelHandler
Netty允许用户自定义ChannelHandler的实现来处理数据。使得ChannelHandler更强大的是可以连接每个ChannelHandler来实现任务,这有助于代码的整洁和重用6.1 ChannelPipeline
6.4.2 ChannelInboundHandler
自定义消息类型来解码字节,可以实现ChannelInboundHandler或ChannelInboundHandlerAdapter。有一个更好的解决方法,使用编解码器的框架可以很容的实现。使用ChannelInboundHandler、ChannelInboundHandlerAdapter、SimpleChannelInboundhandler这三个中的一个来处理接收消息,使用哪一个取决于需求;
大多数时候使用SimpleChannelInboundHandler处理消息,使用ChannelInboundHandlerAdapter处理其他的“入站”事件或状态改变。(leo:???)
ChannelInitializer用来初始化ChannelHandler,将自定义的各种ChannelHandler添加到ChannelPipeline中。
6.4.3 ChannelOutboundHandler
相关文章推荐
- Netty使用Http上传文件
- tomcat、netty以及nodejs的helloworld性能对比 3ff8
- flatbuffers 和netty的结合使用
- netty 处理远程主机强制关闭一个连接
- Netty 源码分析(三):服务器端的初始化和注册过程
- 轻量级分布式 RPC 框架
- spark总体概况
- Netty系列之Netty百万级推送服务设计要点
- Netty初步
- Netty ChannelBuffer 简介
- netty4研究系列-序
- netty io.netty.channel 简介1
- spark overview
- Netty4和Netty5内存池的使用心得
- Netty与Reactor模式
- Netty源码分析之DelimiterBasedFrameDecoder
- Netty4学习笔记-001
- Netty4学习笔记-002
- Netty -- 内存管理
- Netty -- Background