基于微线程的网络服务框架

前面的文章介绍了一个基于微线程的调度器框架,并测试了使用微线程实现远程调用的效率。
本文将微线程和网络事件框架结合起来,在微线程中处理所有的事件(网络消息,用户定时器事件),
这样,在事件回调函数中可以放心的发起远程调用而不用担心阻塞整个线程,只要还有未被阻塞的
微线程,就可以切换到那个微线程上去执行,继续等待在事件队列上处理新到的事件.

typedef struct coronet
{
    netservice_t    nets;
    msg_loop_t      msgl;
    sche_t          coro_sche;
    uint32_t        last_check_timer;
    TimingWheel_t   timer_ms;//精度50ms
    TimingWheel_t   timer_s; //精度1s
    TimingWheel_t   timer_m; //精度1分钟
}*coronet_t;

首先看下事件框架的定义,事件框架结构封装了一个微线程调度器,一个网络服务引擎,一个消息分发器,
还有3种不同精度的用户定时器.

使用者创建coronet后调用coronet_init_net绑定事件回调函数,之后调用coronet_init_coro创建微线程池,
然后调用coronet_run就可以进入事件循环.

下面看下微线程的主处理函数,这个函数要作为coronet_init_coro的第三个参数传入,跟线程的主函数有点类似,
微线程运行之后就会进入这个主函数,如下面的实现,这个主函数只是在一个循环中不断的提取消息并执行,如果
没有消息则会执行50ms的可被打断休眠(如果网络层有事件到达,休眠会被打断).

void* uthread_main_function(void *arg)
{
    coronet_t coron = (coronet_t)arg;
    while(0 == isstop)
    {
        //等待事件的到来并处理,如果没有事件休眠50ms
        peek_msg(coron,50);
    }
}

下面开看下peek_msg的实现:

void peek_msg(coronet_t coron,uint32_t ms)
{
    assert(coron);
    assert(coron->nets);
    assert(coron->msgl);
    assert(coron->coro_sche);
    coro_t co = get_current_coro();

    //首先检查是否有超时的coro,如果有唤醒,被唤醒后的coro会被投入到active_list_1
    check_time_out(coron->coro_sche,GetCurrentMs());
    //查看active_list_1中是否有coro等待执行,如果有,优先先执行coro
    _sche_next_1(coron->coro_sche,co);
    //检查用户定时器,如果有超时事件会触发一个消息并投递到消息队列中,后面的msg_loop_once会提取并执行
    coronet_check_user_timer(coron);
    //等待消息的到来
    msg_loop_once(coron->msgl,coron->nets,ms);    
}

这里先介绍下微线程调度器中的两个激活队列:active_list_1,active_list_2.激活队列分成两个优先级1为最高,2
为最低.其中2级队列存放所有普通的激活队列,例如微线程被创建之后,就会被投入到2级激活队列中。1级优先队列
存放的是优先级更高的微线程,例如rpc调用超时,sleep超时,还有收到rpc回应的微线程.调度器选择下一个被处理
的微线程时首先会从1级队列中提取,当1级队列为空时才调度2级队列中的微线程.

好了回到peek_msg,首先是检查是否有超时的微线程,然后执行_sche_next_1,_sche_next_1会尝试调度1级队列中的
微线程,如果1级队列中没有可以调度的对象,则调用msg_loop_once继续处理事件.

(在这里必须介绍下_sche_next_1的调度策略,它仅仅查看1级队列中是否有可调度对象,如果有则调度运行,没有立即返回,
这样做大大减少了不必要的微线程切换)

下面再来看下rpc的处理:

void process_rpc_return(rpacket_t r)
{
    coro_t co = get_current_coro();
    coro_t co_wakeup = (coro_t)rpacket_read_uint32(r);
    if(rpk_check(co_wakeup,r))
    {
        co_wakeup->rpc_response = r;
        co_wakeup->_goback = co;
        //直接跳过去执行co_wakeup
        set_current_coro(co_wakeup);
        uthread_switch(co->ut,co_wakeup->ut,co);
    }        
}

process_rpc_return在网络包处理回调函数中被调用,当发现一个网络包代表了rpc回应时,就可以调用process_rpc_return
来处理这个rpc回应.这里的处理是设置一些成员变量,然后直接切换到需要被唤醒的微线程的上下文中继续执行.这里着重介绍
_goback成员,_goback成员被设置成从哪个微线程切换到目标微线程的。当目标微线程在执行的过程中,需要主动把执行权切换
出去的时候会首先切换到_goback所指向的微线程去.

项目地址如下:
https://github.com/sniperHW/kendylib/blob/master/netframework/coronet.h

posted @ 2012-12-04 17:34  sniperHW  阅读(3549)  评论(4编辑  收藏  举报