亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

FFRPC應用 Client/Server使用及原理解析

2020-01-26 13:25:23
字體:
來源:轉載
供稿:網友

摘要:

Ffrpc 進行了重構,精簡了代碼,代碼更加清晰簡潔,幾乎完美的達到了我的預想。接下來將寫幾遍文章來介紹ffrpc可以做什么。簡單總結ffrpc的特性是:

  • Ffrpc是c++ 網絡通信庫
  • 全異步 + 回調函數 機制
  • 支持普通二進制協議、protobuf、thrift
  • 基于Broker模式設計
  • 設計精巧,代碼量小,核心ffrpc的代碼只有1000行
  • 接口的性能監控是集成式的,使用者自動獲得了接口性能數據,方便優化接口

普通二進制協議示例

Ffrpc實現了一個最基本的二進制序列化方法,基本的原理就是如果是固定長度那么就直接拷貝,如果是字符串,就先拷貝長度再拷貝內容。所以只支持向后擴展字段,對其他語言支持也不方便,但如果只是c++語言間傳遞消息,則顯得非常的方便和高效。比如網游服務器中各個進程的通信可以采用這種最簡單的二進制協議。Ffrpc中定義了一個工具類ffmsg_t來定義二進制消息.

消息定義:

struct echo_t{  struct in_t: public ffmsg_t<in_t>  {    void encode()    {      encoder() << data;    }    void decode()    {      decoder() >> data;    }    string data;  };  struct out_t: public ffmsg_t<out_t>  {    void encode()    {      encoder() << data;    }    void decode()    {      decoder() >> data;    }    string data;  };};

讀者可以看到,ffmsg_t中提供了流式的序列化方法,使得序列化變得很容易。設計服務器消息的時候,需要注意的點有:

  • 在設計服務器接口的時候,每個接口接受一個消息作為參數,一個處理完畢返回一個消息,這是最傳統的rpc模式。Ffrpc中采用這樣的設計理念以簡化和規范化接口設計。如果使用ffmsg_t定義消息,本人推薦的定義風格類似上面的代碼這樣。上面定義的是echo接口的輸入消息和輸出消息,但是都定義在echo_t結構內可以清晰的表明這是一對接口消息。
  • 傳統的服務器接口會為每個接口定義一個cmd,然后通過cmd反序列化成特定的消息調用特定的接口,ffrpc省略了cmd的定義,而是直接采用消息名稱作為cmd,比如在ffrpc中注冊的接口接受echo_t的消息,那么收到echo_t的消息自然而言的是調用這個接口
  • 接口定義的時候必須的同時制定輸入消息和輸出消息
  • Ffmsg_t支持普通類型,字符串類型、stl類型。

echo服務的實現代碼:

struct echo_service_t{  //! echo接口,返回請求的發送的消息ffreq_t可以提供兩個模板參數,第一個表示輸入的消息(請求者發送的)  //! 第二個模板參數表示該接口要返回的結果消息類型  void echo(ffreq_t<echo_t::in_t, echo_t::out_t>& req_)  {    echo_t::out_t out;    out.data = req_.msg.data;    LOGINFO(("XX", "foo_t::echo: recv %s", req_.msg.data.c_str()));    req_.response(out);  }};echo_service_t foo;  //! broker客戶端,可以注冊到broker,并注冊服務以及接口,也可以遠程調用其他節點的接口  ffrpc_t ffrpc_service("echo");  ffrpc_service.reg(&echo_service_t::echo, &foo);  if (ffrpc_service.open(arg_helper))  {    return -1;}

這樣就定義了echo服務,echo服務提供了一個接口,接受echo_t::in_t消息,返回echo_t::out_t消息。由此可見使用ffrpc定義服務的步驟是:

l 定義消息和接口

將接口注冊到ffrpc的示例中,ffpc提供了reg模板方法,會自動的分析注冊的接口使用神馬輸入消息,從而保證如果echo_t::in_t消息到來一定會調用對應的接口

Ffrpc工作的核心是broker,簡單描述broker的作用就是轉發消息。Ffrpc的client和server是不直接連接的,而是通過broker轉發消息進行通信。

這樣的好處是server的位置對于client是完全透明的,這也是broker模式最精髓的思想。所以ffrpc天生就是scalability的。Ffrpc的client比如要調用echo服務的接口,完全不需要知道serverr對應的位置或者配置,只需要知道echo服務的名字。有人可能擔憂完全的broker轉發可能會帶來很大開銷。

Broker保證了消息轉發的最佳優化,如果client或者server和broker在同一進程,那么消息直接是內存間傳遞的,連序列化都不需要做,這也是得益于broker模式,broker模式的特點就是擁有很好的scalability。這樣無論是簡單的設計一個單進程的server還是設計成多進程分布式的一組服務,ffrpc都能完美勝任。
調用echo服務的client示例:

struct echo_client_t{  //! 遠程調用接口,可以指定回調函數(也可以留空),同樣使用ffreq_t指定輸入消息類型,并且可以使用lambda綁定參數  void echo_callback(ffreq_t<echo_t::out_t>& req_, int index, ffrpc_t* ffrpc_client)  {    if (req_.error())    {      LOGERROR(("XX", "error_msg <%s>", req_.error_msg()));      return;    }    else if (index < 10)    {      echo_t::in_t in;      in.data = "helloworld";      LOGINFO(("XX", "%s %s index=%d callback...", __FUNCTION__, req_.msg.data.c_str(), index));      sleep(1);      ffrpc_client->call("echo", in, ffrpc_ops_t::gen_callback(&echo_client_t::echo_callback, this, ++index, ffrpc_client));    }    else    {      LOGINFO(("XX", "%s %s %d callback end", __FUNCTION__, req_.msg.data.c_str(), index));    }  }};ffrpc_t ffrpc_client;  if (ffrpc_client.open(arg_helper))  {    return -1;  }    echo_t::in_t in;  in.data = "helloworld";  echo_client_t client;  ffrpc_client.call("echo", in, ffrpc_ops_t::gen_callback(&echo_client_t::echo_callback, &client, 1, &ffrpc_client));

使用ffrpc調用遠程接口,只需要制定服務名和輸入消息,broker自動定位echo服務的位置,本示例中由于ffrpc的client和server在同一進程,那么自動通過內存間傳遞,如果server和broker在同一進程,而client在其他進程或者物理機上,則broker和server之間的傳遞為內存傳遞,broker和client的消息傳遞為tcp傳輸,這就跟自己寫一個tcp的server收到消息投遞給service的接口,然后將消息再通過tcp投遞給client。但是必須看到,ffrpc完全簡化了tcp server定義,并且更加scalability,甚至完全可以用來進程內多線程的通訊。

需要注意的是,ffrpc擁有良好的容錯能力,如果服務不存在或者接口不存在或者異常等發生回調函數仍然是會被調用,并且返回錯誤信息,從而使錯誤處理變得更加容易。比如游戲服務器中client登入gate但是scene可能還沒有啟動的時候,這里就能夠很好的處理,回調函數檢查錯誤就可以了。對于回調函數,對于經常使用多線程和任務隊列的開發者一定非常熟悉,回調函數支持lambda參數應該算是錦上添花,使得異步的代碼變得更加清晰易懂。

Broker的啟動方式:

int main(int argc, char* argv[])  {    //! 美麗的日志組件,shell輸出是彩色滴?。  ? LOG.start("-log_path ./log -log_filename log -log_class XX,BROKER,FFRPC -log_print_screen true -log_print_file false -log_level 3");      if (argc == 1)    {      printf("usage: %s -broker tcp://127.0.0.1:10241/n", argv[0]);      return 1;    }    arg_helper_t arg_helper(argc, argv);      //! 啟動broker,負責網絡相關的操作,如消息轉發,節點注冊,重連等      ffbroker_t ffbroker;    if (ffbroker.open(arg_helper))    {      return -1;    }      sleep(1);    if (arg_helper.is_enable_option("-echo_test"))    {      run_echo_test(arg_helper);        }    else if (arg_helper.is_enable_option("-protobuf_test"))    {      run_protobuf_test(arg_helper);    }    else    {      printf("usage %s -broker tcp://127.0.0.1:10241 -echo_test/n", argv[0]);      return -1;    }      ffbroker.close();    return 0;  }

Ffrpc中兩個關鍵的組件broker和rpc,broker負責轉發和注冊服務器,rpc則代表通信節點,可能是client可能是server。即使是多個服務器,只需要broker一個監聽的端口,其他的服務只需要提供不同的服務名即可。

Protobuf協議示例

Ffrpc 良好的設計抽離了對于協議的耦合,使得支持protobuf就增加了10來行代碼。當然這也是由于protobuf生成的消息都繼承message基類。當我實現thrift的時候,事情就稍微麻煩一些,thrift生成的代碼更加簡潔,但是生成的消息不集成基類,需要復制粘貼一些代碼。

Protobuf的定義文件:

package ff;message pb_echo_in_t { required string data = 1;}message pb_echo_out_t { required string data = 1;}

我們仍然設計一個echo服務,定義echo接口的消息,基于ffrpc的設計理念,每個接口都有一個輸入消息和輸出消息。

Echo服務的實現代碼:

struct protobuf_service_t{  //! echo接口,返回請求的發送的消息ffreq_t可以提供兩個模板參數,第一個表示輸入的消息(請求者發送的)  //! 第二個模板參數表示該接口要返回的結果消息類型  void echo(ffreq_t<pb_echo_in_t, pb_echo_out_t>& req_)  {    LOGINFO(("XX", "foo_t::echo: recv data=%s", req_.msg.data()));    pb_echo_out_t out;    out.set_data("123456");    req_.response(out);  }};protobuf_service_t foo;  //! broker客戶端,可以注冊到broker,并注冊服務以及接口,也可以遠程調用其他節點的接口  ffrpc_t ffrpc_service("echo");  ffrpc_service.reg(&protobuf_service_t::echo, &foo);  if (ffrpc_service.open(arg_helper))  {    return -1;  }

跟使用ffmsg_t的方式幾乎是一樣的,ffreq_t 的msg字段是輸入的消息。

調用echo服務器的client的示例代碼

struct protobuf_client_t{  //! 遠程調用接口,可以指定回調函數(也可以留空),同樣使用ffreq_t指定輸入消息類型,并且可以使用lambda綁定參數  void echo_callback(ffreq_t<pb_echo_out_t>& req_, int index, ffrpc_t* ffrpc_client)  {    if (req_.error())    {      LOGERROR(("XX", "error_msg <%s>", req_.error_msg()));      return;    }    else if (index < 10)    {      pb_echo_in_t in;      in.set_data("Ohnice");      LOGINFO(("XX", "%s data=%s index=%d callback...", __FUNCTION__, req_.msg.data(), index));      sleep(1);      ffrpc_client->call("echo", in, ffrpc_ops_t::gen_callback(&protobuf_client_t::echo_callback, this, ++index, ffrpc_client));    }    else    {      LOGINFO(("XX", "%s %d callback end", __FUNCTION__, index));    }  }};  ffrpc_t ffrpc_client;  if (ffrpc_client.open(arg_helper))  {    return -1;  }    protobuf_client_t client;  pb_echo_in_t in;  in.set_data("Ohnice");  ffrpc_client.call("echo", in, ffrpc_ops_t::gen_callback(&protobuf_client_t::echo_callback, &client, 1, &ffrpc_client));

Protobuf的優點是:

支持版本,這樣增加字段變得更加容易

Protobuf是支持多語言的,這樣可以跟其他的語言也可以通訊

Thrift協議的示例

Thrift 定義文件:

namespace cpp ff struct echo_thrift_in_t {    1: string data}struct echo_thrift_out_t {    1: string data}

Thrift 的服務器實現代碼:

struct thrift_service_t{  //! echo接口,返回請求的發送的消息ffreq_t可以提供兩個模板參數,第一個表示輸入的消息(請求者發送的)  //! 第二個模板參數表示該接口要返回的結果消息類型  void echo(ffreq_thrift_t<echo_thrift_in_t, echo_thrift_out_t>& req_)  {    LOGINFO(("XX", "foo_t::echo: recv data=%s", req_.msg.data));    echo_thrift_out_t out;    out.data = "123456";    req_.response(out);  }};thrift_service_t foo;  //! broker客戶端,可以注冊到broker,并注冊服務以及接口,也可以遠程調用其他節點的接口  ffrpc_t ffrpc_service("echo");  ffrpc_service.reg(&thrift_service_t::echo, &foo);  if (ffrpc_service.open(arg_helper))  {    return -1;  }    ffrpc_t ffrpc_client;  if (ffrpc_client.open(arg_helper))  {    return -1;}

調用 echo的client的示例:

struct thrift_client_t{  //! 遠程調用接口,可以指定回調函數(也可以留空),同樣使用ffreq_t指定輸入消息類型,并且可以使用lambda綁定參數  void echo_callback(ffreq_thrift_t<echo_thrift_out_t>& req_, int index, ffrpc_t* ffrpc_client)  {    if (req_.error())    {      LOGERROR(("XX", "error_msg <%s>", req_.error_msg()));      return;    }    else if (index < 10)    {      echo_thrift_in_t in;      in.data = "Ohnice";      LOGINFO(("XX", "%s data=%s index=%d callback...", __FUNCTION__, req_.msg.data, index));      sleep(1);      ffrpc_client->call("echo", in, ffrpc_ops_t::gen_callback(&thrift_client_t::echo_callback, this, ++index, ffrpc_client));    }    else    {      LOGINFO(("XX", "%s %d callback end", __FUNCTION__, index));    }  }};ffrpc_t ffrpc_client;  if (ffrpc_client.open(arg_helper))  {    return -1;  }    thrift_client_t client;  echo_thrift_in_t in;  in.data = "Ohnice";ffrpc_client.call("echo", in, ffrpc_ops_t::gen_callback(&thrift_client_t::echo_callback, &client, 1, &ffrpc_client));

Thrift的優缺點:

  • Thrift 更加靈活,支持list和map,而且可以嵌套
  • 支持N種語言
  • 官方的版本需要依賴boost,ffrpc從中提取出一個最基本的c++版本,只有頭文件,不依賴boost

總結

  • Ffrpc是基于c++的網絡通訊庫,基于broker模式scalability和 易用性是最大的優點
  • 使用ffrpc進行進程間通訊非常的容易,定義服務和接口就行了,你除了使用ffmsg_t最傳統的消息定義,也可以使用google protobuf和facebook thrift。
  • Ffrpc是全異步的,通過回調函數+lambda方式可以很容易操作異步邏輯。
  • Ffrpc 接下來會有更多的示例,當系統復雜時,ffrpc的優勢將會更加明顯。
  • Github的地址: https://github.com/fanchy/FFRPC

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲肉体裸体xxxx137| 国产午夜精品一区二区三区| 日韩欧美aaa| 伊人亚洲福利一区二区三区| 精品偷拍各种wc美女嘘嘘| 国产精品视频中文字幕91| 成人午夜两性视频| 日韩精品视频在线播放| 中文字幕国产精品久久| 国产在线视频一区| 91久久精品久久国产性色也91| 亚洲欧美日韩国产精品| 成人美女免费网站视频| 欧美韩日一区二区| 亚洲精品aⅴ中文字幕乱码| 精品欧美aⅴ在线网站| 91国内在线视频| 亚洲在线www| 亚洲国产精久久久久久久| 亚洲国产天堂久久综合网| 精品国产福利在线| 国产成人一区二区三区小说| 97在线看免费观看视频在线观看| 国产欧美一区二区三区视频| 欧美性生交xxxxxdddd| 亚洲区在线播放| 国产激情视频一区| 欧美亚洲在线视频| 在线观看91久久久久久| 久久久精品欧美| 亚洲人成网在线播放| 亚洲自拍欧美另类| 大伊人狠狠躁夜夜躁av一区| 91夜夜未满十八勿入爽爽影院| 精品国产老师黑色丝袜高跟鞋| 色樱桃影院亚洲精品影院| 国产主播精品在线| 韩国三级日本三级少妇99| 欧美精品一二区| 91精品国产91久久久久| 日韩乱码在线视频| 欧美精品xxx| 亚州成人av在线| 成人性生交大片免费看视频直播| 一色桃子一区二区| 26uuu国产精品视频| 精品国产一区二区三区久久| 97视频在线观看成人| 亚洲黄色av女优在线观看| 欧美日韩成人在线视频| 国产精品欧美激情在线播放| 久久久爽爽爽美女图片| 欧美激情乱人伦一区| 亚洲国产私拍精品国模在线观看| 国产精品久久久久7777婷婷| 欧美高清视频在线观看| 一区国产精品视频| 亚洲free性xxxx护士hd| 精品国产一区二区三区久久| 国产一区二区三区四区福利| 国内精品国产三级国产在线专| 国模私拍视频一区| 久久高清视频免费| 欧美激情一二区| 国产精品av免费在线观看| 久久久久久久久久久网站| 最近2019年中文视频免费在线观看| 亚洲男人天堂古典| 亚洲精品福利视频| 久久99精品久久久久久噜噜| 欧美一级电影久久| 久久精品这里热有精品| 久久久91精品| 亚洲激情视频网| 成人黄色短视频在线观看| 国产亚洲欧洲高清一区| 精品久久久久久久久久久久| 色av中文字幕一区| 一区二区三区美女xx视频| 国产美女精彩久久| 亚洲精品www| 国产精品免费久久久| 亚洲国模精品私拍| 日韩欧美成人网| 国产精品网红直播| 日韩视频亚洲视频| 日韩中文字幕免费| 日韩在线小视频| 欧美视频第一页| 8090成年在线看片午夜| 国产精品欧美日韩久久| 欧美久久精品午夜青青大伊人| 91精品国产91| 亚洲欧美激情视频| 亚洲男人7777| 日韩美女av在线| 成人信息集中地欧美| 国产精品久久久久久久久久久不卡| 91久久在线观看| 精品视频9999| 这里精品视频免费| 亚洲久久久久久久久久| 成人久久久久爱| 亚洲成人动漫在线播放| 欧美精品videos性欧美| 在线成人免费网站| 亚洲国产成人精品久久| 91在线免费看网站| 久久久精品999| 69视频在线免费观看| 亚洲国产日韩欧美在线99| 国产视频精品xxxx| 日韩有码在线电影| 欧美日韩精品中文字幕| 亚洲国产99精品国自产| 欧美日韩成人免费| 欧美福利视频网站| 日韩av手机在线| 国产香蕉精品视频一区二区三区| 国产精品电影网| 色综合色综合网色综合| 亚洲欧洲国产伦综合| 青青草精品毛片| 日韩精品极品视频免费观看| 国产999精品视频| 欧美日韩亚洲网| 日韩成人高清在线| 久久久爽爽爽美女图片| 亚洲a∨日韩av高清在线观看| 国产精品自产拍在线观看| 欧美激情一二区| 国产精品999| 中文字幕免费精品一区高清| 精品国产网站地址| 亚洲一区999| 亚洲欧美一区二区三区久久| 国产在线视频2019最新视频| 亚洲美女视频网站| 国产在线播放91| 国产精品久久久久7777婷婷| 国产精品亚洲美女av网站| 欧美午夜影院在线视频| 中文字幕av一区二区| 精品无码久久久久久国产| 欧美极品在线播放| 色婷婷综合久久久久中文字幕1| 亚洲xxxx视频| 国产精品羞羞答答| 成人欧美一区二区三区黑人孕妇| 国产精品看片资源| 国产精品激情自拍| 中文精品99久久国产香蕉| 欧美大全免费观看电视剧大泉洋| 国产精品久久电影观看| 国产精品女主播| 91精品国产自产在线老师啪| 日本精品一区二区三区在线播放视频| 国自产精品手机在线观看视频| xxx一区二区| 国产欧美一区二区三区四区| 久久久国产精品视频| 久久九九全国免费精品观看| 尤物精品国产第一福利三区| 日韩精品电影网|