免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 51931 | 回复: 52

SPServer: 一个基于线程池(包括HAHS和LF)的开源服务器框架 [复制链接]

论坛徽章:
0
发表于 2007-07-04 19:32 |显示全部楼层
SPServer 是一个实现了半同步/半异步(Half-Sync/Half-Async)和领导者/追随者(Leader/Follower) 模式的服务器框架,能够简化 TCP server 的开发工作。

SPServer 使用 c++ 实现,目前实现了以下功能:
1.封装了 TCP server 中接受连接的功能;
2.使用非阻塞型I/O和事件驱动模型,基于 libevent;
3.对于 HSHA 线程池,由主线程负责处理所有 TCP 连接上的数据读取和发送,因此连接数不受线程数的限制;主线程读取到的数据放入队列,由一个线程池处理实际的业务;
4.对于 LF 线程池,由线程池中的线程轮流获得 leader 角色进行处理;
5.一个 http 服务器框架,即嵌入式 web 服务器。
6.从 0.7 版本开始支持 ssl 。

项目主页
http://code.google.com/p/spserver/

下载地址
http://spserver.googlecode.com/files/spserver-0.6.src.tar.gz
http://code.google.com/p/spserver/downloads/list

详细的介绍
http://iunknown.javaeye.com/blog/59804

HSHA 模式
http://www.cs.wustl.edu/~schmidt/PDF/HS-HA.pdf

LF 模式
http://www.cs.wustl.edu/~schmidt/PDF/lf.pdf

关于 HSHA 和 LF 的一些介绍
http://iunknown.javaeye.com/blog/60414

项目中包含一个线程池子项目
http://iunknown.javaeye.com/blog/38544

[ 本帖最后由 iunknown 于 2007-8-11 13:36 编辑 ]

论坛徽章:
0
发表于 2007-07-04 20:32 |显示全部楼层
小声的说,LZ的博客在我家FF的链接中保存着....

论坛徽章:
0
发表于 2007-07-05 13:20 |显示全部楼层
原帖由 converse 于 2007-7-4 20:32 发表
小声的说,LZ的博客在我家FF的链接中保存着....


多谢!你这样说,我很大压力啊。。。。。

关于 HSHA , LF 线程池,还有 reactor ,proactor 这些模式,目前能找到的参考实现有 ACE 和 twisted 。
不知这里有没有人做过类似的框架?或者有没有人希望使用这样的框架?希望能获得大家的建议,继续完善。

[ 本帖最后由 iunknown 于 2007-7-5 13:21 编辑 ]

论坛徽章:
0
发表于 2007-07-06 13:48 |显示全部楼层

Half-Sync/Half-Async 和 Leader/Follower 模式的实现代码

SPServer 中实现了 HSHA 和 LF 两种线程池。

目前的实现还是比较可读的,这两种线程池最主要的处理逻辑各自都被集中到了一个函数中。

先来看看 HSHA 的核心实现代码

http://spserver.googlecode.com/svn/trunk/spserver.cpp


  1. int SP_Server :: start()
  2. {
  3. ......

  4.                 SP_Executor workerExecutor( mMaxThreads, "work" );
  5.                 SP_Executor actExecutor( 1, "act" );

  6.                 /* Start the event loop. */
  7.                 while( 0 == mIsShutdown ) {
  8.                         event_base_loop( eventArg.getEventBase(), EVLOOP_ONCE );

  9.                         for( ; NULL != eventArg.getInputResultQueue()->top(); ) {
  10.                                 SP_Task * task = (SP_Task*)eventArg.getInputResultQueue()->pop();
  11.                                 workerExecutor.execute( task );
  12.                         }

  13.                         for( ; NULL != eventArg.getOutputResultQueue()->top(); ) {
  14.                                 SP_Message * msg = (SP_Message*)eventArg.getOutputResultQueue()->pop();
  15.                                
  16.                                 ......

  17.                                 actExecutor.execute( outputCompleted, arg );
  18.                         }
  19.                 }

  20. ......
  21. }
