├── .gitignore ├── .vscode └── settings.json ├── CMakeLists.txt ├── README.md ├── autobuild.sh ├── bin └── appConfig.json ├── example ├── CMakeLists.txt ├── client │ └── UerRpcClient.cc ├── protos │ ├── example.service.pb.cc │ ├── example.service.pb.h │ └── example.service.proto └── service │ └── UserRpcService.cc ├── images ├── build-passing-brightgreen.svg ├── design.svg ├── design_init.svg ├── head.png ├── head.psd ├── logger.svg ├── protocol.svg ├── service.svg └── servicemap.svg ├── include ├── lockqueue.h ├── logger.h ├── mpzrpcapplication.h ├── mpzrpcchannel.h ├── mpzrpcconfig.h ├── mpzrpccontroller.h ├── mpzrpcprovider.h ├── rpcheader.pb.h └── zookeeperutil.h ├── lib └── libmpzrpc.a └── src ├── CMakeLists.txt ├── logger.cc ├── mpzrpcapplication.cc ├── mpzrpcchannel.cc ├── mpzrpcconfig.cc ├── mpzrpccontroller.cc ├── mpzrpcprovider.cc ├── rpcheader.pb.cc ├── rpcheader.proto └── zookeeperutil.cc /.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.inc": "cpp", 4 | "array": "cpp", 5 | "atomic": "cpp", 6 | "*.tcc": "cpp", 7 | "cctype": "cpp", 8 | "chrono": "cpp", 9 | "clocale": "cpp", 10 | "cmath": "cpp", 11 | "condition_variable": "cpp", 12 | "cstdarg": "cpp", 13 | "cstddef": "cpp", 14 | "cstdint": "cpp", 15 | "cstdio": "cpp", 16 | "cstdlib": "cpp", 17 | "cstring": "cpp", 18 | "ctime": "cpp", 19 | "cwchar": "cpp", 20 | "cwctype": "cpp", 21 | "deque": "cpp", 22 | "unordered_map": "cpp", 23 | "unordered_set": "cpp", 24 | "vector": "cpp", 25 | "exception": "cpp", 26 | "algorithm": "cpp", 27 | "iterator": "cpp", 28 | "map": "cpp", 29 | "memory": "cpp", 30 | "memory_resource": "cpp", 31 | "optional": "cpp", 32 | "ratio": "cpp", 33 | "set": "cpp", 34 | "string": "cpp", 35 | "string_view": "cpp", 36 | "system_error": "cpp", 37 | "tuple": "cpp", 38 | "type_traits": "cpp", 39 | "utility": "cpp", 40 | "fstream": "cpp", 41 | "initializer_list": "cpp", 42 | "iosfwd": "cpp", 43 | "iostream": "cpp", 44 | "istream": "cpp", 45 | "limits": "cpp", 46 | "mutex": "cpp", 47 | "new": "cpp", 48 | "ostream": "cpp", 49 | "sstream": "cpp", 50 | "stdexcept": "cpp", 51 | "streambuf": "cpp", 52 | "thread": "cpp", 53 | "typeinfo": "cpp", 54 | "list": "cpp", 55 | "bitset": "cpp", 56 | "complex": "cpp", 57 | "functional": "cpp", 58 | "cinttypes": "cpp", 59 | "typeindex": "cpp", 60 | "variant": "cpp", 61 | "codecvt": "cpp", 62 | "forward_list": "cpp", 63 | "filesystem": "cpp", 64 | "iomanip": "cpp", 65 | "numeric": "cpp", 66 | "valarray": "cpp" 67 | }, 68 | "C_Cpp.default.configurationProvider": "vector-of-bool.cmake-tools", 69 | "editor.fontFamily": "monospace" 70 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 设置cmake的最低版本和项目名称 2 | cmake_minimum_required(VERSION 3.0) 3 | project(mpzrpc) 4 | 5 | # 生成debug版本,可以进行gdb调试 6 | set(CMAKE_BUILD_TYPE "Debug") 7 | 8 | # 设置项目库文件输出的路径 9 | set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) 10 | 11 | # 设置项目编译头文件搜索路径 -I 12 | include_directories(${PROJECT_SOURCE_DIR}/include) 13 | include_directories(${PROJECT_SOURCE_DIR}/example/protos) 14 | 15 | # 设置项目库文件搜索路径 -L 16 | link_directories(${PROJECT_SOURCE_DIR}/lib) 17 | 18 | # src包含了mpzrpc框架所有的相关代码 19 | add_subdirectory(src) 20 | # example包含了mpzrpc框架使用的示例代码 21 | add_subdirectory(example) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](images/head.png) 2 | 3 | ![](https://img.shields.io/badge/build-passing-brightgreen) ![](https://img.shields.io/badge/ubuntu-18.04-blue) ![](https://img.shields.io/badge/protobuf-3.20-blue) ![](https://img.shields.io/badge/zookeeper-3.4.10-blue) ![](https://img.shields.io/badge/cmake-3.21-blue) 4 | 5 | 6 | 7 | # 1 概述 8 | 9 | mpzRPC是基于C++实现的RPC分布式网络通信框架,框架基于muduo高性能网络库、protobuf数据交换、Zookeepr服务注册中间件开发,即mpzRPC。mpzRPC在客户端隐藏了通信和连接过程,在服务端提供了简洁本地服务发布注册接口,整体高效易用。 10 | 11 | #### 设计思路 12 | 13 | protofbuf原生提供了基于rpc service方法调用代码框架,无需实现对服务接口的描述,只需要关注服务管理和rpc通信流程;其次,protobuf基于二进制序列化数据的,同带宽具有更高的通信效率;因此,采用protobuf为基础,整体框架初始构想如下; 14 | 15 | ![](images/design_init.svg) 16 | 17 | 灰色区域由protobuf提供,红色区域是框架的待开发部分: 18 | 19 | - 服务映射表:记录服务端发布的本地服务名称和服务对象;当客户端发起调用时,输入RPC方法名->查表->取服务对象->调用; 20 | - 服务注册中间件:服务端在将发布的服务进行注册,客户端根据服务、方法名从服务注册中间件获取服务所在机器的URL,从而定位通信对象; 21 | - 网络中间件:RPC请求的发送和服务调用响应的返回,需要网络中间件实现客户端和服务端的通信; 22 | - 通信协议:客户端调用一个远程RPC服务,需要提供服务名称、方法名称、方法输入参数,因此客户端打包这些参数和服务端解包需要提前约定一个通信协议; 23 | 24 | #### 设计实现 25 | 26 | ![](images/design.svg) 27 | 28 | - 服务映射表:由provider类->publicService实现本地服务发布为RPC服务; 29 | - 服务注册中间件:利用Zookeeper的服务注册功和发现能,具有两方便优势,其一,客户端和服务端解耦,客户端无需手动修改服务URL配置文件;其二,提供者和服务中心间存在心跳检测和服务协调,实时检测更新提供服务的URL; 30 | - 网络中间件:采用muduo网络库实现通信,muduo库采用one loop per thread设计,多线程并发执行多个事件循环,每个事件循环采用非阻塞+epoll作为IO模型,性能优秀; 31 | - 通信协议:head_size+head_str+request_str设计,其中head_str中包含request_size信息,避免Tcp粘包; 32 | 33 | #### 交互流程 34 | 35 | 1. 发布本地服务,在服务端,通过provider->publishService将本地服务封装成可被远程调用的rpc服务,并添加到服务映射表; 36 | 2. 启动服务,provider->run启动TcpServer,并创建一个监听fd->acceptFd;将服务映射表上的RPC服务注册到ZooKeeper; 37 | 3. 客户端发起调用,根据目标服务名、方法名,从ZooKeeper上获取RPC方法所在URL;按通信协议打包数据包,序列化后通过clientFd连接TcpServer,发送数据包; 38 | 4. 建立连接,当客户端连接acceptFd成功时,acceptFd返回connFd与clientFd组成TCP链路,并创建对应TcpConnection对象; 39 | 5. 服务端接收数据,客户端发送数据包写入服务端connFd读缓冲区时,触发TcpConnection对象的onMessageCallback回调; 40 | 6. 服务端解包,在onMessageCallback回调中,反序列化后,按通信协议解包,通过包中rpc方法名查服务映射表获取对应service、MethodDescribe对象,通过包中request_str构建Request对象; 41 | 7. 服务端绑定回调,将provider的sendResponse设置为callMethod中的closure关闭回调对象; 42 | 8. 服务端调用,在onMessageCallback回调中,service对象->callMethod->执行rpc method->执行local method->返回值写入Response对象->触发关闭回调sendResponse; 43 | 9. 服务端返回,sendResponse中将response序列化后返回客户端,客户端clientFd通过recv接收并反序列化后从response对象中获取调用结果,至此完成一轮rpc调用,关闭Tcp连接; 44 | 45 | # 2 安装 46 | 47 | #### 安装依赖库 48 | 49 | - [protobuf](https://github.com/protocolbuffers/protobuf) 50 | - [ZooKeeper](https://github.com/apache/zookeeper) 51 | - [muduo](https://github.com/chenshuo/muduo) 52 | - [cmake](https://github.com/Kitware/CMake) 53 | - [json](https://github.com/nlohmann/json) 54 | 55 | #### 编译 56 | 57 | ```shell 58 | # 克隆 59 | git clone https://github.com/wangzyon/mpzRPC 60 | # 编译 61 | cd ./mpzRPC && sudo ./autobuild.sh 62 | ``` 63 | 64 | # 3 使用 65 | 66 | 67 | 68 | #### 定义RPC接口 69 | 70 | ```protobuf 71 | // protobuf版本 72 | syntax = "proto3"; 73 | // 包名,在C++中表现为命名空间 74 | package example; 75 | // 生成service服务类的描述,默认不生成 76 | option cc_generic_services=true; 77 | // 状态 78 | message ResultCode 79 | { 80 | int32 errcode = 1; 81 | bytes errmsg = 2; 82 | } 83 | // 请求 84 | message LoginRequest 85 | { 86 | bytes name=1; 87 | bytes pwd=2; 88 | } 89 | // 响应 90 | message LoginResponse 91 | { 92 | ResultCode result=1; // 复合message 93 | bool success = 2; 94 | } 95 | // 定义RPC接口 96 | service UserRpcService 97 | { 98 | rpc Login(LoginRequest) returns(LoginResponse); 99 | } 100 | ``` 101 | 102 | #### 发布RPC服务 103 | 104 | ```cpp 105 | #include 106 | #include 107 | #include 108 | #include "example.service.pb.h" 109 | 110 | class UserService : public example::UserRpcService 111 | { 112 | public: 113 | 114 | bool Login(const std::string &name, const std::string pwd) // 本地服务 115 | { 116 | std::cout << "local service: Login" << std::endl; 117 | std::cout << "name:" << name << "pwd" << std::endl; 118 | return pwd == "123"; 119 | } 120 | void Login(::google::protobuf::RpcController *controller, // RPC服务 121 | const ::example::LoginRequest *request, 122 | ::example::LoginResponse *response, 123 | ::google::protobuf::Closure *done) 124 | { 125 | // 框架给业务上报了请求参数LoginRequest,应用获取相应数据做本地业务 126 | std::string name = request->name(); 127 | std::string pwd = request->pwd(); 128 | // 做本地业务 129 | bool ret = Login(name, pwd); 130 | response->set_success(ret); 131 | // 把响应写入,包括错误码、错误消息、返回值 132 | example::ResultCode *result_code = response->mutable_result(); 133 | result_code->set_errcode(0); 134 | result_code->set_errmsg(""); 135 | // 执行回调操作, 执行响应对象数据的序列化和网络发送(都是由框架来完成的) 136 | done->Run(); 137 | }; 138 | }; 139 | 140 | int main(int argc, char **argv) 141 | { 142 | MpzrpcApplication::init(argc, argv); 143 | std::cout << MpzrpcApplication::getApp().getConfig().getRpcServerIp() << std::endl; 144 | MpzrpcProvider provider; 145 | provider.publishService(new UserService()); // 2.发布服务(将本地Login发布为RPC Login) 146 | provider.run(); // 3.启动服务 147 | return 0; 148 | }; 149 | ``` 150 | 151 | #### 调用RPC服务 152 | 153 | ```cpp 154 | #include 155 | #include "example.service.pb.h" 156 | #include 157 | #include 158 | 159 | int main(int argc, char **argv) 160 | { 161 | // 1.初始化框架 162 | MpzrpcApplication::init(argc, argv); 163 | // 2.在客户端创建服务调用类对象stub 164 | example::UserRpcService_Stub stub(new MpzrpcChannel()); 165 | // 3.创建RPC调用的请求对象和响应对象; 166 | example::LoginRequest request; 167 | request.set_name("zhang san"); 168 | request.set_pwd("123456"); 169 | example::LoginResponse response; 170 | // 4.调用 171 | stub.Login(nullptr, &request, &response, nullptr); 172 | // 5.打印响应结果 173 | if (0 == response.result().errcode()) 174 | { 175 | std::cout << "rpc login response success:" << response.success() << std::endl; 176 | } 177 | else 178 | { 179 | std::cout << "rpc login response error : " << response.result().errmsg() << std::endl; 180 | } 181 | return 0; 182 | } 183 | ``` 184 | 185 | 186 | 187 | > 完整例程参考:[example](example/) 188 | 189 | # 4 关键设计 190 | 191 | #### 服务接口设计 192 | 193 | protobuf文件编译后将生成一系列C++类型,从而实现便利的接口服务; 194 | 195 | ![](images/service.svg) 196 | 197 | protobuf中service标识将生成如下C++类型: 198 | 199 | | C++类 | 描述 | 200 | | ---------------- | ------------------------------------------------------------ | 201 | | Service | 服务类,服务端使用,可获取各方法的请求类型、方法的返回类型等; | 202 | | Service_Stub | 服务类,客户端使用,可获取各方法的请求类型、方法的返回类型等; | 203 | | ServiceDescrible | 服务的描述类,记录服务的名称、服务里的方法数量、各方法的描述类等; | 204 | | MethodDescrible | 方法的描述类,记录方法的名称、方法所在服务描述等; | 205 | | Request | Message类,rpc方法的输入类型; | 206 | | Response | Message类,rpc方法的返回类型; | 207 | 208 | protobuf额外生成两个C++类型: 209 | 210 | | C++类 | 描述 | 211 | | ---------- | --------------------------------------------------- | 212 | | Controller | 手动记录rpc调用过程状态,从而查询调用过程是否成功; | 213 | | Closure | 回调; | 214 | 215 | RPC调用无论客户端还是服务端,核心均是调用CallMethod方法; 216 | 217 | ```cpp 218 | // 服务端:Service->CallMethod 219 | // 客户端:Service_Stub->CallMethod 220 | 221 | void CallMethod( 222 | const google::protobuf::MethodDescriptor *method, 223 | google::protobuf::RpcController *controller, 224 | const google::protobuf::Message *request, 225 | google::protobuf::Message *response, 226 | google::protobuf::Closure *done); 227 | ``` 228 | 229 | #### 发布服务设计 230 | 231 | 发布服务指将服务端的本地服务封装成RPC服务后,记录在服务映射表的过程,当获取客户端RPC调用请求时,根据服务名称和方法名称可通过查询服务映射表获取服务和方法对象,服务映射表采用map嵌套结构如下: 232 | 233 | ![](images/servicemap.svg) 234 | 235 | 服务映射表C++代码设计 : 236 | 237 | ```c++ 238 | struct ServiceInfo 239 | { 240 | google::protobuf::Service *m_service; 241 | std::unordered_map m_methodmap; 242 | }; 243 | 244 | std::unordered_map m_servicemap; 245 | ``` 246 | 247 | > 完整实现参考:[mpzrpcprovider.cc](src/mpzrpcprovider.cc)->pulishService 248 | 249 | #### 异步日志设计 250 | 251 | 写日志信息到文件使用磁盘I/O,若直接放到RPC方法调用的业务中,会影响RPC请求->RPC方法执行->RPC响应整个流程的速度,因此在Looger日志模块和RPC业务之间添加一个消息队列作为中间件,Muduo只负责向消息中间件添加日志信息,在新线程中Logger模块从消息队列读日志信息,并执行IO磁盘操作,实现了写日志和磁盘IO操作的解耦; 252 | 253 | > 异步指Muduo中业务线程不用等待日志写入文件,将日志信息添加到消息队列中,即可继续执行业务逻辑; 254 | 255 | ![](images/logger.svg) 256 | 257 | - 线程安全:多个线程同时操作消息队列,因此,在队列的push和pop方法中添加mutex锁保证线程安全; 258 | - 线程通信:pop操作中,若消息队列为空,则一直等待,同时Muduo无法获取锁,而不能添加消息,此时造成死锁;因此,在push和pop间使用condition_variable条件变量实现线程通信,当push操作执行后,通知pop操作可以取锁执行; 259 | 260 | > 完整实现参考:[lockqueue.h](include/lockqueue.h) 261 | > 262 | > 一个功能更加强大的消息中间件:**kafka** 263 | 264 | #### 通信协议设计 265 | 266 | 客户端和服务端通信,为避免粘包,需要约定一个通信协议; 267 | 268 | ![](images/protocol.svg) 269 | 270 | 采用protobuf定义数据包头的数据结构: 271 | 272 | ```protobuf 273 | // protobuf版本 274 | syntax = "proto3"; 275 | 276 | // 包名,在C++中表现为命名空间 277 | package rpcheader; 278 | 279 | message rpcheader 280 | { 281 | bytes service_name=1; 282 | bytes method_name=2; 283 | uint32 request_size=3; 284 | } 285 | ``` 286 | 287 | header_size是一个int32_t类型值,表示header_str长度,header_str由rpcheader序列化产生,包含一个int32_t类型的request_size,即request_str长度,因此,可根据header_size和request_size确定数据包的边界,避免粘包。 288 | 289 | > 采用int32_t类型记录包头大小,而非字符串类型,例如int32_t类型表示范围2^32-1,而4字节字符串表示范围时"0"~"9999" 290 | 291 | 292 | 293 | ##### 打包 294 | 295 | ```c++ 296 | // 设置包头 297 | rpcheader::rpcheader header; 298 | header.set_service_name(service_name); 299 | header.set_method_name(method_name); 300 | header.set_request_size(request_str.size()); 301 | 302 | // 序列化包头->header_str 303 | std::string header_str; 304 | if (!header.SerializeToString(&header_str)) 305 | { 306 | LOG_ERR("%s", "message header_str serialization failed"); 307 | return; 308 | } 309 | 310 | // 4字节int32_t类型包头大小转换为4字节字符类型 311 | uint32_t header_size = header_str.size(); 312 | std::string send_str; 313 | send_str.insert(0, std::string((char *)&header_size, 4)); 314 | 315 | // 打包 316 | send_str += header_str + args_str; 317 | ``` 318 | 319 | ##### 解包 320 | 321 | ```c++ 322 | // 接收数据包 323 | std::string recv_str = buffer->retrieveAllAsString(); 324 | // 从字符流中读取前4个字节的内容,即header_size 325 | uint32_t header_size = 0; 326 | recv_str.copy((char *)&header_size, 4, 0); 327 | // 根据header_size读取数据头的原始字符流,反序列化数据,得到header_str 328 | std::string header_str = recv_str.substr(4, header_size); 329 | rpcheader::rpcheader header; 330 | if (!header.ParseFromString(header_str)) 331 | { 332 | LOG_ERR("%s", "header str deserialization failed"); 333 | return; 334 | } 335 | // 反序列化包头 336 | std::string service_name = header.service_name(); 337 | std::string method_name = header.method_name(); 338 | uint32_t request_size = header.request_size(); 339 | // 获取rpc方法参数的字符流数据,即request_str 340 | std::string request_str = recv_str.substr(4 + header_size, request_size); 341 | ``` 342 | 343 | 344 | -------------------------------------------------------------------------------- /autobuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # 如果没有build目录,创建该目录 6 | if [ ! -d `pwd`/build ]; then 7 | mkdir `pwd`/build 8 | fi 9 | 10 | rm -rf `pwd`/build/* 11 | 12 | cd `pwd`/build && 13 | cmake .. && 14 | make 15 | 16 | # 回到项目根目录 17 | cd .. 18 | 19 | # 把头文件拷贝到 /usr/include/mpzrpc .a静态库拷贝到 /usr/lib 20 | if [ ! -d /usr/include/mpzrpc ]; then 21 | mkdir /usr/include/mpzrpc 22 | fi 23 | 24 | for header in `ls include/*.h` 25 | do 26 | cp $header /usr/include/mpzrpc 27 | done 28 | 29 | cp `pwd`/lib/libmpzrpc.a /usr/lib 30 | 31 | ldconfig -------------------------------------------------------------------------------- /bin/appConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "rpcserverip" :"127.0.0.1", 3 | "rpcserverport": 8805, 4 | "zookeeperip":"127.0.0.1", 5 | "zookeeperport": 2181, 6 | "muduothreadnum": 3 7 | } -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(${PROJECT_SOURCE_DIR}/example/service SERVICE_SRC) 2 | aux_source_directory(${PROJECT_SOURCE_DIR}/example/client CLIENT_SRC) 3 | aux_source_directory(${PROJECT_SOURCE_DIR}/example/protos PROTO_SRC) 4 | 5 | # 设置项目可执行文件输出的路径 6 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 7 | 8 | # 服务提供者 9 | add_executable(serve ${SERVICE_SRC} ${PROTO_SRC}) 10 | target_link_libraries(serve mpzrpc protobuf) 11 | 12 | # 服务调用者 13 | add_executable(client ${CLIENT_SRC} ${PROTO_SRC}) 14 | target_link_libraries(client mpzrpc protobuf) 15 | 16 | -------------------------------------------------------------------------------- /example/client/UerRpcClient.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mpzrpcapplication.h" 3 | #include "example.service.pb.h" 4 | #include "mpzrpcchannel.h" 5 | 6 | int main(int argc, char **argv) 7 | { 8 | // 整个程序启动以后,想使用mpzrpc框架来享受rpc服务调用,一定需要先调用框架的初始化函数(只初始化一次) 9 | MpzrpcApplication::init(argc, argv); 10 | 11 | // 演示调用远程发布的rpc方法Login 12 | example::UserRpcService_Stub stub(new MpzrpcChannel()); 13 | 14 | // rpc方法的请求参数 15 | example::LoginRequest request; 16 | request.set_name("zhang san"); 17 | request.set_pwd("123456"); 18 | // rpc方法的响应 19 | example::LoginResponse response; 20 | // 发起rpc方法的调用 同步的rpc调用过程 MpzrpcChannel::callmethod 21 | stub.Login(nullptr, &request, &response, nullptr); // RpcChannel->RpcChannel::callMethod 集中来做所有rpc方法调用的参数序列化和网络发送 22 | 23 | // 一次rpc调用完成,读调用的结果 24 | if (0 == response.result().errcode()) 25 | { 26 | std::cout << "rpc login response success:" << response.success() << std::endl; 27 | } 28 | else 29 | { 30 | std::cout << "rpc login response error : " << response.result().errmsg() << std::endl; 31 | } 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /example/protos/example.service.proto: -------------------------------------------------------------------------------- 1 | // protobuf版本 2 | syntax = "proto3"; 3 | 4 | // 包名,在C++中表现为命名空间 5 | package example; 6 | 7 | // 生成service服务类的描述,默认不生成 8 | option cc_generic_services=true; 9 | 10 | 11 | // 定义数据结构(消息),1,2指字段标识号,可以理解为字段占位序号 12 | message ResultCode 13 | { 14 | int32 errcode = 1; 15 | bytes errmsg = 2; 16 | } 17 | 18 | message LoginRequest 19 | { 20 | bytes name=1; 21 | bytes pwd=2; 22 | } 23 | 24 | message LoginResponse 25 | { 26 | ResultCode result=1; // 复合message 27 | bool success = 2; 28 | } 29 | 30 | message RegisterRequest 31 | { 32 | uint32 id = 1; 33 | bytes name = 2; 34 | bytes pwd = 3; 35 | } 36 | 37 | message RegisterResponse 38 | { 39 | ResultCode result = 1; 40 | bool sucess = 2; 41 | } 42 | 43 | message GetFriendsListRequest 44 | { 45 | uint32 userid = 1; 46 | } 47 | 48 | message GetFriendsListResponse 49 | { 50 | ResultCode result = 1; 51 | repeated bytes friends = 2; 52 | } 53 | 54 | // 定义用户模块相关RPC接口 55 | service UserRpcService 56 | { 57 | rpc Login(LoginRequest) returns(LoginResponse); 58 | rpc Register(RegisterRequest) returns(RegisterResponse); 59 | } 60 | 61 | 62 | // 定义好友模块相关RPC接口 63 | service FiendRpcService 64 | { 65 | rpc GetFriendsList(GetFriendsListRequest) returns(GetFriendsListResponse); 66 | } -------------------------------------------------------------------------------- /example/service/UserRpcService.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "example.service.pb.h" 3 | #include "mpzrpcapplication.h" 4 | #include "mpzrpcprovider.h" 5 | 6 | class UserService : public example::UserRpcService 7 | { 8 | public: 9 | bool Login(const std::string &name, const std::string pwd) 10 | { 11 | std::cout << "local service: Login" << std::endl; 12 | std::cout << "name:" << name << "pwd" << std::endl; 13 | return pwd == "123"; 14 | } 15 | 16 | // Closure关闭,终止 17 | void Login(::google::protobuf::RpcController *controller, 18 | const ::example::LoginRequest *request, 19 | ::example::LoginResponse *response, 20 | ::google::protobuf::Closure *done) 21 | { 22 | // 框架给业务上报了请求参数LoginRequest,应用获取相应数据做本地业务 23 | std::string name = request->name(); 24 | std::string pwd = request->pwd(); 25 | 26 | // 做本地业务 27 | bool ret = Login(name, pwd); 28 | response->set_success(ret); 29 | 30 | // 把响应写入 包括错误码、错误消息、返回值 31 | example::ResultCode *result_code = response->mutable_result(); 32 | result_code->set_errcode(0); 33 | result_code->set_errmsg(""); 34 | 35 | // 执行回调操作 执行响应对象数据的序列化和网络发送(都是由框架来完成的) 36 | done->Run(); 37 | }; 38 | }; 39 | 40 | int main(int argc, char **argv) 41 | { 42 | MpzrpcApplication::init(argc, argv); 43 | std::cout << MpzrpcApplication::getApp().getConfig().getRpcServerIp() << std::endl; 44 | MpzrpcProvider provider; 45 | provider.publishService(new UserService()); 46 | provider.run(); 47 | 48 | return 0; 49 | }; -------------------------------------------------------------------------------- /images/build-passing-brightgreen.svg: -------------------------------------------------------------------------------- 1 | build: passingbuildpassing -------------------------------------------------------------------------------- /images/design_init.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |

