2021SC@SDUSC BRPC source code analysis thrift

Posted by sukanya.paul on Sun, 12 Dec 2021 12:38:13 +0100

2021SC@SDUSC BRPC source code analysis (XII) thrift

thrift

Introduction to thrift

Thrift is a widely used RPC framework. It was first published by Facebook and later maintained by Apache. In order to communicate with thrift services and solve a series of problems of thrift native solution in terms of multithreading safety, ease of use and concurrency, brpc implements and supports the framework protocol of thrift in NonBlocking mode, which is directly referred to as thrift protocol below.

Advantages over native solutions are:

  • Thread safe. The user does not need to establish a separate client for each thread
  • Support synchronous, asynchronous, batch synchronous, batch asynchronous and other access methods, and can use parallel channel and other combined access methods
  • Support multiple connection modes (connection pool and short connection), and support a series of RPC basic benefits such as timeout, backup request, cancellation, tracing, built-in services, etc
  • Better performance

thrift compilation

In order to reuse the parsing code, brpc's support for thrift still depends on the thrift library and the code generated by thrift.

Brpc does not enable thrift support by default, nor does it require thrift dependency. However, if thrift protocol is required, add – with thrift or - dwith when configuring brpc environment_ THRIFT=ON.

To install thrift dependency under Linux, first refer to the official wiki to install the necessary dependencies and tools, then download the thrift source code from the official website, decompress and compile.

wget http://www.apache.org/dist/thrift/0.11.0/thrift-0.11.0.tar.gz
tar -xf thrift-0.11.0.tar.gz
cd thrift-0.11.0/
./configure --prefix=/usr --with-ruby=no --with-python=no --with-java=no --with-go=no --with-perl=no --with-php=no --with-csharp=no --with-erlang=no --with-lua=no --with-nodejs=no
make CPPFLAGS=-DFORCE_BOOST_SMART_PTR -j 4 -s
sudo make install

make after configuring brpc to support thrift protocol. Libbrpc.exe will be generated after compilation a. It contains the extension code supporting thrift protocol, which can be linked like the code for normal use of brpc.

# Ubuntu
sh config_brpc.sh --headers=/usr/include --libs=/usr/lib --with-thrift
# Fedora/CentOS
sh config_brpc.sh --headers=/usr/include --libs=/usr/lib64 --with-thrift
# Or use cmake
mkdir build && cd build && cmake ../ -DWITH_THRIFT=1

Client access to thrift server

#include <brpc/channel.h>
#include <brpc/thrift_ message. h> / / ThriftStub defined
...

DEFINE_string(server, "0.0.0.0:8019", "IP Address of thrift server");
DEFINE_string(load_balancer, "", "The algorithm for load balancing");
...
  
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_THRIFT;
brpc::Channel thrift_channel;
if (thrift_channel.Init(Flags_server.c_str(), FLAGS_load_balancer.c_str(), &options) != 0) {
   LOG(ERROR) << "Fail to initialize thrift channel";
   return -1;
}

brpc::ThriftStub stub(&thrift_channel);
...

// example::[EchoRequest/EchoResponse] is a message generated by thrift
example::EchoRequest req;
example::EchoResponse res;
req.data = "hello";

stub.CallMethod("Echo", &cntl, &req, &res, NULL);

if (cntl.Failed()) {
    LOG(ERROR) << "Fail to send thrift request, " << cntl.ErrorText();
    return -1;
} 
  • Create a protocol set to brpc::PROTOCOL_THRIFT Channel
  • Create brpc::ThriftStub
  • Initiate access using native Request and native response >

code analysis

class ThriftService : public Describable {
public:
    ThriftService();
    virtual ~ThriftService();
    virtual void ProcessThriftFramedRequest(
        Controller* controller,
        ThriftFramedMessage* request,
        ThriftFramedMessage* response,
        ::google::protobuf::Closure* done) = 0;
    void Describe(std::ostream &os, const DescribeOptions&) const;

private:
DISALLOW_COPY_AND_ASSIGN(ThriftService);
friend class policy::ThriftClosure;
friend void policy::ProcessThriftRequest(InputMessageBase* msg_base);
friend class StatusService;
friend class Server;

private:
    void Expose(const butil::StringPiece& prefix);    
    MethodStatus* _status;
};
}
  • Inherit this class and let the brpc server understand thrift_binary request.
  • Implement this method to handle thrift_binary request.
    Parameters:
    • Controller per RPC settings. Controller - > failed() is always false.
    • Request received thrift_binary request.
    • Thrift that response should fill in_ Binary response.
  • Call done - > run() to end the processing.
  • Put the code description into the stream.
