天天看點

百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊

簡介

sofa-pbrpc是基于Google Protocol Buffers 實作的RPC網絡通信庫,在百度公司各部門得到廣泛使用,每天支撐上億次内部調用。sofa-pbrpc基于百度大搜尋高并發高負載的業務場景不斷打磨,成為一套簡單易用的輕量級高性能RPC架構。2014年sofa-pbrpc正式對外開源受到廣大開發人員的關注,目前sofa-pbrpc已經在浪潮、金山、樂視等各大網際網路公司産品中使用。

開源位址:https://github.com/baidu/sofa-pbrpc

目标

  • 輕量
  • 易用
  • 高性能

特性

  • 接口簡單,容易使用
  • 實作高效,性能優異(高吞吐、低延遲、高并發連接配接數)
  • 測試完善,運作穩定
  • 支援同步和異步調用,滿足不同類型需求
  • 支援多級逾時設定,靈活控制請求逾時時間
  • 支援精準的網絡流量控制,對應用層透明
  • 支援透明壓縮傳輸,節省帶寬
  • 提供服務和方法級别的服務調用統計資訊,友善監控
  • 支援自動建立連接配接和自動重連,使用者無需感覺連接配接
  • 遠端位址相同的Client Stub共享一個連接配接通道,節省資源
  • 空閑連接配接自動關閉,及時釋放資源
  • 支援Mock測試
  • 支援多Server負載均衡與容錯
  • 原生支援HTTP協定通路
  • 提供内建的Web監控頁面
  • 提供Python用戶端庫
  • 支援webservice,使用者快速定義web server處理邏輯
  • 支援profiling,實時檢視程式的資源消耗,友善問題追查
百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊

快速使用

使用sofa-pbrpc隻需要三步:

  • 定義通訊協定
  • 實作Server
  • 實作Client

樣例代碼參見“sample/echo”。

定義通訊協定

定義協定隻需要編寫一個proto檔案即可。 範例:echo_service.proto

package sofa.pbrpc.test;
option cc_generic_services = true;
message EchoRequest {
    required string message = ;
}
message EchoResponse {
    required string message = ;
}
service EchoServer {
    rpc Echo(EchoRequest) returns(EchoResponse);
}
           

使用protoc編譯'echo_service.proto',生成接口檔案'echo_service.pb.h'和'echo_service.pb.cc'。

注意:
  • package會被映射到C++中的namespace,為了避免沖突建議使用package;
  • 需要設定“cc_generic_services”,以通知protoc工具生成RPC架構代碼;
  • 這裡EchoRequest和EchoResponse的成員完全相同,在實際應用中可以設定不同的成員;

實作Server

頭檔案

#include <sofa/pbrpc/pbrpc.h> // sofa-pbrpc頭檔案
#include "echo_service.pb.h" // service接口定義頭檔案
                

實作服務

class EchoServerImpl : public sofa::pbrpc::test::EchoServer
{
public:
    EchoServerImpl() {}
    virtual ~EchoServerImpl() {}

private:
    virtual void Echo(google::protobuf::RpcController* controller,
                      const sofa::pbrpc::test::EchoRequest* request,
                      sofa::pbrpc::test::EchoResponse* response,
                      google::protobuf::Closure* done)
    {
        sofa::pbrpc::RpcController* cntl =
            static_cast<sofa::pbrpc::RpcController*>(controller);
        SLOG(NOTICE, "Echo(): request message from %s: %s",
            cntl->RemoteAddress().c_str(), request->message().c_str());
        response->set_message("echo message: " + request->message());
        done->Run();
    }
};
           
注意:
  • 服務完成後必須調用done->Run(),通知RPC系統服務完成,觸發發送Response;
  • 在調了done->Run()之後,Echo的所有四個參數都不再能通路; done-Run()可以分派到其他線程中執行,以實作了真正的異步處理;

注冊和啟動服務

int main() {
    SOFA_PBRPC_SET_LOG_LEVEL(NOTICE);

    sofa::pbrpc::RpcServerOptions options;
    options.work_thread_num = ;
    sofa::pbrpc::RpcServer rpc_server(options);

    if (!rpc_server.Start("0.0.0.0:12321")) {
        SLOG(ERROR, "start server failed");
        return EXIT_FAILURE;
    }

    sofa::pbrpc::test::EchoServer* echo_service = new EchoServerImpl();
    if (!rpc_server.RegisterService(echo_service)) {
        SLOG(ERROR, "register service failed");
        return EXIT_FAILURE;
    }
    rpc_server.Run();
    rpc_server.Stop();

    return EXIT_SUCCESS;
}
                

實作Client

Client支援同步和異步兩種調用方式:

  • 同步調用時,調用線程會被阻塞,直到收到回複或者逾時;
  • 異步調用時,調用線程不會被阻塞,收到回複或者逾時會調用使用者提供的回調函數;

頭檔案

#include <sofa/pbrpc/pbrpc.h> // sofa-pbrpc頭檔案
#include "echo_service.pb.h" // service接口定義頭檔案
                

同步調用

