论文部分内容阅读
云存储客户端接口的框架(CME)是用C语言编写的并行程序处理框架。目的是实现一种可以应付大规模并发业务的编程框架,可以使用C和C++语言在此框架上进行定制业务的二次开发。CME对于框架基础上的二次开发提倡使用并发程序的设计方式,方便开发者根据业务逻辑的顺序来设计,CME框架的内部则使用事件通知等异步机制来构架的,用于提高框架的整体并发性能。本文将对CME的基本结构进行介绍。
1.设计思路
原先接触的大规模并发的服务器框架大体分两种:单进程多线程的多线程同步框架和单进程少数线程的异步通知框架。
多线程同步框架的程序开发相对简单,只需要根据业务的逻辑顺序来开辟对应的线程,每个线程内部完成对应的业务逻辑,这样能够快速简单地开发业务程序。但是线程间的交互需要格外的小心,在数据同步上有可能需要使用锁等机制来相互制约。并且如果线程的数量太多的话,线程之间的切换也会给CPU带来不小的负担。由于上述的缺点,所以多线程同步框架开发出来的服务器虽然稳定,但是在并发性能上没有下面介绍的异步通知框架开发出的服务器高效。
异步通知框架是把10等费时操作中的等待返回时间利用起来去执行其他的语句,等费时操作的结果消息通知过来后再执行后续的操作。把原本可以独立的业务逻辑都放在了单个或者几个线程里去完成,这样做的好处在于少量线程内的操作减少了线程之间同步的时间,也减少了CPU的线程切换的压力,所以可以开发出比较高效的并发服务器。但是相对的程序的编写复杂度也大大的提高了。
CME则是想结合上述两种框架的方案,实现一个相对高效但又方便二次开发的框架,在CME内部减少线程的使用,通过异步通知的方式来实现网络10、文件10等费时操作。而在CME的业务实现接口层则实现伪线程,让开发人员可以根据多线程的思维方式来编写业务逻辑层的代码。
2.概要
CME分二个大的模块:事件触发模块与业务逻辑模块,结构如图1所示。
图1中的业务逻辑模块和事件触发模块都是单个或者几个线程的结构(线程个数由CPU的个数定)。业务逻辑模块把需要等待的费时操作注册到事件触发模块,事件触发模块把完成的费时操作通知给业务逻辑模块。二个模块之间通过带锁队列来传输消息。
这样的设计与上述的异步通知框架有点不同,这里把业务层与基础事件层隔离开来,这么设计的原因在于业务层所占用的CPU时间远比基础事件(网络10,文件10等)所占用的CPU时间多,为了让基础事件能够高效地接收完成消息,不被业务层减缓处理速度,就把业务层与基础事件层隔离开来。
3.事件触发模块
事件触发模块的功能是注册IO等费时操作,并通过消息等OS提供的接口获得该操作的结果,把操作的结果封装成业务逻辑模块能够解析的结果发送给业务逻辑模块。事件触发模块又根据不同的功能和实现可以分为:网络IO事件触发模块、文件IO事件触发模块和定时器模块等(根据不同的硬件需求可以定制不同的事件触发模块)。
3.1网络IO事件模块
网络IO事件模块:使用的是EPOLL和IOCP技术实现,EPOLL是Linux核心的可扩展IO事件通知机制。IOCP则是Windows下的一种高性能的IO模型。它们都提供了网络句柄的注册接口和事件等待触发接口。
外部调用者通过注册接口把网络句柄与要做的事情(网络事件:监听、链接、读、写)注册入网络IO事件触发器,事件等待触发接口在获得网络事件的完成消息后在把结果打包返回给外部调用者,具体的流程如图2所示。
3.2定时器模块
定时器模块:目前的实现是通过一棵红黑树对注册进来的定时事件进行排序,然后通过一个50毫秒触发一次的检查轮询,查找出过期的事件并把这些需要触发的事件返回给注册方,它的注册接口不像网络IO事件触发模块有现成的注册接口,所以使用了一个带锁的队列来接收和获取注册消息。由于定时器模块的使用频率不是很高,所以在设计上只使用了一个线程来实现,具体的流程如图3所示。
3.3文件IO事件模块
文件IO事件模块由于在云存储的项目里没有具体的使用场景,所以暂时未实现,这里只是列举出了一些想法:
注册接口部分想使用和定时器模块一样的带锁的队列来实现注册消息的接收和获取。
内部由于OS(Windows和Linux)提供的文件操作接口都是阻塞的,没有像网络IO一样有现成的异步模型,所以还是想使用N个线程(N为CPU的个数)来进行文件的操作并把操作后的结果返回给注册方,最后由注册接口部分来进行线程间文件操作的动态均衡,具体的流程如图4所示。
4.业务逻辑模块
业务逻辑模块的功能则是提供接口给开发人员根据不同业务需求进行二次开发。在业务逻辑模块里使用了一个轻量级线程的概念,它的使用是为了实现二次开发的方便性。
业务逻辑模块的结构是由N个线程(N为CPU的个数)组成,它们的注册接口则是由一个带锁的队列来接收和获取返回的消息内容,具体的流程图5所示。
4.1轻量级线程
在业务逻辑的一个线程里,使用了一个轻量级的线程,它的作用就是在业务函数调用到IO等费时操作时,在把这类操作注册到事件触发模块后保存当前函数调用栈,然后退回到线程的入口去执行其他的业务函数。当获取到事件触发模块发来的费时操作的结果后,则再恢复保存的函数调用栈,继续执行这个业务函数的余下的语句。当然上述的操作则都是业务逻辑模块内负责完成的功能。对于二次开发者来说,他们只需要把这个轻量级的线程理解为一个线程而已,那些费时操作看做同步的接口即可,业务逻辑模块会在通过内部的上下文切换来实现看似同步的调用。
轻量级的线程的好处在于可以把一些函数栈的复制与恢复在模块层实现,如果没有这个功能,则需要二次开发者在把费时操作注册给事件触发模块时自己保存需要的临时变量和回调函数,在获取到费时操作结果后也需要自己恢复临时变量,并通过回调函数来运行对应的余下操作。这样给二次开发人员带来了很多要注意的地方,因而提高了程序的开发和维护成本。
轻量级线程的实现:在Windows采用fiber库,在Linux采用setcontext库。
5.结语
以上则是云存储客户端接口的框架的基础设计,云存储客户端接口则是在其上进行的二次开发。CME目前还只是一个雏形,其中还存在很多可以改进的地方:
首先,定时器模块目前是使用红黑树来排序的,也可以测试一下用跳跃表来排序,比较下哪种结构更高效。
其次,轻量级线程的实现依赖于现成的库,但这些库还是有一些的限制的,如fiber库对创建的轻量级线程个数就有限制,目前只支持1000多个。还有这些库对函数栈的拷贝是全部拷贝的,以后可以修改成需要部分拷贝等功能,减少CPU开销提高性能。
第三,业务逻辑模块和事件触发模块之间的交互目前是使用最简单的带锁队列来实现,之后可以根据不同注册和获取规律来使用读写锁,或者采用其他的线程间通讯的方式。
第四,CME目前还只是单进程的框架,以后还可以把业务逻辑模块与事件触发模块分配到不同的进程里实现,实现进程分离。
遗憾的是目前云存储的客户端接口还未完成,不能给出性能测试数据。
1.设计思路
原先接触的大规模并发的服务器框架大体分两种:单进程多线程的多线程同步框架和单进程少数线程的异步通知框架。
多线程同步框架的程序开发相对简单,只需要根据业务的逻辑顺序来开辟对应的线程,每个线程内部完成对应的业务逻辑,这样能够快速简单地开发业务程序。但是线程间的交互需要格外的小心,在数据同步上有可能需要使用锁等机制来相互制约。并且如果线程的数量太多的话,线程之间的切换也会给CPU带来不小的负担。由于上述的缺点,所以多线程同步框架开发出来的服务器虽然稳定,但是在并发性能上没有下面介绍的异步通知框架开发出的服务器高效。
异步通知框架是把10等费时操作中的等待返回时间利用起来去执行其他的语句,等费时操作的结果消息通知过来后再执行后续的操作。把原本可以独立的业务逻辑都放在了单个或者几个线程里去完成,这样做的好处在于少量线程内的操作减少了线程之间同步的时间,也减少了CPU的线程切换的压力,所以可以开发出比较高效的并发服务器。但是相对的程序的编写复杂度也大大的提高了。
CME则是想结合上述两种框架的方案,实现一个相对高效但又方便二次开发的框架,在CME内部减少线程的使用,通过异步通知的方式来实现网络10、文件10等费时操作。而在CME的业务实现接口层则实现伪线程,让开发人员可以根据多线程的思维方式来编写业务逻辑层的代码。
2.概要
CME分二个大的模块:事件触发模块与业务逻辑模块,结构如图1所示。
图1中的业务逻辑模块和事件触发模块都是单个或者几个线程的结构(线程个数由CPU的个数定)。业务逻辑模块把需要等待的费时操作注册到事件触发模块,事件触发模块把完成的费时操作通知给业务逻辑模块。二个模块之间通过带锁队列来传输消息。
这样的设计与上述的异步通知框架有点不同,这里把业务层与基础事件层隔离开来,这么设计的原因在于业务层所占用的CPU时间远比基础事件(网络10,文件10等)所占用的CPU时间多,为了让基础事件能够高效地接收完成消息,不被业务层减缓处理速度,就把业务层与基础事件层隔离开来。
3.事件触发模块
事件触发模块的功能是注册IO等费时操作,并通过消息等OS提供的接口获得该操作的结果,把操作的结果封装成业务逻辑模块能够解析的结果发送给业务逻辑模块。事件触发模块又根据不同的功能和实现可以分为:网络IO事件触发模块、文件IO事件触发模块和定时器模块等(根据不同的硬件需求可以定制不同的事件触发模块)。
3.1网络IO事件模块
网络IO事件模块:使用的是EPOLL和IOCP技术实现,EPOLL是Linux核心的可扩展IO事件通知机制。IOCP则是Windows下的一种高性能的IO模型。它们都提供了网络句柄的注册接口和事件等待触发接口。
外部调用者通过注册接口把网络句柄与要做的事情(网络事件:监听、链接、读、写)注册入网络IO事件触发器,事件等待触发接口在获得网络事件的完成消息后在把结果打包返回给外部调用者,具体的流程如图2所示。
3.2定时器模块
定时器模块:目前的实现是通过一棵红黑树对注册进来的定时事件进行排序,然后通过一个50毫秒触发一次的检查轮询,查找出过期的事件并把这些需要触发的事件返回给注册方,它的注册接口不像网络IO事件触发模块有现成的注册接口,所以使用了一个带锁的队列来接收和获取注册消息。由于定时器模块的使用频率不是很高,所以在设计上只使用了一个线程来实现,具体的流程如图3所示。
3.3文件IO事件模块
文件IO事件模块由于在云存储的项目里没有具体的使用场景,所以暂时未实现,这里只是列举出了一些想法:
注册接口部分想使用和定时器模块一样的带锁的队列来实现注册消息的接收和获取。
内部由于OS(Windows和Linux)提供的文件操作接口都是阻塞的,没有像网络IO一样有现成的异步模型,所以还是想使用N个线程(N为CPU的个数)来进行文件的操作并把操作后的结果返回给注册方,最后由注册接口部分来进行线程间文件操作的动态均衡,具体的流程如图4所示。
4.业务逻辑模块
业务逻辑模块的功能则是提供接口给开发人员根据不同业务需求进行二次开发。在业务逻辑模块里使用了一个轻量级线程的概念,它的使用是为了实现二次开发的方便性。
业务逻辑模块的结构是由N个线程(N为CPU的个数)组成,它们的注册接口则是由一个带锁的队列来接收和获取返回的消息内容,具体的流程图5所示。
4.1轻量级线程
在业务逻辑的一个线程里,使用了一个轻量级的线程,它的作用就是在业务函数调用到IO等费时操作时,在把这类操作注册到事件触发模块后保存当前函数调用栈,然后退回到线程的入口去执行其他的业务函数。当获取到事件触发模块发来的费时操作的结果后,则再恢复保存的函数调用栈,继续执行这个业务函数的余下的语句。当然上述的操作则都是业务逻辑模块内负责完成的功能。对于二次开发者来说,他们只需要把这个轻量级的线程理解为一个线程而已,那些费时操作看做同步的接口即可,业务逻辑模块会在通过内部的上下文切换来实现看似同步的调用。
轻量级的线程的好处在于可以把一些函数栈的复制与恢复在模块层实现,如果没有这个功能,则需要二次开发者在把费时操作注册给事件触发模块时自己保存需要的临时变量和回调函数,在获取到费时操作结果后也需要自己恢复临时变量,并通过回调函数来运行对应的余下操作。这样给二次开发人员带来了很多要注意的地方,因而提高了程序的开发和维护成本。
轻量级线程的实现:在Windows采用fiber库,在Linux采用setcontext库。
5.结语
以上则是云存储客户端接口的框架的基础设计,云存储客户端接口则是在其上进行的二次开发。CME目前还只是一个雏形,其中还存在很多可以改进的地方:
首先,定时器模块目前是使用红黑树来排序的,也可以测试一下用跳跃表来排序,比较下哪种结构更高效。
其次,轻量级线程的实现依赖于现成的库,但这些库还是有一些的限制的,如fiber库对创建的轻量级线程个数就有限制,目前只支持1000多个。还有这些库对函数栈的拷贝是全部拷贝的,以后可以修改成需要部分拷贝等功能,减少CPU开销提高性能。
第三,业务逻辑模块和事件触发模块之间的交互目前是使用最简单的带锁队列来实现,之后可以根据不同注册和获取规律来使用读写锁,或者采用其他的线程间通讯的方式。
第四,CME目前还只是单进程的框架,以后还可以把业务逻辑模块与事件触发模块分配到不同的进程里实现,实现进程分离。
遗憾的是目前云存储的客户端接口还未完成,不能给出性能测试数据。