复制代码


一些细节都被去掉了。从这段代码可以看到,HSHA 的处理流程是:
1.运行 start 函数的线程称为 event_loop 线程,负责 recv/send 。
  所有的 recv/send 都在 event_base_loop 这个函数调用上完成的。
  这个层就是属于异步层。
2. event_base_loop 在 recv 的时候,会调用 MsgDecoder.decode 函数,
  如果 decode 返回 OK ,说明完整地读入数据了,那么就把对应的数据放入
  eventArg.mInputResultQueue 里面。
  在 send 的时候,如果把一个 Message 完整地发送了,
  那么就把这个 Message 放入 eventArg.mOutputResultQueue。
  这两个就是队列,队列里面保存的数据一般称为完成事件。
3.workerExecutor 和 actExecutor 就是同步层。
  由于完成事件的处理可能会涉及很复杂的处理,可能会使用到数据库或者其他,
  因此不能直接使用 event_loop 线程,而是使用线程池。
  这个就是同步层。

再来看 LF 的核心代码

http://spserver.googlecode.com/svn/trunk/splfserver.cpp


  1. int SP_LFServer :: run()
  2. {
  3. ......
  4.                 mThreadPool = new SP_ThreadPool( mMaxThreads );
  5.                 for( int i = 0; i < mMaxThreads; i++ ) {
  6.                         mThreadPool->dispatch( lfHandler, this );
  7.                 }
  8. ......
  9. }

  10. void SP_LFServer :: lfHandler( void * arg )
  11. {
  12.         SP_LFServer * server = (SP_LFServer*)arg;

  13.         for( ; 0 == server->mIsShutdown; ) {
  14.                 /* follower begin */
  15.                 server->handleOneEvent();
  16.         }
  17. }

  18. void SP_LFServer :: handleOneEvent()
  19. {
  20.         SP_Task * task = NULL;
  21.         SP_Message * msg = NULL;

  22.         /* follower wait */

  23.         pthread_mutex_lock( &mMutex );
  24.        
  25.         /* follower end */

  26.         /* leader begin */

  27.         for( ; 0 == mIsShutdown && NULL == task && NULL == msg; ) {
  28.                 if( mEventArg->getInputResultQueue()->getLength() > 0 ) {
  29.                         task = (SP_Task*)mEventArg->getInputResultQueue()->pop();
  30.                 } else if( mEventArg->getOutputResultQueue()->getLength() > 0 ) {
  31.                         msg = (SP_Message*)mEventArg->getOutputResultQueue()->pop();
  32.                 }

  33.                 if( NULL == task && NULL == msg ) {
  34.                         event_base_loop( mEventArg->getEventBase(), EVLOOP_ONCE );
  35.                 }
  36.         }
  37.        
  38.         /* leader end */

  39.         pthread_mutex_unlock( &mMutex );


  40.         /* worker begin */

  41.         if( NULL != task ) task->run();

  42.         if( NULL != msg ) mCompletionHandler->completionMessage( msg );
  43.        
  44.         /* worker end */
  45. }
复制代码


在 run 函数中,启动线程池中的多个线程运行 lfHandler ,
由 lfHandler 不断地运行 handleOneEvent 。
线程的角色转变在上面的注释中可以很清楚地看到。

由于这里的实现比较简单,LF 线程池的线程都是预先创建的,
并且没有实现线程池之外的线程可以加入的功能,
所以在 leader 切换为 worker,并且提升一个 follower 为 leader 的时候,
只需要用一个 mutex 就可以解决了。

