淘宝主要技术总结.doc_第1页
淘宝主要技术总结.doc_第2页
淘宝主要技术总结.doc_第3页
淘宝主要技术总结.doc_第4页
淘宝主要技术总结.doc_第5页
已阅读5页,还剩82页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Tomcat 5源码分析代码准备与DEBUG调试配置官方下载地址:/dist/tomcat/tomcat-5/v5.0.28/src/毕竟太老了(04年的东西),很多jar依赖都下不下来了。建议使用我修正后的source,下载后直接根目录ant即可完成build。为了方便跟踪与调试,bin目录下新建一个debug,后面加上:set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8787即开启JDPA,全称JavaPlatform Debugger Architecture(关于JPDA,这里还有篇论文可以看)同时等待DEBUGER连接后再继续(如果你使用的是我提供的,直接运行debug.bat就可以了)。Eclipse里新建Remote Debug,端口号8787。这样就可以在Eclipse里调试Tomcat 5了。总体结构从下至上: server:代表着整个Catalina(Tomcat内核代号)系统,系统基础,启动入口,全局资源,下属service。注意这里的port是管理用的(比如shutdown什么的),不是http监听端口。 service:代表一种服务,比如HTTP服务、JMX、JPDA,其本质上就是一组connector,接收相应的请求后递交给上层container进行处理。一个server可以有多个service,但一个service只对应一个container。 engine、host、context、wrapper:Tomcat的容器思想,每个容器实现Container接口,一个容拥有自己的资源配置、装载与事件分发机制。在实际中,事件的分发一般通过“观察者”模式,而上层数据则通过pipeline(后再详说)。(图3 注:成员未全部列出)容器与生命周期管理思想容器是tomcat 5中的重要思想。一个容器是负责处理加工来自外部的请求并将结果返回的模块。所有的容器都实现了Container这个接口,其采用了典型的Compositor模式。成员中其中较重要的是pipeline与resource。一个请求是通过invoke方法到达的,Host、Wrapper、Context都继承ContainerBase。前者依次被后者包含,Tomcat5 对这几个的默认实现均称为StandardXXX,如StandardEngine。 Invoke方法在ContainerBase中的实现即简单的调用pipeline的invoke,如下:通过执行pipeline的valve,执行相应的操作。上层数据的递交就是通过pipeline实现的。容器还采用了Observer模式来分发消息,每当容器发生图3所示事件时,都会通知listener。其实不仅是容器,这种设计思想还贯穿于整个设计中。再来说说生命周期管理。在启动tomcat的时候,我们能从控制台上明显看出,启动分两个步骤:init与start。Tomcat 模块生命周期管理都实现了Lifecycle这个接口,其定义了start()与stop()方法,不过奇怪的是init()在lifecycle中却没有定义,但几本上实现了lifecycle的类都有init()方法(所以我觉得这个也应该算在lifecycle里面的)。因此,当启动时,只要递归的调用init()与start()方法,就可以完成启动,注销时则递归地调用stop()即可。与容器的事件设计思想相同,生命周期的每步也会触发事件消息,只要订阅对应的事件,就可以非常容易地知晓其某阶段的进展。非常方便、简洁!HTTP(TCP)连接管理详细HTTP使用的是TCP连接。TCP是网联层协议,是端到端的。Tomcat使用的是最原始的Socket进行连接监听,即.ServerSocket.ServerSocket(int, int)。早些时候Tomcat使用的是一个线程监听,多个工作线程的模式:即一个线程专门负责响应连接请求,再递交给工作线程进行处理。Tomcat5 后开始使用线程池取代了原有的方案。如下图所示:早期的方式新方式那么单从结构来说,新的方式相对了老方法而言,消减了线程之间不必须的关联关系,使单个请求的处理流程完全独立。同时我们看到,在老方式里,一旦listener线程挂掉,那么整个tomcat也就完完了,这是非常危险的事。从总体来看TCP连接结构管理涉及这几个类:TcpWorkerThread 是PoolTcpEndPoint的内联类,继承了ThreadWithAttribute接口。可以理解成一个Runable的类。PoolTcpEndPoint里的ThreadPool执行的就是TcpWorkThread任务。启动时,PoolTcpEndPoint会从ThreadPool里取一个线程来监听端口,一旦有连接进来,它就会从ThreadPool里再取一个继续监听。当一个连接进来并最终到达Container(也就是StandardEngine)来进行对应的处理。先来看看整个请求调用的栈:大概有这么几个步骤:1. 连接进入后,一直执行serverSocket.accept方法并处于block状态的TcpWorkThread会返回socket对象,如果设置了SSL,会进行SSL握手,之后将socket包装成TcpConection递交给Http11ConnectionHandler继续进行处理;2. Http11ConectionHandler主要是从socket从提取出InputStream与OutputStream,然后调用cess()交给Http11Processor来解析http请求,也就是说Http11Processor才是真正读取HTTP请求体的单元;3. Http11Processor主要任务就是解析HTTP请求,它会把InputStream与OutputStream包装成InternalInputBuffer与InternalOutputBuffer,它们主要提供了HTTP头解析的功能。之后设置好maxKeepAlive、timeout(比如上传超时)等socket相关属性,再有就是HTTP一些属性,如是GET请求还是POST等。最后就是对请做一些检查与限制了,如什么样的agent直接deny之类的,然后调用adapter.service()交给CoyoteAdapter处理,之后便进入了Container,对应不同的业务处理逻辑。Deployer模块详细当start(),init()生命周完成后,便到了deployer起作用的时候了。deploy是由org.apache.catalina.Deployer及其实现完成的。最关键的是install方法,URL指一war包的地址,String指的是一个context path。这里顺便说一下tomcat对整个URL请求部分的命名,如下图:Deployer的实现org.apache.catalina.core.StandardHostDeployer,支持jar包、war包及文件夹的形式对,然后就是一些验校,之后便new一个context,加到host里就OK啦资源请求与响应servlet请求servelt 请求响应先来看看整个调用的栈情况,接着HTTP(TCP)请求之后,继续跟踪:需要说明的是,index_jsp这个类是根据jsp页面自动生成的,所以上看去有些古怪_1. 前面我们说到Http11processor会调用CoyoteAdapter以将数据传给上层Container。顾名意义,CoyoteAdapter使用了Adapter模式,即适配上层容器,实际作用就是将请求转发给上层Container。为了适应Container的参数要求,其会对请求再次做一些处理。主要是集中在“postParseRequest()”方法中,关键代码如下:2. Container的invoke()实际上就是简的调用其pipeline的invoke()方法,pipeline里的basicValve为StandardHostValve,它会调用StandardHost的pipeline,然后是StandardContextValve,再调用自己的pipeline,再是StandardWrapperValve,调用pipeline,最后便到了StandardWrapper这里。同样wrapper也是调用自己的pipeline,即StandardWrapperValve,它负责分配servlet(如果此servlet工作在SingleThreadMode(STM)下,则每次都需要分配新的serlvet,后详。3. 同样StandardWrapperValve也是调用自己的pipeline,即StandardWrapperValve,它负责分配servlet(如果此servlet工作在SingleThreadMode(STM)下,则每次都需要分配新的serlvet,后详。然根据servelt与Request创建FilterChain,这里的filter就是我们在tomcat里配置的filter,ApplicationFilterChain调用servlet.service(HttpServletRequest)request,(HttpServletResponse) response);终到达servlet。servlet 加载与管理分为STM(SingleThreadModel)与非STM,STM使用实例栈来管理。负责生成Servlet实例的类为StandardWrapper,对应的方法为allocate()。代码不长,执行流程图如下:从上面我们可以比较明确的看出,servlet的加载分两条路线,一条是STM,即一个servlet对应一个线程,类似Spring里的prototype,另一种则是非STM,有点像Singleton,即单例。这里要说的是,Tomcat的servlet加载是支持自定义classLoader的,这里的classLoader来自于Container,前面说过了,Container的作用是管理自己的生态圈,如资源的加载、类的加载以及生命周期管理,所以自定义loader放在那里再适合不过了。当一次servlet请求完成以后,StandardWrapperValve就会调用ServletWrapper的dellocate来销毁一个servlet,其过程大致就是上面过程的反向,还是分为STM与非STM,非STM只会减少引用量,instance不会销毁,STM就是将serlvet归还给instancePool。静态资源请求上面说的是动态servlet的请求过程,那么静态资源又是怎么样响应的呢?那我们知道,HTTP请求实质上就是处理GET,POST,PUT,DELETE四种请求,所以静态资源本质上也是由一个servlet来处理的,当然这个servlet不同于JSP的,他是一种HTTPServlet(DefaultServlet.java)。资源的加载逻辑在org.apache.catalina.servlets.DefaultServlet.getResources()方法中。其主要逻辑就是先试着在servlet context(即你应用的根,如:E:jakarta-tomcat-5.0.28-srcjakarta-tomcat-5buildwebappsROOT)里寻找资源,如果没有再在JDNI里寻找,如果仍然失败了,就返回null。随便来看看Servlet的总的继承结构:正如前面所说的,所有的jsp页面都会被编译成一个jsp类。以XXX_jsp进行命名。当然这里要列举了一些其它类型的servlet,有兴趣的同学可以去看看。现在我们来请求http:/localhost:8080/tomcat.gif来看看其调用栈:开始仍然是一路请求到wrapper容器,之后到defaultServelt,再到getResource。总之,从下往上看,非常清楚的。DefaultServer获取资源的方式有根据JNDI与servletContext两种。SSL详细SSL即“安全套接字层(Scure Sockets Layer)”,其是这种端到端的加密技术,是“非对称”与“对称加密”的结合(因为对称加密运算少,后详),属于“传输层安全协议”即TLS(Transport Layer Secure),现在普遍使用的是SSL3.0版本。建立SSL需要一个handshake过程,总的说来有这么几个步骤:更详细的信息可以参考wikipedia或RFC6101。现在我们来看看tomcat里是怎么处理SSL连接请求的。要使用SSL首先Tomcat需要配置成支持SSL,详见这里,这样在创建ServerSocket的时候便创建的是ernal.ssl.SSLSocketImpl而不是前说的普通的.ServerSocket.ServerSocket(int, int),之后便用在TcpWorkerThread处理请求时调用其handshake()方法进行上图所描述的协议握手。SSL密文的加解密也是在SSLSocketImpl里进行的,上图已经说明,SSL在session数据传输时使用的是对称加密,不过jsse包并不开源,所以我们无法得知其具体使用的是什么样的对称加密算法(AES、twofish?),不过现在有openJDK,有兴趣的同学的可以去研究一下。之后便到了Http11Processor,这里会根据配置生成一个.SSLSupport用于向request里注入一些SSL信息,一般不会用。这篇文章主要讲的是tomcat5,所以想要更深入的理解SSL,可以参考其它文章。思考与问题Tomcat5中的设计模式facadefacade的作用是为复杂的功能集提供一个统一的入口,还要可以将一个object 隐藏起来,只暴露需要的功能。这里我们举例:org.apache.catalina.core.ApplicationContextFacade,其就是对org.apache.catalina.core.ApplicationContext的mask,比如org.apache.catalina.core.ApplicationContext.getServletContextName()方法,ApplicationContextFacade里就是没有的。另外要说的就是facade里对context的调用全采用的是反射,这样确实可以减少耦合性,但本人觉得是没有什么必须的,因为反射会增加代码的复杂程度(当然facade在另一个项目里就另说了)。chain of responsibility体现责任链模式的设计最明显的当局各Container容器中的pipeline了,每个valve都是一个负责结点,当此任务完成以后,它又会传递给一个结点。为了使用pipeline的执行能够线程安全,负责valve移动的实质上是org.apache.catalina.ValveContext。其负责记录本次pipeline当前执行结点、总结点数及上下文变量。不过比较古怪的是每个pipeline有个名为basic的valve,当pipeline的valves为空时,这个basicvalve就会执行。factory工厂模式是个很简单的模式,到处可见。比如.ServerSocketFactory,此工厂负责生产.ServerSocket。为了方便,工厂本身又是一个单例,所以这里又会涉及一些同步问题,一般有doublecheck,staic initialization之类的方便,不过tomcat里却用的是最简单当然也最有效的方法,就是synchronized method :)observer这个在前面我们就见得很多了,很多容器的事件钩子都是这样实现的。比如:StandardServer.java。观察者都在listener数组中,通知事件时publisher挨个进行通知,当然这里会有一些同步问题,如下:我们看到其在递归通知对listerner进行了clone那是因为不这样做可以会出现IndexOutOfBoundry问题,即使采用安全的iterator,也会出现ConcurrentModificationException.问题当然金无赤足,人无完人。再好的代码也会有问题,下面就来看看:magic number这个是老问题了,其实如果要求不是很严格的话,这个也可以不算个问题。比如:org.apache.coyote.http11.Http11Pcess(InputStream, OutputStream) 第 775 行与第 787 行。显然这里如果将400改为HttpStatusEnum.BAD_REQUEST要好很多,但开发者却没有这么做,估计是太懒了吧 _double checkstandardservice 478行严格来说,只有使用的是1.5JDK及以后的版本,同时将container标记为volatile才能完全的线程安全,不然可能获取到未初始化完全的对象(见Concurrency In Practice16.2.4.Double Checked Locking 一节)。zombie control这个问题子来源于 http11processor 第745行:这个就太明显了,就不多说了。Netty代码分析日期:2010-09-25作者:bucketliNetty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序官方定义,整体来看其包含了以下内容: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有这个事件的响应方法的话那么就会调用到这个方法。在此之后就是数据的传输了。下面是一个简单客户端的代码解读。/ 实例化一个客户端Bootstrap实例,其中NioClientSocketChannelFactory实例由Netty提供ClientBootstrap bootstrap =newClientBootstrap(newNioClientSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool();/ 设置PipelineFactory,由客户端自己实现bootstrap.setPipelineFactory(newFactorialClientPipelineFactory(count);/向目标地址发起一个连接ChannelFuture connectFuture =bootstrap.connect(newInetSocketAddress(host, port);/ 等待链接成功,成功后发起的connected事件将会使handler开始发送信息并且等待messageRecive,当然这只是示例。Channel channel = connectFuture.awaitUninterruptibly().getChannel();/ 得到用户自定义的handlerFactorialClientHandler 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关闭)。网络动作归结到最简单就是服务器端bind-accept-read-write,客户端 connect-read-write,一般bind或者connect后会有多次read、write。这种特性导致,bind,accept与read,write的线程分离,connect与read、write线程分离,这样做的好处就是无论是服务器端还是客户端吞吐量将有效增大,以便充分利用机器的处理能力,而不是卡在网络连接上,不过一旦机器处理能力充分利用后,这种方式反而可能会因为过于频繁的线程切换导致性能损失而得不偿失,并且这种处理模型复杂度比较高。采用什么样的网络事件响应处理机制对于网络吞吐量是非常重要的,Netty采用的是标准的SEDA(Staged Event-Driven Architecture)架构/wiki/ Staged_event-driven_architecture,其所设计的事件类型,代表了网络交互的各个阶段,并且在每个阶段发生时,触发相应事件交给初始化时生成的pipeline实例进行处理。事件处理都是通过Channels类的静态方法调用开始的,将事件、channel传递给 channel持有的Pipeline进行处理,Channels类几乎所有方法都为静态,提供一种Proxy的效果(整个工程里无论何时何地都可以调用其静态方法触发固定的事件流转,但其本身并不关注具体的处理流程)。Channels部分事件流转静态方法1fireChannelOpen 2fireChannelBound 3fireChannelConnected 4fireMessageReceived 5fireWriteComplete 6fireChannelInterestChanged7fireChannelDisconnected 8fireChannelUnbound 9fireChannelClosed 10.fireExceptionCaught 11.fireChildChannelStateChangedNetty提供了全面而又丰富的网络事件类型,其将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事件的处理器。DefaultChannelHandlerContext realCtx = ctx;while(!realCtx.canHandleUpstream() realCtx = realCtx.next;if(realCtx =null) returnnull;returnrealCtx;如果是一个网络会话最末端的事件,比如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进行数据接收。publicvoideventSunk(ChannelPipeline pipeline, ChannelEvent e)throwsException Channel channel = e.getChannel();if(channelinstanceofNioServerSocketChannel) handleServerSocket(e);elseif(channelinstanceofNioSocketChannel) handleAcceptedSocket(e);NioWorker worker = nextWorker();worker.register(newNioAcceptedSocketChannel(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) caseOPEN:if(Boolean.FALSE.equals(value) channel.worker.close(channel, future);break;caseBOUND:caseCONNECTED:if(value =null) channel.worker.close(channel, future);break;caseINTEREST_OPS:channel.worker.setInterestOps(channel, future, (Integer) value).intValue();break;Netty提供了大量的handler来处理网络数据,但是大部分是CODEC相关的,以便支持多种协议,下面一个图绘制了现阶段Netty提供的Handlers(红色部分不完全)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)。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的初步理解,还没有足够的能力指出某一细节的所发挥的作用。BufferedInputStream 源码学习笔记日期:2012-07-31作者:chiyanBufferedInputStream是一个带有内存缓冲的InputStream.1.首先来看类结构:BufferedInputStream是继承自FilterInputStream。FilterInputStream继承自InputStream属于输入流中的链接流,同时引用了InputStream,将InputStream封装成一个内部变量,同时构造方法上需要传入一个InputStream。这是一个典型的装饰器模式,他的任何子类都可以对一个继承自InputStream的原始流或其他链接流进行装饰,如我们常用的使用BufferedInputStream对FileInputStream进行装饰,使普通的文件输入流具备了内存缓存的功能,通过内存缓冲减少磁盘io次数。Java代码1. protectedvolatileInputStreamin;2. protectedFilterInputStream(InputStreamin)3. this.in=in;4. 注意:成员变量in使用了volatile关键字修饰,保障了该成员变量多线程情况下的可见性。2.内存缓冲的实现概要的了解完BufferedInputStream的继承关系,接下来详细理解BufferedInputStream是如何实现内存缓冲。既是内存缓冲,就涉及到内存的分配,管理以及如何实现缓冲。通过构造方法可以看到:初始化了一个byte数组作为内存缓冲区,大小可以由构造方法中的参数指定,也可以是默认的大小。Java代码1. protectedvolatilebytebuf;2. privatestaticintdefaultBufferSize=81

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论