├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── cmake-utility ├── compile_proto.cmake └── create_test.cmake ├── common ├── constants.h ├── is_ascii_string.h ├── macro_utility.h ├── make_general_message.h ├── time.h ├── uuid.h └── zmq_helper.h ├── general-proto ├── command.proto └── general_message.proto ├── mbus ├── message_bus ├── CMakeLists.txt ├── async_log.h ├── bus.h ├── client.h ├── impl │ ├── async_log.cpp │ ├── bus.cpp │ └── client.cpp └── message_dealer.h ├── readme.md ├── rpc ├── CMakeLists.txt ├── broker.cpp ├── broker.h ├── client.cpp ├── client.h ├── readme.md ├── remote_caller.h ├── worker.cpp └── worker.h ├── server.cpp └── tests ├── CMakeLists.txt ├── add_service.pb.cc ├── add_service.pb.h ├── add_service.proto ├── log_.cpp ├── test_async_repreq.cpp ├── test_bus_server.cpp ├── test_pub.cpp ├── test_rpc_client.cpp ├── test_rpc_worker.cpp ├── test_sub.cpp └── test_subt.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto 3 | Language: Cpp 4 | # 关闭格式化 5 | DisableFormat: false 6 | # BasedOnStyle: LLVM 7 | # 访问说明符(public、private等)的偏移 8 | AccessModifierOffset: -4 9 | # 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行) 10 | AlignAfterOpenBracket: Align 11 | # 连续赋值时,对齐所有等号 12 | AlignConsecutiveAssignments: false 13 | # 连续声明时,对齐所有声明的变量名 14 | AlignConsecutiveDeclarations: false 15 | # 左对齐逃脱换行(使用反斜杠换行)的反斜杠 16 | AlignEscapedNewlinesLeft: true 17 | # 水平对齐二元和三元表达式的操作数 18 | AlignOperands: true 19 | # 对齐连续的尾随的注释 20 | AlignTrailingComments: true 21 | # 允许函数声明的所有参数在放在下一行 22 | AllowAllParametersOfDeclarationOnNextLine: true 23 | # 允许短的块放在同一行 24 | AllowShortBlocksOnASingleLine: false 25 | # 允许短的case标签放在同一行 26 | AllowShortCaseLabelsOnASingleLine: false 27 | # 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All 28 | AllowShortFunctionsOnASingleLine: Empty 29 | # 允许短的if语句保持在同一行 30 | AllowShortIfStatementsOnASingleLine: false 31 | # 允许短的循环保持在同一行 32 | AllowShortLoopsOnASingleLine: false 33 | # 总是在定义返回类型后换行(deprecated) 34 | AlwaysBreakAfterDefinitionReturnType: None 35 | # 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数), 36 | # AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义) 37 | AlwaysBreakAfterReturnType: None 38 | # 总是在多行string字面量前换行 39 | AlwaysBreakBeforeMultilineStrings: false 40 | # 总是在template声明后换行 41 | AlwaysBreakTemplateDeclarations: false 42 | # false表示函数实参要么都在同一行,要么都各自一行 43 | BinPackArguments: true 44 | # false表示所有形参要么都在同一行,要么都各自一行 45 | BinPackParameters: true 46 | # 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效 47 | BraceWrapping: 48 | # class定义后面 49 | AfterClass: true 50 | # 控制语句后面 51 | AfterControlStatement: true 52 | # enum定义后面 53 | AfterEnum: true 54 | # 函数定义后面 55 | AfterFunction: true 56 | # 命名空间定义后面 57 | AfterNamespace: true 58 | # struct定义后面 59 | AfterStruct: true 60 | # union定义后面 61 | AfterUnion: true 62 | # catch之前 63 | BeforeCatch: true 64 | # else之前 65 | BeforeElse: true 66 | # 缩进大括号 67 | IndentBraces: false 68 | # 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行) 69 | BreakBeforeBinaryOperators: None 70 | # 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似), 71 | # Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似), 72 | # Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom 73 | # 注:这里认为语句块也属于函数 74 | BreakBeforeBraces: Custom 75 | # 在三元运算符前换行 76 | BreakBeforeTernaryOperators: true 77 | # 在构造函数的初始化列表的逗号前换行 78 | BreakConstructorInitializersBeforeComma: false 79 | # 每行字符的限制,0表示没有限制 80 | ColumnLimit: 0 81 | # 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变 82 | CommentPragmas: '^ IWYU pragma:' 83 | # 构造函数的初始化列表要么都在同一行,要么都各自一行 84 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 85 | # 构造函数的初始化列表的缩进宽度 86 | ConstructorInitializerIndentWidth: 4 87 | # 延续的行的缩进宽度 88 | ContinuationIndentWidth: 4 89 | # 去除C++11的列表初始化的大括号{后和}前的空格 90 | Cpp11BracedListStyle: true 91 | # 继承最常用的指针和引用的对齐方式 92 | DerivePointerAlignment: false 93 | # 自动检测函数的调用和定义是否被格式为每行一个参数(Experimental) 94 | ExperimentalAutoDetectBinPacking: false 95 | # 需要被解读为foreach循环而不是函数调用的宏 96 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 97 | # 对#include进行排序,匹配了某正则表达式的#include拥有对应的优先级,匹配不到的则默认优先级为INT_MAX(优先级越小排序越靠前), 98 | # 可以定义负数优先级从而保证某些#include永远在最前面 99 | IncludeCategories: 100 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 101 | Priority: 2 102 | - Regex: '^(<|"(gtest|isl|json)/)' 103 | Priority: 3 104 | - Regex: '.*' 105 | Priority: 1 106 | # 缩进case标签 107 | IndentCaseLabels: false 108 | # 缩进宽度 109 | IndentWidth: 4 110 | # 函数返回类型换行时,缩进函数声明或函数定义的函数名 111 | IndentWrappedFunctionNames: false 112 | # 保留在块开始处的空行 113 | KeepEmptyLinesAtTheStartOfBlocks: true 114 | # 开始一个块的宏的正则表达式 115 | MacroBlockBegin: '' 116 | # 结束一个块的宏的正则表达式 117 | MacroBlockEnd: '' 118 | # 连续空行的最大数量 119 | MaxEmptyLinesToKeep: 1 120 | # 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All 121 | NamespaceIndentation: All 122 | # 在call(后对函数调用换行的penalty 123 | PenaltyBreakBeforeFirstCallParameter: 19 124 | # 在一个注释中引入换行的penalty 125 | # PenaltyBreakComment: 300 126 | # 第一次在<<前换行的penalty 127 | PenaltyBreakFirstLessLess: 120 128 | # 在一个字符串字面量中引入换行的penalty 129 | PenaltyBreakString: 1000 130 | # 对于每个在行字符数限制之外的字符的penalty 131 | PenaltyExcessCharacter: 1000000 132 | # 将函数的返回类型放到它自己的行的penalty 133 | PenaltyReturnTypeOnItsOwnLine: 60 134 | # 指针和引用的对齐: Left, Right, Middle 135 | PointerAlignment: Right 136 | # 允许重新排版注释 137 | ReflowComments: false 138 | # 允许排序#include 139 | SortIncludes: true 140 | # 在C风格类型转换后添加空格 141 | SpaceAfterCStyleCast: false 142 | # 在赋值运算符之前添加空格 143 | SpaceBeforeAssignmentOperators: true 144 | # 开圆括号之前添加一个空格: Never, ControlStatements, Always 145 | SpaceBeforeParens: ControlStatements 146 | # 在空的圆括号中添加空格 147 | SpaceInEmptyParentheses: false 148 | # 在尾随的评论前添加的空格数(只适用于//) 149 | # SpacesBeforeTrailingComments: 2 150 | # 在尖括号的<后和>前添加空格 151 | SpacesInAngles: false 152 | # 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格 153 | SpacesInContainerLiterals: false 154 | # 在C风格类型转换的括号中添加空格 155 | SpacesInCStyleCastParentheses: false 156 | # 在圆括号的(后和)前添加空格 157 | SpacesInParentheses: false 158 | # 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响 159 | SpacesInSquareBrackets: false 160 | # 标准: Cpp03, Cpp11, Auto 161 | Standard: Auto 162 | # tab宽度 163 | TabWidth: 4 164 | # 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always 165 | UseTab: Never 166 | ... 167 | 168 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.dylib 16 | *.dll 17 | 18 | # Fortran module files 19 | *.mod 20 | *.smod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.lib 26 | 27 | # Executables 28 | *.exe 29 | *.out 30 | *.app 31 | 32 | #build part 33 | /build 34 | /.vscode 35 | /cmake-build-debug 36 | /.idea 37 | /.cache 38 | /.history 39 | /.clang-tidy 40 | 41 | #test build part 42 | /test/app_area_clean_test/build 43 | /test/app_select_area_clean_test/build -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | project(message_bus VERSION 0.1.0) 3 | message(STATUS "message-bus project name: ${PROJECT_NAME}") 4 | message(STATUS "message-bus project name: ${CMAKE_PROJECT_NAME}") 5 | 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS on) 7 | 8 | # "编译模式 Debug/Release" 9 | set(CMAKE_BUILD_TYPE Debug) 10 | option(ENABLE_TESTS "是否编译单元测试" OFF) 11 | 12 | set(SERVER_NODE_NAME "message-bus-server") 13 | 14 | # 区分调试模式和发布模式的编译参数 15 | if (CMAKE_BUILD_TYPE MATCHES Debug) 16 | message(STATUS "Debug 模式") 17 | add_definitions( 18 | -g 19 | -ggdb 20 | -O0 21 | -DDEBUG # 定义宏 DEBUG 22 | ) 23 | 24 | elseif(CMAKE_BUILD_TYPE MATCHES Release) 25 | message(STATUS "Release 模式") 26 | add_definitions( 27 | -O3 28 | -DNDEBUG # 定义宏 NDEBUG,关闭 assert() 函数 29 | ) 30 | endif() 31 | 32 | ## 查找依赖 33 | find_package(ZeroMQ REQUIRED) 34 | find_package(Protobuf REQUIRED) 35 | message(STATUS "找到 protobuf 头文件目录: ${Protobuf_FOUND}") 36 | message(STATUS "找到 protobuf 头文件目录: ${PROTOBUF_INCLUDE_DIR}") 37 | message(STATUS "找到 protobuf 需要链接的库: ${PROTOBUF_LIBRARY}") 38 | find_package(cppzmq REQUIRED) 39 | find_package(gflags REQUIRED) 40 | message(STATUS "gflags include ${gflags_INCLUDE_DIR}") 41 | message(STATUS "gflags libraries ${gflags_LIBRARIES}") 42 | 43 | include(${PROJECT_SOURCE_DIR}/cmake-utility/compile_proto.cmake) 44 | compile_proto(general_proto ${PROJECT_SOURCE_DIR}/general-proto) 45 | 46 | set(PROJECT_INCLUDE_DIR "") 47 | list( 48 | APPEND PROJECT_INCLUDE_DIR 49 | ${PROJECT_SOURCE_DIR} 50 | ${general_proto_INCLUDES} 51 | ${ZeroMQ_INCLUDE_DIR} 52 | ${cppzmq_INCLUDE_DIR} 53 | ) 54 | 55 | set(PROJECT_LINK_LIBRARIES "") 56 | list( 57 | APPEND PROJECT_LINK_LIBRARIES 58 | ${general_proto_LIBRARIES} 59 | ${ZeroMQ_STATIC_LIBRARY} 60 | pthread 61 | ) 62 | 63 | ## 设置编译目标 64 | add_subdirectory(message_bus) 65 | add_subdirectory(rpc) 66 | 67 | if (ENABLE_TESTS) 68 | message(STATUS "启用单元测试") 69 | add_subdirectory(tests) 70 | endif() 71 | 72 | add_executable(${SERVER_NODE_NAME} server.cpp) 73 | 74 | ## 设置编译依赖关系 75 | target_include_directories( 76 | ${SERVER_NODE_NAME} PRIVATE 77 | ${gflags_INCLUDE_DIR} 78 | ) 79 | 80 | target_link_libraries( 81 | ${SERVER_NODE_NAME} PRIVATE 82 | ${PROJECT_NAME} 83 | mbus_rpc 84 | ${gflags_LIBRARIES} 85 | ) 86 | 87 | target_compile_options( 88 | ${SERVER_NODE_NAME} PRIVATE 89 | -std=c++17 90 | ) 91 | 92 | if (CMAKE_BUILD_TYPE MATCHES Debug) 93 | target_link_options( 94 | ${SERVER_NODE_NAME} PRIVATE 95 | -fsanitize=address 96 | ) 97 | target_compile_options( 98 | ${SERVER_NODE_NAME} PRIVATE 99 | -fsanitize=address 100 | -fno-omit-frame-pointer 101 | -fno-sanitize-recover 102 | ) 103 | endif() -------------------------------------------------------------------------------- /cmake-utility/compile_proto.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # @brief 编译指定目录下的 proto 文件 3 | # @param OUTPUT_NAME 指定编译结果的自定义名称 4 | # @param PROTO_DIR 指定需要编译的 proto 目录 5 | # 6 | # 宏 compile_proto 执行后,会定义两个变量,用于后续链接库文件和引入头文件, 7 | # 输出的变量是 _INCLUDES _LIBRARIES 8 | # 用法示例: 9 | # compile_proto(my_proto proto) 10 | # target_include_directories(main PUBLIC ${my_proto_INCLUDES}) 11 | # target_link_libraries(main PUBLIC ${my_proto_LIBRARIES}) 12 | # 13 | macro(compile_proto OUTPUT_NAME PROTO_DIR) 14 | # file(GLOB PROTOS ${PROJECT_SOURCE_DIR}/proto/*.proto) 15 | file(GLOB PROTOS ${PROTO_DIR}/*.proto) 16 | set(PROTO_OUTPUT_DIR "${PROJECT_BINARY_DIR}/${OUTPUT_NAME}") 17 | set(PROTO_SRCS "") 18 | set(PROTO_HDRS "") 19 | execute_process(COMMAND mkdir -p ${PROTO_OUTPUT_DIR}) 20 | message(STATUS "开始编译 proto 文件") 21 | foreach(file ${PROTOS}) 22 | get_filename_component(NAME ${file} NAME_WE) 23 | list(APPEND PROTO_SRCS "${PROTO_OUTPUT_DIR}/${NAME}.pb.cc") 24 | list(APPEND PROTO_HDRS "${PROTO_OUTPUT_DIR}/${NAME}.pb.h") 25 | execute_process( 26 | COMMAND 27 | ${PROTOBUF_PROTOC_EXECUTABLE} -I=${PROTO_DIR} --cpp_out=${PROTO_OUTPUT_DIR} ${file} 28 | ) 29 | message(STATUS "${PROTOBUF_PROTOC_EXECUTABLE} -I=${PROTO_DIR} --cpp_out=${PROTO_OUTPUT_DIR} ${file}") 30 | endforeach() 31 | 32 | # 将 proto c++ 源码编译成静态库 33 | add_library( 34 | ${OUTPUT_NAME} STATIC 35 | ${PROTO_SRCS} 36 | ) 37 | target_include_directories( 38 | ${OUTPUT_NAME} PUBLIC 39 | ${PROTOBUF_INCLUDE_DIR} 40 | ) 41 | target_link_libraries( 42 | ${OUTPUT_NAME} PUBLIC 43 | ${PROTOBUF_LIBRARY} 44 | ) 45 | 46 | message(STATUS "Protobuf_INCLUDE_DIR ${Protobuf_INCLUDE_DIR}") 47 | message(STATUS "Protobuf_LIBRARIES ${Protobuf_LIBRARIES}") 48 | 49 | # 设置 proto 文件的 include 路径 50 | set(${OUTPUT_NAME}_INCLUDES ${PROTO_OUTPUT_DIR}) 51 | # 设置 proto 文件编译后的库文件路径 52 | set(${OUTPUT_NAME}_LIBRARIES ${OUTPUT_NAME}) 53 | endmacro() 54 | 55 | -------------------------------------------------------------------------------- /cmake-utility/create_test.cmake: -------------------------------------------------------------------------------- 1 | # @brief 新增单元测试的可执行文件 2 | # @param target_name 可执行文件名称 3 | # @param file_list 需要编译的 .cpp 文件,长度可变 4 | # 5 | # @example 创一个 hello world 测试程序: 6 | # # 用法一:简单指定测试程序的 cpp 文件 7 | # create_test(TEST_hello hello_impl.cpp main.cpp) 8 | # 9 | # # 用法二:程序程序要指定额外的编译参数和链接库 10 | # # 链接 pthread 库和添加编译参数 -Wextra 11 | # create_test( 12 | # TEST_hello 13 | # FILES hello_impl.cpp main.cpp 14 | # LIBS pthread 15 | # OPTIONS -Wextra 16 | # ) 17 | function(create_test target_name ...) 18 | set(ars_FILES "FILES") # 读取 .cpp 文件名称 19 | set(ars_LIBS "LIBS") # 读取链接库名称 20 | set(ars_OPTIONS "OPTIONS") # 读取编译参数 21 | set(arg_read_status ${ars_FILES}) # 参数解析状态 22 | 23 | set(file_list) # 需要编译的文件 24 | set(lib_list) # 需要链接的库 25 | set(option_list) # 需要添加的编译参数 26 | 27 | # 遍历下标 1 到 argv_len 的全部参数 28 | set(argv_index 1) 29 | list(LENGTH ARGV argv_len) 30 | while(argv_index LESS ${argv_len}) 31 | set(status_change false) 32 | list(GET ARGV ${argv_index} argv_value) 33 | 34 | # message(STATUS "当前解析 ${arg_read_status} ${argv_index} ${argv_value}") 35 | 36 | # 检查是否读取到 FILES LIBS OPTIONS,修改 arg_read_status 的值,调整解析状态 37 | if (${argv_value} STREQUAL ${ars_FILES}) 38 | set(arg_read_status ${ars_FILES}) 39 | set(status_change true) 40 | endif() 41 | 42 | if (${argv_value} STREQUAL ${ars_LIBS}) 43 | set(arg_read_status ${ars_LIBS}) 44 | set(status_change true) 45 | endif() 46 | 47 | if (${argv_value} STREQUAL ${ars_OPTIONS}) 48 | set(arg_read_status ${ars_OPTIONS}) 49 | set(status_change true) 50 | endif() 51 | 52 | if (${status_change}) 53 | math(EXPR argv_index "${argv_index} + 1") 54 | list(GET ARGV ${argv_index} argv_value) 55 | # message(STATUS "解析改变 ${arg_read_status} ${argv_index} ${argv_value}") 56 | endif() 57 | 58 | 59 | # 根据当前 arg_read_status 的值,将参数添加到对应的列表里 60 | if(${arg_read_status} STREQUAL ${ars_FILES}) 61 | list(APPEND file_list ${argv_value}) 62 | endif() 63 | 64 | if(${arg_read_status} STREQUAL ${ars_LIBS}) 65 | list(APPEND lib_list ${argv_value}) 66 | endif() 67 | 68 | if(${arg_read_status} STREQUAL ${ars_OPTIONS}) 69 | list(APPEND option_list ${argv_value}) 70 | endif() 71 | 72 | math(EXPR argv_index "${argv_index} + 1") 73 | endwhile() 74 | 75 | message( 76 | STATUS 77 | "增加单元测试 ${target_name}; " 78 | "编译文件 ${file_list}; " 79 | "编译参数 ${option_list}; " 80 | "链接库 ${lib_list}; " 81 | ) 82 | 83 | add_executable( 84 | ${target_name} 85 | ${file_list} 86 | ) 87 | 88 | target_link_libraries( 89 | ${target_name} PRIVATE 90 | ${lib_list} 91 | ) 92 | 93 | target_include_directories( 94 | ${target_name} PRIVATE 95 | .. 96 | ) 97 | 98 | target_compile_options( 99 | ${target_name} PRIVATE 100 | -O2 101 | -g 102 | -ggdb 103 | -Wall 104 | ${option_list} 105 | ) 106 | 107 | add_test( 108 | NAME ${target_name} 109 | COMMAND ${target_name} 110 | ) 111 | endfunction() -------------------------------------------------------------------------------- /common/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mbus 6 | { 7 | 8 | constexpr const char *RPC_CLIENT = "client"; 9 | 10 | constexpr const char *RPC_WORKER = "worker"; 11 | 12 | constexpr const char *RPC_BROKER = "broker"; 13 | 14 | // client 端请求的协议头 15 | constexpr const char *RPC_REQ = "rpcreq"; 16 | 17 | // client 端查询服务是否存在的协议头 18 | constexpr const char *RPC_QUERY = "rpcqry"; 19 | 20 | // worker 端响应的协议头 21 | constexpr const char *RPC_RES = "rpcres"; 22 | 23 | // worker 端给 broker 端发送心跳包的协议头 24 | constexpr const char *RPC_HB = "rpchbt"; 25 | 26 | // worker 端给 broker 端发送服务注册请求的协议头 27 | constexpr const char *RPC_REG = "rpcreg"; 28 | 29 | // worker 端给 broker 端发送 RPC 响应结果的协议头 30 | // constexpr const char *RPC_REPLY = "rpcrep"; 31 | 32 | // worker 端给 broker 端发送下线请求的协议头 33 | constexpr const char *RPC_UNCONNECT = "rpcunc"; 34 | 35 | // broker 端给 worker 端发送的请求处理成功的协议头 36 | constexpr const char *RPC_SUCCESS = "rpcsuc"; 37 | 38 | // broker 端给 client 端发送的请求处理失败的协议头 39 | constexpr const char *RPC_NOTPROCESSED = "rpcunprocessed"; 40 | 41 | // 心跳包超时等待次数(4*2500 = 10秒) 42 | constexpr size_t HEARTBEAT_LIVENESS = 4; 43 | 44 | // 心跳包等待间隔,单位毫秒 45 | constexpr size_t HEARTBEAT_INTERVAL = 2500; 46 | 47 | // 心跳包最终认定超时的时间,单位毫秒 48 | constexpr size_t HEARTBEAT_EXPIRY = HEARTBEAT_INTERVAL * HEARTBEAT_LIVENESS; 49 | 50 | } // namespace mbus -------------------------------------------------------------------------------- /common/is_ascii_string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mbus 6 | { 7 | /// 判断二进制内容是否为 ASCII 字符串 8 | inline bool IsASCII(const void *ptr, size_t size) 9 | { 10 | if (size == 0) 11 | { 12 | return false; 13 | } 14 | 15 | const char *str = static_cast(ptr); 16 | 17 | size_t text_count = 0; 18 | 19 | for (size_t i = 0; i < size; i++) 20 | { 21 | if ((str[i] >= 0x20 && str[i] <= 0x7f) || 22 | str[i] == '\n' || 23 | str[i] == '\r' || 24 | str[i] == '\t') 25 | { 26 | text_count++; 27 | } 28 | } 29 | 30 | double text_percent = static_cast(text_count) / static_cast(size); 31 | // 90% 以上的 ASCII 字符,就认为是 ASCII 字符串 32 | return text_percent >= 0.9; 33 | } 34 | } // namespace mbus -------------------------------------------------------------------------------- /common/macro_utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 禁用复制构造和复制赋值 4 | #define DISABLE_COPY(Class) \ 5 | Class(const Class &) = delete; \ 6 | Class &operator=(const Class &) = delete; 7 | 8 | // 禁用移动构造和移动赋值 9 | #define DISABLE_MOVE(Class) \ 10 | Class(Class &&) = delete; \ 11 | Class &operator=(Class &&) = delete; 12 | 13 | // 禁用复制和移动 14 | #define DISABLE_COPY_AND_MOVE(Class) \ 15 | DISABLE_COPY(Class) \ 16 | DISABLE_MOVE(Class) 17 | -------------------------------------------------------------------------------- /common/make_general_message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "general_message.pb.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace mbus 9 | { 10 | /// 11 | /// NOTE: 12 | /// 类型特征识别 std::is_convertible,判断某个类型是否能转换到另一个类型 13 | /// https://zh.cppreference.com/w/cpp/types/is_convertible 14 | /// 15 | /// std::is_base_of,判断某个类型是否是指定类型的派生类 16 | /// https://zh.cppreference.com/w/cpp/types/is_base_of 17 | /// 18 | 19 | /// 任何可转换成 std::string 的数据类型,都生成 RAW 协议的通用消息 20 | template 21 | auto MakeGeneralMessage(const MessageType &msg) 22 | -> typename std::enable_if< 23 | std::is_convertible::value, 24 | value::GeneralMessage>::type 25 | { 26 | value::GeneralMessage result; 27 | result.set_not_timeout(true); 28 | result.set_protocal(value::PAYLOAD_PROTOCAL_RAW); 29 | result.set_payload(std::string{msg}); 30 | 31 | return result; 32 | } 33 | 34 | /// 任何派生自 google::protobuf::MessageLite 的类,都生成 PROTO 协议的通用消息 35 | template 36 | auto MakeGeneralMessage(const MessageType &msg) 37 | -> typename std::enable_if< 38 | std::is_base_of::value, 39 | value::GeneralMessage>::type 40 | { 41 | value::GeneralMessage result; 42 | result.set_not_timeout(true); 43 | // 序列化到 std::string 44 | std::string buffer; 45 | msg.SerializeToString(&buffer); 46 | // 填入通用消息体内 47 | result.set_protocal(value::PAYLOAD_PROTOCAL_PROTO); 48 | result.set_payload(std::move(buffer)); 49 | 50 | return result; 51 | } 52 | 53 | /* 下面是解析通用消息获取指定类型的 payload 的实现 */ 54 | 55 | /// 解析通用消息的 payload 部分到 std::string 上,返回解析后的 std::string 实例 56 | /// 只有 MessageType 指定的类型为 std::string,才会重载到这个函数 57 | template 58 | auto UnmakeGeneralMessage(const value::GeneralMessage &msg) 59 | -> typename std::enable_if< 60 | std::is_same::value, 61 | std::string>::type 62 | { 63 | return msg.payload(); 64 | } 65 | 66 | /// 解析通用消息的 payload 部分到各个具体的 protobuf 序列化对象上, 67 | /// 只有 MessageType 指定的类型继承于 google::protobuf::MessageLite,才会重载到这个函数 68 | template 69 | auto UnmakeGeneralMessage(const value::GeneralMessage &msg) 70 | -> typename std::enable_if< 71 | std::is_base_of::value, 72 | MessageType>::type 73 | { 74 | MessageType result; 75 | if (!msg.payload().empty()) 76 | { 77 | result.ParseFromString(msg.payload()); 78 | } 79 | return result; 80 | } 81 | 82 | } // namespace mbus 83 | -------------------------------------------------------------------------------- /common/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace mbus 9 | { 10 | 11 | /// 获取当前系统时间的毫秒数 12 | inline int64_t Now() 13 | { 14 | timeval tv; 15 | gettimeofday(&tv, nullptr); 16 | return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); 17 | } 18 | 19 | } // namespace mbus -------------------------------------------------------------------------------- /common/uuid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace mbus 16 | { 17 | /// 获取 16 byte 的 uuid 18 | inline std::string UUID() 19 | { 20 | auto uuid = boost::uuids::random_generator{}(); 21 | return std::string{uuid.begin(), uuid.end()}; 22 | } 23 | 24 | inline std::string UUID2String(const std::string &uuid) 25 | { 26 | spdlog::info("uuid size is {}", uuid.size()); 27 | assert(uuid.size() == 16); 28 | std::string result{}; 29 | 30 | size_t n = 0; 31 | auto *ptr = result.data(); 32 | for (const auto &c : uuid) 33 | { 34 | uint8_t cc = c; 35 | char s[3]; 36 | sprintf(s, "%02x", cc); 37 | result += s; 38 | } 39 | return result; 40 | } 41 | } // namespace mbus -------------------------------------------------------------------------------- /common/zmq_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/is_ascii_string.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace mbus 19 | { 20 | using MessagePack = std::deque; 21 | using BufferPack = std::deque; 22 | 23 | /// 比较是否相等 24 | inline bool Equal(const zmq::message_t &msg, const std::string &str) 25 | { 26 | return memcmp(msg.data(), str.data(), msg.size()) == 0; 27 | } 28 | 29 | /// 转换 std::string 到 zmq::const_buffer 30 | inline zmq::const_buffer MakeZmqBuffer(const std::string &str) 31 | { 32 | return zmq::const_buffer{str.data(), str.size()}; 33 | } 34 | 35 | /// 转换 std::string 到 zmq::messages_t 36 | inline zmq::message_t MakeMessage(const std::string &str) 37 | { 38 | return zmq::message_t{str}; 39 | } 40 | 41 | inline zmq::const_buffer MakeZmqBuffer(const char *str) 42 | { 43 | return zmq::const_buffer{str, strlen(str)}; 44 | } 45 | 46 | /// 将消息集合转换成字符串 47 | template 48 | inline std::string StringifyMessages(const ListType &msgs) 49 | { 50 | std::string result{"{ "}; 51 | size_t remain = msgs.size(); 52 | for (const auto &m : msgs) 53 | { 54 | if (m.size() == 0) 55 | { 56 | result += "[empty]"; 57 | } 58 | else if (m.size() < 128 && IsASCII(m.data(), m.size())) 59 | { 60 | result += "\""; 61 | result.append(static_cast(m.data()), m.size()); 62 | result += "\""; 63 | } 64 | else 65 | { 66 | result += "[" + std::to_string(m.size()) + " bytes data]"; 67 | } 68 | 69 | if (remain > 1) 70 | { 71 | result += ", "; 72 | } 73 | remain--; 74 | } 75 | 76 | result += " }"; 77 | 78 | return result; 79 | } 80 | 81 | /// 接收 zmq 的全部分块消息 82 | inline std::deque ReceiveAll(zmq::socket_t &socket) 83 | { 84 | std::deque message; 85 | do 86 | { 87 | zmq::message_t msg; 88 | auto result = socket.recv(msg, zmq::recv_flags::none); 89 | if (result.has_value()) 90 | { 91 | message.emplace_back(std::move(msg)); 92 | } 93 | } while (socket.get(zmq::sockopt::rcvmore)); 94 | 95 | return message; 96 | } 97 | 98 | /// 发送全部消息 99 | template 100 | inline bool SendAll(zmq::socket_t &socket, ListType &messages, std::mutex &mux) 101 | { 102 | std::unique_lock lock(mux); 103 | spdlog::info(StringifyMessages(messages)); 104 | auto remain = messages.size(); 105 | 106 | for (auto &msg : messages) 107 | { 108 | auto flags = zmq::send_flags::none; 109 | if (remain > 1) 110 | { 111 | flags = zmq::send_flags::sndmore; 112 | } 113 | spdlog::info(static_cast(flags)); 114 | auto result = socket.send(msg, flags); 115 | if (!result.has_value()) 116 | { 117 | return false; 118 | } 119 | remain--; 120 | } 121 | 122 | return true; 123 | } 124 | 125 | /// 等待 zmq socket 可读 126 | inline bool WaitReadable(zmq::socket_t &socket, int32_t timeout) 127 | { 128 | spdlog::info("wait readable"); 129 | std::array items{zmq::pollitem_t{socket.handle(), 0, ZMQ_POLLIN, 0}}; 130 | zmq::poll(items, std::chrono::milliseconds(timeout)); 131 | return items[0].revents & ZMQ_POLLIN; 132 | } 133 | 134 | inline bool S1BiggersS2(std::string s1, std::string s2) 135 | { 136 | // if (s1.length() == s2.length()) 137 | // { 138 | // for (int i = 0; i < s1.length(); i++) 139 | // { 140 | // if (s1[i] == s2[i]) 141 | // continue; 142 | // if (s1[i] > s2[i]) 143 | // { 144 | // return true; 145 | // } 146 | // else 147 | // { 148 | // return false; 149 | // } 150 | // } 151 | // return false; 152 | // } 153 | 154 | // if (s1.length() > s2.length()) 155 | // { 156 | // return true; 157 | // } 158 | // else 159 | // { 160 | // return false; 161 | // } 162 | int64_t s1_int = std::stol(s1); 163 | int64_t s2_int = std::stol(s2); 164 | return s1_int > s2_int; 165 | } 166 | 167 | } // namespace mbus -------------------------------------------------------------------------------- /general-proto/command.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package value; 3 | 4 | message Command 5 | { 6 | string type = 1; 7 | bytes payload = 2; 8 | } 9 | -------------------------------------------------------------------------------- /general-proto/general_message.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package value; 3 | 4 | // 通用消息内 payload 使用的协议类型枚举 5 | enum PayloadProtocal 6 | { 7 | // 原始字符串类型 8 | PAYLOAD_PROTOCAL_RAW = 0; 9 | // proto 10 | PAYLOAD_PROTOCAL_PROTO = 1; 11 | // json 12 | PAYLOAD_PROTOCAL_JSON = 2; 13 | // xml 14 | PAYLOAD_PROTOCAL_XML = 3; 15 | }; 16 | 17 | // 通用消息 18 | message GeneralMessage 19 | { 20 | // 消息的唯一 id 21 | bytes uuid = 1; 22 | // 消息来源标识符 23 | string source = 2; 24 | // 消息的发布时间,毫秒精度 25 | uint64 timestamp = 3; 26 | // 序号/逻辑时间,用于确定同一时间点上的消息顺序 27 | uint64 seq = 4; 28 | 29 | bool not_timeout = 5; 30 | 31 | // payload 使用的协议 32 | PayloadProtocal protocal = 6; 33 | // payload,存储序列化后的自定义消息 34 | bytes payload = 7; 35 | } 36 | -------------------------------------------------------------------------------- /mbus: -------------------------------------------------------------------------------- 1 | message_bus/ -------------------------------------------------------------------------------- /message_bus/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## 编译 message-bus 的源码成静态库 2 | aux_source_directory(./impl SOURCES) 3 | 4 | add_library( 5 | ${PROJECT_NAME} STATIC 6 | ${SOURCES} 7 | ) 8 | 9 | target_include_directories( 10 | ${PROJECT_NAME} PUBLIC 11 | ${PROJECT_INCLUDE_DIR} 12 | ) 13 | 14 | target_link_libraries( 15 | ${PROJECT_NAME} PUBLIC 16 | ${PROJECT_LINK_LIBRARIES} 17 | ) 18 | 19 | target_compile_options( 20 | ${PROJECT_NAME} PRIVATE 21 | -std=c++17 22 | ) -------------------------------------------------------------------------------- /message_bus/async_log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "general_message.pb.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | namespace mbus 17 | { 18 | 19 | struct BufferNode 20 | { 21 | std::deque buff; 22 | std::unique_ptr mx; 23 | std::string topic; 24 | std::ofstream out; 25 | }; 26 | class AsyncLog 27 | { 28 | public: 29 | static AsyncLog &GetInstance(); 30 | AsyncLog(const AsyncLog &) = delete; 31 | AsyncLog(AsyncLog &&) = delete; 32 | AsyncLog &operator=(const AsyncLog &) = delete; 33 | AsyncLog &operator=(AsyncLog &&) = delete; 34 | ~AsyncLog(); 35 | 36 | void log(const std::string &, const value::GeneralMessage &); 37 | void ParseMessage(); 38 | 39 | private: 40 | AsyncLog(); 41 | void LogLoop(); 42 | void SerializeMessage(BufferNode &); 43 | 44 | private: 45 | const std::string PATH = "msg"; 46 | std::map topic_map_; 47 | std::thread worker_{}; 48 | std::mutex map_mx_; 49 | std::atomic_bool to_run_{true}; 50 | std::atomic_bool to_add_topic{false}; 51 | }; 52 | 53 | inline AsyncLog &AsyncLog::GetInstance() 54 | { 55 | static AsyncLog asyncLog; 56 | return asyncLog; 57 | } 58 | } // namespace mbus -------------------------------------------------------------------------------- /message_bus/bus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace mbus 9 | { 10 | /// 总线服务的实现类 11 | class Bus 12 | { 13 | public: 14 | Bus(const std::shared_ptr &ctx); 15 | ~Bus(); 16 | 17 | /// 启动服务 18 | /// @param host 总线服务的地址 19 | /// @param xpub_port 总线服务的输出端端口号 20 | /// @param xsub_port 总线服务的输入端端口号 21 | void Run(std::string host, int32_t xpub_port, int32_t xsub_port); 22 | 23 | /// 尝试启动仅支持本机进程间通信的服务 24 | bool TryRunOnLocalMode( 25 | int32_t xpub_port, int32_t xsub_port); 26 | 27 | private: 28 | /// 初始化 29 | /// @param host 总线服务的地址 30 | /// @param xpub_port 总线服务的输出端端口号 31 | /// @param xsub_port 总线服务的输入端端口号 32 | void Bind(std::string host, int32_t xpub_port, int32_t xsub_port); 33 | 34 | /// 尝试启动仅支持本机进程间通信的服务 35 | /// @param host 总线服务的地址 36 | /// @param xpub_port 总线服务的输出端端口号 37 | /// @param xsub_port 总线服务的输入端端口号 38 | bool TryBindOnLocalMode(int32_t xpub_port, int32_t xsub_port); 39 | 40 | private: 41 | std::shared_ptr ctx_; 42 | std::unique_ptr backend_; 43 | std::unique_ptr frontend_; 44 | std::unique_ptr log_; 45 | std::thread worker_; 46 | }; 47 | 48 | } // namespace mbus -------------------------------------------------------------------------------- /message_bus/client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/make_general_message.h" 4 | #include "common/time.h" 5 | #include "general_message.pb.h" 6 | #include "message_bus/bus.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace mbus 19 | { 20 | value::GeneralMessage Test(); 21 | 22 | class Client 23 | { 24 | /// 订阅回调函数的类型 25 | using SubscribeHandle = std::function; 26 | 27 | public: 28 | Client(const std::shared_ptr &ctx); 29 | ~Client(); 30 | 31 | /// @brief 连接总线服务 32 | /// @param pub_addr 总线服务的订阅端地址,需要符合 zmq 的地址格式 33 | /// @param sub_addr 总线服务的发布端地址,需要符合 zmq 的地址格式 34 | /// @thread-safe 35 | void Connect(const std::string &pub_addr, const std::string &sub_addr); 36 | 37 | /// @brief 连接或开启总线服务,本机通信模式,仅支持本机的多进程或多线程间通信。 38 | /// 函数调用时会尝试在本机的 pub_port 和 sub_port 端口上开启总线服务,如果失败, 39 | /// 转换成连接总线服务。 40 | /// @param pub_port 总线服务的订阅端端口号 41 | /// @param sub_port 总线服务的发布端端口号 42 | /// @return 如果本次调用启动了总线服务,返回 true,否则 false 43 | /// @thread-safe 44 | bool ConnectOnLocalMode(int32_t pub_port, int32_t sub_port); 45 | 46 | /// @brief 订阅话题 47 | /// @param topic 需要接收消息的话题 48 | /// @param handler 收到新消息后的回调函数,传入的第一个参数类型为 GeneralMessage 49 | /// @note 由于 handler 是在后台线程执行的,用户需要自行保证修改数据时的线程安全问题 50 | /// @thread-safe 51 | void Subscribe(std::string topic, SubscribeHandle handler); 52 | 53 | /// @brief 发布消息 54 | /// @param topic 发布到的话题 55 | /// @param message 任意类型的消息对象,需要能够被 MakeGeneralMessage() 函数转 56 | /// 换成通用的 protobuf 消息 57 | /// @thread-safe 58 | template 59 | void Publish(const std::string &topic, const MessageType &message) 60 | { 61 | /// NOTE: 按话题发送需要使用 zmq 提供的多部分消息。接收部分也同样有相关处理。 62 | /// http://api.zeromq.org/4-1:zmq-send 63 | 64 | value::GeneralMessage general_message = MakeGeneralMessage(message); 65 | general_message.set_timestamp(mbus::Now()); 66 | std::string serialize; 67 | general_message.SerializeToString(&serialize); 68 | 69 | zmq::message_t topic_part{topic}; 70 | zmq::message_t message_part{std::move(serialize)}; 71 | std::lock_guard lock(pub_mutex_); 72 | 73 | pub_->send(topic_part, zmq::send_flags::sndmore); 74 | pub_->send(message_part, zmq::send_flags::none); 75 | // TODO 错误处理 76 | } 77 | 78 | private: 79 | /// 收消息循环 80 | void ReceiveLoop(); 81 | 82 | /// zmq socket 新增话题白名单 83 | void AddTopic(std::string topic); 84 | 85 | /// zmq socket 移除话题过滤白名单 86 | void RemoveTopic(std::string topic); 87 | 88 | private: 89 | /// 全局共享的 zmq IO 事件处理对象 90 | std::shared_ptr ctx_{nullptr}; 91 | /// 发布用的 socket 92 | std::mutex pub_mutex_{}; 93 | std::unique_ptr pub_{nullptr}; 94 | /// 订阅用的 socket 95 | std::mutex sub_mutex_{}; 96 | std::unique_ptr sub_{nullptr}; 97 | /// 存储订阅话题的事件处理函数 98 | std::mutex topic_handlers_mutex_{}; 99 | std::map topic_handlers_{}; 100 | /// 存储订阅的话题 101 | std::set subscribed_; 102 | /// 接收消息的后台工作线程 103 | std::atomic_bool stop_{false}; 104 | std::thread worker_{}; 105 | 106 | /// 本地进程间通信用的总线服务 107 | std::unique_ptr bus_server_{nullptr}; 108 | }; 109 | 110 | } // namespace mbus -------------------------------------------------------------------------------- /message_bus/impl/async_log.cpp: -------------------------------------------------------------------------------- 1 | #include "message_bus/async_log.h" 2 | #include "general_message.pb.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | namespace mbus 22 | { 23 | 24 | AsyncLog::AsyncLog() 25 | { 26 | worker_ = std::thread{[&] { 27 | LogLoop(); 28 | }}; 29 | } 30 | 31 | AsyncLog::~AsyncLog() 32 | { 33 | if (worker_.joinable()) 34 | { 35 | to_run_.store(false); 36 | worker_.join(); 37 | } 38 | } 39 | 40 | void AsyncLog::SerializeMessage(BufferNode &node) 41 | { 42 | // 暂时一次持久化一个节点 43 | auto msg = node.buff.front(); 44 | char buf[msg.ByteSizeLong()]; 45 | auto flag = msg.SerializeToArray(buf, msg.ByteSizeLong()); 46 | node.out.put(msg.ByteSizeLong()); 47 | node.out.write(buf, msg.ByteSizeLong()); 48 | // todo flush可以优化 49 | node.out.flush(); 50 | node.buff.pop_front(); 51 | spdlog::info("buff size is {}", node.buff.size()); 52 | } 53 | 54 | void AsyncLog::ParseMessage() 55 | { 56 | std::ifstream file2(std::filesystem::current_path() / "msg" / "hello", std::ios::binary); 57 | value::GeneralMessage general_message_to_read; 58 | while (file2.peek() != EOF) 59 | { 60 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 61 | auto size = file2.get(); 62 | char buf[size]; 63 | file2.read(buf, size); 64 | general_message_to_read.ParseFromArray(buf, size); 65 | spdlog::info("serialized: {}", general_message_to_read.payload()); 66 | } 67 | } 68 | 69 | // @thread-safe 70 | void AsyncLog::log(const std::string &topic, const value::GeneralMessage &msg) 71 | { 72 | auto search = topic_map_.find(topic); 73 | if (search == topic_map_.end()) 74 | { 75 | // 提醒另一条工作线程释放锁 76 | to_add_topic.store(true); 77 | // 如果主题队列中不存在该主题,则进行添加 78 | std::unique_lock map_lock(map_mx_); 79 | // 再查询一遍并判断是否存在 80 | if ((search = topic_map_.find(topic)) == topic_map_.end()) 81 | { 82 | BufferNode node; 83 | node.topic = topic; 84 | node.mx = std::make_unique(); 85 | node.out = std::ofstream(std::filesystem::current_path() / PATH / topic, std::ios::app | std::ios::binary); 86 | topic_map_.emplace(topic, std::move(node)); 87 | search = topic_map_.find(topic); 88 | spdlog::info("insert"); 89 | } 90 | to_add_topic.store(false); 91 | } 92 | std::lock_guard lock(*search->second.mx.get()); 93 | search->second.buff.push_back(std::move(msg)); 94 | } 95 | 96 | // @thread-safe 97 | void AsyncLog::LogLoop() 98 | { 99 | const auto p = std::filesystem::current_path() / PATH; 100 | if (!std::filesystem::exists(p)) 101 | { 102 | auto c = std::filesystem::create_directory(p); 103 | } 104 | 105 | while (to_run_.load()) 106 | { 107 | // std::this_thread::sleep_for(std::chrono::milliseconds(10000)); 108 | std::unique_lock map_lock(map_mx_); 109 | if (!map_lock.owns_lock()) 110 | continue; 111 | 112 | for (auto begin = topic_map_.begin(); begin != topic_map_.end(); begin++) 113 | { 114 | std::unique_lock try_lock(*begin->second.mx.get(), std::try_to_lock); 115 | if (!try_lock.owns_lock()) 116 | { 117 | // 获取不到锁说明当前节点被其他线程持有 118 | continue; 119 | } 120 | if (begin->second.buff.empty()) 121 | { 122 | // 如果为空则跳过 123 | continue; 124 | } 125 | if (to_add_topic.load()) 126 | { 127 | // 如果其他线程要求存入新的topic,则释放锁 128 | spdlog::info("unlock"); 129 | break; 130 | } 131 | // 序列化消息 132 | SerializeMessage(begin->second); 133 | } 134 | } 135 | } 136 | 137 | } // namespace mbus -------------------------------------------------------------------------------- /message_bus/impl/bus.cpp: -------------------------------------------------------------------------------- 1 | #include "message_bus/bus.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | namespace mbus 19 | { 20 | 21 | Bus::Bus(const std::shared_ptr &ctx) 22 | : ctx_(ctx) 23 | { 24 | backend_ = 25 | std::make_unique(*ctx_, zmq::socket_type::xpub); 26 | assert(backend_ && "Out Of Memory!!"); 27 | 28 | frontend_ = 29 | std::make_unique(*ctx_, zmq::socket_type::xsub); 30 | assert(frontend_ && "Out Of Memory!!"); 31 | 32 | log_ = std::make_unique(*ctx_, zmq::socket_type::pair); 33 | // log_ = zmq_socket(*ctx, zmq::socket_type::pair); 34 | assert(log_ && "Out Of Memory!!"); 35 | } 36 | 37 | Bus::~Bus() 38 | { 39 | if (worker_.joinable()) 40 | { 41 | worker_.join(); 42 | } 43 | } 44 | 45 | void Bus::Run(std::string host, int32_t xpub_port, int32_t xsub_port) 46 | { 47 | Bind(host, xpub_port, xsub_port); 48 | std::this_thread::sleep_for(std::chrono::seconds{1}); 49 | worker_ = std::thread{[&] { 50 | spdlog::info("Running..."); 51 | /// FIXME: 临时移除异步日志 socket 52 | zmq::proxy(zmq::socket_ref{*frontend_}, *backend_); 53 | // zmq::proxy(*frontend_, *backend_); 54 | spdlog::info("Stopped..."); 55 | }}; 56 | } 57 | 58 | /// 尝试启动仅支持本机进程间通信的服务 59 | bool Bus::TryRunOnLocalMode(int32_t xpub_port, int32_t xsub_port) 60 | { 61 | auto result = TryBindOnLocalMode(xpub_port, xsub_port); 62 | if (result) 63 | { 64 | worker_ = std::thread{[&] { 65 | zmq::proxy(*frontend_, *backend_); 66 | }}; 67 | } 68 | return result; 69 | } 70 | 71 | void Bus::Bind(std::string host, int32_t xpub_port, int32_t xsub_port) 72 | { 73 | assert(ctx_); 74 | assert(!host.empty()); 75 | assert(xpub_port > 1023 && xpub_port < 65535); 76 | assert(xsub_port > 1023 && xsub_port < 65535); 77 | 78 | // std::cout << "正在启动服务..." << std::endl; 79 | spdlog::info("starting service..."); 80 | 81 | auto pub_addr = std::string{host}; 82 | pub_addr += ":"; 83 | pub_addr += std::to_string(xpub_port); 84 | // std::cout << "发布端需要连接地址: " << pub_addr << std::endl; 85 | spdlog::info("The address that the publisher needs to connect to is {}", pub_addr); 86 | backend_->bind(pub_addr); 87 | 88 | auto sub_addr = std::string{host}; 89 | sub_addr += ":"; 90 | sub_addr += std::to_string(xsub_port); 91 | // std::cout << "订阅端需要连接地址: " << sub_addr << std::endl; 92 | spdlog::info("The address that the subscriber needs to connect to is {}", sub_addr); 93 | frontend_->bind(sub_addr); 94 | 95 | log_->bind("ipc://log"); 96 | } 97 | 98 | bool Bus::TryBindOnLocalMode(int32_t xpub_port, int32_t xsub_port) 99 | { 100 | /// NOTE: 不考虑端口被非本系统节点程序占用的问题,启动前用户自行处理预期外的端口占用 101 | /// 情况。也不考虑大量程序同时启动的问题,用户自行保证程序按一定时间间隔启动。 102 | /// 启动策略:按 xpub -> xsub 的顺序绑定端口, 103 | /// 如果绑定失败了,随机一段时间后重试,最多重试 3 次,还是失败返回 false。 104 | 105 | auto pub_addr = std::string{"tcp://*:"}; 106 | pub_addr += std::to_string(xpub_port); 107 | auto sub_addr = std::string{"tcp://*:"}; 108 | sub_addr += std::to_string(xsub_port); 109 | 110 | bool pub_bind_success = true; 111 | bool sub_bind_success = true; 112 | 113 | for (size_t i = 0; i < 3; i++) 114 | { 115 | auto random_sleep = std::chrono::milliseconds(rand() % 333); 116 | std::this_thread::sleep_for(random_sleep); 117 | 118 | try 119 | { 120 | backend_->bind(pub_addr); 121 | // std::cout << "订阅端需要连接地址: " << pub_addr << std::endl; 122 | spdlog::info("The address that the subscriber needs to connect to is {} (pub_addr)", pub_addr); 123 | } 124 | catch (std::exception &ex) 125 | { 126 | std::cerr << "尝试启动消息总线服务" << std::endl; 127 | pub_bind_success = false; 128 | continue; 129 | } 130 | pub_bind_success = true; 131 | break; 132 | } 133 | 134 | if (!pub_bind_success) 135 | { 136 | return false; 137 | } 138 | 139 | for (size_t i = 0; i < 3; i++) 140 | { 141 | try 142 | { 143 | frontend_->bind(sub_addr); 144 | // std::cout << "发布端需要连接地址: " << pub_addr << std::endl; 145 | spdlog::info("The address that the publisher needs to connect to is {}", pub_addr); 146 | } 147 | catch (std::exception &ex) 148 | { 149 | std::cerr << ex.what() << std::endl; 150 | sub_bind_success = false; 151 | auto random_sleep = std::chrono::milliseconds(rand() % 333); 152 | std::this_thread::sleep_for(random_sleep); 153 | continue; 154 | } 155 | sub_bind_success = true; 156 | break; 157 | } 158 | 159 | return sub_bind_success; 160 | } 161 | 162 | } // namespace mbus 163 | -------------------------------------------------------------------------------- /message_bus/impl/client.cpp: -------------------------------------------------------------------------------- 1 | #include "message_bus/client.h" 2 | #include "general_message.pb.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace mbus 18 | { 19 | 20 | value::GeneralMessage Test() 21 | { 22 | value::GeneralMessage msg; 23 | msg.set_payload("Hello World"); 24 | return msg; 25 | } 26 | 27 | Client::Client(const std::shared_ptr &ctx) 28 | : ctx_(ctx) 29 | { 30 | } 31 | 32 | Client::~Client() 33 | { 34 | stop_ = true; 35 | if (worker_.joinable()) 36 | { 37 | worker_.join(); 38 | } 39 | } 40 | 41 | /// @thread-safe 42 | void Client::Connect( 43 | const std::string &pub_addr, const std::string &sub_addr) 44 | { 45 | std::lock_guard lock1(pub_mutex_); 46 | std::lock_guard lock2(sub_mutex_); 47 | 48 | assert(!worker_.joinable()); 49 | 50 | pub_ = std::make_unique(*ctx_, zmq::socket_type::pub); 51 | assert(pub_ != nullptr && "Out Of Memory!!"); 52 | // std::cout << "发布端连接 " << pub_addr << std::endl; 53 | spdlog::info("The publisher needs to connect {}", pub_addr); 54 | pub_->connect(pub_addr); 55 | 56 | sub_ = std::make_unique(*ctx_, zmq::socket_type::sub); 57 | assert(sub_ != nullptr && "Out Of Memory!!"); 58 | // std::cout << "订阅端连接 " << sub_addr << std::endl; 59 | spdlog::info("The sublisher needs to connect {}", sub_addr); 60 | sub_->connect(sub_addr); 61 | 62 | // 创建后台接收线程 63 | worker_ = std::thread{[&] { 64 | ReceiveLoop(); 65 | }}; 66 | } 67 | 68 | /// @thread-safe 69 | bool Client::ConnectOnLocalMode(int32_t pub_port, int32_t sub_port) 70 | { 71 | auto localhost = std::string{"tcp://localhost:"}; 72 | auto pub_addr = localhost + std::to_string(pub_port); 73 | auto sub_addr = localhost + std::to_string(sub_port); 74 | Connect(pub_addr, sub_addr); 75 | return true; 76 | } 77 | 78 | /// @thread-safe 79 | void Client::Subscribe(std::string topic, SubscribeHandle handler) 80 | { 81 | // zmq socket 添加话题过滤必须在保存回调函数之后, 82 | // 以保证收到新话题的消息时回调函数是存在的。 83 | { 84 | std::lock_guard lock1(topic_handlers_mutex_); 85 | // 先存一份话题字符串的拷贝 86 | auto [iterator, success] = subscribed_.emplace(topic); 87 | auto &view = *iterator; 88 | assert(view == topic && "std::set::emplace 的返回值不是插入的元素"); 89 | // 然后用 subscribed_ 内话题字符串的 string_view 映射回调函数 90 | topic_handlers_[view] = std::move(handler); 91 | } 92 | // FIXME: 暂不清楚 zmq_setsockopt 是否线程安全,官方文档没看到相关说明, 93 | // 但是在 libzmq 的 github issues 上找到一个 zmq 开发者发布的贴子说是安全的 94 | // https://github.com/zeromq/libzmq/issues/3817 95 | AddTopic(topic); 96 | } 97 | 98 | /// 收消息循环 99 | void Client::ReceiveLoop() 100 | { 101 | // NOTE: 按话题接收需要使用 zmq 的多部分消息处理 102 | // 参考 http://api.zeromq.org/4-1:zmq-recv 103 | 104 | while (!stop_) 105 | { 106 | std::vector message; 107 | std::unique_lock lock1(sub_mutex_); 108 | 109 | do 110 | { 111 | // spdlog::info("receive topic in ReceiveLoop of mbus"); 112 | zmq::message_t msg; 113 | auto result = sub_->recv(msg, zmq::recv_flags::none); 114 | if (result.has_value()) 115 | { 116 | message.emplace_back(std::move(msg)); 117 | } 118 | } while (sub_->get(zmq::sockopt::rcvmore)); 119 | lock1.unlock(); 120 | 121 | if (message.size() <= 1) 122 | { 123 | // TODO: 处理收到没有按话题发布或是超时之类的异常 124 | spdlog::warn("the size of message is less than 2"); 125 | continue; 126 | } 127 | 128 | // 检查话题是否有注册回调函数 129 | auto &topic = message[0]; 130 | // std::string_view topic_sv{ 131 | // static_cast(topic.data()), topic.size()}; 132 | std::string topic_str{ 133 | static_cast(topic.data()), topic.size()}; 134 | // spdlog::info("Receive the topic of message is {}", topic_str); 135 | // std::cout << topic_sv << std::endl; 136 | // 序列化回 proto 对象 137 | auto &msg = message[1]; 138 | value::GeneralMessage general_message; 139 | if (!msg.empty()) 140 | { 141 | general_message.ParseFromArray(msg.data(), msg.size()); 142 | } 143 | 144 | // 调用对应的回调函数 145 | std::unique_lock lock2(topic_handlers_mutex_); 146 | auto iterator = topic_handlers_.find(topic_str); 147 | assert(iterator != topic_handlers_.end() && "收到未注册的话题"); 148 | iterator->second(std::move(general_message)); 149 | } 150 | } 151 | 152 | /// zmq socket 新增话题白名单 153 | void Client::AddTopic(std::string topic) 154 | { 155 | assert(sub_ != nullptr); 156 | sub_->set(zmq::sockopt::subscribe, topic); 157 | } 158 | 159 | /// zmq socket 移除话题过滤白名单 160 | void Client::RemoveTopic(std::string topic) 161 | { 162 | assert(sub_ != nullptr); 163 | sub_->set(zmq::sockopt::unsubscribe, topic); 164 | // subscribed_.erase(std::string{topic}); 165 | } 166 | } // namespace mbus -------------------------------------------------------------------------------- /message_bus/message_dealer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/make_general_message.h" 4 | #include "general_message.pb.h" 5 | #include "message_bus/client.h" 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace mbus 12 | { 13 | 14 | /** 15 | * 消息订阅中心 16 | */ 17 | class MessageDealer 18 | { 19 | public: 20 | /// 禁止复制和移动 21 | MessageDealer(const MessageDealer &) = delete; 22 | MessageDealer(MessageDealer &&) = delete; 23 | MessageDealer &operator=(const MessageDealer &) = delete; 24 | MessageDealer &operator=(MessageDealer &&) = delete; 25 | 26 | /// 构造函数,需要提供一个 zmq context 实例 27 | MessageDealer(const std::shared_ptr &ctx) 28 | : ctx_(ctx), mbus_client_(ctx_) 29 | { 30 | } 31 | 32 | /// 本机自动连接模式 33 | void Connect(int32_t pub_port, int32_t sub_port) 34 | { 35 | mbus_client_.ConnectOnLocalMode(pub_port, sub_port); 36 | } 37 | 38 | /// 指定消息总线服务的地址进行连接 39 | void Connect(const std::string &pub_addr, const std::string &sub_addr) 40 | { 41 | mbus_client_.Connect(pub_addr, sub_addr); 42 | } 43 | 44 | /** 45 | * @brief 添加新的话题订阅回调,同一个话题可以添加多个 46 | * @param MessageType 模板参数,用于指定接收消息后需要解析的类型 47 | * @param topic 订阅的话题 48 | * @param callback 消息处理回调函数,第一个参数是预期接收的消息类型,第二个是原始的 49 | * 通用消息 50 | * @thread-safe 51 | */ 52 | template 53 | void AddTopicListener( 54 | const std::string &topic, 55 | const std::function &callback) 56 | { 57 | // mbus::Client 的回调函数只支持接收一个 value::GeneralMessage 参数, 58 | // 所以必须要额外包装一层 lambda 给到 mbus::Client 订阅话题。 59 | auto unpack_wrapper = [callback](value::GeneralMessage msg) { 60 | // 提取通用消息包中的 payload,并且反序列化到 MessageType 类型上 61 | auto payload_msg = UnmakeGeneralMessage(msg); 62 | // 因为 msg 是值传递的,直接用 move 转移所有权减少复制,target_msg 同理 63 | callback(std::move(payload_msg), std::move(msg)); 64 | }; 65 | 66 | mbus_client_.Subscribe(topic, std::move(unpack_wrapper)); 67 | } 68 | 69 | /** 70 | * @brief 发布新消息到指定话题里 71 | * @param MessageType 发布的消息类型,可省略不写,让函数模板自动推导 72 | * @param topic 发布的目标话题 73 | * @param msg 发布的消息 74 | */ 75 | template 76 | void Publish(const std::string &topic, const MessageType &msg) 77 | { 78 | mbus_client_.Publish(topic, msg); 79 | } 80 | 81 | private: 82 | // zmq context 83 | std::shared_ptr ctx_; 84 | // 消息总线服务的客户端 85 | mbus::Client mbus_client_; 86 | }; 87 | 88 | } // namespace mbus 89 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 消息总线模式的进程间通信库 2 | 3 | ## 依赖 4 | 1. **zmq**,基于 zmq 实现通信 5 | 1. **cppzmq**,c++ 封装的 zmq 接口 6 | 2. **protobuf**,消息序列化 7 | 3. **gflag**,命令行参数解析 8 | 4. **gtest**,单元测试 9 | 10 | ## 后续优化 11 | - [ ] 降低消息接收延迟,消息接收线程收到新消息,将消息序列化和回调函数放入到线程池中执行,避免消息接收线程阻塞造成后续消息延迟。 12 | - [ ] 内存优化,使用 buffer 对象池或内存池,避免大量动态内存分配造成的延迟和 cpu 消耗 13 | 14 | 15 | ## 使用例子 16 | 目前 message-bus 支持发布订阅模式和 RPC 模式两种通信方式。 17 | 18 | 无论是发布订阅还是 RPC,都需要启动一个中心代理服务 `message-bus-server`,本项目编译后能在 `build/` 目录下看到这个程序。只有程序启动后,使用发布订阅和 RPC 进行通信的程序才能正常工作。 19 | 20 | 对于发布订阅,中心代理服务就是简单封装一下 zmq 的 zmq_proxy,实现可以任意动态拓展发布订阅端程序。 21 | 22 | 对于 RPC,中心代理服务区分了 worker 和 client 两种角色,中心代理服务对两者提供了 服务注册、心跳包存活监控、RPC 请求分发等功能。worker 负责处理来自 client 的 RPC 请求,请求和响应的消息包,都由中心代理服务负责转发。 23 | 24 | ### 项目引入方式 25 | 需要用 message-bus 进行进程间通信的项目,主要使用 cmake 提供 FetchContent 模块引入。 26 | 在项目中的 CMakeLists.txt 文件中添加如下代码: 27 | ```cmake 28 | # 引入 FetchContent 模块 29 | include(FetchContent) 30 | # 声明第三方依赖库的资源信息 31 | FetchContent_Declare( 32 | message_bus 33 | GIT_REPOSITORY git@github.com:zjlian/message-bus.git 34 | ) 35 | # 启用 36 | FetchContent_MakeAvailable(message_bus) 37 | ``` 38 | 然后给需要用这个库的 target 添加相关的依赖配置就可以了: 39 | ```cmake 40 | target_include_directories(${PROJECT_NAME} PUBLIC message_bus mbus_rpc) 41 | target_link_libraries(${PROJECT_NAME} PUBLIC message_bus mbus_rpc) 42 | ``` 43 | 44 | 45 | ### 发布订阅模式 46 | 由于 zmq_proxy 的实现,发布订阅模式的发布端和订阅段需要分别连接两个不同的端口。 47 | 48 | 发布订阅模式主要使用头文件 "mbus/message_dealer.h" 下的 mbus::MessageDealer 类进行。 49 | 50 | 下面是一个简单的发布订阅使用例子,发布端程序分别向话题 /hello 和 /world 发布消息,话题 /hello 发布的是普通的字符串消息,话题 /world 发布的是 proto 消息。 51 | 52 | 发布端程序 53 | ```c++ 54 | #include "mbus/message_dealer.h" 55 | #include "tests/add_service.pb.h" 56 | 57 | #include 58 | #include 59 | 60 | int main(int argc, const char **args) 61 | { 62 | auto ctx = std::make_shared(1); 63 | mbus::MessageDealer dealer{ctx}; 64 | // 连接中心代理服务的发布订阅端口 65 | dealer.Connect("tcp://localhost:54321", "tcp://localhost:12345"); 66 | 67 | while (true) 68 | { 69 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 70 | // 随机向话题 /hello 或 /world 发布消息 71 | if ((random() % 100) >= 50) 72 | { 73 | dealer.Publish("/hello", "hello"); 74 | std::cout << "发布 hello 到 /hello" << std::endl; 75 | } 76 | else 77 | { 78 | value::Any world; 79 | world.set_data("world"); 80 | dealer.Publish("/world", world); 81 | std::cout << "发布 world 到 /world" << std::endl; 82 | } 83 | } 84 | } 85 | ``` 86 | 87 | 订阅端的程序 88 | ```c++ 89 | #include "mbus/message_dealer.h" 90 | #include "tests/add_service.pb.h" 91 | 92 | #include 93 | 94 | int main(int argc, const char **args) 95 | { 96 | auto ctx = std::make_shared(1); 97 | mbus::MessageDealer dealer{ctx}; 98 | // 连接中心代理服务的发布订阅端口 99 | dealer.Connect("tcp://localhost:54321", "tcp://localhost:12345"); 100 | 101 | // 订阅话题 /hello,并指定接收到的消息解析成 std::string 类型 102 | dealer.AddTopicListener( 103 | "/hello", 104 | [](std::string msg, value::GeneralMessage general_message) { 105 | std::cout << msg << std::endl; 106 | }); 107 | 108 | // 订阅话题 /world,并指定接收到的消息解析成 value::Any 类型 109 | dealer.AddTopicListener( 110 | "/world", 111 | [](value::Any msg, value::GeneralMessage general_message) { 112 | std::cout << msg.data() << std::endl; 113 | }); 114 | 115 | pause(); 116 | } 117 | ``` 118 | 119 | ### RPC 模式 120 | RPC 模式下,客户端分为两种角色,分别是 worker 和 client,worker 对外提供服务,client 通过服务名称发起请求,中心代理服务会将请求分发给对应的 worker 处理。 121 | 122 | RPC 模式主要使用头文件 "rpc/remote_caller.h" 下的 mbus::RemoteCaller 类进行。 123 | 124 | 下面是使用 RPC 模式实现跨进程计算的例子,client 进程发起 RPC 请求计算两数相加,由 worker 进程接收处理并返回结果。 125 | 126 | protobuf 127 | ```proto 128 | syntax = "proto3"; 129 | package value; 130 | 131 | message AddService 132 | { 133 | // 需要计算的两个数值 134 | int32 number1 = 1; 135 | int32 number2 = 2; 136 | 137 | // 计算结果 138 | int32 result = 3; 139 | } 140 | ``` 141 | 142 | worker 进程的代码 143 | ```c++ 144 | #include "rpc/remote_caller.h" 145 | #include "tests/add_service.pb.h" 146 | 147 | #include 148 | #include 149 | 150 | int main() 151 | { 152 | auto ctx = std::make_shared(1); 153 | mbus::RemoteCaller caller{ctx}; 154 | 155 | auto add_service = [](value::AddService argv, value::GeneralMessage general_message) { 156 | argv.set_result(argv.number1() + argv.number2()); 157 | return argv; 158 | }; 159 | // 第一个模板参数是接收的 rpc 请求的参数的类型,第二个模板参数是响应 rpc 请求的类型 160 | caller.RegisterService("add", add_service); 161 | 162 | caller.Connect("tcp://localhost:5555"); 163 | 164 | // 暂停主线程,避免程序直接结束 165 | pause(); 166 | } 167 | ``` 168 | 169 | client 进程的代码 170 | ```c++ 171 | #include "rpc/remote_caller.h" 172 | #include "tests/add_service.pb.h" 173 | 174 | #include 175 | #include 176 | 177 | int main() 178 | { 179 | auto ctx = std::make_shared(1); 180 | mbus::RemoteCaller caller{ctx}; 181 | caller.Connect("tcp://localhost:5555"); 182 | 183 | int32_t i = 0; 184 | while (true) 185 | { 186 | value::AddService add; 187 | add.set_number1(i); 188 | add.set_number2(i); 189 | 190 | std::cout << "RPC 请求计算 " << i << " + " << i << std::endl; 191 | 192 | // 第一个模板参数是发送的 rpc 请求参数的类型,第二个模板参数是 rpc 请求响应的类型 193 | auto result = caller.SyncCall("add", add); 194 | 195 | std::cout << "计算结果 " << result.payload.result() << std::endl 196 | << std::endl; 197 | 198 | i++; 199 | sleep(1); 200 | } 201 | } 202 | ``` -------------------------------------------------------------------------------- /rpc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(. SOURCES) 2 | add_library( 3 | mbus_rpc STATIC 4 | ${SOURCES} 5 | ) 6 | 7 | target_include_directories( 8 | mbus_rpc PUBLIC 9 | ${PROJECT_INCLUDE_DIR} 10 | ) 11 | 12 | target_link_libraries( 13 | mbus_rpc PUBLIC 14 | ${PROJECT_LINK_LIBRARIES} 15 | ) 16 | 17 | target_compile_options( 18 | mbus_rpc PRIVATE 19 | -std=c++17 20 | ) -------------------------------------------------------------------------------- /rpc/broker.cpp: -------------------------------------------------------------------------------- 1 | #include "rpc/broker.h" 2 | #include "common/constants.h" 3 | #include "common/time.h" 4 | #include "common/uuid.h" 5 | #include "common/zmq_helper.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace mbus 22 | { 23 | /*------ Worker 实现 ------*/ 24 | Worker::Worker(RpcBroker *broker, const std::string &id) 25 | : broker_(broker), identity_(id) 26 | { 27 | } 28 | 29 | /// 比较是否为同一个 worker 30 | bool Worker::operator==(const Worker &other) 31 | { 32 | return identity_ == other.identity_; 33 | } 34 | 35 | /// 获取 worker 的 id 36 | const std::string &Worker::Id() 37 | { 38 | return identity_; 39 | } 40 | 41 | /// 添加该 worker 能处理 service 名称 42 | void Worker::AddServiceName(const std::string &name) 43 | { 44 | service_names_.insert(name); 45 | } 46 | 47 | /// 移除该 worker 要下线的 service 名称 48 | void Worker::DelServiceName(const std::string &name) 49 | { 50 | auto it = service_names_.find(name); 51 | if (it != service_names_.end()) 52 | { 53 | service_names_.erase(it); 54 | } 55 | } 56 | 57 | /// 设置心跳包超时时间 58 | void Worker::SetExpiry(int64_t expiry) 59 | { 60 | expiry_ = expiry; 61 | } 62 | 63 | /// 获取超时时间 64 | int64_t Worker::GetExpiry() 65 | { 66 | return expiry_; 67 | } 68 | 69 | /*------ Service 实现 ------*/ 70 | Service::Service(RpcBroker *broker, const std::string &name) 71 | : broker_(broker), name_(name) 72 | { 73 | } 74 | 75 | /// 比较是否为同一个 worker 76 | bool Service::operator==(const Service &other) 77 | { 78 | return name_ == other.name_; 79 | } 80 | 81 | /// 添加新请求 82 | void Service::AddRequest(MessagePack &&pack) 83 | { 84 | requests_.emplace_back(std::move(pack)); 85 | } 86 | 87 | /// 添加新的 worker 88 | void Service::AddWorker(std::weak_ptr worker) 89 | { 90 | worker_.emplace_back(std::move(worker)); 91 | } 92 | 93 | /// 移除 worker 94 | void Service::RemoveWorker(const std::string &worker_id) 95 | { 96 | auto remove = std::remove_if(worker_.begin(), worker_.end(), [&](std::weak_ptr worker) { 97 | // 移除 weak_ptr 失效和指定要删除的 98 | if (worker.expired() || worker.lock()->Id() == worker_id) 99 | { 100 | spdlog::warn("remove worker_id is {}", worker_id); 101 | } 102 | 103 | return worker.expired() || worker.lock()->Id() == worker_id; 104 | }); 105 | 106 | worker_.erase(remove, worker_.end()); 107 | } 108 | 109 | /// 分发请求队列的消息到 worker 里 110 | void Service::Dispacth() 111 | { 112 | // 如果存在 weak_ptr 失效的 worker,先移除 113 | RemoveWorker(""); 114 | 115 | while (!requests_.empty() && !worker_.empty()) 116 | { 117 | // TODO 后续如果有多个 worker 需要将请求均发到多个 worker 上。 118 | // 目前先实现只发给第一个 worker 119 | auto messages = std::move(requests_.front()); 120 | requests_.pop_front(); 121 | 122 | auto worker = worker_.front().lock(); 123 | broker_->SendMessage(worker->Id(), messages); 124 | } 125 | spdlog::info("name is {} and requests.length is {} and worker_.length is {}", name_, requests_.size(), worker_.size()); 126 | 127 | /// 处理worker不存在的情况 128 | while (!requests_.empty()) 129 | { 130 | broker_->AddUnHandleRequest(std::move(requests_.front())); 131 | requests_.pop_front(); 132 | } 133 | } 134 | 135 | /*------ RpcBroken 实现 ------*/ 136 | RpcBroker::RpcBroker(const std::shared_ptr &ctx) 137 | : ctx_(ctx) 138 | { 139 | assert(ctx_ != nullptr); 140 | 141 | socket_ = std::make_unique(*ctx_, zmq::socket_type::router); 142 | assert(socket_ != nullptr && "Out Of Memory!!!"); 143 | heartbeat_at_ = Now() + HEARTBEAT_INTERVAL; 144 | } 145 | 146 | void RpcBroker::Debug() 147 | { 148 | debug_ = true; 149 | } 150 | 151 | void RpcBroker::Bind(const std::string &endpoint) 152 | { 153 | assert(ctx_ != nullptr); 154 | assert(socket_ != nullptr); 155 | assert(!endpoint.empty()); 156 | 157 | socket_->bind(endpoint); 158 | // std::cout << "rpc 代理服务地址: " << endpoint << std::endl; 159 | spdlog::info("rpc agent service address is {}", endpoint); 160 | } 161 | 162 | void RpcBroker::HandleRequest() 163 | { 164 | while (!stop_) 165 | { 166 | if (messages_task_.empty()) 167 | { 168 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 169 | } 170 | auto &messages = messages_task_.front(); 171 | if (debug_) 172 | { 173 | // std::cout << "收到到消息:" << StringifyMessages(messages) << std::endl; 174 | spdlog::info("Received the message: {}", StringifyMessages(messages)); 175 | } 176 | auto routing_id = std::move(messages.front()); 177 | messages.pop_front(); 178 | assert(routing_id.size() == 16); 179 | 180 | auto serial_num = std::move(messages.front()); 181 | messages.pop_front(); 182 | 183 | // 请求来自 worker 184 | if (Equal(messages.front(), RPC_WORKER)) 185 | { 186 | messages.pop_front(); 187 | HandleWorkerMessage(std::move(routing_id), std::move(serial_num), std::move(messages)); 188 | } 189 | // 请求来自 client 190 | else if (Equal(messages.front(), RPC_CLIENT)) 191 | { 192 | messages.pop_front(); 193 | HandleClientMessage(std::move(routing_id), std::move(serial_num), std::move(messages)); 194 | } 195 | else 196 | { 197 | assert(false && "非法请求"); 198 | } 199 | messages_task_.pop_front(); 200 | } 201 | } 202 | 203 | void RpcBroker::Run() 204 | { 205 | assert(stop_ == true); 206 | assert(ctx_ != nullptr); 207 | assert(socket_ != nullptr); 208 | 209 | stop_ = false; 210 | while (!stop_) 211 | { 212 | if (WaitReadable(*socket_, HEARTBEAT_INTERVAL)) 213 | { 214 | spdlog::info("Prepare to receive data"); 215 | auto messages = ReceiveAll(*socket_); 216 | if (debug_) 217 | { 218 | // std::cout << "收到到消息:" << StringifyMessages(messages) << std::endl; 219 | spdlog::info("Received the message: {}", StringifyMessages(messages)); 220 | } 221 | // messages_task_.push_back(std::move(messages)); 222 | 223 | auto routing_id = std::move(messages.front()); 224 | messages.pop_front(); 225 | assert(routing_id.size() == 16); 226 | 227 | auto serial_num = std::move(messages.front()); 228 | messages.pop_front(); 229 | 230 | // 请求来自 worker 231 | if (Equal(messages.front(), RPC_WORKER)) 232 | { 233 | messages.pop_front(); 234 | HandleWorkerMessage(std::move(routing_id), std::move(serial_num), std::move(messages)); 235 | } 236 | // 请求来自 client 237 | else if (Equal(messages.front(), RPC_CLIENT)) 238 | { 239 | messages.pop_front(); 240 | HandleClientMessage(std::move(routing_id), std::move(serial_num), std::move(messages)); 241 | } 242 | else 243 | { 244 | assert(false && "非法请求"); 245 | } 246 | } 247 | else 248 | { 249 | // 超时时删除过期的 worker 250 | // fixme 当不断接收到消息时,此分支时不会被执行到的 251 | auto current_time = Now() - HEARTBEAT_EXPIRY; 252 | auto it = workers_.begin(); 253 | while (it != workers_.end()) 254 | { 255 | auto cur_worker = it->second; 256 | spdlog::info("worker id is {} and expirt is {} and current_time is {}", cur_worker->Id(), cur_worker->GetExpiry(), current_time); 257 | spdlog::info("expiry of cur_worker is {}", cur_worker->GetExpiry()); 258 | if (cur_worker->GetExpiry() < current_time) 259 | { 260 | spdlog::warn("the worker id is {} will be removed on else", cur_worker->Id()); 261 | auto service_it = services_.begin(); 262 | while (service_it != services_.end()) 263 | { 264 | spdlog::warn("the service {} will be removed on else", service_it->first); 265 | service_it->second->RemoveWorker(cur_worker->Id()); 266 | service_it++; 267 | } 268 | it->second = nullptr; 269 | workers_.erase(it++); 270 | } 271 | else 272 | { 273 | ++it; 274 | } 275 | } 276 | spdlog::info("out delete timeout service"); 277 | } 278 | DeleteTimeoutWorker(workers_, services_); 279 | // 分发各个 service 中的请求到 worker 去 280 | Dispacth(); 281 | } 282 | } 283 | 284 | void RpcBroker::DeleteTimeoutWorker(std::unordered_map> &workers, std::unordered_map> &services) 285 | { 286 | // 超时时删除过期的 worker 287 | auto current_time = Now() - HEARTBEAT_EXPIRY; 288 | auto it = workers_.begin(); 289 | while (it != workers_.end()) 290 | { 291 | auto cur_worker = it->second; 292 | spdlog::info("worker id is {} and expirt is {} and current_time is {}", cur_worker->Id(), cur_worker->GetExpiry(), current_time); 293 | spdlog::info("expiry of cur_worker is {}", cur_worker->GetExpiry()); 294 | if (cur_worker->GetExpiry() < current_time) 295 | { 296 | spdlog::warn("the worker id is {} will be removed", cur_worker->Id()); 297 | auto service_it = services_.begin(); 298 | while (service_it != services_.end()) 299 | { 300 | spdlog::warn("the service {} will be removed", service_it->first); 301 | service_it->second->RemoveWorker(cur_worker->Id()); 302 | service_it++; 303 | } 304 | it->second = nullptr; 305 | workers_.erase(it++); 306 | } 307 | else 308 | { 309 | ++it; 310 | } 311 | } 312 | } 313 | 314 | void RpcBroker::SendMessage(const std::string &routing_id, MessagePack &messages) 315 | { 316 | messages.push_front(MakeMessage(routing_id)); 317 | if (debug_) 318 | { 319 | // std::cout << "分发请求 " << StringifyMessages(messages) << std::endl; 320 | spdlog::info("distribution request is {}", StringifyMessages(messages)); 321 | } 322 | SendAll(*socket_, messages, send_mu_); 323 | } 324 | 325 | /// 获取或创建新的 worker 记录 326 | std::weak_ptr RpcBroker::RequireWorker(const std::string &worker_id) 327 | { 328 | auto iterator = workers_.find(worker_id); 329 | if (iterator == workers_.end()) 330 | { 331 | if (debug_) 332 | { 333 | // std::cout << "创建新的 worker 记录,worker id: " << UUID2String(worker_id) << std::endl; 334 | spdlog::info("create a new worker record, worker id is {}", UUID2String(worker_id)); 335 | } 336 | 337 | workers_[worker_id] = std::make_shared(this, worker_id); 338 | workers_[worker_id]->SetExpiry(Now() + HEARTBEAT_INTERVAL); 339 | } 340 | 341 | iterator = workers_.find(worker_id); 342 | auto worker = iterator->second; 343 | assert(worker->Id() == worker_id); 344 | 345 | return worker; 346 | } 347 | 348 | /// 获取或创建新的 service 记录 349 | Service *RpcBroker::RequireService(const std::string &service_name) 350 | { 351 | auto iterator = services_.find(service_name); 352 | if (iterator == services_.end()) 353 | { 354 | if (debug_) 355 | { 356 | // std::cout << "创建新的 service 记录,service name: " << service_name << std::endl; 357 | spdlog::info("create a new service record, service name is {}", service_name); 358 | } 359 | 360 | services_[service_name] = std::make_unique(this, service_name); 361 | } 362 | 363 | iterator = services_.find(service_name); 364 | auto service = iterator->second.get(); 365 | assert(service != nullptr); 366 | return service; 367 | } 368 | 369 | /// 下线 service 记录 370 | void RpcBroker::DeleteService(const std::string &service_name) 371 | { 372 | auto iterator = services_.find(service_name); 373 | if (iterator == services_.end()) 374 | { 375 | if (debug_) 376 | { 377 | // std::cout << "删除 service 记录时没有该服务记录,service name: " << service_name << std::endl; 378 | spdlog::info("There is no such service record when deleting the service record, service name is {}", service_name); 379 | } 380 | return; 381 | } 382 | if (debug_) 383 | { 384 | // std::cout << "删除新的 service 记录,service name: " << service_name << std::endl; 385 | spdlog::info("delete service record, service name is {}", service_name); 386 | } 387 | iterator->second->RemoveWorker(""); 388 | iterator->second = nullptr; 389 | services_.erase(iterator); 390 | } 391 | 392 | /// @brief 处理 worker 的请求,服务注册、心跳包、断开链接 393 | /// @param worker_id 发送该请求的 worker 的 uuid 394 | /// @param messages 请求携带的全部 message 395 | void RpcBroker::HandleWorkerMessage(zmq::message_t worker_id, zmq::message_t serial_num, MessagePack messages) 396 | { 397 | assert(worker_id.size() == 16); 398 | assert(messages.size() >= 1); 399 | 400 | auto id_string = worker_id.to_string(); 401 | bool new_worker = (workers_.find(id_string) == workers_.end()); 402 | auto worker_wp = RequireWorker(id_string); 403 | auto worker = worker_wp.lock(); 404 | 405 | auto header = std::move(messages.front()); 406 | messages.pop_front(); 407 | assert(header.size() >= 6); 408 | 409 | // 收到 worker 的服务注册请求 410 | if (Equal(header, RPC_REG)) 411 | { 412 | for (const auto &name : messages) 413 | { 414 | assert(!name.empty()); 415 | std::string service_name{name.data(), name.size()}; 416 | auto *service = RequireService(service_name); 417 | worker->AddServiceName(service_name); 418 | service->AddWorker(worker_wp); 419 | 420 | if (debug_) 421 | { 422 | // std::cout << UUID2String(id_string) << " 注册服务 " << service_name << std::endl; 423 | spdlog::info("worker id is {} to sign in service, service name is {}", UUID2String(id_string), service_name); 424 | } 425 | } 426 | ResponseSuccess(id_string); 427 | } 428 | // 收到 worker 回复的 rpc 请求结果 429 | else if (Equal(header, RPC_RES)) 430 | { 431 | auto routing_id = std::move(messages.front()); 432 | messages.pop_front(); 433 | assert(routing_id.size() == 16 && "rpc 响应转发的客户端 id 错误"); 434 | spdlog::info("respond to client and response uuid is {} and serial_num is {}", UUID2String(routing_id.to_string()), serial_num.to_string()); 435 | // messages 目前只剩下服务名和响应内容,需要将相关的头部信息拼接回去,发给客户端 436 | messages.push_front(MakeMessage(RPC_RES)); 437 | messages.push_front(MakeMessage(RPC_WORKER)); 438 | messages.push_front(std::move(serial_num)); 439 | messages.push_front(std::move(routing_id)); 440 | SendAll(*socket_, messages, send_mu_); 441 | } 442 | // 收到 worker 的心跳包 443 | else if (Equal(header, RPC_HB)) 444 | { 445 | // 设置当前 worker 的超时时间,在这个时间之后没收到新的心跳包就要删除 446 | spdlog::info("the id of recevied the beat of worker is {}", worker->Id()); 447 | worker->SetExpiry(Now() + HEARTBEAT_INTERVAL); 448 | } 449 | // 处理 worker 的下线请求 450 | else if (Equal(header, RPC_UNCONNECT)) 451 | { 452 | for (const auto &name : messages) 453 | { 454 | assert(!name.empty()); 455 | std::string service_name{name.data(), name.size()}; 456 | DeleteService(service_name); 457 | auto cur_service = RequireService(service_name); 458 | cur_service->RemoveWorker(id_string); 459 | worker->DelServiceName(service_name); 460 | if (debug_) 461 | { 462 | // std::cout << UUID2String(id_string) << " 下线服务 " << service_name << std::endl; 463 | spdlog::info("worker id is {} to off line service, service name is {}", UUID2String(id_string), service_name); 464 | } 465 | } 466 | if (worker->GetSizeOfService() == 0) 467 | { 468 | worker->SetExpiry(Now()); 469 | } 470 | ResponseSuccess(id_string); 471 | } 472 | 473 | // todo 下线处理 474 | } 475 | 476 | /// 处理 client 的请求,服务查询、服务请求分发 477 | /// @param client_id 发送该请求的 client 的 uuid 478 | /// @param messages 请求携带的全部 message 479 | void RpcBroker::HandleClientMessage(zmq::message_t client_id, zmq::message_t serial_num, MessagePack messages) 480 | { 481 | assert(client_id.size() == 16); 482 | assert(messages.size() >= 1); 483 | 484 | auto id_string = client_id.to_string(); 485 | 486 | if (Equal(messages.front(), RPC_REQ)) 487 | { 488 | // 取出 service 部分 489 | auto service_name = messages[1].to_string(); 490 | auto *service = RequireService(service_name); 491 | 492 | // 将请求加入到对应服务的请求队列中 493 | if (debug_) 494 | { 495 | // std::cout << "来自 " << UUID2String(id_string) << " 的请求加入 " << service_name << std::endl 496 | // << "请求内容 " << StringifyMessages(messages) << std::endl; 497 | spdlog::info("the request that come from worker id {} and serial_num is {} to join service {} and content of request is {}", UUID2String(id_string), serial_num.to_string(), service_name, StringifyMessages(messages)); 498 | } 499 | 500 | // 重新封包被弹出的协议头 501 | messages.push_front(MakeMessage(RPC_CLIENT)); 502 | messages.push_front(std::move(serial_num)); 503 | messages.push_front(std::move(client_id)); 504 | 505 | service->AddRequest(std::move(messages)); 506 | } 507 | } 508 | 509 | /// 分发请求到对应的 worker 510 | void RpcBroker::Dispacth() 511 | { 512 | for (auto &service : services_) 513 | { 514 | spdlog::info("service is {} to dispacth", service.first); 515 | service.second->Dispacth(); 516 | } 517 | while (!not_processed_.empty()) 518 | { 519 | auto messages = std::move(not_processed_.front()); 520 | not_processed_.pop_front(); 521 | auto worker_id = std::move(messages.front()); 522 | messages.pop_front(); 523 | auto serial_id = std::move(messages.front()); 524 | messages.pop_front(); 525 | assert(worker_id.size() == 16); 526 | ResponseNotProcessed(worker_id.to_string(), serial_id.to_string()); 527 | } 528 | } 529 | 530 | /// 回复 worker 请求成功 531 | void RpcBroker::ResponseSuccess(const std::string &worker_id) 532 | { 533 | auto iterator = workers_.find(worker_id); 534 | assert(iterator != workers_.end()); 535 | 536 | BufferPack messages; 537 | messages.push_back(MakeZmqBuffer(worker_id)); 538 | messages.push_back(MakeZmqBuffer(RPC_SUCCESS)); 539 | 540 | SendAll(*socket_, messages, send_mu_); 541 | } 542 | 543 | /// 回复给client 请求未处理的失败 544 | void RpcBroker::ResponseNotProcessed(const std::string &client_id, const std::string &serial_id) 545 | { 546 | BufferPack messages; 547 | messages.push_back(MakeZmqBuffer(client_id)); 548 | messages.push_back(MakeZmqBuffer(serial_id)); 549 | messages.push_back(MakeZmqBuffer(RPC_BROKER)); 550 | messages.push_back(MakeZmqBuffer(RPC_NOTPROCESSED)); 551 | 552 | SendAll(*socket_, messages, send_mu_); 553 | } 554 | 555 | } // namespace mbus -------------------------------------------------------------------------------- /rpc/broker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/macro_utility.h" 4 | #include "common/zmq_helper.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace mbus 19 | { 20 | class RpcBroker; 21 | class Worker; 22 | class Service; 23 | 24 | /// service 信息登记 25 | class Service 26 | { 27 | public: 28 | DISABLE_COPY(Service); 29 | 30 | Service(RpcBroker *broker, const std::string &name); 31 | 32 | /// 比较是否为同一个 worker 33 | bool operator==(const Service &other); 34 | 35 | /// 添加新请求 36 | void AddRequest(MessagePack &&pack); 37 | 38 | /// 添加新的 worker 39 | void AddWorker(std::weak_ptr worker); 40 | 41 | /// 移除 worker 42 | void RemoveWorker(const std::string &worker_id); 43 | 44 | /// 分发请求队列的消息到 worker 里 45 | void Dispacth(); 46 | 47 | private: 48 | // 保存了本 service 的 broker 49 | RpcBroker *broker_{nullptr}; 50 | // service 名称 51 | std::string name_{}; 52 | // 来自 client 的全部 rpc 请求 53 | std::deque requests_{}; 54 | // 能够处理该 service 请求的 worker 55 | std::deque> worker_{}; 56 | }; 57 | 58 | class Worker 59 | { 60 | public: 61 | DISABLE_COPY(Worker); 62 | 63 | Worker(RpcBroker *broker, const std::string &id); 64 | 65 | /// 比较是否为同一个 worker 66 | bool operator==(const Worker &other); 67 | 68 | /// 获取 worker 的 id 69 | const std::string &Id(); 70 | 71 | /// 添加该 worker 能处理 service 名称 72 | void AddServiceName(const std::string &name); 73 | 74 | /// 移除该 worker 请求下线的 service 名称 75 | void DelServiceName(const std::string &name); 76 | 77 | /// 移除该worker 所有的service 78 | void DelAllServiceName(); 79 | 80 | /// 获取该 worker 所提供的服务名称 81 | int GetSizeOfService() const 82 | { 83 | return service_names_.size(); 84 | } 85 | 86 | /// 设置心跳包超时时间 87 | void SetExpiry(int64_t expiry); 88 | 89 | /// 获取超时时间 90 | int64_t GetExpiry(); 91 | 92 | private: 93 | // 保存了本 worker 的 broker 94 | RpcBroker *broker_{nullptr}; 95 | // worker 的唯一 id 96 | std::string identity_{}; 97 | // worker 提供的全部服务的名称 98 | std::unordered_set service_names_{}; 99 | // 到期时间,时间段内没收到对应的 worker 程序的心跳包,就删除该 worker 100 | int64_t expiry_{}; 101 | }; 102 | 103 | class RpcBroker 104 | { 105 | public: 106 | DISABLE_COPY_AND_MOVE(RpcBroker); 107 | 108 | RpcBroker(const std::shared_ptr &ctx); 109 | ~RpcBroker() = default; 110 | 111 | void Debug(); 112 | 113 | /// 在指定的 zmq addr 上开启代理服务 114 | void Bind(const std::string &endpoint); 115 | 116 | // 启动代理服务 117 | void Run(); 118 | 119 | void HandleRequest(); 120 | 121 | /// 添加无法处理的请求,由broker处理响应 122 | void AddUnHandleRequest(MessagePack &&pack) 123 | { 124 | not_processed_.emplace_back(std::move(pack)); 125 | } 126 | 127 | // 发送消息到指定 routing_id 的客户端,可能是 worker 也可能是 client 128 | void SendMessage(const std::string &routing_id, MessagePack &messages); 129 | 130 | private: 131 | /// 获取或创建新的 worker 记录 132 | std::weak_ptr RequireWorker(const std::string &worker_id); 133 | 134 | /// 获取或创建新的 service 记录 135 | Service *RequireService(const std::string &service_name); 136 | 137 | /// 删除 service 记录 138 | void DeleteService(const std::string &service_name); 139 | 140 | /// 删除 超时 worker 141 | void DeleteTimeoutWorker(std::unordered_map> &workers, std::unordered_map> &services); 142 | 143 | /// @brief 处理 worker 的请求,服务注册、心跳包、断开链接 144 | /// @param worker_id 发送该请求的 worker 的 uuid 145 | /// @param messages 请求携带的全部 message 146 | void HandleWorkerMessage(zmq::message_t worker_id, zmq::message_t serial_num, MessagePack messages); 147 | 148 | /// 处理 client 的请求,服务查询、服务请求分发 149 | /// @param client_id 发送该请求的 client 的 uuid 150 | /// @param messages 请求携带的全部 message 151 | void HandleClientMessage(zmq::message_t client_id, zmq::message_t serial_num, MessagePack messages); 152 | 153 | /// 删除心跳包超时的 worker 154 | void Purge(); 155 | 156 | /// 分发请求到对应的 worker 157 | void Dispacth(); 158 | 159 | /// 回复 worker 请求成功 160 | void ResponseSuccess(const std::string &worker_id); 161 | 162 | /// 回复 client 请求未处理的响应 163 | void ResponseNotProcessed(const std::string &client_id, const std::string &); 164 | 165 | private: 166 | std::atomic debug_{false}; 167 | std::atomic stop_{true}; 168 | std::shared_ptr ctx_{}; 169 | std::unique_ptr socket_{}; 170 | std::string bind_addr{}; 171 | 172 | // 收到到所有消息的队列 173 | std::deque> messages_task_{}; 174 | std::unordered_map> services_{}; 175 | std::unordered_map> workers_{}; 176 | std::unordered_map waiting_worker_{}; 177 | // 无法处理的 client 的rpc请求 178 | std::deque not_processed_{}; 179 | uint64_t heartbeat_at_{}; 180 | std::mutex send_mu_; 181 | }; 182 | 183 | } // namespace mbus -------------------------------------------------------------------------------- /rpc/client.cpp: -------------------------------------------------------------------------------- 1 | #include "rpc/client.h" 2 | #include "common/constants.h" 3 | #include "common/time.h" 4 | #include "common/uuid.h" 5 | #include "common/zmq_helper.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | /* 25 | RPC 客户端发出的请求包格式: routing_id | RPC_REQ | service_name | argv 26 | 使用 zmq 的多部分消息发出 27 | routing_id 是 zmq socket 添加相关选项设置后自动携带的,用于区别请求的来源 28 | */ 29 | 30 | namespace mbus 31 | { 32 | 33 | RpcClient::RpcClient(const std::shared_ptr &ctx) 34 | : ctx_(ctx), uuid_(UUID()) 35 | { 36 | assert(ctx_ != nullptr); 37 | } 38 | 39 | void RpcClient::Debug() 40 | { 41 | debug_ = true; 42 | } 43 | 44 | /// 链接 rpc 服务 45 | void RpcClient::Connect(const std::string &broker_addr) 46 | { 47 | assert(ctx_ != nullptr); 48 | assert(!broker_addr.empty()); 49 | 50 | if (debug_ && socket_ == nullptr) 51 | { 52 | auto str = UUID2String(uuid_); 53 | // std::cout << "当前进程的 rpc 客户端 uuid: " << str << std::endl; 54 | spdlog::info("The uuid of the rpc client of the current process is {}", str); 55 | } 56 | 57 | // NOTE: cppzmq 封装的 zmq::socket_t 在析构时会自动断开连接,所以重复调用 Connect 函数,会先断开先前的连接 58 | socket_ = std::make_unique(*ctx_, zmq::socket_type::dealer); 59 | assert(socket_ != nullptr && "Out Of Memory!!!"); 60 | // 设置 socket 的标识符 61 | socket_->set(zmq::sockopt::routing_id, uuid_); 62 | // 设置 socket 关闭时丢弃所有未发送成功的消息 63 | socket_->set(zmq::sockopt::linger, 0); 64 | broker_addr_ = broker_addr; 65 | socket_->connect(broker_addr_); 66 | if (debug_) 67 | { 68 | // std::cout << "连接 rpc 代理服务: " << broker_addr_ << std::endl; 69 | spdlog::info("connect rpc proxy service: {}", broker_addr_); 70 | } 71 | } 72 | 73 | /// 重新连接上一次连接的代理服务 74 | void RpcClient::Reconnect() 75 | { 76 | assert(ctx_ != nullptr); 77 | assert(!broker_addr_.empty()); 78 | 79 | Connect(broker_addr_); 80 | } 81 | 82 | /// 设置 rpc 请求超时时间 83 | void RpcClient::SetTimeout(size_t ms) 84 | { 85 | assert(ms >= 0); 86 | timeout_ = ms; 87 | if (debug_) 88 | { 89 | // std::cout << "rpc 请求超时时间修改为: " << timeout_ << std::endl; 90 | spdlog::info("The timeout of rpc requests is changed to {}", timeout_); 91 | } 92 | } 93 | 94 | /// 设置 rpc 请求失败重试次数 95 | void RpcClient::SetRerties(size_t rerties) 96 | { 97 | assert(rerties >= 0); 98 | retries_ = rerties; 99 | if (debug_) 100 | { 101 | // std::cout << "rpc 请求重试次数修改为: " << timeout_ << std::endl; 102 | spdlog::info("The times of rerty of rpc requests is changed to {}", rerties); 103 | } 104 | } 105 | 106 | /// 发起阻塞等待的 rpc 请求 107 | std::string RpcClient::SyncCall(const std::string &service, const std::string &argv, const int64_t &timeout, const int &rerties) 108 | { 109 | assert(ctx_ != nullptr); 110 | assert(socket_ != nullptr); 111 | assert(!service.empty()); 112 | spdlog::info("rerties is {} and timeout is {}", rerties, timeout); 113 | assert(rerties >= -1); 114 | assert(timeout >= -1); 115 | 116 | std::vector messages; 117 | messages.reserve(5); 118 | // fixme: 暂时使用时间戳当做消息序列号 119 | auto serial_num = std::to_string(Now()); 120 | messages.push_back(MakeZmqBuffer(serial_num)); 121 | messages.push_back(MakeZmqBuffer(RPC_CLIENT)); 122 | messages.push_back(MakeZmqBuffer(RPC_REQ)); 123 | messages.push_back(MakeZmqBuffer(service)); 124 | messages.push_back(MakeZmqBuffer(argv)); 125 | 126 | // 设置请求次数 127 | int to_request_times = retries_; 128 | 129 | if (rerties == -1) 130 | { 131 | // -1 为不重试 132 | to_request_times = 0; 133 | } 134 | else if (rerties > 0) 135 | { 136 | // 大于0位设置实际重试次数,否则为默认重试次数 137 | to_request_times = rerties; 138 | } 139 | // 本身需要请求一次 加上重试一次 140 | to_request_times++; 141 | 142 | int64_t timeout_of_current_request; 143 | 144 | if (timeout == 0) 145 | { 146 | // 使用系统默认值 147 | timeout_of_current_request = timeout_; 148 | } 149 | else if (timeout == -1) 150 | { 151 | // 不设置超时,但还是设置为默认值的16倍 152 | timeout_of_current_request = (timeout_ << 4); 153 | } 154 | else 155 | { 156 | timeout_of_current_request = timeout; 157 | } 158 | // 设置超时时间 159 | 160 | while (to_request_times && ctx_) 161 | { 162 | // 将生成的deadline发送到worker方 163 | int64_t deadline{Now() + timeout_of_current_request}; 164 | std::string deadline_string = std::to_string(deadline); 165 | spdlog::info("deadline_string is {}", deadline_string); 166 | messages.push_back(MakeZmqBuffer(deadline_string)); 167 | 168 | if (debug_) 169 | { 170 | // std::cout << "准备发送消息:" << StringifyMessages(messages) << std::endl; 171 | spdlog::info("ready to send message: {} and serial_num is {}", StringifyMessages(messages), serial_num); 172 | } 173 | SendAll(*socket_, messages, send_mx_); 174 | if (debug_) 175 | { 176 | // std::cout << "发送完成" << std::endl; 177 | spdlog::info("send complete"); 178 | } 179 | RECEIVE_AGAIN: 180 | if (WaitReadable(*socket_, timeout_of_current_request)) 181 | { 182 | auto response = ReceiveAll(*socket_); 183 | 184 | auto &response_serial_num = response.front(); 185 | 186 | if (debug_) 187 | { 188 | // std::cout << "收到服务端响应:" << StringifyMessages(response) << std::endl; 189 | spdlog::info("received server response : {} and message serial_num is {}", StringifyMessages(response), response_serial_num.to_string()); 190 | } 191 | 192 | if (S1BiggersS2(serial_num, response_serial_num.to_string())) 193 | { 194 | // 当前请求的序列号比响应回来的序列号要大,说明是历史消息,忽略掉,重新接受 195 | spdlog::info("serial_num is {} and received server response serial is {}", serial_num, response_serial_num.to_string_view()); 196 | spdlog::info("received server response is expired"); 197 | goto RECEIVE_AGAIN; 198 | } 199 | response.pop_front(); 200 | 201 | assert(response.size() >= 2 && "响应协议错误"); 202 | 203 | auto &res_role = response.front(); 204 | assert((Equal(res_role, RPC_WORKER) || Equal(res_role, RPC_BROKER)) && "响应协议角色错误"); 205 | response.pop_front(); 206 | 207 | auto &res_header = response.front(); 208 | assert((Equal(res_header, RPC_RES) || Equal(res_header, RPC_NOTPROCESSED)) && "响应协议头错误"); 209 | assert(!Equal(res_header, RPC_NOTPROCESSED) && "服务端未注册该服务"); 210 | response.pop_front(); 211 | 212 | auto &res_service = response.front(); 213 | if (debug_) 214 | { 215 | std::string s(res_service.data(), res_service.size()); 216 | spdlog::info("res_service is {} and service is {}", s, service); 217 | } 218 | assert(Equal(res_service, service) && "响应服务错误"); 219 | response.pop_front(); 220 | assert(response.size() >= 1 && "body无内容"); 221 | auto &res_payload = response.front(); 222 | return res_payload.to_string(); 223 | } 224 | else 225 | { 226 | if (debug_) 227 | { 228 | if (to_request_times) 229 | { 230 | // std::cout << "服务端响应超时,重新连接后尝试再次请求" << std::endl; 231 | spdlog::info("The server response timed out, try to request again after reconnecting"); 232 | // 指数退避 233 | timeout_of_current_request = timeout_of_current_request << 1; 234 | } 235 | else 236 | { 237 | // std::cout << "重试次数过多,放弃请求" << std::endl; 238 | spdlog::info("Too many retries, abandoning the request"); 239 | break; 240 | } 241 | } 242 | Reconnect(); 243 | } 244 | to_request_times--; 245 | /// 弹出deadline 246 | messages.pop_back(); 247 | } 248 | // std::cout << "rpc 请求结束" << std::endl; 249 | spdlog::info("end of rpc request"); 250 | return {}; 251 | } 252 | 253 | } // namespace mbus -------------------------------------------------------------------------------- /rpc/client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/macro_utility.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | namespace mbus 10 | { 11 | 12 | /// rpc 服务的客户端 13 | class RpcClient 14 | { 15 | public: 16 | DISABLE_COPY_AND_MOVE(RpcClient); 17 | 18 | RpcClient(const std::shared_ptr &ctx); 19 | 20 | /// 开启调试模式,输出调试信息到终端 21 | void Debug(); 22 | 23 | /// 链接代理服务,多次调用会断开连接后重连 24 | void Connect(const std::string &broker_addr); 25 | /// 重新连接上一次连接的代理服务 26 | void Reconnect(); 27 | 28 | /// 设置 rpc 请求超时时间 29 | void SetTimeout(size_t ms); 30 | 31 | /// 设置 rpc 请求失败重试次数 32 | void SetRerties(size_t rerties); 33 | 34 | /// @brief 发起阻塞等待的 rpc 请求 35 | /// @param service 目标 rpc 服务的名称 36 | /// @param argv 请求参数 37 | /// @param timeout 超时时间 为0时为系统默认值,-1为无限等待,大于0时为实际等待值 38 | /// @param retries 重试次数 为0时为系统默认值,-1为不重试,大于0时为实际重试值 39 | std::string SyncCall(const std::string &service, const std::string &argv, const int64_t &timeout, const int &rerties); 40 | 41 | private: 42 | std::shared_ptr ctx_{}; 43 | std::unique_ptr socket_{}; 44 | /// 代理服务的地址 45 | std::string broker_addr_{}; 46 | /// 唯一 id,用于区分身份 47 | std::string uuid_{}; 48 | 49 | /// 请求超时时间,毫秒 50 | size_t timeout_{20000}; 51 | /// 请求失败重试次数 52 | size_t retries_{3}; 53 | /// 是否输出调试信息到终端 54 | bool debug_{}; 55 | // 同步发送互斥量 56 | std::mutex send_mx_; 57 | }; 58 | 59 | } // namespace mbus -------------------------------------------------------------------------------- /rpc/readme.md: -------------------------------------------------------------------------------- 1 | # RPC 模式通信协议 2 | 3 | ## 协议 4 | 用 zmq 多部分消息包组成一个 RPC 通信协议 5 | 6 | 每个部分的类型均为字符串 7 | 8 | 第一个部分均为 routing_id 9 | 第二格部分为角色标签 10 | 第三个部分为请求类型或者响应类型 11 | 12 | 13 | ### client 会发出的消息包 14 | 15 | 发起 RPC 请求: uuid | RPC_CLIENT | RPC_REQ | 服务名 | 请求参数 16 | 17 | 发起 RPC 服务查询是否存在请求: uuid | RPC_CLIENT | RPC_QUERY | 服务名 18 | 19 | 20 | ### worker 会发出的消息包 21 | 22 | 发起服务注册请求: uuid | RPC_WORKER | RPC_REG | 服务名1 | 服务名2 | ... 23 | 24 | 响应 RPC 请求给 broker 端: uuid | RPC_WORKER | RPC_RES | client_id | 服务名 | body 25 | 26 | 发出心跳包给 broker: uuid | RPC_WORKER | RPC_HB 27 | 28 | 发出下线请求: uuid | RPC_WORKER | RPC_UNCONNECT 29 | 30 | 31 | ### broker 会发出的消息包 32 | 33 | 响应服务注册成功请求: uuid | RPC_BROKER | RPC_SUCCESS 34 | 35 | 转发 rpc 响应给 client: uuid | RPC_WORKER | RPC_RES | 服务名 | body 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /rpc/remote_caller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/make_general_message.h" 4 | #include "common/time.h" 5 | #include "general_message.pb.h" 6 | #include "rpc/client.h" 7 | #include "rpc/worker.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | namespace mbus 21 | { 22 | 23 | /// RPC 请求函数的返回值类型 24 | template 25 | struct ResultPack 26 | { 27 | // 未解的原始消息,附带来源和发送时间等信息 28 | value::GeneralMessage general_message; 29 | // 反序列后的 general_message.payload,存储实际的 RPC 30 | PayloadType payload; 31 | }; 32 | 33 | /// RPC 服务客户端整合工具类,合并了 RpcWorker 和 RpcClient,方便使用 34 | class RemoteCaller 35 | { 36 | public: 37 | RemoteCaller(const std::shared_ptr &ctx) 38 | : ctx_(ctx), rpc_worker_(ctx_), rpc_client_(ctx_) 39 | { 40 | } 41 | 42 | void Debug() 43 | { 44 | debug_ = true; 45 | rpc_worker_.Debug(); 46 | rpc_client_.Debug(); 47 | } 48 | 49 | /// @brief 注册 RPC 服务 50 | /// @param ReceiveType 接收 RPC 请求后,参数解析的目标类型 51 | /// @param ResponseType RPC 请求处理函数的返回值类型 52 | /// @param service_name 服务名 53 | /// @param callback RPC 请求处理函数 54 | template 55 | void RegisterService( 56 | const std::string &service_name, 57 | const std::function &callback) 58 | { 59 | // 必须在链接前注册服务 60 | assert(!connected); 61 | 62 | // RPC 请求处理函数的解包封包包装 63 | auto unpack_wrapper = [=](const std::string &msg, const uint64_t &deadline) { 64 | assert(!msg.empty() && "收到的请求内容为空"); 65 | 66 | value::GeneralMessage general_message; 67 | bool parse_success = general_message.ParseFromString(msg); 68 | assert(parse_success && "收到的请求协议错误,无法解析"); 69 | 70 | // 解析 general_message 的 payload 部分到指定类型 71 | auto argv = UnmakeGeneralMessage(general_message); 72 | ResponseType result; 73 | if (debug_) 74 | { 75 | auto begin = mbus::Now(); 76 | result = callback(std::move(argv), std::move(general_message), deadline); 77 | auto end = Now(); 78 | auto time_consuming = end - begin; 79 | spdlog::info("Calling the function {} takes {} millisecond", service_name, time_consuming); 80 | } 81 | else 82 | { 83 | result = callback(std::move(argv), std::move(general_message), deadline); 84 | } 85 | // 封包 result 到 GeneralMessage,并序列化返回给客户端 86 | auto message_proto = MakeGeneralMessage(result); 87 | std::string message; 88 | message_proto.SerializeToString(&message); 89 | return message; 90 | }; 91 | 92 | rpc_worker_.RegisterService(service_name, unpack_wrapper); 93 | } 94 | 95 | /// @brief 链接 RPC 代理服务 96 | /// @param broker_addr zmq socket 的地址 97 | void Connect(const std::string &broker_addr) 98 | { 99 | // assert(ctx_ != nullptr); 100 | 101 | // rpc_worker_.Connect(broker_addr); 102 | // rpc_client_.Connect(broker_addr); 103 | 104 | // connected = true; 105 | WorkerConnect(broker_addr); 106 | ClientConnect(broker_addr); 107 | } 108 | 109 | void WorkerConnect(const std::string &broker_addr) 110 | { 111 | assert(ctx_ != nullptr); 112 | rpc_worker_.Connect(broker_addr); 113 | connected = true; 114 | } 115 | 116 | void ClientConnect(const std::string &broker_addr) 117 | { 118 | assert(ctx_ != nullptr); 119 | rpc_client_.Connect(broker_addr); 120 | } 121 | 122 | /// @brief worker端断开链接 123 | void Deconnect() 124 | { 125 | rpc_worker_.Deconnect(); 126 | connected = false; 127 | } 128 | 129 | /// @brief 发起同步阻塞的 RPC 请求 130 | /// @thread-safe 131 | template 132 | ResultPack SyncCall(const std::string &service_name, const ArgementType &argv, const int64_t &timeout = 0, const int &rerties = 0) 133 | { 134 | auto argv_proto = MakeGeneralMessage(argv); 135 | std::string message; 136 | argv_proto.SerializeToString(&message); 137 | std::unique_lock lock{rpc_client_mutex_}; 138 | auto response = rpc_client_.SyncCall(service_name, message, timeout, rerties); 139 | lock.unlock(); 140 | // assert(response.size() > 0); 141 | // std::cout << "responce size " << response.size() << std::endl; 142 | spdlog::info("response size is {}", response.size()); 143 | // std::cout << "responce " << response << std::endl; 144 | // spdlog::info("response is {}", response); 145 | ResultPack result; 146 | bool success = result.general_message.ParseFromString(response); 147 | assert(success && "服务端响应协议错误"); 148 | spdlog::info("the response is not timeout ? {}", result.general_message.not_timeout()); 149 | if (result.general_message.not_timeout()) 150 | { 151 | // 如果没有超时,则序列化结果 152 | result.payload = UnmakeGeneralMessage(result.general_message); 153 | } 154 | return result; 155 | } 156 | 157 | private: 158 | std::atomic connected{false}; 159 | std::shared_ptr ctx_{nullptr}; 160 | RpcWorker rpc_worker_; 161 | std::mutex rpc_client_mutex_{}; 162 | RpcClient rpc_client_; 163 | bool debug_{false}; 164 | }; 165 | 166 | } // namespace mbus 167 | -------------------------------------------------------------------------------- /rpc/worker.cpp: -------------------------------------------------------------------------------- 1 | #include "rpc/worker.h" 2 | #include "common/constants.h" 3 | #include "common/time.h" 4 | #include "common/uuid.h" 5 | #include "common/zmq_helper.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | namespace mbus 22 | { 23 | 24 | RpcWorker::RpcWorker(const std::shared_ptr &ctx) 25 | : ctx_(ctx), uuid_(UUID()) 26 | { 27 | assert(ctx_ != nullptr); 28 | } 29 | 30 | RpcWorker::~RpcWorker() 31 | { 32 | stop_ = true; 33 | if (worker_.joinable()) 34 | { 35 | worker_.join(); 36 | } 37 | if (handler_.joinable()) 38 | { 39 | handler_.join(); 40 | } 41 | } 42 | 43 | void RpcWorker::Debug() 44 | { 45 | debug_ = true; 46 | } 47 | 48 | /// 链接代理服务,多次调用会断开连接后重连 49 | void RpcWorker::Connect(const std::string &broker_addr) 50 | { 51 | assert(ctx_ != nullptr); 52 | assert(!broker_addr.empty()); 53 | 54 | if (debug_ && socket_ == nullptr) 55 | { 56 | auto str = UUID2String(uuid_); 57 | // std::cout << "当前进程的 rpc worker uuid: " << str << std::endl; 58 | spdlog::info("uuid of rpc worker of current process is {}", str); 59 | } 60 | 61 | socket_ = std::make_unique(*ctx_, zmq::socket_type::dealer); 62 | assert(socket_ != nullptr && "Out Of Memory!!!"); 63 | // 设置 socket 的标识符 64 | socket_->set(zmq::sockopt::routing_id, uuid_); 65 | // 设置 socket 关闭时丢弃所有未发送成功的消息 66 | // socket_->set(zmq::sockopt::linger, 0); 67 | 68 | broker_addr_ = broker_addr; 69 | socket_->connect(broker_addr_); 70 | if (debug_) 71 | { 72 | // std::cout << "连接 rpc 代理服务: " << broker_addr_ << std::endl; 73 | spdlog::info("connect rpc proxy service: {}", broker_addr_); 74 | } 75 | 76 | // 发送话题注册请求 77 | BufferPack pack; 78 | auto serial_num = std::to_string(Now()); 79 | pack.push_back(MakeZmqBuffer(serial_num)); 80 | pack.push_back(MakeZmqBuffer(RPC_WORKER)); 81 | pack.push_back(MakeZmqBuffer(RPC_REG)); 82 | std::unique_lock lock{services_mutex_}; 83 | for (const auto &item : services_) 84 | { 85 | pack.push_back(MakeZmqBuffer(item.first)); 86 | } 87 | lock.unlock(); 88 | 89 | if (debug_) 90 | { 91 | // std::cout << "发送服务注册请求: " << StringifyMessages(pack) << std::endl; 92 | spdlog::info("Send service registration request: {}", StringifyMessages(pack)); 93 | } 94 | SendAll(*socket_, pack, send_mx_); 95 | if (debug_) 96 | { 97 | // std::cout << "等待 broker 响应" << std::endl; 98 | spdlog::info("Wait for broker response"); 99 | } 100 | auto res = ReceiveAll(*socket_); 101 | 102 | if (debug_) 103 | { 104 | // std::cout << "broker 响应 " << StringifyMessages(res) << std::endl; 105 | spdlog::info("message of response of broker is {}", StringifyMessages(res)); 106 | } 107 | 108 | worker_ = std::thread{[&] { 109 | // std::cout << "worker io 线程启动" << std::endl; 110 | spdlog::info("thread of worker IO start up"); 111 | stop_ = false; 112 | handler_ = std::thread{[&] { 113 | spdlog::info("thread of handler start up"); 114 | HandleMessage(); 115 | }}; 116 | ReceiveLoop(); 117 | }}; 118 | } 119 | 120 | /// 重新连接上一次连接的代理服务 121 | void RpcWorker::Reconnect() 122 | { 123 | assert(ctx_ != nullptr); 124 | assert(!broker_addr_.empty()); 125 | 126 | Connect(broker_addr_); 127 | } 128 | 129 | /// 下线 130 | void RpcWorker::Deconnect() 131 | { 132 | // 发送断开链接请求 133 | BufferPack pack; 134 | 135 | auto serial_num = std::to_string(Now()); 136 | pack.push_back(MakeZmqBuffer(serial_num)); 137 | pack.push_back(MakeZmqBuffer(RPC_WORKER)); 138 | pack.push_back(MakeZmqBuffer(RPC_UNCONNECT)); 139 | std::unique_lock lock{services_mutex_}; 140 | for (const auto &item : services_) 141 | { 142 | pack.push_back(MakeZmqBuffer(item.first)); 143 | } 144 | lock.unlock(); 145 | 146 | if (debug_) 147 | { 148 | // std::cout << "发送断开链接请求: " << StringifyMessages(pack) << std::endl; 149 | spdlog::info("Send a disconnect request is :{}", StringifyMessages(pack)); 150 | } 151 | // fixme: 暴力中断,后期需要优化 152 | stop_ = true; 153 | SendAll(*socket_, pack, send_mx_); 154 | if (debug_) 155 | { 156 | // std::cout << "等待 broker 响应" << std::endl; 157 | spdlog::info("waitting response of broker"); 158 | } 159 | auto res = ReceiveAll(*socket_); 160 | 161 | if (debug_) 162 | { 163 | // std::cout << "broker 响应 " << StringifyMessages(res) << std::endl; 164 | spdlog::info("the content of response of broker is : {}", StringifyMessages(res)); 165 | } 166 | } 167 | 168 | /// 向代理端注册想要接收的服务请求 169 | /// @thread-safe 170 | void RpcWorker::RegisterService(const std::string &service_name, const std::function &callback) 171 | { 172 | std::lock_guard lock{services_mutex_}; 173 | services_[service_name] = callback; 174 | } 175 | 176 | /// 设置信号检测延迟 177 | void RpcWorker::SetHeartbeat(int32_t heartbeat) 178 | { 179 | heartbeat_ = heartbeat; 180 | } 181 | 182 | /// 设置重连延迟 183 | void RpcWorker::SetReconnectDelay(int32_t delay) 184 | { 185 | reconnect_delay_ = delay; 186 | } 187 | 188 | /// 响应请求,client_uuid 为目标客户端的标识符,body 为响应的内容 189 | bool RpcWorker::Response(const std::string &client_uuid, const std::string &serial_num, const std::string &service_name, const std::string &body) 190 | { 191 | assert(ctx_ != nullptr); 192 | assert(socket_ != nullptr); 193 | assert(!broker_addr_.empty()); 194 | 195 | std::vector messages; 196 | messages.reserve(6); 197 | messages.push_back(MakeZmqBuffer(serial_num)); 198 | messages.push_back(MakeZmqBuffer(RPC_WORKER)); 199 | messages.push_back(MakeZmqBuffer(RPC_RES)); 200 | messages.push_back(MakeZmqBuffer(client_uuid)); 201 | messages.push_back(MakeZmqBuffer(service_name)); 202 | messages.push_back(MakeZmqBuffer(body)); 203 | 204 | if (debug_) 205 | { 206 | // std::cout << "响应 RPC 请求,回复内容" << StringifyMessages(messages) << std::endl; 207 | spdlog::info("Response to RPC request of serial_num:{} and uuid is {}, content of reply is {}", serial_num, UUID2String(client_uuid), StringifyMessages(messages)); 208 | } 209 | return SendAll(*socket_, messages, send_mx_); 210 | } 211 | 212 | /// 发送消息到代理端 213 | void RpcWorker::SendToBroker(const std::string &header, const std::string &command, const std::string &body) 214 | { 215 | std::vector messages; 216 | messages.reserve(5); 217 | auto serial_num = std::to_string(Now()); 218 | messages.push_back(MakeZmqBuffer(serial_num)); 219 | messages.push_back(MakeZmqBuffer(RPC_WORKER)); 220 | messages.push_back(MakeZmqBuffer(header)); 221 | if (!command.empty()) 222 | { 223 | messages.push_back(MakeZmqBuffer(command)); 224 | } 225 | if (!body.empty()) 226 | { 227 | messages.push_back(MakeZmqBuffer(body)); 228 | } 229 | 230 | if (debug_) 231 | { 232 | // std::cout << "发送消息到代理端 " << StringifyMessages(messages) << std::endl; 233 | spdlog::info("send message to endpoint of proxy, message is {}", StringifyMessages(messages)); 234 | } 235 | 236 | SendAll(*socket_, messages, send_mx_); 237 | // TODO 错误处理 238 | } 239 | 240 | void RpcWorker::HandleMessage() 241 | { 242 | while (!stop_) 243 | { 244 | 245 | if (!messages_task_one_.empty()) 246 | { 247 | // std::this_thread::sleep_for(std::chrono::milliseconds(1)); 248 | // continue; 249 | std::unique_lock try_lock_one(mx_task_one_, std::try_to_lock); 250 | if (try_lock_one.owns_lock()) 251 | { 252 | spdlog::info("handle message in messages_task_one_ on HandleMessage"); 253 | auto messages = std::move(messages_task_one_.front()); 254 | messages_task_one_.pop_front(); 255 | try_lock_one.unlock(); 256 | 257 | auto req_uuid = std::move(messages.front()); 258 | messages.pop_front(); 259 | 260 | assert(req_uuid.size() == 16 && "客户端 uuid 错误"); 261 | 262 | auto serial_num = std::move(messages.front()); 263 | 264 | uint64_t serial_num_of_request = std::stol(serial_num.to_string()); 265 | 266 | if (serial_num_of_request < current_max_serial_num_of_request) 267 | { 268 | // 如果这条小于当前处理过的最大序列号消息,说明是历史已处理消息,不重复处理,直接跳过 269 | // 这里使用的是以时间戳作为序列号,后续换成其他形式的序列号需要保证单调递增 270 | spdlog::info("current_max_serial_num_of_request is {}", current_max_serial_num_of_request); 271 | spdlog::info("{} Is history request", serial_num_of_request); 272 | continue; 273 | } 274 | 275 | messages.pop_front(); 276 | 277 | auto req_actor = std::move(messages.front()); 278 | messages.pop_front(); 279 | assert(Equal(req_actor, RPC_CLIENT) && "客户端协议头错误"); 280 | 281 | auto req_header = std::move(messages.front()); 282 | messages.pop_front(); 283 | assert(Equal(req_header, RPC_REQ) && "客户端协议头错误"); 284 | 285 | auto req_service = std::move(messages.front()); 286 | messages.pop_front(); 287 | assert(req_service.size() != 0 && "客户端请求的服务名不能为空"); 288 | auto req_argument = std::move(messages.front()); 289 | messages.pop_front(); 290 | 291 | auto deadline_string = std::move(messages.front()); 292 | spdlog::info(deadline_string.to_string()); 293 | int64_t deadline = std::stol(deadline_string.to_string()); 294 | messages.pop_front(); 295 | 296 | auto service_name = req_service.to_string(); 297 | auto argumnet = req_argument.to_string(); 298 | std::lock_guard lock{services_mutex_}; 299 | auto iterator = services_.find(service_name); 300 | assert(iterator != services_.end() && "服务未注册"); 301 | 302 | std::string result = iterator->second(argumnet, deadline); 303 | if (Response(req_uuid.to_string(), serial_num.to_string(), service_name, result)) 304 | { 305 | // 处理并发送成功,并更新当前已处理最大序列号 306 | spdlog::info("current_max_serial_num_of_request is {} and recevied serial num of request is {}", current_max_serial_num_of_request, serial_num_of_request); 307 | current_max_serial_num_of_request = serial_num_of_request; 308 | spdlog::info("response success"); 309 | } 310 | } 311 | } 312 | if (!messages_task_two_.empty()) 313 | { 314 | std::unique_lock try_lock_two(mx_task_two_, std::try_to_lock); 315 | if (try_lock_two.owns_lock()) 316 | { 317 | spdlog::info("handle message in messages_task_two_ on HandleMessage"); 318 | auto messages = std::move(messages_task_two_.front()); 319 | messages_task_two_.pop_front(); 320 | try_lock_two.unlock(); 321 | 322 | auto req_uuid = std::move(messages.front()); 323 | messages.pop_front(); 324 | 325 | assert(req_uuid.size() == 16 && "客户端 uuid 错误"); 326 | 327 | auto serial_num = std::move(messages.front()); 328 | 329 | uint64_t serial_num_of_request = std::stol(serial_num.to_string()); 330 | 331 | if (serial_num_of_request < current_max_serial_num_of_request) 332 | { 333 | // 如果这条小于当前处理过的最大序列号消息,说明是历史已处理消息,不重复处理,直接跳过 334 | // 这里使用的是以时间戳作为序列号,后续换成其他形式的序列号需要保证单调递增 335 | spdlog::info("current_max_serial_num_of_request is {}", current_max_serial_num_of_request); 336 | spdlog::info("{} Is history request", serial_num_of_request); 337 | continue; 338 | } 339 | 340 | messages.pop_front(); 341 | 342 | auto req_actor = std::move(messages.front()); 343 | messages.pop_front(); 344 | assert(Equal(req_actor, RPC_CLIENT) && "客户端协议头错误"); 345 | 346 | auto req_header = std::move(messages.front()); 347 | messages.pop_front(); 348 | assert(Equal(req_header, RPC_REQ) && "客户端协议头错误"); 349 | 350 | auto req_service = std::move(messages.front()); 351 | messages.pop_front(); 352 | assert(req_service.size() != 0 && "客户端请求的服务名不能为空"); 353 | auto req_argument = std::move(messages.front()); 354 | messages.pop_front(); 355 | 356 | auto deadline_string = std::move(messages.front()); 357 | spdlog::info(deadline_string.to_string()); 358 | int64_t deadline = std::stol(deadline_string.to_string()); 359 | messages.pop_front(); 360 | 361 | auto service_name = req_service.to_string(); 362 | auto argumnet = req_argument.to_string(); 363 | std::lock_guard lock{services_mutex_}; 364 | auto iterator = services_.find(service_name); 365 | assert(iterator != services_.end() && "服务未注册"); 366 | 367 | std::string result = iterator->second(argumnet, deadline); 368 | if (Response(req_uuid.to_string(), serial_num.to_string(), service_name, result)) 369 | { 370 | // 处理并发送成功,并更新当前已处理最大序列号 371 | spdlog::info("current_max_serial_num_of_request is {} and recevied serial num of request is {}", current_max_serial_num_of_request, serial_num_of_request); 372 | current_max_serial_num_of_request = serial_num_of_request; 373 | spdlog::info("response success"); 374 | } 375 | } 376 | } 377 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 378 | } 379 | } 380 | 381 | void RpcWorker::ReceiveLoop() 382 | { 383 | assert(ctx_ != nullptr); 384 | assert(socket_ != nullptr); 385 | assert(!broker_addr_.empty()); 386 | assert(stop_ == false); 387 | 388 | while (!stop_) 389 | { 390 | if (WaitReadable(*socket_, heartbeat_)) 391 | { 392 | if (debug_) 393 | { 394 | spdlog::info("current_max_serial_num is {}", current_max_serial_num_of_request); 395 | // std::cout << "等待 rpc 请求..." << std::endl; 396 | spdlog::info("waitting request of rpc ..."); 397 | } 398 | 399 | auto messages = ReceiveAll(*socket_); 400 | assert(messages.size() >= 4); 401 | 402 | if (debug_) 403 | { 404 | // std::cout << "收到客户端的 rpc 请求:" << StringifyMessages(messages) << std::endl; 405 | spdlog::info("Received an rpc request from the client: {}", StringifyMessages(messages)); 406 | } 407 | 408 | 409 | while (true) 410 | { 411 | std::unique_lock try_lock_one(mx_task_one_, std::try_to_lock); 412 | if (try_lock_one.owns_lock()) 413 | { 414 | messages_task_one_.push_back(std::move(messages)); 415 | spdlog::info("push message to task list 1"); 416 | try_lock_one.unlock(); 417 | break; 418 | } 419 | 420 | std::unique_lock try_lock_two(mx_task_two_, std::try_to_lock); 421 | if (try_lock_two.owns_lock()) 422 | { 423 | messages_task_two_.push_back(std::move(messages)); 424 | spdlog::info("push message to task list 2"); 425 | try_lock_two.unlock(); 426 | break; 427 | } 428 | } 429 | 430 | // messages_task_one_.push_back(std::move(messages)); 431 | } 432 | // 定时发送心跳包到代理端 433 | if (Now() >= heartbeat_at_) 434 | { 435 | SendToBroker(RPC_HB, "", ""); 436 | heartbeat_at_ = Now() + heartbeat_; 437 | } 438 | } 439 | } 440 | 441 | } // namespace mbus -------------------------------------------------------------------------------- /rpc/worker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/macro_utility.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace mbus 18 | { 19 | 20 | /// rpc 服务提供端口,客户端请求的服务,最终都由 RpcWorker 实例处理 21 | class RpcWorker 22 | { 23 | public: 24 | DISABLE_COPY_AND_MOVE(RpcWorker); 25 | 26 | RpcWorker(const std::shared_ptr &ctx); 27 | ~RpcWorker(); 28 | 29 | void Debug(); 30 | 31 | /// 响应 rpc 请求的回调函数签名,第一个是请求携带的参数 32 | using ServiceCallback = std::string(const std::string &, const int64_t &); 33 | /// 向代理端注册想要接收的服务请求,需要在 Connect 之前调用 34 | void RegisterService(const std::string &service_name, const std::function &callback); 35 | 36 | /// 链接代理服务,多次调用会断开连接后重连 37 | void Connect(const std::string &broker_addr); 38 | /// 重新连接上一次连接的代理服务 39 | void Reconnect(); 40 | /// 下线 41 | void Deconnect(); 42 | 43 | /// 设置信号检测延迟 44 | void SetHeartbeat(int32_t heartbeat); 45 | 46 | /// 设置重连延迟 47 | void SetReconnectDelay(int32_t delay); 48 | 49 | private: 50 | /// 响应请求,client_uuid 为目标客户端的标识符,body 为响应的内容 51 | bool Response(const std::string &client_uuid, const std::string &serial_num, const std::string &service_name, const std::string &body); 52 | 53 | /// 发送消息到代理端 54 | void SendToBroker(const std::string &header, const std::string &command, const std::string &body); 55 | 56 | /// 循环监听任务 57 | void ReceiveLoop(); 58 | 59 | /// 消息处理 60 | void HandleMessage(); 61 | 62 | private: 63 | std::atomic stop_{true}; 64 | std::shared_ptr ctx_{}; 65 | /// 代理服务的地址 66 | std::string broker_addr_{}; 67 | /// 该 rpc worker 提供的服务名称 68 | std::mutex services_mutex_{}; 69 | std::unordered_map> services_{}; 70 | /// 该 rpc worker 的表示符号 71 | std::string uuid_{}; 72 | std::unique_ptr socket_{}; 73 | std::atomic debug_{}; 74 | 75 | /// 收到到所有消息的队列 76 | std::mutex mx_task_one_; 77 | std::deque> messages_task_one_{}; 78 | std::mutex mx_task_two_; 79 | std::deque> messages_task_two_{}; 80 | /// 执行注册函数后到返回结果 81 | uint64_t current_max_serial_num_of_request{0}; 82 | 83 | // 心跳包发送时间 84 | uint64_t heartbeat_at_{}; 85 | size_t liveness_{}; 86 | // 超时时间 87 | int32_t heartbeat_{2000}; 88 | int32_t reconnect_delay_{2000}; 89 | 90 | int32_t expect_reply{}; 91 | 92 | // 工作线程 93 | std::thread worker_{}; 94 | std::thread handler_{}; 95 | 96 | // 同步发送互斥量 97 | std::mutex send_mx_; 98 | }; 99 | 100 | } // namespace mbus -------------------------------------------------------------------------------- /server.cpp: -------------------------------------------------------------------------------- 1 | #include "common/zmq_helper.h" 2 | #include "general_message.pb.h" 3 | #include "message_bus/bus.h" 4 | #include "message_bus/client.h" 5 | #include "rpc/broker.h" 6 | #include "rpc/client.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | DEFINE_int32(pub_port, 12345, "客户端消息发布需要连接的端口"); 18 | DEFINE_int32(sub_port, 54321, "客户端消息订阅需要连接的端口"); 19 | 20 | int main(int argc, char *argv[]) 21 | { 22 | auto ctx = std::make_shared(3); 23 | gflags::ParseCommandLineFlags(&argc, &argv, true); 24 | // 主进程逻辑,创建消息总线服务 25 | mbus::Bus bus{ctx}; 26 | bus.Run("tcp://*", FLAGS_pub_port, FLAGS_sub_port); 27 | 28 | std::thread rpc_broker_worker{[&] { 29 | mbus::RpcBroker rpc_broker{ctx}; 30 | rpc_broker.Debug(); 31 | rpc_broker.Bind("tcp://*:5555"); 32 | rpc_broker.Run(); 33 | }}; 34 | 35 | pause(); 36 | 37 | rpc_broker_worker.join(); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${PROJECT_SOURCE_DIR}/cmake-utility/create_test.cmake) 2 | 3 | include_directories(${PROJECT_SOURCE_DIR}) 4 | 5 | link_libraries( 6 | ${PROJECT_NAME} 7 | mbus_rpc 8 | ) 9 | 10 | add_compile_options( 11 | -std=c++17 12 | ) 13 | 14 | 15 | create_test(test_bus_server test_bus_server.cpp) 16 | 17 | 18 | 19 | create_test( 20 | test_pub 21 | test_pub.cpp 22 | add_service.pb.cc 23 | ) 24 | 25 | 26 | 27 | create_test( 28 | test_sub 29 | test_sub.cpp 30 | add_service.pb.cc 31 | ) 32 | 33 | create_test( 34 | test_subt 35 | test_subt.cpp 36 | ) 37 | 38 | create_test( 39 | log_ 40 | log_.cpp 41 | ) 42 | 43 | 44 | create_test( 45 | test_async_repreq 46 | test_async_repreq.cpp 47 | ) 48 | 49 | 50 | create_test( 51 | test_rpc_worker 52 | test_rpc_worker.cpp 53 | add_service.pb.cc 54 | ) 55 | 56 | create_test( 57 | test_rpc_client 58 | test_rpc_client.cpp 59 | add_service.pb.cc 60 | ) 61 | -------------------------------------------------------------------------------- /tests/add_service.pb.cc: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: add_service.proto 3 | 4 | #define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION 5 | #include "add_service.pb.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | // @@protoc_insertion_point(includes) 19 | 20 | namespace value { 21 | class AddServiceDefaultTypeInternal { 22 | public: 23 | ::google::protobuf::internal::ExplicitlyConstructed 24 | _instance; 25 | } _AddService_default_instance_; 26 | class AnyDefaultTypeInternal { 27 | public: 28 | ::google::protobuf::internal::ExplicitlyConstructed 29 | _instance; 30 | } _Any_default_instance_; 31 | 32 | namespace protobuf_add_5fservice_2eproto { 33 | 34 | 35 | namespace { 36 | 37 | ::google::protobuf::Metadata file_level_metadata[2]; 38 | 39 | } // namespace 40 | 41 | PROTOBUF_CONSTEXPR_VAR ::google::protobuf::internal::ParseTableField 42 | const TableStruct::entries[] GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = { 43 | {0, 0, 0, ::google::protobuf::internal::kInvalidMask, 0, 0}, 44 | }; 45 | 46 | PROTOBUF_CONSTEXPR_VAR ::google::protobuf::internal::AuxillaryParseTableField 47 | const TableStruct::aux[] GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = { 48 | ::google::protobuf::internal::AuxillaryParseTableField(), 49 | }; 50 | PROTOBUF_CONSTEXPR_VAR ::google::protobuf::internal::ParseTable const 51 | TableStruct::schema[] GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = { 52 | { NULL, NULL, 0, -1, -1, -1, -1, NULL, false }, 53 | { NULL, NULL, 0, -1, -1, -1, -1, NULL, false }, 54 | }; 55 | 56 | const ::google::protobuf::uint32 TableStruct::offsets[] GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = { 57 | ~0u, // no _has_bits_ 58 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(AddService, _internal_metadata_), 59 | ~0u, // no _extensions_ 60 | ~0u, // no _oneof_case_ 61 | ~0u, // no _weak_field_map_ 62 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(AddService, number1_), 63 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(AddService, number2_), 64 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(AddService, result_), 65 | ~0u, // no _has_bits_ 66 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Any, _internal_metadata_), 67 | ~0u, // no _extensions_ 68 | ~0u, // no _oneof_case_ 69 | ~0u, // no _weak_field_map_ 70 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Any, data_), 71 | }; 72 | static const ::google::protobuf::internal::MigrationSchema schemas[] GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = { 73 | { 0, -1, sizeof(AddService)}, 74 | { 8, -1, sizeof(Any)}, 75 | }; 76 | 77 | static ::google::protobuf::Message const * const file_default_instances[] = { 78 | reinterpret_cast(&_AddService_default_instance_), 79 | reinterpret_cast(&_Any_default_instance_), 80 | }; 81 | 82 | namespace { 83 | 84 | void protobuf_AssignDescriptors() { 85 | AddDescriptors(); 86 | ::google::protobuf::MessageFactory* factory = NULL; 87 | AssignDescriptors( 88 | "add_service.proto", schemas, file_default_instances, TableStruct::offsets, factory, 89 | file_level_metadata, NULL, NULL); 90 | } 91 | 92 | void protobuf_AssignDescriptorsOnce() { 93 | static GOOGLE_PROTOBUF_DECLARE_ONCE(once); 94 | ::google::protobuf::GoogleOnceInit(&once, &protobuf_AssignDescriptors); 95 | } 96 | 97 | void protobuf_RegisterTypes(const ::std::string&) GOOGLE_ATTRIBUTE_COLD; 98 | void protobuf_RegisterTypes(const ::std::string&) { 99 | protobuf_AssignDescriptorsOnce(); 100 | ::google::protobuf::internal::RegisterAllTypes(file_level_metadata, 2); 101 | } 102 | 103 | } // namespace 104 | void TableStruct::InitDefaultsImpl() { 105 | GOOGLE_PROTOBUF_VERIFY_VERSION; 106 | 107 | ::google::protobuf::internal::InitProtobufDefaults(); 108 | _AddService_default_instance_._instance.DefaultConstruct(); 109 | ::google::protobuf::internal::OnShutdownDestroyMessage( 110 | &_AddService_default_instance_);_Any_default_instance_._instance.DefaultConstruct(); 111 | ::google::protobuf::internal::OnShutdownDestroyMessage( 112 | &_Any_default_instance_);} 113 | 114 | void InitDefaults() { 115 | static GOOGLE_PROTOBUF_DECLARE_ONCE(once); 116 | ::google::protobuf::GoogleOnceInit(&once, &TableStruct::InitDefaultsImpl); 117 | } 118 | namespace { 119 | void AddDescriptorsImpl() { 120 | InitDefaults(); 121 | static const char descriptor[] GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = { 122 | "\n\021add_service.proto\022\005value\">\n\nAddService" 123 | "\022\017\n\007number1\030\001 \001(\005\022\017\n\007number2\030\002 \001(\005\022\016\n\006re" 124 | "sult\030\003 \001(\005\"\023\n\003Any\022\014\n\004data\030\001 \001(\014b\006proto3" 125 | }; 126 | ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( 127 | descriptor, 119); 128 | ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( 129 | "add_service.proto", &protobuf_RegisterTypes); 130 | } 131 | } // anonymous namespace 132 | 133 | void AddDescriptors() { 134 | static GOOGLE_PROTOBUF_DECLARE_ONCE(once); 135 | ::google::protobuf::GoogleOnceInit(&once, &AddDescriptorsImpl); 136 | } 137 | // Force AddDescriptors() to be called at dynamic initialization time. 138 | struct StaticDescriptorInitializer { 139 | StaticDescriptorInitializer() { 140 | AddDescriptors(); 141 | } 142 | } static_descriptor_initializer; 143 | 144 | } // namespace protobuf_add_5fservice_2eproto 145 | 146 | 147 | // =================================================================== 148 | 149 | #if !defined(_MSC_VER) || _MSC_VER >= 1900 150 | const int AddService::kNumber1FieldNumber; 151 | const int AddService::kNumber2FieldNumber; 152 | const int AddService::kResultFieldNumber; 153 | #endif // !defined(_MSC_VER) || _MSC_VER >= 1900 154 | 155 | AddService::AddService() 156 | : ::google::protobuf::Message(), _internal_metadata_(NULL) { 157 | if (GOOGLE_PREDICT_TRUE(this != internal_default_instance())) { 158 | protobuf_add_5fservice_2eproto::InitDefaults(); 159 | } 160 | SharedCtor(); 161 | // @@protoc_insertion_point(constructor:value.AddService) 162 | } 163 | AddService::AddService(const AddService& from) 164 | : ::google::protobuf::Message(), 165 | _internal_metadata_(NULL), 166 | _cached_size_(0) { 167 | _internal_metadata_.MergeFrom(from._internal_metadata_); 168 | ::memcpy(&number1_, &from.number1_, 169 | static_cast(reinterpret_cast(&result_) - 170 | reinterpret_cast(&number1_)) + sizeof(result_)); 171 | // @@protoc_insertion_point(copy_constructor:value.AddService) 172 | } 173 | 174 | void AddService::SharedCtor() { 175 | ::memset(&number1_, 0, static_cast( 176 | reinterpret_cast(&result_) - 177 | reinterpret_cast(&number1_)) + sizeof(result_)); 178 | _cached_size_ = 0; 179 | } 180 | 181 | AddService::~AddService() { 182 | // @@protoc_insertion_point(destructor:value.AddService) 183 | SharedDtor(); 184 | } 185 | 186 | void AddService::SharedDtor() { 187 | } 188 | 189 | void AddService::SetCachedSize(int size) const { 190 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 191 | _cached_size_ = size; 192 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 193 | } 194 | const ::google::protobuf::Descriptor* AddService::descriptor() { 195 | protobuf_add_5fservice_2eproto::protobuf_AssignDescriptorsOnce(); 196 | return protobuf_add_5fservice_2eproto::file_level_metadata[kIndexInFileMessages].descriptor; 197 | } 198 | 199 | const AddService& AddService::default_instance() { 200 | protobuf_add_5fservice_2eproto::InitDefaults(); 201 | return *internal_default_instance(); 202 | } 203 | 204 | AddService* AddService::New(::google::protobuf::Arena* arena) const { 205 | AddService* n = new AddService; 206 | if (arena != NULL) { 207 | arena->Own(n); 208 | } 209 | return n; 210 | } 211 | 212 | void AddService::Clear() { 213 | // @@protoc_insertion_point(message_clear_start:value.AddService) 214 | ::google::protobuf::uint32 cached_has_bits = 0; 215 | // Prevent compiler warnings about cached_has_bits being unused 216 | (void) cached_has_bits; 217 | 218 | ::memset(&number1_, 0, static_cast( 219 | reinterpret_cast(&result_) - 220 | reinterpret_cast(&number1_)) + sizeof(result_)); 221 | _internal_metadata_.Clear(); 222 | } 223 | 224 | bool AddService::MergePartialFromCodedStream( 225 | ::google::protobuf::io::CodedInputStream* input) { 226 | #define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure 227 | ::google::protobuf::uint32 tag; 228 | // @@protoc_insertion_point(parse_start:value.AddService) 229 | for (;;) { 230 | ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); 231 | tag = p.first; 232 | if (!p.second) goto handle_unusual; 233 | switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { 234 | // int32 number1 = 1; 235 | case 1: { 236 | if (static_cast< ::google::protobuf::uint8>(tag) == 237 | static_cast< ::google::protobuf::uint8>(8u /* 8 & 0xFF */)) { 238 | 239 | DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< 240 | ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( 241 | input, &number1_))); 242 | } else { 243 | goto handle_unusual; 244 | } 245 | break; 246 | } 247 | 248 | // int32 number2 = 2; 249 | case 2: { 250 | if (static_cast< ::google::protobuf::uint8>(tag) == 251 | static_cast< ::google::protobuf::uint8>(16u /* 16 & 0xFF */)) { 252 | 253 | DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< 254 | ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( 255 | input, &number2_))); 256 | } else { 257 | goto handle_unusual; 258 | } 259 | break; 260 | } 261 | 262 | // int32 result = 3; 263 | case 3: { 264 | if (static_cast< ::google::protobuf::uint8>(tag) == 265 | static_cast< ::google::protobuf::uint8>(24u /* 24 & 0xFF */)) { 266 | 267 | DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< 268 | ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( 269 | input, &result_))); 270 | } else { 271 | goto handle_unusual; 272 | } 273 | break; 274 | } 275 | 276 | default: { 277 | handle_unusual: 278 | if (tag == 0) { 279 | goto success; 280 | } 281 | DO_(::google::protobuf::internal::WireFormat::SkipField( 282 | input, tag, _internal_metadata_.mutable_unknown_fields())); 283 | break; 284 | } 285 | } 286 | } 287 | success: 288 | // @@protoc_insertion_point(parse_success:value.AddService) 289 | return true; 290 | failure: 291 | // @@protoc_insertion_point(parse_failure:value.AddService) 292 | return false; 293 | #undef DO_ 294 | } 295 | 296 | void AddService::SerializeWithCachedSizes( 297 | ::google::protobuf::io::CodedOutputStream* output) const { 298 | // @@protoc_insertion_point(serialize_start:value.AddService) 299 | ::google::protobuf::uint32 cached_has_bits = 0; 300 | (void) cached_has_bits; 301 | 302 | // int32 number1 = 1; 303 | if (this->number1() != 0) { 304 | ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->number1(), output); 305 | } 306 | 307 | // int32 number2 = 2; 308 | if (this->number2() != 0) { 309 | ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->number2(), output); 310 | } 311 | 312 | // int32 result = 3; 313 | if (this->result() != 0) { 314 | ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->result(), output); 315 | } 316 | 317 | if ((_internal_metadata_.have_unknown_fields() && ::google::protobuf::internal::GetProto3PreserveUnknownsDefault())) { 318 | ::google::protobuf::internal::WireFormat::SerializeUnknownFields( 319 | (::google::protobuf::internal::GetProto3PreserveUnknownsDefault() ? _internal_metadata_.unknown_fields() : _internal_metadata_.default_instance()), output); 320 | } 321 | // @@protoc_insertion_point(serialize_end:value.AddService) 322 | } 323 | 324 | ::google::protobuf::uint8* AddService::InternalSerializeWithCachedSizesToArray( 325 | bool deterministic, ::google::protobuf::uint8* target) const { 326 | (void)deterministic; // Unused 327 | // @@protoc_insertion_point(serialize_to_array_start:value.AddService) 328 | ::google::protobuf::uint32 cached_has_bits = 0; 329 | (void) cached_has_bits; 330 | 331 | // int32 number1 = 1; 332 | if (this->number1() != 0) { 333 | target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(1, this->number1(), target); 334 | } 335 | 336 | // int32 number2 = 2; 337 | if (this->number2() != 0) { 338 | target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->number2(), target); 339 | } 340 | 341 | // int32 result = 3; 342 | if (this->result() != 0) { 343 | target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(3, this->result(), target); 344 | } 345 | 346 | if ((_internal_metadata_.have_unknown_fields() && ::google::protobuf::internal::GetProto3PreserveUnknownsDefault())) { 347 | target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( 348 | (::google::protobuf::internal::GetProto3PreserveUnknownsDefault() ? _internal_metadata_.unknown_fields() : _internal_metadata_.default_instance()), target); 349 | } 350 | // @@protoc_insertion_point(serialize_to_array_end:value.AddService) 351 | return target; 352 | } 353 | 354 | size_t AddService::ByteSizeLong() const { 355 | // @@protoc_insertion_point(message_byte_size_start:value.AddService) 356 | size_t total_size = 0; 357 | 358 | if ((_internal_metadata_.have_unknown_fields() && ::google::protobuf::internal::GetProto3PreserveUnknownsDefault())) { 359 | total_size += 360 | ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( 361 | (::google::protobuf::internal::GetProto3PreserveUnknownsDefault() ? _internal_metadata_.unknown_fields() : _internal_metadata_.default_instance())); 362 | } 363 | // int32 number1 = 1; 364 | if (this->number1() != 0) { 365 | total_size += 1 + 366 | ::google::protobuf::internal::WireFormatLite::Int32Size( 367 | this->number1()); 368 | } 369 | 370 | // int32 number2 = 2; 371 | if (this->number2() != 0) { 372 | total_size += 1 + 373 | ::google::protobuf::internal::WireFormatLite::Int32Size( 374 | this->number2()); 375 | } 376 | 377 | // int32 result = 3; 378 | if (this->result() != 0) { 379 | total_size += 1 + 380 | ::google::protobuf::internal::WireFormatLite::Int32Size( 381 | this->result()); 382 | } 383 | 384 | int cached_size = ::google::protobuf::internal::ToCachedSize(total_size); 385 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 386 | _cached_size_ = cached_size; 387 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 388 | return total_size; 389 | } 390 | 391 | void AddService::MergeFrom(const ::google::protobuf::Message& from) { 392 | // @@protoc_insertion_point(generalized_merge_from_start:value.AddService) 393 | GOOGLE_DCHECK_NE(&from, this); 394 | const AddService* source = 395 | ::google::protobuf::internal::DynamicCastToGenerated( 396 | &from); 397 | if (source == NULL) { 398 | // @@protoc_insertion_point(generalized_merge_from_cast_fail:value.AddService) 399 | ::google::protobuf::internal::ReflectionOps::Merge(from, this); 400 | } else { 401 | // @@protoc_insertion_point(generalized_merge_from_cast_success:value.AddService) 402 | MergeFrom(*source); 403 | } 404 | } 405 | 406 | void AddService::MergeFrom(const AddService& from) { 407 | // @@protoc_insertion_point(class_specific_merge_from_start:value.AddService) 408 | GOOGLE_DCHECK_NE(&from, this); 409 | _internal_metadata_.MergeFrom(from._internal_metadata_); 410 | ::google::protobuf::uint32 cached_has_bits = 0; 411 | (void) cached_has_bits; 412 | 413 | if (from.number1() != 0) { 414 | set_number1(from.number1()); 415 | } 416 | if (from.number2() != 0) { 417 | set_number2(from.number2()); 418 | } 419 | if (from.result() != 0) { 420 | set_result(from.result()); 421 | } 422 | } 423 | 424 | void AddService::CopyFrom(const ::google::protobuf::Message& from) { 425 | // @@protoc_insertion_point(generalized_copy_from_start:value.AddService) 426 | if (&from == this) return; 427 | Clear(); 428 | MergeFrom(from); 429 | } 430 | 431 | void AddService::CopyFrom(const AddService& from) { 432 | // @@protoc_insertion_point(class_specific_copy_from_start:value.AddService) 433 | if (&from == this) return; 434 | Clear(); 435 | MergeFrom(from); 436 | } 437 | 438 | bool AddService::IsInitialized() const { 439 | return true; 440 | } 441 | 442 | void AddService::Swap(AddService* other) { 443 | if (other == this) return; 444 | InternalSwap(other); 445 | } 446 | void AddService::InternalSwap(AddService* other) { 447 | using std::swap; 448 | swap(number1_, other->number1_); 449 | swap(number2_, other->number2_); 450 | swap(result_, other->result_); 451 | _internal_metadata_.Swap(&other->_internal_metadata_); 452 | swap(_cached_size_, other->_cached_size_); 453 | } 454 | 455 | ::google::protobuf::Metadata AddService::GetMetadata() const { 456 | protobuf_add_5fservice_2eproto::protobuf_AssignDescriptorsOnce(); 457 | return protobuf_add_5fservice_2eproto::file_level_metadata[kIndexInFileMessages]; 458 | } 459 | 460 | #if PROTOBUF_INLINE_NOT_IN_HEADERS 461 | // AddService 462 | 463 | // int32 number1 = 1; 464 | void AddService::clear_number1() { 465 | number1_ = 0; 466 | } 467 | ::google::protobuf::int32 AddService::number1() const { 468 | // @@protoc_insertion_point(field_get:value.AddService.number1) 469 | return number1_; 470 | } 471 | void AddService::set_number1(::google::protobuf::int32 value) { 472 | 473 | number1_ = value; 474 | // @@protoc_insertion_point(field_set:value.AddService.number1) 475 | } 476 | 477 | // int32 number2 = 2; 478 | void AddService::clear_number2() { 479 | number2_ = 0; 480 | } 481 | ::google::protobuf::int32 AddService::number2() const { 482 | // @@protoc_insertion_point(field_get:value.AddService.number2) 483 | return number2_; 484 | } 485 | void AddService::set_number2(::google::protobuf::int32 value) { 486 | 487 | number2_ = value; 488 | // @@protoc_insertion_point(field_set:value.AddService.number2) 489 | } 490 | 491 | // int32 result = 3; 492 | void AddService::clear_result() { 493 | result_ = 0; 494 | } 495 | ::google::protobuf::int32 AddService::result() const { 496 | // @@protoc_insertion_point(field_get:value.AddService.result) 497 | return result_; 498 | } 499 | void AddService::set_result(::google::protobuf::int32 value) { 500 | 501 | result_ = value; 502 | // @@protoc_insertion_point(field_set:value.AddService.result) 503 | } 504 | 505 | #endif // PROTOBUF_INLINE_NOT_IN_HEADERS 506 | 507 | // =================================================================== 508 | 509 | #if !defined(_MSC_VER) || _MSC_VER >= 1900 510 | const int Any::kDataFieldNumber; 511 | #endif // !defined(_MSC_VER) || _MSC_VER >= 1900 512 | 513 | Any::Any() 514 | : ::google::protobuf::Message(), _internal_metadata_(NULL) { 515 | if (GOOGLE_PREDICT_TRUE(this != internal_default_instance())) { 516 | protobuf_add_5fservice_2eproto::InitDefaults(); 517 | } 518 | SharedCtor(); 519 | // @@protoc_insertion_point(constructor:value.Any) 520 | } 521 | Any::Any(const Any& from) 522 | : ::google::protobuf::Message(), 523 | _internal_metadata_(NULL), 524 | _cached_size_(0) { 525 | _internal_metadata_.MergeFrom(from._internal_metadata_); 526 | data_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 527 | if (from.data().size() > 0) { 528 | data_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.data_); 529 | } 530 | // @@protoc_insertion_point(copy_constructor:value.Any) 531 | } 532 | 533 | void Any::SharedCtor() { 534 | data_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 535 | _cached_size_ = 0; 536 | } 537 | 538 | Any::~Any() { 539 | // @@protoc_insertion_point(destructor:value.Any) 540 | SharedDtor(); 541 | } 542 | 543 | void Any::SharedDtor() { 544 | data_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 545 | } 546 | 547 | void Any::SetCachedSize(int size) const { 548 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 549 | _cached_size_ = size; 550 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 551 | } 552 | const ::google::protobuf::Descriptor* Any::descriptor() { 553 | protobuf_add_5fservice_2eproto::protobuf_AssignDescriptorsOnce(); 554 | return protobuf_add_5fservice_2eproto::file_level_metadata[kIndexInFileMessages].descriptor; 555 | } 556 | 557 | const Any& Any::default_instance() { 558 | protobuf_add_5fservice_2eproto::InitDefaults(); 559 | return *internal_default_instance(); 560 | } 561 | 562 | Any* Any::New(::google::protobuf::Arena* arena) const { 563 | Any* n = new Any; 564 | if (arena != NULL) { 565 | arena->Own(n); 566 | } 567 | return n; 568 | } 569 | 570 | void Any::Clear() { 571 | // @@protoc_insertion_point(message_clear_start:value.Any) 572 | ::google::protobuf::uint32 cached_has_bits = 0; 573 | // Prevent compiler warnings about cached_has_bits being unused 574 | (void) cached_has_bits; 575 | 576 | data_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 577 | _internal_metadata_.Clear(); 578 | } 579 | 580 | bool Any::MergePartialFromCodedStream( 581 | ::google::protobuf::io::CodedInputStream* input) { 582 | #define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure 583 | ::google::protobuf::uint32 tag; 584 | // @@protoc_insertion_point(parse_start:value.Any) 585 | for (;;) { 586 | ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); 587 | tag = p.first; 588 | if (!p.second) goto handle_unusual; 589 | switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { 590 | // bytes data = 1; 591 | case 1: { 592 | if (static_cast< ::google::protobuf::uint8>(tag) == 593 | static_cast< ::google::protobuf::uint8>(10u /* 10 & 0xFF */)) { 594 | DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( 595 | input, this->mutable_data())); 596 | } else { 597 | goto handle_unusual; 598 | } 599 | break; 600 | } 601 | 602 | default: { 603 | handle_unusual: 604 | if (tag == 0) { 605 | goto success; 606 | } 607 | DO_(::google::protobuf::internal::WireFormat::SkipField( 608 | input, tag, _internal_metadata_.mutable_unknown_fields())); 609 | break; 610 | } 611 | } 612 | } 613 | success: 614 | // @@protoc_insertion_point(parse_success:value.Any) 615 | return true; 616 | failure: 617 | // @@protoc_insertion_point(parse_failure:value.Any) 618 | return false; 619 | #undef DO_ 620 | } 621 | 622 | void Any::SerializeWithCachedSizes( 623 | ::google::protobuf::io::CodedOutputStream* output) const { 624 | // @@protoc_insertion_point(serialize_start:value.Any) 625 | ::google::protobuf::uint32 cached_has_bits = 0; 626 | (void) cached_has_bits; 627 | 628 | // bytes data = 1; 629 | if (this->data().size() > 0) { 630 | ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( 631 | 1, this->data(), output); 632 | } 633 | 634 | if ((_internal_metadata_.have_unknown_fields() && ::google::protobuf::internal::GetProto3PreserveUnknownsDefault())) { 635 | ::google::protobuf::internal::WireFormat::SerializeUnknownFields( 636 | (::google::protobuf::internal::GetProto3PreserveUnknownsDefault() ? _internal_metadata_.unknown_fields() : _internal_metadata_.default_instance()), output); 637 | } 638 | // @@protoc_insertion_point(serialize_end:value.Any) 639 | } 640 | 641 | ::google::protobuf::uint8* Any::InternalSerializeWithCachedSizesToArray( 642 | bool deterministic, ::google::protobuf::uint8* target) const { 643 | (void)deterministic; // Unused 644 | // @@protoc_insertion_point(serialize_to_array_start:value.Any) 645 | ::google::protobuf::uint32 cached_has_bits = 0; 646 | (void) cached_has_bits; 647 | 648 | // bytes data = 1; 649 | if (this->data().size() > 0) { 650 | target = 651 | ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( 652 | 1, this->data(), target); 653 | } 654 | 655 | if ((_internal_metadata_.have_unknown_fields() && ::google::protobuf::internal::GetProto3PreserveUnknownsDefault())) { 656 | target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( 657 | (::google::protobuf::internal::GetProto3PreserveUnknownsDefault() ? _internal_metadata_.unknown_fields() : _internal_metadata_.default_instance()), target); 658 | } 659 | // @@protoc_insertion_point(serialize_to_array_end:value.Any) 660 | return target; 661 | } 662 | 663 | size_t Any::ByteSizeLong() const { 664 | // @@protoc_insertion_point(message_byte_size_start:value.Any) 665 | size_t total_size = 0; 666 | 667 | if ((_internal_metadata_.have_unknown_fields() && ::google::protobuf::internal::GetProto3PreserveUnknownsDefault())) { 668 | total_size += 669 | ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( 670 | (::google::protobuf::internal::GetProto3PreserveUnknownsDefault() ? _internal_metadata_.unknown_fields() : _internal_metadata_.default_instance())); 671 | } 672 | // bytes data = 1; 673 | if (this->data().size() > 0) { 674 | total_size += 1 + 675 | ::google::protobuf::internal::WireFormatLite::BytesSize( 676 | this->data()); 677 | } 678 | 679 | int cached_size = ::google::protobuf::internal::ToCachedSize(total_size); 680 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 681 | _cached_size_ = cached_size; 682 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 683 | return total_size; 684 | } 685 | 686 | void Any::MergeFrom(const ::google::protobuf::Message& from) { 687 | // @@protoc_insertion_point(generalized_merge_from_start:value.Any) 688 | GOOGLE_DCHECK_NE(&from, this); 689 | const Any* source = 690 | ::google::protobuf::internal::DynamicCastToGenerated( 691 | &from); 692 | if (source == NULL) { 693 | // @@protoc_insertion_point(generalized_merge_from_cast_fail:value.Any) 694 | ::google::protobuf::internal::ReflectionOps::Merge(from, this); 695 | } else { 696 | // @@protoc_insertion_point(generalized_merge_from_cast_success:value.Any) 697 | MergeFrom(*source); 698 | } 699 | } 700 | 701 | void Any::MergeFrom(const Any& from) { 702 | // @@protoc_insertion_point(class_specific_merge_from_start:value.Any) 703 | GOOGLE_DCHECK_NE(&from, this); 704 | _internal_metadata_.MergeFrom(from._internal_metadata_); 705 | ::google::protobuf::uint32 cached_has_bits = 0; 706 | (void) cached_has_bits; 707 | 708 | if (from.data().size() > 0) { 709 | 710 | data_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.data_); 711 | } 712 | } 713 | 714 | void Any::CopyFrom(const ::google::protobuf::Message& from) { 715 | // @@protoc_insertion_point(generalized_copy_from_start:value.Any) 716 | if (&from == this) return; 717 | Clear(); 718 | MergeFrom(from); 719 | } 720 | 721 | void Any::CopyFrom(const Any& from) { 722 | // @@protoc_insertion_point(class_specific_copy_from_start:value.Any) 723 | if (&from == this) return; 724 | Clear(); 725 | MergeFrom(from); 726 | } 727 | 728 | bool Any::IsInitialized() const { 729 | return true; 730 | } 731 | 732 | void Any::Swap(Any* other) { 733 | if (other == this) return; 734 | InternalSwap(other); 735 | } 736 | void Any::InternalSwap(Any* other) { 737 | using std::swap; 738 | data_.Swap(&other->data_); 739 | _internal_metadata_.Swap(&other->_internal_metadata_); 740 | swap(_cached_size_, other->_cached_size_); 741 | } 742 | 743 | ::google::protobuf::Metadata Any::GetMetadata() const { 744 | protobuf_add_5fservice_2eproto::protobuf_AssignDescriptorsOnce(); 745 | return protobuf_add_5fservice_2eproto::file_level_metadata[kIndexInFileMessages]; 746 | } 747 | 748 | #if PROTOBUF_INLINE_NOT_IN_HEADERS 749 | // Any 750 | 751 | // bytes data = 1; 752 | void Any::clear_data() { 753 | data_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 754 | } 755 | const ::std::string& Any::data() const { 756 | // @@protoc_insertion_point(field_get:value.Any.data) 757 | return data_.GetNoArena(); 758 | } 759 | void Any::set_data(const ::std::string& value) { 760 | 761 | data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); 762 | // @@protoc_insertion_point(field_set:value.Any.data) 763 | } 764 | #if LANG_CXX11 765 | void Any::set_data(::std::string&& value) { 766 | 767 | data_.SetNoArena( 768 | &::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); 769 | // @@protoc_insertion_point(field_set_rvalue:value.Any.data) 770 | } 771 | #endif 772 | void Any::set_data(const char* value) { 773 | GOOGLE_DCHECK(value != NULL); 774 | 775 | data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); 776 | // @@protoc_insertion_point(field_set_char:value.Any.data) 777 | } 778 | void Any::set_data(const void* value, size_t size) { 779 | 780 | data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), 781 | ::std::string(reinterpret_cast(value), size)); 782 | // @@protoc_insertion_point(field_set_pointer:value.Any.data) 783 | } 784 | ::std::string* Any::mutable_data() { 785 | 786 | // @@protoc_insertion_point(field_mutable:value.Any.data) 787 | return data_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 788 | } 789 | ::std::string* Any::release_data() { 790 | // @@protoc_insertion_point(field_release:value.Any.data) 791 | 792 | return data_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 793 | } 794 | void Any::set_allocated_data(::std::string* data) { 795 | if (data != NULL) { 796 | 797 | } else { 798 | 799 | } 800 | data_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), data); 801 | // @@protoc_insertion_point(field_set_allocated:value.Any.data) 802 | } 803 | 804 | #endif // PROTOBUF_INLINE_NOT_IN_HEADERS 805 | 806 | // @@protoc_insertion_point(namespace_scope) 807 | 808 | } // namespace value 809 | 810 | // @@protoc_insertion_point(global_scope) 811 | -------------------------------------------------------------------------------- /tests/add_service.pb.h: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: add_service.proto 3 | 4 | #ifndef PROTOBUF_add_5fservice_2eproto__INCLUDED 5 | #define PROTOBUF_add_5fservice_2eproto__INCLUDED 6 | 7 | #include 8 | 9 | #include 10 | 11 | #if GOOGLE_PROTOBUF_VERSION < 3004000 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 3004000 < GOOGLE_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 // IWYU pragma: export 30 | #include // IWYU pragma: export 31 | #include 32 | // @@protoc_insertion_point(includes) 33 | namespace value { 34 | class AddService; 35 | class AddServiceDefaultTypeInternal; 36 | extern AddServiceDefaultTypeInternal _AddService_default_instance_; 37 | class Any; 38 | class AnyDefaultTypeInternal; 39 | extern AnyDefaultTypeInternal _Any_default_instance_; 40 | } // namespace value 41 | 42 | namespace value { 43 | 44 | namespace protobuf_add_5fservice_2eproto { 45 | // Internal implementation detail -- do not call these. 46 | struct TableStruct { 47 | static const ::google::protobuf::internal::ParseTableField entries[]; 48 | static const ::google::protobuf::internal::AuxillaryParseTableField aux[]; 49 | static const ::google::protobuf::internal::ParseTable schema[]; 50 | static const ::google::protobuf::uint32 offsets[]; 51 | static const ::google::protobuf::internal::FieldMetadata field_metadata[]; 52 | static const ::google::protobuf::internal::SerializationTable serialization_table[]; 53 | static void InitDefaultsImpl(); 54 | }; 55 | void AddDescriptors(); 56 | void InitDefaults(); 57 | } // namespace protobuf_add_5fservice_2eproto 58 | 59 | // =================================================================== 60 | 61 | class AddService : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:value.AddService) */ { 62 | public: 63 | AddService(); 64 | virtual ~AddService(); 65 | 66 | AddService(const AddService& from); 67 | 68 | inline AddService& operator=(const AddService& from) { 69 | CopyFrom(from); 70 | return *this; 71 | } 72 | #if LANG_CXX11 73 | AddService(AddService&& from) noexcept 74 | : AddService() { 75 | *this = ::std::move(from); 76 | } 77 | 78 | inline AddService& operator=(AddService&& from) noexcept { 79 | if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { 80 | if (this != &from) InternalSwap(&from); 81 | } else { 82 | CopyFrom(from); 83 | } 84 | return *this; 85 | } 86 | #endif 87 | static const ::google::protobuf::Descriptor* descriptor(); 88 | static const AddService& default_instance(); 89 | 90 | static inline const AddService* internal_default_instance() { 91 | return reinterpret_cast( 92 | &_AddService_default_instance_); 93 | } 94 | static PROTOBUF_CONSTEXPR int const kIndexInFileMessages = 95 | 0; 96 | 97 | void Swap(AddService* other); 98 | friend void swap(AddService& a, AddService& b) { 99 | a.Swap(&b); 100 | } 101 | 102 | // implements Message ---------------------------------------------- 103 | 104 | inline AddService* New() const PROTOBUF_FINAL { return New(NULL); } 105 | 106 | AddService* New(::google::protobuf::Arena* arena) const PROTOBUF_FINAL; 107 | void CopyFrom(const ::google::protobuf::Message& from) PROTOBUF_FINAL; 108 | void MergeFrom(const ::google::protobuf::Message& from) PROTOBUF_FINAL; 109 | void CopyFrom(const AddService& from); 110 | void MergeFrom(const AddService& from); 111 | void Clear() PROTOBUF_FINAL; 112 | bool IsInitialized() const PROTOBUF_FINAL; 113 | 114 | size_t ByteSizeLong() const PROTOBUF_FINAL; 115 | bool MergePartialFromCodedStream( 116 | ::google::protobuf::io::CodedInputStream* input) PROTOBUF_FINAL; 117 | void SerializeWithCachedSizes( 118 | ::google::protobuf::io::CodedOutputStream* output) const PROTOBUF_FINAL; 119 | ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( 120 | bool deterministic, ::google::protobuf::uint8* target) const PROTOBUF_FINAL; 121 | int GetCachedSize() const PROTOBUF_FINAL { return _cached_size_; } 122 | private: 123 | void SharedCtor(); 124 | void SharedDtor(); 125 | void SetCachedSize(int size) const PROTOBUF_FINAL; 126 | void InternalSwap(AddService* other); 127 | private: 128 | inline ::google::protobuf::Arena* GetArenaNoVirtual() const { 129 | return NULL; 130 | } 131 | inline void* MaybeArenaPtr() const { 132 | return NULL; 133 | } 134 | public: 135 | 136 | ::google::protobuf::Metadata GetMetadata() const PROTOBUF_FINAL; 137 | 138 | // nested types ---------------------------------------------------- 139 | 140 | // accessors ------------------------------------------------------- 141 | 142 | // int32 number1 = 1; 143 | void clear_number1(); 144 | static const int kNumber1FieldNumber = 1; 145 | ::google::protobuf::int32 number1() const; 146 | void set_number1(::google::protobuf::int32 value); 147 | 148 | // int32 number2 = 2; 149 | void clear_number2(); 150 | static const int kNumber2FieldNumber = 2; 151 | ::google::protobuf::int32 number2() const; 152 | void set_number2(::google::protobuf::int32 value); 153 | 154 | // int32 result = 3; 155 | void clear_result(); 156 | static const int kResultFieldNumber = 3; 157 | ::google::protobuf::int32 result() const; 158 | void set_result(::google::protobuf::int32 value); 159 | 160 | // @@protoc_insertion_point(class_scope:value.AddService) 161 | private: 162 | 163 | ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; 164 | ::google::protobuf::int32 number1_; 165 | ::google::protobuf::int32 number2_; 166 | ::google::protobuf::int32 result_; 167 | mutable int _cached_size_; 168 | friend struct protobuf_add_5fservice_2eproto::TableStruct; 169 | }; 170 | // ------------------------------------------------------------------- 171 | 172 | class Any : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:value.Any) */ { 173 | public: 174 | Any(); 175 | virtual ~Any(); 176 | 177 | Any(const Any& from); 178 | 179 | inline Any& operator=(const Any& from) { 180 | CopyFrom(from); 181 | return *this; 182 | } 183 | #if LANG_CXX11 184 | Any(Any&& from) noexcept 185 | : Any() { 186 | *this = ::std::move(from); 187 | } 188 | 189 | inline Any& operator=(Any&& from) noexcept { 190 | if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { 191 | if (this != &from) InternalSwap(&from); 192 | } else { 193 | CopyFrom(from); 194 | } 195 | return *this; 196 | } 197 | #endif 198 | static const ::google::protobuf::Descriptor* descriptor(); 199 | static const Any& default_instance(); 200 | 201 | static inline const Any* internal_default_instance() { 202 | return reinterpret_cast( 203 | &_Any_default_instance_); 204 | } 205 | static PROTOBUF_CONSTEXPR int const kIndexInFileMessages = 206 | 1; 207 | 208 | void Swap(Any* other); 209 | friend void swap(Any& a, Any& b) { 210 | a.Swap(&b); 211 | } 212 | 213 | // implements Message ---------------------------------------------- 214 | 215 | inline Any* New() const PROTOBUF_FINAL { return New(NULL); } 216 | 217 | Any* New(::google::protobuf::Arena* arena) const PROTOBUF_FINAL; 218 | void CopyFrom(const ::google::protobuf::Message& from) PROTOBUF_FINAL; 219 | void MergeFrom(const ::google::protobuf::Message& from) PROTOBUF_FINAL; 220 | void CopyFrom(const Any& from); 221 | void MergeFrom(const Any& from); 222 | void Clear() PROTOBUF_FINAL; 223 | bool IsInitialized() const PROTOBUF_FINAL; 224 | 225 | size_t ByteSizeLong() const PROTOBUF_FINAL; 226 | bool MergePartialFromCodedStream( 227 | ::google::protobuf::io::CodedInputStream* input) PROTOBUF_FINAL; 228 | void SerializeWithCachedSizes( 229 | ::google::protobuf::io::CodedOutputStream* output) const PROTOBUF_FINAL; 230 | ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( 231 | bool deterministic, ::google::protobuf::uint8* target) const PROTOBUF_FINAL; 232 | int GetCachedSize() const PROTOBUF_FINAL { return _cached_size_; } 233 | private: 234 | void SharedCtor(); 235 | void SharedDtor(); 236 | void SetCachedSize(int size) const PROTOBUF_FINAL; 237 | void InternalSwap(Any* other); 238 | private: 239 | inline ::google::protobuf::Arena* GetArenaNoVirtual() const { 240 | return NULL; 241 | } 242 | inline void* MaybeArenaPtr() const { 243 | return NULL; 244 | } 245 | public: 246 | 247 | ::google::protobuf::Metadata GetMetadata() const PROTOBUF_FINAL; 248 | 249 | // nested types ---------------------------------------------------- 250 | 251 | // accessors ------------------------------------------------------- 252 | 253 | // bytes data = 1; 254 | void clear_data(); 255 | static const int kDataFieldNumber = 1; 256 | const ::std::string& data() const; 257 | void set_data(const ::std::string& value); 258 | #if LANG_CXX11 259 | void set_data(::std::string&& value); 260 | #endif 261 | void set_data(const char* value); 262 | void set_data(const void* value, size_t size); 263 | ::std::string* mutable_data(); 264 | ::std::string* release_data(); 265 | void set_allocated_data(::std::string* data); 266 | 267 | // @@protoc_insertion_point(class_scope:value.Any) 268 | private: 269 | 270 | ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; 271 | ::google::protobuf::internal::ArenaStringPtr data_; 272 | mutable int _cached_size_; 273 | friend struct protobuf_add_5fservice_2eproto::TableStruct; 274 | }; 275 | // =================================================================== 276 | 277 | 278 | // =================================================================== 279 | 280 | #if !PROTOBUF_INLINE_NOT_IN_HEADERS 281 | #ifdef __GNUC__ 282 | #pragma GCC diagnostic push 283 | #pragma GCC diagnostic ignored "-Wstrict-aliasing" 284 | #endif // __GNUC__ 285 | // AddService 286 | 287 | // int32 number1 = 1; 288 | inline void AddService::clear_number1() { 289 | number1_ = 0; 290 | } 291 | inline ::google::protobuf::int32 AddService::number1() const { 292 | // @@protoc_insertion_point(field_get:value.AddService.number1) 293 | return number1_; 294 | } 295 | inline void AddService::set_number1(::google::protobuf::int32 value) { 296 | 297 | number1_ = value; 298 | // @@protoc_insertion_point(field_set:value.AddService.number1) 299 | } 300 | 301 | // int32 number2 = 2; 302 | inline void AddService::clear_number2() { 303 | number2_ = 0; 304 | } 305 | inline ::google::protobuf::int32 AddService::number2() const { 306 | // @@protoc_insertion_point(field_get:value.AddService.number2) 307 | return number2_; 308 | } 309 | inline void AddService::set_number2(::google::protobuf::int32 value) { 310 | 311 | number2_ = value; 312 | // @@protoc_insertion_point(field_set:value.AddService.number2) 313 | } 314 | 315 | // int32 result = 3; 316 | inline void AddService::clear_result() { 317 | result_ = 0; 318 | } 319 | inline ::google::protobuf::int32 AddService::result() const { 320 | // @@protoc_insertion_point(field_get:value.AddService.result) 321 | return result_; 322 | } 323 | inline void AddService::set_result(::google::protobuf::int32 value) { 324 | 325 | result_ = value; 326 | // @@protoc_insertion_point(field_set:value.AddService.result) 327 | } 328 | 329 | // ------------------------------------------------------------------- 330 | 331 | // Any 332 | 333 | // bytes data = 1; 334 | inline void Any::clear_data() { 335 | data_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 336 | } 337 | inline const ::std::string& Any::data() const { 338 | // @@protoc_insertion_point(field_get:value.Any.data) 339 | return data_.GetNoArena(); 340 | } 341 | inline void Any::set_data(const ::std::string& value) { 342 | 343 | data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); 344 | // @@protoc_insertion_point(field_set:value.Any.data) 345 | } 346 | #if LANG_CXX11 347 | inline void Any::set_data(::std::string&& value) { 348 | 349 | data_.SetNoArena( 350 | &::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); 351 | // @@protoc_insertion_point(field_set_rvalue:value.Any.data) 352 | } 353 | #endif 354 | inline void Any::set_data(const char* value) { 355 | GOOGLE_DCHECK(value != NULL); 356 | 357 | data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); 358 | // @@protoc_insertion_point(field_set_char:value.Any.data) 359 | } 360 | inline void Any::set_data(const void* value, size_t size) { 361 | 362 | data_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), 363 | ::std::string(reinterpret_cast(value), size)); 364 | // @@protoc_insertion_point(field_set_pointer:value.Any.data) 365 | } 366 | inline ::std::string* Any::mutable_data() { 367 | 368 | // @@protoc_insertion_point(field_mutable:value.Any.data) 369 | return data_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 370 | } 371 | inline ::std::string* Any::release_data() { 372 | // @@protoc_insertion_point(field_release:value.Any.data) 373 | 374 | return data_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 375 | } 376 | inline void Any::set_allocated_data(::std::string* data) { 377 | if (data != NULL) { 378 | 379 | } else { 380 | 381 | } 382 | data_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), data); 383 | // @@protoc_insertion_point(field_set_allocated:value.Any.data) 384 | } 385 | 386 | #ifdef __GNUC__ 387 | #pragma GCC diagnostic pop 388 | #endif // __GNUC__ 389 | #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS 390 | // ------------------------------------------------------------------- 391 | 392 | 393 | // @@protoc_insertion_point(namespace_scope) 394 | 395 | 396 | } // namespace value 397 | 398 | // @@protoc_insertion_point(global_scope) 399 | 400 | #endif // PROTOBUF_add_5fservice_2eproto__INCLUDED 401 | -------------------------------------------------------------------------------- /tests/add_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package value; 3 | 4 | message AddService 5 | { 6 | int32 number1 = 1; 7 | int32 number2 = 2; 8 | 9 | int32 result = 3; 10 | } 11 | 12 | message Any 13 | { 14 | bytes data = 1; 15 | } -------------------------------------------------------------------------------- /tests/log_.cpp: -------------------------------------------------------------------------------- 1 | #include "general_message.pb.h" 2 | #include "message_bus/async_log.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main() 16 | { 17 | auto &log_entity = mbus::AsyncLog::GetInstance(); 18 | auto ctx = std::make_shared(1); 19 | auto log = std::make_unique(*ctx, zmq::socket_type::pair); 20 | log->connect("ipc://log"); 21 | 22 | value::GeneralMessage general_message_to_read; 23 | while (true) 24 | { 25 | // while (true) 26 | // { 27 | // std::this_thread::sleep_for(std::chrono::milliseconds(500)); 28 | // logEntity.ParseMessage(); 29 | // } 30 | std::vector message; 31 | do 32 | { 33 | zmq::message_t msg; 34 | // std::cout << "recv begin" << std::endl; 35 | auto result = log->recv(msg, zmq::recv_flags::none); 36 | // std::cout << "recv end" << std::endl; 37 | if (result.has_value()) 38 | { 39 | message.emplace_back(std::move(msg)); 40 | } 41 | } while (log->get(zmq::sockopt::rcvmore)); 42 | 43 | if (message.size() <= 1) 44 | { 45 | // TODO: 处理收到没有按话题发布或是超时之类的异常 46 | continue; 47 | } 48 | 49 | // 检查话题是否有注册回调函数 50 | auto &topic = message[0]; 51 | std::string_view topic_sv{ 52 | static_cast(topic.data()), topic.size()}; 53 | // 序列化回 proto 对象 54 | auto &msg = message[1]; 55 | value::GeneralMessage general_message; 56 | if (!msg.empty()) 57 | { 58 | general_message.ParseFromArray(msg.data(), msg.size()); 59 | } 60 | if (!std::filesystem::is_directory(topic_sv) || !std::filesystem::exists(topic_sv)) 61 | { 62 | auto rp = std::filesystem::create_directory(topic_sv); 63 | assert(rp); 64 | } 65 | std::cout << general_message.payload() << std::endl; 66 | 67 | log_entity.log(std::string{topic_sv}, general_message); 68 | } 69 | } -------------------------------------------------------------------------------- /tests/test_async_repreq.cpp: -------------------------------------------------------------------------------- 1 | #include "general_message.pb.h" 2 | #include "message_bus/bus.h" 3 | #include "message_bus/client.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | constexpr auto SERVER_CONNECT_ADDR = "tcp://localhost:5570"; 25 | constexpr auto SERVER_BIND_ADDR = "tcp://*:5570"; 26 | 27 | std::array UUID() 28 | { 29 | timeval t; 30 | gettimeofday(&t, nullptr); 31 | srandom(t.tv_sec + t.tv_usec + syscall(SYS_gettid)); 32 | std::array id; 33 | sprintf(id.data(), "%04lX-%04lX", random() % 0x10000, random() % 0x10000); 34 | return id; 35 | } 36 | 37 | /// 接收 zmq 的全部分块消息 38 | std::vector ReceiveAll(zmq::socket_t &socket) 39 | { 40 | std::vector message; 41 | do 42 | { 43 | zmq::message_t msg; 44 | auto result = socket.recv(msg, zmq::recv_flags::none); 45 | if (result.has_value()) 46 | { 47 | message.emplace_back(std::move(msg)); 48 | } 49 | } while (socket.get(zmq::sockopt::rcvmore)); 50 | 51 | return message; 52 | } 53 | 54 | void PrintMessage(const std::vector &msgs) 55 | { 56 | for (const auto &m : msgs) 57 | { 58 | std::cout << std::string_view{static_cast(m.data()), m.size()} 59 | << " | "; 60 | } 61 | std::cout << std::endl; 62 | } 63 | 64 | void Client() 65 | { 66 | auto id = UUID(); 67 | std::string_view id_sv{id.data()}; 68 | auto ctx = zmq::context_t{1}; 69 | auto client = zmq::socket_t{ctx, zmq::socket_type::dealer}; 70 | client.set(zmq::sockopt::routing_id, id.data()); 71 | client.connect(SERVER_CONNECT_ADDR); 72 | std::cout << std::string_view{id.data()} << std::endl; 73 | 74 | std::vector items; 75 | items.push_back({client.handle(), 0, ZMQ_POLLIN, 0}); 76 | 77 | size_t request_number = 0; 78 | while (true) 79 | { 80 | for (size_t tick = 0; tick < 100; tick++) 81 | { 82 | 83 | auto poll_result = zmq::poll(items, std::chrono::milliseconds(10)); 84 | assert(poll_result >= 0); 85 | if (poll_result == 0) 86 | { 87 | continue; 88 | } 89 | for (auto &item : items) 90 | { 91 | if (item.events & ZMQ_POLLIN) 92 | { 93 | auto msgs = ReceiveAll(client); 94 | std::cout << id_sv << " 收到服务端的回复: "; 95 | PrintMessage(msgs); 96 | } 97 | } 98 | } 99 | 100 | request_number++; 101 | std::string str{"request #"}; 102 | str += std::to_string(request_number); 103 | std::cout << "客户端发送:" << str << std::endl; 104 | client.send(zmq::message_t{str.data(), str.size()}, zmq::send_flags::none); 105 | } 106 | } 107 | 108 | void ServerWorker(std::string ipc_addr) 109 | { 110 | std::cout << "worker 启动" << std::endl; 111 | auto id = UUID(); 112 | std::string_view id_sv{id.data()}; 113 | auto ctx = zmq::context_t{1}; 114 | auto socket = zmq::socket_t{ctx, zmq::socket_type::dealer}; 115 | socket.connect(ipc_addr); 116 | // socket.connect("tcp://localhost:5555"); 117 | 118 | while (true) 119 | { 120 | std::cout << "worker 等待消息" << std::endl; 121 | auto message = ReceiveAll(socket); 122 | assert(message.size() >= 2); 123 | std::string_view client_uuid{message[0].data(), message[0].size()}; 124 | std::string_view client_message{message[1].data(), message[1].size()}; 125 | assert(!client_message.empty()); 126 | std::cout << "收到来自客户端的消息:"; 127 | PrintMessage(message); 128 | 129 | size_t replise = random() % 5; 130 | for (size_t reply = 0; reply < replise; reply++) 131 | { 132 | std::this_thread::sleep_for(std::chrono::milliseconds((random() % 1000) + 1)); 133 | zmq::const_buffer buffer{client_uuid.data(), client_uuid.size()}; 134 | socket.send(buffer, zmq::send_flags::sndmore); 135 | zmq::const_buffer content{client_message.data(), client_message.size()}; 136 | socket.send(content, zmq::send_flags::sndmore); 137 | socket.send(buffer); 138 | } 139 | } 140 | } 141 | 142 | void Server() 143 | { 144 | auto id = UUID(); 145 | std::string_view id_sv{id.data()}; 146 | auto ctx = zmq::context_t{1}; 147 | 148 | auto frontend = zmq::socket_t{ctx, zmq::socket_type::router}; 149 | frontend.bind(SERVER_BIND_ADDR); 150 | 151 | auto backend = zmq::socket_t{ctx, zmq::socket_type::dealer}; 152 | // 让系统自动分配端口号 153 | std::string backend_addr{"tcp://*:0"}; 154 | backend.bind(backend_addr); 155 | // 获取实际 bind 的地址 156 | backend_addr = backend.get(zmq::sockopt::last_endpoint); 157 | 158 | auto core_size = std::thread::hardware_concurrency(); 159 | // auto core_size = 1; 160 | std::vector worker; 161 | for (size_t i = 0; i < core_size; i++) 162 | { 163 | worker.emplace_back(ServerWorker, backend_addr); 164 | } 165 | std::cout << "proxy 启动" << std::endl; 166 | zmq::proxy(frontend, backend); 167 | for (auto &thr : worker) 168 | { 169 | if (thr.joinable()) 170 | { 171 | thr.join(); 172 | } 173 | } 174 | } 175 | 176 | int main(int argc, char *argv[]) 177 | { 178 | std::thread client1{Client}; 179 | std::thread client2{Client}; 180 | std::thread client3{Client}; 181 | std::thread server{Server}; 182 | 183 | while (true) 184 | { 185 | std::this_thread::sleep_for(std::chrono::seconds(1)); 186 | } 187 | return 0; 188 | } 189 | -------------------------------------------------------------------------------- /tests/test_bus_server.cpp: -------------------------------------------------------------------------------- 1 | #include "message_bus/bus.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int, const char **) 8 | { 9 | auto ctx = std::make_shared(3); 10 | auto bus = mbus::Bus{ctx}; 11 | bus.Run("tcp://*", 1234, 4321); 12 | 13 | while (true) 14 | { 15 | std::this_thread::sleep_for(std::chrono::seconds{1}); 16 | } 17 | return 0; 18 | } -------------------------------------------------------------------------------- /tests/test_pub.cpp: -------------------------------------------------------------------------------- 1 | #include "mbus/client.h" 2 | #include "mbus/message_dealer.h" 3 | #include "tests/add_service.pb.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void sengMsg(mbus::Client &client, int count, int pid, std::string topic, std::string msg) 11 | { 12 | msg += std::to_string(count); 13 | msg += ":"; 14 | msg += std::to_string(pid); 15 | // 发送消息到话题 /hello,目前支持直接传参任意字符串类型和 proto 对象实例 16 | client.Publish(topic, msg); 17 | std::cout << "发送消息" << count << std::endl; 18 | } 19 | 20 | int main(int argc, const char **args) 21 | { 22 | auto ctx = std::make_shared(1); 23 | mbus::MessageDealer dealer{ctx}; 24 | dealer.Connect("tcp://localhost:54321", "tcp://localhost:12345"); 25 | 26 | while (true) 27 | { 28 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 29 | // 随机向话题 /hello 或 /world 发布消息 30 | if ((random() % 100) >= 50) 31 | { 32 | dealer.Publish("/hello", "hello"); 33 | std::cout << "发布 hello 到 /hello" << std::endl; 34 | } 35 | else 36 | { 37 | value::Any world; 38 | world.set_data("world"); 39 | dealer.Publish("/world", world); 40 | std::cout << "发布 world 到 /world" << std::endl; 41 | } 42 | } 43 | 44 | // auto ctx = std::make_shared(1); 45 | // auto client = mbus::Client{ctx}; 46 | // // 连接消息总线服务 47 | // if (argc >= 2 && std::string{args[1]} == std::string{"server"}) 48 | // { 49 | // std::cout << "连接服务器" << std::endl; 50 | // client.Connect("tcp://localhost:4321", "tcp://localhost:1234"); 51 | // } 52 | // else 53 | // { 54 | // client.ConnectOnLocalMode(4321, 1234); 55 | // } 56 | // size_t count = 0; 57 | // auto pid = getpid(); 58 | // while (count < 100000) 59 | // { 60 | // count++; 61 | 62 | // sengMsg(client, count, pid, "a", "hello a"); 63 | // sengMsg(client, count, pid, "b", "hello b"); 64 | // sengMsg(client, count, pid, "c", "hello c"); 65 | // sengMsg(client, count, pid, "d", "hello d"); 66 | // sengMsg(client, count, pid, "e", "hello e"); 67 | // sengMsg(client, count, pid, "f", "hello f"); 68 | 69 | // std::this_thread::sleep_for(std::chrono::seconds(1)); 70 | // } 71 | } 72 | -------------------------------------------------------------------------------- /tests/test_rpc_client.cpp: -------------------------------------------------------------------------------- 1 | #include "rpc/remote_caller.h" 2 | #include "tests/add_service.pb.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | void add(mbus::RemoteCaller &caller, const std::string name, int32_t i) 15 | { 16 | 17 | while (true) 18 | { 19 | value::AddService add; 20 | add.set_number1(i); 21 | add.set_number2(i); 22 | 23 | // std::cout << name << " RPC 请求计算 " << i << " + " << i << std::endl; 24 | spdlog::warn("{} 请求计算 {} + {}", name, i, i); 25 | 26 | // 第一个模板参数是发送的 rpc 请求参数的类型,第二格模板参数是 rpc 请求响应的类型 27 | auto result = caller.SyncCall("add", add, 200000, 10); 28 | 29 | // std::cout << name << " 计算结果 " << result.payload.result() << std::endl 30 | // << std::endl; 31 | spdlog::warn("{} 的计算结果是 {}", name, result.payload.result()); 32 | i++; 33 | // sleep(1); 34 | } 35 | } 36 | 37 | int main(int argc, const char **args) 38 | { 39 | auto ctx = std::make_shared(1); 40 | mbus::RemoteCaller caller{ctx}; 41 | caller.Debug(); 42 | caller.ClientConnect("tcp://localhost:5555"); 43 | std::thread a{[&] { 44 | add(caller, "a", 100); 45 | }}; 46 | std::thread b{[&] { 47 | add(caller, "b", 200); 48 | }}; 49 | std::thread c{[&] { 50 | add(caller, "c", 300); 51 | }}; 52 | std::thread d{[&] { 53 | add(caller, "d", 400); 54 | }}; 55 | std::thread e{[&] { 56 | add(caller, "e", 500); 57 | }}; 58 | pause(); 59 | 60 | // mbus::RpcClient client{ctx}; 61 | // client.Debug(); 62 | // client.Connect("tcp://localhost:5555"); 63 | 64 | // auto result = client.SyncCall("echo", "sbsb"); 65 | // std::cout << result << std::endl; 66 | // result = client.SyncCall("echo2", "sbsb"); 67 | // std::cout << result << std::endl; 68 | } -------------------------------------------------------------------------------- /tests/test_rpc_worker.cpp: -------------------------------------------------------------------------------- 1 | #include "common/time.h" 2 | #include "common/zmq_helper.h" 3 | #include "rpc/client.h" 4 | #include "rpc/remote_caller.h" 5 | #include "rpc/worker.h" 6 | #include "tests/add_service.pb.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | /** 18 | * rpc worker 实现实例,实现接收两个数值,返回给 client 相加后的结果 19 | */ 20 | 21 | int main() 22 | { 23 | auto ctx = std::make_shared(1); 24 | mbus::RemoteCaller caller{ctx}; 25 | 26 | caller.Debug(); 27 | 28 | auto add_service = [](value::AddService argv, value::GeneralMessage general_message, const uint64_t &deadline) { 29 | spdlog::info("number1 is {} and number2 is {}", argv.number1(), argv.number2()); 30 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 31 | argv.set_result(argv.number1() + argv.number2()); 32 | spdlog::info("deadline is {}", deadline); 33 | if (deadline < mbus::Now()) 34 | { 35 | spdlog::info("already timeout"); 36 | } 37 | return argv; 38 | }; 39 | // 第一个模板参数是接收的 rpc 请求的参数的类型,第二格模板参数是响应 rpc 请求的类型 40 | caller.RegisterService("add", add_service); 41 | 42 | caller.WorkerConnect("tcp://localhost:5555"); 43 | pause(); 44 | std::this_thread::sleep_for(std::chrono::milliseconds(10000)); 45 | caller.Deconnect(); 46 | std::cout << "下线" << std::endl; 47 | std::this_thread::sleep_for(std::chrono::milliseconds(2000)); 48 | // pause(); 49 | 50 | // mbus::RpcWorker worker{ctx}; 51 | 52 | // worker.Debug(); 53 | 54 | // worker.RegisterService("echo", [](const std::string &res) -> std::string { 55 | // std::cout << "echo -> " << res << std::endl; 56 | // return res; 57 | // }); 58 | 59 | // worker.RegisterService("echo2", [](const std::string &res) -> std::string { 60 | // auto echo = res + res; 61 | // std::cout << "echo -> " << echo << std::endl; 62 | // return echo; 63 | // }); 64 | 65 | // worker.Connect("tcp://localhost:5555"); 66 | 67 | // while (true) 68 | // { 69 | // sleep(1); 70 | // } 71 | } -------------------------------------------------------------------------------- /tests/test_sub.cpp: -------------------------------------------------------------------------------- 1 | #include "general_message.pb.h" 2 | #include "mbus/client.h" 3 | #include "mbus/message_dealer.h" 4 | #include "tests/add_service.pb.h" 5 | 6 | #include 7 | #include 8 | 9 | void PrintMessage(value::GeneralMessage general_message) 10 | { 11 | std::cout << "收到了:" << general_message.payload() << std::endl; 12 | } 13 | 14 | int main(int argc, const char **args) 15 | { 16 | auto ctx = std::make_shared(1); 17 | mbus::MessageDealer dealer{ctx}; 18 | dealer.Connect("tcp://localhost:54321", "tcp://localhost:12345"); 19 | 20 | dealer.AddTopicListener( 21 | "/hello", 22 | [](std::string msg, value::GeneralMessage general_message) { 23 | std::cout << msg << std::endl; 24 | }); 25 | 26 | dealer.AddTopicListener( 27 | "/world", 28 | [](value::Any msg, value::GeneralMessage general_message) { 29 | std::cout << msg.data() << std::endl; 30 | }); 31 | 32 | pause(); 33 | 34 | // // 创建一个消息总线客户端 35 | // auto client = mbus::Client{ctx}; 36 | // // 连接消息总线服务 37 | // if (argc >= 2 && std::string{args[1]} == std::string{"server"}) 38 | // { 39 | // std::cout << "连接服务器" << std::endl; 40 | // client.Connect("tcp://localhost:4321", "tcp://localhost:1234"); 41 | // } 42 | // else 43 | // { 44 | // client.ConnectOnLocalMode(4321, 1234); 45 | // } 46 | 47 | // // 订阅话题 /hello 48 | // // 当收到新消息后会在后台线程自动执行注册的 PrintMessage 函数 49 | // client.Subscribe("hello", PrintMessage); 50 | 51 | // // 干别的事 52 | // while (true) 53 | // { 54 | // std::this_thread::sleep_for(std::chrono::seconds(1)); 55 | // } 56 | } -------------------------------------------------------------------------------- /tests/test_subt.cpp: -------------------------------------------------------------------------------- 1 | #include "message_bus/client.h" 2 | 3 | #include 4 | 5 | int times = 0; 6 | void PrintMessage(value::GeneralMessage general_message) 7 | { 8 | if (times < 100) 9 | { 10 | std::cout << "收到了:" << general_message.payload() << std::endl; 11 | times++; 12 | } 13 | } 14 | 15 | int main(int argc, const char **args) 16 | { 17 | auto ctx = std::make_shared(1); 18 | // 创建一个消息总线客户端 19 | auto client = mbus::Client{ctx}; 20 | // 连接消息总线服务 21 | if (argc >= 2 && std::string{args[1]} == std::string{"server"}) 22 | { 23 | std::cout << "连接服务器" << std::endl; 24 | client.Connect("tcp://localhost:4321", "tcp://localhost:1234"); 25 | } 26 | else 27 | { 28 | client.ConnectOnLocalMode(4321, 1234); 29 | } 30 | 31 | // 订阅话题 /hello 32 | // 当收到新消息后会在后台线程自动执行注册的 PrintMessage 函数 33 | client.Subscribe("a", PrintMessage); 34 | client.Subscribe("b", PrintMessage); 35 | client.Subscribe("c", PrintMessage); 36 | client.Subscribe("d", PrintMessage); 37 | client.Subscribe("e", PrintMessage); 38 | client.Subscribe("f", PrintMessage); 39 | 40 | // 干别的事 41 | while (true) 42 | { 43 | std::this_thread::sleep_for(std::chrono::seconds(1)); 44 | } 45 | } --------------------------------------------------------------------------------