论坛徽章:
0
发表于 2007-07-06 14:32 |显示全部楼层
下了编译不过.
localhost spserver-0.6 # make
gcc -Wall -D_REENTRANT -D_GNU_SOURCE -g -fPIC -I../libevent/ -c event_msgqueue.c -o event_msgqueue.o
event_msgqueue.c:18:20: error: config.h: No such file or directory
In file included from event_msgqueue.c:19:
event_msgqueue.h:15:19: error: event.h: No such file or directory
In file included from event_msgqueue.c:19:
event_msgqueue.h:24: warning: ‘struct event_base’ declared inside parameter list
event_msgqueue.h:24: warning: its scope is only this definition or declaration, which is probably not what you want
event_msgqueue.c:37: error: field ‘queue_ev’ has incomplete type
event_msgqueue.c:161: warning: ‘struct event_base’ declared inside parameter list
event_msgqueue.c:161: error: conflicting types for ‘msgqueue_new’
event_msgqueue.h:24: error: previous declaration of ‘msgqueue_new’ was here
event_msgqueue.c: In function ‘msgqueue_new’:
event_msgqueue.c:187: warning: implicit declaration of function ‘event_set’
event_msgqueue.c:187: error: ‘EV_READ’ undeclared (first use in this function)
event_msgqueue.c:187: error: (Each undeclared identifier is reported only once
event_msgqueue.c:187: error: for each function it appears in.)
event_msgqueue.c:187: error: ‘EV_PERSIST’ undeclared (first use in this function)
event_msgqueue.c:188: warning: implicit declaration of function ‘event_base_set’
event_msgqueue.c:189: warning: implicit declaration of function ‘event_add’
event_msgqueue.c: In function ‘msgqueue_destroy’:
event_msgqueue.c:202: warning: implicit declaration of function ‘event_del’
make: *** [event_msgqueue.o] Error 1

论坛徽章:
0
发表于 2007-07-06 15:26 |显示全部楼层
原帖由 iunknown 于 2007-7-5 13:20 发表


多谢!你这样说,我很大压力啊。。。。。

关于 HSHA , LF 线程池,还有 reactor ,proactor 这些模式,目前能找到的参考实现有 ACE 和 twisted 。
不知这里有没有人做过类似的框架?或者有没有人希 ...


I've done the reactor service frame

论坛徽章:
0
发表于 2007-07-06 19:26 |显示全部楼层
原帖由 ruchong 于 2007-7-6 14:32 发表
下了编译不过.
localhost spserver-0.6 # make
gcc -Wall -D_REENTRANT -D_GNU_SOURCE -g -fPIC -I../libevent/ -c event_msgqueue.c -o event_msgqueue.o
event_msgqueue.c:18:20: error: config.h: No suc ...


请阅读 README 文件,请先检查是否安装了 libevent,确认安装之后,修改 Makefile 指定 libevent 的路径。

2.Building

Before building spserver, libevent must been installed. Test with libevent 1.1 and 1.2.

You can donwload libevent from its home page:

        http://www.monkey.org/~provos/libevent/

Edit spserver/Makefile to specify the path of libevent:

        LIBEVENT_INCL = -I<path_to_libevent_include>
        LIBEVENT_LIB  = -L<path_to_libevent_library> -levent

[ 本帖最后由 iunknown 于 2007-7-6 19:53 编辑 ]

论坛徽章:
0
发表于 2007-07-06 19:41 |显示全部楼层
原帖由 清汤挂面 于 2007-7-6 15:26 发表


I've done the reactor service frame


reactor 是按 ACE 的接口来实现,还是按 libevent 的接口来实现?
ACE 使用的是 OO 的接口,libevent 使用的是 C 的 callback 。

ACE 基于 OO 的接口

  1. class Event_Handler {
  2. pupblic:
  3.     virtual void handle_input( HANDLE handle ) = 0;
  4.     virtual void handle_output( HANDLE handle ) = 0;
  5.     virtual void handle_timeout( const Time_Value & ) = 0;
  6.     virtual void handle_close( HANDLE handle, Event_Type et ) = 0;
  7. };
复制代码


libevent 基于 C 的 callback 的接口

  1. void event_set(struct event * ev, int fd, short event,
  2. void ( * fn)(int, short, void * ), void * arg);
复制代码


