`
sasion
  • 浏览: 34112 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

高性能的HTTP引擎—— Grizzly(二) Grizzly简介

阅读更多
                             Grizzly简介
正如前文所说,用Java技术来编写一个扩展性能很高的服务器软件是件很困难的事情。Java虚拟机的线程管理机制使得纯Java写的HTTP引擎很难响应成千上万的并发用户。正如Tomcat一样,在并发用户数不是很高的情况下能够获得很高的吞吐量,但是在高并发的情况下性能下降很快,变得不太稳定。

在JDK 1.4推出NIO之后,有很多基于NIO的框架出现,利用NIO的新特性,来编写高性能的HTTP引擎。其中以Jean-Francois Arcand的Grizzly最为引人瞩目。Grizzly最早被用于Sun Java System Application Server, Platform Edition 8.1。随后成为开源软件GlassFish的一部分。在今后,Sun Java System Application Server 9.x的Platform Edition和Enterprise Edition都会使用Grizzly作为HTTP引擎。

17.2.1  Grizzly的基本架构

图17-1描述了Grizzly的基本架构。



图17-1  Grizzly的基本架构

Grizzly的基本架构主要包含以下几个方面:Pipeline、SelectorThread和Task。下面分别加以介绍。

1. Pipeline
在com.sun.enterprise.web.connector.grizzly包下,有许多与Pipeline相关的类,例如Pipeline、KeepAlivePipeline、ThreadPoolExecutorPipeline、LinkedListPipeline等。Pipeline是个不太好理解的词汇,其实把这些类叫做ThreadPoolWrapper可能更加合适和容易理解。只要熟悉服务器端的软件,对Thread Pool(线程池)一定不会陌生。线程比起进程来说,消耗的资源要少,共享数据更加简单。因此,现在大多数服务器软件(特别是HTTP服务器)都会采用多线程模式。但是线程的创建和关闭仍然是比较慢的系统服务,聪明的服务器软件设计者会在系统启动的时候,预先创建一些线程,并且将这些线程管理起来,在系统正常运行的时候服务于客户的请求。通过这样的手段,线程不需要在使用的时候临时创建,大大提高了软件的运行速度和效率。对这种线程的管理方法叫做线程池。线程池中的线程需要互相协作,有序地执行客户的请求。一般用于同步线程的结构叫任务队列。客户的请求根据先后顺序被放到了任务队列中,线程池中空闲的线程会从任务队列中获得任务并执行。

Grizzly中的Pipeline实际上封装了一个Thread Pool(线程池)和一个任务队列。Pipeline的主要目的是封装了一个统一的接口,可以让Grizzly根据配置文件任意选择不同算法的线程池,来获得不同的特点和性能。在Grizzly中已经实现了好几种线程池。其中有ThreadPoolExecutorPipeline(基于java.util.concurrent.ThreadPoolExecutor来实现的线程池),还有LinkedListPipeline(使用简单的linklist数据结构管理的线程池)。在早期的Grizzly中还会看到一些其他的实现。经过测试以后,淘汰了一些性能不好的算法,目前只剩下这两种Pipeline了。事实上在大并发用户的测试中,LinkedListPipeline的性能是最好的,因此被设置为默认的选择。在以后的版本中,ThreadPoolExecutorPipeline也可能会消失,只保留性能最好的算法是明智的选择。但是现在还存在两种算法,其主要原因是java.util.concurrent.ThreadPoolExecutor的名声太响,所有的文章和测试都曾经证明过它的高性能。就连Grizzly的作者本身都不相信LinkedListPipeline的性能要比ThreadPoolExecutorPipeline好,只不过当前的测试结果事实如此。因此该作者自己也说,一旦有证据证明ThreadPoolExecutorPipeline的性能又重新超过LinkedListPipeline,他会立即将默认的设置指向ThreadPoolExecutorPipeline。

KeepAlivePipeline是一个特例,它并不是用来执行特定任务的,而是用来维护HTTP协议中的持久连接的状态,例如维护最大的持久连接数,持久连接的timeout时间等。另外,异步的socketChannel中缺少一个类似socket.setSoTimeout的函数,这个函数在保证服务器软件的可靠性和安全性(抗DOS攻击)上,具有重要的作用。Grizzly是用KeepAlivePipeline类来模拟socket.setSoTimeout的作用。

2. SelectorThread
这是Grizzly的主要入口类,位于com.sun.enterprise.web.connector.grizzly的包下。在SelectorThread中,SocketChannel和Selector被创建并被初始化。当网络有请求进来的时候,Selector会根据不同的请求类型和NIO的不同事件进行不同的处理。

当NIO的事件为OP_READ的时候,表明是原有的连接中有新的请求数据传过来了。这类请求属于ReadTask,应该交给负责处理ReadTask的处理器来处理。ReadTask有自己的Pipeline(也就是线程池)来处理,这样就不会占用主线程来处理Read的请求。

当NIO的事件为OP_ACCEPT的时候,表明是有新的请求进来了,这类请求属于AcceptTask,应该交给负责处理AcceptTask的处理器来处理。在老版本的GlassFish中,AcceptTask也有自己的Pipeline来处理,这样就让AcceptTask在主线程以外的线程中执行。但是经过多次性能测试和比较,发现当AcceptTask在主线程(SelectorThread)中执行的时候,性能最好。因此,在读最新的Grizzly源代码的时候,会发现图17-1中的AcceptPipeline根本不存在,因为AcceptTask已经由SelectThread类中HandleAccept函数来执行了。

当ReadTask执行完以后,表明整个请求的数据已经完全接收到,就可以进行请求处理了,请求处理属于ProcessTask,交给负责处理ProcessTask的处理器来处理。ProcessTask有自己的Pipeline(也就是线程池)来处理,这样就不会占用主线程来处理请求。

3. Task
在Grizzly的框架中包含下面几种任务。

(1)   AcceptTask:用于响应新的连接请求。前面已经说过,这个任务的类事实上已经不存在,没有单独抽象出来。因为处理Accept已经成为SelectThread内部的一部分了。

(2)   ProcessTask:用于处理并且响应请求。这个任务通常是对请求的数据进行解析,解析完后再将请求传递给其他服务的容器(如Servlet容器)进行真正的业务处理。

(3)   ReadTask:用于SocketChannel最初的读取操作。由于NIO是非阻塞的操作,最初的读取往往不能获得全部的请求数据,这时候,ReakTask会将任务委托给StreamAlgorithm,根据不同实现,用不同的方法将剩下的请求数据获取。

在com.sun.enterprise.web.connector.grizzly.algorithms的包下,Grizzly默认实现了4个算法:

l   ContentLengthAlgorithm

l   SeekHeaderAlgorithm

l   StateMachineAlgorithm

l   NoParsingAlgorithm

前3个算法主要是围绕HTTP请求中的Content-length字段来进行解析。只要能读到这个字段的值,那么我们就可以预先判断整个请求的长度,从而确定什么时候完成请求读取,接着进行请求处理了。第4个算法是对请求数据根本不进行预处理,假设所有的数据都读进来了。如果最后发现请求数据读得不完全,再交给请求处理任务(ProcessTask)来负责将剩下的内容读取过来。

17.2.2  源码阅读指南

根据图17-1的结构,结合Grizzly的源代码,可以看到Grizzly的大致脉络。

SelectorThread是个入口,根据Grizzly所在的不同环境,启动的方法有所不同。如果Grizzly作为单独可运行的应用(Grizzly可以从GlassFish中独立出来),在com.sun.enterprise. web.connector.grizzly.standalone包下的Main类是这样使用SelectorThread的:

【例17.4】单独运行的Grizzly对SelectorThread的调用:

private static void start(String args[]) throws Exception {

...

    SelectorThread selectorThread = null;

    String selectorThreadClassname = System.getProperty(SELECTOR_THREAD);

    if (selectorThreadClassname != null){

        selectorThread = loadInstance(selectorThreadClassname);

    } else {

        selectorThread = new SelectorThread();

    }

    selectorThread.setPort(port);

    StaticResourcesAdapter adapter = new StaticResourcesAdapter();

    adapter.setRootFolder(folder);      

    selectorThread.setAdapter(adapter);

    selectorThread.setDisplayConfiguration(true);

    selectorThread.initEndpoint();

    selectorThread.startEndpoint();

}

如果Grizzly是在GlassFish中,它作为服务线程,run()方法是整个线程启动的钥匙。从源码中很容易看出在run()方法中调用了startEndpoint()方法,startEndpoint()在做好一些准备工作之后,调用了startListener()。startListener()便进入了主线程的循环之中。在循环中只有一个方法,那就是doSelect()方法。

在doSelect()中,可以很清楚地看到NIO的框架结构。

【例17.5】SelectorThread中的doSelect():

selectorState = selector.select(selectorTimeout);

...

readyKeys = selector.selectedKeys();

iterator = readyKeys.iterator();

while (iterator.hasNext()) {

    key = iterator.next();

    iterator.remove();

    if (key.isValid()) {

        handleConnection(key);

    } else {

        cancelKey(key);

    }

}

与大多数NIO的架构一样,先是调用selector.select(selectorTimeout),看看当前的频道有没有数据准备好了。如果有的话,通过selector.selectedKeys()将准备好的这些频道的SelectionKey取到。对这些频道的处理就交给handleConnection(key)函数了。

【例17.6】SelectorThread中的handleConnection:

protected void handleConnection(SelectionKey key) throws

IOException,InterruptedException

{

    Task task = null;

    if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT){

        handleAccept(key);

        return;

    } else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ){

        task = handleRead(key);

    }

if (((SocketChannel)key.channel()).isOpen()) {

        task.execute();

} else {

        cancelKey(key);

}

}

handleConnection函数很短,但是有一些重要的特点需要指出来。handleConnection的主要功能是区分那些已经准备好的频道,看看它们是属于新的连接(OP_ACCEPT)还是有新的请求数据(OP_READ)。

如果是OP_ACCEPT,那么就调用函数handleAccept(key)。这个函数会在当前的线程内执行,主要的功能就是根据新来的连接创建新的频道,再将这个频道注册到Selector中。如果是OP_READ,那么就调用函数handleRead(key)。这个函数返回了一个Task。通过task.execute()将这个任务的实际运行交给Pipeline中的线程池来执行。换句话说,对新的请求数据的处理是在另外的线程中来处理的,而不是当前的线程。

事实上,在早期的Grizzly的版本中,对OP_ACCEPT的处理与OP_READ一样,也是有单独的任务(AcceptTask)和单独的线程来执行。但是经过性能测试,证明当对OP_ACCEPT的处理在主线程的时候性能最好。因此就取消了AcceptTask在单独线程中的处理,演化为当前的模型。

再随后的工作主要就交给ReadTask和ProcessTask去做了。这里不作详细的介绍。
分享到:
评论
1 楼 cnliuxj 2008-04-30  
很好,受益匪浅

相关推荐

    grizzly-http-server-monitoring-2.3.9.zip

    Grizzly 是一个高性能、轻量级的Java网络应用框架,主要用于构建HTTP服务器、Servlet容器和其他网络服务。监控组件允许开发者实时查看和分析服务器的运行状态,如请求处理速度、资源消耗等,从而优化系统性能和稳定...

    Grizzly_Architecture

    Grizzly是Sun Microsystems(后被Oracle收购)开发的一款高性能的网络通信框架,主要用于构建非阻塞的网络应用程序。该框架基于Java NIO技术,旨在为开发者提供易于使用的API,同时隐藏了NIO编程中的复杂性。Grizzly...

    grizzly

    标题中的“grizzly”指的是Grizzly,一个由Sun Microsystems(现为Oracle)开发的开源网络应用框架,主要用于构建高性能、可扩展的网络服务器。Grizzly是Java平台上的一个组件,它提供了一组灵活且强大的API,可以...

    grizzly-http-webserver-1.9.59.zip

    Grizzly HTTP Web服务器是Oracle GlassFish服务器的一部分,它是一个轻量级、高性能的网络应用服务器,特别适合于开发和部署Java应用程序。Grizzly提供了丰富的API和组件,使得开发者可以方便地构建基于HTTP的服务器...

    grizzly-http-samples-2.3.13.zip

    Grizzly是Java的一个高性能、轻量级的网络应用框架,由Oracle公司开发,常用于构建HTTP服务器、WebSocket服务器和其他网络服务。 【描述】提及的"lift-jquery-module.zip"则是一个与 Lift Web 框架相关的jQuery模块...

    grizzly-http-servlet-extras-2.3.8-beta1.zip

    Grizzly是Oracle公司提供的一个开源Java网络应用服务器框架,它主要设计用于构建高性能、轻量级的HTTP服务器和Web应用程序。Grizzly HTTP Servlet Extras是Grizzly框架的一个扩展,它为Servlet API提供了额外的功能...

    grizzly-websockets-2.3.4.zip

    Grizzly 是一个由Oracle公司开发的轻量级、高性能的Java服务器框架,它提供了HTTP和WebSocket协议的支持。Grizzly WebSockets 模块使得开发者能够方便地在Web应用中实现WebSocket协议,这是一种双向通信协议,允许...

    grizzly-http-server-2.2.21.zip

    Grizzly HTTP Server 是一个开源的、高性能的网络应用框架,由Java开发,主要用于构建基于HTTP/1.1协议的服务器和应用程序。版本2.2.21是该服务器的一个稳定版本,提供了许多功能和性能改进。 【描述】...

    grizzly-http-ajp-2.3.10.zip

    Grizzly 是一个开源的网络应用框架,由 Oracle 公司开发,主要用于构建高性能、轻量级的网络服务器和客户端。AJP(Apache JServ Protocol)是 Apache 服务器与应用服务器之间通信的一种协议,常用于负载均衡和反向...

    grizzly-websockets-chat-2.3.zip

    Grizzly是Java的一个开源网络应用框架,特别适用于构建高性能、轻量级的服务器端应用。WebSocket协议则是一种在Web上实现实时通信的技术,它允许服务器和客户端进行双向、全双工的数据传输。 描述中的"osgi-kernel....

    Ubuntu13.04安装Grizzly版本的OpenStack

    在本文中,我们将详细介绍如何在Ubuntu 13.04上安装Grizzly版本的OpenStack。OpenStack是一个开源云计算平台,用于构建公共云和私有云。Grizzly是OpenStack的一个重要版本,提供了多种服务,如计算、存储和网络管理...

    grizzly-framework-monitoring-2.3.14.zip

    Grizzly 是一个由 Sun Microsystems 开发并维护的开源框架,主要用于构建高性能、灵活的网络应用服务器。它提供了一组丰富的组件和服务,使得开发者能够轻松创建基于 Java 的网络应用程序。在 Grizzly 框架中,监控...

    Grizzly 2.3.17 API文档CHM版

    2014年10月24日最新Grizzly 2.3.17 API文档CHM版

    grizzly-http-ajp-1.9.42.zip

    Grizzly是Oracle公司开发的一个轻量级、高性能的Java网络应用框架,它提供了HTTP服务器、HTTP客户端以及其他网络协议处理的能力。AJP是一种协议,常用于在Web服务器(如Apache)与应用服务器(如Tomcat)之间进行...

    grizzly-core-2.1.4.jar 下载

    Dubbo的核心jar包,grizzly-core-2.1.4.jar 下载,源码版本为2.5.4开发版

    grizzly初探

    Grizzly 是一个由 Sun Microsystems 开发并维护的开源框架,主要用于构建高性能、轻量级的网络应用服务器。它是一个 Java NIO(非阻塞 I/O)库,用于创建高并发、低延迟的网络服务。在 Java 社区中,Grizzly 以其...

    Tricks and Tips With NIO Using the Grizzly Framework

    Project Grizzly是一个利用Java NIO(非阻塞I/O)技术来构建高性能、可扩展服务器的项目。在Java平台实现可扩展的服务器曾经是一项极具挑战性的任务,特别是在Java NIO出现之前,几乎可以说是不可能完成的任务。而...

    在Glassfish v2ur1 中测试grizzly comet chat demo

    Grizzly是一个开源的网络应用框架,由Java开发,用于构建高性能、可扩展的网络应用程序。Comet技术则是一种服务器推送技术,它允许服务器向客户端实时推送数据,而无需客户端反复发起请求,常用于实现聊天、股票报价...

    grizzly-http-jaxws-samples-2.2.10.zip

    Grizzly是一个轻量级的Java网络应用框架,由Oracle公司开发,它提供了HTTP服务器和网络应用的基础结构,特别适合用于开发高性能的Web服务。JAX-WS则是一种标准的Java API,用于创建和消费基于SOAP的消息,是构建企业...

Global site tag (gtag.js) - Google Analytics