`
sasion
  • 浏览: 34394 次
  • 性别: 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-utils-1.9.18-q.zip

    而“animation4j”这个名字暗示了它可能基于Java语言,与Grizzly框架有关,Grizzly是一个轻量级的Java服务器端框架,常用于构建高性能的网络应用。 在“animation4j”中,我们可以预见到它会提供一套完整的动画管理...

    grizzly-portunif-2.3-rc3.zip

    Grizzly通常指的是一个高性能、轻量级的Java网络应用框架,而PortUnif可能是它的一个组件,可能涉及到网络端口统一或者代理相关的功能。然而,描述中并未直接提到Grizzly或PortUnif,而是提到了“bencode.zip”和...

    fish-letter:鱼书——一款开源通信框架

    1. **高性能**:通过优化的I/O模型和高效的内存管理,鱼书能够在高并发环境下保持良好的性能,降低延迟,提高吞吐量。 2. **易用性**:框架的API设计简洁明了,使得开发者能够快速上手,减少学习成本。同时,丰富的...

    openstack quantum安装文档

    这个标签指出了文档的主题——OpenStack Quantum,即 OpenStack 中用于管理网络资源的组件。 #### 正文内容概览及详细解析 **1. 概述** - **什么是 OpenStack Networking?** - OpenStack Networking(现更名为 ...

    避开10大常见坑:DeepSeekAPI集成中的错误处理与调试指南.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    前端分析-2023071100789

    前端分析-2023071100789

    基于kinect的3D人体建模C++完整代码.cpp

    基于kinect的3D人体建模C++完整代码.cpp

    搞机工具箱10.1.0.7z

    搞机工具箱10.1.0.7z

    GRU+informer时间序列预测(Python完整源码和数据)

    GRU+informer时间序列预测(Python完整源码和数据),python代码,pytorch架构,适合各种时间序列直接预测。 适合小白,注释清楚,都能看懂。功能如下: 代码基于数据集划分为训练集测试集。 1.多变量输入,单变量输出/可改多输出 2.多时间步预测,单时间步预测 3.评价指标:R方 RMSE MAE MAPE,对比图 4.数据从excel/csv文件中读取,直接替换即可。 5.结果保存到文本中,可以后续处理。 代码带数据,注释清晰,直接一键运行即可,适合新手小白。

    性价比革命:DeepSeekAPI成本仅为GPT-4的3%的技术揭秘.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    基于ANSYS LSDyna的DEM-SPH-FEM耦合模拟滑坡入水动态行为研究,基于ANSYS LSDyna的DEM-SPH-FEM耦合的滑坡入水模拟分析研究,基于ansys lsdyna的滑坡入水

    基于ANSYS LSDyna的DEM-SPH-FEM耦合模拟滑坡入水动态行为研究,基于ANSYS LSDyna的DEM-SPH-FEM耦合的滑坡入水模拟分析研究,基于ansys lsdyna的滑坡入水模拟dem-sph-fem耦合 ,基于ANSYS LSDyna; 滑坡入水模拟; DEM-SPH-FEM 耦合,基于DEM-SPH-FEM耦合的ANSYS LSDyna滑坡入水模拟

    auto_gptq-0.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

    auto_gptq-0.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

    复件 复件 建设工程可行性研究合同[示范文本].doc

    复件 复件 建设工程可行性研究合同[示范文本].doc

    13考试真题最近的t64.txt

    13考试真题最近的t64.txt

    Microsoft Visual C++ 2005 SP1 Redistributable PackageX86

    好用我已经解决报错问题

    嵌入式开发入门:用C语言点亮LED灯的全栈开发指南.pdf

    # 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!

    auto_gptq-0.4.2-cp38-cp38-win_amd64.whl

    auto_gptq-0.4.2-cp38-cp38-win_amd64.whl

    自动立体库设计方案.pptx

    自动立体库设计方案.pptx

    手把手教你用C语言实现贪吃蛇游戏:从算法设计到图形渲染.pdf

    # 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!

    性能对决:DeepSeek-V3与ChatGPTAPI在数学推理场景的基准测试.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

Global site tag (gtag.js) - Google Analytics