fn 就是 calback,通常的实现类似

  1. void fifo_read(int fd, short event, void *arg);
复制代码


libevent 的实现方式,其实类似于 《unix网络编程》 (第二版,中文版) Page 141 和 Page 148 的代码。可以看看这里的说明:http://iunknown.javaeye.com/blog/41077

一般情况下,如果业务处理逻辑不涉及过多的 IO 操作,使用单线程运行reactor 的效果是非常好的。比如 memcached 在 1.2 之前的版本就是单线程运行 libevent 实现的,性能非常好。在 memcached 中只有网络请求和响应是 IO 操作,内部的处理逻辑只是 HASH 表的查找和维护,不会造成阻塞,因此使用单线程是完全没有问题的。

但如果业务处理逻辑还涉及很多其他的 IO 操作的话,那么或者把这些 IO 操作也纳入 reactor 的控制,要么就需要使用多线程了。但在某些情况下,业务中涉及的 IO 操作是类似数据库查询这样的 IO 操作,这些 IO 操作很难纳入 reactor 的控制,就要使用多线程了。

[ 本帖最后由 iunknown 于 2007-7-6 19:49 编辑 ]

论坛徽章:
0
发表于 2007-07-06 22:09 |显示全部楼层

学习一下

学习一下设计思路.

论坛徽章:
0
发表于 2007-07-07 09:10 |显示全部楼层

SPServer 的 demo

在 SPServer 中自带了几个使用 SPServer 框架实现的 demo :testecho.cpp ,testsmtp.cpp , testchat.cpp ,testhttp.cpp 。

testecho.cpp 是 echo 服务器。http://spserver.googlecode.com/svn/trunk/testecho.cpp
testchat.cpp 是 chatroom 服务器。http://spserver.googlecode.com/svn/trunk/testchat.cpp
testsmtp.cpp 是 fake smtp 服务器。http://spserver.googlecode.com/svn/trunk/testsmtp.cpp
testhttp.cpp 是 echo http 服务器,把接收到的 http 请求解释出来,把请求的信息输出为一个 html 返回。
http://spserver.googlecode.com/svn/trunk/testhttp.cpp

echo 服务器和 chatroom 服务器应该是很多学网络编程的人完成的第一个完整的应用程序。
使用 SPServer 框架实现的这两个服务器,主要的特点就是开发者(指框架的使用者,不是框架的实现者)只需要处理内存对象,而不需要处理网络 IO ,所有的网络 IO 都由 SPServer 框架来处理。在 echo 这种相对比较简单的服务器上,对开发者屏蔽网络 IO 带来的好处可能不见的很大,但是对于 chatroom 这类相对复杂的服务器,对开发者屏网络 IO 就可以带来很大的好处,开发者可以专住于实现 chatroom 的业务逻辑,开发者所面对的都是内存对象。

testsmtp.cpp 主要是用来演示对于 smtp 这样的协议,SPServer 是怎么支持的。testsmtp 实现的是一个空的 smtp 服务器,能够完成标准的 smtp 应答,不过实际上内部什么事都没干,

以上这些 demo 都可以使用自带的 teststress 测试 工具来做压力测试。

  1. bash-2.05a$ ./testecho

  2. bash-2.05a$ ./teststress -p 3333 -c 100 -m 100
  3. Create 100 connections to server, it will take some minutes to complete.
  4. ..........

  5. Test result :
  6. Clients : 100, Messages Per Client : 100
  7. ExecTimes: 5.308419 seconds

  8. client  Send    Recv
  9. total : 10000   10100
复制代码



testhttp.cpp 是一个 echo http 服务器。可以使用 apache 自带的 ab 工具对 testhttp.cpp 进行测试。
bash-2.05a$ ./testhttp -s lf -t 1  
  
bash-2.05a$ ./ab -n 10000 -c 100 -k http://127.0.0.1:8080/
  
Requests per second:    4576.40 [#/sec] (mean)  

[ 本帖最后由 iunknown 于 2007-7-7 09:23 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP