作者介绍:包子侠
原文链接:原文链接
1. 基本介绍
yugabyte使用google的protobuf实现rpc通信,因此大多数关于protobuf编程的细节可以参考官方文档。另外yugabyte基于protobuf构建rpc通信,其rpc客户端和服务器的实现是通用的,因此yugabyte实现了一个protobuf的plugin来负责对service相关的proto文件进行编译。
2. protobuf简单介绍
传送门:官方文档、中文参考
下面是yugabyte的docdb.proto文件,以此作为protobuf编写的简单介绍:
syntax = "proto2"; // 指定protobuf协议,如果不指定,默认使用proto2
package yb.docdb; // 对应C++的名字空间,这里是两层名字空间yb和docdb
import "yb/common/common.proto"; // 引用其他proto文件中定义的类型
import "yb/util/opid.proto";
option java_package = "org.yb.docdb";
// 定义一个数据结构
// 成员变量除了类型之外,还需要提供一个标注,三选一:optional、repeated、required
// 分别表示可选、可重复[0-n]、必选
// 每个成员后面跟着一个数字,用来在二进制编码中标识该字段,因此必须唯一
message KeyValuePairPB {
optional bytes key = 1;
optional bytes value = 2;
optional fixed64 external_hybrid_time = 3;
}
// A set of key/value pairs to be written into RocksDB.
message KeyValueWriteBatchPB {
repeated KeyValuePairPB write_pairs = 1;
optional TransactionMetadataPB transaction = 2;
optional bool DEPRECATED_may_have_metadata = 3;
// Used by serializable isolation transactions and row locking statements to store read intents.
// In case of read-modify-write operation both read_pairs and write_pairs could present.
repeated KeyValuePairPB read_pairs = 5;
optional RowMarkType row_mark_type = 6;
}
message ConsensusFrontierPB {
optional OpIdPB op_id = 1;
optional fixed64 hybrid_time = 2;
optional fixed64 history_cutoff = 3;
optional fixed64 hybrid_time_filter = 4;
}
protoc会将proto文件中的message编译成C++的类,并生成成员变量的访问接口,每个成员变量会生成固定的一组形式相同的函数,以KeyValuePairPB的key为例:
bool has_key() const;
void clear_key();
const ::std::string& key() const;
void set_key(const ::std::string& value);
#if LANG_CXX11
void set_key(::std::string&& value);
#endif
void set_key(const char* value);
void set_key(const void* value, size_t size);
::std::string* mutable_key();
::std::string* release_key();
void set_allocated_key(::std::string* key);
有些类型,比如int是不支持修改的,其接口要少一些
bool has_external_hybrid_time() const;
void clear_external_hybrid_time();
::google::protobuf::uint64 external_hybrid_time() const;
void set_external_hybrid_time(::google::protobuf::uint64 value);
3. Yugabyte的protoc插件
3.1 protoc-gen-insertions
该插件主要两个功能:
- 给编译后的源代码文件名添加.pb后缀,比如common.h会变成common.pb.h
- 添加头文件:yb/gutil/dynamic_annotations.h
该插件在编译所有yugabyte的proto文件时都会用到,源码位置:
src\yb\util\protoc-gen-insertions.cc
3.2 protoc-gen-yrpc
该插件的主要功能是根据proto文件生成ServiceIf和RPC Proxy源代码,因此只有部分服务相关的proto文件会使用该插件进行编译。
我们在阅读yugabyte的源代码时,会发现一些核心的服务类,只有接口的实现,没有接口调用的地方,主要就是因为接口的调用处理是在ServiceIf文件中实现的,这些文件是通过proto在编译阶段生成的。同时客户端调用rpc请求时,最终的rpc调用函数也无法找到实现的地方,原因类似。
service类的proto以service关键字类定义,里面只有rpc接口声明,用关键字rpc标识,以TabletServerService为例:
syntax = "proto2";
package yb.tserver;
option java_package = "org.yb.tserver";
import "yb/common/common.proto";
import "yb/tserver/tserver.proto";
import "yb/tablet/metadata.proto";
service TabletServerService {
rpc Write(WriteRequestPB) returns (WriteResponsePB);
rpc Read(ReadRequestPB) returns (ReadResponsePB);
rpc NoOp(NoOpRequestPB) returns (NoOpResponsePB);
rpc ListTablets(ListTabletsRequestPB) returns (ListTabletsResponsePB);
rpc GetLogLocation(GetLogLocationRequestPB) returns (GetLogLocationResponsePB);
// Run full-scan data checksum on a tablet to verify data integrity.
//
// TODO: Consider refactoring this as a scan that runs a checksum aggregation
// function.
rpc Checksum(ChecksumRequestPB) returns (ChecksumResponsePB);
rpc ListTabletsForTabletServer(ListTabletsForTabletServerRequestPB)
returns (ListTabletsForTabletServerResponsePB);
rpc ImportData(ImportDataRequestPB) returns (ImportDataResponsePB);
rpc UpdateTransaction(UpdateTransactionRequestPB) returns (UpdateTransactionResponsePB);
// Returns transaction status at coordinator, i.e. PENDING, ABORTED, COMMITTED etc.
rpc GetTransactionStatus(GetTransactionStatusRequestPB) returns (GetTransactionStatusResponsePB);
// Returns transaction status at participant, i.e. number of replicated batches or whether it was
// aborted.
rpc GetTransactionStatusAtParticipant(GetTransactionStatusAtParticipantRequestPB)
returns (GetTransactionStatusAtParticipantResponsePB);
rpc AbortTransaction(AbortTransactionRequestPB) returns (AbortTransactionResponsePB);
rpc Truncate(TruncateRequestPB) returns (TruncateResponsePB);
rpc GetTabletStatus(GetTabletStatusRequestPB) returns (GetTabletStatusResponsePB);
rpc GetMasterAddresses(GetMasterAddressesRequestPB) returns (GetMasterAddressesResponsePB);
rpc Publish(PublishRequestPB) returns (PublishResponsePB);
rpc IsTabletServerReady(IsTabletServerReadyRequestPB) returns (IsTabletServerReadyResponsePB);
// Takes precreated transaction from this tserver.
rpc TakeTransaction(TakeTransactionRequestPB) returns (TakeTransactionResponsePB);
}
该插件的源码位置:
src\yb\rpc\protoc-gen-yrpc.cc
4. Yugabyte构建过程中如何使用proto文件
4.1 普通proto构建
对于非service类的proto定义,通过PROTOBUF_GENERATE_CPP指令来添加到CMakeList中,以docdb.proto为例
# docdb_proto
PROTOBUF_GENERATE_CPP(
DOCDB_PROTO_SRCS DOCDB_PROTO_HDRS DOCDB_PROTO_TGTS
SOURCE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..
BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR}/../..
PROTO_FILES docdb.proto)
ADD_YB_LIBRARY(docdb_proto
SRCS ${DOCDB_PROTO_SRCS}
DEPS protobuf yb_common_proto opid_proto
NONLINK_DEPS ${DOCDB_PROTO_TGTS})
set(DOCDB_ENCODING_SRCS
doc_key.cc
doc_kv_util.cc
key_bytes.cc
primitive_value.cc
primitive_value_util.cc
intent.cc
doc_scanspec_util.cc
)
set(DOCDB_ENCODING_DEPS
docdb_proto
server_common
yb_common
yb_rocksutil
yb_util
)
ADD_YB_LIBRARY(yb_docdb_encoding
SRCS ${DOCDB_ENCODING_SRCS}
DEPS ${DOCDB_ENCODING_DEPS}
)
4.2 service类proto构建
对于service类,需要使用额外的插件来生成代码,在CMakeLists文件中通过YRPC_GENERATE指令进行构建,以tserver_service.proto为例:
#########################################
# tserver_service_proto
#########################################
YRPC_GENERATE(
TSERVER_YRPC_SRCS TSERVER_YRPC_HDRS TSERVER_YRPC_TGTS
SOURCE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..
BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR}/../..
PROTO_FILES tserver_service.proto)
set(TSERVER_YRPC_LIBS
yrpc
yb_common_proto
protobuf
remote_bootstrap_proto
rpc_header_proto
tserver_proto
wire_protocol_proto)
ADD_YB_LIBRARY(tserver_service_proto
SRCS ${TSERVER_YRPC_SRCS}
DEPS ${TSERVER_YRPC_LIBS}
NONLINK_DEPS ${TSERVER_YRPC_TGTS})