int main() {
    SOFA_PBRPC_SET_LOG_LEVEL(NOTICE);
    sofa::pbrpc::RpcClientOptions client_options;
    client_options.work_thread_num = ;
    sofa::pbrpc::RpcClient rpc_client(client_options);
    sofa::pbrpc::RpcChannel rpc_channel(&rpc_client, "127.0.0.1:12321");

    sofa::pbrpc::test::EchoServer_Stub stub(&rpc_channel);

    sofa::pbrpc::test::EchoRequest request;
    request.set_message("Hello world!");
    sofa::pbrpc::test::EchoResponse response;
    sofa::pbrpc::RpcController controller;
    controller.SetTimeout();
    stub.Echo(&controller, &request, &response, NULL);
    if (controller.Failed()) {
        SLOG(ERROR, "request failed: %s", controller.ErrorText().c_str());
    }

    return EXIT_SUCCESS;
}
                

異步調用

void EchoCallback(sofa::pbrpc::RpcController* cntl, sofa::pbrpc::test::EchoRequest* request, sofa::pbrpc::test::EchoResponse* response, bool* callbacked)
{
    SLOG(NOTICE, "RemoteAddress=%s", cntl->RemoteAddress().c_str());
    SLOG(NOTICE, "IsRequestSent=%s", cntl->IsRequestSent() ? "true" : "false");
    if (cntl->IsRequestSent())
    {
        SLOG(NOTICE, "LocalAddress=%s", cntl->LocalAddress().c_str());
        SLOG(NOTICE, "SentBytes=%ld", cntl->SentBytes());
    }
    if (cntl->Failed()) {
        SLOG(ERROR, "request failed: %s", cntl->ErrorText().c_str());
    }
    else {
        SLOG(NOTICE, "request succeed: %s", response->message().c_str());
    }
    delete cntl;
    delete request;
    delete response;

    *callbacked = true;
}

int main()
{
    SOFA_PBRPC_SET_LOG_LEVEL(NOTICE);

    sofa::pbrpc::RpcClientOptions client_options;
    sofa::pbrpc::RpcClient rpc_client(client_options);

    sofa::pbrpc::RpcChannel rpc_channel(&rpc_client, "127.0.0.1:12321");
    sofa::pbrpc::test::EchoServer_Stub stub(&rpc_channel);
    sofa::pbrpc::test::EchoRequest* request = new sofa::pbrpc::test::EchoRequest();
    request->set_message("Hello from qinzuoyan01");
    sofa::pbrpc::test::EchoResponse* response = new sofa::pbrpc::test::EchoResponse();
    sofa::pbrpc::RpcController* cntl = new sofa::pbrpc::RpcController();
    cntl->SetTimeout();
    bool callbacked = false;
    google::protobuf::Closure* done = sofa::pbrpc::NewClosure(
            &EchoCallback, cntl, request, response, &callbacked);

    stub.Echo(cntl, request, response, done);
    while (!callbacked) {
        usleep();
    }

    return EXIT_SUCCESS;
}
                
注意:
  • 異步調用傳入的controller、request、response參數,在回調函數執行之前需一直保持有效;
  • 回調函數的執行會配置設定到專門的回調線程中運作,可以通過設定RpcClientOptions的callback_thread_num來配置回調線程數;

實作

系統結構

百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊
  • RpcClientStream/RpcServerStream:代表client和server之間的連接配接,用于client和server的網絡通信。
  • ThreadGroup:client和server内部線程池,用于io操作和執行回調。
  • TimeoutManager:采用訂閱者模型,對rpc請求進行逾時管理。
  • RpCListenser:接受來自client的連接配接請求,建立與client之間的連接配接。
  • ServicePool:server端服務管理與路由。
百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊

 整個RPC調用經過以下階段:

  • Stub調用RPC函數發起RPC請求.
  • RpcChannel調用CallMethod執行RPC調用。
  • RpcClient選取RpcClientStream異步發送請求,并添加至逾時隊列。
  • server端RpcListener接收到client的請求,建立對應RpcServerStream。
  • RpcServerStream接收資料,根據meta資訊在ServerPool中選取對應Service.Method執行。
  • server通過RpcServerStream發送執行結果,回複過程與請求過程類似。

技術特點

協定棧方式的網絡模型

百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊

 在sofa-pbrpc中網絡資料自上而下流劃分為RpcClientStream/RpcServerStream、RpcMessageStream、RpcByteStream三層。消息流層主要負責網絡通信相關的操作,操作對象為序列化之後的二機制位元組流;消息流層處理的對象是由header、meta和data組裝的消息,負責消息級别的控制與統計;協定層負責異步發送接受請求和響應資料。三層結構每一層是下一層的封裝和擴充,采用這樣協定棧方式的層次劃分更加有利于資料協定的擴充。

ZeroCopy方式管理緩沖區。

百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊
百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊

 sofa-pbrpc将記憶體劃分為固定大小的buffer作為緩沖區,對buffer采用引用計數進行管理,減少不必要的記憶體拷貝。

支援HTTP協定

除了使用原生client通路server外,sofa-pbrpc也支援使用http協定通路server上的服務。同時,使用者可以通過使用server端的WebService工具類,快速實作server的對于http請求的處理邏輯。

支援json格式資料傳輸

sofa-pbrpc支援使用者使用http用戶端向server發送json格式的資料請求,并傳回json格式的響應。

提供豐富的工具類

百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊

 sofa-pbrpc提供常用工具類給開發者,包括:

性能

測試環境

  • cpu 16core
  • memory 64G
  • kernel 2.6.32_1-15-0-0

吞吐

百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊

延遲

百度開源高性能RPC架構 sofa-pbrpc簡介目标特性快速使用實作技術特點性能支援團隊

支援團隊

百度網頁搜尋部開源團隊 [email protected]

繼續閱讀