基於用戶級線程的遠程調用效率測試

將用戶級線程添加到我的工具庫中,主要的目的就是用於實現同步遠程調用接口。這裏的同步,是指在調用返回或超時之前,用戶級線程的執行路徑

阻塞在調用接口上,但其底層的線程並不會阻塞,可以繼續其它的工作。

基於這個結構,我的服務器主線程將運行一個用戶級線程調度器,並預先創建一組用戶級線程池。當收從網絡層收到一個網絡消息時,從線程池中取出

一個空閒的線程,將消息交給它處理。這個線程在執行的過程中如果發生了阻塞調用,就將運行權交換給調度器,由調度器挑選下一個可用的線程執行。

阻塞線程直到超時,或被通知可以解除阻塞才被重新投入到調度器的可運行隊列中。

下面貼出一個簡單的測試代碼,用於測試調用的效率。

首先是兩個遠程函數,一個用於求兩數之和,一個用於求兩數之積:

複製代碼
static inline int sum(int32_t arg1,int32_t arg2)
{
    coro_t co = get_current_coro();
    wpacket_t wpk = wpacket_create(1,wpacket_allocator,64,0);
    wpacket_write_uint32(wpk,(int32_t)co);
    wpacket_write_string(wpk,"sum");
    wpacket_write_uint32(wpk,arg1);
    wpacket_write_uint32(wpk,arg2);
    send_packet(g_channel,wpk);
    //send and block until the response from rpc server
    coro_block(co);
    int ret = rpacket_read_uint32(co->rpc_response);
    rpacket_destroy(&co->rpc_response);
    return ret;
}

static inline int product(int32_t arg1,int32_t arg2)
{
    coro_t co = get_current_coro();
    wpacket_t wpk = wpacket_create(1,NULL,64,0);
    wpacket_write_uint32(wpk,(int32_t)co);
    wpacket_write_string(wpk,"product");
    wpacket_write_uint32(wpk,arg1);
    wpacket_write_uint32(wpk,arg2);    
    send_packet(g_channel,wpk);
    //send and block until the response from rpc server
    coro_block(co);
    int ret = rpacket_read_uint32(co->rpc_response);
    rpacket_destroy(&co->rpc_response);
    return ret;
}
複製代碼

函數都很簡單,將線程id,要求調用的函數名和參數打包併發送給服務器,然後調用coro_block阻塞在上面。當線程重新被喚醒時從應答包中讀出返回值

並從函數返回.

下面是服務器對這兩個遠程調用的處理:

複製代碼
void send2_client(rpacket_t r)
{

    uint32_t coro_id = rpacket_read_uint32(r);
    const  char *function_name = rpacket_read_string(r);
    int32_t arg1 = rpacket_read_uint32(r);
    int32_t arg2 = rpacket_read_uint32(r);
    uint32_t i = 0;
    wpacket_t w;
    for(; i < MAX_CLIENT; ++i)
    {
        if(clients[i])
        {
            w = wpacket_create(0,wpacket_allocator,64,0);
            wpacket_write_uint32(w,coro_id);
            if(strcmp(function_name,"sum") == 0)
                wpacket_write_uint32(w,arg1+arg2);
            else
                wpacket_write_uint32(w,arg1*arg2);
            assert(w);
            connection_send(clients[i],w,NULL);
        }
    }
}
複製代碼

如代碼所示,服務器將調用者的id,函數名和參數取出,執行所要求的計算,然後將計算結果發回給客戶端.http://11bmw.com/zuqiudanchang/

下面再看下客戶端的邏輯主循環:

複製代碼
void *logic_routine(void *arg)
{
    uint32_t tick = GetSystemMs();
    while(1)
    {
        rpacket_t rpk = peek_msg(g_channel,50);
        if(rpk)
        {
            coro_t co = (coro_t)rpacket_read_uint32(rpk);
            co->rpc_response = rpk;
            coro_wakeup(co);
        }
        uint32_t now = GetSystemMs();
        if(now - tick > 1000)
        {
            printf("call_count:%u\n",call_count);
            tick = now;
            call_count = 0;
        }
        sche_schedule(g_sche);
    }
}
複製代碼

主循環不斷的嘗試從網絡層消息隊列中獲取應答包,接到應答包之後就對應的用戶級線程喚醒,然後執行調度器。

在我的實驗環境:一臺i5 2.6GZ的雙核筆記上,同時運行服務器和客戶端,創建15000個用戶級線程,平均每秒的遠程調用次數大概在60W左右.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章