MethodDescriptor
MethodDescriptor
Request
Request
Response
Response
controller
controller
closure
closure
CallMethod
CallMethod
打包
打包
序列化
序列化

MethodDescriptor
MethodDescriptor
Request
Request
Response
Response
controller
controller
closure
closure
CallMethod
CallMethod
反序列化 
反序列化 
解包
解包

服务映射表
服务映射表
序列化 
序列化 
反序列化 
反序列化 
MethodDescriptor
MethodDescriptor
controller
controller

服务注册中间件
服务注册中间件

网络中间件
网络中间件

通信协议
通信协议
客户端Service_Stub
客户端Service_Stub
服务端Service
服务端Service
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /images/head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzyon/mpzRPC/16afcb98446208c35f768632d3a0f733cf177864/images/head.png -------------------------------------------------------------------------------- /images/head.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzyon/mpzRPC/16afcb98446208c35f768632d3a0f733cf177864/images/head.psd -------------------------------------------------------------------------------- /images/logger.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
subEventLoop
subEventLoop
subEventLoop
subEventLoop
subEventLoop
subEventLoop
Muduo
Muduo
LockQueue
LockQueue
Logger
Logger
log.txt
log.txt
notify_one
notify_one
push
push
wait
wait
pop
pop
lock
lock
lock
lock
线程安全
线程安全
线程安全
线程安全
线程通信
线程通信
queue
queue
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /images/protocol.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |

通信协议
通信协议
request_size
request_size
request_str
request_str
header_str
header_str
header_size
header_size
service_name
service_name
method_name
method_name
4 byte
4 byte
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /images/service.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Service
Service
service RpcService
{
    rpc Login(LoginRequest) returns(LoginResponse);
}
service RpcService...
Service
Service
ServiceDescriptor
ServiceDescriptor
Service_Stub
Service_Stub
MethodDescriptor
MethodDescriptor
 Request
 Request
Response
Response
controller
controller
closure
closure
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /images/servicemap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
service_map
service_map
service_info
service_info
method_map
method_map
method_name
method_name
method_describe
method_describe
service
service
method_map
method_map
service_name
service_name
service_info
service_info
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /include/lockqueue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include // pthread_mutex_t 5 | #include // pthread_condition_t 6 | 7 | // 异步写日志的日志队列 8 | template 9 | class LockQueue 10 | { 11 | public: 12 | // 多个worker线程都会写日志queue 13 | void Push(const T &data) 14 | { 15 | std::lock_guard lock(m_mutex); 16 | m_queue.push(data); 17 | m_condvariable.notify_one(); 18 | } 19 | 20 | // 一个线程读日志queue,写日志文件 21 | T Pop() 22 | { 23 | std::unique_lock lock(m_mutex); 24 | while (m_queue.empty()) 25 | { 26 | // 日志队列为空,线程进入wait状态 27 | m_condvariable.wait(lock); 28 | } 29 | 30 | T data = m_queue.front(); 31 | m_queue.pop(); 32 | return data; 33 | } 34 | 35 | private: 36 | std::queue m_queue; 37 | std::mutex m_mutex; 38 | std::condition_variable m_condvariable; 39 | }; -------------------------------------------------------------------------------- /include/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "lockqueue.h" 3 | #include 4 | 5 | // 定义宏 LOG_INFO("xxx %d %s", 20, "xxxx") 6 | #define LOG_INFO(logmsgformat, ...) \ 7 | do \ 8 | { \ 9 | Logger &logger = Logger::GetInstance(); \ 10 | logger.SetLogLevel(INFO); \ 11 | char c[1024] = {0}; \ 12 | snprintf(c, 1024, logmsgformat, ##__VA_ARGS__); \ 13 | logger.Log(c); \ 14 | } while (0) 15 | 16 | #define LOG_ERR(logmsgformat, ...) \ 17 | do \ 18 | { \ 19 | Logger &logger = Logger::GetInstance(); \ 20 | logger.SetLogLevel(ERROR); \ 21 | char c[1024] = {0}; \ 22 | snprintf(c, 1024, logmsgformat, ##__VA_ARGS__); \ 23 | logger.Log(c); \ 24 | } while (0) 25 | 26 | // 定义日志级别 27 | enum LogLevel 28 | { 29 | INFO, // 普通信息 30 | ERROR, // 错误信息 31 | }; 32 | 33 | // Mpzrpc框架提供的日志系统 34 | class Logger 35 | { 36 | public: 37 | // 获取日志的单例 38 | static Logger &GetInstance(); 39 | // 设置日志级别 40 | void SetLogLevel(LogLevel level); 41 | // 写日志 42 | void Log(std::string msg); 43 | 44 | private: 45 | int m_loglevel; // 记录日志级别 46 | LockQueue m_lckQue; // 日志缓冲队列 47 | 48 | Logger(); 49 | Logger(const Logger &) = delete; 50 | Logger(Logger &&) = delete; 51 | }; -------------------------------------------------------------------------------- /include/mpzrpcapplication.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "mpzrpcconfig.h" 5 | 6 | class MpzrpcApplication 7 | { 8 | public: 9 | static void init(int argc, char **argv); 10 | 11 | static void showArgsHelp() 12 | { 13 | std::cout << "format: command -c \n"; 14 | } 15 | 16 | static MpzrpcApplication &getApp(); 17 | static MpzrpcConfig &getConfig(); 18 | 19 | private: 20 | MpzrpcApplication(){}; 21 | MpzrpcApplication(const MpzrpcApplication &) = delete; 22 | MpzrpcApplication(MpzrpcApplication &&) = delete; 23 | }; -------------------------------------------------------------------------------- /include/mpzrpcchannel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class MpzrpcChannel : public google::protobuf::RpcChannel 7 | { 8 | public: 9 | // 所有通过stub代理对象调用的rpc方法,都走到这里了,统一做rpc方法调用的数据数据序列化和网络发送 10 | void CallMethod(const google::protobuf::MethodDescriptor *method, 11 | google::protobuf::RpcController *controller, 12 | const google::protobuf::Message *request, 13 | google::protobuf::Message *response, 14 | google::protobuf::Closure *done); 15 | }; 16 | -------------------------------------------------------------------------------- /include/mpzrpcconfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class MpzrpcConfig 7 | { 8 | public: 9 | void LoadConfigFromFile(const std::string &config_file); 10 | 11 | std::string &getRpcServerIp() { return m_rpcserverip; }; 12 | int &getRpcServerPort() { return m_rpcserverport; }; 13 | std::string &getZooKeeperIp() { return m_zookeeperip; }; 14 | int &getZooKeeperPort() { return m_zookeeperport; }; 15 | int &getMuduoThreadNum() { return m_muduoThreadNum; }; 16 | 17 | private: 18 | std::string m_rpcserverip; 19 | int m_rpcserverport; 20 | std::string m_zookeeperip; 21 | int m_zookeeperport; 22 | int m_muduoThreadNum; 23 | }; -------------------------------------------------------------------------------- /include/mpzrpccontroller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class MpzrpcController : public google::protobuf::RpcController 6 | { 7 | public: 8 | MpzrpcController(); 9 | void Reset(); 10 | bool Failed() const; 11 | std::string ErrorText() const; 12 | void SetFailed(const std::string &reason); 13 | 14 | // 目前未实现具体的功能 15 | void StartCancel(); 16 | bool IsCanceled() const; 17 | void NotifyOnCancel(google::protobuf::Closure *callback); 18 | 19 | private: 20 | bool m_failed; // RPC方法执行过程中的状态 21 | std::string m_errText; // RPC方法执行过程中的错误信息 22 | }; -------------------------------------------------------------------------------- /include/mpzrpcprovider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | class MpzrpcProvider 14 | { 15 | public: 16 | void publishService(::google::protobuf::Service *service); 17 | 18 | void run(); 19 | 20 | void onConnectionCallback(const muduo::net::TcpConnectionPtr &conn) 21 | { 22 | if (!conn->connected()) 23 | { 24 | // 和rpc client的连接断开了 25 | conn->shutdown(); 26 | } 27 | }; 28 | 29 | void onMessageCallback(const muduo::net::TcpConnectionPtr &conn, 30 | muduo::net::Buffer *buffer, 31 | muduo::Timestamp receiveTime); 32 | 33 | void SendRpcResponse(const muduo::net::TcpConnectionPtr &conn, google::protobuf::Message *response); 34 | 35 | private: 36 | struct ServiceInfo 37 | { 38 | google::protobuf::Service *m_service; 39 | std::unordered_map m_methodmap; 40 | }; 41 | 42 | std::unordered_map m_servicemap; 43 | }; -------------------------------------------------------------------------------- /include/rpcheader.pb.h: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: rpcheader.proto 3 | 4 | #ifndef GOOGLE_PROTOBUF_INCLUDED_rpcheader_2eproto 5 | #define GOOGLE_PROTOBUF_INCLUDED_rpcheader_2eproto 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #if PROTOBUF_VERSION < 3020000 12 | #error This file was generated by a newer version of protoc which is 13 | #error incompatible with your Protocol Buffer headers. Please update 14 | #error your headers. 15 | #endif 16 | #if 3020000 < PROTOBUF_MIN_PROTOC_VERSION 17 | #error This file was generated by an older version of protoc which is 18 | #error incompatible with your Protocol Buffer headers. Please 19 | #error regenerate this file with a newer version of protoc. 20 | #endif 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include // IWYU pragma: export 31 | #include // IWYU pragma: export 32 | #include 33 | // @@protoc_insertion_point(includes) 34 | #include 35 | #define PROTOBUF_INTERNAL_EXPORT_rpcheader_2eproto 36 | PROTOBUF_NAMESPACE_OPEN 37 | namespace internal { 38 | class AnyMetadata; 39 | } // namespace internal 40 | PROTOBUF_NAMESPACE_CLOSE 41 | 42 | // Internal implementation detail -- do not use these members. 43 | struct TableStruct_rpcheader_2eproto { 44 | static const uint32_t offsets[]; 45 | }; 46 | extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_rpcheader_2eproto; 47 | namespace rpcheader { 48 | class rpcheader; 49 | struct rpcheaderDefaultTypeInternal; 50 | extern rpcheaderDefaultTypeInternal _rpcheader_default_instance_; 51 | } // namespace rpcheader 52 | PROTOBUF_NAMESPACE_OPEN 53 | template<> ::rpcheader::rpcheader* Arena::CreateMaybeMessage<::rpcheader::rpcheader>(Arena*); 54 | PROTOBUF_NAMESPACE_CLOSE 55 | namespace rpcheader { 56 | 57 | // =================================================================== 58 | 59 | class rpcheader final : 60 | public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rpcheader.rpcheader) */ { 61 | public: 62 | inline rpcheader() : rpcheader(nullptr) {} 63 | ~rpcheader() override; 64 | explicit PROTOBUF_CONSTEXPR rpcheader(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); 65 | 66 | rpcheader(const rpcheader& from); 67 | rpcheader(rpcheader&& from) noexcept 68 | : rpcheader() { 69 | *this = ::std::move(from); 70 | } 71 | 72 | inline rpcheader& operator=(const rpcheader& from) { 73 | CopyFrom(from); 74 | return *this; 75 | } 76 | inline rpcheader& operator=(rpcheader&& from) noexcept { 77 | if (this == &from) return *this; 78 | if (GetOwningArena() == from.GetOwningArena() 79 | #ifdef PROTOBUF_FORCE_COPY_IN_MOVE 80 | && GetOwningArena() != nullptr 81 | #endif // !PROTOBUF_FORCE_COPY_IN_MOVE 82 | ) { 83 | InternalSwap(&from); 84 | } else { 85 | CopyFrom(from); 86 | } 87 | return *this; 88 | } 89 | 90 | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { 91 | return GetDescriptor(); 92 | } 93 | static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { 94 | return default_instance().GetMetadata().descriptor; 95 | } 96 | static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { 97 | return default_instance().GetMetadata().reflection; 98 | } 99 | static const rpcheader& default_instance() { 100 | return *internal_default_instance(); 101 | } 102 | static inline const rpcheader* internal_default_instance() { 103 | return reinterpret_cast( 104 | &_rpcheader_default_instance_); 105 | } 106 | static constexpr int kIndexInFileMessages = 107 | 0; 108 | 109 | friend void swap(rpcheader& a, rpcheader& b) { 110 | a.Swap(&b); 111 | } 112 | inline void Swap(rpcheader* other) { 113 | if (other == this) return; 114 | #ifdef PROTOBUF_FORCE_COPY_IN_SWAP 115 | if (GetOwningArena() != nullptr && 116 | GetOwningArena() == other->GetOwningArena()) { 117 | #else // PROTOBUF_FORCE_COPY_IN_SWAP 118 | if (GetOwningArena() == other->GetOwningArena()) { 119 | #endif // !PROTOBUF_FORCE_COPY_IN_SWAP 120 | InternalSwap(other); 121 | } else { 122 | ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); 123 | } 124 | } 125 | void UnsafeArenaSwap(rpcheader* other) { 126 | if (other == this) return; 127 | GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); 128 | InternalSwap(other); 129 | } 130 | 131 | // implements Message ---------------------------------------------- 132 | 133 | rpcheader* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { 134 | return CreateMaybeMessage(arena); 135 | } 136 | using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; 137 | void CopyFrom(const rpcheader& from); 138 | using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; 139 | void MergeFrom(const rpcheader& from); 140 | private: 141 | static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); 142 | public: 143 | PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; 144 | bool IsInitialized() const final; 145 | 146 | size_t ByteSizeLong() const final; 147 | const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; 148 | uint8_t* _InternalSerialize( 149 | uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; 150 | int GetCachedSize() const final { return _cached_size_.Get(); } 151 | 152 | private: 153 | void SharedCtor(); 154 | void SharedDtor(); 155 | void SetCachedSize(int size) const final; 156 | void InternalSwap(rpcheader* other); 157 | 158 | private: 159 | friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; 160 | static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { 161 | return "rpcheader.rpcheader"; 162 | } 163 | protected: 164 | explicit rpcheader(::PROTOBUF_NAMESPACE_ID::Arena* arena, 165 | bool is_message_owned = false); 166 | public: 167 | 168 | static const ClassData _class_data_; 169 | const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; 170 | 171 | ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; 172 | 173 | // nested types ---------------------------------------------------- 174 | 175 | // accessors ------------------------------------------------------- 176 | 177 | enum : int { 178 | kServiceNameFieldNumber = 1, 179 | kMethodNameFieldNumber = 2, 180 | kArgsSizeFieldNumber = 3, 181 | }; 182 | // bytes service_name = 1; 183 | void clear_service_name(); 184 | const std::string& service_name() const; 185 | template 186 | void set_service_name(ArgT0&& arg0, ArgT... args); 187 | std::string* mutable_service_name(); 188 | PROTOBUF_NODISCARD std::string* release_service_name(); 189 | void set_allocated_service_name(std::string* service_name); 190 | private: 191 | const std::string& _internal_service_name() const; 192 | inline PROTOBUF_ALWAYS_INLINE void _internal_set_service_name(const std::string& value); 193 | std::string* _internal_mutable_service_name(); 194 | public: 195 | 196 | // bytes method_name = 2; 197 | void clear_method_name(); 198 | const std::string& method_name() const; 199 | template 200 | void set_method_name(ArgT0&& arg0, ArgT... args); 201 | std::string* mutable_method_name(); 202 | PROTOBUF_NODISCARD std::string* release_method_name(); 203 | void set_allocated_method_name(std::string* method_name); 204 | private: 205 | const std::string& _internal_method_name() const; 206 | inline PROTOBUF_ALWAYS_INLINE void _internal_set_method_name(const std::string& value); 207 | std::string* _internal_mutable_method_name(); 208 | public: 209 | 210 | // uint32 args_size = 3; 211 | void clear_args_size(); 212 | uint32_t args_size() const; 213 | void set_args_size(uint32_t value); 214 | private: 215 | uint32_t _internal_args_size() const; 216 | void _internal_set_args_size(uint32_t value); 217 | public: 218 | 219 | // @@protoc_insertion_point(class_scope:rpcheader.rpcheader) 220 | private: 221 | class _Internal; 222 | 223 | template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; 224 | typedef void InternalArenaConstructable_; 225 | typedef void DestructorSkippable_; 226 | ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr service_name_; 227 | ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr method_name_; 228 | uint32_t args_size_; 229 | mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; 230 | friend struct ::TableStruct_rpcheader_2eproto; 231 | }; 232 | // =================================================================== 233 | 234 | 235 | // =================================================================== 236 | 237 | #ifdef __GNUC__ 238 | #pragma GCC diagnostic push 239 | #pragma GCC diagnostic ignored "-Wstrict-aliasing" 240 | #endif // __GNUC__ 241 | // rpcheader 242 | 243 | // bytes service_name = 1; 244 | inline void rpcheader::clear_service_name() { 245 | service_name_.ClearToEmpty(); 246 | } 247 | inline const std::string& rpcheader::service_name() const { 248 | // @@protoc_insertion_point(field_get:rpcheader.rpcheader.service_name) 249 | return _internal_service_name(); 250 | } 251 | template 252 | inline PROTOBUF_ALWAYS_INLINE 253 | void rpcheader::set_service_name(ArgT0&& arg0, ArgT... args) { 254 | 255 | service_name_.SetBytes(static_cast(arg0), args..., GetArenaForAllocation()); 256 | // @@protoc_insertion_point(field_set:rpcheader.rpcheader.service_name) 257 | } 258 | inline std::string* rpcheader::mutable_service_name() { 259 | std::string* _s = _internal_mutable_service_name(); 260 | // @@protoc_insertion_point(field_mutable:rpcheader.rpcheader.service_name) 261 | return _s; 262 | } 263 | inline const std::string& rpcheader::_internal_service_name() const { 264 | return service_name_.Get(); 265 | } 266 | inline void rpcheader::_internal_set_service_name(const std::string& value) { 267 | 268 | service_name_.Set(value, GetArenaForAllocation()); 269 | } 270 | inline std::string* rpcheader::_internal_mutable_service_name() { 271 | 272 | return service_name_.Mutable(GetArenaForAllocation()); 273 | } 274 | inline std::string* rpcheader::release_service_name() { 275 | // @@protoc_insertion_point(field_release:rpcheader.rpcheader.service_name) 276 | return service_name_.Release(); 277 | } 278 | inline void rpcheader::set_allocated_service_name(std::string* service_name) { 279 | if (service_name != nullptr) { 280 | 281 | } else { 282 | 283 | } 284 | service_name_.SetAllocated(service_name, GetArenaForAllocation()); 285 | #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING 286 | if (service_name_.IsDefault()) { 287 | service_name_.Set("", GetArenaForAllocation()); 288 | } 289 | #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING 290 | // @@protoc_insertion_point(field_set_allocated:rpcheader.rpcheader.service_name) 291 | } 292 | 293 | // bytes method_name = 2; 294 | inline void rpcheader::clear_method_name() { 295 | method_name_.ClearToEmpty(); 296 | } 297 | inline const std::string& rpcheader::method_name() const { 298 | // @@protoc_insertion_point(field_get:rpcheader.rpcheader.method_name) 299 | return _internal_method_name(); 300 | } 301 | template 302 | inline PROTOBUF_ALWAYS_INLINE 303 | void rpcheader::set_method_name(ArgT0&& arg0, ArgT... args) { 304 | 305 | method_name_.SetBytes(static_cast(arg0), args..., GetArenaForAllocation()); 306 | // @@protoc_insertion_point(field_set:rpcheader.rpcheader.method_name) 307 | } 308 | inline std::string* rpcheader::mutable_method_name() { 309 | std::string* _s = _internal_mutable_method_name(); 310 | // @@protoc_insertion_point(field_mutable:rpcheader.rpcheader.method_name) 311 | return _s; 312 | } 313 | inline const std::string& rpcheader::_internal_method_name() const { 314 | return method_name_.Get(); 315 | } 316 | inline void rpcheader::_internal_set_method_name(const std::string& value) { 317 | 318 | method_name_.Set(value, GetArenaForAllocation()); 319 | } 320 | inline std::string* rpcheader::_internal_mutable_method_name() { 321 | 322 | return method_name_.Mutable(GetArenaForAllocation()); 323 | } 324 | inline std::string* rpcheader::release_method_name() { 325 | // @@protoc_insertion_point(field_release:rpcheader.rpcheader.method_name) 326 | return method_name_.Release(); 327 | } 328 | inline void rpcheader::set_allocated_method_name(std::string* method_name) { 329 | if (method_name != nullptr) { 330 | 331 | } else { 332 | 333 | } 334 | method_name_.SetAllocated(method_name, GetArenaForAllocation()); 335 | #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING 336 | if (method_name_.IsDefault()) { 337 | method_name_.Set("", GetArenaForAllocation()); 338 | } 339 | #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING 340 | // @@protoc_insertion_point(field_set_allocated:rpcheader.rpcheader.method_name) 341 | } 342 | 343 | // uint32 args_size = 3; 344 | inline void rpcheader::clear_args_size() { 345 | args_size_ = 0u; 346 | } 347 | inline uint32_t rpcheader::_internal_args_size() const { 348 | return args_size_; 349 | } 350 | inline uint32_t rpcheader::args_size() const { 351 | // @@protoc_insertion_point(field_get:rpcheader.rpcheader.args_size) 352 | return _internal_args_size(); 353 | } 354 | inline void rpcheader::_internal_set_args_size(uint32_t value) { 355 | 356 | args_size_ = value; 357 | } 358 | inline void rpcheader::set_args_size(uint32_t value) { 359 | _internal_set_args_size(value); 360 | // @@protoc_insertion_point(field_set:rpcheader.rpcheader.args_size) 361 | } 362 | 363 | #ifdef __GNUC__ 364 | #pragma GCC diagnostic pop 365 | #endif // __GNUC__ 366 | 367 | // @@protoc_insertion_point(namespace_scope) 368 | 369 | } // namespace rpcheader 370 | 371 | // @@protoc_insertion_point(global_scope) 372 | 373 | #include 374 | #endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_rpcheader_2eproto 375 | -------------------------------------------------------------------------------- /include/zookeeperutil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // 封装的zk客户端类 8 | class ZkClient 9 | { 10 | public: 11 | ZkClient(); 12 | ~ZkClient(); 13 | // zkclient启动连接zkserver 14 | void Start(); 15 | // 在zkserver上根据指定的path创建znode节点 16 | void Create(const char *path, const char *data, int datalen, int state = 0); 17 | // 根据参数指定的znode节点路径,或者znode节点的值 18 | std::string GetData(const char *path); 19 | 20 | private: 21 | // zk的客户端句柄 22 | zhandle_t *m_zhandle; 23 | }; -------------------------------------------------------------------------------- /lib/libmpzrpc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangzyon/mpzRPC/16afcb98446208c35f768632d3a0f733cf177864/lib/libmpzrpc.a -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC) 2 | 3 | add_library(mpzrpc ${SRC}) 4 | 5 | target_link_libraries(mpzrpc muduo_net muduo_base pthread zookeeper_mt) 6 | 7 | -------------------------------------------------------------------------------- /src/logger.cc: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | #include 3 | #include 4 | 5 | // 获取日志的单例 6 | Logger &Logger::GetInstance() 7 | { 8 | static Logger logger; 9 | return logger; 10 | } 11 | 12 | Logger::Logger() 13 | { 14 | // 启动专门的写日志线程 15 | std::thread writeLogTask([&]() 16 | { 17 | for (;;) 18 | { 19 | // 获取当前的日期,然后取日志信息,写入相应的日志文件当中 a+ 20 | time_t now = time(nullptr); 21 | tm *nowtm = localtime(&now); 22 | 23 | char file_name[128]; 24 | sprintf(file_name, "%d-%d-%d-log.txt", nowtm->tm_year + 1900, nowtm->tm_mon + 1, nowtm->tm_mday); 25 | 26 | FILE *pf = fopen(file_name, "a+"); 27 | if (pf == nullptr) 28 | { 29 | std::cout << "logger file : " << file_name << " open error!" << std::endl; 30 | exit(EXIT_FAILURE); 31 | } 32 | 33 | std::string msg = m_lckQue.Pop(); 34 | 35 | char time_buf[128] = {0}; 36 | sprintf(time_buf, "%d:%d:%d =>[%s] ", 37 | nowtm->tm_hour, 38 | nowtm->tm_min, 39 | nowtm->tm_sec, 40 | (m_loglevel == INFO ? "info" : "error")); 41 | msg.insert(0, time_buf); 42 | msg.append("\n"); 43 | 44 | fputs(msg.c_str(), pf); 45 | fclose(pf); 46 | } 47 | }); 48 | // 设置分离线程,守护线程,进程退出时自动销毁 49 | writeLogTask.detach(); 50 | } 51 | 52 | // 设置日志级别 53 | void Logger::SetLogLevel(LogLevel level) 54 | { 55 | m_loglevel = level; 56 | } 57 | 58 | // 写日志, 把日志信息写入lockqueue缓冲区当中 59 | void Logger::Log(std::string msg) 60 | { 61 | m_lckQue.Push(msg); 62 | } -------------------------------------------------------------------------------- /src/mpzrpcapplication.cc: -------------------------------------------------------------------------------- 1 | #include "mpzrpcapplication.h" 2 | #include 3 | #include 4 | 5 | MpzrpcApplication &MpzrpcApplication::getApp() 6 | { 7 | static MpzrpcApplication app; 8 | return app; 9 | } 10 | 11 | void MpzrpcApplication::init(int argc, char **argv) 12 | { 13 | if (argc < 2) 14 | { 15 | showArgsHelp(); 16 | exit(EXIT_FAILURE); 17 | } 18 | else 19 | { 20 | int o; 21 | std::string config_file; 22 | const char *optstring = "c:"; 23 | while ((o = getopt(argc, argv, optstring)) != -1) 24 | { 25 | switch (o) 26 | { 27 | case 'c': 28 | config_file = optarg; 29 | break; 30 | 31 | default: 32 | break; 33 | } 34 | } 35 | getConfig().LoadConfigFromFile(config_file); 36 | } 37 | }; 38 | 39 | MpzrpcConfig &MpzrpcApplication::getConfig() 40 | { 41 | static MpzrpcConfig m_config; 42 | return m_config; 43 | }; -------------------------------------------------------------------------------- /src/mpzrpcchannel.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "mpzrpcchannel.h" 10 | #include "logger.h" 11 | #include "rpcheader.pb.h" 12 | #include "mpzrpcapplication.h" 13 | #include "zookeeperutil.h" 14 | 15 | void MpzrpcChannel::CallMethod(const google::protobuf::MethodDescriptor *method, 16 | google::protobuf::RpcController *controller, 17 | const google::protobuf::Message *request, 18 | google::protobuf::Message *response, 19 | google::protobuf::Closure *done) 20 | { 21 | const google::protobuf::ServiceDescriptor *service_des = method->service(); 22 | std::string service_name = service_des->name(); 23 | std::string method_name = method->name(); 24 | 25 | std::string args_str; 26 | if (!request->SerializeToString(&args_str)) 27 | { 28 | LOG_ERR("%s", "message request serialization failed"); 29 | return; 30 | } 31 | 32 | rpcheader::rpcheader header; 33 | header.set_service_name(service_name); 34 | header.set_method_name(method_name); 35 | header.set_args_size(args_str.size()); 36 | 37 | std::string header_str; 38 | if (!header.SerializeToString(&header_str)) 39 | { 40 | LOG_ERR("%s", "message header_str serialization failed"); 41 | return; 42 | } 43 | 44 | uint32_t header_size = header_str.size(); 45 | std::string send_str; 46 | send_str.insert(0, std::string((char *)&header_size, 4)); 47 | send_str += header_str + args_str; 48 | 49 | // 使用tcp编程,完成rpc方法的远程调用 50 | int clientfd = socket(AF_INET, SOCK_STREAM, 0); 51 | if (-1 == clientfd) 52 | { 53 | char errtxt[512] = {0}; 54 | sprintf(errtxt, "create socket error! errno:%d", errno); 55 | controller->SetFailed(errtxt); 56 | return; 57 | } 58 | 59 | ZkClient zkCli; 60 | zkCli.Start(); 61 | // 获取待调用的服务名称 /UserServiceRpc/Login 62 | std::string method_path = "/" + service_name + "/" + method_name; 63 | // 获取服务对应的url 64 | std::string host_data = zkCli.GetData(method_path.c_str()); 65 | if (host_data == "") 66 | { 67 | controller->SetFailed(method_path + " is not exist!"); 68 | return; 69 | } 70 | int idx = host_data.find(":"); 71 | if (idx == -1) 72 | { 73 | controller->SetFailed(method_path + " address is invalid!"); 74 | return; 75 | } 76 | std::string ip = host_data.substr(0, idx); 77 | uint16_t port = atoi(host_data.substr(idx + 1, host_data.size() - idx).c_str()); 78 | 79 | struct sockaddr_in server_addr; 80 | server_addr.sin_family = AF_INET; 81 | server_addr.sin_port = htons(port); 82 | server_addr.sin_addr.s_addr = inet_addr(ip.c_str()); 83 | 84 | // 连接rpc服务节点 85 | if (-1 == connect(clientfd, (struct sockaddr *)&server_addr, sizeof(server_addr))) 86 | { 87 | close(clientfd); 88 | char errtxt[512] = {0}; 89 | sprintf(errtxt, "connect error! errno:%d", errno); 90 | controller->SetFailed(errtxt); 91 | return; 92 | } 93 | 94 | std::cout << "connect success" << std::endl; 95 | 96 | // 发送rpc请求 97 | if (-1 == send(clientfd, send_str.c_str(), send_str.size(), 0)) 98 | { 99 | close(clientfd); 100 | char errtxt[512] = {0}; 101 | sprintf(errtxt, "send error! errno:%d", errno); 102 | controller->SetFailed(errtxt); 103 | return; 104 | } 105 | 106 | // 接收rpc请求的响应值 107 | char recv_buf[1024] = {0}; 108 | int recv_size = 0; 109 | if (-1 == (recv_size = recv(clientfd, recv_buf, 1024, 0))) 110 | { 111 | close(clientfd); 112 | char errtxt[512] = {0}; 113 | sprintf(errtxt, "recv error! errno:%d", errno); 114 | controller->SetFailed(errtxt); 115 | return; 116 | } 117 | 118 | // 反序列化rpc调用的响应数据 119 | // std::string response_str(recv_buf, 0, recv_size); //question bug出现问题,recv_buf中遇到\0后面的数据就存不下来了,导致反序列化失败 120 | // if (!response->ParseFromString(response_str)) 121 | if (!response->ParseFromArray(recv_buf, recv_size)) 122 | { 123 | close(clientfd); 124 | char errtxt[512] = {0}; 125 | sprintf(errtxt, "parse error! response_str:%s", recv_buf); 126 | controller->SetFailed(errtxt); 127 | return; 128 | } 129 | 130 | close(clientfd); 131 | } -------------------------------------------------------------------------------- /src/mpzrpcconfig.cc: -------------------------------------------------------------------------------- 1 | #include "mpzrpcconfig.h" 2 | #include 3 | #include 4 | 5 | void MpzrpcConfig::LoadConfigFromFile(const std::string &config_file) 6 | { 7 | std::ifstream i(config_file); 8 | nlohmann::json j; 9 | i >> j; 10 | 11 | m_rpcserverip = j["rpcserverip"]; 12 | m_rpcserverport = j["rpcserverport"]; 13 | m_zookeeperip = j["zookeeperip"]; 14 | m_zookeeperport = j["zookeeperport"]; 15 | m_muduoThreadNum = j["muduothreadnum"]; 16 | } -------------------------------------------------------------------------------- /src/mpzrpccontroller.cc: -------------------------------------------------------------------------------- 1 | #include "mpzrpccontroller.h" 2 | 3 | MpzrpcController::MpzrpcController() 4 | { 5 | m_failed = false; 6 | m_errText = ""; 7 | } 8 | 9 | void MpzrpcController::Reset() 10 | { 11 | m_failed = false; 12 | m_errText = ""; 13 | } 14 | 15 | bool MpzrpcController::Failed() const 16 | { 17 | return m_failed; 18 | } 19 | 20 | std::string MpzrpcController::ErrorText() const 21 | { 22 | return m_errText; 23 | } 24 | 25 | void MpzrpcController::SetFailed(const std::string &reason) 26 | { 27 | m_failed = true; 28 | m_errText = reason; 29 | } 30 | 31 | // 目前未实现具体的功能 32 | void MpzrpcController::StartCancel() {} 33 | bool MpzrpcController::IsCanceled() const { return false; } 34 | void MpzrpcController::NotifyOnCancel(google::protobuf::Closure *callback) {} -------------------------------------------------------------------------------- /src/mpzrpcprovider.cc: -------------------------------------------------------------------------------- 1 | #include "mpzrpcprovider.h" 2 | #include "mpzrpcapplication.h" 3 | #include 4 | #include 5 | #include "rpcheader.pb.h" 6 | #include "logger.h" 7 | #include "zookeeperutil.h" 8 | 9 | void MpzrpcProvider::run() 10 | { 11 | // 读取配置文件rpcserver的信息 12 | std::string ip = MpzrpcApplication::getApp().getConfig().getRpcServerIp(); 13 | uint16_t port = MpzrpcApplication::getApp().getConfig().getRpcServerPort(); 14 | int muduoThreadum = MpzrpcApplication::getApp().getConfig().getMuduoThreadNum(); 15 | 16 | // 创建TcpServer对象 17 | muduo::net::InetAddress address(ip, port); 18 | muduo::net::EventLoop loop; 19 | muduo::net::TcpServer server(&loop, address, "RpcProvider"); 20 | 21 | // 绑定连接回调和消息读写回调方法 分离了网络代码和业务代码 22 | server.setConnectionCallback(std::bind(&MpzrpcProvider::onConnectionCallback, this, std::placeholders::_1)); 23 | server.setMessageCallback(std::bind(&MpzrpcProvider::onMessageCallback, 24 | this, 25 | std::placeholders::_1, 26 | std::placeholders::_2, 27 | std::placeholders::_3)); 28 | // 设置muduo库的线程数量 29 | server.setThreadNum(muduoThreadum); 30 | 31 | ZkClient zkCli; 32 | zkCli.Start(); 33 | for (auto &sp : m_servicemap) 34 | { 35 | // /service_name /UserServiceRpc 36 | std::string service_path = "/" + sp.first; 37 | zkCli.Create(service_path.c_str(), nullptr, 0); 38 | for (auto &mp : sp.second.m_methodmap) 39 | { 40 | // /service_name/method_name /UserServiceRpc/Login 存储当前这个rpc服务节点主机的ip和port 41 | std::string method_path = service_path + "/" + mp.first; 42 | char method_path_data[128] = {0}; 43 | sprintf(method_path_data, "%s:%d", ip.c_str(), port); 44 | // ZOO_EPHEMERAL表示znode是一个临时性节点 45 | zkCli.Create(method_path.c_str(), method_path_data, strlen(method_path_data), ZOO_EPHEMERAL); 46 | } 47 | } 48 | 49 | // rpc服务端准备启动,打印信息 50 | std::cout << "RpcProvider start service at ip:" << ip << " port:" << port << std::endl; 51 | 52 | // 启动subEventLoop用户事件处理服务,启动网络服务接受用户连接 53 | server.start(); 54 | loop.loop(); 55 | }; 56 | 57 | void MpzrpcProvider::publishService(::google::protobuf::Service *service) 58 | { 59 | ServiceInfo servic_info; 60 | 61 | // 获取了服务对象的描述信息 62 | const google::protobuf::ServiceDescriptor *service_des = service->GetDescriptor(); 63 | // 获取服务的名字 64 | std::string service_name = service_des->name(); 65 | // 获取服务对象service的方法的数量 66 | int methodnum = service_des->method_count(); 67 | for (int i = 0; i < methodnum; ++i) 68 | { 69 | // 获取了服务对象指定下标的服务方法的描述(抽象描述) 70 | const google::protobuf::MethodDescriptor *method_des = service_des->method(i); 71 | servic_info.m_methodmap.insert({method_des->name(), method_des}); 72 | } 73 | 74 | servic_info.m_service = service; 75 | m_servicemap.insert({service_name, servic_info}); 76 | }; 77 | 78 | void MpzrpcProvider::onMessageCallback(const muduo::net::TcpConnectionPtr &conn, 79 | muduo::net::Buffer *buffer, 80 | muduo::Timestamp receiveTime) 81 | { 82 | /* 83 | 约定通信协议 84 | 85 | header_size | header string | args string 86 | 4 bytes | header_size bytes | args_size bytes 87 | */ 88 | std::cout << "trigger tcpconnection onMessage callback" << std::endl; 89 | // 取网络发送过来的调用rpc指令 90 | std::string recv_str = buffer->retrieveAllAsString(); 91 | 92 | std::cout << "recv_str: " << recv_str << "##" << std::endl; 93 | 94 | // 从字符流中读取前4个字节的内容 95 | uint32_t header_size = 0; 96 | recv_str.copy((char *)&header_size, 4, 0); 97 | 98 | // 根据header_size读取数据头的原始字符流,反序列化数据,得到rpc请求的详细信息 99 | std::string header_str = recv_str.substr(4, header_size); 100 | rpcheader::rpcheader header; 101 | if (!header.ParseFromString(header_str)) 102 | { 103 | LOG_ERR("%s", "header str deserialization failed"); 104 | return; 105 | } 106 | 107 | std::string service_name = header.service_name(); 108 | std::string method_name = header.method_name(); 109 | uint32_t args_size = header.args_size(); 110 | 111 | // 获取rpc方法参数的字符流数据 112 | std::string args_str = recv_str.substr(4 + header_size, args_size); 113 | auto service_it = m_servicemap.find(service_name); 114 | if (service_it == m_servicemap.end()) 115 | { 116 | LOG_ERR("%s", "unknown service"); 117 | return; 118 | } 119 | 120 | // 获取method描述符 121 | ServiceInfo service_info = service_it->second; 122 | auto method_it = service_info.m_methodmap.find(method_name); 123 | if (method_it == service_info.m_methodmap.end()) 124 | { 125 | LOG_ERR("%s", "unknown method"); 126 | return; 127 | } 128 | 129 | // 打印调试信息 130 | std::cout << "============================================" << std::endl; 131 | std::cout << "header_size: " << header_size << std::endl; 132 | std::cout << "rpc_header_str: " << header_str << std::endl; 133 | std::cout << "service_name: " << service_name << std::endl; 134 | std::cout << "method_name: " << method_name << std::endl; 135 | std::cout << "args_str: " << args_str << std::endl; 136 | std::cout << "============================================" << std::endl; 137 | 138 | // 生成rpc方法调用的请求request和响应response参数 139 | const google::protobuf::MethodDescriptor *method_des = method_it->second; 140 | google::protobuf::Message *request = service_info.m_service->GetRequestPrototype(method_des).New(); 141 | if (!request->ParseFromString(args_str)) 142 | { 143 | LOG_ERR("%s", "message request deserialization failed"); 144 | return; 145 | } 146 | google::protobuf::Message *response = service_info.m_service->GetResponsePrototype(method_des).New(); 147 | 148 | // 给下面的method方法的调用,绑定一个Closure的回调函数 149 | google::protobuf::Closure *done = google::protobuf::NewCallback(this, 152 | &MpzrpcProvider::SendRpcResponse, 153 | conn, 154 | response); 155 | 156 | // Service类callmethod通过method des找到Service重载的对应方法,并传递controller/request/response/done conn, response); 157 | service_info.m_service->CallMethod(method_des, nullptr, request, response, done); 158 | }; 159 | 160 | void MpzrpcProvider::SendRpcResponse(const muduo::net::TcpConnectionPtr &conn, google::protobuf::Message *response) 161 | { 162 | std::string send_str; 163 | if (!response->SerializeToString(&send_str)) 164 | { 165 | LOG_ERR("%s", "message response serialization failed"); 166 | } 167 | else 168 | { 169 | // 序列化成功后,通过网络把rpc方法执行的结果发送会rpc的调用方 170 | conn->send(send_str); 171 | } 172 | conn->shutdown(); // 模拟http的短链接服务,由rpcprovider主动断开连接 173 | } -------------------------------------------------------------------------------- /src/rpcheader.pb.cc: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: rpcheader.proto 3 | 4 | #include "rpcheader.pb.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | // @@protoc_insertion_point(includes) 16 | #include 17 | 18 | PROTOBUF_PRAGMA_INIT_SEG 19 | 20 | namespace _pb = ::PROTOBUF_NAMESPACE_ID; 21 | namespace _pbi = _pb::internal; 22 | 23 | namespace rpcheader { 24 | PROTOBUF_CONSTEXPR rpcheader::rpcheader( 25 | ::_pbi::ConstantInitialized) 26 | : service_name_(&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}) 27 | , method_name_(&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}) 28 | , args_size_(0u){} 29 | struct rpcheaderDefaultTypeInternal { 30 | PROTOBUF_CONSTEXPR rpcheaderDefaultTypeInternal() 31 | : _instance(::_pbi::ConstantInitialized{}) {} 32 | ~rpcheaderDefaultTypeInternal() {} 33 | union { 34 | rpcheader _instance; 35 | }; 36 | }; 37 | PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 rpcheaderDefaultTypeInternal _rpcheader_default_instance_; 38 | } // namespace rpcheader 39 | static ::_pb::Metadata file_level_metadata_rpcheader_2eproto[1]; 40 | static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_rpcheader_2eproto = nullptr; 41 | static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_rpcheader_2eproto = nullptr; 42 | 43 | const uint32_t TableStruct_rpcheader_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { 44 | ~0u, // no _has_bits_ 45 | PROTOBUF_FIELD_OFFSET(::rpcheader::rpcheader, _internal_metadata_), 46 | ~0u, // no _extensions_ 47 | ~0u, // no _oneof_case_ 48 | ~0u, // no _weak_field_map_ 49 | ~0u, // no _inlined_string_donated_ 50 | PROTOBUF_FIELD_OFFSET(::rpcheader::rpcheader, service_name_), 51 | PROTOBUF_FIELD_OFFSET(::rpcheader::rpcheader, method_name_), 52 | PROTOBUF_FIELD_OFFSET(::rpcheader::rpcheader, args_size_), 53 | }; 54 | static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { 55 | { 0, -1, -1, sizeof(::rpcheader::rpcheader)}, 56 | }; 57 | 58 | static const ::_pb::Message* const file_default_instances[] = { 59 | &::rpcheader::_rpcheader_default_instance_._instance, 60 | }; 61 | 62 | const char descriptor_table_protodef_rpcheader_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = 63 | "\n\017rpcheader.proto\022\trpcheader\"I\n\trpcheade" 64 | "r\022\024\n\014service_name\030\001 \001(\014\022\023\n\013method_name\030\002" 65 | " \001(\014\022\021\n\targs_size\030\003 \001(\rb\006proto3" 66 | ; 67 | static ::_pbi::once_flag descriptor_table_rpcheader_2eproto_once; 68 | const ::_pbi::DescriptorTable descriptor_table_rpcheader_2eproto = { 69 | false, false, 111, descriptor_table_protodef_rpcheader_2eproto, 70 | "rpcheader.proto", 71 | &descriptor_table_rpcheader_2eproto_once, nullptr, 0, 1, 72 | schemas, file_default_instances, TableStruct_rpcheader_2eproto::offsets, 73 | file_level_metadata_rpcheader_2eproto, file_level_enum_descriptors_rpcheader_2eproto, 74 | file_level_service_descriptors_rpcheader_2eproto, 75 | }; 76 | PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_rpcheader_2eproto_getter() { 77 | return &descriptor_table_rpcheader_2eproto; 78 | } 79 | 80 | // Force running AddDescriptors() at dynamic initialization time. 81 | PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_rpcheader_2eproto(&descriptor_table_rpcheader_2eproto); 82 | namespace rpcheader { 83 | 84 | // =================================================================== 85 | 86 | class rpcheader::_Internal { 87 | public: 88 | }; 89 | 90 | rpcheader::rpcheader(::PROTOBUF_NAMESPACE_ID::Arena* arena, 91 | bool is_message_owned) 92 | : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { 93 | SharedCtor(); 94 | // @@protoc_insertion_point(arena_constructor:rpcheader.rpcheader) 95 | } 96 | rpcheader::rpcheader(const rpcheader& from) 97 | : ::PROTOBUF_NAMESPACE_ID::Message() { 98 | _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); 99 | service_name_.InitDefault(); 100 | #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING 101 | service_name_.Set("", GetArenaForAllocation()); 102 | #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING 103 | if (!from._internal_service_name().empty()) { 104 | service_name_.Set(from._internal_service_name(), 105 | GetArenaForAllocation()); 106 | } 107 | method_name_.InitDefault(); 108 | #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING 109 | method_name_.Set("", GetArenaForAllocation()); 110 | #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING 111 | if (!from._internal_method_name().empty()) { 112 | method_name_.Set(from._internal_method_name(), 113 | GetArenaForAllocation()); 114 | } 115 | args_size_ = from.args_size_; 116 | // @@protoc_insertion_point(copy_constructor:rpcheader.rpcheader) 117 | } 118 | 119 | inline void rpcheader::SharedCtor() { 120 | service_name_.InitDefault(); 121 | #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING 122 | service_name_.Set("", GetArenaForAllocation()); 123 | #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING 124 | method_name_.InitDefault(); 125 | #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING 126 | method_name_.Set("", GetArenaForAllocation()); 127 | #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING 128 | args_size_ = 0u; 129 | } 130 | 131 | rpcheader::~rpcheader() { 132 | // @@protoc_insertion_point(destructor:rpcheader.rpcheader) 133 | if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { 134 | (void)arena; 135 | return; 136 | } 137 | SharedDtor(); 138 | } 139 | 140 | inline void rpcheader::SharedDtor() { 141 | GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); 142 | service_name_.Destroy(); 143 | method_name_.Destroy(); 144 | } 145 | 146 | void rpcheader::SetCachedSize(int size) const { 147 | _cached_size_.Set(size); 148 | } 149 | 150 | void rpcheader::Clear() { 151 | // @@protoc_insertion_point(message_clear_start:rpcheader.rpcheader) 152 | uint32_t cached_has_bits = 0; 153 | // Prevent compiler warnings about cached_has_bits being unused 154 | (void) cached_has_bits; 155 | 156 | service_name_.ClearToEmpty(); 157 | method_name_.ClearToEmpty(); 158 | args_size_ = 0u; 159 | _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); 160 | } 161 | 162 | const char* rpcheader::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { 163 | #define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure 164 | while (!ctx->Done(&ptr)) { 165 | uint32_t tag; 166 | ptr = ::_pbi::ReadTag(ptr, &tag); 167 | switch (tag >> 3) { 168 | // bytes service_name = 1; 169 | case 1: 170 | if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { 171 | auto str = _internal_mutable_service_name(); 172 | ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); 173 | CHK_(ptr); 174 | } else 175 | goto handle_unusual; 176 | continue; 177 | // bytes method_name = 2; 178 | case 2: 179 | if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { 180 | auto str = _internal_mutable_method_name(); 181 | ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); 182 | CHK_(ptr); 183 | } else 184 | goto handle_unusual; 185 | continue; 186 | // uint32 args_size = 3; 187 | case 3: 188 | if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 24)) { 189 | args_size_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); 190 | CHK_(ptr); 191 | } else 192 | goto handle_unusual; 193 | continue; 194 | default: 195 | goto handle_unusual; 196 | } // switch 197 | handle_unusual: 198 | if ((tag == 0) || ((tag & 7) == 4)) { 199 | CHK_(ptr); 200 | ctx->SetLastTag(tag); 201 | goto message_done; 202 | } 203 | ptr = UnknownFieldParse( 204 | tag, 205 | _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), 206 | ptr, ctx); 207 | CHK_(ptr != nullptr); 208 | } // while 209 | message_done: 210 | return ptr; 211 | failure: 212 | ptr = nullptr; 213 | goto message_done; 214 | #undef CHK_ 215 | } 216 | 217 | uint8_t* rpcheader::_InternalSerialize( 218 | uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { 219 | // @@protoc_insertion_point(serialize_to_array_start:rpcheader.rpcheader) 220 | uint32_t cached_has_bits = 0; 221 | (void) cached_has_bits; 222 | 223 | // bytes service_name = 1; 224 | if (!this->_internal_service_name().empty()) { 225 | target = stream->WriteBytesMaybeAliased( 226 | 1, this->_internal_service_name(), target); 227 | } 228 | 229 | // bytes method_name = 2; 230 | if (!this->_internal_method_name().empty()) { 231 | target = stream->WriteBytesMaybeAliased( 232 | 2, this->_internal_method_name(), target); 233 | } 234 | 235 | // uint32 args_size = 3; 236 | if (this->_internal_args_size() != 0) { 237 | target = stream->EnsureSpace(target); 238 | target = ::_pbi::WireFormatLite::WriteUInt32ToArray(3, this->_internal_args_size(), target); 239 | } 240 | 241 | if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { 242 | target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( 243 | _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); 244 | } 245 | // @@protoc_insertion_point(serialize_to_array_end:rpcheader.rpcheader) 246 | return target; 247 | } 248 | 249 | size_t rpcheader::ByteSizeLong() const { 250 | // @@protoc_insertion_point(message_byte_size_start:rpcheader.rpcheader) 251 | size_t total_size = 0; 252 | 253 | uint32_t cached_has_bits = 0; 254 | // Prevent compiler warnings about cached_has_bits being unused 255 | (void) cached_has_bits; 256 | 257 | // bytes service_name = 1; 258 | if (!this->_internal_service_name().empty()) { 259 | total_size += 1 + 260 | ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( 261 | this->_internal_service_name()); 262 | } 263 | 264 | // bytes method_name = 2; 265 | if (!this->_internal_method_name().empty()) { 266 | total_size += 1 + 267 | ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( 268 | this->_internal_method_name()); 269 | } 270 | 271 | // uint32 args_size = 3; 272 | if (this->_internal_args_size() != 0) { 273 | total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_args_size()); 274 | } 275 | 276 | return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); 277 | } 278 | 279 | const ::PROTOBUF_NAMESPACE_ID::Message::ClassData rpcheader::_class_data_ = { 280 | ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, 281 | rpcheader::MergeImpl 282 | }; 283 | const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*rpcheader::GetClassData() const { return &_class_data_; } 284 | 285 | void rpcheader::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, 286 | const ::PROTOBUF_NAMESPACE_ID::Message& from) { 287 | static_cast(to)->MergeFrom( 288 | static_cast(from)); 289 | } 290 | 291 | 292 | void rpcheader::MergeFrom(const rpcheader& from) { 293 | // @@protoc_insertion_point(class_specific_merge_from_start:rpcheader.rpcheader) 294 | GOOGLE_DCHECK_NE(&from, this); 295 | uint32_t cached_has_bits = 0; 296 | (void) cached_has_bits; 297 | 298 | if (!from._internal_service_name().empty()) { 299 | _internal_set_service_name(from._internal_service_name()); 300 | } 301 | if (!from._internal_method_name().empty()) { 302 | _internal_set_method_name(from._internal_method_name()); 303 | } 304 | if (from._internal_args_size() != 0) { 305 | _internal_set_args_size(from._internal_args_size()); 306 | } 307 | _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); 308 | } 309 | 310 | void rpcheader::CopyFrom(const rpcheader& from) { 311 | // @@protoc_insertion_point(class_specific_copy_from_start:rpcheader.rpcheader) 312 | if (&from == this) return; 313 | Clear(); 314 | MergeFrom(from); 315 | } 316 | 317 | bool rpcheader::IsInitialized() const { 318 | return true; 319 | } 320 | 321 | void rpcheader::InternalSwap(rpcheader* other) { 322 | using std::swap; 323 | auto* lhs_arena = GetArenaForAllocation(); 324 | auto* rhs_arena = other->GetArenaForAllocation(); 325 | _internal_metadata_.InternalSwap(&other->_internal_metadata_); 326 | ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( 327 | &service_name_, lhs_arena, 328 | &other->service_name_, rhs_arena 329 | ); 330 | ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( 331 | &method_name_, lhs_arena, 332 | &other->method_name_, rhs_arena 333 | ); 334 | swap(args_size_, other->args_size_); 335 | } 336 | 337 | ::PROTOBUF_NAMESPACE_ID::Metadata rpcheader::GetMetadata() const { 338 | return ::_pbi::AssignDescriptors( 339 | &descriptor_table_rpcheader_2eproto_getter, &descriptor_table_rpcheader_2eproto_once, 340 | file_level_metadata_rpcheader_2eproto[0]); 341 | } 342 | 343 | // @@protoc_insertion_point(namespace_scope) 344 | } // namespace rpcheader 345 | PROTOBUF_NAMESPACE_OPEN 346 | template<> PROTOBUF_NOINLINE ::rpcheader::rpcheader* 347 | Arena::CreateMaybeMessage< ::rpcheader::rpcheader >(Arena* arena) { 348 | return Arena::CreateMessageInternal< ::rpcheader::rpcheader >(arena); 349 | } 350 | PROTOBUF_NAMESPACE_CLOSE 351 | 352 | // @@protoc_insertion_point(global_scope) 353 | #include 354 | -------------------------------------------------------------------------------- /src/rpcheader.proto: -------------------------------------------------------------------------------- 1 | // protobuf版本 2 | syntax = "proto3"; 3 | 4 | // 包名,在C++中表现为命名空间 5 | package rpcheader; 6 | 7 | message rpcheader 8 | { 9 | bytes service_name=1; 10 | bytes method_name=2; 11 | uint32 args_size=3; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/zookeeperutil.cc: -------------------------------------------------------------------------------- 1 | #include "zookeeperutil.h" 2 | #include "mpzrpcapplication.h" 3 | #include 4 | #include 5 | 6 | // 全局的watcher观察器 zkserver给zkclient的通知 7 | void global_watcher(zhandle_t *zh, int type, 8 | int state, const char *path, void *watcherCtx) 9 | { 10 | if (type == ZOO_SESSION_EVENT) // 回调的消息类型是和会话相关的消息类型 11 | { 12 | if (state == ZOO_CONNECTED_STATE) // zkclient和zkserver连接成功 13 | { 14 | sem_t *sem = (sem_t *)zoo_get_context(zh); 15 | sem_post(sem); 16 | } 17 | } 18 | } 19 | 20 | ZkClient::ZkClient() : m_zhandle(nullptr) 21 | { 22 | } 23 | 24 | ZkClient::~ZkClient() 25 | { 26 | if (m_zhandle != nullptr) 27 | { 28 | zookeeper_close(m_zhandle); // 关闭句柄,释放资源 MySQL_Conn 29 | } 30 | } 31 | 32 | // 连接zkserver 33 | void ZkClient::Start() 34 | { 35 | std::string zookeeper_ip = MpzrpcApplication::getApp().getConfig().getZooKeeperIp(); 36 | int zookeeper_port = MpzrpcApplication::getApp().getConfig().getZooKeeperPort(); 37 | 38 | char port[20] = {0}; 39 | sprintf(port, "%d", zookeeper_port); 40 | std::string connstr = zookeeper_ip + ":" + std::string(port); 41 | /* 42 | zookeeper_mt:多线程版本 43 | zookeeper的API客户端程序提供了三个线程 44 | API调用线程 45 | 网络I/O线程 pthread_create poll 46 | watcher回调线程 pthread_create 47 | */ 48 | m_zhandle = zookeeper_init(connstr.c_str(), global_watcher, 30000, nullptr, nullptr, 0); 49 | if (nullptr == m_zhandle) 50 | { 51 | std::cout << "zookeeper_init error!" << std::endl; 52 | exit(EXIT_FAILURE); 53 | } 54 | 55 | sem_t sem; 56 | sem_init(&sem, 0, 0); 57 | zoo_set_context(m_zhandle, &sem); 58 | 59 | sem_wait(&sem); 60 | std::cout << "zookeeper_init success!" << std::endl; 61 | } 62 | 63 | void ZkClient::Create(const char *path, const char *data, int datalen, int state) 64 | { 65 | char path_buffer[128]; 66 | int bufferlen = sizeof(path_buffer); 67 | int flag; 68 | // 先判断path表示的znode节点是否存在,如果存在,就不再重复创建了 69 | flag = zoo_exists(m_zhandle, path, 0, nullptr); 70 | if (ZNONODE == flag) // 表示path的znode节点不存在 71 | { 72 | // 创建指定path的znode节点了 73 | flag = zoo_create(m_zhandle, path, data, datalen, 74 | &ZOO_OPEN_ACL_UNSAFE, state, path_buffer, bufferlen); 75 | if (flag == ZOK) 76 | { 77 | std::cout << "znode create success... path:" << path << std::endl; 78 | } 79 | else 80 | { 81 | std::cout << "flag:" << flag << std::endl; 82 | std::cout << "znode create error... path:" << path << std::endl; 83 | exit(EXIT_FAILURE); 84 | } 85 | } 86 | } 87 | 88 | // 根据指定的path,获取znode节点的值 89 | std::string ZkClient::GetData(const char *path) 90 | { 91 | char buffer[64]; 92 | int bufferlen = sizeof(buffer); 93 | int flag = zoo_get(m_zhandle, path, 0, buffer, &bufferlen, nullptr); 94 | if (flag != ZOK) 95 | { 96 | std::cout << "get znode error... path:" << path << std::endl; 97 | return ""; 98 | } 99 | else 100 | { 101 | return buffer; 102 | } 103 | } --------------------------------------------------------------------------------