-
JAWS模块分析
2004-04-19
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://jnn.blogbus.com/logs/149434.html
程序功能说明
http服务器,响应客户端HTTP请求,并提供如下的参数调用接口
-p port number
-n threads in the server
-f thread activation flags
= THR_BOUND
= THR_DAEMON
= THR_DETACHED
= THR_NEW_LWP
-t threading strategy
= POOL -> thread pool
= PER_REQUEST -> thread per request
= THROTTLE -> thread per request with throttling
-i I/O strategy
= SYNCH
= ASYNCH
-b backlog value for listen ()静态分析 -- 重点是从头文件,主要分析程序构架,类之间的关系以及类的作用
入手,先找程序入口,
main.cpp -> 找到 ACE_Service_Config daemon ,通过调用daemon.open进入程序
(关注Service_Config实现)查看svc.conf 定位服务主程序为HTTP_Server
HTTP_Server : public ACE_Service_Object
HTTP_Server中包含的内容有 HTTP_SOCK_Acceptor
(如何将线程响应模式封装 Task模式的应用)
Synch_Thread_Pool_Task
Thread_Per_Request_Task
Asynch_Thread_Pool_Task (only for window32)HTTP_Server.{h,cpp} 找到 HTTP_Handler.h 和IO.h
分析IO (为什么要单独提出IO处理类)
JAWS_IO 抽象接口,提供了基本的IO操作描述
JAWS_IO_Handler 抽象接口,提供了IO操作结果的处理
JAWS_Synch_IO : public JAWS_IO
JAWS_Asynch_IO : public JAWS_IO, public ACE_Handler
(为什么异步处理需要继承 ACE_Handler)分析HTTP_Request
HTTP_Request This parses the client request of an HTTP transaction.
(如何定义解析HTTP_Request)
解析HTTP请求,并将文本信息解析并转换成为HTTP_Request对象中的属性
注意 Values for request type,Header strings 的组织
分析HTTP_Response
HTTP_Response Provides an encapsulation of responses to HTTP requests.
For instance, given an HTTP GET request, it will produce
header and body suitable for returning to the client who made
the request.
对HTTP请求的响应封装,--处理请求,并产生响应结果
分析HTTP_Handler
HTTP_Handler : protected JAWS_IO_Handler
实现对HTTP协议的处理
The HTTP_Handler class is a state based implementation of the
HTTP protocol. Therefore, it can be used synchronously and
asynchronously. It uses an abstract IO class to move between
different HTTP protocol states. It is up to the IO class to
decide on synchronous or asynchronous I/O.
(为什么定义friend对象)
HTTP_Handler_Factory 定义如何创建新的HTTP handler
Synch_HTTP_Handler_Factory : public HTTP_Handler_Factory
Asynch_HTTP_Handler_Factory : public HTTP_Handler_Factory, public ACE_Service_Handler
(为什么异步模式还需要继承ACE_Service_Handler,其中的Open所起的作用是什么)
剩余文件 HTTP_Config,HTTP_Helpers,Parse_Headers必定是与实现相关的模块
分析HTTP_Config
HTTP_Config 存储服务器的相关配置信息 (如何用singleton模式实现有个环境变量的东东)
HTTP_Config_Info 实际存储配置信息的地方分析HTTP_Helpers
HTTP_Helpers Static functions to enhance the lives of HTTP programmers everywhere.
(为什么要定义这些方法,而且要用static 以及mutex_)
HTTP_Status_Code 有关状态码的东东 (singleton的另外一种实现方法)分析Parse_Headers
Headers_Map_Item (Map是什么,Item又是什么)
Headers_Map 提供文本头到头值的转换
Headers 完成头到数字的转换 (和Headers_Map有什么关系啊)模块划分分析 (详见POSA2 ch1.3)
为了保证系统的可扩展性,JAWS设计了相关几个模块
并发模型 Concurrency models
e.g.,thread pool vs. thread-per request
事件多路分离模型 Event demultiplexing models
e.g.,sync vs. asynch
文件缓存模型(JAW2 提供相应的模型)File caching models
e.g.,LRU vs. LFU
内容发送协议模型 Content delivery protocols
e.g.,HTTP 1.0+1.1, HTTP-NG, IIOP, DICOM注:在JAWS2中,将Server分割成为HTTPU和JAWS模块
在HTTPU中负责有个协议内容解析的操作
在JAWS中重点加强了文件缓存部分的处理
随机文章:
ACE Tutorial [翻译] 08-page04 2004-11-29ACE Tutorial [翻译] 08-page03 2004-11-29ACE Tutorial [翻译] 08-page02 2004-11-29ACE Tutorial [翻译] 08-page01 2004-11-29ACE Tutorial [翻译] 06 -page05 2004-11-21
收藏到:Del.icio.us









