- 浏览: 440122 次
- 性别:
- 来自: 成都
文章分类
最新评论
-
sunwang810812:
万分感谢中!!!!!这么多年终于看到一个可运行可解决的方案!! ...
POI 后台生成Excel,在前台显示进度 -
zzb7728317:
LZ正解
Spring Jackson AjaxFileUpload 没有执行回调函数的解决办法 -
sleeper_qp:
lz是在源码上修改的么? 源码的话你重新编译一遍了么? 可 ...
由nutch readseg -dump 中文编码乱码想到的…… -
shenjian430:
请问你改好的程序在写在哪了?
由nutch readseg -dump 中文编码乱码想到的…… -
yinxusen:
It seems to be the bug occur in ...
Mahout Local模式 执行example的注意点
转载--http://www.fengfly.com/plus/view-191060-1.html
Netty
提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序[官方定义]
,整体来看其包含了以下内容:1.提供了丰富的协议编解码支持,2.实现自有的buffer系统,减少复制所带来的消耗,3.整套channel的实现,4.基于事件的过程流转以及完整的网络事件响应与扩展,5.丰富的example。本文并不对Netty实际使用中可能出现的问题做分析,只是从代码角度分析它的架构以及实现上的一些关键细节。
首先来看下最如何使用Netty(其自带example很好展示了使用),Netty普通使用一般是通过BootStrap来启动,BootStrap主要分为两类:1.面向连接(TCP)的BootStrap(ClientBootStrap和ServerBootstrap),2.非面向连接(UDP)的(ConnectionlessBootstrap)。
Netty
整体架构很清晰的分成2个部分,ChannelFactory和ChannelPipelineFactory,前者主要生产网络通信相关的Channel实例和ChannelSink实例,Netty提供的ChannelFactory实现基本能够满足绝大部分用户的需求,当然你也可以定制自己的ChannelFactory,后者主要关注于具体传输数据的处理,同时也包括其他方面的内容,比如异常处理等等,只要是你希望的,你都可以往里添加相应的handler,一般ChannelPipelineFactory由用户自己实现,因为传输数据的处理及其他操作和业务关联比较紧密,需要自定义处理的handler。
现在,使用Netty的步骤实际上已经非常明确了,比如面向连接的Netty服务端客户端使用,第一步:实例化一个BootStrap,并且通过构造方法指定一个ChannelFactory实现,第二步:向bootstrap实例注册一个自己实现的ChannelPipelineFactory,第三步:如果是服务器端,bootstrap.bind(new InetSocketAddress(port)),然后等待客户端来连接,如果是客户端,bootstrap.connect(new InetSocketAddress(host,port))取得一个future,这个时候Netty会去连接远程主机,在连接完成后,会发起类型为CONNECTED的ChannelStateEvent,并且开始在你自定义的Pipeline里面流转,如果你注册的handler有这个事件的响应方法的话那么就会调用到这个方法。在此之后就是数据的传输了。下面是一个简单客户端的代码解读。
网络动作归结到最简单就是服务器端bind->accept->read->write,客户端connect->read->write,一般bind或者connect后会有多次read、write。这种特性导致,bind,accept与read,write的线程分离,connect与read、write线程分离,这样做的好处就是无论是服务器端还是客户端吞吐量将有效增大,以便充分利用机器的处理能力,而不是卡在网络连接上,不过一旦机器处理能力充分利用后,这种方式反而可能会因为过于频繁的线程切换导致性能损失而得不偿失,并且这种处理模型复杂度比较高。
采用什么样的网络事件响应处理机制对于网络吞吐量是非常重要的,Netty采用的是标准的SEDA(Staged Event-Driven Architecture)架构[http://en.wikipedia.org/wiki/ Staged_event-driven_architecture],其所设计的事件类型,代表了网络交互的各个阶段,并且在每个阶段发生时,触发相应事件交给初始化时生成的pipeline实例进行处理。事件处理都是通过Channels类的静态方法调用开始的,将事件、channel传递给channel持有的Pipeline进行处理,Channels类几乎所有方法都为静态,提供一种Proxy的效果(整个工程里无论何时何地都可以调用其静态方法触发固定的事件流转,但其本身并不关注具体的处理流程)。
Channels
部分事件流转静态方法
1.
fireChannelOpen
2.
fireChannelBound
3.
fireChannelConnected
4.
fireMessageReceived
5.
fireWriteComplete
6.
fireChannelInterestChanged
7.
fireChannelDisconnected
8.
fireChannelUnbound
9.
fireChannelClosed
10.
fireExceptionCaught
11.
fireChildChannelStateChanged
Netty
提供了全面而又丰富的网络事件类型,其将java中的网络事件分为了两种类型Upstream和Downstream。一般来说,Upstream类型的事件主要是由网络底层反馈给Netty的,比如messageReceived,channelConnected等事件,而Downstream类型的事件是由框架自己发起的,比如bind,write,connect,close等事件。
Netty的
Upstream和
Downstream网络事件类型特性也使一个
Handler分为了
3种类型,专门处理
Upstream,专门处理
Downstream,同时处理
Upstream,Downstream。实现方式是某个具体
Handler通过继承
ChannelUpstreamHandler和
ChannelDownstreamHandler类来进行区分。
PipeLine在
Downstream或者
Upstream类型的网络事件发生时,会调用匹配事件类型的
Handler响应这种调用。
ChannelPipeline维持有所有
handler有序链表,并且由
handler自身控制是否继续流转到下一个
handler(ctx.sendDownstream(e),
这样设计有个好处就是随时终止流转,业务目的达到无需继续流转到下一个
handler)。下面的代码是取得下一个处理
Downstream事件的处理器。
// 实例化一个客户端Bootstrap实例,其中NioClientSocketChannelFactory实例由Netty提供 ClientBootstrap bootstrap = new ClientBootstrap( new NioClientSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // 设置PipelineFactory,由客户端自己实现 bootstrap.setPipelineFactory(new FactorialClientPipelineFactory(count)); //向目标地址发起一个连接 ChannelFuture connectFuture = bootstrap.connect(new InetSocketAddress(host, port)); // 等待链接成功,成功后发起的connected事件将会使handler开始发送信息并且等待messageRecive,当然这只是示例。 Channel channel = connectFuture.awaitUninterruptibly().getChannel(); // 得到用户自定义的handler FactorialClientHandler handler = (FactorialClientHandler) channel.getPipeline().getLast(); // 从handler里面取数据并且打印,这里需要注意的是,handler.getFactorial使用了从结果队列result take数据的阻塞方法,而结果队列会在messageRecieve事件发生时被填充接收回来的数据 System.err.format( "Factorial of %,d is: %,d", count, handler.getFactorial());
Netty提供了NIO与BIO(OIO)两种模式处理这些逻辑,其中NIO主要通过一个BOSS线程处理等待链接的接入,若干个WORKER线程(从worker线程池中挑选一个赋给Channel实例,因为Channel实例持有真正的java网络对象)接过BOSS线程递交过来的CHANNEL进行数据读写并且触发相应事件传递给pipeline进行数据处理,而BIO(OIO)方式服务器端虽然还是通过一个BOSS线程来处理等待链接的接入,但是客户端是由主线程直接connect,另外写数据C/S两端都是直接主线程写,而数据读操作是通过一个WORKER 线程BLOCK方式读取(一直等待,直到读到数据,除非channel关闭)。
DefaultChannelHandlerContext realCtx = ctx; while (!realCtx.canHandleUpstream()) { realCtx = realCtx.next; if (realCtx == null) { return null; } } return realCtx;
如果是一个网络会话最末端的事件,比如 messageRecieve ,那么可能在某个 handler 里面就直接结束整个会话,并把数据交给上层应用,但是如果是网络会话的中途事件,比如 connect 事件,那么当触发 connect 事件时,经过 pipeline 流转,最终会到达挂载 pipeline 最底下的 ChannelSink 实例中,这类实例主要作用就是发送请求和接收请求,以及数据的读写操作。
NIO方式
ChannelSink
一般会有
1
个
BOSS
实例
(implements Runnable)
,以及若干个
worker
实例(不设置默认为
cpu cores*2
个
worker
),这在前面已经提起过,
BOSS
线程在客户端类型的
ChannelSink
和服务器端类型的
ChannelSink
触发条件不一样,客户端类型的
BOSS
线程是在发生
connect
事件时启动,主要监听
connect
是否成功,如果成功,将启动一个
worker
线程
,
将
connected
的
channel
交给这个线程继续下面的工作,而服务器端的
BOSS
线程是发生在
bind
事件时启动,它的工作也相对比较简单,对于
channel.socket().accept()
进来的请求向
Nioworker
进行工作分配即可。这里需要提到的是,Server端ChannelSink实现比较特别,无论是NioServerSocketPipelineSink还是OioServerSocketPipelineSink的eventSunk方法实现都将channel分为ServerSocketChannel和SocketChannel分开处理。这主要原因是Boss线程accept()一个新的连接生成一个SocketChannel交给Worker进行数据接收。
Netty提供了大量的
handler来处理网络数据,但是大部分是
CODEC相关的,以便支持多种协议。
Netty实现封装实现了自己的一套ByteBuffer系统,这个
ByteBuffer系统对外统一的接口就是
ChannelBuffer,这个接口从整体上来说定义了两类方法,一种是类似
getXXX(int index…),
setXXX(int index…)需要指定开始操作
buffer的起始位置,简单点来说就是直接操作底层
buffer,并不用到
Netty特有的高可重用性
buffer特性,所以
Netty内部对于这类方法调用非常少,另外一种是类似
readXXX(),writeXXX()不需要指定位置的
buffer操作,这类方法实现放在了
AbstractChannelBuffer,其主要的特性就是维持
buffer的位置信息,包括
readerIndex,writerIndex,以及回溯作用的
markedReaderIndex和
markedWriterIndex,当用户调用
readXXX()或者
writeXXX()方法时,
AbstractChannelBuffer会根据维护的
readerIndex,writerIndex计算出读取位置,然后调用继承自己的
ChannelBuffer的
getXXX(int index…)或者
setXXX(int index…)方法返回结果,这类方法在
Netty内部被大量调用,因为这个特性最大的好处就是很方便地重用
buffer而不必去费心费力维护
index或者新建大量的
ByteBuffer。
另外
WrappedChannelBuffer接口提供的是对
ChannelBuffer的代理,他的用途说白了就是重用底层
buffer,但是会转换一些
buffer的角色,比如原本是读写皆可
,
wrap成
ReadOnlyChannelBuffer,那么整个
buffer只能使用
readXXX()或者
getXXX()方法,也就是只读,然后底层的
buffer还是原来那个
,再如一个已经进行过读写的
ChannelBuffer被
wrap成
TruncatedChannelBuffer,那么新的
buffer将会忽略掉被
wrap的
buffer内数据,并且可以指定新的
writeIndex,相当于
slice功能。
Netty 实现了自己的一套完整 Channel 系统,这个 channel 说实在也是对 java 网络做了一层封装,加上了 SEDA 特性 ( 基于事件响应,异步,多线程等 ) 。其最终的网络通信还是依靠底下的 java 网络 api 。提到异步,不得不提到 Netty 的 Future 系统,从 channel 的定义来说, write,bind,connect,disconnect,unbind,close, 甚至包括 setInterestOps 等方法都会返回一个 channelFuture ,这这些方法调用都会触发相关网络事件,并且在 pipeline 中流转。 Channel 很多方法调用基本上不会马上就执行到最底层,而是触发事件,在 pipeline 中走一圈,最后才在 channelsink 中执行相关操作,如果涉及网络操作,那么最终调用会回到 Channel 中,也就是 serversocketchannel,socketchannel,serversocket,socket 等 java 原生网络 api 的调用,而这些实例就是 jboss 实现的 channel 所持有的 ( 部分 channel) 。
public void eventSunk( ChannelPipeline pipeline, ChannelEvent e) throws Exception { Channel channel = e.getChannel(); if (channel instanceof NioServerSocketChannel) { handleServerSocket(e); } else if (channel instanceof NioSocketChannel) { handleAcceptedSocket(e); } }
NioWorker worker = nextWorker(); worker.register(new NioAcceptedSocketChannel( channel.getFactory(), pipeline, channel, NioServerSocketPipelineSink.this, acceptedSocket, worker, currentThread), null);
另外两者实例化时都会走一遍如下流程:
setConnected(); fireChannelOpen(this); fireChannelBound(this, getLocalAddress()); fireChannelConnected(this, getRemoteAddress());
而对应的ChannelSink里面的处理代码就不同于ServerSocketChannel了,因为走的是handleAcceptedSocket(e)这一块代码,从默认实现代码来说,实例化调用fireChannelOpen(this);fireChannelBound(this,getLocalAddress());fireChannelConnected(this,getRemoteAddress())没有什么意义,但是对于自己实现的ChannelSink有着特殊意义。具体的用途我没去了解,但是可以让用户插手Server accept连接到准备读写数据这一个过程的处理。
switch (state) { case OPEN: if (Boolean.FALSE.equals(value)) { channel.worker.close(channel, future); } break; case BOUND: case CONNECTED: if (value == null) { channel.worker.close(channel, future); } break; case INTEREST_OPS: channel.worker.setInterestOps(channel, future, ((Integer) value).intValue()); break; }
Netty新版本出现了一个特性
zero-copy,这个机制可以使文件内容直接传输到相应
channel上而不需要通过
cpu参与,也就少了一次内存复制。
Netty内部
ChunkedFile
和
FileRegion
构成了
non zero-copy
和
zero-copy两种形式的文件内容传输机制,前者需要
CPU参与,后者根据操作系统是否支持
zero-copy将文件数据传输到特定
channel,如果操作系统支持,不需要
cpu参与,从而少了一次内存复制。
ChunkedFile主要使用
file的
read,readFully等
API,而
FileRegion使用
FileChannel的
transferTo API,
2者实现并不复杂。
Zero-copy的特性还是得看操作系统的,本身代码没有很大的特别之处。
最后总结下,Netty的架构思想和细节可以说让人眼前一亮,对于java网络IO的各个注意点,可以说Netty已经解决得比较完全了,同时Netty的作者也是另外一个NIO框架MINA的作者,在实际使用中积累了丰富的经验,但是本文也只是一个新手对于Netty的初步理解,还没有足够的能力指出某一细节的所发挥的作用。
发表评论
-
hibernate的Criteria Query(转)
2014-06-29 21:22 712当查询数据时,人们往往需要设置查询条件。在SQL或HQL语句 ... -
【转】hibernate 中 Criteria 的使用介绍
2014-06-29 21:21 964转自:http://www.blogjava.net/jjs ... -
【转】Hibernate中Criteria的完整用法
2014-06-29 21:20 720转自:http://www.cnblogs.com/maba ... -
Java读写Oracle中的BLOB和CLOB
2013-10-16 18:00 2006转:http://www.iteye.com/topic/6 ... -
Spring的事务管理入门:编程式事务管理(TransactionTemplate)
2013-10-15 16:20 1026可有效应用于Oracle 临时表的insert和select ... -
jdbcTemplate 调用存储过程和回到函数
2013-10-15 09:09 3763转:http://blog.csdn.net/dancelo ... -
Spring3核心技术之JdbcTemplate
2013-10-15 09:03 1323转:http://zhou137520.iteye.com/ ... -
验证码在WebLogic下报错java.io.IOException: response already committed
2013-05-06 14:16 2601我的登录验证码代码,一个jsp,在Tomcat下没有问题,但 ... -
Struts2在eclipse的console中总是出现WARN OgnlValueStack:45 - Could not find property
2013-04-17 18:00 1723struts2 在发开时,控制台如输出大量警告信息,太烦人 ... -
【转】Eclipse自动关闭解决方法及eclipse.ini设置
2013-03-29 15:21 3806转自:http://liujun5563.blog.163. ... -
[转]批量将Java源代码文件的编码从GBK转为UTF-8
2012-11-12 13:43 1502转自:http://my.oschina.net/binny/ ... -
对代理模式与Java动态代理类的理解
2012-09-25 01:51 01. 代理模式 代理模式的作用是:为其他对象提供一种 ... -
【转】 java中yield(),sleep()以及wait()的区别
2012-09-13 22:49 847从操作系统的角度讲,os会维护一个ready queue(就绪 ... -
java 继承类 变量、静态变量、构造函数执行顺序
2012-09-10 16:07 1048包含普通变量、静态变量、构造函数、继承类的执行顺序为: 1、父 ... -
京东2012春笔试编程题
2012-09-10 12:58 01、求给定数组中最大的K个数function array[] ... -
Java实现Stack、Queue、BinaryTree
2012-09-11 10:28 14701、用数组实现Stack: public class MySt ... -
Java中a++和++a的分析
2012-09-18 08:34 2866本人从编译后的代码来看a++和++a的区别: 先看代码1: ... -
华为2012校园上机编程题
2012-09-09 16:04 0/** * 1. 手机号码合法 ... -
2011年9月7日,华为上机题Java实现
2012-09-08 15:58 0第二题: /** * 输入字符串长度len,字符串str, ... -
Java对于Cookie的操作详解
2012-09-18 08:34 8161.设置Cookie Cookie cookie = n ...
相关推荐
一个netty的入门教程以及源码分析视频,适合刚学习的人
#### 五、Netty源码分析实战案例 1. **ChannelHandlerContext与ChannelHandlerAdaptor详解**: - 分析`ChannelHandlerContext`的生命周期及其与`ChannelHandler`之间的交互方式。 - 深入理解`...
根据提供的文件信息,这里将对Netty源码的相关知识点进行详细解析。Netty是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器与客户端。Netty因其优秀的性能和灵活的设计而受到...
《Netty源码深入分析》是由美团基础架构部的闪电侠老师所分享的一系列关于Netty源码解析的视频教程。以下将根据标题、描述、标签以及部分内容等信息,对Netty及其源码进行深入剖析。 ### Netty简介 Netty是基于...
一、Netty 源码解析 Netty 的源码是理解其工作原理的关键。通过阅读源码,我们可以了解到 Netty 如何实现高效的网络通信,例如它的非阻塞 I/O 模型、事件驱动架构、内存池管理以及编码解码器等核心组件。其中,...
以上仅是Netty源码分析的一些关键点,实际的学习中还需要结合具体代码和实际案例来深入理解。在分析源码的过程中,我们通常会关注类的设计模式、线程模型、内存管理以及性能优化等方面,这对于提升网络编程和系统...
附带的"netty源码剖析视频.txt"文件很可能是课程大纲或笔记,可能包含每一讲的详细内容概要,如源码解析的关键点、实战项目的步骤指导等。学习者可以结合这个文本资料,对视频内容进行复习和巩固。 总之,《Netty...
### Netty源码分析之Buffer #### Java Buffer 的相关基础知识 **1. Java 基本数据类型** Java 提供了八种基本数据类型:`byte`, `char`, `short`, `int`, `long`, `float`, `double`, `boolean`。 - **`byte`**:...
总的来说,Netty 源码分析涉及了网络编程、并发处理、事件驱动、协议编解码等多个领域,对理解 Java 高性能网络应用开发有着重要的指导意义。通过阅读源码,我们可以更深入地了解 Netty 如何实现高效的网络通信,并...
通过这样的学习过程,最终目标是能够熟练掌握Netty底层原理,解决实际遇到的复杂问题,甚至有能力为Netty项目贡献代码,提出issue或实现自己的定制版Netty。 总的来说,Netty作为一款强大的网络通信框架,其源码...
Netty的服务启动过程涉及到多个核心组件和步骤,下面我们通过一个简单的Netty服务器启动代码示例来进行详细的源码解析。 ##### 1. **初始化线程池** 首先,我们需要初始化两个线程池:`bossGroup` 和 `workerGroup...
《NIO+Netty5视频教程与Netty源码剖析视频教程》是一份全面解析网络编程框架Netty的教育资源,旨在帮助学习者深入理解和应用NIO(非阻塞I/O)以及Netty5的核心技术。该教程分为两个主要部分,分别针对理论分析和实战...
本文档主要讲述了Netty5.0架构剖析和源码解读,涵盖了Netty的架构、源码分析、NIO入门等方面的知识点。 概述 JAVA 的 IO 演进是一个长期的过程,从传统的 BIO 通信到 NIO 的出现,都是为了解决通信中的问题。传统...
### Netty源码剖析与NIO及Netty5各种RPC架构实战演练三部曲知识点解析 #### 一、Netty概述 Netty是一款基于Java NIO的高性能服务器端编程框架,用于快速开发可维护的网络应用程序。它简化了网络编程的复杂性,使...
接下来,我们谈谈 Netty 的源码分析。通过阅读 Netty 源码,我们可以深入了解其设计模式和优化策略: 1. **EventLoop(事件循环)**:Netty 使用单线程的 EventLoop 实现了事件的高效分发,减少了线程切换的开销。 ...
Netty是一款高性能的网络应用程序框架,它使用Java编程语言开发,主要用于网络应用程序的快速和易于开发,支持TCP和UDP...通过对Netty源码的深入分析,可以更好地理解其工作机制,对开发高性能的网络应用有极大的帮助。
源码分析: 1. **协议解析**:JT808协议的解析通常包括消息头的解析、消息体的解析以及校验码的验证。消息头包含消息类型、序列号、发送方和接收方的ID等信息。源码会定义一个类来表示JT808的消息结构,并通过...
通过分析和运行这些示例,你可以了解 Netty 的基本用法,如创建服务器、连接服务器、处理 I/O 事件、自定义编码解码器等。同时,也可以尝试扩展这些示例,例如添加新的处理器以处理特定的业务逻辑,或者实现更复杂的...