namespace brpc {

class ThriftStub;

static const int16_t THRIFT_INVALID_FID = -1;
static const int16_t THRIFT_REQUEST_FID = 1;
static const int16_t THRIFT_RESPONSE_FID = 0;

class ThriftMessageBase {
public:
......
slightly
......
};

class ThriftFramedMessage : public ::google::protobuf::Message {
friend class ThriftStub;
public:
    butil::IOBuf body;
    int16_t field_id;
    
private:
    bool _own_raw_instance;
    ThriftMessageBase* _raw_instance;

public:
   ......
slightly
......

protected:
......
slightly
......

private:
......
slightly
......
};

class ThriftStub {
public:
......
slightly
......

private:
    ChannelBase* _channel;
};

namespace details {

template <typename T>
class ThriftMessageWrapper final : public ThriftMessageBase {
public:
    ThriftMessageWrapper() : msg_ptr(NULL) {}
    ThriftMessageWrapper(T* msg2) : msg_ptr(msg2) {}
    virtual ~ThriftMessageWrapper() {}
    uint32_t Read(::apache::thrift::protocol::TProtocol* iprot) override final
    { return msg_ptr->T::read(iprot); }
    uint32_t Write(::apache::thrift::protocol::TProtocol* oprot) const override final
    { return msg_ptr->T::write(oprot); }
    T* msg_ptr;
};

template <typename T>
class ThriftMessageHolder final : public ThriftMessageBase {
public:
    virtual ~ThriftMessageHolder() {}
    uint32_t Read(::apache::thrift::protocol::TProtocol* iprot) override final
    { return msg.T::read(iprot); }
    uint32_t Write(::apache::thrift::protocol::TProtocol* oprot) const override final
    { return msg.T::write(oprot); }
    T msg;
};

template <typename RESPONSE>
class ThriftDoneWrapper : public ::google::protobuf::Closure {
public:
    explicit ThriftDoneWrapper(::google::protobuf::Closure* done)
        : _done(done) {}
    void Run() override {
        _done->Run();
        delete this;
    }
private:
    ::google::protobuf::Closure* _done;
public:
    ThriftMessageWrapper<RESPONSE> raw_response_wrapper;
    ThriftFramedMessage response;
};

} 

......
slightly
......

template <typename REQUEST, typename RESPONSE>
void ThriftStub::CallMethod(const char* method_name,
                            Controller* cntl,
                            const REQUEST* raw_request,
                            RESPONSE* raw_response,
                            ::google::protobuf::Closure* done) {
    cntl->_thrift_method_name.assign(method_name);

    details::ThriftMessageWrapper<REQUEST>
        raw_request_wrapper(const_cast<REQUEST*>(raw_request));
    ThriftFramedMessage request;
    request._raw_instance = &raw_request_wrapper;

    if (done == NULL) {
        ThriftFramedMessage response;
        details::ThriftMessageWrapper<RESPONSE> raw_response_wrapper(raw_response);
        response._raw_instance = &raw_response_wrapper;
        _channel->CallMethod(NULL, cntl, &request, &response, NULL);
    } else {
        details::ThriftDoneWrapper<RESPONSE>* new_done =
            new details::ThriftDoneWrapper<RESPONSE>(done);
        new_done->raw_response_wrapper.msg_ptr = raw_response;
        new_done->response._raw_instance = &new_done->raw_response_wrapper;
        _channel->CallMethod(NULL, cntl, &request, &new_done->response, new_done);
    }
}
  • Class thriftframedmessage: Public:: Google:: protobuf:: message {} represents the request or response of a thin framework.
    • body; Set to = '{raw_instance}'
    • field_id; Must be set when setting body.
  • "T::" makes function calls work around the virtual function table.
  • The ThriftDoneWrapper closure has additional things that ThriftStub needs.
  • The response ensures that it is not used after synchronizing the RPC and does not need to be allocated on the heap.
  • Let new_done has the response and releases it after Run().

Topics: Apache