评论
前言:学习ACE也已经有半年了,虽然还很肤浅,但感觉有那么一点意思了。最近分析了一下JAWS模块,拿出来跟大家共通交流。欢迎大家的讨论。杜绝商业目的,并保持文章的完整性。
本人信箱:yuanjiandong512@163.com
一 JAWS模块概况
总则:
JAWS主要有四个主要分块:并发策略(线程池、每个申请一个线程、单线程),I/O策略(同步,反应式,异步方式),协议处理(管道方式,模块方式),文件系统(LRU,LFU)。
JAWS主要的设计模式,单体模式,抽象工厂模式,工厂方法模式,反应器模式,主动对象模式,流水线模式,服务器运行时配置方式等)
JAWS的架构如图1-1所示:(总体方案)
图1-1 JAWS Web服务器构架
JAWS的并发策略如图1-2所示:
图1-2 JAWS 并发策略(线程池、Thread-per-Request)
JAWS的I/O策略如图1-3所示:
图1-3 JAWS I/O策略(同步策略、反应式、异步方式)
JAWS的协议流水线策略如图1-4所示:
图1-4 JAWS协议流水线策略
JAWS的文件缓存策略如图1-5所示:
图1-5 JAWS文件缓存策略
二 JAWS1程序分析
一 概述
JAWS1的程序实现没有完成全部JAWS的设计思想,主要进行了并发策略、I/O策略的完成。设计结构跟JAWS2比较,没有JAWS2的面向对象结构化强,没有它封装的好。
二 程序流程
第一阶段:MAIN()到数据库的INIT()
1、main() -> ACE_Service_Config.open()(采用运行时配置模式,svc.conf文件配置)加载生成的动态库(此处采用ACE自己的生产动态库的方式,参考书1,p24)(本分析采用默认选项:线程池,同步I/O)
2、http_server::init(),分析参数,根据参数选择并发策略(同步线程池、异步线程池、PER-REQUEST),生成工厂处理方法(Synch_HTTP_Handler_Factory ()、No_Cache_Synch_HTTP_Handler_Factory ())。
3、http_server::init()生成并发策略,调用http_server ->synch_thread_pool()。
4、http_server ->synch_thread_pool()开始侦听,HTTP_Server ->acceptor_.open()。
注意:不管采用什么并发策略都在server调用自己的函数生成并发策略时在自己的函数里面执行了端口侦听,也就是说open()由HTTP_Server完成。
5、http_server ->synch_thread_pool()构建线程池对象,Synch_Thread_Pool_Task t(),并把acceptor_的引用传递过来。然后等待所有的线程退出,ACE_Thread_Manager:tm_.wait (),程序进入循环等待状态。
6、Synch_Thread_Pool_Task的构造函数Synch_Thread_Pool_Task->activate()产生多个线程开始从SVC()开始执行。
注意:多个线程共用一个主动对象,共用一个消息队列,有同一个ACE_Thread_Manager:tm_进行管理,但每个线程有自己的堆。根据需要,可以设计自己的专有线程存储。
第二阶段:线程的SVC()
1、线程进入死循环
2、创建一个ACE_SOCK_Stream stream,然后this->acceptor_.accept (stream)。
注意这里使用的acceptor_是一个加锁的接收器,也就是多个线程在这里等待,仅有一个线程获取锁继续执行。因为线程池共用一个主动对象,所以也共用一个acceptor_,所以必须采用并发策略。采用同一个acceptor_,可以确保在同一个I/O端口上连接,这也是WEB服务器必须要求的。
3、第一步的2步骤生成的工厂Synch_HTTP_Handler_Factory .create_http_handler (),创建一个处理器。(在JAWS2中没有采用这个方法,由于它的面向对象做的更完美,都是通过JAWS_DATA_BLOCK块在流水线中执行完成的,不同的流水线模块完成不同的功能)
4、处理器开始处理到来的客户连接,handler->open (stream.get_handle (), *mb)。
第三阶段:处理器处理客户连接
1、根据需要对SOCK进行了一些设置,通过ACE_OS::setsockopt()。
2、设置接收流为acceptor_.accept (stream)函数连接的stream。
3、调用this->read_complete(),开始接收数据,并开始了一序列的处理。
注意:JAWS1中并没有采用流水线设计模式,但留出了接口虚函数。
4、主要读取客户申请头,并分析,HTTP_REQUEST.CPP,根据分析的头向客户回复HTTP_RESPONSE.CPP。
第四阶段:读取客户发来的数据,分析协议,并进行回复。(本人没有进行仔细分析,本人认为没有采用文件缓存策略)
注:其它并发方式和I/O方式代替响应的方式就可以了,流程没有大的变化,就是具体实现思想有点改变。
三 JAWS2程序分析
一 概述
JAWS2采用了更多的面向对象封装,增加了流水线处理(但跟书1上讲的实现方式不一样,它采用一个循环,不断的往下一下流模块中压入JAWS_DATA_BLOCK,通过改变JAWS_DATA_BLOCK中内容而改变需要完成的任务,而书1上讲的更制式一点)和文件缓存系统。
主要有4块内容设计:
1、JAWS_Dispatch_Policy(抽象工作模式)定义了:并发策略(Thread Pool Task和Thread Per Task)处理器工厂(生成响应的IO处理方式,采用了工厂方法模式)IO接收器(IO Acceptor的方式:Synch、Asynch)IO流接收方法(IO Stream)。
2、JAWS_IO_Handler
3、JAWS_Data_Block(最重要的数据块,主要通过它在流模块中的传递,实现各个流模块的具体功能)
4、JAWS pipeline Handler,流水线,具体的accept(),stream(),文件的传输都在里面实现(具体参加见参考1)
二 程序流程
第一阶段:main()->数据库的init(),在JAWS2源代码中没有实现main(),而仅仅有动态库的源代码。
1、根据配置文件或命令行参数完成dispatch policy结构的初始化(并发策略:policy_.concurrency(),I/O策略:policy_.io(),acceptor策略:policy_.acceptor(),工厂策略:policy_.ioh_factory())
注意:在JAWS2中更多的采用面向对象结构设计,上面的并发策略都采用单体模式设计。
2、并发策略调用make(),JAWS_Thread_Pool_Singleton::instance ()->make();make函数完成线程池的创建,activate();各个线程进入SVC(),并阻塞在getq (mb)调用上。
注意:在JAWS_Concurrency_Base::singleton_mb()中使用了一个小技巧,在这里使用了“守卫锁”实现并发,只有获取锁的线程才能够阻塞在getq (mb)调用上,而其它线程阻塞在“守卫锁”上。但这个程序就只往消息队列里面压了一个JAWS Data Block数据块,阻塞在getq (mb)上的线程获取以后,消息队列里面就没有了,其它线程就不能够获取这个数据块了,而这个数据块是驱动线程能够继续执行下去的条件。在源代码中,采用编程技巧,但阻塞在getq (mb)上的线程获取JAWS Data Block数据块后,把变量mb_acquired_=1,但其它阻塞在“守卫锁”上的线程获取锁后,首先判断这个mb_acquired变量,如果等于1,则不进行getq (mb)调用,而是直接把第一个线程获取的JAWS Data Block数据块返回。
3、acceptor策略开始open(),policy_.acceptor ()->open (inet_addr)。
注意:I/O策略并没有开始accept(),这些都在流水线中完成,通过向主动对象的队列压入数据块,唤醒阻塞的线程,然后线程调用流水线完成accept,客户的申请,客户的应答。
问题:在JAWS2源码中这时候init()就结束了,没有提供到JAWS_Server::open()的调用,而JAWS_Server::open()中才完成policy_.acceptor ()->accept ()。经过查资料,也没有找到在动态加载中默认调用open()方法,有兴趣的可以查一查资料,有没有这个默认调用,发我信箱共同讨论研究。本人现认为,要不在init()中包含到JAWS_Server::open()的调用,要不在main()中包含到JAWS_Server::open()的调用。
第二阶段:JAWS_Server::open()开始执行,完成ACCEPT,客户申请,客户应答等。
1、构建一个JAWS Data Block数据块,有三个变量:JAWS IO Handler(IO处理器)初始化为0,在accept接收以后,才通过象JAWS1那样的Factory工厂创建,并更改JAWS Data Block数据块中这个变量的值;JAWS Dispatch Policy(包含第一阶段初始化的4个策略方式),初始化为第一阶段生成的Policy对象。JAWS Pipeline Handler(管道处理器),初始化为管道的第一个模块JAWS_Pipeline_Accept_Task_Singleton)
注意:1、管道里面有很多模块,第一个模块完成accept接收,第二个模块到倒数第二个模块完成客户的申请,客户应答等,最后一个模块完成事后处理,把动态生成的对象释放等处理。
注意:2、程序在开始把数据块的指针指向第一个模块,处理完第一个模块后,又把数据块的指针指向第二个模块,由于在线程的SVC函数中执行了一个死循环,所以通过改变JAWS Data Block数据块中JAWS Pipeline Handler的参数,实现调用不同的模块完成不同的任务。(跟书1描述的流水线实现有区别,本人认为书1描述的实现方式结构化更好一点,更容易理解)
2、把JAWS Data Block数据块压入主动对象的消息队列,激活阻塞在上面的等待线程,policy->concurrency ()->put (db)。
3、程序进行等待所有的线程退出,ACE_Thread_Manager::instance ()->wait (),主程序结束。
第三阶段:线程从消息队列里面取出数据块进行处理
1、所有线程都从消息队列里面返回一个JAWS Data Block数据块。
2、SVC函数调用svc_loop函数,这是个死循环。主要调用svc_hook函数完成具体的功能。然后自己完成JAWS Data Block数据块中参数的变化,使再一次循环过来,svc_hook函数能够根据改变后的JAWS Data Block数据块中参数执行。
3、从线程池里面取出一个等待线程完成流水线的任务,在这里采用了线程池的半同步/半异步模型。(这一点没有仔细研究,有兴趣的可以仔细分析,通过JAWS_Waiter实现的)
4、把数据块压入JAWS Pipeline Handler模块,task->put (mb)。
第四阶段:模块开始根据消息块进行处理
1、put函数调用handle_put(),然后把消息块的task_指针改为下一模块,具体工作handle_put()完成。
2、第一个模块的handle_put()函数完成accept(),JAWS_IO *io = policy->io (),io->accept (handler)。然后退出,交给下一个模块进行处理。
注意:这里有好几个循环,本人没有仔细分析各个具体循环的用处,循环的主要目的是实现各个模块,完成流的功能,使用多个循环可能是具体管理上的编程技巧。
四JAWS3程序分析
书1:ACE程序员指南——网络与系统编程的实用设计模式
参考1:jaws 2: Refactorization Framework Design and Utilizatin Overview
apps/jaws2/
apps/jaws3/
是jaws的不同版本。我的这篇文章是以jaws的代码为参考写的。
有关jaws2的代码分析会陆续推出。
ace 目录中
apps/jaws/
apps/jaws2/
apps/jaws3/
下的内容有什么区别?
谢谢!