pax_global_header00006660000000000000000000000064145450225140014514gustar00rootroot0000000000000052 comment=dfecdae207ecd73b187db4d2b2db7e00d53a09d3 srpc-0.10.1/000077500000000000000000000000001454502251400125425ustar00rootroot00000000000000srpc-0.10.1/.bazelignore000066400000000000000000000001271454502251400150440ustar00rootroot00000000000000./third_party/snappy/third_party/benchmark ./third_party/snappy/third_party/googletest srpc-0.10.1/.editorconfig000066400000000000000000000001361454502251400152170ustar00rootroot00000000000000# top-most EditorConfig file root = true # all files [*] indent_style = tab indent_size = 4 srpc-0.10.1/.github/000077500000000000000000000000001454502251400141025ustar00rootroot00000000000000srpc-0.10.1/.github/workflows/000077500000000000000000000000001454502251400161375ustar00rootroot00000000000000srpc-0.10.1/.github/workflows/ci.yml000066400000000000000000000035451454502251400172640ustar00rootroot00000000000000name: ci build on: push: branches: [ master ] pull_request: branches: [ master ] jobs: ubuntu-cmake: name: ubuntu runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: install deps run: | sudo apt-get update sudo apt-get install -y libprotobuf-dev protobuf-compiler libgtest-dev valgrind - name: update submodules run: git submodule update --init --recursive - name: make run: make -j4 - name: make tutorial run: make tutorial -j4 - name: make check run: make check -j4 - name: make install run: sudo make install fedora-cmake: name: fedora runs-on: ubuntu-latest steps: - name: Setup Podman run: | sudo apt update sudo apt-get -y install podman podman pull fedora:rawhide - name: Get source uses: actions/checkout@master with: path: 'workflow' - name: Create container and run tests run: | { echo 'FROM fedora:rawhide' echo 'RUN dnf -y update' echo 'RUN dnf -y install cmake gcc-c++ gtest-devel git make' echo 'RUN dnf -y install openssl-devel protobuf-devel' echo 'RUN dnf -y install lz4-devel snappy-devel' echo 'RUN dnf clean all' echo 'COPY workflow workflow' echo 'WORKDIR /workflow' echo 'RUN git submodule update --init --recursive' echo 'RUN cmake' echo 'RUN make' echo 'RUN make check' echo 'RUN make tutorial' } > podmanfile podman build --tag fedorarawhide -f ./podmanfile ubuntu-bazel: name: bazel runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: bazel build run: bazel build ... srpc-0.10.1/.gitignore000066400000000000000000000005431454502251400145340ustar00rootroot00000000000000*.a *.bak *.gz *.zip *.tar *.la *.lo *.o *.rpm *.so *.so.* *.cmake *.vcxproj *.filters *.sln *.pb.h *.pb.cc *.log *.srpc.h *.thrift.h *.pb_skeleton.h *.pb_skeleton.cc *.thrift_skeleton.h *.thrift_skeleton.cc _bin/ _include/ _lib/ .deps/ build/ build_pkg/ CMakeFiles/ Debug/ Release/ missing SRCINFO SRCNUMVER SRCVERSION CMakeCache.txt Makefile bazel-* srpc-0.10.1/.gitmodules000066400000000000000000000004621454502251400147210ustar00rootroot00000000000000[submodule "workflow"] path = workflow url = https://github.com/sogou/workflow.git [submodule "third_party/snappy"] path = third_party/snappy url = https://github.com/google/snappy branch = 1.1.9 [submodule "third_party/lz4"] path = third_party/lz4 url = https://github.com/lz4/lz4 branch = v1.9.3 srpc-0.10.1/BUILD000066400000000000000000000162331454502251400133310ustar00rootroot00000000000000load("@rules_cc//cc:defs.bzl", "cc_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") load(":srpc.bzl", "srpc_cc_library") proto_library( name = "message_proto", srcs = [ "src/message/rpc_meta.proto", "src/message/rpc_meta_brpc.proto", "src/message/rpc_meta_trpc.proto", ], strip_import_prefix = "src/message", ) cc_proto_library( name = "MessageProto", deps = [":message_proto"], ) proto_library( name = "module_proto", srcs = [ "src/module/proto/opentelemetry_common.proto", "src/module/proto/opentelemetry_resource.proto", "src/module/proto/opentelemetry_trace.proto", "src/module/proto/opentelemetry_metrics.proto", "src/module/proto/opentelemetry_metrics_service.proto", ], strip_import_prefix = "src/module/proto", ) cc_proto_library( name = "ModuleProto", deps = [":module_proto"], ) cc_library( name = "srpc_hdrs", hdrs = glob(["src/include/srpc/*"]), includes = ["src/include"], visibility = ["//visibility:public"], deps = [ "@workflow//:workflow_hdrs", ], ) cc_library( name = "srpc", srcs = glob(["src/**/*.cc"]), hdrs = glob([ "src/**/*.h", "src/**/*.inl", ]), includes = [ "src", "src/compress", "src/message", "src/module", "src/thrift", "src/var", ], visibility = ["//visibility:public"], deps = [ ":MessageProto", ":ModuleProto", "@lz4", "@snappy", "@workflow//:http", "@workflow//:redis", "@workflow//:upstream", ], ) cc_library( name = "srpc_generator_lib", srcs = glob( [ "src/generator/*.cc", ], exclude = [ "src/compiler.cc", ], ), hdrs = glob([ "src/generator/*.h", ]), includes = ["src/generator"], visibility = ["//visibility:public"], deps = [ ":srpc", ], ) cc_binary( name = "srpc_generator", srcs = ["src/generator/compiler.cc"], visibility = ["//visibility:public"], deps = [ ":srpc", ":srpc_generator_lib", ], ) proto_library( name = "echo_pb_proto", srcs = [ "tutorial/echo_pb.proto", ], strip_import_prefix = "tutorial", ) cc_proto_library( name = "EchoProto", deps = [":echo_pb_proto"], ) srpc_cc_library( name = "echo_pb_srpc", srcs = ["tutorial/echo_pb.proto"], deps = [":EchoProto"], ) cc_binary( name = "srpc_pb_server", srcs = ["tutorial/tutorial-01-srpc_pb_server.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_pb_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "srpc_pb_client", srcs = ["tutorial/tutorial-02-srpc_pb_client.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_pb_srpc", ":srpc", ":srpc_hdrs", ], ) srpc_cc_library( name = "echo_thrift_srpc", srcs = ["tutorial/echo_thrift.thrift"], type = "thrift", ) cc_binary( name = "srpc_thrift_server", srcs = ["tutorial/tutorial-03-srpc_thrift_server.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_thrift_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "srpc_thrift_client", srcs = ["tutorial/tutorial-04-srpc_thrift_client.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_thrift_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "brpc_pb_server", srcs = ["tutorial/tutorial-05-brpc_pb_server.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_pb_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "brpc_pb_client", srcs = ["tutorial/tutorial-06-brpc_pb_client.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_pb_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "thrift_thrift_server", srcs = ["tutorial/tutorial-07-thrift_thrift_server.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_thrift_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "thrift_thrift_client", srcs = ["tutorial/tutorial-08-thrift_thrift_client.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_thrift_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "client_task", srcs = ["tutorial/tutorial-09-client_task.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_pb_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "server_async", srcs = ["tutorial/tutorial-10-server_async.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_pb_srpc", ":srpc", ":srpc_hdrs", ], ) proto_library( name = "helloworld_proto", srcs = [ "tutorial/helloworld.proto", ], strip_import_prefix = "tutorial", ) cc_proto_library( name = "HelloworldProto", deps = [":helloworld_proto"], ) srpc_cc_library( name = "helloworld_pb_srpc", srcs = ["tutorial/helloworld.proto"], deps = [":HelloworldProto"], ) cc_binary( name = "trpc_pb_server", srcs = ["tutorial/tutorial-11-trpc_pb_server.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":helloworld_pb_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "trpc_pb_client", srcs = ["tutorial/tutorial-12-trpc_pb_client.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":helloworld_pb_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "trpc_http_server", srcs = ["tutorial/tutorial-13-trpc_http_server.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":helloworld_pb_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "trpc_http_client", srcs = ["tutorial/tutorial-14-trpc_http_client.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":helloworld_pb_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "srpc_pb_proxy", srcs = ["tutorial/tutorial-15-srpc_pb_proxy.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_pb_srpc", ":srpc", ":srpc_hdrs", ], ) cc_binary( name = "server_with_metrics", srcs = ["tutorial/tutorial-16-server_with_metrics.cc"], linkopts = [ "-lpthread", "-lssl", "-lcrypto", ], deps = [ ":echo_pb_srpc", ":srpc", ":srpc_hdrs", ], ) srpc-0.10.1/CMakeLists.txt000066400000000000000000000132071454502251400153050ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "build type") set(CMAKE_SKIP_RPATH TRUE) project(srpc VERSION 0.10.1 LANGUAGES C CXX) ###Options if (WIN32) option(SRPC_BUILD_STATIC_RUNTIME "Use static runtime" ON) endif () #### CHECK include(CheckIncludeFile) include(CheckIncludeFileCXX) set(THIRD_PARTY_FATAL_MESSAGE " is neither installed nor found in third_party! Sugguestion to initial third_party: \"git submodule update --init\"" ) find_library(LZ4_LIBRARY NAMES lz4) check_include_file("lz4.h" LZ4_INSTALLED) if (NOT LZ4_INSTALLED AND ${LZ4_LIBRARY} STREQUAL "LZ4_LIBRARY-NOTFOUND") if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/lz4/lib/lz4.h") message( FATAL_ERROR "\nLz4" ${THIRD_PARTY_FATAL_MESSAGE} ) endif () else () if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/lz4/lib/lz4.h") message("Lz4 third_party FOUND. Use for source code dependencies.") set(LZ4_INSTALLED 0 CACHE INTERNAL "check_lz4_installed") else () find_path(LZ4_INCLUDE_DIR NAMES "lz4.h") include_directories(${LZ4_INCLUDE_DIR}) set(LZ4_INSTALLED 1 CACHE INTERNAL "check_lz4_installed") endif () endif () find_package(Snappy) check_include_file_cxx("snappy.h" SNAPPY_INSTALLED) if (NOT SNAPPY_INSTALLED AND NOT ${Snappy_FOUND}) if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/snappy/cmake") message( FATAL_ERROR "\nSnappy" ${THIRD_PARTY_FATAL_MESSAGE} ) endif () else () if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/snappy/cmake") message("Snappy third_party FOUND. Use for source code dependencies.") set(SNAPPY_INSTALLED 0 CACHE INTERNAL "check_snappy_installed") else () find_path(Snappy_INCLUDE_DIR NAMES "snappy.h") include_directories(${Snappy_INCLUDE_DIR}) set(SNAPPY_INSTALLED 1 CACHE INTERNAL "check_snappy_installed") endif () endif () check_include_file_cxx("workflow/Workflow.h" WORKFLOW_INSTALLED) if (NOT WORKFLOW_INSTALLED) if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/workflow/workflow-config.cmake.in") message( FATAL_ERROR "\nWorkflow" ${THIRD_PARTY_FATAL_MESSAGE} ) endif () else () if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/workflow/workflow-config.cmake.in") message("Workflow third_party FOUND. Use for source code dependencies.") set(WORKFLOW_INSTALLED 0) endif () endif () find_program(PROTOC "protoc") if(${PROTOC} STREQUAL "PROTOC-NOTFOUND") message( FATAL_ERROR "Protobuf compiler is missing!") endif() #### PREPARE set(INC_DIR ${PROJECT_SOURCE_DIR}/_include CACHE PATH "srpc inc") set(LIB_DIR ${PROJECT_SOURCE_DIR}/_lib CACHE PATH "srpc lib") set(BIN_DIR ${PROJECT_SOURCE_DIR}/_bin CACHE PATH "srpc bin") include(GNUInstallDirs) set(CMAKE_CONFIG_INSTALL_FILE ${PROJECT_BINARY_DIR}/config.toinstall.cmake) set(CMAKE_CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIB_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIB_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BIN_DIR}) add_custom_target( LINK_HEADERS ALL COMMENT "link headers..." ) INCLUDE(CMakeLists_Headers.txt) macro(makeLink src dest target) add_custom_command( TARGET ${target} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dest} DEPENDS ${dest} ) endmacro() add_custom_command( TARGET LINK_HEADERS PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${INC_DIR}/${PROJECT_NAME} ) foreach(header_file ${INCLUDE_HEADERS}) string(REPLACE "/" ";" arr ${header_file}) list(GET arr -1 file_name) makeLink(${PROJECT_SOURCE_DIR}/${header_file} ${INC_DIR}/${PROJECT_NAME}/${file_name} LINK_HEADERS) endforeach() if (WIN32) if (SRPC_BUILD_STATIC_RUNTIME) set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL ) foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") endforeach () endif () endif () add_subdirectory(src) #### CONFIG include(CMakePackageConfigHelpers) set(CONFIG_INC_DIR ${INC_DIR}) set(CONFIG_LIB_DIR ${LIB_DIR}) if (VCPKG_TOOLCHAIN AND EXISTS "${CMAKE_INSTALL_PREFIX}/tools/srpc") set(CONFIG_BIN_DIR ${CMAKE_INSTALL_PREFIX}/tools/srpc) set(WITH_VCPKG_TOOLCHAIN 1 CACHE INTERNAL "build_with_vcpkg_toolchain") message("Install with VCPKG toolchain. Dir ${CMAKE_INSTALL_PREFIX}/tools/srpc.") else() set(CONFIG_BIN_DIR ${BIN_DIR}) set(WITH_VCPKG_TOOLCHAIN 0 CACHE INTERNAL "build_with_vcpkg_toolchain") endif() configure_package_config_file( ${PROJECT_NAME}-config.cmake.in ${PROJECT_SOURCE_DIR}/${PROJECT_NAME}-config.cmake INSTALL_DESTINATION ${CMAKE_CONFIG_INSTALL_DIR} PATH_VARS CONFIG_INC_DIR CONFIG_LIB_DIR CONFIG_BIN_DIR ) set(CONFIG_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}) set(CONFIG_LIB_DIR ${CMAKE_INSTALL_LIBDIR}) set(CONFIG_BIN_DIR ${CMAKE_INSTALL_BINDIR}) configure_package_config_file( ${PROJECT_NAME}-config.cmake.in ${CMAKE_CONFIG_INSTALL_FILE} INSTALL_DESTINATION ${CMAKE_CONFIG_INSTALL_DIR} PATH_VARS CONFIG_INC_DIR CONFIG_LIB_DIR CONFIG_BIN_DIR ) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake VERSION ${WORKFLOW_VERSION} COMPATIBILITY AnyNewerVersion ) install( FILES ${CMAKE_CONFIG_INSTALL_FILE} DESTINATION ${CMAKE_CONFIG_INSTALL_DIR} COMPONENT devel RENAME ${PROJECT_NAME}-config.cmake ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake DESTINATION ${CMAKE_CONFIG_INSTALL_DIR} COMPONENT devel ) install( FILES ${INCLUDE_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} COMPONENT devel ) install( FILES README.md DESTINATION "${CMAKE_INSTALL_DOCDIR}-${PROJECT_VERSION}" COMPONENT devel ) srpc-0.10.1/CMakeLists_Headers.txt000066400000000000000000000027721454502251400167450ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) set(SRC_HEADERS src/compress/rpc_compress.h src/compress/rpc_compress_gzip.h src/message/rpc_message.h src/message/rpc_message_srpc.h src/message/rpc_message_thrift.h src/message/rpc_message_brpc.h src/message/rpc_message_trpc.h src/thrift/rpc_thrift_buffer.h src/thrift/rpc_thrift_enum.h src/thrift/rpc_thrift_idl.h src/thrift/rpc_thrift_idl.inl src/var/ckms_quantiles.h src/var/time_window_quantiles.h src/var/rpc_var.h src/module/rpc_module.h src/module/rpc_trace_module.h src/module/rpc_metrics_module.h src/module/rpc_filter.h src/module/rpc_trace_filter.h src/module/rpc_metrics_filter.h src/rpc_basic.h src/rpc_buffer.h src/rpc_client.h src/rpc_context.h src/rpc_context.inl src/rpc_global.h src/rpc_options.h src/rpc_server.h src/rpc_service.h src/rpc_task.inl src/rpc_types.h src/rpc_zero_copy_stream.h src/rpc_define.h ) if (NOT WIN32) set(SRC_HEADERS ${SRC_HEADERS} src/http/http_task.h src/http/http_module.h src/http/http_client.h src/http/http_server.h ) endif () if (NOT SNAPPY_INSTALLED) set(SNAPPY_HEADERS third_party/snappy/snappy.h third_party/snappy/snappy-c.h third_party/snappy/snappy-sinksource.h third_party/snappy/snappy-stubs-public.h ) endif () if (NOT LZ4_INSTALLED) set(LZ4_HEADERS third_party/lz4/lib/lz4.h third_party/lz4/lib/lz4frame.h ) endif () if (WITH_VCPKG_TOOLCHAIN) set(INCLUDE_HEADERS ${SRC_HEADERS}) else() set(INCLUDE_HEADERS ${SRC_HEADERS} ${SNAPPY_HEADERS} ${LZ4_HEADERS}) endif() srpc-0.10.1/CODE_OF_CONDUCT.md000066400000000000000000000062571454502251400153530ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others’ private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at liyingxin@sogou-inc.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ srpc-0.10.1/GNUmakefile000066400000000000000000000032531454502251400146170ustar00rootroot00000000000000ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) ALL_TARGETS := all base check install preinstall package rpm clean tutorial example MAKE_FILE := Makefile DEFAULT_BUILD_DIR := build.cmake BUILD_DIR := $(shell if [ -f $(MAKE_FILE) ]; then echo "."; else echo $(DEFAULT_BUILD_DIR); fi) CMAKE3 := $(shell if which cmake3>/dev/null ; then echo cmake3; else echo cmake; fi;) WORKFLOW := $(shell if [ -f "workflow/workflow-config.cmake.in" ]; then echo "Found"; else echo "NotFound"; fi) .PHONY: $(ALL_TARGETS) all: base make -C $(BUILD_DIR) -f Makefile base: ifeq ("$(WORKFLOW)","Found") make -C workflow endif mkdir -p $(BUILD_DIR) ifeq ($(DEBUG),y) cd $(BUILD_DIR) && $(CMAKE3) -D CMAKE_BUILD_TYPE=Debug $(ROOT_DIR) else ifneq ("${INSTALL_PREFIX}install_prefix", "install_prefix") cd $(BUILD_DIR) && $(CMAKE3) -DCMAKE_INSTALL_PREFIX:STRING=${INSTALL_PREFIX} $(ROOT_DIR) else cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR) endif tutorial: all make -C tutorial check: all make -C test check install preinstall package: base mkdir -p $(BUILD_DIR) cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR) make -C $(BUILD_DIR) -f Makefile $@ rpm: package ifneq ($(BUILD_DIR),.) mv $(BUILD_DIR)/*.rpm ./ endif clean: ifeq ("$(WORKFLOW)","Found") -make -C workflow clean endif -make -C test clean -make -C tutorial clean -make -C benchmark clean rm -rf $(DEFAULT_BUILD_DIR) rm -rf _include rm -rf _lib rm -rf _bin rm -f SRCINFO SRCNUMVER SRCVERSION rm -f ./*.rpm rm -f src/message/*.pb.h src/message/*.pb.cc find . -name CMakeCache.txt | xargs rm -f find . -name Makefile | xargs rm -f find . -name "*.cmake" | xargs rm -f find . -name CMakeFiles | xargs rm -rf srpc-0.10.1/LICENSE000066400000000000000000000236761454502251400135650ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS srpc-0.10.1/README.md000066400000000000000000000203671454502251400140310ustar00rootroot00000000000000[中文版入口](README_cn.md)
srpc-logo ### NEW !!! 👉 [SRPC tools : build Workflow and SRPC projects easily.](/tools/README.md) ## Introduction **SRPC is an enterprise-level RPC system used by almost all online services in Sogou. It handles tens of billions of requests every day, covering searches, recommendations, advertising system, and other types of services.** Bases on [Sogou C++ Workflow](https://github.com/sogou/workflow), it is an excellent choice for high-performance, low-latency, lightweight RPC systems. Contains AOP aspect-oriented modules that can report Metrics and Trace to a variety of cloud-native systems, such as OpenTelemetry, etc. Its main features include: * Support multiple RPC protocols: [`SPRC`](/tutorial/tutorial-01-srpc_pb_server.cc), [`bRPC`](/tutorial/tutorial-05-brpc_pb_server.cc), [`Thrift`](/tutorial/tutorial-07-thrift_thrift_server.cc), [`tRPC`](/tutorial/tutorial-11-trpc_pb_server.cc) * Support multiple operating systems: `Linux`, `MacOS`, `Windows` * Support several IDL formats: [`Protobuf`](/tutorial/echo_pb.proto), [`Thrift`](/tutorial/echo_thrift.thrift) * Support several data formats transparently: `Json`, `Protobuf`, `Thrift Binary` * Support several compression formats, the framework automatically decompresses: `gzip`, `zlib`, `snappy`, `lz4` * Support several communication protocols transparently: `tcp`, `http`, `ssl`, `https` * With [HTTP+JSON](/docs/docs-07-srpc-http.md), you can communicate with the client or server in any language * Use it together with [Workflow Series and Parallel](/docs/docs-06-workflow.md) to facilitate the use of calculations and other asynchronous resources * Perfectly compatible with all Workflow functions, such as name service, [upstream](docs/docs-06-workflow.md#3-upstream) and other components * Report [Tracing](/docs/docs-08-tracing.md) to [OpenTelemetry](https://opentelemetry.io) * Report [Metrics](/docs/docs-09-metrics.md) to [OpenTelemetry](https://opentelemetry.io) and [Prometheus](https://prometheus.io) * [More features...](/docs/en/rpc.md) ## Installation srpc has been packaged for Debian and Fedora. Therefore, we can install it from source code or from the package in the system. reference: [Linux, MacOS, Windows Installation and Compilation Guide](docs/en/installation.md) ## Quick Start Let's quickly learn how to use it in a few steps. For more detailed usage, please refer to [Documents](/docs) and [Tutorial](/tutorial). #### 1\. example.proto ~~~proto syntax = "proto3";// You can use either proto2 or proto3. Both are supported by srpc message EchoRequest { string message = 1; string name = 2; }; message EchoResponse { string message = 1; }; service Example { rpc Echo(EchoRequest) returns (EchoResponse); }; ~~~ #### 2\. generate code ~~~sh protoc example.proto --cpp_out=./ --proto_path=./ srpc_generator protobuf ./example.proto ./ ~~~ #### 3\. server.cc ~~~cpp #include #include #include "example.srpc.h" using namespace srpc; class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { response->set_message("Hi, " + request->name()); printf("get_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); } }; void sig_handler(int signo) { } int main() { signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); SRPCServer server_tcp; SRPCHttpServer server_http; ExampleServiceImpl impl; server_tcp.add_service(&impl); server_http.add_service(&impl); server_tcp.start(1412); server_http.start(8811); getchar(); // press "Enter" to end. server_http.stop(); server_tcp.stop(); return 0; } ~~~ #### 4\. client.cc ~~~cpp #include #include "example.srpc.h" using namespace srpc; int main() { Example::SRPCClient client("127.0.0.1", 1412); EchoRequest req; req.set_message("Hello, srpc!"); req.set_name("workflow"); client.Echo(&req, [](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); getchar(); // press "Enter" to end. return 0; } ~~~ #### 5\. make These compile commands are only for Linux system. On other system, complete cmake in [tutorial](/tutorial) is recommanded. ~~~sh g++ -o server server.cc example.pb.cc -std=c++11 -lsrpc g++ -o client client.cc example.pb.cc -std=c++11 -lsrpc ~~~ #### 6\. run Terminal 1: ~~~sh ./server ~~~ Terminal 2: ~~~sh ./client ~~~ We can also use CURL to post Http request: ~~~sh curl 127.0.0.1:8811/Example/Echo -H 'Content-Type: application/json' -d '{message:"from curl",name:"CURL"}' ~~~ Output of Terminal 1: ~~~sh get_req: message: "Hello, srpc!" name: "workflow" set_resp: message: "Hi, workflow" get_req: message: "from curl" name: "CURL" set_resp: message: "Hi, CURL" ~~~ Output of Terminal 2: ~~~sh message: "Hi, workflow" ~~~ Output of CURL: ~~~sh {"message":"Hi, CURL"} ~~~ ## Benchmark * CPU 2-chip/8-core/32-processor Intel(R) Xeon(R) CPU E5-2630 v3 @2.40GHz * Memory all 128G * 10 Gigabit Ethernet * BAIDU brpc-client in pooled (connection pool) mode #### QPS at cross-machine single client→ single server under different concurrency ~~~ Client = 1 ClientThread = 64, 128, 256, 512, 1024 RequestSize = 32 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 ~~~ ![IMG](/docs/images/benchmark2.png) #### QPS at cross-machine multi-client→ single server under different client processes ~~~ Client = 1, 2, 4, 8, 16 ClientThread = 32 RequestSize = 32 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 ~~~ ![IMG](/docs/images/benchmark4.png) #### QPS at same-machine single client→ single server under different concurrency ~~~ Client = 1 ClientThread = 1, 2, 4, 8, 16, 32, 64, 128, 256 RequestSize = 1024 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 ~~~ ![IMG](/docs/images/benchmark3.png) #### QPS at same-machine single client→ single server under different request sizes ~~~ Client = 1 ClientThread = 100 RequestSize = 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 ~~~ ![IMG](/docs/images/benchmark1.png) #### Latency CDF for fixed QPS at same-machine single client→ single server ~~~ Client = 1 ClientThread = 50 ClientQPS = 10000 RequestSize = 1024 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 Outiler = 1% ~~~ ![IMG](/docs/images/benchmark5.png) #### Latency CDF for fixed QPS at cross-machine multi-client→ single server ~~~ Client = 32 ClientThread = 16 ClientQPS = 2500 RequestSize = 512 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 Outiler = 1% ~~~ ![IMG](/docs/images/benchmark6.png) ## Contact * **Email** - **[liyingxin@sogou-inc.com](mailto:liyingxin@sogou-inc.com)** - main author * **Issue** - You are very welcome to post questions to [issues](https://github.com/sogou/srpc/issues) list. * **QQ** - Group number: ``618773193`` srpc-0.10.1/README_cn.md000066400000000000000000000175601454502251400145120ustar00rootroot00000000000000[English version](README.md)   |   [Wiki:SRPC架构介绍](/docs/wiki.md) srpc-logo ### NEW !!! 👉 [SRPC tools : 一个帮你快速构建Workflow和SRPC项目的小工具.](/tools/README_cn.md) ## Introduction **SRPC是全搜狗业务线上使用的企业级RPC系统,目前每天承载上百亿的请求量,涵盖搜广推及其他类型业务。** 底层基于[Sogou C++ Workflow](https://github.com/sogou/workflow),是高性能、低延迟、轻量级RPC系统的极佳选择。且加入了AOP面向切面的模块,支持Metrics(监控指标)和Trace(链路追踪)功能,可上报到多种云原生系统,包括OpenTelemetry。 主要功能和示例: * 支持多种RPC协议:[`SPRC`](/tutorial/tutorial-01-srpc_pb_server.cc)、[`bRPC`](/tutorial/tutorial-05-brpc_pb_server.cc)、[`Thrift`](/tutorial/tutorial-07-thrift_thrift_server.cc)、[`tRPC`](/tutorial/tutorial-11-trpc_pb_server.cc) * 支持多种操作系统:`Linux` / `MacOS` / `Windows` * 支持多种IDL格式:[`Protobuf`](/tutorial/echo_pb.proto)、[`Thrift`](/tutorial/echo_thrift.thrift) * 支持多种数据布局,使用上完全透明:`Json`、`Protobuf`、`Thrift Binary` * 支持多种压缩,框架自动解压:`gzip`、`zlib`、`snappy`、`lz4` * 支持多种通信协议:`tcp`、`http`、`sctp`、`ssl`、`https` * 可以通过[http+json实现跨语言](/docs/docs-07-srpc-http.md) * 可以使用[Workflow串并联任务流](/docs/docs-06-workflow.md),打通计算及其他异步资源 * 完美兼容Workflow所有功能:命名服务体系、[upstream](docs/docs-06-workflow.md#3-upstream)、其他组件等 * 链路追踪功能:上报[Tracing](/docs/docs-08-tracing.md)到[OpenTelemetry](https://opentelemetry.io) * 监控功能:上报[Metrics](/docs/docs-09-metrics.md)到OpenTelemetry和[Prometheus](https://prometheus.io) ## Installation SRPC是Debian Linux和Fedora的自带安装包,因此可以通过源码安装,和系统自带安装包安装。 具体参考:[Linux、MacOS、Windows安装和编译指南](docs/installation.md) ## Quick Start 我们通过几个步骤快速了解如何使用。 更详细的用法可以参考[更多文档](/docs),和[官方教程](/tutorial)。 #### 1. example.proto ~~~proto syntax = "proto3";//这里proto2和proto3都可以,srpc都支持 message EchoRequest { string message = 1; string name = 2; }; message EchoResponse { string message = 1; }; service Example { rpc Echo(EchoRequest) returns (EchoResponse); }; ~~~ #### 2. generate code ~~~sh protoc example.proto --cpp_out=./ --proto_path=./ srpc_generator protobuf ./example.proto ./ ~~~ #### 3. server.cc ~~~cpp #include #include #include "example.srpc.h" using namespace srpc; class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { response->set_message("Hi, " + request->name()); printf("get_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); } }; void sig_handler(int signo) { } int main() { signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); SRPCServer server_tcp; SRPCHttpServer server_http; ExampleServiceImpl impl; server_tcp.add_service(&impl); server_http.add_service(&impl); server_tcp.start(1412); server_http.start(8811); getchar(); // press "Enter" to end. server_http.stop(); server_tcp.stop(); return 0; } ~~~ #### 4. client.cc ~~~cpp #include #include "example.srpc.h" using namespace srpc; int main() { Example::SRPCClient client("127.0.0.1", 1412); EchoRequest req; req.set_message("Hello, srpc!"); req.set_name("workflow"); client.Echo(&req, [](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); getchar(); // press "Enter" to end. return 0; } ~~~ #### 5. make 在Linux系统下的编译示例如下,其他平台建议到[tutorial](/tutorial)目录下使用完整的cmake文件协助解决编译依赖问题。 ~~~sh g++ -o server server.cc example.pb.cc -std=c++11 -lsrpc g++ -o client client.cc example.pb.cc -std=c++11 -lsrpc ~~~ #### 6. run 终端1: ~~~sh ./server ~~~ 终端2: ~~~sh ./client ~~~ 也可以用CURL发送http请求: ~~~sh curl 127.0.0.1:8811/Example/Echo -H 'Content-Type: application/json' -d '{message:"from curl",name:"CURL"}' ~~~ 终端1输出: ~~~sh get_req: message: "Hello, srpc!" name: "workflow" set_resp: message: "Hi, workflow" get_req: message: "from curl" name: "CURL" set_resp: message: "Hi, CURL" ~~~ 终端2输出: ~~~sh message: "Hi, workflow" ~~~ CURL收到的回复: ~~~sh {"message":"Hi, CURL"} ~~~ ## Benchmark * CPU 2-chip/8-core/32-processor Intel(R) Xeon(R) CPU E5-2630 v3 @2.40GHz * Memory all 128G * 10 Gigabit Ethernet * BAIDU brpc-client使用连接池pooled模式 #### 跨机单client→单server在不同并发的QPS ~~~ Client = 1 ClientThread = 64, 128, 256, 512, 1024 RequestSize = 32 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 ~~~ ![IMG](/docs/images/benchmark2.png) #### 跨机多client→单server在不同client进程数的QPS ~~~ Client = 1, 2, 4, 8, 16 ClientThread = 32 RequestSize = 32 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 ~~~ ![IMG](/docs/images/benchmark4.png) #### 同机单client→单server在不同并发下的QPS ~~~ Client = 1 ClientThread = 1, 2, 4, 8, 16, 32, 64, 128, 256 RequestSize = 1024 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 ~~~ ![IMG](/docs/images/benchmark3.png) #### 同机单client→单server在不同请求大小下的QPS ~~~ Client = 1 ClientThread = 100 RequestSize = 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 ~~~ ![IMG](/docs/images/benchmark1.png) #### 同机单client→单server在固定QPS下的延时CDF ~~~ Client = 1 ClientThread = 50 ClientQPS = 10000 RequestSize = 1024 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 Outiler = 1% ~~~ ![IMG](/docs/images/benchmark5.png) #### 跨机多client→单server在固定QPS下的延时CDF ~~~ Client = 32 ClientThread = 16 ClientQPS = 2500 RequestSize = 512 Duration = 20s Server = 1 ServerIOThread = 16 ServerHandlerThread = 16 Outiler = 1% ~~~ ![IMG](/docs/images/benchmark6.png) ## 与我们联系 * **Email** - **[liyingxin@sogou-inc.com](mailto:liyingxin@sogou-inc.com)** - 主要作者 * **Issue** - 使用中的任何问题都欢迎到[issues](https://github.com/sogou/srpc/issues)进行交流。 * **QQ** - 群号: ``618773193`` srpc-0.10.1/WORKSPACE000066400000000000000000000022371454502251400140270ustar00rootroot00000000000000workspace(name = "srpc") load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_proto", sha256 = "d8992e6eeec276d49f1d4e63cfa05bbed6d4a26cfe6ca63c972827a0d141ea3b", strip_prefix = "rules_proto-cfdc2fa31879c0aebe31ce7702b1a9c8a4be02d2", urls = [ "https://github.com/bazelbuild/rules_proto/archive/cfdc2fa31879c0aebe31ce7702b1a9c8a4be02d2.tar.gz", ], ) load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") rules_proto_dependencies() rules_proto_toolchains() git_repository( name = "workflow", commit = "3a8c14ce6bf328978d8dca3b3bb29bf5fd02a122", remote = "https://github.com/sogou/workflow.git") new_git_repository( name = "lz4", build_file = "@//third_party:lz4.BUILD", tag = "v1.9.3", remote = "https://github.com/lz4/lz4.git") new_git_repository( name = "snappy", build_file = "@//third_party:snappy.BUILD", tag = "1.1.9", remote = "https://github.com/google/snappy.git") srpc-0.10.1/benchmark/000077500000000000000000000000001454502251400144745ustar00rootroot00000000000000srpc-0.10.1/benchmark/CMakeLists.txt000066400000000000000000000070311454502251400172350ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "build type") project(srpc_benchmark LANGUAGES C CXX ) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}) if (NOT "$ENV{LIBRARY_PATH}" STREQUAL "") string(REPLACE ":" ";" LIBRARY_PATH $ENV{LIBRARY_PATH}) set(CMAKE_SYSTEM_LIBRARY_PATH ${LIBRARY_PATH};${CMAKE_SYSTEM_LIBRARY_PATH}) endif () if (NOT "$ENV{CPLUS_INCLUDE_PATH}" STREQUAL "") string(REPLACE ":" ";" INCLUDE_PATH $ENV{CPLUS_INCLUDE_PATH}) set(CMAKE_SYSTEM_INCLUDE_PATH ${INCLUDE_PATH};${CMAKE_SYSTEM_INCLUDE_PATH}) endif () find_package(OpenSSL REQUIRED) set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "") if (WIN32) find_package(Protobuf CONFIG REQUIRED) find_library(LZ4_LIBRARY NAMES lz4) find_package(Snappy CONFIG REQUIRED) else () find_package(Protobuf REQUIRED) endif () if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/lz4/lib/lz4.h") set(LZ4_LIB lz4) endif () if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/snappy/cmake") set(SNAPPY_LIB snappy) endif () if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/workflow/workflow-config.cmake.in") find_package(Workflow REQUIRED CONFIG HINTS ../workflow) endif () find_package(srpc REQUIRED CONFIG HINTS ..) include_directories( ${OPENSSL_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${Protobuf_INCLUDE_DIR} ${WORKFLOW_INCLUDE_DIR} ${SRPC_INCLUDE_DIR} ) if (WIN32) link_directories(${SRPC_LIB_DIR} ${WORKFLOW_LIB_DIR} ${Protobuf_LIB_DIR}) set(SRPC_GEN_PROGRAM ${SRPC_BIN_DIR}/Debug/srpc_generator.exe) else () get_filename_component(Protobuf_LIB_DIR ${Protobuf_LIBRARY} DIRECTORY) link_directories(${SRPC_LIB_DIR} ${WORKFLOW_LIB_DIR} ${Protobuf_LIB_DIR}) set(SRPC_GEN_PROGRAM ${SRPC_BIN_DIR}/srpc_generator) endif () protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS benchmark_pb.proto) add_custom_target( BENCHMARK_GEN ALL COMMAND ${SRPC_GEN_PROGRAM} ${PROJECT_SOURCE_DIR}/benchmark_pb.proto ${PROJECT_SOURCE_DIR} COMMAND ${SRPC_GEN_PROGRAM} ${PROJECT_SOURCE_DIR}/benchmark_thrift.thrift ${PROJECT_SOURCE_DIR} COMMENT "srpc generator..." ) if (WIN32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP /wd4200") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4200 /Zc:__cplusplus /std:c++14") else () set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fPIC -pipe -std=gnu90") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -pipe -std=c++11 -fno-exceptions") endif () if (APPLE) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ${CMAKE_FIND_LIBRARY_SUFFIXES}) find_library(Workflow_LIB workflow HINTS ../workflow/_lib) find_library(Srpc_LIB srpc HINTS ../_lib) set(SRPC_LIB ${Srpc_LIB} ${Workflow_LIB} pthread OpenSSL::SSL OpenSSL::Crypto protobuf z ) elseif (WIN32) set(SRPC_LIB srpc workflow ws2_32 wsock32 OpenSSL::SSL OpenSSL::Crypto protobuf::libprotobuf ZLIB::ZLIB Snappy::snappy ${LZ4_LIBRARY} ) else () set(SRPC_LIB srpc workflow pthread OpenSSL::SSL OpenSSL::Crypto protobuf z ${SNAPPY_LIB} ${LZ4_LIB} ) endif () add_executable(server server.cc ${PROTO_SRCS} ${PROTO_HDRS}) target_link_libraries(server ${SRPC_LIB}) add_dependencies(server BENCHMARK_GEN) add_executable(client client.cc ${PROTO_SRCS} ${PROTO_HDRS}) target_link_libraries(client ${SRPC_LIB}) add_dependencies(client BENCHMARK_GEN) add_executable(client_cdf client_cdf.cc ${PROTO_SRCS} ${PROTO_HDRS}) target_link_libraries(client_cdf ${SRPC_LIB}) add_dependencies(client_cdf BENCHMARK_GEN) add_executable(proxy proxy.cc ${PROTO_SRCS} ${PROTO_HDRS}) target_link_libraries(proxy ${SRPC_LIB}) add_dependencies(proxy BENCHMARK_GEN) srpc-0.10.1/benchmark/GNUmakefile000066400000000000000000000017731454502251400165560ustar00rootroot00000000000000ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) ALL_TARGETS := all clean MAKE_FILE := Makefile DEFAULT_BUILD_DIR := build.cmake BUILD_DIR := $(shell if [ -f $(MAKE_FILE) ]; then echo "."; else echo $(DEFAULT_BUILD_DIR); fi) CMAKE3 := $(shell if which cmake3>/dev/null ; then echo cmake3; else echo cmake; fi;) .PHONY: $(ALL_TARGETS) all: mkdir -p $(BUILD_DIR) ifeq ($(DEBUG),y) cd $(BUILD_DIR) && $(CMAKE3) -D CMAKE_BUILD_TYPE=Debug $(ROOT_DIR) else ifneq ("${Workflow_DIR}workflow", "workflow") cd $(BUILD_DIR) && $(CMAKE3) -DWorkflow_DIR:STRING=${Workflow_DIR} $(ROOT_DIR) else cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR) endif make -C $(BUILD_DIR) -f Makefile clean: ifeq ($(MAKE_FILE), $(wildcard $(MAKE_FILE))) -make -f Makefile clean else ifeq ($(DEFAULT_BUILD_DIR), $(wildcard $(DEFAULT_BUILD_DIR))) -make -C $(DEFAULT_BUILD_DIR) clean endif rm -rf $(DEFAULT_BUILD_DIR) #g++ -o thrift_server thrift_server.cc gen-cpp/*.cpp -O2 -g -lthrift -lthriftnb -levent -lpthread -std=c++11 srpc-0.10.1/benchmark/benchmark_pb.proto000066400000000000000000000003241454502251400201730ustar00rootroot00000000000000syntax="proto3"; message EmptyPBMsg { } message FixLengthPBMsg { bytes msg = 1; } service BenchmarkPB { rpc echo_pb(FixLengthPBMsg) returns (EmptyPBMsg); rpc slow_pb(FixLengthPBMsg) returns (EmptyPBMsg); } srpc-0.10.1/benchmark/benchmark_thrift.thrift000066400000000000000000000001371454502251400212310ustar00rootroot00000000000000service BenchmarkThrift { void echo_thrift(1:string msg); void slow_thrift(1:string msg); } srpc-0.10.1/benchmark/client.cc000066400000000000000000000120421454502251400162600ustar00rootroot00000000000000#include #include #include #include #include #include #include "benchmark_pb.srpc.h" #include "benchmark_thrift.srpc.h" using namespace srpc; #define TEST_SECOND 20 #define GET_CURRENT_NS std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count() std::atomic query_count(0); std::atomic success_count(0); std::atomic error_count(0); std::atomic latency_sum(0); volatile bool stop_flag = false; int PARALLEL_NUMBER; std::string request_msg; template static void do_echo_pb(CLIENT *client) { FixLengthPBMsg req; req.set_msg(request_msg); int64_t ns_st = GET_CURRENT_NS; ++query_count; client->echo_pb(&req, [client, ns_st](EmptyPBMsg *response, RPCContext *ctx) { if (ctx->success()) { //printf("%s\n", ctx->get_remote_ip().c_str()); latency_sum += GET_CURRENT_NS - ns_st; ++success_count; } else { printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); ++error_count; } if (!stop_flag) do_echo_pb(client); //printf("echo done. seq_id=%d\n", ctx->get_task_seq()); }); } template static void do_echo_thrift(CLIENT *client) { BenchmarkThrift::echo_thriftRequest req; req.msg = request_msg; int64_t ns_st = GET_CURRENT_NS; ++query_count; client->echo_thrift(&req, [client, ns_st](BenchmarkThrift::echo_thriftResponse *response, RPCContext *ctx) { if (ctx->success()) { //printf("%s\n", ctx->get_remote_ip().c_str()); latency_sum += GET_CURRENT_NS - ns_st; ++success_count; } else { printf("status[%d] error[%d] errmsg:%s \n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); ++error_count; } if (!stop_flag) do_echo_thrift(client); //printf("echo done. seq_id=%d\n", ctx->get_task_seq()); }); } int main(int argc, char* argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 7) { fprintf(stderr, "Usage: %s \n", argv[0]); abort(); } WFGlobalSettings setting = GLOBAL_SETTINGS_DEFAULT; setting.endpoint_params.max_connections = 2048; setting.poller_threads = 16; setting.handler_threads = 16; WORKFLOW_library_init(&setting); RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; client_params.task_params.keep_alive_timeout = -1; client_params.host = argv[1]; client_params.port = atoi(argv[2]); std::string server_type = argv[3]; std::string idl_type = argv[4]; PARALLEL_NUMBER = atoi(argv[5]); int REQUEST_BYTES = atoi(argv[6]); request_msg.resize(REQUEST_BYTES, 'r'); //for (int i = 0; i < REQUEST_BYTES; i++) // request_msg[i] = (unsigned char)(rand() % 256); int64_t start = GET_CURRENT_MS(); if (server_type == "srpc") { if (idl_type == "pb") { auto *client = new BenchmarkPB::SRPCClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) do_echo_pb(client); } else if (idl_type == "thrift") { auto *client = new BenchmarkThrift::SRPCClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) do_echo_thrift(client); } else abort(); } else if (server_type == "brpc") { auto *client = new BenchmarkPB::BRPCClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) { if (idl_type == "pb") do_echo_pb(client); else if (idl_type == "thrift") abort(); else abort(); } } else if (server_type == "thrift") { auto *client = new BenchmarkThrift::ThriftClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) { if (idl_type == "pb") abort(); else if (idl_type == "thrift") do_echo_thrift(client); else abort(); } } else if (server_type == "srpc_http") { if (idl_type == "pb") { auto *client = new BenchmarkPB::SRPCHttpClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) do_echo_pb(client); } else if (idl_type == "thrift") { auto *client = new BenchmarkThrift::SRPCHttpClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) do_echo_thrift(client); } else abort(); } else if (server_type == "thrift_http") { auto *client = new BenchmarkThrift::ThriftHttpClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) { if (idl_type == "pb") abort(); else if (idl_type == "thrift") do_echo_thrift(client); else abort(); } } else abort(); std::this_thread::sleep_for(std::chrono::seconds(TEST_SECOND)); stop_flag = true; int64_t end = GET_CURRENT_MS(); int tot = query_count; int s = success_count; int e = error_count; int64_t l = latency_sum; fprintf(stderr, "\nquery\t%d\ttimes, %d success, %d error.\n", tot, s, e); fprintf(stderr, "total\t%.3lf\tseconds\n", (end - start) / 1000.0); fprintf(stderr, "qps=%.0lf\n", tot * 1000.0 / (end - start)); fprintf(stderr, "latency=%.0lfus\n", s > 0 ? l * 1.0 / s / 1000 : 0); std::this_thread::sleep_for(std::chrono::seconds(1)); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/benchmark/client_cdf.cc000066400000000000000000000171031454502251400170770ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "benchmark_pb.srpc.h" #include "benchmark_thrift.srpc.h" using namespace srpc; #define TEST_SECOND 20 #define GET_CURRENT_NS std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count() std::atomic query_count(0); std::atomic slow_count(0); std::atomic success_count(0); std::atomic error_count(0); //std::atomic latency_sum(0); std::vector> latency_lists; volatile bool stop_flag = false; int PARALLEL_NUMBER; std::string request_msg; int QPS; template static void do_echo_pb(CLIENT *client, int idx) { std::mutex mutex; auto& latency_list = latency_lists[idx]; FixLengthPBMsg req; req.set_msg(request_msg); int usleep_gap = 1000000 / QPS * PARALLEL_NUMBER; while (!stop_flag) { int64_t ns_st = GET_CURRENT_NS; if (++query_count % 100 > 0) { client->echo_pb(&req, [ns_st, &latency_list, &mutex](EmptyPBMsg *response, RPCContext *ctx) { if (ctx->success()) { //printf("%s\n", ctx->get_remote_ip().c_str()); ++success_count; //latency_sum += GET_CURRENT_NS - ns_st; mutex.lock(); latency_list.emplace_back(GET_CURRENT_NS - ns_st); mutex.unlock(); } else { printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); ++error_count; } //printf("echo done. seq_id=%d\n", ctx->get_task_seq()); }); } else { client->slow_pb(&req, [](EmptyPBMsg *response, RPCContext *ctx) { slow_count++; if (!ctx->success()) printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); } std::this_thread::sleep_for(std::chrono::microseconds(usleep_gap)); } } template static void do_echo_thrift(CLIENT *client, int idx) { std::mutex mutex; auto& latency_list = latency_lists[idx]; BenchmarkThrift::echo_thriftRequest req; req.msg = request_msg; BenchmarkThrift::slow_thriftRequest slow_req; slow_req.msg = request_msg; int usleep_gap = 1000000 / QPS * PARALLEL_NUMBER; while (!stop_flag) { int64_t ns_st = GET_CURRENT_NS; if (++query_count % 100 > 0) { client->echo_thrift(&req, [ns_st, &latency_list, &mutex](BenchmarkThrift::echo_thriftResponse *response, RPCContext *ctx) { if (ctx->success()) { //printf("%s\n", ctx->get_remote_ip().c_str()); ++success_count; //latency_sum += GET_CURRENT_NS - ns_st; mutex.lock(); latency_list.emplace_back(GET_CURRENT_NS - ns_st); mutex.unlock(); } else { printf("status[%d] error[%d] errmsg:%s \n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); ++error_count; } //printf("echo done. seq_id=%d\n", ctx->get_task_seq()); }); } else { client->slow_thrift(&slow_req, [](BenchmarkThrift::slow_thriftResponse *response, RPCContext *ctx) { slow_count++; if (!ctx->success()) printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); } std::this_thread::sleep_for(std::chrono::microseconds(usleep_gap)); } } int main(int argc, char* argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 8) { fprintf(stderr, "Usage: %s \n", argv[0]); abort(); } WFGlobalSettings setting = GLOBAL_SETTINGS_DEFAULT; setting.endpoint_params.max_connections = 2048; setting.poller_threads = 16; setting.handler_threads = 16; WORKFLOW_library_init(&setting); RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; client_params.task_params.keep_alive_timeout = -1; client_params.host = argv[1]; client_params.port = atoi(argv[2]); std::string server_type = argv[3]; std::string idl_type = argv[4]; PARALLEL_NUMBER = atoi(argv[5]); int REQUEST_BYTES = atoi(argv[6]); QPS = atoi(argv[7]); request_msg.resize(REQUEST_BYTES, 'r'); //for (int i = 0; i < REQUEST_BYTES; i++) // request_msg[i] = (unsigned char)(rand() % 256); latency_lists.resize(PARALLEL_NUMBER); std::vector th; int64_t start = GET_CURRENT_MS(); if (server_type == "srpc") { if (idl_type == "pb") { auto *client = new BenchmarkPB::SRPCClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) th.push_back(new std::thread(do_echo_pb, client, i)); } else if (idl_type == "thrift") { auto *client = new BenchmarkThrift::SRPCClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) th.push_back(new std::thread(do_echo_thrift, client, i)); } else abort(); } else if (server_type == "brpc") { auto *client = new BenchmarkPB::BRPCClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) { if (idl_type == "pb") th.push_back(new std::thread(do_echo_pb, client, i)); else if (idl_type == "thrift") abort(); else abort(); } } else if (server_type == "thrift") { auto *client = new BenchmarkThrift::ThriftClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) { if (idl_type == "pb") abort(); else if (idl_type == "thrift") th.push_back(new std::thread(do_echo_thrift, client, i)); else abort(); } } else if (server_type == "srpc_http") { if (idl_type == "pb") { auto * client = new BenchmarkPB::SRPCHttpClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) th.push_back(new std::thread(do_echo_pb, client, i)); } else if (idl_type == "thrift") { auto *client = new BenchmarkThrift::SRPCHttpClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) th.push_back(new std::thread(do_echo_thrift, client, i)); } else abort(); } else if (server_type == "thrift_http") { auto *client = new BenchmarkThrift::ThriftHttpClient(&client_params); for (int i = 0; i < PARALLEL_NUMBER; i++) { if (idl_type == "pb") abort(); else if (idl_type == "thrift") th.push_back(new std::thread(do_echo_thrift, client, i)); else abort(); } } else abort(); std::this_thread::sleep_for(std::chrono::seconds(TEST_SECOND)); stop_flag = true; for (auto *t : th) { t->join(); delete t; } int64_t end = GET_CURRENT_MS(); int tot = query_count - slow_count; int s = success_count; int e = error_count; int64_t l = 0;//latency_sum; std::vector all_lc; for (const auto& list : latency_lists) { for (auto v : list) { //fprintf(stderr, "%lld\n", (long long int)v); l += v; } all_lc.insert(all_lc.end(), list.begin(), list.end()); } sort(all_lc.begin(), all_lc.end()); for (double r = 0.950; r <= 0.999; r += 0.001) { double d = r * all_lc.size(); int idx = (int)(d + 1.0e-8); if (fabs(d - int(d)) > 1.0e-8) idx++; printf("%.3lf %lld\n", r, (long long int)all_lc[idx - 1]/1000); } //printf("%.3lf %lld\n", 1.0, (long long int)all_lc[all_lc.size() - 1]/1000); fprintf(stderr, "\nquery\t%d\ttimes, %d success, %d error.\n", tot, s, e); fprintf(stderr, "total\t%.3lf\tseconds\n", (end - start) / 1000.0); fprintf(stderr, "qps=%.0lf\n", tot * 1000.0 / (end - start)); fprintf(stderr, "latency=%.0lfus\n", s > 0 ? l * 1.0 / s / 1000 : 0); std::this_thread::sleep_for(std::chrono::seconds(1)); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/benchmark/proxy.cc000066400000000000000000000124771454502251400161770ustar00rootroot00000000000000#include #include #include "benchmark_pb.srpc.h" #include "benchmark_thrift.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; std::atomic query_count(0); // per_second std::atomic last_timestamp(0L); //volatile bool stop_flag = false; int max_qps = 0; long long total_count = 0; std::string remote_host; unsigned short remote_port; WFFacilities::WaitGroup wait_group(1); inline void collect_qps() { int64_t ms_timestamp = GET_CURRENT_MS(); ++query_count; if (ms_timestamp / 1000 > last_timestamp) { last_timestamp = ms_timestamp / 1000; int count = query_count; query_count = 0; total_count += count; if (count > max_qps) max_qps = count; long long ts = ms_timestamp; fprintf(stdout, "TIMESTAMP(ms) = %llu QPS = %d\n", ts, count); } } template class BenchmarkPBServiceImpl : public BenchmarkPB::Service { public: void echo_pb(FixLengthPBMsg *request, EmptyPBMsg *response, RPCContext *ctx) override { auto *task = this->client->create_echo_pb_task( [](EmptyPBMsg *remote_resp, srpc::RPCContext *remote_ctx) { collect_qps(); }); task->user_data = response; task->serialize_input(request); ctx->get_series()->push_back(task); } void slow_pb(FixLengthPBMsg *request, EmptyPBMsg *response, RPCContext *ctx) override { auto *task = WFTaskFactory::create_timer_task(15000, nullptr); ctx->get_series()->push_back(task); } CLIENT *client; }; template class BenchmarkThriftServiceImpl : public BenchmarkThrift::Service { public: void echo_thrift(BenchmarkThrift::echo_thriftRequest *request, BenchmarkThrift::echo_thriftResponse *response, RPCContext *ctx) override { auto *task = this->client->create_echo_thrift_task( [](BenchmarkThrift::echo_thriftResponse *remote_resp, srpc::RPCContext *remote_ctx) { collect_qps(); }); task->user_data = response; task->serialize_input(request); ctx->get_series()->push_back(task); } void slow_thrift(BenchmarkThrift::slow_thriftRequest *request, BenchmarkThrift::slow_thriftResponse *response, RPCContext *ctx) override { auto *task = WFTaskFactory::create_timer_task(15000, nullptr); ctx->get_series()->push_back(task); } CLIENT *client; }; static void sig_handler(int signo) { wait_group.done(); } template class SERVICE, class CLIENT> static void init_proxy_client(SERVICE& service_impl) { RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; client_params.task_params.keep_alive_timeout = -1; client_params.host = remote_host; client_params.port = remote_port; service_impl.client = new CLIENT(&client_params); } static void run_srpc_proxy(unsigned short port) { RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT; params.max_connections = 2048; SRPCServer proxy_server(¶ms); BenchmarkPBServiceImpl pb_impl; BenchmarkThriftServiceImpl thrift_impl; init_proxy_client(pb_impl); init_proxy_client(thrift_impl); proxy_server.add_service(&pb_impl); proxy_server.add_service(&thrift_impl); if (proxy_server.start(port) == 0) { wait_group.wait(); proxy_server.stop(); } else perror("server start"); } template static void run_pb_proxy(unsigned short port) { RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT; params.max_connections = 2048; SERVER server(¶ms); BenchmarkPBServiceImpl pb_impl; init_proxy_client(pb_impl); server.add_service(&pb_impl); if (server.start(port) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); } template static void run_thrift_proxy(unsigned short port) { RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT; params.max_connections = 2048; SERVER server(¶ms); BenchmarkThriftServiceImpl thrift_impl; init_proxy_client(thrift_impl); server.add_service(&thrift_impl); if (server.start(port) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); } int main(int argc, char* argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 5) { fprintf(stderr, "Usage: %s " " \n", argv[0]); abort(); } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); WFGlobalSettings my = GLOBAL_SETTINGS_DEFAULT; my.poller_threads = 16; my.handler_threads = 16; WORKFLOW_library_init(&my); unsigned short port = atoi(argv[1]); remote_host = argv[2]; remote_port = atoi(argv[3]); std::string server_type = argv[4]; if (server_type == "srpc") run_srpc_proxy(port); else if (server_type == "brpc") run_pb_proxy(port); else if (server_type == "thrift") run_thrift_proxy(port); else if (server_type == "srpc_http") run_pb_proxy(port); else if (server_type == "thrift_http") run_thrift_proxy(port); else abort(); fprintf(stdout, "\nTotal query: %llu max QPS: %d\n", total_count, max_qps); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/benchmark/server.cc000066400000000000000000000122241454502251400163120ustar00rootroot00000000000000#include #include #include "benchmark_pb.srpc.h" #include "benchmark_thrift.srpc.h" #include "workflow/WFFacilities.h" #ifdef _WIN32 #include "workflow/PlatformSocket.h" #else #include #include #include #include #include #endif using namespace srpc; std::atomic query_count(0); // per_second std::atomic last_timestamp(0L); //volatile bool stop_flag = false; int max_qps = 0; long long total_count = 0; WFFacilities::WaitGroup wait_group(1); inline void collect_qps() { int64_t ms_timestamp = GET_CURRENT_MS(); ++query_count; if (ms_timestamp / 1000 > last_timestamp) { last_timestamp = ms_timestamp / 1000; int count = query_count; query_count = 0; total_count += count; if (count > max_qps) max_qps = count; long long ts = ms_timestamp; fprintf(stdout, "TIMESTAMP(ms) = %llu QPS = %d\n", ts, count); } } class BenchmarkPBServiceImpl : public BenchmarkPB::Service { public: void echo_pb(FixLengthPBMsg *request, EmptyPBMsg *response, RPCContext *ctx) override { collect_qps(); } void slow_pb(FixLengthPBMsg *request, EmptyPBMsg *response, RPCContext *ctx) override { auto *task = WFTaskFactory::create_timer_task(15000, nullptr); ctx->get_series()->push_back(task); } }; class BenchmarkThriftServiceImpl : public BenchmarkThrift::Service { public: void echo_thrift(BenchmarkThrift::echo_thriftRequest *request, BenchmarkThrift::echo_thriftResponse *response, RPCContext *ctx) override { collect_qps(); } void slow_thrift(BenchmarkThrift::slow_thriftRequest *request, BenchmarkThrift::slow_thriftResponse *response, RPCContext *ctx) override { auto *task = WFTaskFactory::create_timer_task(15000, nullptr); ctx->get_series()->push_back(task); } }; static void sig_handler(int signo) { wait_group.done(); } static inline int create_bind_socket(unsigned short port) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd >= 0) { struct sockaddr_in sin = { }; sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sockfd, (struct sockaddr *)&sin, sizeof sin) >= 0) return sockfd; close(sockfd); } return -1; } static void run_srpc_server(unsigned short port, int proc_num) { RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT; params.max_connections = 2048; SRPCServer server(¶ms); BenchmarkPBServiceImpl pb_impl; BenchmarkThriftServiceImpl thrift_impl; server.add_service(&pb_impl); server.add_service(&thrift_impl); int sockfd = create_bind_socket(port); if (sockfd < 0) { perror("create socket"); exit(1); } while ((proc_num /= 2) != 0) fork(); if (server.serve(sockfd) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); close(sockfd); } template static void run_pb_server(unsigned short port, int proc_num) { RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT; params.max_connections = 2048; SERVER server(¶ms); BenchmarkPBServiceImpl pb_impl; server.add_service(&pb_impl); int sockfd = create_bind_socket(port); if (sockfd < 0) { perror("create socket"); exit(1); } while ((proc_num /= 2) != 0) fork(); if (server.serve(sockfd) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); close(sockfd); } template static void run_thrift_server(unsigned short port, int proc_num) { RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT; params.max_connections = 2048; SERVER server(¶ms); BenchmarkThriftServiceImpl thrift_impl; server.add_service(&thrift_impl); int sockfd = create_bind_socket(port); if (sockfd < 0) { perror("create socket"); exit(1); } while ((proc_num /= 2) != 0) fork(); if (server.serve(sockfd) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); close(sockfd); } int main(int argc, char* argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; int proc_num = 1; if (argc == 4) { proc_num = atoi(argv[3]); if (proc_num != 1 && proc_num != 2 && proc_num != 4 && proc_num != 8 && proc_num != 16) { fprintf(stderr, "Usage: %s [proc num (1/2/4/8/16)]\n", argv[0]); abort(); } } else if (argc != 3) { fprintf(stderr, "Usage: %s [proc num (1/2/4/8/16)]\n", argv[0]); abort(); } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); WFGlobalSettings my = GLOBAL_SETTINGS_DEFAULT; my.poller_threads = 16; my.handler_threads = 16; WORKFLOW_library_init(&my); unsigned short port = atoi(argv[1]); std::string server_type = argv[2]; if (server_type == "srpc") run_srpc_server(port, proc_num); else if (server_type == "brpc") run_pb_server(port, proc_num); else if (server_type == "thrift") run_thrift_server(port, proc_num); else if (server_type == "srpc_http") run_pb_server(port, proc_num); else if (server_type == "thrift_http") run_thrift_server(port, proc_num); else abort(); fprintf(stdout, "\nTotal query: %llu max QPS: %d\n", total_count, max_qps); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/benchmark/test.py000066400000000000000000000011751454502251400160310ustar00rootroot00000000000000import os import time #serverlist = [("srpc", "pb"), ("brpc", "pb"), ("thrift", "thrift")] serverlist = [("thrift", "thrift")] #reqlist = [16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768] parlist = [1, 2, 4, 8, 16, 32, 64, 128, 256] for server, idl in serverlist: #os.system("nohup ./server 8811 %s &" % server) for par in parlist: #for reqsize in reqlist: #cmd = "./echo_client %s" % reqsize #cmd = "./client 127.0.0.1 8811 %s %s 100 %s" % (server, idl, reqsize) cmd = "./client 127.0.0.1 8811 %s %s %s 1024" % (server, idl, par) print cmd os.system(cmd); time.sleep(1); #os.system("killall server") srpc-0.10.1/benchmark/thrift_server.cc000066400000000000000000000027051454502251400176750ustar00rootroot00000000000000#include "gen-cpp/BenchmarkThrift.h" #include #include #include #include #include #include using namespace ::apache::thrift; using namespace ::apache::thrift::protocol; using namespace ::apache::thrift::transport; using namespace ::apache::thrift::server; using namespace ::apache::thrift::concurrency; using boost::shared_ptr; class BenchmarkThriftHandler : virtual public BenchmarkThriftIf { public: void echo_thrift(const std::string& msg) { } void slow_thrift(const std::string& msg) { usleep(15000); } }; int main(int argc, char **argv) { int port = 8811; shared_ptr handler(new BenchmarkThriftHandler()); shared_ptr processor(new BenchmarkThriftProcessor(handler)); shared_ptr protocolFactory(new TBinaryProtocolFactory()); boost::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(16); boost::shared_ptr threadFactory = boost::shared_ptr(new PosixThreadFactory()); TNonblockingServer server(processor, protocolFactory, port, threadManager); server.setMaxConnections(2048); server.setNumIOThreads(16); threadManager->threadFactory(threadFactory); threadManager->start(); server.serve(); return 0; } srpc-0.10.1/docs/000077500000000000000000000000001454502251400134725ustar00rootroot00000000000000srpc-0.10.1/docs/docs-01-idl.md000066400000000000000000000013101454502251400157230ustar00rootroot00000000000000[English version](/docs/en/docs-01-idl.md) ## 01 - RPC IDL - 描述文件 - 前后兼容 - Protobuf/Thrift ### 示例 下面我们通过一个具体例子来呈现 - 我们拿pb举例,定义一个ServiceName为``Example``的``example.proto``文件 - rpc接口名为``Echo``,输入参数为``EchoRequest``,输出参数为``EchoResponse`` - ``EchoRequest``包括两个string:``message``和``name`` - ``EchoResponse``包括一个string:``message`` ~~~proto syntax="proto2"; message EchoRequest { optional string message = 1; optional string name = 2; }; message EchoResponse { optional string message = 1; }; service Example { rpc Echo(EchoRequest) returns (EchoResponse); }; ~~~ srpc-0.10.1/docs/docs-02-service.md000066400000000000000000000022271454502251400166240ustar00rootroot00000000000000[English version](/docs/en/docs-02-service.md) ## 02 - RPC Service - 组成SRPC服务的基本单元 - 每一个Service一定由某一种IDL生成 - Service只与IDL有关,与网络通信具体协议无关 ### 示例 下面我们通过一个具体例子来呈现 - 沿用上面的``example.proto``IDL描述文件 - 执行官方的``protoc example.proto --cpp_out=./ --proto_path=./``获得``example.pb.h``和``example.pb.cpp``两个文件 - 执行SRPC的``srpc_generator protobuf ./example.proto ./``获得``example.srpc.h``文件 - 我们派生``Example::Service``来实现具体的rpc业务逻辑,这就是一个RPC Service - 注意这个Service没有任何网络、端口、通信协议等概念,仅仅负责完成实现从``EchoRequest``输入到输出``EchoResponse``的业务逻辑 ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { response->set_message("Hi, " + request->name()); printf("get_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); } }; ~~~ srpc-0.10.1/docs/docs-03-server.md000066400000000000000000000047421454502251400164770ustar00rootroot00000000000000[English version](/docs/en/docs-03-server.md) ## 03 - RPC Server - 每一个Server对应一个端口 - 每一个Server对应一个确定的网络通信协议 - 每一个Service可以添加到任意的Server里 - 每一个Server可以拥有任意的Service,但在当前Server里ServiceName必须唯一 - 不同IDL的Service是可以放进同一个Server中的 ### 示例 下面我们通过一个具体例子来呈现 - 沿用上面的``ExampleServiceImpl``Service - 首先,我们创建1个RPC Server、需要确定协议 - 然后,我们可以创建任意个数的Service实例、任意不同proto形成的Service,把这些Service通过``add_service()``接口添加到Server里 - 最后,通过Server的``start``或者``serve``开启服务,处理即将到来的rpc请求 - 想像一下,我们也可以从``Example::Service``派生更多的功能的rpc``Echo``不同实现的Service - 想像一下,我们可以在N个不同的端口创建N个不同的RPC Server、代表着不同的协议 - 想像一下,我们可以把同一个ServiceIMPL实例``add_service``到不同的Server上,我们也可以把不同的ServiceIMPL实例``add_service``到同一个Server上 - 想像一下,我们可以用同一个``ExampleServiceImpl``,在三个不同端口、同时服务于BPRC-STD、SRPC-STD、SRPC-Http - 甚至,我们可以将1个PB的``ExampleServiceImpl``和1个Thrift的``AnotherThriftServiceImpl``,``add_service``到同一个SRPC-STD Server,两种IDL在同一个端口上完美工作! ~~~cpp int main() { SRPCServer server_srpc; SRPCHttpServer server_srpc_http; BRPCServer server_brpc; ThriftServer server_thrift; TRPCServer server_trpc; TRPCHttpServer server_trpc_http; ExampleServiceImpl impl_pb; AnotherThriftServiceImpl impl_thrift; server_srpc.add_service(&impl_pb); server_srpc.add_service(&impl_thrift); server_srpc_http.add_service(&impl_pb); server_srpc_http.add_service(&impl_thrift); server_brpc.add_service(&impl_pb); server_thrift.add_service(&impl_thrift); server_trpc.add_service(&impl_pb); server_trpc_http.add_service(&impl_thrift); server_srpc.start(1412); server_srpc_http.start(8811); server_brpc.start(2020); server_thrift.start(9090); server_trpc.start(2022); server_trpc_http.start(8822); getchar(); server_trpc_http.stop(); server_trpc.stop(); server_thrift.stop(); server_brpc.stop(); server_srpc_http.stop(); server_srpc.stop(); return 0; } ~~~ srpc-0.10.1/docs/docs-04-client.md000066400000000000000000000040531454502251400164430ustar00rootroot00000000000000[English version](/docs/en/docs-04-client.md) ## 04 - RPC Client - 每一个Client对应着一个确定的目标/一个确定的集群 - 每一个Client对应着一个确定的网络通信协议 - 每一个Client对应着一个确定的IDL ### 示例 下面我们通过一个具体例子来呈现 - 沿用上面的例子,client相对简单,直接调用即可 - 通过``Example::XXXClient``创建某种RPC的client实例,需要目标的ip+port或url - 利用client实例直接调用rpc函数``Echo``即可,这是一次异步请求,请求完成后会进入回调函数 - 具体的RPC Context用法请看下一个段落 ~~~cpp #include #include "example.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; int main() { Example::SRPCClient client("127.0.0.1", 1412); EchoRequest req; req.set_message("Hello!"); req.set_name("SRPCClient"); WFFacilities::WaitGroup wait_group(1); client.Echo(&req, [&wait_group](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); wait_group.done(); }); wait_group.wait(); return 0; } ~~~ ### 启动参数 Client可以直接通过传入ip、port启动,或者通过参数启动。 上面的例子: ~~~cpp Example::SRPCClient client("127.0.0.1", 1412); ~~~ 等同于: ~~~cpp struct RPCClientParams param = RPC_CLIENT_PARAMS_DEFAULT; param.host = "127.0.0.1"; param.port = 1412; Example::SRPCClient client(¶m); ~~~ 也等同于: ~~~cpp struct RPCClientParams param = RPC_CLIENT_PARAMS_DEFAULT; param.url = "srpc://127.0.0.1:1412"; Example::SRPCClient client(¶m); ~~~ 注意这里一定要使用`RPC_CLIENT_PARAMS_DEFAULT`去初始化我们的参数,里边包含了一个`RPCTaskParams`,包括默认的data_type、compress_type、重试次数和多种超时,具体结构可以参考[rpc_options.h](/src/rpc_options.h)。 srpc-0.10.1/docs/docs-05-context.md000066400000000000000000000053741454502251400166610ustar00rootroot00000000000000[English version](/docs/en/docs-05-context.md) ## 05 - RPC Context - RPCContext专门用来辅助异步接口,Service和Client通用 - 每一个异步接口都会提供Context,用来给用户提供更高级的功能,比如获取对方ip、获取连接seqid等 - Context上一些功能是Server或Client独有的,比如Server可以设置回复数据的压缩方式,Client可以获取请求成功或失败 - Context上可以通过get_series获得所在的series,与workflow的异步模式无缝结合 ### RPCContext API - Common #### ``long long get_seqid() const;`` 请求+回复视为1次完整通信,获得当前socket连接上的通信sequence id,seqid=0代表第1次 #### ``std::string get_remote_ip() const;`` 获得对方IP地址,支持ipv4/ipv6 #### ``int get_peer_addr(struct sockaddr *addr, socklen_t *addrlen) const;`` 获得对方地址,in/out参数为更底层的数据结构sockaddr #### ``const std::string& get_service_name() const;`` 获取RPC Service Name #### ``const std::string& get_method_name() const;`` 获取RPC Methode Name #### ``SeriesWork *get_series() const;`` 获取当前ServerTask/ClientTask所在series ### RPCContext API - Only for client done #### ``bool success() const;`` client专用。这次请求是否成功 #### ``int get_status_code() const;`` client专用。这次请求的rpc status code #### ``const char *get_errmsg() const;`` client专用。这次请求的错误信息 #### ``int get_error() const;`` client专用。这次请求的错误码 #### ``void *get_user_data() const;`` client专用。获取ClientTask的user_data。如果用户通过create_xxx_task接口产生task,则可以通过user_data域记录上下文,在创建task时设置,在回调函数中拿回。 ### RPCContext API - Only for server process #### ``void set_data_type(RPCDataType type);`` Server专用。设置数据打包类型 - RPCDataProtobuf - RPCDataThrift - RPCDataJson #### ``void set_compress_type(RPCCompressType type);`` Server专用。设置数据压缩类型(注:Client的压缩类型在Client或Task上设置) - RPCCompressNone - RPCCompressSnappy - RPCCompressGzip - RPCCompressZlib - RPCCompressLz4 #### ``void set_attachment_nocopy(const char *attachment, size_t len);`` Server专用。设置attachment附件。 #### ``bool get_attachment(const char **attachment, size_t *len) const;`` Server专用。获取attachment附件。 #### ``void set_reply_callback(std::function cb);`` Server专用。设置reply callback,操作系统写入socket缓冲区成功后被调用。 #### ``void set_send_timeout(int timeout);`` Server专用。设置发送超时,单位毫秒。-1代表无限。 #### ``void set_keep_alive(int timeout);`` Server专用。设置连接保活时间,单位毫秒。-1代表无限。 srpc-0.10.1/docs/docs-06-workflow.md000066400000000000000000000126171454502251400170460ustar00rootroot00000000000000[English version](/docs/en/docs-06-workflow.md) ## 06 - 与workflow异步框架的结合 ### 1. Server 下面我们通过一个具体例子来呈现 - Echo RPC在接收到请求时,向下游发起一次http请求 - 对下游请求完成后,我们将http response的body信息填充到response的message里,回复给客户端 - 我们不希望阻塞/占据着Handler的线程,所以对下游的请求一定是一次异步请求 - 首先,我们通过Workflow框架的工厂``WFTaskFactory::create_http_task``创建一个异步任务http_task - 然后,我们利用RPCContext的``ctx->get_series()``获取到ServerTask所在的SeriesWork - 最后,我们使用SeriesWork的``push_back``接口将http_task放到SeriesWork的后面 ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { auto *http_task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [request, response](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); response->mutable_message()->assign((const char *)data, len); } else response->set_message("Error: " + std::to_string(task->get_error())); printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); }); ctx->get_series()->push_back(http_task); } }; ~~~ ### 2. Client 下面我们通过一个具体例子来呈现 - 我们并行发出两个请求,1个是rpc请求,1个是http请求 - 两个请求都结束后,我们再发起一次计算任务,计算两个数的平方和 - 首先,我们通过RPC Client的``create_Echo_task``创建一个rpc异步请求的网络任务rpc_task - 然后,我们通过Workflow框架的工厂``WFTaskFactory::create_http_task``和``WFTaskFactory::create_go_task``分别创建异步网络任务http_task,和异步计算任务calc_task - 最后,我们利用串并联流程图,乘号代表并行、大于号代表串行,将3个异步任务组合起来执行start ~~~cpp void calc(int x, int y) { int z = x * x + y * y; printf("calc result: %d\n", z); } int main() { Example::SRPCClient client("127.0.0.1", 1412); auto *rpc_task = client.create_Echo_task([](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); auto *http_task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { std::string body; const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); body.assign((const char *)data, len); printf("%s\n\n", body.c_str()); } else printf("Http request fail\n\n"); }); auto *calc_task = WFTaskFactory::create_go_task(calc, 3, 4); EchoRequest req; req.set_message("Hello!"); req.set_name("1412"); rpc_task->serialize_input(&req); WFFacilities::WaitGroup wait_group(1); SeriesWork *series = Workflow::create_series_work(http_task, [&wait_group](const SeriesWork *) { wait_group.done(); }); series->push_back(rpc_task); series->push_back(calc_task); series->start(); wait_group.wait(); return 0; } ~~~ ### 3. Upstream SRPC可以直接使用Workflow的任何组件,最常用的就是[Upstream](https://github.com/sogou/workflow/blob/master/docs/about-upstream.md),SRPC的任何一种client都可以使用Upstream。 我们通过参数来看看如何构造可以使用Upstream的client: ```cpp #include "workflow/UpstreamManager.h" int main() { // 1. 创建upstream并添加实例 UpstreamManager::upstream_create_weighted_random("echo_server", true); UpstreamManager::upstream_add_server("echo_server", "127.0.0.1:1412"); UpstreamManager::upstream_add_server("echo_server", "192.168.10.10"); UpstreamManager::upstream_add_server("echo_server", "internal.host.com"); // 2. 构造参数,填上upstream的名字 RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; client_params.host = "echo_server"; client_params.port = 1412; // 这个port只用于upstream URI解析,不影响具体实例的选取 // 3. 用参数创建client,其他用法与示例类似 Example::SRPCClient client(&client_params); ... ``` 如果使用了ConsistentHash或者Manual方式创建upstream,则我们往往需要对不同的task进行区分、以供选取算法使用。这时候可以使用client task上的`int set_uri_fragment(const std::string& fragment);`接口,设置请求级相关的信息。 这个域的是URI里的fragment,语义请参考[RFC3689 3.5-Fragment](https://datatracker.ietf.org/doc/html/rfc3986#section-3.5),任何需要用到fragment的功能(如其他选取策略里附带的其他信息),都可以利用这个域。 srpc-0.10.1/docs/docs-07-srpc-http.md000066400000000000000000000153131454502251400171150ustar00rootroot00000000000000[English version](/docs/docs-07-srpc-http.md) ## 07 - 使用SPRC、TRPC、Thrift发送Http **SRPC**支持**HTTP**协议,只要把**idl**的内容作填到**HTTP**的**body**中,并且在**header**里填上**idl**的类型(**json**/**protobuf**/**thrift**),就可以与其他框架通过**HTTP**协议互通,由此可以实现跨语言。 - 启动**SRPCHttpServer**/**TRPCHttpServer**/**ThriftHttpServer**,可以接收由任何语言实现的HTTP client发过来的请求; - 启动**SRPCHttpClient**/**TRPCHttpClient**/**ThriftHttpClient**,也可以向任何语言实现的Http Server发送请求; - **HTTP header**:`Content-Type`设置为`application/json`表示json,`application/x-protobuf`表示protobuf,`application/x-thrift`表示thrift; - **HTTP body**: 如果body中涉及**bytes**类型,**json**中需要使用**base64**进行encode; ### 1. 示例 想实现**SRPCHttpClient**,可以把[tutorial-02-srpc_pb_client.cc](https://github.com/sogou/srpc/blob/master/tutorial/tutorial-02-srpc_pb_client.cc)或者[tutorial-09-client_task.cc](https://github.com/sogou/srpc/blob/master/tutorial/tutorial-09-client_task.cc)中的`SRPCClient`改成`SRPCHttpClient`即可。 在项目的[README.md](/docs//README_cn.md#6-run)中,我们演示了如何使用**curl**向**SRPCHttpServer**发送请求,下面我们给出例子演示如何使用**python**作为客户端,向**TRPCHttpServer**发送请求。 **proto文件:** ```proto syntax="proto3"; // proto2 or proto3 are both supported package trpc.test.helloworld; message AddRequest { string message = 1; string name = 2; bytes info = 3; int32 error = 4; }; message AddResponse { string message = 1; }; service Batch { rpc Add(AddRequest) returns (AddResponse); }; ``` **python客户端:** ```py import json import requests from base64 import b64encode class Base64Encoder(json.JSONEncoder): def default(self, o): if isinstance(o, bytes): return b64encode(o).decode() return json.JSONEncoder.default(self, o) headers = {'Content-Type': 'application/json'} req = { 'message': 'hello', 'name': 'k', 'info': b'i am binary' } print(json.dumps(req, cls=Base64Encoder)) ret = requests.post(url = "http://localhost:8800/trpc.test.helloworld.Batch/Add", headers = headers, data = json.dumps(req, cls=Base64Encoder)) print(ret.json()) ``` ### 2. 请求路径拼接 [README.md](/docs//README_cn.md#6-run)中,我们可以看到,路径是由service名和rpc名拼接而成的。而对于以上带package名 `package trpc.test.helloworld;`的例子, package名也需要拼接到路径中,**SRPCHttp** 和 **TRPCHttp** 的拼接路径方式并不一样,而**ThriftHttp**由于SRPC的thrift不支持多个service所以无需拼接任何路径。 我们以**curl**为例子: 与**SRPCHttpServer**互通: ```sh curl 127.0.0.1:8811/trpc/test/helloworld/Batch/Add -H 'Content-Type: application/json' -d '{...}' ``` 与**TRPCHttpServer**互通: ```sh curl 127.0.0.1:8811/trpc.test.helloworld.Batch/Add -H 'Content-Type: application/json' -d '{...}' ``` 与**ThriftHttpServer**互通: ```sh curl 127.0.0.1:8811 -H 'Content-Type: application/json' -d '{...}' ``` ### 3. HTTP状态码 SRPC支持server在`process()`中设置状态码,接口为**RPCContext**上的`set_http_code(int code)`。只有在框架能够正确处理请求的情况下,该错误码才有效,否则会被设置为框架层级的错误码。 **用法:** ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { if (req->name() != "workflow") ctx->set_http_code(404); // 设置HTTP状态码404 else resp->set_message("Hi back"); } }; ~~~ **CURL命令:** ~~~sh curl -i 127.0.0.1:1412/Example/Echo -H 'Content-Type: application/json' -d '{message:"from curl",name:"CURL"}' ~~~ **结果:** ~~~sh HTTP/1.1 404 Not Found SRPC-Status: 1 SRPC-Error: 0 Content-Type: application/json Content-Encoding: identity Content-Length: 21 Connection: Keep-Alive ~~~ **注意:** 我们依然可以通过返回结果的header中的`SRPC-Status: 1`来判断这个请求在框架层面是正确的,`404`是来自server的状态码。 ### 4. HTTP Header 用户可以通过以下三个接口来设置/获取http header: ~~~cpp bool get_http_header(const std::string& name, std::string& value) const; bool set_http_header(const std::string& name, const std::string& value); bool add_http_header(const std::string& name, const std::string& value); ~~~ 对于**server**来说,这些接口在`RPCContext`上。 对于**client**来说,需要通过`RPCClientTask`设置**请求上的http header**、并且在回调函数的`RPCContext`上**获取回复上的http header**,用法如下所示: ~~~cpp int main() { Example::SRPCHttpClient client("127.0.0.1", 80); EchoRequest req; req.set_message("Hello, srpc!"); auto *task = client.create_Echo_task([](EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) { std::string value; ctx->get_http_header("server_key", value); // 获取回复中的header } }); task->serialize_input(&req); task->set_http_header("client_key", "client_value"); // 设置请求中的header task->start(); wait_group.wait(); return 0; } ~~~ ### 5. IDL传输格式问题 如果我们填写的是Protobuf且用的标准为proto3,每个域由于没有optional和required区分,所以都是带有默认值的。如果我们设置的值正好等于默认值,则proto3不能识别为被set过,就不能被序列化的时候发出。 在protobuf转json的过程中,SRPC在**RPCContext上**提供了几个接口,支持 [JsonPrintOptions](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.util.json_util#JsonPrintOptions) 上的功能。具体接口与用法描述可以查看:[rpc_context.h](/src/rpc_context.h) **示例:** ```cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { resp->set_message("Hi back"); resp->set_error(0); // 0是error类型int32在proto3中的默认值 ctx->set_json_always_print_primitive_fields(true); // 带上所有原始域 ctx->set_json_add_whitespace(true); // 增加json格式的空格 } }; ``` **原始输出:** ```sh {"message":"Hi back"} ``` **通过RPCContext设置过json options之后的输出:** ```sh { "message": "Hi back", "error": 0 } ``` srpc-0.10.1/docs/docs-08-tracing.md000066400000000000000000000111431454502251400166160ustar00rootroot00000000000000[English version](/docs/en/docs-08-tracing.md) ## 08 - 上报Tracing到OpenTelemetry **SRPC**支持产生和上报链路信息trace和span,并且可以通过多种途径进行上报,其中包括本地导出数据和上报到[OpenTelemetry](https://opentelemetry.io). **SRPC**遵循**OpenTelemetry**的[数据规范(data specification)](https://github.com/open-telemetry/opentelemetry-specification)以及[w3c的trace context](https://www.w3.org/TR/trace-context/),因此可以使用插件**RPCTraceOpenTelemetry**进行上报。 秉承着**Workflow**的风格,所有的上报都是异步任务模式,对RPC的请求和服务不会产生任何性能影响。 ### 1. 用法 先构造插件`RPCTraceOpenTelemetry`,然后通过`add_filter()`把插件加到**server**或**client**中。 以[tutorial-02-srpc_pb_client.cc](https://github.com/sogou/srpc/blob/master/tutorial/tutorial-02-srpc_pb_client.cc)作为client的示例,我们如下加两行代码: ```cpp int main() { Example::SRPCClient client("127.0.0.1", 1412); RPCTraceOpenTelemetry span_otel("http://127.0.0.1:4318"); client.add_filter(&span_otel); ... } ``` 以[tutorial-01-srpc_pb_server.cc](https://github.com/sogou/srpc/blob/master/tutorial/tutorial-01-srpc_pb_server.cc)作为server的示例,也增加类似的两行。同时我们还增加一个本地插件,用于把本次请求的trace数据也在屏幕上打印: ```cpp int main() { SRPCServer server; RPCTraceOpenTelemetry span_otel("http://127.0.0.1:4318"); server.add_filter(&span_otel); RPCTraceDefault span_log; // 这个插件会把本次请求的trace信息打到屏幕上 server.add_filter(&span_log); ... } ``` 执行命令`make tutorial`可以编译出示例程序,并且分别把server和client跑起来,我们可以在屏幕上看到一些tracing信息: image 我们可以看到上图client这边的span_id: **04d070f537f17d00**,它在下图server这里变成了parent_span_id: **04d070f537f17d00**: image ### 2. Traces上报到Jaeger 打开Jaeger的主页,我们可以找到我们名为**Example**的服务(service)和名为**Echo**的函数(method)。这一个tracing记录上有两个span节点,是由server和client分别上报的。 image 我们可以在Jaeger看到client所上报的span_id: **04d070f537f17d00**和server所上报的span_id: **00202cf737f17d00**,同时也是能对应上刚才在屏幕看到的id的。 image ### 3. 参数 多久收集一份trace信息、上报请求的重试次数、以及其他参数,都可以通过`RPCTraceOpenTelemetry`的构造函数指定。代码参考:[src/module/rpc_trace_filter.h](https://github.com/sogou/srpc/blob/master/src/module/rpc_trace_filter.h#L238) 默认每秒收集1000条trace信息,并且透传tracing信息等其他功能也已遵循上述规范实现。 ### 4. Attributes 我们可以通过`add_attributes()`添加某些额外的信息,比如数据规范中的OTEL_RESOURCE_ATTRIBUTES。 注意我们的service名"Example"也是设置到attributes中的,key为`service.name`。如果用户也在OTEL_RESOURCE_ATTRIBUTES中使用了`service.name`这个key,则SRPC的service name优先级更高,参考:[OpenTelemetry#resource](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry#resource) ### 5. Log和Baggage SRPC提供了`log()`和`baggage()`接口,用户可以添加需要通过链路透传的数据。 ```cpp void log(const RPCLogVector& fields); void baggage(const std::string& key, const std::string& value); ``` 作为Server,我们可以使用`RPCContext`上的接口来添加log: ```cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { resp->set_message("Hi back"); ctx->log({{"event", "info"}, {"message", "rpc server echo() end."}}); } }; ``` 作为client,我们可以使用`RPCClientTask`上的接口添加log: ```cpp srpc::SRPCClientTask *task = client.create_Echo_task(...); task->log({{"event", "info"}, {"message", "log by rpc client echo()."}}); ``` srpc-0.10.1/docs/docs-09-metrics.md000066400000000000000000000264111454502251400166420ustar00rootroot00000000000000[English version](/docs/en/docs-09-metrics.md) ## 09 - 上报Metrics **Metrics**(指标)是常用的监控需求,**SRPC**支持产生与统计Metrics,并通过多种途径上报,其中包括上报到[Prometheus](https://prometheus.io/)和[OpenTelemetry](https://opentelemetry.io)。 秉承着**Workflow**的风格,所有的上报都是异步任务推送或拉取模式,对RPC的请求和服务不会产生任何性能影响。 本文档会介绍Metrics的概念、结合tutorial-16对接口进行讲解、上报Prometheus的特点、上报OpenTelemetry的特点、以及介绍使用了thread local进行性能提速的Var模块。 ### 1. Metrics概念介绍 **Prometheus**的数据类型可以参考官方文档:[Concepts - Metrics](https://prometheus.io/docs/concepts/metric_types/) 和 [Type of Metrics](https://prometheus.io/docs/tutorials/understanding_metric_types/)。 **OpenTelemetry**的可以参考[数据规范(data specification)](https://github.com/open-telemetry/opentelemetry-specification)以及[Metrics的datamodel.md](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/datamodel.md)。 其中四种基本指标的概念都是一致的,因此SRPC对这四种指标进行了最基本的支持,可分别对应到Prometheus和OpenTelemetry的上报数据中。参考下表: |指标类型|Prometheus|OpenTelemetry| SRPC中的创建接口 | SRPC中的常用操作 | |-------|----------|------------|-----------------|-------------------| |单个数值| Gauge | Gauge | create_gauge(name, help); | void increase(); void decrease();| |计数器 | Counter | Sum | create_counter(name, help); |GaugeVar *add(labels);| |直方图 |Histogram | Histogram | create_histogram(name, help, buckets); |void observe(data);| |采样 | Summary | Summary | create_summary(name, help, quantiles); |void observe(data);| 四种指标的大致描述: 1. **单个数值**:简单数值的度量,可以简单的增减。 2. **计数器**:累计的度量,可以通过添加若干**label**去区分不同label下的数值。 3. **直方图**:对观察observer()得到的接口进行分区间累加,因此需要传入**buckets**告知需要划分的区间; 比如传入的bucket分桶值是{ 1, 10, 100 }这3个数,则我们可以得到数据分布于{ 0 - 1, 0 - 10, 0 - 100, 0 - +Inf }4个区间的数据累计值,同时也会得到整体的总以及数据个数。 4. **采样**:与直方图类似,但传入的是**分位数quantiles**,比如{0.5, 0.9}以及它们的精度,主要用于事先不知道数据分布具体数值的场景(相比之下,直方图需要传入具体数值)。 采样是带有**时间窗口**的,可以通过接口指定**max_age统计窗口时长**以及**age_bucket内部分桶个数**,SRPC中默认的是60秒分5个桶切换时间窗口。 考虑到**采样Summary**本身的统计复杂性,一般来说建议优先使用**直方图Histogram**。 为了方便用户使用,目前SRPC里这四种指标都使用double类型进行统计。 ### 2. 用法示例 我们结合[tutorial-16-server_with_metrics.cc](/tutorial/tutorial-16-server_with_metrics.cc)看看基本用法,本示例虽然是server,但client中的用法也是一样的。 #### (1) 创建插件 我们选择Prometheus作为我们的上报对象,因此需要使用**RPCMetricsPull**插件。 ~~~cpp #include "srpc/rpc_metrics_filter.h" // Metrics插件所在的头文件 int main() { SRPCServer server; ExampleServiceImpl impl; RPCMetricsPull filter; // 创建一个插件 filter.init(8080); // 配合Prometheus中填好的收集数据的端口 ~~~ #### (2) 添加指标 这个**RPCMetricsPull**插件本身自带了统计部分常用指标,包括整体请求个数统计、按照service和method作为label的不同维度的请求个数统计以及请求耗时的分位数统计等。 用户可以自行增加想要统计的值,这里我们增加一个用于统计请求大小的直方图histogram,名字为"echo_request_size",上报时的指标描述信息是"Echo request size",数据bucket划分是 { 1, 10, 100 } 。 ~~~cpp filter.create_histogram("echo_request_size", "Echo request size", {1, 10, 100}); ~~~ 说明: **添加指标的时机** 指标是可以随时添加的,即使server/client跑起来之后也可以。但**必须在操作这个指标之前添加**,否则获取指标的时候会获取到空指针,无法进行统计操作。 **指标的名字** 指标的名字是**全局唯一**的(无论是四种基本类型中的哪种),且**只能包含大小写字母和下划线,即a-z, A-Z, _** 。如果使用已经存在的名字创建指标,则会创建失败并返回NULL。 一旦创建成功,我们之后都会使用这同一个名字去操作这个指标。 **创建其他指标的接口** 可以查看刚才include的头文件[rpc_metrics_filter.h](/src/module/rpc_metrics_filter.h)中的:`class RPCMetricsFilter`。 #### (3) 把插件添加到server/client中 由于我们需要操作指标时,是需要调用这个插件上的接口的,因此我们在service中保留一下这个指针。这只是示例程序的用法,用户可以使用自己习惯的方式: ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override; void set_filter(RPCMetricsPull *filter) { this->filter = filter; } private: RPCMetricsPull *filter; // 保留了插件的指针,并实现接口设置进去,这并非SRPC框架的接口 }; ~~~ main函数中继续这样写: ~~~cpp int main() { ... impl.set_filter(&filter); // 设置到我们刚才为service留的接口中 server.add_filter(&filter); // client也一样可以调用add_filter() server.add_service(&impl); filter.deinit(); return 0; } ~~~ #### (4) 操作指标 我们在每次收到请求的时候,都把EchoRequest的大小统计到刚才创建的直方图指标上: ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { resp->set_message("Hi back"); this->filter->histogram("echo_request_size")->observe(req->ByteSizeLong()); } ~~~ 可以看到,我们通过filter上的histogram()接口,就可以带着刚才的名字去到指标的指针,并且通过observe()填入数据大小。 四种基本类型的获取接口如下: ~~~cpp class RpcMetricsFilter : public RPCFilter { GaugeVar *gauge(const std::string& name); CounterVar *counter(const std::string& name); HistogramVar *histogram(const std::string& name); SummaryVar *summary(const std::string& name); ~~~ 如果找到,会返回四种基本指标类型的指针,可以如示例进行下一步操作比如histogram的统计接口observe(),**注意:不存在这个名字则会返回空指针,因此要保证我们拿到的变量一定是成功创建过的**。 四种类型常用的操作接口如上文表格所示,具体可以参考[rpc_var.h](/src/var/rpc_var.h)。 值得说明一下Counter类型的接口: ~~~cpp class CounterVar : public RPCVar { GaugeVar *add(const std::map& labels); ~~~ 接口可以对Counter指标添加某个维度的Gauge值,各维度的统计是分开的,且labels为一个map,即可以通过多组label指定一个维度,比如: ~~~cpp filter->counter("service_method_count")->add({"service", "Example"}, {"method", "Echo"}})->increase(); ~~~ 就可以获得一个针对`{service="Example",method="Echo"}`统计出来的数值。 #### (5) 自动上报 SRPC的插件都是自动上报的,因此无需用户调用任何接口。我们尝试调用client发送请求产生一些统计数据,然后看看上报出来的数据是什么。 ~~~sh ./srpc_pb_client message: "Hi back" message: "Hi back" ~~~ 由于Prometheus是使用Pull模式拉取,即会通过我们注册到Prometheus的端口和/metrics进行拉取,也就是我们刚才初始化上报插件需要配对上端口的原因。通过与Prometheus相同的方式,我们可以本地访问一下,会看到这样的一些数据: ~~~sh curl localhost:8080/metrics # HELP total_request_count total request count # TYPE total_request_count gauge total_request_count 2.000000 # HELP total_request_method request method statistics # TYPE total_request_method counter total_request_method{method="Echo",service="Example"} 2.000000 # HELP total_request_latency request latency nano seconds # TYPE total_request_latency summary total_request_latency{quantile="0.500000"} 645078.500000 total_request_latency{quantile="0.900000"} 645078.500000 total_request_latency_sum 1290157.000000 total_request_latency_count 2 # HELP echo_request_size Echo request size # TYPE echo_request_size histogram echo_request_size_bucket{le="1.000000"}0 echo_request_size_bucket{le="10.000000"}0 echo_request_size_bucket{le="100.000000"}2 echo_request_size_bucket{le="+Inf"} 2 echo_request_size_sum 40.000000 echo_request_size_count 2 ~~~ 可以看到,我们针对tutorial-02的client产生的两个请求,分别获得的四种基本指标的统计数据。其中histogram是我们创建的,而gauge、counter、summary都是插件中自带的。插件中默认统计的数据还会陆续添加,以方便开发者。 ### 3. 上报Prometheus的特点 上报Prometheus主要特点刚才已经大概描述过: 1. 使用Pull模式,定期被收集; 2. 需要指定我们被收集数据的端口; 3. 通过/metrics返回具体数据内容; 4. 数据内容为Prometheus所约定的string格式; 界面参考如下: ![prometheus_img](https://raw.githubusercontent.com/wiki/holmes1412/holmes1412/workflow-prometheus_example.png) ### 4. 上报OpenTelemetry的特点 上报OpenTelemetry的主要特点: 1. 使用推送模式,定期发送http请求; 2. 插件需要填入要上报的URL; 3. 内部默认通过/v1/metrics上报数据; 4. 数据内容为OpenTelemetry所约定的[protobuf](/src/module/proto/opentelemetry_metrics.proto); 基本接口参考: ~~~cpp class RPCMetricsOTel : public RPCMetricsFilter { public: RPCMetricsOTel(const std::string& url); RPCMetricsOTel(const std::string& url, unsigned int redirect_max, unsigned int retry_max, size_t report_threshold, size_t report_interval); ~~~ 用户可以指定累计多少个请求上报一次或者累计多久上报一次,默认为累计100个请求或1秒上报一次。 ### 5. Var模块 上面可以看到,我们每个请求都会对全局唯一名称指定的变量进行操作,那么多线程调用时,对复杂的指标类型(比如直方图或者采样)操作会成为性能瓶颈吗? 答案是不会的,因为通过filter获取对应指标的接口是**thread local**的。 SRPC内部引入了线程安全的var结构,每次获取var时调用的接口,拿到的都是thread local的指标指针,每次统计也都是分别收集到本线程,因此多线程情况下的统计不会造成对全局的争抢。而上报是异步上报的,在上报被触发的时候,全局会通过expose()挨个把每个线程中相应的指标reduce()到一起,最后通过具体模块需要的格式进行上报。 srpc-0.10.1/docs/docs-10-http-with-modules.md000066400000000000000000000301451454502251400205610ustar00rootroot00000000000000 ## 10 - 带生态插件的HttpServer和HttpClient **srpc**提供带有插件功能的**HttpServer**和**HttpClient**,可以上报**trace**和**metrcis**,用法和功能完全兼容**Workflow**,添加插件的用法也和srpc目前的Server和Client一样,用于使用**Http功能**同时需**要采集trace、metrics等信息并上报**的场景。 此功能更加适用于原先已经使用Workflow收发Http的开发者,几乎不改动现有代码即可拥有生态上报功能。 (1) 补充插件基本介绍: - **链路信息**:如何使用**trace**插件,并上报到[OpenTelemetry](https://opentelemetry.io)? 参考:[docs-08-tracing.md](https://github.com/sogou/srpc/blob/master/docs/docs-08-tracing.md) - **监控指标**:如何使用**metrics**插件,并且上报到[Prometheus](https://prometheus.io/)和[OpenTelemetry](https://opentelemetry.io)?参考:[docs-09-metrics.md](https://github.com/sogou/srpc/blob/master/docs/docs-09-metrics.md) (2) 补充Workflow中的Http用法: - [Http客户端任务:wget](https://github.com/sogou/workflow/blob/master/docs/tutorial-01-wget.md) - [ttp服务器:http_echo_server](https://github.com/sogou/workflow/blob/master/docs/tutorial-04-http_echo_server.md) (3) 补充SRPC中带插件上报功能的示例代码: - [tutorial-17-http_server.cc](/tutorialtutorial-17-http_server.cc) - [tutorial-18-http_client.cc](/tutorialtutorial-18-http_client.cc) ### 1. HttpServer用法 ```cpp int main() { // 1. 构造方式与Workflow类似,Workflow用法是: // WFHttpServer server([](WFHttpTask *task){ /* process */ }); srpc::HttpServer server([](WFHttpTask *task){ /* process */ }); // 2. 插件与SRPC通用,添加方式一致 srpc::RPCTraceOpenTelemetry otel("http://127.0.0.1:4318"); server.add_filter(&otel); // 3. server启动方式与Workflow/SRPC一样 if (server.start(1412) == 0) { ... } } ``` 由于这里直接收发Http请求,因此有几个注意点: 1. 操作的是`WFHttpTask`(与Workflow中的一致),而不是由Protobuf或者Thrift等IDL定义的结构体; 2. 接口也不再是RPC里定义的**Service**了,因此也无需派生ServiceImpl实现RPC函数,而是直接给Server传一个`process函数`; 3. `process函数`格式也与Workflow一致:`std::function`; 4. process函数里拿到的参数是task,通过task->get_req()和task->get_resp()可以拿到请求与回复,分别是`HttpRequest`和`HttpResponse`,而其他上下文也在task而非**RPCContext**上; ### 2. HttpClient用法 ```cpp int main() { // 1. 先构造一个client(与Workflow用法不同,与SRPC用法类似) srpc::HttpClient client; // 2. 插件与SRPC通用,添加方式一致 srpc::RPCTraceDefault trace_log; client.add_filter(&trace_log); // 3. 发出Http请求的方式与Workflow类似,Workflow用法是: // WFHttpTask *task = WFTaskFactory::create_http_task(...); // 函数名、参数、返回值与Workflow用法一致 WFHttpTask *task = client.create_http_task("http://127.0.0.1:1412", REDIRECT_MAX, RETRY_MAX, [](WFHttpTask *task){ /* callback */ }); task->start(); ... return 0; } ``` 同样几个注意点: 1. 操作的是`WFHttpTask`(与Workflow中的一致),而不是由Protobuf或者Thrift等IDL定义的结构; 2. 如果想拿到req,可以`task->get_req()`,拿到的是`HttpRequest`,而不是XXXRequest之类; 3. 和请求相关的上下文也不再需要**RPCContext**了,都在task上; 4. callback函数格式也与Workflow一致:`std::function`; ### 3. Http协议采集的数据 **OpenTelemetry的trace数据官方文档**: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/http/ SRPC框架的trace模块已经采集以下内容,并会通过各自的filter以不同的形式发出。 **1. 公共指标** | 指标名 | 含义 | 类型 | 例子 | 备注 | |-------|-----|-----|------|-----| |task.state| 框架状态码 | int | 0 |(以下简称state) | |task.error| 框架错误码 | int | 1 |state!=0时才有 | |http.status_code| Http返回码 | string | 200 | state=0时才有 | |http.method| Http请求方法 | string | GET | | |http.scheme| scheme | string | https | | |http.request_content_length | 请求大小 | int | 3840 | state=0时才有 | |http.response_content_length| 回复大小 | int | 141 | state=0时才有 | |net.sock.family| 协议地址族 | string | inet | | |net.sock.peer.addr| 远程地址 | string | 10.xx.xx.xx | state=0时才有 | |net.sock.peer.port| 远程端口 | int | 8080 | state=0时才有 | **2. Client指标** | 指标名 | 含义 | 类型 | 例子 | 备注 | |-------|-----|-----|------|-----| |srpc.timeout_reason| 超时原因 | int | 2 | state=WFT_STATE_SYS_ERROR(1)和error=ETIMEDOUT(116)时才有 | |http.resend_count| 框架重试次数 | int | 0 | state=0时才有 | |net.peer.addr| uri请求的地址 | string | 10.xx.xx.xx | | |net.peer.port| uri请求的端口 | int | 80 | | |* http.url| 请求的url | string | | | state与error参考:[workflow/src/kernel/Communicator.h](workflow/src/kernel/Communicator.h) timeout_reason参考:[workflow/src/kernel/CommRequest.h](workflow/src/kernel/CommRequest.h) **3. Server指标** | 指标名 | 含义 | 类型 | 例子 | 备注 | |-------|-----|-----|------|-----| |net.host.name| 服务器名称 | string | example.com | 虚拟主机名,来自比如对方header里发过来的Host信息等 | |net.host.port| 监听的端口 | int | 80 | | |http.target| 完整的请求目标 | string | /users/12314/?q=ddds | | |http.client_ip| 原始client地址 | string | 10.x.x.x | 有时候会有,比如转发场景下,header中带有“X-Forwarded-For”等 | **OpenTelemetry的metrics数据官方文档(SRPC正在支持中)**: https://opentelemetry.io/docs/reference/specification/metrics/semantic_conventions/http-metrics/ ### 4. 示例数据 以下通过client-server的调用,我们可以看到这样的链路图: ``` [trace_id : 005028aa52fb0000005028aa52fb0002] [client]->[server] timeline: 1681380123462593000 1681380123462895000 1681380123463045000 1681380123463213000 [client][begin].........................................................................[end] span_id : 0400fb52aa285000 [server][begin].....................[end] span_id : 00305c54aa285000 parent_span_id : 0400fb52aa285000 ``` 先通过`make tutorial`命令可以把tutorial里的`http_server`和`http_client`编出来,其中tutorial-17-http_server.cc中可以把上报OpenTelemetry的插件打开: ```cpp srpc::RPCTraceOpenTelemetry otel("http://127.0.0.1:4318"); srpc::HttpServer server(process); server.add_filter(&otel); ... ``` 分别按照如下执行,可以看到我们的client通过RPCTraceDefault插件本地打印的trace信息: ```sh ./http_client ``` ``` callback. state = 0 error = 0 Hello from server! finish print body. body_len = 31 [SPAN_LOG] trace_id: 005028aa52fb0000005028aa52fb0002 span_id: 0400fb52aa285000 start_time: 1681380123462593000 finish_time: 1681380123463213000 duration: 620000(ns) http.method: GET http.request_content_length: 123145504128464 http.resend_count: 0 http.response_content_length: 31 http.scheme: http http.status_code: 200 net.peer.name: 127.0.0.1 net.peer.port: 1412 net.sock.family: inet net.sock.peer.addr: 127.0.0.1 net.sock.peer.port: 1412 component: srpc.srpc span.kind: srpc.client state: 0 ^C ``` 我们把server对OpenTelemetry上报的Protobuf内容也同时打印出来: ```sh ./http_server ``` ```sh http server get request_uri: / [SPAN_LOG] trace_id: 005028aa52fb0000005028aa52fb0002 span_id: 00305c54aa285000 parent_span_id: 0400fb52aa285000 start_time: 1681380123462895000 finish_time: 1681380123463045000 duration: 150000(ns) http.method: GET http.request_content_length: 0 http.response_content_length: 31 http.scheme: http http.status_code: 200 http.target: / net.host.name: 127.0.0.1:1412 net.host.port: 1412 net.sock.family: inet net.sock.peer.addr: 127.0.0.1 net.sock.peer.port: 56591 component: srpc.srpc span.kind: srpc.server state: 0 resource_spans { resource { } instrumentation_library_spans { spans { trace_id: "\000P(\252R\373\000\000\000P(\252R\373\000\002" span_id: "\0000\\T\252(P\000" parent_span_id: "\004\000\373R\252(P\000" name: "GET" kind: SPAN_KIND_SERVER start_time_unix_nano: 1681380123462895000 end_time_unix_nano: 1681380123463045000 attributes { key: "http.method" value { string_value: "GET" } } attributes { key: "http.request_content_length" value { int_value: 0 } } attributes { key: "http.response_content_length" value { int_value: 31 } } attributes { key: "http.scheme" value { string_value: "http" } } attributes { key: "http.status_code" value { int_value: 200 } } attributes { key: "http.target" value { string_value: "/" } } attributes { key: "net.host.name" value { string_value: "127.0.0.1:1412" } } attributes { key: "net.host.port" value { int_value: 1412 } } attributes { key: "net.sock.family" value { string_value: "inet" } } attributes { key: "net.sock.peer.addr" value { string_value: "127.0.0.1" } } attributes { key: "net.sock.peer.port" value { int_value: 56591 } } status { } } } } ``` 可以看到用法和SRPC框架默认的trace模块是一样的。 ### 5. 其他 补充一些常见问题,帮助开发中更好地理解这个模块。 **Q1: 本次新增的HttpServer与HttpClient,与SRPCHttpServer/SRPCHttpClient有什么共同点/不同点?** 共同点: - 应用层协议相同,都是Http协议进行网络收发; - 采集的数据依据相同,都是从Http Header收集和透传出去; 不同点: - 用法不同:本次新增的Http模块是延续Workflow风格的用法,比如WFHttpTask;而SRPCHttp/ThriftHttp/TRPCHttp的用法是RPC模式,且包括了同步/异步/半同步的使用方式; - 接口不同:前者用url直接定位要发的请求,而server也是一个process函数作为处理请求的统一入口;而原先的模块对Http只是网络层面的收发,url中的路由信息是通过${service}和${method}进行拼接的,然后把Protobuf或者Thrift这个结构体作为Http协议的body发出; - 开发者接触的请求/回复不同:前者从task上拿出HttpRequest和HttpResponse,后者是Protobuf/Thrift里定义的Message; - 框架级state和error略有不同:前者是task.state和task.error,使用workflow的状态码,比如0表示成功;而后者是srpc.state和srpc.error,使用SRPC的[状态码](/src/rpc_basic.h),比如1表示成功; **Q2: 和Workflow原生的Http协议是什么关系?** srpc中新增的功能,开发者拿到的也是Workflow中定义的WFHttpTask,在实现上进行了行为派生,因此收发期间有几个切面可以进行一些模块化编程,这是和Workflow的Http相比更多的功能。 只要开发者把模块通过add_filter()加入到Server/Client中,通过产生的任务就是带有切面功能的,而服务开发者无需感知。 **Q3: 为什么放在SRPC项目中?** 目前生态插件所需要的几个功能都在SRPC项目中,包括: - 收集数据的rpc_module,包括生成trace_id等通用功能; - 上报信息的rpc_filter,包括使用Protobuf格式上报OpenTelemetry; - 统计监控指标的rpc_var; srpc-0.10.1/docs/en/000077500000000000000000000000001454502251400140745ustar00rootroot00000000000000srpc-0.10.1/docs/en/docs-01-idl.md000066400000000000000000000014371454502251400163370ustar00rootroot00000000000000[中文版](/docs/docs-01-idl.md) ## 01 - RPC IDL - Interface Description Languaue file - Backward and forward compatibility - Protobuf/Thrift ### Sample You can follow the detailed example below: - Take pb as an example. First, define an `example.proto` file with the ServiceName as `Example`. - The name of the rpc interface is `Echo`, with the input parameter as `EchoRequest`, and the output parameter as `EchoResponse`. - `EchoRequest` consists of two strings: `message` and `name`. - `EchoResponse` consists of one string: `message`. ~~~proto syntax="proto2"; message EchoRequest { optional string message = 1; optional string name = 2; }; message EchoResponse { optional string message = 1; }; service Example { rpc Echo(EchoRequest) returns (EchoResponse); }; ~~~ srpc-0.10.1/docs/en/docs-02-service.md000066400000000000000000000023351454502251400172260ustar00rootroot00000000000000[中文版](/docs/docs-02-service.md) ## 02 - RPC Service - It is the basic unit for SRPC services. - Each service must be generated by one type of IDLs. - Service is determined by IDL type, not by specific network communication protocol. ### Sample You can follow the detailed example below: - Use the same `example.proto` IDL above. - Run the official `protoc example.proto --cpp_out=./ --proto_path=./` to get two files: `example.pb.h` and `example.pb.cpp`. - Run the `srpc_generator protobuf ./example.proto ./` in SRPC to get `example.srpc.h`. - Derive `Example::Service` to implement the rpc business logic, which is an RPC Service. - Please note that this Service does not involve any concepts such as network, port, communication protocol, etc., and it is only responsible for completing the business logic that convert `EchoRequest` to `EchoResponse`. ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { response->set_message("Hi, " + request->name()); printf("get_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); } }; ~~~ srpc-0.10.1/docs/en/docs-03-server.md000066400000000000000000000051211454502251400170710ustar00rootroot00000000000000[中文版](/docs/docs-03-server.md) ## 03 - RPC Server - Each server corresponds to one port - Each server corresponds to one specific network communication protocol - One service may be added into multiple Servers - One Server may have one or more Services, but the ServiceName must be unique within that Server - Services from different IDLs can be added into the same Server ### Sample You can follow the detailed example below: - Follow the above `ExampleServiceImpl` Service - First, create an RPC Server and determine the proto file. - Then, create any number of Service instances and any number of Services for different protocols, and add these services to the Server through the `add_service()`interface. - Finally, use `start()` or `serve()` to start the services in the Server and handle the upcoming rpc requests through the Server. - Imagine that we can also derive more Serivce from `Example::Service`, which have different implementations of rpc `Echo`. - Imagine that we can create N different RPC Servers on N different ports, serving on different network protocols. - Imagine that we can use `add_service()` to add the same ServiceIMPL instance on different Servers, or we can use `add_service()` to add different ServiceIMPL instances on the same server. - Imagine that we can use the same `ExampleServiceImpl`, serving BPRC-STD, SRPC-STD, SRPC-Http at three different ports at the same time. - And we can use `add_service()` to add one `ExampleServiceImpl` related to Protobuf IDL and one `AnotherThriftServiceImpl` related to Thrift IDL to the same SRPC-STD Server, and the two IDLs work perfectly on the same port! ~~~cpp int main() { SRPCServer server_srpc; SRPCHttpServer server_srpc_http; BRPCServer server_brpc; ThriftServer server_thrift; TRPCServer server_trpc; TRPCHttpServer server_trpc_http; ExampleServiceImpl impl_pb; AnotherThriftServiceImpl impl_thrift; server_srpc.add_service(&impl_pb); server_srpc.add_service(&impl_thrift); server_srpc_http.add_service(&impl_pb); server_srpc_http.add_service(&impl_thrift); server_brpc.add_service(&impl_pb); server_thrift.add_service(&impl_thrift); server_trpc.add_service(&impl_pb); server_trpc_http.add_service(&impl_pb); server_srpc.start(1412); server_srpc_http.start(8811); server_brpc.start(2020); server_thrift.start(9090); server_trpc.start(2022); server_trpc_http.start(8822); getchar(); server_trpc_http.stop(); server_trpc.stop(); server_thrift.stop(); server_brpc.stop(); server_srpc_http.stop(); server_srpc.stop(); return 0; } ~~~ srpc-0.10.1/docs/en/docs-04-client.md000066400000000000000000000043761454502251400170550ustar00rootroot00000000000000[中文版](/docs/docs-04-client.md) ## 04 - RPC Client - Each Client corresponds to one specific target/one specific cluster - Each Client corresponds to one specific network communication protocol - Each Client corresponds to one specific IDL ### Sample You can follow the detailed example below: - Following the above example, the client is relatively simple and you can call the method directly. - Use `Example::XXXClient` to create a client instance of some RPC. The IP+port or URL of the target is required. - With the client instance, directly call the rpc function `Echo`. This is an asynchronous request, and the callback function will be invoked after the request is completed. - For the usage of the RPC Context, please check [RPC Context](/docs/en/rpc.md#rpc-context). ~~~cpp #include #include "example.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; int main() { Example::SRPCClient client("127.0.0.1", 1412); EchoRequest req; req.set_message("Hello!"); req.set_name("SRPCClient"); WFFacilities::WaitGroup wait_group(1); client.Echo(&req, [&wait_group](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); wait_group.done(); }); wait_group.wait(); return 0; } ~~~ ### Client startup parameters Client can be started directly by passing in ip, port, or through client startup parameters. The above example: ~~~cpp Example::SRPCClient client("127.0.0.1", 1412); ~~~ is equivalent to: ~~~cpp struct RPCClientParams param = RPC_CLIENT_PARAMS_DEFAULT; param.host = "127.0.0.1"; param.port = 1412; Example::SRPCClient client(¶m); ~~~ also equivalent to: ~~~cpp struct RPCClientParams param = RPC_CLIENT_PARAMS_DEFAULT; param.url = "srpc://127.0.0.1:1412"; Example::SRPCClient client(¶m); ~~~ Note that `RPC_CLIENT_PARAMS_DEFAULT` must be used to initialize the client's parameters, which contains a `RPCTaskParams`, including the default data_type, compress_type, retry_max and various timeouts. The specific struct can refer to [rpc_options.h](/src/rpc_options.h). srpc-0.10.1/docs/en/docs-05-context.md000066400000000000000000000064021454502251400172540ustar00rootroot00000000000000[中文版](/docs/docs-05-context.md) ## 05 - RPC Context - RPCContext is used specially to assist asynchronous interfaces, and can be used in both Service and Client. - Each asynchronous interface will provide a Context, which offers higher-level functions, such as obtaining the remote IP, the connection seqid, and so on. - Some functions on Context are unique to Server or Client. For example, you can set the compression mode of the response data on Server, and you can obtain the success or failure status of a request on Client. - On the Context, you can use ``get_series()`` to obtain the SeriesWork, which is seamlessly integrated with the asynchronous mode of Workflow. ### RPCContext API - Common #### `long long get_seqid() const;` One complete communication consists of request+response. The sequence id of the communication on the current socket connection can be obtained, and seqid=0 indicates the first communication. #### `std::string get_remote_ip() const;` Get the remote IP address. IPv4/IPv6 is supported. #### `int get_peer_addr(struct sockaddr *addr, socklen_t *addrlen) const;` Get the remote address. The in/out parameter is the lower-level data structure sockaddr. #### `const std::string& get_service_name() const;` Get RPC Service Name. #### `const std::string& get_method_name() const;` Get RPC Method Name. #### `SeriesWork *get_series() const;` Get the SeriesWork of the current ServerTask/ClientTask. ### RPCContext API - Only for client done #### `bool success() const;` For client only. The success or failure of the request. #### `int get_status_code() const;` For client only. The rpc status code of the request. #### `const char *get_errmsg() const;` For client only. The error info of the request. #### `int get_error() const;` For client only. The error code of the request. #### `void *get_user_data() const;` For client only. Get the user\_data of the ClientTask. If a user generates a task through the ``create_xxx_task()`` interface, the context can be recorded in the user_data field. You can set that field when creating the task, and retrieve it in the callback function. ### RPCContext API - Only for server process #### `void set_data_type(RPCDataType type);` For Server only. Set the data packaging type - RPCDataProtobuf - RPCDataThrift - RPCDataJson #### `void set_compress_type(RPCCompressType type);` For Server only. Set the data compression type (note: the compression type for the Client is set on Client or Task) - RPCCompressNone - RPCCompressSnappy - RPCCompressGzip - RPCCompressZlib - RPCCompressLz4 #### `void set_attachment_nocopy(const char *attachment, size_t len);` For Server only. Set the attachment. #### `bool get_attachment(const char **attachment, size_t *len) const;` For Server only. Get the attachment. #### `void set_reply_callback(std::function cb);` For Server only. Set reply callback, which is called after the operating system successfully writes the data into the socket buffer. #### `void set_send_timeout(int timeout);` For Server only. Set the maximum time for sending the message, in milliseconds. -1 indicates unlimited time. #### `void set_keep_alive(int timeout);` For Server only. Set the maximum connection keep-alive time, in milliseconds. -1 indicates unlimited time. srpc-0.10.1/docs/en/docs-06-workflow.md000066400000000000000000000135061454502251400174460ustar00rootroot00000000000000[中文版](/docs/docs-06-workflow.md) ## 06 - Integrating with the asynchronous Workflow framework ### 1. Server You can follow the detailed example below: - Echo RPC sends an HTTP request to the upstream modules when it receives the request. - After the request to the upstream modules is completed, the server populates the body of HTTP response into the message of the response and send a reply to the client. - We don't want to block/occupy the handler thread, so the request to the upstream must be asynchronous. - First, we can use `WFTaskFactory::create_http_task()` of the factory of Workflow to create an asynchronous http_task. - Then, we use `ctx->get_series()` of the RPCContext to get the SeriesWork of the current ServerTask. - Finally, we use the `push_back()` interface of the SeriesWork to append the http\_task to the SeriesWork. ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { auto *http_task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [request, response](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); response->mutable_message()->assign((const char *)data, len); } else response->set_message("Error: " + std::to_string(task->get_error())); printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); }); ctx->get_series()->push_back(http_task); } }; ~~~ ### 2. Client You can follow the detailed example below: - We send two requests in parallel. One is an RPC request and the other is an HTTP request. - After both requests are finished, we initiate a calculation task again to calculate the sum of the squares of the two numbers. - First, use `create_Echo_task()` of the RPC Client to create an rpc\_task, which is an asynchronous RPC network request. - Then, use `WFTaskFactory::create_http_task` and `WFTaskFactory::create_go_task` in the the factory of Workflow to create an asynchronous network task http\_task and an asynchronous computing task calc\_task respectively. - Finally, use the serial-parallel graph to organize three asynchronous tasks, in which the multiplication sign indicates parallel tasks and the greater than sign indicates serial tasks and then execute ``start()``. ~~~cpp void calc(int x, int y) { int z = x * x + y * y; printf("calc result: %d\n", z); } int main() { Example::SRPCClient client("127.0.0.1", 1412); auto *rpc_task = client.create_Echo_task([](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); auto *http_task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { std::string body; const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); body.assign((const char *)data, len); printf("%s\n\n", body.c_str()); } else printf("Http request fail\n\n"); }); auto *calc_task = WFTaskFactory::create_go_task(calc, 3, 4); EchoRequest req; req.set_message("Hello!"); req.set_name("1412"); rpc_task->serialize_input(&req); WFFacilities::WaitGroup wait_group(1); SeriesWork *series = Workflow::create_series_work(http_task, [&wait_group](const SeriesWork *) { wait_group.done(); }); series->push_back(rpc_task); series->push_back(calc_task); series->start(); wait_group.wait(); return 0; } ~~~ ### 3. Upstream SRPC can directly use any component of Workflow, the most commonly used is [Upstream](https://github.com/sogou/workflow/blob/master/docs/en/about-upstream.md), any kind of client of SRPC can use Upstream. You may use the example below to construct a client that can use Upstream through parameters: ```cpp #include "workflow/UpstreamManager.h" int main() { // 1. create upstream and add server instances UpstreamManager::upstream_create_weighted_random("echo_server", true); UpstreamManager::upstream_add_server("echo_server", "127.0.0.1:1412"); UpstreamManager::upstream_add_server("echo_server", "192.168.10.10"); UpstreamManager::upstream_add_server("echo_server", "internal.host.com"); // 2. create params and fill upstream name RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; client_params.host = "echo_server"; client_params.port = 1412; // this port only used when upstream URI parsing and will not affect the select of instances // 3. construct client by params, the rest of usage is similar as other tutorials Example::SRPCClient client(&client_params); ... ``` If we use the **ConsistentHash** or **Manual** upstream, we often need to distinguish different tasks for the selection algorithm. At this time, we may use the `int set_uri_fragment(const std::string& fragment);` interface on the client task to set request-level related information. This field is the fragment in the URI. For the semantics, please refer to [RFC3689 3.5-Fragment](https://datatracker.ietf.org/doc/html/rfc3986#section-3.5), any infomation that needs to use the fragment (such as other information included in some other selection policy), you may use this field as well. srpc-0.10.1/docs/en/docs-07-srpc-http.md000066400000000000000000000162221454502251400175170ustar00rootroot00000000000000[中文版](/docs/docs-07-srpc-http.md) ## 07 - Use Http with SRPC, TRPC and Thrift **srpc** supports **HTTP** protocol, which make it convenient to communicate to other language. Just fill the **IDL** content into **HTTP body**, and fill the **IDL** type(**json**/**protobuf**/**thrift**) into **Http header** , we communicate with other frameworks through the **HTTP** protocol. - **SRPCHttpServer**, **TRPCHttpServer** and **ThriftHttpServer** can receive HTTP requests from client implemented by any language. - **SRPCHttpClient**, **TRPCHttpClient** and **ThriftHttpClient** can send HTTP requests to servers implemented by any language. - **HTTP header**: `Content-Type` needs to be set as `application/json` when body is json, or set as `application/x-protobuf` when body is protobuf, or set as `application/x-thrift` when body is thrift; - **HTTP body**: If there is **bytes** type in body, **json** needs to be encoded with **base64**. ### 1. Example To implement **SRPCHttpClient** is so simple that we just need to refer to [tutorial-02-srpc_pb_client.cc](https://github.com/sogou/srpc/blob/master/tutorial/tutorial-02-srpc_pb_client.cc) or [tutorial-09-client_task.cc](https://github.com/sogou/srpc/blob/master/tutorial/tutorial-09-client_task.cc) and change the `SRPCClient` into `SRPCHttpClient`. [README.md](/docs/en/README.md#6-run) demonstrates how to use **curl** to send a request to **SRPCHttpServer**, and below we give an example to demonstrate how to use **python** as a client to send a request to **TRPCHttpServer**. **proto file :** ```proto syntax="proto3"; // proto2 or proto3 are both supported package trpc.test.helloworld; message AddRequest { string message = 1; string name = 2; bytes info = 3; int32 error = 4; }; message AddResponse { string message = 1; }; service Batch { rpc Add(AddRequest) returns (AddResponse); }; ``` **python client :** ```py import json import requests from base64 import b64encode class Base64Encoder(json.JSONEncoder): def default(self, o): if isinstance(o, bytes): return b64encode(o).decode() return json.JSONEncoder.default(self, o) headers = {'Content-Type': 'application/json'} req = { 'message': 'hello', 'name': 'k', 'info': b'i am binary' } print(json.dumps(req, cls=Base64Encoder)) ret = requests.post(url = "http://localhost:8800/trpc.test.helloworld.Batch/Add", headers = headers, data = json.dumps(req, cls=Base64Encoder)) print(ret.json()) ``` ### 2. Splicing the path of HTTP request [README.md](/docs/en/README_cn.md#6-run) shows that the path is concatenated by the service name and the rpc name. Moreover, for the above proto file example with the package name `package trpc.test.helloworld;`, the package name also needs to be spliced into the path. The splicing paths of **SRPCHttp** and **TRPCHttpClient** are different. For **ThriftHttp**, multi service is not supported in SRPC thrift, so no path is required. Let's take **curl** as an example: Request to **SRPCHttpServer**: ```sh curl 127.0.0.1:8811/trpc/test/helloworld/Batch/Add -H 'Content-Type: application/json' -d '{...}' ``` Request to **TRPCHttpServer**: ```sh curl 127.0.0.1:8811/trpc.test.helloworld.Batch/Add -H 'Content-Type: application/json' -d '{...}' ``` Request to **ThriftHttpServer**: ```sh curl 127.0.0.1:8811 -H 'Content-Type: application/json' -d '{...}' ``` ### 3. HTTP status code SRPC supports server to set HTTP status code in `process()`, the interface is `set_http_code(int code)` on **RPCContext**. This error code is only valid if the framework can handle the request correctly, otherwise it will be set to the SRPC framework-level error code. **Usage :** ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { if (req->name() != "workflow") ctx->set_http_code(404); // set HTTP status code as 404 else resp->set_message("Hi back"); } }; ~~~ **CURL command :** ~~~sh curl -i 127.0.0.1:1412/Example/Echo -H 'Content-Type: application/json' -d '{message:"from curl",name:"CURL"}' ~~~ **Result :** ~~~sh HTTP/1.1 404 Not Found SRPC-Status: 1 SRPC-Error: 0 Content-Type: application/json Content-Encoding: identity Content-Length: 21 Connection: Keep-Alive ~~~ **Notice :** We can still distinguish this request is correct at framework-level by `SRPC-Status: 1` in the header of the returned result, and `404` is the status code from the server. ### 4. HTTP Header Use the following three interfaces to get or set HTTP header: ~~~cpp bool get_http_header(const std::string& name, std::string& value) const; bool set_http_header(const std::string& name, const std::string& value); bool add_http_header(const std::string& name, const std::string& value); ~~~ For **server**, these interfaces are on `RPCContext`. For **client**, we need to set the **HTTP header on the request** through `RPCClientTask`, and get the **HTTP header on the response** on the `RPCContext` of the callback function. The usage is as follows: ~~~cpp int main() { Example::SRPCHttpClient client("127.0.0.1", 80); EchoRequest req; req.set_message("Hello, srpc!"); auto *task = client.create_Echo_task([](EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) { std::string value; ctx->get_http_header("server_key", value); // get the HTTP header on response } }); task->serialize_input(&req); task->set_http_header("client_key", "client_value"); // set the HTTP headers on request task->start(); wait_group.wait(); return 0; } ~~~ ### 5. proto3 transport format problem When IDL is protobuf, by default proto3 primitive fields with default values will be omitted in JSON output because there is no **optional** and **required** distinction. For example, an int32 field set to 0 will be omitted.So proto3 cannot be recognized as being set, and it cannot be emitted when serialized. JsonPrintOptions can use a flag to override the default behavior and print primitive fields regardless of their values. SRPC provides some interfaces on **RPCContext上** to implement this and other flags on [JsonPrintOptions](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.util.json_util#JsonPrintOptions). The specific interface and usage description can be refered in:[rpc_context.h](/src/rpc_context.h) **Exmaple :** ```cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { resp->set_message("Hi back"); resp->set_error(0); // the type of error is int32 and 0 is the default value of int32 ctx->set_json_always_print_primitive_fields(true); // with all primitive fields ctx->set_json_add_whitespace(true); // add spaces, line breaks and indentation } }; ``` **Origin output :** ```sh {"message":"Hi back"} ``` **After set json options on RPCContext:** ```sh { "message": "Hi back", "error": 0 } ``` srpc-0.10.1/docs/en/docs-08-tracing.md000066400000000000000000000111331454502251400172170ustar00rootroot00000000000000[中文版](/docs/docs-08-tracing.md) ## 08 - Report Tracing to OpenTelemetry **SRPC** supports generating and reporting tracing and spans, which can be reported in multiple ways, including exporting data locally or to [OpenTelemetry](https://opentelemetry.io). Since **SRPC** follows the [data specification](https://github.com/open-telemetry/opentelemetry-specification) of **OpenTelemetry** and the specification of [w3c trace context](https://www.w3.org/TR/trace-context/), now we can use **RPCTraceOpenTelemetry** as the reporting plugin. The report conforms to the **Workflow** style, which is pure asynchronous task and therefore has no performance impact on the RPC requests and services. ### 1. Usage After the plugin `RPCTraceOpenTelemetry` is constructed, we can use `add_filter()` to add it into **server** or **client**. For [tutorial-02-srpc_pb_client.cc](https://github.com/sogou/srpc/blob/master/tutorial/tutorial-02-srpc_pb_client.cc), add 2 lines like the following : ```cpp int main() { Example::SRPCClient client("127.0.0.1", 1412); RPCTraceOpenTelemetry span_otel("http://127.0.0.1:4318"); client.add_filter(&span_otel); ... } ``` For [tutorial-01-srpc_pb_server.cc](https://github.com/sogou/srpc/blob/master/tutorial/tutorial-01-srpc_pb_server.cc), add the similar 2 lines. We also add the local plugin to print the reported data on the screen : ```cpp int main() { SRPCServer server; RPCTraceOpenTelemetry span_otel("http://127.0.0.1:4318"); server.add_filter(&span_otel); RPCTraceDefault span_log; // this plugin will print the tracing info on the screen server.add_filter(&span_log); ... } ``` make the tutorial and run both server and client, we can see some tracing information on the screen. image We can find the span_id: **04d070f537f17d00** in client become parent_span_id: **04d070f537f17d00** in server: image ### 2. Traces on Jaeger Open the show page of Jaeger, we can find our service name **Example** and method name **Echo**. Here are two span nodes, which were reported by server and client respectively. image As what we saw on the screen, the client reported span_id: **04d070f537f17d00** and server reported span_id: **00202cf737f17d00**, these span and the correlated tracing information can be found on Jaeger, too. image ### 3. About Parameters How long to collect a trace, and the number of reported retries and other parameters can be specified through the constructor parameters of `RPCTraceOpenTelemetry`. Code reference: [src/module/rpc_trace_filter.h](https://github.com/sogou/srpc/blob/master/src/module/rpc_trace_filter.h#L238) The default value is to collect up to 1000 trace information per second, and features such as transferring tracing information through the srpc framework transparently have also been implemented, which also conform to the specifications. ### 4. Attributes We can also use `add_attributes()` to add some other informations as OTEL_RESOURCE_ATTRIBUTES. Please notice that our service name "Example" is set also thought this attributes, the key of which is `service.name`. If `service.name` is also provided in OTEL_RESOURCE_ATTRIBUTES by users, then srpc service name takes precedence. Refers to : [OpenTelemetry#resource](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry#resource) ### 5. Log and Baggage SRPC provides `log()` and `baggage()` to carry some user data through span. API : ```cpp void log(const RPCLogVector& fields); void baggage(const std::string& key, const std::string& value); ``` As a server, we can use `RPCContext` to add log annotation: ```cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { resp->set_message("Hi back"); ctx->log({{"event", "info"}, {"message", "rpc server echo() end."}}); } }; ``` As a client, we can use `RPCClientTask` to add log on span: ```cpp srpc::SRPCClientTask *task = client.create_Echo_task(...); task->log({{"event", "info"}, {"message", "log by rpc client echo()."}}); ``` srpc-0.10.1/docs/en/docs-09-metrics.md000066400000000000000000000276101454502251400172460ustar00rootroot00000000000000[中文版](/docs/docs-09-metrics.md) ## 09 - Report Metrics to OpenTelemetry / Prometheus **Metrics** are common monitoring requirements. **SRPC** supports the generation and statistics of Metrics, and reports through various way, including reporting to [Prometheus](https://prometheus.io/) and [OpenTelemetry](https://opentelemetry.io). The report conforms to the **Workflow** style, which is pure asynchronous task or pull mode, and therefore has no performance impact on the RPC requests and services. This document will introduce the concept of Metrics, explain the interface of [tutorial-16](/tutorial/tutorial-16-server_with_metrics.cc), the usage of reporting to Prometheus, the usage of reporting to OpenTelemetry, and introduce the Var module which uses thread local to speed up performance. ### 1. Introduction The metrics type of **Prometheus** can be refered through the official documentation : [Concepts - Metrics](https://prometheus.io/docs/concepts/metric_types/) and [Type of Metrics](https://prometheus.io/docs/tutorials/understanding_metric_types/)。 The basic metrics concept of **OpenTelemetry** can be refer through [(data specification)](https://github.com/open-telemetry/opentelemetry-specification) and [Metrics的datamodel.md](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/datamodel.md). The concepts of the four basic metrics are the same, so SRPC provides the most basic support for these four metrics, which can correspond to the reported data of Prometheus and OpenTelemetry respectively. Please refer to the table below: |Metrics type|Prometheus|OpenTelemetry| create api in SRPC | some operations in SRPC | |-------|----------|------------|-----------------|-------------------| |single value| Gauge | Gauge | create_gauge(name, help); | void increase(); void decrease();| |counter| Counter | Sum | create_counter(name, help); |GaugeVar *add(labels);| |histogram |Histogram | Histogram | create_histogram(name, help, buckets); |void observe(data);| |samples | Summary | Summary | create_summary(name, help, quantiles); |void observe(data);| Basic description of the four types of metrics: 1. **Single value**: A simple value that can be increased or decreased. 2. **Counter**: A counter is a cumulative metrics, which can be added several **labels** to distinguish the values under different labels. 3. **Histogram**: A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets, so it is necessary to pass in **buckets** to inform the interval to be divided. For example, if the input bucket value is { 1, 10, 100 }, then we can get the data distributed in these four intervals { 0 - 1, 0 - 10, 0 - 100, 0 - +Inf }. It also provides a sum and the total count of all observed values. 4. **Samples**: Similar to histogram, but **quantiles** are passed in, such as {0.5, 0.9} and their precision. It mainly used in scenarios where the specific value of the data distribution is not known in advance (In contrast, histograms require specific values to be passed in). Sampling comes with a **time window**. You may specify the statistics window by **max_age** and the number of buckets by **age_bucket**. The default in SRPC is using 5 bucket in 60 seconds to switch times window. Considering the statistical complexity of **sampling Summary**, **Histogram** is recommended in preference instead. For convenience, these four type of metrics in SRPC are of type **double**. ### 2. Usage The example shows usage via [tutorial-16-server_with_metrics.cc](/tutorial/tutorial-16-server_with_metrics.cc). Althought this example is for the server, the usage on the client is the same. #### (1) create filter Here we use Prometheus to be reported, thus we need the filter **RPCMetricsPull** as the plugin. ~~~cpp #include "srpc/rpc_metrics_filter.h" // metrics filter header file int main() { SRPCServer server; ExampleServiceImpl impl; RPCMetricsPull filter; // create a filter filter.init(8080); // the port for Prometheus to pull metrics data ~~~ #### (2) add metrics By default, there are some common metrics in the filter **RPCMetricsPull**, including the number of overall request, the number of requests in different service or method as labels, and the quantiles of latency. Users can add any metrics they want. Here we add a histogram namee "echo_request_size" to count the size of requests. This metric describes the information as "Echo request size", and the data bucket division is { 1, 10, 100 }. ~~~cpp filter.create_histogram("echo_request_size", "Echo request size", {1, 10, 100}); ~~~ More illutrations: **Timing to add metrics** Metrics can be added any time, even after the server/client has been running. But **must be added before we use** this metrics, otherwise a NULL pointer will be obtained when we acquire the metrics by name. **The name of the metrics** The name of metrics is **globally unique** (no matter which of the four basic types), and **can only contain uppercase and lowercase letters and underscores, ie a~z, A~Z, _** . The creation will fail and return NULL if we create with an existed metric‘s name. We will use this unique name to operate the metrics after it has been created. **Other APIs to create metrics** You can check this header file [rpc_metrics_filter.h](/src/module/rpc_metrics_filter.h): `class RPCMetricsFilter`. #### (3) add filter into server / client The following example keeps the pointer of this filter in ExampleServiceImpl because we may need the APIs on it to operate our metrics. It`s just one of the sample usages and users may use it in the way you used to. ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override; void set_filter(RPCMetricsPull *filter) { this->filter = filter; } private: RPCMetricsPull *filter; // keep the pointer of filter and add set_filter(), notice that it's not APIs of SRPC }; ~~~ The usage in main: ~~~cpp int main() { ... impl.set_filter(&filter); // keep the filter throught the APIs in service we mentioned above server.add_filter(&filter); // client can also call add_filter() server.add_service(&impl); filter.deinit(); return 0; } ~~~ #### (4) operation with the metrics Every time we receive a request, we get cumulative calculation of the size of the EchoRequest with the histogram we just created. ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { resp->set_message("Hi back"); this->filter->histogram("echo_request_size")->observe(req->ByteSizeLong()); } ~~~ As has been shown, we use the API `histogram()` on filter to get the histogram metrics by it's name. Then we use `observe()` to fill in the data size. Let's take a look at the APIs to acquire four types of metrics. ~~~cpp class RpcMetricsFilter : public RPCFilter { GaugeVar *gauge(const std::string& name); CounterVar *counter(const std::string& name); HistogramVar *histogram(const std::string& name); SummaryVar *summary(const std::string& name); ~~~ If the metric is found by this name, the relevant type of pointer will be returned and we may use it to do the relevant next step, such as calling the observe() of historgram we did above. **Notice : If the metric name does not exist, Null will be returned, so we must ensure that the metrics we want must have be successfully created.** Common APIs can be refered to the table above. For more details please refer to [rpc_var.h](/src/var/rpc_var.h). It's worth taking a look at the API on Counter: ~~~cpp class CounterVar : public RPCVar { GaugeVar *add(const std::map& labels); ~~~ This can add the Gauge value of a certain dimension into this Counter. The statistics of each dimension are separated. The labels are a map, that is, a dimension can be specified through multiple groups of labels, for example: ~~~cpp filter->counter("service_method_count")->add({"service", "Example"}, {"method", "Echo"}})->increase(); ~~~ Adn we can get the statistics calculated by `{service="Example",method="Echo"}`. #### (5) Reporting Reporting in SRPC filters is automatic, so users don't need to do anything. Next we will use a client to make some requests and check the format of data which will be reported. ~~~sh ./srpc_pb_client message: "Hi back" message: "Hi back" ~~~ Since Prometheus will use Pull mode, which means will pull through the PORT we set before and the URL path as /metrics. When pulling with the same way as Prometheus do, we can get these with a simple local request. ~~~sh curl localhost:8080/metrics # HELP total_request_count total request count # TYPE total_request_count gauge total_request_count 2.000000 # HELP total_request_method request method statistics # TYPE total_request_method counter total_request_method{method="Echo",service="Example"} 2.000000 # HELP total_request_latency request latency nano seconds # TYPE total_request_latency summary total_request_latency{quantile="0.500000"} 645078.500000 total_request_latency{quantile="0.900000"} 645078.500000 total_request_latency_sum 1290157.000000 total_request_latency_count 2 # HELP echo_request_size Echo request size # TYPE echo_request_size histogram echo_request_size_bucket{le="1.000000"}0 echo_request_size_bucket{le="10.000000"}0 echo_request_size_bucket{le="100.000000"}2 echo_request_size_bucket{le="+Inf"} 2 echo_request_size_sum 40.000000 echo_request_size_count 2 ~~~ It can be seen that we obtained four types of metrics by sending 2 requests with tutorial-02 client, among which the histogram was created by us and gauge, counter and summary were created by filter. The default metrics will be increased for the convinience of users. ### 3. Features of reporting to Prometheus The main features of reporting to Prometheus have just been described: 1. Use the Pull mode and will collected regularly; 2. PORT is required to be pull; 3. Return data content through /metrics; 4. The data content is in the string format defined by Prometheus; The interface reference is as follows: ![prometheus_img](https://raw.githubusercontent.com/wiki/holmes1412/holmes1412/workflow-prometheus_example.png) ### 4. Features of reporting OpenTelemetry The main features of reporting to OpenTelemetry: 1. Use the push mode and will send http requests regularly; 2. URL is required to make report; 3. Reports data through /v1/metrics; 4. The data content is [protobuf] (/src/module/proto/opentelemetry_metrics.proto) as agreed by OpenTelemetry; Basic APIs references: ~~~cpp class RPCMetricsOTel : public RPCMetricsFilter { public: RPCMetricsOTel(const std::string& url); RPCMetricsOTel(const std::string& url, unsigned int redirect_max, unsigned int retry_max, size_t report_threshold, size_t report_interval); ~~~ The time interval to make report and the numbers of requests to make a report can be specified. By default, the filter will accumulate 100 requests or report once every second. ### 5. Var Would it be the bottle neck of performance when every request use the globally unique name to get the metrics, especially multi-threaded operations with the complicated metrics (histogram or summary)? The answer is NEVER. Because the APIs for getting metrics through filters are **thread-local**. SRPC builds a thread-local var system. Every time we get a var, we get its thread-local var pointer. Therefore every time we calculate will only be collected by the current thread. That's why multiple threads will not be conflicted by global mutex. Moreover, since reporting is always asynchronous, a global API expose() will get all the vars in all the threads and reduce them together when reports. Lastly the data will be reported in the format with specific filter. srpc-0.10.1/docs/en/installation.md000066400000000000000000000075641454502251400171330ustar00rootroot00000000000000# Installation ## 1. Install from source code on Linux SRPC depends on the following modules : **CMake**(Require >= v3.6.0), **OpenSSL**(Recommend >= v1.1.0), **Protobuf**(Require >= v3.5.0) Will get the following output: 1. static library: libsrpc.a (or dylib) 2. dynamic library: libsrpc.so (or dll) 3. tool for generating code: srpc_generator - **cmake** ~~~sh git clone --recursive https://github.com/sogou/srpc.git cd srpc make make install # compile tutorial cd tutorial make ~~~ - **bazel** ~~~sh git clone --recursive https://github.com/sogou/srpc.git cd srpc bazel build ... # It can compile out lib and src generator and all tutorials into bazel-bin/ ~~~ In addition, we can use srpc_tools to install and deploy skeleton project. Refer to the usage:[srpc/tools/README.md](srpc/tools/README.md) Workflow, snappy and lz4 can also be found via installed package in the system. If the submodule dependencies are not pulled in third\_party by '--recursive', they will be searched from the default installation path of the system. The version of snappy is required v1.1.6 or above. If you need to install **Protobuf** from source code, refer to the command: ~~~sh git clone -b 3.20.x https://github.com/protocolbuffers/protobuf.git protobuf.3.20 cd protobuf.3.20 sh autogen.sh ./configure make -j4 make install ~~~ ## 2. Debian Linux SRPC has been packaged for Debian. It is currently in Debian sid (unstable) but will eventually be placed into the stable repository. In order to access the unstable repository, you will need to edit your /etc/apt/sources.list file. sources.list has the format: `deb ` Simply add the 'unstable' sub branch to your repo: ~~~~sh deb http://deb.debian.org/ main contrib non-free --> deb http://deb.debian.org/ unstable main contrib non-free ~~~~ Once that is added, update your repo list and then you should be able to install it: ~~~~sh sudo apt-get update ~~~~ To install the srpc library for development purposes: ~~~~sh sudo apt-get install libsrpc-dev ~~~~ To install the srpc library for deployment: ~~~~sh sudo apt-get install libsrpc ~~~~ ## 3. Fedora Linux SRPC has been packaged for Fedora. To install the srpc library for development purposes: ~~~~sh sudo dnf install srpc-devel ~~~~ To install the srpc library for deployment: ~~~~sh sudo dnf install srpc ~~~~ ## 4. Windows There is no difference in the srpc code under the Windows version, but users need to use the [windows branch](https://github.com/sogou/workflow/tree/windows) of Workflow Moreover, srpc_tools is not supported on Windows. ## 5. MacOS - Install dependency `OpenSSL` ``` brew install openssl ``` - Install `CMake` ``` brew install cmake ``` - Specify the `OpenSSL` environment variable The soft link of OpenSSL will not be automatically built after installation by brew, because LibreSSL is provided by default under MacOS. We need to manually configure the execution path, compilation path, and find_package path into the environment variables of cmake. You can execute `brew info openssl` to view relevant information, or configure it as follows: ``` echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >> ~/.bash_profile echo 'export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"' >> ~/.bash_profile echo 'export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"' >> ~/.bash_profile echo 'export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig"' >> ~/.bash_profile echo 'export OPENSSL_ROOT_DIR=/usr/local/opt/openssl' >> ~/.bash_profile echo 'export OPENSSL_LIBRARIES=/usr/local/opt/openssl/lib' >> ~/.bash_profile ``` If you use `zsh`, you need one more step to load the bash configuration: ``` echo 'test -f ~/.bash_profile && source ~/.bash_profile' >> ~/.zshrc source ~/.zshrc ``` The remaining steps are no different from compiling on Linux. srpc-0.10.1/docs/en/rpc.md000066400000000000000000000614111454502251400152050ustar00rootroot00000000000000[中文版](/docs/rpc.md) ## Comparison of basic features | RPC | IDL | Communication | Network data | Compression | Attachment | Semi-synchronous | Asynchronous | Streaming | | --------------------------- | --------- | ------------- | ------------ | -------------------- | ------------- | ---------------- | ------------- | ------------- | | Thrift Binary Framed | Thrift | TCP | Binary | Not supported | Not supported | Supported | Not supported | Not supported | | Thrift Binary HttpTransport | Thrift | HTTP | Binary | Not supported | Not supported | Supported | Not supported | Not supported | | GRPC | PB | HTTP2 | Binary | gzip/zlib/lz4/snappy | Supported | Not supported | Supported | Supported | | BRPC Std | PB | TCP | Binary | gzip/zlib/lz4/snappy | Supported | Not supported | Supported | Supported | | SRPC Std | PB/Thrift | TCP | Binary/JSON | gzip/zlib/lz4/snappy | Supported | Supported | Supported | Not supported | | SRPC Std HTTP | PB/Thrift | HTTP | Binary/JSON | gzip/zlib/lz4/snappy | Supported | Supported | Supported | Not supported | ## Basic concepts - Communication layer: TCP/TPC\_SSL/HTTP/HTTPS/HTTP2 - Protocol layer: Thrift-binary/BRPC-std/SRPC-std/SRPC-http/tRPC-std/tRPC-http - Compression layer: no compression/gzip/zlib/lz4/snappy - Data layer: PB binary/Thrift binary/JSON string - IDL serialization layer: PB/Thrift serialization - RPC invocation layer: Service/Client IMPL ## RPC Global - Get srpc version `srpc::SRPCGlobal::get_instance()->get_srpc_version()` ## RPC Status Code | Name | Value | Description | | ----------------------------------- | ----- | -------------------------------------------------------- | | RPCStatusUndefined | 0 | Undefined | | RPCStatusOK | 1 | Correct/Success | | RPCStatusServiceNotFound | 2 | Cannot find the RPC service name | | RPCStatusMethodNotFound | 3 | Cannot find the RPC function name | | RPCStatusMetaError | 4 | Meta error/ parsing failed | | RPCStatusReqCompressSizeInvalid | 5 | Incorrect size for the request when compressing | | RPCStatusReqDecompressSizeInvalid | 6 | Incorrect size for the request when decompressing | | RPCStatusReqCompressNotSupported | 7 | The compression type for the request is not supported | | RPCStatusReqDecompressNotSupported | 8 | The decompression type for the request is not supported | | RPCStatusReqCompressError | 9 | Failed to compress the request | | RPCStatusReqDecompressError | 10 | Failed to decompress the request | | RPCStatusReqSerializeError | 11 | Failed to serialize the request by IDL | | RPCStatusReqDeserializeError | 12 | Failed to deserialize the request by IDL | | RPCStatusRespCompressSizeInvalid | 13 | Incorrect size for the response when compressing | | RPCStatusRespDecompressSizeInvalid | 14 | Incorrect size for the response when compressing | | RPCStatusRespCompressNotSupported | 15 | The compression type for the response is not supported | | RPCStatusRespDecompressNotSupported | 16 | The decompression type for the response not supported | | RPCStatusRespCompressError | 17 | Failed to compress the response | | RPCStatusRespDecompressError | 18 | Failed to decompress the response | | RPCStatusRespSerializeError | 19 | Failed to serialize the response by IDL | | RPCStatusRespDeserializeError | 20 | Failed to deserialize the response by IDL | | RPCStatusIDLSerializeNotSupported | 21 | IDL serialization type is not supported | | RPCStatusIDLDeserializeNotSupported | 22 | IDL deserialization type is not supported | | RPCStatusURIInvalid | 30 | Illegal URI | | RPCStatusUpstreamFailed | 31 | Upstream is failed | | RPCStatusSystemError | 100 | System error | | RPCStatusSSLError | 101 | SSL error | | RPCStatusDNSError | 102 | DNS error | | RPCStatusProcessTerminated | 103 | Program exited&terminated | ## RPC IDL - Interface Description Languaue file - Backward and forward compatibility - Protobuf/Thrift ### Sample You can follow the detailed example below: - Take pb as an example. First, define an `example.proto` file with the ServiceName as `Example`. - The name of the rpc interface is `Echo`, with the input parameter as `EchoRequest`, and the output parameter as `EchoResponse`. - `EchoRequest` consists of two strings: `message` and `name`. - `EchoResponse` consists of one string: `message`. ~~~proto syntax="proto2"; message EchoRequest { optional string message = 1; optional string name = 2; }; message EchoResponse { optional string message = 1; }; service Example { rpc Echo(EchoRequest) returns (EchoResponse); }; ~~~ ## RPC Service - It is the basic unit for SRPC services. - Each service must be generated by one type of IDLs. - Service is determined by IDL type, not by specific network communication protocol. ### Sample You can follow the detailed example below: - Use the same `example.proto` IDL above. - Run the official `protoc example.proto --cpp_out=./ --proto_path=./` to get two files: `example.pb.h` and `example.pb.cpp`. - Run the `srpc_generator protobuf ./example.proto ./` in SRPC to get `example.srpc.h`. - Derive `Example::Service` to implement the rpc business logic, which is an RPC Service. - Please note that this Service does not involve any concepts such as network, port, communication protocol, etc., and it is only responsible for completing the business logic that convert `EchoRequest` to `EchoResponse`. ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { response->set_message("Hi, " + request->name()); printf("get_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); } }; ~~~ ## RPC Server - Each server corresponds to one port - Each server corresponds to one specific network communication protocol - One service may be added into multiple Servers - One Server may have one or more Services, but the ServiceName must be unique within that Server - Services from different IDLs can be added into the same Server ### Sample You can follow the detailed example below: - Follow the above `ExampleServiceImpl` Service - First, create an RPC Server and determine the proto file. - Then, create any number of Service instances and any number of Services for different protocols, and add these services to the Server through the `add_service()`interface. - Finally, use `start()` or `serve()` to start the services in the Server and handle the upcoming rpc requests through the Server. - Imagine that we can also derive more Serivce from `Example::Service`, which have different implementations of rpc `Echo`. - Imagine that we can create N different RPC Servers on N different ports, serving on different network protocols. - Imagine that we can use `add_service()` to add the same ServiceIMPL instance on different Servers, or we can use `add_service()` to add different ServiceIMPL instances on the same server. - Imagine that we can use the same `ExampleServiceImpl`, serving BPRC-STD, SRPC-STD, SRPC-Http at three different ports at the same time. - And we can use `add_service()` to add one `ExampleServiceImpl` related to Protobuf IDL and one `AnotherThriftServiceImpl` related to Thrift IDL to the same SRPC-STD Server, and the two IDLs work perfectly on the same port! ~~~cpp int main() { SRPCServer server_srpc; SRPCHttpServer server_srpc_http; BRPCServer server_brpc; ThriftServer server_thrift; TRPCServer server_trpc; TRPCHttpServer server_trpc_http; ExampleServiceImpl impl_pb; AnotherThriftServiceImpl impl_thrift; server_srpc.add_service(&impl_pb); server_srpc.add_service(&impl_thrift); server_srpc_http.add_service(&impl_pb); server_srpc_http.add_service(&impl_thrift); server_brpc.add_service(&impl_pb); server_thrift.add_service(&impl_thrift); server_trpc.add_service(&impl_pb); server_trpc_http.add_service(&impl_pb); server_srpc.start(1412); server_srpc_http.start(8811); server_brpc.start(2020); server_thrift.start(9090); server_trpc.start(2022); server_trpc_http.start(8822); getchar(); server_trpc_http.stop(); server_trpc.stop(); server_thrift.stop(); server_brpc.stop(); server_srpc_http.stop(); server_srpc.stop(); return 0; } ~~~ ## RPC Client - Each Client corresponds to one specific target/one specific cluster - Each Client corresponds to one specific network communication protocol - Each Client corresponds to one specific IDL ### Sample You can follow the detailed example below: - Following the above example, the client is relatively simple and you can call the method directly. - Use `Example::XXXClient` to create a client instance of some RPC. The IP+port or URL of the target is required. - With the client instance, directly call the rpc function `Echo`. This is an asynchronous request, and the callback function will be invoked after the request is completed. - For the usage of the RPC Context, please check [RPC Context](/docs/en/rpc.md#rpc-context). ~~~cpp #include #include "example.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; int main() { Example::SRPCClient client("127.0.0.1", 1412); EchoRequest req; req.set_message("Hello!"); req.set_name("SRPCClient"); WFFacilities::WaitGroup wait_group(1); client.Echo(&req, [&wait_group](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); wait_group.done(); }); wait_group.wait(); return 0; } ~~~ ## RPC Context - RPCContext is used specially to assist asynchronous interfaces, and can be used in both Service and Client. - Each asynchronous interface will provide a Context, which offers higher-level functions, such as obtaining the remote IP, the connection seqid, and so on. - Some functions on Context are unique to Server or Client. For example, you can set the compression mode of the response data on Server, and you can obtain the success or failure status of a request on Client. - On the Context, you can use ``get_series()`` to obtain the SeriesWork, which is seamlessly integrated with the asynchronous mode of Workflow. ### RPCContext API - Common #### `long long get_seqid() const;` One complete communication consists of request+response. The sequence id of the communication on the current socket connection can be obtained, and seqid=0 indicates the first communication. #### `std::string get_remote_ip() const;` Get the remote IP address. IPv4/IPv6 is supported. #### `int get_peer_addr(struct sockaddr *addr, socklen_t *addrlen) const;` Get the remote address. The in/out parameter is the lower-level data structure sockaddr. #### `const std::string& get_service_name() const;` Get RPC Service Name. #### `const std::string& get_method_name() const;` Get RPC Method Name. #### `SeriesWork *get_series() const;` Get the SeriesWork of the current ServerTask/ClientTask. #### `bool get_http_header(const std::string& name, std::string& value);` If using the HTTP protocol, get the value in the HTTP header according to the name ### RPCContext API - Only for client done #### `bool success() const;` For client only. The success or failure of the request. #### `int get_status_code() const;` For client only. The rpc status code of the request. #### `const char *get_errmsg() const;` For client only. The error info of the request. #### `int get_error() const;` For client only. The error code of the request. #### `void *get_user_data() const;` For client only. Get the user\_data of the ClientTask. If a user generates a task through the ``create_xxx_task()`` interface, the context can be recorded in the user_data field. You can set that field when creating the task, and retrieve it in the callback function. ### RPCContext API - Only for server process #### `void set_data_type(RPCDataType type);` For Server only. Set the data packaging type - RPCDataProtobuf - RPCDataThrift - RPCDataJson #### `void set_compress_type(RPCCompressType type);` For Server only. Set the data compression type (note: the compression type for the Client is set on Client or Task) - RPCCompressNone - RPCCompressSnappy - RPCCompressGzip - RPCCompressZlib - RPCCompressLz4 #### `void set_attachment_nocopy(const char *attachment, size_t len);` For Server only. Set the attachment. #### `bool get_attachment(const char **attachment, size_t *len) const;` For Server only. Get the attachment. #### `void set_reply_callback(std::function cb);` For Server only. Set reply callback, which is called after the operating system successfully writes the data into the socket buffer. #### `void set_send_timeout(int timeout);` For Server only. Set the maximum time for sending the message, in milliseconds. -1 indicates unlimited time. #### `void set_keep_alive(int timeout);` For Server only. Set the maximum connection keep-alive time, in milliseconds. -1 indicates unlimited time. #### `bool set_http_code(int code);` For Server only. If using the HTTP protocol, set the http status code. Only works if the srpc framework handles correctly. #### `bool set_http_header(const std::string& name, const std::string& value);` For Server only. If using the HTTP protocol, set into http header. If the name is set, old value will be overwritten. #### `bool add_http_header(const std::string& name, const std::string& value);` For Server only. If using the HTTP protocol, set into http header. Will keep multiple values if the name is set. #### `void log(const RPCLogVector& fields);` For Server only. For transparent data transmission, please refer to the log semantics in OpenTelemetry. #### `void baggage(const std::string& key, const std::string& value);` For Server only. For transparent data transmission, please refer to the baggage semantics in OpenTelemetry. #### `void set_json_add_whitespace(bool on);` For Server only. For JsonPrintOptions, whether to add white space and so on to make JSON output easy to read. #### `void set_json_always_print_enums_as_ints(bool flag);` For Server only. For JsonPrintOptions, whether to always print enums as ints. #### `void set_json_preserve_proto_field_names(bool flag);` For Server only. For JsonPrintOptions, whether to preserve proto field names. #### `void set_json_always_print_primitive_fields(bool flag);` For Server only. For JsonPrintOptions, whether to always print primitive fields. ## RPC Options ### Server Parameters | Name | Default value | Description | | ------------------------ | --------------------------- | ------------------------------------------------------------ | | max\_connections | 2000 | The maximum number of connections for the Server. The default value is 2000. | | peer\_response\_timeout: | 10\* 1000 | The maximum time for each read IO. The default value is 10 seconds. | | receive\_timeout: | -1 | The maximum read time of each complete message. -1 indicates unlimited time. | | keep\_alive\_timeout: | 60 \* 1000 | Maximum keep_alive time for idle connection. -1 indicates no disconnection. 0 indicates short connection. The default keep-alive time for long connection is 60 seconds | | request\_size\_limit | 2LL \* 1024 \* 1024 \* 1024 | Request packet size limit. Maximum: 2 GB | | ssl\_accept\_timeout: | 10 \* 1000 | SSL connection timeout. The default value is 10 seconds. | ### Client Parameters | Name | Default value | Description | | ------------ | ----------------------------------- | ------------------------------------------------------------ | | host | "" | Target host, which can be an IP address or a domain name | | port | 1412 | Destination port number. The default value is 1412 | | is\_ssl | false | SSL switch. The default value is off | | url | "" | The URL is valid only when the host is empty. URL will override three items: host/port/is\_ssl | | task\_params | The default configuration for tasks | See the configuration items below | ### Task Parameters | Name | Default value | Description | | -------------------- | ---------------- | ------------------------------------------------------------ | | send\_timeout | -1 | Maximum time for sending the message. The default value is unlimited time. | | receive\_timeout | -1 | Maximum time for sending a response. The default value is unlimited time. | | watch\_timeout | 0 | The maximum time of the first reply from remote. The default value is 0, indicating unlimited time. | | keep\_alive\_timeout | 30 \* 1000 | The maximum keep-alive time for idle connections. -1 indicates no disconnection. The default value is 30 seconds. | | retry\_max | 0 | Maximum number of retries. The default value is 0, indicating no retry. | | compress\_type | RPCCompressNone | Compression type. The default value is no compression. | | data\_type | RPCDataUndefined | The data type of network packets. The default value is consistent with the default value for RPC. For SRPC-Http protocol, it is JSON, and for others, they are the corresponding IDL types. | ## Integrating with the asynchronous Workflow framework ### 1. Server You can follow the detailed example below: - Echo RPC sends an HTTP request to the upstream modules when it receives the request. - After the request to the upstream modules is completed, the server populates the body of HTTP response into the message of the response and send a reply to the client. - We don't want to block/occupy the handler thread, so the request to the upstream must be asynchronous. - First, we can use `WFTaskFactory::create_http_task()` of the factory of Workflow to create an asynchronous http_task. - Then, we use `ctx->get_series()` of the RPCContext to get the SeriesWork of the current ServerTask. - Finally, we use the `push_back()` interface of the SeriesWork to append the http\_task to the SeriesWork. ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { auto *http_task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [request, response](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); response->mutable_message()->assign((const char *)data, len); } else response->set_message("Error: " + std::to_string(task->get_error())); printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); }); ctx->get_series()->push_back(http_task); } }; ~~~ ### 2. Client You can follow the detailed example below: - We send two requests in parallel. One is an RPC request and the other is an HTTP request. - After both requests are finished, we initiate a calculation task again to calculate the sum of the squares of the two numbers. - First, use `create_Echo_task()` of the RPC Client to create an rpc\_task, which is an asynchronous RPC network request. - Then, use `WFTaskFactory::create_http_task` and `WFTaskFactory::create_go_task` in the the factory of Workflow to create an asynchronous network task http\_task and an asynchronous computing task calc\_task respectively. - Finally, use the serial-parallel graph to organize three asynchronous tasks, in which the multiplication sign indicates parallel tasks and the greater than sign indicates serial tasks and then execute ``start()``. ~~~cpp void calc(int x, int y) { int z = x * x + y * y; printf("calc result: %d\n", z); } int main() { Example::SRPCClient client("127.0.0.1", 1412); auto *rpc_task = client.create_Echo_task([](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); auto *http_task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { std::string body; const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); body.assign((const char *)data, len); printf("%s\n\n", body.c_str()); } else printf("Http request fail\n\n"); }); auto *calc_task = WFTaskFactory::create_go_task(calc, 3, 4); EchoRequest req; req.set_message("Hello!"); req.set_name("1412"); rpc_task->serialize_input(&req); WFFacilities::WaitGroup wait_group(1); SeriesWork *series = Workflow::create_series_work(http_task, [&wait_group](const SeriesWork *) { wait_group.done(); }); series->push_back(rpc_task); series->push_back(calc_task); series->start(); wait_group.wait(); return 0; } ~~~ ### 3. Upstream SRPC can directly use any component of Workflow, the most commonly used is [Upstream](https://github.com/sogou/workflow/blob/master/docs/en/about-upstream.md), any kind of client of SRPC can use Upstream. You may use the example below to construct a client that can use Upstream through parameters: ```cpp #include "workflow/UpstreamManager.h" int main() { // 1. create upstream and add server instances UpstreamManager::upstream_create_weighted_random("echo_server", true); UpstreamManager::upstream_add_server("echo_server", "127.0.0.1:1412"); UpstreamManager::upstream_add_server("echo_server", "192.168.10.10"); UpstreamManager::upstream_add_server("echo_server", "internal.host.com"); // 2. create params and fill upstream name RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; client_params.host = "srpc::echo_server"; // this scheme only used when upstream URI parsing client_params.port = 1412; // this port only used when upstream URI parsing and will not affect the select of instances // 3. construct client by params, the rest of usage is similar as other tutorials Example::SRPCClient client(&client_params); ... ``` If we use the **ConsistentHash** or **Manual** upstream, we often need to distinguish different tasks for the selection algorithm. At this time, we may use the `int set_uri_fragment(const std::string& fragment);` interface on the client task to set request-level related information. This field is the fragment in the URI. For the semantics, please refer to [RFC3689 3.5-Fragment](https://datatracker.ietf.org/doc/html/rfc3986#section-3.5), any infomation that needs to use the fragment (such as other information included in some other selection policy), you may use this field as well. srpc-0.10.1/docs/images/000077500000000000000000000000001454502251400147375ustar00rootroot00000000000000srpc-0.10.1/docs/images/benchmark1.png000066400000000000000000001163661454502251400174750ustar00rootroot00000000000000PNG  IHDR*v)sRGBgAMA a pHYsodIDATx^ y vvbo8!1v8^b±?'^u;|t| X02Ұ]!v6 I !UhA!wvLuUuo7oUA  P7n4@ hAuA  P7n4@ hh~_7'\xy7n\_hQa)AC%4x5¾fmȂ!Z֖9Jmq^lYaͽc;v 4{;ٗc=Cؾ6^^ ߟvFš{{">ºjQ%7m0JA4`!<^|֋Yi索~']2ux_BJIz_.$TJECrmTZQߣݔk3.zzoy!hh A_ӃȰ+L2C>~_^?9 NАMzF}ہWzXÄ>òx0xۨvRm?>rkDz@&h)܉ B\; m,kYo_s|lǼmOK9wagďig ssx_XUxϟ`Ա>]X A@`+AH ѐ6,J?fa5 $ Jrxxg4k z> nr$~,gvRJpa_’rm?LVo7eL?Vyy[ I{rViGgԱkzռ>44 ]<@H5,+2_V僔zeŃx+Äxx{x'u8Rj'NM<"^j)⿖q[c NhSys^ƾ/cqm=|b/Lp;~ ~:ʼn逯}9~,plվ>44[Dӛ?W<C ^?Ԃo%=ďe^>z€(>="˃/A|O릏#Mur?xW|x%.o'a0Y qJ(ETŃZiZcI^@&-}rK_WeqHVɾMub_վ>44`+AV0dWz ?ՊAHz=P, >8 f[ Z<1 ;=XjY-q*Y)kORe(XXS&:g%9%=y0s/O{־b1,* BM<(L ˟#kԀVfį\ů/^^x`ݪwdO7_[>z-b+4g jj~rbW*%?ޗ?_L^g.P거\g ^^K:,w, g0zWܶҮr>44;Rx;Ǽ*!2^į#Ξ>YJ|xUjp^wAdOQnF@xA-sYJ@m9cVrl/\z,,DƯ7;rį;lWn_cYaWx `'Pnp6XXU > Y84{" 82w^OR8 JrZ~V샞((~,vP̛rdz7(2~_⠠%{æZ8{?+tz@&hPDŽ^N|`|psR!/8u|5P2/c.&^q*=V>upK<`}YLvߛyw%K~%rm?ބy_ۜOq{smbcc_'lK\c\o Hܶ|_YTB&hp| aW %kxRYSjyx*kz~/+Y/mʕ=p߁IA#nSۏ> JTvJŞ+g=*~_=@yVvg56 ^}}!hhzlf9MU>ZN 4 A@2鿤KuЈd{$> h:BuA  P7n4@ hAuA  P7* lQXb6hРdYRQcǎM~e^}`?|~ve˖ɗ@ߦl}tcз)4xJ-5ks=GQEQEQEQM]>>ΓAjooOxjpZZZZZ%o] ҉gu.֨^^ 4T-Ղ_-Ղ_-Ղ_-Ղ_-Ղ_-y)hpYlJjjjjjjoACZZZZZ%o PՂ_-Ղ_-Ղ_-Ղ_-Ղ_-y%hjjjjj/A~W ~W ~W ~W ~WK~ &ZZZZZK5_-Ղ_-Ղ_-Ղ_-Ղ_-Ւ_ jjjjj4@MW ~W ~W ~W ~W ~헠jZZZZZ%o PՂ_-Ղ_-Ղ_-Ղ_-Ղ_-y%hjjjjj/A~W ~W ~W ~W ~WK~ &ZZZZZK5_-Ղ_-Ղ_-Ղ_-Ղ_-Ւ_ jjjjj4@MW ~W ~W ~W ~W ~헠jZZZZZ%o PՂ_-Ղ_-Ղ_-Ղ_-Ղ_-y%hjjjjj/A~W ~W ~W ~W ~WK~ &ZZZZZK5_-Ղ_-Ղ_-Ղ_-Ղ_-Ւ_ jjjjj4@MW ~W ~W ~W ~W ~헠jZZZZZ%o PՂ_-Ղ_-Ղ_-Ղ_-Ղ_-y%hjjjjj/A~W ~W ~W ~W ~WK~ &ZZZZZK5_-Ղ_-Ղ_-Ղ_-Ղ_-Ւ_ jjjjj4@MW ~W ~W ~W ~W ~헠jZZZZZ%o PՂ_-Ղ_-Ղ_-Ղ_-Ղ_-y%hjjjjj/A~W ~W ~W ~W ~WK~ &ZZZZZK5_-Ղ_-Ղ_-Ղ_-Ղ_-Ւ_ jjjjj4@MW ~W ~W ~W ~W ~헠jZZZZZ%o PՂ_-Ղ_-Ղ_-Ղ_-Ղ_-y%hjjjjj/A~W ~W ~W ~W ~WK~ &ZZZZZK5_-Ղ_-Ղ_-Ղ_-Ղ_-Ւ_ jjjjj4@MW ~W ~W ~W ~W ~헠jZZZZZ%o PՂ_-Ղ_-Ղ_-Ղ_-Ղ_-y%hjjjjjoAkٲeɲ{`РA;:: K뷼RhZZZZZZ[6h! cv x^˫ZZZZZ%o:.B0> ^˫ZZZZZ%o:ZX4 @굼hZZZZZZ[!ޞN_ ((((A= ky5-jjjjjo٠!\a=CE뵼hZZZZZZ[6h!CNۿ 0ݺǫ1ݖbcڿjg|sFנ ]! x8!AR&j3Ofh+|>⧑x)%^~ ޅ_Gï}Q {ܱ֫)bC yo4dpۈ}ڼ3os~ZEaPGSff-jۿ__C`5U Ué.qNw ^BxA_%? i?Z>5Y'y+\ş˟Ӄm?̙VyAk|C7}am^r|\lS -Ղ_-ՒQ^c҈ _J__9%< &˞k ;ۿSi߭bew7;/wSѾB ovߵwh/l#-6ykqWHЗ.VL1C0ʃFg.yd,Kj_cr^ /R~Ɋ 6ou]xڷ./KbMfMk[L.9]4?go<^<NH%i"SA}#?5dg> ɾH𾠏'ܝ%=raƊ[ބ+;N/ZS%዇PW ~WK~;4&4"\K\_l)+ܽk/^kS:w 6/`Ÿӟۏŏɏ͏яO=8x;n*v3sOϷ_cޗ+- ׿/tt"ʼn;-O:EukowESC; mN-_=g <g^m4ZZ%oRj/OW=T~Bu_˹}||u} Y_׷?:p/-k]kۚ CiæP7m{X9z\j׾\U^vO/ ;?KpS@Z~g)$>ǯI#ߕjjv~ iLhz3,uXzk@ f/]}R> /Pg|5YѴCSjk{.*\5_5f\utv]C`}YMJd2{v#Yv̘zb)H>{Ѓ?i>SW ~WK~;Y4&4T e/͹f@~e랿/Kї膙4|\l#_g`+TB?#)bǡ4> ty^R<.eo6^4Xgjˆ}:+ _jP<$~xx!B-w )S >zFZZ U ~@¿̿4/>8 0 yxS}K~K3x_t=:_?l僦_/Ѕ AQ{sy^/Cq/ ܷR#.~ζɮ W e7?MO& ?;r&~_LӯUg.Vȸ>{Ip^l 4A 5爮m-{}k^ijog/ߘPWDj/aլF3Vx}+^n*+'v:sviMCz , Α_/$Ym}_Dկ-,0%q@O{˟/G "gZZ٣7&4T-/`TV~q^W~+8zs^~NA5R(៣N U.^uXr]ȩ6ޟYd o|߰f9[|kfl5jDgZZc7&4T-Ղ߈zn0oo<^ṽSBᘽ گ~?}9X{q:cca H.zݟy_o̠TLiz_{L.к~3 Qx]޿to'3RAN/*[S̚<Ղ_-Ւ1j ^`jگC>Tfgk8G6_e jac8Y͖.m_|@ SS*w`/AADUjjogۘPW ~E0?hZC:ضY[,~IdVP~p N_RI/ADYW ~GmLhZZZ[#BR5U9e@9C p Nm>oY-8}qÒ}|z oMD?hjog٘PW ~W ~W@Zn|¥t ΑwWg7Ȥ^uoۉ?߫j m{K+.a/jm%(-ՂҼy-~aIAS(@Ȭ):?LNk3cE ٩ۘY}-ՒΞqj{g,Wo[ÐR02޾uc~ʏ!}\jSS3_[V,?YHI~ߋ(u/{"gx|.joux8+퉵gMN}/3Jꌻ~\.H ߯_ ^稃P/WK~;{ɐ5Pf0#G[yeX|x0%3qwͿ, 7ҁwwQnxaF_t_lyǏ2CnvƲwH\=U6wKl־Ɋwۭ'kW AC/k1L\|fól6Ի/,Sζmu;s3w[SN-7͓6]/qwk/4|-96e>:m(U#>gώ^ĩGڢ(w ^5c힡,aSث&>خ|PQ]ݒVȡzSמps?Ung̿sXwg>6;xkӚdo~ER~ ς4אS9|_~[Ά;%#D<|DvȐ.v ;jЂ_- Æ +[j1H~vwcEGQ1Ƣp;~SpƛRnl8kgٕtmy=yfח֣d02i<Ȫ6*yKUUk/Uiw*J*ves;s|ޘzlbFJ8}Ih̹$ $g@y&26_$Dd{mQ2[bׂY ܎3>%#4{轩eQTWH-BP'זۗ_p\Pn\EG3T4`֌eTՕ=>'̉*~{SmkO*3>ϋŧeܽnf< dňq`NjC(ÍRnx}UVo^sv٭g¦ylƸo˿eqRO̝ ^Go Ono0s?{׍:6Q;ԟ|?{kI8cܻtr\<$D *8%#;z?캈dVd%}Ϙ0ւ_- uZ|aqE&C =ӏoqǛ.:1 6{/f̤8ÍAQq+ 8B19T m mێyfy{cs/{emo|rQBEP)W-܅ :̆QP_T~QJ91k=%]jpv:oE0Ђ_-yQ0Pkd?ă+Ō%qFU{exy>EKG|;(3쏊fL/C >4 ׯ#?֩B姭s}6gk[tV]}1٧ ci7դ` c33ύv._WoQ_|C L, "|v0_-yEjfh)wۣSB$HZ(H }=E׆wck n9ٞ+{-6n\x_$5#?N=®~M?fWx⁅_? ]<&2$x5z~M<8șr@jdŵlE?蚕q`{Yg/%Ďw9o#Q_N_a=yB(m|T WK~;{ƄZݹ"o[}We^\$|bn6/8 E*uͶ E>ֿ  g~9 =X<$[zX1sOʡ+P& {|'x̺ҿ/D{$Yd uӏMfMlI8̚7]&ܒJ̚xmzՂA=~:%c 6{E  Ki~F8x |L_-Ւ1jjjoy6Ⱒ`Clw&ĴKx:ԤS-c{s[tswnkkﻠ?˼6s||^p⾋ٓfM<;vzk; *C n1%cYg~\oIZ 4&4T-Ղ_-Ղ_-aM}& 9nd&D>k¯Gvi bwmΔ_'׹cy}|{[u }wvϱ-7KMm AQɬLN<?䔎cccn8Eq A<u-g6W ~ߘPW ~W ~WKo b:1>g,Yy/fMs|QPbƲ/؆'ט_]9]heO1I};ci'ڥ'Ϩۭ8඙Z%oƄZZZ~R_ې̚[.%wr눮Y8 ':YBoOIfH\sDæX&25ET)~ Aw xL_-ՒOxcBCՂ_-Ղ_-Ղ_-o_".Y'wHfM̹ڶθ,5w(y֌e=AD66xXè{~ܥ$|6_Tz!J?Eïw;Xl۱5_4m3WK~;?э U ~W ~W ~u-p + jf\̔HNõ%=䴋O13UtI_ǯd?m&jo秸1jjj/>xr쪖aۋ3/ ljjjjAeϤ|ΑIM=wgiOk[>يI?[sz_֣3CJj_y\/2هnIZ,O8}d?zy5PW ~W ~W ~{GVq#2ÁPϮ{vv˰߳)$u&uYǰ$5wlO$?gG|:#;P>I#[7]dr6A ~X>AW ~W ~W ~돟6ⰢSؒ/=}JĊo>U">1aɌ{N잩pyGu`_jbvy!{o~6lޕ'ڢ͖{=;SHfCkF"96A ~W9> `@\$EQEQ۵gMr]s؜{.\73zv?ڤ:=tI;I}ٴ+).9n:KI]{g|,xޗ50nP$վgYl;~4k۽fy`]CI-d q]جk~W зE&wm춙oЁnƾf#B%oƄZZZ gCzξ{G<ВVޘPW ~W ~W ~WK_wX=|&p+Xǰs|6Ԓܒo'56)N]u/l[l3†_f>^`†vƄZZZZwmKﶇ'n9O3Æ&oӆn8yҿ]l>;{^d2wߑLQmfjf?hogKnLhZZZZ𫥿}uM>caC\Z;| c[5s[gٶGzR~fϏ8* BȤ_OX7x4GP!ЀͤВؘPW ~W ~W ~W ~JrGnM>̰!Zb?/ "m8KGm2 V88 rLگvƄZZZZ7~G&،klמ̰!}GZ#hώtr%nM~ SǷͤjog+kLhZZZZ=K'w{wmҩ2Æ|M8eeZ%m|m^NS88춙_-ylY U ~W ~W ~W ~:-l>w"{oϩgͶ:¶?3v^{6%o1jjjje;%w B﬩GC-tGoIՒԘPW ~W ~W ~W ~+Z:.ﮘWS >tLگvƄZZZZ[^|z8(XbvEEBšbС[c8m3iZRjjjjn:6aAvOObU.O  n< XQq̷؎U2r oIՒΖјPW ~W ~W ~W ~[a+3NͤjogkhLhZZZZZ*B}f],Be.mqYš> m3iZjjjjjm-{ZC9Vax۹~uaogSf~]oLhZZZZZz;tǶuքC}886sfk;guu'nLhW ~W ~W ~W ~ooCwvjao{f~C/s ;zwN6&tZZZZZ~=tX̜St++tx_ D䶙q-laCybcBGZZZZEwwlSS{ >=#C͓ն=:v=䶙>JCؘjjjjjo͡~:YfJP:ƄV ~W ~W ~W ~W˾m B-OOi][ Z seŠ[n`cBGZZZZ/ݵk]v=t/>8xNB g~yС6,~*6rF^~^uD\Px%K5_-Ղ_-Ղ_-Ղ_-_B=~R}C?dKG|֍<&  gnCmk.  ϷjȻ4@MW ~W ~W ~W ~4JBZ, ^vE-J}w Kj{Ga/A~W ~W ~W ~WK6tX2XeCY;=a񷻖CE~ &ZZZZ»[s?8x3?kO/lqlֹػ.B~Q8lXqRApn PՂ_-Ղ_-Ղ_-Ղ_-|pMk|~;xِ~*عÆu[LȽ4@MW ~W ~W ~W ~vڴϾ`ƍ8lU띮p!zW~ &ZZZZ[Bp˅f ^O6]{wżVU38lx+oAkٲeɲ{`РA;:: K뷼RW ~W ~W ~W ~\~e ^N;^l 2d۵13yaޑ]lRne nmm9lرϾNWjjjj֟9 㟇` {/֙m-+12 |떘MHl| 굼W ~W ~W ~W ~W N=?x7b]6cEW۩٪cc۟YزBޘ$ /k2n > o8!>!x0ky5hjjjj}a8(lbQ[7|կl纕+g1l6,D~+ ?(Z^ .(((5vхG ^W ;Ԗq_ۚϰeO->]+ޝ=aCg{?2mʓr= ky5-jjjj,v;gmH}yx ; [y@ ]whpne2qYqj#Ђ_-Ղ_-Ղ_-Ղ_-Ղ_-~ixw-mCgG|֏y/lڧ [a'^6l>ٓJN~{[ n PՂ_-Ղ_-Ղ_-Ղ_-[WL< ;(le[0ݳ6]{wżV)6/4{#{7y_ jjjjju[^ 7|(lm[5s{N|Ra>_2 ^+O/<ɻ4@MW ~W ~W ~W ~WKڽ]:NXtEaÄS}֍F~ &ZZZZ/]9}/aâkڋ3|v>avn)o_ jjjjj+~wn=qt _K\6\9}oغ N;ƶO98lX/A~W ~W ~W ~W ~5Ͻθ'vMߴqQuت*ݰ^~ {ܷ헠jZZZZZ߭ny 7d!EaÄ '6=ߞNy%hjjjjwњY6[6GZ+ n:Ӷ\ 6>X۾!o PՂ_-Ղ_-Ղ_-Ղ_-nܺu Kf7 vi ^h( i==aC?vCao_ jjjjj/~gf-߳n;;O†i+jm:s :k/y%hjjjjͫmnrqv( 'aHxm] ;caO_ jjjjjo~wiw?s< g]9|Eaէ}ؖϾ6OhP6{ǟڎ5 {ӓ_ jjjjj~W_je2aoG}(l{wm+ݩŷ|wm; {ӑ_ jjjjj~j=qY6xq6nEaC[9ێ ;xmضYc/A~W ~W ~W ~W ~4%̳3I6r7X6xMu۴_Æߗ\8?/쭾헠jZZZZZ[ߴyg7lEaÕC'v=~w7Lrk-][(>헠jZZZZZs솛iko?coqHwذmL† o̞T)K5_-Ղ_-Ղ_-Ղ_-Ղ_-wuvݳNmovوC† G'3{p?r'kE?wW+vK5_-Ղ_-Ղ_-Ղ_-Ղ_-w)$l8loߵ_8t{޾aז'ۮ/X=y%hjjjjF6fƠ ï]z Z9ޝ6l1[1aÆ3l[g;Z9y%hjjjjfkN)8wv΅+R&|+lq_ ^~Ƕ?3/A~W ~W ~W ~W ~4UYw{vÐ뎳K c|ŏWv[YkyK5_-Ղ_-Ղ_-Ղ_-Ղ_-wێv󢋻0꒣a+ == Glù(N?޾bž/A~W ~W ~W ~W ~4%̳w;p|6oyﱷf}o!6wm^&o PՂ_-Ղ_-Ղ_-Ղ_-~gvCgy_لS- چǞ!=(.֨^^ 4T-Ղ_-Ղ_-Ղ_-Ղ_-Ղ_-y-4) B萵Q/jjjjjV|1 U ~W ~W ~W ~W ~WK~ &ZZZZZK5_-Ղ_-Ղ_-Ղ_-Ղ_-Ւ_ jjjjj4@MW ~W ~W ~W ~W ~헠jZZZZZ%o PՂ_-Ղ_-Ղ_-Ղ_-Ղ_-y%hjjjjj/A~W ~W ~W ~W ~WK~ &ZZZZZpAÃ7ڱc#Ͷc<@ ~W ~W ~W ~W ~a9iVw>lM_Fa tZZZZZ%o 4NJBPm_~zRg7>g߽213W[WP ؚ :-Ղ_-Ղ_-Ղ_-Ղ_-Ւ߆ ><̠5`hyּf 0W ~5xƩkzhZZZ%o 4|mV8 $7x[Ե|jگjjoC /_|y_%uâ׺a>l ɀĉKG^/U_g !]W;n.uqv\Z+qb\9evQ Sfw7ˏ7^{ӟ@XZZ𫅠ώ^`}r| ԏwvڬ_GCYd{TPvHs>20Jώg^>J{7ƃjj/ACP_|:5 >qg?ϡ~u{\?Rw_ĵP,|uSa澼׺}~>)WH6Y6{j'?fC{i'p㵝Y 53-Ղ_-Ւ_5PҌT7TJUzWr {xSu,18 "/ _ټ8uM{O}!9iV+MzR>rKU]__ d~ Z6tJ׷-O^srϘ[P~W.?[ = }׼pQ.0#zZ𫅠NP54@/_Y 2 G >huEZC<f A*~?l{:9v]x^y(׻65jj/A~WAN>~5~󡯵p* 4 i> dPO}+MŌ=ZK5_-Ղ_-Ղ_-/0p&:*a_n|MF+ Tj/A~W ~W ~4;v-=}3k|nc.*vŤIy_84n1a#Dj/A~W ~W ~_ֳ1]iF An[Y?\/$OWK~ &ZZ#4[ׄw0k;p7JЂ_-y%hjjjoɚ12`l;㞕ɠ?\#\4 <, NU~O%Z~L~AS'!sW7[,W ~헠jZZZz3c$O ?\sO)lN+x$`"Fg5o% گj/A~W ~W ~W ~/$np/8PW.XCU!LS8x19>c"lܺJ*_'C%o PՂ_-Ղ_-Ղ_-W;۫^Udd_"\Ϙ$(ʃ Nn???&?6?F?V?>4헠jZZZZɯ:pg!g%p Upŏ'afx8ׁ j-إ~ς[V{FC%o PՂ_-Ղ_-Ղ_-Ղ;\ >kzdք!ט;nxa_dӟ' X'hZK5_-Ղ_-Ղ_-Ղ_-?Y}l綧;r5jM~IiᧄE5}6_ϢYj/A~W ~W ~W ~WC%]IX3"|6ukG)^l _o=/گ4@MW ~W ~W ~W ~6$w'֟HfS9|Hע[6 _-y%hjjjj_PTBpCO3'|/o|.Q30ZxWK~ &ZZZZhk5~_LrZ.J׎mZ~~;Nگ4@MW ~W ~W ~W ~k~ k߽W7/.~;Nگ4@MW ~W ~W ~W ~4߾x;Nگ4@MW ~W ~W ~W ~4}q;N_%uJO헠jZZZZ𫥙nTÇIn %K5_-Ղ_-Ղ_-Ղ_-Ղ߽8b@}ɻ4@MW ~W ~W ~W ~:z{;=z3t /A~W ~W ~W ~W ~C|;o~IBޓw%hjjjjok16n,;eݏEÚ {JȻ4@MW ~W ~W ~W ~W :!|悇 *x:x~jC~ &ZZZZ𫥔_Bw1hXl 8 K,ٗ4hP򎎎-:-Ղ_-Ղ_-Ղ_-Ղ_-R:|wS{3.Bɻ |~AC{;69lky5hjjjjjnٶӮ]8qIlNn:@ wQ@GZZZZZz16mw9h5ħ6z-:-Ղ_-Ղ_-Ղ_-Ղ_-R/~k bۓ`!hjjj_.?n ڑ8x5Cwu8AWY((?]vϓq^ؠd?WeF8pYqj[$W ~W ~W ~lu=tԨzJ{|–o٠CZ a exy< ^+JAZZZe_C 8x!oh@)W ~W ~W ~;v`93:t~\;i>:헠-Ղ_-Ղ_-Ղ_-}o#y%hA ~W ~W ~WKB_߶< :헠-Ղ_-Ղ_-Ղ_-կ (:l(<헠-Ղ_-Ղ_-Ղ_-ිOC~ Ђ_-Ղ_-Ղ_-h~Z萷_ 4Բe K˽|֢ďsncǎ-EGGG}?V?f-Ղ_-Ղ_-Ղ_-w0YW:cWa_紵u=ăi_/ =(P  m_ c%h -Ղ_-Ղ_-Ղ_-[z  ^Ԧ.^W!o +vm[4Ͷ=:%!Y||O8lPn[ |]6&ay=kݰWkuaWT!h -Ղ_-Ղ_-Ղ_-7!o P3۟֟Uptc^x: =:@m=&h˷cJSx}ߗ34jjjfB[8x&t/At}=l/kù߰w]a_.,lvn[muҏZ7X|p̾|Y wz} -Ղ_-Ղ_-Ղ_-u[{W1c BpWm˶-K_fE? Ty!lpNǃz'^ܶξJEРA ~W ~W ~W ~$t8e8qI!o Ѝ0p3C\<>X:z;zzwǷ !J=}A'<_x}ʠЂ_-Ղ_-Ղ_-Ղߞ%txpF;vcvİv{4@M?+{~W֪0hNx`<~<x?5'󤃅߳aGe=c e AZZZØ :!;䇊>lM_Fa/:f=65 6£G:x jjjΚYkʆq}v–:W̆wݘT3 jjjևJBO~h:W ~W ~W ~ooQ k hA ~W ~W ~W ~_asBÇv-O^XCA4ZZZZ&_g228 t?hjjj4@AZZZZKMjjjj/A4ZZZZ%o t?hjjj4@AZZZZKMjjjj/A4ZZZZ%o t?hjjj4@AZZZZKMjjjj/A4ZZZZ%o t?hjjj4@AZZZZKMjjjj/A4ZZZZ%o t?hjjj4@AZZZZKMjjjj/As-[fAC{{{z}[;[Zo A ~W ~W ~W ~헠9 A/v[[[{V%h-AC}Ђ_-Ղ_-Ղ_-Ղ_-y%hޱm+'t\YAxX6YaB& |]c~X~Z7~@xXu}yį'}P±@Ђ_-Ղ_-Ղ_-Ղ_-y%h=v#1,\ ۤwBP I?Vjݬ˷ !wz}-Ղ_-Ղ_-Ղ_-Ւ_cBCGe8;w=t-Ӧ`A|}I#?ӿ;m:YACֺ^q |?>^-Ղ_-Ղ_-Ղ_-Ւ_cBCJ;@4l]^X2@:}Pݿ K?Oe뤃߳pla?~?hjjj4@<^^)4T(jjjjo٠>!p^^ 4T(jjjjoEN+|P/*A ~W ~W ~W ~!=P/*A ~W ~W ~W ~Z?YE((jNA ~W ~W ~W ~/ԘuFj@)W ~W ~W ~WK~ ݷ ų + Ђ_-Ղ_-Ղ_-Ղ_-yhFC Ђ_-Ղ_-Ղ_-Ղ_-y%hA ~W ~W ~W ~헠-Ղ_-Ղ_-Ղ_-Ւ_h:W ~W ~W ~WK~ Ђ_-Ղ_-Ղ_-Ղ_-y%hA ~W ~W ~W ~헠-Ղ_-Ղ_-Ղ_-Ւ_h:W ~W ~W ~WK~ Ђ_-Ղ_-Ղ_-Ղ_-y%hA ~W ~W ~W ~헠-Ղ_-Ղ_-Ղ_-Ւ_h:W ~W ~W ~WK~ O0h 8p`R˖-+,",bZ[[O?oϑW{{;[6sV/^#?-Ղ_-Ղ_-Ղ_-Ւ_紵u=>0v m u}嶭&h 3Mo*A ~W ~W ~W ~헠zƭllʻk!bJ'N,;/  ~(?,~tr< 銃@9q ϕ>_c 9Ђ_-Ղ_-Ղ_-Ղ_-y%hylL}2-?ڂU0` 𬁸GXܶ>͌xc5!p?I/:Z?3,?hjjj4@7̲wN5bجS ܅}0‚8hAoW>M}}I?=O߷Io+^^'>xZ?~jjjj/At3_f:3! N?xr:})hH.Wqx^Cݗ=4ZZZZ%o Ѝ0hi^f(P:y?شxB_u@׋g-AAO:} }4ß#_grw㱓fA ~W ~W ~W ~헠jbۯP¯ӰnK*#I?,{8?=mďǡENマ@4?^[GrxwY@ZZZZK53o=I@xs(Xa : }`Ҥa]j_~ >xs)o翇Az~|YIޗxaINaxjjjj/Atsâ##4K^]0kM>p 4>xMlnRJ/}ߥ๧~=Z/Aot^Mz_xyzpRNmZZZZ%o Qd00 As/ڗ?~}Ƴ;N,އ=&:奎˭ |zz-j [fgO~Z=\;MAkv|=u{įڼx}w53=tuz7Rm0Tq gYp\;?2;M_A py":]>PA 5ளCi6:xWh>JSczye6{(v {H; _UX\5Xs|eI{8&+oOurJ,/Ip?N6SL4!4m҉݆gyCxP3`BuA  P7n4@ hAuA o_QlIENDB`srpc-0.10.1/docs/images/benchmark2.png000066400000000000000000000704651454502251400174750ustar00rootroot00000000000000PNG  IHDR) [fsRGBgAMA a pHYsodpIDATx^ TyijM46i6m6MImo`$|CiҴM=1$[#x (l5kfuu]ֽ~ 64@hmC 64@hmC 64@hmC 64 Qwq3fLEnEjoT.<-^^ . (5TA!]Fb4 g===߰SOcf~NU+|=sNi) 4 Qa!`2eJ|/uт?f51SCRPкѺZ7 *yzko@>Gߛ~7}]:Vvz}6i3oSxpyn*:kF z*B8*E~|#_$1Z}lTfA*U59XVjMPp~n5kB6P&4 Q4p E]XDV۹1C#ݿ^ŦFoE{kDd(} =dHs>}(~Lk\sh>~W5~}ݢmw>rцF$Fũ 2}X`d~y#Fu(Z]ﭞ|;bAuo9 0_ vϜ[4MsLM HTFCp'}b'fםjta9x]ۨbjh~ 3}~5=:B()CIxPZ%^1j˛_N7†@Z_|.֧jW60D/\O~,t? (~G<_J2V߹OE4R5*.B6,6-v(7:â=7lBW{m.GQKVG(|_C멷 1auyU1E?_z-a!ϩ14K㗇c 4I fT,;FJzLxE}t? pg<,EX8xsoou67_7a )"7.7/S>o(3y>_ t{ϗޛF埣{٨j-/ kT-k5Q T4z -7|}Ο]@h4@‚'_I^EXH.*pbﷹk6'1 a6>|S)\w=~3T^KMs>)<%<&lFU{Lly?_n<'v[H8ԊpLPqC/%ToݍF$F߉ ;E wPo@X+ h"&˫ z^9z5\Ply?_ncnlFg%̡jᅿmK޶HT+ hARK씊vmoVpm?l̄o} ]OxZGh(^ƄQQOoS=|VޓzjU[{[3VZzcL^?:!l \oݍF$^Pm'^:ZٹW7_/|~f56T|JI a#ިV>sPly?_n_sߓ^_w>O#yhϜ"[WנJ??ux}VZP7utU hMA/l(jzǶ h4@4TWxTES,‚$/|j/^H>VK-X7󾆿FZO>_s/z_ ?G#̅CՖU0|#gm|.f@k>U(߄.mEUvP_z*T0͌jT9Â{~cϧ~o^=ްv!.o} (=ߖ+v8:憞S %,ݭj3W_mU6CexPS3VF?v |^{U?cz@Zh4@B 4Ŝ_B3R&E``S' }VA~o;IG=7R:F$*l4ᐮt0x6HTx~ 7tnE~UiD 64@hmC 64@hmC 64@hmC 6 5ݘ1c;vli v~9Nu *U{m4 y*`ǗUS>}A2^WCWA  7ov<AAAAPIM:67:z\G6ˀ 庍pRF~bH-P#@: 2>\lFs42ExA7\G6ˀ ]hH!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2Oz}M߲ɀ 4 ` l 40awl9&]%{H h4@@.cOcFbmo,ǂ.*ǕwfAv;kr\EǸϗ3sqk]~ ￴|wl-+o[K[`7FC 2F3Ӿ̃U.sQ.gZQr7I 1mG^5o_9¿GbGRM&6h(\Fٹg{;*W}+ykfVa?-^{DN1Y6Eupʿ7W#9u˿e_S,t9y /.}47FC 2*|QEsʅ°׷߾xplT+~pbCNușp 2Q3dgrddz15^6Ԙ3t&6h(a4rX(ζ"&6,`aᷱ_Ŋ  (VL)?qF{|ÂeV.^~o E71j6p,WEYǁ5ìi>:)Q>:Z*>: #h4@M;&M[oIfïjJ` wUްTX9<!ڊ|0mEfxι"] gz=LtTϱL%:'6a2/gWE " ?jo^%S+GfYK8תpp##^4 `H9&yYZtߠw<Áo;.%<3eGciX܇㯕KԔ)l(W %1prWt4h(;oYPzM;y_yw_t'wYW.k_]w͢w'ef|4w߲cmWrO۱~{U56U&#%:is%:mQsr?#殊,Gk;|G~7|X`(FC#KliMkr.lhg$F)Ku\Wn.GO/w3?Mh9X?s|HNS?6;vMx'vucץsW_7n۽hFs;ՍmMk}bӥO+:呁n_u}~]v[. kxzI8OxV.:+-0« Fxa#a/?색JI4 }˒yg?3O3n>- q!L,bX3ؽhs&/meL>^yr}9,RSq~|ϕ??ti{p2c]*eXKtj/u)ҰV>nT=~=ΘB'\]&l6H$a^6 Խh4@h~?"pQ蛔V03YfIwsK{?]3b OCwqs&yʾ7'*bE>eb.V{X|Z-bEm,TS('{>6M-pOb;c`['~⯴&`Kqݬ5M3?]w)s7)rKV^nռ#ݪǹV{G9'7.w;m(ǫ Os1L8Kn#7FKͷ+F6_G # #V+m'F>KՆUFxŪ0tUgGWΉ}Zϲƈ^pLJ@uub!:ﲕ&]7]O[NS=sܞ^{l;TH?q7eF3^zaWE1Lxr]i6 TQ 5o8X|7͗~n,h[ӿk֍˧]|׏~]|VT&lnjȲvFfSr,r, M O騹sSF6q|aW ##a #u0apjLE^j> daCl,~5+`Ҡ?t.o1xɗ?̾OKf|]<ssl?csឿ4+߿Dj ,.ԞD_.k_^ٵm{ឹYT_xۢ;c!N좋ޝw+o|{9w@JFok/X19Y֋&Nʚ?I.X52V-=loScn7Yp?h{iaHMډ9Wh4]H'p1u6u& ҹSh;}Ηf'_Mb+?br#ݖu#|8Ϲ˦al$#u[}S'>ҫv/~7s~SKHX&ŶMWYiYb#{N5)4mllmGmjL~S:Oܭ&]-3쌯g:=vinE>;@;ezSt+/)Ƒ@Fkt25*&Ń+.&ōg 5).?Ԥ8&m6=q?'f{rDhda^O6Ɵwn©[n\ i_+b^BFZM m ΟVwEGu'3]s'h'+M2 _w-77oe?Ο>1ongNR| :A/hr݊ye }q^{~G֤xyӚS>6kTIyq&?ǡ&ũGۡm|F͟miw~ͯף6e{fs~;vrj^ԨN75_?|PػVۧv&ku/fmuw?w7kIcʹW>ɞO:sֳ>ngIq7F WjhD6f[f5)5_&;J945D)fN5BHBaC8|/*N|TH?͇SИM0t3^ښ4酗wg'hk/ϟ bMZ1yWa/_~|"ns|c@>6MN<-:է~+ѝzq]IAW69N\;@tT4*4COuͽ{F6. k =Nz?'7h—≞?r'~6Iym]ZMfơkLw-v3rHyZ G&faa.. {EX؇*Y#9Ƥ;~o}6ܘ1cʡcǖ v~yةA#v=ݲl3n92; 0:A׬:ͷ,u;^KAZsv's{Ip7NUw~)^ MԤ2(klx]WrHyFD',*UTt䙧i^ۨKju妬Iq5קLJ}7WkL>ȭ]v>E|7_*󅵊|ɫx\|VF[U'?Cq»\qn8}Ɠ?}g[z﹥UʣUq_WaU-GQ#h(†K6Ƭ1O/ #lx{>r>Yj4Oz{{ tzy3ةܹɭytY6¬US]B5 j4֥947KO<%vtؔ XXlu'=~7󨟏B>]72kY2#4 ik3/k^;Ȋ ?y* ^8T8ܷ({=3 ]rZ-!:!k7BtD*~o4'ec}Z?ܬUĥ{`Eh_Rz~-9-qopM:=[ګF 5<<88`fyݖg̎& \ߊ)Q̟B:AG;z{o,4Vmc?uc>wݲI jS6 z/?d{L t3 O)G)ćoTcaQ#^/Ph?1cķU \EyPou_GHۇ 2M:|ӓ`Ʈ/rOf츖O8#',yp[ o 4inAnʣMu7oA &nLN\&2U2>Vi_!+jK3s<4I\´ˠfM*Ykף#DTo?/+/W# TI/M(ZWUCseTDWQ:' E~>r.EΥT|sm,K#3شl|n L3sTHPGG+hvkj2 n(T[ vjҷsl5 j BW jLܶ.Վ. Itȇ&:Y ou;o&i_&r=s2"@D3o8+t  i.~2 w}j4j57:ک!҈,q7]7cq{hàV5snʵ;kn.s[֯Y6S?O{y[q;‰p\kWu9ܬ):nE_}[2wU@AQ$c[z{x\~<{n%ǹ.nY_sO >㶝9xg&{aZqCX AՋ*׸n"nqFV7h ߮~w".Q3'ͻnMѠ# D5&kfO (?A*|iP+ W-͟ct~{v܆?.y턷d<t2:٭Kϊv"@wNY:Ⱦign;rWyx/‡R}i0 >t kͽ"vBJ 2ܵnApP<<ڠN :-A'4b z$tNH]/l2tx:K͆ḂLӧN҄8ϟ [?a &tTcbDjXTGݣ/vw_np.7{.9tO>[q\z64rHy M͆gN?<;b?OH\.h@]NM) ԣNYЩ A%&uI2KO:BT<˃r_nᙟtW.w~Wv8ݕ_L <l h4@ظW_{=l_.픖O8p׻?5.:<Ipcug~>7͂Fɿ[hj6묞"ǀ 2`9jU_{~ymkԊ͟nYUv4x;94ׁ<Ύ25 \_q O#w{포2F@crFCy =-{7 '-hà^qz֣i?F׭ݴnAv]AWeu;͂zA'-9n͏6_s{%/=#Fvjǀ 2`F@#t$(БgrdvAaP+ :AG:h# ^ܽ#mNr7]7gݹ6 Ņ~fp܊ɿ~=1g{a{k/pN >\lPP'hu9f́bMZhuiN͠'w< \-7﬏Nx{YP/?e讝d]Ǽ=x~=4]׺W\uN >\lP@j0h]AWgQ̟B~]5BWVn݊nqeSv3ǿ!0M8-knt^nv;g{3!@;5@crFC:~iW6ρ;f츖O8#',yp[̟/ឹeonq}=o6 ~]>MNMw1|zܶcv5H5_}v vjǀ 2`Fz ܹg{6Wuvv5b z+Ch5&nxct{o;nysh=~w_v7Ouw'{Y33g~t۳B҃+k{vhvjǀ 2`k :5a7{tBaP+<‚.*ϟS*<ȇnD!c6 .9gƉOz'm= m9\Inϭ7ɕ0lةG6ˀ 4 hOP&OPS@́XӠVԔPsBM 5+B2k]&򺙇L O v'{+ ~_gg|=iWq8vjǀ 2`Zy4NK :MA+贅Xà^4 .&tOޑNᅗ\'Ln2?=nnoG[q;c6N@c>v^+?] }1` @wѷݤ/i -9O&j?A*:”M հ򗉜ΧZLs;n)?]~DE a+?^x6W~@RةG6ˀ 4y_hL7R:etI]jRLғN'.9g[L䉿=#nه+f5bo*l;S<l h44IG2߹Ȳ}có5/uk]V?W_L˿]&m Mਉo:pru.t?}&<ٕ.ĕ`;5@crFC&jO'([_]r:tG2M=8,h$tICS# 4kD#ÊKF vjǀ 2`&iNc̝~AIӏ{sNX5 ᕗD/h^p_ώvذJkݞ;&Z|od/F~*sZ#e MZ7c/T4s_,~o] A1h^<]RG1.k Ģ|s  }1` @IWMh"˪+C R+GNU) {nbrv*Ni5bQqq#e M61N]awӅ_q]; =i{qҚ{e$.hZ>cHy @.6hhh(n͒nӺn {gȎ0ؽhF1hZdW~b2W~: }1` @I:͡7U4?lGO+lN?bW~ش+?N >\lФ^=n_rqn7"]'Xq+g0\ةG6ˀ 4쏾ml;ÑeGŕ\ϕ.N >\lФFN1Њ+?ңt#vjǀ 2`&=sE ۿI$ }1` @Ij&l&ޟ5#4S<l h4@͆gN?m;ݎ~&8vjǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` fn?ĽέdrFC @T8[S>Kw&6ˀ 4 ` DR^ܞ uCEx~s5o,s~9̹>U?V}йǒ* >csVu=rj}y{@w/@e P!Ë"z,}K`&FW>gKl*6V<63sLWh/rFCՃ" ŲLj1kQ~'>m#gw/>^m(Ne ͺO;M1p`hʃE\>>4^"VŢ7y>'^C7޷>izb۽~xR}{㿻vbdcSSj>7L\*bck/zeAk }}}n̘1n``Ĺcfˆ{yM-I/(/j|{m~ޡoS"2Q>{gWPʵX*Wc9<6#j^~Ow|${Us,9P3-Q-9S>qPcRW ]hPS.xe]:ܡErHFb˼5>tQl 1PW{uFlܽNꚏjC >'uJ>bXg=BɇNdh4W6T~nmP`bm /c^yxͻpXՄW+[ ψ7)9t~1j0HN/oíQFKƊ-]z5,|RbOv/8uXZmſ/ bfMլz8 0e an(T[ rHT u^G^S cY>Xjsڜ(ЩFNE&ǢӕcQЍģ?%ֹ񪑨vFB i Fa8:iQ }?cY# ,}c˛  -wp/.(×{3#ZV[T76{|Euc˚swZ7s{zgW~x~Gyxq{K.~S8&nrs뇣H\HX1zQѱшP-|.~cCG']s£ 4oClN/oL#ZYS=8 FG|i@lkm\(hN/o;5 2>HX3WCxvyyHXC}c Dh40M$" M>f{j;5 2>HġFQ#Q OFQhl<9(j0MH=<(j$HQH,g t]]6@crHX3G'D@#ǀ 2a::FG.#eN2]\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` 1cƔcppԹcǖ v~ye }1` 0⍆rݮtzy3e }1` 0⍆ 5D~^ BrHy @.6tESZof06@cr+ fP/4   Nj :A ft0\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#ݼ~;vIK܇~e  <ҥB sK9_tR0L@#Hɮ^q|>q7=xCq 7/}}}ukܧκn|p*VDcqK*  lOYYڇFG.RË"zѝl o8rq/Hhe M*R=D"zP$#ߍ,#"r[phe5[B}ڢMBgsoDaLt8q 6q9wGO7łuiǞۧy_]|>}sS0L@#Y1q]YqOf7lt=m ݛO[9W"z"EqwO @+i4z2P *V7>bz"+T䍛PV~n=Y!;*mWۭ?G9ɀ 4 ` l 5 oݝ5 t *?2`I:Aۢ#07z :Ro9ɀ 4 ` l 1:]Ŷ dG\x_V벍*Gb=[s(h[4Mۨm6kŠפ6crFC 2K\igVl/8YT N۳mQ:AyGנ=5<l h4@@.C>RV\<>|gYk"]H_˲w3[6g'Ѽ y @.6h(\e_\͂',g54?Zhz E/McrFC ryײ:?W!00PZb l ;'6o*U/P+; жi;_fy @.6x\===fj H7\/@Ŷ oޯBEᛗ>woV${>z>Au7l̎.vj{yl^Ml h4@G恾*-EI$k z~mG~]A {7\lP!P݈4r9 jh opopޟdrFC m4GF%w54Ϡ:Btؽwm47\lP!,hy#G vD恮p]" <l h4@nA3e }1` @B '54 [6gW6P،5۳AoW=y'7A.#e 0恊v*U̫ב]恚jVyռPCGBhtd@e }1` @´RT@bHy @.6hh\l5JQ{FH;5@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@8ܘ1c)-_MWŹz{{K X?Yږ֩t#&T"ǀ 2`[O^X}UV;\WA| 7'}EMZ z|$}  crFC yܶm?CqG݋w/ڸR/n z4hPW or_)bU>.Y*gD.#e 0yqnCݶcO=^\zOMO Z-T뱢b>lHF.=>,ۤ}}٠zZoF*@crFC C9K@'b+=Tx~fAx p=J3_Kxmo=V?ѐ_O  crFC Ct"hk{gױߺ-,%H6jQoWB>rHy @.6h(yxm;#44O+6 k_h_ZŻB P=~^h;:-ϧŶw#ǀ 2`[sB/vYYl/M9FcE?ǎ::bE7;Bn˴p=ڦ|#2`y @.6h(u:aϊKhHk ?rHy @.6h(@a@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 0>7f̘r缞,<'_~+}``Ӑ^_5>rHy @.6h(pd}j-Sc`ر%<6&I5zjh44\G6ˀ 4 ` ,=l[6HPC!|!AgQqnhPq1mZ}2E9D!|\7SyQ <l h4@آ'f9M z<~Aƅ",ɠQ 7jRAׯ 6v_߀%<2ѠuoD>rHy @.6h(KݲX66O/=MVAH?&q6Jxmo"&Boh]a! !ǀ 2`KVo t",=i/}}$辱 WZFC{@crFC %:`@;c{h\ImE|V1>*u?cB=>F'ho[#k|Ai}4C.#e 0`疼q&iؽtBڇ/y+ jTǎ:c jeZGmS}e }1` @=vN(m9tm <l h4@XlrP4y$Ch4t'rHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` ?S~x5ь~pN/o!` <l FѠ_ aAy#^ BrHy @.6xSTg` l ǀ 2`CW7¿'k tzy3e }1` @FC v   sI:@crk 16Yc7\G6ˀ #h/_b҇. 6F16@cra )c l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` YZ̳tH~&3_}īQDϭmEϡ6B 뷣=G~} <l h4@8tIR_WQQ< uߜ2ݯcF60i4^:Z}ˀ1` @ؽݶF| 7Y}_HAWs‡j(9۴n0:k9vkO/\lPaknߴȍ0ʍ7nm/}S1/n z4h|uנ} \O>\Ymm\G6ˀ 4 ` r[ܱsg;ݲҳC| McH!,}@? m-]}v~~}j_2>\lPSnf1Љ8ҳVRZn g _i-zvۡf .~V@crFC CtG#k7oҳҞ^п[~Oc6 j=VϞc?ɯ3O ^Ojk\G6ˀ 4 ` l޶p#M Ӱuף{5FMʊ mX́0B+9=lZ۫-Ygs~u?-ha <l h4@ؚ : B/}ҭvj< e }1` @Ȇ%ˢ#0tă&\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` l ǀ 2`e }1` @BrHy @.6h(\G6ˀ 4 ` Y}}}n̘1ϡ,B.#׺]탃MZg#}7j"rHy @.6h(uwn{\XVvcǎ-Y簡[W\WKbz"^I3/|_g^+a <l h4@ؚY?4~;`@9.Xxƅzεp j Hl]Y˥~ھjLs ̯?\ץO?}ß}&p}iO/ee1Љ8pP@T?"6V؆n^uk*c!nU!_mM|Qkx~Y~z^C+. <_?v@crFC CtD7؍PYk7‚Wehp9.Ӿw_"?/T+/˯#s^~]4:kup2>\lPa6l8~ߦiXuw^z(*UXt_V[W~֭hYAuBz [.ÂSWx9OIkƌrHy @.6h(5x8a ; B&Knm\X{*\V>r_l痫hZ6m)+c_Yuvϑ>ZW[@crFC ӑ g.ْEG2 }j< #\G6ˀ 4 ` ZSB.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@@.#e 06@crFC 2>\lP!` <l h4@8ܘ1c)-_MWŹz{{K X?Yږnׂ8rHy @.6h(pd;\WA| 7'}EZnn4hMPuu2>\lPa{f{`e[bfV n z4hcT+Pe^đ@crFC Y,wޤ7> .(8*}S":6fҳNh_j}4hAezۡmm uב@crFC C.zp1Љ8KZIſ/c/o+6<ZG1 #ǀ 2`(s'm 3f[>\Y sO/쐊l ?,c?v 6Ă?hr|ZWe }1` @ܶ744Oó[ח_<+|텷)7zؑz>5~m_W6Lסהou2>\lPak@@.#e %4ы\G6ˀ Pԉ͛7go AAAACZѠI N W6|yffï -4@hmC 64@hmCFCOO3fL}}}CƎTœ/-\ R188-pyYy7X@s6\Ss1[[ZYoFހjoEt' bӰi?OyWS?"a^UX/־rX>KO4jԛpg%x~>t0O5㢝!E6j^- |ݫ 4ܧVӣѠ7DEH~oI^(u>t߉ѿpy;2+y 20bk(oܛa8t0_󴣣0u.>J><"#.0F^mѠ? v\7wc}5Og#G+wU+^X/X!lah%4]|%.l8r;`d&&,F¿Ս4( ?hXr8l4(GUcúY;_W7L`GNCA/7e\ sS/_.FNXe Go,mh4چFh mh4چFh mh4ĹKI!-IENDB`srpc-0.10.1/docs/images/benchmark3.png000066400000000000000000001460101454502251400174640ustar00rootroot00000000000000PNG  IHDR) 0XsRGBgAMA a pHYsod˝IDATx^ |T**Uj˶ֶwJ[u+]ֶ{í>vBBIc%!^!H B7rN8̙L2'ss͙\\\/Fa" D (Q6 l@DAa" D (Q6 l@DA4vXKIOOZ\N0A?#~As bH%&&G#/?wb~RRR|.\561 N!̙3 w2229yc׸ʕ+v|k_6?/\ȝPn:=<;o\(u]+;gwq87{8ŀxO{? wͷa/^GRA Tf&NwWװwEJ:J\3ΝR;O`A` #f}ϙsk̏La*D#6w͛7[:vd/||0mH|3u7:A` 栁}kg}_{.1vm}.4i{Ψ%Z1鮮 mERAacr |s'ܡNk[w;N#5;`HsH F({Ψ%Z1=;z՞Haxބ ۚc ! ko }H?t{y~ܡ5:1s8cv!qx<ε[w;y9(bOӱAZ[ z3s1CcQ~Mssvu0aG?èPߗ猿;+g/9_9sGHb?Cý8ƅ1{5N32 &TG e޾Nϑ@u80hJyt3cF]_gts3@q3hsGp*Τ~N7sw(| s~p:uq2_;z\| TbhP:y9 *nt<@s6ױ v?킽:Xtx A /{'\wt.(HgQatHuda|ÿýp:NkwQ}.b_}e,뙇@C6fG\v҉`@{_6bnC??3=6ax vvJ4"8=@-F}.0{Ἦkwn؍z =y-0?G.a@쏩;z3s1t'u  tN(mXsntc~x92s1:wx A"}4 y=wCKx8ѡ6'Φ{W y]޺"p鵷^ > s ed912*=yq:Ws1{:6ّyD ؏qA4l`_͝:NpGfhsH\0Mk)wSߨ%()Lj ? ߯'N'~~v n@B aГQ~:g~|]000ױ?@݆1o\8|0>핟yd r={u|}~}op,x<e2p3iY}X,xV8<eg Y>p,xp,x<e2p3iY}X,xV8<eg Y>p,xp,x<e2p3iY}X,xV8<eg Y>p,xp,x<e2p3iY}X,xV8<eg Y>p,xp,x<e2p3iY}X,xV8<eg Y>p,xp,x<e2p3iY}X,xV8<eg Y>p,xp,x<e2ps(#aq< m\ Y}X,xV8<eG6p` 222p%pqG18a A/Y}X,xVDFP{]DwmY,7= ##qരc Y}X,xVo'Zi]w]ZW,x $f0p8c>n㡂!xV8<AQxL.ȀՍ£;G}LĢh2g &h2gc 4a`h_Mk#DcsGG#l $v`םFQ`QLe S4 Yax}`4Fch s;T ~Ǟ '9U8E1іe2pӳKM8d2gGy}t@c7vvU^ow`g~@Y-7=>C>p,x<{ A7wb/A:p"^\|Qms $h2pӳSBM8d2g0'`X_yzA%mQLsu.x1LQgS9=MϾO5A<ez%;QB/ɣ1=āsO$ܩ@{}j!xV8<=՜r4wy8}nQLŘaL0k-< gC>p,xZ"b#;P).xpgגC>p,xp,xp,xp,x1dYo&m6Ҧ]VM:闺^p9A;n_{<þ'dŨ脂}R5 Y܇pG矛;&{!j+^K3SKv|t`4zc~"W~.5=yfw.4 NFj{O^4oɻw/^0w:w qs.T|<e}s#ۮ4p玃y͂kU'_msU4?nz{L<dx+HmװoY2(szwtJݣ&N>p,x<}%q[^F{0jik±Ղr xtR #5xm;GM Y}X,xviD;\ϼ_.h`Xm0ct*^G%p(:g Oˁ"Xh ,x \8TZt4O>jd2gKnrՌ88<4"sQXDh ٞ}2gc xN76)/;B40p,xV3p,xi^u[ 'h`X88컟sod5:켮 w3ϝz^k;<Ń;Ou\` ks  uD8Fc2e2gs(aʡC$h'A2g(ש0:ܹrayg^)) #0FC*x ?VƏs/Q</#Kx\wU5 Y}X,x%<=L+_ :lpX,x/:ɁE}^g;ܡGYp Aټإs<ʾ'|WT4 Y xzjR_^#ʺSP\8<]d,xOc1 wG,g>oʁE"Ux⻢qe2t۠c #p͡E]ng`Q38zg`k@,8uwE5A<e=>4_ga"40p,x<+wD|UC>p,x<Z/:*D*h`X,xV78xxV8<CwACްabOĂe2ga4>p,xp,x^4/yoP@V gc D{ZEtx5hF\W<*p頡*fXk4.Jt u/:{sg!|/T]z/ ٻ!/ ٺ|_`Cr(ܮ# iY}X,%{5h(ݹK؃pF[k+ܛJΣ-eꢍTJ.$ᎈgc Dz({5h(IOS6ւ-ٴj_Nbo-ej#6O2g\:2ppan -RQPmY,x6O2g# {!j+[ACs*mbo b;h2g m0vخrI(?u<;;[?PA<e sYíAáGZ+ Vиq. mx#hX%m;'Жe2g9hؐSRR~ 0n4 Y{δ{5h(׹奋4tSX?kvr@[<easpCG=}<8d2g(|CM{7F4hHK=2p\>rmY,x  ?p!xV8<@In #YO>~+h6X7r*o;Uaҝa?'+n4lZptx8e2P/(,A$'iDásYٞVO?Db?o .C-n,xp,xmUWkEe YnzF< <e:{ ;w#"4ԖyӞ_ }V"ڲ Y,7=#le2jϗe %ɾ!AC{U EC,A{sRc;;i-e2p3iY}X,\:2pp;3C|0m%h\0ZjZmY,x6O2gDgq|5hsQC^oN #4lK3~H'e >^4;%mY,x6O2gD疳D[CzL/ {fF,hhٿ*~O%hز hkڲ Y,7=#le2ϗv E:CMǗgç9k U&>H ־'A,xG вBT/]շ-e2p3iY}X,~|(ACΝ(H  ([~A}me YnzF< <e/'d J}KDrp,xAz.a 8t ګJv(:<6񆮠a!TY?Жe2ga4>p,xAxX=5"4S͌i zmY,x6O2g,֠#Dz4,;K?[at \ޛ3\=(mY,x6O2gr6!֠h\疗=h5xpiٿGж/Y R@[<eg Yy>Fs.S??D*hhZBUIӸ? FՖQ;Жe2ga4>p,xA=rn+ 7Ղ  [=AmzmY,x6O2gD*ãAC0BVRe⃴6v%hض)迩!݁,xp,xAD<_ZO=4$'k{4t4\ւ-ACf뵢e YnzF< <ekϥ3!'{а8r4칇!_؃;&PK[~64ګJv(:0l N0%Жe2ga4>p,xA<%?4zBD"hh+ͧSQ·hq̍]AÒ)p^;-e2p3iY}X,=_&=4򲟉DZE N_Pq=ڲ Y,7=#le24 s%ACT<M&4,6j+ ZmY,x6O2gG-p=d3v^DМC{&+dޜe YnzF< <eЭUDGYay^[iƍs!+~_XPe YnzF< <e󕣾}֠`tNQsY4aO{4tSX-h%KА`$6 ڲ Y,7=#le2zA֠$Y?؃[+hHKaҝacڣt%:H,xxVڂ#Yz-5A[<eg Ya8hȾ.4@T^1:mZE Je17w iz-uA[<egj!xV8<+́Y켩3"N_*Uв-U'oҘ]AqYMЖe2gqe 38pEp9ACsN&Gcn LDgZꃶ,xp,xV^aÆ{Аa U7^vOƹZpx7(m ]AiCbY^Kh2g ^M8d2g->,k4%1)h/vCG;5Ղ}4V倶,xp,xV GZ[:G4(4\kmiACnWB.Jѳg_,x̷p@<=OuQ מ{44n eai"mY ,x f]3y4D Y}X!., w40}ACG;5Ղo%Sѹ[ e Ynz9lFS|.RC>p,xgS \x d Z~sZpx7,ACzTq&"A[<e#p"##\B@Á_4Ȕ&Y]ߤӅ9u(K3 bvork.ŇQׂNio0 )o@AAAAAbqn`9$0FhxV8i"mY ,x "2q3"RCC>p,xB8h85hxq˳Sp\~60mT3aJNd 2-= U$,xm]*kH,xp,x#8huAG'ACk!'B.JzMh2g xxV8<^ eS~osOk:y4l%h\0AC,xp,xv3֠!d Lw{44n ҆IwZ-˞6& Жe2ga4>p,xvS+0NAúËtSX-hL|>d v?AC?,xF e YnzF< <{ .q`5h(IOFf+4S͌([8ƮaɔAT~j^7h2g xxV8<  gSхᙃPւ-]A)ߠ׃eSSřl&Жe2ga4>p,x!֠a[aJg%wKz-ZZpp)m ]AiCAT,xLYc0aO&:کau2T% qa 2- ze-e2p3iY}X"՛ACj(fÆp,xgACް xˤ c,AÇް8aVO53ւ҄hE쭖HV^x e YnzF< <854$j\.a q)?gcxn-b 2Sz{-e2p3iY}XlZQ ֠#Q45Ѳ,fn~]><[$U1վ(UO4%|4,2Joڲ Y,7=#le:4{4|<ʨj(9[~c JP?…T0围8v%h8x@[<eg \ DePP&+Kаr uo>i C|&4,6+^mY,x6O2^4 Q *C8|DͬGD·hiz'fz4|m7=m V hp JW@-xu~+̖<eg Dy@P\CwL GM=Wyk!CUpʎ,c $AGs2g xxV8ϗ]m9hx|m- ̡~ ~.,MޝUJt6A3[,x6O2)hcQD֩LmM#dfC0Pu-h(Nkz'KА`d׎h2g xxV8yO0Fq~]%xw h}wAB &Mi3Om-О%2g xxV8Ҟ^!Jj([ v%Е:?unѓZP mt%dX2eT gc YnzF< <@Yϼ9h.KO<ѯ,AcKVOsFiAÙhu@Kаf0*k[A{V8<eg A2ACk-3e#9YgZZД{hq̍agjK^gc YnzF< <@9ϧcACHK6PZTh9[~Cez :Jt-dm-%dx;n ^90h2g xxV8RO/9_WB37?k  DG%_Zp.q{%hX=t,Ogc YnzF< <@ ڈ Ǟ!?iS%Pl;鿈T;Q-h(L  +@{V8<eg <AgxuYF3$mCEδ@53ւ!m-bzA{V8<eg AC}s -Ȋ )~KMAF#tS7|Q;,MLNn+ڳ Y,7=w6;h2g 4lHOOω]?h?y Y}XXn Jg'3[9l:^UB= &2mѻh2g 4l0Ý~#l?n Y}XAþ֠l~Q +2p e5_߫'*Oh LDW5{ڳ Y,7=6Hq;Bp8p@<S w=d9|~n@s>o fӕ:3M;Ҩ:y% h FGY}X,xC P!p %&j4\q?<߱n_2(>,AòWDQc}.';g8<8FKаnxAmQPPPPPP)nm~^ ο}<|@2˾v>!{ѥcsJ qй,3t1T4}2^kFgc Ynz6p{GT8-p@< =s9hcHK[-}2a֧|]^Ùgv(:8>d VL.5#ڳ Y,7= xQPA< =s9h@sh޳Z[0>Zs%hؘ(5_kڳ Y,7=   Y}XQ A/ُ *a i V[δ@5/OMnks}2mڳ Y6ۋkiD+>ݯi6O2ϧ[D ɾ'dd+dr| @G;5~J'q_ K gc Y]8X}R6]]^#2 le|4oAZX} 2G7V_3Zs=/}ދ%hxoP8Оe2gqJ0aKQ ﭠ7s)yS ů?CϬJa4>p,~Añ'Aڦ*m=sа +V[!t9tt7iquĖeOՖa>p,x<=m״Q x;ju౴ȂC4t>g0acxnD 3\(m  균w&A@;Q,4ㇴ3˖!-vJk?h2gshhis Z`E-03-0xZ`0ja-0iʽ3"2 le/9h5AOl=Z[2G;iڶAaL^+:@{V8<@+W N ] pXԊB-,x( NqU?>~cσ+8T;ⲵ`$ le瞯V c}KK[i0st.Zau,H-4|h5@{V8n-=80u$f{F< <uWFEpf[[ z gҕtp8jokkF7h2gN6w>dn{\@2ng \|9({c}LYMM%hXm`t4\?>I%dX2v*ڗh2gp:XI36jwNH $f{F< <5ϵۣ"h[)d+dNegVvzދ%hX5^Th2;G_uGh2m4$) w8+dX@"ng \$x44 ^?82!i:}@t,~Zs%h؜>߭"ZA{V8g[ᎸlWEсa4>p,{\ACmSn%hc75N g/[Bz%o>p,xvV>}Y9yF/.ash`L% ˜e)qc8"PU2Ok>eNw^ f{F< < y+K#d[]V'⋇ Coh#q*~~4,T~j^˻=#w굎?:  8^# &8ࠂ6ppYnzF< < b hvr&2e L{e1z'fKа~o[=CC[G ViSz5$O<Y)<" xxV8AD<o G'݇wXwe4ҹ˧i-B{Aio {7SGW>p,x 3N <;VuSXJEyqG'xG^𢯀ga4>p,^{v nŪr7aѮjctmt%dH|iKа"o@dwTmN'n M]*k ڳ 31h^wi‚P E<{" eYnzF< < dϼe0kl-TxkJEichiKаq?RkSߠ=ˠ=WԷRŒ1im0(.[[fLXB.h(6O$@{6O23 ZIw>p,znnЦ$0nu6e.82S'x S*xj=h2p3iY}XA=sа{5hF?./h#Aw%s靘[,AúCB!gc<,mnKEyDs*NaA(u9yG^ h2p3iY}X=_&|=hDT7Nejk2!CLϵ5.6U4Z6::dF{V8VndpלԶ{myG`jm'9%oK*$.-=Mgc8z栁s\w%̣x ޅ;Z?Ok SkY}XMx={{ÃQxAG^Gc frU'M=Mgcyi栁Q'ݣn}4,ڕ@WZ:V}48.?hjpEY'NA#xx-)hYnzF< <♃^4 .Sx!&+KаҐg8dZs%h?Ֆ`О 5<3}cgw8+wf5:fPga4>p,.oZMǗ[gMGshk2MfОx-d(;A: ߜnɲ#.[Y ڳ xxV8VDFP{]D_2AC;74]m s*i:Ikd VFk;h/ȋ;&o*vg]ѩ ,8DK$Ey-wjk8 hPga4>p04dn v||] %hH3S[ ;VX붖5j.k'О^̑p!Y Жe2p3iY}t4$w.hrw. Ds_f8:_W̹[Ļ,ACfThꣲ֮b;bn,x6OQN&YC2qέ.C79P`4pDwEJ4J*nkH>*96s(ZQPX(e xxV8RK F[C."+NO= *a ^qA٧/k9>HX9Ư?mY,7=w6i V[\mi cnCZmО'Zbk]Z)L 9]9+-eaw7PGFDx8q2q6s9d8x-D1? x}3gSWULmKа2v I'Z/@{Vr̋1bϬ<b\?-ҋ9ڲ Ynzq`Lq0OsڹH4q?#x9d#xC G->jJ )9L4lt'ռ u4\kY}1/#H?b/|X-#@A[p =dx乶J[4,ȊmFGGo &@{&|g'О ǼV`,k)8 k29/$z1G7@[ KwN:7C9+o މN$Omx6(((S[l?}onuN@\nI?Jɥ߽o?JGp6 q^ >CA+KJbjmM#dMvZ>m%hXycj+kY}z☧4bMzD{fZF3,>lJ` PMuo Y}Bq̋1򢌼8#/&}3jyH,-eaA<ąD9!C@df_2,M>HkD hk*H9@wgiw4sm%Y̑b ڲ YnzF< <G+GGZC.G%j*+ 4L%\0 Ot|^30i,A{%z--=  O.xkL}!/ȂCb @[GwY?%ApKа"V:0W7grО /8w[ZxX` ~oT+@[^44,N$|jS~F' =G-喢plZQ\A֐aםDovx+˙ \羨@Sik#hI]CiXb?Ge CsFQ[V=Gni%o*R: ufl.z֑e2ga4>ps9(o5dRQk^o aѮw(<  K&n 哸/RU҃TIh xQԦFQ rCV ڨ7c YnzF< {GmD NQFix݆e2ga4>bS$!O4e"dV:p>]ikuO]U1=ǰogh޵L Tk(9tQwp ;KVCE}tp,xF32d $*AVm+˸іʒχBkS-ߜLK \x!2Vz{ ڈSA\5P |fwR\DgG1YQ^hD2g xxV7|2p)M\W[8nel,n 2'2HG|S[^jfւGg6dP(>pa^/e2p3iY}uS&N'92칇j^o/~!Ü-⋇Ziok#Yl`aEtpש*i,\N5}]76AH>[Bii<)X0 Oi .iBz,xw/p`8t8B1MUBaй,VpJR; CBⷵ:y]ɘN [mY^\u*-ϻ@O( 9QC Cp\BT02p9Q};=w$[YDw[Y^*ϧCOHe s=dHlosЖeПyK'{C ˦ы (-w;P3a17sc`]tD #8/e2p3iY}qkQS!C0_=WwT7^prgHy^˙ꒀ!ú[C@M[ߠk{ Oʶtߥ ujF{.n7s鱴^xOTX .e2ga4>Q㘧LM"aw>ުr㱥~[Ye'Vʲvg kcщou #G/QGCp~.6[[Zx{A^Џw¡٧/kN.q|a9Q`kȐ5$o+ $,}/d, /8Oqh_:}0i Jz+dyԼk]k_yx?Op41g e~}!aHwG=si"׶2Y,x6O+GGZC.G%j*+ -mM[Y ʒT8qrrǐaɤ[('!Ë?mZs~s/Wo#wyD(F[Fxᅩ#<GJ𴎳-3^mY,x6O1S&r&ZW;>V+p+˒胔!í?.$~+dNAL [rG5T 6S 4r۩#d9O*9mY,x6OqǕ:CsÙ)) spcv92J ^]ߺp;܉t8T  z ڲ[y@At,x@lgBã!>@_[Y('Om bCv <$v.hv\u\㷕%Ӱ`n\ǐ!-FtPtB9H2b܁h2gc3f{F< *<+Lvm݅?HtS$!O}8eJ l Uku GRi!!Cʟ7T42\~z|~-1ˎk"{"m)kG'h2gcHz3f?i;OpFF`&N-}@{?0P`<.=?N9&P]#ok6#q= MBo2ja>N(4dn v~‹B}oey$q+ˬS][Yvtѱo: $Ey3/dM6:B_G'8UbpBů?Cgk[[D7h2gcH|y, + }@wvޖbcހnF1ױ TXgo˿k}G~{}`=7 4 ng'xV߳ [6}%|,9d(ڗNf2,Os7;T kmv8D0)d>wBDT`-e 3],Rx'OR{}37:|ܱgݖϰ! xu6=7]`7H<+jȺ9hyWwT@)~k ,Ȋu%z-_kC)s2cjڶ5WӗJp,xV8RAÁ_4oEǩԜÄ\5G(K .CGciþU]r6Az/dX -fN9 0pfl."h2gc3f{6x|=•D'!9?dsԀ ?嘷~[Y&5XۅC飝CLgg/dj=MW5uNx( :XIm[ ڲ Y}X :ٞ6OQlÉ.,'֦Wv&Ҏ9D0CsBUӖeO \6}RA-3}wЮߓ3 鱴,zjE!啅K*-e_M!(xRt=Bti^{"阧EsQt ~F2#*MWv(mkm9T׳YC쭟)?U"h2gc3f{}W|QNo'xC}k؉c^z $/ X_A<2|:~!CK?mZsQ‡4(.1d9?(fD0Жe2gxxxgw8QS^9|z㘷-+yJs[[ue;c<7?dx2j85w\Gq xĸEA e up=#l5L|%v =u|iKeEXs6%: kTXNa |_YDmS8K23}6UU ڲ Y}X :ٞ6OR(9j2YG;Dp bBmss2'2|0wXK+ +'P{茣s 8UbôHJtڲ Y}X :ٞ6O}H]n%u=!TWZL0vʤC8 f}rҏB%Im=\8Hp xD@[<P7۳&<s 2qyEs[Yfʤ2p萑lO2K+Cs/dz"KTʴ)N!\ڲ Y}X :ٞ}5 %xT?9dqQA7ˢɔ2𱃫U>2ZfW/#xĂSA򢐠-ekP|#L{C w FÉg }c2}La'A۶$Ѫ9 k0Q;g5~]Ω-%oO0 %oo zڲ Y}X :ٞ6Ow#,q:&";Kcr㱥[Yf~2VϹ/duT~!дm]knkrCmwTȂ,xV8<ng s/%_D܈,Sʶ(>hK,i \xޟw!Cއ/P^5nKך{#+qNx,(m*kH,xV8<ng si':!Χu4&e/X0yV_v@Y꽉~!Cu6:.hS%VR: wNEם&>pe up=#läzS%>>J3nast)c*؃D~E}+\JCv; Cg׳QC5-eCzz:;vbuVESRR(##C-a >e!;KT Dvrr2?|%A oyS?O-:s_Ȑ;$UA53 'ѮS%ZQ8Ub4zqJh2gcs`N<ױc<ό=@n'h ~!|lϾCP;K{'xg N4]m3Su`HH>-/X+J5/dz2:hyr/`S%b2OSIuMhRgcsh9۹8OU{9 0C3|a=@pt.Po|9>?Nuc\s0;#P1~7a4*d!C S{\$)p, VQ:0;kt֍b70L0}6x. †f{@g^ȣ1bt.OVS\haҼ_bnt `}qMtx7 /Nf,u'0)d>w?T l {N#I#]Ic; mtns~qz Yn` ߧSTxl`<6qz{ 8phg qgzb5tc C悑t$+.> |VJoOލUkZnI~tD^2gcs4e};S`Lt#pTK>5 n>4oc=\^N1:^7۳&< CYQй,J˙J!`o[QtltȢ:є_ n1s.$/ Cl 9L/&m:DWI p=#lFyϼ#/!L#Zwx%u: 0I+UtL]ɘnپj n0l.Nom2gcs^,ݭ?@ٞ6OgޚN!L[[}̕::I)~0ĮG߄0;FRGswT q"Nd3[>p,x@lQ3O8L%xA>YI^ȣe{gQLϻ Rw9 \C{e15m}.1`L~ip,x@lQ3/X0?`‹A~}DUC9mp,x@lϾCP\C!áG7&[)1!9}4K .Pwg.$j;0ʍ&˦5.>e2gѨ >eI< |Z%=;KT@+Bq N_NW[Zc-_KKӶt rRѽU徙{)yS W5Kt}tĝt8g Y}X :ٞ6O)ϼD;KЧ6Uigiһ?/`hmCayáG))%.p{Z.ů?CGfmY,xV8<ng xsk]w |$NJE(Ghh; \Vj=>H; G^NO.pC/|XL٧/,0h2g up=#l&=7v,!Cݝ;K;5?ҔчޠiϰhЕTҏ#?xy-S0(.YyP[5uڲ Y>p,x@lJϗ,Q';K\ik)%n%SѶOѩU|j8jr "|'409`EcuGCn-e2g >eUyg A)dݣZ;TuЀa۵:jokn^qAsu '}}yMYO&9$#K;J.PCKvހ,x<P7۳&A{ p,x@lqs}^ ; vٱ4gGEw9 h@Z(:u<:::wJǷQ T˯..p{Z._wO_֮_-e2gx<vٹ Tɦk/M p˛ҲE?]uS{cpr?).pCVіjy~mY,xV8<4vX$&&G;:%==]?CtI˹L'n?g |=s]qtw@asR| yg ˉwx=_Z<. o?>vPFDmt壗f?9 e[̠Yے)`MO(uG&`0,x< wsnQNq`eB 9'l05cf{2g;K?`'!rT8`'1\üԿ?M+l8ARGIߧwl1,] ڲ Y>p,xڦ*[)l~ _w>_ݖ %lP a>6BQ^a>i\uǍc9k,9-S 73izYY"9Gtg vgeӆ8 \LHx^Z7tADeԴ ǀ2{1J& 0ȆQ SZyr ,x<@6M4/i_lq#03οqsfݖ %l`qٸ5~f9F`Nw{u1kØ?z\O%lgY@;K|6;b;K.pa+_kxun)Qwv78 \6G^YC'; \FϧgU^mY,xV8t chjIXkQV7:q'omބ 掹0s1̏xc_x\:zo~mUx<7㼳`㹓[a.F0kOh}j(nwKuo?.p9c,+IsӌͥtEA[<e O9nZs3c]p-a|^xX!X}#x1"ng ӄf Q#:w!{0|-:1\b IH; '#km 94nue?2A[<eᱧ-\)\]?ςhsng #= w, <Gr wD_x䃪S(73=8hȾ(4l8sgfNjm=k47Υڹ: 瓾OD? qdH5uNh2g up=zdc<]"uK0m㓴r,R[O; \6x^H}N 0 Rw-N-e2g롅<(F0`C"u<!Z+rNuXDn0u 򲽳DY_KKs |)wd5 fl.&с@[<eK 6np:|hPk`pabľ[:G4؂- t*U.GNRRҫoӃ3.wO˥č%At Жe2gc3f{zok+pH` RÁ_4/goCsrXjM0TsQMP70{w,/% wѻ~?^OYϥ_Einu ̟ǀartl:z; G MBI1\3ǒEcpr?)p˝wѸE:e Y}X :ٞ}i :*# }7Cz$pLc}ucﳾJ30p7uH/`ѯ]T;Qǀ|9i<5ڲ Y>p,x@lϾޠC0B4M%:DhB #;\Eyā-3hcڣxc,9ӞOkp *G_g^]G&9 Zu8@"pDz.C+_*iS[)C )O_0!)z(Fz=ᶮa'/O: \vK_]N_I 0 fl.|w@[<e{T|:`x4Jp .)ehdx(Z2ec`/oNҴ7Ѥ#F*qyqտ8 _M~u1;9\r̽?--e2gM >}z n/ȋ4. #/FP_]Mص ec`/boH^%}&ώ!Q2~An|^_'s JAWP=8cci4~)-ӟFЖe2gc3f{.D䇠=\ s1 DttŲ<:J[=A˧ q eaK_ ?Xs[럥5OӞNh7^K_IXEDCGt2q$B?'pD;B#x<P7۳&">7':΁칇3DDMTK[OyiGB1oiܯR򢿣I1TZXZIںT"`^>km۴b>fAqԊBZ_p: x|<eCzz:;fb&##rRRR~-;?'Os_3yu嶁|=q=zj`[m…B*}sf0 ^ޜ9%{)9N J[h+#Ä@iM ɢы h֡͡?3EF,xV8<lBF;fu wst-qq{xŘ;ݝ7_SG 'l00?G~M;=p=zjć UN'?9L}C} U"j/f,ͥδ/; `@;/)2%bʇѼ5I[_!>] ZаrXD3Vy NZxX ohiן!:B#x<cŁ t[l>),t;>nts8  0N8yr8ao8f\|ܸ>o޼F}Fc~uǍcĘ9k,9-&lϾީxCÁpÅaa <%:ڻ!>}Jtc`)17hS"fM)k„k~NiK>~a}TC{P_SXjڶBfSZ`p]aV~pWG@%F,xV8tϫVҐݎG#Yxg{[.F1F4_q vNǍηӵw#ycs矍3~1uˏ1g87^sh뭪'>9& Fp(#,̥w; S"^9^z^JX2&?|??mجF+Լjx?Pmt4;\m5XQ֩[c,֦LW Y}X==#{ ntpG֩sk t-qweyܙc0b<.u4buq T&lϾޫD payp`DO(86 -]c@0^$|_7w~H/JO6ZaF+x=IW&SeZ^UBMmfl.PԩpaߖwrU~;ޙ{4( e2G`N_#YLI1{ ;\qv"صǍߝj#y:|1guدNxqhѪIT|r 3PS`..$/ɻMJk=>4-emS`)<%"ii[k^*|Th~Luo=MW2kYQ[=&^'H?FTxOTpԨe2)nFruZatpG00u̯:1 w d7۳&!XHTfB@C :ӹ˧(?ч;[+~F z=60V A^_6}PKPᣗytdu4\i;p,x{ӠIw`.4~6tv; w#g؏nq8;9.:m`ĩԙ7q1?f|M;\~c FlϾɇ.C{s`.FPLTYP'5Tx!\CwϠW%4ﯵ84p 卄hь!hokpP?㗩yjz&5Zku\zz=[]D׆u9 CxN t٩e2]Ko $Zp=zjʋ֐Ot6 كp,x <lϾDE3…Dt>MTKtjbm=̤9[~C -&KڤiUikT-Jz?THկoj$Z '4<6.  -|} S8<eCV_kmG -\:DukvRYq96ZaAV,MW4նLx{VNsmkI0^ޙ4>L'J =N +'t 31ڨ?gxk%b(STx^hx1h٩e2g1I/9\<*)P0=\>=^A Wjf}:N ;D͛y ^&Hkc/)ߤ ߦCOSOO"0;STGMwRTm_?;c Y}X :ٞ}=hg^i !\h+*=k:KHGq-~B/J2-1Le17I_=BʼnPOPXjڶZ P{e16zUZ_pfl.ы i!S[ ߖN}X,xV8<ng_Z!8LȾdJw;AJnC;I?C3?y*e{LIiߥRb?G['UjZBǷQ{UIȡã x:< )Dp*(e).gI9*O$☛h]ik?SPá:C0ux}^'KuB1 >Ӡ;N}X,xV8<ng†ߠIkGY‚XK2r^~?W^RS"O6>D.::_wrxgၷt  ;I0!g>p,x<P7۳RaCGC>p̘7h;17: s-~o_EXhF(-p,x<P7۳RaÔ~BSҿOh;AF0iHoۻ2 Ul ]] ą .] .vڤڂURi-Mk F,?EojMdL**i }>;o{Nss|?2s9=OΟT e;xǮN+^t8ЙDS0ʦ :Ag<̇ȅvvq 9G13GUN6߰q;wݐ,]iؗ?~ӹ3k5NF=tO~7%BB}{3 W.t#9rc g.bãn2;j/N|xMT=A7cMkEZ=ǛIzncg@1d9~tٟ]ZT?:\1=3/?K"^}td\?7c$kEZggW֛>h;;8r#͗tKmMmT$PVD5!T:AE td9@q evUl6Db]#> A7Ԋ&tϠ4 c ?2?Ϯ >[ޙãI D=A7b kEZӍ?ZDO$v;;8r#ԩ]m~K_l!GI&5=rJgGWb>c ?2wh4J M[^^O`uuȷAr+zϯIؠfPLg|7Euٟ)6?2c g8rޞ?_I^VMύ,>`>tٟ)6?2c g8r>~m/(6̧.39G1s @΀]g 5rc ?2?k@1d9~tٟ)6?2c g8r?Sl@d9@q e؀^#g8r#LFq 9G13Gbz#9rc g.39G1s @1s ]<ņմд9b g8r#9r˜ؠ"F *>l#rc ?2c y. *,g3`0 #rc ?2c yn yqa4mR :G @1d9@1t39G1s @1s ]2ӧO78Fh4Fh72Jo v\d8^|vj;gn (6VQlZEbh* U. 0-,,x3 Ք7Z]]mrWerǶեE2wbyy'ehy{4O,; igF_)?6ƾtjYL+Bllϴoy^,?cYy؀^7%Qlvj0?Q|>\Ur j2Ih)r)6[: O;1V+gݾ(ScSԠ `/e_9>|xeT]QYmטoӎ5O4xQ.>ퟷ:|Sveluvl9i;]?w}\˗`>ZDY@iwumEhgoTgrC.y3-γ5;PUt5vVTO:mijVT(6?egX3ΠZEbh* U@(6VQlZ0:b۠IENDB`srpc-0.10.1/docs/images/benchmark4.png000066400000000000000000000776711454502251400175050ustar00rootroot00000000000000PNG  IHDR*eBsRGBgAMA a pHYsodNIDATx^ y!e4۸[;-m$NB\ih8uEwNKXBZ, Y@`€A  $$J YHyVϼ컻33~?u_Ҿ3a~f{}jf) h6jf) h6jf) h6jf) ~Yr 6,Z[[K~SN-]:twyswSzf_X4SRth_zr=ދhYaq!.LocL??_׏ ""Eoh(}굋}(M]P[ 7*\Q4c4? ~kAUEYb7.22ȷ)aÃi6Ŀ/Ej [3MCIP qc>7h͏f_lo՚ 6ai_?Jt~}|A__E~Vq#U1V_H7bpQ$NXj[Q^c/s ߯^16Gq47 RE_"2V n|B4u>ޖqqS7۩AQ~T^X؞Su SNR!=~5Lŷ K7c' ~Q |*ȸ V|J+#b6.8XG[JVyo'"X?%ޏJ׭w8P!CJ\xoQ_~tVqZ@lK\TXw/}[|__3R$!~]eǩ Ea!p$gqUnwqMty"=p}T-J&WzX%;  @EW /$.*E\L_+j,x==H  9"*kSI\d7~SgT&Roo..qK-~.>ZƏ-?ޢ藞 hNwڛfCOTE{+nTE;? ~S֣otP& 3G1@͆4S<>`Wm6ȟ3gN3S 뽽7?gf pE8XY^ A>Ӏlhf |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(V6lXlҤI])J͙32vۖE>|~jzz3 BN~l(wd/{ W Zۨ)F@,m zJ@apA||!?h6m;Cuٶq\5tiqfC,n*LjMj4t;]o_96EJ>C%ޏ.0x A>Ӏ4 jGl3l녿W6˶m{>"4DPdM%\,v[қfCx\}\ǗIj{bk~π/4͆}qגzŶKtGxo Mu!_'Y꺩ǢoxӀ4 jO3 ]@c쩶ɥ{.:ة?ࡐׯv[fC%z.?efCq'}Gl'ܟz| _i y~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A ZZZlذa]V(momm-yz}T:yÇOC]| _i 1U7̙ӭֶI&è @7I5~π/4͆~Ӏ4 jO3 n ::;.o{x,EO.̈?mTez~π/4ǀ6b*TI<^ |3 BN~ ZAſ_M@?fD= A>Ӏlr _{{o0~π3 9 1(͆xY4K u   gRfO@F_(| _io6̙3'Y,뽽7?gf j8R*Y^ A>ӀlhV |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?ga;٤'εsM|ﳟ4z3 | Bƾ^شt ͢L@#?gqm,T|y=/lk/Ͳ/̴W\iK/(D*+LƙmA4tܓW⻥h6 A6lذ,ؤI.S.13gNe9V---e?ӓÇ[{{{괿p]Wx=ݾ}- A>sYY`EYɵd ?{]0p]`dVL{[v=,6qh6 1*"_ @WS nXsJf71zl!?g=wf ^+|2k˴47ay3/?o=l>xW\qEe8 1sZ~9>#a05 .%?Taǡ׭'dSY=o<='Qsxk[ Jfc3\'/$i64g6O|=CqA>wmȊk}#ܴ袆Zp٣J1޹1?~WBxоҳ&z?+74q|iJW疮w4 Ԕ1s6{sSQ |A7tR(2 >4$_?c- a%6<{L3\=VO֨˳e9&rknsڬO+Z̼KmţcE;-~͝zzk)vQ*k4لϖAl$dzJQ PdBA|jPlzT*%_?3/ǡPPscAd?gFX/dŵ o ["+e99Y7k8oh6 41篓oRMSG\T(84T@ =Ӻ j>nT x?}v5}K=/>~Cˑgt~9‚uew~9„y;+3Rcc~9 9w9B#]ח$ W+Y^09Y/;"Y?6d~5_L귎d%/f__Qlhzxq4}T(0Vč/gu_VT*ݡI.6}?sJ]?m |r*UdTWQ\`/GPBC =A =f5;r5CA1*L 1'}>ٛ{$ w7*+Ch{PqBG' 'nfPW u$ FbpQ%O&{UnI*aɿϭO=kѲ?s̉Y㩿h64ɚ ZzR4fX2yphx9Bw~9PrT!?P_ǧ/GPxXLT̼⤵8(ށf5݊ TfdHf,~6sPa\Iqߵ%I[PC')ɿߛ|g߳fx1lFC- BMTxE_f4`hf?o(N? Zȫ\RWa(4AC),GЁ53B!,GЁ5'MaZ}|(š05߂vň6bCHZ/nz"]x ͟XVбEB[qbώ7Kh~πC9_t~9;A P**tl=rAωCE0?VBQc<%?|E'%ƴĶf 6"]"q_V+tv|A =DY݊"=đ#J@ml(|h|^ߺ&+uV**u捲Aggr/Y P[@}Z7PhbB1NQ Gq 59=N!݊q[ Z/:!r4 8 ?!UL#,z크/GPR@EAǘ_'r 3oU܆mH з*҇>mȯYra1ju@9fCA'3TOz\=vWa9;gYqQVl?pLV7rZ_|7{La9N#9:|Q}۳-kp}ΡE l)"B =0_ \Qo@zPP  47:R\|,[#ݖ#-VN pF>=р4 b 6Om 5,0PC 5 j/GP#D =w b:8]hdPSQ#އB;4fdqo@[s GC ^mk2T{4͆?g}- PA*Z*Z,GH 7^q3S*fLV||,Y7ByǨw8N 9 >5zh 1~@sPa:b ~8FetpF5 tx9樆P?ա fJOtL>a-BZ>P%|?ohEhpf{4͆?gq 1 tTCA{#m=ւ^| g2vç h3LyLWڙ t=_!AΖр4 b ơq.tq!*R3tG=BgY҇4lڙ R:|hY}UvߩQ"U7BT;աfFU;ANu A >x:N8}E6b_' niٱtr*|g^p|Y:D{y錬:Z|d,:L 4©u 1 3 A >:Bb¼mxc]9w #1t5Rg2vC!̬ޣ?h6A>A Pⱗ Mz.>9Y+%6©APLS9rр4 b $tjğm_Żz!HNuhYY; ȟɀSh 1~@gE ݵ)kΦ_~ƙcN̚р4 b૬ņ mmmK:i[kkkN&M꺾n5gΜ>}/jkg;|:w"tEogjqhfNJčT5L j&|֦!}ŧi4N A ߛXӛlU\Yb?.x 9n*oux_yOJQ}8>=ĸ$DgDбt <1Tu0E]ڙgrfCA[}d;y٦ҥ7tx|}UXԾtYIaz|uEhx{=K&Ou]_jyߟGN=l>xW\qEe7|*+z 3Hhf"hFf& qhf:hCу,π/4͆}r|b=Y8i"oT@V^WT+m)}*++4#z\~C!%_]tmG8K㔰m |ʩaK AAB[ԁBAhh_1+;fC_π/4͆}˒z P!"9_ܼJkj<}~UxWphzS11AؖG Meo8|KxM|:   o+ݲaödل% q:36Q 3 9 AF4 Z1=e#{tqѫo%o?=]mϋ/|1/a[~ #ޞz9~Ε/=5'= Hk\uTsA?U sZ{a{hvӨM.>9;U= BN~l(~~ a&K7DEۘZ P P%PP_m]'.\Ǜ*T/b^¶>?vzDKGqgm?N__u>`P@Mgxa$;;;CI6BLy͝z=3R{g||!?h6mm;l9BUw=M [F"oW;=.T\Ǯ1A"XQ jY*T惚o[7=_ _i 1r쇡|8ĻWe -{Tc!ZF={Pf3H:Pب7 !4{Aԁ Qπ/4͆?g4ޜABLABg/ԕ 97;Ni grh~カvNmKc~ F3HAb-־b=q7햋NJ6B ؋][ח\grhnj,lǶm5fG>h=hC3HhD8Dp!Z-ɦBwM<Ŗaok Րπ&پϡՏ؁)/=1O؞{9v>ǎf'Uv5Boh6hx3ب79AbMʲV{ldS!f/̻ft۳ "߄R>RemǶ˿T&@=}G@#1X0Щ'AB g셍f& qsթLF|'U޺!YP }ONV3{Q^N³7[/|bAץQzl6:ݞbᥭ?lokk+m0A>cyp$=dcAɖR93 Bn,vЬ|FC;r[!H}[!n~ԭ}y m/'h>nECqP8nnU8떯+6 J+E[/&ǩB(=mV_WFUm6Wj&M1 | QO:X :~)B:>ÊGfg>{yhCVw0->}hbb#$;oV"8?\5(B{TbߝѰ?f4Ԣ U *5!Oſ~noo Z g+zu[ dgi¬O03MD>ϑv+ T| /=ѭٿnG&w+v9[Q$ |I/O8k*5k"{W0j^gPU ---]KaA Z$4{aÚm=I6BLywi̼K =OEK3ۻ\7\Ven܁ewu+=v]P/SwŅѿ򯱖5t}-'w;n/-Pg q4 $}՗3Hl߼ƞ?~4dc!ĭ>iO][jܷtSAUۭ:e}E t/:[An>[::K X{wO[:uK(7рl/qzP hXvkOgMx7ӫſff\ɦBG?y?\@X|m"# .{G,/Fxw޼xcC7wk -W=fV.,W& ZlԨb?,Rv5 zoZPjt7:6ByfoSz 4ޣ? *1Ta"uPE1~Wo\T 5+nɖC$Z^V >xvքX쭶ы64{AA u0H޼6;>=@f7Mvo G~l(2- FijJ,:~G|Yn:ȧ1k-]g8L6$OO촓BRi,u:Kӷگe:h_&3N}h|(G|=fCA |iZDž m]WQS"jFn6'͆@>-:c/Q ?5v5_L6لْ#y^ZZ-O<3HH(c8:ƀV||!?h6q۳M{eYY:=_ 93!ŪV6tmW뫩k~ggvE^~ϡyCl||O|śm¼l,(4GgYkǒM0ۋ][חi;roz,+;`"DHŶ_]{rzv *wgrfCA+o?b3.8>KN(C#@(\}%$/ Vźi6Tڏŏ5Y7tY~Jcg6q ͆3H\7ň{b͙={ɦBwM<Ş~pm\7ߎ9T+[¼`;oNCt\?[q{@VgrfCA>[55ŵ[.:V/\TD7&Ԫ O׶PKjC!8+=Et(S(uY\K|jtY~ )lh||i^E gv?[&9qU[lkmCw‘1i ,H5R}Y3bYsBlH3 9 AF4@rO}uo:(x/IEjA(EqmEy<<|sA?$77D}W{|i.E 1/ٕ3nw >BhvW̲{ehY~$[ְou;dв]3.[FKhY`#_i yt`?ЗSxVAw_ݢƗj|BB"Dj6/ 0Jcof`pA $4{aM_){yotPkOͯ/)Б-xy{bZv@!ƞQ!s9D"_i y^~zͼQ 5^Xt]ҡ%||iLA_|7`8?Kw6n<ג:;{O̖C,-;_M7STcdvC}E>Ӏ4 8i dї ^lz tW6x<.WxQRjܩ۪;vNF6!HǶ˿dn>=v]r|grfCA | g}ŕ3H'v5.vc=uw.݃_ݞ5 FB(-PCf_eP<;;Č%t;D6{ۄҦua͟h7)ݧl9:C1ҶOt#!Z:mmX3P\:ĢHAb-d^ol%F|0X1{gs纞:/f!j9ĭvA 5a!_i 1~Q ?ٟE707mcM3]KFr&;nN'j$CqtJA xCN~l(|Jg?˯]uH6Bllن5CK{Agtxv~^vƇT#!;ZvFK0}9D_π/4͆?羫tq? :CɦB?<{qYq8roz,׹"DHŶ_\t{}Wi BN~l(|$?'ڮ{b]Omw3{![hj||!?h6A>  w ߷+~ OH6Sm-6;rPiM jgl,H5R}Y3bYsBl@}π/4͆?$&n]|ݿ+/u_M6BrIجewצpp#ٲ7~+[j$C&vl.ex3 9 A >ϯo]AGc߿mS튟|ʦl*q5_ͯ/)d!^^`rf|r+\3 9 A > ? 6߱\vRf]|Ϳ۶nlۿgKiA̖C,-;_M7STcdvCb9DPPp[O9_17}O6eGMGc]{=3R{gޚ-~d;}#j$c_]7cs9wK{D3P| ۿl^YɸbF|0XqOSw~^}.;o{io۳ƀ~Y HjHXy޼G4+ޟ_i 1~x3H?.ma׎pQܩ۪MϗԸ\qH>ҍDh;Gg=au/3 9 A >fp]zͿɗ|ndc!>xqw`wiOI t=_Br[1@jPG3`jo9&LMMGkM?Ӟ?Ѷo^SK"b/ɎI )u\Ri+14 BN~l(9Ay#~&_g/vo{ε kÇ8tF^[aeg|H5Regؿ ,@3 9 A >Fuwm?񿴩?l*C7z۹txXv3s9Dmܹ^"{Nx|!?h6HkVj>tJM8f])[<V-xa}d;[ql9DlX#P π/4͆?;76'cSGZbakN=;,api{f3B3RTL-̀grfCA |GWWK=T߲'=6vP9#Á=͚&B*O8#kF_03kN @ BN~l(c Yk†5^ɦBWMڭjWbG!޲>}Gs7FB>]%u9:C~G1 BN~l(7}[`7ϯ~셩oo{1C0H5Ei9Vg7-h?Ӏ4 bmo8>f]dS+F|&_ l ewGd!^_չ⎑}_ ҉wn{hl_@Pt*{:f=ϲu3lǦ v̇mi,k{n۾oKzlqgzf; x3 9 A >p5kv˅'mǴ'6S_[ݥ֖N{qMvN2HȇNO2t:NN_ ?Ӏ4 b6LaDmS|OvͧmK.١õ]r3: o?/;CW-;wdg`92ޟ_i 1kMv_TPLShe'ٸ~.vs+kw#lϜq!MTlONwv @x|!?h645MU8 ]p;Ћ7ۆml9 l#mMrg!:nd{8 4;ޟ_i 1ͣRܒcnRV}[>j鿗5ڱ.SDnKc~//}OLXx9S>j9āt.0`x|!?h645V/lBMQca۸?.kl ZZX 2ޟ_i 1ZA`S5#`&í[ +dY#HH6:b_CVn BN~ x!n06iҤj1  |d:XwlƢ dPl4;¼ҭ4#ޟ_ih6W u |XvW&ϳKfێϲm_hfCX/g---e |@}Ul2:.j2OGI>~π/4ǀ6bsɚRB Oβ^+6yms'ovS߷w/B&æ  Rfhv9PQ 6oLnMΙ .L<||!?٠ a :xc |@mTj2LAoMG֭π3 9 15c6zo/w7n8cP  A>Ӏlhf |@ߨ0wݚ 7M'π3 9 A >wo^S07mƼ;M| _i 1ܲڍ#>pM| _i 1Ul2tĕ? z! ?grfCA |@]lrᑿo&C@>~π/4͆rj2<~ #de'5d! ?grfCA |@{Tl2Lvi&C@>~π/4͆0ܷ=nMdgPjM/th&C@>~π/4͆0>>|A&χ5]! ?grfCA |*dX2b>ݚ \xBS7||!?h654,۰-dgP:r=6٦&C@>~π/4͆Mh2xIݚ SGko&C@>~π/4͆3 dumx55{! ?grfCA |3l1d  A>Ӏ4 bC3duMq5yk23 BN~l(ﶛ \Yf9M| _i 1𡙨0c§5n23 BN~l(͠j?WZRfdgPٺOP A>Ӏ4 bC#da)G||!?h6FlUbK,7ԛ  A>Ӏ4 bC#Pau5h2 A>Ӏ4 b`d'}^zenh2π3 9 A > 77&Cπ3 9 A > d$5~) D>~π/4͆0޲jn+]M!?grfCA |'5n?QFaπ3 9 A >C&ømK,GgPjiY_dXkP3 BN~l(5n:MO0@gPZᆱOM#G}M ?grfCA |;߰mJGh24||!?h6ިdq q4 A>Ӏ4 bC[vo͊M_Ç}ME>~π/4͆PMdmi24||!?h6jMFgG;o{Gd\3 BN~l(U|&C"?grfCA |&øҽ0{b?dh3 BN~l(ohSGO&/~ۻkSGdhL3fϜj|l;xPД5;&]h28B>MN͞bf-]@=fCA |CK&Èڃ?okҵg`xlةعi9]D#FPDzϕ7T**0-߉**P^^+B zj2wۻW}MF>.".4K0|ḥEP@TlvB(^u Y~9^߷g-Dw*oʤ"W>T4ƿ|K|C'LMH-ңbFqBʡ?S6pfCA'ddX8~t忲i#>Ha!Cۏ~qq!<| WvC0.h4!d;J랊1/IE<[&ZvFo):ݥ%8D8,SOxKE*BԺ 4 8&rM_T?6[ ϔ?Dk=: CSpThLȑ|čTKRyX9ߡC,hR4 LsSŗv&ufM^#/¬Hxmjsgt<>F>cHTp cO*1<lTMTc>[ A7Vu8K_O|iNpڦ:&2V-wH}x]ַuu g ?Z>vtt͇fk1:`* Q* ݦN`ؓO|i.=5fwlo@947G0p+⁞!0d 8qXBxՆ fmmm-fÇ϶ 0&ò٥Mp˺o8{41 BNQ4XS^z¿WaҤIMK_c+k2>[aEa[;遯?ΐ2ZE3c/թW7T~noo`kLj2ܶvubevȡ-:dAs?\cSǞ*S& q.`k,pMtRQ]jfp@) BN,+wSG ~,[Ц=bWh2l=qυ/n튧훿o^nMM7/{ue'#^]6o;jAS̈́Rޯm; s$MAAD:>hcΜ9Y3 .Cs{/z';&Ê+_H6nOmvPZsf28!`Q O霙-GfC^<@qH{opM/h&Ì1'dN(A u!Tu]ʱ ghfijY^*dXX;o{h28 j*ġm3 BN~ Z0 d6Od*t`E|Qgih;!TSyʠ> ?grfCA |Sacm48lf0[tS!]Bg&j| _i 1Gh2Rɰxloi24}kޚbLǥ !yͳ7j| _i 1VWaivNû;~ӟI7BӀ4 b્ #m۹emh24k6L4{g/,dGb}jL ||!?h6?0? ^IfK?n*PAM7ƛyA>~π/4͆&n2LvÈtk4<6l dΆ=hCB -rP A>Ӏ4 bdu+6m=QFh24 QjuTS!f/:T4(||!?h6WLh2ٟ+˦ MfSL`NyN]SYꔖM| _i 1UW0w"6BA엳|lIB̞/π3 9 A &ÝnOt&uKDalG_L7X~٫#̶k Րπ3 9 A reM>mSGӭpߵdh$7mj5{lK7Bh‹_7{{ف7K;| _i 1u*d{mnщ& ^93!TS;g:6c3 BN~l(h|iM},MFc(h6f%hvBB4A4a  BN~l(h|E ֭]E' DOqt<"TCgX?lg/TPPB἟[ʲV;rhJa[k֔3B4{aɝgЙ&t d A>Ӏ4 *_YSlʘdla=tS!Sǘ:Kv-/y| _i y4f]|^0:zC uwنf4[p\b';@ky١>~π/4͆|]M{.{6x nw皵cSB^xt7ƛyd?grfCA|>ڭ1{nD Fjj,PB-v6&/| _i yʛ baţcྣSi2Ԁ8hӀ4 j/n2m%0tFQmi@`:($23 9 Af4nu-= K4/󴓩BRi,u:K >~π/4͆m U {wm*݊&C\b~/ q,٫#̶c  BN~l(Y|[ach2mSKg-XB^/23 9 AF6m۹} lGgNM7X~kͶ-d?grfCA:MoS07_4{{zNH5Bhvf9hf=A||!?h6h_&b-_P(hFf& qhf:hd?grfCA2Mqz&ãgٻWnE!IPбJϳd曝g' A>Ӏ4 /doٴav"T1fN<˄6W A>Ӏ4 ham[do[SVitc!^fv A>Ӏ4 聯?˯4M"6{wY9fK?n*gO7{cٮ`(  BN~l(hTac5T 6L4{g/,d/f٘PPpޝgTl2={KM5 ,P@̓TS!jBwMi A>Ӏ4 7۸k?gSGK!OA!Xg:Qh9PP_M?c+fn5 :@Ԩ6B^ u Hd?grfCAz&vWʲV;rPv!d)&uɕ_yN]{)-^  BN~l(/T&ؔ1dO &MfefNJ7B`W6^o}i@A||!?h6ha'o]6?dl}3 q,?f3{5PPo_&.{vͅ' &Á76t !4{ů=v@A||!?h6TJ5ݚ -UdDЌLH5 tЌ| _i B cfZɰѱvpv. :YRM:6f9h >~π/4͆*P&[&_ M?aӀ4zl9&>'ʚ Ndػs@56p pӟI7B]w2;Թ4h| _i UhFG\=D:C^]y?K6m۹emvۦh2h-TB͇gO7{cٞK;d?grfCfnFjBMdP@ 5!XlLNA||!?h6Tql00G[ϲdm&8@:`> !4{AR -  BN~lbfǺ7qUvl2 :XQ uJK`  BN~ xaذa]^j6|mmm^ݓ~7h}ŧɠKrٚo-:)TV>w A>Ӏlhmm*I&u.Wc@꽽'o8Z/HYaKvp 2۹lX_L7X9WGmd?grc@ 1j5Yޓ^[n/^;vc|nq=&Mf:^6[tS!f/uxd?grc a)CKKKiK_9P=y75:ٿum2hf6{tS!Nn23 9 1͆@TK6rPc}  Bn,vЬA||!? jh{{Ov\ߓ͆w&pWۛ[mۊivşwWk_y)O   h2(̙7X=y J;3B4{Ag&t q@Mz0xgrc@ j0c6Y A=uP=Qag֋{h>l\?n*xU~i&d?grc@ ͬW/5foN6[}قҍO6{fg^z  BN~l( uxٻs1[tS!f/<{<_JSw^R?jӀ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4nNz>>z}~Zπ3 9 475?~{Ovʼn-]h6hx3 BNlߺX)T>ǘmB_.gL}N S&C#5B|eWh6hx3 "ӮQ](NE۫; Jq׳$ |L~dQ,[$T TsSɢ[/  A>OEjo]>p8Yc} J*Sq飯' |8gx!YC͘TQO^$'uħ/{}G@#?g``{Wo*SϹ>ʊc+L&F/\lCsJϗ5*'Χj7ƪżW5*Ś{rRlw(#$eۉڲY/E@#?g+7wSUV|WӮYUV8G3EĿj\W 5₸R|uewbʊJoZp8tR6p1>􃧲 h4 | Ѩ6:U<(.-i T1ٴQauF=J&.E:W N₸R\hcRhGP)oUV W 44<|FoEPi:q\썮"2`>Dž}i̢[qWvue x}N7P5Pf?g v6X bMO^"??~(x^uxFN~l(|*oCoBTL"5P*Mw(z x\{?f8_ׯH3&/ }^s=c Æ VÇwmokk+m?g>>zAv\?7*6<ZWeZ^=*=qk4qkc&OR_m9 1֮͆B_oiiIr5{| /6-ef5[ˊK}iBao7>ίߧȯW3':+BЯk^s~5E 1͆lPY |3Иts4:Epf {F,(k?k ]ſE|9nؾPрlL8|& |3P_*`UȆS ӗa et̀]eOw !4k^U+XhAi6@PB  R ᠇*_y8aX :[_Oqc>p,i? : qWa!@*O(Y;q 8ye4 !ԩUyټ7k fC3c _:Uo.[)ꔀ*҄O\탹4!04Eǭ [Wź||!?h6A>#Pa_<4A`-M?T"^Ɔzai }]ЌgrfCA |?*MW都4A_N_grfCA |sc/M7* F_EzXoSE}C&hfBKuiB3!_i 1~&ֱTx :V xi)* :Ƃ[4Ab&!B>Ӏ4 b Qq &*UkIv U;-YA;^APPg$PMv-MP!_pʄ僾4sW,K.Mq9`zN*=K e?Ӏ4 bh|޴` SU4A5T}&S'=S4A3443 9 A >CG/[p-Y/Mkbi.?^pf3,Mxxy94grfCA |@Sao?>zAvP@ 2+UZV5ㅲ ǜTg>uaiS~iKЬx|!?h647ǟVVtBQp}_oT&hCX07ǭYzaifQCπ/4͆Ƥ*A8be ~t,G#,MP 4AM=xi:ޟ_i 1?A8Ţ "<Pg'P%,A&hyD4A =^=4A,ܴ@} BN~l(,`["[gPѭ)*Ϲ=+m~X0gK~b[G;bAQ74A=?&grfCA |n˞Z TPfAL 'Y,*tzz~π/4͆}Q| _i   BN~l聊S'=c _?/d?grfC*?|)GG-K@  BN~l I>~π/4͆*j9#23 9 Aߝ"88v_41>~π/4͆*4ࣣ5NՖ-Wм A>Ӏ4zgXfS˞8P A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grc 6l0kmm-m4|lۋb ?grc@ *U7I&eבzo >||!?W*f@{{o0~π3 9 0͆oiiɚ |3 BN~l(| _ii6h{{o"   XR :xc~ ||!?0gΜOmzo/| _im643>||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?gP A>Ӏ4 b ?grfCA |3 BN~l(| _i 1~π3 9 A >||!?h6A>~π/4͆?g2͆9sذaòhii)m-| _i!lP= j> A>ӀC٠B||!?h6A>~π/4ǐi6wśoXAAAATеԐ! uja 9l5EPS4@Ml5EPS. 6l0koo/mЌߜ9s|ֿפIޟ^ f&@s?l_XiWlP0u44<}Q@4?rY+Kz?s+ Z~](KVkB ,5  (ColаT.(afм\ 4C9?kPG754 Ah^jh6/~f"|CSAa[f5ۙ `pl5EPS4@Ml5EPS4@Ml5EPCf?ߚIENDB`srpc-0.10.1/docs/images/benchmark5.png000066400000000000000000001505641454502251400174770ustar00rootroot00000000000000PNG  IHDRAiBN,sRGBgAMA a pHYsod IDATx^ pUw'xn3l/Cxz-Ǵ=vLt#қeLgiO# ǐSUISP)JV&`.d`ldCB @zg{K^'>?r9CDDDDDDDXQV` BDDDDDDDY%e DDDDDDDXQV` BDDDDDDDY%e DDDDDDDXQV` BDDDDDDDY%e DDDDDDDXQV` BDDDDDDDY%Ay7^{mt[|2իClkkkDDDD`h%H /Ad҅% AC^P;Glܸ1cb0/\pqrD߻wWlr?y-@aaϞ/O$ss^o߾}Mkmz""",A ^0 ۣmR0ѶBCfo ¿V![6He!fKoN6ypM DD%𿐊WoWm~A/w${{)/P'G M^<˿_.{+/?^"|?:Ex8߯|,ON W~璯{ zۉw(Z?>畟ɛK~DDDXx/b~$_ۼ~Ʉ QG!Eˋhi!_0F? ] Xy^eI})hA2;"_yXKXA)A}& DD# hˋA AZ  iE^ &"M}xńyᛈ8b{,y__%d_+X+?w^ ~WGe%ߥLX?;)'~wLDD%B{M^{/=A/~\ ^ &B|7كn?(~6?Q?-_\{?cF}7 /$_-;?(dKw9}mJ""Ta BDD0/_x/}x"/~6WhG8z1 /ß?my|(٤h;8Qu?O+Ys/zxo&v['?w?ټ=?> ngX DD# KtX;ӿ^ J"&ދx/"%_xxGE{;N/~DW26XHexo&v["?ǟ//L X"""$,AFRn}/0y)oG3s>KEd"?W[b/O"/%{Q"?wzDǻyGƈX=C)/ɐ'߇?S߫w{&߇|<~f""",A 袉! """JK""dbb 2v7CDDD!""Xd.[U%Hh zk%%A˷l,A2Wx "F;ExX'%""ı!""""""K""""""" ,A(+!""""""K""""""" ,A(+!""""""K""""""" ,A(+!""""""BZK;˗/ןٓ^sR"G!""""""` BDDDDDDDY%e-A\\v7nܸqƍ7nܸq6;wWfel rQ!`b. &悉`b. .fV.i)Ajkka"` ;Ls\01Ls\p1LrI w &悉`b. &悉b6ls\01Ls\01\\XQ܁`b. &悉`b. &悋` K2;Ls\01Ls\p1Lra BFq`b. &悉`b. .fV.,A(@01Ls\01HCΣ;NKŴl|8<ܾZl}U}۹<1m;Ί|9f o_oǵ'mRq ~o[=9d+OJYb BF01L\۝EӷZN^^CYt97Dﶝ[~mG'?-7_ +pmp61MOl9 lطӜOV'?N˶>^u6. }m-_pƍ l` BF6&Eʎ_;>`m_/;}Y_8Aƍ7nٹ}Xt*#w';)6e)Φ_nrQFoiW,]}+μe/;/yř7Eo8?^8wBn6!bsI!nGλ ,+웭 %mkg5m[_87;%t?p~dݩΧS?r>w>]ϜOۦۿ)GgGHlӿw;;>w?Ibo}}wލosztҲOd:#oۿ?g3S{׳+m/;5V'~>gm;,v;N{NB]鸚mpmv9Nwcsik[zt8No=4n7 ۆ{Fkq:9Gv:/m<g;'b)KJgVRk+6+ʙ*uΊmgo, d_lcb.!G|*(>VL"_,pۮ;}=c3RaS3%(9NՆ9\jsF[3dNi/we8g;IsmeO9ιsǹ}"麤GĄ1Ls\pΦwpةlt>?߬^ܿRxٙs֙$oRfw^=FʂUn9C;u9z֞A&[|%&11Ԫ_G)O,3\oU:s[gsWι}o{Վ3<5A79Ns}[x~-:nyRrHa &悉`b. .isTBgW,7mڻ'/#*L:3f/[kKL,A0q玉%cYѫΗ>VG{I6x |'7shS9T}q<YvLv쐣@䨎҄ʎh^01Ls\p5V띗 .9O-+ ,:9CJRtM2֚a BFq玉uQMH!o9V[]&1DrnwZsf9Υ_m5Z~l}qXL)0ȍݣ9K)S5=C3bz\01Lsh6rFm5m*ABdiMgׅNMs~4%֚a BFq玉!q.ox X Ԗ9;;VPGݲd|ͳN`me:(3#;ljS=q9NG# &悉`b. l<GZ5o G 9y&Gw[`2֚a BFq玉wDEʏE{Y'<~-OU![9_=9ioy֪zqWGz &悉`b. .fpxDrRJIKr5O] K2;wL9ѣ?O,sZ:[29x͟R\gNZ.<1ILr4z\01Ls#WVBcoN!X:OTz K2;wLŌ[^XoSvk}^:t<{Go:mp=N{K~Thqǩx>KLǼ~~q`b. &悉E"Wby3̤Lf[k%ŝ;&2>#CΑ]?TH_\6YC-*/W8Z~tn_ 6T9HQ|sL~T8^vB:q~nz\01LsI=J6u9[D![VT%, K2;wLe쪛N999"cۿ->c*?ȝ>zGƗ;B搷TJ)C?:#Ls\01-y;LZx<-r9`6lsI޽zgcCe$ 9}9mhx>tn:ܽ~ٷcd5sc{!e\%H &悉`b.YrǡkUn+ȕZ )AfSo)(ԵGb6lsIqѷO^EW}h".g[ź?-?-0U%yu[\\+>d r `b. &2>%k(]e}ISСfV.,A(@01t>9cǹ<5K`euSh]Fȥm)~U_:q.Ծ8]2 &悉`b.Kr*ڣ^VJ)CNd:0Lra BFqWsYV*?f9wS>\/ &悉`\h9C.[!lr^9#]f0ʅ%&J =Gcu}Kbm.sp]}ܡUNI:?qxp`b. &)rhd+ dw DL/C-Nsd }Krh+o+D9 $p`b. &i""=tRKй==䲵f0ʅ%&c7U¤Owvzۋ m3tUy^r ۗ-r :K^01Ls4r=>)\p)J.S9rEhXf0ʅ%&z }=Fz;ΝKGՇH钳i}Kt0d?z\01LS]/x~m|m6gpxD?*3p` K2;Lٞx NkT!rDGՑR_I{.s'Gȹ?䈐,&悉`b.21ngہ((iӏL\3lS62<24zd yC O fhrxgx8kا59ڣt[~1Ls\0eB.޹=xnjsyG,\3lS6b*@V8v/=?Vpz"#>?jpĎ\܏ab. & 5~uW /J.6p` K2;Lٚ;Tplj\r[Uwշo"RK[?r &悉`b.rtYujΨkPG{5V.,A(@0ec.=-;_T %ilÝ5g:Hѓ&/+pE| 1Ls\03)3-,Aos:Gޱ;/وk\XQ܁`\ Szu[)@O)DIO/Roz9sKI.#}uo?y~ s\01LEﱧřԲCEf0ʅ%l#A䈐x58kg=E_Rӝ1\gGddc &悉`JE.^!G=~\]VN|-osI &[!M#eH<-0$;D Ѥcg5KsY(01Lsd+)4ks:+D5V.,A(@0eS.` O֓#?[`ֿ9wh*@dVXaѓߞ8/l c &悉`2tގkouCNz Lra BFq)[rU]Oܹ49@URxsr\~r^}%1Ls\0E.gmup` K2;LِK`U Rqz]ߙ]yֻzI.[}baLra BFqi""N3?U%HzI!+w. 9,@,~ s\01LAȑrS[^c9ׇRk\XQ܁` ;+Jb=; D)D= 54|;] _9R~H r'O01Ls$[[^)rYUΡk(f0%n "EnnX )D^wDl,ŇKL܁`[_ꧪ_U`rb۪ٕ3gcʥ滝H$01LssՙSŇ s_>@p`KD H>;D9K;L1=Ud sE<_ҹ79Nnr=$Ӹ\01L/m0Z~Nm0<LrI˗/xKHhs)@oXX`D˥ m4:)>b"WSJR u:NnRyp?`b.Kjq^;?Z~L}-(Lr1RRC6yLm2ɐD~)ܸq˾-Uy/vV{K?g3oGغN*@OSWSxnܸqƍ[zeS8sNEUMq{aE+5ͥId/*Dʥn*@| O"ֽ?G 3= PS nW=Ms_R!&悉`b.'G~]C.swNȕ^ .fV.qK9iOm.G%b ;L%%qn~ 59VP|NgT RU$n"Wi~/\01L%uˏgV(?<d+%z;^y!wWhsy7m,X`DeT{/$no@h Y ݿ =qsxr%!&悉`b.Ϯ>nrG="1\\*A u6<_F 9n=}BSxښoijEsr[\ ܏ab. &bϮ .?<d+ dw &B.?PG|yc=ndi];S Շ4TƗQ 'v'33KLq?`b.yrT~x .fV.,A(@0ez.u. gSO9 ?דPwUHNO^\nq 9dE)U\01LŜK .[+'U~x .fV.,A(@0ez.r)Aғ;:W}_ 4Ծ /zy@$R<*CJ%01Ls~gΤU1NΑ&b6lS&rAUUa0t^ ΝK$T=""r~-@/CJ501Ls֞AgzgU~ȟ \Wb. &[!HU "eHN= wJ~ s\01y?V3Z~IOT;!9sl0ʅ%LŻ"fxdHO>.@ZkTS~.h.y^c &悉ENv*凜cՁ[:i" .fV.,A(@0eb.[NJ5$֜Ya$ԉoDDryr5R} c &悉DWw~ٛGy,sl0ʅ%L˥Y?t:zit7+TҖzJQ|*A5oCrq[\n܏ab. &IKzW}yr55 .fV.,A(@0eZ.Gjw@ O֓غT%Hϑ|= u's w?ȉ3`=Ȕ01Ls ~[;k\6Yd+ dw 2-5+$~?Q%H*An\ܥ'F-A~1Ls\\GvZ{_.{*@:> .8$r _e&%JD`b. lȥwpYZtә*?]}?\\XQ܁`ʄ\RG' ԏ dNOCpٕ󼞤\EJkԈs\01L=G-/R~H "e"^p1Lra BFq)r U~ȟyzl0ʅ%&\v_J=$\;jշzkQ ѓ4:-@~?\/ &悉`H4wT#B2 .fV.,A(@0!28,7[ 4XPz5ϪQiY8'9Ndu2T?Ls\0M\5o;˝` K2;LȹuP GRO Ԗ;íw4GUg$.tTz\01LSRL9;zW|Zd+ dw sPX e7֓`mQ%Hof= vt\U=BOQ[=8z\01LS"GiPGqb6lj.d;=zʑ''CH}_NQ'Ere\/ &悉`\:/m0zl0ʅ%&\7d$X{Q ''.D"M{nrKz\01LS"G{LYRʏ9.<зL,\/ &[!\ oE=$W(3T r.=I#(B=`b. LeEq2/zl0ʅ%&\.4䣃$X}+B [ߟ r99q{0 &悉`b.2!ξ!u[Y^d"zl0ʅ%&\O,S%ȱzkJU}? K] Wz\01Lz.-]΋*U!oo ` K2;Lh$zBTdDiu>]2Y 4MZd[^^01LsKSGGgT2ݓΥ{]d+ dw rN\US\ RvIGspNڐ ^փ^01LsKMsh"J!M^p1Lra BFq -JDZѓ= +y>,҃^01Ls!G~H"oɶDpb6lR.ךϫd?דT "oJUly)gh0IHS-@ʞzY\?Ls\0Rzm/mN^p1Lra BFq )mkU r=[Ut~E9m=I DL &悉`b.rZqߙD r5~X^p1Lra BFq %u2T)AZ{Z4T s@OBɥp|Z rܴ#?䒸R%r`b. &\J!W\/ &[!Ky}*@6,֓:6\ WK$Tc!U9AQ[~Vz\01LSs\pIwon\/ &[!ҥ2$nS%Hw:s*A0iwe[Zq`b. &)?YyJ rzl0ʅ%&\ 7IQ?}SO"yWw?Od^01Ls\.Kn#?Ka \\XQ܁`Bȥ*@fֹ}*AzONOBd/I5w 3 1\/ &悉`Ju.oY;8o% .fV.,A(@0!r1Ul*@O|/I ͟Ab^01Ls\RxxG`0d+ dw rs1_ GjwIt=?VGtl+=T}b*AJxKOҤ-@NLu^=L &悉`b.R; \WE` K2;L;P u.I~uL0ez1NZ[ȟIz\01L\:W /C眊}+` K2;Lx'Eԓ`Uߪ7?ѓ`6)A/ѓ4#?ʞrK =L &悉`b.lRP~ϙ U~ȕ_Q'Dpb6l2dk{iާTOw ҝˣ = 彧T \'i`[z\01L\jUr%Nk`˘K)=WXX檏m \N$2= 6tU ?3߭zZTz&UD3\/ &悉`2mgU~<~}+%d+1 RÏRD'%&@0;WwDNwBԮ+$FJ}^ԓ48'9Ndhp`b. &D.ZgPJc`˸J!Gv%Ffrԇ{Ly2X`Ssr#UZO{B$ohUQ W'Ls\0'9ckշxpb6lb&1ɐD~)ܸqV~=W %oyEjM}wKU<x{*?R%Hcէsƍ7nJkoS_Nq{4ZQ Op`b. &i,|RvW]E90d+1 v)>DOmKL܁`Jg. 䣃$XU}ސ9 =Iy +`l^01LsL.-]MգGrd+1 "2rtwd^a"͓w ҙDJS$wi܁%zI4U4luO*%u^01Lsh.5QR~Q [+[\\UKL܁`Jg._^X GjwI~S%HoF=U _Nד4J"'Ls\0EN~`ѣ?^\W?J[L .fV.,A(@03P%ȵzi*@~= g vեRtyI &悉`b.b"enjs+:pK"d .fV.,A(@03%fgSO"ߧJ$҃ Uly-@ʞr\/ &悉`˞g;'T"-ծoTzl0ʅ%tѠ '͋{R;hAn r`b. \HEnENʓ .fV.,A(@0+ %TWrDOBɑrI\)AZk4ȏS &悉`b._fn8c;7 .fV.,A(@0+ͪ9P]OH C-zU9AV9o{z\01L˥{]γOdڻ'4zl0ʅ%tRP*ANInUa9u*AOד4[+Ѓz\01LRzM]V 9jSG҅d+ dw ҕ˲WU XO"yWiϏ~ސm+V%HW[q )3z\01LSWa}  .fV.,A(@0#)>"$S;T ՇzJ)@I9 o\/ &悉u{{)!zl0ʅ%t"oDKޕ2$ȍT rh+zbrBT$z^01LsRTP&\\XQ܁`JG.r2T)A_LO}0Ld+ dw Rˑ]WU'g|9 rrԔp SBz\01L%=xq]*@0rDs\p1Lra BFq)չZJГ`W}_ Ý$sU ru}[/^01LsI9sl0ʅ%Ty^'Tx͟IݿJ zBO%HO`b.Kjt -@s\p1Lra BFq) dɾz[Utl+= 5<<|d*A:4E4=s\01Թ7zgWZ悉b6lS*sZ GROuJIw+U+y=I=n R/Ls\Rc2r2sc. .fV.,A(@02cuU rۿP%H!= U{v*AlCOR"i;^01LsΔ%:*00Ld+ dw R˶)OOɹ@zԠ'N]J<=I /%H &悉`b.v-_ѷ\01\\XQ܁`Je.k*AGݓ~=oËiY')T:qOr!"z\01L9˪𸳾Qߒ悉b6lSrw*Ah'E-=ST Ӫ')]r9=s\01|r)@m0E`b. &[!\)@hXzDNQ%U|Y=IUp`b. &b\噕T"^ץoIs\p1Lra BFq)Uy@r#= ֹc{RԪo$ԍT rh+zB׸%ͥz` &悉`b.hssB /mt [\01\\XQ܁`JU.;H.= ֖IQ$Ty"U\'*AM͉IG :N^m &悉`b.:>: /sl0ʅ%& tDKݪbiUHI`w> 5^01Ls/@^\Wt_&悉b6l\5W%ҥzuOzwz"7ER[Z`b.K(D0Ld+ dw lrv*A\ד`퟾J$TUI*A')2^WJ:=L Ls\"k@ߒzsl0ʅ%&ZJГ`W}_ #z莹>^OR!-@.`b.K 9+@䈐tb. .fV.,A(@0e?W%Ƚz=4\ ֜YzI#%Hr=IA9[0Ls\ \01\\XQ܁`K`*@웭'uO} QxX OHq`b. l_<D"^01\\*Aϟkj+--Pi輰PϠy2X`\n6rix|M"GscUt~K{K*@>_󬞤!G\xI҃s\0eS. \=Fbz\p1Lr[N?6?]ʒd&Z JOeqIQ/|'n\ܥJC[^ѓ-A4z\01Lْsc=b6lR^xWbHR|x ۼrD矏KL܁`\FJBL,9T 2\'ʋܡUzrH"^01LS6wh=b6lP %&@0%_E=4u$Y}HO,t)n ߤ`b.&z.XLd+ dw L2<2,cU tiT ҞDSj:+3z>\/ &i"I]UVx:y^01\\XQ܁`2˽zU>==)'d$?wKz>\/ &i"G}R sl0ʅ%&ӹT*A6':XJ G ś^֓NZ Mz\01L1L/@ &悋` K2;Lss1_ jI'Em'XJr vAp`b. KQdՁ[zy^01\\XQ܁`2˺c U RuLO" ;VP|@lU!)s9麤`b.&R.7ڜ)KJU`u=L\/ .fV.,A(@0eɾ٪i~2(Im+V%$%{d &悉`(LDp`b. &[!d2λy竟I09 ]{WI( ˧I tT%z^\/ &i"/@ϸ^01\\XQ܁`2˅Ul,]'0Rڡ'n_.R%3$׻%ȵyz^\/ &)s ^vG-sl0ʅ%&*AUIU 2P'.Q%ɽ $vK;yz^\/ &)sz\p1Lra BFqd.eDjxyT 2߭myE gI mz^\/ &)Ssz\p1Lra BFqd.]  zi=)W$kU%ûzbȠ{BT)A[0^01LS&R9 b6l\U"W7biUȧK&;ÃzjYg[~Vҏs\0eZ.5δwONDp`b. &[!d*Ywlޟ= tT =CORV &悉`ʤ\:/WM\/ .fV.,A(@0H.U치'?}S OI: U:}Csl0ʅ%&Sl9*AN:'z7T R}b@n Hҏs\0eB.Rxw^ sNDp`b. &[!d*\ m$Уy'vFoO$. LsK3=*@lٍsl0ʅ%{/U rQDnuO$_P%Hr=IRpp`b. &\b6lxsYo*Az:$U翮'6r*A{ZIJ_)@J&; ^01LSs2yQSQنsl0ʅ%Ӣ w'z'Eo$TWUl}@G[X^01LS:s䓲oF\/ .fV.,A(@0'GU gz;)j}zQ*A@z{[p`b. t"o{y~m &悋` K2;L+AVxSO^OB6-ADIz\01Le"b6lxrIHIQ?DJK rv[bq`b. t"o}D s^ &悋` K2;L%*A:DJy "'B=>݆{ &悉`Ju. ΤU "'E`\/ .fV.,A(@0'vUO$Ry-٬'zZT R|X&ĕ@hP\/ &)t 8Ϭ< S b6lxsDh)A2H "[J+pKs &悉`Je.T2#38<sl0ʅ%i*A[c_0%\FJ;yzs\0*yjYsOO)Ld+ dw ƛKd=)It)-Aο mzs\0"C&S|\/ .fV.,A(@07X%`C*A_דRZNqKN=`b.l"G}r Bz\p1Lra BFqi< dIS;T ҵ{THIzbQ%x^0q`b. &y?R@(q\/ .fV.,A(@0'T ПI#?cyPJ߯}NO,-A.^01L\ 0Ra08Ld+ dw Ɠ[U RxjJvP  gE%HC`z\01Lr|*@<  zJz\p1Lra BFqi<*P%ȁz*h*ANTO,8-A&Lsd#K)KJU QO)\/ .fV.,A(@0'T RuLOB%ZozY +?NLuK=`b.L7gTz\p1LrZxoocc"ϕ,)A7;[?}mwv*A۫We8v[|Ux;7nܸqGÍ7nܲ{aI! Jd/*Xr)/V%Ȧ$XΥG śRp[#Al\/ &iԵ?YQYsl0e%;#ډQ'C&#H%KL܁`K.;Hm}'?M 5zbQlՃ`b.rZq_ r5=Tz\p1Lra BFq)\)A< Rt}j SĢ9dLsKkϠ3ݓ)(j\/ .fV.,A(@0%KcuU=zk Uݭ'/Q%%ƺ+z9^01LSx.P_;'\/ .fV.,A(@0%ˣYY3=4zRzܡU9R="F2 &悉`Rי'Cb6lS2$RЖWT rs=LyXs\0s*@l'.\/ .fV.,A(@0%Kd^ ?ғHrW%û5)hу`b.\*;U"ŭkQ3JLd+ dw %^ *A:t OR%`Zw-@ʟփ`b.\^pA \WSzq`b. &[!L.މQ?:8OOBlV%HyzJkZ%Hu Nj &悉`\D.+Wz\p1Lra BFq)\.4dSzǪ9'^?J/ED@\/ &kγO$HRq`b. &[!L.P%Ⱦ= 5zy$TJKK/%^01L_8 )BGҍsl0ʅ%drr#UT*A\ RAfz\01= uQUm|IO,+H 2 #^01L%5Z{zs\01\\XQ܁`J$Ww I( q`b.Kj,s] /ԓؘ &悉b6lS"ZJ%G*AΟ'`.}5ΤVة1Lsl0ʅ%DrY}MU4]דP<<LsoV~: dގs\p1Lra BFq)^.]Yo3<2X`b.v'-Aj҃`b.%q]}Z yib &悉b6lP 2|^S[iinVVXX2ϓw RTY WI#?tU ]mwĠڷ'z0p`b.KU%qZz4u &悉b6lSP. = T ҽ?GOB6רq Bi\01`/W%Ȣ}7$ &悉b6lSP.ךϫdcR=|UҀ`b.VWȴwO:=zZs\p1Lra BFq)(U ?*A;I;$OO c Bi\01PrIܧ/W%HA==M=悉`b. &[!Kd*@~=ϩACҀ`b.V׫dzٔ^7s\01\\XQ܁` %^ "osGdW V4`.zɋJT rUOӃ`b. .fV.,A(@09:= n*@hkXP0L9[DL7悉`b. &[!;T = յ{*A֓P+?W%ȁzbEn rkL,\/ &*Ѧ 9DI7悉`b. &[!ˁ)ެ'm0RW}')T%՚K/%ȃ]z0p`b.1#*Ab. &悋` K2;LAlXJӷI?W%9[t`b.  rUξ!=M/悉`b. &[!GJ{ 2 %`z\0e{.=δwOdk}=M?Lsl0ʅ%\:zd;#8vCzz Q g`\T y &悉b6lSx.KT wziĨI(Ĩ'+pK7`z\0es.u-=ΤU R~]O1p`b. .fV.,A(@0粯@ {.Ix%GU R%=1m &)sy*@n'8^01Ld+ dw sXT Uw$RKs3\&LSrcULYR4u)Lsl0ʅ%\ꧪԓHi-A{$w'(LS6"}~m*AVX^01Ld+ dw tdYѫz,%!G  &)sY_Ҩ gVrz &悉b6l?U+Ak+A< RyA&0LS5L}*Av]xx^01Ld+ dw uP *ImJ+G$哟%rqK[`bz\0e[.v\S̍ &悉b6l?~Wz-OU 2R'ʋkĠSϸ%HwLL\/ lE.+\[O1q`b. .fV.,A(@0s)/[ ъ3<7JK{Đ&91U&.LS"'C{N Kn).Lsl0ʅ%&. 2pB ISU vGO i ^փs-|RvW O_tH^01Ld+ dw $RW%H= n*@tc[Z &)r zs\p1Lra BFqɟKu)UX':~1OZ}b*A. `ʆ\칞'Cz\01\\U,_yԶw^= 5q%%&@0s.dDzn'g廊eO%@L\\/ K]K: zs\p1Lrs "V \A汰&.Ji͙'ȝJ[zb`[H^01L=9[dz^01Ld+1 RÏQ!Rd&.J ߨsB=Tx*AĐrdLDΥC 8z43p`b. .fV.*Ao$IKL܁`>=)wimQk~ uo`ȹ*A's\p1LrZxooR,)Ao+v$ۛP%ȭX ~}= $u^01L1~uEL`b. &[A#;rC𷽄/$Y,A0qɟGj=  vDE@0s\p1LrW N,A0q˥gS w WG :\/ )Ѧs\p1Lra BFq˥~*AB}d~JOB՜*P% W &iUL9's\p1Lra BFq˥֘'E]W|9C R-A,`hTJOIfz\01\\XQ܁`rXT UwAX &i"5L^TN*g2Lsl0ʅ%&/%fD  Kz\0M\ 謹@fWIz\01\\XQ܁`\Z{ZT7s4Kz\0M\^\WJ$sq`b. .fV.,A(@0I.IQ[ J^҈`(iSoHO3 &悉b6lrAUl?+= [Z'jU `]V=ظ^01L%Unzٸ^01Ld+ dw $DK֜YzԠ'^?J/Af%H{Ll\/ sQ%HQC=l\/ &悋` K2;LKy}qd[ ?DZNqK=ظ^01L!NU<`b. &[!$_٪J3iI$k%Hg[ &i"`uUȟ &悉b6lH s$_ s$^[Tփsȑ=J9"dz\01\\XQ܁`\)A_9' %ȝ<=^01LD V`b. &[!$$R<~>k%șꤨs鹼\pI Kdbz\01\\XQ܁`\ r5)@0X)A[DN:2 &)sLZxܙiX8Lsl0ʅ%&ɥתy~>J$Q[\0|t 8LS&粴: dz2qp`b. .fV.,A(@0I.oT%HK+P%5+%Hz{[ &)SsVN\/ &悋` K2;LKsRՇ$XCM*AΟ'He'E\/ Lͥtӄȋz)Wb_ ۼ {ẍ́D~bǏ%H|,A(1I.e7Ytl+ru9 G']`0L~U"fLsIYxAzH6{%L}co5y.Y]>%HHXQ.&eeT ѠO[`?:8OOBX =G$XCM*Ar-=.\/ &\Z{)KJU "'GV\/ &B[k%ŝ;w$IQw,V%sH'E\/ &\Q$;q`b. Qrl dwxlU%˟I( 7cUp%y|H & )ξ!gڻ'U rUO &悉%֚a BFq+A _Ji`Be~/m'ًs\ckͰ!sidJO QDDH &\:D^01L̅(9 K2;w=n\/ &B[k%ŝ;cuU 兏$T"%HUI*A FUvK p`b.ҝ U sAOs\ck$T̟?yVZZeSٻw\G_>OKLܹ9}*AJ9P8[ r\uK&ϛ!^01LeiMU<Sz\01Z3qK)(rssRxERzxKN;Ï%&(A?M .!,AG`Jg.u9Pp`b. Qrl%?s"|RH)%ŝ;x%HodVhR8 #B\03ٛU!Ts!J5P /8/_12;D <[lba ;w )A' T RptU7J%{o?]U|Uj:NUܻoƍto3sJ oo;x;7nܸM IkLymu<שw,U6$A7&Wj&ZH lEs1_ GjHVD4tH=q`b.ґKѣ@Z{^01L%9%ER!3kDŽ/Moh%^[zf c _{Tv_|` ;w<e锞Jk R:-A: p`b.ґˢ}7ܣ@ `b. /)uvy/)1A*A#I=<>7{=l{}sLTLD_< pۼd sU ܱX Շ$RzF &)չ;O-+S%\q`b.Kr䵣5#;b{M^l*:b'_U^zkov_p#9\/ TRxI / `\gϖMNr+K ?X@ =/AŸG'[k滿i%&Gwe_k=Ļ2̝<=>\/ T2#*A>?߬'sꐏۼB c< A ObCA%H4=yK;k滿i%&G˗>V%ȶ_Ix%HͩU7y?vz}^01L̥[ vy[ E`b. /) R;1r?O"1RJxۼID!#9bF7Hl dwXr*Ap+=v| "'Bdd^01LezU p`b.Kr/s)do~QD{!=X2Oxy}k{syp}ͽ{IK2;w#C΢?vS]SFOZR}E=N\/ TӪ9Zת' &悉L|5ǽzuG%%r m=N\/ TRzM O_' &悉L|,A̲fXQܹ㨺[Jb撖2$q`b.R˂=U h =X^01L̅(9 K2;wP%Ⱦ翮J['(U-f%Cz^01LEN*'CSO)Ls!J5G񕭪?cd;wS%哟r3)AZ{s`b.Rˮ T2=笞P<\/ &B[k%ŝ;DJ)>yO$Ҟu/dzQ^01LeV~*A֗4 `b.DɱfXQܹH-S%HgoIAg/UX(\/ &۹iSE%NkO^;Y\/ &B[k%ŝ;DJT s$_OBR~szbKsd;办P"^01L̅(9 K2;w/iMU!A.Q%ȑo!މQAvz\0E.+%:GeLs!J5G#AF|=)jo:'EW%HSd'L\k䙕`b.DɱfXQܹW 6Tmz5ϪKsd3y kJ &悉%֚a BFq#^ {wbJR|Dz\0ʥk@ u䨔Ls!J5GU {bN3 yK &V.y@^.' Ls!J5Gcݓ^-ѓP9zbPn « sd+VdOUP2^01L̅(9 K2;woT% ̥5Gn'XJӸs3F2 &F.yjY38< LsIk6ֆܶ|r}k~[ZZ\je޽Nnn,6y.}ѾFM$ K2;w%Hԃ`Ңy:=dq`b.Kr䅽W>H9!%J yHͻDKyL { 7l dw8`K^lHRd/L\~\ 7`b. ~2 /r#6X M fM_fx>¿}O>ջd7mgR&l dw8`K[jd/L\䙕Ƃs\h=`ozϑ wG> ?gX߇'tfXQܹ-A/pK[ {q`b.L2o5U,_'4\/ LeS)SB+"/ #"y#!%o甏_n c}stfXQ.%Hά%HEr;%wR,2z\0̥wpؙ U\ץ4\/ L%%Ȇ^AE?c{U><.y?}b_{I[k%<DNz|Lv^=^\/ &nRȋ U&p`b.KreX>{/=^Y =V^!?%AA<y|/| E>d& 8|;I[k%ŝ;D@οٍsd2T f=z\01ȋwym~᷅&WHx2W=Z o^|3̤/93׋v~tfXQܹ,A%^@1LrhPȴwO:#zJc`b. J K2;w%Hn Ҹ^ &T.oU%ȊbLsL G FZ3,A(qۭJ//|KWU 2\'.Q%Hy"=1LyTٍsd"~ggc?Ls!J5T "eHP.JzڙBU1WO |-Axesd"ﲸs_/Ls!J5G_~E`.i)AN= < ]p`b.ƛKec:DīS`b.DɱfXQܹcr웭JǁI?Lxsy!: dzB&p`b. Qrl dwۮdeAx-A.Avz\0'{yjY3d &悉%֚a BFq玡תr#yP.i)A.sp`b.ƚKK׀32z\01Z3,A(1eq2D$^01Lc;*Lz\01Z3,A(1>*Aʅ%Hq`b.ƒKvU Q/S2s\ckͰ!sOvUO$8 `K.sΪdiM=!Ӹ^01L̅(9 K2;Z -ԓ\X &)\v]x itzLz\01Z3,A(;)syz Kz\0%Ӫ)<ݤd &悉%֚a BFq~WDҖjq9R=1ti/׃`J&=U-H!Bp`b. Qrl dwP 2< G+f*AcJ%`d7LS2ʯR%'ewlz\01Z3,A(oT rfD2\JI]9ϫACe 'q`b.ͥG S:}%/`b.)--u^{mWaaֆcL6&*eⱵfXQܹ_"%IQIsVzbn +Ìz\0%֊y!>m\/ &)/B>BnYAs\^IvF^؛L<\s}{OD>n [kf\%G~pb ;W w>TUדH f=N9>cR^01Lm\/ LejoM6w׋^)B{.s9-J!vye=υ7 c\RG>{ǎ53D~X_"޻wsX`t/^ *A:6\O".Jޮ='9CK(LS\ZI M>z\0ej.,A^pAŅB~ ?>z.vyb_D+›?GŸ9h߇̼9|DZ3c.A!?ϼ_F_d_QfUD:LU mjmQkr<= `F??:\/ &H'/CW4ȟן=W\[6r?5l"{+kP^ xy?Gq}{_#!3M/sclq )p/P~&dĝ{_٪Jϥe+}H ś ^E)A/يs/UD B`b.ɑ׍A!עLj\@-~6#^&mtUܩ* 7nƳ9{E pqpƍ H lvp/B'T#A0Jw$HkT 2zWOB=B 1q\/ X,_J (5^01L̅:%Y̘Kd+` ;c 9^01Lrq~\ GZRs\ck͌rdm.B Cm"X`=V%ȞzWvI{;Xz\0E˥*@茞P*q`b. Qrlq w'ƕD.xU sU ra$ &)Z.ޥ'J\/ &B[k%ŝ{+Aa sKSG3iqǔz\/ &B[k%ŝ{A O ` E@hJLs!J5=.A$ILSP.Ϯ>J9/ &悉%֚a BFq~e7V%ȎsONl%aJ'?Xz\0"WD #Wz\01Z3,A(ӏ\/ \VJE7ҁs\ckͰ!sO? st&/*Q%H^=tz\01Z3,A(ӯzs9j' g%Hgz\0sY_Ҩ 6\JLs!J5= OV%;$4?W% = e[rq`b.=J҅s\ckͰ!sOU v]OBsa sRة pzՌ҇s\ckͰ!sOgmp_OCsWԜ*P%\% ^01L^. \W%[;k^\/ &撼^{mt󫭭 mGob=4 ՆNo'[k%ŝ{z]k>; =qsW\;SJ;,Aq`b.$9ee)ծotz\01掖~޽c@/6B>'cK ? c BFq^ޕa\? 8^01Lˮ T &悉$_+-<^t_NJDK1yEۛyK<+EC%H5s^I!$|Or?$,A=}G%fEO]\Xz\0?wřZҍs(0[ {y ry\M/d"s}=xM"?  8 d>UU=yŸK{,Aq`b.z{Yٛp`b.25T E+J xzH} ~ !H b<+A$/:\/ *A !z\01x/>r+69/=Vy8]w{z/9Kyy?W3Z3,A('E\db :\/ uIgpxDO  &悉${am~M69R6o*)0۽%Ͻ)4Ÿ›{ߓ2g  dwT R^_'Osi];S #ݭzU "o1i >$[q`b.xnJE7Pp`b.Kv /E(y K2;Jӷ^.R|Hσ/Jۗd:ds7LYRJn=%\/ &]X5=})AR%HsA|Z d:+t=  &悥*@9's\ckͰ!sODJ[T ҵ;|R|HR|p'-A`y!*AVB`b.DɱfXQܹO"%H;[}zKse[+p`b.8/Ra*)!z\01Z3,A(';)PK[]JxK X' 5z@LUJ91*s\01L̅(9 K2;W%q"$/ruC뫷-@N|{`K%qK2Ls\ckͰ!sOx%H}iUl}z:NMn R5KȏsPTP Ϯ>>g. &悉%֚a BFq>JoT "'G |\ rn.uK"p`b.foV%Ț÷s\01Z3,A('^ ұT 刞9UJC[^.\R4_K׀3iqiS3悉`b. .fV.,A(@'^ 'EmPET r*=1 -p`b.sA2+JO *悉`b. &[!Ix%HkT "'H rt\U\;S'z-AsIVdׅz\P1Lsl0ʅ%H<\/KzjWSʜa=e. &悉b6l$} ?*AF4 &^v\S%Ȣ}7\01Lsl0ʅ%H+A- z\/Kt 9Sҽ.=u1Ls\p1Lra BFq>,A2 &>TBy=y`b. &悋` K2;,A*_tKV \/KH!%! &悉`b. &[!IK,Abz\C"F`b. &悋` K2;{bUW%HJO$&L%=DRȉQ0Ls\p1Lra BFq>J\"%HL\/Kɥp咸R%r0Ls\p1Lra BFq>%OsI]z`b. &悋` K2;WUJ# %^ R^H aq`b.7+J 9G$s\01Ld+ dw S|e*ApK?t z@^01ԺLZx\m-]z`b. &悋` K2;+A:*sz@^01Zu: z`b. &悋` K2;+A׻%Hl=p\/Kj=*AI0悉`b. .fV.,A(@:-Az\Rh]*@{`s\01\\XQ܁\ "o\( &:s_U%Ңzs\01Ld+J;JKK4wljVXX2c͓w U HC Fgߐ3yQ*Aj4:悉`b. .fV.qK)(rssRH!Nn{GfAGy2X`$}J=n rqPL%5 uzs\01Ld+%?sHOL,Aw U/wKz\Rㅼ2$s\01\\*A##39=F^E'%&@[4sO"Ȕ%m1`. &悉b6lblG&mKL܁OQq Yjx-A:*p`b.-wC rbD1Ls\p1LrIHh#AcD~)ܸqsu3o*A?x A SlJW΍ݪ\uR/xnܸqƍ7l[IOT?9C" md/*ŗsx*AGf ;y`SϸGz\Zq_ ϯMn_\01Lsl0%n ""EW^H&Bn?m I!gɾ٪'$dNOt6r*A:tNLuK= \/] Q?) .da. &悉b6lP %&@Cd7$ұr yVOI (&LŞΤOa. &悉b6l$=V%ȶzJrUT7dX$s筝y#a. &悉b6l$= OV%!A$X%ȡ-D#X$swpX"%ȥ{]z8悉`b. .fV.,A(@ceT v]OBI.J>Jw+dX$sͪsVO\01Lsl0ʅ%HuDN*'H "*Ao]- R8>-AF =1{S*Aѓ0Ls\p1Lra BFqzu.dݱzIrvܻ׏d߆^"7!\/y-]ΤvOO\01Lsl0ʅ%H\ߧJI$%Z R}b*AJxKO ` Lż%(/I &悉`b. &[!I//|J#$Қ3+9mU\<'I &bK.dk}=Is\01Ld+ dw T rA=tJ*@'O1W ,A58<2zU~=Ms\01Ld+ dw H raUUO` >\/Yyf)=悉`b. .fV.,A(@R/J +%\( &b'ewU Je=悉`b. .fV.,A(@R/V%HVJ /%H+i,\/Y \W%Ȋ\01Lsl0ʅ%Hy%H?I9a#%+AjnX$s1kV~*Av]x'c\01Lsl0ʅ%H;dU W}q  >\/YϮ>JKdl &悉`b. &[!Ix%H#_I( #Wd`. &悉b6l$ ׹)٬' g%HgPL9CJV1Ls\p1Lra BFqzJ U "GR z\ 3wU=;悉`b. .fV.,A(@R/^ dNOBILŜ9[Dʐb. &悉b6l$7zJ F$okĀSdE( &bF3eI*AZzt &悉`b. &[!I+[U "JhXJ)>){J(LŌ=U-sVOƇ`b. &悋` K2;ԃ+AJ z@p`b.fxoYus\01Ld+ dw W^ E_K׀3yQ4n^=悉`b. .fV.,A(@R ͟E_Αud &悉`b. &[!IDJY$%wR&=h^01qYyJ 5XYq߰p… ]4.LJ.L…iMӤ1,\Q۱?τF,:1"5QlA)dF 0sy;yyfXp~s}ϹsG.6MbE66ʅIqTO|^[)MW=A.6MbE66ʅIqTT rz ?@#ȥ;>>J5Ó~D.6Mbؔ+J$R=S%Īz "ebD.: KM4&r\l"Ʀ\P )*A. G_l"J $r\l".)W. Hz/ޕ /'s}yz uݹvW='?Ž%șC~F/6KJ MbD.vMrAR [;9's+A>vO\ Ѿ Rȅ_l"_Yr_ #&r\l"Ʀ\P )ݷfWLOjVl|JW|qxO@ MҙG] rͣ{$-r\l".)W. HZMM?=s5*AΞ> w,͞.PbtWd?&$-r\l".)W. HZJm C~%J_l"+AR5"&r\"rB 8@% UJ|JeR1xt-ȥ32)A^8'iMbD.vMrAR jKQE$/E;S/Axw_l"\z+AN&r\l"Ʀ\P )j/\ rb樟ר̕ N|']ue&riߧ'κ&r\l"Ʀ\P )ȻH+IX "#Oi &riFJeO&r\l"Ʀ\P )K`u )AOi &ri3̭ߪ;r\l".)W. H:[Ǟq%Ȗrdd?(A~\ww] $=r\l".)W. H:&r\l"Ʀ\t\H|?顊3)L(A.^ u+AN\3o{O{҆ϷsWۿW9..#k?xp{mH!b5Ԑ'r K{[-5ka" ѹ\@*A J(A@O=J(A@O=aѕ ˗O4-~R|ʕ+4>Gzd͕ ]s]8G9{)6G:\eOpN.Y?tq9*[L.?*| ғ! Y*W͑d.;{DFA;½ Iϻؼ] p;QoZ2ZXrHP|`%身Ǣ+=0# X#0|\D.Փ,I]I>',b]!!,%½#\͑|.*LkD%*{얌&YlމEWPfՒoZ42ZXfqÛӲ9uWqDY9#Yȹ%)6G>a.{E.]{r}Q\o=bs'k-K.G ғuzbsg>WbAZcLqKl J$!CF +,9u/#Eyz~͑G1ܔȥz[ϹȞu/u}RAw!p]'H+\WYoY2_dshwcL ;ad›O9›"=(bs^F_r e7HM͑^Y.p_usM>|+:'S9Yo#%rOcG.#-~/""]Dyu |tXT%M,V&HЛ9$͑fuz1B@Y9Ҋ T#\WYg9YHvHKXc{'\+ޫ!/Y #Ys=t{J2u3J][lމEU9L/W᠗򂌪'@ :+Ȗ͑uL*)BZar }p g"6GZ\y/ȥEqùޔt;{'t%'n95.O.N''cv- 'P@ z% 'P@ z% 'P@ z@/̒e IENDB`srpc-0.10.1/docs/images/benchmark6.png000066400000000000000000001121151454502251400174660ustar00rootroot00000000000000PNG  IHDRBhb%DsRGBgAMA a pHYsodIDATx^}S7MZm61Y[kڤYԧ>vkvJ./smCֵ@QJbtbPe/ OTjGw߿oϙo~sޯ:9g sə3JCh !m0Ch !m0Ch !m0Ch !m0d|_NFU:uttd`k٥Ç! g;1\!N`1$CHr~ѣg|ߧ҃s={K/~{ݝ]2}.@WWW^9P9c_s>wu-ޟ5kVɾzTo(aHeAS^^d#Cv8B;v^Gu+zVDxj!aj_-šRn O^ةn3&C@ C@B`j!$`[/;T^>v =C.>Ʈg#}J:Qo۳@eU }6Ng/~}'d_OY}5 ! =t2;F ؏xZ׆v>fԫCu]B<O1$M??@C@`_fAZe_lFQtۃAǮ1g;?#?aٳOl¿chE?:TmE* ;_׾/ߞ gz k_s~E?~-ݾ bm:ɟoP0؃J:? d&# gdct8\Ʉ4G?ru_>>xT? !$QS8ߋm{W\k YzE#Vjr_&B{0Ur{@>?Xۨ>{@X~yx8Ln|~)~&k$ a4س} {ʏ$dz?{v]{_ {(bH`tBvʝ* !EOǽ~v۰SzSڏRtyɟ¯a(q ;Ge>F~"p YaT4T:GS "L~M` Zv5|ݶfڃ؟}B9o˳v1>'B~ZvN~|?>OX-. YS޶ϐ1>ƅs ka>}L=/ϟ1= w@ C@=( x5:km@BBZCHgq P=p'+b^-E??vbC-~*(/Ch !m0Ch !m0Ch !m0Ch !m 2z褣#{ ` 2jԨ!D5laQ1ƈB֯_=kcۋ=ϟWzƒyq co΍t#ފ&%}'yal7ǶϡU}n8sk:\=\ ?^}`j6^|!0S8B4rz?)# 1^6i}6gj7lG&衉&h&hم!ߜ$Ϝ vz$ٽ*jimXኣ<;OItCfZ.Grk['[7wƪ]_q$l†ҩOuɣ3/H57nDZ!wOΡ~ddt]\./=.6pt\'m7^bq|lxj\{4?4EM4E]4ES. !G]I들tn_o_IV0Vc,cW8z\З;]W=@0=oLQ6>ذPyn'_~|xQt(b?bai SoY Y.x@`c/~x`#BZ ~1`R/>7{M?mӿW}Q.zh.袉.bvaiwo-I{Ⱥg詵J=?cO/p/r?Hm[9Vv xk&7l<'oqFϏ.vϲa_qʼn`'6Ya~%V4xzO/e1Bxsuᆋ-J'{V H]D]4E]4Ҏ^$OPi߀}_n4}ҐDž-#7kjg ?FiT׺pϺ(weC_MtD=4DMtDM10 #.-OrT59ua/t/fڌ_[[7믬rc=bcSݏظaĞyQ 8ݶ} X1ky"g`T~{ o~o/Hig5¡³.y ԇ|5E]D]4E]4vZiSrٜ=%3M_[iBu/n6ކBo;׍u~ͬF5? G] &xq?A]XvK"|JwQ̋A~d&h&衉&h P~TeϹgqܱ:q}L侇,Y][ ᏥFWlj#F!Q?nwظ6P#GH_Z0~p4cΣ>vGG׼x~|oߔ_wW]4E]D]4^ C8^][w{2}0|L䶅_K}d#O}frpŠ7>WYWVoqLߒe?b}=2g|oq?aCƴi_ xhB,7؋ڀev?袉.袇&袉.@b_B@ʀÞb֞b{ߎc*Y~W̾7k8?]OL^oA {!fq)g|\M?m2{̯&|ד'}0yjo%^d˄K^?%Ͻ; m?fmL_3Z5E]4E]D]4ѥ ~6>?d&2]c3!q'< !CW^ckLJq_{˹3{6oMM53JɮWdY >yRy&8k&h&衉&hKp yE*uS>evbk?m;m;6؟B!d-oZ6,%wGW&\;rlٻ!;SyMtDMtD=4DMt)?W]uUe^!$u+|s?04Ccuny|]ے[NpngTwk#%[K3pDMtDMtCMtѤeKPGA=cDC]ޯu u>ry>QnN3{K}h'1/ !Cd[ت[2S*POCȎ9# yMtDMtD=4DMt)*F=Ag#&>$QBz{{߱2D\> ! ~ !:!nB]]+s/I8.=ޝ9pDMtDMtCMtDb<܏ ~ȿOao]Vi( '߮[׵?-|m;m;?ߣļ0 Ǹ!dтk`֗7s7zv= Ğ 쌑&h&hh& Ə .7 !~eog/+w];?¿F}n~C\E C/Cct?k?]#` bCI#yMtDMtD=4DM]֝>qxQA637Nr_>2d]6CHvb_~G<#a;<7<1!dǹ!]]#֒쌑|5E]4E]D]4w9>;a#?Qx E#Wn0]h)>'y1 r;rQBF?dm!!d&d؁a{̑|5E]4E]D]4ѥX~ EÂ6E>}z/ɿ :vL~QGcv>V2v~ !1?< !}ߚ]+b8k&h&衉&hK1@ߟA޶ dC÷CH0vqy6@䇏k۟ogw|۱BC7 qʼnnyvCfʼ8.B^,;cd .袉.袇&袉.#?b_B !w.֍ ]'$o}n9ٵ2LB!#yMtDMtD=4DMti !C/!d,톐-N񘾩sC;wekrp[vA^]4E]4EM4E]ż0 g!d.wCOn 7qkdnHG'fg\5E]4E]D]4^ Cv7pCȂw7ͮ/s#yMtDMtD=4DMt2f !?{BuBv͓!K3F.袉.袉.zh.T/!d!{nYytCȞ'e<{~:l+F袉.袉.zh.T/!d3.BuCȺ;!d߂52OOݫ3F.袉.袉.zh.T/!dL_8 !~L7pGs?QJ'K#3G.袉.袉.zh.T/!dL}tBw[䆐{g8j3b i5E]4E]D]4^ C}n1\7޾)QN{A^]4E]4EM4E]ż :=:5j;d/Soo;/|𲮮~۵RB|F}O՟vC;we8q쌑&h&hh&Ջy8Hl#yv3 c?߮[RB cBf]n1uCȎ9#yMtDMtD=4DMtRq"|ϳgycI !w} !tCH_K3O!8k&h&衉&h PAp1v=+KqFGcM%Cν!?< !?SnuWke1F1-&h&hh&>& c2{Ǜ2smUc]S k>޿^yixo6;S)^ !ERļTB}_bu۳۬rwB}B=~kv_!#yMtDMtD=4DM]n[r{4k G=cB?FC6 C]?}ql#CH8nn/ o'geWx~ߖ]?$e!Dl~۴/!ʹ5ztykIv?袉.袉.zh.ԻB/} * v~ބׯ&T oͼ+ݿC=8k&h&衉&h P& ]?/RE ]oγ^ߔ ^6oo=}v;Waϥ^1/ !Mt~ !;&酯LgOn1㫉.袉.袇&袉.#gwȏ _{?Ͽʟom';??`Pvs-CAhho#v}ޞ]71}v]foL1/ !M4k nNc?)[!dE-&h&hh& DŽP~0v={@ncm۩pD__r,Qck{hh myy?c?V$~!d9o _!;o 袉.袉.zh.R4@ng?dm_؟ C}Q'| !M2pd_M/_kCA^]4E]4EM4E]CHÅBT; .v{m  | !MBvNd=c mS _MtDMtD=4DM]b!n>;|? ~$ׯ!ij7: o!$$ECȞ'd5[j&h&衉&hK1?{߶l <̏ vY8`kȿgaoˌvJm ߫*b_Bdɘ>F?plٿ9B]5qDMtDMtCMtDb? /S8 68g#܏(EC86jo+} ;n!B$Bʆߘ$o-I̮ٚ8k&h&衉&hKk#h&)B-\BW0袉.袉.zh.ҚBF CHdO!ᆐKICȳglM5E]4E]D]4^ CHZqCry'=7X97IuCƁ.pDMtDMtCMtDz1/ !MotB_ply욭&h&hh&Ջyai{BB~qyl!A^]4E]4EM4E]ż04cI3 !m7zcC֩5[yMtDMtD=4DMt$6\ynyhb1G^IM_KA^]4E]4EM4E]ż045}2tz"6l;fk .袉.袇&袉.@b_Bd 7B{B6}A^]4E]4EM4E]4ES.u!ɨQܩ;;ѣGγίr>ꆐq3Bg3`E]4E]D]4E]4Rbc ! y]]]F{zƏЈBV~2BoNnQL4E]4E]D]4E]4Rb#H,{?˟gѤ톐0`E]4E]D]4E]4R6lÈY!^1rY_B^t9;}Eq0DMtDMtCMtDMtː !GfSY#_+!>|z vjqL4E]4E]D]4E]4RahL#3!ľHJ7ҝo#ȑ'~e9qĉ'N8qĉ'NN5؏aʽXj(|Ԑ'JTr7 !}(}F.=^'E袉.袉.zh.袉.bvg6j\?l/AğoZ bT)S>손? tDMtD=4DMtDM1= 3ٓ!ήպ8h&h&衉&h&hم!I 'C]4E]4EM4E]4ES. !MknzJiykd5[MtDMtD=4DMtDM104ν!7niuWd[W:l(f`.袉.袇&袉.)f&CkNrC ch&hh&h]B`Ϲ!!d=c^05B^,f`.袉.袇&袉.)f&7̸d7<7cɞ'%'B-&h&hh&h]B!ߝ>7l !@MtDMtCMtDMt CH~yBw !dSd!i5[MtDMtD=4DMtDM104-CHDZg[0#}TBES[MtDMtD=4DMtDM104BlΉ tDMtD=4DMtDM104Oޕ$Ϝ!;dl]L4E]4E]D]4E]4L._B$N>w(p0DMtDMtCMtDMt CH2ϸ '0@]4E]4EM4E]4ES. !M!w^>7\`d:$f`.袉.袇&袉.)f&757$YzJ:ߜ]uq0DMtDMtCMtDMt CHY3#?uCȽ>tDMtD=4DMtDM1047$w} !W|!2袉.袉.zh.袉.bvaipsn9cs,|O:ٟ]uq0DMtDMtCMtDMt CHBƧ?stS`.袉.袇&袉.)f&![NwCȃ?tDMtD=4DMtDM104A8u(q0DMtDMtCMtDMt CHLeh&hh&h]BT4[0#I:-BnȮ8h&h&衉&h&hم!A[vnpC-~ ![&^:,=%Boή8h&h&衉&h&hم!A^[㆐N|BN}Ƞ&h&衉&h&hم!AECȁsd{!욭&h&hh&h]BTvNm&h&hh&h]B`.袉.袇&袉.)f!dƸ_vC/: 9t9>V`.袉.袇&袉.)f~ya~;GC`j`.袉.袇&袉.)f-!г2@]4E]4EM4E]4ES. ! *Bz tDMtD=4DMtDM104Ȇ1w}ƍ 3j:!ʮ8h&h&衉&h&hم!Ar_?>;V`.袉.袇&袉.)f-~' !Gq0DMtDMtCMtDMt CH毟i7忞X7!sk>&袉.袉.zh.袉.bvai !w} !W| !NJej}L4E]4E]D]4E]4Ҡp37r) !@MtDMtCMtDMt CH !?w tDMtD=4DMtDM104ppi:l]q0DMtDMtCMtDMtˠCѣQFSOOOvnr;f|{VJC GcMB .袉.袇&袉.)fC6(g1t;'玎ҟ4Zqñ!d!dѿ+CE]4E]D]4E]4RqQ#|>ϳgymb bCȶ# MtDMtD=4DMtDM1 :# ^X>khLꆐq'%otA>;}FH9b~ӢztDMtD=4DMtDM1TBqp԰6~hb}d?CȮۿtDMtD=4DMtDM1 :(RB/3pYzJ:ߜ]q0DMtDMtCMtDMt CHBRL4E]4E]D]4E]4 MtDMtD=4DMtDM104ȆoBpd h&hh&h]BdCȔ)vCI?!2袉.袉.zh.袉.bvaiCH&h&hh&h]B`.袉.袇&袉.)f]З!O!ʮ8h&h&衉&h&hم!A_8pNm&h&hh&h]B`.袉.袇&袉.)frutCȚ'o0@]4E]4EM4E]4ES. ! !dUwC K-tDMtD=4DMtDM104(?_pC:,:!F{`.袉.袇&袉.)f BMm&h&hh&h]BtrMtDMtD=4DMtDM104>Bf h&hh&h]BB,4BV]=p0DMtDMtCMtDMt CHl֕'!dӄ':CdE]4E]D]4E]4 BOxBNah&hh&h]B4`Y6>B֝]=p0DMtDMtCMtDMt CHCȡ!/ʮ8h&h&衉&h&hم!ᆐ;~!$h&hh&h]BsrB^]=p0DMtDMtCMtDMt CH J>1&袉.袉.zh.袉.bvai !Bf= !?c h&hh&h]B`C]pCc!d !AMtDMtCMtDMt CH eCȾMٵMtDMtD=4DMtDM104`CٵMtDMtD=4DMtDM104` !&袉.袉.zh.袉.bvai@t9-V{`.袉.袇&袉.)f!6ةp0DMtDMtCMtDMt CHl32K޸3 !BMtDMtCMtDMt CHl'}gh&hh&h]Br MtDMtD=4DMtDM104 ?HG%'ghL4E]4E]D]4E]4ҀY+n !w:دm3L4E]4E]D]4E]4c8h&h&衉&h&hم! !p0DMtDMtCMtDMt CHl<47<:7]+H&h&衉&h&hم!6\!gI޻KgdhL4E]4E]D]4E]4ҀCȬs!d5MtDMtD=4DMtDM104!&袉.袉.zh.袉.bvaicB2L4E]4E]D]4E]4c8h&h&衉&h&hم! !p0DMtDMtCMtDMt CHl!dɸ'~̹5MtDMtD=4DMtDM104)S>손?!/ʮ>8h&h&衉&h&hم! !p0DMtDMtCMtDMtKӇdԨQՕ_GGG:ٹcD]4E]4EM4E]4ES.MBzzzѣGgo%nJaCˊ3!䚇`.袉.袇&袉.)f!lY vl*C2d8h&h&衉&h&h٥CH8r({ۮ%aD]4E]4EM4E]4ES.ч_ğB4ܧ?pS7㆐7*>'N8qĉ'N8qĩKɳbvqǒC>#d#7-GMtDMtCMtDMtKS={Ԑ]߿pj-Bp0DMtDMtCMtDMtKSSqPmcѤ*Cع1d8h&h&衉&h&h٥CH *C%}67|&B^%F`.袉.袇&袉.)f䇐Ï*B |VD]4E]4EM4E]4ES. ! !d!dO? !BMtDMtCMtDMt CHCȑ!̮>8h&h&衉&h&hم!NsCM~tjb`.袉.袇&袉.)f:Bn8!(&袉.袉.zh.袉.bvaCHL4E]4E]D]4E]4R'?Kn1w}$B<]}p0DMtDMtCMtDMt CHb#vNEJk&袉.袉.zh.袉.bvaCHL4E]4E]D]4E]4R'8h&h&衉&h&hم!N !q0DMtDMtCMtDMt CHd Z탃&h&hh&h]BT8b6D]4E]4EM4E]4ES. !u!s7D]4E]4EM4E]4ES. !u!d]qCȝcD]4E]4EM4E]4ES. !ub鏃&h&hh&h]B4`ty텃&h&hh&h]BBtYzJvD]4E]4EM4E]4ES. !ub鏃&h&hh&h]BMtDMtD=4DMtDM107?6|= !L$E]4E]D]4E]4RM96y7CItDMtD=4DMtDM10ԉ!?&袉.袉.zh.袉.bvaCHL4E]4E]D]4E]4RKggh/L4E]4E]D]4E]4R'8h&h&衉&h&hم!N !q0DMtDMtCMtDMt CH־2?1@]4E]4EM4E]4ES. !uZ~C}!r袉.袉.zh.袉.bvaCHL4E]4E]D]4E]4R'8h&h&衉&h&hم!N6\noa:lRvD]4E]4EM4E]4ES. !uJO!1'txQvD]4E]4EM4E]4ES. !u!gWȡ&h&衉&h&hم!N `.袉.袇&袉.)f:_?!$D]4E]4EM4E]4ES. !ub鏃&h&hh&h]BMtDMtD=4DMtDM10ɆI3 !}r#F{`.袉.袇&袉.)f:r-!dRvD]4E]4EM4E]4ES. !ub鏃&h&hh&h]B4w !&袉.袉.zh.袉.bvaӬ708h&h&衉&h&hم!N8B^]p0DMtDMtCMtDMt CHCȡHm]5 MtDMtD=4DMtDM10ɆoBЂ?ah&hh&h]BdCȵBzƝ~S !CMtDMtCMtDMt CHB`.袉.袇&袉.)f:ݱrf:l;F{`.袉.袇&袉.)f:M_8rCHnÁ&h&hh&h]BMtDMtD=4DMtDM10ɆB0CItDMtD=4DMtDM10Ɇ)S>손?D]4E]4EM4E]4ES. !ub鏃&h&hh&h]B4y !&袉.袉.zh.袉.bva!ty'F{`.袉.袇&袉.)f:2!d$nyyB8H&h&衉&h&hم!N !q0DMtDMtCMtDMt CHB`.袉.袇&袉.)f:BnU7lS!dY5MtDMtD=4DMtDM10r|!d5MtDMtD=4DMtDM10ԁ!d &袉.袉.zh.袉.bvaBnn7wB8H&h&衉&h&hم!~N1&袉.袉.zh.袉.bvaC@L4E]4E]D]4E]4R{z!/Ȯ~8h&h&衉&h&hم!/揤CƋk&袉.袉.zh.袉.bvaæ0p0DMtDMtCMtDMt CHB`.袉.袇&袉.)f:rB`.袉.袇&袉.)f:2ϸ!1N;k5MtDMtD=4DMtDM10!?쟕͓k&袉.袉.zh.袉.bvaC@L4E]4E]D]4E]4R?CHMtDMtD=4DMtDM10aٖBr8h&h&衉&h&hم! !q0DMtDMtCMtDMt CH !|4B~~}vD]4E]4EM4E]4E]i1!?t֕]p0DMtDMtCMtDMtR߁d)&ܡR8h&h&衉&h& $3s7~&/ʮ1B2MtDMtD=4DMtD›Wy.9~lOi8akmJo CHB`.袉.袇&袉.mINfYg|nݳC]B2MtDMtD=4DMtDeㆍ6vㇽ"6م! !q0DMtDMtCMtDMtK\|oq?~粅E6&mˮU^. !u0퇃&h&hh&hKl޹G_>9mer_$ή9]BB@: 58h&h&衉&h&4o!7ru~W.I{1ٰ}ovR8h&h&衉&h&Бw9kw$w=~ŏ`tpiD. !u`&h&hh&hKu?۞8aqi8'sn^\jM?2]B`Cȸ;>冐{Jr2p0DMtDMtCMtDMt~1uE2uֲQ10˻>ᆐ~C !L$E]4E]D]4E]}!YC'f:0 D]4E]4EM4E]4%e1oCم!K6 !'Cț=5MtDMtD=4DMt]oݕ\2БqRgBeG-bvaC@5E]4E]D]4EStҵl{G8~4IQ]B2yMtDMtD=4DMt]=5>>ark|ݏZRB&~ 7'$G~>袉.袉.zh.袩؏L99e~c ƞ.f:r-!OYpCyItDMtD=4DMt*]EM^=܌Clټsv͑!f:0 A^]4E]4EM4E]4._oS~,fم! !qDMtDMtCMtDM#K߾Cwtg1u;Ǟk\10!?Nm&h&hh&hI]L{Ƈ?N=#d]ٵZC. !upCM !=!(袉.袉.zh.I=cꂭi=gO_\{mV CHl[uCHϸB .袉.袇&袉.T,րg|咤け§م! !qDMtDMtCMtDMJ]k>eyi8'so}&Бwk]B2yMtDMtD=4DMtѤ_I.79~lOgد}@v CHB .袉.袇&袉.=ۓn\U?kvճ?R8k&h&衉&h]_I,L/ͮ]~ё5ʝs=zt: KO.mO5E]4E]D]4ES.yߏzͲd/qż5ؘaCuF^WWWîc׵g1 .袉.袇&袉.˪w'_s{S?؏Y#ļ5> J?2o !95E]4E]D]4EPu֕Bz6YuWv TR6lÈ=yv*z 2k !95E]4E]D]4ESbo"C+!k#B97=qDMtDMtCMtDMk}\Ж=ng3B5qļ0!BN@:l(=qDMtDMtCMtDME]^=9e\pzb_B2yMtDMtD=4DMtvYuWr֍JșO'=/]b_B2yMtDMtD=4DMtd]>c kٶ1/ !u/3pDMtDMtCMtD=: t׊ v<9}pv :L!CHyMtDMtD=4DMt2o^~E.4ļ0ԁ!d 袉.袉.zh.᥾]ϖӯ]ya !SBB .袉.袇&袉.Бw,|99a"7؏L{dF(ya !ӮzB^ CQ5E]4E]D]4ezywi+K9u!.bvaC@L4E]4E]D]4%>{澐wn9%ɜ;KSt CHB`.袉.袇&袉.q/jC%s/m0t CH.k&袉.袉.zh.G߾CE6~ Χ[weDM10>D]4E]4EM4E]= ~\0`{J)f:0 D]4E]4EM4E]NY ¨..bvaC@L4E]4E]D]4eh^Zr5= $DM10ԁ!d &袉.袉.zh.\;l] ES. !u`&h&hh&4IY !hم! !q0DMtDMtCMtDٯކ]B!/~tyD]4E]4EM4E]d[ɩ,sq>\Ж]Bo!dD]4E]4EM4E]ccǤa#i=zywvi)f:0 D]4E]4EM4E]jg?rfӥ}d6]4R8h&h&衉&hKmnYJr7|d%EM10ԁ!d &袉.袉.zh.Rݶ,}e6]4R8h&h&衉&hy @ 3]2t)f:0 D]4E]4EM4E]ʳDl gدݼsvТ]B`CȍwC/ntyiZvi{`.袉.袇&袉.QϘ a74V.bva !' !t֕]ڞ8h&h&衉&h@ 2yid[%ES. !u`&h&hh&A/ݹ!}pvi\t CHB`.袉.袇&袉.QO({AM10hWrsG! !)&袉.袉.zh.ڽKQϞ:y@v)f1`.袉.袇&袩_GQ+)f1`.袉.袇&袩]/zuO%˷.ES. !5C-c톐wt1'F{`.袉.袇&袩ݺ;lGa.w^/bvaBlsOp0DMtDMtCMtN]گõ^u%zhم!F !8h&h&衉&hj.: 6ud%hم!F !8h&h&衉&hj.vLιyMGa{QQ+)f1`.袉.袇&袩߰39in#M10(?fOvD]4E]4EM4ES+vg|3?@!̐]BjBR]BjR&h&hh&.ֽb#M1]BjR&h&hh&.W?%9';7$.i?_4R .g)D]4E]4EM4EBBliw_4Re[f)D]4E]4EM4Epwy@rEU10Ԉ!MtDMtD=4DMM}Pm9eER)f2O!1`.袉.袇&iܺ95/bvѣQFSOOOvnr;uuue?םgׯp!w} !9!$D]4E]4EM4ES.\|o@tɜy]*>FEr?faㆱpb(_^ !z8h&h&衉&h~WĞ ҵl[v h٥#x>g^8leEcCHk`.袉.袇&)VU/NNԍ K]"_426fx]KY#_];`T9_φ'.m_L4E]4E]D]4r7rf7Š2/bvix?cxv~ѳDLwww*!ľHq[BO{>\x]N8qĉ'N8q%|z sdպibt 0bH>~$dˋ 3Bt7Sz=ڒ]jpK!Ğ1؋yφ @NB~Cj&h&hhar#H̼ugZ_4Rq16f5u>aIxZ vyx׃!DMtDMtD=4DM2r5(qˠC"=L4E]4E]D]45[ݏk}KP+/bvaCH1&袉.袉.zh.e#E6N潘:Nv)ES. !5*B]ھ8h&h&衉&hj˶]ON[Yz=_.A#hم!F!.m_L4E]4E]D]45e]It#W.IV;)f1`.袉.袇&袩.wb#Wgyhم!F !8h&h&衉&hˤKr񽽼)f-R&h&hhZ؋~ n3Kl_4Rg2`.袉.袇&袩.Eagv M10Ԉ!MtDMtD=4DMty{S&/u#w%*_4R~C !MtDMtD=4DMuylS_EQϺq/ M10(BtxQvi`.袉.袇&袩R٫_sb#E6q CHBq0DMtDMtCMtTw\A.|v.b)f]{+CH&袉.袉.zh. 3손gX*CQtDMtD=4D ;MQt CHBq0DMtDMtCMt^^(E]4R#bL4E]4E]D]OE]4R#bL4E]4E]D]GE]4R#bL4E]4E]D]l1tDM10Ԉ!MtDMtD=4DA ]4ES. !5b)D]4E]4EM4%jGCMt CHBq0DMtDMtCMtE]4R#bL4E]4E]D]^#&hم!F !8h&h&衉& zFCMt CHBq0DMtDMtCMt:i+A ]4ES. !5b)D]4E]4EM4eh#ޮ]4ES. !57Lˆ-Wg/&袉.袉.zh.gg;h]Bj4* !~W!d[Wvi`.袉.袇&\6k42h]Bj4y !8h&h&衉&4OFCMt CHBq0DMtDMtCMtif .)f1`.袉.袇&Ҹp9iKGMt CHl5'!`.袉.袇&ҘCGIιyMiٰ}ovIc袉.bva !ӮzB~+#٥틃&h&hhKl9٦ .)fB~tGMtDMtD=4D b袉.bvağ^R&h&hhKz1tDM10>R&h&hhKm=3ؓ]\tDM10Ԉ!MtDMtD=4D\p›ٹGMt CHBq0DMtDMtCMt%s2h]Bj!/tLvi`.袉.袇&R#&hم!FKKMtDMtD=4DA ]4ES. !5b)D]4E]4EM4ѥq^t#{.[m1tDM10Ԉ!MtDMtD=4D=Adײs㠋&hم!F !8h&h&衉&E]4R#bL4E]4E]D]U7ةkٶܸ袉.bvaCH1&袉.袉.zh.ٳ?Y 6سB ]4ES. !5 gt9;}q0DMtDMtCMt9^ Ua1tDM10(B^φp0EMtDMtCMtImؾ79a"7\6CMt CHBq0DMtDMtCMtIm&^̍ w=:Nv&hم!FOB<&袉.袉.zhݻ>p8䴕n9Um_4ES. !5!d!d !MtDMtD=4]ݶ΍ {f /)f1`.袉.袇&ڹF',v&h٥;;;QFSWWWvn؟k&h&hhj.YAoQE]4G===ѣ7tfov}{Z0`.袉.袇&ڱ˼u~Mfj.bvix{6H,{ۆ!$? FeyQ&h&hhj.^ޝ?Ǎ lE]4!$>l)gj}6QBp0DMtDMtCM^ O;gj.bv!Ğb3CC&袉.袉.zh=}Arh]>GяǑTcժUn 7rYr7~'d}$}Qx=N8qĉ'N85o<'=X|9'NFixjb`/jÇ<{VH~, M6^v _8`(m0Ch !m0Ch#jNFNٹ;;;Kwuue?-=5f^>~-e?yG}av?j=)!~:|{_OݧZG}Nv?1=PcQ?o; ^ G?׳ۛoB/q?yv~ ;_[; ׿Q}=^x [; [=gž6ٿ$C:oQ|a׼Q;sq(O;}Mqk^}=~۟xac LQ;o>y7b !?#F ^`o6Ey~{|j9IuͿmsߣhj9}qyW|4.z﷿Oz>gMxw_o*w>Wmzo{ WQr3j=#j |Se_o;XƳ& QG俦|x0|4&u)G.+w>u׽8}]Nr~¾w}|'}KQ|'<`z>7پvxS|Ǿ~ko'tk= !Zkory_S;H[oS|4v{U*:_ZG׷}ecP"k4嵞{x,^_;FyGQ*^|Ǿku^)w>˝~4uM1={;h={;<{F;.s?v|r1v? w|4ƾ]nc_(@ٟo¯X|ξ?g?s[}g?_o*w>˝߈3E< ߨhk=?<ߢ} |K>dwþg#v^QN_>_Oxo+׳v2_h\8T˿GcS{+knh}mO^v^7b !ƾ `'q_{;xv@)w>c_Ku'/<2^vvL gN?G{-7GaZG튎aoL|{ _ -w>j{`"~vgǡMx>=zz |4ƾ3ǐm/w>jۛoBCh !m0Ch !m0ChI{S\IENDB`srpc-0.10.1/docs/installation.md000066400000000000000000000071661454502251400165270ustar00rootroot00000000000000# 安装 ## 1. Linux源码安装 源码安装需要一些前置依赖:CMake(要求3.6以上)、OpenSSL(推荐1.1及以上)、Protobuf(要求3.5及以上) 默认会编译出: 1. 静态库:libsrpc.a(或者dylib) 2. 动态库:libsrpc.so(或者dll) 3. 用于生成代码的二进制工具:srpc_generator - **cmake** ~~~sh git clone --recursive https://github.com/sogou/srpc.git cd srpc make make install # 编译示例 cd tutorial make ~~~ - **bazel(二选一)** ~~~sh git clone --recursive https://github.com/sogou/srpc.git cd srpc bazel build ... # 在bazel-bin/目录下,编译出lib和srpc_generator以及所有示例编译出来的可执行文件 ~~~ 此外,还可以借助srpc_tools安装和部署脚手架。小工具用法参考:[srpc/tools/README_cn.md](srpc/tools/README_cn.md) Workflow、snappy和lz4也可以系统预装,如果third_party中没有通过`--recursive`参数拉取源码依赖,则会从系统默认安装路径寻找,snappy的预装要求版本是v1.1.6或以上。 如果需要源码安装`Protobuf`,参考命令: ~~~sh git clone -b 3.20.x https://github.com/protocolbuffers/protobuf.git protobuf.3.20 cd protobuf.3.20 sh autogen.sh ./configure make make install ~~~ ## 2. Debian Linux和Ubuntu自带安装包 SRPC已经打包到Debian,目前是Debian sid(unstable)的自带安装包,最终会进入stable的repository。 需要编辑/etc/apt/sources.list文件,才能访问unstable的repository: sources.list文件格式: `deb ` 将'unstable'字段加到我们的repo中,作为sub branch之一: ~~~~sh deb http://deb.debian.org/ main contrib non-free --> deb http://deb.debian.org/ unstable main contrib non-free ~~~~ 然后update我们的repo列表,就可以安装SRPC: ~~~~sh sudo apt-get update ~~~~ 安装SRPC库用于开发: ~~~~sh sudo apt-get install libsrpc-dev ~~~~ 安装SRPC库用于部署: ~~~~sh sudo apt-get install libsrpc ~~~~ ## 3. Fedora Linux自带安装包 SRPC已经是Fedora系统的自带安装包。 为了开发目的安装srpc库: ~~~sh sudo dnf install srpc-devel ~~~ 要安装srpc库以进行部署,请执行以下操作: ~~~sh sudo dnf install srpc ~~~ ## 4. Windows下安装 Windows版下srpc代码无差异,注意需要依赖Workflow的[windows分支](https://github.com/sogou/workflow/tree/windows)。 另外,srpc_tools暂时不支持Windows下使用。 ## 5. MacOS源码安装 - 安装依赖 `OpenSSL` ``` brew install openssl ``` - 安装 `CMake` ``` brew install cmake ``` - 指定 `OpenSSL` 环境变量 由于MacOS下默认有LibreSSL,因此在brew安装后,并不会自动建软链,我们需要手动把执行路径、编译路径、cmake时的find_package路径都配置到bash的环境变量中。用户可以执行`brew info openssl`查看相关信息,也可以如下配置: ``` echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >> ~/.bash_profile echo 'export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"' >> ~/.bash_profile echo 'export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"' >> ~/.bash_profile echo 'export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig"' >> ~/.bash_profile echo 'export OPENSSL_ROOT_DIR=/usr/local/opt/openssl' >> ~/.bash_profile echo 'export OPENSSL_LIBRARIES=/usr/local/opt/openssl/lib' >> ~/.bash_profile ``` 如果使用zsh,则还需要以下一步,把bash的配置加载一下: ``` echo 'test -f ~/.bash_profile && source ~/.bash_profile' >> ~/.zshrc source ~/.zshrc ``` 剩下的步骤和Linux下编译没有区别。 srpc-0.10.1/docs/rpc.md000066400000000000000000000532501454502251400146050ustar00rootroot00000000000000[English version](/docs/en/rpc.md) ## 基础功能对比 |RPC |IDL |通信 | 网络数据 |压缩 | Attachement | 半同步 | 异步 | Streaming | |---------------------------|-----------|------|------------|--------------------|------------|---------|--------|-------------| |Thrift Binary Framed | Thrift | tcp | 二进制 |不支持 | 不支持 | 支持 | 不支持 | 不支持 | |Thrift Binary HttpTransport| Thrift | http | 二进制 |不支持 | 不支持 | 支持 | 不支持 | 不支持 | |GRPC | PB | http2| 二进制 |gzip/zlib/lz4/snappy| 支持 | 不支持 | 支持 | 支持 | |BRPC Std | PB | tcp | 二进制 |gzip/zlib/lz4/snappy| 支持 | 不支持 | 支持 | 支持 | |SRPC Std | PB/Thrift | tcp | 二进制/JSON |gzip/zlib/lz4/snappy| 支持 | 支持 | 支持 | 不支持 | |SRPC Std Http | PB/Thrift | http | 二进制/JSON |gzip/zlib/lz4/snappy| 支持 | 支持 | 支持 | 不支持 | ## 基础概念 - 通信层:TCP/TPC_SSL/HTTP/HTTPS/HTTP2 - 协议层:Thrift-binary/BRPC-std/SRPC-std/SRPC-http/tRPC-std/tRPC-http - 压缩层:不压缩/gzip/zlib/lz4/snappy - 数据层:PB binary/Thrift binary/Json string - IDL序列化层:PB/Thrift serialization - RPC调用层:Service/Client IMPL ## RPC Global - 获取srpc版本号``srpc::SRPCGlobal::get_instance()->get_srpc_version()`` ## RPC Status Code |name | value |含义 | |-----------------------------------|-----------|-------------------| |RPCStatusUndefined | 0 | 未定义 | |RPCStatusOK | 1 | 正确/成功 | |RPCStatusServiceNotFound | 2 | 找不到Service名 | |RPCStatusMethodNotFound | 3 | 找不到RPC函数名 | |RPCStatusMetaError | 4 | Meta错误/解析失败 | |RPCStatusReqCompressSizeInvalid | 5 | 请求压缩大小错误 | |RPCStatusReqDecompressSizeInvalid | 6 | 请求解压大小错误 | |RPCStatusReqCompressNotSupported | 7 | 请求压缩类型不支持 | |RPCStatusReqDecompressNotSupported | 8 | 请求解压类型不支持 | |RPCStatusReqCompressError | 9 | 请求压缩失败 | |RPCStatusReqDecompressError | 10 | 请求解压失败 | |RPCStatusReqSerializeError | 11 | 请求IDL序列化失败 | |RPCStatusReqDeserializeError | 12 | 请求IDL反序列化失败| |RPCStatusRespCompressSizeInvalid | 13 | 回复压缩大小错误 | |RPCStatusRespDecompressSizeInvalid | 14 | 回复解压大小错误 | |RPCStatusRespCompressNotSupported | 15 | 回复压缩类型不支持 | |RPCStatusRespDecompressNotSupported| 16 | 回复解压类型不支持 | |RPCStatusRespCompressError | 17 | 回复压缩失败 | |RPCStatusRespDecompressError | 18 | 回复解压失败 | |RPCStatusRespSerializeError | 19 | 回复IDL序列化失败 | |RPCStatusRespDeserializeError | 20 | 回复IDL反序列化失败| |RPCStatusIDLSerializeNotSupported | 21 | 不支持IDL序列化 | |RPCStatusIDLDeserializeNotSupported| 22 | 不支持IDL反序列化 | |RPCStatusURIInvalid | 30 | URI非法 | |RPCStatusUpstreamFailed | 31 | Upstream全熔断 | |RPCStatusSystemError | 100 | 系统错误 | |RPCStatusSSLError | 101 | SSL错误 | |RPCStatusDNSError | 102 | DNS错误 | |RPCStatusProcessTerminated | 103 | 程序退出&终止 | ## RPC IDL - 描述文件 - 前后兼容 - Protobuf/Thrift ### 示例 下面我们通过一个具体例子来呈现 - 我们拿pb举例,定义一个ServiceName为``Example``的``example.proto``文件 - rpc接口名为``Echo``,输入参数为``EchoRequest``,输出参数为``EchoResponse`` - ``EchoRequest``包括两个string:``message``和``name`` - ``EchoResponse``包括一个string:``message`` ~~~proto syntax="proto2"; message EchoRequest { optional string message = 1; optional string name = 2; }; message EchoResponse { optional string message = 1; }; service Example { rpc Echo(EchoRequest) returns (EchoResponse); }; ~~~ ## RPC Service - 组成SRPC服务的基本单元 - 每一个Service一定由某一种IDL生成 - Service由IDL决定,与网络通信具体协议无关 ### 示例 下面我们通过一个具体例子来呈现 - 沿用上面的``example.proto``IDL描述文件 - 执行官方的``protoc example.proto --cpp_out=./ --proto_path=./``获得``example.pb.h``和``example.pb.cpp``两个文件 - 执行SRPC的``srpc_generator protobuf ./example.proto ./``获得``example.srpc.h``文件 - 我们派生``Example::Service``来实现具体的rpc业务逻辑,这就是一个RPC Service - 注意这个Service没有任何网络、端口、通信协议等概念,仅仅负责完成实现从``EchoRequest``输入到输出``EchoResponse``的业务逻辑 ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { response->set_message("Hi, " + request->name()); printf("get_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); } }; ~~~ ## RPC Server - 每一个Server对应一个端口 - 每一个Server对应一个确定的网络通信协议 - 一个Service可以添加到任意的Server里 - 一个Server可以拥有任意多个Service,但在当前Server里ServiceName必须唯一 - 不同IDL的Service是可以放进同一个Server中的 ### 示例 下面我们通过一个具体例子来呈现 - 沿用上面的``ExampleServiceImpl``Service - 首先,我们创建1个RPC Server,并确定proto文件的内容 - 然后,我们可以创建任意个数的Service实例、任意不同IDL proto形成的Service,把这些Service通过``add_service()``接口添加到Server里 - 最后,通过Server的``start()``或者``serve()``开启服务,处理即将到来的rpc请求 - 想像一下,我们也可以从``Example::Service``派生出多个Service,而它们的rpc``Echo``实现的功能可以不同 - 想像一下,我们可以在N个不同的端口创建N个不同的RPC Server,代表着不同的协议 - 想像一下,我们可以把同一个ServiceIMPL实例``add_service()``到不同的Server上,我们也可以把不同的ServiceIMPL实例``add_service``到同一个Server上 - 想像一下,我们可以用同一个``ExampleServiceImpl``,在三个不同端口、同时服务于BPRC-STD、SRPC-STD、SRPC-Http - 甚至,我们可以将1个Protobuf IDL相关的``ExampleServiceImpl``和1个Thrift IDL相关的``AnotherThriftServiceImpl``,``add_service``到同一个SRPC-STD Server,两种IDL在同一个端口上完美工作! ~~~cpp int main() { SRPCServer server_srpc; SRPCHttpServer server_srpc_http; BRPCServer server_brpc; ThriftServer server_thrift; TRPCServer server_trpc; TRPCHttpServer server_trpc_http; ExampleServiceImpl impl_pb; AnotherThriftServiceImpl impl_thrift; server_srpc.add_service(&impl_pb); server_srpc.add_service(&impl_thrift); server_srpc_http.add_service(&impl_pb); server_srpc_http.add_service(&impl_thrift); server_brpc.add_service(&impl_pb); server_thrift.add_service(&impl_thrift); server_trpc.add_service(&impl_pb); server_trpc_http.add_service(&impl_pb); server_srpc.start(1412); server_srpc_http.start(8811); server_brpc.start(2020); server_thrift.start(9090); server_trpc.start(2022); server_trpc_http.start(8822); getchar(); server_trpc_http.stop(); server_trpc.stop(); server_thrift.stop(); server_brpc.stop(); server_srpc_http.stop(); server_srpc.stop(); return 0; } ~~~ ## RPC Client - 每一个Client对应着一个确定的目标/一个确定的集群 - 每一个Client对应着一个确定的网络通信协议 - 每一个Client对应着一个确定的IDL ### 示例 下面我们通过一个具体例子来呈现 - 沿用上面的例子,client相对简单,直接调用即可 - 通过``Example::XXXClient``创建某种RPC的client实例,需要目标的ip+port或url - 利用client实例直接调用rpc函数``Echo``即可,这是一次异步请求,请求完成后会进入回调函数 - 具体的RPC Context用法请看下一个段落: [RPC Context](/docs/rpc.md#rpc-context)) ~~~cpp #include #include "example.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; int main() { Example::SRPCClient client("127.0.0.1", 1412); EchoRequest req; req.set_message("Hello!"); req.set_name("SRPCClient"); WFFacilities::WaitGroup wait_group(1); client.Echo(&req, [&wait_group](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); wait_group.done(); }); wait_group.wait(); return 0; } ~~~ ## RPC Context - RPCContext专门用来辅助异步接口,Service和Client通用 - 每一个异步接口都会提供Context,用来给用户提供更高级的功能,比如获取对方ip、获取连接seqid等 - Context上一些功能是Server或Client独有的,比如Server可以设置回复数据的压缩方式,Client可以获取请求成功或失败 - Context上可以通过``get_series()``获得所在的series,与workflow的异步模式无缝结合 ### RPCContext API - Common #### ``long long get_seqid() const;`` 请求+回复视为1次完整通信,获得当前socket连接上的通信sequence id,seqid=0代表第1次 #### ``std::string get_remote_ip() const;`` 获得对方IP地址,支持ipv4/ipv6 #### ``int get_peer_addr(struct sockaddr *addr, socklen_t *addrlen) const;`` 获得对方地址,in/out参数为更底层的数据结构sockaddr #### ``const std::string& get_service_name() const;`` 获取RPC Service Name #### ``const std::string& get_method_name() const;`` 获取RPC Methode Name #### ``SeriesWork *get_series() const;`` 获取当前ServerTask/ClientTask所在series #### ``bool get_http_header(const std::string& name, std::string& value);`` 如果通讯使用HTTP协议,则根据name获取HTTP header中的value ### RPCContext API - Only for client done #### ``bool success() const;`` client专用。这次请求是否成功 #### ``int get_status_code() const;`` client专用。这次请求的rpc status code #### ``const char *get_errmsg() const;`` client专用。这次请求的错误信息 #### ``int get_error() const;`` client专用。这次请求的错误码 #### ``void *get_user_data() const;`` client专用。获取ClientTask的user_data。如果用户通过``create_xxx_task()``接口产生task,则可以通过user_data域记录上下文,在创建task时设置,在回调函数中拿回。 ### RPCContext API - Only for server process #### ``void set_data_type(RPCDataType type);`` Server专用。设置数据打包类型 - RPCDataProtobuf - RPCDataThrift - RPCDataJson #### ``void set_compress_type(RPCCompressType type);`` Server专用。设置数据压缩类型(注:Client的压缩类型在Client或Task上设置) - RPCCompressNone - RPCCompressSnappy - RPCCompressGzip - RPCCompressZlib - RPCCompressLz4 #### ``void set_attachment_nocopy(const char *attachment, size_t len);`` Server专用。设置attachment附件。 #### ``bool get_attachment(const char **attachment, size_t *len) const;`` Server专用。获取attachment附件。 #### ``void set_reply_callback(std::function cb);`` Server专用。设置reply callback,操作系统写入socket缓冲区成功后被调用。 #### ``void set_send_timeout(int timeout);`` Server专用。设置发送超时,单位毫秒。-1代表无限。 #### ``void set_keep_alive(int timeout);`` Server专用。设置连接保活时间,单位毫秒。-1代表无限。 #### ``bool set_http_code(int code);`` Server专用。如果通讯使用HTTP协议,则可以设置http status code返回码。仅在框架层能正确响应时有效。 #### ``bool set_http_header(const std::string& name, const std::string& value);`` Server专用。如果通讯使用HTTP协议,可以在回复中设置HTTP header,如果name被设置过会覆盖旧value。 #### ``bool add_http_header(const std::string& name, const std::string& value);`` Server专用。如果通讯使用HTTP协议,可以在回复中添加HTTP header,如果有重复name,会保留多个value。 #### ``void log(const RPCLogVector& fields);`` Server专用。透传数据相关,请参考OpenTelemetry数据协议中的log语义。 #### ``void baggage(const std::string& key, const std::string& value);`` Server专用。透传数据相关,参考OpenTelemetry数据协议中的baggage语义。 #### ``void set_json_add_whitespace(bool on);`` Server专用。JsonPrintOptions相关,可设置增加json空格等。 #### ``void set_json_always_print_enums_as_ints(bool flag);`` Server专用。JsonPrintOptions相关,可设置用int打印enum名。 #### ``void set_json_preserve_proto_field_names(bool flag);`` Server专用。JsonPrintOptions相关,可设置保留原始字段名字。 #### ``void set_json_always_print_primitive_fields(bool flag);`` Server专用。JsonPrintOptions相关,可设置带上所有默认的proto数据中的域。 ## RPC Options ### Server Params |name |默认 |含义 | |---------------------------|--------------------------|--------------------------------| |max_connections | 2000 | Server的最大连接数,默认2000个 | |peer_response_timeout | 10 * 1000 | 每一次IO的读超时,默认10秒 | |receive_timeout | -1 | 每一条完整消息的读超时,默认无限 | |keep_alive_timeout | 60 * 1000 | 空闲连接保活,-1代表永远不断开,0代表短连接,默认长连接保活60秒 | |request_size_limit | 2LL * 1024 * 1024 * 1024 | 请求包大小限制,最大2GB | |ssl_accept_timeout | 10 * 1000 | SSL连接超时,默认10秒 | ### Client Params |name |默认 |含义 | |---------------------------|--------------------------|--------------------------------| |host | "" | 目标host,可以是ip、域名 | |port | 1412 | 目标端口号,默认1412 | |is_ssl | false | ssl开关,默认关闭 | |url | "" | 当host为空,url设置才有效。url将屏蔽host/port/is_ssl三项 | |task_params | TASK默认配置 | 见下方 | ### Task Params |name |默认 |含义 | |---------------------------|--------------------------|--------------------------------| |send_timeout | -1 | 发送写超时,默认无限 | |receive_timeout | -1 | 回复超时,默认无限 | |watch_timeout | 0 | 对方第一次回复的超时,默认0不设置 | |keep_alive_timeout | 30 * 1000 | 空闲连接保活,-1代表永远不断开,默认30s | |retry_max | 0 | 最大重试次数,默认0不重试 | |compress_type | RPCCompressNone | 压缩类型,默认不压缩 | |data_type | RPCDataUndefined | 网络包数据类型,默认与RPC默认值一致,SRPC-Http协议为json,其余为对应IDL的类型 | ## 与workflow异步框架的结合 ### 1. Server 下面我们通过一个具体例子来呈现 - Echo RPC在接收到请求时,向下游发起一次http请求 - 对下游请求完成后,我们将http response的body信息填充到response的message里,回复给客户端 - 我们不希望阻塞/占据着Handler的线程,所以对下游的请求一定是一次异步请求 - 首先,我们通过Workflow框架的工厂``WFTaskFactory::create_http_task``创建一个异步任务http_task - 然后,我们利用RPCContext的``ctx->get_series()``获取到ServerTask所在的SeriesWork - 最后,我们使用SeriesWork的``push_back``接口将http_task放到SeriesWork的后面 ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { auto *http_task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [request, response](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); response->mutable_message()->assign((const char *)data, len); } else response->set_message("Error: " + std::to_string(task->get_error())); printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n", request->DebugString().c_str(), response->DebugString().c_str()); }); ctx->get_series()->push_back(http_task); } }; ~~~ ### 2. Client 下面我们通过一个具体例子来呈现 - 我们并行发出两个请求,1个是rpc请求,1个是http请求 - 两个请求都结束后,我们再发起一次计算任务,计算两个数的平方和 - 首先,我们通过RPC Client的``create_Echo_task``创建一个rpc异步请求的网络任务rpc_task - 然后,我们通过Workflow框架的工厂``WFTaskFactory::create_http_task``和``WFTaskFactory::create_go_task``分别创建异步网络任务http_task,和异步计算任务calc_task - 最后,我们利用串并联流程图,乘号代表并行、大于号代表串行,将3个异步任务组合起来执行``start()`` ~~~cpp void calc(int x, int y) { int z = x * x + y * y; printf("calc result: %d\n", z); } int main() { Example::SRPCClient client("127.0.0.1", 1412); auto *rpc_task = client.create_Echo_task([](EchoResponse *response, RPCContext *ctx) { if (ctx->success()) printf("%s\n", response->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); auto *http_task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { std::string body; const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); body.assign((const char *)data, len); printf("%s\n\n", body.c_str()); } else printf("Http request fail\n\n"); }); auto *calc_task = WFTaskFactory::create_go_task(calc, 3, 4); EchoRequest req; req.set_message("Hello!"); req.set_name("1412"); rpc_task->serialize_input(&req); WFFacilities::WaitGroup wait_group(1); SeriesWork *series = Workflow::create_series_work(http_task, [&wait_group](const SeriesWork *) { wait_group.done(); }); series->push_back(rpc_task); series->push_back(calc_task); series->start(); wait_group.wait(); return 0; } ~~~ ### 3. Upstream SRPC可以直接使用Workflow的任何组件,最常用的就是[Upstream](https://github.com/sogou/workflow/blob/master/docs/about-upstream.md),SRPC的任何一种client都可以使用Upstream。 我们通过参数来看看如何构造可以使用Upstream的client: ```cpp #include "workflow/UpstreamManager.h" int main() { // 1. 创建upstream并添加实例 UpstreamManager::upstream_create_weighted_random("echo_server", true); UpstreamManager::upstream_add_server("echo_server", "127.0.0.1:1412"); UpstreamManager::upstream_add_server("echo_server", "192.168.10.10"); UpstreamManager::upstream_add_server("echo_server", "internal.host.com"); // 2. 构造参数,填上upstream的名字 RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; client_params.host = "srpc::echo_server"; // 这个scheme只用于upstream URI解析 client_params.port = 1412; // 这个port只用于upstream URI解析,不影响具体实例的选取 // 3. 用参数创建client,其他用法与示例类似 Example::SRPCClient client(&client_params); ... ``` 如果使用了ConsistentHash或者Manual方式创建upstream,则我们往往需要对不同的task进行区分、以供选取算法使用。这时候可以使用client task上的`int set_uri_fragment(const std::string& fragment);`接口,设置请求级相关的信息。 这个域的是URI里的fragment,语义请参考[RFC3689 3.5-Fragment](https://datatracker.ietf.org/doc/html/rfc3986#section-3.5),任何需要用到fragment的功能(如其他选取策略里附带的其他信息),都可以利用这个域。 srpc-0.10.1/docs/wiki.md000066400000000000000000000477601454502251400147750ustar00rootroot00000000000000# SRPC架构介绍 - Sogou基于Workflow的自研RPC框架 srpc是sogou内部自研的基于Workflow项目做的RPC生态项目,通过解析部分IDL(接口描述文件)和进行代码生成,实现了与workflow底层通信框架的对接和非常简洁的用户接口。 srpc整体代码量大约1万行,内部使用多种泛型编程的方式,实现纵向解耦合、横向可扩展,核心使用同一套代码,架构十分精巧美丽。以下是架构设计思路详解。 ## 目录 **一、整体介绍** **二、接口描述、协议和通信层次介绍** 1. 接口描述文件层 2. RPC协议层 3. 网络通信层 4. 协议+网络通信结合 **三、架构层次介绍** **四、代码生成** 1. IDL原生接口 2. srpc代码生成的接口 3. srpc内部各网络协议 4. service与server的n对m关系 **五、同步、异步、半同步接口,及打通workflow任务流** 1. 异步接口 2. 同步接口 3. 半同步接口 4. task接口 5. 利用Context打通workflow任务流,以及异步server的做法 **六、其他可以借用workflow做的事情** **七、写在最后** ## 一、整体介绍 作为一个RPC,srpc的定位很明确,是做多个层级的纵向拆解和横向解耦。层次包括: * **用户代码**(client的发送函数/server的函数实现) * **IDL序列化**(protobuf/thrift serialization) * **数据组织** (protobuf/thrift/json) * **压缩**(none/gzip/zlib/snappy/lz4) * **协议** (SRPC-std/BRPC-std/Thrift-framed/TRPC) * **通信** (TCP/HTTP) 以上各层级可以相互拼装,利用函数重载、派生子类实现父类接口和模版特化等多种多态方式,来实现内部使用同一套代码的高度复用,以后如果想架构升级,无论是中间再加一层、还是某层内横向添加一种内容都非常方便,整体设计比较精巧。 且得益于workflow的性能,srpc本身的性能也很优异。srpc系统除了SRPC-std协议以外,同时实现了BRPC-std协议、Thrift-framed协议、腾讯的TRPC协议,同协议下与brpc系统和apache thrift系统进行了性能对比,吞吐性能领先,长尾性能与brpc各有优势但都比thrift强。 srpc目前在搜狗公司内部和开源后业界一些开发者的团队中已经稳定使用一段时间,并以其简单的接口让小伙伴们可以快速开发,且协助业务性能带来好几倍的提升。 本篇主要是对srpc架构设计思路的阐述,建议开发者结合主页readme等文档一起参考。 ## 二、 接口描述、协议和通信层次介绍 一般来说用户使用任何一种RPC,需要关心的是:``method``,``request``,``response``,以下会结合这三项来讲述如何和各层级打交道。 刚才整体介绍里看到的层级很多,但是作为srpc的开发,需要知道的层级是以下三种: srpc_protocol_transport_hierarchy ### 1. 接口描述文件层 如图从左到右,是用户接触得最多到最少的层次。我们作为一个已经使用了某种RPC的用户,一般会选择一种现有的IDL,目前小伙伴们用得比较多的是``protobuf``和``thrift``,这两种srpc都支持了。其中: * thrift:IDL纯手工解析,用户使用srpc是不需要链thrift的库的 !!! * protobuf:service的定义部分纯手工解析 用户在写开发代码的时候,必定是请求一个method,拿着某种IDL填request,拿回同种IDL定义的response。 **一种IDL决定了一种service。** ### 2. RPC协议层 中间那列是具体的协议,我们实现了``SRPC-std``,``BRPC-std``,``Thrift-framed``和``TRPC``,其中**SRPC-std**的协议如下: srpc_protocol 其中RPC的``service``名字和``method``名字等信息会的填到``meta``中,第三部分是完完整整的用户的IDL:具体的``request``/``response``的内容。 ### 3. 网络通信层 以上一片内容,如果直接二进制走TCP发出去,那么肯定要求对方要是相同协议的server/client才能解析。但是,如果我们希望跨语言,最简单的方式是借用HTTP协议,于是乎,我们的第三列网络通信,和TCP二进制内容并列的,是HTTP协议。 ### 4. 协议+网络通信结合 第二列和第三列理论上是可以两两结合的,只需要第二列的具体RPC协议在发送时,把HTTP相关的内容进行特化,不要按照自己的协议去发,而按照HTTP需要的形式去发。HTTP需要什么?**URL**,**header**和**body**。 我们把``service``/``method``拼到``URL``的host后面,接下来把具体协议的``meta``的内容按照HTTP可以识别的header key一项一项填进``header``里。 最后把具体``request``/``response``作为HTTP的``body``填好,发送出去。 我们除了BRPC + Http没有实现,其他两两结合一共实现出了7种网络通信协议: * SRPCStd * SRPCHttp * BRPCStd * TRPCStd * TRPCHttp * ThriftBinaryFramed * ThriftBinaryHttp 另外,为了更好地兼容HTTP用户,我们的body除了``protobuf``/``thrift``之外,还可以支持``json``,也就是说,client可以是任何语言,只要发一个带json的HTTP请求给server,server收到的依然是一个IDL结构体。 **一种网络通信协议决定了一种server/client/task。** ## 三、架构层次介绍 我们先看一下,把request通过method发送出去并处理response再回来的整件事情是怎么做的: srpc_one_round 根据上图, 可以更清楚地看到文章开头提到的各个层级,其中压缩层、序列化层、协议层是互相解耦打通的,代码实现上非常统一,横向增加任何一种压缩算法或IDL或协议都不需要改动现有的代码~ 在做srpc的架构设计期间的工作基本可以总结为:不停地解耦。 另外,作为一个通信框架,srpc为了支持外部服务的调用和可扩展的控制逻辑,对各个切面进行了**模块化插件**的设计。以server收消息到回复的一条线展开,模块化插件的架构图如下: srpc_module 插件如果需要使用,都提前需要注册,以做到不使用的模块无任何额外消耗。每个模块的内部实现都会把具体执行方式封装成一个任务,这是遵循workflow内部任务流的编程范式思想。 ## 四、 代码生成 可以看到上图中,有许多步骤是通过代码生成完成的。做rpc框架,因为用户要自定义自己的IDL结构体,对于网络上收到一片内存如何按照结构体定义的内容反射出来,并且为用户提供自定义结构体作为参数的接口,这些对于C++来说都是需要生成一部分代码来做的。理论上,如果依赖了protobuf和thrift,确实可以不用纯手工代码生成。 因此这里给大家介绍一下我们为什么要代码生成。 ### 1. IDL原生接口 我们说过,对于一个protobuf的service定义,或者thrift的全部内容,我们都选择了代码生成。这里以protobuf为例: ```cpp message EchoRequest { optional string message = 1; }; message EchoResponse { optional string message = 1; }; service ExamplePB { rpc Echo(EchoRequest) returns (EchoResponse); }; ``` 其实pb本身只需要配上: ```cpp option cc_generic_services = true; ``` 就可以帮你生成具体service/method的接口了,但是protobuf本身不带网络通信功能,你需要基于这个接口做开发,但是我们可以看看pb这个原生接口,非常的复杂: ```cpp // SERVER代码 class ExamplePB : public ::PROTOBUF_NAMESPACE_ID::Service { public: // 这里是接口层,用户需要派生这个类,实现同名函数,在函数里写server的业务逻辑 virtual void Echo(::PROTOBUF_NAMESPACE_ID::RpcController* controller, const ::example::EchoRequest* request, ::example::EchoResponse* response, ::google::protobuf::Closure* done); }; // CLIENT代码 class ExamplePB_Stub : public ExamplePB { public: // 发起RPC调用也需要知道controller和Closure done void Echo(::PROTOBUF_NAMESPACE_ID::RpcController* controller, const ::example::EchoRequest* request, ::example::EchoResponse* response, ::google::protobuf::Closure* done); }; ``` google的风格就是喜欢把接口层和实现层区分开来,想要走protobuf默认的代码生成,就需要让用户也这样写``controller``和``closure``,这两者都是可以再做具体实现的,一般来说支持protobuf的RPC都是这样做的。但是,用户要看到这么多复杂的东西会很头疼,尤其是``controller``,明明有了client还要多学习一个结构,不太符合架构的审美。 另外一点,response和controller的生命周期也是一个很confusing的东西,pb这种接口是需要让用户传入,用户来保证生命周期必须保持到异步请求结束,显然不太友好。 thrift的话就更不用说了,请求与连接本身结合太紧密,不利于我们自己做网络通信层。 ### 2. srpc代码生成的接口 为了支持两种不同的IDL都可以顺利请求workflow,我们进行了代码生成,包括基本的``generator``->``parser``->``printer``,这部分风格代码比较C。需要编译完之后对IDL文件进行如下调用: ```cpp srpc_generator [protobuf|thrift] [proto_file|thrift_file] out_dir ``` 具体文件生成到``xxx.srpc.h``中,还是拿刚才protobuf的例子,srpc生成出来的代码如下: ```cpp // SERVER代码 class Service : public srpc::RPCService { public: // 用户需要自行派生实现这个函数,与刚才pb生成的是对应的 virtual void Echo(EchoRequest *request, EchoResponse *response, srpc::RPCContext *ctx) = 0; }; // CLIENT代码 using EchoDone = std::function; class SRPCClient : public srpc::SRPCClient { public: void Echo(const EchoRequest *req, EchoDone done); }; ``` 用户的接口使用起来就会非常简单,尤其是client的接口,其中``done``是我们的回调函数,而类似于controller这种**会话上下文信息**的东西,用户不想用的话可以选择性忽略。 我们还模仿了thrift给生成了skeleton示例: ``server.xx_skeleton.cc``和``client.xx_skeleton.cc``,帮忙在main函数里构造了client和server以及service的派生,妈妈再也不用担心我连创建的步骤都搞错了。 ```cpp //以下是自动生成的代码server.pb_skeleton.cc #include "xxxx.srpc.h" class ExamplePBServiceImpl : public ::example::ExamplePB::Service { public: void Echo(::example::EchoRequest *request, ::example::EchoResponse *response, srpc::RPCContext *ctx) override { /* 收到请求后要做的事情,并填好回复;*/ } }; int main() { // 1. 定义一个server SRPCServer server; // 2. 定义一个service,并加到server中 ExamplePBServiceImpl examplepb_impl; server.add_service(&examplepb_impl); // 3. 把server启动起来 server.start(PORT); // 4. server启动是异步的,需要暂时卡住主线程 getchar(); server.stop(); return 0; } ``` ### 3. srpc内部各网络协议 这里可以顺理成章地给梳理通信层和协议层的配合了。我们继续看看``xxxx.srpc.h``这个文件,里边定义了好几种协议: ```cpp // 由protobuf生成的代码 class Service : public srpc::RPCService; class SRPCClient : public srpc::SRPCClient; class SRPCHttpClient : public srpc::SRPCHttpClient; class BRPCClient : public srpc::BRPCClient; class TRPCClient : public srpc::TRPCClient; ``` 如果我们的IDL是thrift,那么会生成的xxxx.srpc.h就会有这些: ```cpp // 由thrift生成的代码 class Service : public srpc::RPCService; class SRPCClient : public srpc::SRPCClient; class SRPCHttpClient : public srpc::SRPCHttpClient; class ThriftClient : public srpc::ThriftClient; class ThriftHttpClient : public srpc::ThriftHttpClient; ``` 可以看到,大家都是只有一个service,因为**service只与IDL相关**。 然后对于不同的RPC协议(``SRPC``/``BRPC``/``Thrift``/``TRPC``),如果有配套的通信协议(``TCP``/``HTTP``)实现,那么就会有一个对应的client。 ### 4. service与server的N对M关系 我们刚才介绍过,**一个IDL定义一个servcie,一个网络通信协议定义一个server**。然后srpc内部各层是可以互相组合的,这意味着: * 一个协议的server内可以添加多个service(这个不新鲜,除了部分rpc不可以,大部份人都是可以的) * 用户实现一个service的函数,**可以加到多个不同协议的server中**,都可以run起来; * 一个service的函数也可以同时支持服务TCP和HTTP了,业务逻辑复用非常方便~ ```cpp int main() { SRPCServer server_srpc; SRPCHttpServer server_srpc_http; BRPCServer server_brpc; ThriftServer server_thrift; TRPCServer server_trpc; TRPCHttpServer server_trpc_http; ExampleServiceImpl impl_pb; // 使用pb作为接口的service AnotherThriftServiceImpl impl_thrift; // 使用thrift作为接口的service server_srpc.add_service(&impl_pb); // 只要协议本身支持这种IDL,就可以把这类service往里加 server_srpc.add_service(&impl_thrift); server_srpc_http.add_service(&impl_pb); // 还可以同时提供二进制协议和Http服务 server_srpc_http.add_service(&impl_thrift); server_brpc.add_service(&impl_pb); // brpc-std协议只支持了protobuf server_thrift.add_service(&impl_thrift); // thrift-binary协议只支持了thrift server_trpc.add_service(&impl_pb); // 只需要改一个字母,就可以方便兼容不同协议 server_trpc_http.add_service(&impl_pb); // 目前也是唯一开源的trpc协议实现 server_srpc.start(1412); server_srpc_http.start(8811); server_brpc.start(2020); server_trpc.start(2021); server_thrift.start(9090); server_trpc_http.start(8822); .... return 0; } ``` ## 五、同步、异步、半同步接口,及打通workflow任务流 srpc提供了同步、半同步、异步的接口,其中异步接口是用来打通workflow任务流的关键。 如果我们继续看生成代码,其中一个client为例,一个具体协议所定义出来的client所有的接口是这样的: ```cpp class SRPCClient : public srpc::SRPCClient { public: // 异步接口 void Echo(const EchoRequest *req, EchoDone done); // 同步接口 void Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx); // 半同步接口 WFFuture> async_Echo(const EchoRequest *req); public: SRPCClient(const char *host, unsigned short port); SRPCClient(const struct srpc::RPCClientParams *params); public: // 异步接口,用法与workflow其他task一致 srpc::SRPCClientTask *create_Echo_task(EchoDone done); }; ``` 这几种接口,相当于是变着花样地使用这四种东西:``request``,``response``,``context``和``done``。 ### 1. 异步接口 异步接口本身比较好理解,而且用户发请求时不用带着response的指针,也不用自己去保证它的生命周期了~刚才已经看过我们的回调函数``done``,是一个``std::function``,框架会在调用done的生成代码中创建一个response供用户使用。在skeleton代码中例子是这样的: ```cpp static void echo_done(::example::EchoResponse *response, srpc::RPCContext *context) { // 这个RPCContext可以拿到请求期间框架可以提供的状态 } ``` ### 2. 同步接口 同步接口没有生命周期的问题,直接让用户操作``response``和``context``。 ### 3. 半同步接口 半同步接口会返回一个**future**,这是Workflow内部封装的``WFFuture``,用法与``std::future``类似,需要等待的时候可以通过``get()``接口拿到结果,结果就是``response``和``context``。 ### 4. task接口 其实异步接口,内部也是通过task接口实现的。这里可以看到,我们通过一个client创建task的方式: ```cpp srpc::SRPCClientTask * task = SRPCClient::create_Echo_task(done); task->serialize_input(req); task->start(); ``` 这个方式,充分体现了workflow的**二级工厂概念**。srpc对于workflow来说就是几种特定的协议和一些额外的功能(比如用户IDL的序列化和压缩),所以在workflow实现一个带复杂功能的协议其实就是实现一个二级工厂。 拿到``task``之后,需要填``request``并且**start**起来,这部分也是和workflow的做法一模一样的。 这个接口最重要的意义是在于: **我们通过client二级工厂创建出来的rpc,可以串到任何workflow的任务流图中,自动随流图执行被调起。** ### 5. 利用Context打通workflow任务流,以及异步server的做法 我们在两个地方可以拿到``RPCContext``: * server实现service中的函数参数 * client的done的参数 这个RPCContext的功能非常多,主要是以下三种: 1. server想要给当前这个response设置一些会话级(或者说task级)的信息的接口,比如压缩类型等; 2. client拿到回复时,查看请求的状态和其他信息; 3. 拿到series; 拿series的接口如下: ```cpp SeriesWork *get_series() const; ``` 因此,我们在server的函数实现中,就可以往这个``series``后串别的``task``从而实现**异步server**了(server的reply时机为此series上所有任务跑完),也可以在我作为client的回调函数里继续往任务流后面串其他workflow任务。这应该是和现有的其他RPC相比最简单的异步server实现方式了。 再以刚才的service为例做异步server: ```cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override { auto *task = WFTaskFactory::create_timer_task(10, nullptr); ctx->get_series()->push_back(task); // 此RPC的回复时机是这个task执行完,与workflow一致 } }; ``` ### 六、其他可以借用workflow做的事情 以上可以看到,我们顺利地打通workflow,把rpc请求本身也都按照workflow的任务概念划分好了:``协议``、``请求``、``回复``。 那么workflow还有什么功能我们可以用得到的呢? 首先是**服务治理**。workflow的upstream是本进程内把一批机器绑定到一个域名下的upstream管理,自带多种方式的负载均衡和熔断恢复等机制,srpc的client都可以直接拿来用,一个client创建出来对应的是一个ip,或者一个带本地服务治理的集群。 **Client和task其他层级的配置**。比如workflow的各种超时都可以用上,workflow的网络请求本身有重试次数,rpc默认配为0,有需要的话用户可以自行配置。 **其他系统资源**。rpc只是网络相关,而workflow包含了如计算和异步文件IO等资源,这些在打通了任务流之后,都可以为srpc所用。 ## 七、 写在最后 随着srpc架构演进,使用到的泛型编程的方式有如下多种: srpc_multi 架构设计中有些其他细节比如离散内存的管理、内存反射、模块化插件的具体做法暂时还不在本篇wiki中。srpc项目目前在服务层面上做一些拓展和调研,也在研究相关的生态项目。从用户使用和落地情况的良好反馈来看,我们非常有信心继续推进workflow与srpc的生态,建立起和大家一起学习一起进步的合作方式。 srpc-0.10.1/src/000077500000000000000000000000001454502251400133315ustar00rootroot00000000000000srpc-0.10.1/src/CMakeLists.txt000066400000000000000000000103271454502251400160740ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) if (NOT "$ENV{LIBRARY_PATH}" STREQUAL "") string(REPLACE ":" ";" LIBRARY_PATH $ENV{LIBRARY_PATH}) set(CMAKE_SYSTEM_LIBRARY_PATH ${LIBRARY_PATH};${CMAKE_SYSTEM_LIBRARY_PATH}) endif () if (NOT "$ENV{CPLUS_INCLUDE_PATH}" STREQUAL "") string(REPLACE ":" ";" INCLUDE_PATH $ENV{CPLUS_INCLUDE_PATH}) set(CMAKE_SYSTEM_INCLUDE_PATH ${INCLUDE_PATH};${CMAKE_SYSTEM_INCLUDE_PATH}) endif () find_package(OpenSSL REQUIRED) set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "") if (WIN32) find_package(Protobuf REQUIRED CONFIG) else () find_package(Protobuf 3.5.0 REQUIRED) endif () if (WIN32) set (HAVE_SYS_UIO_H_01 false) else () set (HAVE_SYS_UIO_H_01 true) endif () if (WITH_VCPKG_TOOLCHAIN) find_package(Workflow REQUIRED CONFIG) else () # Try to find the package if workflow is nor installed, otherwise proceed with find_package() if (NOT WORKFLOW_INSTALLED) find_package(Workflow REQUIRED CONFIG HINTS ../workflow) endif () if (NOT SNAPPY_INSTALLED) set(SNAPPY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/snappy) configure_file(${SNAPPY_DIR}/cmake/config.h.in ${SNAPPY_DIR}/config.h) configure_file(${SNAPPY_DIR}/snappy-stubs-public.h.in ${SNAPPY_DIR}/snappy-stubs-public.h) else () find_package(Snappy 1.1.6 REQUIRED CONFIG) set(SNAPPY_LIB snappy) endif () if (LZ4_INSTALLED) set(LZ4_LIB lz4) endif () endif() include_directories( ${OPENSSL_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${Protobuf_INCLUDE_DIR} ${WORKFLOW_INCLUDE_DIR} ${INC_DIR}/srpc ) if (WIN32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP /wd4200") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4200 /Zc:__cplusplus /std:c++14") else () set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fPIC -pipe -std=gnu90") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -pipe -std=c++11 -fno-exceptions") endif () add_subdirectory(generator) set(SRC rpc_buffer.cc rpc_basic.cc rpc_global.cc ) add_subdirectory(module) add_subdirectory(var) add_subdirectory(thrift) add_subdirectory(compress) add_subdirectory(message) add_dependencies(module LINK_HEADERS) add_dependencies(var LINK_HEADERS) add_dependencies(thrift LINK_HEADERS) add_dependencies(compress LINK_HEADERS) add_dependencies(message LINK_HEADERS) if (NOT WIN32) add_subdirectory(http) add_dependencies(http LINK_HEADERS) endif () if (WIN32) add_library( ${PROJECT_NAME} STATIC ${SRC} $ $ $ $ $ ) add_dependencies(${PROJECT_NAME} LINK_HEADERS) target_compile_definitions( ${PROJECT_NAME} PRIVATE strdup=_strdup strcasecmp=_stricmp strncasecmp=_strnicmp ) install( TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel ) else () set(STATIC_LIB_NAME ${PROJECT_NAME}-static) set(SHARED_LIB_NAME ${PROJECT_NAME}-shared) get_filename_component(Protobuf_LIB_DIR ${Protobuf_LIBRARY} DIRECTORY) link_directories(${OPENSSL_LINK_DIR} ${WORKFLOW_LIB_DIR} ${Protobuf_LIB_DIR}) add_library( ${STATIC_LIB_NAME} STATIC ${SRC} $ $ $ $ $ $ ) add_library( ${SHARED_LIB_NAME} SHARED ${SRC} $ $ $ $ $ $ ) if (APPLE) target_link_libraries(${SHARED_LIB_NAME} OpenSSL::SSL OpenSSL::Crypto pthread ${Protobuf_LIBRARY} workflow z ${SNAPPY_LIB} ${LZ4_LIB}) else () target_link_libraries(${SHARED_LIB_NAME}) endif () set_target_properties(${STATIC_LIB_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) set_target_properties(${SHARED_LIB_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME} VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) add_dependencies(${STATIC_LIB_NAME} LINK_HEADERS) add_dependencies(${SHARED_LIB_NAME} LINK_HEADERS) install( TARGETS ${STATIC_LIB_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel ) install( TARGETS ${SHARED_LIB_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel ) endif () srpc-0.10.1/src/compress/000077500000000000000000000000001454502251400151645ustar00rootroot00000000000000srpc-0.10.1/src/compress/CMakeLists.txt000066400000000000000000000024141454502251400177250ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(compress) set(SRC rpc_compress.cc rpc_compress_snappy.cc ) set_property(SOURCE rpc_compress_snappy.cc APPEND PROPERTY COMPILE_OPTIONS "-fno-rtti") if (WITH_VCPKG_TOOLCHAIN) add_library(${PROJECT_NAME} OBJECT ${SRC}) target_link_libraries(${PROJECT_NAME} lz4 snappy) else () if (SNAPPY_INSTALLED) set(SNAPPY_LIB snappy) else () set(SNAPPY_SRC ../../third_party/snappy/config.h ../../third_party/snappy/snappy-internal.h ../../third_party/snappy/snappy-sinksource.cc ../../third_party/snappy/snappy-sinksource.h ../../third_party/snappy/snappy-stubs-internal.cc ../../third_party/snappy/snappy-stubs-internal.h ../../third_party/snappy/snappy-stubs-public.h ../../third_party/snappy/snappy.cc ../../third_party/snappy/snappy.h ) endif () if (LZ4_INSTALLED) set(LZ4_LIB lz4) else () set(LZ4_SRC ../../third_party/lz4/lib/lz4.c ../../third_party/lz4/lib/lz4.h ../../third_party/lz4/lib/lz4hc.h ../../third_party/lz4/lib/lz4hc.c ../../third_party/lz4/lib/lz4frame.h ../../third_party/lz4/lib/lz4frame.c ../../third_party/lz4/lib/xxhash.c ../../third_party/lz4/lib/xxhash.h ) endif () add_library( ${PROJECT_NAME} OBJECT ${SRC} ${SNAPPY_SRC} ${LZ4_SRC} ) endif() srpc-0.10.1/src/compress/rpc_compress.cc000066400000000000000000000045231454502251400201760ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "rpc_compress.h" #include "rpc_compress_gzip.h" #include "rpc_compress_snappy.h" #include "rpc_compress_lz4.h" namespace srpc { int RPCCompressor::add(RPCCompressType type) { if (type >= RPCCompressMax || type <= RPCCompressNone) return -2; int ret = 0; if (this->handler[type].compress && this->handler[type].decompress && this->handler[type].lease_size) { ret = 1; } //this->handler[type].type = type; switch (type) { case RPCCompressSnappy: this->handler[type].compress = SnappyManager::SnappyCompress; this->handler[type].decompress = SnappyManager::SnappyDecompress; this->handler[type].compress_iovec = SnappyManager::SnappyCompressIOVec; this->handler[type].decompress_iovec = SnappyManager::SnappyDecompressIOVec; this->handler[type].lease_size = SnappyManager::SnappyLeaseSize; break; case RPCCompressGzip: this->handler[type].compress = GzipCompress; this->handler[type].decompress = GzipDecompress; this->handler[type].compress_iovec = GzipCompressIOVec; this->handler[type].decompress_iovec = GzipDecompressIOVec; this->handler[type].lease_size = GzipLeaseSize; break; case RPCCompressZlib: this->handler[type].compress = ZlibCompress; this->handler[type].decompress = ZlibDecompress; this->handler[type].compress_iovec = ZlibCompressIOVec; this->handler[type].decompress_iovec = ZlibDecompressIOVec; this->handler[type].lease_size = ZlibLeaseSize; break; case RPCCompressLz4: this->handler[type].compress = LZ4Compress; this->handler[type].decompress = LZ4Decompress; this->handler[type].compress_iovec = LZ4CompressIOVec; this->handler[type].decompress_iovec = LZ4DecompressIOVec; this->handler[type].lease_size = LZ4LeaseSize; break; default: ret = -2; break; } return ret; } } // namespace srpc srpc-0.10.1/src/compress/rpc_compress.h000066400000000000000000000130271454502251400200370ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_COMPRESS_H__ #define __RPC_COMPRESS_H__ #include "rpc_basic.h" #include "rpc_options.h" namespace srpc { using CompressFunction = int (*)(const char*, size_t, char *, size_t); using DecompressFunction = int (*)(const char *, size_t, char *, size_t); using CompressIOVecFunction = int (*)(RPCBuffer *, RPCBuffer *); using DempressIOVecFunction = int (*)(RPCBuffer *, RPCBuffer *); using LeaseSizeFunction = int (*)(size_t); class CompressHandler { public: CompressHandler() { //this->type = RPCCompressNone; this->compress = nullptr; this->decompress = nullptr; this->compress_iovec = nullptr; this->decompress_iovec = nullptr; this->lease_size = nullptr; } //int type; CompressFunction compress; DecompressFunction decompress; CompressIOVecFunction compress_iovec; DempressIOVecFunction decompress_iovec; LeaseSizeFunction lease_size; }; class RPCCompressor { public: static RPCCompressor *get_instance() { static RPCCompressor kInstance; return &kInstance; } public: // parse message from compressed data // -1: error // -2, invalid compress type or decompress function int parse_from_compressed(const char *buf, size_t buflen, char *msg, size_t msglen, int type) const; int parse_from_compressed(RPCBuffer *src, RPCBuffer *dest, int type) const; // serialized to compressed data // -1: error // -2, invalid compress type or compress function int serialize_to_compressed(const char *msg, size_t msglen, char *buf, size_t buflen, int type) const; int serialize_to_compressed(RPCBuffer *src, RPCBuffer *dest, int type) const; /* * ret: >0: the theoretically lease size of compressed data * -1: error * -2, invalid compress type or lease_size function */ int lease_compressed_size(int type, size_t origin_size) const; /* * Add existed compress_type * ret: 0, success * 1, handler existed and update success * -2, invalid compress type */ int add(RPCCompressType type); /* * Add self-define compress algorithm * ret: 0, success * 1, handler existed and update success * -2, invalid compress type or compress/decompress/lease_size function */ int add_handler(int type, CompressHandler&& handler); const CompressHandler *find_handler(int type) const; // clear all the registed handler void clear(); private: RPCCompressor() { this->add(RPCCompressGzip); this->add(RPCCompressZlib); this->add(RPCCompressSnappy); this->add(RPCCompressLz4); } CompressHandler handler[RPCCompressMax]; }; //////// // inl inline int RPCCompressor::add_handler(int type, CompressHandler&& handler) { if (type >= RPCCompressMax || type <= RPCCompressNone) return -2; if (!handler.compress || !handler.decompress || !handler.lease_size) return -2; int ret = 0; if (this->handler[type].compress && this->handler[type].decompress && this->handler[type].lease_size) { ret = 1; } this->handler[type] = std::move(handler); return ret; } inline const CompressHandler *RPCCompressor::find_handler(int type) const { if (type >= RPCCompressMax || type <= RPCCompressNone) { return NULL; } if (!this->handler[type].compress || !this->handler[type].decompress || !this->handler[type].lease_size) { return NULL; } return &this->handler[type]; } inline int RPCCompressor::parse_from_compressed(const char *buf, size_t buflen, char *msg, size_t msglen, int type) const { if (type >= RPCCompressMax || type <= RPCCompressNone || !this->handler[type].decompress) { return -2; } return this->handler[type].decompress(buf, buflen, msg, msglen); } inline int RPCCompressor::parse_from_compressed(RPCBuffer *src, RPCBuffer *dest, int type) const { if (type >= RPCCompressMax || type <= RPCCompressNone || !this->handler[type].decompress_iovec) { return -2; } return this->handler[type].decompress_iovec(src, dest); } inline int RPCCompressor::serialize_to_compressed(const char *msg, size_t msglen, char *buf, size_t buflen, int type) const { if (type >= RPCCompressMax || type <= RPCCompressNone || !this->handler[type].compress) { return -2; } return this->handler[type].compress(msg, msglen, buf, buflen); } inline int RPCCompressor::serialize_to_compressed(RPCBuffer *src, RPCBuffer *dest, int type) const { if (type >= RPCCompressMax || type <= RPCCompressNone || !this->handler[type].compress) { return -2; } return this->handler[type].compress_iovec(src, dest); } inline int RPCCompressor::lease_compressed_size(int type, size_t origin_size) const { if (type >= RPCCompressMax || type <= RPCCompressNone || !this->handler[type].lease_size) { return -2; } return this->handler[type].lease_size(origin_size); } inline void RPCCompressor::clear() { for (int i = 0; i < RPCCompressMax; i++) { //this->handler[i].type = RPCCompressNone; this->handler[i].compress = nullptr; this->handler[i].decompress = nullptr; this->handler[i].lease_size = nullptr; } } } // namespace srpc #endif srpc-0.10.1/src/compress/rpc_compress_gzip.h000066400000000000000000000217731454502251400210770ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_COMPRESS_GZIP_H__ #define __RPC_COMPRESS_GZIP_H__ //#include //#include #include #include "rpc_basic.h" //#define COMPRESS_LEVEL 6 //#define ZLIB_LEASE_HEADER 100 #define GzipDecompress CommonDecompress #define ZlibDecompress CommonDecompress #define GzipDecompressIOVec CommonDecompressIOVec #define ZlibDecompressIOVec CommonDecompressIOVec namespace srpc { static constexpr int GZIP_LEASE_HEADER = 20; static constexpr int WINDOW_BITS = 15; static constexpr int OPTION_FORMAT_ZLIB = 0; static constexpr int OPTION_FORMAT_GZIP = 16; static constexpr int OPTION_FORMAT_AUTO = 32; /* static int protobuf_stream_compress(const ProtobufIDLMessage* msg, char *buf, size_t buflen, int compress_type) { google::protobuf::io::GzipOutputStream::Options options; switch(compress_type) { case RPCCompressGzip: options.format = google::protobuf::io::GzipOutputStream::GZIP; break; case RPCCompressZlib: options.format = google::protobuf::io::GzipOutputStream::ZLIB; break; default: return -1; } options.compression_level = COMPRESS_LEVEL; google::protobuf::io::ArrayOutputStream arr(buf, buflen); google::protobuf::io::GzipOutputStream gzipOutputStream(&arr, options); if (!msg->SerializeToZeroCopyStream(&gzipOutputStream)) return -1; gzipOutputStream.Flush(); gzipOutputStream.Close(); return arr.ByteCount(); } static int protobuf_stream_decompress(const char *buf, size_t buflen, ProtobufIDLMessage* msg, size_t msglen, int compress_type) { google::protobuf::io::GzipInputStream::Format format; switch(compress_type) { case RPCCompressGzip: format = google::protobuf::io::GzipInputStream::GZIP; break; case RPCCompressZlib: format = google::protobuf::io::GzipInputStream::ZLIB; break; default: return -1; } google::protobuf::io::ArrayInputStream arr(buf, buflen); google::protobuf::io::GzipInputStream gzipInputStream(&arr, format); if (!msg->ParseFromZeroCopyStream(&gzipInputStream)) return -1; return msg->ByteSizeLong(); } */ static int CommonCompress(const char *msg, size_t msglen, char *buf, size_t buflen, int option_format) { if (!msg) return 0; z_stream c_stream; c_stream.zalloc = (alloc_func)0; c_stream.zfree = (free_func)0; c_stream.opaque = (voidpf)0; if(deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, WINDOW_BITS | option_format, 8, Z_DEFAULT_STRATEGY) != Z_OK) { return -1; } c_stream.next_in = (Bytef *)msg; c_stream.avail_in = msglen; c_stream.next_out = (Bytef *)buf; c_stream.avail_out = (uInt)buflen; while (c_stream.avail_in != 0 && c_stream.total_in < buflen) { if (deflate(&c_stream, Z_NO_FLUSH) != Z_OK) return -1; } if (c_stream.avail_in != 0) return c_stream.avail_in; for (;;) { int err = deflate(&c_stream, Z_FINISH); if(err == Z_STREAM_END) break; if(err != Z_OK) return -1; } if (deflateEnd(&c_stream) != Z_OK) return -1; return c_stream.total_out; } /* * compress serialized msg into buf. * ret: -1: failed * >0: byte count of compressed data */ static inline int GzipCompress(const char *msg, size_t msglen, char *buf, size_t buflen) { return CommonCompress(msg, msglen, buf, buflen, OPTION_FORMAT_GZIP); } /* * compress serialized msg into buf. * ret: -1: failed * >0: byte count of compressed data */ static inline int ZlibCompress(const char *msg, size_t msglen, char *buf, size_t buflen) { return CommonCompress(msg, msglen, buf, buflen, OPTION_FORMAT_ZLIB); } static constexpr unsigned char dummy_head[2] = { 0xB + 0x8 * 0x10, (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, }; /* * decompress and parse buf into msg * ret: -1: failed * >0: byte count of compressed data */ static int CommonDecompress(const char *buf, size_t buflen, char *msg, size_t msglen) { int err; z_stream d_stream = {0}; /* decompression stream */ d_stream.zalloc = (alloc_func)0; d_stream.zfree = (free_func)0; d_stream.opaque = (voidpf)0; d_stream.next_in = (Bytef *)buf; d_stream.avail_in = 0; d_stream.next_out = (Bytef *)msg; if (inflateInit2(&d_stream, WINDOW_BITS | OPTION_FORMAT_AUTO) != Z_OK) return -1; while (d_stream.total_out < msglen && d_stream.total_in < buflen) { d_stream.avail_in = d_stream.avail_out = (uInt)msglen; err = inflate(&d_stream, Z_NO_FLUSH); if(err == Z_STREAM_END) break; if (err != Z_OK) { if (err != Z_DATA_ERROR) return -1; d_stream.next_in = (Bytef*) dummy_head; d_stream.avail_in = sizeof (dummy_head); if (inflate(&d_stream, Z_NO_FLUSH) != Z_OK) return -1; } } if (inflateEnd(&d_stream) != Z_OK) return -1; return d_stream.total_out; } static int CommonCompressIOVec(RPCBuffer *src, RPCBuffer *dst, int option_format) { z_stream c_stream; int err; size_t total_alloc = 0; const void *in; void *out; size_t buflen = src->size(); size_t out_len = buflen; c_stream.zalloc = (alloc_func)0; c_stream.zfree = (free_func)0; c_stream.opaque = (voidpf)0; if (deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, WINDOW_BITS | option_format, 8, Z_DEFAULT_STRATEGY) != Z_OK) { return -1; } c_stream.avail_in = 0; c_stream.avail_out = 0; while (c_stream.total_in < buflen) { if (c_stream.avail_in == 0) { if ((c_stream.avail_in = (uInt)src->fetch(&in)) == 0) return -1; c_stream.next_in = static_cast(const_cast(in)); } if (c_stream.avail_out == 0) { if (dst->acquire(&out, &out_len) == false) return -1; total_alloc += out_len; c_stream.next_out = static_cast(out); c_stream.avail_out = (uInt)out_len; } if (deflate(&c_stream, Z_NO_FLUSH) != Z_OK) return -1; } // if (c_stream.avail_in != 0) // return c_stream.avail_in; for (;;) { if (c_stream.avail_out == 0) { if (dst->acquire(&out, &out_len) == false) return -1; total_alloc += out_len; c_stream.next_out = static_cast(out); c_stream.avail_out = (uInt)out_len; } err = deflate(&c_stream, Z_FINISH); if(err == Z_STREAM_END) break; if(err != Z_OK) return -1; } if (deflateEnd(&c_stream) != Z_OK) return -1; dst->backup(total_alloc - c_stream.total_out); return c_stream.total_out; } /* * compress serialized RPCBuffer src(Source) into RPCBuffer dst(Sink). * ret: -1: failed * >0: byte count of compressed data */ static int GzipCompressIOVec(RPCBuffer *src, RPCBuffer *dst) { return CommonCompressIOVec(src, dst, OPTION_FORMAT_GZIP); } /* * compress serialized RPCBuffer src(Source) into RPCBuffer dst(Sink). * ret: -1: failed * >0: byte count of compressed data */ static int ZlibCompressIOVec(RPCBuffer *src, RPCBuffer *dst) { return CommonCompressIOVec(src, dst, OPTION_FORMAT_ZLIB); } /* * decompress RPCBuffer src(Source) into RPCBuffer dst(Sink). * ret: -1: failed * >0: byte count of compressed data */ static int CommonDecompressIOVec(RPCBuffer *src, RPCBuffer *dst) { int err; z_stream d_stream = { 0 }; /* decompression stream */ size_t total_alloc = 0; const void *in; void *out; size_t buflen = src->size(); size_t out_len = buflen; d_stream.zalloc = (alloc_func)0; d_stream.zfree = (free_func)0; d_stream.opaque = (voidpf)0; if (inflateInit2(&d_stream, WINDOW_BITS | OPTION_FORMAT_AUTO) != Z_OK) return -1; d_stream.avail_in = 0; d_stream.avail_out = 0; while (d_stream.total_in < buflen) { if (d_stream.avail_in == 0) { if ((d_stream.avail_in = (uInt)src->fetch(&in)) == 0) return -1; d_stream.next_in = static_cast(const_cast(in)); } if (d_stream.avail_out == 0) { if (dst->acquire(&out, &out_len) == false) return -1; total_alloc += out_len; d_stream.next_out = static_cast(out); d_stream.avail_out = (uInt)out_len; } err = inflate(&d_stream, Z_NO_FLUSH); if (err == Z_STREAM_END) break; if (err != Z_OK) { if (err != Z_DATA_ERROR) return -1; d_stream.next_in = (Bytef*) dummy_head; d_stream.avail_in = sizeof (dummy_head); if (inflate(&d_stream, Z_NO_FLUSH) != Z_OK) return -1; } } if (inflateEnd(&d_stream) != Z_OK) return -1; dst->backup(total_alloc - d_stream.total_out); return d_stream.total_out; } /* * lease size after compress origin_size data */ static int GzipLeaseSize(size_t origin_size) { return (int)(origin_size + 5 * (origin_size / 16383 + 1) + GZIP_LEASE_HEADER); } static int ZlibLeaseSize(size_t origin_size) { return compressBound((uLong)origin_size); } } // end namespace srpc #endif srpc-0.10.1/src/compress/rpc_compress_lz4.h000066400000000000000000000142331454502251400206300ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_COMPRESS_LZ4_H__ #define __RPC_COMPRESS_LZ4_H__ #include "lz4.h" #include "lz4frame.h" #include "rpc_basic.h" namespace srpc { //#define IN_CHUNK_SIZE (16*1024) static constexpr LZ4F_preferences_t kPrefs = { { LZ4F_max256KB, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame, 0 /* unknown content size */, 0 /* no dictID */ , LZ4F_noBlockChecksum }, 0, /* compression level; 0 == default */ 0, /* autoflush */ 0, /* favor decompression speed */ { 0, 0, 0 }, /* reserved, must be set to 0 */ }; /* static size_t get_block_size(const LZ4F_frameInfo_t* info) { switch (info->blockSizeID) { case LZ4F_default: case LZ4F_max64KB: return 1 << 16; case LZ4F_max256KB: return 1 << 18; case LZ4F_max1MB: return 1 << 20; case LZ4F_max4MB: return 1 << 22; default: printf("Impossible with expected frame specification (<=v1.6.1)\n"); } return 0; } */ /* * compress serialized msg into buf. * ret: -1: failed * >0: byte count of compressed data */ static int LZ4Compress(const char* msg, size_t msglen, char *buf, size_t buflen) { int ret = LZ4_compress_default(msg, buf, (int)msglen, (int)buflen); if (ret < 0) ret = -1; return ret; } /* * decompress and parse buf into msg * ret: -1: failed * >0: byte count of compressed data */ static int LZ4Decompress(const char *buf, size_t buflen, char *msg, size_t msglen) { int ret = LZ4_decompress_safe(buf, msg, (int)buflen, (int)msglen); if (ret < 0) return -1; return ret; } /* * compress RPCBuffer src into RPCBuffer dst * ret: -1: failed * >0: byte count of compressed data */ static int LZ4CompressIOVec(RPCBuffer *src, RPCBuffer *dst) { const void *in_buf; size_t in_len; void *out_buf; size_t out_len; size_t total_out; size_t header_size; size_t compressed_size; LZ4F_cctx *ctx = NULL; size_t const ctx_status = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); if (LZ4F_isError(ctx_status)) { LZ4F_freeCompressionContext(ctx); return -1; } out_len = LZ4F_HEADER_SIZE_MAX; if (dst->acquire(&out_buf, &out_len) == false) { LZ4F_freeCompressionContext(ctx); return -1; } // write frame header header_size = LZ4F_compressBegin(ctx, out_buf, out_len, &kPrefs); if (LZ4F_isError(header_size)) { LZ4F_freeCompressionContext(ctx); return -1; } dst->backup(out_len - header_size); total_out = header_size; //int count = 0; // write every in_buf while ((in_len = src->fetch(&in_buf)) != 0) { //count++; out_len = LZ4F_compressBound(in_len, &kPrefs); if (dst->acquire(&out_buf, &out_len) == false) { LZ4F_freeCompressionContext(ctx); return -1; } compressed_size = LZ4F_compressUpdate(ctx, out_buf, out_len, in_buf, in_len, NULL); if (LZ4F_isError(compressed_size)) { LZ4F_freeCompressionContext(ctx); return -1; } dst->backup(out_len - compressed_size); total_out += compressed_size; } // flush whatever remains within internal buffers out_len = LZ4F_compressBound(0, &kPrefs); if (dst->acquire(&out_buf, &out_len) == false) { LZ4F_freeCompressionContext(ctx); return -1; } compressed_size = LZ4F_compressEnd(ctx, out_buf, out_len, NULL); if (LZ4F_isError(compressed_size)) { LZ4F_freeCompressionContext(ctx); return -1; } dst->backup(out_len - compressed_size); total_out += compressed_size; LZ4F_freeCompressionContext(ctx); return (int)total_out; } /* * decompress RPCBuffer src into RPCBuffer dst * ret: -1: failed * >0: byte count of compressed data */ static int LZ4DecompressIOVec(RPCBuffer *src, RPCBuffer *dst) { const void *in_buf; size_t in_len; void *out_buf; size_t out_len; size_t consumed_len; size_t decompressed_size; size_t total_out = 0; LZ4F_dctx *dctx = NULL; size_t const dctx_status = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION); if (LZ4F_isError(dctx_status)) { LZ4F_freeDecompressionContext(dctx); return -1; } // get framed info LZ4F_frameInfo_t info; memset(&info, 0, sizeof (info)); in_len = src->fetch(&in_buf); consumed_len = in_len; size_t const frame_info_ret = LZ4F_getFrameInfo(dctx, &info, in_buf, &consumed_len); if (LZ4F_isError(frame_info_ret)) { LZ4F_freeDecompressionContext(dctx); return -1; } /* size_t const dest_capacity = get_block_size(&info); if (dest_capacity == 0) { LZ4F_freeDecompressionContext(ctx); return -1; } fprintf(stderr, "get_block_size=%d\n", dest_capacity); */ size_t ret = 1; bool first_chunk = true; const char *start; const char *end; while (ret != 0) { if (first_chunk) { in_len = in_len - consumed_len; in_buf = (const char *)in_buf + consumed_len; first_chunk = false; } else { in_len = src->fetch(&in_buf); } out_len = in_len; start = (const char *)in_buf; end = (const char *)in_buf + in_len; consumed_len = 0; while (start != end && ret != 0) { if (dst->acquire(&out_buf, &out_len) == false) { LZ4F_freeDecompressionContext(dctx); return -1; } decompressed_size = out_len; consumed_len = end - start; ret = LZ4F_decompress(dctx, out_buf, &decompressed_size, start, &consumed_len, NULL); if (LZ4F_isError(ret)) { LZ4F_freeDecompressionContext(dctx); return -1; } start = start + consumed_len; dst->backup(out_len - decompressed_size); total_out += decompressed_size; } if (start != end) { LZ4F_freeDecompressionContext(dctx); return -1; } } LZ4F_freeDecompressionContext(dctx); return (int)total_out; } /* * lease size after compress origin_size data */ static int LZ4LeaseSize(size_t origin_size) { return LZ4_compressBound((int)origin_size); } } // end namespace srpc #endif srpc-0.10.1/src/compress/rpc_compress_snappy.cc000066400000000000000000000052141454502251400215660ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "snappy.h" #include "snappy-sinksource.h" #include "rpc_compress_snappy.h" namespace srpc { class RPCSnappySink : public snappy::Sink { public: RPCSnappySink(RPCBuffer *buf) { this->buf = buf; } void Append(const char *bytes, size_t n) override { this->buf->append(bytes, n, BUFFER_MODE_COPY); } size_t size() const { return this->buf->size(); } private: RPCBuffer *buf; }; class RPCSnappySource : public snappy::Source { public: RPCSnappySource(RPCBuffer *buf) { this->buf = buf; this->buf_size = this->buf->size(); this->pos = 0; } size_t Available() const override { return this->buf_size - this->pos; } const char *Peek(size_t *len) override { const void *pos; *len = this->buf->peek(&pos); return (const char *)pos; } void Skip(size_t n) override { this->pos += this->buf->seek(n); } private: RPCBuffer *buf; size_t buf_size; size_t pos; }; int SnappyManager::SnappyCompress(const char *msg, size_t msglen, char *buf, size_t buflen) { size_t compressed_len = buflen; snappy::RawCompress(msg, msglen, buf, &compressed_len); if (compressed_len > buflen) return -1; return (int)compressed_len; } int SnappyManager::SnappyDecompress(const char *buf, size_t buflen, char *msg, size_t msglen) { size_t origin_len; if (snappy::GetUncompressedLength(buf, buflen, &origin_len) && origin_len <= msglen && snappy::RawUncompress(buf, buflen, msg)) { return (int)origin_len; } return -1; } int SnappyManager::SnappyCompressIOVec(RPCBuffer *src, RPCBuffer *dst) { RPCSnappySource source(src); RPCSnappySink sink(dst); return (int)snappy::Compress(&source, &sink); } int SnappyManager::SnappyDecompressIOVec(RPCBuffer *src, RPCBuffer *dst) { RPCSnappySource source(src); RPCSnappySink sink(dst); // if (snappy::IsValidCompressed(&source)) // if (snappy::GetUncompressedLength(&source, &origin_len)) return (int)snappy::Uncompress(&source, &sink) ? sink.size() : -1; } int SnappyManager::SnappyLeaseSize(size_t origin_size) { return (int)snappy::MaxCompressedLength(origin_size); } } // end namespace srpc srpc-0.10.1/src/compress/rpc_compress_snappy.h000066400000000000000000000031631454502251400214310ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_COMPRESS_SNAPPY_H__ #define __RPC_COMPRESS_SNAPPY_H__ #include "rpc_buffer.h" namespace srpc { class SnappyManager { public: /* * compress serialized msg into buf. * ret: -1: failed * >0: byte count of compressed data */ static int SnappyCompress(const char *msg, size_t msglen, char *buf, size_t buflen); /* * decompress and parse buf into msg * ret: -1: failed * >0: byte count of compressed data */ static int SnappyDecompress(const char *buf, size_t buflen, char *msg, size_t msglen); /* * compress RPCBuffer src(Source) into RPCBuffer dst(Sink) * ret: -1: failed * >0: byte count of compressed data */ static int SnappyCompressIOVec(RPCBuffer *src, RPCBuffer *dst); /* * decompress RPCBuffer src(Source) into RPCBuffer dst(Sink) * ret: -1: failed * >0: byte count of compressed data */ static int SnappyDecompressIOVec(RPCBuffer *src, RPCBuffer *dst); /* * lease size after compress origin_size data */ static int SnappyLeaseSize(size_t origin_size); }; } // end namespace srpc #endif srpc-0.10.1/src/generator/000077500000000000000000000000001454502251400153175ustar00rootroot00000000000000srpc-0.10.1/src/generator/CMakeLists.txt000066400000000000000000000006301454502251400200560ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(srpc_generator) set(SRC compiler.cc generator.cc parser.cc ) add_executable(${PROJECT_NAME} ${SRC}) if (WIN32) target_compile_definitions( ${PROJECT_NAME} PRIVATE getcwd=_getcwd strdup=_strdup strcasecmp=_stricmp strncasecmp=_strnicmp ) endif () install( TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT devel ) srpc-0.10.1/src/generator/compiler.cc000066400000000000000000000140771454502251400174510ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #ifdef _WIN32 #include #define MAXPATHLEN 4096 #else #include #include #include #endif #include "generator.h" const char *SRPC_VERSION = "0.10.1"; /* LQ - prototype to determine if the file type is thrift */ enum { TYPE_UNKNOWN = -1, TYPE_PROTOBUF = 0, TYPE_THRIFT = 1, }; static int check_file_idl_type(const char *filename); static int parse_origin(int argc, const char *argv[], struct GeneratorParams& params, int& idl_type); #ifndef _WIN32 static int parse_getopt(int argc, char * const *argv, struct GeneratorParams& params, int& idl_type); #endif static int parse_prefix_dir(std::string& file, std::string& dir); static int get_idl_type(const char *argv); static bool is_root(std::string& file); const char *SRPC_GENERATOR_USAGE = "\ Usage:\n\ %s [protobuf|thrift] \n\n\ Available options(linux):\n\ -f, --idl_file : IDL file name. If multiple files are imported, specify the top one. Will parse recursively.\n\ -o, --output_dir : Output directory.\n\ -i, --input_dir : Specify the directory in which to search for imports.\n\ -s, --skip_skeleton : Skip generating skeleton file. (default: generate)\n\ -v, --version : Show version.\n\ -h, --help : Show usage.\n"; int main(int argc, const char *argv[]) { int idl_type = TYPE_UNKNOWN; struct GeneratorParams params; #ifndef _WIN32 if (parse_origin(argc, argv, params, idl_type) == 0) { if (parse_getopt(argc, (char * const *)argv, params, idl_type) != 0) return 0; } #else parse_origin(argc, argv, params, idl_type); #endif if (params.out_dir == NULL || params.idl_file.empty()) { fprintf(stderr, SRPC_GENERATOR_USAGE, argv[0]); return 0; } if (idl_type == TYPE_UNKNOWN) idl_type = check_file_idl_type(params.idl_file.data()); if (is_root(params.idl_file)) { if (params.input_dir.empty()) parse_prefix_dir(params.idl_file, params.input_dir); else { fprintf(stderr, "ERROR: input_dir must encompasses the idl_file %s\n", params.idl_file.c_str()); return 0; } } else { if (params.input_dir.empty() || is_root(params.input_dir) == false) { char current_dir[MAXPATHLEN] = {}; getcwd(current_dir, MAXPATHLEN); current_dir[strlen(current_dir)] = '/'; if (params.input_dir.empty()) params.input_dir = current_dir; else params.input_dir = current_dir + params.input_dir; } } Generator gen(idl_type == TYPE_THRIFT ? true : false); fprintf(stdout, "[Generator Begin]\n"); gen.generate(params); fprintf(stdout, "[Generator Done]\n"); return 0; } int parse_origin(int argc, const char *argv[], struct GeneratorParams& params, int& idl_type) { int idl_file_id = 1; for (int i = 1; i < argc && i < 4 && argv[i][0] != '-'; i++) { if (i == 1) // parse [protobuf|thrift] { idl_type = get_idl_type(argv[1]); if (idl_type != TYPE_UNKNOWN) { idl_file_id++; continue; } } if (i == idl_file_id) // parse { int tmp_type = check_file_idl_type(argv[i]); if (tmp_type == TYPE_UNKNOWN) { fprintf(stderr, "ERROR: Invalid IDL file \"%s\".\n", argv[i]); return -1; } if (idl_type == TYPE_UNKNOWN) idl_type = tmp_type; params.idl_file = argv[idl_file_id]; } else // parse params.out_dir = argv[i]; } return 0; } #ifndef _WIN32 int parse_getopt(int argc, char * const *argv, struct GeneratorParams& params, int& idl_type) { int ch; static struct option longopts[] = { { "version", no_argument, NULL, 'v'}, { "idl_file", required_argument, NULL, 'f'}, { "output_dir", required_argument, NULL, 'o'}, { "input_dir", required_argument, NULL, 'i'}, { "skip_skeleton", no_argument, NULL, 's'}, { "help", no_argument, NULL, 'h'} }; while ((ch = getopt_long(argc, argv, "vf:o:i:sh", longopts, NULL)) != -1) { switch (ch) { case 'v': fprintf(stderr, "srpc_generator version %s\n", SRPC_VERSION); return 1; case 'f': if (!params.idl_file.empty()) { fprintf(stderr, "Error: -i is duplicate with idl_file %s\n", params.idl_file.c_str()); return -1; } params.idl_file = optarg; break; case 'o': if (params.out_dir != NULL) { fprintf(stderr, "Error: -o is duplicate with output_dir %s\n", params.out_dir); return -1; } params.out_dir = optarg; break; case 'i': params.input_dir = optarg; if (params.input_dir.at(params.input_dir.length() - 1) != '/') params.input_dir += '/'; break; case 's': params.generate_skeleton = false; break; case 'h': break; default: return 0; } } return 0; } #endif int get_idl_type(const char *type) { if (strcasecmp(type, "protobuf") == 0) return TYPE_PROTOBUF; if (strcasecmp(type, "thrift") == 0) return TYPE_THRIFT; return TYPE_UNKNOWN; } // idl file will not start with - int check_file_idl_type(const char *filename) { size_t len = strlen(filename); if (len > 6 && strcmp(filename + len - 6, ".proto") == 0) return TYPE_PROTOBUF; else if (len > 7 && strcmp(filename + len - 7, ".thrift") == 0) return TYPE_THRIFT; return TYPE_UNKNOWN; } int parse_prefix_dir(std::string& file, std::string& dir) { auto pos = file.find_last_of('/'); if (pos == std::string::npos) return -1; dir = file.substr(0, pos + 1); file = file.substr(pos + 1); return 0; } bool is_root(std::string& file) { if (file[0] == '/' || file[1] == ':') return true; return false; } srpc-0.10.1/src/generator/descriptor.h000066400000000000000000000127461454502251400176600ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_DESCRIPTOR_H__ #define __RPC_DESCRIPTOR_H__ #include #include #include struct rpc_param { std::string type_name; std::string var_name; std::string default_value; int16_t field_id; int8_t data_type; int8_t required_state; }; struct rpc_descriptor { std::string method_name; std::string request_name; std::string response_name; std::vector req_params; std::vector resp_params; }; struct struct_descriptor { std::vector params; }; struct typedef_descriptor { std::string old_type_name; std::string new_type_name; }; struct Descriptor { std::string block_name; std::string block_type; //service extends std::string extends_type; // serivce std::vector rpcs; // struct struct_descriptor st; // enum std::vector enum_lines; }; //class ServiceDescrpitor : public Descriptor //{}; struct idl_info { std::string file_name; std::string input_dir; std::string absolute_file_path; std::string file_name_prefix; std::vector package_name; std::list desc_list; std::list include_list; std::list typedef_list; //typedef name -> real cpptype std::map typedef_mapping; }; class SGenUtil { public: static const char *skip_string(const char *cursor) { while (*cursor && *cursor != '\"') { if (*cursor == '\\') { cursor++; if (!*cursor) break; } cursor++; } return cursor; } static std::vector split_skip_string(const std::string& str, char sep) { std::vector res; /* Can not use '\"' as the seperator. */ if (sep == '\"') return res; const char *cursor = str.c_str(); const char *start = cursor; while (*cursor) { if (*cursor == '\"') { cursor = SGenUtil::skip_string(++cursor); if (!*cursor) break; } else if (*cursor == sep) { if (start < cursor) res.emplace_back(start, cursor); start = cursor + 1; } cursor++; } if (start < cursor) res.emplace_back(start, cursor); return res; } /* static std::vector split_filter_empty(const std::string& str, char sep) { std::vector res; std::string::const_iterator start = str.begin(); std::string::const_iterator end = str.end(); std::string::const_iterator next = find(start, end, sep); while (next != end) { if (start < next) res.emplace_back(start, next); start = next + 1; next = find(start, end, sep); } if (start < next) res.emplace_back(start, next); return res; } */ static std::vector split_by_space(const std::string& str) { std::vector elems; size_t offset = 0; size_t start = 0; size_t end = 0; while(offset < str.size()) { start = SGenUtil::find_next_nonspace(str,offset); if (start == std::string::npos) break; end = SGenUtil::find_next_space(str,start+1); if (end == std::string::npos) { elems.push_back(str.substr(start)); break; } elems.push_back(str.substr(start,end-start)); offset = end+1; } return elems; } static std::string strip(const std::string& str) { std::string res; if (!str.empty()) { const char *st = str.c_str(); const char *ed = st + str.size() - 1; while (st <= ed) { if (!isspace(*st)) break; st++; } while (ed >= st) { if (!isspace(*ed)) break; ed--; } if (ed >= st) res.assign(st, ed - st + 1); } return res; } static std::string lstrip(const std::string& str) { std::string res; if (!str.empty()) { const char *st = str.c_str(); const char *ed = st + str.size(); while (st < ed) { if (!isspace(*st)) break; st++; } if (st < ed) res.assign(st, ed - st); } return res; } static bool start_with(const std::string &str, const std::string prefix) { size_t prefix_len = prefix.size(); if (str.size() < prefix_len) return false; for (size_t i = 0; i < prefix_len; i++) { if (str[i] != prefix[i]) return false; } return true; } static void replace(std::string& str, const std::string& before, const std::string& after) { for (size_t pos = 0; pos < str.size(); pos += after.length()) { pos = str.find(before, pos); if (pos != std::string::npos) str.replace(pos, before.length(), after); else break; } } static size_t find_next_nonspace(const std::string& str, size_t pos) { for(size_t i = pos; i < str.size(); i++) { if (!std::isspace(str[i])) return i; } return std::string::npos; } static size_t find_next_space(const std::string& str, size_t pos) { for(size_t i = pos; i < str.size(); i++) { if (std::isspace(str[i])) return i; } return std::string::npos; } static std::set *get_enum_set() { static std::set kInstance; return &kInstance; } }; #endif srpc-0.10.1/src/generator/generator.cc000066400000000000000000000301201454502251400176100ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "generator.h" bool Generator::init_file_names(const std::string& idl_file, const char *out_dir) { if (idl_file.size() <= 5) return false; const char *p; if (this->is_thrift) { p = idl_file.c_str() + idl_file.size() - 7; if (strncmp(p, ".thrift", 7) != 0) return false; } else { p = idl_file.c_str() + idl_file.size() - 6; if (strncmp(p, ".proto", 6) != 0) return false; } p = idl_file.c_str() + idl_file.size() - 1; while (p > idl_file.c_str()) { if (*p == '/') break; p--; } if (*p == '/') p++; std::string str = p; auto pos = str.find("."); if (pos == std::string::npos) return false; this->prefix = str.substr(0, pos); str = out_dir; if (str[str.length() - 1] != '/') str.append("/"); this->out_dir = str; this->srpc_file = str; this->srpc_file.append(this->prefix); this->srpc_file.append(this->suffix); this->srpc_file.append("h"); this->thrift_type_file = str; this->thrift_type_file.append(this->prefix); this->thrift_type_file.append(this->thrift_suffix); this->thrift_type_file.append("h"); fprintf(stdout, "[Generator] generate srpc files: %s %s\n", this->srpc_file.c_str(), this->is_thrift ? this->thrift_type_file.c_str() : ""); return true; } bool Generator::generate(struct GeneratorParams& params) { this->info.input_dir = params.input_dir; if (this->parser.parse(params.idl_file, this->info) == false) { fprintf(stderr, "[Generator Error] parse failed.\n"); return false; } if (this->generate_header(this->info, params) == false) { fprintf(stderr, "[Generator Error] generate failed.\n"); return false; } if (params.generate_skeleton == true) this->generate_skeleton(this->info.file_name); return true; } bool Generator::generate_header(idl_info& cur_info, struct GeneratorParams& params) { for (auto& sub_info : cur_info.include_list) { fprintf(stdout, "[Generator] auto generate include file [%s]\n", sub_info.absolute_file_path.c_str()); if (!this->generate_header(sub_info, params)) return false; } // for protobuf: if no [rpc], don`t need to generate xxx.srpc.h if (this->init_file_names(cur_info.absolute_file_path, params.out_dir) == false) { fprintf(stderr, "[Generator Error] init file name failed. %s %s\n", cur_info.absolute_file_path.c_str(), params.out_dir); return false; } // will generate skeleton file only once if (this->is_thrift) { //[prefix].thrift.h if (!this->generate_thrift_type_file(cur_info)) { fprintf(stderr, "[Generator Error] generate thrift type file failed.\n"); return false; } } for (const auto& desc : this->info.desc_list) { if (desc.block_type == "service") { //has_service = true; if (!this->generate_srpc_file(cur_info)) // [prefix].srpc.h { fprintf(stderr, "[Generator Error] generate srpc file failed.\n"); return false; } break; } } fprintf(stdout, "[Generator Done]\n"); return true; } void Generator::generate_skeleton(const std::string& idl_file) { const char *p; if (this->is_thrift) { p = idl_file.c_str() + idl_file.size() - 7; if (strncmp(p, ".thrift", 7) != 0) return; } else { p = idl_file.c_str() + idl_file.size() - 6; if (strncmp(p, ".proto", 6) != 0) return; } p = idl_file.c_str() + idl_file.size() - 1; while (p > idl_file.c_str()) { if (*p == '/') break; p--; } if (*p == '/') p++; std::string str = p; auto pos = str.find("."); if (pos == std::string::npos) return; std::string idl_file_name = str.substr(0, pos); // server.skeleton.cc this->generate_server_cpp_file(this->info, idl_file_name); // client.skeleton.cc this->generate_client_cpp_file(this->info, idl_file_name); fprintf(stdout, "[Generator] generate server files: %s, client files: %s\n", this->server_cpp_file.c_str(), this->client_cpp_file.c_str()); return; } void Generator::thrift_replace_include(const idl_info& cur_info, std::vector& params) { for (auto& param : params) { if (param.data_type == srpc::TDT_LIST || param.data_type == srpc::TDT_MAP || param.data_type == srpc::TDT_SET) { for (const auto& desc : cur_info.desc_list) { if (desc.block_type == "enum") SGenUtil::replace(param.type_name, desc.block_name, "int32_t"); } for (const auto& sub_info : cur_info.include_list) { for (const auto& desc : sub_info.desc_list) { if (desc.block_type == "enum") SGenUtil::replace(param.type_name, sub_info.file_name_prefix + desc.block_name, "int32_t"); } } } auto pos = param.type_name.find('.'); if (pos != std::string::npos) { for (const auto& sub_info : cur_info.include_list) { if (sub_info.package_name.empty()) continue; //printf("????%s %s\n", sub_info.file_name_prefix.c_str(), sub_info.package_name.c_str()); SGenUtil::replace(param.type_name, sub_info.file_name_prefix, "::" + join_package_names(sub_info.package_name) + "::"); } } } } bool Generator::generate_thrift_type_file(idl_info& cur_info) { if (!this->printer.open(this->thrift_type_file)) { fprintf(stderr, "[Generator Error] can't write to thirft_type_file: %s.\n", this->thrift_type_file.c_str()); return false; } this->printer.print_thrift_include(cur_info); this->printer.print_thrift_struct_declaration(cur_info); this->printer.print_thrift_typedef(cur_info); for (auto& desc : cur_info.desc_list) { if (desc.block_type == "service") { this->printer.print_service_namespace(desc.block_name); for (auto& rpc : desc.rpcs) { this->thrift_replace_include(cur_info, rpc.req_params); this->printer.print_rpc_thrift_struct_class(rpc.request_name, rpc.req_params,cur_info); this->thrift_replace_include(cur_info, rpc.resp_params); this->printer.print_rpc_thrift_struct_class(rpc.response_name, rpc.resp_params,cur_info); } this->printer.print_service_namespace_end(desc.block_name); } else if (desc.block_type == "struct" || desc.block_type == "union") { this->thrift_replace_include(cur_info, desc.st.params); this->printer.print_rpc_thrift_struct_class(desc.block_name, desc.st.params,cur_info); } else if (desc.block_type == "enum") { this->printer.print_thrift_include_enum(desc.block_name, desc.enum_lines); } } this->printer.print_end(cur_info.package_name); this->printer.close(); return true; } bool Generator::generate_srpc_file(const idl_info& cur_info) { if (!this->printer.open(this->srpc_file)) { fprintf(stderr, "[Generator Error] can't write to srpc file: %s.\n", this->srpc_file.c_str()); return false; } this->printer.print_srpc_include(this->prefix, cur_info.package_name); std::vector rpc_list; std::string package; if (this->is_thrift) { rpc_list.push_back("SRPC"); rpc_list.push_back("SRPCHttp"); rpc_list.push_back("Thrift"); rpc_list.push_back("ThriftHttp"); } else { rpc_list.push_back("SRPC"); rpc_list.push_back("SRPCHttp"); rpc_list.push_back("BRPC"); rpc_list.push_back("TRPC"); rpc_list.push_back("TRPCHttp"); } for (const std::string& p : cur_info.package_name) { package.append(p); package.push_back('.'); } for (const auto& desc : cur_info.desc_list) { if (desc.block_type != "service") continue; this->printer.print_service_namespace(desc.block_name); this->printer.print_server_comment(); if (this->is_thrift) this->printer.print_server_class_thrift(desc.block_name, desc.rpcs); else this->printer.print_server_class(desc.block_name, desc.rpcs); this->printer.print_client_comment(); for (const auto& rpc : desc.rpcs) { this->printer.print_client_define_done(rpc.method_name, rpc.response_name); } this->printer.print_empty_line(); for (const auto& type : rpc_list) this->printer.print_client_class(type, desc.block_name, desc.rpcs); this->printer.print_implement_comments(); this->printer.print_server_constructor(package + desc.block_name, desc.rpcs); if (this->is_thrift) this->printer.print_server_methods_thrift(desc.block_name, desc.rpcs); for (const auto& type : rpc_list) { this->printer.print_client_constructor(type, desc.block_name, cur_info.package_name); this->printer.print_client_methods(type, desc.block_name, desc.rpcs, cur_info.package_name); this->printer.print_client_create_task(type, desc.block_name, desc.rpcs, cur_info.package_name); } this->printer.print_service_namespace_end(desc.block_name); } this->printer.print_end(cur_info.package_name); this->printer.close(); return true; } bool Generator::generate_server_cpp_file(const idl_info& cur_info, const std::string& idl_file_name) { this->server_cpp_file = this->out_dir; this->server_cpp_file.append("server"); this->server_cpp_file.append(this->is_thrift ? ".thrift_skeleton." : ".pb_skeleton."); this->server_cpp_file.append("cc"); if (this->printer.open(this->server_cpp_file) == false) return false; this->printer.print_server_file_include(idl_file_name); for (const auto& desc : cur_info.desc_list) { if (desc.block_type != "service") continue; this->printer.print_server_class_impl(cur_info.package_name, desc.block_name); for (const auto& rpc : desc.rpcs) { this->printer.print_server_impl_method(cur_info.package_name, desc.block_name, rpc.method_name, rpc.request_name, rpc.response_name); } this->printer.print_server_class_impl_end(); } this->printer.print_server_main_begin(); this->printer.print_server_main_address(); for (const auto& desc : cur_info.desc_list) { if (desc.block_type != "service") continue; this->printer.print_server_main_method(desc.block_name); } this->printer.print_server_main_end(); this->printer.print_server_main_return(); this->printer.close(); return true; } bool Generator::generate_client_cpp_file(const idl_info& cur_info, const std::string& idl_file_name) { this->client_cpp_file = this->out_dir; this->client_cpp_file.append("client"); this->client_cpp_file.append(this->is_thrift ? ".thrift_skeleton." : ".pb_skeleton."); this->client_cpp_file.append("cc"); if (this->printer.open(this->client_cpp_file) == false) return false; this->printer.print_client_file_include(idl_file_name); for (const auto& desc : cur_info.desc_list) { if (desc.block_type != "service") continue; for (const auto& rpc : desc.rpcs) { this->printer.print_client_done_method(cur_info.package_name, desc.block_name, rpc.method_name, rpc.response_name); } } this->printer.print_client_main_begin(); this->printer.print_client_main_address(); int id = 0; for (const auto& desc : cur_info.desc_list) { if (desc.block_type != "service") continue; std::string suffix; if (id != 0) suffix = std::to_string(id); id++; if (this->is_thrift) this->printer.print_client_main_service("Thrift", cur_info.package_name, desc.block_name, suffix); else this->printer.print_client_main_service("SRPC", cur_info.package_name, desc.block_name, suffix); auto rpc_it = desc.rpcs.cbegin(); if (rpc_it != desc.rpcs.cend()) { this->printer.print_client_main_method_call(cur_info.package_name, desc.block_name, rpc_it->method_name, rpc_it->request_name, suffix); rpc_it++; //if (rpc_it == desc.rpcs.end()) // rpc_it = desc.rpcs.begin(); //this->printer.print_client_main_create_task(print_client_main_create_task, rpc_it->method_name, // rpc_it->request_name); } } this->printer.print_client_main_end(); this->printer.close(); return true; } srpc-0.10.1/src/generator/generator.h000066400000000000000000000043411454502251400174600ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_GENERATOR_H__ #define __RPC_GENERATOR_H__ #include #include #include #include #include #include #include #include #include "printer.h" #include "parser.h" struct GeneratorParams { const char *out_dir; bool generate_skeleton; std::string idl_file; std::string input_dir; GeneratorParams() : out_dir(NULL), generate_skeleton(true) { } }; class Generator { public: Generator(bool is_thrift): parser(is_thrift), printer(is_thrift) { this->suffix = ".srpc."; this->thrift_suffix = ".thrift."; this->skeleton_suffix = ".skeleton."; this->is_thrift = is_thrift; } bool generate(struct GeneratorParams& params); protected: virtual bool generate_server_cpp_file(const idl_info& cur_info, const std::string& idle_file_name); virtual bool generate_client_cpp_file(const idl_info& cur_info, const std::string& idle_file_name); std::string server_cpp_file; std::string client_cpp_file; private: bool generate_header(idl_info& cur_info, struct GeneratorParams& params); void generate_skeleton(const std::string& idl_file); bool generate_srpc_file(const idl_info& cur_info); bool generate_thrift_type_file(idl_info& cur_info); void thrift_replace_include(const idl_info& cur_info, std::vector& params); bool init_file_names(const std::string& idl_file, const char *out_dir); Parser parser; Printer printer; idl_info info; std::string out_dir; std::string suffix; std::string thrift_suffix; std::string skeleton_suffix; std::string prefix; std::string srpc_file; std::string thrift_type_file; bool is_thrift; }; #endif srpc-0.10.1/src/generator/parser.cc000066400000000000000000001031321454502251400171220ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "parser.h" #include "thrift/rpc_thrift_enum.h" #define LINE_LENGTH_MAX 2048 static std::string gen_param_var(const std::string& type_name, size_t& cur, idl_info& info); std::string type_prefix_to_namespace(const std::string& type_name, idl_info& info); Descriptor *search_cur_file_descriptor(idl_info& info, const std::string& block_type, const std::string& block_name); Descriptor *search_include_file_descriptor(idl_info& info, const std::string include_file_name, const std::string& block_type, const std::string& block_name); idl_info *search_include_file(idl_info& info, const std::string file_name); idl_info *search_namespace(idl_info& info, const std::string name_space); void parse_thrift_type_name(const std::string& type_name, std::string& type_prefix, std::string& real_type_name); std::vector parse_thrift_variable(const std::string& str, char sep); bool Parser::parse(const std::string& proto_file, idl_info& info) { std::string idl_file = info.input_dir + proto_file; auto pos = idl_file.find_last_of('/'); if (pos == std::string::npos) info.file_name = idl_file; else info.file_name = idl_file.substr(pos + 1); pos = info.file_name.find_last_of('.'); if (pos == std::string::npos) info.file_name_prefix = info.file_name + "."; else info.file_name_prefix = info.file_name.substr(0, pos + 1); info.absolute_file_path = idl_file; FILE *in = fopen(idl_file.c_str(), "r"); if (!in) { fprintf(stderr, "[Parser] proto file: [%s] not exists.\n", idl_file.c_str()); return false; } fprintf(stdout, "proto file: [%s]\n", idl_file.c_str()); char line_buffer[LINE_LENGTH_MAX]; std::string file_path; std::string block; int state = PARSER_ST_OUTSIDE_BLOCK; bool flag_append; int stack_count = 0; std::string block_name; std::string block_type; std::string extends_type; std::string old_type_name; std::string new_type_name; std::string thrift_type_prefix; std::string thrift_type_name; bool succ; while (fgets(line_buffer, LINE_LENGTH_MAX, in)) { std::string line = SGenUtil::lstrip(line_buffer); if (line.empty()) continue; this->check_single_comments(line); if (line.empty()) continue; this->check_single_comments(line); if ((state & PARSER_ST_COMMENT_MASK) == PARSER_ST_INSIDE_COMMENT) { if (this->check_multi_comments_end(line)) { state -= PARSER_ST_INSIDE_COMMENT; // state |= PARSER_ST_OUTSIDE_COMMENT_MASK; if (line.empty()) continue; } else continue; } else if (this->check_multi_comments_begin(line)) { state = (state & PARSER_ST_OUTSIDE_COMMENT_MASK) + PARSER_ST_INSIDE_COMMENT; if (line.empty()) continue; } if (this->is_thrift == false) { int rpc_option = this->parse_pb_rpc_option(line); if (rpc_option == 1) continue; else if (rpc_option == 2) { fprintf(stderr, "[Parser ERROR] %s must not set " "\"option cc_generic_services = true\" for srpc.\n", idl_file.c_str()); return false; } } if (this->parse_package_name(line, info.package_name) == true) continue; if (this->parse_include_file(line, file_path) == true) { if (!this->is_thrift) { if (file_path.rfind("google/protobuf/", 0) == 0) continue; } info.include_list.resize(info.include_list.size() + 1); info.include_list.back().input_dir = info.input_dir; succ = this->parse(file_path, info.include_list.back()); if (!succ) { info.include_list.pop_back(); fprintf(stderr, "[Parser ERROR] failed to parse " "\" %s \" in \" %s \"\n", file_path.c_str(), idl_file.c_str()); return false; } continue; } if (this->is_thrift && this->parse_thrift_typedef(line, old_type_name, new_type_name, info) == true) { info.typedef_list.push_back(typedef_descriptor{old_type_name, new_type_name}); continue; } flag_append = false; if ((state & PARSER_ST_BLOCK_MASK) == PARSER_ST_OUTSIDE_BLOCK) { if (this->check_block_begin(in, line) == true) { state |= PARSER_ST_INSIDE_BLOCK; // stack_count++; block.clear(); block = line; if (this->parse_block_name(line, block_type, block_name, extends_type) == false) { fprintf(stderr, "Invalid proto file line: %s\n", line.c_str()); fprintf(stderr, "Failed to parse block name or value.\n"); return false; } } } else flag_append = true; // not else, because { }; can in the same line if ((state & PARSER_ST_BLOCK_MASK) == PARSER_ST_INSIDE_BLOCK) { if (flag_append == true) block.append(line); if (this->check_block_begin(line) == true) ++stack_count; if (this->check_block_end(line) == true) { --stack_count; if (stack_count == 0) { state = PARSER_ST_OUTSIDE_BLOCK; //state &= PARSER_ST_OUTSIDE_BLOCK_MASK; Descriptor desc; if (block_type == "service") { if (this->is_thrift) succ = this->parse_service_thrift(info.file_name_prefix, block, desc, info); else succ = this->parse_service_pb(block, desc); if (!succ) return false; desc.block_type = block_type; desc.block_name = block_name; desc.extends_type = extends_type; if (desc.extends_type != "" && this->is_thrift) { Descriptor *extended_desc = NULL; parse_thrift_type_name(desc.extends_type, thrift_type_prefix, thrift_type_name); if (thrift_type_prefix == "") extended_desc = search_cur_file_descriptor(info, "service", thrift_type_name); else extended_desc = search_include_file_descriptor(info, thrift_type_prefix+".thrift", "service", thrift_type_name); if (!extended_desc) { fprintf(stderr,"service %s extends type %s not found\n", desc.block_name.c_str(), desc.extends_type.c_str()); } else { desc.rpcs.insert(desc.rpcs.begin(), extended_desc->rpcs.begin(), extended_desc->rpcs.end()); } } info.desc_list.emplace_back(std::move(desc)); } else if ((block_type == "struct" || block_type == "union") && this->is_thrift) { succ = this->parse_struct_thrift(info.file_name_prefix, block, desc, info); if (!succ) return false; desc.block_type = block_type; desc.block_name = block_name; info.desc_list.emplace_back(std::move(desc)); } else if (block_type == "exception" && this->is_thrift) { succ = this->parse_struct_thrift(info.file_name_prefix, block, desc, info); if (!succ) return false; desc.block_type = "struct"; desc.block_name = block_name; info.desc_list.emplace_back(std::move(desc)); } else if (block_type == "enum" && this->is_thrift) { succ = this->parse_enum_thrift(block, desc); if (!succ) return false; SGenUtil::get_enum_set()->insert(info.file_name_prefix + block_name); desc.block_type = block_type; desc.block_name = block_name; info.desc_list.emplace_back(std::move(desc)); } } } } } build_typedef_mapping(info); fclose(in); fprintf(stdout, "finish parsing proto file: [%s]\n", idl_file.c_str()); return true; } std::string Parser::find_typedef_mapping_type(std::string& type_name, size_t& cur, idl_info& info) { size_t st = cur; cur = SGenUtil::find_next_nonspace(type_name,cur); for (; cur < type_name.size(); cur++) { if (type_name[cur] == ',' || type_name[cur] == '>' || type_name[cur] == '<') break; } auto idl_type = SGenUtil::strip(type_name.substr(st, cur - st)); if (info.typedef_mapping.find(idl_type) != info.typedef_mapping.end()) return info.typedef_mapping[idl_type]; if (idl_type == "bool" || idl_type == "int8_t" || idl_type == "int16_t" || idl_type == "int32_t" || idl_type == "int64_t" || idl_type == "uint64_t" || idl_type == "double" || idl_type == "std::string") { return idl_type; } else if (idl_type == "std::map" && cur < type_name.size() && type_name[cur] == '<') { auto key_type = find_typedef_mapping_type(type_name, ++cur, info); auto val_type = find_typedef_mapping_type(type_name, ++cur, info); ++cur; return "std::map<" + key_type + ", " + val_type + ">"; } else if (idl_type == "std::set" && cur < type_name.size() && type_name[cur] == '<') { auto val_type = find_typedef_mapping_type(type_name, ++cur, info); ++cur; return "std::set<" + val_type + ">"; } else if (idl_type == "std::vector" && cur < type_name.size() && type_name[cur] == '<') { auto val_type = find_typedef_mapping_type(type_name, ++cur, info); ++cur; return "std::vector<" + val_type + ">"; } size_t pos = idl_type.find("::",0); std::string real_type_name; std::string type_namespace; if (pos == std::string::npos) real_type_name = idl_type; else { real_type_name = idl_type.substr(pos+2); type_namespace = idl_type.substr(0,pos); } for (auto& include : info.include_list) { if ( (type_namespace != "" && include.package_name.size() > 0 && include.package_name[0] == type_namespace) || (type_namespace == "" && include.package_name.size() == 0) ) { for (auto& t : include.typedef_list) { if (real_type_name == t.new_type_name) { size_t offset = 0; include.typedef_mapping[real_type_name] = find_typedef_mapping_type(t.old_type_name, offset, include); return include.typedef_mapping[real_type_name]; } } } } for (auto& t : info.typedef_list) { if (real_type_name == t.new_type_name) { size_t offset = 0; info.typedef_mapping[real_type_name] = find_typedef_mapping_type(t.old_type_name, offset, info); return info.typedef_mapping[real_type_name]; } } return idl_type; } void Parser::build_typedef_mapping(idl_info& info) { for (auto &include:info.include_list) { for (auto &t:include.typedef_list) { if (include.typedef_mapping.find(t.new_type_name) != include.typedef_mapping.end()) continue; size_t cur = 0; include.typedef_mapping[t.new_type_name] = find_typedef_mapping_type(t.old_type_name, cur, include); } } for (auto &t:info.typedef_list) { if (info.typedef_mapping.find(t.new_type_name) != info.typedef_mapping.end()) continue; size_t cur = 0; info.typedef_mapping[t.new_type_name] = find_typedef_mapping_type(t.old_type_name, cur, info); } } // check / * and cut the first available line bool Parser::check_multi_comments_begin(std::string& line) { size_t pos = line.find("/*"); if (pos == std::string::npos) return false; line = line.substr(0, pos); return true; } // check * / and cut the rest available line bool Parser::check_multi_comments_end(std::string& line) { size_t pos = line.find("*/"); if (pos == std::string::npos) return false; pos += 2; while (line[pos] == ' ') pos++; line = line.substr(pos, line.length() - 1 - pos); return true; } // [ret] 0: no rpc option; 1: rpc_option = false; 2: rpc_option = true; int Parser::parse_pb_rpc_option(const std::string& line) { size_t pos = line.find("option"); if (pos == std::string::npos) return 0; pos = line.find("cc_generic_services", pos); if (pos == std::string::npos) return 0; pos = line.find("true", pos); if (pos == std::string::npos) return 1; return 2; } bool Parser::parse_thrift_typedef(const std::string& line, std::string& old_type_name, std::string& new_type_name, idl_info&info) { std::vector elems = SGenUtil::split_by_space(line); if (elems.size() >= 3 && elems[0] == "typedef") { size_t offset = 0; std::string idl_type; for (size_t i = 1; i < elems.size()-1; i++) idl_type.append(elems[i]); old_type_name = gen_param_var(idl_type,offset,info); new_type_name = elems[elems.size()-1]; return true; } return false; } bool Parser::parse_include_file(const std::string& line, std::string& file_name) { std::string include_prefix = (this->is_thrift ? "include" : "import"); size_t pos = line.find(include_prefix); if (pos != 0) return false; auto st = line.find_first_of('\"'); auto ed = line.find_last_of('\"'); if (st == std::string::npos || ed == std::string::npos || st == ed) return false; file_name = line.substr(st + 1, ed - st - 1); // fprintf(stderr, "parse_include_file(%s,%s)\n", line.c_str(), file_name.c_str()); return true; } bool Parser::parse_package_name(const std::string& line, std::vector& package_name) { std::string package_prefix = (this->is_thrift ? "namespace" : "package"); size_t pos = line.find(package_prefix); if (pos != 0) return false; pos += package_prefix.length(); while (pos < line.length() && isspace(line[pos])) pos++; if (this->is_thrift) { pos = line.find("cpp", pos); if (pos == std::string::npos) return false; pos += 3; while (pos < line.length() && isspace(line[pos])) pos++; } size_t begin; if (this->is_thrift) { begin = line.find_last_of('/'); if (begin == std::string::npos) begin = pos; else begin++; } else { begin = pos; } while (pos < line.length() && !isspace(line[pos]) && line[pos] != ';') { pos++; } std::string names = line.substr(begin, pos - begin); pos = names.find('.'); while (pos != (size_t)-1) { package_name.push_back(names.substr(0, pos)); names = names.substr(pos + 1, names.length() - pos); pos = names.find('.'); } package_name.push_back(names.substr(0, pos)); return true; } static std::string gen_param_var(const std::string& type_name, size_t& cur, idl_info& info) { size_t st = cur; cur = SGenUtil::find_next_nonspace(type_name,cur); for (; cur < type_name.size(); cur++) { if (type_name[cur] == ',' || type_name[cur] == '>' || type_name[cur] == '<') break; } auto idl_type = SGenUtil::strip(type_name.substr(st, cur - st)); if (idl_type == "bool") return "bool"; else if (idl_type == "i8" || idl_type == "byte") return "int8_t"; else if (idl_type == "i16") return "int16_t"; else if (idl_type == "i32") return "int32_t"; else if (idl_type == "i64") return "int64_t"; else if (idl_type == "u64") return "uint64_t"; else if (idl_type == "double") return "double"; else if (idl_type == "string" || idl_type == "binary") return "std::string"; else if (idl_type == "map" && cur < type_name.size() && type_name[cur] == '<') { auto key_type = gen_param_var(type_name, ++cur, info); auto val_type = gen_param_var(type_name, ++cur, info); ++cur; return "std::map<" + key_type + ", " + val_type + ">"; } else if (idl_type == "set" && cur < type_name.size() && type_name[cur] == '<') { auto val_type = gen_param_var(type_name, ++cur, info); ++cur; return "std::set<" + val_type + ">"; } else if (idl_type == "list" && cur < type_name.size() && type_name[cur] == '<') { auto val_type = gen_param_var(type_name, ++cur, info); ++cur; return "std::vector<" + val_type + ">"; } return type_prefix_to_namespace(idl_type, info); } static void fill_rpc_param_type(const std::string& file_name_prefix, const std::string idl_type, rpc_param& param, idl_info& info) { if (idl_type == "bool") { param.data_type = srpc::TDT_BOOL; param.type_name = "bool"; } else if (idl_type == "i8" || idl_type == "byte") { param.data_type = srpc::TDT_I08; param.type_name = "int8_t"; } else if (idl_type == "i16") { param.data_type = srpc::TDT_I16; param.type_name = "int16_t"; } else if (idl_type == "i32") { param.data_type = srpc::TDT_I32; param.type_name = "int32_t"; } else if (idl_type == "i64") { param.data_type = srpc::TDT_I64; param.type_name = "int64_t"; } else if (idl_type == "u64") { param.data_type = srpc::TDT_U64; param.type_name = "uint64_t"; } else if (idl_type == "double") { param.data_type = srpc::TDT_DOUBLE; param.type_name = "double"; } else if (idl_type == "string" || idl_type == "binary") { param.data_type = srpc::TDT_STRING; param.type_name = "std::string"; } else if (SGenUtil::start_with(idl_type, "list")) { size_t cur = 0; param.data_type = srpc::TDT_LIST; param.type_name = gen_param_var(idl_type, cur, info); } else if (SGenUtil::start_with(idl_type, "map")) { size_t cur = 0; param.data_type = srpc::TDT_MAP; param.type_name = gen_param_var(idl_type, cur, info); } else if (SGenUtil::start_with(idl_type, "set")) { size_t cur = 0; param.data_type = srpc::TDT_SET; param.type_name = gen_param_var(idl_type, cur, info); } else { auto *enum_set = SGenUtil::get_enum_set(); if (enum_set->count(idl_type) > 0 || enum_set->count(file_name_prefix + idl_type) > 0) { //enum param.data_type = srpc::TDT_I32; param.type_name = "int32_t"; } else { //struct param.data_type = srpc::TDT_STRUCT; param.type_name = type_prefix_to_namespace(idl_type,info); } } } std::vector Parser::split_thrift_rpc(const std::string& str) { std::vector res; std::string::const_iterator start = str.begin(); std::string::const_iterator parameter_end = str.end(); std::string::const_iterator throws_end = str.end(); while (1) { parameter_end = find(start,str.end(), ')'); if (parameter_end == str.end()) { res.emplace_back(start,parameter_end); break; } std::string::const_iterator offset = find_if(parameter_end + 1, str.end(), [](char c){return !std::isspace(c);}); if (offset == str.end()) { res.emplace_back(start,parameter_end); break; } if (str.compare(offset-str.begin(), 6, "throws") == 0) { throws_end = find(offset,str.end(), ')'); res.emplace_back(start,throws_end); start = throws_end + 1; } else { res.emplace_back(start,parameter_end); start = parameter_end + 1; } } return res; } bool Parser::parse_rpc_param_thrift(const std::string& file_name_prefix, const std::string& str, std::vector& params, idl_info& info) { size_t left_b = 0; rpc_param param; std::string idl_type; if (left_b + 1 < str.size()) { auto bb = parse_thrift_variable(str.substr(left_b + 1), ','); for (const auto& ele : bb) { auto single_line = SGenUtil::split_skip_string(ele, '\n'); if (single_line.size() != 1) continue; auto filedparam = SGenUtil::split_skip_string(single_line[0], ':'); if (filedparam.size() != 2) continue; auto typevar = parse_thrift_variable(filedparam[1], ' '); if (typevar.size() != 2) continue; param.var_name = typevar[1]; param.required_state = srpc::THRIFT_STRUCT_FIELD_REQUIRED; param.field_id = atoi(SGenUtil::strip(filedparam[0]).c_str()); idl_type = SGenUtil::strip(typevar[0]); fill_rpc_param_type(file_name_prefix, idl_type, param, info); params.push_back(param); } } return true; } bool Parser::parse_service_thrift(const std::string& file_name_prefix, const std::string& block, Descriptor& desc, idl_info& info) { rpc_descriptor rpc_desc; auto st = block.find_first_of('{'); auto ed = block.find_last_of('}'); if (st == std::string::npos || ed == std::string::npos || st == ed) return false; std::string valid_block = block.substr(st + 1, ed - st - 1); auto arr = split_thrift_rpc(valid_block); for (const auto& ele : arr) { auto line = SGenUtil::strip(ele); size_t i = 0; for (; i < line.size(); i++) { if (line[i] != ';' && line[i] != ',' && !isspace(line[i])) break; } if (i == line.size()) continue; line = line.substr(i); if (line.empty()) continue; auto left_b = line.find('('); if (left_b == std::string::npos) continue; auto aa = SGenUtil::split_skip_string(line.substr(0, left_b), ' '); if (aa.size() != 2) continue; rpc_desc.method_name = SGenUtil::strip(aa[1]); if (rpc_desc.method_name.empty()) continue; rpc_desc.request_name = rpc_desc.method_name + "Request"; rpc_desc.response_name = rpc_desc.method_name + "Response"; auto idl_type = SGenUtil::strip(aa[0]); rpc_param param; param.var_name = "result"; param.required_state = srpc::THRIFT_STRUCT_FIELD_REQUIRED; param.field_id = 0; if (idl_type != "void") { fill_rpc_param_type(file_name_prefix, idl_type, param, info); rpc_desc.resp_params.push_back(param); } auto right_b = line.find(')',left_b); if (right_b == std::string::npos) { parse_rpc_param_thrift(file_name_prefix, line.substr(left_b), rpc_desc.req_params,info); } else { parse_rpc_param_thrift(file_name_prefix, line.substr(left_b, right_b - left_b), rpc_desc.req_params, info); auto throws_start = line.find("throws", right_b); if (throws_start != std::string::npos) { left_b = line.find('(', throws_start + 6); parse_rpc_param_thrift(file_name_prefix, line.substr(left_b), rpc_desc.resp_params, info); } } fprintf(stdout, "Successfully parse method:%s req:%s resp:%s\n", rpc_desc.method_name.c_str(), rpc_desc.request_name.c_str(), rpc_desc.response_name.c_str()); desc.rpcs.emplace_back(std::move(rpc_desc)); } return true; } void Parser::check_single_comments(std::string& line) { size_t pos = line.find("#"); if (pos == std::string::npos) pos = line.find("//"); if (pos != std::string::npos) { line.resize(pos); return; } if (pos == std::string::npos) pos = line.find("/*"); size_t end; while (pos != std::string::npos) { end = line.find("*/", pos + 2); if (end == std::string::npos) return; // multi_comments can handle this, except for 'a/*\n' line.erase(pos, end - pos + 2); pos = line.find("/*", pos); } } bool Parser::parse_enum_thrift(const std::string& block, Descriptor& desc) { rpc_param param; auto st = block.find_first_of('{'); auto ed = block.find_last_of('}'); if (st == std::string::npos || ed == std::string::npos || st == ed) return false; std::string valid_block = block.substr(st + 1, ed - st - 1); for (size_t i = 0; i < valid_block.size(); i++) if (valid_block[i] == '\n' || valid_block[i] == '\r' || valid_block[i] == ',') valid_block[i] = ';'; auto arr = SGenUtil::split_skip_string(valid_block, ';'); for (const auto& ele : arr) { auto line = SGenUtil::strip(ele); if (line.empty()) continue; if (line.back() == ';' || line.back() == ',') line.resize(line.size() - 1); desc.enum_lines.push_back(line); } return true; } bool Parser::parse_struct_thrift(const std::string& file_name_prefix, const std::string& block, Descriptor& desc, idl_info& info) { auto st = block.find_first_of('{'); auto ed = block.find_last_of('}'); if (st == std::string::npos || ed == std::string::npos || st == ed) return false; std::string valid_block = block.substr(st + 1, ed - st - 1); int deep = 0; bool in_string = false; for (size_t i = 0; i < valid_block.size(); i++) { int c = valid_block[i]; if (c == '\n' || c == '\r') { valid_block[i] = ';'; in_string = false; } else if (c == ',' && !in_string && deep == 0) valid_block[i] = ';'; else if (c == '<' && !in_string) deep++; else if (c == '>' && !in_string) deep--; else if (c == '[' && !in_string) valid_block[i] = '{'; else if (c == ']' && !in_string) valid_block[i] = '}'; else if (c == '\"') in_string = !in_string; else if (in_string && c == '\\') { if (i + 1 < valid_block.size()) i++; } } auto arr = SGenUtil::split_skip_string(valid_block, ';'); for (const auto& ele : arr) { auto line = SGenUtil::strip(ele); if (line.empty()) continue; auto aabb = SGenUtil::split_skip_string(line, ':'); if (aabb.size() != 2) continue; auto aa = SGenUtil::strip(aabb[0]); if (aa.empty()) continue; rpc_param param; param.field_id = atoi(aa.c_str()); auto bb = SGenUtil::strip(aabb[1]); auto bbcc = SGenUtil::split_skip_string(bb, '='); if (bbcc.size() == 2) { bb = SGenUtil::strip(bbcc[0]); param.default_value = SGenUtil::strip(bbcc[1]); } auto idx1 = std::string::npos;//bb.find_first_of(' '); auto idx2 = std::string::npos;//bb.find_last_of(' '); for (size_t i = 0; i < bb.size(); i++) { if (isspace(bb[i])) { idx1 = i; break; } } for (size_t i = 0; i < bb.size(); i++) { if (isspace(bb[bb.size() - i - 1])) { idx2 = bb.size() - i - 1; break; } } if (idx1 == std::string::npos || idx2 == std::string::npos) continue; std::string b1 = SGenUtil::strip(bb.substr(0, idx1)); std::string b2, b3; if (idx1 == idx2 || (b1 != "required" && b1 != "optional")) { param.required_state = srpc::THRIFT_STRUCT_FIELD_DEFAULT; b1 = "default"; b2 = SGenUtil::strip(bb.substr(0, idx2)); b3 = SGenUtil::strip(bb.substr(idx2 + 1)); } else { param.required_state = (b1 == "required") ? srpc::THRIFT_STRUCT_FIELD_REQUIRED : srpc::THRIFT_STRUCT_FIELD_OPTIONAL; b2 = SGenUtil::strip(bb.substr(idx1 + 1, idx2 - idx1 - 1)); b3 = SGenUtil::strip(bb.substr(idx2 + 1)); } if (b1.empty() || b2.empty() || b3.empty()) continue; param.var_name = b3; fill_rpc_param_type(file_name_prefix, b2, param, info); fprintf(stdout, "Successfully parse struct param: %s %s %s\n", param.type_name.c_str(), param.var_name.c_str(), param.default_value.c_str()); desc.st.params.push_back(param); } return true; } bool Parser::parse_service_pb(const std::string& block, Descriptor& desc) { size_t pos = block.find("{"); if (pos == std::string::npos) return false; while (pos < block.length()) { rpc_descriptor rpc_desc; pos = block.find("rpc", pos); if (pos == std::string::npos) { if (desc.rpcs.size() == 0) { fprintf(stderr, "no \"rpc\" in service block [%s]\n", block.c_str()); return false; } else { return true; } } pos += strlen("rpc"); while (block[pos] == ' ' && pos < block.length()) pos++; if (pos == block.length()) return false; size_t method_name_pos = block.find("(", pos); if (method_name_pos == std::string::npos) { fprintf(stderr, "no method_name in service block [%s]\n", block.c_str()); return false; } rpc_desc.method_name = std::string(&block[pos], &block[method_name_pos]); rpc_desc.method_name.erase(rpc_desc.method_name.find_last_not_of(" ") + 1); pos = method_name_pos; while (block[pos] == ' ' && pos < block.length()) pos++; if (pos == block.length()) return false; size_t request_name_pos = block.find(")", pos + 1); if (request_name_pos == std::string::npos) { fprintf(stderr, "no request_name in service block [%s]\n", block.c_str()); return false; } rpc_desc.request_name = std::string(&block[pos + 1], &block[request_name_pos]); pos = block.find("returns", pos + 1); if (pos == std::string::npos) { fprintf(stderr, "no \"returns\" in service block [%s]\n", block.c_str()); return false; } while (block[pos] == ' ' && pos < block.length()) pos++; if (pos == block.length()) return true; size_t response_name_pos = block.find("(", pos + 1); size_t response_name_end = block.find(")", pos + 1); if (response_name_pos == std::string::npos || response_name_end == std::string::npos) { fprintf(stderr, "no response_name in service block [%s]\n", block.c_str()); return false; } rpc_desc.response_name = std::string(&block[response_name_pos + 1], &block[response_name_end]); fprintf(stdout, "Successfully parse method:%s req:%s resp:%s\n", rpc_desc.method_name.c_str(), rpc_desc.request_name.c_str(), rpc_desc.response_name.c_str()); desc.rpcs.emplace_back(std::move(rpc_desc)); pos = response_name_end; } return true; } bool Parser::parse_block_name(const std::string& line, std::string& block_name, std::string& block_name_value, std::string& extends_type) { size_t pos = line.find("{"); if (pos == std::string::npos) { fprintf(stderr, "failed to parse block name in %s\n",line.c_str()); return false; } std::vector elems = SGenUtil::split_by_space(line.substr(0,pos)); if (elems.size() == 2) { block_name = elems[0]; block_name_value = elems[1]; extends_type = ""; } else if (this->is_thrift && elems.size() == 4 && elems[0] == "service" && elems[2] == "extends") { block_name = elems[0]; block_name_value = elems[1]; extends_type = elems[3]; } else { fprintf(stderr, "failed to parse block name in %s\n", line.c_str()); return false; } fprintf(stdout, "Successfully parse service block [%s] : %s\n", block_name.c_str(), block_name_value.c_str()); return true; } /* bool Parser::parse_block_name(const std::string& line, std::string& block_name, std::string& block_name_value) { size_t pos = line.find_first_of(" ", 0); if (pos == std::string::npos) { fprintf(stderr, "failed to parse rpc name in %s\n", line.c_str()); return false; } block_name = std::string(&line[0], &line[pos]); size_t value_pos = line.find_first_of(" ", pos + 1); if (value_pos != std::string::npos) { block_name_value = std::string(&line[pos + 1], &line[value_pos]); } else { size_t end = line.find("{"); if (end == std::string::npos) return false; end--; while (line[end] == '\n' || line[end] == '\r' || line[end] == ' ') end --; // block_name_value = std::string(&line[pos + 1], &line[line.length() - 1]); block_name_value = std::string(&line[pos + 1], &line[end + 1]); } fprintf(stdout, "Successfully parse service block [%s] : %s\n", block_name.c_str(), block_name_value.c_str()); return true; } */ bool Parser::check_block_begin(FILE *in, std::string& line) { if (line.find(";") != std::string::npos) return false; if (line.find("{") != std::string::npos) return true; char line_buffer[LINE_LENGTH_MAX]; if (fgets(line_buffer, LINE_LENGTH_MAX, in)) { std::string next = SGenUtil::strip(line_buffer); if (next.find("{") != std::string::npos) { line.append(next.c_str()); return true; } } return false; } bool Parser::check_block_begin(const std::string& line) { if (line.find("{") == std::string::npos) return false; return true; } bool Parser::check_block_end(const std::string& line) { if (line.find("}") == std::string::npos && line.find("};") == std::string::npos) return false; return true; } void parse_thrift_type_name(const std::string& type_name, std::string& type_prefix, std::string& real_type_name) { size_t pos = type_name.find('.',0); if (pos == std::string::npos) { type_prefix = ""; real_type_name = SGenUtil::strip(type_name); } else { type_prefix = SGenUtil::strip(type_name.substr(0,pos)); real_type_name = SGenUtil::strip(type_name.substr(pos+1)); } } std::string type_prefix_to_namespace(const std::string& type_name, idl_info& info) { std::string prefix; std::string real_type; parse_thrift_type_name(type_name, prefix, real_type); if (prefix == "") return type_name; idl_info *include = search_include_file(info, prefix + ".thrift"); if (include == NULL) { fprintf(stderr,"cannot find type %s\n",type_name.c_str()); return type_name; } if (include->package_name.size() > 0) return include->package_name[0]+"::"+real_type; return "::"+real_type; } Descriptor *search_cur_file_descriptor(idl_info& info, const std::string& block_type, const std::string& block_name) { for (auto &desc : info.desc_list) { if (desc.block_type == block_type && desc.block_name == block_name) return &desc; } return NULL; } Descriptor *search_include_file_descriptor(idl_info& info, const std::string include_file_name, const std::string& block_type, const std::string& block_name) { for (auto &include : info.include_list) { if (include.file_name == include_file_name) return search_cur_file_descriptor(include, block_type, block_name); } return NULL; } idl_info *search_include_file(idl_info& info, const std::string file_name) { for (auto &include : info.include_list) { if (include.file_name == file_name) return &include; } return NULL; } idl_info *search_namespace(idl_info& info, const std::string name_space) { if (name_space == "") return &info; for (auto &include : info.include_list) { if (include.package_name.size() > 0 && include.package_name[0] == name_space) return &include; } return NULL; } int Parser::find_valid(const std::string& line) { /* char *p = line.c_str(); char *q = p; while (q != p + line.length()) { if(*q != ' ' && *q != '\t') return pos; } */ return 0; } std::vector parse_thrift_variable(const std::string& str, char sep) { std::vector res; if (sep == '\"') return res; const char *cursor = str.c_str(); const char *start = cursor; bool in_map = false; std::string param; while (*cursor) { if (*cursor == '\"') { cursor = SGenUtil::skip_string(++cursor); if (!*cursor) break; } else if (*cursor == sep) { param = std::string(start, cursor); if (in_map == false && param.find("map") != std::string::npos && param.find("<") != std::string::npos) { in_map = true; cursor++; continue; } if (start < cursor) res.emplace_back(param); start = cursor + 1; in_map = false; } cursor++; } if (start < cursor) res.emplace_back(start, cursor); return res; } srpc-0.10.1/src/generator/parser.h000066400000000000000000000057521454502251400167750ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_PARSER_H__ #define __RPC_PARSER_H__ #define PARSER_ST_BLOCK_MASK 0x01 #define PARSER_ST_OUTSIDE_BLOCK 0x00 #define PARSER_ST_INSIDE_BLOCK 0x01 #define PARSER_ST_OUTSIDE_BLOCK_MASK 0xFFFE #define PARSER_ST_COMMENT_MASK 0x10 #define PARSER_ST_INSIDE_COMMENT 0x10 #define PARSER_ST_OUTSIDE_COMMENT_MASK 0xFFFD #define PARSER_ST_INSIDE_COMMENT_MASK 0xFFFF #define DIRECTORY_LENGTH 2048 #include #include #include #include #include #include #include #include #include #include "descriptor.h" class Parser { public: bool parse(const std::string& proto_file, idl_info& info); std::string find_typedef_mapping_type(std::string& type_name, size_t& cur, idl_info& info); void build_typedef_mapping(idl_info& info); int find_valid(const std::string& line); bool check_block_begin(FILE *in, std::string& line); bool check_block_begin(const std::string& line); bool check_block_end(const std::string& line); void check_single_comments(std::string& line); bool parse_block_name(const std::string& line, std::string& block_name, std::string& block_name_value, std::string& extend_type); bool parse_service_pb(const std::string& block, Descriptor& desc); std::vector split_thrift_rpc(const std::string &str); bool parse_thrift_typedef(const std::string& line, std::string& old_type_name, std::string& new_type_name, idl_info& info); bool parse_rpc_param_thrift(const std::string& file_name_prefix, const std::string &str, std::vector ¶ms, idl_info& info); bool parse_service_thrift(const std::string& file_name_prefix, const std::string& block, Descriptor& desc, idl_info& info); bool parse_struct_thrift(const std::string& file_name_prefix, const std::string& block, Descriptor& desc, idl_info& info); bool parse_enum_thrift(const std::string& block, Descriptor& desc); bool parse_package_name(const std::string& line, std::vector& package_name); bool parse_include_file(const std::string& line, std::string& file_name); bool check_multi_comments_begin(std::string& line); bool check_multi_comments_end(std::string& line); int parse_pb_rpc_option(const std::string& line); Parser(bool is_thrift) { this->is_thrift = is_thrift; } private: bool is_thrift; }; #endif srpc-0.10.1/src/generator/printer.h000066400000000000000000001412401454502251400171550ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_PRINTER_H__ #define __RPC_PRINTER_H__ #include #include #include #include #include "descriptor.h" #include "thrift/rpc_thrift_enum.h" static inline std::string join_package_names(const std::vector& package) { std::string ret = package[0]; for (size_t i = 1; i < package.size(); i++) { ret.append("::"); ret.append(package[i]); } return ret; } static inline std::string change_include_prefix(const std::string& param) { size_t pos = param.find('.'); if (pos == std::string::npos) return param; std::string ret = param.substr(0, pos); ret.append("::"); ret.append(param.substr(pos + 1, param.length() - 1 - pos)); return ret; } static inline std::string make_thrift_package_prefix(const std::vector& package, const std::string& service, const std::string& param) { if (package.size() == 0) return service + "::" + param; std::string package_str = join_package_names(package); return "::" + package_str + "::" + service + "::" + param; } static inline std::string make_package_prefix(const std::vector& package, const std::string& param) { size_t pos = param.find('.'); if (pos != std::string::npos) return change_include_prefix(param); if (package.size() == 0) return param; std::string package_str = join_package_names(package); return "::" + package_str + "::" + param; } static inline std::string make_srpc_service_prefix(const std::vector& package, const std::string& service, char sp) { std::string name; for (const std::string& p : package) { name.append(p); name.push_back(sp); } name.append(service); return name; } static inline std::string make_trpc_service_prefix(const std::vector& package, const std::string& service) { if (package.size() == 0) return service; std::string package_prefix = package[0] + "."; for (size_t i = 1; i < package.size(); i++) package_prefix = package_prefix + package[i] + "."; return package_prefix + service; } static inline std::string make_trpc_method_prefix(const std::vector& package, const std::string& service, const std::string& method) { std::string method_prefix = "/"; for (size_t i = 0; i < package.size(); i++) method_prefix = method_prefix + package[i] + "."; return method_prefix + service + "/" + method; } static inline bool is_simple_type(int8_t data_type) { return data_type == srpc::TDT_BOOL || data_type == srpc::TDT_I08 || data_type == srpc::TDT_I16 || data_type == srpc::TDT_I32 || data_type == srpc::TDT_I64 || data_type == srpc::TDT_U64 || data_type == srpc::TDT_DOUBLE; } static inline void fill_thrift_sync_params(const rpc_descriptor& rpc, std::string& return_type, std::string& handler_params, std::string& send_params) { return_type = "void"; handler_params.clear(); send_params.clear(); if (!rpc.resp_params.empty()) { const auto ¶m = rpc.resp_params[0]; if (param.field_id == 0) { if (is_simple_type(param.data_type)) return_type = param.type_name; else { handler_params += param.type_name; handler_params += "& _return"; } } } for (const auto ¶m : rpc.req_params) { if (!handler_params.empty()) handler_params += ", "; if (!send_params.empty()) send_params += ", "; handler_params += "const "; handler_params += param.type_name; send_params += "const "; send_params += param.type_name; if (!is_simple_type(param.data_type)) { handler_params += "&"; send_params += "&"; } handler_params += " "; handler_params += param.var_name; send_params += " "; send_params += param.var_name; } } static inline void fill_thrift_async_params(const rpc_descriptor& rpc, std::string& return_type, std::string& handler_params) { return_type.clear(); handler_params.clear(); if (!rpc.resp_params.empty()) { const auto ¶m = rpc.resp_params[0]; if (param.field_id == 0) { if (is_simple_type(param.data_type)) return_type += "response->result = "; else handler_params += "response->result"; } } for (const auto ¶m : rpc.req_params) { if (!handler_params.empty()) handler_params += ", "; handler_params += "request->"; handler_params += param.var_name; } } static void output_descriptor(int& i, FILE *f, const std::string& type_name, size_t& cur, const idl_info& info) { int8_t data_type; size_t st = cur; std::string key_arg = "void"; std::string val_arg = "void"; for (; cur < type_name.size(); cur++) { if (type_name[cur] == ',' || type_name[cur] == '>' || type_name[cur] == '<') break; } auto cpptype = SGenUtil::strip(type_name.substr(st, cur - st)); if (info.typedef_mapping.find(cpptype) != info.typedef_mapping.end()) { size_t offset = 0; output_descriptor(i,f,info.typedef_mapping.at(cpptype),offset,info); return; } std::string ret; if (cpptype == "bool") data_type = srpc::TDT_BOOL; else if (cpptype == "int8_t") data_type = srpc::TDT_I08; else if (cpptype == "int16_t") data_type = srpc::TDT_I16; else if (cpptype == "int32_t") data_type = srpc::TDT_I32; else if (cpptype == "int64_t") data_type = srpc::TDT_I64; else if (cpptype == "uint64_t") data_type = srpc::TDT_U64; else if (cpptype == "double") data_type = srpc::TDT_DOUBLE; else if (cpptype == "std::string") data_type = srpc::TDT_STRING; else if (cpptype == "std::map" && cur < type_name.size() && type_name[cur] == '<') { data_type = srpc::TDT_MAP; output_descriptor(i, f, type_name, ++cur, info); key_arg = "subtype_" + std::to_string(i); output_descriptor(i, f, type_name, ++cur, info); val_arg = "subtype_" + std::to_string(i); cpptype = SGenUtil::strip(type_name.substr(st, ++cur - st)); } else if (cpptype == "std::set" && cur < type_name.size() && type_name[cur] == '<') { data_type = srpc::TDT_SET; output_descriptor(i, f, type_name, ++cur, info); val_arg = "subtype_" + std::to_string(i); cpptype = SGenUtil::strip(type_name.substr(st, ++cur - st)); } else if (cpptype == "std::vector" && cur < type_name.size() && type_name[cur] == '<') { data_type = srpc::TDT_LIST; output_descriptor(i, f, type_name, ++cur, info); val_arg = "subtype_" + std::to_string(i); cpptype = SGenUtil::strip(type_name.substr(st, ++cur - st)); } else data_type = srpc::TDT_STRUCT; fprintf(f, "\t\tusing subtype_%d = srpc::ThriftDescriptorImpl<%s, %d, %s, %s>;\n", ++i, cpptype.c_str(), data_type, key_arg.c_str(), val_arg.c_str()); } class Printer { public: bool open(std::string out_file) { this->out_file = fopen(out_file.c_str(), "w"); return this->out_file; } void close() { fclose(this->out_file); } void print_thrift_include(const idl_info& cur_info) { fprintf(this->out_file, "%s", this->thrift_include_format.c_str()); for (const auto& sub_info : cur_info.include_list) fprintf(this->out_file, "#include \"%s.h\"\n", sub_info.file_name.c_str()); //if (cur_info.package_name.length()) for (size_t i = 0; i < cur_info.package_name.size(); i++) { fprintf(this->out_file, this->namespace_package_start_format.c_str(), cur_info.package_name[i].c_str()); } } void print_thrift_struct_declaration(const idl_info& cur_info) { fprintf(this->out_file,"\n"); for (const auto& desc : cur_info.desc_list) { if (desc.block_type == "struct") fprintf(this->out_file,"class %s;\n",desc.block_name.c_str()); } fprintf(this->out_file,"\n"); } void print_thrift_typedef(const idl_info& cur_info) { fprintf(this->out_file,"\n"); for (const auto& t : cur_info.typedef_list) { fprintf(this->out_file,"typedef %s %s;\n",t.old_type_name.c_str(),t.new_type_name.c_str()); } fprintf(this->out_file,"\n"); } void print_thrift_include_enum(const std::string& name, const std::vector& enum_lines) { fprintf(this->out_file, "\nenum %s {\n", name.c_str()); for (const auto& line : enum_lines) fprintf(this->out_file, "\t%s,\n", line.c_str()); fprintf(this->out_file, "};\n"); } void print_rpc_thrift_struct_class(const std::string& class_name, const std::vector& class_params,const idl_info& info) { fprintf(this->out_file, thrift_struct_class_begin_format.c_str(), class_name.c_str()); for (const auto& ele : class_params) { if (ele.default_value.empty()) fprintf(this->out_file, "\t%s %s;\n", ele.type_name.c_str(), ele.var_name.c_str()); else fprintf(this->out_file, "\t%s %s = %s;\n", ele.type_name.c_str(), ele.var_name.c_str(), ele.default_value.c_str()); } fprintf(this->out_file, "%s", thrift_struct_class_isset_begin_format.c_str()); for (const auto& ele : class_params) { if (ele.required_state != srpc::THRIFT_STRUCT_FIELD_REQUIRED) { if (!ele.default_value.empty()) fprintf(this->out_file, "\t\tbool %s = true;\n", ele.var_name.c_str()); else fprintf(this->out_file, "\t\tbool %s = false;\n", ele.var_name.c_str()); } } fprintf(this->out_file, thrift_struct_class_isset_end_format.c_str(), class_name.c_str()); for (const auto& ele : class_params) { std::string type = ele.type_name; if (!is_simple_type(ele.data_type)) type += '&'; if (ele.required_state == srpc::THRIFT_STRUCT_FIELD_REQUIRED) fprintf(this->out_file, thrift_struct_class_set_required_format.c_str(), ele.var_name.c_str(), type.c_str(), ele.var_name.c_str()); else fprintf(this->out_file, thrift_struct_class_set_optional_format.c_str(), ele.var_name.c_str(), type.c_str(), ele.var_name.c_str(), ele.var_name.c_str()); } fprintf(this->out_file, thrift_struct_class_operator_equal_begin_format.c_str(), class_name.c_str()); for (const auto& ele : class_params) { if (ele.required_state == srpc::THRIFT_STRUCT_FIELD_REQUIRED) fprintf(this->out_file, thrift_struct_class_operator_equal_required_format.c_str(), ele.var_name.c_str(), ele.var_name.c_str()); else fprintf(this->out_file, thrift_struct_class_operator_equal_optional_format.c_str(), ele.var_name.c_str(), ele.var_name.c_str(), ele.var_name.c_str(), ele.var_name.c_str(), ele.var_name.c_str()); } fprintf(this->out_file, "%s", thrift_struct_class_operator_equal_end_format.c_str()); fprintf(this->out_file, thrift_struct_class_elements_start_format.c_str(), class_name.c_str(), class_name.c_str(), class_name.c_str(), class_name.c_str(), class_name.c_str(), class_name.c_str(), class_name.c_str(), class_name.c_str(), class_name.c_str(), class_name.c_str(), class_name.c_str()); for (const auto& ele : class_params) { if (is_simple_type(ele.data_type)) { if (ele.default_value.empty()) fprintf(this->out_file, "\t\tthis->%s = 0;\n", ele.var_name.c_str()); } } fprintf(this->out_file, thrift_struct_class_constructor_end_format.c_str(), class_name.c_str(), class_name.c_str()); fprintf(this->out_file, thrift_struct_element_impl_begin_format.c_str(), class_name.c_str(), class_name.c_str()); int i = 0; for (const auto& ele : class_params) { size_t cur = 0; output_descriptor(i, this->out_file, ele.type_name, cur, info); std::string p_isset = "0"; if (ele.required_state != srpc::THRIFT_STRUCT_FIELD_REQUIRED) p_isset = "(const char *)(&st->__isset." + ele.var_name + ") - base"; fprintf(this->out_file, "\t\telements->push_back({subtype_%d::get_instance(), \"%s\", %s, (const char *)(&st->%s) - base, %d, %d});\n", i, ele.var_name.c_str(), p_isset.c_str(), ele.var_name.c_str(), ele.field_id, ele.required_state); } fprintf(this->out_file, thrift_struct_class_end_format.c_str(), class_name.c_str()); } void print_srpc_include(const std::string& prefix, const std::vector& package) { fprintf(this->out_file, this->srpc_include_format.c_str(), prefix.c_str(), this->is_thrift ? "thrift" : "pb"); for (size_t i = 0; i < package.size(); i++) fprintf(this->out_file, this->namespace_package_start_format.c_str(), package[i].c_str()); } void print_server_file_include(const std::string& prefix) { fprintf(this->out_file, this->srpc_file_include_format.c_str(), prefix.c_str()); fprintf(this->out_file, "%s", this->using_namespace_sogou_format.c_str()); fprintf(this->out_file, "%s", this->wait_group_declaration_format.c_str()); } void print_client_file_include(const std::string& prefix) { fprintf(this->out_file, this->srpc_file_include_format.c_str(), prefix.c_str()); fprintf(this->out_file, "%s", this->using_namespace_sogou_format.c_str()); fprintf(this->out_file, "%s", this->wait_group_declaration_format.c_str()); } void print_server_comment() { fprintf(this->out_file, "%s", this->server_comment_format.c_str()); } void print_client_comment() { fprintf(this->out_file, "%s", this->client_comment_format.c_str()); } void print_client_define_done(const std::string& method, const std::string& response) { std::string resp = change_include_prefix(response); fprintf(this->out_file, client_define_done_format.c_str(), method.c_str(), resp.c_str()); } /* void print_client_define_task(const std::string& package, const std::string& method, const std::string& request, const std::string& response) { std::string req = make_package_prefix(package, request); std::string resp = make_package_prefix(package, response); fprintf(this->out_file, client_define_task_format.c_str(), method.c_str(), req.c_str(), resp.c_str()); } void print_server_method(const std::string& package, const std::string& method, const std::string& request, const std::string& response, const std::string& service) { std::string req = make_package_prefix(package, request); std::string resp = make_package_prefix(package, response); fprintf(this->out_file, this->server_method_format.c_str(), method.c_str(), req.c_str(), resp.c_str(), service.c_str(), method.c_str(), req.c_str(), resp.c_str(), method.c_str()); } */ void print_server_class_impl(const std::vector& package, const std::string& service) { std::string base_service = make_package_prefix(package, service); fprintf(this->out_file, this->print_server_class_impl_format.c_str(), service.c_str(), base_service.c_str()); } void print_server_impl_method(const std::vector& package, const std::string& service, const std::string& method, const std::string& request, const std::string& response) { std::string req; std::string resp; if (this->is_thrift) { req = make_thrift_package_prefix(package, service, request); resp = make_thrift_package_prefix(package, service, response); } else { req = make_package_prefix(package, request); resp = make_package_prefix(package, response); } fprintf(this->out_file, this->server_impl_method_format.c_str(), method.c_str(), req.c_str(), resp.c_str()); } void print_server_class_impl_end() { fprintf(this->out_file, "};"); } void print_server_main_begin() { fprintf(this->out_file, "%s", this->server_main_begin_format.c_str()); if (!this->is_thrift) fprintf(this->out_file, "%s", this->main_pb_version_format.c_str()); } void print_server_main_address() { if (this->is_thrift) fprintf(this->out_file, this->server_main_ip_port_format.c_str(), "Thrift"); else fprintf(this->out_file, this->server_main_ip_port_format.c_str(), "SRPC"); } void print_server_main_method(const std::string& service) { std::string service_lower = service; std::transform(service_lower.begin(), service_lower.end(), service_lower.begin(), ::tolower); fprintf(this->out_file, this->server_main_method_format.c_str(), service.c_str(), service_lower.c_str(), service_lower.c_str()); } void print_server_main_end() { fprintf(this->out_file, "%s", this->server_main_end_format.c_str()); } void print_server_main_return() { if (!this->is_thrift) fprintf(this->out_file, "%s", this->main_pb_shutdown_format.c_str()); fprintf(this->out_file, "%s", this->main_end_return_format.c_str()); } void print_server_class_thrift(const std::string& service, const std::vector& rpcs) { std::string return_type; std::string handler_params; std::string send_params; fprintf(this->out_file, "%s", this->server_class_constructor_format.c_str()); for (const auto& rpc : rpcs) { fill_thrift_sync_params(rpc, return_type, handler_params, send_params); fprintf(this->out_file, this->server_class_method_declaration_thrift_handler_format.c_str(), return_type.c_str(), rpc.method_name.c_str(), handler_params.c_str()); } fprintf(this->out_file, "\npublic:\n"); for (const auto& rpc : rpcs) { fill_thrift_async_params(rpc, return_type, handler_params); fprintf(this->out_file, this->server_class_method_declaration_thrift_format.c_str(), rpc.method_name.c_str(), rpc.request_name.c_str(), rpc.response_name.c_str()); } fprintf(this->out_file, "%s", this->server_class_end_format.c_str()); } void print_server_methods_thrift(const std::string& service, const std::vector& rpcs) { std::string return_type; std::string handler_params; std::string send_params; for (const auto& rpc : rpcs) { fill_thrift_sync_params(rpc, return_type, handler_params, send_params); fprintf(this->out_file, this->server_class_method_implementation_thrift_handler_format.c_str(), return_type.c_str(), rpc.method_name.c_str(), handler_params.c_str(), return_type == "void" ? " " : " return 0; "); } for (const auto& rpc : rpcs) { fill_thrift_async_params(rpc, return_type, handler_params); fprintf(this->out_file, this->server_class_method_implementation_thrift_format.c_str(), rpc.method_name.c_str(), rpc.request_name.c_str(), rpc.response_name.c_str(), return_type.c_str(), rpc.method_name.c_str(), handler_params.c_str()); } } void print_server_class(const std::string& service, const std::vector& rpcs) { fprintf(this->out_file, "%s", this->server_class_constructor_format.c_str()); for (const auto& rpc : rpcs) { std::string req = change_include_prefix(rpc.request_name); std::string resp = change_include_prefix(rpc.response_name); fprintf(this->out_file, this->server_class_method_format.c_str(), rpc.method_name.c_str(), req.c_str(), resp.c_str()); } fprintf(this->out_file, "%s", this->server_class_end_format.c_str()); } void print_server_methods(const std::string& service, const std::vector& rpcs) { for (const auto& rpc : rpcs) { fprintf(this->out_file, this->server_methods_format.c_str(), service.c_str(), service.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str()); } } void print_client_class(const std::string& type, const std::string& service, const std::vector& rpcs) { fprintf(this->out_file, this->client_class_constructor_start_format.c_str(), type.c_str(), type.c_str()); if (this->is_thrift) { std::string return_type; std::string handler_params; std::string send_params; std::string recv_params; for (const auto& rpc : rpcs) { fill_thrift_sync_params(rpc, return_type, handler_params, send_params); if (return_type != "void") recv_params.clear(); else if (!rpc.resp_params.empty() && rpc.resp_params[0].field_id == 0) recv_params = rpc.resp_params[0].type_name + "& _return"; else recv_params.clear(); fprintf(this->out_file, this->client_class_construct_methods_thrift_format.c_str(), return_type.c_str(), rpc.method_name.c_str(), handler_params.c_str(), rpc.method_name.c_str(), send_params.c_str(), return_type.c_str(), rpc.method_name.c_str(), recv_params.c_str()); } fprintf(this->out_file, "%s", this->client_class_get_last_thrift_format.c_str()); fprintf(this->out_file, "public:\n"); } for (const auto& rpc : rpcs) { std::string req = change_include_prefix(rpc.request_name); std::string resp = change_include_prefix(rpc.response_name); fprintf(this->out_file, this->client_class_construct_methods_format.c_str(), rpc.method_name.c_str(), req.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str(), req.c_str(), resp.c_str(), resp.c_str(), rpc.method_name.c_str(), req.c_str()); } fprintf(this->out_file, this->client_class_constructor_format.c_str(), type.c_str(), type.c_str()); for (const auto& rpc : rpcs) { fprintf(this->out_file, this->client_class_create_methods_format.c_str(), type.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str()); //type.c_str(), rpc.method_name.c_str(), rpc.request_name.c_str(), rpc.method_name.c_str()); } if (this->is_thrift) { fprintf(this->out_file, "%s", client_class_private_get_thrift_format.c_str()); for (const auto& rpc : rpcs) { std::string resp = change_include_prefix(rpc.response_name); fprintf(this->out_file, this->client_class_get_rpc_thread_resp_thrift_format.c_str(), resp.c_str(), rpc.method_name.c_str()); } } fprintf(this->out_file, this->client_class_constructor_end_format.c_str(), service.c_str()); } void print_implement_comments() { fprintf(this->out_file, "\n///// implements detials /////\n"); } void print_server_constructor(const std::string& service, const std::vector& rpcs) { fprintf(this->out_file, this->server_constructor_method_format.c_str(), service.c_str()); for (const auto& rpc : rpcs) { fprintf(this->out_file, this->server_constructor_add_method_format.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str()); } fprintf(this->out_file, "}\n"); } void print_client_constructor(const std::string& type, const std::string& service, const std::vector& package) { bool is_srpc_thrift = (this->is_thrift && (type == "SRPC" || type == "SRPCHttp")); const char *method_ip = is_srpc_thrift ? client_constructor_methods_ip_srpc_thrift_format.c_str() : ""; const char *method_params = is_srpc_thrift ? client_constructor_methods_params_srpc_thrift_format.c_str() : ""; std::string full_service = service; if (type == "TRPC") full_service = make_trpc_service_prefix(package, service); else if (type == "SRPC" || type == "BRPC" || type == "TRPCHttp") full_service = make_srpc_service_prefix(package, service, '.'); else if (type == "SRPCHttp") full_service = make_srpc_service_prefix(package, service, '/'); fprintf(this->out_file, this->client_constructor_methods_format.c_str(), type.c_str(), type.c_str(), type.c_str(), full_service.c_str(), method_ip, type.c_str(), type.c_str(), type.c_str(), type.c_str(), full_service.c_str(), method_params, type.c_str()); } void print_client_methods(const std::string& type, const std::string& service, const std::vector& rpcs, const std::vector& package) { for (const auto& rpc : rpcs) { std::string req = change_include_prefix(rpc.request_name); std::string resp = change_include_prefix(rpc.response_name); if (type == "TRPC") { std::string full_method = make_trpc_method_prefix(package, service, rpc.method_name); fprintf(this->out_file, this->client_method_trpc_format.c_str(), type.c_str(), rpc.method_name.c_str(), req.c_str(), rpc.method_name.c_str(), full_method.c_str(), type.c_str(), rpc.method_name.c_str(), req.c_str(), resp.c_str(), rpc.method_name.c_str(), resp.c_str(), type.c_str(), rpc.method_name.c_str(), req.c_str(), resp.c_str(), resp.c_str(), full_method.c_str(), resp.c_str()); } else if (type == "TRPCHttp") { fprintf(this->out_file, this->client_method_trpc_format.c_str(), type.c_str(), rpc.method_name.c_str(), req.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str(), type.c_str(), rpc.method_name.c_str(), req.c_str(), resp.c_str(), rpc.method_name.c_str(), resp.c_str(), type.c_str(), rpc.method_name.c_str(), req.c_str(), resp.c_str(), resp.c_str(), rpc.method_name.c_str(), resp.c_str()); } else { fprintf(this->out_file, this->client_method_format.c_str(), type.c_str(), rpc.method_name.c_str(), req.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str(), type.c_str(), rpc.method_name.c_str(), req.c_str(), resp.c_str(), rpc.method_name.c_str(), resp.c_str(), type.c_str(), rpc.method_name.c_str(), req.c_str(), resp.c_str(), resp.c_str(), rpc.method_name.c_str(), resp.c_str()); } } if (this->is_thrift) { std::string return_type; std::string handler_params; std::string send_params; std::string recv_params; for (const auto& rpc : rpcs) { std::string req = change_include_prefix(rpc.request_name); std::string resp = change_include_prefix(rpc.response_name); std::string last_return; fill_thrift_sync_params(rpc, return_type, handler_params, send_params); if (return_type != "void") last_return = "return __thrift__sync__resp.result"; else if (!rpc.resp_params.empty() && rpc.resp_params[0].field_id == 0) last_return = "_return = std::move(__thrift__sync__resp.result)"; else last_return = "(void)__thrift__sync__resp"; fprintf(this->out_file, this->client_method_thrift_begin_format.c_str(), resp.c_str(), type.c_str(), rpc.method_name.c_str(), resp.c_str(), return_type.c_str(), type.c_str(), rpc.method_name.c_str(), handler_params.c_str(), req.c_str(), resp.c_str()); for (const auto ¶m : rpc.req_params) fprintf(this->out_file, "\t__thrift__sync__req.%s = %s;\n", param.var_name.c_str(), param.var_name.c_str()); fprintf(this->out_file, this->client_method_thrift_end_format.c_str(), rpc.method_name.c_str(), type.c_str(), last_return.c_str(), type.c_str(), rpc.method_name.c_str(), send_params.c_str(), req.c_str()); for (const auto ¶m : rpc.req_params) fprintf(this->out_file, "\t__thrift__sync__req.%s = %s;\n", param.var_name.c_str(), param.var_name.c_str()); if (return_type != "void") { last_return = "return std::move(res.result);"; recv_params.clear(); } else if (!rpc.resp_params.empty() && rpc.resp_params[0].field_id == 0) { last_return = "_return = std::move(res.result)"; recv_params = rpc.resp_params[0].type_name + "& _return"; } else { last_return = "(void)res"; recv_params.clear(); } fprintf(this->out_file, this->cilent_method_thrift_send_end_format.c_str(), resp.c_str(), rpc.method_name.c_str(), resp.c_str(), rpc.method_name.c_str(), return_type.c_str(), type.c_str(), rpc.method_name.c_str(), recv_params.c_str(), rpc.method_name.c_str(), last_return.c_str()); } fprintf(this->out_file, this->client_method_get_last_thrift_format.c_str(), type.c_str(), type.c_str(), type.c_str()); } } void print_client_create_task(const std::string& type, const std::string& service, const std::vector& rpcs, const std::vector& package) { for (const auto& rpc : rpcs) { if (type == "TRPC") { std::string full_method = make_trpc_method_prefix(package, service, rpc.method_name); fprintf(this->out_file, this->client_create_task_trpc_format.c_str(), type.c_str(), type.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str(), full_method.c_str()); } else if (type == "TRPCHttp") { fprintf(this->out_file, this->client_create_task_trpc_format.c_str(), type.c_str(), type.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str()); } else { fprintf(this->out_file, this->client_create_task_format.c_str(), type.c_str(), type.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str(), rpc.method_name.c_str()); } } } void print_service_namespace(const std::string& service) { fprintf(this->out_file, this->namespace_service_start_format.c_str(), service.c_str()); } void print_service_namespace_end(const std::string& service) { fprintf(this->out_file, this->namespace_service_end_format.c_str(), service.c_str()); } void print_create_client_controller(const std::string& service, const std::vector& rpcs) { for (const auto& rpc : rpcs) { this->print_create_method_controller(service, rpc.method_name); } } void print_create_method_controller(const std::string& service, const std::string& method) { std::string method_lower = method; std::transform(method_lower.begin(), method_lower.end(), method_lower.begin(), ::tolower); fprintf(this->out_file, this->namespace_method_task_format.c_str(), //create_method_cntl(stub) method.c_str(), method_lower.c_str(), service.c_str(), method.c_str(), method.c_str(), method.c_str()); } void print_client_done_method(const std::vector& package, const std::string& service, const std::string& method, const std::string& response) { std::string method_lower = method; std::transform(method_lower.begin(), method_lower.end(), method_lower.begin(), ::tolower); std::string resp; if (this->is_thrift) resp = make_thrift_package_prefix(package, service, response); else resp = make_package_prefix(package, response); fprintf(this->out_file, this->client_done_format.c_str(), method_lower.c_str(), resp.c_str(), service.c_str(), method.c_str()); } void print_client_main_begin() { fprintf(this->out_file, "%s", this->client_main_begin_format.c_str()); if (!this->is_thrift) fprintf(this->out_file, "%s", this->main_pb_version_format.c_str()); } void print_client_main_address() { fprintf(this->out_file, "%s", this->client_main_ip_port_format.c_str()); } void print_client_main_service(const std::string& type, const std::vector& package, const std::string& service, const std::string& suffix) { //std::string service_lower = service; //std::transform(service_lower.begin(), service_lower.end(), // service_lower.begin(), ::tolower); std::string base_service = make_package_prefix(package, service); fprintf(this->out_file, this->client_main_service_format.c_str(), base_service.c_str(), type.c_str(), suffix.c_str()); } void print_client_main_method_call(const std::vector& package, const std::string& service, const std::string& method, const std::string& request, const std::string& suffix) { std::string method_lower = method; std::transform(method_lower.begin(), method_lower.end(), method_lower.begin(), ::tolower); std::string req; if (this->is_thrift) req = make_thrift_package_prefix(package, service, request); else req = make_package_prefix(package, request); fprintf(this->out_file, this->client_main_method_call_format.c_str(), req.c_str(), method_lower.c_str(), method_lower.c_str(), suffix.c_str(), method.c_str(), method_lower.c_str(), method_lower.c_str()); } /* void print_client_main_create_task(const std::string& package, const std::string& service, const std::string& method, const std::string& request) { std::string method_lower = method; std::transform(method_lower.begin(), method_lower.end(), method_lower.begin(), ::tolower); std::string req = make_package_prefix(package, request); fprintf(this->out_file, this->client_main_create_task_format.c_str(), req.c_str(), method_lower.c_str(), service.c_str(), method.c_str(), method_lower.c_str(), method.c_str(), method_lower.c_str(), method_lower.c_str(), method_lower.c_str(), method_lower.c_str()); } */ void print_client_main_end() { fprintf(this->out_file, "%s", this->client_main_end_format.c_str()); if (!this->is_thrift) fprintf(this->out_file, "%s", this->main_pb_shutdown_format.c_str()); fprintf(this->out_file, "%s", this->main_end_return_format.c_str()); } void print_end(const std::vector& package) { for (size_t i = package.size() - 1; i != (size_t)-1; i--) fprintf(this->out_file, namespace_package_end_format.c_str(), package[i].c_str()); // fprintf(this->out_file, "} // namespace srpc\n"); } void print_empty_line() { fprintf(this->out_file, "\n"); } Printer(bool is_thrift) { this->is_thrift = is_thrift; } protected: FILE *out_file; bool is_thrift; private: std::string thrift_include_format = R"(#pragma once #include "srpc/rpc_thrift_idl.h" )"; std::string srpc_include_format = R"(#pragma once #include #include #include "srpc/rpc_define.h" #include "%s.%s.h" )"; std::string thrift_include_package_format = R"( namespace %s { )"; std::string srpc_include_package_format = R"( using namespace %s; )"; std::string srpc_file_include_format = R"(#include "%s.srpc.h" #include "workflow/WFFacilities.h" )"; std::string using_namespace_sogou_format = R"( using namespace srpc; )"; std::string wait_group_declaration_format = R"( static WFFacilities::WaitGroup wait_group(1); void sig_handler(int signo) { wait_group.done(); } )"; /* std::string namespace_total_start_format = R"( namespace srpc { )"; */ std::string namespace_package_start_format = R"( namespace %s { )"; std::string namespace_package_end_format = R"(} // end namespace %s )"; std::string server_comment_format = R"( /* * Server codes * Generated by SRPC */ )"; std::string client_comment_format = R"( /* * Client codes * Generated by SRPC */ )"; std::string client_define_done_format = R"( using %sDone = std::function;)"; std::string client_define_task_format = R"(using %sTask = srpc::RPCClientTask<%s, %s>; )"; std::string server_context_format = R"( using %sContext = srpc::RPCContext<%s, %s>; )"; std::string server_controller_format = R"( class Server%sCntl : public srpc::%sCntl { public: // must implement this in server codes void %s(%s *request, %s *response); Server%sCntl(srpc::RPCTask *task) : %sCntl(task) {} }; )"; std::string client_controller_format = R"( class Client%sCntl; typedef std::function %sDone; class Client%sCntl : public srpc::%sCntl { public: Client%sCntl(srpc::RPCTask *task) : %sCntl(task) {} void %s(%sDone done, SeriesWork *series) { this->done = std::move(done); if (series != NULL) { this->set_series(series); series->push_back(this->get_task()); } this->service->call_method("%s", this); } %sDone done; }; )"; std::string server_method_format = R"( void %s(srpc::RPCTask *task, %s *request, %s *response); static inline void %s%s(srpc::RPCTask *task, \ rpc_buf_t *req_buf, rpc_buf_t *resp_buf) { %s pb_req; %s pb_resp; pb_req.ParseFromArray(req_buf->buf, req_buf->len); %s(task, &pb_req, &pb_resp); resp_buf->buf = malloc(pb_resp.ByteSizeLong()); resp_buf->len = pb_resp.ByteSizeLong(); pb_resp.SerializeToArray(resp_buf->buf, resp_buf->len); } )"; std::string server_class_constructor_format = R"( class Service : public srpc::RPCService { public: // please implement these methods in server.cc )"; std::string server_class_end_format = R"( public: Service(); }; )"; std::string server_class_method_format = R"( virtual void %s(%s *request, %s *response, srpc::RPCContext *ctx) = 0; )"; std::string server_class_method_declaration_thrift_format = R"( virtual void %s(%s *request, %s *response, srpc::RPCContext *ctx); )"; std::string server_class_method_implementation_thrift_format = R"( inline void Service::%s(%s *request, %s *response, srpc::RPCContext *ctx) { %sthis->%s(%s); } )"; std::string server_class_method_declaration_thrift_handler_format = R"( virtual %s %s(%s); )"; std::string server_class_method_implementation_thrift_handler_format = R"( inline %s Service::%s(%s) {%s} )"; std::string server_class_construct_method_format = R"( void %s%s(srpc::RPCTask *task); )"; std::string server_constructor_method_format = R"( inline Service::Service(): srpc::RPCService("%s") {)"; std::string server_constructor_add_method_format = R"( this->srpc::RPCService::add_method("%s", [this](srpc::RPCWorker& worker) ->int { return ServiceRPCCallImpl(this, worker, &Service::%s); }); )"; std::string server_methods_format = R"( void %s::%s%s(srpc::RPCTask *task) { Server%sCntl cntl(task); srpc::RPCService::parse_from_buffer(task, cntl.get_input(), true); cntl.%s(cntl.get_req(), cntl.get_resp()); srpc::RPCService::serialize_to_buffer(task, cntl.get_output(), false); } )"; std::string client_class_constructor_start_format = R"( class %sClient : public srpc::%sClient { public:)"; std::string client_constructor_methods_format = R"( inline %sClient::%sClient(const char *host, unsigned short port): srpc::%sClient("%s") { struct srpc::RPCClientParams params = srpc::RPC_CLIENT_PARAMS_DEFAULT; %s params.host = host; params.port = port; this->srpc::%sClient::init(¶ms); } inline %sClient::%sClient(const struct srpc::RPCClientParams *params): srpc::%sClient("%s") { const struct srpc::RPCClientParams *temp = params; struct srpc::RPCClientParams temp_params; %s this->srpc::%sClient::init(temp); } )"; std::string client_constructor_methods_ip_srpc_thrift_format = R"( params.task_params.data_type = srpc::RPCDataThrift; )"; std::string client_constructor_methods_params_srpc_thrift_format = R"( if (params->task_params.data_type == srpc::RPCDataUndefined) { temp_params = *temp; temp_params.task_params.data_type = srpc::RPCDataThrift; temp = &temp_params; } )"; std::string client_class_constructor_ip_port_format = R"( %sClient(const char *host, unsigned short port) : srpc::RPCService("%s") { this->params.service_params.host = host; this->params.service_params.port = port; this->register_compress_type(srpc::RPCCompressGzip); )"; std::string client_class_constructor_params_format = R"( %sClient(struct srpc::RPCServiceParams& params) : srpc::RPCService("%s") { this->params.service_params = params; if (!params.url.empty()) { URIParser::parse(params.url, this->params.uri); this->params.has_uri = true; } else { this->params.has_uri = false; } if (!params.host.empty() && !params.port.empty()) { get_addr_info(params.host.c_str(), params.port.c_str(), &this->params.ai); this->params.has_addr_info = true; } else { this->params.has_addr_info = false; } this->register_compress_type(srpc::RPCCompressGzip); )"; std::string client_class_functions = R"( const struct srpc::RPCClientParams *get_params() { return &this->params; } )"; std::string client_class_variables = R"( struct srpc::RPCClientParams params; )"; std::string tab_right_braket = R"( } )"; std::string client_class_done_format = R"( %sDone %s_done; )"; std::string client_class_construct_methods_thrift_format = R"( %s %s(%s); void send_%s(%s); %s recv_%s(%s); )"; std::string client_class_get_last_thrift_format = R"( bool thrift_last_sync_success() const; const srpc::RPCSyncContext& thrift_last_sync_ctx() const; )"; std::string client_class_private_get_thrift_format = R"( private: static srpc::RPCSyncContext *get_thread_sync_ctx(); )"; std::string client_class_get_rpc_thread_resp_thrift_format = R"( static srpc::ThriftReceiver<%s> *get_thread_sync_receiver_%s(); )"; std::string client_class_construct_methods_format = R"( void %s(const %s *req, %sDone done); void %s(const %s *req, %s *resp, srpc::RPCSyncContext *sync_ctx); WFFuture> async_%s(const %s *req); )"; std::string client_class_constructor_format = R"( public: %sClient(const char *host, unsigned short port); %sClient(const struct srpc::RPCClientParams *params); public: )"; std::string client_class_create_methods_format = R"( srpc::%sClientTask *create_%s_task(%sDone done); )"; //%sClientTask *create_%s_task(%s *req, %sDone done); std::string client_class_constructor_end_format = R"(}; )"; std::string client_method_format = R"( inline void %sClient::%s(const %s *req, %sDone done) { auto *task = this->create_rpc_client_task("%s", std::move(done)); task->serialize_input(req); task->start(); } inline void %sClient::%s(const %s *req, %s *resp, srpc::RPCSyncContext *sync_ctx) { auto res = this->async_%s(req).get(); if (resp && res.second.success) *resp = std::move(res.first); if (sync_ctx) *sync_ctx = std::move(res.second); } inline WFFuture> %sClient::async_%s(const %s *req) { using RESULT = std::pair<%s, srpc::RPCSyncContext>; auto *pr = new WFPromise(); auto fr = pr->get_future(); auto *task = this->create_rpc_client_task<%s>("%s", srpc::RPCAsyncFutureCallback<%s>); task->serialize_input(req); task->user_data = pr; task->start(); return fr; } )"; std::string client_method_trpc_format = R"( inline void %sClient::%s(const %s *req, %sDone done) { auto *task = this->create_rpc_client_task("%s", std::move(done)); if (this->params.callee_timeout >= 0) task->get_req()->set_callee_timeout(this->params.callee_timeout); if (!this->params.caller.empty()) task->get_req()->set_caller_name(this->params.caller); task->serialize_input(req); task->start(); } inline void %sClient::%s(const %s *req, %s *resp, srpc::RPCSyncContext *sync_ctx) { auto res = this->async_%s(req).get(); if (resp && res.second.success) *resp = std::move(res.first); if (sync_ctx) *sync_ctx = std::move(res.second); } inline WFFuture> %sClient::async_%s(const %s *req) { using RESULT = std::pair<%s, srpc::RPCSyncContext>; auto *pr = new WFPromise(); auto fr = pr->get_future(); auto *task = this->create_rpc_client_task<%s>("%s", srpc::RPCAsyncFutureCallback<%s>); if (this->params.callee_timeout >= 0) task->get_req()->set_callee_timeout(this->params.callee_timeout); if (!this->params.caller.empty()) task->get_req()->set_caller_name(this->params.caller); task->serialize_input(req); task->user_data = pr; task->start(); return fr; } )"; std::string client_method_thrift_begin_format = R"( inline srpc::ThriftReceiver<%s> *%sClient::get_thread_sync_receiver_%s() { static thread_local srpc::ThriftReceiver<%s> receiver; return &receiver; } inline %s %sClient::%s(%s) { %s __thrift__sync__req; %s __thrift__sync__resp; )"; std::string client_method_thrift_end_format = R"( this->%s(&__thrift__sync__req, &__thrift__sync__resp, %sClient::get_thread_sync_ctx()); %s; } inline void %sClient::send_%s(%s) { %s __thrift__sync__req; )"; std::string cilent_method_thrift_send_end_format = R"( auto *task = this->create_rpc_client_task<%s>("%s", srpc::ThriftSendCallback<%s>); task->serialize_input(&__thrift__sync__req); task->user_data = get_thread_sync_receiver_%s(); task->start(); } inline %s %sClient::recv_%s(%s) { int cookie = 0; auto *receiver = get_thread_sync_receiver_%s(); std::unique_lock lock(receiver->mutex); if (!receiver->is_done) { cookie = WFGlobal::sync_operation_begin(); while (!receiver->is_done) receiver->cond.wait(lock); } receiver->is_done = false; *get_thread_sync_ctx() = std::move(receiver->ctx); auto res = std::move(receiver->output); lock.unlock(); WFGlobal::sync_operation_end(cookie); %s; } )"; std::string client_method_get_last_thrift_format = R"( inline srpc::RPCSyncContext *%sClient::get_thread_sync_ctx() { static thread_local srpc::RPCSyncContext thread_sync_ctx; return &thread_sync_ctx; } inline bool %sClient::thrift_last_sync_success() const { return get_thread_sync_ctx()->success; } inline const srpc::RPCSyncContext& %sClient::thrift_last_sync_ctx() const { return *get_thread_sync_ctx(); } )"; std::string client_create_task_format = R"( inline srpc::%sClientTask *%sClient::create_%s_task(%sDone done) { return this->create_rpc_client_task("%s", std::move(done)); } )"; std::string client_create_task_trpc_format = R"( inline srpc::%sClientTask *%sClient::create_%s_task(%sDone done) { auto *task = this->create_rpc_client_task("%s", std::move(done)); if (!this->params.caller.empty()) task->get_req()->set_caller_name(this->params.caller); return task; } )"; /* inline %sClientTask *%sClient::create_%s_task(%s *req, %sDone done) { auto *task = this->create_rpc_client_task("%s", std::move(done)); task->serialize_input(req); return task; }*/ std::string namespace_service_start_format = R"( namespace %s { )"; std::string namespace_service_end_format = R"( } // end namespace %s )"; std::string namespace_method_task_format = R"( static Client%sCntl *create_%s_cntl(%sClient *stub) { rpc_callback_t cb = std::bind(&%sClient::%sCallback, stub, std::placeholders::_1); auto *task = new ComplexRPCClientTask(stub->get_params(), std::move(cb)); if (task->get_error()) { errno = task->get_error(); delete task; return NULL; } Client%sCntl *cntl = new Client%sCntl(task); cntl->set_service(stub); task->set_controller(cntl); return cntl; } )"; std::string print_server_class_impl_format = R"( class %sServiceImpl : public %s::Service { public: )"; std::string server_impl_method_format = R"( void %s(%s *request, %s *response, srpc::RPCContext *ctx) override { // TODO: fill server logic here } )"; std::string server_main_begin_format = R"( int main() {)"; std::string server_main_ip_port_format = R"( unsigned short port = 1412; %sServer server; )"; std::string server_main_method_format = R"( %sServiceImpl %s_impl; server.add_service(&%s_impl); )"; std::string server_main_end_format = R"( server.start(port); wait_group.wait(); server.stop();)"; std::string client_done_format = R"( static void %s_done(%s *response, srpc::RPCContext *context) { } )"; std::string main_pb_version_format = R"( GOOGLE_PROTOBUF_VERIFY_VERSION;)"; std::string client_main_begin_format = R"( int main() {)"; std::string client_main_ip_port_format = R"( const char *ip = "127.0.0.1"; unsigned short port = 1412; )"; std::string client_main_service_format = R"( %s::%sClient client%s(ip, port); )"; std::string client_main_method_call_format = R"( // example for RPC method call %s %s_req; //%s_req.set_message("Hello, srpc!"); client%s.%s(&%s_req, %s_done); )"; /* std::string client_main_create_task_format = R"( // example for creating RPC task %s %s_req; srpc::RPC%s::%sTask *%s_task = client.create_%s_task(%s_done); %s_task->serialize_input(&%s_req); %s_task->start(); )"; */ std::string client_main_end_format = R"( wait_group.wait();)"; std::string main_pb_shutdown_format = R"( google::protobuf::ShutdownProtobufLibrary();)"; std::string main_end_return_format = R"( return 0; } )"; std::string thrift_struct_class_begin_format = R"( class %s : public srpc::ThriftIDLMessage { public: )"; std::string thrift_struct_class_constructor_end_format = R"( this->elements = srpc::ThriftElementsImpl<%s>::get_elements_instance(); this->descriptor = srpc::ThriftDescriptorImpl<%s, srpc::TDT_STRUCT, void, void>::get_instance(); } )"; std::string thrift_struct_class_end_format = R"( } friend class srpc::ThriftElementsImpl<%s>; }; )"; std::string thrift_struct_element_impl_begin_format = R"( private: static void StaticElementsImpl(std::list *elements) { const %s *st = (const %s *)0; const char *base = (const char *)st; (void)base; )"; std::string thrift_struct_class_set_required_format = R"( void __set_%s(const %s val) { this->%s = val; } )"; std::string thrift_struct_class_set_optional_format = R"( void __set_%s(const %s val) { this->%s = val; this->__isset.%s = true; } )"; std::string thrift_struct_class_isset_begin_format = R"( public: struct ISSET { )"; std::string thrift_struct_class_isset_end_format = R"( } __isset; )"; std::string thrift_struct_class_operator_equal_begin_format = R"( bool operator == (const %s& rhs) const { )"; std::string thrift_struct_class_operator_equal_required_format = R"( if (!(this->%s == rhs.%s)) return false; )"; std::string thrift_struct_class_operator_equal_optional_format = R"( if (this->__isset.%s != rhs.__isset.%s) return false; else if (this->__isset.%s && !(this->%s == rhs.%s)) return false; )"; std::string thrift_struct_class_operator_equal_end_format = R"( return true; } )"; std::string thrift_struct_class_elements_start_format = R"( bool operator != (const %s& rhs) const { return !(*this == rhs); } bool operator < (const %s & ) const; %s(const %s&) = default; %s(%s&&) = default; %s& operator= (const %s&) = default; %s& operator= (%s&&) = default; %s() { )"; }; #endif srpc-0.10.1/src/http/000077500000000000000000000000001454502251400143105ustar00rootroot00000000000000srpc-0.10.1/src/http/CMakeLists.txt000066400000000000000000000005471454502251400170560ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(http) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(SRC http_module.cc http_task.cc http_client.cc http_server.cc ) add_library(${PROJECT_NAME} OBJECT ${SRC}) if (WIN32) target_compile_definitions( ${PROJECT_NAME} PRIVATE strdup=_strdup strcasecmp=_stricmp strncasecmp=_strnicmp ) endif () srpc-0.10.1/src/http/http_client.cc000066400000000000000000000067451454502251400171500ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "http_client.h" #include "http_task.h" #include "http_module.h" namespace srpc { WFHttpTask *HttpClient::create_http_task(const std::string& url, int redirect_max, int retry_max, http_callback_t callback) { std::list module; for (int i = 0; i < SRPC_MODULE_MAX; i++) { if (this->modules[i]) module.push_back(this->modules[i]); } auto&& cb = std::bind(&HttpClient::callback, this, std::placeholders::_1); HttpClientTask *task = new HttpClientTask(redirect_max, retry_max, std::move(module), std::move(cb)); ParsedURI uri; URIParser::parse(url, uri); task->init(std::move(uri)); task->set_keep_alive(HTTP_KEEPALIVE_DEFAULT); // task->set_url(url); task->user_callback_ = std::move(callback); return task; } WFHttpTask *HttpClient::create_http_task(const ParsedURI& uri, int redirect_max, int retry_max, http_callback_t callback) { std::list module; for (int i = 0; i < SRPC_MODULE_MAX; i++) { if (this->modules[i]) module.push_back(this->modules[i]); } auto&& cb = std::bind(&HttpClient::callback, this, std::placeholders::_1); HttpClientTask *task = new HttpClientTask(redirect_max, retry_max, std::move(module), std::move(cb)); task->init(uri); task->set_keep_alive(HTTP_KEEPALIVE_DEFAULT); // task->set_url(url); task->user_callback_ = std::move(callback); return task; } void HttpClient::init() { URIParser::parse(this->params.url, this->params.uri); if (this->params.uri.scheme && strcasecmp(this->params.uri.scheme, "https") == 0) { this->params.is_ssl = true; } } void HttpClient::callback(WFHttpTask *task) { HttpClientTask *client_task = (HttpClientTask *)task; const std::list& module_list = client_task->get_module_list(); void *series_data; RPCModuleData *resp_data; if (!module_list.empty()) { resp_data = client_task->mutable_module_data(); if (resp_data->empty()) // get series module data failed previously { series_data = series_of(task)->get_specific(SRPC_MODULE_DATA); if (series_data) resp_data = (RPCModuleData *)series_data; } // else // http_get_header_module_data(task->get_resp(), *resp_data); for (RPCModule *module : module_list) module->client_task_end(task, *resp_data); } if (client_task->user_callback_) client_task->user_callback_(task); } void HttpClient::add_filter(RPCFilter *filter) { int type = filter->get_module_type(); this->mutex.lock(); if (type < SRPC_MODULE_MAX && type >= 0) { RPCModule *module = this->modules[type]; if (!module) { switch (type) { case RPCModuleTypeTrace: module = new HttpTraceModule(); break; case RPCModuleTypeMetrics: module = new HttpMetricsModule(); break; default: break; } this->modules[type] = module; } if (module) module->add_filter(filter); } this->mutex.unlock(); return; } } // end namespace srpc srpc-0.10.1/src/http/http_client.h000066400000000000000000000044141454502251400170010ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_HTTP_CLIENT_H__ #define __RPC_HTTP_CLIENT_H__ #include "workflow/WFTask.h" #include "workflow/HttpMessage.h" #include "workflow/WFTaskFactory.h" #include "rpc_options.h" #include "rpc_basic.h" #include "http_task.h" namespace srpc { #define HTTP_REDIRECT_DEFAULT 2 #define HTTP_RETRY_DEFAULT 5 #define HTTP_KEEPALIVE_DEFAULT (60 * 1000) struct HttpTaskParams { int send_timeout; int receive_timeout; int watch_timeout; int keep_alive_timeout; int redirect_max; int retry_max; }; struct HttpClientParams { HttpTaskParams task_params; bool is_ssl; std::string url; // can be empty ParsedURI uri; }; static constexpr struct HttpTaskParams HTTP_TASK_PARAMS_DEFAULT = { /* .send_timeout = */ -1, /* .receive_timeout = */ -1, /* .watch_timeout = */ 0, /* .keep_alive_timeout = */ HTTP_KEEPALIVE_DEFAULT, /* .retry_max = */ HTTP_REDIRECT_DEFAULT, /* .redirect_max = */ HTTP_RETRY_DEFAULT, }; static const struct HttpClientParams HTTP_CLIENT_PARAMS_DEFAULT = { /* .task_params = */ HTTP_TASK_PARAMS_DEFAULT, /* .is_ssl = */ false, /* .url = */ "", /* .uri = */ }; class HttpClient { public: HttpClient() { } WFHttpTask *create_http_task(const std::string& url, int redirect_max, int retry_max, http_callback_t callback); WFHttpTask *create_http_task(const ParsedURI& uri, int redirect_max, int retry_max, http_callback_t callback); void add_filter(RPCFilter *filter); private: void callback(WFHttpTask *task); void init(); void task_init(HttpClientTask *task); protected: HttpClientParams params; ParsedURI uri; private: std::mutex mutex; RPCModule *modules[SRPC_MODULE_MAX] = { 0 }; }; } // end namespace srpc #endif srpc-0.10.1/src/http/http_module.cc000066400000000000000000000113121454502251400171410ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "http_module.h" #include "http_task.h" #ifdef _WIN32 #include #else #include #include #endif namespace srpc { bool HttpTraceModule::client_begin(SubTask *task, RPCModuleData& data) { TraceModule::client_begin(task, data); auto *client_task = static_cast(task); TraceModule::client_begin_request(client_task->get_req(), data); data[SRPC_COMPONENT] = SRPC_COMPONENT_SRPC; data[SRPC_HTTP_PEER_NAME] = client_task->get_uri_host(); data[SRPC_HTTP_PEER_PORT] = client_task->get_uri_port(); data[SRPC_HTTP_SCHEME] = client_task->get_uri_scheme(); // TODO: data[SRPC_HTTP_CLIENT_URL] = client_task->get_url(); return true; } bool HttpTraceModule::client_end(SubTask *task, RPCModuleData& data) { TraceModule::client_end(task, data); auto *client_task = static_cast(task); data[WF_TASK_STATE] = std::to_string(client_task->get_state()); if (client_task->get_state() != WFT_STATE_SUCCESS) { data[WF_TASK_ERROR] = std::to_string(client_task->get_error()); if (client_task->get_error() == ETIMEDOUT) { data[SRPC_TIMEOUT_REASON] = std::to_string(client_task->get_timeout_reason()); } return true; } TraceModule::client_end_response(client_task->get_resp(), data); data[SRPC_HTTP_RESEND_COUNT] = std::to_string(client_task->get_retry_times()); char addrstr[128]; struct sockaddr_storage addr; socklen_t l = sizeof addr; unsigned short port = 0; client_task->get_peer_addr((struct sockaddr *)&addr, &l); if (addr.ss_family == AF_INET) { data[SRPC_HTTP_SOCK_FAMILY] = "inet"; struct sockaddr_in *sin = (struct sockaddr_in *)&addr; inet_ntop(AF_INET, &sin->sin_addr, addrstr, 128); port = ntohs(sin->sin_port); } else if (addr.ss_family == AF_INET6) { data[SRPC_HTTP_SOCK_FAMILY] = "inet6"; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, 128); port = ntohs(sin6->sin6_port); } else memset(addrstr, 0, sizeof (addrstr)); data[SRPC_HTTP_SOCK_ADDR] = addrstr; data[SRPC_HTTP_SOCK_PORT] = std::to_string(port); return true; } bool HttpTraceModule::server_begin(SubTask *task, RPCModuleData& data) { TraceModule::server_begin(task, data); auto *server_task = static_cast(task); TraceModule::server_begin_request(server_task->get_req(), data); data[SRPC_COMPONENT] = SRPC_COMPONENT_SRPC; data[SRPC_HTTP_SCHEME] = server_task->is_ssl() ? "https" : "http"; data[SRPC_HTTP_HOST_PORT] = std::to_string(server_task->listen_port()); char addrstr[128]; struct sockaddr_storage addr; socklen_t l = sizeof addr; unsigned short port = 0; server_task->get_peer_addr((struct sockaddr *)&addr, &l); if (addr.ss_family == AF_INET) { data[SRPC_HTTP_SOCK_FAMILY] = "inet"; struct sockaddr_in *sin = (struct sockaddr_in *)&addr; inet_ntop(AF_INET, &sin->sin_addr, addrstr, 128); port = ntohs(sin->sin_port); } else if (addr.ss_family == AF_INET6) { data[SRPC_HTTP_SOCK_FAMILY] = "inet6"; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, 128); port = ntohs(sin6->sin6_port); } // else : Unknown data[SRPC_HTTP_SOCK_ADDR] = addrstr; data[SRPC_HTTP_SOCK_PORT] = std::to_string(port); return true; } bool HttpTraceModule::server_end(SubTask *task, RPCModuleData& data) { TraceModule::server_end(task, data); auto *server_task = static_cast(task); data[SRPC_STATE] = std::to_string(server_task->get_state()); if (server_task->get_state() != WFT_STATE_SUCCESS) { data[SRPC_ERROR] = std::to_string(server_task->get_error()); if (server_task->get_error() == ETIMEDOUT) { data[SRPC_TIMEOUT_REASON] = std::to_string(server_task->get_timeout_reason()); } return true; } TraceModule::server_end_response(server_task->get_resp(), data); return true; } bool HttpMetricsModule::client_begin(SubTask *task, RPCModuleData& data) { MetricsModule::client_begin(task, data); // TODO: return true; } bool HttpMetricsModule::server_begin(SubTask *task, RPCModuleData& data) { MetricsModule::server_begin(task, data); // TODO: return true; } } // end namespace srpc srpc-0.10.1/src/http/http_module.h000066400000000000000000000024351454502251400170110ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_HTTP_MODULE_H__ #define __RPC_HTTP_MODULE_H__ #include "rpc_basic.h" #include "rpc_module.h" #include "rpc_trace_module.h" #include "rpc_metrics_module.h" namespace srpc { class HttpTraceModule : public TraceModule { public: bool client_begin(SubTask *task, RPCModuleData& data) override; bool client_end(SubTask *task, RPCModuleData& data) override; bool server_begin(SubTask *task, RPCModuleData& data) override; bool server_end(SubTask *task, RPCModuleData& data) override; }; class HttpMetricsModule : public MetricsModule { public: bool client_begin(SubTask *task, RPCModuleData& data) override; bool server_begin(SubTask *task, RPCModuleData& data) override; }; } // end namespace srpc #endif srpc-0.10.1/src/http/http_server.cc000066400000000000000000000046741454502251400171770ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "http_server.h" #include "http_task.h" #include "http_module.h" #ifdef _WIN32 #include #else #include #include #endif namespace srpc { void HttpServer::add_filter(RPCFilter *filter) { int type = filter->get_module_type(); this->mutex.lock(); if (type < SRPC_MODULE_MAX && type >= 0) { RPCModule *module = this->modules[type]; if (!module) { switch (type) { case RPCModuleTypeTrace: module = new HttpTraceModule(); break; case RPCModuleTypeMetrics: module = new HttpMetricsModule(); break; default: break; } this->modules[type] = module; } if (module) module->add_filter(filter); } this->mutex.unlock(); return; } CommSession *HttpServer::new_session(long long seq, CommConnection *conn) { HttpServerTask *task; std::list module; for (int i = 0; i < SRPC_MODULE_MAX; i++) { if (this->modules[i]) module.push_back(this->modules[i]); } task = new HttpServerTask(this, std::move(module), this->process); task->set_keep_alive(this->params.keep_alive_timeout); task->set_receive_timeout(this->params.receive_timeout); task->get_req()->set_size_limit(this->params.request_size_limit); task->set_is_ssl(this->get_ssl_ctx() != NULL); task->set_listen_port(this->get_listen_port()); return task; } unsigned short HttpServer::get_listen_port() { if (this->listen_port == 0) { struct sockaddr_storage addr; socklen_t l = sizeof addr; this->get_listen_addr((struct sockaddr *)&addr, &l); if (addr.ss_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)&addr; this->listen_port = ntohs(sin->sin_port); } else // if (addr.ss_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; this->listen_port = ntohs(sin6->sin6_port); } } return this->listen_port; } } // namespace srpc srpc-0.10.1/src/http/http_server.h000066400000000000000000000023011454502251400170220ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_HTTP_SERVER_H__ #define __RPC_HTTP_SERVER_H__ #include "workflow/WFHttpServer.h" #include "rpc_basic.h" #include "rpc_filter.h" #include "rpc_module.h" namespace srpc { class HttpServer : public WFHttpServer { public: HttpServer(http_process_t proc) : WFHttpServer(std::move(proc)), listen_port(0) { } void add_filter(RPCFilter *filter); protected: CommSession *new_session(long long seq, CommConnection *conn) override; private: unsigned short get_listen_port(); private: std::mutex mutex; RPCModule *modules[SRPC_MODULE_MAX] = { NULL }; unsigned short listen_port; }; } // namespace srpc #endif srpc-0.10.1/src/http/http_task.cc000066400000000000000000000206421454502251400166240ustar00rootroot00000000000000#include #include #include #include #include "workflow/WFTaskError.h" #include "workflow/WFTaskFactory.h" #include "workflow/HttpUtil.h" #include "workflow/StringUtil.h" #include "rpc_module.h" #include "http_task.h" namespace srpc { using namespace protocol; #define HTTP_KEEPALIVE_MAX (300 * 1000) HttpClientTask::HttpClientTask(int redirect_max, int retry_max, std::list&& modules, http_callback_t&& callback) : WFComplexClientTask(retry_max, std::move(callback)), redirect_max_(redirect_max), redirect_count_(0), modules_(std::move(modules)) { protocol::HttpRequest *client_req = this->get_req(); client_req->set_method(HttpMethodGet); client_req->set_http_version("HTTP/1.1"); } std::string HttpClientTask::get_uri_host() const { if (uri_.state == URI_STATE_SUCCESS) return uri_.host; return ""; } std::string HttpClientTask::get_uri_port() const { if (uri_.state == URI_STATE_SUCCESS) return uri_.port; return ""; } std::string HttpClientTask::get_url() const { if (url_.empty()) return url_; return ""; //TODO: fill with uri and other info } std::string HttpClientTask::get_uri_scheme() const { if (uri_.state == URI_STATE_SUCCESS) return uri_.scheme; return ""; } CommMessageOut *HttpClientTask::message_out() { HttpRequest *req = this->get_req(); struct HttpMessageHeader header; bool is_alive; void *series_data = series_of(this)->get_specific(SRPC_MODULE_DATA); RPCModuleData *data = (RPCModuleData *)series_data; if (data) this->set_module_data(*data); data = this->mutable_module_data(); for (auto *module : modules_) module->client_task_begin(this, *data); http_set_header_module_data(*data, req); // from ComplexHttpTask::message_out() if (!req->is_chunked() && !req->has_content_length_header()) { size_t body_size = req->get_output_body_size(); const char *method = req->get_method(); if (body_size != 0 || strcmp(method, "POST") == 0 || strcmp(method, "PUT") == 0) { char buf[32]; header.name = "Content-Length"; header.name_len = strlen("Content-Length"); header.value = buf; header.value_len = sprintf(buf, "%zu", body_size); req->add_header(&header); } } if (req->has_connection_header()) is_alive = req->is_keep_alive(); else { header.name = "Connection"; header.name_len = strlen("Connection"); is_alive = (this->keep_alive_timeo != 0); if (is_alive) { header.value = "Keep-Alive"; header.value_len = strlen("Keep-Alive"); } else { header.value = "close"; header.value_len = strlen("close"); } req->add_header(&header); } if (!is_alive) this->keep_alive_timeo = 0; else if (req->has_keep_alive_header()) { HttpHeaderCursor req_cursor(req); //req---Connection: Keep-Alive //req---Keep-Alive: timeout=0,max=100 header.name = "Keep-Alive"; header.name_len = strlen("Keep-Alive"); if (req_cursor.find(&header)) { std::string keep_alive((const char *)header.value, header.value_len); std::vector params = StringUtil::split(keep_alive, ','); for (const auto& kv : params) { std::vector arr = StringUtil::split(kv, '='); if (arr.size() < 2) arr.emplace_back("0"); std::string key = StringUtil::strip(arr[0]); std::string val = StringUtil::strip(arr[1]); if (strcasecmp(key.c_str(), "timeout") == 0) { this->keep_alive_timeo = 1000 * atoi(val.c_str()); break; } } } if ((unsigned int)this->keep_alive_timeo > HTTP_KEEPALIVE_MAX) this->keep_alive_timeo = HTTP_KEEPALIVE_MAX; } return this->WFComplexClientTask::message_out(); } CommMessageIn *HttpClientTask::message_in() { HttpResponse *resp = this->get_resp(); if (strcmp(this->get_req()->get_method(), HttpMethodHead) == 0) resp->parse_zero_body(); return this->WFComplexClientTask::message_in(); } int HttpClientTask::keep_alive_timeout() { return this->resp.is_keep_alive() ? this->keep_alive_timeo : 0; } void HttpClientTask::set_empty_request() { HttpRequest *client_req = this->get_req(); client_req->set_request_uri("/"); client_req->set_header_pair("Host", ""); } void HttpClientTask::init_failed() { this->set_empty_request(); } bool HttpClientTask::init_success() { HttpRequest *client_req = this->get_req(); std::string request_uri; std::string header_host; bool is_ssl; if (uri_.scheme && strcasecmp(uri_.scheme, "http") == 0) is_ssl = false; else if (uri_.scheme && strcasecmp(uri_.scheme, "https") == 0) is_ssl = true; else { this->state = WFT_STATE_TASK_ERROR; this->error = WFT_ERR_URI_SCHEME_INVALID; this->set_empty_request(); return false; } //todo http+unix //https://stackoverflow.com/questions/26964595/whats-the-correct-way-to-use-a-unix-domain-socket-in-requests-framework //https://stackoverflow.com/questions/27037990/connecting-to-postgres-via-database-url-and-unix-socket-in-rails if (uri_.path && uri_.path[0]) request_uri = uri_.path; else request_uri = "/"; if (uri_.query && uri_.query[0]) { request_uri += "?"; request_uri += uri_.query; } if (uri_.host && uri_.host[0]) header_host = uri_.host; if (uri_.port && uri_.port[0]) { int port = atoi(uri_.port); if (is_ssl) { if (port != 443) { header_host += ":"; header_host += uri_.port; } } else { if (port != 80) { header_host += ":"; header_host += uri_.port; } } } this->WFComplexClientTask::set_transport_type(is_ssl ? TT_TCP_SSL : TT_TCP); client_req->set_request_uri(request_uri.c_str()); client_req->set_header_pair("Host", header_host.c_str()); return true; } bool HttpClientTask::redirect_url(HttpResponse *client_resp, ParsedURI& uri) { if (redirect_count_ < redirect_max_) { redirect_count_++; std::string url; HttpHeaderCursor cursor(client_resp); if (!cursor.find("Location", url) || url.empty()) { this->state = WFT_STATE_TASK_ERROR; this->error = WFT_ERR_HTTP_BAD_REDIRECT_HEADER; return false; } if (url[0] == '/') { if (url[1] != '/') { if (uri.port) url = ':' + (uri.port + url); url = "//" + (uri.host + url); } url = uri.scheme + (':' + url); } URIParser::parse(url, uri); return true; } return false; } bool HttpClientTask::need_redirect(ParsedURI& uri) { HttpRequest *client_req = this->get_req(); HttpResponse *client_resp = this->get_resp(); const char *status_code_str = client_resp->get_status_code(); const char *method = client_req->get_method(); if (!status_code_str || !method) return false; int status_code = atoi(status_code_str); switch (status_code) { case 301: case 302: case 303: if (redirect_url(client_resp, uri)) { if (strcasecmp(method, HttpMethodGet) != 0 && strcasecmp(method, HttpMethodHead) != 0) { client_req->set_method(HttpMethodGet); } return true; } else break; case 307: case 308: if (redirect_url(client_resp, uri)) return true; else break; default: break; } return false; } void HttpClientTask::check_response() { HttpResponse *resp = this->get_resp(); resp->end_parsing(); if (this->state == WFT_STATE_SYS_ERROR && this->error == ECONNRESET) { /* Servers can end the message by closing the connection. */ if (resp->is_header_complete() && !resp->is_keep_alive() && !resp->is_chunked() && !resp->has_content_length_header()) { this->state = WFT_STATE_SUCCESS; this->error = 0; } } } bool HttpClientTask::finish_once() { if (this->state != WFT_STATE_SUCCESS) this->check_response(); if (this->state == WFT_STATE_SUCCESS) { if (this->need_redirect(uri_)) this->set_redirect(uri_); else if (this->state != WFT_STATE_SUCCESS) this->disable_retry(); } return true; } /**********Server**********/ void HttpServerTask::handle(int state, int error) { if (state == WFT_STATE_TOREPLY) { // fill module data from request to series ModuleSeries *series = new ModuleSeries(this); http_get_header_module_data(this->get_req(), this->module_data_); for (auto *module : this->modules_) { if (module) module->server_task_begin(this, this->module_data_); } series->set_module_data(this->mutable_module_data()); series->start(); } else if (this->state == WFT_STATE_TOREPLY) { // prepare module_data from series to response for (auto *module : modules_) module->server_task_end(this, this->module_data_); http_set_header_module_data(this->module_data_, this->get_resp()); } WFHttpServerTask::handle(state, error); } } // end namespace srpc srpc-0.10.1/src/http/http_task.h000066400000000000000000000076601454502251400164730ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_HTTP_TASK_H__ #define __RPC_HTTP_TASK_H__ #include #include #include #include #include "workflow/HttpUtil.h" #include "workflow/WFTaskFactory.h" #include "workflow/WFHttpServerTask.h" #include "rpc_module.h" namespace srpc { // copy part of workflow/src/factory/HttpTaskImpl.cc class HttpClientTask : public WFComplexClientTask { public: HttpClientTask(int redirect_max, int retry_max, std::list&& modules, http_callback_t&& callback); RPCModuleData *mutable_module_data() { return &module_data_; } void set_module_data(RPCModuleData data) { module_data_ = std::move(data); } int get_retry_times() const { return retry_times_; } void set_url(std::string url) { this->url_ = std::move(url); } const std::list& get_module_list() const { return this->modules_; } std::string get_uri_host() const; std::string get_uri_port() const; std::string get_uri_scheme() const; std::string get_url() const; /* // similar to opentracing: log({{"event", "error"}, {"message", "application log"}}); void log(const RPCLogVector& fields); // Baggage Items, which are just key:value pairs that cross process boundaries void add_baggage(const std::string& key, const std::string& value); bool get_baggage(const std::string& key, std::string& value); */ protected: virtual CommMessageOut *message_out(); virtual CommMessageIn *message_in(); virtual int keep_alive_timeout(); virtual bool init_success(); virtual void init_failed(); virtual bool finish_once(); protected: bool need_redirect(ParsedURI& uri); bool redirect_url(protocol::HttpResponse *client_resp, ParsedURI& uri); void set_empty_request(); void check_response(); public: http_callback_t user_callback_; private: std::string url_; int redirect_max_; int redirect_count_; RPCModuleData module_data_; std::list modules_; }; class HttpServerTask : public WFHttpServerTask { public: HttpServerTask(CommService *service, std::list&& modules, std::function& process) : WFHttpServerTask(service, process), modules_(std::move(modules)) {} void set_is_ssl(bool is_ssl) { this->is_ssl_ = is_ssl; } void set_listen_port(unsigned short port) { this->listen_port_ = port; } bool is_ssl() const { return this->is_ssl_; } unsigned short listen_port() const { return this->listen_port_; } class ModuleSeries : public Series { public: ModuleSeries(WFServerTask *task) : WFServerTask::Series(task), module_data_(NULL) {} RPCModuleData *get_module_data() { return module_data_; } void set_module_data(RPCModuleData *data) { module_data_ = data; } virtual void *get_specific(const char *key) { if (strcmp(key, SRPC_MODULE_DATA) == 0) return this->module_data_; else return NULL; } private: RPCModuleData *module_data_; }; RPCModuleData *mutable_module_data() { return &module_data_; } void set_module_data(RPCModuleData data) { module_data_ = std::move(data); } protected: virtual void handle(int state, int error); protected: RPCModuleData module_data_; std::list modules_; bool is_ssl_; unsigned short listen_port_; }; } // end namespace srpc #endif srpc-0.10.1/src/include/000077500000000000000000000000001454502251400147545ustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/000077500000000000000000000000001454502251400157235ustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/ckms_quantiles.h000077700000000000000000000000001454502251400255222../../var/ckms_quantiles.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/descriptor.h000077700000000000000000000000001454502251400252102../../generator/descriptor.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/generator.h000077700000000000000000000000001454502251400246302../../generator/generator.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/parser.h000077700000000000000000000000001454502251400234442../../generator/parser.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/printer.h000077700000000000000000000000001454502251400240222../../generator/printer.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_basic.h000077700000000000000000000000001454502251400225402../../rpc_basic.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_buffer.h000077700000000000000000000000001454502251400231202../../rpc_buffer.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_client.h000077700000000000000000000000001454502251400231322../../rpc_client.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_compress.h000077700000000000000000000000001454502251400257172../../compress/rpc_compress.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_compress_gzip.h000077700000000000000000000000001454502251400300012../../compress/rpc_compress_gzip.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_compress_lz4.h000077700000000000000000000000001454502251400273012../../compress/rpc_compress_lz4.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_compress_snappy.h000077700000000000000000000000001454502251400307032../../compress/rpc_compress_snappy.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_context.h000077700000000000000000000000001454502251400235462../../rpc_context.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_context.inl000077700000000000000000000000001454502251400244342../../rpc_context.inlustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_define.h000077700000000000000000000000001454502251400230622../../rpc_define.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_filter.h000077700000000000000000000000001454502251400244352../../module/rpc_filter.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_global.h000077700000000000000000000000001454502251400230762../../rpc_global.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_message.h000077700000000000000000000000001454502251400250722../../message/rpc_message.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_message_brpc.h000077700000000000000000000000001454502251400271062../../message/rpc_message_brpc.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_message_srpc.h000077700000000000000000000000001454502251400271502../../message/rpc_message_srpc.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_message_thrift.h000077700000000000000000000000001454502251400300322../../message/rpc_message_thrift.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_message_trpc.h000077700000000000000000000000001454502251400271522../../message/rpc_message_trpc.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_metrics_filter.h000077700000000000000000000000001454502251400277112../../module/rpc_metrics_filter.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_metrics_module.h000077700000000000000000000000001454502251400277112../../module/rpc_metrics_module.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_module.h000077700000000000000000000000001454502251400244352../../module/rpc_module.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_options.h000077700000000000000000000000001454502251400235642../../rpc_options.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_server.h000077700000000000000000000000001454502251400232122../../rpc_server.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_service.h000077700000000000000000000000001454502251400234762../../rpc_service.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_task.inl000077700000000000000000000000001454502251400231702../../rpc_task.inlustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_thrift_buffer.h000077700000000000000000000000001454502251400273602../../thrift/rpc_thrift_buffer.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_thrift_enum.h000077700000000000000000000000001454502251400265462../../thrift/rpc_thrift_enum.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_thrift_idl.h000077700000000000000000000000001454502251400261562../../thrift/rpc_thrift_idl.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_thrift_idl.inl000077700000000000000000000000001454502251400270442../../thrift/rpc_thrift_idl.inlustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_trace_filter.h000077700000000000000000000000001454502251400267712../../module/rpc_trace_filter.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_trace_module.h000077700000000000000000000000001454502251400267712../../module/rpc_trace_module.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_types.h000077700000000000000000000000001454502251400227062../../rpc_types.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_var.h000077700000000000000000000000001454502251400225462../../var/rpc_var.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/rpc_zero_copy_stream.h000077700000000000000000000000001454502251400273462../../rpc_zero_copy_stream.hustar00rootroot00000000000000srpc-0.10.1/src/include/srpc/time_window_quantiles.h000077700000000000000000000000001454502251400305022../../var/time_window_quantiles.hustar00rootroot00000000000000srpc-0.10.1/src/message/000077500000000000000000000000001454502251400147555ustar00rootroot00000000000000srpc-0.10.1/src/message/CMakeLists.txt000066400000000000000000000010421454502251400175120ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(message) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(PROTO_LIST rpc_meta.proto rpc_meta_brpc.proto rpc_meta_trpc.proto) protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_LIST}) set(SRC rpc_message_brpc.cc rpc_message_srpc.cc rpc_message_thrift.cc rpc_message_trpc.cc ${PROTO_SRCS} ${PROTO_HDRS} ) add_library(${PROJECT_NAME} OBJECT ${SRC}) if (WIN32) target_compile_definitions( ${PROJECT_NAME} PRIVATE strdup=_strdup strcasecmp=_stricmp strncasecmp=_strnicmp ) endif () srpc-0.10.1/src/message/rpc_message.h000066400000000000000000000111621454502251400174170ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_MESSAGE_H__ #define __RPC_MESSAGE_H__ #include #include #include #include "rpc_basic.h" #include "rpc_filter.h" #include "rpc_thrift_idl.h" namespace srpc { class RPCRequest { public: virtual bool serialize_meta() = 0; virtual bool deserialize_meta() = 0; virtual const std::string& get_service_name() const = 0; virtual const std::string& get_method_name() const = 0; virtual void set_service_name(const std::string& service_name) = 0; virtual void set_method_name(const std::string& method_name) = 0; virtual bool get_meta_module_data(RPCModuleData& data) const = 0; virtual bool set_meta_module_data(const RPCModuleData& data) = 0; virtual void set_seqid(long long seqid) {} public: virtual ~RPCRequest() { } }; class RPCResponse { public: virtual bool serialize_meta() = 0; virtual bool deserialize_meta() = 0; virtual int get_status_code() const = 0; virtual int get_error() const = 0; virtual const char *get_errmsg() const = 0; virtual void set_status_code(int code) = 0; virtual void set_error(int error) = 0; virtual bool set_http_code(int code) { return false; } public: virtual ~RPCResponse() { } }; class RPCMessage { public: virtual void set_data_type(int type) = 0; virtual void set_compress_type(int type) = 0; virtual int get_compress_type() const = 0; virtual int get_data_type() const = 0; public: //return RPCStatus virtual int compress() = 0; virtual int decompress() = 0; virtual bool get_meta_module_data(RPCModuleData& data) const = 0; virtual bool set_meta_module_data(const RPCModuleData& data) = 0; virtual bool set_http_header(const std::string& name, const std::string& value) { return false; } virtual bool add_http_header(const std::string& name, const std::string& value) { return false; } virtual bool get_http_header(const std::string& name, std::string& value) const { return false; } virtual void set_json_add_whitespace(bool on); virtual bool get_json_add_whitespace() const; virtual void set_json_enums_as_ints(bool on); virtual bool get_json_enums_as_ints() const; virtual void set_json_preserve_names(bool on); virtual bool get_json_preserve_names() const; virtual void set_json_print_primitive(bool on); virtual bool get_json_print_primitive() const; public: //pb virtual int serialize(const ProtobufIDLMessage *idl_msg) { return RPCStatusIDLSerializeNotSupported; } virtual int deserialize(ProtobufIDLMessage *idl_msg) { return RPCStatusIDLDeserializeNotSupported; } public: //thrift virtual int serialize(const ThriftIDLMessage *idl_msg) { return RPCStatusIDLSerializeNotSupported; } virtual int deserialize(ThriftIDLMessage *idl_msg) { return RPCStatusIDLDeserializeNotSupported; } public: RPCMessage() { this->flags = 0; } virtual ~RPCMessage() { } protected: uint32_t flags; }; // implementation inline void RPCMessage::set_json_add_whitespace(bool on) { if (on) this->flags |= SRPC_JSON_OPTION_ADD_WHITESPACE; else this->flags &= ~SRPC_JSON_OPTION_ADD_WHITESPACE; } inline bool RPCMessage::get_json_add_whitespace() const { return this->flags & SRPC_JSON_OPTION_ADD_WHITESPACE; } inline void RPCMessage::set_json_enums_as_ints(bool on) { if (on) this->flags |= SRPC_JSON_OPTION_ENUM_AS_INITS; else this->flags &= ~SRPC_JSON_OPTION_ENUM_AS_INITS; } inline bool RPCMessage::get_json_enums_as_ints() const { return this->flags & SRPC_JSON_OPTION_ENUM_AS_INITS; } inline void RPCMessage::set_json_preserve_names(bool on) { if (on) this->flags |= SRPC_JSON_OPTION_PRESERVE_NAMES; else this->flags &= ~SRPC_JSON_OPTION_PRESERVE_NAMES; } inline bool RPCMessage::get_json_preserve_names() const { return this->flags & SRPC_JSON_OPTION_PRESERVE_NAMES; } inline void RPCMessage::set_json_print_primitive(bool on) { if (on) this->flags |= SRPC_JSON_OPTION_PRINT_PRIMITIVE; else this->flags &= ~SRPC_JSON_OPTION_PRINT_PRIMITIVE; } inline bool RPCMessage::get_json_print_primitive() const { return this->flags & SRPC_JSON_OPTION_PRINT_PRIMITIVE; } } // namespace srpc #endif srpc-0.10.1/src/message/rpc_message_brpc.cc000066400000000000000000000365321454502251400205730ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include "rpc_basic.h" #include "rpc_compress.h" #include "rpc_meta_brpc.pb.h" #include "rpc_message_brpc.h" #include "rpc_zero_copy_stream.h" namespace srpc { static constexpr int BRPC_ENOSERVICE = 1001; static constexpr int BRPC_ENOMETHOD = 1002; static constexpr int BRPC_EREQUEST = 1003; static constexpr int BRPC_EINTERNAL = 2001; static constexpr int BRPC_ERESPONSE = 2002; static constexpr int BRPC_ELOGOFF = 2003; BRPCMessage::BRPCMessage() { this->nreceived = 0; this->meta_buf = NULL; this->meta_len = 0; this->message_len = 0; this->attachment_len = 0; memset(this->header, 0, sizeof (this->header)); this->meta = new BrpcMeta(); this->message = new RPCBuffer(); this->attachment = NULL; } bool BRPCRequest::deserialize_meta() { BrpcMeta *meta = static_cast(this->meta); if (meta->ParseFromArray(this->meta_buf, (int)this->meta_len)) { if (meta->has_attachment_size()) { this->attachment_len = meta->attachment_size(); this->message_len -= this->attachment_len; this->attachment = new RPCBuffer(); this->message->cut(this->message_len, this->attachment); } return true; } return false; } bool BRPCResponse::deserialize_meta() { BrpcMeta *meta = static_cast(this->meta); int error; if (meta->ParseFromArray(this->meta_buf, (int)this->meta_len)) { if (meta->has_attachment_size()) { this->attachment_len = meta->attachment_size(); this->message_len -= this->attachment_len; this->attachment = new RPCBuffer(); this->message->cut(this->message_len, this->attachment); } this->srpc_status_code = RPCStatusOK; if (meta->has_response()) { error = meta->mutable_response()->error_code(); if (error != 0) { this->srpc_status_code = this->error_code_brpc_srpc(error); this->srpc_error_msg = meta->mutable_response()->error_text(); } } return true; } return false; } int BRPCMessage::append(const void *buf, size_t *size, size_t size_limit) { uint32_t *p; size_t header_left, body_received, buf_len; if (this->nreceived < BRPC_HEADER_SIZE) { //receive header header_left = BRPC_HEADER_SIZE - this->nreceived; if (*size >= header_left) { //receive the whole header and ready to recieve body memcpy(this->header + this->nreceived, buf, header_left); this->nreceived += header_left; p = (uint32_t *)this->header + 1; buf_len = ntohl(*p); // payload_len p = (uint32_t *)this->header + 2; this->meta_len = ntohl(*p); this->message_len = buf_len - this->meta_len; // msg_len + attachment_len if (buf_len >= size_limit) { errno = EMSGSIZE; return -1; } else if (buf_len > 0) { if (*size - header_left > buf_len) *size = header_left + buf_len; this->meta_buf = new char[this->meta_len]; // this->buf = new char[this->message_len]; if (*size - header_left <= this->meta_len) { memcpy(this->meta_buf, (const char *)buf + header_left, *size - header_left); } else { memcpy(this->meta_buf, (const char *)buf + header_left, this->meta_len); // memcpy(this->buf, // (const char *)buf + header_left + this->meta_len, // *size - header_left - this->meta_len); this->message->append((const char *)buf + header_left + this->meta_len, *size - header_left - this->meta_len, BUFFER_MODE_COPY); } this->nreceived += *size - header_left; if (this->nreceived == BRPC_HEADER_SIZE + buf_len) return 1; else return 0; } else if (*size == header_left) { return 1; // means body_size == 0 and finish recieved header } else { // means buf_len < 0 errno = EBADMSG; return -1; } } else { // only receive header memcpy(this->header + this->nreceived, buf, *size); this->nreceived += *size; return 0; } } else { // have already received the header and now is for body only body_received = this->nreceived - BRPC_HEADER_SIZE; buf_len = this->meta_len + this->message_len; if (body_received + *size > buf_len) *size = buf_len - body_received; if (body_received + *size <= this->meta_len) { memcpy(this->meta_buf + body_received, buf, *size); } else if (body_received < this->meta_len) { memcpy(this->meta_buf + body_received, buf, this->meta_len - body_received); if (body_received + *size > this->meta_len)// useless. always true // memcpy(this->buf, (const char *)buf + this->meta_len - body_received, // *size - this->meta_len + body_received); this->message->append((const char *)buf + this->meta_len - body_received, *size - this->meta_len + body_received, BUFFER_MODE_COPY); } else { // memcpy(this->buf + body_received - this->meta_len, buf, *size); this->message->append((const char *)buf, *size, BUFFER_MODE_COPY); } this->nreceived += *size; return this->nreceived == BRPC_HEADER_SIZE + buf_len; } } bool BRPCRequest::serialize_meta() { this->meta_len = meta->ByteSizeLong(); this->meta_buf = new char[this->meta_len]; return this->meta->SerializeToArray(this->meta_buf, (int)this->meta_len); } bool BRPCResponse::serialize_meta() { BrpcMeta *meta = static_cast(this->meta); int error; if (this->srpc_status_code != RPCStatusOK) { error = this->error_code_srpc_brpc(this->srpc_status_code); meta->mutable_response()->set_error_code(error); meta->mutable_response()->set_error_text(this->srpc_error_msg); } this->meta_len = meta->ByteSizeLong(); this->meta_buf = new char[this->meta_len]; return meta->SerializeToArray(this->meta_buf, (int)this->meta_len); } int BRPCMessage::get_compress_type() const { BrpcMeta *meta = static_cast(this->meta); return meta->compress_type(); } void BRPCMessage::set_compress_type(int type) { BrpcMeta *meta = static_cast(this->meta); meta->set_compress_type(type); } void BRPCMessage::set_attachment_nocopy(const char *attachment, size_t len) { BrpcMeta *meta = static_cast(this->meta); this->attachment_len += len; meta->set_attachment_size(this->attachment_len); this->attachment = new RPCBuffer(); this->attachment->append(attachment, len, BUFFER_MODE_NOCOPY); } bool BRPCMessage::get_attachment_nocopy(const char **attachment, size_t *len) const { size_t tmp_len = (size_t)-1; const void *tmp_buf; if (this->attachment == NULL || this->attachment->fetch(&tmp_buf, &tmp_len) == false) { return false; } *attachment = (const char *)tmp_buf; *len = tmp_len; return true; } const std::string& BRPCRequest::get_service_name() const { BrpcMeta *meta = static_cast(this->meta); return meta->mutable_request()->service_name(); } const std::string& BRPCRequest::get_method_name() const { BrpcMeta *meta = static_cast(this->meta); return meta->mutable_request()->method_name(); } void BRPCRequest::set_service_name(const std::string& service_name) { BrpcMeta *meta = static_cast(this->meta); meta->mutable_request()->set_service_name(service_name); } void BRPCRequest::set_method_name(const std::string& method_name) { BrpcMeta *meta = static_cast(this->meta); meta->mutable_request()->set_method_name(method_name); } int64_t BRPCRequest::get_correlation_id() const { const BrpcMeta *meta = static_cast(this->meta); if (meta->has_correlation_id()) return meta->correlation_id(); return -1; } int BRPCResponse::get_status_code() const { return this->srpc_status_code; } void BRPCResponse::set_status_code(int code) { this->srpc_status_code = code; if (code != RPCStatusOK) this->srpc_error_msg = this->get_errmsg(); } int BRPCResponse::get_error() const { BrpcMeta *meta = static_cast(this->meta); return meta->mutable_response()->error_code(); } const char *BRPCResponse::get_errmsg() const { switch (this->srpc_status_code) { case RPCStatusOK: return "OK"; case RPCStatusUndefined: return "Undefined Error"; case RPCStatusServiceNotFound: return "Service Not Found"; case RPCStatusMethodNotFound: return "Method Not Found"; case RPCStatusMetaError: return "Meta Error"; case RPCStatusReqCompressSizeInvalid: return "Request Compress-size Invalid"; case RPCStatusReqDecompressSizeInvalid: return "Request Decompress-size Invalid"; case RPCStatusReqCompressNotSupported: return "Request Compress Not Supported"; case RPCStatusReqDecompressNotSupported: return "Request Decompress Not Supported"; case RPCStatusReqCompressError: return "Request Compress Error"; case RPCStatusReqDecompressError: return "Request Decompress Error"; case RPCStatusReqSerializeError: return "Request Serialize Error"; case RPCStatusReqDeserializeError: return "Request Deserialize Error"; case RPCStatusRespCompressSizeInvalid: return "Response Compress-size Invalid"; case RPCStatusRespDecompressSizeInvalid: return "Response Decompress-size Invalid"; case RPCStatusRespCompressNotSupported: return "Response Compress Not Supported"; case RPCStatusRespDecompressNotSupported: return "Response Decompress Not Supported"; case RPCStatusRespCompressError: return "Response Compress Error"; case RPCStatusRespDecompressError: return "Response Decompress Error"; case RPCStatusRespSerializeError: return "Response Serialize Error"; case RPCStatusRespDeserializeError: return "Response Deserialize Error"; case RPCStatusIDLSerializeNotSupported: return "IDL Serialize Not Supported"; case RPCStatusIDLDeserializeNotSupported: return "IDL Deserialize Not Supported"; case RPCStatusURIInvalid: return "URI Invalid"; case RPCStatusUpstreamFailed: return "Upstream Failed"; case RPCStatusSystemError: return "System Error. Use get_error() to get errno"; case RPCStatusSSLError: return "SSL Error. Use get_error() to get SSL-Error"; case RPCStatusDNSError: return "DNS Error. Use get_error() to get GAI-Error"; case RPCStatusProcessTerminated: return "Process Terminated"; default: return "Unknown Error"; } } void BRPCResponse::set_error(int error) { BrpcMeta *meta = static_cast(this->meta); meta->mutable_response()->set_error_code(error); } void BRPCResponse::set_correlation_id(int64_t cid) { BrpcMeta *meta = static_cast(this->meta); meta->set_correlation_id(cid); } int BRPCMessage::serialize(const ProtobufIDLMessage *pb_msg) { if (!pb_msg) return RPCStatusOK; BrpcMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); int msg_len = pb_msg->ByteSizeLong(); RPCOutputStream stream(this->message, pb_msg->ByteSizeLong()); int ret = pb_msg->SerializeToZeroCopyStream(&stream) ? 0 : -1; if (ret < 0) return is_resp ? RPCStatusRespSerializeError : RPCStatusReqSerializeError; this->message_len = msg_len; return RPCStatusOK; } int BRPCMessage::deserialize(ProtobufIDLMessage *pb_msg) { const BrpcMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); RPCInputStream stream(this->message); if (pb_msg->ParseFromZeroCopyStream(&stream) == false) return is_resp ? RPCStatusRespDeserializeError : RPCStatusReqDeserializeError; return RPCStatusOK; } int BRPCMessage::compress() { BrpcMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); int type = meta->compress_type(); size_t buflen = this->message_len; int status_code = RPCStatusOK; if (buflen == 0) return status_code; if (type == RPCCompressNone) return status_code; static RPCCompressor *compressor = RPCCompressor::get_instance(); int ret = compressor->lease_compressed_size(type, buflen); if (ret == -2) return is_resp ? RPCStatusReqCompressNotSupported : RPCStatusRespCompressNotSupported; else if (ret <= 0) return is_resp ? RPCStatusRespCompressSizeInvalid : RPCStatusReqCompressSizeInvalid; //buflen = ret; RPCBuffer *dst_buf = new RPCBuffer(); ret = compressor->serialize_to_compressed(this->message, dst_buf, type); if (ret == -2) status_code = is_resp ? RPCStatusRespCompressNotSupported : RPCStatusReqCompressNotSupported; else if (ret == -1) status_code = is_resp ? RPCStatusRespCompressError : RPCStatusReqCompressError; else if (ret <= 0) status_code = is_resp ? RPCStatusRespCompressSizeInvalid : RPCStatusReqCompressSizeInvalid; else buflen = ret; if (status_code == RPCStatusOK) { delete this->message; this->message = dst_buf; this->message_len = buflen; } else { delete dst_buf; } return status_code; } int BRPCMessage::decompress() { const BrpcMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); int type = meta->compress_type(); int status_code = RPCStatusOK; if (this->message_len == 0 || type == RPCCompressNone) return status_code; RPCBuffer *dst_buf = new RPCBuffer(); static RPCCompressor *compressor = RPCCompressor::get_instance(); int ret = compressor->parse_from_compressed(this->message, dst_buf, type); if (ret == -2) status_code = is_resp ? RPCStatusRespDecompressNotSupported : RPCStatusReqDecompressNotSupported; else if (ret == -1) status_code = is_resp ? RPCStatusRespDecompressError : RPCStatusReqDecompressError; else if (ret <= 0) status_code = is_resp ? RPCStatusRespDecompressSizeInvalid : RPCStatusReqDecompressSizeInvalid; if (status_code == RPCStatusOK) { delete this->message; this->message = dst_buf; this->message_len = ret; } else { delete dst_buf; } return status_code; } inline int BRPCMessage::error_code_srpc_brpc(int srpc_status_code) const { switch (srpc_status_code) { case RPCStatusServiceNotFound: return BRPC_ENOSERVICE; case RPCStatusMethodNotFound: return BRPC_ENOMETHOD; case RPCStatusMetaError: case RPCStatusReqCompressSizeInvalid: case RPCStatusReqDecompressSizeInvalid: case RPCStatusReqCompressNotSupported: case RPCStatusReqDecompressNotSupported: case RPCStatusReqCompressError: case RPCStatusReqDecompressError: case RPCStatusReqSerializeError: case RPCStatusReqDeserializeError: return BRPC_EREQUEST; case RPCStatusRespCompressSizeInvalid: case RPCStatusRespDecompressSizeInvalid: case RPCStatusRespCompressNotSupported: case RPCStatusRespDecompressNotSupported: case RPCStatusRespCompressError: case RPCStatusRespDecompressError: case RPCStatusRespSerializeError: case RPCStatusRespDeserializeError: return BRPC_ERESPONSE; case RPCStatusProcessTerminated: return BRPC_ELOGOFF; default: return BRPC_EINTERNAL; } } inline int BRPCMessage::error_code_brpc_srpc(int brpc_error_code) const { switch (brpc_error_code) { case BRPC_ENOSERVICE: return RPCStatusServiceNotFound; case BRPC_ENOMETHOD: return RPCStatusMethodNotFound; case BRPC_EREQUEST: return RPCStatusReqDeserializeError; case BRPC_ERESPONSE: return RPCStatusRespDeserializeError; case BRPC_ELOGOFF: return RPCStatusProcessTerminated; default: return RPCStatusSystemError; } } } // namesapce sogou srpc-0.10.1/src/message/rpc_message_brpc.h000066400000000000000000000147651454502251400204410ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_MESSAGE_BRPC_H__ #define __RPC_MESSAGE_BRPC_H__ #ifdef _WIN32 #include #else #include #endif #include "rpc_message.h" #include "rpc_basic.h" namespace srpc { static constexpr int BRPC_HEADER_SIZE = 12; class BRPCMessage : public RPCMessage { public: BRPCMessage(); virtual ~BRPCMessage(); int encode(struct iovec vectors[], int max, size_t size_limit); int append(const void *buf, size_t *size, size_t size_limit); int get_compress_type() const override; void set_compress_type(int type) override; bool get_attachment_nocopy(const char **attachment, size_t *len) const; void set_attachment_nocopy(const char *attachment, size_t len); int get_data_type() const override { return RPCDataProtobuf; } void set_data_type(int type) override { } //Different data type is not supported in BrpcMeta. bool get_meta_module_data(RPCModuleData& data) const override { return false; } bool set_meta_module_data(const RPCModuleData& data) override { return false; } public: using RPCMessage::serialize; using RPCMessage::deserialize; int serialize(const ProtobufIDLMessage *pb_msg) override; int deserialize(ProtobufIDLMessage *pb_msg) override; int compress() override; int decompress() override; protected: // "PRPC" + PAYLOAD_SIZE + META_SIZE char header[BRPC_HEADER_SIZE]; size_t nreceived; size_t meta_len; size_t message_len; size_t attachment_len; char *meta_buf; RPCBuffer *message; RPCBuffer *attachment; ProtobufIDLMessage *meta; protected: int error_code_srpc_brpc(int srpc_status_code) const; int error_code_brpc_srpc(int brpc_error_code) const; }; class BRPCRequest : public BRPCMessage { public: bool serialize_meta(); bool deserialize_meta(); const std::string& get_service_name() const; const std::string& get_method_name() const; void set_service_name(const std::string& service_name); void set_method_name(const std::string& method_name); int64_t get_correlation_id() const; }; class BRPCResponse : public BRPCMessage { public: bool serialize_meta(); bool deserialize_meta(); int get_status_code() const; int get_error() const; const char *get_errmsg() const; void set_status_code(int code); void set_error(int error); void set_correlation_id(int64_t cid); protected: int srpc_status_code = RPCStatusOK; std::string srpc_error_msg; }; class BRPCStdRequest : public protocol::ProtocolMessage, public RPCRequest, public BRPCRequest { public: int encode(struct iovec vectors[], int max) override { return this->BRPCRequest::encode(vectors, max, this->size_limit); } int append(const void *buf, size_t *size) override { return this->BRPCRequest::append(buf, size, this->size_limit); } public: bool serialize_meta() override { return this->BRPCRequest::serialize_meta(); } bool deserialize_meta() override { return this->BRPCRequest::deserialize_meta(); } public: const std::string& get_service_name() const override { return this->BRPCRequest::get_service_name(); } const std::string& get_method_name() const override { return this->BRPCRequest::get_method_name(); } void set_service_name(const std::string& service_name) override { return this->BRPCRequest::set_service_name(service_name); } void set_method_name(const std::string& method_name) override { return this->BRPCRequest::set_method_name(method_name); } bool set_meta_module_data(const RPCModuleData& data) override { return this->BRPCMessage::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->BRPCMessage::get_meta_module_data(data); } public: BRPCStdRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class BRPCStdResponse : public protocol::ProtocolMessage, public RPCResponse, public BRPCResponse { public: int encode(struct iovec vectors[], int max) override { return this->BRPCResponse::encode(vectors, max, this->size_limit); } int append(const void *buf, size_t *size) override { return this->BRPCResponse::append(buf, size, this->size_limit); } public: bool serialize_meta() override { return this->BRPCResponse::serialize_meta(); } bool deserialize_meta() override { return this->BRPCResponse::deserialize_meta(); } public: int get_status_code() const override { return this->BRPCResponse::get_status_code(); } int get_error() const override { return this->BRPCResponse::get_error(); } const char *get_errmsg() const override { return this->BRPCResponse::get_errmsg(); } void set_status_code(int code) override { return this->BRPCResponse::set_status_code(code); } void set_error(int error) override { return this->BRPCResponse::set_error(error); } bool set_meta_module_data(const RPCModuleData& data) override { return this->BRPCMessage::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->BRPCMessage::get_meta_module_data(data); } public: BRPCStdResponse() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; //////// // inl inline BRPCMessage::~BRPCMessage() { delete this->message; delete this->attachment; delete []this->meta_buf; delete this->meta; } inline int BRPCMessage::encode(struct iovec vectors[], int max, size_t size_limit) { size_t sz = this->meta_len + this->message_len + this->attachment_len; if (sz > 0x7FFFFFFF) { errno = EOVERFLOW; return -1; } int ret; int total; char *p = this->header; memcpy(p, "PRPC", 4); p += 4; *(uint32_t *)(p) = htonl((uint32_t)sz); p += 4; *(uint32_t *)(p) = htonl((uint32_t)this->meta_len); vectors[0].iov_base = this->header; vectors[0].iov_len = BRPC_HEADER_SIZE; vectors[1].iov_base = this->meta_buf; vectors[1].iov_len = this->meta_len; ret = this->message->encode(vectors + 2, max - 2); if (ret < 0) return ret; total = ret; if (this->attachment_len) { ret = this->attachment->encode(vectors + 2 + ret, max - 2 - ret); if (ret < 0) return ret; total += ret; } return 2 + total; } } // namespace srpc #endif srpc-0.10.1/src/message/rpc_message_srpc.cc000066400000000000000000000634371454502251400206200ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include "rpc_basic.h" #include "rpc_compress.h" #include "rpc_meta.pb.h" #include "rpc_message_srpc.h" #include "rpc_zero_copy_stream.h" #include "rpc_module.h" #include "rpc_trace_module.h" namespace srpc { struct SRPCHttpHeadersString { const std::string RPCCompressType = "Content-Encoding"; const std::string OriginSize = "Origin-Size"; const std::string CompressdSize = "Content-Length"; const std::string DataType = "Content-Type"; const std::string SRPCStatus = "SRPC-Status"; const std::string SRPCError = "SRPC-Error"; }; struct CaseCmp { bool operator()(const std::string& lhs, const std::string& rhs) const { return strcasecmp(lhs.c_str(), rhs.c_str()) < 0; } }; static const struct SRPCHttpHeadersString SRPCHttpHeaders; static const std::map SRPCHttpHeadersCode = { {SRPCHttpHeaders.RPCCompressType, 1}, {SRPCHttpHeaders.OriginSize, 2}, {SRPCHttpHeaders.CompressdSize, 3}, {SRPCHttpHeaders.DataType, 4}, {SRPCHttpHeaders.SRPCStatus, 5}, {SRPCHttpHeaders.SRPCError, 6} }; static const std::vector RPCDataTypeString = { "application/x-protobuf", "application/x-thrift", "application/json" }; static const std::vector RPCRPCCompressTypeString = { "identity", "x-snappy", "gzip", "deflate", "x-lz4" }; static constexpr const char *kTypePrefix = "type.googleapis.com"; class ResolverInstance { public: static google::protobuf::util::TypeResolver *get_resolver() { static ResolverInstance kInstance; return kInstance.resolver_; } private: ResolverInstance() { resolver_ = google::protobuf::util::NewTypeResolverForDescriptorPool( kTypePrefix, google::protobuf::DescriptorPool::generated_pool()); } ~ResolverInstance() { delete resolver_; } google::protobuf::util::TypeResolver *resolver_; }; static inline std::string GetTypeUrl(const ProtobufIDLMessage *pb_msg) { return std::string(kTypePrefix) + "/" + pb_msg->GetDescriptor()->full_name(); } SRPCMessage::SRPCMessage() { this->nreceived = 0; this->meta_buf = NULL; this->meta_len = 0; this->message_len = 0; memset(this->header, 0, sizeof (this->header)); this->meta = new RPCMeta(); this->buf = new RPCBuffer(); } int SRPCMessage::append(const void *buf, size_t *size, size_t size_limit) { uint32_t *p; size_t header_left, body_received, buf_len; if (this->nreceived < SRPC_HEADER_SIZE) { //receive header header_left = SRPC_HEADER_SIZE - this->nreceived; if (*size >= header_left) { //receive the whole header and ready to recieve body memcpy(this->header + this->nreceived, buf, header_left); this->nreceived += header_left; p = (uint32_t *)this->header + 1; this->meta_len = ntohl(*p); p = (uint32_t *)this->header + 2; this->message_len = ntohl(*p); buf_len = this->meta_len + this->message_len; if (buf_len >= size_limit) { errno = EMSGSIZE; return -1; } else if (buf_len > 0) { if (*size - header_left > buf_len) *size = header_left + buf_len; this->meta_buf = new char[this->meta_len]; if (*size - header_left <= this->meta_len) { memcpy(this->meta_buf, (const char *)buf + header_left, *size - header_left); } else { memcpy(this->meta_buf, (const char *)buf + header_left, this->meta_len); this->buf->append((const char *)buf + header_left + this->meta_len, *size - header_left - this->meta_len, BUFFER_MODE_COPY); } this->nreceived += *size - header_left; if (this->nreceived == SRPC_HEADER_SIZE + buf_len) return 1; else return 0; } else if (*size == header_left) { return 1; // means body_size == 0 and finish recieved header } else { // means buf_len < 0 errno = EBADMSG; return -1; } } else { // only receive header memcpy(this->header + this->nreceived, buf, *size); this->nreceived += *size; return 0; } } else { // have already received the header and now is for body only body_received = this->nreceived - SRPC_HEADER_SIZE; buf_len = this->meta_len + this->message_len; if (body_received + *size > buf_len) *size = buf_len - body_received; if (body_received + *size <= this->meta_len) { // 100 + 3 <= 106 memcpy(this->meta_buf + body_received, buf, *size); } else if (body_received < this->meta_len) { // 100 + ? > 106, 100 < 106 memcpy(this->meta_buf + body_received, buf, this->meta_len - body_received); if (body_received + *size > this->meta_len)// useless. always true // 100 + 10 > 106 this->buf->append((const char *)buf + this->meta_len - body_received, *size - this->meta_len + body_received, BUFFER_MODE_COPY); } else { // 110 > 106 this->buf->append((const char *)buf, *size, BUFFER_MODE_COPY); } this->nreceived += *size; return this->nreceived == SRPC_HEADER_SIZE + buf_len; } } int SRPCMessage::get_compress_type() const { RPCMeta *meta = static_cast(this->meta); return meta->compress_type(); } int SRPCMessage::get_data_type() const { RPCMeta *meta = static_cast(this->meta); return meta->data_type(); } void SRPCMessage::set_compress_type(int type) { RPCMeta *meta = static_cast(this->meta); meta->set_compress_type(type); } void SRPCMessage::set_data_type(int type) { RPCMeta *meta = static_cast(this->meta); meta->set_data_type(type); } void SRPCMessage::set_attachment_nocopy(const char *attachment, size_t len) { //TODO: } bool SRPCMessage::get_attachment_nocopy(const char **attachment, size_t *len) const { //TODO: return false; } bool SRPCMessage::set_meta_module_data(const RPCModuleData& data) { RPCMeta *meta = static_cast(this->meta); RPCMetaKeyValue *meta_kv; for (const auto& kv : data) { meta_kv = meta->add_trans_info(); if (kv.first == SRPC_TRACE_ID) { meta_kv->set_key(SRPC_TRACE_ID); meta_kv->set_bytes_value(kv.second.c_str(), SRPC_TRACEID_SIZE); } else if (kv.first == SRPC_SPAN_ID) { meta_kv->set_key(SRPC_SPAN_ID); meta_kv->set_bytes_value(kv.second.c_str(), SRPC_SPANID_SIZE); } else { meta_kv->set_key(kv.first); meta_kv->set_bytes_value(kv.second); } } return true; } bool SRPCMessage::get_meta_module_data(RPCModuleData& data) const { RPCMeta *meta = static_cast(this->meta); RPCMetaKeyValue *meta_kv; for (int i = 0; i < meta->trans_info_size(); i++) { meta_kv = meta->mutable_trans_info(i); if (meta_kv->key() == SRPC_TRACE_ID) data[SRPC_TRACE_ID] = meta_kv->bytes_value(); else if (meta_kv->key() == SRPC_SPAN_ID) data[SRPC_SPAN_ID] = meta_kv->bytes_value(); else if (meta_kv->key() == SRPC_PARENT_SPAN_ID) data[SRPC_PARENT_SPAN_ID] = meta_kv->bytes_value(); else data[meta_kv->key()] = meta_kv->bytes_value(); } return true; } const std::string& SRPCRequest::get_service_name() const { RPCMeta *meta = static_cast(this->meta); return meta->mutable_request()->service_name(); } const std::string& SRPCRequest::get_method_name() const { RPCMeta *meta = static_cast(this->meta); return meta->mutable_request()->method_name(); } void SRPCRequest::set_service_name(const std::string& service_name) { RPCMeta *meta = static_cast(this->meta); meta->mutable_request()->set_service_name(service_name); } void SRPCRequest::set_method_name(const std::string& method_name) { RPCMeta *meta = static_cast(this->meta); meta->mutable_request()->set_method_name(method_name); } int SRPCResponse::get_status_code() const { RPCMeta *meta = static_cast(this->meta); return meta->mutable_response()->status_code(); } void SRPCResponse::set_status_code(int code) { RPCMeta *meta = static_cast(this->meta); meta->mutable_response()->set_status_code(code); } int SRPCResponse::get_error() const { RPCMeta *meta = static_cast(this->meta); return meta->mutable_response()->error(); } const char *SRPCResponse::get_errmsg() const { switch (this->get_status_code()) { case RPCStatusOK: return "OK"; case RPCStatusUndefined: return "Undefined Error"; case RPCStatusServiceNotFound: return "Service Not Found"; case RPCStatusMethodNotFound: return "Method Not Found"; case RPCStatusMetaError: return "Meta Error"; case RPCStatusReqCompressSizeInvalid: return "Request Compress-size Invalid"; case RPCStatusReqDecompressSizeInvalid: return "Request Decompress-size Invalid"; case RPCStatusReqCompressNotSupported: return "Request Compress Not Supported"; case RPCStatusReqDecompressNotSupported: return "Request Decompress Not Supported"; case RPCStatusReqCompressError: return "Request Compress Error"; case RPCStatusReqDecompressError: return "Request Decompress Error"; case RPCStatusReqSerializeError: return "Request Serialize Error"; case RPCStatusReqDeserializeError: return "Request Deserialize Error"; case RPCStatusRespCompressSizeInvalid: return "Response Compress-size Invalid"; case RPCStatusRespDecompressSizeInvalid: return "Response Decompress-size Invalid"; case RPCStatusRespCompressNotSupported: return "Response Compress Not Supported"; case RPCStatusRespDecompressNotSupported: return "Response Decompress Not Supported"; case RPCStatusRespCompressError: return "Response Compress Error"; case RPCStatusRespDecompressError: return "Response Decompress Error"; case RPCStatusRespSerializeError: return "Response Serialize Error"; case RPCStatusRespDeserializeError: return "Response Deserialize Error"; case RPCStatusIDLSerializeNotSupported: return "IDL Serialize Not Supported"; case RPCStatusIDLDeserializeNotSupported: return "IDL Deserialize Not Supported"; case RPCStatusURIInvalid: return "URI Invalid"; case RPCStatusUpstreamFailed: return "Upstream Failed"; case RPCStatusSystemError: return "System Error. Use get_error() to get errno"; case RPCStatusSSLError: return "SSL Error. Use get_error() to get SSL-Error"; case RPCStatusDNSError: return "DNS Error. Use get_error() to get GAI-Error"; case RPCStatusProcessTerminated: return "Process Terminated"; default: return "Unknown Error"; } } void SRPCResponse::set_error(int error) { RPCMeta *meta = static_cast(this->meta); meta->mutable_response()->set_error(error); } int SRPCMessage::serialize(const ProtobufIDLMessage *pb_msg) { using namespace google::protobuf; RPCMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); int data_type = meta->data_type(); int ret; if (!pb_msg) return RPCStatusOK; RPCOutputStream output_stream(this->buf, pb_msg->ByteSizeLong()); if (data_type == RPCDataProtobuf) { ret = pb_msg->SerializeToZeroCopyStream(&output_stream) ? 0 : -1; this->message_len = this->buf->size(); } else if (data_type == RPCDataJson) { std::string binary_input = pb_msg->SerializeAsString(); io::ArrayInputStream input_stream(binary_input.data(), (int)binary_input.size()); const auto *pool = pb_msg->GetDescriptor()->file()->pool(); auto *resolver = (pool == DescriptorPool::generated_pool() ? ResolverInstance::get_resolver() : util::NewTypeResolverForDescriptorPool(kTypePrefix, pool)); util::JsonOptions options; options.add_whitespace = this->get_json_add_whitespace(); options.always_print_enums_as_ints = this->get_json_enums_as_ints(); options.preserve_proto_field_names = this->get_json_preserve_names(); options.always_print_primitive_fields = this->get_json_print_primitive(); ret = BinaryToJsonStream(resolver, GetTypeUrl(pb_msg), &input_stream, &output_stream, options).ok() ? 0 : -1; if (pool != DescriptorPool::generated_pool()) delete resolver; this->message_len = this->buf->size(); } else ret = -1; if (ret < 0) return is_resp ? RPCStatusRespSerializeError : RPCStatusReqSerializeError; return RPCStatusOK; } int SRPCMessage::deserialize(ProtobufIDLMessage *pb_msg) { using namespace google::protobuf; const RPCMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); int data_type = meta->data_type(); int ret; RPCInputStream input_stream(this->buf); if (data_type == RPCDataProtobuf) ret = pb_msg->ParseFromZeroCopyStream(&input_stream) ? 0 : -1; else if (data_type == RPCDataJson) { std::string binary_output; io::StringOutputStream output_stream(&binary_output); const auto *pool = pb_msg->GetDescriptor()->file()->pool(); auto *resolver = (pool == DescriptorPool::generated_pool() ? ResolverInstance::get_resolver() : util::NewTypeResolverForDescriptorPool(kTypePrefix, pool)); if (JsonToBinaryStream(resolver, GetTypeUrl(pb_msg), &input_stream, &output_stream).ok()) { ret = pb_msg->ParseFromString(binary_output) ? 0 : -1; } else ret = -1; if (pool != DescriptorPool::generated_pool()) delete resolver; } else ret = -1; if (ret < 0) return is_resp ? RPCStatusRespDeserializeError : RPCStatusReqDeserializeError; return RPCStatusOK; } int SRPCMessage::serialize(const ThriftIDLMessage *thrift_msg) { RPCMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); int data_type = meta->data_type(); int ret; if (!thrift_msg) return RPCStatusOK; ThriftBuffer thrift_buffer(this->buf); if (data_type == RPCDataThrift) ret = thrift_msg->descriptor->writer(thrift_msg, &thrift_buffer) ? 0 : -1; else if (data_type == RPCDataJson) ret = thrift_msg->descriptor->json_writer(thrift_msg, &thrift_buffer) ? 0 : -1; else ret = -1; if (ret < 0) return is_resp ? RPCStatusRespSerializeError : RPCStatusReqSerializeError; this->message_len = this->buf->size(); return RPCStatusOK; } int SRPCMessage::deserialize(ThriftIDLMessage *thrift_msg) { const RPCMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); int data_type = meta->data_type(); int ret; if (this->buf->size() == 0 || this->message_len == 0) return is_resp ? RPCStatusRespDeserializeError : RPCStatusReqDeserializeError; ThriftBuffer thrift_buffer(this->buf); if (data_type == RPCDataThrift) ret = thrift_msg->descriptor->reader(&thrift_buffer, thrift_msg) ? 0 : 1; else if (data_type == RPCDataJson) ret = thrift_msg->descriptor->json_reader(&thrift_buffer, thrift_msg) ? 0 : 1; else ret = -1; if (ret < 0) return is_resp ? RPCStatusRespDeserializeError : RPCStatusReqDeserializeError; return RPCStatusOK; } int SRPCMessage::compress() { RPCMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); int type = meta->compress_type(); size_t buflen = this->message_len; int origin_size; int status_code = RPCStatusOK; if (buflen == 0) { if (type != RPCCompressNone) { meta->set_origin_size(0); meta->set_compressed_size(0); } return status_code; } if (type == RPCCompressNone) return status_code; if (buflen > 0x7FFFFFFF) { return is_resp ? RPCStatusRespCompressSizeInvalid : RPCStatusReqCompressSizeInvalid; } origin_size = (int)buflen; static RPCCompressor *compressor = RPCCompressor::get_instance(); int ret = compressor->lease_compressed_size(type, buflen); if (ret == -2) { return is_resp ? RPCStatusReqCompressNotSupported : RPCStatusRespCompressNotSupported; } else if (ret <= 0) { return is_resp ? RPCStatusRespCompressSizeInvalid : RPCStatusReqCompressSizeInvalid; } buflen = ret; if (this->buf->size() != this->message_len) return is_resp ? RPCStatusRespCompressError : RPCStatusReqCompressError; RPCBuffer *dst_buf = new RPCBuffer(); ret = compressor->serialize_to_compressed(this->buf, dst_buf, type); if (ret == -2) { status_code = is_resp ? RPCStatusRespCompressNotSupported : RPCStatusReqCompressNotSupported; } else if (ret == -1) { status_code = is_resp ? RPCStatusRespCompressError : RPCStatusReqCompressError; } else if (ret <= 0) { status_code = is_resp ? RPCStatusRespCompressSizeInvalid : RPCStatusReqCompressSizeInvalid; } else { meta->set_origin_size(origin_size); meta->set_compressed_size(ret); buflen = ret; } if (status_code == RPCStatusOK) { delete this->buf; this->buf = dst_buf; this->message_len = buflen; } else delete dst_buf; return status_code; } int SRPCMessage::decompress() { const RPCMeta *meta = static_cast(this->meta); bool is_resp = !meta->has_request(); int type = meta->compress_type(); int status_code = RPCStatusOK; if (this->message_len == 0 || type == RPCCompressNone) return status_code; if (meta->compressed_size() == 0) { return is_resp ? RPCStatusRespDecompressSizeInvalid : RPCStatusReqDecompressSizeInvalid; } if (this->buf->size() != (size_t)meta->compressed_size()) return is_resp ? RPCStatusRespCompressError : RPCStatusReqCompressError; RPCBuffer *dst_buf = new RPCBuffer(); static RPCCompressor *compressor = RPCCompressor::get_instance(); int ret = compressor->parse_from_compressed(this->buf, dst_buf, type); if (ret == -2) { status_code = is_resp ? RPCStatusRespDecompressNotSupported : RPCStatusReqDecompressNotSupported; } else if (ret == -1) { status_code = is_resp ? RPCStatusRespDecompressError : RPCStatusReqDecompressError; } else if (ret <= 0 || (meta->has_origin_size() && ret != meta->origin_size())) { status_code = is_resp ? RPCStatusRespDecompressSizeInvalid : RPCStatusReqDecompressSizeInvalid; } if (status_code == RPCStatusOK) { delete this->buf; this->buf = dst_buf; this->message_len = ret; } else delete dst_buf; return status_code; } static bool __deserialize_meta_http(const char *request_uri, protocol::HttpMessage *http_msg, SRPCMessage *srpc_msg, ProtobufIDLMessage *pb_msg) { RPCMeta *meta = static_cast(pb_msg); protocol::HttpHeaderCursor header_cursor(http_msg); std::string key, value; while (header_cursor.next(key, value)) { const auto it = SRPCHttpHeadersCode.find(key); if (it != SRPCHttpHeadersCode.cend()) { switch (it->second) { case 1: for (size_t i = 0; i < RPCRPCCompressTypeString.size(); i++) { if (strcasecmp(RPCRPCCompressTypeString[i].c_str(), value.c_str()) == 0) { meta->set_compress_type(i); break; } } break; case 2: meta->set_origin_size(atoi(value.c_str())); break; case 4: for (size_t i = 0; i < RPCDataTypeString.size(); i++) { if (strcasecmp(RPCDataTypeString[i].c_str(), value.c_str()) == 0) { meta->set_data_type(i); break; } } break; default: continue; } } } if (request_uri && request_uri[0] == '/') { std::string str = request_uri + 1; auto pos = str.find_first_of("?#"); if (pos != std::string::npos) str.erase(pos); if (!str.empty() && str.back() == '/') str.pop_back(); for (char& c : str) { if (c == '/') c = '.'; } pos = str.find_last_of('.'); if (pos != std::string::npos) { meta->mutable_request()->set_service_name(str.substr(0, pos)); meta->mutable_request()->set_method_name(str.substr(pos + 1)); } } const void *ptr; size_t len; http_msg->get_parsed_body(&ptr, &len); if (len > 0x7FFFFFFF) return false; protocol::HttpChunkCursor chunk_cursor(http_msg); RPCBuffer *buf = srpc_msg->get_buffer(); size_t msg_len = 0; while (chunk_cursor.next(&ptr, &len)) { buf->append((const char *)ptr, len, BUFFER_MODE_NOCOPY); msg_len += len; } srpc_msg->set_message_len(msg_len); if (!meta->has_data_type()) meta->set_data_type(RPCDataJson); if (!meta->has_compress_type()) meta->set_compress_type(RPCCompressNone); if (meta->compress_type() == RPCCompressNone) { if (msg_len == 0 && meta->data_type() == RPCDataJson) { buf->append("{}", 2, BUFFER_MODE_NOCOPY); srpc_msg->set_message_len(2); } } else meta->set_compressed_size(msg_len); return true; } bool SRPCHttpRequest::serialize_meta() { if (this->buf->size() > 0x7FFFFFFF) return false; RPCMeta *meta = static_cast(this->meta); int data_type = meta->data_type(); int compress_type = meta->compress_type(); set_http_version("HTTP/1.1"); set_method("POST"); set_request_uri("/" + meta->mutable_request()->service_name() + "/" + meta->mutable_request()->method_name()); //set header set_header_pair(SRPCHttpHeaders.DataType, RPCDataTypeString[data_type]); set_header_pair(SRPCHttpHeaders.RPCCompressType, RPCRPCCompressTypeString[compress_type]); if (compress_type != RPCCompressNone) { set_header_pair(SRPCHttpHeaders.CompressdSize, std::to_string(meta->compressed_size())); set_header_pair(SRPCHttpHeaders.OriginSize, std::to_string(meta->origin_size())); } else { set_header_pair("Content-Length", std::to_string(this->message_len)); } set_header_pair("Connection", "Keep-Alive"); const void *buffer; size_t buflen; //append body while (buflen = this->buf->fetch(&buffer), buffer && buflen > 0) this->append_output_body_nocopy(buffer, buflen); return true; } bool SRPCHttpRequest::deserialize_meta() { const char *request_uri = this->get_request_uri(); return __deserialize_meta_http(request_uri, this, this, this->meta); } bool SRPCHttpResponse::serialize_meta() { if (this->buf->size() > 0x7FFFFFFF) return false; RPCMeta *meta = static_cast(this->meta); int data_type = meta->data_type(); int compress_type = meta->compress_type(); int rpc_status_code = this->get_status_code(); const char *http_status_code = this->protocol::HttpResponse::get_status_code(); set_http_version("HTTP/1.1"); if (rpc_status_code == RPCStatusOK) { if (http_status_code) protocol::HttpUtil::set_response_status(this, atoi(http_status_code)); else protocol::HttpUtil::set_response_status(this, HttpStatusOK); } else if (rpc_status_code == RPCStatusServiceNotFound || rpc_status_code == RPCStatusMethodNotFound || rpc_status_code == RPCStatusMetaError || rpc_status_code == RPCStatusURIInvalid) { protocol::HttpUtil::set_response_status(this, HttpStatusBadRequest); } else if (rpc_status_code == RPCStatusRespCompressNotSupported || rpc_status_code == RPCStatusRespDecompressNotSupported || rpc_status_code == RPCStatusIDLSerializeNotSupported || rpc_status_code == RPCStatusIDLDeserializeNotSupported) { protocol::HttpUtil::set_response_status(this, HttpStatusNotImplemented); } else if (rpc_status_code == RPCStatusUpstreamFailed) { protocol::HttpUtil::set_response_status(this, HttpStatusServiceUnavailable); } else { protocol::HttpUtil::set_response_status(this, HttpStatusInternalServerError); } //set header set_header_pair(SRPCHttpHeaders.SRPCStatus, std::to_string(meta->mutable_response()->status_code())); set_header_pair(SRPCHttpHeaders.SRPCError, std::to_string(meta->mutable_response()->error())); set_header_pair(SRPCHttpHeaders.DataType, RPCDataTypeString[data_type]); set_header_pair(SRPCHttpHeaders.RPCCompressType, RPCRPCCompressTypeString[compress_type]); if (compress_type != RPCCompressNone) { set_header_pair(SRPCHttpHeaders.CompressdSize, std::to_string(meta->compressed_size())); set_header_pair(SRPCHttpHeaders.OriginSize, std::to_string(meta->origin_size())); } else { set_header_pair("Content-Length", std::to_string(this->message_len)); } set_header_pair("Connection", "Keep-Alive"); const void *buffer; size_t buflen; //append body while (buflen = this->buf->fetch(&buffer), buffer && buflen > 0) this->append_output_body_nocopy(buffer, buflen); return true; } bool SRPCHttpResponse::deserialize_meta() { return __deserialize_meta_http(NULL, this, this, this->meta); } bool SRPCHttpRequest::set_meta_module_data(const RPCModuleData& data) { return http_set_header_module_data(data, this); } bool SRPCHttpRequest::get_meta_module_data(RPCModuleData& data) const { return http_get_header_module_data(this, data); } bool SRPCHttpResponse::set_meta_module_data(const RPCModuleData& data) { return http_set_header_module_data(data, this); } bool SRPCHttpResponse::get_meta_module_data(RPCModuleData& data) const { return http_get_header_module_data(this, data); } bool SRPCHttpRequest::set_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::set_header_pair(name, value); } bool SRPCHttpRequest::add_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::add_header_pair(name, value); } bool SRPCHttpRequest::get_http_header(const std::string& name, std::string& value) const { protocol::HttpHeaderCursor cursor(this); return cursor.find(name, value); } bool SRPCHttpResponse::set_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::set_header_pair(name, value); } bool SRPCHttpResponse::add_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::add_header_pair(name, value); } bool SRPCHttpResponse::get_http_header(const std::string& name, std::string& value) const { protocol::HttpHeaderCursor cursor(this); return cursor.find(name, value); } } // namespace srpc srpc-0.10.1/src/message/rpc_message_srpc.h000066400000000000000000000214761454502251400204570ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_MESSAGE_SRPC_H__ #define __RPC_MESSAGE_SRPC_H__ #ifdef _WIN32 #include #else #include #endif #include #include "rpc_message.h" #include "rpc_basic.h" #include "rpc_thrift_idl.h" #include "rpc_buffer.h" namespace srpc { static constexpr int SRPC_HEADER_SIZE = 16; // define srpc protocol class SRPCMessage : public RPCMessage { public: SRPCMessage(); virtual ~SRPCMessage(); int encode(struct iovec vectors[], int max, size_t size_limit); int append(const void *buf, size_t *size, size_t size_limit); bool serialize_meta(); bool deserialize_meta(); int get_compress_type() const override; int get_data_type() const override; void set_compress_type(int type) override; void set_data_type(int type) override; void set_attachment_nocopy(const char *attachment, size_t len); bool get_attachment_nocopy(const char **attachment, size_t *len) const; bool set_meta_module_data(const RPCModuleData& data) override; bool get_meta_module_data(RPCModuleData& data) const override; public: using RPCMessage::serialize; using RPCMessage::deserialize; int serialize(const ProtobufIDLMessage *pb_msg) override; int deserialize(ProtobufIDLMessage *pb_msg) override; int serialize(const ThriftIDLMessage *thrift_msg) override; int deserialize(ThriftIDLMessage *thrift_msg) override; int compress() override; int decompress() override; public: RPCBuffer *get_buffer() const { return this->buf; } size_t get_message_len() const { return this->message_len; } void set_message_len(size_t len) { this->message_len = len; } protected: void init_meta(); // "SRPC" + META_LEN + MESSAGE_LEN + RESERVED char header[SRPC_HEADER_SIZE]; RPCBuffer *buf; char *meta_buf; size_t nreceived; size_t meta_len; size_t message_len; ProtobufIDLMessage *meta; }; class SRPCRequest : public SRPCMessage { public: const std::string& get_service_name() const; const std::string& get_method_name() const; void set_service_name(const std::string& service_name); void set_method_name(const std::string& method_name); }; class SRPCResponse : public SRPCMessage { public: int get_status_code() const; int get_error() const; const char *get_errmsg() const; void set_status_code(int code); void set_error(int error); }; class SRPCStdRequest : public protocol::ProtocolMessage, public RPCRequest, public SRPCRequest { public: int encode(struct iovec vectors[], int max) override { return this->SRPCRequest::encode(vectors, max, this->size_limit); } int append(const void *buf, size_t *size) override { return this->SRPCRequest::append(buf, size, this->size_limit); } public: bool serialize_meta() override { return this->SRPCRequest::serialize_meta(); } bool deserialize_meta() override { return this->SRPCRequest::deserialize_meta(); } public: const std::string& get_service_name() const override { return this->SRPCRequest::get_service_name(); } const std::string& get_method_name() const override { return this->SRPCRequest::get_method_name(); } void set_service_name(const std::string& service_name) override { return this->SRPCRequest::set_service_name(service_name); } void set_method_name(const std::string& method_name) override { return this->SRPCRequest::set_method_name(method_name); } bool set_meta_module_data(const RPCModuleData& data) override { return this->SRPCMessage::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->SRPCMessage::get_meta_module_data(data); } public: SRPCStdRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class SRPCStdResponse : public protocol::ProtocolMessage, public RPCResponse, public SRPCResponse { public: int encode(struct iovec vectors[], int max) override { return this->SRPCResponse::encode(vectors, max, this->size_limit); } int append(const void *buf, size_t *size) override { return this->SRPCResponse::append(buf, size, this->size_limit); } public: bool serialize_meta() override { return this->SRPCResponse::serialize_meta(); } bool deserialize_meta() override { return this->SRPCResponse::deserialize_meta(); } public: int get_status_code() const override { return this->SRPCResponse::get_status_code(); } int get_error() const override { return this->SRPCResponse::get_error(); } const char *get_errmsg() const override { return this->SRPCResponse::get_errmsg(); } void set_status_code(int code) override { return this->SRPCResponse::set_status_code(code); } void set_error(int error) override { return this->SRPCResponse::set_error(error); } bool set_meta_module_data(const RPCModuleData& data) override { return this->SRPCMessage::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->SRPCMessage::get_meta_module_data(data); } public: SRPCStdResponse() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class SRPCHttpRequest : public protocol::HttpRequest, public RPCRequest, public SRPCRequest { public: bool serialize_meta() override; bool deserialize_meta() override; public: const std::string& get_service_name() const override { return this->SRPCRequest::get_service_name(); } const std::string& get_method_name() const override { return this->SRPCRequest::get_method_name(); } void set_service_name(const std::string& service_name) override { return this->SRPCRequest::set_service_name(service_name); } void set_method_name(const std::string& method_name) override { return this->SRPCRequest::set_method_name(method_name); } bool set_meta_module_data(const RPCModuleData& data) override; bool get_meta_module_data(RPCModuleData& data) const override; bool set_http_header(const std::string& name, const std::string& value) override; bool add_http_header(const std::string& name, const std::string& value) override; bool get_http_header(const std::string& name, std::string& value) const override; public: SRPCHttpRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class SRPCHttpResponse : public protocol::HttpResponse, public RPCResponse, public SRPCResponse { public: bool serialize_meta() override; bool deserialize_meta() override; public: int get_status_code() const override { return this->SRPCResponse::get_status_code(); } int get_error() const override { return this->SRPCResponse::get_error(); } const char *get_errmsg() const override { return this->SRPCResponse::get_errmsg(); } void set_status_code(int code) override { return this->SRPCResponse::set_status_code(code); } void set_error(int error) override { return this->SRPCResponse::set_error(error); } bool set_http_code(int code) override { return this->protocol::HttpResponse::set_status_code(std::to_string(code)); } bool set_meta_module_data(const RPCModuleData& data) override; bool get_meta_module_data(RPCModuleData& data) const override; bool set_http_header(const std::string& name, const std::string& value) override; bool add_http_header(const std::string& name, const std::string& value) override; bool get_http_header(const std::string& name, std::string& value) const override; public: SRPCHttpResponse() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; //////// // inl inline SRPCMessage::~SRPCMessage() { delete []this->meta_buf; delete this->meta; delete this->buf; } inline int SRPCMessage::encode(struct iovec vectors[], int max, size_t size_limit) { if (this->message_len > 0x7FFFFFFF) { errno = EOVERFLOW; return -1; } char *p = this->header; memcpy(p, "SRPC", 4); p += 4; *(uint32_t *)(p) = htonl((uint32_t)this->meta_len); p += 4; *(uint32_t *)(p) = htonl((uint32_t)this->message_len); vectors[0].iov_base = this->header; vectors[0].iov_len = SRPC_HEADER_SIZE; vectors[1].iov_base = this->meta_buf; vectors[1].iov_len = this->meta_len; int ret = this->buf->encode(vectors + 2, max - 2); return ret < 0 ? ret : ret + 2; } inline bool SRPCMessage::serialize_meta() { this->meta_len = this->meta->ByteSizeLong(); this->meta_buf = new char[this->meta_len]; return this->meta->SerializeToArray(this->meta_buf, (int)this->meta_len); } inline bool SRPCMessage::deserialize_meta() { return this->meta->ParseFromArray(this->meta_buf, (int)this->meta_len); } } // namespace srpc #endif srpc-0.10.1/src/message/rpc_message_thrift.cc000066400000000000000000000215701454502251400211410ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include "rpc_message_thrift.h" namespace srpc { static int thrift_parser_append_message(const void *buf, size_t *size, ThriftBuffer *TBuffer) { if (TBuffer->status == THRIFT_PARSE_END) { *size = 0; return 1; } /* if (TBuffer->status == THRIFT_PARSE_INIT) TBuffer->status = THRIFT_GET_FRAME_SIZE; */ if (TBuffer->status == THRIFT_GET_FRAME_SIZE) { size_t framesize_bytelen = sizeof (TBuffer->framesize); char *readbuf = (char*)&TBuffer->framesize; size_t read_size = 0; for (size_t i = 0; i < *size; i++) { read_size++; ((char *)readbuf)[TBuffer->framesize_read_byte] = ((char *)buf)[i]; if (++TBuffer->framesize_read_byte == framesize_bytelen) { TBuffer->status = THRIFT_GET_DATA; TBuffer->framesize = ntohl(TBuffer->framesize); if (TBuffer->framesize < 0) { errno = EBADMSG; return -1; } //TBuffer->readbuf = new char[TBuffer->framesize]; break; } } size_t left_size = *size - read_size; *size = read_size; if (left_size == 0) { if (TBuffer->status == THRIFT_GET_DATA && TBuffer->framesize == 0) { TBuffer->status = THRIFT_PARSE_END; return 1; } else return 0; } else { int ret = thrift_parser_append_message((char *)buf + read_size, &left_size, TBuffer); *size += left_size; return ret; } } else if (TBuffer->status == THRIFT_GET_DATA) { size_t read_size = *size; if (TBuffer->readbuf_size + *size > (size_t)TBuffer->framesize) read_size = TBuffer->framesize - TBuffer->readbuf_size; TBuffer->buffer->append((const char *)buf, read_size, BUFFER_MODE_COPY); TBuffer->readbuf_size += read_size; *size = read_size; if (TBuffer->readbuf_size < (size_t)TBuffer->framesize) return 0; else if (TBuffer->readbuf_size == (uint32_t)TBuffer->framesize) { TBuffer->status = THRIFT_PARSE_END; return 1; } } errno = EBADMSG; return -1; } int ThriftMessage::append(const void *buf, size_t *size, size_t size_limit) { return thrift_parser_append_message(buf, size, &TBuffer_); } bool ThriftResponse::serialize_meta() { if (status_code_ == RPCStatusOK) TBuffer_.meta.message_type = TMT_REPLY; else { ThriftException ex; ex.type = (status_code_ == RPCStatusMethodNotFound ? TET_UNKNOWN_METHOD : TET_UNKNOWN); ex.message = errmsg_; ex.descriptor->writer(&ex, &TBuffer_); TBuffer_.meta.message_type = TMT_EXCEPTION; } return TBuffer_.writeMessageBegin(); } const char *thrift_error2errmsg(int error) { switch (error) { case TET_UNKNOWN: return "TApplicationException: Unknown application exception"; case TET_UNKNOWN_METHOD: return "TApplicationException: Unknown method"; case TET_INVALID_MESSAGE_TYPE: return "TApplicationException: Invalid message type"; case TET_WRONG_METHOD_NAME: return "TApplicationException: Wrong method name"; case TET_BAD_SEQUENCE_ID: return "TApplicationException: Bad sequence identifier"; case TET_MISSING_RESULT: return "TApplicationException: Missing result"; case TET_INTERNAL_ERROR: return "TApplicationException: Internal error"; case TET_PROTOCOL_ERROR: return "TApplicationException: Protocol error"; case TET_INVALID_TRANSFORM: return "TApplicationException: Invalid transform"; case TET_INVALID_PROTOCOL: return "TApplicationException: Invalid protocol"; case TET_UNSUPPORTED_CLIENT_TYPE: return "TApplicationException: Unsupported client type"; default: return "TApplicationException: (Invalid exception type)"; }; } bool ThriftResponse::deserialize_meta() { if (TBuffer_.readMessageBegin()) { if (TBuffer_.meta.message_type == TMT_EXCEPTION) { ThriftException ex; if (ex.descriptor->reader(&TBuffer_, &ex)) { status_code_ = (ex.type == TET_UNKNOWN_METHOD ? RPCStatusMethodNotFound : RPCStatusMetaError); error_ = ex.type; errmsg_ = ex.message; } else { status_code_ = RPCStatusMetaError; error_ = TET_INTERNAL_ERROR; errmsg_ = thrift_error2errmsg(error_); } } return true; } return false; } const char *ThriftResponse::get_errmsg() const { if (!errmsg_.empty()) return errmsg_.c_str(); return thrift_error2errmsg(error_); } bool ThriftHttpRequest::serialize_meta() { if (buf_.size() > 0x7FFFFFFF) return false; if (!this->ThriftRequest::serialize_meta()) return false; set_http_version("HTTP/1.1"); set_method("POST"); set_request_uri("/"); set_header_pair("Connection", "Keep-Alive"); set_header_pair("Content-Type", "application/x-thrift"); set_header_pair("Content-Length", std::to_string(TBuffer_.meta.writebuf.size() + buf_.size())); this->append_output_body_nocopy(TBuffer_.meta.writebuf.c_str(), TBuffer_.meta.writebuf.size()); const void *buf; size_t buflen; while (buflen = buf_.fetch(&buf), buf && buflen > 0) this->append_output_body_nocopy(buf, buflen); return true; } bool ThriftHttpRequest::deserialize_meta() { const void *body; size_t body_len; get_parsed_body(&body, &body_len); if (body_len > 0x7FFFFFFF) return false; buf_.append((const char *)body, body_len, BUFFER_MODE_NOCOPY); TBuffer_.framesize = (int32_t)body_len; return this->ThriftRequest::deserialize_meta(); } bool ThriftHttpResponse::serialize_meta() { if (buf_.size() > 0x7FFFFFFF) return false; if (!this->ThriftResponse::serialize_meta()) return false; int rpc_status_code = this->get_status_code(); const char *http_status_code = this->protocol::HttpResponse::get_status_code(); set_http_version("HTTP/1.1"); if (rpc_status_code == RPCStatusOK) { if (http_status_code) protocol::HttpUtil::set_response_status(this, atoi(http_status_code)); else protocol::HttpUtil::set_response_status(this, HttpStatusOK); } else if (rpc_status_code == RPCStatusServiceNotFound || rpc_status_code == RPCStatusMethodNotFound || rpc_status_code == RPCStatusMetaError || rpc_status_code == RPCStatusURIInvalid) protocol::HttpUtil::set_response_status(this, HttpStatusBadRequest); else if (rpc_status_code == RPCStatusRespCompressNotSupported || rpc_status_code == RPCStatusRespDecompressNotSupported || rpc_status_code == RPCStatusIDLSerializeNotSupported || rpc_status_code == RPCStatusIDLDeserializeNotSupported) protocol::HttpUtil::set_response_status(this, HttpStatusNotImplemented); else if (rpc_status_code == RPCStatusUpstreamFailed) protocol::HttpUtil::set_response_status(this, HttpStatusServiceUnavailable); else protocol::HttpUtil::set_response_status(this, HttpStatusInternalServerError); set_header_pair("Connection", "Keep-Alive"); set_header_pair("Content-Type", "application/x-thrift"); set_header_pair("Content-Length", std::to_string(TBuffer_.meta.writebuf.size() + buf_.size())); this->append_output_body_nocopy(TBuffer_.meta.writebuf.c_str(), TBuffer_.meta.writebuf.size()); const void *buf; size_t buflen; while (buflen = buf_.fetch(&buf), buf && buflen > 0) this->append_output_body_nocopy(buf, buflen); return true; } bool ThriftHttpResponse::deserialize_meta() { const void *body; size_t body_len; get_parsed_body(&body, &body_len); if (body_len > 0x7FFFFFFF) return false; buf_.append((const char *)body, body_len, BUFFER_MODE_NOCOPY); TBuffer_.framesize = (int32_t)body_len; return this->ThriftResponse::deserialize_meta(); } bool ThriftHttpRequest::set_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::set_header_pair(name, value); } bool ThriftHttpRequest::add_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::add_header_pair(name, value); } bool ThriftHttpRequest::get_http_header(const std::string& name, std::string& value) const { protocol::HttpHeaderCursor cursor(this); return cursor.find(name, value); } bool ThriftHttpResponse::set_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::set_header_pair(name, value); } bool ThriftHttpResponse::add_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::add_header_pair(name, value); } bool ThriftHttpResponse::get_http_header(const std::string& name, std::string& value) const { protocol::HttpHeaderCursor cursor(this); return cursor.find(name, value); } } // namespace srpc srpc-0.10.1/src/message/rpc_message_thrift.h000066400000000000000000000262311454502251400210020ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_MESSAGE_THRIFT_H__ #define __RPC_MESSAGE_THRIFT_H__ #include #include #include "rpc_message.h" #include "rpc_thrift_idl.h" namespace srpc { class ThriftException : public ThriftIDLMessage { public: std::string message; int32_t type; public: struct ISSET { bool message = true; bool type = true; }__isset; ThriftException() { this->elements = ThriftElementsImpl::get_elements_instance(); this->descriptor = ThriftDescriptorImpl::get_instance(); } static void StaticElementsImpl(std::list *elements) { const ThriftException *st = (const ThriftException *)0; const char *base = (const char *)st; using subtype_1 = ThriftDescriptorImpl; using subtype_2 = ThriftDescriptorImpl; elements->push_back({subtype_1::get_instance(), "message", (const char *)(&st->__isset.message) - base, (const char *)(&st->message) - base, 1}); elements->push_back({subtype_2::get_instance(), "type", (const char *)(&st->__isset.type) - base, (const char *)(&st->type) - base, 2}); } }; class ThriftMessage : public RPCMessage { public: ThriftMessage() : TBuffer_(&buf_) { } virtual ~ThriftMessage() { } //copy constructor ThriftMessage(const ThriftMessage&) = delete; //copy operator ThriftMessage& operator= (const ThriftMessage&) = delete; //move constructor ThriftMessage(ThriftMessage&&) = delete; //move operator ThriftMessage& operator= (ThriftMessage&&) = delete; public: int get_compress_type() const override { return RPCCompressNone; } int get_data_type() const override { return RPCDataThrift; } void set_compress_type(int type) override {} void set_data_type(int type) override {} void set_attachment_nocopy(const char *attachment, size_t len) { } bool get_attachment_nocopy(const char **attachment, size_t *len) const { return false; } public: int serialize(const ThriftIDLMessage *thrift_msg) override; int deserialize(ThriftIDLMessage *thrift_msg) override; int compress() override { return RPCStatusOK; } int decompress() override { return RPCStatusOK; } bool get_meta_module_data(RPCModuleData& data) const override { return false; } bool set_meta_module_data(const RPCModuleData& data) override { return false; } public: const ThriftMeta *get_meta() const { return &TBuffer_.meta; } ThriftMeta *get_meta() { return &TBuffer_.meta; } protected: int encode(struct iovec vectors[], int max, size_t size_limit); int append(const void *buf, size_t *size, size_t size_limit); RPCBuffer buf_; ThriftBuffer TBuffer_; }; class ThriftRequest : public ThriftMessage { public: bool serialize_meta() { return TBuffer_.writeMessageBegin(); } bool deserialize_meta() { return TBuffer_.readMessageBegin(); } public: const std::string& get_service_name() const { return TBuffer_.meta.method_name; } const std::string& get_method_name() const { return TBuffer_.meta.method_name; } void set_service_name(const std::string& service_name) { } void set_method_name(const std::string& method_name) { TBuffer_.meta.method_name = method_name; } void set_seqid(long long seqid) { TBuffer_.meta.seqid = (int)seqid; } }; class ThriftResponse : public ThriftMessage { public: bool serialize_meta(); bool deserialize_meta(); bool get_meta_module_data(RPCModuleData& data) const override { return false; } bool set_meta_module_data(const RPCModuleData& data) override { return false; } public: int get_status_code() const { return status_code_; } int get_error() const { return error_; } const char *get_errmsg() const; void set_status_code(int code) { status_code_ = code; } void set_error(int error) { error_ = error; } protected: int status_code_ = RPCStatusOK; int error_ = TET_UNKNOWN; std::string errmsg_; }; class ThriftStdRequest : public protocol::ProtocolMessage, public RPCRequest, public ThriftRequest { public: int encode(struct iovec vectors[], int max) override { return this->ThriftRequest::encode(vectors, max, this->size_limit); } int append(const void *buf, size_t *size) override { return this->ThriftRequest::append(buf, size, this->size_limit); } public: bool serialize_meta() override { return this->ThriftRequest::serialize_meta(); } bool deserialize_meta() override { return this->ThriftRequest::deserialize_meta(); } public: const std::string& get_service_name() const override { return this->ThriftRequest::get_service_name(); } const std::string& get_method_name() const override { return this->ThriftRequest::get_method_name(); } void set_service_name(const std::string& service_name) override { return this->ThriftRequest::set_service_name(service_name); } void set_method_name(const std::string& method_name) override { return this->ThriftRequest::set_method_name(method_name); } void set_seqid(long long seqid) override { this->ThriftRequest::set_seqid(seqid); } bool set_meta_module_data(const RPCModuleData& data) override { return this->ThriftMessage::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->ThriftMessage::get_meta_module_data(data); } public: ThriftStdRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class ThriftStdResponse : public protocol::ProtocolMessage, public RPCResponse, public ThriftResponse { public: int encode(struct iovec vectors[], int max) override { return this->ThriftResponse::encode(vectors, max, this->size_limit); } int append(const void *buf, size_t *size) override { return this->ThriftResponse::append(buf, size, this->size_limit); } public: bool serialize_meta() override { return this->ThriftResponse::serialize_meta(); } bool deserialize_meta() override { return this->ThriftResponse::deserialize_meta(); } public: int get_status_code() const override { return this->ThriftResponse::get_status_code(); } int get_error() const override { return this->ThriftResponse::get_error(); } const char *get_errmsg() const override { return this->ThriftResponse::get_errmsg(); } void set_status_code(int code) override { return this->ThriftResponse::set_status_code(code); } void set_error(int error) override { return this->ThriftResponse::set_error(error); } bool set_meta_module_data(const RPCModuleData& data) override { return this->ThriftMessage::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->ThriftMessage::get_meta_module_data(data); } public: ThriftStdResponse() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class ThriftHttpRequest : public protocol::HttpRequest, public RPCRequest, public ThriftRequest { public: bool serialize_meta() override; bool deserialize_meta() override; public: const std::string& get_service_name() const override { return this->ThriftRequest::get_service_name(); } const std::string& get_method_name() const override { return this->ThriftRequest::get_method_name(); } void set_service_name(const std::string& service_name) override { return this->ThriftRequest::set_service_name(service_name); } void set_method_name(const std::string& method_name) override { return this->ThriftRequest::set_method_name(method_name); } void set_seqid(long long seqid) override { this->ThriftRequest::set_seqid(seqid); } bool set_meta_module_data(const RPCModuleData& data) override { return this->ThriftMessage::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->ThriftMessage::get_meta_module_data(data); } bool set_http_header(const std::string& name, const std::string& value) override; bool add_http_header(const std::string& name, const std::string& value) override; bool get_http_header(const std::string& name, std::string& value) const override; public: ThriftHttpRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class ThriftHttpResponse : public protocol::HttpResponse, public RPCResponse, public ThriftResponse { public: bool serialize_meta() override; bool deserialize_meta() override; public: int get_status_code() const override { return this->ThriftResponse::get_status_code(); } int get_error() const override { return this->ThriftResponse::get_error(); } const char *get_errmsg() const override { return this->ThriftResponse::get_errmsg(); } void set_status_code(int code) override { return this->ThriftResponse::set_status_code(code); } void set_error(int error) override { return this->ThriftResponse::set_error(error); } bool set_http_code(int code) override { return this->protocol::HttpResponse::set_status_code(std::to_string(code)); } bool set_meta_module_data(const RPCModuleData& data) override { return this->ThriftMessage::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->ThriftMessage::get_meta_module_data(data); } bool set_http_header(const std::string& name, const std::string& value) override; bool add_http_header(const std::string& name, const std::string& value) override; bool get_http_header(const std::string& name, std::string& value) const override; public: ThriftHttpResponse() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; //////// // inl inline int ThriftMessage::serialize(const ThriftIDLMessage *thrift_msg) { if (thrift_msg) { if (!thrift_msg->descriptor->writer(thrift_msg, &TBuffer_)) { return TBuffer_.meta.message_type == TMT_CALL ? RPCStatusReqSerializeError : RPCStatusRespSerializeError; } } return RPCStatusOK; } inline int ThriftMessage::deserialize(ThriftIDLMessage *thrift_msg) { if (thrift_msg) { if (!thrift_msg->descriptor->reader(&TBuffer_, thrift_msg)) { return TBuffer_.meta.message_type == TMT_CALL ? RPCStatusReqDeserializeError : RPCStatusRespDeserializeError; } } return RPCStatusOK; } inline int ThriftMessage::encode(struct iovec vectors[], int max, size_t size_limit) { size_t sz = TBuffer_.meta.writebuf.size() + buf_.size(); if (sz > 0x7FFFFFFF) { errno = EOVERFLOW; return -1; } if (sz > 0) { TBuffer_.framesize = ntohl((int32_t)sz); vectors[0].iov_base = (char *)&TBuffer_.framesize; vectors[0].iov_len = sizeof (int32_t); vectors[1].iov_base = const_cast(TBuffer_.meta.writebuf.c_str()); vectors[1].iov_len = TBuffer_.meta.writebuf.size(); int ret = buf_.encode(vectors + 2, max - 2); return ret < 0 ? ret : 2 + ret; } return 0; } } // namespace srpc #endif srpc-0.10.1/src/message/rpc_message_trpc.cc000066400000000000000000001147531454502251400206170ustar00rootroot00000000000000/* Copyright (c) 2021 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include "rpc_message_trpc.h" #include "rpc_meta_trpc.pb.h" #include "rpc_basic.h" #include "rpc_compress.h" #include "rpc_zero_copy_stream.h" #include "rpc_module.h" namespace srpc { namespace TRPCHttpHeaders { const std::string CompressType = "Content-Encoding"; const std::string DataType = "Content-Type"; const std::string CallType = "trpc-call-type"; const std::string RequestId = "trpc-request-id"; const std::string Timeout = "trpc-timeout"; const std::string Caller = "trpc-caller"; const std::string Callee = "trpc-callee"; const std::string Func = "trpc-func"; const std::string Ret = "trpc-ret"; const std::string FuncRet = "trpc-func-ret"; const std::string ErrorMsg = "trpc-error-msg"; const std::string MessageType = "trpc-message-type"; const std::string TransInfo = "trpc-trans-info"; const std::string SRPCStatus = "SRPC-Status"; const std::string SRPCError = "SRPC-Error"; } namespace TRPCHttpHeadersCode { enum { Unknown = 0, CompressType, DataType, CallType, RequestId, Timeout, Caller, Callee, Func, Ret, FuncRet, ErrorMsg, MessageType, TransInfo, SRPCStatus, SRPCError }; } struct CaseCmp { bool operator()(const std::string& lhs, const std::string& rhs) const { return strcasecmp(lhs.c_str(), rhs.c_str()) < 0; } }; static int GetHttpHeadersCode(const std::string& header) { static const std::map M = { {TRPCHttpHeaders::CompressType, TRPCHttpHeadersCode::CompressType}, {TRPCHttpHeaders::DataType, TRPCHttpHeadersCode::DataType}, {TRPCHttpHeaders::CallType, TRPCHttpHeadersCode::CallType}, {TRPCHttpHeaders::RequestId, TRPCHttpHeadersCode::RequestId}, {TRPCHttpHeaders::Timeout, TRPCHttpHeadersCode::Timeout}, {TRPCHttpHeaders::Caller, TRPCHttpHeadersCode::Caller}, {TRPCHttpHeaders::Callee, TRPCHttpHeadersCode::Callee}, {TRPCHttpHeaders::Func, TRPCHttpHeadersCode::Func}, {TRPCHttpHeaders::Ret, TRPCHttpHeadersCode::Ret}, {TRPCHttpHeaders::FuncRet, TRPCHttpHeadersCode::FuncRet}, {TRPCHttpHeaders::ErrorMsg, TRPCHttpHeadersCode::ErrorMsg}, {TRPCHttpHeaders::MessageType, TRPCHttpHeadersCode::MessageType}, {TRPCHttpHeaders::TransInfo, TRPCHttpHeadersCode::TransInfo}, {TRPCHttpHeaders::SRPCStatus, TRPCHttpHeadersCode::SRPCStatus}, {TRPCHttpHeaders::SRPCError, TRPCHttpHeadersCode::SRPCError} }; auto it = M.find(header); return it == M.end() ? TRPCHttpHeadersCode::Unknown : it->second; } static int GetHttpDataType(const std::string &type) { static const std::unordered_map M = { {"application/json", RPCDataJson}, {"application/x-protobuf", RPCDataProtobuf}, {"application/protobuf", RPCDataProtobuf}, {"application/pb", RPCDataProtobuf}, {"application/proto", RPCDataProtobuf} }; auto it = M.find(type); return it == M.end() ? RPCDataUndefined : it->second; } static std::string GetHttpDataTypeStr(int type) { switch (type) { case RPCDataJson: return "application/json"; case RPCDataProtobuf: return "application/proto"; } return ""; } static int GetHttpCompressType(const std::string &type) { static const std::unordered_map M = { {"identity", RPCCompressNone}, {"x-snappy", RPCCompressSnappy}, {"gzip", RPCCompressGzip}, {"deflate", RPCCompressZlib}, {"x-lz4", RPCCompressLz4} }; auto it = M.find(type); return it == M.end() ? RPCCompressNone : it->second; } static std::string GetHttpCompressTypeStr(int type) { switch (type) { case RPCCompressNone: return "identity"; case RPCCompressSnappy: return "x-snappy"; case RPCCompressGzip: return "gzip"; case RPCCompressZlib: return "deflate"; case RPCCompressLz4: return "x-lz4"; } return ""; } static int Base64Encode(const std::string& in, std::string& out) { size_t len = in.length(); size_t base64_len = (len + 2) / 3 * 4; const unsigned char *f = (const unsigned char *)in.c_str(); out.resize(base64_len + 1); EVP_EncodeBlock((unsigned char *)&out[0], f, len); out.pop_back(); return 0; } static int Base64Decode(const char *in, size_t len, std::string& out) { size_t origin_len = (len + 3) / 4 * 3; const unsigned char *f = (const unsigned char *)in; int ret; int zeros = 0; out.resize(origin_len); ret = EVP_DecodeBlock((unsigned char *)&out[0], f, len); if (ret < 0) return ret; while (len != 0 && in[--len] == '=') zeros++; if (zeros > 2) return -1; out.resize(ret - zeros); return 0; } static std::string JsonEscape(const std::string& in) { std::string out; out.reserve(in.size()); for (char c : in) { switch (c) { case '\"': out.append("\\\""); break; case '\\': out.append("\\\\"); break; case '/': out.append("\\/"); break; case '\b': out.append("\\b"); break; case '\f': out.append("\\f"); break; case '\n': out.append("\\n"); break; case '\r': out.append("\\r"); break; case '\t': out.append("\\t"); break; default: out.push_back(c); break; } } return out; } using TransInfoMap = ::google::protobuf::Map<::std::string, ::std::string>; static int DecodeTransInfo(const std::string& content, TransInfoMap& map) { json_value_t *json; json_object_t *obj; const json_value_t *value; const char *name, *str; size_t len; std::string decoded; int errno_bak = errno; errno = EBADMSG; json = json_value_parse(content.c_str()); if (json == nullptr) return -1; errno = errno_bak; if (json_value_type(json) != JSON_VALUE_OBJECT) { json_value_destroy(json); return -1; } obj = json_value_object(json); json_object_for_each(name, value, obj) { str = json_value_string(value); if (!name || !str) continue; len = strlen(str); if (Base64Decode(str, len, decoded) == 0) map[name].assign(std::move(decoded)); else map[name].assign(str, len); } json_value_destroy(json); return 0; } static std::string EncodeTransInfo(const TransInfoMap& map) { std::string s; std::string encoded; s.append("{"); for (const auto& kv : map) { Base64Encode(kv.second, encoded); s.append("\"").append(JsonEscape(kv.first)).append("\":"); s.append("\"").append(encoded).append("\","); } if (s.back() == ',') s.back() = '}'; else s.push_back('}'); return s; } static constexpr const char *kTypePrefix = "type.googleapis.com"; class ResolverInstance { public: static google::protobuf::util::TypeResolver *get_resolver() { static ResolverInstance kInstance; return kInstance.resolver_; } private: ResolverInstance() { resolver_ = google::protobuf::util::NewTypeResolverForDescriptorPool( kTypePrefix, google::protobuf::DescriptorPool::generated_pool()); } ~ResolverInstance() { delete resolver_; } google::protobuf::util::TypeResolver *resolver_; }; static inline std::string GetTypeUrl(const ProtobufIDLMessage *pb_msg) { return std::string(kTypePrefix) + "/" + pb_msg->GetDescriptor()->full_name(); } TRPCMessage::TRPCMessage() { this->nreceived = 0; this->meta_buf = NULL; this->meta_len = 0; this->message_len = 0; memset(this->header, 0, TRPC_HEADER_SIZE); this->message = new RPCBuffer(); } TRPCMessage::~TRPCMessage() { delete this->message; delete []this->meta_buf; delete this->meta; } TRPCRequest::TRPCRequest() { this->meta = new RequestProtocol(); } TRPCResponse::TRPCResponse() { this->meta = new ResponseProtocol(); } int TRPCMessage::encode(struct iovec vectors[], int max, size_t size_limit) { size_t sz = TRPC_HEADER_SIZE + this->meta_len + this->message_len; if (sz > 0x7FFFFFFF) { errno = EOVERFLOW; return -1; } int ret; char *p = this->header; *(uint16_t *)p = ntohs((uint16_t)TrpcMagic::TRPC_MAGIC_VALUE); p += 2; p += 1; // TrpcDataFrameType p += 1; // TrpcDataFrameState *(uint32_t *)(p) = htonl((uint32_t)sz); p += 4; *(uint16_t *)(p) = htons((uint16_t)this->meta_len); // 2: stream_id + 4 : reserved vectors[0].iov_base = this->header; vectors[0].iov_len = TRPC_HEADER_SIZE; vectors[1].iov_base = this->meta_buf; vectors[1].iov_len = this->meta_len; ret = this->message->encode(vectors + 2, max - 2); if (ret < 0) return ret; return 2 + ret; } int TRPCMessage::append(const void *buf, size_t *size, size_t size_limit) { uint32_t *p; uint16_t *sp; size_t header_left, body_received, buf_len; if (this->nreceived < TRPC_HEADER_SIZE) { //receive header header_left = TRPC_HEADER_SIZE - this->nreceived; if (*size >= header_left) { //receive the whole header and ready to recieve body memcpy(this->header + this->nreceived, buf, header_left); this->nreceived += header_left; sp = (uint16_t *)this->header; uint16_t magic_value = ntohs(*sp); if (magic_value != TrpcMagic::TRPC_MAGIC_VALUE || this->header[2] || this->header[3]) { errno = EBADMSG; return -1; } p = (uint32_t *)this->header + 1; buf_len = ntohl(*p); sp = (uint16_t *)this->header + 4; this->meta_len = ntohs(*sp); this->message_len = buf_len - TRPC_HEADER_SIZE - this->meta_len; buf_len -= TRPC_HEADER_SIZE; if (buf_len >= size_limit) { errno = EMSGSIZE; return -1; } else if (buf_len > 0) { if (*size - header_left > buf_len) *size = header_left + buf_len; this->meta_buf = new char[this->meta_len]; if (*size - header_left <= this->meta_len) { memcpy(this->meta_buf, (const char *)buf + header_left, *size - header_left); } else { memcpy(this->meta_buf, (const char *)buf + header_left, this->meta_len); this->message->append((const char *)buf + header_left + this->meta_len, *size - header_left - this->meta_len, BUFFER_MODE_COPY); } this->nreceived += *size - header_left; if (this->nreceived == TRPC_HEADER_SIZE + buf_len) return 1; else return 0; } else if (*size == header_left) { return 1; // means body_size == 0 and finish recieved header } else { // means buf_len < 0 errno = EBADMSG; return -1; } } else { // only receive header memcpy(this->header + this->nreceived, buf, *size); this->nreceived += *size; return 0; } } else { // have already received the header and now is for body only body_received = this->nreceived - TRPC_HEADER_SIZE; buf_len = this->meta_len + this->message_len; if (body_received + *size > buf_len) *size = buf_len - body_received; if (body_received + *size <= this->meta_len) { memcpy(this->meta_buf + body_received, buf, *size); } else if (body_received < this->meta_len) { memcpy(this->meta_buf + body_received, buf, this->meta_len - body_received); if (body_received + *size > this->meta_len)// useless. always true this->message->append((const char *)buf + this->meta_len - body_received, *size - this->meta_len + body_received, BUFFER_MODE_COPY); } else { this->message->append((const char *)buf, *size, BUFFER_MODE_COPY); } this->nreceived += *size; return this->nreceived == TRPC_HEADER_SIZE + buf_len; } } bool TRPCRequest::deserialize_meta() { RequestProtocol *meta = static_cast(this->meta); if (!meta->ParseFromArray(this->meta_buf, (int)this->meta_len)) return false; if (meta->version() != TrpcProtoVersion::TRPC_PROTO_V1 || meta->call_type() != TrpcCallType::TRPC_UNARY_CALL || meta->content_type() != TrpcContentEncodeType::TRPC_PROTO_ENCODE) { // this->trpc_error = ST_ERR_UNSUPPORTED_PROTO_TYPE; return false; } // this->timeout = meta->timeout(); return true; } bool TRPCResponse::deserialize_meta() { ResponseProtocol *meta = static_cast(this->meta); if (!meta->ParseFromArray(this->meta_buf, (int)this->meta_len)) return false; this->srpc_status_code = this->status_code_trpc_srpc(meta->ret()); if (!meta->error_msg().empty()) this->srpc_error_msg = meta->error_msg().data(); return true; } bool TRPCRequest::serialize_meta() { RequestProtocol *meta = static_cast(this->meta); meta->set_content_type(TrpcContentEncodeType::TRPC_PROTO_ENCODE); meta->set_version(TrpcProtoVersion::TRPC_PROTO_V1); meta->set_call_type(TrpcCallType::TRPC_UNARY_CALL); this->meta_len = meta->ByteSizeLong(); this->meta_buf = new char[this->meta_len]; return this->meta->SerializeToArray(this->meta_buf, (int)this->meta_len); } bool TRPCResponse::serialize_meta() { ResponseProtocol *meta = static_cast(this->meta); meta->set_version(TrpcProtoVersion::TRPC_PROTO_V1); meta->set_call_type(TrpcCallType::TRPC_UNARY_CALL); meta->set_ret(this->status_code_srpc_trpc(this->srpc_status_code)); // meta->set_error_msg(this->error_msg_srpc_trpc(this->srpc_status_code)); meta->set_error_msg(this->srpc_error_msg); this->meta_len = meta->ByteSizeLong(); this->meta_buf = new char[this->meta_len]; return meta->SerializeToArray(this->meta_buf, (int)this->meta_len); } inline int TRPCMessage::compress_type_trpc_srpc(int trpc_content_encoding) const { switch (trpc_content_encoding) { case TrpcCompressType::TRPC_DEFAULT_COMPRESS : return RPCCompressNone; case TrpcCompressType::TRPC_GZIP_COMPRESS : return RPCCompressGzip; case TrpcCompressType::TRPC_SNAPPY_COMPRESS : return RPCCompressSnappy; default : return -1; } } inline int TRPCMessage::compress_type_srpc_trpc(int srpc_compress_type) const { switch (srpc_compress_type) { case RPCCompressNone : return TrpcCompressType::TRPC_DEFAULT_COMPRESS; case RPCCompressGzip : return TrpcCompressType::TRPC_GZIP_COMPRESS; case RPCCompressSnappy : return TrpcCompressType::TRPC_SNAPPY_COMPRESS; case RPCCompressZlib : return TrpcCompressType::TRPC_ZLIB_COMPRESS; case RPCCompressLz4 : return TrpcCompressType::TRPC_LZ4_COMPRESS; default : return -1; } } inline int TRPCMessage::data_type_trpc_srpc(int trpc_content_type) const { switch (trpc_content_type) { case TrpcContentEncodeType::TRPC_PROTO_ENCODE : return RPCDataProtobuf; case TrpcContentEncodeType::TRPC_JSON_ENCODE : return RPCDataJson; default : return -1; } } inline int TRPCMessage::data_type_srpc_trpc(int srpc_data_type) const { switch (srpc_data_type) { case RPCDataProtobuf : return TrpcContentEncodeType::TRPC_PROTO_ENCODE; case RPCDataJson : return TrpcContentEncodeType::TRPC_JSON_ENCODE; default : return -1; } } inline int TRPCMessage::status_code_srpc_trpc(int srpc_status_code) const { switch (srpc_status_code) { case RPCStatusOK: return TrpcRetCode::TRPC_INVOKE_SUCCESS; case RPCStatusServiceNotFound: return TrpcRetCode::TRPC_SERVER_NOSERVICE_ERR; case RPCStatusMethodNotFound: return TrpcRetCode::TRPC_SERVER_NOFUNC_ERR; case RPCStatusURIInvalid: case RPCStatusMetaError: case RPCStatusReqCompressSizeInvalid: case RPCStatusReqCompressNotSupported: case RPCStatusReqCompressError: case RPCStatusReqSerializeError: return TrpcRetCode::TRPC_CLIENT_ENCODE_ERR; case RPCStatusReqDecompressSizeInvalid: case RPCStatusReqDecompressNotSupported: case RPCStatusReqDecompressError: case RPCStatusReqDeserializeError: return TrpcRetCode::TRPC_SERVER_DECODE_ERR; case RPCStatusRespCompressSizeInvalid: case RPCStatusRespCompressNotSupported: case RPCStatusRespCompressError: case RPCStatusRespSerializeError: return TrpcRetCode::TRPC_SERVER_ENCODE_ERR; case RPCStatusRespDecompressSizeInvalid: case RPCStatusRespDecompressNotSupported: case RPCStatusRespDecompressError: case RPCStatusRespDeserializeError: return TrpcRetCode::TRPC_CLIENT_DECODE_ERR; case RPCStatusUpstreamFailed: case RPCStatusDNSError: return TrpcRetCode::TRPC_CLIENT_ROUTER_ERR; case RPCStatusSystemError: return TrpcRetCode::TRPC_SERVER_SYSTEM_ERR; // return TrpcRetCode::TRPC_CLINET_NETWORK_ERR; default: return TrpcRetCode::TRPC_INVOKE_UNKNOWN_ERR; } } inline int TRPCMessage::status_code_trpc_srpc(int trpc_ret_code) const { switch (trpc_ret_code) { case TrpcRetCode::TRPC_INVOKE_SUCCESS: return RPCStatusOK; case TrpcRetCode::TRPC_SERVER_NOSERVICE_ERR: return RPCStatusServiceNotFound; case TrpcRetCode::TRPC_SERVER_NOFUNC_ERR: return RPCStatusMethodNotFound; case TrpcRetCode::TRPC_CLIENT_ENCODE_ERR: return RPCStatusReqSerializeError; case TrpcRetCode::TRPC_SERVER_DECODE_ERR: return RPCStatusReqDeserializeError; case TrpcRetCode::TRPC_SERVER_ENCODE_ERR: return RPCStatusRespSerializeError; case TrpcRetCode::TRPC_CLIENT_DECODE_ERR: return RPCStatusRespDeserializeError; case TrpcRetCode::TRPC_CLIENT_ROUTER_ERR: return RPCStatusUpstreamFailed; // return RPCStatusDNSError; default: return RPCStatusSystemError; } } const char *TRPCMessage::error_msg_srpc_trpc(int srpc_status_code) const { return "";//TODO } int TRPCRequest::get_compress_type() const { RequestProtocol *meta = static_cast(this->meta); return this->compress_type_trpc_srpc(meta->content_encoding()); } void TRPCRequest::set_compress_type(int type) { RequestProtocol *meta = static_cast(this->meta); meta->set_content_encoding(this->compress_type_srpc_trpc(type)); } int TRPCRequest::get_data_type() const { RequestProtocol *meta = static_cast(this->meta); return this->data_type_trpc_srpc(meta->content_type()); } void TRPCRequest::set_data_type(int type) { RequestProtocol *meta = static_cast(this->meta); meta->set_content_type(this->data_type_srpc_trpc(type)); } void TRPCRequest::set_request_id(int32_t req_id) { RequestProtocol *meta = static_cast(this->meta); meta->set_request_id(req_id); } int32_t TRPCRequest::get_request_id() const { RequestProtocol *meta = static_cast(this->meta); return meta->request_id(); } const std::string& TRPCRequest::get_service_name() const { RequestProtocol *meta = static_cast(this->meta); return meta->callee(); } const std::string& TRPCRequest::get_method_name() const { RequestProtocol *meta = static_cast(this->meta); return meta->func(); } void TRPCRequest::set_service_name(const std::string& service_name) { RequestProtocol *meta = static_cast(this->meta); meta->set_callee(service_name); } void TRPCRequest::set_method_name(const std::string& method_name) { RequestProtocol *meta = static_cast(this->meta); meta->set_func(method_name); } void TRPCRequest::set_callee_timeout(int timeout) { RequestProtocol *meta = static_cast(this->meta); meta->set_timeout(timeout); } void TRPCRequest::set_caller_name(const std::string& caller_name) { RequestProtocol *meta = static_cast(this->meta); meta->set_caller(caller_name); } const std::string& TRPCRequest::get_caller_name() const { RequestProtocol *meta = static_cast(this->meta); return meta->caller(); } int TRPCResponse::get_compress_type() const { ResponseProtocol *meta = static_cast(this->meta); return this->compress_type_trpc_srpc(meta->content_encoding()); } int TRPCResponse::get_data_type() const { ResponseProtocol *meta = static_cast(this->meta); return this->data_type_trpc_srpc(meta->content_type()); } void TRPCResponse::set_compress_type(int type) { ResponseProtocol *meta = static_cast(this->meta); meta->set_content_encoding(this->compress_type_srpc_trpc(type)); } void TRPCResponse::set_data_type(int type) { ResponseProtocol *meta = static_cast(this->meta); meta->set_content_type(this->data_type_srpc_trpc(type)); } void TRPCResponse::set_request_id(int32_t req_id) { ResponseProtocol *meta = static_cast(this->meta); meta->set_request_id(req_id); } int32_t TRPCResponse::get_request_id() const { ResponseProtocol *meta = static_cast(this->meta); return meta->request_id(); } int TRPCResponse::get_status_code() const { return this->srpc_status_code; } void TRPCResponse::set_status_code(int code) { this->srpc_status_code = code; if (code != RPCStatusOK) this->srpc_error_msg = this->get_errmsg(); } int TRPCResponse::get_error() const { ResponseProtocol *meta = static_cast(this->meta); return meta->ret();//TODO } void TRPCResponse::set_error(int error) { ResponseProtocol *meta = static_cast(this->meta); meta->set_ret(error); } const char *TRPCResponse::get_errmsg() const { ResponseProtocol *meta = static_cast(this->meta); return meta->error_msg().c_str(); } int TRPCMessage::serialize(const ProtobufIDLMessage *pb_msg) { if (!pb_msg) //TODO: make sure trpc is OK to send a NULL user pb_msg return RPCStatusOK; using namespace google::protobuf; ResponseProtocol *meta = dynamic_cast(this->meta); bool is_resp = (meta != NULL); int data_type = this->get_data_type(); RPCOutputStream output_stream(this->message, pb_msg->ByteSizeLong()); int ret; if (data_type == RPCDataProtobuf) ret = pb_msg->SerializeToZeroCopyStream(&output_stream) ? 0 : -1; else if (data_type == RPCDataJson) { std::string binary_input = pb_msg->SerializeAsString(); io::ArrayInputStream input_stream(binary_input.data(), (int)binary_input.size()); const auto *pool = pb_msg->GetDescriptor()->file()->pool(); auto *resolver = (pool == DescriptorPool::generated_pool() ? ResolverInstance::get_resolver() : util::NewTypeResolverForDescriptorPool(kTypePrefix, pool)); util::JsonOptions options; options.add_whitespace = this->get_json_add_whitespace(); options.always_print_enums_as_ints = this->get_json_enums_as_ints(); options.preserve_proto_field_names = this->get_json_preserve_names(); options.always_print_primitive_fields = this->get_json_print_primitive(); ret = BinaryToJsonStream(resolver, GetTypeUrl(pb_msg), &input_stream, &output_stream, options).ok() ? 0 : -1; if (pool != DescriptorPool::generated_pool()) delete resolver; } else ret = -1; this->message_len = this->message->size(); if (ret < 0) return is_resp ? RPCStatusRespSerializeError : RPCStatusReqSerializeError; return RPCStatusOK; } int TRPCMessage::deserialize(ProtobufIDLMessage *pb_msg) { using namespace google::protobuf; ResponseProtocol *meta = dynamic_cast(this->meta); bool is_resp = (meta != NULL); int data_type = this->get_data_type(); int ret; RPCInputStream input_stream(this->message); if (data_type == RPCDataProtobuf) ret = pb_msg->ParseFromZeroCopyStream(&input_stream) ? 0 : -1; else if (data_type == RPCDataJson) { std::string binary_output; io::StringOutputStream output_stream(&binary_output); const auto *pool = pb_msg->GetDescriptor()->file()->pool(); auto *resolver = (pool == DescriptorPool::generated_pool() ? ResolverInstance::get_resolver() : util::NewTypeResolverForDescriptorPool(kTypePrefix, pool)); if (JsonToBinaryStream(resolver, GetTypeUrl(pb_msg), &input_stream, &output_stream).ok()) { ret = pb_msg->ParseFromString(binary_output) ? 0 : -1; } else ret = -1; if (pool != DescriptorPool::generated_pool()) delete resolver; } else ret = -1; if (ret < 0) return is_resp ? RPCStatusRespDeserializeError : RPCStatusReqDeserializeError; return RPCStatusOK; } int TRPCMessage::compress() { ResponseProtocol *meta = dynamic_cast(this->meta); bool is_resp = (meta != NULL); int type = this->get_compress_type(); size_t buflen = this->message_len; int status_code = RPCStatusOK; if (buflen == 0) return status_code; if (type == RPCCompressNone) return status_code; static RPCCompressor *compressor = RPCCompressor::get_instance(); int ret = compressor->lease_compressed_size(type, buflen); if (ret == -2) return is_resp ? RPCStatusReqCompressNotSupported : RPCStatusRespCompressNotSupported; else if (ret <= 0) return is_resp ? RPCStatusRespCompressSizeInvalid : RPCStatusReqCompressSizeInvalid; //buflen = ret; RPCBuffer *dst_buf = new RPCBuffer(); ret = compressor->serialize_to_compressed(this->message, dst_buf, type); if (ret == -2) { status_code = is_resp ? RPCStatusRespCompressNotSupported : RPCStatusReqCompressNotSupported; } else if (ret == -1) { status_code = is_resp ? RPCStatusRespCompressError : RPCStatusReqCompressError; } else if (ret <= 0) { status_code = is_resp ? RPCStatusRespCompressSizeInvalid : RPCStatusReqCompressSizeInvalid; } else buflen = ret; if (status_code == RPCStatusOK) { delete this->message; this->message = dst_buf; this->message_len = buflen; } else delete dst_buf; return status_code; } int TRPCMessage::decompress() { ResponseProtocol *meta = dynamic_cast(this->meta); bool is_resp = (meta != NULL); int type = this->get_compress_type(); int status_code = RPCStatusOK; if (this->message_len == 0 || type == RPCCompressNone) return status_code; RPCBuffer *dst_buf = new RPCBuffer(); static RPCCompressor *compressor = RPCCompressor::get_instance(); int ret = compressor->parse_from_compressed(this->message, dst_buf, type); if (ret == -2) { status_code = is_resp ? RPCStatusRespDecompressNotSupported : RPCStatusReqDecompressNotSupported; } else if (ret == -1) { status_code = is_resp ? RPCStatusRespDecompressError : RPCStatusReqDecompressError; } else if (ret <= 0) { status_code = is_resp ? RPCStatusRespDecompressSizeInvalid : RPCStatusReqDecompressSizeInvalid; } if (status_code == RPCStatusOK) { delete this->message; this->message = dst_buf; this->message_len = ret; } else delete dst_buf; return status_code; } static std::string set_trace_parent(std::string& trace, std::string& span, const RPCModuleData& data) { std::string str = "00-"; // set version char trace_id_buf[SRPC_TRACEID_SIZE * 2 + 1]; TRACE_ID_BIN_TO_HEX((uint64_t *)trace.data(), trace_id_buf); str.append(trace_id_buf); str.append("-"); char span_id_buf[SRPC_SPANID_SIZE * 2 + 1]; SPAN_ID_BIN_TO_HEX((uint64_t *)span.data(), span_id_buf); str.append(span_id_buf); str.append("-"); str.append("01"); // set traceflag : sampled return str; } bool TRPCRequest::set_meta_module_data(const RPCModuleData& data) { RequestProtocol *meta = static_cast(this->meta); std::string trace; std::string span; int flag = 0; for (const auto & pair : data) { if (pair.first.compare(SRPC_TRACE_ID) == 0) { trace = pair.second; flag |= 1; } else if (pair.first.compare(SRPC_SPAN_ID) == 0) { span = pair.second; flag |= (1 << 1); } else meta->mutable_trans_info()->insert({pair.first, pair.second}); } if (flag == 3) meta->mutable_trans_info()->insert({OTLP_TRACE_PARENT, set_trace_parent(trace, span, data)}); return true; } static bool get_trace_parent(const std::string& str, RPCModuleData& data) { // only support version = 00 : "00-" trace-id "-" parent-id "-" trace-flags size_t begin = OTLP_TRACE_VERSION_SIZE; if (str.length() < 55 || str.substr(0, begin).compare("00") != 0) return false; // data[OTLP_TRACE_VERSION] = "00"; uint64_t trace[2]; begin += 1; TRACE_ID_HEX_TO_BIN(str.substr(begin, SRPC_TRACEID_SIZE * 2).data(), trace); data[SRPC_TRACE_ID] = std::string((char *)trace, SRPC_TRACEID_SIZE); uint64_t span[1]; begin += SRPC_TRACEID_SIZE * 2 + 1; SPAN_ID_HEX_TO_BIN(str.substr(begin, SRPC_SPANID_SIZE * 2).data(), span); data[SRPC_SPAN_ID] = std::string((char *)span, SRPC_SPANID_SIZE); // begin += SRPC_SPANID_SIZE + 1; // data[OTLP_TRACE_FLAG] = str.substr(begin); return true; } bool TRPCRequest::get_meta_module_data(RPCModuleData& data) const { RequestProtocol *meta = static_cast(this->meta); for (const auto & pair : meta->trans_info()) { if (pair.first.compare(OTLP_TRACE_PARENT) == 0) get_trace_parent(pair.second, data); else if (pair.first.compare(OTLP_TRACE_STATE) == 0) ;// TODO: support tracestate else data.insert(pair); } return true; } bool TRPCRequest::trim_method_prefix() { RequestProtocol *meta = static_cast(this->meta); std::string *method = meta->mutable_func(); auto pos = method->find_last_of('/'); if (pos == std::string::npos) return false; meta->set_func(method->substr(pos + 1, method->length())); return true; } bool TRPCResponse::set_meta_module_data(const RPCModuleData& data) { ResponseProtocol *meta = static_cast(this->meta); for (const auto & pair : data) meta->mutable_trans_info()->insert({pair.first, pair.second}); return true; } bool TRPCResponse::get_meta_module_data(RPCModuleData& data) const { ResponseProtocol *meta = static_cast(this->meta); for (const auto & pair : meta->trans_info()) data.insert(pair); return true; } bool TRPCHttpRequest::serialize_meta() { if (this->message->size() > 0x7FFFFFFF) return false; int data_type = this->get_data_type(); int compress_type = this->get_compress_type(); std::string uri("/"); uri += this->get_service_name(); uri += "/"; uri += this->get_method_name(); set_http_version("HTTP/1.1"); set_method("POST"); set_request_uri(uri); //set header set_header_pair(TRPCHttpHeaders::DataType, GetHttpDataTypeStr(data_type)); set_header_pair(TRPCHttpHeaders::CompressType, GetHttpCompressTypeStr(compress_type)); set_header_pair("Connection", "Keep-Alive"); set_header_pair("Content-Length", std::to_string(this->message_len)); set_header_pair(TRPCHttpHeaders::CallType, "0"); set_header_pair(TRPCHttpHeaders::RequestId, std::to_string(this->get_request_id())); set_header_pair(TRPCHttpHeaders::Callee, this->get_method_name()); set_header_pair(TRPCHttpHeaders::Func, this->get_service_name()); set_header_pair(TRPCHttpHeaders::Caller, this->get_caller_name()); auto *req_meta = (RequestProtocol *)this->meta; set_header_pair(TRPCHttpHeaders::TransInfo, EncodeTransInfo(req_meta->trans_info())); const void *buffer; size_t buflen; while (buflen = this->message->fetch(&buffer), buffer && buflen > 0) this->append_output_body_nocopy(buffer, buflen); return true; } bool TRPCHttpRequest::deserialize_meta() { const char *request_uri = this->get_request_uri(); protocol::HttpHeaderCursor header_cursor(this); auto *meta = (RequestProtocol *)this->meta; std::string key, value; this->set_data_type(RPCDataJson); this->set_compress_type(RPCCompressNone); while (header_cursor.next(key, value)) { switch (GetHttpHeadersCode(key)) { case TRPCHttpHeadersCode::DataType: this->set_data_type(GetHttpDataType(value)); break; case TRPCHttpHeadersCode::CompressType: this->set_compress_type(GetHttpCompressType(value)); break; case TRPCHttpHeadersCode::CallType: meta->set_call_type(std::atoi(value.c_str())); break; case TRPCHttpHeadersCode::RequestId: meta->set_request_id(std::atoi(value.c_str())); break; case TRPCHttpHeadersCode::Timeout: meta->set_timeout(std::atoi(value.c_str())); break; case TRPCHttpHeadersCode::Caller: meta->set_caller(value); break; case TRPCHttpHeadersCode::Callee: meta->set_callee(value); break; case TRPCHttpHeadersCode::MessageType: meta->set_message_type(std::atoi(value.c_str())); break; case TRPCHttpHeadersCode::TransInfo: DecodeTransInfo(value, *meta->mutable_trans_info()); break; default: break; } } if (request_uri && request_uri[0] == '/') { std::string str = request_uri + 1; auto pos = str.find_first_of("?#"); if (pos != std::string::npos) str.erase(pos); if (!str.empty() && str.back() == '/') str.pop_back(); pos = str.find_last_of('/'); if (pos != std::string::npos) { this->set_service_name(str.substr(0, pos)); this->set_method_name(str.substr(pos + 1)); } } const void *ptr; size_t len; this->get_parsed_body(&ptr, &len); if (len > 0x7FFFFFFF) return false; protocol::HttpChunkCursor chunk_cursor(this); RPCBuffer *buf = this->get_buffer(); size_t msg_len = 0; while (chunk_cursor.next(&ptr, &len)) { msg_len += len; buf->append((const char *)ptr, len, BUFFER_MODE_NOCOPY); } if (this->get_compress_type() == RPCCompressNone && msg_len == 0 && this->get_data_type() == RPCDataJson) { buf->append("{}", 2, BUFFER_MODE_NOCOPY); msg_len = 2; } this->message_len = msg_len; return true; } bool TRPCHttpResponse::serialize_meta() { if (this->message->size() > 0x7FFFFFFF) return false; auto *meta = (ResponseProtocol *)this->meta; int data_type = this->get_data_type(); int compress_type = this->get_compress_type(); int rpc_status_code = this->get_status_code(); int rpc_error = this->get_error(); const char *http_status_code = this->protocol::HttpResponse::get_status_code(); set_http_version("HTTP/1.1"); if (rpc_status_code == RPCStatusOK) { if (http_status_code) protocol::HttpUtil::set_response_status(this, atoi(http_status_code)); else protocol::HttpUtil::set_response_status(this, HttpStatusOK); } else if (rpc_status_code == RPCStatusServiceNotFound || rpc_status_code == RPCStatusMethodNotFound || rpc_status_code == RPCStatusMetaError || rpc_status_code == RPCStatusURIInvalid) { protocol::HttpUtil::set_response_status(this, HttpStatusBadRequest); } else if (rpc_status_code == RPCStatusRespCompressNotSupported || rpc_status_code == RPCStatusRespDecompressNotSupported || rpc_status_code == RPCStatusIDLSerializeNotSupported || rpc_status_code == RPCStatusIDLDeserializeNotSupported) { protocol::HttpUtil::set_response_status(this, HttpStatusNotImplemented); } else if (rpc_status_code == RPCStatusUpstreamFailed) { protocol::HttpUtil::set_response_status(this, HttpStatusServiceUnavailable); } else { protocol::HttpUtil::set_response_status(this, HttpStatusInternalServerError); } //set header set_header_pair(TRPCHttpHeaders::SRPCStatus, std::to_string(rpc_status_code)); set_header_pair(TRPCHttpHeaders::SRPCError, std::to_string(rpc_error)); set_header_pair(TRPCHttpHeaders::DataType, GetHttpDataTypeStr(data_type)); set_header_pair(TRPCHttpHeaders::CompressType, GetHttpCompressTypeStr(compress_type)); set_header_pair(TRPCHttpHeaders::CallType, std::to_string(meta->call_type())); set_header_pair(TRPCHttpHeaders::RequestId, std::to_string(meta->request_id())); set_header_pair(TRPCHttpHeaders::Ret, std::to_string(meta->ret())); set_header_pair(TRPCHttpHeaders::FuncRet, std::to_string(meta->func_ret())); set_header_pair(TRPCHttpHeaders::ErrorMsg, meta->error_msg()); set_header_pair(TRPCHttpHeaders::MessageType, std::to_string(meta->message_type())); set_header_pair("Content-Length", std::to_string(this->message_len)); set_header_pair("Connection", "Keep-Alive"); set_header_pair(TRPCHttpHeaders::TransInfo, EncodeTransInfo(meta->trans_info())); const void *buffer; size_t buflen; while (buflen = this->message->fetch(&buffer), buffer && buflen > 0) this->append_output_body_nocopy(buffer, buflen); return true; } bool TRPCHttpResponse::deserialize_meta() { protocol::HttpHeaderCursor header_cursor(this); auto *meta = (ResponseProtocol *)this->meta; std::string key, value; this->set_data_type(RPCDataJson); this->set_compress_type(RPCCompressNone); while (header_cursor.next(key, value)) { switch (GetHttpHeadersCode(key)) { case TRPCHttpHeadersCode::DataType: this->set_data_type(GetHttpDataType(value)); break; case TRPCHttpHeadersCode::CompressType: this->set_compress_type(GetHttpCompressType(value)); break; case TRPCHttpHeadersCode::CallType: meta->set_call_type(std::atoi(value.c_str())); break; case TRPCHttpHeadersCode::RequestId: meta->set_request_id(std::atoi(value.c_str())); break; case TRPCHttpHeadersCode::Ret: meta->set_ret(std::atoi(value.c_str())); break; case TRPCHttpHeadersCode::FuncRet: meta->set_func_ret(std::atoi(value.c_str())); break; case TRPCHttpHeadersCode::ErrorMsg: meta->set_error_msg(value); break; case TRPCHttpHeadersCode::MessageType: meta->set_message_type(std::atoi(value.c_str())); break; case TRPCHttpHeadersCode::TransInfo: DecodeTransInfo(value, *meta->mutable_trans_info()); break; default: break; } } const void *ptr; size_t len; this->get_parsed_body(&ptr, &len); if (len > 0x7FFFFFFF) return false; protocol::HttpChunkCursor chunk_cursor(this); RPCBuffer *buf = this->get_buffer(); size_t msg_len = 0; while (chunk_cursor.next(&ptr, &len)) { buf->append((const char *)ptr, len, BUFFER_MODE_NOCOPY); msg_len += len; } this->message_len = msg_len; return true; } bool TRPCHttpRequest::set_meta_module_data(const RPCModuleData& data) { return this->TRPCRequest::set_meta_module_data(data); } bool TRPCHttpRequest::get_meta_module_data(RPCModuleData& data) const { return this->TRPCRequest::get_meta_module_data(data); } bool TRPCHttpResponse::set_meta_module_data(const RPCModuleData& data) { return this->TRPCResponse::set_meta_module_data(data); } bool TRPCHttpResponse::get_meta_module_data(RPCModuleData& data) const { return this->TRPCResponse::get_meta_module_data(data); } bool TRPCHttpRequest::set_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::set_header_pair(name, value); } bool TRPCHttpRequest::add_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::add_header_pair(name, value); } bool TRPCHttpRequest::get_http_header(const std::string& name, std::string& value) const { protocol::HttpHeaderCursor cursor(this); return cursor.find(name, value); } bool TRPCHttpResponse::set_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::set_header_pair(name, value); } bool TRPCHttpResponse::add_http_header(const std::string& name, const std::string& value) { return this->protocol::HttpMessage::add_header_pair(name, value); } bool TRPCHttpResponse::get_http_header(const std::string& name, std::string& value) const { protocol::HttpHeaderCursor cursor(this); return cursor.find(name, value); } } // namespace srpc srpc-0.10.1/src/message/rpc_message_trpc.h000066400000000000000000000217141454502251400204530ustar00rootroot00000000000000/* Copyright (c) 2021 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_MESSAGE_TRPC_H__ #define __RPC_MESSAGE_TRPC_H__ #ifdef _WIN32 #include #else #include #endif #include #include "rpc_message.h" #include "rpc_basic.h" namespace srpc { static constexpr int TRPC_HEADER_SIZE = 16; class TRPCMessage : public RPCMessage { public: TRPCMessage(); virtual ~TRPCMessage(); int encode(struct iovec vectors[], int max, size_t size_limit); int append(const void *buf, size_t *size, size_t size_limit); void set_attachment_nocopy(const char *attachment, size_t len) { } bool get_attachment_nocopy(const char **attachment, size_t *len) const { return false; } public: using RPCMessage::serialize; using RPCMessage::deserialize; int serialize(const ProtobufIDLMessage *pb_msg) override; int deserialize(ProtobufIDLMessage *pb_msg) override; int compress() override; int decompress() override; protected: char header[TRPC_HEADER_SIZE]; size_t nreceived; size_t meta_len; size_t message_len; char *meta_buf; RPCBuffer *message; ProtobufIDLMessage *meta; protected: int compress_type_trpc_srpc(int trpc_content_encoding) const; int compress_type_srpc_trpc(int srpc_compress_type) const; int data_type_trpc_srpc(int trpc_content_type) const; int data_type_srpc_trpc(int srpc_data_type) const; int status_code_srpc_trpc(int srpc_status_code) const; int status_code_trpc_srpc(int trpc_ret_code) const; const char *error_msg_srpc_trpc(int srpc_status_code) const; RPCBuffer *get_buffer() const { return this->message; } }; class TRPCRequest : public TRPCMessage { public: TRPCRequest(); bool serialize_meta(); bool deserialize_meta(); const std::string& get_service_name() const; const std::string& get_method_name() const; const std::string& get_caller_name() const; void set_service_name(const std::string& service_name); void set_method_name(const std::string& method_name); void set_callee_timeout(int timeout); void set_caller_name(const std::string& caller_name); int get_compress_type() const override; void set_compress_type(int type) override; int get_data_type() const override; void set_data_type(int type) override; void set_request_id(int32_t req_id); int32_t get_request_id() const; bool set_meta_module_data(const RPCModuleData& data) override; bool get_meta_module_data(RPCModuleData& data) const override; bool trim_method_prefix(); }; class TRPCResponse : public TRPCMessage { public: TRPCResponse(); bool serialize_meta(); bool deserialize_meta(); int get_compress_type() const override; void set_compress_type(int type) override; int get_data_type() const override; void set_data_type(int type) override; int get_status_code() const; int get_error() const; const char *get_errmsg() const; void set_status_code(int code); void set_error(int error); void set_request_id(int32_t req_id); int32_t get_request_id() const; bool set_meta_module_data(const RPCModuleData& data) override; bool get_meta_module_data(RPCModuleData& data) const override; protected: int srpc_status_code = RPCStatusOK; std::string srpc_error_msg; }; class TRPCStdRequest : public protocol::ProtocolMessage, public RPCRequest, public TRPCRequest { public: int encode(struct iovec vectors[], int max) override { return this->TRPCRequest::encode(vectors, max, this->size_limit); } int append(const void *buf, size_t *size) override { return this->TRPCRequest::append(buf, size, this->size_limit); } public: bool serialize_meta() override { return this->TRPCRequest::serialize_meta(); } bool deserialize_meta() override { return this->TRPCRequest::deserialize_meta(); } public: const std::string& get_service_name() const override { return this->TRPCRequest::get_service_name(); } const std::string& get_method_name() const override { return this->TRPCRequest::get_method_name(); } void set_service_name(const std::string& service_name) override { return this->TRPCRequest::set_service_name(service_name); } void set_method_name(const std::string& method_name) override { return this->TRPCRequest::set_method_name(method_name); } bool set_meta_module_data(const RPCModuleData& data) override { return this->TRPCRequest::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->TRPCRequest::get_meta_module_data(data); } void set_caller_name(const std::string& caller_name) { return this->TRPCRequest::set_caller_name(caller_name); } public: TRPCStdRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class TRPCStdResponse : public protocol::ProtocolMessage, public RPCResponse, public TRPCResponse { public: int encode(struct iovec vectors[], int max) override { return this->TRPCResponse::encode(vectors, max, this->size_limit); } int append(const void *buf, size_t *size) override { return this->TRPCResponse::append(buf, size, this->size_limit); } public: bool serialize_meta() override { return this->TRPCResponse::serialize_meta(); } bool deserialize_meta() override { return this->TRPCResponse::deserialize_meta(); } public: int get_status_code() const override { return this->TRPCResponse::get_status_code(); } int get_error() const override { return this->TRPCResponse::get_error(); } const char *get_errmsg() const override { return this->TRPCResponse::get_errmsg(); } void set_status_code(int code) override { return this->TRPCResponse::set_status_code(code); } void set_error(int error) override { return this->TRPCResponse::set_error(error); } bool set_meta_module_data(const RPCModuleData& data) override { return this->TRPCResponse::set_meta_module_data(data); } bool get_meta_module_data(RPCModuleData& data) const override { return this->TRPCResponse::get_meta_module_data(data); } public: TRPCStdResponse() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class TRPCHttpRequest : public protocol::HttpRequest, public RPCRequest, public TRPCRequest { public: bool serialize_meta() override; bool deserialize_meta() override; public: const std::string& get_service_name() const override { return this->TRPCRequest::get_service_name(); } const std::string& get_method_name() const override { return this->TRPCRequest::get_method_name(); } void set_service_name(const std::string& service_name) override { return this->TRPCRequest::set_service_name(service_name); } void set_method_name(const std::string& method_name) override { return this->TRPCRequest::set_method_name(method_name); } bool set_meta_module_data(const RPCModuleData& data) override; bool get_meta_module_data(RPCModuleData& data) const override; void set_caller_name(const std::string& caller_name) { return this->TRPCRequest::set_caller_name(caller_name); } const std::string& get_caller_name() const { return this->TRPCRequest::get_caller_name(); } bool set_http_header(const std::string& name, const std::string& value) override; bool add_http_header(const std::string& name, const std::string& value) override; bool get_http_header(const std::string& name, std::string& value) const override; public: TRPCHttpRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; class TRPCHttpResponse : public protocol::HttpResponse, public RPCResponse, public TRPCResponse { public: bool serialize_meta() override; bool deserialize_meta() override; public: int get_status_code() const override { return this->TRPCResponse::get_status_code(); } int get_error() const override { return this->TRPCResponse::get_error(); } const char *get_errmsg() const override { return this->TRPCResponse::get_errmsg(); } void set_status_code(int code) override { return this->TRPCResponse::set_status_code(code); } void set_error(int error) override { return this->TRPCResponse::set_error(error); } bool set_http_code(int code) override { return this->protocol::HttpResponse::set_status_code(std::to_string(code)); } bool set_meta_module_data(const RPCModuleData& data) override; bool get_meta_module_data(RPCModuleData& data) const override; bool set_http_header(const std::string& name, const std::string& value) override; bool add_http_header(const std::string& name, const std::string& value) override; bool get_http_header(const std::string& name, std::string& value) const override; public: TRPCHttpResponse() { this->size_limit = RPC_BODY_SIZE_LIMIT; } }; } // namespace srpc #endif srpc-0.10.1/src/message/rpc_meta.proto000066400000000000000000000013451454502251400176370ustar00rootroot00000000000000syntax="proto2"; message RPCMetaKeyValue { required string key = 1; oneof value { int32 int_value = 2; string str_value = 3; bytes bytes_value = 4; } }; message RPCRequestMeta { optional string service_name = 1; optional string method_name = 2; optional int64 log_id = 3; }; message RPCResponseMeta { optional int32 status_code = 1 [default = 0]; optional int32 error = 2 [default = 0]; }; message RPCMeta { optional RPCRequestMeta request = 1; optional RPCResponseMeta response = 2; optional uint32 srpc_version = 3; optional int32 compress_type = 4 [default = 0]; optional int32 origin_size = 5; optional int32 compressed_size = 6; optional int32 data_type = 7; repeated RPCMetaKeyValue trans_info = 8; }; srpc-0.10.1/src/message/rpc_meta_brpc.proto000066400000000000000000000103221454502251400206400ustar00rootroot00000000000000// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. syntax="proto2"; // Not support streaming yet //import "brpc/streaming_rpc_meta.proto"; //package brpc; message BrpcMeta { optional BrpcRequestMeta request = 1; optional BrpcResponseMeta response = 2; optional int32 compress_type = 3; optional int64 correlation_id = 4; optional int32 attachment_size = 5; optional ChunkInfo chunk_info = 6; optional bytes authentication_data = 7; optional StreamSettings stream_settings = 8; } message BrpcRequestMeta { required string service_name = 1; required string method_name = 2; optional int64 log_id = 3; optional int64 trace_id = 4; optional int64 span_id = 5; optional int64 parent_span_id = 6; } message BrpcResponseMeta { optional int32 error_code = 1; optional string error_text = 2; } ////// brpc/options.proto ///////// //import "google/protobuf/descriptor.proto"; enum TalkType { TALK_TYPE_NORMAL = 0; TALK_TYPE_ONEWAY = 1; // TODO } enum ProtocolType { PROTOCOL_UNKNOWN = 0; PROTOCOL_BAIDU_STD = 1; /* PROTOCOL_STREAMING_RPC = 2; PROTOCOL_HULU_PBRPC = 3; PROTOCOL_SOFA_PBRPC = 4; PROTOCOL_RTMP = 5; PROTOCOL_THRIFT = 6; PROTOCOL_HTTP = 7; PROTOCOL_PUBLIC_PBRPC = 8; PROTOCOL_NOVA_PBRPC = 9; PROTOCOL_REDIS = 10; PROTOCOL_NSHEAD_CLIENT = 11; // implemented in baidu-rpc-ub PROTOCOL_NSHEAD = 12; PROTOCOL_HADOOP_RPC = 13; PROTOCOL_HADOOP_SERVER_RPC = 14; PROTOCOL_MONGO = 15; // server side only PROTOCOL_UBRPC_COMPACK = 16; PROTOCOL_DIDX_CLIENT = 17; // Client side only PROTOCOL_MEMCACHE = 18; // Client side only PROTOCOL_ITP = 19; PROTOCOL_NSHEAD_MCPACK = 20; PROTOCOL_DISP_IDL = 21; // Client side only PROTOCOL_ERSDA_CLIENT = 22; // Client side only PROTOCOL_UBRPC_MCPACK2 = 23; // Client side only // Reserve special protocol for cds-agent, which depends on FIFO right now PROTOCOL_CDS_AGENT = 24; // Client side only PROTOCOL_ESP = 25; // Client side only PROTOCOL_H2 = 26; */ } message ChunkInfo { required int64 stream_id = 1; required int64 chunk_id = 2; } // for streaming message StreamSettings { required int64 stream_id = 1; optional bool need_feedback = 2 [default = false]; optional bool writable = 3 [default = false]; } enum FrameType { FRAME_TYPE_UNKNOWN = 0; FRAME_TYPE_RST = 1; FRAME_TYPE_CLOSE = 2; FRAME_TYPE_DATA = 3; FRAME_TYPE_FEEDBACK= 4; } message StreamFrameMeta { required int64 stream_id = 1; optional int64 source_stream_id = 2; optional FrameType frame_type = 3; optional bool has_continuation = 4; optional Feedback feedback = 5; } message Feedback { optional int64 consumed_size = 1; } /* extend google.protobuf.ServiceOptions { // Timeout in milliseconds, at service level. optional int64 service_timeout = 90000 [default = 10000]; } extend google.protobuf.MethodOptions { // Talk type. optional TalkType request_talk_type = 90001 [default = TALK_TYPE_NORMAL]; optional TalkType response_talk_type = 90002 [default = TALK_TYPE_NORMAL]; // If set, override service_timeout. optional int64 method_timeout = 90003; // Compression for request/response. optional CompressType request_compression = 90004 [default = COMPRESS_TYPE_NONE]; optional CompressType response_compression = 90005 [default = COMPRESS_TYPE_NONE]; } */ srpc-0.10.1/src/message/rpc_meta_trpc.proto000066400000000000000000000050241454502251400206650ustar00rootroot00000000000000syntax = "proto3"; enum TrpcMagic { TRPC_DEFAULT_NONE = 0x00; TRPC_MAGIC_VALUE = 0x930; } enum TrpcDataFrameType { TRPC_UNARY_FRAME = 0x00; TRPC_STREAM_FRAME = 0x01; } enum TrpcDataFrameState { TRPC_NO_STATE = 0x00; TRPC_STREAM_FINISH = 0x01; } enum TrpcProtoVersion { TRPC_PROTO_V1 = 0; } enum TrpcCallType { TRPC_UNARY_CALL = 0; TRPC_ONEWAY_CALL = 1; TRPC_CLIENT_STREAM_CALL = 2; TRPC_SERVER_STREAM_CALL = 3; TRPC_BIDI_STREAM_CALL = 4; } enum TrpcMessageType { TRPC_DEFAULT = 0x00; TRPC_DYEING_MESSAGE = 0x01; TRPC_TRACE_MESSAGE = 0x02; TRPC_MULTI_ENV_MESSAGE = 0x04; TRPC_GRID_MESSAGE = 0x08; TRPC_SETNAME_MESSAGE = 0x10; } enum TrpcContentEncodeType { TRPC_PROTO_ENCODE = 0; TRPC_JCE_ENCODE = 1; TRPC_JSON_ENCODE = 2; TRPC_FLATBUFFER_ENCODE = 3; TRPC_NOOP_ENCODE = 4; } enum TrpcCompressType { TRPC_DEFAULT_COMPRESS = 0; TRPC_GZIP_COMPRESS = 1; TRPC_SNAPPY_COMPRESS = 2; // srpc framework support zlib and lz4 TRPC_ZLIB_COMPRESS = 3; TRPC_LZ4_COMPRESS = 4; } enum TrpcRetCode { TRPC_INVOKE_SUCCESS = 0; TRPC_SERVER_DECODE_ERR = 1; TRPC_SERVER_ENCODE_ERR = 2; TRPC_SERVER_NOSERVICE_ERR = 11; TRPC_SERVER_NOFUNC_ERR = 12; TRPC_SERVER_TIMEOUT_ERR = 21; TRPC_SERVER_OVERLOAD_ERR = 22; TRPC_SERVER_SYSTEM_ERR = 31; TRPC_SERVER_AUTH_ERR = 41; TRPC_CLIENT_INVOKE_TIMEOUT_ERR = 101; TRPC_CLIENT_CONNECT_ERR = 111; TRPC_CLIENT_ENCODE_ERR = 121; TRPC_CLIENT_DECODE_ERR = 122; TRPC_CLIENT_ROUTER_ERR = 131; TRPC_CLINET_NETWORK_ERR = 141; TRPC_INVOKE_UNKNOWN_ERR = 999; } message RequestProtocol { uint32 version = 1; uint32 call_type = 2; uint32 request_id = 3; uint32 timeout = 4; bytes caller = 5; bytes callee = 6; bytes func = 7; uint32 message_type = 8; map trans_info = 9; uint32 content_type = 10; uint32 content_encoding = 11; } message ResponseProtocol { uint32 version = 1; uint32 call_type = 2; uint32 request_id = 3; int32 ret = 4; int32 func_ret = 5; bytes error_msg = 6; uint32 message_type = 7; map trans_info = 8; uint32 content_type = 9; uint32 content_encoding = 10; } srpc-0.10.1/src/module/000077500000000000000000000000001454502251400146165ustar00rootroot00000000000000srpc-0.10.1/src/module/CMakeLists.txt000066400000000000000000000012621454502251400173570ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(module) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(PROTO_LIST proto/opentelemetry_common.proto proto/opentelemetry_resource.proto proto/opentelemetry_trace.proto proto/opentelemetry_metrics.proto proto/opentelemetry_metrics_service.proto) protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_LIST}) set(SRC rpc_module.cc rpc_trace_module.cc rpc_metrics_module.cc rpc_trace_filter.cc rpc_metrics_filter.cc ${PROTO_SRCS} ${PROTO_HDRS} ) add_library(${PROJECT_NAME} OBJECT ${SRC}) if (WIN32) target_compile_definitions( ${PROJECT_NAME} PRIVATE strdup=_strdup strcasecmp=_stricmp strncasecmp=_strnicmp ) endif () srpc-0.10.1/src/module/proto/000077500000000000000000000000001454502251400157615ustar00rootroot00000000000000srpc-0.10.1/src/module/proto/opentelemetry_common.proto000066400000000000000000000061311454502251400233130ustar00rootroot00000000000000// Copyright 2019, OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package opentelemetry.proto.common.v1; // AnyValue is used to represent any type of attribute value. AnyValue may contain a // primitive value such as a string or integer or it may contain an arbitrary nested // object containing arrays, key-value lists and primitives. message AnyValue { // The value is one of the listed fields. It is valid for all values to be unspecified // in which case this AnyValue is considered to be "null". oneof value { string string_value = 1; bool bool_value = 2; int64 int_value = 3; double double_value = 4; ArrayValue array_value = 5; KeyValueList kvlist_value = 6; bytes bytes_value = 7; } } // ArrayValue is a list of AnyValue messages. We need ArrayValue as a message // since oneof in AnyValue does not allow repeated fields. message ArrayValue { // Array of values. The array may be empty (contain 0 elements). repeated AnyValue values = 1; } // KeyValueList is a list of KeyValue messages. We need KeyValueList as a message // since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need // a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to // avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches // are semantically equivalent. message KeyValueList { // A collection of key/value pairs of key-value pairs. The list may be empty (may // contain 0 elements). // The keys MUST be unique (it is not allowed to have more than one // value with the same key). repeated KeyValue values = 1; } // KeyValue is a key-value pair that is used to store Span attributes, Link // attributes, etc. message KeyValue { string key = 1; AnyValue value = 2; } // StringKeyValue is a pair of key/value strings. This is the simpler (and faster) version // of KeyValue that only supports string values. message StringKeyValue { option deprecated = true; string key = 1; string value = 2; } ////////// deprecated // InstrumentationLibrary is a message representing the instrumentation library information // such as the fully qualified name and version. message InstrumentationLibrary { // An empty instrumentation library name means the name is unknown. string name = 1; string version = 2; } // such as the fully qualified name and version. message InstrumentationScope { // An empty instrumentation scope name means the name is unknown. string name = 1; string version = 2; repeated KeyValue attributes = 3; uint32 dropped_attributes_count = 4; } srpc-0.10.1/src/module/proto/opentelemetry_metrics.proto000066400000000000000000000677641454502251400235140ustar00rootroot00000000000000// Copyright 2019, OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package opentelemetry.proto.metrics.v1; import "opentelemetry_common.proto"; import "opentelemetry_resource.proto"; // MetricsData represents the metrics data that can be stored in a persistent // storage, OR can be embedded by other protocols that transfer OTLP metrics // data but do not implement the OTLP protocol. // // The main difference between this message and collector protocol is that // in this message there will not be any "control" or "metadata" specific to // OTLP protocol. // // When new fields are added into this message, the OTLP request MUST be updated // as well. message MetricsData { // An array of ResourceMetrics. // For data coming from a single resource this array will typically contain // one element. Intermediary nodes that receive data from multiple origins // typically batch the data before forwarding further and in that case this // array will contain multiple elements. repeated ResourceMetrics resource_metrics = 1; } ////// deprecated: A collection of InstrumentationLibraryMetrics from a Resource. // A collection of ScopeMetrics from a Resource. message ResourceMetrics { reserved 1000; // The resource for the metrics in this message. // If this field is not set then no resource info is known. opentelemetry.proto.resource.v1.Resource resource = 1; ////// deprecated //repeated InstrumentationLibraryMetrics instrumentation_library_metrics = 2; // A list of metrics that originate from a resource. repeated ScopeMetrics scope_metrics = 2; // This schema_url applies to the data in the "resource" field. It does not apply // to the data in the "scope_metrics" field which have their own schema_url field. string schema_url = 3; } ///// deprecated: A collection of Metrics produced by an InstrumentationLibrary. //message InstrumentationLibraryMetrics { // The instrumentation library information for the metrics in this message. // Semantically when InstrumentationLibrary isn't set, it is equivalent with // an empty instrumentation library name (unknown). // opentelemetry.proto.common.v1.InstrumentationLibrary instrumentation_library = 1; // A list of metrics that originate from an instrumentation library. // repeated Metric metrics = 2; // This schema_url applies to all metrics in the "metrics" field. // string schema_url = 3; //} // A collection of Metrics produced by an Scope. message ScopeMetrics { // The instrumentation scope information for the metrics in this message. // Semantically when InstrumentationScope isn't set, it is equivalent with // an empty instrumentation scope name (unknown). opentelemetry.proto.common.v1.InstrumentationScope scope = 1; // A list of metrics that originate from an instrumentation library. repeated Metric metrics = 2; // This schema_url applies to all metrics in the "metrics" field. string schema_url = 3; } // Defines a Metric which has one or more timeseries. The following is a // brief summary of the Metric data model. For more details, see: // // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md // // // The data model and relation between entities is shown in the // diagram below. Here, "DataPoint" is the term used to refer to any // one of the specific data point value types, and "points" is the term used // to refer to any one of the lists of points contained in the Metric. // // - Metric is composed of a metadata and data. // - Metadata part contains a name, description, unit. // - Data is one of the possible types (Sum, Gauge, Histogram, Summary). // - DataPoint contains timestamps, attributes, and one of the possible value type // fields. // // Metric // +------------+ // |name | // |description | // |unit | +------------------------------------+ // |data |---> |Gauge, Sum, Histogram, Summary, ... | // +------------+ +------------------------------------+ // // Data [One of Gauge, Sum, Histogram, Summary, ...] // +-----------+ // |... | // Metadata about the Data. // |points |--+ // +-----------+ | // | +---------------------------+ // | |DataPoint 1 | // v |+------+------+ +------+ | // +-----+ ||label |label |...|label | | // | 1 |-->||value1|value2|...|valueN| | // +-----+ |+------+------+ +------+ | // | . | |+-----+ | // | . | ||value| | // | . | |+-----+ | // | . | +---------------------------+ // | . | . // | . | . // | . | . // | . | +---------------------------+ // | . | |DataPoint M | // +-----+ |+------+------+ +------+ | // | M |-->||label |label |...|label | | // +-----+ ||value1|value2|...|valueN| | // |+------+------+ +------+ | // |+-----+ | // ||value| | // |+-----+ | // +---------------------------+ // // Each distinct type of DataPoint represents the output of a specific // aggregation function, the result of applying the DataPoint's // associated function of to one or more measurements. // // All DataPoint types have three common fields: // - Attributes includes key-value pairs associated with the data point // - TimeUnixNano is required, set to the end time of the aggregation // - StartTimeUnixNano is optional, but strongly encouraged for DataPoints // having an AggregationTemporality field, as discussed below. // // Both TimeUnixNano and StartTimeUnixNano values are expressed as // UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. // // # TimeUnixNano // // This field is required, having consistent interpretation across // DataPoint types. TimeUnixNano is the moment corresponding to when // the data point's aggregate value was captured. // // Data points with the 0 value for TimeUnixNano SHOULD be rejected // by consumers. // // # StartTimeUnixNano // // StartTimeUnixNano in general allows detecting when a sequence of // observations is unbroken. This field indicates to consumers the // start time for points with cumulative and delta // AggregationTemporality, and it should be included whenever possible // to support correct rate calculation. Although it may be omitted // when the start time is truly unknown, setting StartTimeUnixNano is // strongly encouraged. message Metric { reserved 4, 6, 8; // name of the metric, including its DNS name prefix. It must be unique. string name = 1; // description of the metric, which can be used in documentation. string description = 2; // unit in which the metric value is reported. Follows the format // described by http://unitsofmeasure.org/ucum.html. string unit = 3; // Data determines the aggregation type (if any) of the metric, what is the // reported value type for the data points, as well as the relatationship to // the time interval over which they are reported. oneof data { Gauge gauge = 5; Sum sum = 7; Histogram histogram = 9; ExponentialHistogram exponential_histogram = 10; Summary summary = 11; } } // Gauge represents the type of a scalar metric that always exports the // "current value" for every data point. It should be used for an "unknown" // aggregation. // // A Gauge does not support different aggregation temporalities. Given the // aggregation is unknown, points cannot be combined using the same // aggregation, regardless of aggregation temporalities. Therefore, // AggregationTemporality is not included. Consequently, this also means // "StartTimeUnixNano" is ignored for all data points. message Gauge { repeated NumberDataPoint data_points = 1; } // Sum represents the type of a scalar metric that is calculated as a sum of all // reported measurements over a time interval. message Sum { repeated NumberDataPoint data_points = 1; // aggregation_temporality describes if the aggregator reports delta changes // since last report time, or cumulative changes since a fixed start time. AggregationTemporality aggregation_temporality = 2; // If "true" means that the sum is monotonic. bool is_monotonic = 3; } // Histogram represents the type of a metric that is calculated by aggregating // as a Histogram of all reported measurements over a time interval. message Histogram { repeated HistogramDataPoint data_points = 1; // aggregation_temporality describes if the aggregator reports delta changes // since last report time, or cumulative changes since a fixed start time. AggregationTemporality aggregation_temporality = 2; } // ExponentialHistogram represents the type of a metric that is calculated by aggregating // as a ExponentialHistogram of all reported double measurements over a time interval. message ExponentialHistogram { repeated ExponentialHistogramDataPoint data_points = 1; // aggregation_temporality describes if the aggregator reports delta changes // since last report time, or cumulative changes since a fixed start time. AggregationTemporality aggregation_temporality = 2; } // Summary metric data are used to convey quantile summaries, // a Prometheus (see: https://prometheus.io/docs/concepts/metric_types/#summary) // and OpenMetrics (see: https://github.com/OpenObservability/OpenMetrics/blob/4dbf6075567ab43296eed941037c12951faafb92/protos/prometheus.proto#L45) // data type. These data points cannot always be merged in a meaningful way. // While they can be useful in some applications, histogram data points are // recommended for new applications. message Summary { repeated SummaryDataPoint data_points = 1; } // AggregationTemporality defines how a metric aggregator reports aggregated // values. It describes how those values relate to the time interval over // which they are aggregated. enum AggregationTemporality { // UNSPECIFIED is the default AggregationTemporality, it MUST not be used. AGGREGATION_TEMPORALITY_UNSPECIFIED = 0; // DELTA is an AggregationTemporality for a metric aggregator which reports // changes since last report time. Successive metrics contain aggregation of // values from continuous and non-overlapping intervals. // // The values for a DELTA metric are based only on the time interval // associated with one measurement cycle. There is no dependency on // previous measurements like is the case for CUMULATIVE metrics. // // For example, consider a system measuring the number of requests that // it receives and reports the sum of these requests every second as a // DELTA metric: // // 1. The system starts receiving at time=t_0. // 2. A request is received, the system measures 1 request. // 3. A request is received, the system measures 1 request. // 4. A request is received, the system measures 1 request. // 5. The 1 second collection cycle ends. A metric is exported for the // number of requests received over the interval of time t_0 to // t_0+1 with a value of 3. // 6. A request is received, the system measures 1 request. // 7. A request is received, the system measures 1 request. // 8. The 1 second collection cycle ends. A metric is exported for the // number of requests received over the interval of time t_0+1 to // t_0+2 with a value of 2. AGGREGATION_TEMPORALITY_DELTA = 1; // CUMULATIVE is an AggregationTemporality for a metric aggregator which // reports changes since a fixed start time. This means that current values // of a CUMULATIVE metric depend on all previous measurements since the // start time. Because of this, the sender is required to retain this state // in some form. If this state is lost or invalidated, the CUMULATIVE metric // values MUST be reset and a new fixed start time following the last // reported measurement time sent MUST be used. // // For example, consider a system measuring the number of requests that // it receives and reports the sum of these requests every second as a // CUMULATIVE metric: // // 1. The system starts receiving at time=t_0. // 2. A request is received, the system measures 1 request. // 3. A request is received, the system measures 1 request. // 4. A request is received, the system measures 1 request. // 5. The 1 second collection cycle ends. A metric is exported for the // number of requests received over the interval of time t_0 to // t_0+1 with a value of 3. // 6. A request is received, the system measures 1 request. // 7. A request is received, the system measures 1 request. // 8. The 1 second collection cycle ends. A metric is exported for the // number of requests received over the interval of time t_0 to // t_0+2 with a value of 5. // 9. The system experiences a fault and loses state. // 10. The system recovers and resumes receiving at time=t_1. // 11. A request is received, the system measures 1 request. // 12. The 1 second collection cycle ends. A metric is exported for the // number of requests received over the interval of time t_1 to // t_0+1 with a value of 1. // // Note: Even though, when reporting changes since last report time, using // CUMULATIVE is valid, it is not recommended. This may cause problems for // systems that do not use start_time to determine when the aggregation // value was reset (e.g. Prometheus). AGGREGATION_TEMPORALITY_CUMULATIVE = 2; } // DataPointFlags is defined as a protobuf 'uint32' type and is to be used as a // bit-field representing 32 distinct boolean flags. Each flag defined in this // enum is a bit-mask. To test the presence of a single flag in the flags of // a data point, for example, use an expression like: // // (point.flags & FLAG_NO_RECORDED_VALUE) == FLAG_NO_RECORDED_VALUE // enum DataPointFlags { FLAG_NONE = 0; // This DataPoint is valid but has no recorded value. This value // SHOULD be used to reflect explicitly missing data in a series, as // for an equivalent to the Prometheus "staleness marker". FLAG_NO_RECORDED_VALUE = 1; // Bits 2-31 are reserved for future use. } // NumberDataPoint is a single data point in a timeseries that describes the // time-varying scalar value of a metric. message NumberDataPoint { reserved 1; // The set of key/value pairs that uniquely identify the timeseries from // where this point belongs. The list may be empty (may contain 0 elements). // Attribute keys MUST be unique (it is not allowed to have more than one // attribute with the same key). repeated opentelemetry.proto.common.v1.KeyValue attributes = 7; // StartTimeUnixNano is optional but strongly encouraged, see the // the detailed comments above Metric. // // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January // 1970. fixed64 start_time_unix_nano = 2; // TimeUnixNano is required, see the detailed comments above Metric. // // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January // 1970. fixed64 time_unix_nano = 3; // The value itself. A point is considered invalid when one of the recognized // value fields is not present inside this oneof. oneof value { double as_double = 4; sfixed64 as_int = 6; } // (Optional) List of exemplars collected from // measurements that were used to form the data point repeated Exemplar exemplars = 5; // Flags that apply to this specific data point. See DataPointFlags // for the available flags and their meaning. uint32 flags = 8; } // HistogramDataPoint is a single data point in a timeseries that describes the // time-varying values of a Histogram. A Histogram contains summary statistics // for a population of values, it may optionally contain the distribution of // those values across a set of buckets. // // If the histogram contains the distribution of values, then both // "explicit_bounds" and "bucket counts" fields must be defined. // If the histogram does not contain the distribution of values, then both // "explicit_bounds" and "bucket_counts" must be omitted and only "count" and // "sum" are known. message HistogramDataPoint { reserved 1; // The set of key/value pairs that uniquely identify the timeseries from // where this point belongs. The list may be empty (may contain 0 elements). // Attribute keys MUST be unique (it is not allowed to have more than one // attribute with the same key). repeated opentelemetry.proto.common.v1.KeyValue attributes = 9; // StartTimeUnixNano is optional but strongly encouraged, see the // the detailed comments above Metric. // // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January // 1970. fixed64 start_time_unix_nano = 2; // TimeUnixNano is required, see the detailed comments above Metric. // // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January // 1970. fixed64 time_unix_nano = 3; // count is the number of values in the population. Must be non-negative. This // value must be equal to the sum of the "count" fields in buckets if a // histogram is provided. fixed64 count = 4; // sum of the values in the population. If count is zero then this field // must be zero. // // Note: Sum should only be filled out when measuring non-negative discrete // events, and is assumed to be monotonic over the values of these events. // Negative events *can* be recorded, but sum should not be filled out when // doing so. This is specifically to enforce compatibility w/ OpenMetrics, // see: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#histogram double sum = 5; // bucket_counts is an optional field contains the count values of histogram // for each bucket. // // The sum of the bucket_counts must equal the value in the count field. // // The number of elements in bucket_counts array must be by one greater than // the number of elements in explicit_bounds array. repeated fixed64 bucket_counts = 6; // explicit_bounds specifies buckets with explicitly defined bounds for values. // // The boundaries for bucket at index i are: // // (-infinity, explicit_bounds[i]] for i == 0 // (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds) // (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds) // // The values in the explicit_bounds array must be strictly increasing. // // Histogram buckets are inclusive of their upper boundary, except the last // bucket where the boundary is at infinity. This format is intentionally // compatible with the OpenMetrics histogram definition. repeated double explicit_bounds = 7; // (Optional) List of exemplars collected from // measurements that were used to form the data point repeated Exemplar exemplars = 8; // Flags that apply to this specific data point. See DataPointFlags // for the available flags and their meaning. uint32 flags = 10; // min is the minimum value over (start_time, end_time]. double min = 11; // max is the maximum value over (start_time, end_time]. double max = 12; } // ExponentialHistogramDataPoint is a single data point in a timeseries that describes the // time-varying values of a ExponentialHistogram of double values. A ExponentialHistogram contains // summary statistics for a population of values, it may optionally contain the // distribution of those values across a set of buckets. // message ExponentialHistogramDataPoint { // The set of key/value pairs that uniquely identify the timeseries from // where this point belongs. The list may be empty (may contain 0 elements). // Attribute keys MUST be unique (it is not allowed to have more than one // attribute with the same key). repeated opentelemetry.proto.common.v1.KeyValue attributes = 1; // StartTimeUnixNano is optional but strongly encouraged, see the // the detailed comments above Metric. // // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January // 1970. fixed64 start_time_unix_nano = 2; // TimeUnixNano is required, see the detailed comments above Metric. // // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January // 1970. fixed64 time_unix_nano = 3; // count is the number of values in the population. Must be // non-negative. This value must be equal to the sum of the "bucket_counts" // values in the positive and negative Buckets plus the "zero_count" field. fixed64 count = 4; // sum of the values in the population. If count is zero then this field // must be zero. // // Note: Sum should only be filled out when measuring non-negative discrete // events, and is assumed to be monotonic over the values of these events. // Negative events *can* be recorded, but sum should not be filled out when // doing so. This is specifically to enforce compatibility w/ OpenMetrics, // see: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#histogram double sum = 5; // scale describes the resolution of the histogram. Boundaries are // located at powers of the base, where: // // base = (2^(2^-scale)) // // The histogram bucket identified by `index`, a signed integer, // contains values that are greater than or equal to (base^index) and // less than (base^(index+1)). // // The positive and negative ranges of the histogram are expressed // separately. Negative values are mapped by their absolute value // into the negative range using the same scale as the positive range. // // scale is not restricted by the protocol, as the permissible // values depend on the range of the data. sint32 scale = 6; // zero_count is the count of values that are either exactly zero or // within the region considered zero by the instrumentation at the // tolerated degree of precision. This bucket stores values that // cannot be expressed using the standard exponential formula as // well as values that have been rounded to zero. // // Implementations MAY consider the zero bucket to have probability // mass equal to (zero_count / count). fixed64 zero_count = 7; // positive carries the positive range of exponential bucket counts. Buckets positive = 8; // negative carries the negative range of exponential bucket counts. Buckets negative = 9; // Buckets are a set of bucket counts, encoded in a contiguous array // of counts. message Buckets { // Offset is the bucket index of the first entry in the bucket_counts array. // // Note: This uses a varint encoding as a simple form of compression. sint32 offset = 1; // Count is an array of counts, where count[i] carries the count // of the bucket at index (offset+i). count[i] is the count of // values greater than or equal to base^(offset+i) and less than // base^(offset+i+1). // // Note: By contrast, the explicit HistogramDataPoint uses // fixed64. This field is expected to have many buckets, // especially zeros, so uint64 has been selected to ensure // varint encoding. repeated uint64 bucket_counts = 2; } // Flags that apply to this specific data point. See DataPointFlags // for the available flags and their meaning. uint32 flags = 10; // (Optional) List of exemplars collected from // measurements that were used to form the data point repeated Exemplar exemplars = 11; // min is the minimum value over (start_time, end_time]. double min = 12; // max is the maximum value over (start_time, end_time]. double max = 13; } // SummaryDataPoint is a single data point in a timeseries that describes the // time-varying values of a Summary metric. message SummaryDataPoint { reserved 1; // The set of key/value pairs that uniquely identify the timeseries from // where this point belongs. The list may be empty (may contain 0 elements). // Attribute keys MUST be unique (it is not allowed to have more than one // attribute with the same key). repeated opentelemetry.proto.common.v1.KeyValue attributes = 7; // StartTimeUnixNano is optional but strongly encouraged, see the // the detailed comments above Metric. // // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January // 1970. fixed64 start_time_unix_nano = 2; // TimeUnixNano is required, see the detailed comments above Metric. // // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January // 1970. fixed64 time_unix_nano = 3; // count is the number of values in the population. Must be non-negative. fixed64 count = 4; // sum of the values in the population. If count is zero then this field // must be zero. // // Note: Sum should only be filled out when measuring non-negative discrete // events, and is assumed to be monotonic over the values of these events. // Negative events *can* be recorded, but sum should not be filled out when // doing so. This is specifically to enforce compatibility w/ OpenMetrics, // see: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#summary double sum = 5; // Represents the value at a given quantile of a distribution. // // To record Min and Max values following conventions are used: // - The 1.0 quantile is equivalent to the maximum value observed. // - The 0.0 quantile is equivalent to the minimum value observed. // // See the following issue for more context: // https://github.com/open-telemetry/opentelemetry-proto/issues/125 message ValueAtQuantile { // The quantile of a distribution. Must be in the interval // [0.0, 1.0]. double quantile = 1; // The value at the given quantile of a distribution. // // Quantile values must NOT be negative. double value = 2; } // (Optional) list of values at different quantiles of the distribution calculated // from the current snapshot. The quantiles must be strictly increasing. repeated ValueAtQuantile quantile_values = 6; // Flags that apply to this specific data point. See DataPointFlags // for the available flags and their meaning. uint32 flags = 8; } // A representation of an exemplar, which is a sample input measurement. // Exemplars also hold information about the environment when the measurement // was recorded, for example the span and trace ID of the active span when the // exemplar was recorded. message Exemplar { reserved 1; // The set of key/value pairs that were filtered out by the aggregator, but // recorded alongside the original measurement. Only key/value pairs that were // filtered out by the aggregator should be included repeated opentelemetry.proto.common.v1.KeyValue filtered_attributes = 7; // time_unix_nano is the exact time when this exemplar was recorded // // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January // 1970. fixed64 time_unix_nano = 2; // The value of the measurement that was recorded. An exemplar is // considered invalid when one of the recognized value fields is not present // inside this oneof. oneof value { double as_double = 3; sfixed64 as_int = 6; } // (Optional) Span ID of the exemplar trace. // span_id may be missing if the measurement is not recorded inside a trace // or if the trace is not sampled. bytes span_id = 4; // (Optional) Trace ID of the exemplar trace. // trace_id may be missing if the measurement is not recorded inside a trace // or if the trace is not sampled. bytes trace_id = 5; } srpc-0.10.1/src/module/proto/opentelemetry_metrics_service.proto000066400000000000000000000031171454502251400252120ustar00rootroot00000000000000// Copyright 2019, OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package opentelemetry.proto.collector.metrics.v1; import "opentelemetry_metrics.proto"; // Service that can be used to push metrics between one Application // instrumented with OpenTelemetry and a collector, or between a collector and a // central collector. service MetricsService { // For performance reasons, it is recommended to keep this RPC // alive for the entire life of the application. rpc Export(ExportMetricsServiceRequest) returns (ExportMetricsServiceResponse) {} } message ExportMetricsServiceRequest { // An array of ResourceMetrics. // For data coming from a single resource this array will typically contain one // element. Intermediary nodes (such as OpenTelemetry Collector) that receive // data from multiple origins typically batch the data before forwarding further and // in that case this array will contain multiple elements. repeated opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1; } message ExportMetricsServiceResponse { } srpc-0.10.1/src/module/proto/opentelemetry_resource.proto000066400000000000000000000021621454502251400236520ustar00rootroot00000000000000// Copyright 2019, OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package opentelemetry.proto.resource.v1; import "opentelemetry_common.proto"; // Resource information. message Resource { // Set of attributes that describe the resource. // Attribute keys MUST be unique (it is not allowed to have more than one // attribute with the same key). repeated opentelemetry.proto.common.v1.KeyValue attributes = 1; // dropped_attributes_count is the number of dropped attributes. If the value is 0, then // no attributes were dropped. uint32 dropped_attributes_count = 2; } srpc-0.10.1/src/module/proto/opentelemetry_trace.proto000066400000000000000000000274221454502251400231270ustar00rootroot00000000000000// Copyright 2019, OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package opentelemetry.proto.trace.v1; import "opentelemetry_common.proto"; import "opentelemetry_resource.proto"; // A collection of InstrumentationLibrarySpans from a Resource. // TracesData represents the traces data that can be stored in a persistent storage, // OR can be embedded by other protocols that transfer OTLP traces data but do // not implement the OTLP protocol. // // The main difference between this message and collector protocol is that // in this message there will not be any "control" or "metadata" specific to // OTLP protocol. // // When new fields are added into this message, the OTLP request MUST be updated // as well. message TracesData { // An array of ResourceSpans. // For data coming from a single resource this array will typically contain // one element. Intermediary nodes that receive data from multiple origins // typically batch the data before forwarding further and in that case this // array will contain multiple elements. repeated ResourceSpans resource_spans = 1; } // A collection of ScopeSpans from a Resource. message ResourceSpans { // The resource for the spans in this message. // If this field is not set then no resource info is known. opentelemetry.proto.resource.v1.Resource resource = 1; // A list of InstrumentationLibrarySpans that originate from a resource. repeated InstrumentationLibrarySpans instrumentation_library_spans = 2; // This schema_url applies to the data in the "resource" field. It does not apply // to the data in the "instrumentation_library_spans" field which have their own // schema_url field. string schema_url = 3; } // A collection of Spans produced by an InstrumentationLibrary. message InstrumentationLibrarySpans { // The instrumentation library information for the spans in this message. // Semantically when InstrumentationLibrary isn't set, it is equivalent with // an empty instrumentation library name (unknown). opentelemetry.proto.common.v1.InstrumentationLibrary instrumentation_library = 1; // A list of Spans that originate from an instrumentation library. repeated Span spans = 2; // This schema_url applies to all spans and span events in the "spans" field. string schema_url = 3; } // Span represents a single operation within a trace. Spans can be // nested to form a trace tree. Spans may also be linked to other spans // from the same or different trace and form graphs. Often, a trace // contains a root span that describes the end-to-end latency, and one // or more subspans for its sub-operations. A trace can also contain // multiple root spans, or none at all. Spans do not need to be // contiguous - there may be gaps or overlaps between spans in a trace. // // The next available field id is 17. message Span { // A unique identifier for a trace. All spans from the same trace share // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes // is considered invalid. // // This field is semantically required. Receiver should generate new // random trace_id if empty or invalid trace_id was received. // // This field is required. bytes trace_id = 1; // A unique identifier for a span within a trace, assigned when the span // is created. The ID is an 8-byte array. An ID with all zeroes is considered // invalid. // // This field is semantically required. Receiver should generate new // random span_id if empty or invalid span_id was received. // // This field is required. bytes span_id = 2; // trace_state conveys information about request position in multiple distributed tracing graphs. // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header // See also https://github.com/w3c/distributed-tracing for more details about this field. string trace_state = 3; // The `span_id` of this span's parent span. If this is a root span, then this // field must be empty. The ID is an 8-byte array. bytes parent_span_id = 4; // A description of the span's operation. // // For example, the name can be a qualified method name or a file name // and a line number where the operation is called. A best practice is to use // the same display name at the same call point in an application. // This makes it easier to correlate spans in different traces. // // This field is semantically required to be set to non-empty string. // When null or empty string received - receiver may use string "name" // as a replacement. There might be smarted algorithms implemented by // receiver to fix the empty span name. // // This field is required. string name = 5; // SpanKind is the type of span. Can be used to specify additional relationships between spans // in addition to a parent/child relationship. enum SpanKind { // Unspecified. Do NOT use as default. // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. SPAN_KIND_UNSPECIFIED = 0; // Indicates that the span represents an internal operation within an application, // as opposed to an operation happening at the boundaries. Default value. SPAN_KIND_INTERNAL = 1; // Indicates that the span covers server-side handling of an RPC or other // remote network request. SPAN_KIND_SERVER = 2; // Indicates that the span describes a request to some remote service. SPAN_KIND_CLIENT = 3; // Indicates that the span describes a producer sending a message to a broker. // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship // between producer and consumer spans. A PRODUCER span ends when the message was accepted // by the broker while the logical processing of the message might span a much longer time. SPAN_KIND_PRODUCER = 4; // Indicates that the span describes consumer receiving a message from a broker. // Like the PRODUCER kind, there is often no direct critical path latency relationship // between producer and consumer spans. SPAN_KIND_CONSUMER = 5; } // Distinguishes between spans generated in a particular context. For example, // two spans with the same name may be distinguished using `CLIENT` (caller) // and `SERVER` (callee) to identify queueing latency associated with the span. SpanKind kind = 6; // start_time_unix_nano is the start time of the span. On the client side, this is the time // kept by the local machine where the span execution starts. On the server side, this // is the time when the server's application handler starts running. // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. // // This field is semantically required and it is expected that end_time >= start_time. fixed64 start_time_unix_nano = 7; // end_time_unix_nano is the end time of the span. On the client side, this is the time // kept by the local machine where the span execution ends. On the server side, this // is the time when the server application handler stops running. // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. // // This field is semantically required and it is expected that end_time >= start_time. fixed64 end_time_unix_nano = 8; // attributes is a collection of key/value pairs. The value can be a string, // an integer, a double or the Boolean values `true` or `false`. Note, global attributes // like server name can be set using the resource API. Examples of attributes: // // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" // "/http/server_latency": 300 // "abc.com/myattribute": true // "abc.com/score": 10.239 repeated opentelemetry.proto.common.v1.KeyValue attributes = 9; // dropped_attributes_count is the number of attributes that were discarded. Attributes // can be discarded because their keys are too long or because there are too many // attributes. If this value is 0, then no attributes were dropped. uint32 dropped_attributes_count = 10; // Event is a time-stamped annotation of the span, consisting of user-supplied // text description and key-value pairs. message Event { // time_unix_nano is the time the event occurred. fixed64 time_unix_nano = 1; // name of the event. // This field is semantically required to be set to non-empty string. string name = 2; // attributes is a collection of attribute key/value pairs on the event. repeated opentelemetry.proto.common.v1.KeyValue attributes = 3; // dropped_attributes_count is the number of dropped attributes. If the value is 0, // then no attributes were dropped. uint32 dropped_attributes_count = 4; } // events is a collection of Event items. repeated Event events = 11; // dropped_events_count is the number of dropped events. If the value is 0, then no // events were dropped. uint32 dropped_events_count = 12; // A pointer from the current span to another span in the same trace or in a // different trace. For example, this can be used in batching operations, // where a single batch handler processes multiple requests from different // traces or when the handler receives a request from a different project. message Link { // A unique identifier of a trace that this linked span is part of. The ID is a // 16-byte array. bytes trace_id = 1; // A unique identifier for the linked span. The ID is an 8-byte array. bytes span_id = 2; // The trace_state associated with the link. string trace_state = 3; // attributes is a collection of attribute key/value pairs on the link. repeated opentelemetry.proto.common.v1.KeyValue attributes = 4; // dropped_attributes_count is the number of dropped attributes. If the value is 0, // then no attributes were dropped. uint32 dropped_attributes_count = 5; } // links is a collection of Links, which are references from this span to a span // in the same or different trace. repeated Link links = 13; // dropped_links_count is the number of dropped links after the maximum size was // enforced. If this value is 0, then no links were dropped. uint32 dropped_links_count = 14; // An optional final status for this span. Semantically when Status isn't set, it means // span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). Status status = 15; } // The Status type defines a logical error model that is suitable for different // programming environments, including REST APIs and RPC APIs. message Status { // IMPORTANT: Backward compatibility notes: // The deprecated status code. This is an optional field. // DeprecatedStatusCode deprecated_code = 1 [deprecated=true]; // A developer-facing human readable error message. string message = 2; // For the semantics of status codes see // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status enum StatusCode { // The default status. STATUS_CODE_UNSET = 0; // The Span has been validated by an Application developers or Operator to have // completed successfully. STATUS_CODE_OK = 1; // The Span contains an error. STATUS_CODE_ERROR = 2; }; // The status code. StatusCode code = 3; } srpc-0.10.1/src/module/rpc_filter.h000066400000000000000000000055241454502251400171260ustar00rootroot00000000000000/* Copyright (c) 2021 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_FILTER_H__ #define __RPC_FILTER_H__ #include #include #include #include "workflow/WFTask.h" #include "workflow/WFTaskFactory.h" namespace srpc { // for transfer between client-server static constexpr const char *OTLP_TRACE_PARENT = "traceparent"; static constexpr const char *OTLP_TRACE_STATE = "tracestate"; static constexpr const char *OTLP_AFFINITY_ATTRIBUTE = "affinity_attribute"; static constexpr size_t OTLP_TRACE_VERSION_SIZE = 2; static constexpr char const *OTLP_TRACE_VERSION = "trace_version"; static constexpr char const *OTLP_TRACE_FLAG = "trace_flag"; // for report static constexpr unsigned int OTLP_HTTP_REDIRECT_MAX = 0; static constexpr unsigned int OTLP_HTTP_RETRY_MAX = 1; static constexpr const char *OTLP_SERVICE_NAME = "rpc.service"; static constexpr const char *OTLP_METHOD_NAME = "rpc.method"; static constexpr const char *SRPC_HTTP_METHOD = "http.method"; static constexpr const char *SRPC_HTTP_STATUS_CODE = "http.status_code"; static constexpr size_t RPC_REPORT_THREHOLD_DEFAULT = 100; static constexpr size_t RPC_REPORT_INTERVAL_DEFAULT = 1000; /* msec */ static constexpr const char *SRPC_MODULE_DATA = "srpc_module_data"; using RPCModuleData = std::map; static RPCModuleData global_empty_map; class RPCFilter { public: SubTask *create_filter_task(const RPCModuleData& data) { if (this->filter(const_cast(data))) return this->create(const_cast(data)); return WFTaskFactory::create_empty_task(); } private: virtual SubTask *create(RPCModuleData& data) = 0; virtual bool filter(RPCModuleData& data) = 0; public: RPCFilter(enum RPCModuleType module_type) { this->module_type = module_type; } virtual ~RPCFilter() { } enum RPCModuleType get_module_type() const { return this->module_type; } virtual bool client_begin(SubTask *task, RPCModuleData& data) { return true; } virtual bool server_begin(SubTask *task, RPCModuleData& data) { return true; } virtual bool client_end(SubTask *task, RPCModuleData& data) { return true; } virtual bool server_end(SubTask *task, RPCModuleData& data) { return true; } private: enum RPCModuleType module_type; }; } // end namespace srpc #endif srpc-0.10.1/src/module/rpc_metrics_filter.cc000066400000000000000000000441701454502251400210120ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include "workflow/HttpMessage.h" #include "workflow/HttpUtil.h" #include "workflow/WFTaskFactory.h" #include "workflow/WFHttpServer.h" #include "rpc_basic.h" #include "rpc_var.h" #include "rpc_metrics_filter.h" #include "opentelemetry_metrics_service.pb.h" namespace srpc { static constexpr size_t METRICS_DEFAULT_MAX_AGE = 60; static constexpr size_t METRICS_DEFAULT_AGE_BUCKET = 5; static constexpr const char *METRICS_PULL_METRICS_PATH = "/metrics"; static constexpr const char *OTLP_METRICS_PATH = "/v1/metrics"; static constexpr const char *METRICS_REQUEST_COUNT = "total_request_count"; static constexpr const char *METRICS_REQUEST_METHOD = "total_request_method"; static constexpr const char *METRICS_REQUEST_LATENCY = "total_request_latency"; //static constexpr const char *METRICS_REQUEST_SIZE = "total_request_size"; //static constexpr const char *METRICS_RESPONSE_SIZE = "total_response_size"; using namespace opentelemetry::proto::collector::metrics::v1; using namespace opentelemetry::proto::metrics::v1; using namespace opentelemetry::proto::common::v1; using namespace opentelemetry::proto::resource::v1; RPCMetricsFilter::RPCMetricsFilter() : RPCFilter(RPCModuleTypeMetrics) { this->create_gauge(METRICS_REQUEST_COUNT, "total request count"); this->create_counter(METRICS_REQUEST_METHOD, "request method statistics"); // this->create_histogram(METRICS_REQUEST_SIZE, "total request body size", // {256, 512, 1024, 16384}); // this->create_histogram(METRICS_RESPONSE_SIZE, "total response body size", // {256, 512, 1024, 16384}); this->create_summary(METRICS_REQUEST_LATENCY, "request latency nano seconds", {{0.5, 0.05}, {0.9, 0.01}}); } bool RPCMetricsFilter::client_end(SubTask *task, RPCModuleData& data) { this->gauge(METRICS_REQUEST_COUNT)->increase(); this->counter(METRICS_REQUEST_METHOD)->add( {{"service", data[OTLP_SERVICE_NAME]}, {"method", data[OTLP_METHOD_NAME] }})->increase(); this->summary(METRICS_REQUEST_LATENCY)->observe(atoll(data[SRPC_DURATION].data())); return true; } bool RPCMetricsFilter::server_end(SubTask *task, RPCModuleData& data) { this->gauge(METRICS_REQUEST_COUNT)->increase(); this->counter(METRICS_REQUEST_METHOD)->add( {{"service", data[OTLP_SERVICE_NAME]}, {"method", data[OTLP_METHOD_NAME] }})->increase(); this->summary(METRICS_REQUEST_LATENCY)->observe(atoll(data[SRPC_DURATION].data())); return true; } GaugeVar *RPCMetricsFilter::gauge(const std::string& name) { return RPCVarFactory::gauge(name); } CounterVar *RPCMetricsFilter::counter(const std::string& name) { return RPCVarFactory::counter(name); } HistogramVar *RPCMetricsFilter::histogram(const std::string& name) { return RPCVarFactory::histogram(name); } SummaryVar *RPCMetricsFilter::summary(const std::string& name) { return RPCVarFactory::summary(name); } GaugeVar *RPCMetricsFilter::create_gauge(const std::string& name, const std::string& help) { if (RPCVarFactory::check_name_format(name) == false) { errno = EINVAL; return NULL; } this->mutex.lock(); const auto it = var_names.insert(name); this->mutex.unlock(); if (!it.second) { errno = EEXIST; return NULL; } GaugeVar *gauge = new GaugeVar(name, help); RPCVarLocal::get_instance()->add(name, gauge); return gauge; } CounterVar *RPCMetricsFilter::create_counter(const std::string& name, const std::string& help) { if (RPCVarFactory::check_name_format(name) == false) { errno = EINVAL; return NULL; } this->mutex.lock(); const auto it = var_names.insert(name); this->mutex.unlock(); if (!it.second) { errno = EEXIST; return NULL; } CounterVar *counter = new CounterVar(name, help); RPCVarLocal::get_instance()->add(name, counter); return counter; } HistogramVar *RPCMetricsFilter::create_histogram(const std::string& name, const std::string& help, const std::vector& bucket) { if (RPCVarFactory::check_name_format(name) == false) { errno = EINVAL; return NULL; } this->mutex.lock(); const auto it = var_names.insert(name); this->mutex.unlock(); if (!it.second) { errno = EEXIST; return NULL; } HistogramVar *histogram = new HistogramVar(name, help, bucket); RPCVarLocal::get_instance()->add(name, histogram); return histogram; } SummaryVar *RPCMetricsFilter::create_summary(const std::string& name, const std::string& help, const std::vector& quantile) { if (RPCVarFactory::check_name_format(name) == false) { errno = EINVAL; return NULL; } this->mutex.lock(); const auto it = var_names.insert(name); this->mutex.unlock(); if (!it.second) { errno = EEXIST; return NULL; } SummaryVar *summary = new SummaryVar(name, help, quantile, std::chrono::seconds(METRICS_DEFAULT_MAX_AGE), METRICS_DEFAULT_AGE_BUCKET); RPCVarLocal::get_instance()->add(name, summary); return summary; } SummaryVar *RPCMetricsFilter::create_summary(const std::string& name, const std::string& help, const std::vector& quantile, const std::chrono::milliseconds max_age, int age_bucket) { if (RPCVarFactory::check_name_format(name) == false) { errno = EINVAL; return NULL; } this->mutex.lock(); const auto it = var_names.insert(name); this->mutex.unlock(); if (!it.second) { errno = EEXIST; return NULL; } SummaryVar *summary = new SummaryVar(name, help, quantile, max_age, age_bucket); RPCVarLocal::get_instance()->add(name, summary); return summary; } void RPCMetricsFilter::reduce(std::unordered_map& out) { std::unordered_map::iterator it; RPCVarGlobal *global_var = RPCVarGlobal::get_instance(); global_var->mutex.lock(); for (RPCVarLocal *local : global_var->local_vars) { local->mutex.lock(); for (it = local->vars.begin(); it != local->vars.end(); it++) { if (this->var_names.find(it->first) == this->var_names.end()) continue; if (out.find(it->first) == out.end()) out.insert(std::make_pair(it->first, it->second->create(true))); else out[it->first]->reduce(it->second->get_data(), it->second->get_size()); } local->mutex.unlock(); } global_var->mutex.unlock(); } RPCMetricsPull::RPCMetricsPull() : collector(this->report_output), server(std::bind(&RPCMetricsPull::pull, this, std::placeholders::_1)) { } bool RPCMetricsPull::init(unsigned short port) { return this->server.start(port) == 0; } void RPCMetricsPull::deinit() { this->server.stop(); } void RPCMetricsPull::pull(WFHttpTask *task) { if (strcmp(task->get_req()->get_request_uri(), METRICS_PULL_METRICS_PATH)) return; this->mutex.lock(); this->expose(); task->get_resp()->append_output_body(std::move(this->report_output)); this->report_output.clear(); this->mutex.unlock(); } bool RPCMetricsPull::expose() { std::unordered_map tmp; std::unordered_map::iterator it; std::string output; this->reduce(tmp); for (it = tmp.begin(); it != tmp.end(); it++) { RPCVar *var = it->second; this->report_output += "# HELP " + var->get_name() + " " + var->get_help() + "\n# TYPE " + var->get_name() + " " + var->get_type_str() + "\n"; output.clear(); var->collect(&this->collector); this->report_output += output; } for (it = tmp.begin(); it != tmp.end(); it++) delete it->second; return true; } void RPCMetricsPull::Collector::collect_gauge(RPCVar *gauge, double data) { this->report_output += gauge->get_name() + " " + std::to_string(data) + "\n"; } void RPCMetricsPull::Collector::collect_counter_each(RPCVar *counter, const std::string& label, double data) { this->report_output += counter->get_name() + "{" + label + "} " + std::to_string(data) + "\n"; } void RPCMetricsPull::Collector::collect_histogram_each(RPCVar *histogram, double bucket_boundary, size_t current_count) { this->report_output += histogram->get_name(); if (bucket_boundary != DBL_MAX) { this->report_output += "_bucket{le=\"" + std::to_string(bucket_boundary) + "\"} "; } else this->report_output += "_bucket{le=\"+Inf\"} "; this->report_output += std::to_string(current_count) + "\n"; } void RPCMetricsPull::Collector::collect_histogram_end(RPCVar *histogram, double sum, size_t count) { this->report_output += histogram->get_name() + "_sum " + std::to_string(sum) + "\n"; this->report_output += histogram->get_name() + "_count " + std::to_string(count) + "\n"; } void RPCMetricsPull::Collector::collect_summary_each(RPCVar *summary, double quantile, double quantile_out) { this->report_output += summary->get_name() + "{quantile=\"" + std::to_string(quantile) + "\"} "; if (quantile_out == 0) this->report_output += "NaN"; else this->report_output += std::to_string(quantile_out); this->report_output += "\n"; } void RPCMetricsPull::Collector::collect_summary_end(RPCVar *summary, double sum, size_t count) { this->report_output += summary->get_name() + "_sum " + std::to_string(sum) + "\n"; this->report_output += summary->get_name() + "_count " + std::to_string(count) + "\n"; } bool RPCFilterPolicy::report(size_t count) { long long timestamp = GET_CURRENT_MS(); if (this->last_report_timestamp == 0) this->last_report_timestamp = timestamp; if (timestamp > this->last_report_timestamp + (long long)this->report_interval || count >= this->report_threshold) { this->last_report_timestamp = timestamp; return true; } return false; } RPCMetricsOTel::RPCMetricsOTel(const std::string& url, unsigned int redirect_max, unsigned int retry_max, size_t report_threshold, size_t report_interval_msec) : url(url + OTLP_METRICS_PATH), redirect_max(redirect_max), retry_max(retry_max), policy(report_threshold, report_interval_msec), report_counts(0) { } RPCMetricsOTel::RPCMetricsOTel(const std::string& url) : url(url + OTLP_METRICS_PATH), redirect_max(OTLP_HTTP_REDIRECT_MAX), retry_max(OTLP_HTTP_RETRY_MAX), policy(RPC_REPORT_THREHOLD_DEFAULT, RPC_REPORT_INTERVAL_DEFAULT), report_counts(0) { } RPCMetricsOTel::Collector::~Collector() { for (const auto& kv : this->label_map) delete kv.second; } void RPCMetricsOTel::add_attributes(const std::string& key, const std::string& value) { this->mutex.lock(); this->attributes.insert(std::make_pair(key, value)); this->mutex.unlock(); } size_t RPCMetricsOTel::clear_attributes() { size_t ret; this->mutex.lock(); ret = this->attributes.size(); this->attributes.clear(); this->mutex.unlock(); return ret; } SubTask *RPCMetricsOTel::create(RPCModuleData& data) { std::string *output = new std::string; ExportMetricsServiceRequest req; ResourceMetrics *rm = req.add_resource_metrics(); Resource *resource = rm->mutable_resource(); ScopeMetrics *metrics = rm->add_scope_metrics(); KeyValue *attribute; AnyValue *value; InstrumentationScope *scope = metrics->mutable_scope(); scope->set_name(this->scope_name); auto iter = data.find(OTLP_METHOD_NAME); if (iter != data.end()) { attribute = resource->add_attributes(); attribute->set_key(OTLP_METHOD_NAME); value = attribute->mutable_value(); value->set_string_value(iter->second); } this->mutex.lock(); for (const auto& attr : this->attributes) { KeyValue *attribute = resource->add_attributes(); attribute->set_key(attr.first); AnyValue *value = attribute->mutable_value(); value->set_string_value(attr.second); } this->mutex.unlock(); iter = data.find(OTLP_SERVICE_NAME); // if attributes also set service.name, data takes precedence if (iter != data.end()) { attribute = resource->add_attributes(); attribute->set_key(OTLP_SERVICE_NAME); value = attribute->mutable_value(); value->set_string_value(iter->second); } this->expose(metrics); // fprintf(stderr, "[Metrics info to report]\n%s\n", req.DebugString().c_str()); req.SerializeToString(output); this->report_counts = 0; WFHttpTask *task = WFTaskFactory::create_http_task(this->url, this->redirect_max, this->retry_max, [](WFHttpTask *task) { /* protocol::HttpResponse *resp = task->get_resp(); fprintf(stderr, "[metrics report callback] state=%d error=%d\n", task->get_state(), task->get_error()); if (task->get_state() == WFT_STATE_SUCCESS) { fprintf(stderr, "%s %s %s\r\n", resp->get_http_version(), resp->get_status_code(), resp->get_reason_phrase()); } */ delete (std::string *)task->user_data; }); protocol::HttpRequest *http_req = task->get_req(); http_req->set_method(HttpMethodPost); http_req->add_header_pair("Content-Type", "application/x-protobuf"); task->user_data = output; http_req->append_output_body_nocopy(output->c_str(), output->length()); return task; } bool RPCMetricsOTel::expose(google::protobuf::Message *msg) { std::unordered_map tmp; std::unordered_map::iterator it; ScopeMetrics *metrics; metrics = static_cast(msg); this->reduce(tmp); for (it = tmp.begin(); it != tmp.end(); it++) { RPCVar *var = it->second; Metric *m = metrics->add_metrics(); google::protobuf::Message *current_var; m->set_name(var->get_name()); m->set_description(var->get_help()); switch(var->get_type()) { default: case VAR_GAUGE: current_var = m->mutable_gauge(); break; case VAR_COUNTER: current_var = m->mutable_sum(); break; case VAR_HISTOGRAM: current_var = m->mutable_histogram(); break; case VAR_SUMMARY: current_var = m->mutable_summary(); break; } this->collector.set_current_message(current_var); this->collector.set_current_nano(GET_CURRENT_NS()); var->collect(&this->collector); } for (it = tmp.begin(); it != tmp.end(); it++) delete it->second; return true; } void RPCMetricsOTel::Collector::collect_gauge(RPCVar *gauge, double data) { Gauge *report_gauge = static_cast(this->current_msg); NumberDataPoint *data_points = report_gauge->add_data_points(); data_points->set_as_double(data); data_points->set_time_unix_nano(this->current_timestamp_nano); } void RPCMetricsOTel::Collector::add_counter_label(const std::string& label) { const char *key; const char *value; size_t key_len; size_t lpos; size_t rpos; size_t pos = -1; LABEL_MAP *m = new LABEL_MAP(); do { pos++; lpos = label.find_first_of('=', pos); key = label.data() + pos; key_len = lpos - pos; rpos = label.find_first_of(',', lpos + 1); pos = rpos; if (rpos == std::string::npos) rpos = label.length(); value = label.data() + lpos + 2; // value_len = rpos - lpos - 3; m->emplace(std::string{key, key_len}, std::string{value, rpos - lpos - 3}); } while (pos != std::string::npos); this->label_map.emplace(label, m); } void RPCMetricsOTel::Collector::collect_counter_each(RPCVar *counter, const std::string& label, double data) { Sum *report_sum = static_cast(this->current_msg); NumberDataPoint *data_points = report_sum->add_data_points(); std::map::iterator it = this->label_map.find(label); std::string key; std::string value; if (it == this->label_map.end()) { this->add_counter_label(label); it = this->label_map.find(label); } for (const auto& kv : *(it->second)) { KeyValue *attribute = data_points->add_attributes(); attribute->set_key(kv.first); AnyValue *value = attribute->mutable_value(); value->set_string_value(kv.second); } data_points->set_as_double(data); data_points->set_time_unix_nano(this->current_timestamp_nano); } void RPCMetricsOTel::Collector::collect_histogram_begin(RPCVar *histogram) { Histogram *report_histogram = static_cast(this->current_msg); HistogramDataPoint *data_points = report_histogram->add_data_points(); this->current_msg = data_points; data_points->set_time_unix_nano(this->current_timestamp_nano); } void RPCMetricsOTel::Collector::collect_histogram_each(RPCVar *histogram, double bucket_boundary, size_t current_count) { HistogramDataPoint *data_points = static_cast(this->current_msg); data_points->add_bucket_counts(current_count); if (bucket_boundary != DBL_MAX) data_points->add_explicit_bounds(bucket_boundary); } void RPCMetricsOTel::Collector::collect_histogram_end(RPCVar *histogram, double sum, size_t count) { HistogramDataPoint *data_points = static_cast(this->current_msg); data_points->set_sum(sum); data_points->set_count(count); } void RPCMetricsOTel::Collector::collect_summary_begin(RPCVar *summary) { Summary *report_summary = static_cast(this->current_msg); SummaryDataPoint *data_points = report_summary->add_data_points(); this->current_msg = data_points; data_points->set_time_unix_nano(this->current_timestamp_nano); } void RPCMetricsOTel::Collector::collect_summary_each(RPCVar *summary, double quantile, double quantile_out) { SummaryDataPoint *data_points = static_cast(this->current_msg); SummaryDataPoint::ValueAtQuantile *vaq = data_points->add_quantile_values(); vaq->set_quantile(quantile); vaq->set_value(quantile_out); } void RPCMetricsOTel::Collector::collect_summary_end(RPCVar *summary, double sum, size_t count) { SummaryDataPoint *data_points = static_cast(this->current_msg); data_points->set_sum(sum); data_points->set_count(count); } } // end namespace srpc srpc-0.10.1/src/module/rpc_metrics_filter.h000066400000000000000000000151171454502251400206530ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_METRIC_FILTER_H__ #define __RPC_METRIC_FILTER_H__ #include #include #include #include #include #include #include #include #include "workflow/WFTask.h" #include "workflow/WFTaskFactory.h" #include "workflow/WFHttpServer.h" #include "rpc_basic.h" #include "rpc_var.h" #include "rpc_metrics_module.h" namespace srpc { class RPCMetricsFilter : public RPCFilter { public: GaugeVar *create_gauge(const std::string& name, const std::string& help); CounterVar *create_counter(const std::string& name, const std::string& help); HistogramVar *create_histogram(const std::string& name, const std::string& help, const std::vector& bucket); SummaryVar *create_summary(const std::string& name, const std::string& help, const std::vector& quantile); SummaryVar *create_summary(const std::string& name, const std::string& help, const std::vector& quantile, const std::chrono::milliseconds max_age, int age_bucket); // thread local api GaugeVar *gauge(const std::string& name); CounterVar *counter(const std::string& name); HistogramVar *histogram(const std::string& name); SummaryVar *summary(const std::string& name); // filter api bool client_end(SubTask *task, RPCModuleData& data) override; bool server_end(SubTask *task, RPCModuleData& data) override; public: RPCMetricsFilter(); protected: void reduce(std::unordered_map& out); protected: std::mutex mutex; std::set var_names; }; class RPCMetricsPull : public RPCMetricsFilter { public: bool init(unsigned short port); // pull port for Prometheus void deinit(); public: RPCMetricsPull(); private: SubTask *create(RPCModuleData& data) override { return WFTaskFactory::create_empty_task(); } bool filter(RPCModuleData& data) override { return false; // will not collect by policy.collect(data); } private: class Collector : public RPCVarCollector { public: Collector(std::string& output) : report_output(output) { } void collect_gauge(RPCVar *gauge, double data) override; void collect_counter_each(RPCVar *counter, const std::string& label, double data) override; void collect_histogram_begin(RPCVar *histogram) override { } void collect_histogram_each(RPCVar *histogram, double bucket_boudary, size_t current_count) override; void collect_histogram_end(RPCVar *histogram, double sum, size_t count) override; void collect_summary_begin(RPCVar *summary) override { } void collect_summary_each(RPCVar *summary, double quantile, double quantile_out) override; void collect_summary_end(RPCVar *summary, double sum, size_t count) override; private: std::string& report_output; }; private: bool expose(); void pull(WFHttpTask *task); std::string report_output; Collector collector; WFHttpServer server; }; class RPCFilterPolicy { public: RPCFilterPolicy(size_t report_threshold, size_t report_interval_msec) : report_threshold(report_threshold), report_interval(report_interval_msec), last_report_timestamp(0) { } void set_report_threshold(size_t threshold) { if (threshold <= 0) threshold = 1; this->report_threshold = threshold; } void set_report_interval(int msec) { if (msec <= 0) msec = 1; this->report_interval = msec; } bool report(size_t count); private: size_t report_threshold; // metrics to report at most size_t report_interval; long long last_report_timestamp; }; class RPCMetricsOTel : public RPCMetricsFilter { public: void set_report_threshold(size_t threshold) { this->policy.set_report_threshold(threshold); } void set_report_interval(int msec) { this->policy.set_report_interval(msec); } // for client level attributes, such as ProviderID void add_attributes(const std::string& key, const std::string& value); size_t clear_attributes(); // refer to 'ScopeMetrics' : a collection of Metrics in a Scope void set_scope_name(const std::string& name) { this->scope_name = name; } public: RPCMetricsOTel(const std::string& url); RPCMetricsOTel(const std::string& url, unsigned int redirect_max, unsigned int retry_max, size_t report_threshold, size_t report_interval); private: SubTask *create(RPCModuleData& data) override; bool filter(RPCModuleData& data) override { return this->policy.report(++this->report_counts); } private: class Collector : public RPCVarCollector { public: Collector() { } virtual ~Collector(); void set_current_message(google::protobuf::Message *var_msg) { this->current_msg = var_msg; } void set_current_nano(unsigned long long ns) { this->current_timestamp_nano = ns; } void collect_gauge(RPCVar *gauge, double data) override; void collect_counter_each(RPCVar *counter, const std::string& label, double data) override; void collect_histogram_begin(RPCVar *histogram) override; void collect_histogram_each(RPCVar *histogram, double bucket_boudary, size_t current_count) override; void collect_histogram_end(RPCVar *histogram, double sum, size_t count) override; void collect_summary_begin(RPCVar *summary) override; void collect_summary_each(RPCVar *summary, double quantile, double quantile_out) override; void collect_summary_end(RPCVar *summary, double sum, size_t count) override; private: void add_counter_label(const std::string& label); private: using LABEL_MAP = std::map; google::protobuf::Message *current_msg; unsigned long long current_timestamp_nano; std::map label_map; }; private: bool expose(google::protobuf::Message *metrics); private: std::string url; int redirect_max; int retry_max; Collector collector; RPCFilterPolicy policy; std::atomic report_counts; std::map attributes; std::string scope_name; }; } // end namespace srpc #endif srpc-0.10.1/src/module/rpc_metrics_module.cc000066400000000000000000000036361454502251400210140ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "rpc_metrics_module.h" namespace srpc { bool MetricsModule::client_begin(SubTask *task, RPCModuleData& data) { data[SRPC_START_TIMESTAMP] = std::to_string(GET_CURRENT_NS()); // clear other unnecessary module_data since the data comes from series data.erase(SRPC_DURATION); data.erase(SRPC_FINISH_TIMESTAMP); return true; } bool MetricsModule::client_end(SubTask *task, RPCModuleData& data) { if (data.find(SRPC_START_TIMESTAMP) != data.end() && data.find(SRPC_DURATION) == data.end()) { unsigned long long end_time = GET_CURRENT_NS(); data[SRPC_FINISH_TIMESTAMP] = std::to_string(end_time); data[SRPC_DURATION] = std::to_string(end_time - atoll(data[SRPC_START_TIMESTAMP].data())); } return true; } bool MetricsModule::server_begin(SubTask *task, RPCModuleData& data) { if (data.find(SRPC_START_TIMESTAMP) == data.end()) data[SRPC_START_TIMESTAMP] = std::to_string(GET_CURRENT_NS()); return true; } bool MetricsModule::server_end(SubTask *task, RPCModuleData& data) { if (data.find(SRPC_START_TIMESTAMP) != data.end() && data.find(SRPC_DURATION) == data.end()) { unsigned long long end_time = GET_CURRENT_NS(); data[SRPC_FINISH_TIMESTAMP] = std::to_string(end_time); data[SRPC_DURATION] = std::to_string(end_time - atoll(data[SRPC_START_TIMESTAMP].data())); } return true; } } // end namespace srpc srpc-0.10.1/src/module/rpc_metrics_module.h000066400000000000000000000044501454502251400206510ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_METRICS_MODULE_H__ #define __RPC_METRICS_MODULE_H__ #include "rpc_basic.h" #include "rpc_context.h" #include "rpc_module.h" namespace srpc { // Basic MetricsModlue for generating general metrics data. // Each kind of network task can derived its own MetricsModlue. class MetricsModule : public RPCModule { public: bool client_begin(SubTask *task, RPCModuleData& data) override; bool client_end(SubTask *task, RPCModuleData& data) override; bool server_begin(SubTask *task, RPCModuleData& data) override; bool server_end(SubTask *task, RPCModuleData& data) override; public: MetricsModule() : RPCModule(RPCModuleTypeMetrics) { } }; // Fill RPC related data in metrics module template class RPCMetricsModule : public MetricsModule { public: bool client_begin(SubTask *task, RPCModuleData& data) override; bool server_begin(SubTask *task, RPCModuleData& data) override; }; ////////// impl template bool RPCMetricsModule::client_begin(SubTask *task, RPCModuleData& data) { MetricsModule::client_begin(task, data); auto *client_task = static_cast(task); auto *req = client_task->get_req(); data[OTLP_SERVICE_NAME] = req->get_service_name(); data[OTLP_METHOD_NAME] = req->get_method_name(); return true; } template bool RPCMetricsModule::server_begin(SubTask *task, RPCModuleData& data) { MetricsModule::server_begin(task, data); auto *server_task = static_cast(task); auto *req = server_task->get_req(); data[OTLP_SERVICE_NAME] = req->get_service_name(); data[OTLP_METHOD_NAME] = req->get_method_name(); return true; } } // end namespace srpc #endif srpc-0.10.1/src/module/rpc_module.cc000066400000000000000000000112231454502251400172550ustar00rootroot00000000000000/* Copyright (c) 2021 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include "workflow/WFTask.h" #include #include "rpc_basic.h" #include "rpc_module.h" #include "rpc_trace_module.h" namespace srpc { bool RPCModule::client_task_begin(SubTask *task, RPCModuleData& data) { bool ret = this->client_begin(task, data); for (RPCFilter *filter : this->filters) ret = ret && filter->client_begin(task, data); return ret; } bool RPCModule::server_task_begin(SubTask *task, RPCModuleData& data) { bool ret = this->server_begin(task, data); for (RPCFilter *filter : this->filters) ret = ret && filter->server_begin(task, data); return ret; } bool RPCModule::client_task_end(SubTask *task, RPCModuleData& data) { SubTask *filter_task; bool ret = this->client_end(task, data); for (RPCFilter *filter : this->filters) { ret = ret && filter->client_end(task, data); filter_task = filter->create_filter_task(data); series_of(task)->push_front(filter_task); } return ret; } bool RPCModule::server_task_end(SubTask *task, RPCModuleData& data) { SubTask *filter_task; bool ret = this->server_end(task, data); for (RPCFilter *filter : this->filters) { ret = ret && filter->server_end(task, data); filter_task = filter->create_filter_task(data); series_of(task)->push_front(filter_task); } return ret; } SnowFlake::SnowFlake(int timestamp_bits, int group_bits, int machine_bits) { this->timestamp_bits = timestamp_bits; this->group_bits = group_bits; this->machine_bits = machine_bits; this->sequence_bits = SRPC_TOTAL_BITS - timestamp_bits - group_bits - machine_bits; this->last_timestamp = 1L; this->sequence = 0L; this->group_id_max = 1 << this->group_bits; this->machine_id_max = 1 << this->machine_bits; this->sequence_max = 1 << this->sequence_bits; this->machine_shift = this->sequence_bits; this->group_shift = this->machine_shift + this->machine_bits; this->timestamp_shift = this->group_shift + this->group_bits; } bool SnowFlake::get_id(long long group_id, long long machine_id, unsigned long long *uid) { if (group_id > this->group_id_max || machine_id > this->machine_id_max) return false; long long timestamp = GET_CURRENT_MS_STEADY(); long long seq_id; if (timestamp < this->last_timestamp) return false; if (timestamp == this->last_timestamp) { if (++this->sequence > this->sequence_max) return false; // too many sequence_id in one millie second } else this->sequence = 0L; seq_id = this->sequence++; this->last_timestamp = timestamp; *uid = (timestamp << this->timestamp_shift) | (group_id << this->group_shift) | (machine_id << this->machine_shift) | seq_id; return true; } bool http_set_header_module_data(const RPCModuleData& data, protocol::HttpMessage *msg) { auto it = data.begin(); int flag = 0; while (it != data.end() && flag != 3) { if (it->first == SRPC_TRACE_ID) { char trace_id_buf[SRPC_TRACEID_SIZE * 2 + 1]; TRACE_ID_BIN_TO_HEX((uint64_t *)it->second.data(), trace_id_buf); msg->set_header_pair("Trace-Id", trace_id_buf); flag |= 1; } else if (it->first == SRPC_SPAN_ID) { char span_id_buf[SRPC_SPANID_SIZE * 2 + 1]; SPAN_ID_BIN_TO_HEX((uint64_t *)it->second.data(), span_id_buf); msg->set_header_pair("Span-Id", span_id_buf); flag |= (1 << 1); } ++it; } return true; } bool http_get_header_module_data(const protocol::HttpMessage *msg, RPCModuleData& data) { protocol::HttpHeaderCursor cursor(msg); std::string name; std::string value; int flag = 0; while (cursor.next(name, value) && flag != 3) { if (strcasecmp(name.c_str(), "Trace-Id") == 0 && value.size() == SRPC_TRACEID_SIZE * 2) { uint64_t trace_id[2]; TRACE_ID_HEX_TO_BIN(value.data(), trace_id); data[SRPC_TRACE_ID] = std::string((char *)trace_id, SRPC_TRACEID_SIZE); flag |= 1; } else if (strcasecmp(name.c_str(), "Span-Id") == 0 && value.size() == SRPC_SPANID_SIZE * 2) { uint64_t span_id[1]; SPAN_ID_HEX_TO_BIN(value.data(), span_id); data[SRPC_SPAN_ID] = std::string((char *)span_id, SRPC_SPANID_SIZE); flag |= (1 << 1); } } return true; } } // end namespace srpc srpc-0.10.1/src/module/rpc_module.h000066400000000000000000000071111454502251400171200ustar00rootroot00000000000000/* Copyright (c) 2021 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_MODULE_H__ #define __RPC_MODULE_H__ #include #include #include #include "workflow/WFTask.h" #include "rpc_basic.h" #include "rpc_filter.h" namespace srpc { static constexpr char const *SRPC_COMPONENT_SRPC = "srpc"; static constexpr char const *SRPC_SPAN_LOG = "srpc.log"; static constexpr char const *SRPC_SPAN_EVENT = "event"; static constexpr char const *SRPC_SPAN_MESSAGE = "message"; static constexpr char const *SRPC_START_TIMESTAMP = "srpc.start_time"; static constexpr char const *SRPC_FINISH_TIMESTAMP = "srpc.finish_time"; static constexpr char const *SRPC_DURATION = "srpc.duration"; static constexpr char const *SRPC_TIMEOUT_REASON = "srpc.timeout_reason"; static constexpr char const *SRPC_SPAN_ID = "srpc.span_id"; static constexpr char const *SRPC_TRACE_ID = "srpc.trace_id"; static constexpr char const *SRPC_PARENT_SPAN_ID = "srpc.parent_span_id"; //for SnowFlake: u_id = [timestamp][group][machine][sequence] static constexpr int SRPC_TIMESTAMP_BITS = 38; static constexpr int SRPC_GROUP_BITS = 4; static constexpr int SRPC_MACHINE_BITS = 10; //static constexpr int SRPC_SEQUENCE_BITS = 12; static constexpr int SRPC_TOTAL_BITS = 64; class RPCModule { protected: virtual bool client_begin(SubTask *task, RPCModuleData& data) = 0; virtual bool server_begin(SubTask *task, RPCModuleData& data) = 0; virtual bool client_end(SubTask *task, RPCModuleData& data) = 0; virtual bool server_end(SubTask *task, RPCModuleData& data) = 0; public: void add_filter(RPCFilter *filter) { this->mutex.lock(); this->filters.push_back(filter); this->mutex.unlock(); } size_t get_filters_size() const { return this->filters.size(); } enum RPCModuleType get_module_type() const { return this->module_type; } bool client_task_begin(SubTask *task, RPCModuleData& data); bool server_task_begin(SubTask *task, RPCModuleData& data); bool client_task_end(SubTask *task, RPCModuleData& data); bool server_task_end(SubTask *task, RPCModuleData& data); RPCModule(enum RPCModuleType module_type) { this->module_type = module_type; } virtual ~RPCModule() {} private: enum RPCModuleType module_type; std::mutex mutex; std::list filters; }; class SnowFlake { public: SnowFlake(int timestamp_bits, int group_bits, int machine_bits); SnowFlake() : SnowFlake(SRPC_TIMESTAMP_BITS, SRPC_GROUP_BITS, SRPC_MACHINE_BITS) { } bool get_id(long long group_id, long long machine_id, unsigned long long *uid); private: std::atomic last_timestamp; // -1L; std::atomic sequence; // 0L; int timestamp_bits; int group_bits; int machine_bits; int sequence_bits; long long group_id_max; long long machine_id_max; long long sequence_max; long long timestamp_shift; long long group_shift; long long machine_shift; }; bool http_get_header_module_data(const protocol::HttpMessage *msg, RPCModuleData& data); bool http_set_header_module_data(const RPCModuleData& data, protocol::HttpMessage *msg); } // end namespace srpc #endif srpc-0.10.1/src/module/rpc_trace_filter.cc000066400000000000000000000331341454502251400204400ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include "workflow/WFTask.h" #include "workflow/HttpUtil.h" #include "rpc_trace_filter.h" #include "opentelemetry_trace.pb.h" namespace srpc { using namespace opentelemetry::proto::trace::v1; using namespace opentelemetry::proto::common::v1; using namespace opentelemetry::proto::resource::v1; static constexpr char const *SRPC_COMPONENT_OTEL_STR = "rpc.system"; static InstrumentationLibrarySpans * rpc_span_fill_pb_request(const RPCModuleData& data, const std::unordered_map& attributes, TracesData *req) { ResourceSpans *rs = req->add_resource_spans(); InstrumentationLibrarySpans *spans = rs->add_instrumentation_library_spans(); Resource *resource = rs->mutable_resource(); KeyValue *attribute; AnyValue *value; auto iter = data.find(OTLP_METHOD_NAME); if (iter != data.end()) { attribute = resource->add_attributes(); attribute->set_key(OTLP_METHOD_NAME); value = attribute->mutable_value(); value->set_string_value(iter->second); } for (const auto& attr : attributes) { KeyValue *attribute = resource->add_attributes(); attribute->set_key(attr.first); AnyValue *value = attribute->mutable_value(); value->set_string_value(attr.second); } // if attributes also set service.name, data takes precedence iter = data.find(OTLP_SERVICE_NAME); if (iter != data.end()) { attribute = resource->add_attributes(); attribute->set_key(OTLP_SERVICE_NAME); value = attribute->mutable_value(); value->set_string_value(iter->second); } return spans; } static void rpc_span_fill_pb_span(RPCModuleData& data, const std::unordered_map& attributes, InstrumentationLibrarySpans *spans) { Span *span = spans->add_spans(); Status *status = span->mutable_status(); KeyValue *attribute; AnyValue *attr_value; for (const auto& attr : attributes) { attribute = span->add_attributes(); attribute->set_key(attr.first); attr_value = attribute->mutable_value(); attr_value->set_string_value(attr.second); } span->set_span_id(data[SRPC_SPAN_ID].c_str(), SRPC_SPANID_SIZE); span->set_trace_id(data[SRPC_TRACE_ID].c_str(), SRPC_TRACEID_SIZE); // name is required and specified in OpenTelemetry semantic conventions. if (data.find(OTLP_METHOD_NAME) != data.end()) { span->set_name(data[OTLP_METHOD_NAME]); // for RPC attribute= span->add_attributes(); attribute->set_key(SRPC_COMPONENT_OTEL_STR); // srpc.component -> rpc.system attr_value = attribute->mutable_value(); attr_value->set_string_value(data[SRPC_COMPONENT]); } else span->set_name(data[SRPC_HTTP_METHOD]); // for HTTP // refer to : trace/semantic_conventions/http/#status int http_status_code = 0; auto iter = data.find(SRPC_HTTP_STATUS_CODE); if (iter != data.end()) http_status_code = atoi(iter->second.c_str()); for (const auto& kv : data) { const std::string& key = kv.first; const std::string& val = kv.second; if (key.compare(SRPC_PARENT_SPAN_ID) == 0) { span->set_parent_span_id(val); } else if (key.compare(SRPC_SPAN_KIND) == 0) { if (val.compare(SRPC_SPAN_KIND_CLIENT) == 0) { span->set_kind(Span_SpanKind_SPAN_KIND_CLIENT); if (http_status_code >= 400) status->set_code(Status_StatusCode_STATUS_CODE_ERROR); } else if (val.compare(SRPC_SPAN_KIND_SERVER) == 0) { span->set_kind(Span_SpanKind_SPAN_KIND_SERVER); if (http_status_code >= 500) status->set_code(Status_StatusCode_STATUS_CODE_ERROR); } } else if (key.compare(SRPC_START_TIMESTAMP) == 0) { span->set_start_time_unix_nano(atoll(data[SRPC_START_TIMESTAMP].data())); } else if (key.compare(SRPC_FINISH_TIMESTAMP) == 0) { span->set_end_time_unix_nano(atoll(data[SRPC_FINISH_TIMESTAMP].data())); } else if (key.compare(SRPC_STATE) == 0) { int state = atoi(val.c_str()); if (state == RPCStatusOK) status->set_code(Status_StatusCode_STATUS_CODE_OK); else status->set_code(Status_StatusCode_STATUS_CODE_ERROR); } else if (key.compare(WF_TASK_STATE) == 0) { int state = atoi(val.c_str()); if (state == WFT_STATE_SUCCESS) status->set_code(Status_StatusCode_STATUS_CODE_OK); else status->set_code(Status_StatusCode_STATUS_CODE_ERROR); } else if (key.compare(0, 5, "srpc.") != 0) { attribute= span->add_attributes(); attribute->set_key(key); attr_value = attribute->mutable_value(); size_t len = key.length(); if ((len > 4 && key.substr(len - 4).compare("port") == 0) || (len > 5 && key.substr(len - 5).compare("count") == 0) || (len > 6 && key.substr(len - 6).compare("length") == 0) || key.compare(SRPC_HTTP_STATUS_CODE)== 0) { attr_value->set_int_value(atoi(val.c_str())); } else { attr_value->set_string_value(val); } } } } static size_t rpc_span_log_format(RPCModuleData& data, char *str, size_t len) { const uint64_t *trace_id = (const uint64_t *)data[SRPC_TRACE_ID].c_str(); const uint64_t *span_id = (const uint64_t *)data[SRPC_SPAN_ID].c_str(); char trace_id_buf[SRPC_TRACEID_SIZE * 2 + 1]; char span_id_buf[SRPC_SPANID_SIZE * 2 + 1]; TRACE_ID_BIN_TO_HEX(trace_id, trace_id_buf); SPAN_ID_BIN_TO_HEX(span_id, span_id_buf); size_t ret = snprintf(str, len, "trace_id: %s span_id: %s", trace_id_buf, span_id_buf); for (const auto& iter : data) { if (strcmp(iter.first.c_str(), SRPC_TRACE_ID) == 0 || strcmp(iter.first.c_str(), SRPC_SPAN_ID) == 0 || strcmp(iter.first.c_str(), SRPC_FINISH_TIMESTAMP) == 0 || strcmp(iter.first.c_str(), SRPC_DURATION) == 0) { continue; } if (strcmp(iter.first.c_str(), SRPC_PARENT_SPAN_ID) == 0) { char parent_span_id_buf[SRPC_SPANID_SIZE * 2 + 1]; span_id = (const uint64_t *)iter.second.c_str(); SPAN_ID_BIN_TO_HEX(span_id, parent_span_id_buf); ret += snprintf(str + ret, len - ret, " parent_span_id: %s", parent_span_id_buf); } else if (strcmp(iter.first.c_str(), SRPC_START_TIMESTAMP) == 0) { ret += snprintf(str + ret, len - ret, " start_time: %s finish_time: %s duration: %s(ns)", data[SRPC_START_TIMESTAMP].c_str(), data[SRPC_FINISH_TIMESTAMP].c_str(), data[SRPC_DURATION].c_str()); } else if (strcmp(iter.first.c_str(), SRPC_STATE) == 0 || strcmp(iter.first.c_str(), SRPC_ERROR) == 0) { ret += snprintf(str + ret, len - ret, " %s: %s", iter.first.c_str(), iter.second.c_str()); } /* else if (strcmp(it.first.c_str(), SRPC_SPAN_LOG) == 0) { ret += snprintf(str + ret, len - ret, "\n%s trace_id: %s span_id: %s" " timestamp: %s %s", "[ANNOTATION]", trace_id_buf, span_id_buf, it.first.c_str() + strlen(SRPC_SPAN_LOG) + 1, it.second.c_str()); } */ else { const char *key = iter.first.c_str(); if (iter.first.compare(0, 5, "srpc.") == 0) key += 5; ret += snprintf(str + ret, len - ret, " %s: %s", key, iter.second.c_str()); } } return ret; } bool RPCTraceFilterPolicy::collect(RPCModuleData& span) { if (span.find(SRPC_TRACE_ID) == span.end()) return false; long long timestamp = GET_CURRENT_MS(); if (timestamp < this->last_collect_timestamp + this->stat_interval && this->spans_interval_count < this->spans_per_interval && this->spans_second_count < this->spans_per_sec) { this->spans_interval_count++; this->spans_second_count++; return true; } else if (timestamp >= this->last_collect_timestamp + this->stat_interval && this->spans_per_sec) { this->spans_interval_count = 0; if (timestamp / 1000 > this->last_collect_timestamp / 1000) // next second this->spans_second_count = 0; this->last_collect_timestamp = timestamp; if (this->spans_second_count < this->spans_per_sec) { this->spans_second_count++; this->spans_interval_count++; return true; } } return false; } bool RPCTraceFilterPolicy::report(size_t count) { long long timestamp = GET_CURRENT_MS(); if (this->last_report_timestamp == 0) this->last_report_timestamp = timestamp; if (timestamp > this->last_report_timestamp + (long long)this->report_interval || count >= this->report_threshold) { this->last_report_timestamp = timestamp; return true; } return false; } void RPCTraceLogTask::dispatch() { char str[SPAN_LOG_MAX_LENGTH]; rpc_span_log_format(this->span, str, SPAN_LOG_MAX_LENGTH); fprintf(stderr, "[SPAN_LOG] %s\n", str); this->subtask_done(); } SubTask *RPCTraceRedis::create(RPCModuleData& span) { auto iter = span.find(SRPC_TRACE_ID); if (iter == span.end()) return WFTaskFactory::create_empty_task(); auto *task = WFTaskFactory::create_redis_task(this->redis_url, this->retry_max, nullptr); protocol::RedisRequest *req = task->get_req(); char value[SPAN_LOG_MAX_LENGTH]; value[0] = '0'; rpc_span_log_format(span, value, SPAN_LOG_MAX_LENGTH); req->set_request("SET", { span[SRPC_TRACE_ID], value} ); return task; } RPCTraceOpenTelemetry::RPCTraceOpenTelemetry(const std::string& url) : RPCFilter(RPCModuleTypeTrace), url(url + OTLP_TRACES_PATH), redirect_max(OTLP_HTTP_REDIRECT_MAX), retry_max(OTLP_HTTP_RETRY_MAX), filter_policy(SPANS_PER_SECOND_DEFAULT, RPC_REPORT_THREHOLD_DEFAULT, RPC_REPORT_INTERVAL_DEFAULT), report_status(false), report_span_count(0) { this->report_req = new TracesData; } RPCTraceOpenTelemetry::RPCTraceOpenTelemetry(const std::string& url, int redirect_max, int retry_max, size_t spans_per_second, size_t report_threshold, size_t report_interval) : RPCFilter(RPCModuleTypeTrace), url(url + OTLP_TRACES_PATH), redirect_max(redirect_max), retry_max(retry_max), filter_policy(spans_per_second, report_threshold, report_interval), report_status(false), report_span_count(0) { this->report_req = new TracesData; } RPCTraceOpenTelemetry::~RPCTraceOpenTelemetry() { delete this->report_req; } SubTask *RPCTraceOpenTelemetry::create(RPCModuleData& span) { std::string *output = new std::string; SubTask *next = NULL; TracesData *req = (TracesData *)this->report_req; this->mutex.lock(); if (!this->report_status) next = WFTaskFactory::create_empty_task(); else { // fprintf(stderr, "[Trace info to report]\n%s\n\n", req->DebugString().c_str()); req->SerializeToString(output); this->report_status = false; this->report_span_count = 0; req->clear_resource_spans(); this->report_map.clear(); } this->mutex.unlock(); if (next) return next; WFHttpTask *task = WFTaskFactory::create_http_task(this->url, this->redirect_max, this->retry_max, [](WFHttpTask *task) { /* protocol::HttpResponse *resp = task->get_resp(); fprintf(stderr, "[Trace report callback] state=%d error=%d\n", task->get_state(), task->get_error()); if (task->get_state() == WFT_STATE_SUCCESS) { fprintf(stderr, "%s %s %s\r\n", resp->get_http_version(), resp->get_status_code(), resp->get_reason_phrase()); } */ delete (std::string *)task->user_data; }); protocol::HttpRequest *http_req = task->get_req(); http_req->set_method(HttpMethodPost); http_req->add_header_pair("Content-Type", "application/x-protobuf"); task->user_data = output; http_req->append_output_body_nocopy(output->c_str(), output->length()); return task; } void RPCTraceOpenTelemetry::add_attributes(const std::string& key, const std::string& value) { this->mutex.lock(); this->attributes.insert(std::make_pair(key, value)); this->mutex.unlock(); } void RPCTraceOpenTelemetry::add_span_attributes(const std::string& key, const std::string& value) { this->mutex.lock(); this->span_attributes.insert(std::make_pair(key, value)); this->mutex.unlock(); } size_t RPCTraceOpenTelemetry::clear_attributes() { size_t ret; this->mutex.lock(); ret = this->attributes.size(); this->attributes.clear(); this->mutex.unlock(); return ret; } size_t RPCTraceOpenTelemetry::clear_span_attributes() { size_t ret; this->mutex.lock(); ret = this->span_attributes.size(); this->span_attributes.clear(); this->mutex.unlock(); return ret; } bool RPCTraceOpenTelemetry::filter(RPCModuleData& data) { std::unordered_map::iterator it; InstrumentationLibrarySpans *spans; std::string service_name; bool ret; auto iter = data.find(OTLP_SERVICE_NAME); if (iter != data.end()) { service_name = iter->second; } else // for HTTP { service_name = data[SRPC_COMPONENT] + std::string(".") + data[SRPC_HTTP_SCHEME]; if (data.find(SRPC_SPAN_KIND_CLIENT) != data.end()) service_name += ".client"; else service_name += ".server"; } this->mutex.lock(); if (this->filter_policy.collect(data)) { ++this->report_span_count; it = this->report_map.find(service_name); if (it == this->report_map.end()) { spans = rpc_span_fill_pb_request(data, this->attributes, (TracesData *)this->report_req); this->report_map.insert({service_name, spans}); } else spans = (InstrumentationLibrarySpans *)it->second; rpc_span_fill_pb_span(data, this->span_attributes, spans); } ret = this->filter_policy.report(this->report_span_count); if (ret) this->report_status = true; this->mutex.unlock(); return ret; } } // end namespace srpc srpc-0.10.1/src/module/rpc_trace_filter.h000066400000000000000000000151661454502251400203070ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_TRACE_FILTER_H__ #define __RPC_TRACE_FILTER_H__ #include #include #include #include #include #include #include #include #include "workflow/WFTask.h" #include "workflow/WFTaskFactory.h" #include "workflow/RedisMessage.h" #include "rpc_basic.h" #include "rpc_trace_module.h" namespace srpc { static constexpr unsigned int SPAN_LIMIT_DEFAULT = 1; static constexpr size_t SPAN_LOG_MAX_LENGTH = 1024; static constexpr size_t UINT64_STRING_LENGTH = 20; static constexpr unsigned int SPAN_REDIS_RETRY_MAX = 0; static constexpr const char *SPAN_BATCH_LOG_NAME_DEFAULT = "./trace_info.log"; static constexpr size_t SPAN_BATCH_LOG_SIZE_DEFAULT = 4 * 1024 * 1024; static constexpr unsigned int SPANS_PER_SECOND_DEFAULT = 1000; static constexpr const char *OTLP_TRACES_PATH = "/v1/traces"; class RPCTraceFilterPolicy { public: RPCTraceFilterPolicy(size_t spans_per_second, size_t report_threshold, size_t report_interval_msec) : stat_interval(1), // default 1 msec spans_per_sec(spans_per_second), last_collect_timestamp(0), spans_second_count(0), spans_interval_count(0), report_threshold(report_threshold), report_interval(report_interval_msec), last_report_timestamp(0) { this->spans_per_interval = (this->spans_per_sec + 999) / 1000; } void set_spans_per_sec(size_t n) { this->spans_per_sec = n; this->spans_per_interval = (n * this->stat_interval + 999 ) / 1000; } void set_stat_interval(int msec) { if (msec <= 0) msec = 1; this->stat_interval = msec; this->spans_per_interval = (this->spans_per_sec * msec + 999) / 1000; } void set_report_threshold(size_t threshold) { if (threshold <= 0) threshold = 1; this->report_threshold = threshold; } void set_report_interval(int msec) { if (msec <= 0) msec = 1; this->report_interval = msec; } bool collect(RPCModuleData& span); bool report(size_t count); private: int stat_interval; size_t spans_per_sec; size_t spans_per_interval; long long last_collect_timestamp; std::atomic spans_second_count; std::atomic spans_interval_count; size_t report_threshold; // spans to report at most size_t report_interval; long long last_report_timestamp; }; class RPCTraceLogTask : public WFGenericTask { public: RPCTraceLogTask(RPCModuleData& span, std::function callback) : span(span), callback(std::move(callback)) {} private: virtual void dispatch(); virtual SubTask *done() { SeriesWork *series = series_of(this); if (this->callback) this->callback(this); delete this; return series->pop(); } public: RPCModuleData span; std::function callback; }; class RPCTraceDefault : public RPCFilter { public: RPCTraceDefault() : RPCFilter(RPCModuleTypeTrace), filter_policy(SPANS_PER_SECOND_DEFAULT, RPC_REPORT_THREHOLD_DEFAULT, RPC_REPORT_INTERVAL_DEFAULT) {} RPCTraceDefault(size_t spans_per_second) : RPCFilter(RPCModuleTypeTrace), filter_policy(spans_per_second, RPC_REPORT_THREHOLD_DEFAULT, RPC_REPORT_INTERVAL_DEFAULT) {} private: SubTask *create(RPCModuleData& span) override { return new RPCTraceLogTask(span, nullptr); } bool filter(RPCModuleData& span) override { return this->filter_policy.collect(span); } public: void set_spans_per_sec(size_t n) { this->filter_policy.set_spans_per_sec(n); } void set_stat_interval(int msec) { this->filter_policy.set_stat_interval(msec); } private: RPCTraceFilterPolicy filter_policy; }; class RPCTraceRedis : public RPCFilter { public: RPCTraceRedis(const std::string& url) : RPCFilter(RPCModuleTypeTrace), retry_max(SPAN_REDIS_RETRY_MAX), filter_policy(SPANS_PER_SECOND_DEFAULT, RPC_REPORT_THREHOLD_DEFAULT, RPC_REPORT_INTERVAL_DEFAULT) {} RPCTraceRedis(const std::string& url, int retry_max, size_t spans_per_second) : RPCFilter(RPCModuleTypeTrace), redis_url(url), retry_max(retry_max), filter_policy(spans_per_second, RPC_REPORT_THREHOLD_DEFAULT, RPC_REPORT_INTERVAL_DEFAULT) {} private: std::string redis_url; int retry_max; private: SubTask *create(RPCModuleData& span) override; bool filter(RPCModuleData& span) override { return this->filter_policy.collect(span); } public: void set_spans_per_sec(size_t n) { this->filter_policy.set_spans_per_sec(n); } void set_stat_interval(int msec) { this->filter_policy.set_stat_interval(msec); } private: RPCTraceFilterPolicy filter_policy; }; class RPCTraceOpenTelemetry : public RPCFilter { public: void set_spans_per_sec(size_t n) { this->filter_policy.set_spans_per_sec(n); } void set_stat_interval(int msec) { this->filter_policy.set_stat_interval(msec); } void set_report_threshold(size_t threshold) { this->filter_policy.set_report_threshold(threshold); } void set_report_interval(int msec) { this->filter_policy.set_report_interval(msec); } // attributes for client level, such as ProviderID void add_attributes(const std::string& key, const std::string& value); size_t clear_attributes(); // attributes for each span void add_span_attributes(const std::string& key, const std::string& value); size_t clear_span_attributes(); private: std::string url; int redirect_max; int retry_max; RPCTraceFilterPolicy filter_policy; google::protobuf::Message *report_req; std::unordered_map report_map; bool report_status; size_t report_span_count; protected: std::mutex mutex; std::unordered_map attributes; std::unordered_map span_attributes; private: SubTask *create(RPCModuleData& span) override; bool filter(RPCModuleData& span) override; public: RPCTraceOpenTelemetry(const std::string& url); RPCTraceOpenTelemetry(const std::string& url, int redirect_max, int retry_max, size_t spans_per_second, size_t report_threshold, size_t report_interval); virtual ~RPCTraceOpenTelemetry(); }; } // end namespace srpc #endif srpc-0.10.1/src/module/rpc_trace_module.cc000066400000000000000000000113731454502251400204410ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "rpc_trace_module.h" #include "workflow/HttpUtil.h" namespace srpc { static void generate_common_trace(SubTask *task, RPCModuleData& data) { // Don't set start_timestamp here, which may mislead other modules in end() if (data.find(SRPC_TRACE_ID) == data.end()) { uint64_t trace_id_high = SRPCGlobal::get_instance()->get_random(); uint64_t trace_id_low = SRPCGlobal::get_instance()->get_random(); std::string trace_id_buf(SRPC_TRACEID_SIZE + 1, 0); memcpy((char *)trace_id_buf.c_str(), &trace_id_high, SRPC_TRACEID_SIZE / 2); memcpy((char *)trace_id_buf.c_str() + SRPC_TRACEID_SIZE / 2, &trace_id_low, SRPC_TRACEID_SIZE / 2); data[SRPC_TRACE_ID] = std::move(trace_id_buf); } else data[SRPC_PARENT_SPAN_ID] = data[SRPC_SPAN_ID]; uint64_t span_id = SRPCGlobal::get_instance()->get_random(); std::string span_id_buf(SRPC_SPANID_SIZE + 1, 0); memcpy((char *)span_id_buf.c_str(), &span_id, SRPC_SPANID_SIZE); data[SRPC_SPAN_ID] = std::move(span_id_buf); } bool TraceModule::client_begin(SubTask *task, RPCModuleData& data) { generate_common_trace(task, data); data[SRPC_START_TIMESTAMP] = std::to_string(GET_CURRENT_NS()); data[SRPC_SPAN_KIND] = SRPC_SPAN_KIND_CLIENT; // clear other unnecessary module_data since the data comes from series data.erase(SRPC_DURATION); data.erase(SRPC_FINISH_TIMESTAMP); return true; } bool TraceModule::client_end(SubTask *task, RPCModuleData& data) { // 1. failed to call any client_begin() previously if (data.find(SRPC_START_TIMESTAMP) == data.end()) { generate_common_trace(task, data); } // 2. other modules has not set duration yet else if (data.find(SRPC_DURATION) == data.end()) { unsigned long long end_time = GET_CURRENT_NS(); data[SRPC_FINISH_TIMESTAMP] = std::to_string(end_time); data[SRPC_DURATION] = std::to_string(end_time - atoll(data[SRPC_START_TIMESTAMP].data())); } return true; } bool TraceModule::server_begin(SubTask *task, RPCModuleData& data) { generate_common_trace(task, data); data[SRPC_START_TIMESTAMP] = std::to_string(GET_CURRENT_NS()); data[SRPC_SPAN_KIND] = SRPC_SPAN_KIND_SERVER; return true; } bool TraceModule::server_end(SubTask *task, RPCModuleData& data) { // 1. failed to call any server_begin() previously if (data.find(SRPC_START_TIMESTAMP) == data.end()) { generate_common_trace(task, data); } // 2. some other module has not set duration yet else if (data.find(SRPC_DURATION) == data.end()) { unsigned long long end_time = GET_CURRENT_NS(); data[SRPC_FINISH_TIMESTAMP] = std::to_string(end_time); data[SRPC_DURATION] = std::to_string(end_time - atoll(data[SRPC_START_TIMESTAMP].data())); } return true; } void TraceModule::client_begin_request(protocol::HttpRequest *req, RPCModuleData& data) const { data[SRPC_HTTP_METHOD] = req->get_method(); const void *body; size_t body_len; req->get_parsed_body(&body, &body_len); data[SRPC_HTTP_REQ_LEN] = std::to_string(body_len); } void TraceModule::client_end_response(protocol::HttpResponse *resp, RPCModuleData& data) const { data[SRPC_HTTP_STATUS_CODE] = resp->get_status_code(); const void *body; size_t body_len; resp->get_parsed_body(&body, &body_len); data[SRPC_HTTP_RESP_LEN] = std::to_string(body_len); } void TraceModule::server_begin_request(protocol::HttpRequest *req, RPCModuleData& data) const { data[SRPC_HTTP_METHOD] = req->get_method(); data[SRPC_HTTP_TARGET] = req->get_request_uri(); const void *body; size_t body_len; req->get_parsed_body(&body, &body_len); data[SRPC_HTTP_REQ_LEN] = std::to_string(body_len); std::string name; std::string value; protocol::HttpHeaderCursor req_cursor(req); int flag = 0; while (req_cursor.next(name, value) && flag != 3) { if (strcasecmp(name.data(), "Host") == 0) { data[SRPC_HTTP_HOST_NAME] = value; flag |= 1; } else if (strcasecmp(name.data(), "X-Forwarded-For") == 0) { data[SRPC_HTTP_CLIENT_IP] = value; flag |= (1 << 1); } } } void TraceModule::server_end_response(protocol::HttpResponse *resp, RPCModuleData& data) const { data[SRPC_HTTP_STATUS_CODE] = resp->get_status_code(); data[SRPC_HTTP_RESP_LEN] = std::to_string(resp->get_output_body_size()); } } // end namespace srpc srpc-0.10.1/src/module/rpc_trace_module.h000066400000000000000000000200451454502251400202770ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_TRACE_MODULE_H__ #define __RPC_TRACE_MODULE_H__ #include #include #include #include #include #include "workflow/WFTask.h" #include "rpc_basic.h" #include "rpc_module.h" #include "rpc_context.h" #ifdef _WIN32 #include #else #include #include #endif namespace srpc { // follows the basic conventions of RPC part in opentracing static constexpr char const *SRPC_COMPONENT = "srpc.component"; // span tags static constexpr char const *SRPC_SPAN_KIND = "srpc.span.kind"; static constexpr char const *SRPC_SPAN_KIND_CLIENT = "srpc.client"; static constexpr char const *SRPC_SPAN_KIND_SERVER = "srpc.server"; static constexpr char const *SRPC_STATE = "rpc.state"; static constexpr char const *SRPC_ERROR = "rpc.error"; static constexpr char const *WF_TASK_STATE = "task.state"; static constexpr char const *WF_TASK_ERROR = "task.error"; static constexpr char const *SRPC_REMOTE_IP = "server.address"; static constexpr char const *SRPC_REMOTE_PORT = "server.port"; static constexpr char const *SRPC_SAMPLING_PRIO = "srpc.sampling.priority"; // for ext tags static constexpr char const *SRPC_DATA_TYPE = "srpc.data.type"; static constexpr char const *SRPC_COMPRESS_TYPE = "srpc.compress.type"; // for http static constexpr char const *SRPC_HTTP_SOCK_FAMILY = "net.sock.family"; static constexpr char const *SRPC_HTTP_SOCK_ADDR = "net.sock.peer.addr"; static constexpr char const *SRPC_HTTP_SOCK_PORT = "net.sock.peer.port"; static constexpr char const *SRPC_HTTP_REQ_LEN = "http.request_content_length"; static constexpr char const *SRPC_HTTP_RESP_LEN = "http.response_content_length"; // for http client static constexpr char const *SRPC_HTTP_CLIENT_URL = "http.url"; static constexpr char const *SRPC_HTTP_PEER_NAME = "net.peer.name"; static constexpr char const *SRPC_HTTP_PEER_PORT = "net.peer.port"; static constexpr char const *SRPC_HTTP_RESEND_COUNT = "http.resend_count"; // for http server static constexpr char const *SRPC_HTTP_SCHEME = "http.scheme"; static constexpr char const *SRPC_HTTP_HOST_NAME = "net.host.name"; static constexpr char const *SRPC_HTTP_HOST_PORT = "net.host.port"; // /users/12314/?q=ddds static constexpr char const *SRPC_HTTP_TARGET = "http.target"; // The IP address of the original client behind all proxies, from X-Forwarded-For static constexpr char const *SRPC_HTTP_CLIENT_IP = "http.client_ip"; // Basic TraceModule for generating general span data. // Each kind of network task can derived its own TraceModule. class TraceModule : public RPCModule { public: bool client_begin(SubTask *task, RPCModuleData& data) override; bool client_end(SubTask *task, RPCModuleData& data) override; bool server_begin(SubTask *task, RPCModuleData& data) override; bool server_end(SubTask *task, RPCModuleData& data) override; protected: void client_begin_request(protocol::HttpRequest *req, RPCModuleData& data) const; void client_end_response(protocol::HttpResponse *resp, RPCModuleData& data) const; void server_begin_request(protocol::HttpRequest *req, RPCModuleData& data) const; void server_end_response(protocol::HttpResponse *resp, RPCModuleData& data) const; void client_begin_request(protocol::ProtocolMessage *req, RPCModuleData& data) const { } void client_end_response(protocol::ProtocolMessage *resp, RPCModuleData& data) const { } void server_begin_request(protocol::ProtocolMessage *req, RPCModuleData& data) const { } void server_end_response(protocol::ProtocolMessage *resp, RPCModuleData& data) const { } public: TraceModule() : RPCModule(RPCModuleTypeTrace) { } }; // Fill RPC related data in trace module template class RPCTraceModule : public TraceModule { public: bool client_begin(SubTask *task, RPCModuleData& data) override; bool client_end(SubTask *task, RPCModuleData& data) override; bool server_begin(SubTask *task, RPCModuleData& data) override; bool server_end(SubTask *task, RPCModuleData& data) override; private: void client_basic_info(CLIENT_TASK *task, RPCModuleData& data); void server_basic_info(SERVER_TASK *task, RPCModuleData& data); }; ////////// impl template void RPCTraceModule::client_basic_info(CTASK *task, RPCModuleData& data) { auto *req = task->get_req(); data[SRPC_COMPONENT] = SRPC_COMPONENT_SRPC; data[OTLP_SERVICE_NAME] = req->get_service_name(); data[OTLP_METHOD_NAME] = req->get_method_name(); data[SRPC_DATA_TYPE] = std::to_string(req->get_data_type()); data[SRPC_COMPRESS_TYPE] = std::to_string(req->get_compress_type()); } template bool RPCTraceModule::client_begin(SubTask *task, RPCModuleData& data) { TraceModule::client_begin(task, data); auto *client_task = static_cast(task); this->client_basic_info(client_task, data); auto *req = client_task->get_req(); TraceModule::client_begin_request(req, data); return true; } template bool RPCTraceModule::client_end(SubTask *task, RPCModuleData& data) { TraceModule::client_end(task, data); std::string ip; unsigned short port; auto *client_task = static_cast(task); auto *resp = client_task->get_resp(); // failed to call client_begin if (data.find(SRPC_COMPONENT) == data.end()) this->client_basic_info(client_task, data); data[SRPC_STATE] = std::to_string(resp->get_status_code()); if (resp->get_status_code() != RPCStatusOK) { data[SRPC_ERROR] = std::to_string(resp->get_error()); return true; } if (client_task->get_remote(ip, &port)) { data[SRPC_REMOTE_IP] = std::move(ip); data[SRPC_REMOTE_PORT] = std::to_string(port); } TraceModule::client_end_response(resp, data); return true; } template void RPCTraceModule::server_basic_info(STASK *task, RPCModuleData& data) { std::string ip; unsigned short port; auto *req = task->get_req(); data[SRPC_COMPONENT] = SRPC_COMPONENT_SRPC; data[OTLP_SERVICE_NAME] = req->get_service_name(); data[OTLP_METHOD_NAME] = "/" + req->get_service_name() + "/" + req->get_method_name(); data[SRPC_DATA_TYPE] = std::to_string(req->get_data_type()); data[SRPC_COMPRESS_TYPE] = std::to_string(req->get_compress_type()); if (task->get_remote(ip, &port)) { data[SRPC_REMOTE_IP] = std::move(ip); data[SRPC_REMOTE_PORT] = std::to_string(port); } } template bool RPCTraceModule::server_begin(SubTask *task, RPCModuleData& data) { TraceModule::server_begin(task, data); auto *server_task = static_cast(task); this->server_basic_info(server_task, data); auto *req = server_task->get_req(); TraceModule::server_begin_request(req, data); return true; } template bool RPCTraceModule::server_end(SubTask *task, RPCModuleData& data) { TraceModule::server_end(task, data); auto *server_task = static_cast(task); auto *resp = server_task->get_resp(); // failed to call server_begin if (data.find(SRPC_COMPONENT) == data.end()) this->server_basic_info(server_task, data); data[SRPC_STATE] = std::to_string(resp->get_status_code()); if (resp->get_status_code() != RPCStatusOK) { data[SRPC_ERROR] = std::to_string(resp->get_error()); return true; } TraceModule::client_end_response(resp, data); return true; } } // end namespace srpc #endif srpc-0.10.1/src/rpc_basic.cc000066400000000000000000000034251454502251400155710ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifdef _WIN32 #include #else #include #include #endif #include "rpc_basic.h" #include "rpc_module.h" namespace srpc { void RPCCommon::log_format(std::string& key, std::string& value, const RPCLogVector& fields) { if (fields.size() == 0) return; char buffer[100]; snprintf(buffer, 100, "%s%c%lld", SRPC_SPAN_LOG, ' ', GET_CURRENT_MS()); key = std::move(buffer); value = "{\""; for (auto& field : fields) { value = value + std::move(field.first) + "\":\"" + std::move(field.second) + "\","; } value[value.length() - 1] = '}'; } bool RPCCommon::addr_to_string(const struct sockaddr *addr, char *ip_str, socklen_t len, unsigned short *port) { const char *ret = NULL; if (addr->sa_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)addr; ret = inet_ntop(AF_INET, &sin->sin_addr, ip_str, len); *port = ntohs(sin->sin_port); } else if (addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; ret = inet_ntop(AF_INET6, &sin6->sin6_addr, ip_str, len); *port = ntohs(sin6->sin6_port); } else errno = EINVAL; return ret != NULL; } } // namespace srpc srpc-0.10.1/src/rpc_basic.h000066400000000000000000000124071454502251400154330ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_BASIC_H__ #define __RPC_BASIC_H__ #include #include #include #include "rpc_buffer.h" #include #ifdef _WIN32 #include #else #include #include #endif namespace srpc { static constexpr const char *SRPC_SCHEME = "srpc"; static constexpr unsigned short SRPC_DEFAULT_PORT = 1412; static constexpr const char *SRPC_SSL_SCHEME = "srpcs"; static constexpr unsigned short SRPC_SSL_DEFAULT_PORT = 6462; static constexpr size_t RPC_BODY_SIZE_LIMIT = 2LL * 1024 * 1024 * 1024; static constexpr int SRPC_MODULE_MAX = 5; static constexpr size_t SRPC_SPANID_SIZE = 8; static constexpr size_t SRPC_TRACEID_SIZE = 16; #ifndef htonll #if __BYTE_ORDER == __LITTLE_ENDIAN static inline uint64_t htonll(uint64_t x) { return ((uint64_t)htonl(x & 0xFFFFFFFF) << 32) + htonl(x >> 32); } static inline uint64_t ntohll(uint64_t x) { return ((uint64_t)ntohl(x & 0xFFFFFFFF) << 32) + ntohl(x >> 32); } #elif __BYTE_ORDER == __BIG_ENDIAN static inline uint64_t htonll(uint64_t x) { return x; } static inline uint64_t ntohll(uint64_t x) { return x; } #else # error "unknown byte order" #endif #endif #define SRPC_JSON_OPTION_ADD_WHITESPACE (1<<3) #define SRPC_JSON_OPTION_ENUM_AS_INITS (1<<4) #define SRPC_JSON_OPTION_PRESERVE_NAMES (1<<5) #define SRPC_JSON_OPTION_PRINT_PRIMITIVE (1<<6) using ProtobufIDLMessage = google::protobuf::Message; using RPCLogVector = std::vector>; enum RPCDataType { RPCDataUndefined = -1, RPCDataProtobuf = 0, RPCDataThrift = 1, RPCDataJson = 2, }; enum RPCStatusCode { RPCStatusUndefined = 0, RPCStatusOK = 1, RPCStatusServiceNotFound = 2, RPCStatusMethodNotFound = 3, RPCStatusMetaError = 4, RPCStatusReqCompressSizeInvalid = 5, RPCStatusReqDecompressSizeInvalid = 6, RPCStatusReqCompressNotSupported = 7, RPCStatusReqDecompressNotSupported = 8, RPCStatusReqCompressError = 9, RPCStatusReqDecompressError = 10, RPCStatusReqSerializeError = 11, RPCStatusReqDeserializeError = 12, RPCStatusRespCompressSizeInvalid = 13, RPCStatusRespDecompressSizeInvalid = 14, RPCStatusRespCompressNotSupported = 15, RPCStatusRespDecompressNotSupported = 16, RPCStatusRespCompressError = 17, RPCStatusRespDecompressError = 18, RPCStatusRespSerializeError = 19, RPCStatusRespDeserializeError = 20, RPCStatusIDLSerializeNotSupported = 21, RPCStatusIDLDeserializeNotSupported = 22, RPCStatusURIInvalid = 30, RPCStatusUpstreamFailed = 31, RPCStatusSystemError = 100, RPCStatusSSLError = 101, RPCStatusDNSError = 102, RPCStatusProcessTerminated = 103, }; enum RPCCompressType { RPCCompressNone = 0, RPCCompressSnappy = 1, RPCCompressGzip = 2, RPCCompressZlib = 3, RPCCompressLz4 = 4, RPCCompressMax = 5, }; enum RPCModuleType { RPCModuleTypeEmpty = 0, RPCModuleTypeTrace = 1, RPCModuleTypeMetrics = 2, RPCModuleTypeLog = 3, }; class RPCCommon { public: static void log_format(std::string& key, std::string& value, const RPCLogVector& fields); static bool addr_to_string(const struct sockaddr *addr, char *ip_str, socklen_t len, unsigned short *port); }; static inline long long GET_CURRENT_MS() { return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); }; static inline long long GET_CURRENT_MS_STEADY() { return std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()).count(); } static inline unsigned long long GET_CURRENT_NS() { return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); } static inline void TRACE_ID_BIN_TO_HEX(const uint64_t trace_id[2], char hex[SRPC_TRACEID_SIZE * 2 + 1]) { sprintf(hex, "%016llx%016llx", (unsigned long long)ntohll(trace_id[0]), (unsigned long long)ntohll(trace_id[1])); } static inline void SPAN_ID_BIN_TO_HEX(const uint64_t span_id[1], char hex[SRPC_SPANID_SIZE * 2 + 1]) { sprintf(hex, "%016llx", (unsigned long long)ntohll(span_id[0])); } static inline void TRACE_ID_HEX_TO_BIN(const char hex[SRPC_TRACEID_SIZE * 2 + 1], uint64_t trace_id[2]) { sscanf(hex, "%016llx%016llx", (unsigned long long *)&trace_id[0], (unsigned long long *)&trace_id[1]); trace_id[0] = ntohll(trace_id[0]); trace_id[1] = ntohll(trace_id[1]); } static inline void SPAN_ID_HEX_TO_BIN(const char hex[SRPC_SPANID_SIZE * 2 + 1], uint64_t span_id[1]) { sscanf(hex, "%016llx", (unsigned long long *)&span_id[0]); span_id[0] = ntohll(span_id[0]); } } // end namespace srpc #endif srpc-0.10.1/src/rpc_buffer.cc000066400000000000000000000222141454502251400157560ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include "rpc_buffer.h" namespace srpc { void RPCBuffer::clear_list_buffer() { for (const auto& ele: buffer_list_) { if (!ele.is_nocopy) { if (ele.is_new) delete []((char *)ele.buf); else free(ele.buf); } } } void RPCBuffer::clear() { clear_list_buffer(); buffer_list_.clear(); size_ = 0; piece_min_size_ = BUFFER_PIECE_MIN_SIZE; piece_max_size_ = BUFFER_PIECE_MAX_SIZE; init_read_over_ = false; last_piece_left_ = 0; } bool RPCBuffer::append(void *buf, size_t buflen, int mode) { if (mode == BUFFER_MODE_COPY) return write(buf, buflen); buffer_t ele; void *left_buf; if (last_piece_left_ > 0) { const auto it = buffer_list_.rbegin(); left_buf = (char *)it->buf + it->buflen; } ele.buflen = buflen; ele.buf = buf; ele.is_nocopy = (mode == BUFFER_MODE_NOCOPY); ele.is_new = (mode == BUFFER_MODE_GIFT_NEW); buffer_list_.emplace_back(std::move(ele)); size_ += buflen; if (last_piece_left_ > 0) { ele.buflen = 0; ele.buf = left_buf; ele.is_nocopy = true; ele.is_new = false; buffer_list_.emplace_back(std::move(ele)); } return true; } bool RPCBuffer::append(const void *buf, size_t buflen, int mode) { if (mode == BUFFER_MODE_COPY) return write(buf, buflen); return append(const_cast(buf), buflen, mode); } size_t RPCBuffer::backup(size_t count) { if (count == 0 || buffer_list_.empty()) return 0; const auto it = buffer_list_.rbegin(); size_t sz = 0; if (it->buflen > count) { sz = count; it->buflen -= count; } else { sz = it->buflen; it->buflen = 0; } last_piece_left_ += sz; size_ -= sz; return sz; } bool RPCBuffer::read(void *buf, size_t buflen) { while (buflen > 0) { const void *p; size_t sz = buflen; if (!fetch(&p, &sz)) return false; memcpy(buf, p, sz); buf = (char *)buf + sz; buflen -= sz; } return true; } bool RPCBuffer::write(const void *buf, size_t buflen) { while (buflen > 0) { void *p; size_t sz = buflen; if (!acquire(&p, &sz)) return false; memcpy(p, buf, sz); buf = (const char *)buf + sz; buflen -= sz; } return true; } void RPCBuffer::rewind() { cur_.first = buffer_list_.begin(); cur_.second = 0; init_read_over_ = true; } long RPCBuffer::seek(long offset) { if (offset > 0) return read_skip(offset); else if (offset < 0) return read_back(offset); return 0; } size_t RPCBuffer::peek(const void **buf) { return internal_fetch(buf, BUFFER_FETCH_STAY); } size_t RPCBuffer::fetch(const void **buf) { return internal_fetch(buf, BUFFER_FETCH_MOVE); } RPCBuffer::~RPCBuffer() { clear_list_buffer(); } size_t RPCBuffer::acquire(void **buf) { if (last_piece_left_ > 0) { const auto it = buffer_list_.rbegin(); size_t sz = last_piece_left_; *buf = (char *)it->buf + it->buflen; it->buflen += last_piece_left_; size_ += last_piece_left_; last_piece_left_ = 0; return sz; } *buf = malloc(piece_min_size_); if (*buf == NULL) return 0; append(*buf, piece_min_size_, BUFFER_MODE_GIFT_MALLOC); return piece_min_size_; } bool RPCBuffer::acquire(void **buf, size_t *size) { if (last_piece_left_ == 0) { size_t sz = *size; if (sz > piece_max_size_) sz = piece_max_size_; if (sz < piece_min_size_) sz = piece_min_size_; *buf = malloc(sz); if (*buf == NULL) { *size = 0; return false; } append(*buf, 0, BUFFER_MODE_GIFT_MALLOC); last_piece_left_ = sz; } const auto it = buffer_list_.rbegin(); *buf = (char *)it->buf + it->buflen; if (last_piece_left_ <= *size) { *size = last_piece_left_; last_piece_left_ = 0; } else last_piece_left_ -= *size; it->buflen += *size; size_ += *size; return true; } int RPCBuffer::merge_all(struct iovec& iov) { size_t sz = 0; void *new_base = malloc(size_); if (!new_base) return -1; for (const auto& ele : buffer_list_) { memcpy((char *)new_base + sz, ele.buf, ele.buflen); sz += ele.buflen; if (!ele.is_nocopy) { if (ele.is_new) delete []((char *)ele.buf); else free(ele.buf); } } iov.iov_base = new_base; iov.iov_len = sz; buffer_list_.resize(1); auto head = buffer_list_.begin(); head->buf = new_base; head->buflen = sz; head->is_nocopy = false; head->is_new = false; return 1; } int RPCBuffer::encode(struct iovec *iov, int count) { if (count <= 0) { errno = EINVAL; return -1; } if (count == 1) return merge_all(iov[0]) == 0 ? 1 : -1; //if (buffer_list_.size() > count) //{ // // todo try merge all Adjacent copyed // // nocopy copy nocopy copy copy nocopy ... // // 0 1 0 1 1 0 ... // // merge same, test if <= count //} while (buffer_list_.size() > (size_t)count) { //merge half auto next = buffer_list_.begin(); auto cur = next; ++next; while (next != buffer_list_.end()) { size_t sz = cur->buflen + next->buflen; void *new_base = malloc(sz); if (!new_base) return -1; memcpy(new_base, cur->buf, cur->buflen); memcpy((char *)new_base + cur->buflen, next->buf, next->buflen); if (!cur->is_nocopy) { if (cur->is_new) delete []((char *)cur->buf); else free(cur->buf); } if (!next->is_nocopy) { if (next->is_new) delete []((char *)next->buf); else free(next->buf); } cur->buf = new_base; cur->buflen = sz; cur->is_nocopy = false; cur->is_new = false; buffer_list_.erase(next); ++cur; next = cur; ++next; } } int n = 0; for (const auto& ele : buffer_list_) { if (ele.buflen > 0) { iov[n].iov_base = ele.buf; iov[n].iov_len = ele.buflen; n++; } } return n; } bool RPCBuffer::fetch(const void **buf, size_t *size) { if (!init_read_over_) rewind(); while (cur_.first != buffer_list_.end() && cur_.second >= cur_.first->buflen) { ++cur_.first; cur_.second = 0; } if (cur_.first != buffer_list_.end()) { size_t n = cur_.first->buflen - cur_.second; *buf = (char *)cur_.first->buf + cur_.second; if (*size < n) cur_.second += *size; else { *size = n; ++cur_.first; cur_.second = 0; } return true; } *buf = nullptr; *size = 0; return false; } size_t RPCBuffer::internal_fetch(const void **buf, bool move_or_stay) { size_t sz = 0; if (!init_read_over_) rewind(); while (cur_.first != buffer_list_.end() && cur_.second >= cur_.first->buflen) { ++cur_.first; cur_.second = 0; } if (cur_.first == buffer_list_.end()) *buf = nullptr; else { *buf = (char *)cur_.first->buf + cur_.second; sz = cur_.first->buflen - cur_.second; if (move_or_stay) { ++cur_.first; cur_.second = 0; } } return sz; } long RPCBuffer::read_skip(long offset) { long origin = offset; if (!init_read_over_) rewind(); while (offset > 0 && cur_.first != buffer_list_.end()) { if (cur_.second < cur_.first->buflen) { long n = cur_.first->buflen - cur_.second; if (offset < n) { cur_.second += offset; offset = 0; break; } offset -= n; } ++cur_.first; cur_.second = 0; } return origin - offset; } long RPCBuffer::read_back(long offset) { long origin = offset; if (!init_read_over_) rewind(); if (cur_.first == buffer_list_.end()) { if (buffer_list_.empty()) return 0; else { --cur_.first; cur_.second = cur_.first->buflen; } } while (offset < 0) { if (cur_.second > 0) { long n = cur_.second; if (n + offset >= 0) { cur_.second += offset; offset = 0; break; } offset += n; } if (cur_.first == buffer_list_.begin()) break; --cur_.first; cur_.second = cur_.first->buflen; } return origin - offset; } size_t RPCBuffer::cut(size_t offset, RPCBuffer *out) { rewind(); size_ = seek(offset); size_t cutsize = 0; const void *buf; size_t sz = peek(&buf); // sz is the length in this current buf, not final len if (sz > 0) { out->last_piece_left_ = 0; // drop auto erase_it = buffer_list_.end(); while (cur_.first != buffer_list_.end()) { auto it = cur_.first; size_t cur_len = it->buflen - cur_.second; if (it->is_nocopy || cur_.second != 0) { out->append((char *)it->buf + cur_.second, cur_len, BUFFER_MODE_NOCOPY); } else { out->append((char *)it->buf + cur_.second, cur_len, it->is_new ? BUFFER_MODE_GIFT_NEW : BUFFER_MODE_GIFT_MALLOC); } if (erase_it == buffer_list_.end() && cur_.second == 0) erase_it = it; else cur_.first->buflen = cur_.second; cutsize += cur_len; ++cur_.first; cur_.second = 0; } if (last_piece_left_ > 0) out->last_piece_left_ = last_piece_left_; if (erase_it != buffer_list_.end()) buffer_list_.erase(erase_it, buffer_list_.end()); } rewind(); return cutsize; } } // namespace srpc srpc-0.10.1/src/rpc_buffer.h000066400000000000000000000154051454502251400156240ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_BUFFER_H__ #define __RPC_BUFFER_H__ #ifdef _WIN32 #include #else #include #endif #include #include #include namespace srpc { static constexpr int BUFFER_PIECE_MIN_SIZE = 2 * 1024; static constexpr int BUFFER_PIECE_MAX_SIZE = 256 * 1024; static constexpr int BUFFER_MODE_COPY = 0; static constexpr int BUFFER_MODE_NOCOPY = 1; static constexpr int BUFFER_MODE_GIFT_NEW = 2; static constexpr int BUFFER_MODE_GIFT_MALLOC = 3; static constexpr bool BUFFER_FETCH_MOVE = true; static constexpr bool BUFFER_FETCH_STAY = false; /** * @brief Buffer Class * @details * - Thread Safety : NO * - All buffer should allocated by new char[...] or malloc * - Gather buffer piece by piece * - Get buffer one by one */ class RPCBuffer { public: /** * @brief Free all buffer and rewind every state to initialize. * @note NEVER fail */ void clear(); /** * @brief Get all size of buffer */ size_t size() const { return size_; } /** * @brief Cut current buffer at absolutely offset. Current buffer keeps * the first part and gives the second part to the out buffer. * @param[in] offset where to cut * @param[in] out points to out buffer * @return actual give how many bytes to out * @note this will cause current buffer rewind() */ size_t cut(size_t offset, RPCBuffer *out); public: /** * @brief For write. Add one buffer allocated by RPCBuffer * @param[in,out] buf a pointer to a buffer * @param[in,out] size points to the expect size of buffer, return actual size * @return false if OOM, or your will get a buffer actual size <=expect-size * @note Ownership of this buffer remains with the stream */ bool acquire(void **buf, size_t *size); /** * @brief For write. Add one buffer allocated by RPCBuffer * @param[in,out] buf a pointer to a buffer * @return 0 if OOM, or the actual size of the buffer * @note Ownership of this buffer remains with the stream */ size_t acquire(void **buf); /** * @brief For write. Add one buffer * @param[in] buf upstream name * @param[in] buflen consistent-hash functional * @param[in] mode BUFFER_MODE_XXX * @return false if OOM * @note BUFFER_MODE_NOCOPY mean in deconstructor ignore this buffer * @note BUFFER_MODE_COPY mean memcpy this buffer right here at once * @note BUFFER_MODE_GIFT_NEW mean in deconstructor delete this buffer * @note BUFFER_MODE_GIFT_MALLOC mean in deconstructor free this buffer */ bool append(void *buf, size_t buflen, int mode); bool append(const void *buf, size_t buflen, int mode); /** * @brief For write. Backs up a number of bytes of last buffer * @param[in] count how many bytes back up * @return the actual bytes backup * @note It is affect the buffer with both acquire and append * @note count should be less than or equal to the size of the last buffer */ size_t backup(size_t count); /** * @brief For write. Add one buffer use memcpy * @param[in] buf buffer * @param[in] buflen buffer size * @return false if OOM */ bool write(const void *buf, size_t buflen); public: /** * @brief For workflow message encode. * @param[in] iov iov vector * @param[in] count iov vector count * @return use how many iov * @retval -1 when count <=0, and set errno EINVAL */ int encode(struct iovec *iov, int count); /** * @brief merge all buffer into one piece * @param[out] iov pointer and length of result * @return suceess or OOM * @retval 0 success * @retval -1 OOM */ int merge_all(struct iovec& iov); public: /** * @brief For read. Reset buffer position to head * @note NEVER fail */ void rewind(); /** * @brief For read. Get one buffer, NO move offset * @param[in,out] buf a pointer to a buffer * @return 0 if no more data to read, or the forward size available */ size_t peek(const void **buf); /** * @brief For read. Get one buffer by except size, move offset * @param[in,out] buf a pointer to a buffer * @param[in,out] size except buffer size, and return actual size * @return false if OOM, or your will get a buffer actual size <=expect-size */ bool fetch(const void **buf, size_t *size); /** * @brief For read. Get one buffer, move offset * @param[in,out] buf a pointer to a buffer * @return 0 if no more data to read, or the forward size available */ size_t fetch(const void **buf); /** * @brief For read. Fill one buffer with memcpy, move offset * @param[in] buf buffer wait to fill * @param[in] buflen except buffer size * @return true if fill buffer exactly bytes, false if no more data to read */ bool read(void *buf, size_t buflen); /** * @brief For read. move offset, positive mean skip, negative mean backward * @param[in] offset except move offset * @return actual move offset * @note If offset=0, do nothing at all */ long seek(long offset); public: void set_piece_min_size(size_t size) { piece_min_size_ = size; } void set_piece_max_size(size_t size) { piece_max_size_ = size; } RPCBuffer() = default; ~RPCBuffer(); RPCBuffer(const RPCBuffer&) = delete; RPCBuffer(RPCBuffer&&) = delete; RPCBuffer& operator=(const RPCBuffer&) = delete; RPCBuffer& operator=(RPCBuffer&&) = delete; private: struct buffer_t { void *buf; size_t buflen; bool is_nocopy; bool is_new; }; void clear_list_buffer(); size_t internal_fetch(const void **buf, bool move_or_stay); long read_skip(long offset); long read_back(long offset); std::list buffer_list_; std::pair::iterator, size_t> cur_; size_t size_ = 0; size_t piece_min_size_ = BUFFER_PIECE_MIN_SIZE; size_t piece_max_size_ = BUFFER_PIECE_MAX_SIZE; bool init_read_over_ = false; size_t last_piece_left_ = 0; }; } // namespace srpc #endif srpc-0.10.1/src/rpc_client.h000066400000000000000000000136641454502251400156360ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_CLIENT_H__ #define __RPC_CLIENT_H__ #include "rpc_types.h" #include "rpc_context.h" #include "rpc_options.h" #include "rpc_global.h" #include "rpc_trace_module.h" #include "rpc_metrics_module.h" namespace srpc { template class RPCClient { public: using TASK = RPCClientTask; protected: using COMPLEXTASK = WFComplexClientTask; public: RPCClient(const std::string& service_name); virtual ~RPCClient() { }; const RPCTaskParams *get_task_params() const; const std::string& get_service_name() const; void task_init(COMPLEXTASK *task) const; void set_keep_alive(int timeout); void set_watch_timeout(int timeout); void add_filter(RPCFilter *filter); protected: template TASK *create_rpc_client_task(const std::string& method_name, std::function&& done) { std::list module; for (int i = 0; i < SRPC_MODULE_MAX; i++) { if (this->modules[i]) module.push_back(this->modules[i]); } auto *task = new TASK(this->service_name, method_name, &this->params.task_params, std::move(module), [done](int status_code, RPCWorker& worker) -> int { return ClientRPCDoneImpl(status_code, worker, done); }); this->task_init(task); return task; } void init(const RPCClientParams *params); std::string service_name; private: void __task_init(COMPLEXTASK *task) const; protected: RPCClientParams params; ParsedURI uri; private: struct sockaddr_storage ss; socklen_t ss_len; bool has_addr_info; std::mutex mutex; RPCModule *modules[SRPC_MODULE_MAX] = { 0 }; }; //////// // inl template inline RPCClient::RPCClient(const std::string& service_name): params(RPC_CLIENT_PARAMS_DEFAULT), has_addr_info(false) { SRPCGlobal::get_instance(); this->service_name = service_name; } template inline const RPCTaskParams *RPCClient::get_task_params() const { return &this->params.task_params; } template inline const std::string& RPCClient::get_service_name() const { return this->service_name; } template inline void RPCClient::set_keep_alive(int timeout) { this->params.task_params.keep_alive_timeout = timeout; } template inline void RPCClient::set_watch_timeout(int timeout) { this->params.task_params.watch_timeout = timeout; } template void RPCClient::add_filter(RPCFilter *filter) { using CLIENT_TASK = RPCClientTask; using SERVER_TASK = RPCServerTask; int type = filter->get_module_type(); this->mutex.lock(); if (type < SRPC_MODULE_MAX && type >= 0) { RPCModule *module = this->modules[type]; if (!module) { switch (type) { case RPCModuleTypeTrace: module = new RPCTraceModule(); break; case RPCModuleTypeMetrics: module = new RPCMetricsModule(); break; default: break; } this->modules[type] = module; } if (module) module->add_filter(filter); } this->mutex.unlock(); return; } template inline void RPCClient::init(const RPCClientParams *params) { this->params = *params; if (this->params.task_params.data_type == RPCDataUndefined) this->params.task_params.data_type = RPCTYPE::default_data_type; this->has_addr_info = SRPCGlobal::get_instance()->task_init(this->params, this->uri, &this->ss, &this->ss_len); } template inline void RPCClient::__task_init(COMPLEXTASK *task) const { if (this->has_addr_info) task->init(this->params.is_ssl ? TT_TCP_SSL : TT_TCP, (const struct sockaddr *)&this->ss, this->ss_len, ""); else { task->init(this->uri); task->set_transport_type(this->params.is_ssl ? TT_TCP_SSL : TT_TCP); } } template inline void RPCClient::task_init(COMPLEXTASK *task) const { __task_init(task); } static inline void __set_host_by_uri(const ParsedURI *uri, bool is_ssl, std::string& header_host) { if (uri->host && uri->host[0]) header_host = uri->host; if (uri->port && uri->port[0]) { int port = atoi(uri->port); if (is_ssl) { if (port != 443) { header_host += ":"; header_host += uri->port; } } else { if (port != 80) { header_host += ":"; header_host += uri->port; } } } } template<> inline void RPCClient::task_init(COMPLEXTASK *task) const { __task_init(task); std::string header_host; if (this->has_addr_info) header_host += this->params.host + ":" + std::to_string(this->params.port); else __set_host_by_uri(task->get_current_uri(), this->params.is_ssl, header_host); task->get_req()->set_header_pair("Host", header_host.c_str()); } template<> inline void RPCClient::task_init(COMPLEXTASK *task) const { __task_init(task); std::string header_host; if (this->has_addr_info) header_host += this->params.host + ":" + std::to_string(this->params.port); else __set_host_by_uri(task->get_current_uri(), this->params.is_ssl, header_host); task->get_req()->set_header_pair("Host", header_host.c_str()); } } // namespace srpc #endif srpc-0.10.1/src/rpc_context.h000066400000000000000000000073521454502251400160410ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_CONTEXT_H__ #define __RPC_CONTEXT_H__ #include #include #include #include "rpc_basic.h" namespace srpc { struct RPCSyncContext { long long seqid; std::string errmsg; std::string remote_ip; int status_code; int error; bool success; int timeout_reason; }; class RPCContext { public: virtual long long get_seqid() const = 0; virtual std::string get_remote_ip() const = 0; virtual int get_peer_addr(struct sockaddr *addr, socklen_t *addrlen) const = 0; virtual const std::string& get_service_name() const = 0; virtual const std::string& get_method_name() const = 0; virtual SeriesWork *get_series() const = 0; virtual bool get_http_header(const std::string& name, std::string& value) const = 0; public: // for client-done virtual bool success() const = 0; virtual int get_status_code() const = 0; virtual const char *get_errmsg() const = 0; virtual int get_error() const = 0; virtual void *get_user_data() const = 0; virtual int get_timeout_reason() const = 0; public: // for server-process virtual void set_data_type(RPCDataType type) = 0;//enum RPCDataType virtual void set_compress_type(RPCCompressType type) = 0;//enum RPCCompressType virtual void set_attachment_nocopy(const char *attachment, size_t len) = 0; virtual bool get_attachment(const char **attachment, size_t *len) const = 0; virtual void set_reply_callback(std::function cb) = 0; virtual void set_send_timeout(int timeout) = 0; virtual void set_keep_alive(int timeout) = 0; virtual bool set_http_code(int code) = 0; virtual bool set_http_header(const std::string& name, const std::string& value) = 0; virtual bool add_http_header(const std::string& name, const std::string& value) = 0; virtual bool log(const RPCLogVector& fields) = 0; // Refer to : https://opentelemetry.io/docs/reference/specification/baggage/api // corresponding to SetValue(), GetValue(), RemoveValue() virtual bool add_baggage(const std::string& key, const std::string& value) = 0; virtual bool get_baggage(const std::string& key, std::string& value) = 0; //virtual bool remove_baggage(const std::string& key) = 0; //virtual void noreply(); //virtual WFConnection *get_connection(); public: // for json format // Currently only support pb to json. Default : false // Whether to add spaces, line breaks and indentation to make the JSON // output easy to read. virtual void set_json_add_whitespace(bool on) = 0; // Whether to always print enums as ints. virtual void set_json_always_print_enums_as_ints(bool flag) = 0; // Whether to preserve proto field names. virtual void set_json_preserve_proto_field_names(bool flag) = 0; // Whether to always print primitive fields. // By default proto3 primitive fields with default values will be omitted // in JSON output. For example, an int32 field set to 0 will be omitted. // Set this flag to true will override the default behavior and print // primitive fields regardless of their values. virtual void set_json_always_print_primitive_fields(bool flag) = 0; public: virtual ~RPCContext() { } }; } // namespace srpc //////// // inl #include "rpc_context.inl" #include "rpc_task.inl" #endif srpc-0.10.1/src/rpc_context.inl000066400000000000000000000142211454502251400163650ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include "rpc_message.h" #include "rpc_module.h" namespace srpc { template struct ThriftReceiver { std::mutex mutex; std::condition_variable cond; RPCSyncContext ctx; T output; bool is_done = false; }; template class RPCContextImpl : public RPCContext { public: long long get_seqid() const override { return task_->get_task_seq(); } std::string get_remote_ip() const override { char ip_str[INET6_ADDRSTRLEN + 1] = { 0 }; struct sockaddr_storage addr; socklen_t addrlen = sizeof (addr); if (this->get_peer_addr((struct sockaddr *)&addr, &addrlen) == 0) { if (addr.ss_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)(&addr); inet_ntop(AF_INET, &sin->sin_addr, ip_str, INET_ADDRSTRLEN); } else if (addr.ss_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(&addr); inet_ntop(AF_INET6, &sin6->sin6_addr, ip_str, INET6_ADDRSTRLEN); } } return std::string(ip_str); } int get_peer_addr(struct sockaddr *addr, socklen_t *addrlen) const override { return task_->get_peer_addr(addr, addrlen); } const std::string& get_service_name() const override { return task_->get_req()->get_service_name(); } const std::string& get_method_name() const override { return task_->get_req()->get_method_name(); } void set_data_type(RPCDataType type) override { task_->get_resp()->set_data_type(type); } void set_compress_type(RPCCompressType type) override { task_->get_resp()->set_compress_type(type); } void set_send_timeout(int timeout) override { task_->set_send_timeout(timeout); } void set_keep_alive(int timeout) override { task_->set_keep_alive(timeout); } SeriesWork *get_series() const override { return series_of(task_); } void *get_user_data() const override { return task_->user_data; } bool get_attachment(const char **attachment, size_t *len) const override { if (this->is_server_task()) return task_->get_req()->get_attachment_nocopy(attachment, len); else return task_->get_resp()->get_attachment_nocopy(attachment, len); } bool get_http_header(const std::string& name, std::string& value) const override { if (this->is_server_task()) return task_->get_req()->get_http_header(name, value); else return task_->get_resp()->get_http_header(name, value); } public: // for client-done bool success() const override { return task_->get_resp()->get_status_code() == RPCStatusOK; } int get_status_code() const override { return task_->get_resp()->get_status_code(); } const char *get_errmsg() const override { return task_->get_resp()->get_errmsg(); } int get_error() const override { return task_->get_resp()->get_error(); } int get_timeout_reason() const override { return task_->get_timeout_reason(); } public: // for server-process void set_attachment_nocopy(const char *attachment, size_t len) override { task_->get_resp()->set_attachment_nocopy(attachment, len); } void set_reply_callback(std::function cb) override { if (this->is_server_task()) { if (cb) { task_->set_callback([this, cb](SubTask *task) { cb(this); }); } else task_->set_callback(nullptr); } } bool set_http_code(int code) override { if (this->is_server_task()) return task_->get_resp()->set_http_code(code); return false; } bool set_http_header(const std::string& name, const std::string& value) override { if (this->is_server_task()) return task_->get_resp()->set_http_header(name, value); return false; } bool add_http_header(const std::string& name, const std::string& value) override { if (this->is_server_task()) return task_->get_resp()->add_http_header(name, value); return false; } bool log(const RPCLogVector& fields) override { if (this->is_server_task() && module_data_) { std::string key; std::string value; RPCCommon::log_format(key, value, fields); module_data_->emplace(std::move(key), std::move(value)); return true; } return false; } bool add_baggage(const std::string& key, const std::string& value) override { if (this->is_server_task() && module_data_) { (*module_data_)[key] = value; return true; } return false; } bool get_baggage(const std::string& key, std::string& value) override { if (module_data_) { const auto it = module_data_->find(key); if (it != module_data_->cend()) { value = it->second; return true; } } return false; } void set_json_add_whitespace(bool on) override { if (this->is_server_task()) task_->get_resp()->set_json_add_whitespace(on); } void set_json_always_print_enums_as_ints(bool on) override { if (this->is_server_task()) task_->get_resp()->set_json_enums_as_ints(on); } void set_json_preserve_proto_field_names(bool on) override { if (this->is_server_task()) task_->get_resp()->set_json_preserve_names(on); } void set_json_always_print_primitive_fields(bool on) override { if (this->is_server_task()) task_->get_resp()->set_json_print_primitive(on); } //void noreply() override; //WFConnection *get_connection() override; public: RPCContextImpl(WFNetworkTask *task, RPCModuleData *module_data) : task_(task), module_data_(module_data) { } protected: bool is_server_task() const { return task_->get_state() == WFT_STATE_TOREPLY || task_->get_state() == WFT_STATE_NOREPLY; } protected: WFNetworkTask *task_; private: RPCModuleData *module_data_; }; } // namespace srpc srpc-0.10.1/src/rpc_define.h000066400000000000000000000035001454502251400155760ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_DEFINE_H__ #define __RPC_DEFINE_H__ #if __cplusplus < 201100 #error CPLUSPLUS VERSION required at least C++11. Please use "-std=c++11". #include #endif #include "rpc_server.h" #include "rpc_client.h" namespace srpc { using SRPCServer = RPCServer; using SRPCClient = RPCClient; using SRPCClientTask = SRPCClient::TASK; using SRPCHttpServer = RPCServer; using SRPCHttpClient = RPCClient; using SRPCHttpClientTask = SRPCHttpClient::TASK; using BRPCServer = RPCServer; using BRPCClient = RPCClient; using BRPCClientTask = BRPCClient::TASK; using ThriftServer = RPCServer; using ThriftClient = RPCClient; using ThriftClientTask = ThriftClient::TASK; using ThriftHttpServer = RPCServer; using ThriftHttpClient = RPCClient; using ThriftHttpClientTask = ThriftHttpClient::TASK; using TRPCServer = RPCServer; using TRPCClient = RPCClient; using TRPCClientTask = TRPCClient::TASK; using TRPCHttpServer = RPCServer; using TRPCHttpClient = RPCClient; using TRPCHttpClientTask = TRPCHttpClient::TASK; } // namespace srpc #endif srpc-0.10.1/src/rpc_global.cc000066400000000000000000000060641454502251400157520ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifdef _WIN32 #include #else #include #include #include #endif #include #include #include "rpc_basic.h" #include "rpc_global.h" namespace srpc { SRPCGlobal::SRPCGlobal() : gen(rd()) { WFGlobal::register_scheme_port(SRPC_SCHEME, SRPC_DEFAULT_PORT); WFGlobal::register_scheme_port(SRPC_SSL_SCHEME, SRPC_SSL_DEFAULT_PORT); this->group_id = this->gen() % (1 << SRPC_GROUP_BITS); this->machine_id = this->gen() % (1 << SRPC_MACHINE_BITS); } static int __get_addr_info(const std::string& host, unsigned short port, struct sockaddr_storage *ss, socklen_t *ss_len) { if (!host.empty()) { char front = host.front(); char back = host.back(); if (host.find(':') != std::string::npos) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; memset(sin6, 0, sizeof (struct sockaddr_in6)); if (inet_pton(AF_INET6, host.c_str(), &sin6->sin6_addr) == 1) { sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); *ss_len = sizeof (struct sockaddr_in6); return 0; } } else if (isdigit(back) && isdigit(front)) { struct sockaddr_in *sin = (struct sockaddr_in *)ss; memset(sin, 0, sizeof (struct sockaddr_in)); if (inet_pton(AF_INET, host.c_str(), &sin->sin_addr) == 1) { sin->sin_family = AF_INET; sin->sin_port = htons(port); *ss_len = sizeof (struct sockaddr_in); return 0; } } } return -1; } bool SRPCGlobal::task_init(RPCClientParams& params, ParsedURI& uri, struct sockaddr_storage *ss, socklen_t *ss_len) const { if (!params.host.empty()) { if (__get_addr_info(params.host, params.port, ss, ss_len) == 0) return true; if (params.is_ssl) uri.scheme = strdup(SRPC_SSL_SCHEME); else uri.scheme = strdup(SRPC_SCHEME); uri.host = strdup(params.host.c_str()); uri.port = strdup(std::to_string(params.port).c_str()); if (uri.scheme && uri.host && uri.port) uri.state = URI_STATE_SUCCESS; else { uri.state = URI_STATE_ERROR; uri.error = errno; } } else { URIParser::parse(params.url, uri); params.is_ssl = (uri.scheme && (strcasecmp(uri.scheme, "https") == 0 || strcasecmp(uri.scheme, "srpcs") == 0)); } return false; } unsigned long long SRPCGlobal::get_random() { unsigned long long id = 0; this->snowflake.get_id(this->group_id, this->machine_id, &id); return id; } static const SRPCGlobal *srpc_global = SRPCGlobal::get_instance(); } // namespace srpc srpc-0.10.1/src/rpc_global.h000066400000000000000000000025471454502251400156160ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_GLOBAL_H__ #define __RPC_GLOBAL_H__ #include #include #include "rpc_options.h" #include "rpc_module.h" namespace srpc { class SRPCGlobal { public: static SRPCGlobal *get_instance() { static SRPCGlobal kInstance; return &kInstance; } public: const char *get_srpc_version() const; bool task_init(RPCClientParams& params, ParsedURI& uri, struct sockaddr_storage *ss, socklen_t *ss_len) const; unsigned long long get_random(); void set_group_id(unsigned short id) { this->group_id = id; } void set_machine_id(unsigned short id) { this->machine_id = id; } private: SRPCGlobal(); SnowFlake snowflake; std::random_device rd; std::mt19937 gen; unsigned short group_id; unsigned short machine_id; }; } // namespace srpc #endif srpc-0.10.1/src/rpc_options.h000066400000000000000000000041631454502251400160450ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_OPTIONS_H__ #define __RPC_OPTIONS_H__ #include #include #include #include #include "rpc_basic.h" namespace srpc { struct RPCTaskParams { int send_timeout; int receive_timeout; int watch_timeout; int keep_alive_timeout; int retry_max; int compress_type; //RPCCompressType int data_type; //RPCDataType }; struct RPCClientParams { RPCTaskParams task_params; //host + port + is_ssl std::string host; unsigned short port; bool is_ssl; //or URL std::string url; int callee_timeout; std::string caller; }; struct RPCServerParams : public WFServerParams { RPCServerParams() : WFServerParams({ /* .max_connections = */ 2000, /* .peer_response_timeout = */ 10 * 1000, /* .receive_timeout = */ -1, /* .keep_alive_timeout = */ 60 * 1000, /* .request_size_limit = */ RPC_BODY_SIZE_LIMIT, /* .ssl_accept_timeout = */ 10 * 1000 }) {} }; static constexpr struct RPCTaskParams RPC_TASK_PARAMS_DEFAULT = { /* .send_timeout = */ -1, /* .receive_timeout = */ -1, /* .watch_timeout = */ 0, /* .keep_alive_timeout = */ 30 * 1000, /* .retry_max = */ 0, /* .compress_type = */ RPCCompressNone, /* .data_type = */ RPCDataUndefined }; static const struct RPCClientParams RPC_CLIENT_PARAMS_DEFAULT = { /* .task_params = */ RPC_TASK_PARAMS_DEFAULT, /* .host = */ "", /* .port = */ SRPC_DEFAULT_PORT, /* .is_ssl = */ false, /* .url = */ "", /* .callee_timeout = */ -1, /* .caller = */ "" }; static const RPCServerParams RPC_SERVER_PARAMS_DEFAULT; } // end namespace #endif srpc-0.10.1/src/rpc_server.h000066400000000000000000000147771454502251400156740ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_SERVER_H__ #define __RPC_SERVER_H__ #include #include #include #include #include #include "rpc_types.h" #include "rpc_service.h" #include "rpc_options.h" #include "rpc_trace_module.h" #include "rpc_metrics_module.h" namespace srpc { template class RPCServer : public WFServer { public: using REQTYPE = typename RPCTYPE::REQ; using RESPTYPE = typename RPCTYPE::RESP; using TASK = RPCServerTask; using SERIES = typename TASK::RPCSeries; protected: using NETWORKTASK = WFNetworkTask; public: RPCServer(); RPCServer(const struct RPCServerParams *params); int add_service(RPCService *service); const RPCService* find_service(const std::string& name) const; void add_filter(RPCFilter *filter); protected: RPCServer(const struct RPCServerParams *params, std::function&& process); CommSession *new_session(long long seq, CommConnection *conn) override; void server_process(NETWORKTASK *task) const; private: std::mutex mutex; std::map service_map; RPCModule *modules[SRPC_MODULE_MAX] = { NULL }; }; //////// // inl template inline RPCServer::RPCServer(): WFServer(&RPC_SERVER_PARAMS_DEFAULT, std::bind(&RPCServer::server_process, this, std::placeholders::_1)) {} template inline RPCServer::RPCServer(const struct RPCServerParams *params): WFServer(params, std::bind(&RPCServer::server_process, this, std::placeholders::_1)) {} template inline RPCServer::RPCServer(const struct RPCServerParams *params, std::function&& process): WFServer(¶ms, std::move(process)) {} template inline int RPCServer::add_service(RPCService* service) { const auto it = this->service_map.emplace(service->get_name(), service); if (!it.second) { errno = EEXIST; return -1; } return 0; } template<> inline int RPCServer::add_service(RPCService* service) { const std::string &name = service->get_name(); const auto it = this->service_map.emplace(name, service); if (!it.second) { errno = EEXIST; return -1; } auto pos = name.find_last_of('.'); if (pos != std::string::npos) this->service_map.emplace(name.substr(pos + 1), service); return 0; } template<> inline int RPCServer::add_service(RPCService* service) { const std::string &name = service->get_name(); const auto it = this->service_map.emplace(name, service); if (!it.second) { errno = EEXIST; return -1; } auto pos = name.find_last_of('.'); if (pos != std::string::npos) this->service_map.emplace(name.substr(pos + 1), service); return 0; } template void RPCServer::add_filter(RPCFilter *filter) { using CLIENT_TASK = RPCClientTask; using SERVER_TASK = RPCServerTask; int type = filter->get_module_type(); this->mutex.lock(); if (type < SRPC_MODULE_MAX && type >= 0) { RPCModule *module = this->modules[type]; if (!module) { switch (type) { case RPCModuleTypeTrace: module = new RPCTraceModule(); break; case RPCModuleTypeMetrics: module = new RPCMetricsModule(); break; default: break; } this->modules[type] = module; } if (module) module->add_filter(filter); } this->mutex.unlock(); return; } template inline const RPCService * RPCServer::find_service(const std::string& name) const { const auto it = this->service_map.find(name); if (it != this->service_map.cend()) return it->second; return NULL; } template inline CommSession *RPCServer::new_session(long long seq, CommConnection *conn) { /* TODO: Change to a factory function. */ std::list module; for (int i = 0; i < SRPC_MODULE_MAX; i++) { if (this->modules[i]) module.push_back(this->modules[i]); } auto *task = new TASK(this, this->process, std::move(module)); task->set_keep_alive(this->params.keep_alive_timeout); task->get_req()->set_size_limit(this->params.request_size_limit); return task; } template void RPCServer::server_process(NETWORKTASK *task) const { auto *req = task->get_req(); auto *resp = task->get_resp(); int status_code; if (!req->deserialize_meta()) status_code = RPCStatusMetaError; else { auto *server_task = static_cast(task); RPCModuleData *task_data = server_task->mutable_module_data(); req->get_meta_module_data(*task_data); RPCTYPE::server_reply_init(req, resp); auto *service = this->find_service(req->get_service_name()); if (!service) status_code = RPCStatusServiceNotFound; else { auto *rpc = service->find_method(req->get_method_name()); if (!rpc) status_code = RPCStatusMethodNotFound; else { for (auto *module : this->modules) { if (module) module->server_task_begin(server_task, *task_data); } status_code = req->decompress(); if (status_code == RPCStatusOK) status_code = (*rpc)(server_task->worker); } } SERIES *series = static_cast(series_of(task)); series->set_module_data(task_data); } resp->set_status_code(status_code); } template<> inline const RPCService * RPCServer::find_service(const std::string& name) const { if (this->service_map.empty()) return NULL; return this->service_map.cbegin()->second; } template<> inline const RPCService * RPCServer::find_service(const std::string& name) const { if (this->service_map.empty()) return NULL; return this->service_map.cbegin()->second; } } // namespace srpc #endif srpc-0.10.1/src/rpc_service.h000066400000000000000000000044171454502251400160140ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_SERVICE_H__ #define __RPC_SERVICE_H__ #include #include #include #include "rpc_context.h" #include "rpc_options.h" namespace srpc { class RPCService { protected: using rpc_method_t = std::function; public: RPCService(const std::string& name) : name_(name) { } RPCService(RPCService&& move) = delete; RPCService& operator=(RPCService&& move) = delete; RPCService(const RPCService& copy) = delete; RPCService& operator=(const RPCService& copy) = delete; virtual ~RPCService() { }; const std::string& get_name() const { return name_; } const rpc_method_t *find_method(const std::string& method_name) const; protected: void add_method(const std::string& method_name, rpc_method_t&& method); private: std::unordered_map methods_; std::string name_; }; //////// // inl template static inline int ServiceRPCCallImpl(SERVICE *service, RPCWorker& worker, void (SERVICE::*rpc)(INPUT *, OUTPUT *, RPCContext *)) { auto *in = new INPUT; worker.set_server_input(in); int status_code = worker.req->deserialize(in); if (status_code == RPCStatusOK) { auto *out = new OUTPUT; worker.set_server_output(out); (service->*rpc)(in, out, worker.ctx); } return status_code; } inline void RPCService::add_method(const std::string& method_name, rpc_method_t&& method) { methods_.emplace(method_name, std::move(method)); } inline const RPCService::rpc_method_t *RPCService::find_method(const std::string& method_name) const { const auto it = methods_.find(method_name); if (it != methods_.cend()) return &it->second; return NULL; } } // namespace srpc #endif srpc-0.10.1/src/rpc_task.inl000066400000000000000000000427651454502251400156610ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include "rpc_basic.h" #include "rpc_message.h" #include "rpc_options.h" #include "rpc_global.h" namespace srpc { class RPCWorker { public: RPCWorker(RPCContext *ctx, RPCMessage *req, RPCMessage *resp) { this->ctx = ctx; this->req = req; this->resp = resp; this->__server_serialize = NULL; } ~RPCWorker() { delete this->ctx; delete this->pb_input; delete this->pb_output; delete this->thrift_intput; delete this->thrift_output; } void set_server_input(ProtobufIDLMessage *input) { this->pb_input = input; } void set_server_input(ThriftIDLMessage *input) { this->thrift_intput = input; } void set_server_output(ProtobufIDLMessage *output) { this->pb_output = output; this->__server_serialize = &RPCWorker::resp_serialize_pb; } void set_server_output(ThriftIDLMessage *output) { this->thrift_output = output; this->__server_serialize = &RPCWorker::resp_serialize_thrift; } int server_serialize() { if (!this->__server_serialize) return RPCStatusOK; return (this->*__server_serialize)(); } public: RPCContext *ctx; RPCMessage *req; RPCMessage *resp; private: int resp_serialize_pb() { return this->resp->serialize(this->pb_output); } int resp_serialize_thrift() { return this->resp->serialize(this->thrift_output); } int (RPCWorker::*__server_serialize)(); ProtobufIDLMessage *pb_input = NULL; ProtobufIDLMessage *pb_output = NULL; ThriftIDLMessage *thrift_intput = NULL; ThriftIDLMessage *thrift_output = NULL; }; template class RPCClientTask : public WFComplexClientTask { public: // before rpc call void set_data_type(RPCDataType type); void set_compress_type(RPCCompressType type); void set_retry_max(int retry_max); void set_attachment_nocopy(const char *attachment, size_t len); int set_uri_fragment(const std::string& fragment); int serialize_input(const ProtobufIDLMessage *in); int serialize_input(const ThriftIDLMessage *in); // similar to opentracing: log({{"event", "error"}, {"message", "application log"}}); void log(const RPCLogVector& fields); // Baggage Items, which are just key:value pairs that cross process boundaries void add_baggage(const std::string& key, const std::string& value); bool get_baggage(const std::string& key, std::string& value); bool set_http_header(const std::string& name, const std::string& value); bool add_http_header(const std::string& name, const std::string& value); // JsonPrintOptions void set_json_add_whitespace(bool on); void set_json_always_print_enums_as_ints(bool on); void set_json_preserve_proto_field_names(bool on); void set_json_always_print_primitive_fields(bool on); protected: using user_done_t = std::function; using WFComplexClientTask::set_callback; void init_failed() override; bool check_request() override; CommMessageOut *message_out() override; bool finish_once() override; void rpc_callback(WFNetworkTask *task); int first_timeout() override { return watch_timeout_; } public: RPCClientTask(const std::string& service_name, const std::string& method_name, const RPCTaskParams *params, std::list&& modules, user_done_t&& user_done); bool get_remote(std::string& ip, unsigned short *port) const; RPCModuleData *mutable_module_data() { return &module_data_; } void set_module_data(RPCModuleData data) { module_data_ = std::move(data); } private: template int __serialize_input(const IDL *in); user_done_t user_done_; bool init_failed_; int watch_timeout_; RPCModuleData module_data_; std::list modules_; }; template class RPCServerTask : public WFServerTask { public: RPCServerTask(CommService *service, std::function *)>& process, std::list&& modules) : WFServerTask(service, WFGlobal::get_scheduler(), process), worker(new RPCContextImpl(this, &module_data_), &this->req, &this->resp), modules_(std::move(modules)) { } public: class RPCSeries : public WFServerTask::Series { public: RPCSeries(WFServerTask *task) : WFServerTask::Series(task), module_data(NULL) {} RPCModuleData *get_module_data() { return this->module_data; } void set_module_data(RPCModuleData *data) { this->module_data = data; } virtual void *get_specific(const char *key) { if (strcmp(key, SRPC_MODULE_DATA) == 0) return this->module_data; else return NULL; } private: RPCModuleData *module_data; }; protected: CommMessageOut *message_out() override; void handle(int state, int error) override; public: bool get_remote(std::string& ip, unsigned short *port) const; RPCModuleData *mutable_module_data() { return &module_data_; } void set_module_data(RPCModuleData data) { module_data_ = std::move(data); } public: RPCWorker worker; private: RPCModuleData module_data_; std::list modules_; }; template static void RPCAsyncFutureCallback(OUTPUT *output, srpc::RPCContext *ctx) { using RESULT = std::pair; auto *pr = static_cast *>(ctx->get_user_data()); RESULT res; res.second.seqid = ctx->get_seqid(); res.second.errmsg = ctx->get_errmsg(); res.second.remote_ip = ctx->get_remote_ip(); res.second.status_code = ctx->get_status_code(); res.second.error = ctx->get_error(); res.second.success = ctx->success(); res.second.timeout_reason = ctx->get_timeout_reason(); if (res.second.success) res.first = std::move(*output); pr->set_value(std::move(res)); delete pr; } template static void ThriftSendCallback(OUTPUT *output, srpc::RPCContext *ctx) { auto *receiver = static_cast *>(ctx->get_user_data()); receiver->mutex.lock(); receiver->ctx.seqid = ctx->get_seqid(); receiver->ctx.errmsg = ctx->get_errmsg(); receiver->ctx.remote_ip = ctx->get_remote_ip(); receiver->ctx.status_code = ctx->get_status_code(); receiver->ctx.error = ctx->get_error(); receiver->ctx.success = ctx->success(); if (receiver->ctx.success) receiver->output = std::move(*output); receiver->is_done = true; receiver->cond.notify_one(); receiver->mutex.unlock(); } template static inline int ClientRPCDoneImpl(int status_code, RPCWorker& worker, const std::function& rpc_done) { if (status_code == RPCStatusOK) { OUTPUT out; status_code = worker.resp->deserialize(&out); if (status_code == RPCStatusOK) rpc_done(&out, worker.ctx); return status_code; } rpc_done(NULL, worker.ctx); return status_code; } template CommMessageOut *RPCServerTask::message_out() { int status_code = this->worker.server_serialize(); if (status_code == RPCStatusOK) status_code = this->resp.compress(); // for server, this is the where series->module_data stored RPCModuleData *data = this->mutable_module_data(); for (auto *module : modules_) module->server_task_end(this, *data); this->resp.set_meta_module_data(*data); if (status_code == RPCStatusOK) { if (!this->resp.serialize_meta()) status_code = RPCStatusMetaError; } if (this->resp.get_status_code() == RPCStatusOK) this->resp.set_status_code(status_code); if (status_code == RPCStatusOK) return this->WFServerTask::message_out(); errno = EBADMSG; return NULL; } template void RPCServerTask::handle(int state, int error) { if (state != WFT_STATE_TOREPLY) return WFServerTask::handle(state, error); this->state = WFT_STATE_TOREPLY; this->target = this->get_target(); RPCSeries *series = new RPCSeries(this); series->start(); } template inline void RPCClientTask::set_data_type(RPCDataType type) { this->req.set_data_type(type); } template inline void RPCClientTask::set_compress_type(RPCCompressType type) { this->req.set_compress_type(type); } template inline void RPCClientTask::set_attachment_nocopy(const char *attachment, size_t len) { this->req.set_attachment_nocopy(attachment, len); } template int RPCClientTask::set_uri_fragment(const std::string& fragment) { char *str = strdup(fragment.c_str()); if (str) { free(this->uri_.fragment); this->uri_.fragment = str; return 0; } return -1; } template inline void RPCClientTask::set_retry_max(int retry_max) { this->retry_max_ = retry_max; } template inline int RPCClientTask::serialize_input(const ProtobufIDLMessage *in) { return __serialize_input(in); } template inline int RPCClientTask::serialize_input(const ThriftIDLMessage *in) { return __serialize_input(in); } template template inline int RPCClientTask::__serialize_input(const IDL *in) { if (init_failed_ == false) { int status_code = this->req.serialize(in); this->resp.set_status_code(status_code); if (status_code == RPCStatusOK) return 0; } return -1; } template inline RPCClientTask::RPCClientTask( const std::string& service_name, const std::string& method_name, const RPCTaskParams *params, std::list&& modules, user_done_t&& user_done): WFComplexClientTask(0, nullptr), user_done_(std::move(user_done)), init_failed_(false), modules_(std::move(modules)) { if (user_done_) this->set_callback(std::bind(&RPCClientTask::rpc_callback, this, std::placeholders::_1)); this->set_send_timeout(params->send_timeout); this->set_receive_timeout(params->receive_timeout); watch_timeout_ = params->watch_timeout; this->set_keep_alive(params->keep_alive_timeout); this->set_retry_max(params->retry_max); if (params->compress_type != RPCCompressNone) this->req.set_compress_type(params->compress_type); if (params->data_type != RPCDataUndefined) this->req.set_data_type(params->data_type); this->req.set_service_name(service_name); this->req.set_method_name(method_name); } template void RPCClientTask::init_failed() { init_failed_ = true; } template bool RPCClientTask::check_request() { int status_code = this->resp.get_status_code(); return status_code == RPCStatusOK || status_code == RPCStatusUndefined; } template CommMessageOut *RPCClientTask::message_out() { this->req.set_seqid(this->get_task_seq()); int status_code = this->req.compress(); void *series_data = series_of(this)->get_specific(SRPC_MODULE_DATA); RPCModuleData *data = (RPCModuleData *)series_data; if (data) this->set_module_data(*data); data = this->mutable_module_data(); for (auto *module : modules_) module->client_task_begin(this, *data); this->req.set_meta_module_data(*data); if (status_code == RPCStatusOK) { if (!this->req.serialize_meta()) status_code = RPCStatusMetaError; } if (status_code == RPCStatusOK) return this->WFClientTask::message_out(); this->disable_retry(); this->resp.set_status_code(status_code); errno = EBADMSG; return NULL; } template bool RPCClientTask::finish_once() { int status_code = this->resp.get_status_code(); if (this->state == WFT_STATE_SUCCESS && (status_code == RPCStatusOK || status_code == RPCStatusUndefined)) { if (this->resp.deserialize_meta() == false) this->resp.set_status_code(RPCStatusMetaError); } return true; } template void RPCClientTask::rpc_callback(WFNetworkTask *task) { RPCWorker worker(new RPCContextImpl(this, &module_data_), &this->req, &this->resp); int status_code = this->resp.get_status_code(); if (status_code != RPCStatusOK && status_code != RPCStatusUndefined) { this->state = WFT_STATE_TASK_ERROR; this->error = status_code; } else if (this->state == WFT_STATE_SUCCESS) { status_code = this->resp.decompress(); if (status_code == RPCStatusOK) { this->resp.set_status_code(RPCStatusOK); status_code = user_done_(status_code, worker); } if (status_code != RPCStatusOK) { this->state = WFT_STATE_TASK_ERROR; this->error = status_code; } } if (this->state == WFT_STATE_TASK_ERROR) { switch (this->error) { case WFT_ERR_URI_PARSE_FAILED: case WFT_ERR_URI_SCHEME_INVALID: case WFT_ERR_URI_PORT_INVALID: status_code = RPCStatusURIInvalid; break; case WFT_ERR_UPSTREAM_UNAVAILABLE: status_code = RPCStatusUpstreamFailed; break; default: break; } } else if (this->state != WFT_STATE_SUCCESS) { switch (this->state) { case WFT_STATE_SYS_ERROR: status_code = RPCStatusSystemError; break; case WFT_STATE_SSL_ERROR: status_code = RPCStatusSSLError; break; case WFT_STATE_DNS_ERROR: status_code = RPCStatusDNSError; break; case WFT_STATE_ABORTED: status_code = RPCStatusProcessTerminated; break; default: status_code = RPCStatusUndefined; break; } } this->resp.set_status_code(status_code); this->resp.set_error(this->error); if (!modules_.empty()) { void *series_data; RPCModuleData *resp_data = this->mutable_module_data(); if (resp_data->empty()) // get series module data failed previously { series_data = series_of(this)->get_specific(SRPC_MODULE_DATA); if (series_data) resp_data = (RPCModuleData *)series_data; } // else // this->resp.get_meta_module_data(resp_data); for (auto *module : modules_) module->client_task_end(this, *resp_data); } if (status_code != RPCStatusOK) user_done_(status_code, worker); } template bool RPCClientTask::get_remote(std::string& ip, unsigned short *port) const { struct sockaddr_storage addr; socklen_t addrlen = sizeof (addr); char buf[INET6_ADDRSTRLEN + 1] = { 0 }; if (this->get_peer_addr((struct sockaddr *)&addr, &addrlen) == 0 && RPCCommon::addr_to_string((struct sockaddr *)&addr, buf, INET6_ADDRSTRLEN + 1, port) == true) { ip = buf; return true; } return false; } template bool RPCServerTask::get_remote(std::string& ip, unsigned short *port) const { struct sockaddr_storage addr; socklen_t addrlen = sizeof (addr); char buf[INET6_ADDRSTRLEN + 1] = { 0 }; if (this->get_peer_addr((struct sockaddr *)&addr, &addrlen) == 0 && RPCCommon::addr_to_string((struct sockaddr *)&addr, buf, INET6_ADDRSTRLEN + 1, port) == true) { ip = buf; return true; } return false; } template void RPCClientTask::log(const RPCLogVector& fields) { std::string key; std::string value; RPCCommon::log_format(key, value, fields); module_data_.insert(std::make_pair(std::move(key), std::move(value))); } template void RPCClientTask::add_baggage(const std::string& key, const std::string& value) { module_data_[key] = value; } template bool RPCClientTask::get_baggage(const std::string& key, std::string& value) { const auto it = module_data_.find(key); if (it != module_data_.cend()) { value = it->second; return true; } return false; } template inline void RPCClientTask::set_json_add_whitespace(bool on) { this->req.set_json_add_whitespace(on); } template inline void RPCClientTask::set_json_always_print_enums_as_ints(bool on) { this->req.set_json_enums_as_ints(on); } template inline void RPCClientTask::set_json_preserve_proto_field_names(bool on) { this->req.set_json_preserve_names(on); } template inline void RPCClientTask::set_json_always_print_primitive_fields(bool on) { this->req.set_json_print_primitive(on); } template inline bool RPCClientTask::set_http_header(const std::string& name, const std::string& value) { return this->req.set_http_header(name, value); } template inline bool RPCClientTask::add_http_header(const std::string& name, const std::string& value) { return this->req.add_http_header(name, value); } } // namespace srpc srpc-0.10.1/src/rpc_types.h000066400000000000000000000066451454502251400155250ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_TYPE_H__ #define __RPC_TYPE_H__ #include "rpc_basic.h" #include "rpc_message.h" #include "rpc_message_srpc.h" #include "rpc_message_thrift.h" #include "rpc_message_brpc.h" #include "rpc_message_trpc.h" namespace srpc { struct RPCTYPESRPC { using REQ = SRPCStdRequest; using RESP = SRPCStdResponse; static constexpr RPCDataType default_data_type = RPCDataProtobuf; static inline void server_reply_init(const REQ *req, RESP *resp) { resp->set_data_type(req->get_data_type()); } }; struct RPCTYPESRPCHttp { using REQ = SRPCHttpRequest; using RESP = SRPCHttpResponse; static constexpr RPCDataType default_data_type = RPCDataJson; static inline void server_reply_init(const REQ *req, RESP *resp) { resp->set_data_type(req->get_data_type()); } }; struct RPCTYPEGRPC { //using REQ = GRPCHttp2Request; //using RESP = GRPCHttp2Response; static constexpr RPCDataType default_data_type = RPCDataProtobuf; }; struct RPCTYPEBRPC { using REQ = BRPCStdRequest; using RESP = BRPCStdResponse; static constexpr RPCDataType default_data_type = RPCDataProtobuf; static inline void server_reply_init(const REQ *req, RESP *resp) { resp->set_correlation_id(req->get_correlation_id()); } }; struct RPCTYPEThrift { using REQ = ThriftStdRequest; using RESP = ThriftStdResponse; static constexpr RPCDataType default_data_type = RPCDataThrift; static inline void server_reply_init(const REQ *req, RESP *resp) { resp->set_data_type(req->get_data_type()); auto *req_meta = req->get_meta(); auto *resp_meta = resp->get_meta(); resp_meta->is_strict = req_meta->is_strict; resp_meta->seqid = req_meta->seqid; resp_meta->method_name = req_meta->method_name; } }; struct RPCTYPEThriftHttp { using REQ = ThriftHttpRequest; using RESP = ThriftHttpResponse; static constexpr RPCDataType default_data_type = RPCDataThrift; static inline void server_reply_init(const REQ *req, RESP *resp) { resp->set_data_type(req->get_data_type()); auto *req_meta = req->get_meta(); auto *resp_meta = resp->get_meta(); resp_meta->is_strict = req_meta->is_strict; resp_meta->seqid = req_meta->seqid; resp_meta->method_name = req_meta->method_name; } }; struct RPCTYPETRPC { using REQ = TRPCStdRequest; using RESP = TRPCStdResponse; static constexpr RPCDataType default_data_type = RPCDataProtobuf; static inline void server_reply_init(const REQ *req, RESP *resp) { const_cast(req)->trim_method_prefix(); resp->set_request_id(req->get_request_id()); } }; struct RPCTYPETRPCHttp { using REQ = TRPCHttpRequest; using RESP = TRPCHttpResponse; static constexpr RPCDataType default_data_type = RPCDataJson; static inline void server_reply_init(const REQ *req, RESP *resp) { resp->set_data_type(req->get_data_type()); const_cast(req)->trim_method_prefix(); resp->set_request_id(req->get_request_id()); } }; } // end namespace srpc #endif srpc-0.10.1/src/rpc_zero_copy_stream.h000066400000000000000000000046731454502251400177440ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_ZERO_COPY_STREAM_H__ #define __RPC_ZERO_COPY_STREAM_H__ #include namespace srpc { class RPCOutputStream : public google::protobuf::io::ZeroCopyOutputStream { public: RPCOutputStream(RPCBuffer *buf, size_t size); bool Next(void **data, int *size) override; void BackUp(int count) override; int64_t ByteCount() const override; private: RPCBuffer *buf; size_t size; }; class RPCInputStream : public google::protobuf::io::ZeroCopyInputStream { public: RPCInputStream(RPCBuffer *buf); bool Next(const void **data, int *size) override; void BackUp(int count) override; bool Skip(int count) override; int64_t ByteCount() const override; private: RPCBuffer *buf; }; inline RPCOutputStream::RPCOutputStream(RPCBuffer *buf, size_t size) { this->buf = buf; this->size = size; } inline bool RPCOutputStream::Next(void **data, int *size) { size_t tmp; if (this->size > 0) { tmp = this->size; if (this->buf->acquire(data, &tmp)) { this->size -= tmp; *size = (int)tmp; return true; } } else { tmp = this->buf->acquire(data); if (tmp > 0) { *size = (int)tmp; return true; } } return false; } inline void RPCOutputStream::BackUp(int count) { this->buf->backup(count); } inline int64_t RPCOutputStream::ByteCount() const { return this->buf->size(); } inline RPCInputStream::RPCInputStream(RPCBuffer *buf) { this->buf = buf; } inline bool RPCInputStream::Next(const void **data, int *size) { size_t len = this->buf->fetch(data); if (len == 0) return false; *size = (int)len; return true; } inline bool RPCInputStream::Skip(int count) { return this->buf->seek(count) == count ? true : false; } inline void RPCInputStream::BackUp(int count) { this->buf->seek(0 - count); } inline int64_t RPCInputStream::ByteCount() const { return (int64_t)this->buf->size(); } } // namespace srpc #endif srpc-0.10.1/src/thrift/000077500000000000000000000000001454502251400146315ustar00rootroot00000000000000srpc-0.10.1/src/thrift/CMakeLists.txt000066400000000000000000000002251454502251400173700ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(thrift) set(SRC rpc_thrift_buffer.cc rpc_thrift_idl.cc ) add_library(${PROJECT_NAME} OBJECT ${SRC}) srpc-0.10.1/src/thrift/rpc_thrift_buffer.cc000066400000000000000000000134531454502251400206430ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "rpc_thrift_buffer.h" #include "rpc_basic.h" namespace srpc { bool ThriftBuffer::readI08(int8_t& val) { return this->buffer->read((char *)&val, 1); } bool ThriftBuffer::readI16(int16_t& val) { if (!this->buffer->read((char *)&val, 2)) return false; val = ntohs(val); return true; } bool ThriftBuffer::readI32(int32_t& val) { if (!this->buffer->read((char *)&val, 4)) return false; val = ntohl(val); return true; } bool ThriftBuffer::readI64(int64_t& val) { if (!this->buffer->read((char *)&val, 8)) return false; val = ntohll(val); return true; } bool ThriftBuffer::readU64(uint64_t& val) { if (!this->buffer->read((char *)&val, 8)) return false; val = ntohll(val); return true; } bool ThriftBuffer::readFieldBegin(int8_t& field_type, int16_t& field_id) { if (!readI08(field_type)) return false; if (field_type == TDT_STOP) field_id = 0; else if (!readI16(field_id)) return false; return true; } bool ThriftBuffer::readString(std::string& str) { int32_t slen; if (!readI32(slen) || slen < 0) return false; if (!readStringBody(str, slen)) return false; return true; } bool ThriftBuffer::readStringBody(std::string& str, int32_t slen) { if (slen < 0) return false; str.resize(slen); return this->buffer->read(const_cast(str.c_str()), slen); } bool ThriftBuffer::writeFieldStop() { return writeI08((int8_t)TDT_STOP); } bool ThriftBuffer::writeI08(int8_t val) { return this->buffer->write((char *)&val, 1); } bool ThriftBuffer::writeI16(int16_t val) { int16_t x = htons(val); return this->buffer->write((char *)&x, 2); } bool ThriftBuffer::writeI32(int32_t val) { int32_t x = htonl(val); return this->buffer->write((char *)&x, 4); } bool ThriftBuffer::writeI64(int64_t val) { int64_t x = htonll(val); return this->buffer->write((char *)&x, 8); } bool ThriftBuffer::writeU64(uint64_t val) { uint64_t x = htonll(val); return this->buffer->write((char *)&x, 8); } bool ThriftBuffer::writeFieldBegin(int8_t field_type, int16_t field_id) { if (!writeI08(field_type)) return false; return writeI16(field_id); } bool ThriftBuffer::writeString(const std::string& str) { int32_t slen = (int32_t)str.size(); if (!writeI32(slen)) return false; return writeStringBody(str); } bool ThriftBuffer::writeStringBody(const std::string& str) { return this->buffer->write(str.c_str(), str.size()); } bool ThriftMeta::writeI08(int8_t val) { this->writebuf.append(1, (char)val); return true; } bool ThriftMeta::writeI32(int32_t val) { int32_t x = htonl(val); this->writebuf.append((const char *)&x, 4); return true; } bool ThriftMeta::writeString(const std::string& str) { int32_t slen = (int32_t)str.size(); writeI32(slen); if (slen > 0) this->writebuf.append(str); return true; } bool ThriftBuffer::readMessageBegin() { int32_t header; if (!readI32(header)) return false; if (header < 0) { int32_t version = header & THRIFT_VERSION_MASK; if (version != THRIFT_VERSION_1) return false;//bad version if (!readString(meta.method_name)) return false; if (!readI32(meta.seqid)) return false; meta.message_type = header & 0xFF; meta.is_strict = true; } else { if (!readStringBody(meta.method_name, header)) return false; if (!readI08(meta.message_type)) return false; if (!readI32(meta.seqid)) return false; meta.is_strict = false; } return true; } bool ThriftBuffer::writeMessageBegin() { if (meta.is_strict) { int32_t version = (THRIFT_VERSION_1) | ((int32_t)meta.message_type); if (!meta.writeI32(version)) return false; if (!meta.writeString(meta.method_name)) return false; if (!meta.writeI32(meta.seqid)) return false; } else { if (!meta.writeString(meta.method_name)) return false; if (!meta.writeI08(meta.message_type)) return false; if (!meta.writeI32(meta.seqid)) return false; } return true; } bool ThriftBuffer::skip(int8_t field_type) { switch (field_type) { case TDT_I08: case TDT_BOOL: return this->buffer->seek(1) == 1; case TDT_I16: return this->buffer->seek(2) == 2; case TDT_I32: return this->buffer->seek(4) == 4; case TDT_I64: case TDT_U64: case TDT_DOUBLE: return this->buffer->seek(8) == 8; case TDT_STRING: case TDT_UTF8: case TDT_UTF16: { int32_t slen; if (!readI32(slen) || slen < 0) return false; return this->buffer->seek(slen) == slen; } case TDT_STRUCT: { int8_t field_type; int16_t field_id; while (true) { if (!readFieldBegin(field_type, field_id)) return false; if (field_type == TDT_STOP) break; if (!skip(field_type)) return false; } break; } case TDT_MAP: { int8_t key_type; int8_t val_type; int32_t count; if (!readI08(key_type)) return false; if (!readI08(val_type)) return false; if (!readI32(count)) return false; for (int32_t i = 0; i < count; i++) { if (!skip(key_type)) return false; if (!skip(val_type)) return false; } break; } case TDT_LIST: case TDT_SET: { int8_t val_type; int32_t count; if (!readI08(val_type)) return false; if (!readI32(count)) return false; for (int32_t i = 0; i < count; i++) { if (!skip(val_type)) return false; } break; } default://return false?? break; } return true; } } // end namespace srpc srpc-0.10.1/src/thrift/rpc_thrift_buffer.h000066400000000000000000000051771454502251400205110ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_THRIFT_BUFFER_H__ #define __RPC_THRIFT_BUFFER_H__ #include #include #include #include "rpc_thrift_enum.h" #include "rpc_buffer.h" namespace srpc { static constexpr int32_t THRIFT_VERSION_MASK = ((int32_t)0xffff0000); static constexpr int32_t THRIFT_VERSION_1 = ((int32_t)0x80010000); enum { //THRIFT_PARSE_INIT = 0, THRIFT_GET_FRAME_SIZE = 1, THRIFT_GET_DATA, THRIFT_PARSE_END }; class ThriftMeta { public: std::string writebuf; std::string method_name; int seqid = 0; int8_t message_type = TMT_CALL; bool is_strict = true; public: ThriftMeta() = default; ThriftMeta(const ThriftMeta&) = delete; ThriftMeta& operator= (const ThriftMeta&) = delete; ThriftMeta(ThriftMeta&& move) = delete; ThriftMeta& operator= (ThriftMeta &&move) = delete; bool writeI08(int8_t val); bool writeI32(int32_t val); bool writeString(const std::string& str); }; class ThriftBuffer { public: ThriftMeta meta; RPCBuffer *buffer; size_t readbuf_size = 0; size_t framesize_read_byte = 0; int32_t framesize = 0; int status = THRIFT_GET_FRAME_SIZE; public: ThriftBuffer(RPCBuffer *buf): buffer(buf) { } ThriftBuffer(const ThriftBuffer&) = delete; ThriftBuffer& operator= (const ThriftBuffer&) = delete; ThriftBuffer(ThriftBuffer&& move) = delete; ThriftBuffer& operator= (ThriftBuffer &&move) = delete; public: bool readMessageBegin(); bool readFieldBegin(int8_t& field_type, int16_t& field_id); bool readI08(int8_t& val); bool readI16(int16_t& val); bool readI32(int32_t& val); bool readI64(int64_t& val); bool readU64(uint64_t& val); bool readString(std::string& str); bool readStringBody(std::string& str, int32_t slen); bool skip(int8_t field_type); bool writeMessageBegin(); bool writeFieldBegin(int8_t field_type, int16_t field_id); bool writeFieldStop(); bool writeI08(int8_t val); bool writeI16(int16_t val); bool writeI32(int32_t val); bool writeI64(int64_t val); bool writeU64(uint64_t val); bool writeString(const std::string& str); bool writeStringBody(const std::string& str); }; } // end namespace srpc #endif srpc-0.10.1/src/thrift/rpc_thrift_enum.h000066400000000000000000000031341454502251400201730ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_THRIFT_ENUM_H__ #define __RPC_THRIFT_ENUM_H__ namespace srpc { static constexpr int THRIFT_STRUCT_FIELD_REQUIRED = 0; static constexpr int THRIFT_STRUCT_FIELD_OPTIONAL = 1; static constexpr int THRIFT_STRUCT_FIELD_DEFAULT = 2; enum ThriftMessageType { TMT_CALL = 1, TMT_REPLY = 2, TMT_EXCEPTION = 3, TMT_ONEWAY = 4 }; enum ThriftDataType { TDT_STOP = 0, TDT_VOID = 1, TDT_BOOL = 2, TDT_BYTE = 3, TDT_I08 = 3, TDT_I16 = 6, TDT_I32 = 8, TDT_U64 = 9, TDT_I64 = 10, TDT_DOUBLE = 4, TDT_STRING = 11, TDT_UTF7 = 11, TDT_STRUCT = 12, TDT_MAP = 13, TDT_SET = 14, TDT_LIST = 15, TDT_UTF8 = 16, TDT_UTF16 = 17 }; enum ThriftExceptionType { TET_UNKNOWN = 0, TET_UNKNOWN_METHOD = 1, TET_INVALID_MESSAGE_TYPE = 2, TET_WRONG_METHOD_NAME = 3, TET_BAD_SEQUENCE_ID = 4, TET_MISSING_RESULT = 5, TET_INTERNAL_ERROR = 6, TET_PROTOCOL_ERROR = 7, TET_INVALID_TRANSFORM = 8, TET_INVALID_PROTOCOL = 9, TET_UNSUPPORTED_CLIENT_TYPE = 10 }; } // end namespace srpc #endif srpc-0.10.1/src/thrift/rpc_thrift_idl.cc000066400000000000000000000253341454502251400201430ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include "rpc_thrift_idl.h" namespace srpc { static inline char __hex_ch(int x) { if (x >= 0 && x < 10) return x + '0'; else if (x >= 10 && x < 16) return x - 10 + 'A'; return '0'; } static inline int __hex_int(unsigned char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; else if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; else if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; return -1; } bool ThriftJsonUtil::skip_whitespace(ThriftBuffer *buffer) { const void *buf; size_t buflen; while (buflen = buffer->buffer->peek(&buf), buf && buflen > 0) { char *base = (char *)buf; for (size_t i = 0; i < buflen; i++) { if (!isspace(base[i])) { buffer->buffer->seek(i); return true; } } buffer->buffer->seek(buflen); } return false; } bool ThriftJsonUtil::peek_first_meaningful_char(ThriftBuffer *buffer, char& ch) { if (!skip_whitespace(buffer)) return false; const void *buf; size_t buflen = buffer->buffer->peek(&buf); if (buf && buflen > 0) { ch = *((char *)buf); return true; } return false; } bool ThriftJsonUtil::skip_character(ThriftBuffer *buffer, char ch) { char mchar; if (!peek_first_meaningful_char(buffer, mchar)) return false; buffer->buffer->seek(1); return mchar == ch; } bool ThriftJsonUtil::skip_simple_string(ThriftBuffer *buffer, const std::string& str) { if (!skip_whitespace(buffer)) return false; size_t cur = 0; const void *buf; size_t buflen; while (cur < str.size() && (buflen = buffer->buffer->peek(&buf), buf && buflen > 0)) { size_t i = 0; char *base = (char *)buf; while (cur < str.size() && i < buflen) { if (base[i++] != str[cur++]) return false; } buffer->buffer->seek(i); } return cur == str.size(); } bool ThriftJsonUtil::read_int64(ThriftBuffer *buffer, int64_t& intv) { const void *buf; size_t buflen; char ch; bool is_negative = false; bool first_digit = false; if (!peek_first_meaningful_char(buffer, ch)) return false; if (ch == '-') { is_negative = true; buffer->buffer->seek(1); } intv = 0; while (buflen = buffer->buffer->peek(&buf), buf && buflen > 0) { size_t i = 0; char *base = (char *)buf; for (i = 0; i < buflen; i++) { if (!first_digit) { if (isdigit(base[i])) first_digit = true; else return false; } if (!isdigit(base[i])) { buffer->buffer->seek(i); if (is_negative) intv *= -1; return true; } intv *= 10; intv += base[i] - '0'; } buffer->buffer->seek(buflen); } if (!first_digit) return false; if (is_negative) intv *= -1; return true; } bool ThriftJsonUtil::read_uint64(ThriftBuffer *buffer, uint64_t& intv) { const void *buf; size_t buflen; char ch; bool first_digit = false; if (!peek_first_meaningful_char(buffer, ch)) return false; if (!isdigit(ch)) return false; intv = 0; while (buflen = buffer->buffer->peek(&buf), buf && buflen > 0) { size_t i = 0; char *base = (char *)buf; for (i = 0; i < buflen; i++) { if (!first_digit) { if (isdigit(base[i])) first_digit = true; else return false; } if (!isdigit(base[i])) { buffer->buffer->seek(i); return true; } intv *= 10; intv += base[i] - '0'; } buffer->buffer->seek(buflen); } if (!first_digit) return false; return true; } bool ThriftJsonUtil::read_double(ThriftBuffer *buffer, double& d) { if (!skip_whitespace(buffer)) return false; const void *buf; size_t buflen; std::string str; while (buflen = buffer->buffer->peek(&buf), buf && buflen > 0) { size_t i = 0; char *base = (char *)buf; for (i = 0; i < buflen; i++) { if (!isdigit(base[i]) && base[i] != '.' && base[i] !='+' && base[i] !='-' && base[i] !='e' && base[i] !='E') break; } str.append(base, base + i); if (i < buflen) break; buffer->buffer->seek(buflen); } if (str.empty()) return false; char *end; int errno_bak = errno; d = strtod(str.c_str(), &end); if (errno == ERANGE) errno = errno_bak; if (end == str.c_str() // strtod error || end < str.c_str() // should never happend || end > str.c_str() + str.size()) // should never happend return false; buffer->buffer->seek(str.c_str() + str.size() - end); return true; } static constexpr int THRIFT_JSON_STATE_STRING_STATE_NORMAL = 0; static constexpr int THRIFT_JSON_STATE_STRING_STATE_Q1 = 1; static constexpr int THRIFT_JSON_STATE_STRING_STATE_U4 = 2; static constexpr int THRIFT_JSON_STATE_STRING_STATE_U3 = 3; static constexpr int THRIFT_JSON_STATE_STRING_STATE_U2 = 4; static constexpr int THRIFT_JSON_STATE_STRING_STATE_U1 = 5; bool ThriftJsonUtil::read_string(ThriftBuffer *buffer, std::string *str) { int state = THRIFT_JSON_STATE_STRING_STATE_NORMAL; const void *buf; size_t buflen; int n; if (!skip_character(buffer, '\"')) return false; if (str) str->clear(); while (buflen = buffer->buffer->peek(&buf), buf && buflen > 0) { size_t i = 0; char *base = (char *)buf; while (i < buflen) { unsigned char ch = base[i++]; if (state == THRIFT_JSON_STATE_STRING_STATE_NORMAL) { if (ch == '\"') { if (i > 0) { if (str) str->append(base, base + i - 1); } buffer->buffer->seek(i); return true; } else if (ch == '\\') state = THRIFT_JSON_STATE_STRING_STATE_Q1; else if (ch >= 0 && ch < 32) return false; } else if (state == THRIFT_JSON_STATE_STRING_STATE_Q1) { switch (ch) { case '\"': if (str) *str += (char)0x22; break; case '\\': if (str) *str += (char)0x5C; break; case '/': if (str) *str += (char)0x2F; break; case 'b': if (str) *str += (char)0x08; break; case 'f': if (str) *str += (char)0x0C; break; case 'n': if (str) *str += (char)0x0A; break; case 'r': if (str) *str += (char)0x0D; break; case 't': if (str) *str += (char)0x09; break; case 'u': n = 0; state = THRIFT_JSON_STATE_STRING_STATE_U4; break; default: return false; } state = THRIFT_JSON_STATE_STRING_STATE_NORMAL; } else if (state == THRIFT_JSON_STATE_STRING_STATE_U4 || state == THRIFT_JSON_STATE_STRING_STATE_U3 || state == THRIFT_JSON_STATE_STRING_STATE_U2 || state == THRIFT_JSON_STATE_STRING_STATE_U1) { int x = __hex_int(ch); if (x < 0) return false; n = n * 16 + x; if (state == THRIFT_JSON_STATE_STRING_STATE_U1) { if (n < 0 || n >= 65536) return false; if (n < 128) { if (str) *str += (char)n; } else if (n < 2048) { if (str) { *str += (char)(192 | (n >> 6)); *str += (char)(128 | (n & 0x3F)); } } else { if (str) { *str += (char)(224 | (n >> 12)); *str += (char)(128 | (n & 0XFC0)); *str += (char)(128 | (n & 0x3F)); } } state = THRIFT_JSON_STATE_STRING_STATE_NORMAL; } else if (state == THRIFT_JSON_STATE_STRING_STATE_U2) state = THRIFT_JSON_STATE_STRING_STATE_U1; else if (state == THRIFT_JSON_STATE_STRING_STATE_U3) state = THRIFT_JSON_STATE_STRING_STATE_U2; else if (state == THRIFT_JSON_STATE_STRING_STATE_U4) state = THRIFT_JSON_STATE_STRING_STATE_U3; } else return false; } if (str) str->append(base, base + buflen); buffer->buffer->seek(buflen); } return false; } bool ThriftJsonUtil::skip_one_element(ThriftBuffer *buffer) { char ch; if (!peek_first_meaningful_char(buffer, ch)) return false; if (ch == '{') { while (ch != '}') { buffer->buffer->seek(1); if (!read_string(buffer, nullptr)) return false; if (!skip_character(buffer, ':')) return false; if (!skip_one_element(buffer)) return false; if (!peek_first_meaningful_char(buffer, ch)) return false; if (ch != ',' && ch == '}') return false; } return true; } else if (ch == '[') { while (ch != ']') { buffer->buffer->seek(1); if (!skip_one_element(buffer)) return false; if (!peek_first_meaningful_char(buffer, ch)) return false; if (ch != ',' && ch == ']') return false; } return true; } else if (ch == '\"') return read_string(buffer, nullptr); else if (ch =='t') return skip_simple_string(buffer, "true"); else if (ch == 'f') return skip_simple_string(buffer, "false"); else if (ch == 'n') return skip_simple_string(buffer, "null"); else if (ch =='-' || ch == '.' || ch == 'e' || ch == 'E' || isdigit(ch)) { double d; return read_double(buffer, d); } return false; } bool ThriftJsonUtil::escape_string(const std::string& str, std::string& escape_str) { size_t slen = str.size(); escape_str = '\"'; for (size_t i = 0; i < slen; i++) { unsigned char ch = str[i]; if (ch > 127) { int n = 0; if ((ch >> 5) == 6) { if (i + 1 >= slen) return false; n = ch & 0x1F; ch = str[++i]; if ((ch >> 6) != 2) return false; n += ch & 0x3F; if (n < 0x80) return false; } else if ((ch >> 4) == 14) { if (i + 2 >= slen) return false; n = ch & 0xF; ch = str[++i]; if ((ch >> 6) != 2) return false; n += ch & 0x3F; ch = str[++i]; if ((ch >> 6) != 2) return false; n += ch & 0x3F; if (n < 0x800) return false; } else return false; escape_str += "\\u"; escape_str += __hex_ch(n / 4096); n /= 4096; escape_str += __hex_ch(n / 256); n /= 256; escape_str += __hex_ch(n / 16); n /= 16; escape_str += __hex_ch(n); } else if (ch == 0x22) escape_str += "\\\""; else if (ch == 0x5C) escape_str += "\\\\"; else if (ch == 0x2F) escape_str += "\\/"; else if (ch == 0x08) escape_str += "\\b"; else if (ch == 0x0C) escape_str += "\\f"; else if (ch == 0x0A) escape_str += "\\n"; else if (ch == 0x0D) escape_str += "\\r"; else if (ch == 0x09) escape_str += "\\t"; else if (ch >= 0 && ch < 32) { escape_str += "\\u00"; escape_str += __hex_ch(ch / 16); ch /= 16; escape_str += __hex_ch(ch); } else escape_str += ch; } escape_str += '\"'; return true; } } // end namespace srpc srpc-0.10.1/src/thrift/rpc_thrift_idl.h000066400000000000000000000043271454502251400200040ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_THRIFT_IDL_H__ #define __RPC_THRIFT_IDL_H__ #include #include #include #include #include #include "rpc_thrift_buffer.h" namespace srpc { class ThriftDescriptor { public: int8_t data_type; protected: using ReaderFunctionPTR = bool (*)(ThriftBuffer *, void *); using WriterFunctionPTR = bool (*)(const void *, ThriftBuffer *); virtual ~ThriftDescriptor() { } ThriftDescriptor(): reader(nullptr), writer(nullptr), json_reader(nullptr), json_writer(nullptr) {} ThriftDescriptor(const ReaderFunctionPTR r, const WriterFunctionPTR w, const ReaderFunctionPTR jr, const WriterFunctionPTR jw): reader(r), writer(w), json_reader(jr), json_writer(jw) {} public: const ReaderFunctionPTR reader; const WriterFunctionPTR writer; const ReaderFunctionPTR json_reader; const WriterFunctionPTR json_writer; }; struct struct_element { const ThriftDescriptor *desc; const char *name; ptrdiff_t isset_offset; ptrdiff_t data_offset; int16_t field_id; int8_t required_state; }; template class ThriftElementsImpl { public: static const std::list *get_elements_instance() { static const ThriftElementsImpl kInstance; return &kInstance.elements; } private: ThriftElementsImpl() { T::StaticElementsImpl(&this->elements); } std::list elements; }; class ThriftIDLMessage { public: const ThriftDescriptor *descriptor = nullptr; const std::list *elements = nullptr; std::string debug_string() const { return ""; } virtual ~ThriftIDLMessage() { } }; } // end namespace srpc #include "rpc_thrift_idl.inl" #endif srpc-0.10.1/src/thrift/rpc_thrift_idl.inl000066400000000000000000000627331454502251400203440ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ namespace srpc { class ThriftJsonUtil { public: static bool skip_whitespace(ThriftBuffer *buffer); static bool skip_character(ThriftBuffer *buffer, char ch); static bool peek_first_meaningful_char(ThriftBuffer *buffer, char& ch); static bool skip_simple_string(ThriftBuffer *buffer, const std::string& str); static bool read_int64(ThriftBuffer *buffer, int64_t& intv); static bool read_uint64(ThriftBuffer *buffer, uint64_t& intv); static bool read_double(ThriftBuffer *buffer, double& d); static bool read_string(ThriftBuffer *buffer, std::string *str); static bool skip_one_element(ThriftBuffer *buffer); static bool escape_string(const std::string& str, std::string& escape_str); }; template class ThriftDescriptorImpl : public ThriftDescriptor { public: static const ThriftDescriptor *get_instance() { static const ThriftDescriptorImpl kInstance; return &kInstance; } public: static bool read(ThriftBuffer *buffer, void *data); static bool write(const void *data, ThriftBuffer *buffer); static bool read_json(ThriftBuffer *buffer, void *data); static bool write_json(const void *data, ThriftBuffer *buffer); private: ThriftDescriptorImpl(): ThriftDescriptor(ThriftDescriptorImpl::read, ThriftDescriptorImpl::write, ThriftDescriptorImpl::read_json, ThriftDescriptorImpl::write_json) { this->data_type = DATA; //this->reader = ThriftDescriptorImpl::read; //this->writer = ThriftDescriptorImpl::write; //this->json_reader = ThriftDescriptorImpl::read_json; //this->json_writer = ThriftDescriptorImpl::write_json; } }; template class ThriftDescriptorImpl : public ThriftDescriptor { public: static const ThriftDescriptor *get_instance() { static const ThriftDescriptorImpl kInstance; return &kInstance; } static bool read(ThriftBuffer *buffer, void *data) { T *list = static_cast(data); const auto *val_desc = VALIMPL::get_instance(); int8_t field_type; int32_t count; if (!buffer->readI08(field_type)) return false; if (!buffer->readI32(count)) return false; list->resize(count); for (auto& ele : *list) { if (!val_desc->reader(buffer, &ele)) return false; } return true; } static bool write(const void *data, ThriftBuffer *buffer) { const T *list = static_cast(data); const auto *val_desc = VALIMPL::get_instance(); if (!buffer->writeI08(val_desc->data_type)) return false; if (!buffer->writeI32(list->size())) return false; for (const auto& ele : *list) { if (!val_desc->writer(&ele, buffer)) return false; } return true; } static bool read_json(ThriftBuffer *buffer, void *data) { T *list = static_cast(data); const auto *val_desc = VALIMPL::get_instance(); bool is_first = true; typename T::value_type ele; char ch; if (!ThriftJsonUtil::skip_character(buffer, '[')) return false; while (ThriftJsonUtil::peek_first_meaningful_char(buffer, ch)) { if (ch == ']') { buffer->buffer->seek(1); return true; } if (is_first) is_first = false; else { buffer->buffer->seek(1); if (ch != ',') break; } if (!val_desc->json_reader(buffer, &ele)) break; list->emplace_back(std::move(ele)); } return false; } static bool write_json(const void *data, ThriftBuffer *buffer) { const T *list = static_cast(data); const auto *val_desc = VALIMPL::get_instance(); bool is_first = true; if (!buffer->writeI08('[')) return false; for (const auto& ele : *list) { if (is_first) is_first = false; else if (!buffer->writeI08(',')) return false; if (!val_desc->json_writer(&ele, buffer)) return false; } return buffer->writeI08(']'); } private: ThriftDescriptorImpl(): ThriftDescriptor(ThriftDescriptorImpl::read, ThriftDescriptorImpl::write, ThriftDescriptorImpl::read_json, ThriftDescriptorImpl::write_json) { this->data_type = TDT_LIST; //this->reader = ThriftDescriptorImpl::read; //this->writer = ThriftDescriptorImpl::write; //this->json_reader = ThriftDescriptorImpl::read_json; //this->json_writer = ThriftDescriptorImpl::write_json; } }; template class ThriftDescriptorImpl : public ThriftDescriptor { public: static const ThriftDescriptor *get_instance() { static const ThriftDescriptorImpl kInstance; return &kInstance; } static bool read(ThriftBuffer *buffer, void *data) { T *set = static_cast(data); const auto *val_desc = VALIMPL::get_instance(); int8_t field_type; int32_t count; typename T::value_type ele; if (!buffer->readI08(field_type)) return false; if (!buffer->readI32(count)) return false; set->clear(); for (int i = 0; i < count; i++) { if (!val_desc->reader(buffer, &ele)) return false; set->insert(std::move(ele)); } return true; } static bool write(const void *data, ThriftBuffer *buffer) { const T *set = static_cast(data); const auto *val_desc = VALIMPL::get_instance(); if (!buffer->writeI08(val_desc->data_type)) return false; if (!buffer->writeI32(set->size())) return false; for (const auto& ele : *set) { if (!val_desc->writer(&ele, buffer)) return false; } return true; } static bool read_json(ThriftBuffer *buffer, void *data) { T *set = static_cast(data); const auto *val_desc = VALIMPL::get_instance(); bool is_first = true; typename T::value_type ele; char ch; if (!ThriftJsonUtil::skip_character(buffer, '[')) return false; while (ThriftJsonUtil::peek_first_meaningful_char(buffer, ch)) { if (ch == ']') { buffer->buffer->seek(1); return true; } if (is_first) is_first = false; else { buffer->buffer->seek(1); if (ch != ',') break; } if (!val_desc->json_reader(buffer, &ele)) break; set->insert(std::move(ele)); } return false; } static bool write_json(const void *data, ThriftBuffer *buffer) { const T *set = static_cast(data); const auto *val_desc = VALIMPL::get_instance(); bool is_first = true; if (!buffer->writeI08('[')) return false; for (const auto& ele : *set) { if (is_first) is_first = false; else if (!buffer->writeI08(',')) return false; if (!val_desc->json_writer(&ele, buffer)) return false; } return buffer->writeI08(']'); } private: ThriftDescriptorImpl(): ThriftDescriptor(ThriftDescriptorImpl::read, ThriftDescriptorImpl::write, ThriftDescriptorImpl::read_json, ThriftDescriptorImpl::write_json) { this->data_type = TDT_SET; //this->reader = ThriftDescriptorImpl::read; //this->writer = ThriftDescriptorImpl::write; //this->json_reader = ThriftDescriptorImpl::read_json; //this->json_writer = ThriftDescriptorImpl::write_json; } }; template class ThriftDescriptorImpl : public ThriftDescriptor { public: static const ThriftDescriptor *get_instance() { static const ThriftDescriptorImpl kInstance; return &kInstance; } static bool read(ThriftBuffer *buffer, void *data) { T *map = static_cast(data); const auto *key_desc = KEYIMPL::get_instance(); const auto *val_desc = VALIMPL::get_instance(); int8_t field_type; int32_t count; typename T::key_type key; typename T::mapped_type val; if (!buffer->readI08(field_type)) return false; if (!buffer->readI08(field_type)) return false; if (!buffer->readI32(count)) return false; map->clear(); for (int i = 0; i < count; i++) { if (!key_desc->reader(buffer, &key)) return false; if (!val_desc->reader(buffer, &val)) return false; map->insert(std::make_pair(std::move(key), std::move(val))); } return true; } static bool write(const void *data, ThriftBuffer *buffer) { const T *map = static_cast(data); const auto *key_desc = KEYIMPL::get_instance(); const auto *val_desc = VALIMPL::get_instance(); if (!buffer->writeI08(key_desc->data_type)) return false; if (!buffer->writeI08(val_desc->data_type)) return false; if (!buffer->writeI32(map->size())) return false; for (const auto& kv : *map) { if (!key_desc->writer(&kv.first, buffer)) return false; if (!val_desc->writer(&kv.second, buffer)) return false; } return true; } static bool read_json(ThriftBuffer *buffer, void *data) { T *map = static_cast(data); const auto *key_desc = KEYIMPL::get_instance(); const auto *val_desc = VALIMPL::get_instance(); bool is_first = true; typename T::key_type key; typename T::mapped_type val; char ch; if (!ThriftJsonUtil::skip_character(buffer, '[')) return false; while (ThriftJsonUtil::peek_first_meaningful_char(buffer, ch)) { if (ch == ']') { buffer->buffer->seek(1); return true; } if (is_first) is_first = false; else { buffer->buffer->seek(1); if (ch != ',') break; } if (!ThriftJsonUtil::skip_character(buffer, '{')) return false; if (!ThriftJsonUtil::skip_simple_string(buffer, "key")) return false; if (!ThriftJsonUtil::skip_character(buffer, ':')) return false; if (!key_desc->json_reader(buffer, &key)) break; if (!ThriftJsonUtil::skip_character(buffer, ',')) return false; if (!ThriftJsonUtil::skip_simple_string(buffer, "value")) return false; if (!ThriftJsonUtil::skip_character(buffer, ':')) return false; if (!val_desc->json_reader(buffer, &val)) break; ThriftJsonUtil::skip_character(buffer, '}'); map->insert(std::make_pair(std::move(key), std::move(val))); } return false; } static bool write_json(const void *data, ThriftBuffer *buffer) { const T *map = static_cast(data); const auto *key_desc = KEYIMPL::get_instance(); const auto *val_desc = VALIMPL::get_instance(); bool is_first = true; if (!buffer->writeI08('[')) return false; for (const auto& kv : *map) { if (is_first) is_first = false; else if (!buffer->writeI08(',')) return false; if (!buffer->writeStringBody("{\"key\":")) return false; if (!key_desc->json_writer(&kv.first, buffer)) return false; if (!buffer->writeStringBody(",\"value\":")) return false; if (!val_desc->json_writer(&kv.second, buffer)) return false; if (!buffer->writeI08('}')) return false; } return buffer->writeI08(']'); } private: ThriftDescriptorImpl(): ThriftDescriptor(ThriftDescriptorImpl::read, ThriftDescriptorImpl::write, ThriftDescriptorImpl::read_json, ThriftDescriptorImpl::write_json) { this->data_type = TDT_MAP; //this->reader = ThriftDescriptorImpl::read; //this->writer = ThriftDescriptorImpl::write; //this->json_reader = ThriftDescriptorImpl::read_json; //this->json_writer = ThriftDescriptorImpl::write_json; } }; template class ThriftDescriptorImpl : public ThriftDescriptor { public: static const ThriftDescriptor *get_instance() { static const ThriftDescriptorImpl kInstance; return &kInstance; } public: static bool read(ThriftBuffer *buffer, void *data) { T *st = static_cast(data); char *base = (char *)data; int8_t field_type; int16_t field_id; auto it = st->elements->cbegin(); while (true) { if (!buffer->readFieldBegin(field_type, field_id)) return false; if (field_type == TDT_STOP) return true; while (it != st->elements->cend() && it->field_id < field_id) ++it; if (it != st->elements->cend() && it->field_id == field_id && it->desc->data_type == field_type) { if (it->required_state != THRIFT_STRUCT_FIELD_REQUIRED) *((bool *)(base + it->isset_offset)) = true; if (!it->desc->reader(buffer, base + it->data_offset)) return false; } else { if (!buffer->skip(field_type)) return false; } } return true; } static bool write(const void *data, ThriftBuffer *buffer) { const T *st = static_cast(data); const char *base = (const char *)data; for (const auto& ele : *(st->elements)) { if (ele.required_state != THRIFT_STRUCT_FIELD_OPTIONAL || *((bool *)(base + ele.isset_offset)) == true) { if (!buffer->writeFieldBegin(ele.desc->data_type, ele.field_id)) return false; if (!ele.desc->writer(base + ele.data_offset, buffer)) return false; } } return buffer->writeFieldStop(); } static bool read_json(ThriftBuffer *buffer, void *data) { T *st = static_cast(data); char *base = (char *)data; bool is_first = true; std::string name; char ch; if (!ThriftJsonUtil::skip_character(buffer, '{')) return false; while (ThriftJsonUtil::peek_first_meaningful_char(buffer, ch)) { if (ch == '}') { buffer->buffer->seek(1); return true; } if (is_first) is_first = false; else { buffer->buffer->seek(1); if (ch != ',') break; } if (!ThriftJsonUtil::read_string(buffer, &name)) return false; if (!ThriftJsonUtil::skip_character(buffer, ':')) return false; bool is_find = false; for (const auto& ele : *(st->elements)) { if (name == ele.name) { is_find = true; if (ele.required_state != THRIFT_STRUCT_FIELD_REQUIRED) *((bool *)(base + ele.isset_offset)) = true; if (!ele.desc->json_reader(buffer, base + ele.data_offset)) return false; break; } } if (!is_find) { if (!ThriftJsonUtil::skip_one_element(buffer)) return false; } } return false; } static bool write_json(const void *data, ThriftBuffer *buffer) { const T *st = static_cast(data); const char *base = (const char *)data; bool is_first = true; if (!buffer->writeI08('{')) return false; for (const auto& ele : *(st->elements)) { if (ele.required_state != THRIFT_STRUCT_FIELD_OPTIONAL || *((bool *)(base + ele.isset_offset)) == true) { if (is_first) is_first = false; else if (!buffer->writeI08(',')) return false; if (!buffer->writeStringBody(std::string("\"") + ele.name + "\":")) return false; if (!ele.desc->json_writer(base + ele.data_offset, buffer)) return false; } } return buffer->writeI08('}'); } private: ThriftDescriptorImpl(): ThriftDescriptor(ThriftDescriptorImpl::read, ThriftDescriptorImpl::write, ThriftDescriptorImpl::read_json, ThriftDescriptorImpl::write_json) { this->data_type = TDT_STRUCT; //this->reader = ThriftDescriptorImpl::read; //this->writer = ThriftDescriptorImpl::write; //this->json_reader = ThriftDescriptorImpl::read_json; //this->json_writer = ThriftDescriptorImpl::write_json; } }; template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { int8_t *p = static_cast(data); return buffer->readI08(*p); } template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { int8_t *p = static_cast(data); return buffer->readI08(*p); } template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { int16_t *p = static_cast(data); return buffer->readI16(*p); } template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { int32_t *p = static_cast(data); return buffer->readI32(*p); } template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { int64_t *p = static_cast(data); return buffer->readI64(*p); } template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { uint64_t *p = static_cast(data); return buffer->readU64(*p); } template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { uint64_t *p = static_cast(data); return buffer->readU64(*p); } template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { std::string *p = static_cast(data); return buffer->readString(*p); } template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { std::string *p = static_cast(data); return buffer->readString(*p); } template<> inline bool ThriftDescriptorImpl::read(ThriftBuffer *buffer, void *data) { std::string *p = static_cast(data); return buffer->readString(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const int8_t *p = static_cast(data); return buffer->writeI08(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const int8_t *p = static_cast(data); return buffer->writeI08(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const int16_t *p = static_cast(data); return buffer->writeI16(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const int32_t *p = static_cast(data); return buffer->writeI32(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const int64_t *p = static_cast(data); return buffer->writeI64(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const uint64_t *p = static_cast(data); return buffer->writeU64(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const uint64_t *p = static_cast(data); return buffer->writeU64(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const std::string *p = static_cast(data); return buffer->writeString(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const std::string *p = static_cast(data); return buffer->writeString(*p); } template<> inline bool ThriftDescriptorImpl::write(const void *data, ThriftBuffer *buffer) { const std::string *p = static_cast(data); return buffer->writeString(*p); } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { int8_t *p = static_cast(data); int64_t intv; if (!ThriftJsonUtil::read_int64(buffer, intv)) return false; *p = (int8_t)intv; return true; } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { int8_t *p = static_cast(data); int64_t intv; if (!ThriftJsonUtil::read_int64(buffer, intv)) return false; *p = (int8_t)intv; return true; } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { int16_t *p = static_cast(data); int64_t intv; if (!ThriftJsonUtil::read_int64(buffer, intv)) return false; *p = (int16_t)intv; return true; } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { int32_t *p = static_cast(data); int64_t intv; if (!ThriftJsonUtil::read_int64(buffer, intv)) return false; *p = (int32_t)intv; return true; } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { int64_t *p = static_cast(data); return ThriftJsonUtil::read_int64(buffer, *p); } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { uint64_t *p = static_cast(data); return ThriftJsonUtil::read_uint64(buffer, *p); } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { double *p = static_cast(data); return ThriftJsonUtil::read_double(buffer, *p); } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { std::string *p = static_cast(data); return ThriftJsonUtil::read_string(buffer, p); } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { std::string *p = static_cast(data); return ThriftJsonUtil::read_string(buffer, p); } template<> inline bool ThriftDescriptorImpl::read_json(ThriftBuffer *buffer, void *data) { std::string *p = static_cast(data); return ThriftJsonUtil::read_string(buffer, p); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const int8_t *p = static_cast(data); return buffer->writeStringBody(std::to_string(*p)); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const int8_t *p = static_cast(data); return buffer->writeStringBody(std::to_string(*p)); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const int16_t *p = static_cast(data); return buffer->writeStringBody(std::to_string(*p)); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const int32_t *p = static_cast(data); return buffer->writeStringBody(std::to_string(*p)); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const int64_t *p = static_cast(data); return buffer->writeStringBody(std::to_string(*p)); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const uint64_t *p = static_cast(data); return buffer->writeStringBody(std::to_string(*p)); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const double *p = static_cast(data); return buffer->writeStringBody(std::to_string(*p)); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const std::string *p = static_cast(data); std::string str; if (!ThriftJsonUtil::escape_string(*p, str)) return false; return buffer->writeStringBody(str); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const std::string *p = static_cast(data); std::string str; if (!ThriftJsonUtil::escape_string(*p, str)) return false; return buffer->writeStringBody(str); } template<> inline bool ThriftDescriptorImpl::write_json(const void *data, ThriftBuffer *buffer) { const std::string *p = static_cast(data); std::string str; if (!ThriftJsonUtil::escape_string(*p, str)) return false; return buffer->writeStringBody(str); } } // end namespace srpc srpc-0.10.1/src/var/000077500000000000000000000000001454502251400141215ustar00rootroot00000000000000srpc-0.10.1/src/var/CMakeLists.txt000066400000000000000000000002471454502251400166640ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(var) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(SRC rpc_var.cc ) add_library(${PROJECT_NAME} OBJECT ${SRC}) srpc-0.10.1/src/var/ckms_quantiles.h000066400000000000000000000111631454502251400173160ustar00rootroot00000000000000#ifndef __CKMS_QUANTILE_H__ #define __CKMS_QUANTILE_H__ #include #include #include #include #include #include #include #include namespace srpc { struct Quantile { Quantile(double q, double err) { quantile = q; error = err; u = 2.0 * err / (1.0 - q); v = 2.0 * err / q; } double quantile; double error; double u; double v; }; template class CKMSQuantiles { private: struct Item { Item(TYPE value, int lower_delta, int delta); TYPE value; int g; int delta; }; public: CKMSQuantiles(const std::vector *quantiles); CKMSQuantiles& operator= (const CKMSQuantiles& copy); CKMSQuantiles(const CKMSQuantiles& copy); void insert(TYPE value); TYPE get(double q); void reset(); size_t get_count() const { return this->count; } TYPE get_sum() const { return this->sum; } private: double allowable_error(int rank); bool insert_batch(); void compress(); private: const std::vector *quantiles; TYPE sum; size_t count; std::vector sample; std::array buffer; size_t buffer_count; }; ////////// impl template CKMSQuantiles::Item::Item(TYPE val, int lower_del, int del) { value = val; g = lower_del; delta = del; } template CKMSQuantiles::CKMSQuantiles(const std::vector *q) : quantiles(q) { this->count = 0; this->sum = 0; this->buffer_count = 0; } template CKMSQuantiles::CKMSQuantiles(const CKMSQuantiles& copy) { this->operator= (copy); } template CKMSQuantiles& CKMSQuantiles::operator= (const CKMSQuantiles& copy) { if (this != ©) { this->quantiles = copy.quantiles; this->count = copy.count; this->sum = copy.sum; this->sample = copy.sample; this->buffer = copy.buffer; this->buffer_count = copy.buffer_count; } return *this; } template void CKMSQuantiles::insert(TYPE value) { this->sum += value; this->buffer[this->buffer_count] = value; ++this->buffer_count; if (this->buffer_count == this->buffer.size()) { this->insert_batch(); this->compress(); } } template TYPE CKMSQuantiles::get(double q) { this->insert_batch(); this->compress(); if (this->sample.empty()) return std::numeric_limits::quiet_NaN(); int rank_min = 0; const auto desired = static_cast(q * this->count); const auto bound = desired + (allowable_error(desired) / 2); auto it = this->sample.begin(); decltype(it) prev; auto cur = it++; while (it != this->sample.end()) { prev = cur; cur = it++; rank_min += prev->g; if (rank_min + cur->g + cur->delta > bound) return prev->value; } return this->sample.back().value; } template void CKMSQuantiles::reset() { this->count = 0; this->sum = 0; this->sample.clear(); this->buffer_count = 0; } template double CKMSQuantiles::allowable_error(int rank) { auto size = this->sample.size(); double min_error = size + 1; for (const auto& q : *(this->quantiles)) { double error; if (rank <= q.quantile * size) error = q.u * (size - rank); else error = q.v * rank; if (error < min_error) min_error = error; } return min_error; } template bool CKMSQuantiles::insert_batch() { if (this->buffer_count == 0) return false; std::sort(this->buffer.begin(), this->buffer.begin() + this->buffer_count); size_t start = 0; size_t idx = 0; size_t item = idx++; TYPE v; int delta; if (this->sample.empty()) { this->sample.emplace_back(this->buffer[0], 1, 0); ++start; ++this->count; } for (size_t i = start; i < this->buffer_count; ++i) { v = this->buffer[i]; while (idx < this->sample.size() && this->sample[item].value < v) item = idx++; if (this->sample[item].value > v) --idx; if (idx - 1 == 0 || idx + 1 == this->sample.size()) delta = 0; else delta = static_cast(std::floor(allowable_error(idx + 1))) + 1; this->sample.emplace(this->sample.begin() + idx, v, 1, delta); this->count++; item = idx++; } this->buffer_count = 0; return true; } template void CKMSQuantiles::compress() { if (this->sample.size() < 2) return; size_t idx = 0; size_t prev; size_t next = idx++; while (idx < this->sample.size()) { prev = next; next = idx++; if (this->sample[prev].g + this->sample[next].g + this->sample[next].delta <= allowable_error(idx - 1)) { this->sample[next].g += this->sample[prev].g; this->sample.erase(this->sample.begin() + prev); } } } } // end namespace srpc #endif srpc-0.10.1/src/var/rpc_var.cc000066400000000000000000000302101454502251400160600ustar00rootroot00000000000000/* Copyright (c) 2021 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include "rpc_var.h" namespace srpc { GaugeVar *RPCVarFactory::gauge(const std::string& name) { return static_cast(RPCVarFactory::var(name)); } CounterVar *RPCVarFactory::counter(const std::string& name) { return static_cast(RPCVarFactory::var(name)); } HistogramVar *RPCVarFactory::histogram(const std::string& name) { return static_cast(RPCVarFactory::var(name)); } SummaryVar *RPCVarFactory::summary(const std::string& name) { return static_cast(RPCVarFactory::var(name)); } RPCVar *RPCVarFactory::var(const std::string& name) { RPCVar *var; RPCVar *new_var; RPCVarLocal *local = RPCVarLocal::get_instance(); auto it = local->vars.find(name); if (it != local->vars.end()) return it->second; var = RPCVarGlobal::get_instance()->find(name); if (var) { new_var = var->create(false); local->add(name, new_var); return new_var; } return NULL; } // a~z, A~Z, _ bool RPCVarFactory::check_name_format(const std::string& name) { for (size_t i = 0; i < name.length(); i++) { if ((name.at(i) < 65 || name.at(i) > 90) && (name.at(i) < 97 || name.at(i) > 122) && name.at(i) != 95) return false; } return true; } void RPCVar::format_name() { //TODO: change aaa.bbb AAA.BBB to aaa_bbb } RPCVarLocal::~RPCVarLocal() { RPCVarGlobal *global_var = RPCVarGlobal::get_instance(); global_var->remove(this); global_var->dup(this->vars); for (auto it = this->vars.begin(); it != this->vars.end(); it++) delete it->second; } RPCVarGlobal::~RPCVarGlobal() { this->finished = true; for (auto& local : this->local_vars) delete local; } void RPCVarGlobal::dup(const std::unordered_map& vars) { if (this->finished == true) return; RPCVarLocal *local; this->mutex.lock(); if (this->local_vars.empty()) local = NULL; else local = this->local_vars[0]; this->mutex.unlock(); if (local == NULL) local = new RPCVarLocal(); local->mutex.lock(); std::unordered_map& local_var = local->vars; for (auto it = vars.begin(); it != vars.end(); it++) { if (local_var.find(it->first) == local_var.end()) { local_var.insert(std::make_pair(it->first, it->second->create(true))); } else { local_var[it->first]->reduce(it->second->get_data(), it->second->get_size()); } } local->mutex.unlock(); } void RPCVarGlobal::remove(const RPCVarLocal *var) { this->mutex.lock(); for (size_t i = 0; i < this->local_vars.size(); i++) { if (this->local_vars[i] == var) { for (size_t j = i; j < this->local_vars.size() - 1; j++) this->local_vars[j] = this->local_vars[j + 1]; break; } } this->local_vars.resize(this->local_vars.size() - 1); this->mutex.unlock(); } RPCVar *RPCVarGlobal::find(const std::string& name) { std::unordered_map::iterator it; RPCVarGlobal *global_var = RPCVarGlobal::get_instance(); RPCVar *ret = NULL; RPCVarLocal *local; global_var->mutex.lock(); for (size_t i = 0; i < global_var->local_vars.size() && !ret; i++) { local = global_var->local_vars[i]; for (it = local->vars.begin(); it != local->vars.end(); it++) { if (!name.compare(it->second->get_name())) { ret = it->second; break; } } } global_var->mutex.unlock(); return ret; } ///////////// var impl RPCVar *GaugeVar::create(bool with_data) { GaugeVar *var = new GaugeVar(this->name, this->help); if (with_data) var->data = this->data; return var; } CounterVar::~CounterVar() { for (auto it = this->data.begin(); it != this->data.end(); it++) delete it->second; } RPCVar *CounterVar::create(bool with_data) { CounterVar *var = new CounterVar(this->name, this->help); if (with_data) { for (auto it = this->data.begin(); it != this->data.end(); it++) { var->data.insert(std::make_pair(it->first, (GaugeVar *)it->second->create(true))); } } return var; } bool CounterVar::label_to_str(const LABEL_MAP& labels, std::string& str) { for (auto it = labels.begin(); it != labels.end(); it++) { if (it != labels.begin()) str += ","; //TODO: check label name regex is "[a-zA-Z_:][a-zA-Z0-9_:]*" str += it->first + "=\"" + it->second + "\""; } return true; } GaugeVar *CounterVar::add(const LABEL_MAP& labels) { std::string label_str; GaugeVar *var; if (!this->label_to_str(labels, label_str)) return NULL; auto it = this->data.find(label_str); if (it == this->data.end()) { var = new GaugeVar(label_str, ""); this->data.insert(std::make_pair(label_str, var)); } else var = it->second; return var; } bool CounterVar::reduce(const void *ptr, size_t) { std::unordered_map *data; data = (std::unordered_map *)ptr; for (auto it = data->begin(); it != data->end(); it++) { auto my_it = this->data.find(it->first); if (my_it == this->data.end()) { GaugeVar *var = (GaugeVar *)it->second->create(true); this->data.insert(std::make_pair(it->first, var)); } else my_it->second->reduce(it->second->get_data(), it->second->get_size()); } return true; } void CounterVar::collect(RPCVarCollector *collector) { for (auto it = this->data.begin(); it != this->data.end(); it++) collector->collect_counter_each(this, it->first, it->second->get()); } void CounterVar::reset() { for (auto it = this->data.begin(); it != this->data.end(); it++) delete it->second; this->data.clear(); } void HistogramVar::observe(double value) { size_t i = 0; for (; i < this->bucket_boundaries.size(); i++) { if (value <= this->bucket_boundaries[i]) break; } this->bucket_counts[i]++; this->sum += value; this->count++; } HistogramVar::HistogramVar(const std::string& name, const std::string& help, const std::vector& bucket) : RPCVar(name, help, VAR_HISTOGRAM), bucket_boundaries(bucket), bucket_counts(bucket.size() + 1) { this->sum = 0; this->count = 0; } RPCVar *HistogramVar::create(bool with_data) { HistogramVar *var = new HistogramVar(this->name, this->help, this->bucket_boundaries); if (with_data) { var->bucket_counts = this->bucket_counts; var->sum = this->sum; var->count = this->count; } return var; } bool HistogramVar::observe_multi(const std::vector& multi, double sum) { if (multi.size() != this->bucket_counts.size()) return false; for (size_t i = 0; i < multi.size(); i ++) { this->bucket_counts[i] += multi[i]; this->count += multi[i]; } this->sum += sum; return true; } bool HistogramVar::reduce(const void *ptr, size_t sz) { if (sz != this->bucket_boundaries.size() + 1) return false; const HistogramVar *data = (const HistogramVar *)ptr; const std::vector *src_bucket_counts = data->get_bucket_counts(); for (size_t i = 0; i < sz; i++) this->bucket_counts[i] += (*src_bucket_counts)[i]; this->sum += data->get_sum(); this->count += data->get_count(); return true; } void HistogramVar::collect(RPCVarCollector *collector) { size_t i = 0; size_t current = 0; collector->collect_histogram_begin(this); for (; i < this->bucket_boundaries.size(); i++) { current += this->bucket_counts[i]; collector->collect_histogram_each(this, this->bucket_boundaries[i], current); } current += this->bucket_counts[i]; collector->collect_histogram_each(this, DBL_MAX, current); collector->collect_histogram_end(this, this->sum, this->count); } void HistogramVar::reset() { for (size_t i = 0; i < this->bucket_boundaries.size(); i++) this->bucket_counts[i] = 0; this->sum = 0; this->count = 0; } SummaryVar::SummaryVar(const std::string& name, const std::string& help, const std::vector& quantile, const std::chrono::milliseconds max_age, int age_bucket) : RPCVar(name, help, VAR_SUMMARY), quantiles(quantile), quantile_values(&this->quantiles, max_age, age_bucket) { this->sum = 0; this->count = 0; this->max_age = max_age; this->age_buckets = age_bucket; this->quantile_size = this->quantiles.size(); // for output if (this->quantiles[this->quantile_size - 1].quantile != 1.0) { struct Quantile q(1.0, 0.1); this->quantiles.push_back(q); } this->quantile_out.resize(this->quantiles.size(), 0); } RPCVar *SummaryVar::create(bool with_data) { SummaryVar *var = new SummaryVar(this->name, this->help, this->quantiles, this->max_age, this->age_buckets); if (with_data) { var->sum = this->sum; var->count = this->count; var->quantile_values = this->quantile_values; var->quantile_size = this->quantile_size; var->quantile_out = this->quantile_out; } return var; } void SummaryVar::observe(double value) { this->quantile_values.insert(value); } bool SummaryVar::reduce(const void *ptr, size_t sz) { if (sz != this->quantiles.size()) return false; SummaryVar *data = (SummaryVar *)ptr; TimeWindowQuantiles *src = data->get_quantile_values(); double get_val; size_t src_count = 0; double *src_value = new double[sz](); double src_sum = src->get_sum(); for (size_t i = 0; i < sz; i++) { src_count = src->get(this->quantiles[i].quantile, &get_val); src_value[i] = get_val; } double pilot; size_t cnt; size_t idx; double range; double count = 0; double value = 0; size_t src_idx = 0; size_t dst_idx = 0; size_t total = this->count + src_count; double *out = new double[sz](); for (size_t i = 0; i < sz; i++) { pilot = this->quantiles[i].quantile * total; while (count < pilot && src_idx < sz && dst_idx < sz) { if (this->quantile_out[dst_idx] <= src_value[src_idx]) { value = this->quantile_out[dst_idx]; idx = dst_idx; cnt = this->count; dst_idx++; } else { value = src_value[src_idx]; idx = src_idx; cnt = src_count; src_idx++; } if (idx == 0) range = this->quantiles[0].quantile; else range = this->quantiles[idx].quantile - this->quantiles[idx - 1].quantile; count += cnt * range; } if (count >= pilot) out[i] = value; else if (src_idx < sz) out[i] = src_value[i]; else out[i] = this->quantile_out[i]; } for (size_t i = 0; i < sz; i++) this->quantile_out[i] = out[i]; this->count = total; this->sum += src_sum; delete[] out; delete[] src_value; return true; } void SummaryVar::collect(RPCVarCollector *collector) { collector->collect_summary_begin(this); for (size_t i = 0; i < this->quantile_size; i++) { collector->collect_summary_each(this, this->quantiles[i].quantile, this->quantile_out[i]); } collector->collect_summary_end(this, this->sum, this->count); this->quantile_out.clear(); } TimedGaugeVar::TimedGaugeVar(const std::string& name, const std::string& help, Clock::duration duration, size_t bucket_num) : GaugeVar(name, help), RPCTimeWindow(duration, bucket_num) { for (size_t i = 0; i < bucket_num; i ++) this->data_bucket.push_back(GaugeVar(name, help)); } void TimedGaugeVar::increase() { this->rotate(); for (auto& bucket : this->data_bucket) bucket.increase(); } const void *TimedGaugeVar::get_data() { GaugeVar& bucket = this->rotate(); return bucket.get_data(); } RPCVar *TimedGaugeVar::create(bool with_data) { GaugeVar& current_bucket = this->rotate(); RPCVar *var; if (with_data) // for reduce { GaugeVar *gauge = new GaugeVar(this->get_name(), this->get_help()); gauge->set(*(double *)current_bucket.get_data()); var = gauge; } else // for dup into other threads { TimedGaugeVar *timed_gauge = new TimedGaugeVar(this->get_name(), this->get_help(), this->duration, this->bucket_num); var = timed_gauge; } return var; } } // end namespace srpc srpc-0.10.1/src/var/rpc_var.h000066400000000000000000000224711454502251400157340ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __RPC_VAR_H__ #define __RPC_VAR_H__ #include #include #include #include #include #include #include #include "time_window_quantiles.h" namespace srpc { class RPCVar; class RPCVarLocal; class GaugeVar; class CounterVar; class HistogramVar; class SummaryVar; enum RPCVarType { VAR_GAUGE = 0, VAR_COUNTER = 1, VAR_HISTOGRAM = 2, VAR_SUMMARY = 3 }; static std::string type_string(RPCVarType type) { switch (type) { case VAR_GAUGE: return "gauge"; case VAR_COUNTER: return "counter"; case VAR_HISTOGRAM: return "histogram"; case VAR_SUMMARY: return "summary"; default: break; } return ""; } class RPCVarFactory { public: // thread local api static GaugeVar *gauge(const std::string& name); static CounterVar *counter(const std::string& name); static HistogramVar *histogram(const std::string& name); static SummaryVar *summary(const std::string& name); static RPCVar *var(const std::string& name); static bool check_name_format(const std::string& name); }; class RPCVarGlobal { public: static RPCVarGlobal *get_instance() { static RPCVarGlobal kInstance; return &kInstance; } void add(RPCVarLocal *local) { this->mutex.lock(); this->local_vars.push_back(local); this->mutex.unlock(); } RPCVar *find(const std::string& name); // following APIs are used when thread exit and its ~RPCVarLocal() // remove a RPCVarLocal from Glolba->local_vars void remove(const RPCVarLocal *var); // duplicate the vars into global existing RPCVarLocal void dup(const std::unordered_map& vars); private: RPCVarGlobal() { this->finished = false; } ~RPCVarGlobal(); public: std::mutex mutex; std::vector local_vars; bool finished; // friend class RPCVarFactory; }; class RPCVarLocal { public: static RPCVarLocal *get_instance() { static thread_local RPCVarLocal kInstance; return &kInstance; } void add(std::string name, RPCVar *var) { this->mutex.lock(); const auto it = this->vars.find(name); if (it == this->vars.end()) this->vars.insert(std::make_pair(std::move(name), var)); this->mutex.unlock(); } virtual ~RPCVarLocal(); private: RPCVarLocal() { RPCVarGlobal::get_instance()->add(this); } public: std::mutex mutex; std::unordered_map vars; friend class RPCVarGlobal; // friend class RPCVarFactory; }; class RPCVarCollector { public: virtual void collect_gauge(RPCVar *gauge, double data) = 0; virtual void collect_counter_each(RPCVar *counter, const std::string& label, double data) = 0; virtual void collect_histogram_begin(RPCVar *histogram) = 0; virtual void collect_histogram_each(RPCVar *histogram, double bucket_boudary, size_t current_count) = 0; virtual void collect_histogram_end(RPCVar *histogram, double sum, size_t count) = 0; virtual void collect_summary_begin(RPCVar *summary) = 0; virtual void collect_summary_each(RPCVar *summary, double quantile, double quantile_out) = 0; virtual void collect_summary_end(RPCVar *summary, double sum, size_t count) = 0; }; class RPCVar { public: const std::string& get_name() const { return this->name; } const std::string& get_help() const { return this->help; } RPCVarType get_type() const { return this->type; } const std::string get_type_str() const { return type_string(this->type); } virtual RPCVar *create(bool with_data) = 0; virtual bool reduce(const void *ptr, size_t sz) = 0; virtual size_t get_size() const = 0; virtual const void *get_data() = 0; virtual void collect(RPCVarCollector *collector) = 0; virtual void reset() = 0; public: RPCVar(const std::string& name, const std::string& help, RPCVarType type) : name(name), help(help), type(type) { // this->format_name(); } virtual ~RPCVar() {} private: void format_name(); protected: std::string name; std::string help; RPCVarType type; }; class GaugeVar : public RPCVar { public: virtual void increase() { ++this->data; } virtual void decrease() { --this->data; } size_t get_size() const override { return sizeof(double); } const void *get_data() override { return &this->data; } virtual double get() { return this->data; } virtual void set(double var) { this->data = var; } RPCVar *create(bool with_data) override; bool reduce(const void *ptr, size_t sz) override { this->data += *(double *)ptr; // fprintf(stderr, "reduce data=%d *ptr=%d\n", this->data, *(double *)ptr); return true; } void collect(RPCVarCollector *collector) override { collector->collect_gauge(this, this->data); } void reset() override { this->data = 0; } public: GaugeVar(const std::string& name, const std::string& help) : RPCVar(name, help, VAR_GAUGE) { this->data = 0; } private: double data; }; class CounterVar : public RPCVar { public: using LABEL_MAP = std::map; GaugeVar *add(const LABEL_MAP& labels); RPCVar *create(bool with_data) override; bool reduce(const void *ptr, size_t sz) override; void collect(RPCVarCollector *collector) override; size_t get_size() const override { return this->data.size(); } const void *get_data() override { return &this->data; } static bool label_to_str(const LABEL_MAP& labels, std::string& str); void reset() override; public: CounterVar(const std::string& name, const std::string& help) : RPCVar(name, help, VAR_COUNTER) { } ~CounterVar(); private: std::unordered_map data; }; class HistogramVar : public RPCVar { public: void observe(double value); // multi is the histogram count of each bucket, including +inf bool observe_multi(const std::vector& multi, double sum); RPCVar *create(bool with_data) override; bool reduce(const void *ptr, size_t sz) override; void collect(RPCVarCollector *collector) override; size_t get_size() const override { return this->bucket_counts.size(); } const void *get_data() override { return this; } double get_sum() const { return this->sum; } size_t get_count() const { return this->count; } const std::vector *get_bucket_counts() const { return &this->bucket_counts; } void reset() override; public: HistogramVar(const std::string& name, const std::string& help, const std::vector& bucket); private: std::vector bucket_boundaries; std::vector bucket_counts; double sum; size_t count; }; class SummaryVar : public RPCVar { public: void observe(double value); RPCVar *create(bool with_data) override; bool reduce(const void *ptr, size_t sz) override; void collect(RPCVarCollector *collector) override; size_t get_size() const override { return this->quantiles.size(); } const void *get_data() override { return this; } double get_sum() const { return this->sum; } size_t get_count() const { return this->count; } TimeWindowQuantiles *get_quantile_values() { return &this->quantile_values; } void reset() override { /* no TimedSummary so no reset for Summary */} public: SummaryVar(const std::string& name, const std::string& help, const std::vector& quantile, const std::chrono::milliseconds max_age, int age_bucket); private: std::vector quantiles; double sum; size_t count; size_t quantile_size; std::chrono::milliseconds max_age; int age_buckets; TimeWindowQuantiles quantile_values; std::vector quantile_out; }; template class RPCTimeWindow { public: using Clock = std::chrono::steady_clock; RPCTimeWindow(Clock::duration duration, size_t bucket_num); protected: VAR& rotate(); protected: std::vector data_bucket; Clock::duration duration; size_t bucket_num; private: size_t current_bucket; Clock::time_point last_rotation; Clock::duration rotation_interval; }; template RPCTimeWindow::RPCTimeWindow(Clock::duration duration, size_t bucket_num) : duration(duration), bucket_num(bucket_num), rotation_interval(duration / bucket_num) { this->current_bucket = 0; this->last_rotation = Clock::now(); } template VAR& RPCTimeWindow::rotate() { auto delta = Clock::now() - this->last_rotation; while (delta > this->rotation_interval) { this->data_bucket[this->current_bucket].reset(); if (++this->current_bucket >= this->data_bucket.size()) this->current_bucket = 0; delta -= this->rotation_interval; this->last_rotation += this->rotation_interval; } return this->data_bucket[this->current_bucket]; } class TimedGaugeVar : public GaugeVar, RPCTimeWindow { public: TimedGaugeVar(const std::string& name, const std::string& help, Clock::duration duration, size_t bucket_num); // for collect void increase() override; // for reduce const void *get_data() override; RPCVar *create(bool with_data) override; }; } // end namespace srpc #endif srpc-0.10.1/src/var/time_window_quantiles.h000066400000000000000000000053571454502251400207160ustar00rootroot00000000000000#ifndef __TIME_WINDOW_QUANTILES_H__ #define __TIME_WINDOW_QUANTILES_H__ #include #include #include #include #include #include "ckms_quantiles.h" namespace srpc { template class TimeWindowQuantiles { public: using Clock = std::chrono::steady_clock; size_t get(double quantile, TYPE *value); void insert(TYPE value); TYPE get_sum() { return this->ckms_quantiles[current_bucket].get_sum(); } private: CKMSQuantiles& rotate(); const std::vector *quantiles; std::vector> ckms_quantiles; size_t current_bucket; Clock::time_point last_rotation; Clock::duration rotation_interval; public: TimeWindowQuantiles(const std::vector *quantiles, Clock::duration max_age_seconds, int age_buckets); TimeWindowQuantiles(const TimeWindowQuantiles& copy); TimeWindowQuantiles& operator= (const TimeWindowQuantiles& copy); }; ////////// impl template TimeWindowQuantiles::TimeWindowQuantiles(const std::vector *q, const Clock::duration max_age, const int age_buckets) : quantiles(q), ckms_quantiles(age_buckets, CKMSQuantiles(this->quantiles)), rotation_interval(max_age / age_buckets) { this->current_bucket = 0; this->last_rotation = Clock::now(); } template TimeWindowQuantiles::TimeWindowQuantiles(const TimeWindowQuantiles& copy) { this->operator= (copy); } template TimeWindowQuantiles& TimeWindowQuantiles::operator= (const TimeWindowQuantiles& copy) { if (this != ©) { this->quantiles = copy.quantiles; this->ckms_quantiles = copy.ckms_quantiles; this->current_bucket = copy.current_bucket; this->last_rotation = copy.last_rotation; this->rotation_interval = copy.rotation_interval; } return *this; } template size_t TimeWindowQuantiles::get(double quantile, TYPE *value) { CKMSQuantiles& current_bucket = this->rotate(); *value = current_bucket.get(quantile); return current_bucket.get_count(); } template void TimeWindowQuantiles::insert(TYPE value) { this->rotate(); for (auto& bucket : this->ckms_quantiles) bucket.insert(value); } template CKMSQuantiles& TimeWindowQuantiles::rotate() { auto delta = Clock::now() - this->last_rotation; while (delta > this->rotation_interval) { this->ckms_quantiles[this->current_bucket].reset(); if (++this->current_bucket >= this->ckms_quantiles.size()) this->current_bucket = 0; delta -= this->rotation_interval; this->last_rotation += this->rotation_interval; } return this->ckms_quantiles[this->current_bucket]; } } // end namespace srpc #endif srpc-0.10.1/srpc-config.cmake.in000066400000000000000000000006001454502251400163570ustar00rootroot00000000000000@PACKAGE_INIT@ set(SRPC_VERSION "@srpc_VERSION@") set_and_check(SRPC_INCLUDE_DIR "@PACKAGE_CONFIG_INC_DIR@") set_and_check(SRPC_LIB_DIR "@PACKAGE_CONFIG_LIB_DIR@") set_and_check(SRPC_BIN_DIR "@PACKAGE_CONFIG_BIN_DIR@") if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/srpc-targets.cmake") include ("${CMAKE_CURRENT_LIST_DIR}/srpc-targets.cmake") endif () check_required_components(srpc) srpc-0.10.1/srpc.bzl000066400000000000000000000034121454502251400142220ustar00rootroot00000000000000""" Rules for building C++ srpc with Bazel. """ load("@rules_cc//cc:defs.bzl", "cc_library") tool_path = ":srpc_generator" def srpc_cc_library( name, srcs, deps = [], type = "proto", out_prefix = "", visibility = None): output_directory = ( ("$(@D)/%s" % (out_prefix)) if len(srcs) > 1 else ("$(@D)") ) proto_output_headers = [ (out_prefix + "%s.srpc.h") % (s.replace(".%s" % type, "").split("/")[-1]) for s in srcs ] thrift_output_headers = [ (out_prefix + "%s.thrift.h") % (s.replace(".%s" % type, "").split("/")[-1]) for s in srcs ] if type == "thrift": output_headers = proto_output_headers + thrift_output_headers gen_proto = "thrift" if type == "proto": output_headers = proto_output_headers gen_proto = "protobuf" genrule_cmd = " ".join([ "SRCS=($(SRCS));", "for f in $${SRCS[@]:0:%s}; do" % len(srcs), "$(location %s)" % (tool_path), " %s " % gen_proto, "$$f", output_directory + ";", "done", ]) srcs_lib = "%s_srcs" % (name) native.genrule( name = srcs_lib, srcs = srcs, outs = output_headers, tools = [tool_path], cmd = genrule_cmd, output_to_bindir = True, message = "Generating srpc files for %s:" % (name), ) runtime_deps = deps + [":srpc"] print(runtime_deps) cc_library( name = name, hdrs = [ ":" + srcs_lib, ], srcs = [ ":" + srcs_lib, ], features = [ "-parse_headers", ], deps = runtime_deps, includes = [], linkstatic = 1, visibility = visibility, ) srpc-0.10.1/test/000077500000000000000000000000001454502251400135215ustar00rootroot00000000000000srpc-0.10.1/test/CMakeLists.txt000066400000000000000000000113311454502251400162600ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "build type") project(srpc_test LANGUAGES C CXX ) if (NOT "$ENV{LIBRARY_PATH}" STREQUAL "") string(REPLACE ":" ";" LIBRARY_PATH $ENV{LIBRARY_PATH}) set(CMAKE_SYSTEM_LIBRARY_PATH ${LIBRARY_PATH};${CMAKE_SYSTEM_LIBRARY_PATH}) endif () if (NOT "$ENV{CPLUS_INCLUDE_PATH}" STREQUAL "") string(REPLACE ":" ";" INCLUDE_PATH $ENV{CPLUS_INCLUDE_PATH}) set(CMAKE_SYSTEM_INCLUDE_PATH ${INCLUDE_PATH};${CMAKE_SYSTEM_INCLUDE_PATH}) endif () find_package(OpenSSL REQUIRED) find_package(srpc REQUIRED CONFIG HINTS ..) set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "") if (WIN32) find_package(Protobuf CONFIG REQUIRED) else () find_package(Protobuf REQUIRED) endif () if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/lz4/lib/lz4.h") set(LZ4_LIB lz4) endif () if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/snappy/cmake") set(SNAPPY_LIB snappy) endif () if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/workflow/workflow-config.cmake.in") find_package(Workflow REQUIRED CONFIG HINTS ../workflow) endif () include_directories( ${OPENSSL_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${Protobuf_INCLUDE_DIR} ${SRPC_INCLUDE_DIR} ${WORKFLOW_INCLUDE_DIR} ) if (WIN32) link_directories(${SRPC_LIB_DIR} ${WORKFLOW_LIB_DIR} ${Protobuf_LIB_DIR}) set(SRPC_GEN_PROGRAM ${SRPC_BIN_DIR}/Debug/srpc_generator.exe) else () get_filename_component(Protobuf_LIB_DIR ${Protobuf_LIBRARY} DIRECTORY) link_directories(${SRPC_LIB_DIR} ${WORKFLOW_LIB_DIR} ${Protobuf_LIB_DIR}) set(SRPC_GEN_PROGRAM ${SRPC_BIN_DIR}/srpc_generator) endif () find_program(CMAKE_MEMORYCHECK_COMMAND valgrind) set(memcheck_command ${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS} --error-exitcode=1 --leak-check=full --show-leak-kinds=all) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) enable_testing() if (WIN32) find_package(GTest CONFIG REQUIRED) else () find_package(GTest REQUIRED) endif () set(CXX_STD "c++11") if (GTest_VERSION VERSION_GREATER_EQUAL "1.13.0") set(CXX_STD "c++14") else () get_filename_component(GTEST_PATH ${GTEST_LIBRARY} DIRECTORY ABSOLUTE) execute_process(COMMAND grep Version "${GTEST_PATH}/pkgconfig/gtest.pc" RESULT_VARIABLE GTEST_VERSION_RESULT OUTPUT_VARIABLE GTEST_VERSION_OUTPUT) if (${GTEST_VERSION_RESULT} EQUAL "0") string(REPLACE " " ";" GTEST_VERSION_STR ${GTEST_VERSION_OUTPUT}) list(GET GTEST_VERSION_STR 1 GTEST_VERSION) if (${GTEST_VERSION} VERSION_GREATER_EQUAL "1.13.0") set(CXX_STD "c++14") endif () endif () endif () if (WIN32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP /wd4200") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4200 /Zc:__cplusplus /std:c++14") else () set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fPIC -pipe -std=gnu90") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -pipe -std=${CXX_STD} -fno-exceptions") endif () protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS test_pb.proto) add_custom_target( SRPC_GEN ALL COMMAND ${SRPC_GEN_PROGRAM} ${PROJECT_SOURCE_DIR}/test_pb.proto ${PROJECT_SOURCE_DIR} COMMAND ${SRPC_GEN_PROGRAM} ${PROJECT_SOURCE_DIR}/test_thrift.thrift ${PROJECT_SOURCE_DIR} COMMENT "srpc generator..." ) set(GTEST_LIB GTest::GTest GTest::Main) if (APPLE) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ${CMAKE_FIND_LIBRARY_SUFFIXES}) find_library(Workflow_LIB workflow HINTS ../workflow/_lib) find_library(Srpc_LIB srpc HINTS ../_lib) set(SRPC_LIB ${Srpc_LIB} ${Workflow_LIB} pthread OpenSSL::SSL OpenSSL::Crypto protobuf z ) elseif (WIN32) set(SRPC_LIB srpc workflow ws2_32 wsock32 OpenSSL::SSL OpenSSL::Crypto protobuf::libprotobuf ZLIB::ZLIB Snappy::snappy ${LZ4_LIBRARY} ) set(GTEST_LIB GTest::gtest GTest::gtest_main) else () if (SNAPPY_INSTALLED) set(SNAPPY_LIB snappy) endif () if (LZ4_INSTALLED) set(LZ4_LIB lz4) endif () set(SRPC_LIB srpc workflow pthread OpenSSL::SSL OpenSSL::Crypto protobuf z ${SNAPPY_LIB} ${LZ4_LIB} ) endif () set(TEST_LIST unittest) set(BASIC_TEST var_unittest) set(ALL_TEST var_unittest unittest) foreach(src ${TEST_LIST}) add_executable(${src} EXCLUDE_FROM_ALL ${src}.cc ${PROTO_SRCS} ${PROTO_HDRS}) target_link_libraries(${src} ${SRPC_LIB} ${GTEST_LIB}) add_test(${src} ${src}) add_dependencies(${src} SRPC_GEN) add_dependencies(check ${src}) endforeach() foreach(src ${BASIC_TEST}) add_executable(${src} EXCLUDE_FROM_ALL ${src}.cc) target_link_libraries(${src} ${SRPC_LIB} ${GTEST_LIB}) add_test(${src} ${src}) add_dependencies(check ${src}) endforeach() if (WIN32) set(memcheck nothing) elseif (NOT ${CMAKE_MEMORYCHECK_COMMAND} STREQUAL "CMAKE_MEMORYCHECK_COMMAND-NOTFOUND") foreach(src ${ALL_TEST}) add_test(${src}-memory-check ${memcheck_command} ./${src}) endforeach() endif () srpc-0.10.1/test/GNUmakefile000066400000000000000000000016411454502251400155750ustar00rootroot00000000000000ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) ALL_TARGETS := all check clean MAKE_FILE := Makefile DEFAULT_BUILD_DIR := build.cmake BUILD_DIR := $(shell if [ -f $(MAKE_FILE) ]; then echo "."; else echo $(DEFAULT_BUILD_DIR); fi) CMAKE3 := $(shell if which cmake3 ; then echo cmake3; else echo cmake; fi;) .PHONY: $(ALL_TARGETS) all: mkdir -p $(BUILD_DIR) ifeq ($(DEBUG),y) cd $(BUILD_DIR) && $(CMAKE3) -D CMAKE_BUILD_TYPE=Debug $(ROOT_DIR) else ifneq ("${Workflow_DIR}workflow", "workflow") cd $(BUILD_DIR) && $(CMAKE3) -DWorkflow_DIR:STRING=${Workflow_DIR} $(ROOT_DIR) else cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR) endif make -C $(BUILD_DIR) -f Makefile check: mkdir -p $(BUILD_DIR) cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR) make -C $(BUILD_DIR) check CTEST_OUTPUT_ON_FAILURE=1 clean: ifeq ($(MAKE_FILE), $(wildcard $(MAKE_FILE))) -make -f Makefile clean endif rm -rf $(DEFAULT_BUILD_DIR) srpc-0.10.1/test/test_pb.proto000066400000000000000000000006721454502251400162530ustar00rootroot00000000000000syntax="proto2"; package unit; message AddRequest { required int32 a = 1; required int32 b = 2; }; message AddResponse { required int32 c = 1; }; message SubstrRequest { required string str = 1; required int32 idx = 2; optional int32 length = 3; }; message SubstrResponse { required string str = 1; }; service TestPB { rpc Add(AddRequest) returns (AddResponse); rpc Substr(SubstrRequest) returns (SubstrResponse); }; srpc-0.10.1/test/test_thrift.thrift000066400000000000000000000002131454502251400172760ustar00rootroot00000000000000namespace cpp unit; service TestThrift { i32 add(1:i32 a, 2:i32 b); string substr(1:string str, 2:i32 idx, 3:i32 length); }; srpc-0.10.1/test/unittest.cc000066400000000000000000000150421454502251400157110ustar00rootroot00000000000000/* Copyright (c) 2020 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include "workflow/WFOperator.h" #include "workflow/WFFacilities.h" #include "test_pb.srpc.h" #include "test_thrift.srpc.h" using namespace srpc; using namespace unit; class ForceShutdown { public: ~ForceShutdown() { google::protobuf::ShutdownProtobufLibrary(); } } g_holder; class TestPBServiceImpl : public TestPB::Service { public: void Add(AddRequest *request, AddResponse *response, RPCContext *ctx) override { response->set_c(request->a() + request->b()); } void Substr(SubstrRequest *request, SubstrResponse *response, RPCContext *ctx) override { if (request->has_length()) response->set_str(std::string(request->str(), request->idx(), request->length())); else response->set_str(std::string(request->str(), request->idx())); } }; class TestThriftServiceImpl : public TestThrift::Service { public: int32_t add(const int32_t a, const int32_t b) override { return a + b; } void substr(std::string& _return, const std::string& str, const int32_t idx, const int32_t length) override { if (length < 0) _return = std::string(str, idx); else _return = std::string(str, idx, length); } }; template void test_pb(SERVER& server) { WFFacilities::WaitGroup wg(1); RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; TestPBServiceImpl impl; server.add_service(&impl); EXPECT_TRUE(server.start("127.0.0.1", 9964) == 0) << "server start failed"; client_params.host = "127.0.0.1"; client_params.port = 9964; CLIENT client(&client_params); AddRequest req1; req1.set_a(123); req1.set_b(456); client.Add(&req1, [&client, &wg](AddResponse *response, RPCContext *ctx) { EXPECT_EQ(ctx->get_status_code(), RPCStatusOK); if (ctx->success()) { EXPECT_EQ(response->c(), 123 + 456); SubstrRequest req2; req2.set_str("hello world!"); req2.set_idx(6); client.Substr(&req2, [&wg](SubstrResponse *response, RPCContext *ctx) { EXPECT_EQ(ctx->get_status_code(), RPCStatusOK); EXPECT_TRUE(response->str() == "world!"); wg.done(); }); } else { wg.done(); } }); wg.wait(); AddResponse resp1; RPCSyncContext ctx1; client.Add(&req1, &resp1, &ctx1); EXPECT_EQ(ctx1.success, true); EXPECT_EQ(resp1.c(), 123 + 456); auto fr = client.async_Add(&req1); auto res = fr.get(); EXPECT_EQ(res.second.success, true); EXPECT_EQ(res.first.c(), 123 + 456); server.stop(); } template void test_thrift(SERVER& server) { WFFacilities::WaitGroup wg(1); RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; TestThriftServiceImpl impl; server.add_service(&impl); EXPECT_TRUE(server.start("127.0.0.1", 9965) == 0) << "server start failed"; client_params.host = "127.0.0.1"; client_params.port = 9965; CLIENT client(&client_params); TestThrift::addRequest req1; req1.a = 123; req1.b = 456; client.add(&req1, [&client, &wg](TestThrift::addResponse *response, RPCContext *ctx) { EXPECT_EQ(ctx->get_status_code(), RPCStatusOK); if (ctx->success()) { EXPECT_EQ(response->result, 123 + 456); TestThrift::substrRequest req2; req2.str = "hello world!"; req2.idx = 6; req2.length = -1; client.substr(&req2, [&wg](TestThrift::substrResponse *response, RPCContext *ctx) { EXPECT_EQ(ctx->get_status_code(), RPCStatusOK); EXPECT_TRUE(response->result == "world!"); wg.done(); }); } else { wg.done(); } }); wg.wait(); int32_t c = client.add(123, 456); EXPECT_EQ(client.thrift_last_sync_success(), true); EXPECT_EQ(c, 123 + 456); client.send_add(123, 456); c = client.recv_add(); EXPECT_EQ(client.thrift_last_sync_success(), true); EXPECT_EQ(c, 123 + 456); server.stop(); } TEST(SRPC, unittest) { RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT; SRPCServer server(&server_params); test_pb(server); test_thrift(server); } TEST(SRPCHttp, unittest) { RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT; SRPCHttpServer server(&server_params); test_pb(server); } TEST(BRPC, unittest) { RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT; BRPCServer server(&server_params); test_pb(server); } TEST(Thrift, unittest) { RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT; ThriftServer server(&server_params); test_thrift(server); } TEST(ThriftHttp, unittest) { RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT; ThriftHttpServer server(&server_params); test_thrift(server); } TEST(SRPC_COMPRESS, unittest) { WFFacilities::WaitGroup wg(1); RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT; RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; SRPCServer server(&server_params); TestPBServiceImpl impl; server.add_service(&impl); EXPECT_TRUE(server.start("127.0.0.1", 9964) == 0) << "server start failed"; client_params.host = "127.0.0.1"; client_params.port = 9964; TestPB::SRPCClient client(&client_params); AddRequest req; req.set_a(123); req.set_b(456); auto&& cb = [](AddResponse *response, RPCContext *ctx) { EXPECT_EQ(ctx->get_status_code(), RPCStatusOK); EXPECT_EQ(ctx->success(), true); EXPECT_EQ(response->c(), 123 + 456); }; auto *t1 = client.create_Add_task(cb); auto *t2 = client.create_Add_task(cb); auto *t3 = client.create_Add_task(cb); auto *t4 = client.create_Add_task(cb); t1->set_compress_type(RPCCompressSnappy); t2->set_compress_type(RPCCompressGzip); t3->set_compress_type(RPCCompressZlib); t4->set_compress_type(RPCCompressLz4); t1->serialize_input(&req); t2->serialize_input(&req); t3->serialize_input(&req); t4->serialize_input(&req); auto& par = *t1 * t2 * t3 * t4; par.set_callback([&wg](const ParallelWork *par) { wg.done(); }); par.start(); wg.wait(); server.stop(); } srpc-0.10.1/test/var_unittest.cc000066400000000000000000000044431454502251400165640ustar00rootroot00000000000000/* Copyright (c) 2023 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include "srpc/rpc_var.h" using namespace srpc; RPCVar *get_and_reduce(std::string&& var_name) { RPCVar *result = NULL; RPCVarGlobal *global_var = RPCVarGlobal::get_instance(); std::unordered_map::iterator it; global_var->mutex.lock(); for (RPCVarLocal *local : global_var->local_vars) { local->mutex.lock(); it = local->vars.find(var_name); if (it != local->vars.end()) { if (result == NULL) result = it->second->create(true); else result->reduce(it->second->get_data(), it->second->get_size()); } local->mutex.unlock(); } global_var->mutex.unlock(); return result; } TEST(var_unittest, GaugeVar) { GaugeVar *req = new GaugeVar("req", "total req"); RPCVarLocal::get_instance()->add("req", req); RPCVarFactory::gauge("req")->increase(); GaugeVar *gauge = (GaugeVar *)get_and_reduce("req"); EXPECT_EQ(gauge->get(), 1.0); delete gauge; } TEST(var_unittest, TimedGaugeVar) { TimedGaugeVar *qps = new TimedGaugeVar("qps", "req per second", std::chrono::seconds(1), 4); RPCVarLocal::get_instance()->add("qps", qps); RPCVarFactory::gauge("qps")->increase(); RPCVarFactory::gauge("qps")->increase(); GaugeVar *gauge = (GaugeVar *)get_and_reduce("qps"); EXPECT_EQ(gauge->get(), 2.0); delete gauge; usleep(500000); RPCVarFactory::gauge("qps")->increase(); gauge = (GaugeVar *)get_and_reduce("qps"); EXPECT_EQ(gauge->get(), 3.0); delete gauge; usleep(500000); gauge = (GaugeVar *)get_and_reduce("qps"); EXPECT_EQ(gauge->get(), 1.0); delete gauge; } srpc-0.10.1/third_party/000077500000000000000000000000001454502251400150735ustar00rootroot00000000000000srpc-0.10.1/third_party/BUILD000066400000000000000000000000001454502251400156430ustar00rootroot00000000000000srpc-0.10.1/third_party/lz4/000077500000000000000000000000001454502251400156045ustar00rootroot00000000000000srpc-0.10.1/third_party/lz4.BUILD000066400000000000000000000005331454502251400163660ustar00rootroot00000000000000cc_library( name = "lz4", srcs = [ 'lib/lz4.c', 'lib/lz4hc.c', 'lib/lz4frame.c', 'lib/xxhash.c', ], hdrs = [ 'lib/lz4.h', 'lib/lz4.c', 'lib/lz4hc.h', 'lib/lz4frame.h', 'lib/xxhash.h', ], includes = ['lib'], visibility = ["//visibility:public"], ) srpc-0.10.1/third_party/snappy/000077500000000000000000000000001454502251400164055ustar00rootroot00000000000000srpc-0.10.1/third_party/snappy.BUILD000066400000000000000000000017451454502251400171750ustar00rootroot00000000000000genrule( name = "snappy_stubs_public_h", srcs = [ "snappy-stubs-public.h.in", ], outs = [ "snappy-stubs-public.h", ], cmd = "sed 's/$${HAVE_SYS_UIO_H_01}/true/g' $(<) | " + "sed 's/$${PROJECT_VERSION_MAJOR}/0/g' | " + "sed 's/$${PROJECT_VERSION_MINOR}/9/g' | " + "sed 's/$${PROJECT_VERSION_PATCH}/2/g' >$(@)", ) cc_library( name = "snappy", srcs = [ "snappy.cc", "snappy-c.cc", "snappy-sinksource.cc", "snappy-stubs-internal.cc", ], hdrs = [ ":snappy_stubs_public_h", "snappy.h", "snappy-c.h", "snappy-internal.h", "snappy-sinksource.h", "snappy-stubs-internal.h", "snappy-stubs-public.h.in", ], copts = [ "-Wno-non-virtual-dtor", "-Wno-unused-variable", "-Wno-implicit-fallthrough", "-Wno-unused-function", ], includes = ["."], visibility = ["//visibility:public"], ) srpc-0.10.1/tools/000077500000000000000000000000001454502251400137025ustar00rootroot00000000000000srpc-0.10.1/tools/CMakeLists.txt000066400000000000000000000012531454502251400164430ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(srpc-ctl VERSION 0.10.1 LANGUAGES C CXX ) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_BUILD_TYPE RelWithDebInfo) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}) set(generator_code "../src/generator/generator.cc" "../src/generator/parser.cc") set(srpc_ctl_code "srpc_ctl.cc" "srpc_config.cc" "srpc_controller.cc" "srpc_basic_controller.cc" "srpc_rpc_controller.cc" "srpc_proxy_controller.cc") include_directories("../src/") add_executable(srpc ${srpc_ctl_code} ${generator_code}) target_link_libraries(srpc ${LIBRARY_NAME}) srpc-0.10.1/tools/GNUmakefile000066400000000000000000000005541454502251400157600ustar00rootroot00000000000000ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) BUILD_DIR := build.cmake .PHONY: all clean all: base make -C $(BUILD_DIR) -f Makefile base: mkdir -p $(BUILD_DIR) cd $(BUILD_DIR) && cmake -D CMAKE_BUILD_TYPE=Debug $(ROOT_DIR) clean: ifeq ($(BUILD_DIR), $(wildcard $(BUILD_DIR))) make -C $(BUILD_DIR) clean rm -rf $(BUILD_DIR) endif srpc-0.10.1/tools/README.md000066400000000000000000000260201454502251400151610ustar00rootroot00000000000000[中文版入口](README_cn.md) # srpc tools ### Simple generator for building Workflow and SRPC projects. ## 1. COMPILE ``` git clone https://github.com/sogou/srpc cd srpc/tools make ``` ## 2. USAGE ``` ./srpc ``` ``` Description: Simple generator for building Workflow and SRPC projects. Usage: ./srpc [FLAGS] Available Commands: http - create project with both client and server redis - create project with both client and server rpc - create project with both client and server proxy - create proxy for some client and server protocol file - create project with asynchronous file service compute - create project with asynchronous computing service ``` ## 3. START Execute this simple example: ```sh ./srpc http project_name1 ``` And we will get this on the screen, new project is in `./project_name1/`. ``` Success: make project path " ./project_name1/ " done. Commands: cd ./project_name1/ make -j Execute: ./server ./client ``` Let's take a look at the project: ``` cd ./project_name1/ && tree ``` These files are generated. ``` . ├── CMakeLists.txt ├── GNUmakefile ├── client.conf ├── client_main.cc ├── config │   ├── Json.cc │   ├── Json.h │   ├── config.cc │   ├── config.h │   └── util.h ├── full.conf ├── server.conf └── server_main.cc 2 directories, 12 files ``` And we can try to make the project accorrding to the suggestions above. ## 4. HTTP COMMAND commands for HTTP: ``` ./srpc http ``` will get the following instructions: ``` Missing: PROJECT_NAME Usage: ./srpc http [FLAGS] Example: ./srpc redis my_http_project Available Flags: -o : project output path (default: CURRENT_PATH) -d : path of dependencies (default: COMPILE_PATH) ``` ## 5. RPC COMMAND commands for RPCs: ``` ./srpc rpc ``` will get the following instructions: ``` Missing: PROJECT_NAME Usage: ./srpc rpc [FLAGS] Example: ./srpc redis my_rpc_project Available Flags: -r : rpc type [ SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: SRPC) -o : project output path (default: CURRENT_PATH) -s : service name (default: PROJECT_NAME) -i : idl type [ protobuf | thrift ] (default: protobuf) -x : data type [ protobuf | thrift | json ] (default: idl type. json for http) -c : compress type [ gzip | zlib | snappy | lz4 ] (default: no compression) -d : path of dependencies (default: COMPILE_PATH) -f : specify the idl_file to generate codes (default: template/rpc/IDL_FILE) -p : specify the path for idl_file to depend (default: template/rpc/) ``` We can specified our IDL files with the following command: ``` ./srpc rpc rpc_example -f test_proto/test.proto ``` And we can see the infomation when generating files, which is similar to srpc_genrator. ``` Info: srpc generator begin. proto file: [/root/srpc/tools/rpc_example/test.proto] Successfully parse service block [message] : EchoRequest Successfully parse service block [message] : EchoResponse Successfully parse service block [service] : new_test Successfully parse method:Echo req:EchoRequest resp:EchoResponse finish parsing proto file: [/root/srpc/tools/rpc_example/test.proto] [Generator] generate srpc files: /root/srpc/tools/rpc_example/test.srpc.h [Generator Done] [Generator] generate server files: /root/srpc/tools/rpc_example/server_main.cc, client files: /root/srpc/tools/rpc_example/client_main.cc Info: srpc generator done. Success: make project path " /root/srpc/tools/rpc_example/ " done. Commands: cd /root/srpc/tools/rpc_example/ make -j Execute: ./server ./client ``` ## 6. API COMMAND This command is to create an IDL file, which is convenient for us to create rpc project after changing the file. The command is as follows: ``` Missing: FILE_NAME Usage: ./srpc api [FLAGS] Example: ./srpc api my_api Available Flags: -o : file output path (default: CURRENT_PATH) -i : idl type [ protobuf | thrift ] (default: protobuf) ``` Create a file in proto format by default: ``` ./srpc api test ``` ``` Success: Create api file test.proto at path /root/srpc/tools done. Suggestions: Modify the api file as you needed. And make rpc project base on this file with the following command: ./srpc rpc my_rpc_project -f test.proto -p /root/srpc/tools ``` Then we can create an rpc project through the above suggested commands. The rpc project uses the test.proto file to create the generated code. ## 7. REDIS COMMAND commands for REDIS: ``` ./srpc redis ``` will get the following instructions: ``` Missing: PROJECT_NAME Usage: ./srpc redis [FLAGS] Example: ./srpc redis my_redis_project Available Flags: -o : project output path (default: CURRENT_PATH) -d : path of dependencies (default: COMPILE_PATH) ``` Make a project with the instructions, we can get the simple redis server and client. The client will send a basic command `SET k1 v1`, and the server will reply `OK` for every request. ``` ./server Redis server started, port 6379 redis server get cmd: [SET] from peer address: 127.0.0.1:60665, seq: 0. ``` ``` ./client Redis client state = 0 error = 0 response: OK ``` If there is user name and password for redis server, client may fill them into client.conf: ``` 1 { 2 "client": 3 { 4 "remote_host": "127.0.0.1", 5 "remote_port": 6379, 6 "retry_max": 2, 7 "user_name": "root", 8 "password": "" 9 } 10 } ``` ## 8. PROXY COMMAND commands for PROXY: ``` ./srpc proxy ``` will get the following instructions: ``` Missing: PROJECT_NAME Usage: ./srpc proxy [FLAGS] Example: ./srpc redis my_proxy_project Available Flags: -c : client type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http) -s : server type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http) -o : project output path (default: CURRENT_PATH) -d : path of dependencies (default: COMPILE_PATH) ``` Let's make a project with diffrent protocol: ``` ./srpc proxy srpc_trpc_proxy_example -c SRPC -s TRPC ``` ``` Success: make project path " ./srpc_trpc_proxy_example/ " done. Commands: cd ./srpc_trpc_proxy_example/ make -j Execute: ./server ./proxy ./client ``` Check the files in directory: ``` cd srpc_trpc_proxy_example && tree ``` ``` . ├── CMakeLists.txt ├── GNUmakefile ├── client.conf ├── client_main.cc ├── config │   ├── Json.cc │   ├── Json.h │   ├── config.cc │   └── config.h ├── proxy.conf ├── proxy_main.cc ├── server.conf ├── server_main.cc └── srpc_trpc_proxy_example.proto 2 directories, 13 files ``` Execute `./server` `./proxy` and `./client` on the three sessions respectively, and we will see that the client sends trpc protocol requests "Hello, this is sync request!" and "Hello, this is async request!" to the proxy, and the proxy receives the request and send to server as srpc protocol. SRPC server fill the response "Hi back" and will finnally transfer back to client by proxy. ``` ./server srpc_trpc_proxy_example TRPC server started, port 1412 get req: message: "Hello, this is sync request!" get req. message: "Hello, this is async request!" ``` ``` ./proxy srpc_trpc_proxy_example [SRPC]-[TRPC] proxy start, port 1411 srpc_trpc_proxy_example proxy get request from client. ip : 127.0.0.1 message: "Hello, this is sync request!" srpc_trpc_proxy_example proxy get request from client. ip : 127.0.0.1 message: "Hello, this is async request!" ``` ``` ./client sync resp. message: "Hi back" async resp. message: "Hi back" ``` ## 9. FILE COMMAND This is a simple file server. File reading is asynchronous, and the threads for process( ) will not be blocked by file IOs. ``` ./srpc file file_project ``` will get the following infomation: ``` Success: make project path " ./file_project/ " done. Commands: cd ./file_project/ make -j Execute: ./server Try file service: curl localhost:8080/index.html curl -i localhost:8080/a/b/ ``` Check the files in directories: ``` . ├── CMakeLists.txt ├── GNUmakefile ├── config │   ├── Json.cc │   ├── Json.h │   ├── config.cc │   └── config.h ├── file_service.cc ├── file_service.h ├── html │   ├── 404.html │   ├── 50x.html │   └── index.html ├── server.conf └── server_main.cc 3 directories, 13 files ``` Also check the `server.conf`, we can see the `root` and `error_page` are added into file service. We can specify the root to find files for this service, and fill the page coresponding to the error codes. ``` 1 { 2 "server": 3 { 4 "port": 8080, 5 "root": "./html/", 6 "error_page" : [ 7 { 8 "error" : [ 404 ], 9 "page" : "404.html" 10 }, 11 { 12 "error" : [ 500, 502, 503, 504], 13 "page" : "50x.html" 14 } 15 ] 16 } 17 } ``` After we execute `make` and run the server with `./server`, we can make requests with `curl`: example 1: read the file `index.html` in root path `./html/`. ``` curl localhost:8080/index.html Hello from workflow and srpc file server! ``` example 2: read the file `/a/b/`. This does't exist, so the error page `404.html` for `404` will be return as we fill them into `server.conf` above. ``` curl -i localhost:8080/a/b/ HTTP/1.1 404 Not Found Server: SRPC HTTP File Server Content-Length: 59 Connection: Keep-Alive This is not the web page you are looking for. ``` The log printed by server: ``` ./server http file service start, port 8080 file service get request: /a/b/ ``` ## 10. COMPUTE COMMAND Next is a simple calculation server, similarly, the calculation will not block the server processing thread. ``` ./srpc compute compute_test ``` Through the above command, we can create a project, which receives the url request as the parameter n, and performs Fibonacci calculation. ``` Success: make project path " compute_test/ " done. Commands: cd compute_test/ make -j Execute: ./server Try compute with n=8: curl localhost:8080/8 ``` Enter the `compute_test/` directory and use `make` to compile and execute `./server` to run. Then we can use curl or browser to enter `localhost:8080/8` to calculate the 8th Fibonacci number. Here take curl as an example: ``` curl localhost:8080/8

0 + 1 = 1.

1 + 1 = 2.

1 + 2 = 3.

2 + 3 = 5.

3 + 5 = 8.

5 + 8 = 13.

The No. 8 Fibonacci number is: 13.

``` We can see the calculation steps inside the server. This computing example of the server uses go_task to encapsulate a function. Welcome to try more computing scheduling. srpc-0.10.1/tools/README_cn.md000066400000000000000000000302231454502251400156410ustar00rootroot00000000000000[English version](README.md) # srpc小工具 ### 一个帮你快速生成Workflow和SRPC项目的脚手架小工具。 ## 1. 编译 先从github上把srpc项目拷贝下来,小工具代码在srpc/tools/目录下,执行`make`即可编译。 ``` git clone https://github.com/sogou/srpc cd srpc/tools make ``` ## 2. 用法 执行`./srpc`即可看到小工具的用法介绍: ``` ./srpc ``` ``` Description: Simple generator for building Workflow and SRPC projects. Usage: ./srpc [FLAGS] Available Commands: http - create project with both client and server redis - create project with both client and server rpc - create project with both client and server api - create protobuf or thrift IDL api proxy - create proxy for some client and server protocol file - create project with asynchronous file service compute - create project with asynchronous computing service ``` ## 3. 入门 我们先从最简单的命令开始入门: ```sh ./srpc http project_name1 ``` 然后就可以看到屏幕上显示,项目建立在了新目录`project_name1/`中,并且带有编译和执行的提示命令。 ``` Success: make project path project_name1/ done. Commands: cd project_name1/ make -j Execute: ./server ./client ``` 打开目录,我们查看一下有什么文件: ``` cd ./project_name1/ && tree ``` ``` . ├── CMakeLists.txt ├── GNUmakefile ├── client.conf ├── client_main.cc ├── config │   ├── Json.cc │   ├── Json.h │   ├── config.cc │   ├── config.h │   └── util.h ├── full.conf ├── server.conf └── server_main.cc 2 directories, 12 files ``` 然后我们就可以根据上面执行`srpc`命令时所看到的指引,编译和执行这个项目。 ## 4. HTTP 创建HTTP项目的用法如下,可以创建http协议的server和client。其中server和client里的示例代码都可以自行改动,配置文件`server.conf`和`client.conf`里也可以指定基本的配置项,cmake编译文件都已经生成好了,整个项目可以直接拿走使用。 ``` ./srpc http ``` ``` Missing: PROJECT_NAME Usage: ./srpc http [FLAGS] Example: ./srpc http my_http_project Available Flags: -o : project output path (default: CURRENT_PATH) -d : path of dependencies (default: COMPILE_PATH) ``` ## 5. RPC 创建RPC项目的用法如下,包括了多种rpc协议,protobuf或thrift的文件: ``` ./srpc rpc ``` ``` Missing: PROJECT_NAME Usage: ./srpc rpc [FLAGS] Example: ./srpc rpc my_rpc_project Available Flags: -r : rpc type [ SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: SRPC) -o : project output path (default: CURRENT_PATH) -s : service name (default: PROJECT_NAME) -i : idl type [ protobuf | thrift ] (default: protobuf) -x : data type [ protobuf | thrift | json ] (default: idl type. json for http) -c : compress type [ gzip | zlib | snappy | lz4 ] (default: no compression) -d : path of dependencies (default: COMPILE_PATH) -f : specify the idl_file to generate codes (default: template/rpc/IDL_FILE) -p : specify the path for idl_file to depend (default: template/rpc/) ``` 我们通过以下命令,试一下指定要创建的RPC项目所依赖的proto文件: ``` ./srpc rpc rpc_example -f test_proto/test.proto ``` 然后就可以看到一些生成代码多信息,这和原先使用`srpc_genrator`时所看到的是类似的。 ``` Info: srpc generator begin. proto file: [/root/srpc/tools/rpc_example/test.proto] Successfully parse service block [message] : EchoRequest Successfully parse service block [message] : EchoResponse Successfully parse service block [service] : new_test Successfully parse method:Echo req:EchoRequest resp:EchoResponse finish parsing proto file: [/root/srpc/tools/rpc_example/test.proto] [Generator] generate srpc files: /root/srpc/tools/rpc_example/test.srpc.h [Generator Done] [Generator] generate server files: /root/srpc/tools/rpc_example/server_main.cc, client files: /root/srpc/tools/rpc_example/client_main.cc Info: srpc generator done. Success: make project path rpc_example/ done. Commands: cd rpc_example/ make -j Execute: ./server ./client ``` ## 6. API API命令用于简单地创建一个IDL文件,这样方便我们改完文件之后再进行rpc项目调查创建。命令如下: ``` Missing: FILE_NAME Usage: ./srpc api [FLAGS] Example: ./srpc api my_api Available Flags: -o : file output path (default: CURRENT_PATH) -i : idl type [ protobuf | thrift ] (default: protobuf) ``` 我们通过以下命令,可以创建一个默认为proto格式的文件: ``` ./srpc api test ``` ``` Success: Create api file test.proto at path /root/srpc/tools done. Suggestions: Modify the api file as you needed. And make rpc project base on this file with the following command: ./srpc rpc my_rpc_project -f test.proto -p /root/srpc/tools ``` 之后就可以通过以上建议的命令创建一个rpc项目了,rpc项目以test.proto文件创建生成代码,使用方法如`RPC`部分介绍。 ## 7. REDIS 创建REDIS协议的client和server,命令如下: ``` ./srpc redis ``` will get the following instructions: ``` Missing: PROJECT_NAME Usage: ./srpc redis [FLAGS] Example: ./srpc redis my_redis_project Available Flags: -o : project output path (default: CURRENT_PATH) -d : path of dependencies (default: COMPILE_PATH) ``` 根据以上指引我们创建了一个项目后,就可以得到最简单的redis server和client。client就简单地实现了发送`SET k1 v1`命令,而server无论收到什么都会简单地回复一个`OK`。我们可以用这简单的示例,改造一个可以请求任何redis协议服务的client,也可以构造一个简单的redis服务器。 ``` ./server Redis server start, port 6379 redis server get cmd: [SET] from peer address: 127.0.0.1:60665, seq: 0. ``` ``` ./client Redis client state = 0 error = 0 response: OK ``` 如果client有填写用户名和密码的需求,可以填到`client.conf`中。我们打开这个配置文件看看: ``` 1 { 2 "client": 3 { 4 "remote_host": "127.0.0.1", 5 "remote_port": 6379, 6 "retry_max": 2, 7 "user_name": "root", 8 "password": "" 9 } 10 } ``` ## 8. PROXY 这个命令用于构建一个转发服务器,并且还有与其协议相关的server和client。 ``` ./srpc proxy ``` 执行上述命令,我们可以看到proxy命令的指引: ``` Missing: PROJECT_NAME Usage: ./srpc proxy [FLAGS] Example: ./srpc redis my_proxy_project Available Flags: -c : client type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http) -s : server type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http) -o : project output path (default: CURRENT_PATH) -d : path of dependencies (default: COMPILE_PATH) ``` 让我们来试一下,创建一个转发不同协议的项目: ``` ./srpc proxy srpc_trpc_proxy_example -c SRPC -s TRPC ``` ``` Success: make project path srpc_trpc_proxy_example/ " done. Commands: cd srpc_trpc_proxy_example/ make -j Execute: ./server ./proxy ./client ``` 查看新创建的项目中有什么文件: ``` cd srpc_trpc_proxy_example && tree ``` ``` . ├── CMakeLists.txt ├── GNUmakefile ├── client.conf ├── client_main.cc ├── config │   ├── Json.cc │   ├── Json.h │   ├── config.cc │   └── config.h ├── proxy.conf ├── proxy_main.cc ├── server.conf ├── server_main.cc └── srpc_trpc_proxy_example.proto 2 directories, 13 files ``` 分别在三个终端执行`./server` `./proxy` 和 `./client`,我们可以看到,client发送了trpc协议的请求"Hello, this is sync request!" 和一个异步请求Hello, this is async request!"给proxy,而proxy收到之后把请求用srpc协议发给了server。SRPC server填了回复"Hi back"并通过刚才的proxy路线转回给了client,期间转发纯异步,不会阻塞任何线程。 ``` ./server srpc_trpc_proxy_example TRPC server start, port 1412 get req: message: "Hello, this is sync request!" get req. message: "Hello, this is async request!" ``` ``` ./proxy srpc_trpc_proxy_example [SRPC]-[TRPC] proxy start, port 1411 srpc_trpc_proxy_example proxy get request from client. ip : 127.0.0.1 message: "Hello, this is sync request!" srpc_trpc_proxy_example proxy get request from client. ip : 127.0.0.1 message: "Hello, this is async request!" ``` ``` ./client sync resp. message: "Hi back" async resp. message: "Hi back" ``` ## 9. FILE 这是一个简单的文件服务器,文件读取都是异步的,不会因为读文件阻塞当前服务器的处理线程: ``` ./srpc file file_project ``` ``` Success: make project path file_project/ done. Commands: cd ./file_project/ make -j Execute: ./server Try file service: curl localhost:8080/index.html curl -i localhost:8080/a/b/ ``` 我们通过上述命令创建之后,可以看看文件服务器的目录结构如下: ``` . ├── CMakeLists.txt ├── GNUmakefile ├── config │   ├── Json.cc │   ├── Json.h │   ├── config.cc │   └── config.h ├── file_service.cc ├── file_service.h ├── html │   ├── 404.html │   ├── 50x.html │   └── index.html ├── server.conf └── server_main.cc 3 directories, 13 files ``` 打开`server.conf`,就可以看到我们为文件服务器添加的具体配置项:`root`和`error_page`。我们可以通过root去指定打开文件的根目录,以及通过error_page去关联具体的错误码和它们所要返回作body的页面名称。 ``` 1 { 2 "server": 3 { 4 "port": 8080, 5 "root": "./html/", 6 "error_page" : [ 7 { 8 "error" : [ 404 ], 9 "page" : "404.html" 10 }, 11 { 12 "error" : [ 500, 502, 503, 504], 13 "page" : "50x.html" 14 } 15 ] 16 } 17 } ``` 我们执行`make`进行编译,然后执行`./server`把文件服务器跑起来,然后用`curl`进行测试: 示例1:在根目录`./html/`下读取文件`index.html`(即使请求localhost:8080,默认也是读index.html)。 ``` curl localhost:8080/index.html Hello from workflow and srpc file server! ``` 示例2:读文件`/a/b/`,这个文件不存在,所以我们根据上面配置文件`server.conf`中所指定的,填入`404`错误码会返回页面`404.html`的内容。 ``` curl -i localhost:8080/a/b/ HTTP/1.1 404 Not Found Server: SRPC HTTP File Server Content-Length: 59 Connection: Keep-Alive This is not the web page you are looking for. ``` 以下信息在server端可以看到: ``` ./server http file service start, port 8080 file service get request: /a/b/ ``` ## 10. COMPUTE 接下来是一个简单的计算服务器,同理,计算也不会阻塞当前服务器的处理线程: ``` ./srpc compute compute_test ``` 通过以上命令,我们可以创建一个小项目,项目默认接收url请求作为参数n,并进行斐波那契计算。 ``` Success: make project path compute_test/ done. Commands: cd compute_test/ make -j Execute: ./server Try compute with n=8: curl localhost:8080/8 ``` 进入`compute_test/`目录执行`make`,并执行`./server`运行起来。然后我们就可以使用curl或者浏览器输入`localhost:8080/8`,计算第8个斐波那契数是多少。这里以`curl`为例: ``` curl localhost:8080/8

0 + 1 = 1.

1 + 1 = 2.

1 + 2 = 3.

2 + 3 = 5.

3 + 5 = 8.

5 + 8 = 13.

The No. 8 Fibonacci number is: 13.

``` 我们看到了server内部的计算步骤。server的计算例子使用了go_task去封装一个计算函数,欢迎尝试更多的计算调度。 srpc-0.10.1/tools/srpc_basic_controller.cc000066400000000000000000000342551454502251400205750ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include "srpc_controller.h" using DEFAULT_FILES = std::vector; static std::string server_process_codes(uint8_t type) { if (type == PROTOCOL_TYPE_HTTP) return std::string(R"( fprintf(stderr, "http server get request_uri: %s\n", task->get_req()->get_request_uri()); print_peer_address(task); task->get_resp()->append_output_body("Hello from server!");)"); if (type == PROTOCOL_TYPE_REDIS) return std::string(R"( protocol::RedisRequest *req = task->get_req(); protocol::RedisResponse *resp = task->get_resp(); protocol::RedisValue val; std::string cmd; if (req->parse_success() == false || req->get_command(cmd) == false) return; fprintf(stderr, "redis server get cmd: [%s] from ", cmd.c_str()); print_peer_address(task); val.set_status("OK"); // example: return OK to every requests resp->set_result(val);)"); return std::string("Unknown type"); } static std::string client_redirect_codes(uint8_t type) { if (type == PROTOCOL_TYPE_HTTP) return std::string(R"( config.redirect_max(),)"); return std::string(""); } static std::string client_task_callback_codes(uint8_t type) { if (type == PROTOCOL_TYPE_HTTP) return std::string(R"( if (state == WFT_STATE_SUCCESS) // print server response body { const void *body; size_t body_len; task->get_resp()->get_parsed_body(&body, &body_len); fwrite(body, 1, body_len, stdout); fflush(stdout); })"); if (type == PROTOCOL_TYPE_REDIS) return std::string(R"( protocol::RedisResponse *resp = task->get_resp(); protocol::RedisValue val; if (state == WFT_STATE_SUCCESS && resp->parse_success() == true) { resp->get_result(val); fprintf(stderr, "response: %s\n", val.string_value().c_str()); })"); return std::string("Unknown type"); } static std::string client_set_request_codes(uint8_t type) { if (type == PROTOCOL_TYPE_HTTP) return std::string(R"( protocol::HttpRequest *req = task->get_req(); req->set_request_uri("/client_request"); // will send to server by proxy )"); if (type == PROTOCOL_TYPE_REDIS) return std::string(R"( task->get_req()->set_request("SET", {"k1", "v1"}); )"); return std::string("Unknown type"); } static std::string username_passwd_codes(uint8_t type) { if (type == PROTOCOL_TYPE_REDIS || type == PROTOCOL_TYPE_MYSQL) return std::string(R"(config.client_user_name() + std::string(":") + config.client_password() + std::string("@") +)"); return std::string(""); } static uint8_t get_protocol_type(const struct srpc_config *config, uint8_t type) { if (config->type == COMMAND_HTTP || config->type == COMMAND_COMPUTE || (config->type == COMMAND_PROXY && type == PROTOCOL_TYPE_HTTP)) return PROTOCOL_TYPE_HTTP; if (config->type == COMMAND_REDIS || (config->type == COMMAND_PROXY && type == PROTOCOL_TYPE_REDIS)) return PROTOCOL_TYPE_REDIS; if (config->type == COMMAND_MYSQL || (config->type == COMMAND_PROXY && type == PROTOCOL_TYPE_MYSQL)) return PROTOCOL_TYPE_MYSQL; return PROTOCOL_TYPE_MAX; } static inline uint8_t get_client_protocol_type(const struct srpc_config *config) { return get_protocol_type(config, config->proxy_client_type); } static inline uint8_t get_server_protocol_type(const struct srpc_config *config) { return get_protocol_type(config, config->proxy_server_type); } bool basic_server_config_transform(const std::string& format, FILE *out, const struct srpc_config *config) { unsigned short port; if (get_server_protocol_type(config) == PROTOCOL_TYPE_HTTP) port = 8080; else if (get_server_protocol_type(config) == PROTOCOL_TYPE_REDIS) port = 6379; else if (get_server_protocol_type(config) == PROTOCOL_TYPE_MYSQL) port = 3306; else port = 1412; size_t len = fprintf(out, format.c_str(), port); return len > 0; } bool basic_client_config_transform(const std::string& format, FILE *out, const struct srpc_config *config) { unsigned short port; std::string redirect_code; std::string user_and_passwd; if (get_client_protocol_type(config) == PROTOCOL_TYPE_HTTP) { port = 8080; redirect_code = R"( "redirect_max": 2,)"; } else if (get_client_protocol_type(config) == PROTOCOL_TYPE_REDIS) { port = 6379; user_and_passwd = R"(, "user_name": "root", "password": "")"; } else if (get_client_protocol_type(config) == PROTOCOL_TYPE_MYSQL) port = 3306; else port = 1412; // for proxy if (config->type == COMMAND_PROXY) { if (get_client_protocol_type(config) == PROTOCOL_TYPE_HTTP) port = 8888; else port = port - 1; } size_t len = fprintf(out, format.c_str(), port, redirect_code.c_str(), user_and_passwd.c_str()); return len > 0; } bool basic_server_transform(const std::string& format, FILE *out, const struct srpc_config *config) { uint8_t server_type = get_server_protocol_type(config); const char *type = get_type_string(server_type); size_t len = fprintf(out, format.c_str(), type, type, server_process_codes(server_type).c_str(), type, type); return len > 0; } bool basic_client_transform(const std::string& format, FILE *out, const struct srpc_config *config) { uint8_t client_type = get_client_protocol_type(config); const char *type = get_type_string(client_type); std::string client_lower = type; std::transform(client_lower.begin(), client_lower.end(), client_lower.begin(), ::tolower); std::string client_request_codes; if ((config->type == COMMAND_PROXY && client_type == PROTOCOL_TYPE_HTTP) || config->type == COMMAND_REDIS) client_request_codes = client_set_request_codes(client_type); size_t len = fprintf(out, format.c_str(), type, type, type, client_task_callback_codes(client_type).c_str(), client_lower.c_str(), username_passwd_codes(client_type).c_str(), type, client_lower.c_str(), client_redirect_codes(client_type).c_str(), client_request_codes.c_str()); return len > 0; } static void basic_default_file_initialize(DEFAULT_FILES& files) { struct CommandController::file_info info; info = { "basic/server.conf", "server.conf", basic_server_config_transform }; files.push_back(info); info = { "basic/client.conf", "client.conf", basic_client_config_transform }; files.push_back(info); info = { "basic/server_main.cc", "server_main.cc", basic_server_transform }; files.push_back(info); info = { "basic/client_main.cc", "client_main.cc", basic_client_transform }; files.push_back(info); info = { "common/config.json", "full.conf", nullptr }; files.push_back(info); info = { "common/util.h", "config/util.h", nullptr }; files.push_back(info); info = { "common/CMakeLists.txt", "CMakeLists.txt", common_cmake_transform }; files.push_back(info); info = { "common/GNUmakefile", "GNUmakefile", nullptr }; files.push_back(info); info = { "config/Json.h", "config/Json.h", nullptr }; files.push_back(info); info = { "config/Json.cc", "config/Json.cc", nullptr }; files.push_back(info); info = { "config/config_simple.h", "config/config.h", nullptr }; files.push_back(info); info = { "config/config_simple.cc", "config/config.cc", nullptr }; files.push_back(info); } static bool get_opt_args(int argc, const char **argv, struct srpc_config *config) { int c; while ((c = getopt(argc, (char * const *)argv, "o:t:d:")) >= 0) { switch (c) { case 'o': if (sscanf(optarg, "%s", config->output_path) != 1) return false; break; case 't': if (sscanf(optarg, "%s", config->template_path) != 1) return false; //TODO: break; case 'd': config->specified_depend_path = true; memset(config->depend_path, 0, MAXPATHLEN); if (sscanf(optarg, "%s", config->depend_path) != 1) return false; break; default: printf(COLOR_RED"Error:\n Unknown args : " COLOR_BLUE"%s\n\n" COLOR_OFF, argv[optind - 1]); return false; } } return true; } static bool basic_get_opt(int argc, const char **argv, struct srpc_config *config) { optind = 2; if (get_opt_args(argc, argv, config) == false) return false; if (optind == argc) { printf(COLOR_RED "Missing: PROJECT_NAME\n\n" COLOR_OFF); return false; } config->project_name = argv[optind]; optind++; if (get_opt_args(argc, argv, config) == false) return false; if (config->project_name == NULL) { printf(COLOR_RED "Missing: PROJECT_NAME\n\n" COLOR_OFF); return false; } return true; } static void basic_print_usage(const char *name, const char *command) { printf(COLOR_PINK"Usage:\n" COLOR_INFO" %s " COLOR_COMMAND "%s " COLOR_INFO"" COLOR_FLAG " [FLAGS]\n\n" COLOR_PINK"Example:\n" COLOR_PURPLE" %s %s my_%s_project\n\n" COLOR_PINK"Available Flags:\n" COLOR_FLAG" -o : " COLOR_WHITE"project output path (default: CURRENT_PATH)\n" COLOR_FLAG" -d : " COLOR_WHITE"path of dependencies (default: COMPILE_PATH)\n" COLOR_OFF, name, command, name, command, command); } HttpController::HttpController() { this->config.type = COMMAND_HTTP; basic_default_file_initialize(this->default_files); } void HttpController::print_usage(const char *name) const { basic_print_usage(name, "http"); } bool HttpController::get_opt(int argc, const char **argv) { return basic_get_opt(argc, argv, &this->config); } RedisController::RedisController() { this->config.type = COMMAND_REDIS; basic_default_file_initialize(this->default_files); } void RedisController::print_usage(const char *name) const { basic_print_usage(name, "redis"); } bool RedisController::get_opt(int argc, const char **argv) { return basic_get_opt(argc, argv, &this->config); } FileServiceController::FileServiceController() { this->config.type = COMMAND_FILE; struct file_info info; info = { "file/server.conf", "server.conf", nullptr }; this->default_files.push_back(info); info = { "file/server_main.cc", "server_main.cc", nullptr }; this->default_files.push_back(info); info = { "file/file_service.cc", "file_service.cc", nullptr }; this->default_files.push_back(info); info = { "file/file_service.h", "file_service.h", nullptr }; this->default_files.push_back(info); info = { "file/index.html", "html/index.html", nullptr }; this->default_files.push_back(info); info = { "file/404.html", "html/404.html", nullptr }; this->default_files.push_back(info); info = { "file/50x.html", "html/50x.html", nullptr }; this->default_files.push_back(info); info = { "common/CMakeLists.txt", "CMakeLists.txt", common_cmake_transform }; this->default_files.push_back(info); info = { "common/GNUmakefile", "GNUmakefile", nullptr }; this->default_files.push_back(info); info = { "config/Json.h", "config/Json.h", nullptr }; this->default_files.push_back(info); info = { "config/Json.cc", "config/Json.cc", nullptr }; this->default_files.push_back(info); info = { "config/config_simple.h", "config/config.h", nullptr }; this->default_files.push_back(info); info = { "config/config_simple.cc", "config/config.cc", nullptr }; this->default_files.push_back(info); } void FileServiceController::print_usage(const char *name) const { basic_print_usage(name, "file"); } bool FileServiceController::get_opt(int argc, const char **argv) { return basic_get_opt(argc, argv, &this->config); } void FileServiceController::print_success_info() const { printf(COLOR_GREEN"Success:\n make project path " COLOR_BLUE"\" %s \"" COLOR_GREEN "done.\n\n", this->config.output_path); printf(COLOR_PINK"Commands:\n " COLOR_BLUE "cd %s\n make -j\n\n", this->config.output_path); printf(COLOR_PINK"Execute:\n " COLOR_GREEN "./server\n\n"); printf(COLOR_PINK"Try file service:\n"); printf(COLOR_WHITE" curl localhost:8080/index.html\n"); printf(" curl -i localhost:8080/a/b/\n\n" COLOR_OFF); } ComputeController::ComputeController() { this->config.type = COMMAND_COMPUTE; struct CommandController::file_info info; info = { "basic/server.conf", "server.conf", basic_server_config_transform }; this->default_files.push_back(info); info = { "basic/compute_server_main.cc", "server_main.cc", nullptr }; this->default_files.push_back(info); info = { "common/CMakeLists.txt", "CMakeLists.txt", common_cmake_transform }; this->default_files.push_back(info); info = { "common/GNUmakefile", "GNUmakefile", nullptr }; this->default_files.push_back(info); info = { "config/Json.h", "config/Json.h", nullptr }; this->default_files.push_back(info); info = { "config/Json.cc", "config/Json.cc", nullptr }; this->default_files.push_back(info); info = { "config/config_simple.h", "config/config.h", nullptr }; this->default_files.push_back(info); info = { "config/config_simple.cc", "config/config.cc", nullptr }; this->default_files.push_back(info); } void ComputeController::print_usage(const char *name) const { basic_print_usage(name, "compute"); } bool ComputeController::get_opt(int argc, const char **argv) { return basic_get_opt(argc, argv, &this->config); } void ComputeController::print_success_info() const { printf(COLOR_GREEN"Success:\n make project path " COLOR_BLUE"\" %s \"" COLOR_GREEN " done.\n\n", this->config.output_path); printf(COLOR_PINK"Commands:\n " COLOR_BLUE "cd %s\n make -j\n\n", this->config.output_path); printf(COLOR_PINK"Execute:\n " COLOR_GREEN "./server\n\n"); printf(COLOR_PINK"Try compute with n=8:\n"); printf(COLOR_WHITE" curl localhost:8080/8\n\n" COLOR_OFF); } srpc-0.10.1/tools/srpc_config.cc000066400000000000000000000330761454502251400165160ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include "srpc_config.h" std::vector RPC_PROTOC_SKIP_FILES = { "config_simple.h", "config_simple.cc", "rpc.thrift", "client_thrift.cc", "server_thrift.cc" }; std::vector RPC_THRIFT_SKIP_FILES = { "config_simple.h", "config_simple.cc", "rpc.proto", "client_protobuf.cc", "server_protobuf.cc" }; void usage_kafka(int argc, const char *argv[]) { printf("Usage:\n" " %s kafka [FLAGS]\n\n" "Available Flags:\n" " -o : project output path (default: CURRENT_PATH)\n" " -h : kafka broker url (example: kafka://10.160.23.23:9000," "10.123.23.23,kafka://kafka.sogou)\n" " -g : group name\n" " -d : path of dependencies (default: COMPILE_PATH)\n" , argv[0]); } const char *get_type_string(uint8_t type) { switch (type) { case PROTOCOL_TYPE_HTTP: return "Http"; case PROTOCOL_TYPE_REDIS: return "Redis"; case PROTOCOL_TYPE_MYSQL: return "MySQL"; case PROTOCOL_TYPE_KAFKA: return "Kafka"; case PROTOCOL_TYPE_SRPC: return "SRPC"; case PROTOCOL_TYPE_SRPC_HTTP: return "SRPCHttp"; case PROTOCOL_TYPE_BRPC: return "BRPC"; case PROTOCOL_TYPE_THRIFT: return "Thrift"; case PROTOCOL_TYPE_THRIFT_HTTP: return "ThriftHttp"; case PROTOCOL_TYPE_TRPC: return "TRPC"; case PROTOCOL_TYPE_TRPC_HTTP: return "TRPCHttp"; default: return "Unknown type"; } } static int check_file_idl_type(const char *filename) { size_t len = strlen(filename); if (len > 6 && strcmp(filename + len - 6, ".proto") == 0) return IDL_TYPE_PROTOBUF; else if (len > 7 && strcmp(filename + len - 7, ".thrift") == 0) return IDL_TYPE_THRIFT; return IDL_TYPE_MAX; } srpc_config::srpc_config() { project_name = NULL; service_name = NULL; rpc_type = PROTOCOL_TYPE_SRPC; idl_type = IDL_TYPE_DEFAULT; data_type = DATA_TYPE_DEFAULT; compress_type = COMPRESS_TYPE_NONE; specified_depend_path = false; specified_idl_file = NULL; specified_idl_path = NULL; } bool srpc_config::prepare_specified_idl_file() { if (this->specified_idl_file == NULL) { if (this->specified_idl_path != NULL) { printf(COLOR_RED"Error:\n idl_path is specified " "but NO idl_file.\n\n" COLOR_OFF); return false; } return true; } else if (this->service_name != NULL) { printf(COLOR_RED"Error:\n [-s service_name] does NOT take effect " "when idl_file is specified.\n\n" COLOR_OFF); return false; } this->idl_type = check_file_idl_type(this->specified_idl_file); if (this->idl_type == IDL_TYPE_MAX) { printf(COLOR_RED"Error:\n Invalid idl type. file :" COLOR_BLUE " %s\n\n" COLOR_OFF, this->specified_idl_file); return false; } if (access(this->specified_idl_file, F_OK) != 0) { printf(COLOR_RED"Error:\n idl_file" COLOR_BLUE " %s " COLOR_RED "does NOT exist.\n\n" COLOR_OFF, this->specified_idl_file); return false; } if (this->specified_idl_path == NULL) this->specified_idl_path = this->output_path; return true; } const char *srpc_config::rpc_type_string() const { return get_type_string(this->rpc_type); } const char *srpc_config::rpc_compress_string() const { switch (this->compress_type) { case COMPRESS_TYPE_SNAPPY: return "RPCCompressSnappy"; case COMPRESS_TYPE_GZIP: return "RPCCompressGzip"; case COMPRESS_TYPE_ZLIB: return "RPCCompressZlib"; case COMPRESS_TYPE_LZ4: return "RPCCompressLz4"; default: return "Unknown type"; } } const char *srpc_config::rpc_data_string() const { switch (this->data_type) { case DATA_TYPE_PROTOBUF: return "RPCDataProtobuf"; case DATA_TYPE_THRIFT: return "RPCDataThrift"; case DATA_TYPE_JSON: return "RPCDataJson"; default: return "Unknown type"; } } void srpc_config::set_rpc_type(const char *type) { if (strcasecmp(type, "SRPCHttp") == 0) this->rpc_type = PROTOCOL_TYPE_SRPC_HTTP; else if (strcasecmp(type, "SRPC") == 0) this->rpc_type = PROTOCOL_TYPE_SRPC; else if (strcasecmp(type, "BRPC") == 0) this->rpc_type = PROTOCOL_TYPE_BRPC; else if (strcasecmp(type, "ThriftHttp") == 0) this->rpc_type = PROTOCOL_TYPE_THRIFT_HTTP; else if (strcasecmp(type, "Thrift") == 0) this->rpc_type = PROTOCOL_TYPE_THRIFT; else if (strcasecmp(type, "TRPCHttp") == 0) this->rpc_type = PROTOCOL_TYPE_TRPC_HTTP; else if (strcasecmp(type, "TRPC") == 0) this->rpc_type = PROTOCOL_TYPE_TRPC; else this->rpc_type = PROTOCOL_TYPE_MAX; } void srpc_config::set_idl_type(const char *type) { if (strcasecmp(type, "protobuf") == 0) this->idl_type = IDL_TYPE_PROTOBUF; else if (strcasecmp(type, "thrift") == 0) this->idl_type = IDL_TYPE_THRIFT; else this->idl_type = IDL_TYPE_MAX; } void srpc_config::set_data_type(const char *type) { if (strcasecmp(type, "protobuf") == 0) this->data_type = DATA_TYPE_PROTOBUF; else if (strcasecmp(type, "thrift") == 0) this->data_type = DATA_TYPE_THRIFT; else if (strcasecmp(type, "json") == 0) this->data_type = DATA_TYPE_JSON; else this->data_type = DATA_TYPE_MAX; } void srpc_config::set_compress_type(const char *type) { if (strcasecmp(type, "snappy") == 0) this->compress_type = COMPRESS_TYPE_SNAPPY; else if (strcasecmp(type, "gzip") == 0) this->compress_type = COMPRESS_TYPE_GZIP; else if (strcasecmp(type, "zlib") == 0) this->compress_type = COMPRESS_TYPE_ZLIB; else if (strcasecmp(type, "lz4") == 0) this->compress_type = COMPRESS_TYPE_LZ4; else this->compress_type = COMPRESS_TYPE_MAX; } const char *srpc_config::proxy_server_type_string() const { return get_type_string(this->proxy_server_type); } const char *srpc_config::proxy_client_type_string() const { return get_type_string(this->proxy_client_type); } int check_proxy_type(uint8_t type) { if (type == PROTOCOL_TYPE_HTTP || type == PROTOCOL_TYPE_REDIS || type == PROTOCOL_TYPE_MYSQL || type == PROTOCOL_TYPE_KAFKA) return PROXY_BASIC_TYPE; if (type == PROTOCOL_TYPE_SRPC || type == PROTOCOL_TYPE_SRPC_HTTP || type == PROTOCOL_TYPE_BRPC || type == PROTOCOL_TYPE_TRPC || type == PROTOCOL_TYPE_TRPC_HTTP) return PROXY_PROTOBUF_TYPE; if (type == PROTOCOL_TYPE_THRIFT || type == PROTOCOL_TYPE_THRIFT_HTTP) return PROXY_THRIFT_TYPE; return -1; } ControlGenerator::ControlGenerator(const struct srpc_config *config) : Generator(config->idl_type == IDL_TYPE_THRIFT ? true : false), ctl_printer(config->idl_type == IDL_TYPE_THRIFT ? true : false), config(config) { } bool ControlGenerator::generate_client_cpp_file(const idl_info& info, const std::string& idl_file) { this->client_cpp_file = this->config->output_path; this->client_cpp_file += "client_main.cc"; if (this->ctl_printer.open(this->client_cpp_file) == false) return false; this->ctl_printer.print_clt_include(); this->ctl_printer.print_client_file_include(idl_file); for (const auto& desc : info.desc_list) { if (desc.block_type != "service") continue; for (const auto& rpc : desc.rpcs) { this->ctl_printer.print_client_done_method_ctl(info.package_name, desc.block_name, rpc.method_name, rpc.response_name); } } this->ctl_printer.print_client_main_begin(); this->ctl_printer.print_client_load_config(); this->ctl_printer.print_client_params(); int id = 0; for (const auto& desc : info.desc_list) { if (desc.block_type != "service") continue; std::string suffix; if (id != 0) suffix = std::to_string(id); id++; this->ctl_printer.print_client_main_service_ctl(this->config->rpc_type_string(), info.package_name, desc.block_name, suffix); auto rpc_it = desc.rpcs.cbegin(); if (rpc_it != desc.rpcs.cend()) { this->ctl_printer.print_client_main_method_call(info.package_name, desc.block_name, rpc_it->method_name, rpc_it->request_name, suffix); rpc_it++; } } this->ctl_printer.print_client_main_end(); this->ctl_printer.close(); return false; } bool ControlGenerator::generate_server_cpp_file(const idl_info& info, const std::string& idl_file) { this->server_cpp_file = this->config->output_path; this->server_cpp_file += "server_main.cc"; if (this->ctl_printer.open(this->server_cpp_file) == false) return false; this->ctl_printer.print_clt_include(); this->ctl_printer.print_server_file_include(idl_file); for (const auto& desc : info.desc_list) { if (desc.block_type != "service") continue; this->ctl_printer.print_server_class_impl(info.package_name, desc.block_name); for (const auto& rpc : desc.rpcs) { this->ctl_printer.print_server_impl_method(info.package_name, desc.block_name, rpc.method_name, rpc.request_name, rpc.response_name); } this->ctl_printer.print_server_class_impl_end(); } this->ctl_printer.print_server_main_begin(); this->ctl_printer.print_server_load_config(); this->ctl_printer.print_server_construct(this->config->rpc_type_string()); for (const auto& desc : info.desc_list) { if (desc.block_type != "service") continue; this->ctl_printer.print_server_main_method(desc.block_name); } this->ctl_printer.print_server_main_end_ctl(this->config->project_name, this->config->rpc_type_string()); this->ctl_printer.print_server_main_return(); this->ctl_printer.close(); return true; } static std::string ctl_include_format = R"(#include #include "config/config.h" )"; static std::string ctl_load_config_format = R"( // load config srpc::RPCConfig config; if (config.load("./%s") == false) { perror("Load config failed"); exit(1); } signal(SIGINT, sig_handler); )"; static std::string ctl_client_load_params_format = R"( // start client RPCClientParams params = RPC_CLIENT_PARAMS_DEFAULT; params.host = config.client_host(); params.port = config.client_port(); )"; static std::string ctl_client_main_params_format = R"( %s::%sClient client%s(¶ms); config.load_filter(client%s); )"; static std::string ctl_client_done_protobuf_format = R"( // printf("%%s\n", response->DebugString().c_str()); )"; static std::string ctl_client_done_format = R"( static void %s_done(%s *response, srpc::RPCContext *context) { if (context->success()) { // TODO: fill your logic to set response%s } else printf("%s %s status[%%d] error[%%d] errmsg:%%s\n", context->get_status_code(), context->get_error(), context->get_errmsg()); } )"; static std::string ctl_server_main_end_format = R"( config.load_filter(server); if (server.start(config.server_port()) == 0) { printf("%s %s server started, port %%u\n", config.server_port()); wait_group.wait(); server.stop(); } else perror("server start"); )"; void ControlGenerator::ControlPrinter::print_clt_include() { fprintf(this->out_file, "%s", ctl_include_format.c_str()); } void ControlGenerator::ControlPrinter::print_client_load_config() { fprintf(this->out_file, ctl_load_config_format.c_str(), "./client.conf"); } void ControlGenerator::ControlPrinter::print_server_load_config() { fprintf(this->out_file, ctl_load_config_format.c_str(), "./server.conf"); } void ControlGenerator::ControlPrinter::print_client_params() { fprintf(this->out_file, "%s", ctl_client_load_params_format.c_str()); fprintf(this->out_file, "\tparams.is_ssl = config.client_is_ssl();\n"); fprintf(this->out_file, "\tparams.url = config.client_url();\n"); fprintf(this->out_file, "\tparams.caller = config.client_caller();\n"); // TODO: set client task params } void ControlGenerator::ControlPrinter:: print_client_done_method_ctl(const std::vector& package, const std::string& service, const std::string& method, const std::string& response) { std::string method_lower = method; std::transform(method_lower.begin(), method_lower.end(), method_lower.begin(), ::tolower); std::string resp; const char *set_resp_code = ""; if (this->is_thrift) resp = make_thrift_package_prefix(package, service, response); else resp = make_package_prefix(package, response); if (!this->is_thrift) set_resp_code = ctl_client_done_protobuf_format.c_str(); fprintf(this->out_file, ctl_client_done_format.c_str(), method_lower.c_str(), resp.c_str(), set_resp_code, service.c_str(), method.c_str(), service.c_str(), method.c_str()); } void ControlGenerator::ControlPrinter:: print_client_main_service_ctl(const std::string& type, const std::vector& package, const std::string& service, const std::string& suffix) { std::string base_service = make_package_prefix(package, service); fprintf(this->out_file, ctl_client_main_params_format.c_str(), base_service.c_str(), type.c_str(), suffix.c_str(), suffix.c_str()); } void ControlGenerator::ControlPrinter:: print_server_construct(const char *rpc_type) { fprintf(this->out_file, " %sServer server;\n", rpc_type); } void ControlGenerator::ControlPrinter:: print_server_main_end_ctl(const char *project_name, const char *rpc_type) { fprintf(this->out_file, ctl_server_main_end_format.c_str(), project_name, rpc_type); } srpc-0.10.1/tools/srpc_config.h000066400000000000000000000110121454502251400163420ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __SRPC_CONFIG__ #define __SRPC_CONFIG__ #include #include #ifndef _WIN32 #include #include #else #define MAXPATHLEN 4096 #include #endif #define COLOR_OFF "\033[0m" #define COLOR_WHITE "\033[37;1m" #define COLOR_RED "\033[31;1m" #define COLOR_GREEN "\033[32;1m" #define COLOR_YELLOW "\033[33;1m" #define COLOR_PURPLE "\033[34;1m" #define COLOR_PINK "\033[35;1m" #define COLOR_BLUE "\033[36;1m" #define COLOR_LPURPLE "\033[94;1m" #define COLOR_COMMAND COLOR_BLUE #define COLOR_INFO COLOR_YELLOW #define COLOR_FLAG COLOR_PURPLE struct srpc_config { uint8_t type; const char *project_name; const char *service_name; char template_path[MAXPATHLEN]; char depend_path[MAXPATHLEN]; char output_path[MAXPATHLEN]; // rpc uint8_t rpc_type; uint8_t idl_type; uint8_t data_type; uint8_t compress_type; bool specified_depend_path; const char *specified_idl_file; const char *specified_idl_path; // proxy uint8_t proxy_server_type; uint8_t proxy_client_type; srpc_config(); bool prepare_args(); bool prepare_project_path(); bool prepare_dependencies() const; bool prepare_specified_idl_file(); const char *rpc_type_string() const; const char *rpc_compress_string() const; const char *rpc_data_string() const; void set_rpc_type(const char *optarg); void set_idl_type(const char *optarg); void set_data_type(const char *optarg); void set_compress_type(const char *optarg); const char *proxy_server_type_string() const; const char *proxy_client_type_string() const; }; class ControlGenerator : public Generator { public: bool generate_client_cpp_file(const idl_info& cur_info, const std::string& idl_file_name); bool generate_server_cpp_file(const idl_info& cur_info, const std::string& idl_file_name); ControlGenerator(const struct srpc_config *config); private: class ControlPrinter : public Printer { public: ControlPrinter(bool is_thrift) : Printer(is_thrift) { } void print_clt_include(); void print_server_load_config(); void print_client_load_config(); void print_client_params(); void print_client_done_method_ctl(const std::vector& pkg, const std::string& service, const std::string& method, const std::string& response); void print_client_main_service_ctl(const std::string& type, const std::vector& pkg, const std::string& service, const std::string& suffix); void print_server_construct(const char *rpc_type); void print_server_main_end_ctl(const char *project_name, const char *rpc_type); }; ControlPrinter ctl_printer; const struct srpc_config *config; }; enum { COMMAND_HTTP, COMMAND_REDIS, COMMAND_MYSQL, COMMAND_KAFKA, COMMAND_RPC, COMMAND_API, COMMAND_PROXY, COMMAND_FILE, COMMAND_COMPUTE }; enum { PROTOCOL_TYPE_HTTP = COMMAND_HTTP, PROTOCOL_TYPE_REDIS = COMMAND_REDIS, PROTOCOL_TYPE_MYSQL = COMMAND_MYSQL, PROTOCOL_TYPE_KAFKA = COMMAND_KAFKA, PROTOCOL_TYPE_SRPC = 22, PROTOCOL_TYPE_SRPC_HTTP, PROTOCOL_TYPE_BRPC, PROTOCOL_TYPE_THRIFT, PROTOCOL_TYPE_THRIFT_HTTP, PROTOCOL_TYPE_TRPC, PROTOCOL_TYPE_TRPC_HTTP, PROTOCOL_TYPE_MAX }; enum { IDL_TYPE_DEFAULT, IDL_TYPE_PROTOBUF, IDL_TYPE_THRIFT, IDL_TYPE_MAX }; enum { DATA_TYPE_DEFAULT, DATA_TYPE_PROTOBUF, DATA_TYPE_THRIFT, DATA_TYPE_JSON, DATA_TYPE_MAX }; enum { COMPRESS_TYPE_NONE, COMPRESS_TYPE_SNAPPY, COMPRESS_TYPE_GZIP, COMPRESS_TYPE_ZLIB, COMPRESS_TYPE_LZ4, COMPRESS_TYPE_MAX }; enum { PROXY_BASIC_TYPE = 1, PROXY_PROTOBUF_TYPE = 2, PROXY_THRIFT_TYPE = 3 }; void usage(int argc, const char *argv[]); void usage_http(int argc, const char *argv[]); void usage_db(int argc, const char *argv[], const struct srpc_config *config); void usage_kafka(int argc, const char *argv[]); void usage_rpc(int argc, const char *argv[], const struct srpc_config *config); const char *get_type_string(uint8_t type); int check_proxy_type(uint8_t type); #endif srpc-0.10.1/tools/srpc_controller.cc000066400000000000000000000350711454502251400174310ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "srpc_controller.h" static constexpr const char *DEPENDENCIES_ERROR = R"(Warning: Default dependencies path : %s does not have third_party dependencies. This may cause link error in project : %s Please check or specify srpc path with '-d' Or use the following command to pull srpc: "git clone --recursive https://github.com/sogou/srpc.git" "cd srpc && make" )"; static constexpr const char *CMAKE_PROTOC_CODES = R"( find_program(PROTOC "protoc") if(${PROTOC} STREQUAL "PROTOC-NOTFOUND") message(FATAL_ERROR "Protobuf compiler is missing!") endif () protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${IDL_FILE}))"; int mkdir_p(const char *name, mode_t mode) { int ret = mkdir(name, mode); if (ret == 0 || errno != ENOENT) return ret; size_t len = strlen(name); if (len > MAXPATHLEN) { errno = ENAMETOOLONG; return -1; } char path[MAXPATHLEN + 1] = {}; memcpy(path, name, len); if (name[len - 1] != '/') { path[len] = '/'; len++; } bool has_valid = false; for (int i = 0; i < len; i++) { if (path[i] != '/' && path[i] != '.') // simple check of valid path { has_valid = true; continue; } if (path[i] == '/' && has_valid == true) { path[i] = '\0'; ret = mkdir(path, mode); if (ret != 0 && errno != EEXIST) return ret; path[i] = '/'; has_valid = false; } } return ret; } static const char *get_proxy_rpc_string(const char *type) { if (strcasecmp(type, "SRPCHttp") == 0) return "SRPCHttp"; if (strcasecmp(type, "SRPC") == 0) return "SRPC"; if (strcasecmp(type, "BRPC") == 0) return "BRPC"; if (strcasecmp(type, "ThriftHttp") == 0) return "ThriftHttp"; if (strcasecmp(type, "Thrift") == 0) return "Thrift"; if (strcasecmp(type, "TRPCHttp") == 0) return "TRPCHttp"; if (strcasecmp(type, "TRPC") == 0) return "TRPC"; return "Unknown type"; } // path=/root/a/ // file=b/c/d.txt // make the middle path and return the full file name static std::string make_file_path(const char *path, const std::string& file) { DIR *dir; auto pos = file.find_last_of('/'); std::string file_name; if (pos != std::string::npos) { file_name = file.substr(pos + 1); std::string dir_name = std::string(path) + std::string("/") + file.substr(0, pos + 1); dir = opendir(dir_name.c_str()); if (dir == NULL) { if (mkdir_p(dir_name.c_str(), 0755) != 0) return ""; } else closedir(dir); } file_name = path; file_name += "/"; file_name += file; return file_name; } static std::string rpc_client_file_codes(const struct srpc_config *config) { std::string ret; if (config->compress_type != COMPRESS_TYPE_NONE) { ret += "\tparams.task_params.compress_type = "; ret += config->rpc_compress_string(); ret += ";\n"; } if (config->data_type != DATA_TYPE_DEFAULT) { ret += "\tparams.task_params.data_type = "; ret += config->rpc_data_string(); ret += ";\n"; } return ret; } static std::string rpc_server_file_codes(const struct srpc_config *config) { std::string ret; if (config->compress_type != COMPRESS_TYPE_NONE) { ret += " ctx->set_compress_type("; ret += config->rpc_compress_string(); ret += ");\n"; } if (config->data_type != DATA_TYPE_DEFAULT) { ret += " ctx->set_data_type("; ret += config->rpc_data_string(); ret += ");\n"; } ret += " "; return ret; } bool rpc_idl_transform(const std::string& format, FILE *out, const struct srpc_config *config) { size_t len = fprintf(out, format.c_str(), config->service_name); return len > 0; } bool rpc_client_transform(const std::string& format, FILE *out, const struct srpc_config *config) { std::string prepare_params = rpc_client_file_codes(config); const char *rpc_type; if (config->type == COMMAND_PROXY) rpc_type = get_proxy_rpc_string(config->proxy_client_type_string()); else rpc_type = config->rpc_type_string(); const char *thrift_namespace = ""; if (config->idl_type == IDL_TYPE_THRIFT) thrift_namespace = config->project_name; size_t len = fprintf(out, format.c_str(), config->service_name, prepare_params.c_str(), config->service_name, rpc_type, thrift_namespace, thrift_namespace); return len > 0; } bool rpc_server_transform(const std::string& format, FILE *out, const struct srpc_config *config) { std::string prepare_ctx = rpc_server_file_codes(config); const char *rpc_type; if (config->type == COMMAND_PROXY) rpc_type = get_proxy_rpc_string(config->proxy_server_type_string()); else rpc_type = config->rpc_type_string(); size_t len = fprintf(out, format.c_str(), config->service_name, config->service_name, prepare_ctx.c_str(), rpc_type, config->project_name, rpc_type); return len > 0; } bool CommandController::parse_args(int argc, const char **argv) { if (argc < 3) { if (argc == 2 && strncmp(argv[1], "api", strlen(argv[1])) == 0) printf(COLOR_RED "Missing: FILE_NAME\n\n" COLOR_OFF); else printf(COLOR_RED "Missing: PROJECT_NAME\n\n" COLOR_OFF); return false; } memset(this->config.output_path, 0, MAXPATHLEN); if (get_path(__FILE__, this->config.depend_path, 2) == false) return false; if (get_path(__FILE__, this->config.template_path, 1) == false) return false; snprintf(this->config.template_path + strlen(this->config.template_path), MAXPATHLEN - strlen(this->config.template_path), "templates/"); if (this->get_opt(argc, argv) == false) return false; if (this->check_args() == false) return false; return true; } bool CommandController::check_args() { size_t path_len = strlen(this->config.output_path); if (strlen(this->config.project_name) >= MAXPATHLEN - path_len - 2) { printf(COLOR_RED"Error:\n project name" COLOR_BLUE " %s " COLOR_RED"or path" COLOR_BLUE " %s " COLOR_RED" is too long. Total limit : %d.\n\n" COLOR_OFF, this->config.project_name, this->config.output_path, MAXPATHLEN); return false; } if (path_len != 0 && this->config.output_path[path_len - 1] != '/') { this->config.output_path[path_len] = '/'; path_len++; } snprintf(this->config.output_path + path_len, MAXPATHLEN - path_len, "%s/", this->config.project_name); return true; } static inline bool is_with_srpc(const struct srpc_config *config) { if (config->type == COMMAND_RPC || (config->type == COMMAND_PROXY && check_proxy_type(config->proxy_server_type) != PROXY_BASIC_TYPE && check_proxy_type(config->proxy_client_type) != PROXY_BASIC_TYPE)) { return true; } return false; } static bool check_dependencies_and_init(const struct srpc_config *config) { int status; bool with_srpc = is_with_srpc(config); // the specified_depend_path must for workflow if (config->specified_depend_path == true && with_srpc == false) return true; std::string check_file = config->depend_path; if (with_srpc == false) check_file += "workflow/workflow-config.cmake.in"; else check_file += "third_party/snappy/cmake"; if (access(check_file.c_str(), 0) != 0) { std::string cmd = "cd "; cmd += config->depend_path; cmd += "&& git submodule update --init"; if (with_srpc == false) cmd += " workflow"; status = system(cmd.c_str()); if (status == -1 || status == 127) { perror("git submodule update failed"); return false; } if (access(check_file.c_str(), 0) != 0) { printf(DEPENDENCIES_ERROR, config->depend_path, config->project_name); return false; } } return true; } static bool check_libraries_and_compile(const struct srpc_config *config) { int status; std::string library_path; std::string compile_path = config->depend_path; if (is_with_srpc(config) == false && config->specified_depend_path == false) compile_path += "/workflow"; library_path = compile_path; library_path += "/_lib"; if (access(library_path.c_str(), 0) != 0) { std::string cmd = "cd "; cmd += compile_path; cmd += " && make -j4"; status = system(cmd.c_str()); if (status == -1 || status == 127) { perror("execute command make failed"); return false; } } return true; } bool CommandController::dependencies_and_dir() { struct srpc_config *config = &this->config; DIR *dir; dir = opendir(config->output_path); if (dir != NULL) { printf(COLOR_RED "Error:\n project path " COLOR_BLUE " %s " COLOR_RED " EXISTS.\n\n" COLOR_OFF, config->output_path); closedir(dir); return false; } dir = opendir(config->template_path); if (dir == NULL) { printf(COLOR_RED "Error:\n template path " COLOR_BLUE " %s " COLOR_RED "does NOT exist.\n" COLOR_OFF, config->template_path); return false; } closedir(dir); if (check_dependencies_and_init(config) == false) return false; if (check_libraries_and_compile(config) == false) return false; if (mkdir_p(config->output_path, 0755) != 0) { perror("Error:\n failed to make project "); return false; } std::string config_path = config->output_path; config_path += "/config"; if (mkdir(config_path.c_str(), 0755) != 0) { perror("Error:\n failed to make project config path "); return false; } return true; } // get the depth-th upper path of file bool CommandController::get_path(const char *file, char *path, int depth) { size_t len = strlen(file); size_t i; memset(path, 0, MAXPATHLEN); if (len == 0 || depth <= 0) return false; int state = 0; for (i = len - 1; i >= 0; i--) { if (file[i] == '/') { state++; if (state >= depth) break; } } if (state != depth) return false; memcpy(path, file, i + 1); return true; } bool CommandController::copy_single_file(const std::string& in_file, const std::string& out_file, transform_function_t transform) { FILE *read_fp; FILE *write_fp; size_t size; char *buf; bool ret = false; read_fp = fopen(in_file.c_str(), "r"); if (read_fp) { write_fp = fopen(out_file.c_str(), "w"); if (write_fp) { fseek(read_fp, 0, SEEK_END); size = ftell(read_fp); buf = (char *)malloc(size); if (buf) { fseek(read_fp, 0, SEEK_SET); if (fread(buf, size, 1, read_fp) == 1) { if (transform != nullptr) { std::string format = std::string(buf, size); ret = transform(format, write_fp, &this->config); } else ret = fwrite(buf, size, 1, write_fp) >= 0 ? true : false; } else { printf(COLOR_RED"Error:\n read " COLOR_WHITE " %s " COLOR_RED "failed\n\n" COLOR_OFF, in_file.c_str()); } free(buf); } else printf(COLOR_RED"Error:\n system error.\n\n" COLOR_OFF); fclose(write_fp); } else { printf(COLOR_RED"Error:\n write" COLOR_WHITE " %s " COLOR_RED"failed\n\n" COLOR_OFF, out_file.c_str()); } } else { printf(COLOR_RED"Error:\n open" COLOR_WHITE " %s " COLOR_RED " failed.\n\n" COLOR_OFF, in_file.c_str()); } return ret; } bool CommandController::copy_files() { std::string read_file; std::string write_file; bool ret = true; for (auto it = this->default_files.begin(); it != this->default_files.end() && ret == true; it++) { read_file = this->config.template_path; read_file += it->in_file; write_file = make_file_path(this->config.output_path, it->out_file); if (write_file.empty()) ret = false; else ret = this->copy_single_file(read_file, write_file, it->transform); } return ret; } void CommandController::print_success_info() const { printf(COLOR_GREEN"Success!\n make project path" COLOR_BLUE" %s " COLOR_GREEN " done.\n\n", this->config.output_path); printf(COLOR_PINK"Commands:\n " COLOR_BLUE "cd %s\n make -j\n\n", this->config.output_path); printf(COLOR_PINK"Execute:\n " COLOR_GREEN "./server\n ./client\n\n" COLOR_OFF); } bool common_cmake_transform(const std::string& format, FILE *out, const struct srpc_config *config) { std::string path = config->depend_path; if (config->specified_depend_path == false) path += "workflow"; std::string codes_str; std::string executors_str; if (config->type == COMMAND_FILE) codes_str = " file_service.cc"; if (config->type != COMMAND_FILE && config->type != COMMAND_COMPUTE) executors_str = " client"; if (config->type == COMMAND_PROXY) executors_str += " proxy"; size_t len = fprintf(out, format.c_str(), config->project_name, path.c_str(), codes_str.c_str(), executors_str.c_str()); return len > 0; } void CommandController::fill_rpc_default_files() { struct file_info info; std::string idl_file_name, client_file_name, server_file_name; std::string out_file_name = this->config.project_name; if (this->config.idl_type == IDL_TYPE_PROTOBUF) { out_file_name += ".proto"; idl_file_name = "rpc/rpc.proto"; client_file_name = "rpc/client_protobuf.cc"; server_file_name = "rpc/server_protobuf.cc"; } else { out_file_name += ".thrift"; idl_file_name = "rpc/rpc.thrift"; client_file_name = "rpc/client_thrift.cc"; server_file_name = "rpc/server_thrift.cc"; } info = { idl_file_name , out_file_name, rpc_idl_transform }; this->default_files.push_back(info); info = { client_file_name, "client_main.cc", rpc_client_transform }; this->default_files.push_back(info); info = { server_file_name, "server_main.cc", rpc_server_transform }; this->default_files.push_back(info); info = { "rpc/server.conf", "server.conf", nullptr }; this->default_files.push_back(info); if (this->config.type == COMMAND_RPC) info = { "rpc/client.conf", "client.conf", nullptr }; else info = { "proxy/client_rpc.conf", "client.conf", nullptr }; this->default_files.push_back(info); } bool rpc_cmake_transform(const std::string& format, FILE *out, const struct srpc_config *config) { std::string idl_file_name; std::string srpc_path = config->depend_path; std::string workflow_path = config->depend_path; workflow_path += "workflow"; if (config->specified_idl_file != NULL) { idl_file_name = config->specified_idl_file; size_t pos = idl_file_name.find_last_of('/'); if (pos != std::string::npos) idl_file_name = idl_file_name.substr(pos + 1); } else if (config->idl_type == IDL_TYPE_PROTOBUF) { idl_file_name = config->project_name; idl_file_name += ".proto"; } else { idl_file_name = config->project_name; idl_file_name += ".thrift"; } std::string is_proxy_str = config->type == COMMAND_PROXY ? " proxy" : ""; size_t len = fprintf(out, format.c_str(), config->project_name, workflow_path.c_str(), srpc_path.c_str(), idl_file_name.c_str(), config->idl_type == IDL_TYPE_PROTOBUF ? CMAKE_PROTOC_CODES : "", is_proxy_str.c_str()); return len > 0; } srpc-0.10.1/tools/srpc_controller.h000066400000000000000000000112601454502251400172650ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __SRPC_CONTROLLER__ #define __SRPC_CONTROLLER__ #include "srpc_config.h" class CommandController { public: bool parse_args(int argc, const char **argv); virtual bool dependencies_and_dir(); virtual bool copy_files(); virtual void print_success_info() const; virtual void print_usage(const char *name) const = 0; protected: virtual bool check_args(); private: virtual bool get_opt(int argc, const char **argv) = 0; public: CommandController() { } virtual ~CommandController() { } using transform_function_t = bool (*)(const std::string&, FILE *, const struct srpc_config *); struct file_info { std::string in_file; std::string out_file; transform_function_t transform; }; protected: std::vector default_files; struct srpc_config config; protected: bool get_path(const char *file, char *path, int depth); bool copy_single_file(const std::string& in_file, const std::string& out_file, transform_function_t transform); void fill_rpc_default_files(); }; class HttpController : public CommandController { public: void print_usage(const char *name) const override; private: bool get_opt(int argc, const char **argv) override; public: HttpController(); ~HttpController() { } }; class RedisController : public CommandController { public: void print_usage(const char *name) const override; private: bool get_opt(int argc, const char **argv) override; public: RedisController(); ~RedisController() { } }; class RPCController : public CommandController { public: void print_usage(const char *name) const override; bool copy_files() override; protected: bool check_args() override; private: bool get_opt(int argc, const char **argv) override; public: RPCController(); ~RPCController() { } }; class APIController : public CommandController { public: bool dependencies_and_dir() override; void print_usage(const char *name) const override; void print_success_info() const override; protected: bool check_args() override { return true; } private: bool get_opt(int argc, const char **argv) override; public: APIController(); ~APIController() { } }; class ProxyController : public CommandController { public: void print_usage(const char *name) const override; void print_success_info() const override; bool copy_files() override; protected: bool check_args() override; private: bool get_opt(int argc, const char **argv) override; public: ProxyController(); ~ProxyController() { } }; class FileServiceController : public CommandController { public: void print_usage(const char *name) const override; void print_success_info() const override; private: bool get_opt(int argc, const char **argv) override; public: FileServiceController(); ~FileServiceController() { } }; class ComputeController : public CommandController { public: void print_usage(const char *name) const override; void print_success_info() const override; private: bool get_opt(int argc, const char **argv) override; public: ComputeController(); ~ComputeController() { } }; ////////// common functions ////////// int mkdir_p(const char *name, mode_t mode); ////////// common transform functions ////////// bool common_cmake_transform(const std::string& format, FILE *out, const struct srpc_config *config); bool basic_client_transform(const std::string& format, FILE *out, const struct srpc_config *config); bool basic_server_transform(const std::string& format, FILE *out, const struct srpc_config *config); bool basic_client_config_transform(const std::string& format, FILE *out, const struct srpc_config *config); bool basic_server_config_transform(const std::string& format, FILE *out, const struct srpc_config *config); bool rpc_idl_transform(const std::string& format, FILE *out, const struct srpc_config *config); bool rpc_client_transform(const std::string& format, FILE *out, const struct srpc_config *config); bool rpc_server_transform(const std::string& format, FILE *out, const struct srpc_config *config); bool rpc_cmake_transform(const std::string& format, FILE *out, const struct srpc_config *config); #endif srpc-0.10.1/tools/srpc_ctl.cc000066400000000000000000000051031454502251400160210ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "srpc_controller.h" static void usage(const char *name) { printf(COLOR_PINK"Description:\n" COLOR_PURPLE" Simple generator for building Workflow and SRPC projects.\n\n" COLOR_PINK"Usage:\n" COLOR_INFO" %s" COLOR_COMMAND " " COLOR_INFO" " COLOR_FLAG " [FLAGS]\n\n" COLOR_PINK"Available Commands:\n" COLOR_COMMAND" http" COLOR_WHITE" - create project with both client and server\n" COLOR_COMMAND" redis" COLOR_WHITE" - create project with both client and server\n" COLOR_COMMAND" rpc" COLOR_WHITE" - create project with both client and server\n" COLOR_COMMAND" api" COLOR_WHITE" - create protobuf or thrift IDL api\n" COLOR_COMMAND" proxy" COLOR_WHITE" - create proxy for some client and server protocol\n" COLOR_COMMAND" file" COLOR_WHITE" - create project with asynchronous file service\n" COLOR_COMMAND" compute" COLOR_WHITE" - create project with asynchronous computing service\n" COLOR_OFF, name); } int main(int argc, const char *argv[]) { if (argc < 2) { usage(argv[0]); return 0; } CommandController *ctl; if (strcasecmp(argv[1], "http") == 0) { ctl = new HttpController; } else if (strcasecmp(argv[1], "redis") == 0) { ctl = new RedisController; } else if (strcasecmp(argv[1], "rpc") == 0) { ctl = new RPCController; } else if (strcasecmp(argv[1], "api") == 0) { ctl = new APIController; } else if (strcasecmp(argv[1], "proxy") == 0) { ctl = new ProxyController; } else if (strcasecmp(argv[1], "file") == 0) { ctl = new FileServiceController; } else if (strcasecmp(argv[1], "compute") == 0) { ctl = new ComputeController; } else { usage(argv[0]); return 0; } if (ctl->parse_args(argc, argv) == true) { if (ctl->dependencies_and_dir() == true) { if (ctl->copy_files() == true) ctl->print_success_info(); } } else ctl->print_usage(argv[0]); delete ctl; return 0; } srpc-0.10.1/tools/srpc_proxy_controller.cc000066400000000000000000000302761454502251400206740ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include "srpc_controller.h" static std::string default_server_port(uint8_t type) { if (type == PROTOCOL_TYPE_HTTP) return "8080"; if (type == PROTOCOL_TYPE_REDIS) return "6379"; if (type == PROTOCOL_TYPE_MYSQL) return "3306"; // Add other protocol here return "1412"; } static std::string default_proxy_port(uint8_t type) { if (type == PROTOCOL_TYPE_HTTP) return "8888"; if (type == PROTOCOL_TYPE_REDIS) return "6378"; if (type == PROTOCOL_TYPE_MYSQL) return "3305"; // Add other protocol here return "1411"; } static std::string proxy_process_request_codes(uint8_t server_type, uint8_t client_type) { if (server_type == client_type) return std::string(R"( *client_task->get_req() = std::move(*req); )"); else return std::string(R"( { // TODO: fill the client request to server request } )"); } static std::string proxy_callback_response_codes(uint8_t server_type, uint8_t client_type) { if (server_type != client_type) return std::string(R"( { // TODO: fill the server response to client response } )"); if (server_type == PROTOCOL_TYPE_HTTP) return std::string(R"( { const void *body; size_t len; resp->get_parsed_body(&body, &len); resp->append_output_body_nocopy(body, len); *proxy_resp = std::move(*resp); } else { proxy_resp->set_status_code("404"); proxy_resp->append_output_body_nocopy( "404 Not Found.", 27); } )"); return std::string(R"( *proxy_resp = std::move(*resp); )"); } static std::string proxy_redirect_codes(uint8_t type) { if (type == PROTOCOL_TYPE_HTTP) return std::string(R"( config.redirect_max(),)"); return std::string(""); } static bool proxy_basic_transform(const std::string& format, FILE *out, const struct srpc_config *config) { const char *server_type = config->proxy_server_type_string(); const char *client_type = config->proxy_client_type_string(); std::string server_lower = server_type; std::transform(server_lower.begin(), server_lower.end(), server_lower.begin(), ::tolower); std::string server_port = default_server_port(config->proxy_server_type); std::string proxy_port = default_proxy_port(config->proxy_client_type); size_t len = fprintf(out, format.c_str(), client_type, server_type, server_type, client_type, client_type, proxy_callback_response_codes(config->proxy_server_type, config->proxy_client_type).c_str(), // process client_type, client_type, server_lower.c_str(), server_type, server_lower.c_str(), proxy_redirect_codes(config->proxy_server_type).c_str(), proxy_process_request_codes(config->proxy_server_type, config->proxy_client_type).c_str(), // main client_type, server_type, client_type); return len > 0; } static bool proxy_config_transform(const std::string& format, FILE *out, const struct srpc_config *config) { std::string server_port = default_server_port(config->proxy_server_type); std::string proxy_port = default_proxy_port(config->proxy_client_type); size_t len = fprintf(out, format.c_str(), proxy_port.c_str(), server_port.c_str()); return len > 0; } static bool proxy_rpc_proxy_transform(const std::string& format, FILE *out, const struct srpc_config *config) { const char *server_type = config->proxy_server_type_string(); const char *client_type = config->proxy_client_type_string(); size_t len = fprintf(out, format.c_str(), config->project_name, // not support specified idl file config->project_name, config->project_name, config->project_name, server_type, // main client_type, config->project_name, client_type, server_type); return len > 0; } ProxyController::ProxyController() { this->config.type = COMMAND_PROXY; this->config.proxy_client_type = PROTOCOL_TYPE_HTTP; this->config.proxy_server_type = PROTOCOL_TYPE_HTTP; struct file_info info; info = { "proxy/proxy.conf", "proxy.conf", proxy_config_transform }; this->default_files.push_back(info); info = { "basic/server.conf", "server.conf", basic_server_config_transform }; this->default_files.push_back(info); info = { "basic/client.conf", "client.conf", basic_client_config_transform }; this->default_files.push_back(info); info = { "common/GNUmakefile", "GNUmakefile", nullptr }; this->default_files.push_back(info); info = { "config/Json.h", "config/Json.h", nullptr }; this->default_files.push_back(info); info = { "config/Json.cc", "config/Json.cc", nullptr }; this->default_files.push_back(info); } void ProxyController::print_usage(const char *name) const { printf(COLOR_PINK"Usage:\n" COLOR_INFO" %s " COLOR_BLUE "proxy " COLOR_INFO "" COLOR_FLAG " [FLAGS]\n\n" COLOR_PINK"Example:\n" COLOR_PURPLE" %s proxy my_proxy_project\n\n" COLOR_PINK"Available Flags:\n" COLOR_FLAG" -c :" COLOR_WHITE" client type for proxy [ Http | Redis | SRPC | SRPCHttp" " | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)\n" COLOR_FLAG" -s :" COLOR_WHITE" server type for proxy [ Http | Redis | SRPC | SRPCHttp" " | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)\n" COLOR_FLAG" -o :" COLOR_WHITE" project output path (default: CURRENT_PATH)\n" COLOR_FLAG" -d :" COLOR_WHITE" path of dependencies (default: COMPILE_PATH)\n" COLOR_OFF, name, name); } void ProxyController::print_success_info() const { printf(COLOR_GREEN"Success:\n make project path " COLOR_BLUE" %s " COLOR_GREEN " done.\n\n" COLOR_OFF, this->config.output_path); printf(COLOR_PINK"Commands:\n" COLOR_BLUE " cd %s\n make -j\n\n" COLOR_OFF, this->config.output_path); printf(COLOR_PINK"Execute:\n" COLOR_GREEN" ./server\n ./proxy\n ./client\n\n" COLOR_OFF); } bool ProxyController::copy_files() { struct file_info info; if (check_proxy_type(this->config.proxy_client_type) == PROXY_BASIC_TYPE && check_proxy_type(this->config.proxy_server_type) == PROXY_BASIC_TYPE) { info = { "common/CMakeLists.txt", "CMakeLists.txt", common_cmake_transform }; this->default_files.push_back(info); info = { "common/util.h", "config/util.h", nullptr }; this->default_files.push_back(info); info = { "proxy/proxy_main.cc", "proxy_main.cc", proxy_basic_transform }; this->default_files.push_back(info); info = { "basic/client_main.cc", "client_main.cc", basic_client_transform }; this->default_files.push_back(info); info = { "basic/server_main.cc", "server_main.cc", basic_server_transform }; this->default_files.push_back(info); info = { "config/config_simple.h", "config/config.h", nullptr }; this->default_files.push_back(info); info = { "config/config_simple.cc", "config/config.cc", nullptr }; this->default_files.push_back(info); } else { std::string proxy_main = "proxy/proxy_main_"; if (this->config.idl_type == IDL_TYPE_PROTOBUF) proxy_main += "proto.cc"; else proxy_main += "thrift.cc"; info = { std::move(proxy_main), "proxy_main.cc", proxy_rpc_proxy_transform }; this->default_files.push_back(info); info = { "rpc/CMakeLists.txt", "CMakeLists.txt", rpc_cmake_transform }; this->default_files.push_back(info); info = { "config/config_full.h", "config/config.h", nullptr }; this->default_files.push_back(info); info = { "config/config_full.cc", "config/config.cc", nullptr }; this->default_files.push_back(info); if (this->config.specified_idl_file == NULL) this->fill_rpc_default_files(); else return false; // TODO: NOT supported yet } return CommandController::copy_files(); } static uint8_t proxy_string_to_type(const char *type) { if (strcasecmp(type, "http") == 0) return PROTOCOL_TYPE_HTTP; else if (strcasecmp(type, "redis") == 0) return PROTOCOL_TYPE_REDIS; else if (strcasecmp(type, "mysql") == 0) return PROTOCOL_TYPE_MYSQL; else if (strcasecmp(type, "kafka") == 0) return PROTOCOL_TYPE_KAFKA; else if (strcasecmp(type, "SRPC") == 0) return PROTOCOL_TYPE_SRPC; else if (strcasecmp(type, "SRPCHttp") == 0) return PROTOCOL_TYPE_SRPC_HTTP; else if (strcasecmp(type, "BRPC") == 0) return PROTOCOL_TYPE_BRPC; else if (strcasecmp(type, "TRPC") == 0) return PROTOCOL_TYPE_TRPC; else if (strcasecmp(type, "TRPCHttp") == 0) return PROTOCOL_TYPE_TRPC_HTTP; else if (strcasecmp(type, "Thrift") == 0) return PROTOCOL_TYPE_THRIFT; else if (strcasecmp(type, "ThriftHTTP") == 0) return PROTOCOL_TYPE_THRIFT_HTTP; return PROTOCOL_TYPE_MAX; } static bool proxy_get_opt(int argc, const char **argv, struct srpc_config *config) { int c; while ((c = getopt(argc, (char * const *)argv, "o:c:s:d:")) >= 0) { switch (c) { case 'o': if (sscanf(optarg, "%s", config->output_path) != 1) return false; break; case 'c': config->proxy_client_type = proxy_string_to_type(optarg); break; case 's': config->proxy_server_type = proxy_string_to_type(optarg); break; case 'd': config->specified_depend_path = true; memset(config->depend_path, 0, MAXPATHLEN); if (sscanf(optarg, "%s", config->depend_path) != 1) return false; break; default: printf(COLOR_RED"Error:\n Unknown args : " COLOR_BLUE"%s\n\n" COLOR_OFF, argv[optind - 1]); return false; } } return true; } bool ProxyController::get_opt(int argc, const char **argv) { optind = 2; getcwd(this->config.output_path, MAXPATHLEN); if (proxy_get_opt(argc, argv, &this->config) == false) return false; if (optind == argc) { printf(COLOR_RED "Missing: PROJECT_NAME\n\n" COLOR_OFF); return false; } this->config.project_name = argv[optind]; optind++; if (proxy_get_opt(argc, argv, &this->config) == false) return false; if (this->config.project_name == NULL) { printf(COLOR_RED "Missing: PROJECT_NAME\n\n" COLOR_OFF); return false; } this->config.service_name = this->config.project_name; return true; } bool ProxyController::check_args() { int server_type = check_proxy_type(this->config.proxy_server_type); int client_type = check_proxy_type(this->config.proxy_client_type); if (CommandController::check_args() == false) return false; if (client_type < 0 || server_type < 0) { printf(COLOR_RED"Error:\n Invalid type :" COLOR_BLUE" %s" COLOR_RED ", " COLOR_BLUE "%s\n\n" COLOR_OFF, this->config.proxy_server_type_string(), this->config.proxy_client_type_string()); return false; } if ((client_type == PROXY_BASIC_TYPE && server_type > PROXY_BASIC_TYPE) || (server_type == PROXY_BASIC_TYPE && client_type > PROXY_BASIC_TYPE) || // TODO: temperarily only support workflow to workflow, rpc to rpc (client_type == PROXY_PROTOBUF_TYPE && server_type == PROXY_THRIFT_TYPE) || (server_type == PROXY_PROTOBUF_TYPE && client_type == PROXY_THRIFT_TYPE)) // TODO: temperarily NOT support protobuf with thrift { printf(COLOR_RED"Error:\n Temperarily not support " COLOR_BLUE"%s" COLOR_RED " and " COLOR_BLUE "%s" COLOR_RED" together\n\n" COLOR_OFF, this->config.proxy_server_type_string(), this->config.proxy_client_type_string()); return false; } if (client_type == PROXY_PROTOBUF_TYPE) { this->config.idl_type = IDL_TYPE_PROTOBUF; } else if (client_type == PROXY_THRIFT_TYPE) { // this->config.idl_type = IDL_TYPE_THRIFT; printf(COLOR_RED"Error:\n Temperarily not support IDL thrift.\n\n" COLOR_OFF); return false; } return true; } srpc-0.10.1/tools/srpc_rpc_controller.cc000066400000000000000000000264031454502251400202740ustar00rootroot00000000000000/* Copyright (c) 2022 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include "srpc_controller.h" RPCController::RPCController() { this->config.type = COMMAND_RPC; struct file_info info; info = { "rpc/CMakeLists.txt", "CMakeLists.txt", rpc_cmake_transform }; this->default_files.push_back(info); info = { "common/config.json", "client.conf", nullptr }; this->default_files.push_back(info); info = { "common/config.json", "server.conf", nullptr }; this->default_files.push_back(info); info = { "common/GNUmakefile", "GNUmakefile", nullptr }; this->default_files.push_back(info); info = { "config/Json.h", "config/Json.h", nullptr }; this->default_files.push_back(info); info = { "config/Json.cc", "config/Json.cc", nullptr }; this->default_files.push_back(info); info = { "config/config_full.h", "config/config.h", nullptr }; this->default_files.push_back(info); info = { "config/config_full.cc", "config/config.cc", nullptr }; this->default_files.push_back(info); info = { "common/config.json", "full.conf", nullptr }; this->default_files.push_back(info); } void RPCController::print_usage(const char *name) const { printf(COLOR_PINK"Usage:\n" COLOR_INFO" %s " COLOR_COMMAND "rpc " COLOR_INFO" " COLOR_FLAG "[FLAGS]\n\n" COLOR_PINK"Example:\n" COLOR_PURPLE" %s rpc my_rpc_project\n\n" COLOR_PINK"Available Flags:\n" COLOR_FLAG" -r " COLOR_WHITE": rpc type [ SRPC | SRPCHttp | BRPC | Thrift | " "ThriftHttp | TRPC | TRPCHttp ] (default: SRPC)\n" COLOR_FLAG" -o " COLOR_WHITE": project output path (default: CURRENT_PATH)\n" COLOR_FLAG" -s " COLOR_WHITE": service name (default: PROJECT_NAME)\n" COLOR_FLAG" -i " COLOR_WHITE": idl type [ protobuf | thrift ] (default: protobuf)\n" COLOR_FLAG" -x " COLOR_WHITE": data type [ protobuf | thrift | json ] " "(default: idl type. json for http)\n" COLOR_FLAG" -c " COLOR_WHITE": compress type [ gzip | zlib | snappy | lz4 ] " "(default: no compression)\n" COLOR_FLAG" -d " COLOR_WHITE": path of dependencies (default: COMPILE_PATH)\n" COLOR_FLAG" -f " COLOR_WHITE": specify the idl_file to generate codes " "(default: templates/rpc/IDL_FILE)\n" COLOR_FLAG" -p " COLOR_WHITE": specify the path for idl_file to depend " "(default: templates/rpc/)\n" COLOR_OFF, name, name); } bool RPCController::copy_files() { struct srpc_config *config = &this->config; int ret = true; if (config->specified_idl_file == NULL) // fill the default rpc files { this->fill_rpc_default_files(); } else { // copy specified idl file and generate rpc files struct GeneratorParams params; params.out_dir = config->output_path; params.input_dir = config->specified_idl_path; if (params.input_dir[params.input_dir.length() - 1] != '/') params.input_dir += "/"; params.idl_file = config->specified_idl_file; auto pos = params.idl_file.find_last_of('/'); if (pos != std::string::npos) params.idl_file = params.idl_file.substr(pos + 1); std::string out_file = std::string(config->output_path) + std::string("/") + params.idl_file; if (CommandController::copy_single_file(config->specified_idl_file, out_file, nullptr) == false) return false; ControlGenerator gen(config); printf(COLOR_PURPLE"Info: srpc generator begin.\n" COLOR_OFF); ret = gen.generate(params); if (ret == false) { printf(COLOR_RED"Error: srpc generator error.\n\n" COLOR_OFF); return false; } printf(COLOR_PURPLE"Info: srpc generator done.\n\n" COLOR_OFF); } return CommandController::copy_files(); } static bool rpc_get_opt(int argc, const char **argv, struct srpc_config *config) { int c; while ((c = getopt(argc, (char * const *)argv, "o:r:i:x:c:s:d:f:p:")) >= 0) { switch (c) { case 'o': if (sscanf(optarg, "%s", config->output_path) != 1) return false; break; case 'r': config->set_rpc_type(optarg); break; case 'i': config->set_idl_type(optarg); break; case 'x': config->set_data_type(optarg); break; case 'c': config->set_compress_type(optarg); break; case 's': config->service_name = optarg; break; case 'd': config->specified_depend_path = true; memset(config->depend_path, 0, MAXPATHLEN); if (sscanf(optarg, "%s", config->depend_path) != 1) return false; break; case 'f': config->specified_idl_file = optarg; break; case 'p': config->specified_idl_path = optarg; break; default: printf(COLOR_RED "Error:\n Unknown args : " COLOR_BLUE "%s\n\n" COLOR_OFF, argv[optind - 1]); return false; } } return true; } bool RPCController::get_opt(int argc, const char **argv) { optind = 2; if (rpc_get_opt(argc, argv, &this->config) == false) return false; if (optind == argc) { printf(COLOR_RED "Missing: PROJECT_NAME\n\n" COLOR_OFF); return false; } this->config.project_name = argv[optind]; optind++; if (rpc_get_opt(argc, argv, &this->config) == false) return false; if (this->config.project_name == NULL) { printf(COLOR_RED "Missing: PROJECT_NAME\n\n" COLOR_OFF); return false; } return true; } bool RPCController::check_args() { if (CommandController::check_args() == false) return false; struct srpc_config *config = &this->config; if (config->rpc_type == PROTOCOL_TYPE_MAX || config->idl_type == IDL_TYPE_MAX || config->data_type == DATA_TYPE_MAX || config->compress_type == COMPRESS_TYPE_MAX) { printf(COLOR_RED"Error:\n Invalid rpc args : -r | -i | -c | -d .\n\n" COLOR_OFF); return false; } switch (config->rpc_type) { case PROTOCOL_TYPE_THRIFT: case PROTOCOL_TYPE_THRIFT_HTTP: if (config->idl_type == IDL_TYPE_PROTOBUF || config->data_type == DATA_TYPE_PROTOBUF) { printf(COLOR_RED"Error:\n " COLOR_BLUE"\" %s \" " COLOR_RED"does NOT support protobuf as idl or data type.\n\n" COLOR_OFF, config->rpc_type_string()); return false; } if (config->idl_type == IDL_TYPE_DEFAULT) config->idl_type = IDL_TYPE_THRIFT; // as default; break; case PROTOCOL_TYPE_BRPC: case PROTOCOL_TYPE_TRPC: case PROTOCOL_TYPE_TRPC_HTTP: if (config->idl_type == IDL_TYPE_THRIFT || config->data_type == DATA_TYPE_THRIFT) { printf(COLOR_RED "Error:\n " COLOR_BLUE "\" %s \" " COLOR_RED "does NOT support thrift as idl or data type.\n" COLOR_OFF, config->rpc_type_string()); return false; } default: if (config->idl_type == IDL_TYPE_DEFAULT) config->idl_type = IDL_TYPE_PROTOBUF; // as default; break; } if (config->prepare_specified_idl_file() == false) return false; if (config->service_name == NULL) config->service_name = config->project_name; return true; } APIController::APIController() { this->config.type = COMMAND_API; this->config.idl_type = IDL_TYPE_PROTOBUF; } void APIController::print_usage(const char *name) const { printf(COLOR_PINK"Usage:\n" COLOR_INFO" %s " COLOR_COMMAND "api " COLOR_INFO" " COLOR_FLAG "[FLAGS]\n\n" COLOR_PINK"Example:\n" COLOR_PURPLE" %s api my_api\n\n" COLOR_PINK"Available Flags:\n" COLOR_FLAG" -o " COLOR_WHITE": file output path (default: CURRENT_PATH)\n" COLOR_FLAG" -i " COLOR_WHITE": idl type [ protobuf | thrift ] (default: protobuf)\n" COLOR_OFF, name, name); } static bool api_get_opt(int argc, const char **argv, struct srpc_config *config) { char c; while ((c = getopt(argc, (char * const *)argv, "o:i:")) != -1) { switch (c) { case 'o': memset(config->output_path, 0, MAXPATHLEN); if (sscanf(optarg, "%s", config->output_path) != 1) return false; break; case 'i': config->set_idl_type(optarg); break; default: printf(COLOR_RED "Error:\n Unknown args : " COLOR_BLUE "%s\n\n" COLOR_OFF, argv[optind - 1]); return false; } } return true; } bool APIController::get_opt(int argc, const char **argv) { optind = 2; getcwd(this->config.output_path, MAXPATHLEN); if (api_get_opt(argc, argv, &this->config) == false) return false; if (optind == argc) { printf(COLOR_RED "Missing: FILE_NAME\n\n" COLOR_OFF); return false; } this->config.project_name = argv[optind]; optind++; if (api_get_opt(argc, argv, &this->config) == false) return false; if (this->config.project_name == NULL) { printf(COLOR_RED "Missing: FILE_NAME\n\n" COLOR_OFF); return false; } this->config.service_name = this->config.project_name; return true; } bool APIController::dependencies_and_dir() { std::string idl_file_name; std::string out_file_name = this->config.project_name; if (this->config.idl_type == IDL_TYPE_PROTOBUF) { out_file_name += ".proto"; idl_file_name = "rpc/rpc.proto"; } else if (this->config.idl_type == IDL_TYPE_THRIFT) { out_file_name += ".thrift"; idl_file_name = "rpc/rpc.thrift"; } std::string abs_file_name = this->config.output_path; if (abs_file_name.at(abs_file_name.length() - 1) != '/') abs_file_name += "/"; abs_file_name += out_file_name; DIR *dir; dir = opendir(this->config.output_path); if (dir == NULL) { if (mkdir_p(this->config.output_path, 0755) != 0) { perror("Error:\n failed to make output_path "); return false; } } else { closedir(dir); struct stat st; if (stat(abs_file_name.c_str(), &st) >= 0) { printf(COLOR_RED"Error:\n file" COLOR_BLUE" %s " COLOR_RED "EXISTED in path" COLOR_BLUE" %s " COLOR_RED ".\n" COLOR_OFF, out_file_name.c_str(), this->config.output_path); return false; } } dir = opendir(this->config.template_path); if (dir == NULL) { printf(COLOR_RED"Error:\n template path " COLOR_BLUE" %s " COLOR_RED "does NOT exist.\n" COLOR_OFF, this->config.template_path); return false; } struct file_info info; info = { idl_file_name , out_file_name, rpc_idl_transform }; this->default_files.push_back(info); closedir(dir); return true; } void APIController::print_success_info() const { std::string file_name = this->config.project_name; if (this->config.idl_type == IDL_TYPE_PROTOBUF) file_name += ".proto"; else if (this->config.idl_type == IDL_TYPE_THRIFT) file_name += ".thrift"; printf(COLOR_GREEN"Success:\n Create api file" COLOR_BLUE" %s " COLOR_GREEN "at path" COLOR_BLUE" %s " COLOR_GREEN "done.\n\n" COLOR_OFF, file_name.c_str(), this->config.output_path); printf(COLOR_PINK"Suggestions:\n" COLOR_WHITE" Modify the api file as you needed.\n" " And make rpc project base on this file with the following command:\n\n" COLOR_GREEN" ./srpc rpc my_rpc_project -f %s -p %s\n\n" COLOR_OFF, file_name.c_str(), this->config.output_path); } srpc-0.10.1/tools/templates/000077500000000000000000000000001454502251400157005ustar00rootroot00000000000000srpc-0.10.1/tools/templates/basic/000077500000000000000000000000001454502251400167615ustar00rootroot00000000000000srpc-0.10.1/tools/templates/basic/client.conf000066400000000000000000000001471454502251400211100ustar00rootroot00000000000000{ "client": { "remote_host": "127.0.0.1", "remote_port": %u,%s "retry_max": 2%s } } srpc-0.10.1/tools/templates/basic/client_main.cc000066400000000000000000000021461454502251400215550ustar00rootroot00000000000000#include #include #include "workflow/%sMessage.h" #include "workflow/WFTaskFactory.h" #include "workflow/WFFacilities.h" #include "config/config.h" static WFFacilities::WaitGroup wait_group(1); static srpc::RPCConfig config; void sig_handler(int signo) { wait_group.done(); } void init() { if (config.load("./client.conf") == false) { perror("Load config failed"); exit(1); } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); } void callback(WF%sTask *task) { int state = task->get_state(); int error = task->get_error(); fprintf(stderr, "%s client state = %%d error = %%d\n", state, error); %s } int main() { init(); std::string url = std::string("%s://") + %sconfig.client_host() + std::string(":") + std::to_string(config.client_port()); WF%sTask *task = WFTaskFactory::create_%s_task(url,%s config.retry_max(), callback); %s task->start(); wait_group.wait(); return 0; } srpc-0.10.1/tools/templates/basic/compute_server_main.cc000066400000000000000000000041101454502251400233320ustar00rootroot00000000000000#include #include #include #include "workflow/WFHttpServer.h" #include "workflow/WFFacilities.h" #include "config/config.h" static WFFacilities::WaitGroup wait_group(1); static srpc::RPCConfig config; void sig_handler(int signo) { wait_group.done(); } void init() { if (config.load("./server.conf") == false) { perror("Load config failed"); exit(1); } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); } // Example for Fibonacci. void Fibonacci(int n, protocol::HttpResponse *resp) { unsigned long long x = 0, y = 1; char buf[256]; int i; if (n <= 0 || n > 94) { resp->append_output_body_nocopy("Invalid Number.", strlen("Invalid Number.")); return; } resp->append_output_body_nocopy("", strlen("")); for (i = 2; i < n; i++) { sprintf(buf, "

%llu + %llu = %llu.

", x, y, x + y); resp->append_output_body(buf); y = x + y; x = y - x; } if (n == 1) y = 0; sprintf(buf, "

The No. %d Fibonacci number is: %llu.

", n, y); resp->append_output_body(buf); resp->append_output_body_nocopy("", strlen("")); } void process(WFHttpTask *task) { const char *uri = task->get_req()->get_request_uri(); if (*uri == '/') uri++; int n = atoi(uri); protocol::HttpResponse *resp = task->get_resp(); fprintf(stderr, "server get request. n = %d\n", n); // All calculations can be encapsulated by 'go_task' WFGoTask *go_task = WFTaskFactory::create_go_task("go", Fibonacci, n, resp); // Tasks will be dispatch asynchonously after 'push_back()' series_of(task)->push_back(go_task); } int main() { init(); WFHttpServer server(process); if (server.start(config.server_port()) == 0) { fprintf(stderr, "Computing server started, port %u\n", config.server_port()); wait_group.wait(); server.stop(); } else perror("server start"); return 0; } srpc-0.10.1/tools/templates/basic/server.conf000066400000000000000000000000501454502251400211310ustar00rootroot00000000000000{ "server": { "port": %u } } srpc-0.10.1/tools/templates/basic/server_main.cc000066400000000000000000000015741454502251400216110ustar00rootroot00000000000000#include #include #include "workflow/WF%sServer.h" #include "workflow/WFFacilities.h" #include "config/util.h" #include "config/config.h" static WFFacilities::WaitGroup wait_group(1); static srpc::RPCConfig config; void sig_handler(int signo) { wait_group.done(); } void init() { if (config.load("./server.conf") == false) { perror("Load config failed"); exit(1); } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); } void process(WF%sTask *task) { // delete the example codes and fill your logic %s } int main() { init(); WF%sServer server(process); if (server.start(config.server_port()) == 0) { fprintf(stderr, "%s server started, port %%u\n", config.server_port()); wait_group.wait(); server.stop(); } else perror("server start"); return 0; } srpc-0.10.1/tools/templates/common/000077500000000000000000000000001454502251400171705ustar00rootroot00000000000000srpc-0.10.1/tools/templates/common/CMakeLists.txt000066400000000000000000000021101454502251400217220ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(%s LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_BUILD_TYPE RelWithDebInfo) # Find all the dependencies find_package(OpenSSL REQUIRED) set(Workflow_DIR "%s") find_package(Workflow REQUIRED CONFIG HINTS ${Workflow_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}) # Prefer to static link first set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ${CMAKE_FIND_LIBRARY_SUFFIXES}) find_library(Workflow_LIB workflow HINTS ${Workflow_DIR}/_lib) # Set all the libraries here set(LIB ${Workflow_LIB} pthread OpenSSL::SSL OpenSSL::Crypto) # Add all the common codes here set(COMMON_CODE config/config.cc config/Json.cc%s) # Add header directories and library directories here include_directories(${OPENSSL_INCLUDE_DIR} ${WORKFLOW_INCLUDE_DIR}) link_directories(${OPENSSL_LINK_DIR} ${WORKFLOW_LIB_DIR}) # Build executable outputs set(PROJECT_OUTPUT server%s) foreach(output ${PROJECT_OUTPUT}) add_executable(${output} ${output}_main.cc ${COMMON_CODE}) target_link_libraries(${output} ${LIB}) endforeach() srpc-0.10.1/tools/templates/common/GNUmakefile000066400000000000000000000005221454502251400212410ustar00rootroot00000000000000ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) BUILD_DIR := build.cmake .PHONY: all clean all: base make -C $(BUILD_DIR) -f Makefile base: mkdir -p $(BUILD_DIR) cd $(BUILD_DIR) && cmake $(ROOT_DIR) clean: ifeq ($(BUILD_DIR), $(wildcard $(BUILD_DIR))) make -C $(BUILD_DIR) clean rm -rf $(BUILD_DIR) endif srpc-0.10.1/tools/templates/common/config.json000066400000000000000000000040001454502251400213220ustar00rootroot00000000000000{ "server": { "port": 8080 }, "client": { "remote_host": "127.0.0.1", "remote_port": 8080, "is_ssl" : false, "redirect_max": 2, "retry_max": 1, "user_name": "root", "password": "", "callee" : "rpc_client" }, "global": { "poller_threads": 4, "handler_threads": 20, "dns_threads": 4, "compute_threads": -1, "dns_ttl_default": 43200, "dns_ttl_min": 180, "resolv_conf_path": "/etc/resolv.conf", "hosts_path": "/etc/hosts", "endpoint_params": { "max_connections": 200, "connect_timeout": 10000, "response_timeout": 10000, "ssl_connect_timeout": 10000, "use_tls_sni": false }, "dns_server_params": { "max_connections": 200 } }, "metrics":[ { "filter": "prometheus", "port": 8000 }, { "filter": "opentelemetry", "address": "http://opentelemetry.com:4389", "redirect_max": 0, "retry_max": 1, "report_threshold": 100, "report_interval_ms": 1000, "attributes": [ { "key": "tenant.id", "value": "abcd" } ] } ], "trace":[ { "filter": "default", "span_per_second": 1000 }, { "filter": "opentelemetry", "address": "http://opentelemetry.com:4389", "redirect_max": 0, "retry_max": 1, "span_per_second": 1000, "report_threshold": 100, "report_interval_ms": 1000, "attributes": [ { "key": "tenant.id", "value": "abcd" } ] } ], "upstream":[ { "name": "test_weighted_random_host", "type": "weighted_random", "try_another": false, "server":[ { "host": "127.0.0.1:8080", "params": {"weight": 2} }, { "host": "127.0.0.1:8081" } ] }, { "name": "test_consistent_hash_host", "type": "consistent_hash", "server":[ { "host": "127.0.0.1:8083" }, { "host": "127.0.0.1:8084" } ] } ] } srpc-0.10.1/tools/templates/common/util.h000066400000000000000000000017451454502251400203250ustar00rootroot00000000000000#include #include #include #include #include #include "workflow/WFTaskFactory.h" template void print_peer_address(TASK *server_task) { char addrstr[128]; struct sockaddr_storage addr; socklen_t l = sizeof addr; unsigned short port = 0; long long seq = server_task->get_task_seq(); server_task->get_peer_addr((struct sockaddr *)&addr, &l); if (addr.ss_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)&addr; inet_ntop(AF_INET, &sin->sin_addr, addrstr, 128); port = ntohs(sin->sin_port); } else if (addr.ss_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, 128); port = ntohs(sin6->sin6_port); } else strcpy(addrstr, "Unknown"); fprintf(stderr, "peer address: %s:%d, seq: %lld.\n", addrstr, port, seq); } srpc-0.10.1/tools/templates/config/000077500000000000000000000000001454502251400171455ustar00rootroot00000000000000srpc-0.10.1/tools/templates/config/Json.cc000066400000000000000000000620131454502251400203670ustar00rootroot00000000000000#include "Json.h" namespace wfrest { namespace { json_value_t* json_value_copy(const json_value_t* val); json_value_t* json_value_copy_object(const json_value_t* val) { json_value_t* dest_val = json_value_create(JSON_VALUE_OBJECT); json_object_t* dest_obj = json_value_object(dest_val); json_object_t* obj = json_value_object(val); const char* name; json_object_for_each(name, val, obj) json_object_append(dest_obj, name, 0, json_value_copy(val)); return dest_val; } json_value_t* json_value_copy_array(const json_value_t* val) { json_value_t* dest_val = json_value_create(JSON_VALUE_ARRAY); json_array_t* dest_arr = json_value_array(dest_val); json_array_t* arr = json_value_array(val); json_array_for_each(val, arr) json_array_append(dest_arr, 0, json_value_copy(val)); return dest_val; } json_value_t* json_value_copy(const json_value_t* val) { switch (json_value_type(val)) { case JSON_VALUE_STRING: return json_value_create(JSON_VALUE_STRING, json_value_string(val)); case JSON_VALUE_NUMBER: return json_value_create(JSON_VALUE_NUMBER, json_value_number(val)); case JSON_VALUE_OBJECT: return json_value_copy_object(val); case JSON_VALUE_ARRAY: return json_value_copy_array(val); default: return json_value_create(json_value_type(val)); } } } // namespace // ------------------------ Constructor ------------------------- Json::Json() : node_(json_value_create(JSON_VALUE_NULL)), parent_(nullptr), allocated_(true) { } Json::Json(const std::string& str) : node_(json_value_create(JSON_VALUE_STRING, str.c_str())), parent_(nullptr), allocated_(true) { } Json::Json(const char* str) : node_(json_value_create(JSON_VALUE_STRING, str)), parent_(nullptr), allocated_(true) { } Json::Json(std::nullptr_t null) : node_(json_value_create(JSON_VALUE_NULL)), parent_(nullptr), allocated_(true) { } Json::Json(bool val) : node_(val ? json_value_create(JSON_VALUE_TRUE) : json_value_create(JSON_VALUE_FALSE)), parent_(nullptr), allocated_(true) { } Json::Json(const std::vector& val) : node_(json_value_create(JSON_VALUE_ARRAY)), parent_(nullptr), allocated_(true) { json_array_t* arr = json_value_array(node_); for (const auto& str : val) { json_array_append(arr, JSON_VALUE_STRING, str.c_str()); } } // for parse Json::Json(const std::string& str, bool parse_flag) : parent_(nullptr) { node_ = json_value_parse(str.c_str()); allocated_ = node_ == nullptr ? false : true; } Json::~Json() { destroy_node(&node_); } // watcher constructor Json::Json(const json_value_t* node, const json_value_t* parent, std::string&& key) : node_(const_cast(node)), parent_(parent), allocated_(false), parent_key_(std::move(key)) { } Json::Json(const json_value_t* node, const json_value_t* parent, const std::string& key) : node_(const_cast(node)), parent_(parent), allocated_(false), parent_key_(key) { } Json::Json(JsonType type) : allocated_(true) { if (type == JsonType::Object) { node_ = json_value_create(JSON_VALUE_OBJECT); } else { node_ = json_value_create(JSON_VALUE_ARRAY); } } Json::Json(const Json& other) { node_ = json_value_copy(other.node_); allocated_ = true; } Json& Json::operator=(const Json& other) { if (this == &other) { return *this; } destroy_node(&node_); node_ = json_value_copy(other.node_); parent_ = nullptr; allocated_ = true; parent_key_.clear(); return *this; } Json::Json(Json&& other) : node_(other.node_), parent_(other.parent_), allocated_(other.allocated_), parent_key_(std::move(other.parent_key_)) { other.node_ = nullptr; other.parent_ = nullptr; other.allocated_ = false; } Json& Json::operator=(Json&& other) { if (this == &other) { return *this; } destroy_node(&node_); node_ = other.node_; other.node_ = nullptr; parent_ = other.parent_; other.parent_ = nullptr; allocated_ = other.allocated_; other.allocated_ = false; parent_key_ = std::move(other.parent_key_); return *this; } Json Json::parse(const std::string& str) { return Json(str, true); } Json Json::parse(const std::ifstream& stream) { std::stringstream buffer; buffer << stream.rdbuf(); return Json(buffer.str(), true); } Json Json::parse(FILE* fp) { if (fp == nullptr) { return Json(); } fseek(fp, 0, SEEK_END); long length = ftell(fp); fseek(fp, 0, SEEK_SET); char* buffer = (char*)malloc(length + 1); buffer[length] = '\0'; long ret = fread(buffer, 1, length, fp); Json js; if (ret != length) { js = Json(); } else { js = Json(buffer, true); } free(buffer); return js; } std::string Json::dump() const { return dump(0); } std::string Json::dump(int spaces) const { std::string str; str.reserve(64); value_convert(node_, spaces, 0, &str); return str; } Json Json::operator[](const char* key) { if (is_null() && is_root()) { // todo : need is_root here? to_object(); } else if (is_object()) { // if exists json_object_t* obj = json_value_object(node_); const json_value_t* res = json_object_find(key, obj); if (res != nullptr) { return Json(res, node_, std::string(key)); } } if (is_placeholder()) { destroy_node(&node_); json_object_t* parent_obj = json_value_object(parent_); node_ = const_cast(json_object_append( parent_obj, parent_key_.c_str(), JSON_VALUE_OBJECT)); } if (!is_object()) { return Json(); } // (null, parent(node_), key) Json js = Json(); js.set_parent(node_, std::string(key)); return js; } Json Json::operator[](const char* key) const { if (!is_object()) { return Json(); } const json_value_t* val = node_; json_object_t* obj = json_value_object(val); const json_value_t* res = json_object_find(key, obj); if (res != nullptr) { return Json(res, node_, ""); } return Json(); } Json Json::operator[](const std::string& key) { return this->operator[](key.c_str()); } Json Json::operator[](const std::string& key) const { return this->operator[](key.c_str()); } bool Json::has(const std::string& key) const { json_object_t* obj = json_value_object(node_); const json_value_t* find = json_object_find(key.c_str(), obj); return find != nullptr; } void Json::erase(const std::string& key) { if (!is_object()) return; json_object_t* obj = json_value_object(node_); const json_value_t* find = json_object_find(key.c_str(), obj); if (find == nullptr) return; json_value_t* remove_val = json_object_remove(find, obj); json_value_destroy(remove_val); } Json Json::operator[](int index) { if (!is_array() || index < 0 || index > this->size()) { return Json(); } const json_value_t* val; json_array_t* arr = json_value_array(node_); json_array_for_each(val, arr) { if (index == 0) { return Json(val, node_, ""); } index--; } return Json(); } void Json::erase(int index) { if (!is_array()) return; int cnt = 0; json_array_t* arr = json_value_array(node_); const json_value_t* arr_cursor = nullptr; json_array_for_each(arr_cursor, arr) { if (cnt++ == index) break; } json_value_t* remove_val = json_array_remove(arr_cursor, arr); json_value_destroy(remove_val); } Json Json::operator[](int index) const { if (!is_array() || index < 0 || index > this->size()) { return Json(); } const json_value_t* val; json_array_t* arr = json_value_array(node_); json_array_for_each(val, arr) { if (index == 0) { return Json(val, node_, ""); } index--; } return Json(); } bool Json::can_obj_push_back() { if (is_placeholder() || (parent_ != nullptr && json_value_type(parent_) == JSON_VALUE_OBJECT)) { return true; } if (is_root() && is_null()) { to_object(); } return is_object(); } void Json::push_back(const std::string& key, bool val) { if (is_placeholder()) { *this = Json::Object{{key, val}}; return; } if (!can_obj_push_back()) { return; } json_object_t* obj = json_value_object(node_); int type = val ? JSON_VALUE_TRUE : JSON_VALUE_FALSE; json_object_append(obj, key.c_str(), type); } void Json::push_back(const std::string& key, std::nullptr_t val) { if (is_placeholder()) { *this = Json::Object{{key, val}}; return; } if (!can_obj_push_back()) { return; } json_object_t* obj = json_value_object(node_); json_object_append(obj, key.c_str(), JSON_VALUE_NULL); } void Json::push_back(const std::string& key, const std::string& val) { push_back(key, val.c_str()); } void Json::push_back(const std::string& key, const char* val) { if (is_placeholder()) { *this = Json::Object{{key, val}}; return; } if (!can_obj_push_back()) { return; } json_object_t* obj = json_value_object(node_); json_object_append(obj, key.c_str(), JSON_VALUE_STRING, val); } void Json::push_back(const std::string& key, const std::vector& val) { if (is_placeholder()) { *this = Json::Object{{key, val}}; return; } if (!can_obj_push_back()) { return; } json_object_t* obj = json_value_object(node_); const json_value_t* v = json_object_append(obj, key.c_str(), JSON_VALUE_ARRAY); json_array_t* arr = json_value_array(v); for (const auto& str : val) { json_array_append(arr, JSON_VALUE_STRING, str.c_str()); } } void Json::push_back(const std::string& key, const Json& val) { if (is_placeholder()) { *this = Json::Object{{key, val}}; return; } if (!can_obj_push_back()) { return; } json_object_t* obj = json_value_object(node_); Json copy_json = val; json_object_append(obj, key.c_str(), 0, copy_json.node_); copy_json.reset(); } void Json::placeholder_push_back(const std::string& key, bool val) { json_object_t* obj = json_value_object(parent_); destroy_node(&node_); if (val) { node_ = const_cast( json_object_append(obj, key.c_str(), JSON_VALUE_TRUE)); } else { node_ = const_cast( json_object_append(obj, key.c_str(), JSON_VALUE_FALSE)); } } void Json::placeholder_push_back(const std::string& key, std::nullptr_t val) { json_object_t* obj = json_value_object(parent_); destroy_node(&node_); node_ = const_cast( json_object_append(obj, key.c_str(), JSON_VALUE_NULL)); } void Json::placeholder_push_back(const std::string& key, const std::string& val) { placeholder_push_back(key, val.c_str()); } void Json::placeholder_push_back(const std::string& key, const char* val) { json_object_t* obj = json_value_object(parent_); destroy_node(&node_); node_ = const_cast( json_object_append(obj, key.c_str(), JSON_VALUE_STRING, val)); } void Json::placeholder_push_back(const std::string& key, const std::vector& val) { json_object_t* obj = json_value_object(parent_); destroy_node(&node_); node_ = const_cast( json_object_append(obj, key.c_str(), JSON_VALUE_ARRAY)); json_array_t* arr = json_value_array(node_); for (const auto& str : val) json_array_append(arr, JSON_VALUE_STRING, str.c_str()); } void Json::placeholder_push_back(const std::string& key, const Json& val) { json_object_t* obj = json_value_object(parent_); destroy_node(&node_); Json copy_json = val; node_ = const_cast( json_object_append(obj, key.c_str(), 0, copy_json.node_)); copy_json.reset(); } void Json::normal_push_back(const std::string& key, bool val) { json_object_t* obj = json_value_object(parent_); const json_value_t* find = json_object_find(key.c_str(), obj); int type = val ? JSON_VALUE_TRUE : JSON_VALUE_FALSE; if (find == nullptr) { json_object_append(obj, key.c_str(), type); return; } json_object_insert_before(find, obj, key.c_str(), type); json_value_t* remove_val = json_object_remove(find, obj); json_value_destroy(remove_val); } void Json::normal_push_back(const std::string& key, std::nullptr_t val) { json_object_t* obj = json_value_object(parent_); const json_value_t* find = json_object_find(key.c_str(), obj); if (find == nullptr) { json_object_append(obj, key.c_str(), JSON_VALUE_NULL); return; } json_object_insert_before(find, obj, key.c_str(), JSON_VALUE_NULL); json_value_t* remove_val = json_object_remove(find, obj); json_value_destroy(remove_val); } void Json::normal_push_back(const std::string& key, const std::string& val) { normal_push_back(key, val.c_str()); } void Json::normal_push_back(const std::string& key, const char* val) { json_object_t* obj = json_value_object(parent_); const json_value_t* find = json_object_find(key.c_str(), obj); if (find == nullptr) { json_object_append(obj, key.c_str(), JSON_VALUE_STRING, val); return; } json_object_insert_before(find, obj, key.c_str(), JSON_VALUE_STRING, val); json_value_t* remove_val = json_object_remove(find, obj); json_value_destroy(remove_val); } void Json::normal_push_back(const std::string& key, const std::vector& val) { json_object_t* obj = json_value_object(parent_); const json_value_t* find = json_object_find(key.c_str(), obj); const json_value_t* v; if (find == nullptr) { v = json_object_append(obj, key.c_str(), JSON_VALUE_ARRAY); } else { v = json_object_insert_before(find, obj, key.c_str(), JSON_VALUE_ARRAY); json_value_t* remove_val = json_object_remove(find, obj); json_value_destroy(remove_val); } json_array_t* arr = json_value_array(v); for (const auto& str : val) json_array_append(arr, JSON_VALUE_STRING, str.c_str()); } void Json::normal_push_back(const std::string& key, const Json& val) { json_object_t* obj = json_value_object(parent_); const json_value_t* find = json_object_find(key.c_str(), obj); Json copy_json = val; if (find == nullptr) { json_object_append(obj, key.c_str(), 0, copy_json.node_); copy_json.node_ = nullptr; return; } json_object_insert_before(find, obj, key.c_str(), 0, copy_json.node_); copy_json.reset(); json_value_t* remove_val = json_object_remove(find, obj); json_value_destroy(remove_val); } bool Json::can_arr_push_back() { if (is_root() && is_null()) { to_array(); } return is_array(); } Json Json::copy() const { return *this; } void Json::push_back(bool val) { if (is_placeholder()) { *this = Json::Array{{val}}; return; } if (!can_arr_push_back()) { return; } json_array_t* arr = json_value_array(node_); int type = val ? JSON_VALUE_TRUE : JSON_VALUE_FALSE; json_array_append(arr, type); } void Json::push_back(std::nullptr_t val) { if (is_placeholder()) { *this = Json::Array{{val}}; return; } if (!can_arr_push_back()) { return; } json_array_t* arr = json_value_array(node_); json_array_append(arr, JSON_VALUE_NULL); } void Json::push_back(const std::string& val) { push_back(val.c_str()); } void Json::push_back(const std::vector& val) { for (const auto& str : val) { push_back(str.c_str()); } } void Json::push_back(const char* val) { if (is_placeholder()) { *this = Json::Array{{val}}; return; } if (!can_arr_push_back()) { return; } json_array_t* arr = json_value_array(node_); json_array_append(arr, JSON_VALUE_STRING, val); } void Json::push_back(const Json& val) { if (is_placeholder()) { *this = Json::Array{{val}}; return; } if (!can_arr_push_back()) { return; } json_array_t* arr = json_value_array(node_); Json copy_json = val; json_array_append(arr, 0, copy_json.node_); copy_json.reset(); } void Json::update_arr(bool val) { json_array_t* arr = json_value_array(parent_); int type = val ? JSON_VALUE_TRUE : JSON_VALUE_FALSE; json_array_insert_before(node_, arr, type); json_value_t* remove_val = json_array_remove(node_, arr); json_value_destroy(remove_val); } void Json::update_arr(std::nullptr_t val) { json_array_t* arr = json_value_array(parent_); json_array_insert_before(node_, arr, JSON_VALUE_NULL); json_value_t* remove_val = json_array_remove(node_, arr); json_value_destroy(remove_val); } void Json::update_arr(const std::string& val) { update_arr(val.c_str()); } void Json::update_arr(const char* val) { json_array_t* arr = json_value_array(parent_); json_array_insert_before(node_, arr, JSON_VALUE_STRING, val); json_value_t* remove_val = json_array_remove(node_, arr); json_value_destroy(remove_val); } void Json::update_arr(const std::vector& val) { json_array_t* arr = json_value_array(parent_); const json_value_t* v = json_array_insert_before(node_, arr, JSON_VALUE_ARRAY); json_value_t* remove_val = json_array_remove(node_, arr); json_value_destroy(remove_val); arr = json_value_array(v); for (const auto& str : val) { json_array_append(arr, JSON_VALUE_STRING, str.c_str()); } } void Json::update_arr(const Json& val) { json_array_t* arr = json_value_array(parent_); Json copy_json = val; json_array_insert_before(node_, arr, 0, copy_json.node_); copy_json.reset(); json_value_t* remove_val = json_array_remove(node_, arr); json_value_destroy(remove_val); } std::string Json::type_str() const { switch (type()) { case JSON_VALUE_STRING: return "string"; case JSON_VALUE_NUMBER: return "number"; case JSON_VALUE_OBJECT: return "object"; case JSON_VALUE_ARRAY: return "array"; case JSON_VALUE_TRUE: return "true"; case JSON_VALUE_FALSE: return "false"; case JSON_VALUE_NULL: return "null"; } return "unknown"; } int Json::size() const { if (type() == JSON_VALUE_ARRAY) { json_array_t* array = json_value_array(node_); return json_array_size(array); } else if (type() == JSON_VALUE_OBJECT) { json_object_t* obj = json_value_object(node_); return json_object_size(obj); } return 1; } bool Json::empty() const { switch (type()) { case JSON_VALUE_NULL: { // null values are empty return true; } case JSON_VALUE_ARRAY: case JSON_VALUE_OBJECT: { return size() == 0; } default: // all other types are nonempty return false; } } void Json::clear() { int type = json_value_type(node_); destroy_node(&node_); parent_ = nullptr; allocated_ = true; parent_key_.clear(); if (type == JSON_VALUE_STRING) { node_ = json_value_create(JSON_VALUE_STRING, ""); } else if (type == JSON_VALUE_NUMBER) { node_ = json_value_create(JSON_VALUE_NUMBER, 0.0); } else { node_ = json_value_create(type); } } bool Json::to_object() { if (!allocated_ || !is_null()) { // watcher and non-null type can't change type return false; } destroy_node(&node_); node_ = json_value_create(JSON_VALUE_OBJECT); allocated_ = true; return true; } bool Json::to_array() { if (!allocated_ || !is_null()) { // watcher and non-null type can't change type return false; } destroy_node(&node_); node_ = json_value_create(JSON_VALUE_ARRAY); allocated_ = true; return true; } void Json::value_convert(const json_value_t* val, int spaces, int depth, std::string* out_str) { if (val == nullptr || out_str == nullptr) return; switch (json_value_type(val)) { case JSON_VALUE_STRING: string_convert(json_value_string(val), out_str); break; case JSON_VALUE_NUMBER: number_convert(json_value_number(val), out_str); break; case JSON_VALUE_OBJECT: object_convert(json_value_object(val), spaces, depth, out_str); break; case JSON_VALUE_ARRAY: array_convert(json_value_array(val), spaces, depth, out_str); break; case JSON_VALUE_TRUE: out_str->append("true"); break; case JSON_VALUE_FALSE: out_str->append("false"); break; case JSON_VALUE_NULL: out_str->append("null"); break; } } void Json::string_convert(const char* str, std::string* out_str) { out_str->append("\""); while (*str) { switch (*str) { case '\r': out_str->append("\\r"); break; case '\n': out_str->append("\\n"); break; case '\f': out_str->append("\\f"); break; case '\b': out_str->append("\\b"); break; case '\"': out_str->append("\\\""); break; case '\t': out_str->append("\\t"); break; case '\\': out_str->append("\\\\"); break; default: out_str->push_back(*str); break; } str++; } out_str->append("\""); } void Json::number_convert(double number, std::string* out_str) { std::ostringstream oss; long long integer = number; if (integer == number) oss << integer; else oss << number; out_str->append(oss.str()); } void Json::array_convert_not_format(const json_array_t* arr, std::string* out_str) { const json_value_t* val; int n = 0; out_str->append("["); json_array_for_each(val, arr) { if (n != 0) { out_str->append(","); } n++; value_convert(val, 0, 0, out_str); } out_str->append("]"); } void Json::array_convert(const json_array_t* arr, int spaces, int depth, std::string* out_str) { if (spaces == 0) { return array_convert_not_format(arr, out_str); } const json_value_t* val; int n = 0; int i; std::string padding(spaces, ' '); out_str->append("[\n"); json_array_for_each(val, arr) { if (n != 0) { out_str->append(",\n"); } n++; for (i = 0; i < depth + 1; i++) { out_str->append(padding); } value_convert(val, spaces, depth + 1, out_str); } out_str->append("\n"); for (i = 0; i < depth; i++) { out_str->append(padding); } out_str->append("]"); } void Json::object_convert_not_format(const json_object_t* obj, std::string* out_str) { const char* name; const json_value_t* val; int n = 0; out_str->append("{"); json_object_for_each(name, val, obj) { if (n != 0) { out_str->append(","); } n++; out_str->append("\""); out_str->append(name); out_str->append("\":"); value_convert(val, 0, 0, out_str); } out_str->append("}"); } void Json::object_convert(const json_object_t* obj, int spaces, int depth, std::string* out_str) { if (spaces == 0) { return object_convert_not_format(obj, out_str); } const char* name; const json_value_t* val; int n = 0; int i; std::string padding(spaces, ' '); out_str->append("{\n"); json_object_for_each(name, val, obj) { if (n != 0) { out_str->append(",\n"); } n++; for (i = 0; i < depth + 1; i++) { out_str->append(padding); } out_str->append("\""); out_str->append(name); out_str->append("\": "); value_convert(val, spaces, depth + 1, out_str); } out_str->append("\n"); for (i = 0; i < depth; i++) { out_str->append(padding); } out_str->append("}"); } } // namespace wfrest srpc-0.10.1/tools/templates/config/Json.h000066400000000000000000000571231454502251400202370ustar00rootroot00000000000000#ifndef WFREST_JSON_H_ #define WFREST_JSON_H_ #include "workflow/json_parser.h" #include #include #include #include #include #include #include #include #include #include #include namespace wfrest { namespace detail { template struct is_string { static constexpr bool value = false; }; template struct is_string> { static constexpr bool value = true; }; template struct is_char : std::integral_constant::value || std::is_same::value || std::is_same::value || std::is_same::value> { }; template struct is_number : std::integral_constant::value && !std::is_same::value && !detail::is_char::value> { }; } // namespace detail class Object_S; class Array_S; class Json { public: using Object = Object_S; using Array = Array_S; public: static Json parse(const std::string& str); static Json parse(const std::ifstream& stream); static Json parse(FILE* fp); std::string dump() const; std::string dump(int spaces) const; // for correctness in clang Json operator[](const char* key); Json operator[](const char* key) const; Json operator[](const std::string& key); Json operator[](const std::string& key) const; Json operator[](int index); Json operator[](int index) const; template void operator=(const T& val) { if (parent_ == nullptr) return; if (json_value_type(parent_) == JSON_VALUE_ARRAY) { update_arr(val); } else if (json_value_type(parent_) == JSON_VALUE_OBJECT) { push_back_obj(parent_key_, val); } } bool has(const std::string& key) const; template typename std::enable_if::value, T>::type get() const { return json_value_type(node_) == JSON_VALUE_TRUE ? true : false; } template typename std::enable_if::value, T>::type get() const { return static_cast(json_value_number(node_)); } template typename std::enable_if::value, T>::type get() const { return std::string(json_value_string(node_)); } template typename std::enable_if::value, T>::type get() const; template typename std::enable_if::value, T>::type get() const; template typename std::enable_if::value, T>::type get() const { return nullptr; } template operator T() { return get(); } public: int type() const { return json_value_type(node_); } std::string type_str() const; bool is_null() const { return type() == JSON_VALUE_NULL; } bool is_number() const { return type() == JSON_VALUE_NUMBER; } bool is_boolean() const { int type = this->type(); return type == JSON_VALUE_TRUE || type == JSON_VALUE_FALSE; } bool is_object() const { return type() == JSON_VALUE_OBJECT; } bool is_array() const { return type() == JSON_VALUE_ARRAY; } bool is_string() const { return type() == JSON_VALUE_STRING; } bool is_valid() const { return node_ != nullptr; } int size() const; bool empty() const; void clear(); Json copy() const; std::string key() const { return parent_key_; } const Json& value() const { return *this; } public: // for object template ::value, bool>::type = true> void push_back(const std::string& key, const T& val) { if (is_placeholder()) { *this = Json::Object{{key, val}}; return; } if (!can_obj_push_back()) { return; } json_object_t* obj = json_value_object(node_); json_object_append(obj, key.c_str(), JSON_VALUE_NUMBER, static_cast(val)); } template ::value || std::is_same::value, bool>::type = true> void push_back(const std::string& key, const T& val) { if (is_placeholder()) { *this = Json::Object{{key, val}}; return; } if (!can_obj_push_back()) { return; } json_object_t* obj = json_value_object(node_); Json copy_json = val.copy(); json_object_append(obj, key.c_str(), 0, copy_json.node_); copy_json.reset(); } void push_back(const std::string& key, bool val); void push_back(const std::string& key, std::nullptr_t val); void push_back(const std::string& key, const std::string& val); void push_back(const std::string& key, const char* val); void push_back(const std::string& key, const std::vector& val); void push_back(const std::string& key, const std::initializer_list& val) { push_back(key, std::vector(val)); } void push_back(const std::string& key, const Json& val); private: // for object template void push_back_obj(const std::string& key, const T& val) { if (!can_obj_push_back()) { return; } if (is_placeholder()) { placeholder_push_back(key, val); } else { normal_push_back(key, val); } } template ::value, bool>::type = true> void placeholder_push_back(const std::string& key, const T& val) { json_object_t* obj = json_value_object(parent_); destroy_node(&node_); node_ = const_cast(json_object_append( obj, key.c_str(), JSON_VALUE_NUMBER, static_cast(val))); } template ::value || std::is_same::value, bool>::type = true> void placeholder_push_back(const std::string& key, const T& val) { json_object_t* obj = json_value_object(parent_); destroy_node(&node_); Json copy_json = val.copy(); node_ = const_cast( json_object_append(obj, key.c_str(), 0, copy_json.node_)); copy_json.reset(); } void placeholder_push_back(const std::string& key, bool val); void placeholder_push_back(const std::string& key, std::nullptr_t val); void placeholder_push_back(const std::string& key, const std::string& val); void placeholder_push_back(const std::string& key, const char* val); void placeholder_push_back(const std::string& key, const std::vector& val); void placeholder_push_back(const std::string& key, const Json& val); template ::value, bool>::type = true> void normal_push_back(const std::string& key, const T& val) { json_object_t* obj = json_value_object(parent_); const json_value_t* find = json_object_find(key.c_str(), obj); if (find == nullptr) { json_object_append(obj, key.c_str(), JSON_VALUE_NUMBER, static_cast(val)); return; } json_object_insert_before(find, obj, key.c_str(), JSON_VALUE_NUMBER, static_cast(val)); json_value_t* remove_val = json_object_remove(find, obj); json_value_destroy(remove_val); } template ::value || std::is_same::value, bool>::type = true> void normal_push_back(const std::string& key, const T& val) { json_object_t* obj = json_value_object(parent_); const json_value_t* find = json_object_find(key.c_str(), obj); Json copy_json = val.copy(); if (find == nullptr) { json_object_append(obj, key.c_str(), 0, copy_json.node_); copy_json.reset(); return; } json_object_insert_before(find, obj, key.c_str(), 0, copy_json.node_); copy_json.reset(); json_value_t* remove_val = json_object_remove(find, obj); json_value_destroy(remove_val); } void normal_push_back(const std::string& key, bool val); void normal_push_back(const std::string& key, std::nullptr_t val); void normal_push_back(const std::string& key, const std::string& val); void normal_push_back(const std::string& key, const char* val); void normal_push_back(const std::string& key, const std::vector& val); void normal_push_back(const std::string& key, const Json& val); public: void erase(const std::string& key); template ::value, bool>::type = true> void push_back(T val) { if (is_placeholder()) { *this = Json::Array{{val}}; return; } if (!can_arr_push_back()) { return; } json_array_t* arr = json_value_array(node_); json_array_append(arr, JSON_VALUE_NUMBER, static_cast(val)); } template ::value || std::is_same::value, bool>::type = true> void push_back(const T& val) { if (is_placeholder()) { *this = Json::Array{{val}}; return; } if (!can_arr_push_back()) { return; } json_array_t* arr = json_value_array(node_); Json copy_json = val.copy(); json_array_append(arr, 0, copy_json.node_); copy_json.reset(); } void push_back(bool val); void push_back(std::nullptr_t val); void push_back(const std::string& val); void push_back(const char* val); void push_back(const std::vector& val); void push_back(const std::initializer_list& val) { push_back(std::vector(val)); } void push_back(const Json& val); void erase(int index); private: template ::value, bool>::type = true> void update_arr(T val) { json_array_t* arr = json_value_array(parent_); json_array_insert_before(node_, arr, JSON_VALUE_NUMBER, static_cast(val)); json_value_t* remove_val = json_array_remove(node_, arr); json_value_destroy(remove_val); } template ::value || std::is_same::value, bool>::type = true> void update_arr(const T& val) { json_array_t* arr = json_value_array(parent_); Json copy_json = val.copy(); json_array_insert_before(node_, arr, 0, copy_json.node_); copy_json.reset(); json_value_t* remove_val = json_array_remove(node_, arr); json_value_destroy(remove_val); } void update_arr(bool val); void update_arr(std::nullptr_t val); void update_arr(const std::string& val); void update_arr(const char* val); void update_arr(const std::vector& val); void update_arr(const Json& val); public: class IteratorBase { public: friend class Json; explicit IteratorBase(const json_value_t* val) : val_(val), json_(new Json(nullptr, nullptr, "")) { } ~IteratorBase() { if (json_ != nullptr) { delete json_; } } IteratorBase(const IteratorBase& iter) { val_ = iter.val_; key_ = iter.key_; json_ = new Json(iter.json_->node_, iter.json_->parent_, iter.json_->parent_key_); } IteratorBase& operator=(const IteratorBase& iter) { if (this == &iter) { return *this; } val_ = iter.val_; key_ = iter.key_; json_->reset(iter.json_->node_, iter.json_->parent_, false, iter.json_->parent_key_); return *this; } IteratorBase(IteratorBase&& iter) : val_(iter.val_), key_(iter.key_), json_(iter.json_) { iter.val_ = nullptr; iter.key_ = nullptr; iter.json_ = nullptr; } IteratorBase& operator=(IteratorBase&& iter) { if (this == &iter) { return *this; } val_ = iter.val_; iter.val_ = nullptr; key_ = iter.key_; iter.key_ = nullptr; delete json_; json_ = iter.json_; iter.json_ = nullptr; return *this; } Json& operator*() const { if (key_ != nullptr) { json_->parent_key_ = std::string(key_); } else { json_->parent_key_ = ""; } return *json_; } Json* operator->() const { if (key_ != nullptr) { json_->parent_key_ = std::string(key_); } else { json_->parent_key_ = ""; } return json_; } bool operator==(const IteratorBase& other) const { return json_->node_ == other.json_->node_; } bool operator!=(IteratorBase const& other) const { return !(*this == other); } private: const json_value_t* val_ = nullptr; const char* key_ = nullptr; Json* json_; // cursor }; class iterator : public IteratorBase { public: friend class Json; explicit iterator(const json_value_t* val) : IteratorBase(val) { } iterator& operator++() { if (is_end()) { return *this; } forward(); return *this; } iterator operator++(int) { iterator old = (*this); if (!is_end()) { ++(*this); } return old; } private: void set_begin() { json_->node_ = nullptr; key_ = nullptr; forward(); } void set_end() { json_->node_ = nullptr; key_ = nullptr; } bool is_end() { return json_->node_ == nullptr && key_ == nullptr; } void forward() { if (json_value_type(val_) == JSON_VALUE_OBJECT) { json_object_t* obj = json_value_object(val_); key_ = json_object_next_name(key_, obj); json_->node_ = const_cast( json_object_next_value(json_->node_, obj)); } else if (json_value_type(val_) == JSON_VALUE_ARRAY) { json_array_t* arr = json_value_array(val_); json_->node_ = const_cast( json_array_next_value(json_->node_, arr)); } } }; class reverse_iterator : public IteratorBase { public: friend class Json; explicit reverse_iterator(const json_value_t* val) : IteratorBase(val) { } reverse_iterator& operator++() { if (is_rend()) { return *this; } backward(); return *this; } reverse_iterator operator++(int) { reverse_iterator old = (*this); if (!is_rend()) { ++(*this); } return old; } private: void set_rbegin() { json_->node_ = nullptr; key_ = nullptr; backward(); } void set_rend() { json_->node_ = nullptr; key_ = nullptr; } bool is_rend() { return json_->node_ == nullptr && key_ == nullptr; } void backward() { if (json_value_type(val_) == JSON_VALUE_OBJECT) { json_object_t* obj = json_value_object(val_); key_ = json_object_prev_name(key_, obj); json_->node_ = const_cast( json_object_prev_value(json_->node_, obj)); } else if (json_value_type(val_) == JSON_VALUE_ARRAY) { json_array_t* arr = json_value_array(val_); json_->node_ = const_cast( json_array_prev_value(json_->node_, arr)); } } }; iterator begin() const { iterator iter(node_); iter.set_begin(); return iter; } iterator end() const { iterator iter(node_); iter.set_end(); return iter; } reverse_iterator rbegin() const { reverse_iterator iter(node_); iter.set_rbegin(); return iter; } reverse_iterator rend() const { reverse_iterator iter(node_); iter.set_rend(); return iter; } private: bool can_obj_push_back(); bool can_arr_push_back(); // can convert to any type, just like a placeholder bool is_placeholder() const { return is_null() && parent_ != nullptr; } void destroy_node(json_value_t** node) { if (allocated_) { json_value_destroy(*node); *node = nullptr; allocated_ = false; } } void copy(const Json& other); public: // Constructors for the various types of JSON value. // Basic json type Constructors Json(); Json(const std::string& str); Json(const char* str); Json(std::nullptr_t null); template ::value, bool>::type = true> Json(T val) : node_(json_value_create(JSON_VALUE_NUMBER, static_cast(val))), parent_(nullptr), allocated_(true) { } Json(bool val); Json(const std::vector& val); // For parse Json(const std::string& str, bool parse_flag); Json(const Json& json); Json& operator=(const Json& json); Json(Json&& other); Json& operator=(Json&& other); ~Json(); protected: // watcher Json(const json_value_t* node, const json_value_t* parent, std::string&& key); Json(const json_value_t* node, const json_value_t* parent, const std::string& key); enum class JsonType { Object, Array, }; // For Object && Array Construct Json(JsonType type); bool is_root() const { return parent_ == nullptr; } bool to_object(); bool to_array(); void reset() { node_ = nullptr; parent_ = nullptr; allocated_ = false; parent_key_.clear(); } void reset(json_value_t* node, const json_value_t* parent, bool allocated, const std::string& parent_key) { node_ = node; parent_ = parent; allocated_ = allocated; parent_key_ = parent_key; } void reset(json_value_t* node, const json_value_t* parent, bool allocated, std::string&& parent_key) { node_ = node; parent_ = parent; allocated_ = allocated; parent_key_ = std::move(parent_key); } void set_parent(const json_value_t* parent, std::string&& parent_key) { parent_ = parent; parent_key_ = std::move(parent_key); } private: json_value_t* node_ = nullptr; const json_value_t* parent_ = nullptr; bool allocated_ = false; std::string parent_key_; private: // for parse static void value_convert(const json_value_t* val, int spaces, int depth, std::string* out_str); static void string_convert(const char* raw_str, std::string* out_str); static void number_convert(double number, std::string* out_str); static void array_convert(const json_array_t* arr, int spaces, int depth, std::string* out_str); static void array_convert_not_format(const json_array_t* arr, std::string* out_str); static void object_convert(const json_object_t* obj, int spaces, int depth, std::string* out_str); static void object_convert_not_format(const json_object_t* obj, std::string* out_str); friend inline std::ostream& operator<<(std::ostream& os, const Json& json) { return (os << json.dump()); } }; class Object_S : public Json { public: Object_S() : Json(JsonType::Object) { } Object_S(const json_value_t* node, const json_value_t* parent) : Json(node, parent, "") { } using string_type = std::basic_string, std::allocator>; using pair_type = std::pair; Object_S(std::initializer_list list) { std::for_each(list.begin(), list.end(), [this](const pair_type& pair) { this->push_back(pair.first, pair.second); }); } }; class Array_S : public Json { public: Array_S() : Json(JsonType::Array) { } Array_S(const json_value_t* node, const json_value_t* parent) : Json(node, parent, "") { } Array_S(std::initializer_list list) { std::for_each(list.begin(), list.end(), [this](const Json& js) { this->push_back(js); }); } }; template typename std::enable_if::value, T>::type Json::get() const { return Json::Object(node_, parent_); } template typename std::enable_if::value, T>::type Json::get() const { return Json::Array(node_, parent_); } } // namespace wfrest #endif // WFREST_JSON_H_ srpc-0.10.1/tools/templates/config/config_full.cc000066400000000000000000000375131454502251400217540ustar00rootroot00000000000000#include #include "config.h" #include "workflow/WFGlobal.h" #include "workflow/UpstreamManager.h" #include "workflow/UpstreamPolicies.h" #include "srpc/rpc_metrics_filter.h" #include "srpc/rpc_trace_filter.h" using namespace srpc; // default upstream_route_t static unsigned int default_consistent_hash(const char *path, const char *query, const char *fragment) { return 0; } static unsigned int default_select_route(const char *path, const char *query, const char *fragment) { return 0; } static void set_endpoint_params(const wfrest::Json& data, struct EndpointParams *params) { *params = ENDPOINT_PARAMS_DEFAULT; for (const auto& it : data) { if (it.key() == "max_connections") params->max_connections = data["max_connections"]; else if (it.key() == "connect_timeout") params->connect_timeout = data["connect_timeout"]; else if (it.key() == "response_timeout") params->response_timeout = data["response_timeout"]; else if (it.key() == "ssl_connect_timeout") params->ssl_connect_timeout = data["ssl_connect_timeout"]; else if (it.key() == "use_tls_sni") params->use_tls_sni = data["use_tls_sni"]; else { printf("[INFO][set_endpoint_params] Unknown key : %s\n", it.key().c_str()); } } } static void load_global(const wfrest::Json& data) { struct WFGlobalSettings settings = GLOBAL_SETTINGS_DEFAULT; std::string resolv_conf_path; std::string hosts_path; for (const auto& it : data) { if (it.key() == "endpoint_params") { set_endpoint_params(data["endpoint_params"], &settings.endpoint_params); } else if (it.key() == "dns_server_params") { set_endpoint_params(data["dns_server_params"], &settings.dns_server_params); } else if (it.key() == "dns_ttl_default") settings.dns_ttl_default = data["dns_ttl_default"]; else if (it.key() == "dns_ttl_min") settings.dns_ttl_min = data["dns_ttl_min"]; else if (it.key() == "dns_threads") settings.dns_threads = data["dns_threads"]; else if (it.key() == "poller_threads") settings.poller_threads = data["poller_threads"]; else if (it.key() == "handler_threads") settings.handler_threads = data["handler_threads"]; else if (it.key() == "compute_threads") settings.compute_threads = data["compute_threads"]; else if (it.key() == "resolv_conf_path") { resolv_conf_path = data["resolv_conf_path"].get(); settings.resolv_conf_path = resolv_conf_path.c_str(); } else if (it.key() == "hosts_path") { hosts_path = data["hosts_path"].get(); settings.hosts_path = hosts_path.c_str(); } else printf("[INFO][load_global] Unknown key : %s\n", it.key().c_str()); } WORKFLOW_library_init(&settings); } static bool load_upstream_server(const wfrest::Json& data, std::vector& hosts, std::vector& params) { AddressParams param; hosts.clear(); params.clear(); for (const auto& server : data) { if (server.has("host") == false) { printf("[ERROR][load_upstream] Invalid upstream server\n"); continue; } param = ADDRESS_PARAMS_DEFAULT; if (server.has("params")) { for (const auto& p : server["params"]) { if (p.key() == "endpoint_params") set_endpoint_params(p.value(), ¶m.endpoint_params); else if (p.key() == "weight") param.weight = p.value().get(); else if (p.key() == "max_fails") param.max_fails = p.value().get(); else if (p.key() == "dns_ttl_default") param.dns_ttl_default = p.value().get(); else if (p.key() == "dns_ttl_min") param.dns_ttl_min = p.value().get(); else if (p.key() == "server_type") param.server_type = p.value().get(); else if (p.key() == "group_id") param.group_id = p.value().get(); else printf("[ERROR][load_upstream] Invalid params: %s\n", p.key().c_str()); } } hosts.push_back(server["host"]); params.push_back(param); } if (hosts.size() == 0) return false; else return true; } static void load_upstream(const wfrest::Json& data) { std::string name; std::string type; bool try_another; std::vector hosts; std::vector params; for (const auto& it : data) { if (it.has("name") == false || it.has("type") == false || it.has("server") == false || load_upstream_server(it["server"], hosts, params) == false) { printf("[ERROR][load_upstream] Invalid upstream\n"); continue; } name = it["name"].get(); type = it["type"].get(); if (it.has("try_another")) try_another = it["try_another"]; else try_another = false; if (type == "weighted_random") { UpstreamManager::upstream_create_weighted_random(name, try_another); } else if (type == "consistent_hash") { UpstreamManager::upstream_create_consistent_hash(name, default_consistent_hash); } else if (type == "round_robin") { UpstreamManager::upstream_create_round_robin(name, try_another); } else if (type == "manual") { UpstreamManager::upstream_create_manual(name, default_select_route, try_another, default_consistent_hash); } else if (type == "vnswrr") { UpstreamManager::upstream_create_vnswrr(name); } else { printf("[INFO][load_upstream] Unknown type : %s\n", type.c_str()); continue; } for (size_t i = 0; i < hosts.size(); i++) UpstreamManager::upstream_add_server(name, hosts[i], ¶ms[i]); } } void RPCConfig::load_server() { if (this->data["server"].has("port")) this->s_port = this->data["server"]["port"]; if (this->data["server"].has("root")) this->root_path = this->data["server"]["root"].get(); if (this->data["server"].has("cert_file")) this->s_cert_file = this->data["server"]["cert_file"].get(); if (this->data["server"].has("file_key")) this->s_file_key = this->data["server"]["file_key"].get(); if (this->data["server"].has("error_page")) { for (const auto& it : this->data["server"]["error_page"]) { std::string page; if (it.has("error") == true && it.has("error") == true) { page = it["page"].get(); for (const auto& e : it["error"]) this->error_page.insert(std::make_pair(e.get(), page)); } else { printf("[ERROR][load_file_service] Invalid error_page\n"); continue; } } } } void RPCConfig::load_client() { if (this->data["client"].has("remote_host")) this->c_host = this->data["client"]["remote_host"].get(); if (this->data["client"].has("remote_port")) this->c_port = this->data["client"]["remote_port"]; if (this->data["client"].has("redirect_max")) this->c_redirect_max = this->data["client"]["redirect_max"]; if (this->data["client"].has("retry_max")) this->c_retry_max = this->data["client"]["retry_max"]; if (this->data["client"].has("user_name")) this->c_user_name = this->data["client"]["user_name"].get(); if (this->data["client"].has("password")) this->c_password = this->data["client"]["password"].get(); } bool RPCConfig::load(const char *file) { FILE *fp = fopen(file, "r"); if (!fp) return false; this->data = wfrest::Json::parse(fp); fclose(fp); if (this->data.is_valid() == false) return false; for (const auto& it : this->data) { if (it.key() == "server") this->load_server(); else if (it.key() == "client") this->load_client(); else if (it.key() == "global") load_global(it.value()); else if (it.key() == "upstream") load_upstream(it.value()); else if (it.key() == "metrics") this->load_metrics(); else if (it.key() == "trace") this->load_trace(); else printf("[INFO][RPCConfig::load] Unknown key: %s\n", it.key().c_str()); } return true; }; void RPCConfig::load_metrics() { for (const auto& it : this->data["metrics"]) { if (it.has("filter") == false) continue; std::string filter_name = it["filter"]; if (filter_name.compare("prometheus") == 0) { if (it.has("port") == false) continue; RPCMetricsPull *filter = new RPCMetricsPull(); unsigned short port = it["port"]; filter->init(port); this->filters.push_back(filter); } else if (filter_name.compare("opentelemetry") == 0) { if (it.has("address") == false) continue; std::string url = it["address"]; unsigned int redirect_max = OTLP_HTTP_REDIRECT_MAX; unsigned int retry_max = OTLP_HTTP_RETRY_MAX; size_t report_threshold = RPC_REPORT_THREHOLD_DEFAULT; size_t report_interval = RPC_REPORT_INTERVAL_DEFAULT; if (it.has("redirect_max")) redirect_max = it["redirect_max"]; if (it.has("retry_max")) retry_max = it["retry_max"]; if (it.has("report_threshold")) report_threshold = it["report_threshold"]; if (it.has("report_interval_ms")) report_interval = it["report_interval_ms"]; RPCMetricsOTel *filter = new RPCMetricsOTel(url, redirect_max, retry_max, report_threshold, report_interval); if (it.has("attributes")) { for (const auto& kv : it["attributes"]) { if (kv.has("key") == false || kv.has("value") == false) continue; filter->add_attributes(kv["key"], kv["value"]); } } this->filters.push_back(filter); } else { printf("[ERROR][RPCConfig::load_metrics] Unknown metrics: %s\n", filter_name.c_str()); } } } void RPCConfig::load_trace() { for (const auto& it : this->data["trace"]) { if (it.has("filter") == false) continue; std::string filter_name = it["filter"]; size_t spans_per_second = SPANS_PER_SECOND_DEFAULT; if (filter_name.compare("default") == 0) { if (it.has("spans_per_second")) spans_per_second = it["spans_per_second"]; auto *filter = new RPCTraceDefault(spans_per_second); this->filters.push_back(filter); } else if (filter_name.compare("opentelemetry") == 0) { if (it.has("address") == false) continue; std::string url = it["address"]; unsigned int redirect_max = OTLP_HTTP_REDIRECT_MAX; unsigned int retry_max = OTLP_HTTP_RETRY_MAX; size_t report_threshold = RPC_REPORT_THREHOLD_DEFAULT; size_t report_interval = RPC_REPORT_INTERVAL_DEFAULT; if (it.has("redirect_max")) redirect_max = it["redirect_max"]; if (it.has("retry_max")) retry_max = it["retry_max"]; if (it.has("report_threshold")) report_threshold = it["report_threshold"]; if (it.has("report_interval_ms")) report_interval = it["report_interval_ms"]; auto *filter = new RPCTraceOpenTelemetry(url, redirect_max, retry_max, spans_per_second, report_threshold, report_interval); if (it.has("attributes")) { for (const auto& kv : it["attributes"]) { if (kv.has("key") == false || kv.has("value") == false) continue; filter->add_attributes(kv["key"], kv["value"]); } } this->filters.push_back(filter); } else { printf("[ERROR][RPCConfig::load_metrics] Unknown metrics: %s\n", filter_name.c_str()); } } } void RPCConfig::load_filter(SRPCServer& server) { for (auto *filter : this->filters) server.add_filter(filter); } void RPCConfig::load_filter(SRPCClient& client) { for (auto *filter : this->filters) client.add_filter(filter); } void RPCConfig::load_filter(SRPCHttpServer& server) { for (auto *filter : this->filters) server.add_filter(filter); } void RPCConfig::load_filter(SRPCHttpClient& client) { for (auto *filter : this->filters) client.add_filter(filter); } void RPCConfig::load_filter(BRPCServer& server) { for (auto *filter : this->filters) server.add_filter(filter); } void RPCConfig::load_filter(BRPCClient& client) { for (auto *filter : this->filters) client.add_filter(filter); } void RPCConfig::load_filter(ThriftServer& server) { for (auto *filter : this->filters) server.add_filter(filter); } void RPCConfig::load_filter(ThriftClient& client) { for (auto *filter : this->filters) client.add_filter(filter); } void RPCConfig::load_filter(ThriftHttpServer& server) { for (auto *filter : this->filters) server.add_filter(filter); } void RPCConfig::load_filter(ThriftHttpClient& client) { for (auto *filter : this->filters) client.add_filter(filter); } void RPCConfig::load_filter(TRPCServer& server) { for (auto *filter : this->filters) server.add_filter(filter); } void RPCConfig::load_filter(TRPCClient& client) { for (auto *filter : this->filters) client.add_filter(filter); } void RPCConfig::load_filter(TRPCHttpServer& server) { for (auto *filter : this->filters) server.add_filter(filter); } void RPCConfig::load_filter(TRPCHttpClient& client) { for (auto *filter : this->filters) client.add_filter(filter); } RPCConfig::~RPCConfig() { for (size_t i = 0; i < this->filters.size(); i++) delete this->filters[i]; } srpc-0.10.1/tools/templates/config/config_full.h000066400000000000000000000051121454502251400216040ustar00rootroot00000000000000#ifndef _RPC_CONFIG_H_ #define _RPC_CONFIG_H_ #include #include #include #include "Json.h" #include "srpc/rpc_types.h" #include "srpc/rpc_define.h" #include "srpc/rpc_filter.h" namespace srpc { class RPCConfig { public: using ErrorPageMap = std::unordered_map; bool load(const char *file); void load_filter(SRPCServer& server); void load_filter(SRPCClient& client); void load_filter(SRPCHttpServer& server); void load_filter(SRPCHttpClient& client); void load_filter(BRPCServer& server); void load_filter(BRPCClient& client); void load_filter(ThriftServer& server); void load_filter(ThriftClient& client); void load_filter(ThriftHttpServer& server); void load_filter(ThriftHttpClient& client); void load_filter(TRPCServer& server); void load_filter(TRPCClient& client); void load_filter(TRPCHttpServer& server); void load_filter(TRPCHttpClient& client); unsigned short server_port() const { return this->s_port; } const char *server_cert_file() const { return this->s_cert_file.c_str(); } const char *server_file_key() const { return this->s_file_key.c_str(); } unsigned short client_port() const { return this->c_port; } const char *client_host() const { return this->c_host.c_str(); } bool client_is_ssl() const { return this->c_is_ssl; } const char *client_url() const { return this->c_url.c_str(); } int redirect_max() const { return this->c_redirect_max; } int retry_max() const { return this->c_retry_max; } const char *client_caller() const { return this->c_caller.c_str(); } const char *client_user_name() const { return this->c_user_name.c_str(); } const char *client_password() const { return this->c_password.c_str(); } const char *get_root_path() const { return this->root_path.c_str(); } const ErrorPageMap& get_error_page() const { return this->error_page; } public: RPCConfig() : s_port(0), c_port(0), c_is_ssl(false), c_redirect_max(0), c_retry_max(0) { } ~RPCConfig(); private: void load_server(); void load_client(); void load_metrics(); void load_trace(); wfrest::Json data; std::vector filters; unsigned short s_port; std::string s_cert_file; std::string s_file_key; std::string c_host; unsigned short c_port; bool c_is_ssl; std::string c_url; int c_redirect_max; int c_retry_max; std::string c_caller; std::string c_user_name; std::string c_password; std::string root_path; ErrorPageMap error_page; }; } #endif srpc-0.10.1/tools/templates/config/config_simple.cc000066400000000000000000000224631454502251400223010ustar00rootroot00000000000000#include #include "config.h" #include "workflow/WFGlobal.h" #include "workflow/UpstreamManager.h" #include "workflow/UpstreamPolicies.h" using namespace srpc; // default upstream_route_t static unsigned int default_consistent_hash(const char *path, const char *query, const char *fragment) { return 0; } static unsigned int default_select_route(const char *path, const char *query, const char *fragment) { return 0; } static void set_endpoint_params(const wfrest::Json& data, struct EndpointParams *params) { *params = ENDPOINT_PARAMS_DEFAULT; for (const auto& it : data) { if (it.key() == "max_connections") params->max_connections = data["max_connections"]; else if (it.key() == "connect_timeout") params->connect_timeout = data["connect_timeout"]; else if (it.key() == "response_timeout") params->response_timeout = data["response_timeout"]; else if (it.key() == "ssl_connect_timeout") params->ssl_connect_timeout = data["ssl_connect_timeout"]; else if (it.key() == "use_tls_sni") params->use_tls_sni = data["use_tls_sni"]; else { printf("[INFO][set_endpoint_params] Unknown key : %s\n", it.key().c_str()); } } } static void load_global(const wfrest::Json& data) { struct WFGlobalSettings settings = GLOBAL_SETTINGS_DEFAULT; std::string resolv_conf_path; std::string hosts_path; for (const auto& it : data) { if (it.key() == "endpoint_params") { set_endpoint_params(data["endpoint_params"], &settings.endpoint_params); } else if (it.key() == "dns_server_params") { set_endpoint_params(data["dns_server_params"], &settings.dns_server_params); } else if (it.key() == "dns_ttl_default") settings.dns_ttl_default = data["dns_ttl_default"]; else if (it.key() == "dns_ttl_min") settings.dns_ttl_min = data["dns_ttl_min"]; else if (it.key() == "dns_threads") settings.dns_threads = data["dns_threads"]; else if (it.key() == "poller_threads") settings.poller_threads = data["poller_threads"]; else if (it.key() == "handler_threads") settings.handler_threads = data["handler_threads"]; else if (it.key() == "compute_threads") settings.compute_threads = data["compute_threads"]; else if (it.key() == "resolv_conf_path") { resolv_conf_path = data["resolv_conf_path"].get(); settings.resolv_conf_path = resolv_conf_path.c_str(); } else if (it.key() == "hosts_path") { hosts_path = data["hosts_path"].get(); settings.hosts_path = hosts_path.c_str(); } else printf("[INFO][load_global] Unknown key : %s\n", it.key().c_str()); } WORKFLOW_library_init(&settings); } static bool load_upstream_server(const wfrest::Json& data, std::vector& hosts, std::vector& params) { AddressParams param; hosts.clear(); params.clear(); for (const auto& server : data) { if (server.has("host") == false) { printf("[ERROR][load_upstream] Invalid upstream server\n"); continue; } param = ADDRESS_PARAMS_DEFAULT; if (server.has("params")) { for (const auto& p : server["params"]) { if (p.key() == "endpoint_params") set_endpoint_params(p.value(), ¶m.endpoint_params); else if (p.key() == "weight") param.weight = p.value().get(); else if (p.key() == "max_fails") param.max_fails = p.value().get(); else if (p.key() == "dns_ttl_default") param.dns_ttl_default = p.value().get(); else if (p.key() == "dns_ttl_min") param.dns_ttl_min = p.value().get(); else if (p.key() == "server_type") param.server_type = p.value().get(); else if (p.key() == "group_id") param.group_id = p.value().get(); else printf("[ERROR][load_upstream] Invalid params: %s\n", p.key().c_str()); } } hosts.push_back(server["host"]); params.push_back(param); } if (hosts.size() == 0) return false; else return true; } static void load_upstream(const wfrest::Json& data) { std::string name; std::string type; bool try_another; std::vector hosts; std::vector params; for (const auto& it : data) { if (it.has("name") == false || it.has("type") == false || it.has("server") == false || load_upstream_server(it["server"], hosts, params) == false) { printf("[ERROR][load_upstream] Invalid upstream\n"); continue; } name = it["name"].get(); type = it["type"].get(); if (it.has("try_another")) try_another = it["try_another"]; else try_another = false; if (type == "weighted_random") { UpstreamManager::upstream_create_weighted_random(name, try_another); } else if (type == "consistent_hash") { UpstreamManager::upstream_create_consistent_hash(name, default_consistent_hash); } else if (type == "round_robin") { UpstreamManager::upstream_create_round_robin(name, try_another); } else if (type == "manual") { UpstreamManager::upstream_create_manual(name, default_select_route, try_another, default_consistent_hash); } else if (type == "vnswrr") { UpstreamManager::upstream_create_vnswrr(name); } else { printf("[INFO][load_upstream] Unknown type : %s\n", type.c_str()); continue; } for (size_t i = 0; i < hosts.size(); i++) UpstreamManager::upstream_add_server(name, hosts[i], ¶ms[i]); } } void RPCConfig::load_server() { if (this->data["server"].has("port")) this->s_port = this->data["server"]["port"]; if (this->data["server"].has("root")) this->root_path = this->data["server"]["root"].get(); if (this->data["server"].has("cert_file")) this->s_cert_file = this->data["server"]["cert_file"].get(); if (this->data["server"].has("file_key")) this->s_file_key = this->data["server"]["file_key"].get(); if (this->data["server"].has("error_page")) { for (const auto& it : this->data["server"]["error_page"]) { std::string page; if (it.has("error") == true && it.has("error") == true) { page = it["page"].get(); for (const auto& e : it["error"]) this->error_page.insert(std::make_pair(e.get(), page)); } else { printf("[ERROR][load_file_service] Invalid error_page\n"); continue; } } } } void RPCConfig::load_client() { if (this->data["client"].has("remote_host")) this->c_host = this->data["client"]["remote_host"].get(); if (this->data["client"].has("remote_port")) this->c_port = this->data["client"]["remote_port"]; if (this->data["client"].has("redirect_max")) this->c_redirect_max = this->data["client"]["redirect_max"]; if (this->data["client"].has("retry_max")) this->c_retry_max = this->data["client"]["retry_max"]; if (this->data["client"].has("user_name")) this->c_user_name = this->data["client"]["user_name"].get(); if (this->data["client"].has("password")) this->c_password = this->data["client"]["password"].get(); } bool RPCConfig::load(const char *file) { FILE *fp = fopen(file, "r"); if (!fp) return false; this->data = wfrest::Json::parse(fp); fclose(fp); if (this->data.is_valid() == false) return false; for (const auto& it : this->data) { if (it.key() == "server") this->load_server(); else if (it.key() == "client") this->load_client(); else if (it.key() == "global") load_global(it.value()); else if (it.key() == "upstream") load_upstream(it.value()); else printf("[INFO][RPCConfig::load] Unknown key: %s\n", it.key().c_str()); } return true; }; RPCConfig::~RPCConfig() { } srpc-0.10.1/tools/templates/config/config_simple.h000066400000000000000000000030041454502251400221310ustar00rootroot00000000000000#ifndef _RPC_CONFIG_H_ #define _RPC_CONFIG_H_ #include #include #include #include "Json.h" namespace srpc { class RPCConfig { public: using ErrorPageMap = std::unordered_map; bool load(const char *file); unsigned short server_port() const { return this->s_port; } const char *server_cert_file() const { return this->s_cert_file.c_str(); } const char *server_file_key() const { return this->s_file_key.c_str(); } unsigned short client_port() const { return this->c_port; } const char *client_host() const { return this->c_host.c_str(); } int redirect_max() const { return this->c_redirect_max; } int retry_max() const { return this->c_retry_max; } const char *client_user_name() const { return this->c_user_name.c_str(); } const char *client_password() const { return this->c_password.c_str(); } const char *get_root_path() const { return this->root_path.c_str(); } const ErrorPageMap& get_error_page() const { return this->error_page; } public: RPCConfig() : s_port(0), c_port(0), c_redirect_max(0), c_retry_max(0) { } ~RPCConfig(); private: void load_server(); void load_client(); wfrest::Json data; unsigned short s_port; std::string s_cert_file; std::string s_file_key; std::string c_host; unsigned short c_port; int c_redirect_max; int c_retry_max; std::string c_user_name; std::string c_password; std::string root_path; ErrorPageMap error_page; }; } #endif srpc-0.10.1/tools/templates/file/000077500000000000000000000000001454502251400166175ustar00rootroot00000000000000srpc-0.10.1/tools/templates/file/404.html000066400000000000000000000000731454502251400200140ustar00rootroot00000000000000This is not the web page you are looking for. srpc-0.10.1/tools/templates/file/50x.html000066400000000000000000000010501454502251400201150ustar00rootroot00000000000000 Error

An error occurred.

Sorry, the page you are looking for is currently unavailable.
Please try again later.

If you are the system administrator of this resource then you should check the srpc-ctl README.md for details.

Faithfully yours, srpc.

srpc-0.10.1/tools/templates/file/file_service.cc000066400000000000000000000061321454502251400215670ustar00rootroot00000000000000#include "file_service.h" SubTask *FileService::create_error_task(int code, struct FileService::ModuleCtx *ctx) { if (ctx->is_error_set == false) { ctx->resp->set_status_code(std::to_string(code)); ctx->is_error_set = true; const auto it = this->error_page.find(code); if (it != this->error_page.end()) return this->create_file_task(it->second, ctx); } return WFTaskFactory::create_empty_task(); } void FileService::pread_callback(WFFileIOTask *task) { FileIOArgs *args = task->get_args(); long ret = task->get_retval(); struct ModuleCtx *ctx = (struct ModuleCtx *)series_of(task)->get_context(); protocol::HttpResponse *resp = ctx->resp; if (task->get_state() == WFT_STATE_SUCCESS) { resp->append_output_body_nocopy(args->buf, ret); } else { auto *error_task = this->create_error_task(503, ctx); series_of(task)->push_back(error_task); } } SubTask *FileService::create_file_task(const std::string& path, struct FileService::ModuleCtx *ctx) { SubTask *task; struct stat st; std::string abs_path = this->root + path; if (abs_path.back() == '/') abs_path += "index.html"; if (stat(abs_path.c_str(), &st) >= 0) { size_t size = st.st_size; void *buf = malloc(size); if (buf) { ctx->buf = buf; auto&& cb = std::bind(&FileService::pread_callback, this, std::placeholders::_1); task = WFTaskFactory::create_pread_task(abs_path, buf, size, 0, cb); } else { task = this->create_error_task(503, ctx); } } else { task = this->create_error_task(404, ctx); } return task; } WFModuleTask *FileService::create_module(WFHttpTask *server_task, const std::string& path) { fprintf(stderr, "file service get request: %s\n", path.c_str()); struct ModuleCtx *ctx = new ModuleCtx(server_task->get_resp()); SubTask *next = this->create_file_task(path, ctx); WFModuleTask *module = WFTaskFactory::create_module_task(next, [server_task](const WFModuleTask *mod) { struct ModuleCtx *ctx; ctx = (struct ModuleCtx *)mod->sub_series()->get_context(); void *buf = ctx->buf; server_task->set_callback([buf](WFHttpTask *t){ free(buf); }); delete ctx; }); module->sub_series()->set_context(ctx); return module; } void FileService::process(WFHttpTask *server_task) { protocol::HttpRequest *req = server_task->get_req(); protocol::HttpResponse *resp = server_task->get_resp(); std::string path = req->get_request_uri(); auto pos = path.find_first_of('?'); if (pos != std::string::npos) path = path.substr(0, pos); resp->add_header_pair("Server", "SRPC HTTP File Server"); WFModuleTask *module = this->create_module(server_task, path); series_of(server_task)->push_back(module); } srpc-0.10.1/tools/templates/file/file_service.h000066400000000000000000000025731454502251400214360ustar00rootroot00000000000000#ifndef _RPC_FILE_SERVICE_H_ #define _RPC_FILE_SERVICE_H_ #include #include #include #include "workflow/Workflow.h" #include "workflow/HttpMessage.h" #include "workflow/HttpUtil.h" #include "workflow/WFHttpServer.h" #include "workflow/WFFacilities.h" // This is a simple exmaple for file service class FileService { public: using ErrorPageMap = std::unordered_map; void process(WFHttpTask *server_task); void pread_callback(WFFileIOTask *task); protected: struct ModuleCtx { protocol::HttpResponse *resp; void *buf; bool is_error_set; ModuleCtx(protocol::HttpResponse * resp) : resp(resp), buf(NULL), is_error_set(false) { } }; private: WFModuleTask *create_module(WFHttpTask *task, const std::string& path); SubTask *create_file_task(const std::string& path, struct ModuleCtx *ctx); SubTask *create_error_task(int code, struct ModuleCtx *ctx); public: FileService(std::string root, const ErrorPageMap& error_page) : root(std::move(root)), error_page(error_page) { if (this->root.empty()) root = "./"; else if (this->root.at(this->root.length() - 1) != '/') root += "/"; } private: std::string root; const ErrorPageMap& error_page; }; #endif srpc-0.10.1/tools/templates/file/index.html000066400000000000000000000000671454502251400206170ustar00rootroot00000000000000Hello from workflow and srpc file server! srpc-0.10.1/tools/templates/file/server.conf000066400000000000000000000003721454502251400207760ustar00rootroot00000000000000{ "server": { "port": 8080, "root": "./html/", "error_page" : [ { "error" : [ 404 ], "page" : "404.html" }, { "error" : [ 500, 502, 503, 504], "page" : "50x.html" } ] } } srpc-0.10.1/tools/templates/file/server_main.cc000066400000000000000000000025021454502251400214370ustar00rootroot00000000000000#include #include #include "workflow/HttpMessage.h" #include "workflow/HttpUtil.h" #include "workflow/WFHttpServer.h" #include "workflow/WFFacilities.h" #include "config/config.h" #include "file_service.h" using namespace protocol; static WFFacilities::WaitGroup wait_group(1); static srpc::RPCConfig config; void sig_handler(int signo) { wait_group.done(); } void init() { if (config.load("./server.conf") == false) { perror("Load config failed"); exit(1); } signal(SIGINT, sig_handler); } int main() { init(); unsigned short port = config.server_port(); const char *cert_file = config.server_cert_file(); const char *file_key = config.server_file_key(); FileService service(config.get_root_path(), config.get_error_page()); auto&& proc = std::bind(&FileService::process, &service, std::placeholders::_1); int ret; WFHttpServer server(proc); if (strlen(cert_file) != 0 && strlen(file_key) != 0) ret = server.start(port, cert_file, file_key); else ret = server.start(port); if (ret == 0) { fprintf(stderr, "http file service start, port %u\n", port); wait_group.wait(); server.stop(); } else perror("http file service start"); return 0; } srpc-0.10.1/tools/templates/proxy/000077500000000000000000000000001454502251400170615ustar00rootroot00000000000000srpc-0.10.1/tools/templates/proxy/client_rpc.conf000066400000000000000000000002571454502251400220560ustar00rootroot00000000000000{ "client": { "remote_host": "127.0.0.1", "remote_port": 1411, "is_ssl" : false, "redirect_max": 2, "retry_max": 1, "callee" : "rpc_client" } } srpc-0.10.1/tools/templates/proxy/proxy.conf000066400000000000000000000002101454502251400211020ustar00rootroot00000000000000{ "server": { "port": %s }, "client": { "remote_host": "127.0.0.1", "remote_port": %s, "retry_max": 2 } } srpc-0.10.1/tools/templates/proxy/proxy_main.cc000066400000000000000000000043411454502251400215570ustar00rootroot00000000000000#include #include #include "workflow/WFTaskFactory.h" #include "workflow/WF%sServer.h" #include "workflow/WFFacilities.h" #include "config/config.h" #include "config/util.h" static WFFacilities::WaitGroup wait_group(1); static srpc::RPCConfig config; void sig_handler(int signo) { wait_group.done(); } void init() { if (config.load("./proxy.conf") == false) { perror("Load config failed"); exit(1); } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); } void callback(WF%sTask *client_task) { int state = client_task->get_state(); int error = client_task->get_error(); SeriesWork *series = series_of(client_task); protocol::%sResponse *resp = client_task->get_resp(); protocol::%sResponse *proxy_resp = (protocol::%sResponse *)series->get_context(); // Copy the remote server's response, to proxy response. if (state == WFT_STATE_SUCCESS)%s fprintf(stderr, "backend server state = %%d error = %%d. response client.\n", state, error); } void process(WF%sTask *server_task) { protocol::%sRequest *req = server_task->get_req(); std::string backend_server = config.client_host(); unsigned short backend_server_port = config.client_port(); std::string url = std::string("%s://") + backend_server + std::string(":") + std::to_string(backend_server_port); WF%sTask *client_task = WFTaskFactory::create_%s_task(url,%s config.retry_max(), callback); // Copy user's request to the new task's request using std::move() %s SeriesWork *series = series_of(server_task); series->set_context(server_task->get_resp()); series->push_back(client_task); fprintf(stderr, "proxy get request from client: "); print_peer_address(server_task); } int main() { init(); WF%sServer proxy_server(process); if (proxy_server.start(config.server_port()) == 0) { fprintf(stderr, "[%s]-[%s] proxy started, port %%u\n", config.server_port()); wait_group.wait(); proxy_server.stop(); } else perror("server start"); return 0; } srpc-0.10.1/tools/templates/proxy/proxy_main_proto.cc000066400000000000000000000042171454502251400230040ustar00rootroot00000000000000#include #include #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" #include "config/config.h" #include "%s.srpc.h" using namespace srpc; static srpc::RPCConfig config; static WFFacilities::WaitGroup wait_group(1); static void sig_handler(int signo) { wait_group.done(); } static void init() { if (config.load("./proxy.conf") == false) { perror("Load config failed"); exit(1); } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); } class ProxyServiceImpl : public %s::Service { public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *context) override { fprintf(stderr, "%s proxy get request from client. ip : %%s\n%%s\n", context->get_remote_ip().c_str(), request->DebugString().c_str()); // 5. process() : get request from client and send to remote server auto *task = this->client.create_Echo_task([response](EchoResponse *resp, RPCContext *ctx) { // 6. callback() : fill remote server response to client if (ctx->success()) *response = std::move(*resp); }); task->serialize_input(request); context->get_series()->push_back(task); } public: ProxyServiceImpl(RPCClientParams *params) : client(params) { } private: %s::%sClient client; }; int main() { // 1. load config init(); // 2. make client for remote server RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT; client_params.host = config.client_host(); client_params.port = config.client_port(); // 3. start proxy server %sServer server; ProxyServiceImpl impl(&client_params); server.add_service(&impl); config.load_filter(server); if (server.start(config.server_port()) == 0) { // 4. main thread success and wait fprintf(stderr, "%s [%s]-[%s] proxy started, port %%u\n", config.server_port()); wait_group.wait(); server.stop(); } else perror("server start"); return 0; } srpc-0.10.1/tools/templates/rpc/000077500000000000000000000000001454502251400164645ustar00rootroot00000000000000srpc-0.10.1/tools/templates/rpc/CMakeLists.txt000066400000000000000000000053601454502251400212300ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) project(%s LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_BUILD_TYPE RelWithDebInfo) set(Protobuf_ERROR_MSG "ERROR: Failed to find protobuf. Some suggestions for installation.") set(Protobuf_ERROR_MSG_LINUX "${Protobuf_ERROR_MSG} For Debian / Ubuntu ...: sudo apt-get update sudo apt-get install -y libprotobuf-dev protobuf-compiler For RedHat / Fedora / CentOS ...: sudo yum makecache sudo yum install -y protobuf-devel protobuf-compiler") set(Protobuf_ERROR_MSG_MACOS "${Protobuf_ERROR_MSG} For MacOS : sudo brew update sudo brew install protobuf protobuf-c") # Find all the dependencies find_package(OpenSSL REQUIRED) set(Workflow_DIR "%s") find_package(Workflow REQUIRED CONFIG HINTS ${Workflow_DIR}) set(Srpc_DIR "%s") find_package(srpc REQUIRED CONFIG HINTS ${Srpc_DIR}) find_package(Protobuf) if ("x${Protobuf_DIR}" STREQUAL "xProtobuf_DIR-NOTFOUND") if (APPLE) message (FATAL_ERROR ${Protobuf_ERROR_MSG_MACOS}) else () message (FATAL_ERROR ${Protobuf_ERROR_MSG_LINUX}) endif () endif () get_filename_component(Protobuf_LIB_DIR ${Protobuf_LIBRARY} DIRECTORY) if (NOT EXISTS "${Srpc_DIR}/third_party/lz4/lib/lz4.h") set(LZ4_LIB lz4) endif () if (NOT EXISTS "${Srpc_DIR}/third_party/snappy/cmake") set(SNAPPY_LIB snappy) endif () find_package(ZLIB REQUIRED) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}) # Generate idl code: xx.srpc.h xx.pb.h xx.pb.cc xx.thrift.h set(IDL_FILE %s) set(SRPC_GEN_PROGRAM ${SRPC_BIN_DIR}/srpc_generator) %s add_custom_target(SRPC_GEN ALL COMMAND ${SRPC_GEN_PROGRAM} ${PROJECT_SOURCE_DIR}/${IDL_FILE} ${PROJECT_SOURCE_DIR} -s COMMENT "sprc generator..." ) # Prefer to static link first set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ${CMAKE_FIND_LIBRARY_SUFFIXES}) find_library(Workflow_LIB workflow HINTS ${Workflow_DIR}/_lib) find_library(Srpc_LIB srpc HINTS ${Srpc_DIR}/_lib) # Set all the libraries here set(LIB ${Srpc_LIB} ${Workflow_LIB} pthread OpenSSL::SSL OpenSSL::Crypto protobuf z ${SNAPPY_LIB} ${LZ4_LIB}) # Add all the common code here set(COMMON_CODE config/config.cc config/Json.cc ${PROTO_SRCS}) # Add header directories and library directories here include_directories(${OPENSSL_INCLUDE_DIR} ${Protobuf_INCLUDE_DIR} ${WORKFLOW_INCLUDE_DIR} ${SRPC_INCLUDE_DIR} ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) link_directories(${OPENSSL_LINK_DIR} ${Protobuf_LIB_DIR} ${WORKFLOW_LIB_DIR} ${SRPC_LIB_DIR}) # Build executable outputs set(PROJECT_OUTPUT server client%s) foreach(output ${PROJECT_OUTPUT}) add_executable(${output} ${output}_main.cc ${COMMON_CODE}) target_link_libraries(${output} ${LIB}) endforeach() srpc-0.10.1/tools/templates/rpc/client.conf000066400000000000000000000002571454502251400206150ustar00rootroot00000000000000{ "client": { "remote_host": "127.0.0.1", "remote_port": 1412, "is_ssl" : false, "redirect_max": 2, "retry_max": 1, "callee" : "rpc_client" } } srpc-0.10.1/tools/templates/rpc/client_protobuf.cc000066400000000000000000000030451454502251400221730ustar00rootroot00000000000000#include #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" #include "%s.srpc.h" #include "config/config.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); static srpc::RPCConfig config; void init() { if (config.load("./client.conf") == false) { perror("Load config failed"); exit(1); } } int main() { // 1. load config init(); // 2. start client RPCClientParams params = RPC_CLIENT_PARAMS_DEFAULT; params.host = config.client_host(); params.port = config.client_port(); %s %s::%sClient client(¶ms); config.load_filter(client); // 3. request with sync api EchoRequest req; EchoResponse resp; RPCSyncContext ctx; req.set_message("Hello, this is sync request!"); client.Echo(&req, &resp, &ctx); if (ctx.success) fprintf(stderr, "sync resp. %%s\n", resp.DebugString().c_str()); else fprintf(stderr, "sync status[%%d] error[%%d] errmsg:%%s\n", ctx.status_code, ctx.error, ctx.errmsg.c_str()); // 4. request with async api req.set_message("Hello, this is async request!"); client.Echo(&req, [](EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) fprintf(stderr, "async resp. %%s\n", resp->DebugString().c_str()); else fprintf(stderr, "async status[%%d] error[%%d] errmsg:%%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); wait_group.done(); }); wait_group.wait(); return 0; } srpc-0.10.1/tools/templates/rpc/client_thrift.cc000066400000000000000000000031311454502251400216270ustar00rootroot00000000000000#include #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" #include "%s.srpc.h" #include "config/config.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); static srpc::RPCConfig config; void init() { if (config.load("./client.conf") == false) { perror("Load config failed"); exit(1); } } int main() { // 1. load config init(); // 2. start client RPCClientParams params = RPC_CLIENT_PARAMS_DEFAULT; params.host = config.client_host(); params.port = config.client_port(); %s %s::%sClient client(¶ms); config.load_filter(client); // 3. request with sync api EchoResult res; client.Echo(res, "Hello, this is sync request!"); if (client.thrift_last_sync_success()) fprintf(stderr, "sync resp. %%s\n", res.message.c_str()); else { const auto& sync_ctx = client.thrift_last_sync_ctx(); fprintf(stderr, "sync status[%%d] error[%%d] errmsg:%%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); } // 4. request with async api %s::EchoRequest req; req.message = "Hello, this is async request!"; client.Echo(&req, [](%s::EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) fprintf(stderr, "async resp. %%s\n", resp->result.message.c_str()); else fprintf(stderr, "async status[%%d] error[%%d] errmsg:%%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); wait_group.done(); }); wait_group.wait(); return 0; } srpc-0.10.1/tools/templates/rpc/rpc.proto000066400000000000000000000003711454502251400203360ustar00rootroot00000000000000syntax="proto2"; message EchoRequest { required string message = 1; }; message EchoResponse { required string message = 1; optional int32 state = 2; optional int32 error = 3; }; service %s { rpc Echo(EchoRequest) returns (EchoResponse); }; srpc-0.10.1/tools/templates/rpc/rpc.thrift000066400000000000000000000002251454502251400204710ustar00rootroot00000000000000struct EchoResult { 1:required string message; 2:optional i32 state; 3:optional i32 error; } service %s { EchoResult Echo(1:string message); } srpc-0.10.1/tools/templates/rpc/server.conf000066400000000000000000000000511454502251400206350ustar00rootroot00000000000000{ "server": { "port": 1412 } } srpc-0.10.1/tools/templates/rpc/server_protobuf.cc000066400000000000000000000023771454502251400222320ustar00rootroot00000000000000#include #include #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" #include "config/config.h" #include "%s.srpc.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); static srpc::RPCConfig config; void sig_handler(int signo) { wait_group.done(); } void init() { if (config.load("./server.conf") == false) { perror("Load config failed"); exit(1); } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); } class ServiceImpl : public %s::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { %s// 4. delete the following codes and fill your logic fprintf(stderr, "get req. %%s\n", req->DebugString().c_str()); resp->set_message("Hi back"); } }; int main() { // 1. load config init(); // 2. start server %sServer server; ServiceImpl impl; server.add_service(&impl); config.load_filter(server); if (server.start(config.server_port()) == 0) { // 3. success and wait fprintf(stderr, "%s %s server started, port %%u\n", config.server_port()); wait_group.wait(); server.stop(); } else perror("server start"); return 0; } srpc-0.10.1/tools/templates/rpc/server_thrift.cc000066400000000000000000000022411454502251400216600ustar00rootroot00000000000000#include #include #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" #include "config/config.h" #include "%s.srpc.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); static srpc::RPCConfig config; void sig_handler(int signo) { wait_group.done(); } void init() { if (config.load("./server.conf") == false) { perror("Load config failed"); exit(1); } } class ServiceImpl : public %s::Service { public: void Echo(EchoResult& _return, const std::string& message) override { %s// 4. delete the following codes and fill your logic fprintf(stderr, "get req. %%s\n", message.c_str()); _return.message = "Hi back."; } }; int main() { // 1. load config init(); // 2. start server %sServer server; ServiceImpl impl; server.add_service(&impl); config.load_filter(server); if (server.start(config.server_port()) == 0) { // 3. success and wait printf("%s %s server started, port %%u\n", config.server_port()); wait_group.wait(); server.stop(); } else perror("server start"); return 0; } srpc-0.10.1/tutorial/000077500000000000000000000000001454502251400144055ustar00rootroot00000000000000srpc-0.10.1/tutorial/CMakeLists.txt000066400000000000000000000127211454502251400171500ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "build type") project(srpc_tutorial LANGUAGES C CXX ) ###Options if (WIN32) option(SRPC_TUTORIAL_BUILD_STATIC_RUNTIME "Use static runtime" ON) endif () set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}) if (NOT "$ENV{LIBRARY_PATH}" STREQUAL "") string(REPLACE ":" ";" LIBRARY_PATH $ENV{LIBRARY_PATH}) set(CMAKE_SYSTEM_LIBRARY_PATH ${LIBRARY_PATH};${CMAKE_SYSTEM_LIBRARY_PATH}) endif () if (NOT "$ENV{CPLUS_INCLUDE_PATH}" STREQUAL "") string(REPLACE ":" ";" INCLUDE_PATH $ENV{CPLUS_INCLUDE_PATH}) set(CMAKE_SYSTEM_INCLUDE_PATH ${INCLUDE_PATH};${CMAKE_SYSTEM_INCLUDE_PATH}) endif () find_package(OpenSSL REQUIRED) find_package(srpc REQUIRED CONFIG HINTS ..) set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "") if (WIN32) find_package(Protobuf CONFIG REQUIRED) find_library(LZ4_LIBRARY NAMES lz4) if(LZ4_LIBRARY STREQUAL "LZ4_LIBRARY-NOTFOUND") find_package(lz4) if(lz4_FOUND) set(LZ4_LIBRARY ${lz4_LIBRARIES}) endif() endif() find_package(Snappy CONFIG REQUIRED) else () find_package(Protobuf REQUIRED) endif () if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/lz4/lib/lz4.h") set(LZ4_LIB lz4) endif () if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/snappy/cmake") set(SNAPPY_LIB snappy) endif () if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/workflow/workflow-config.cmake.in") find_package(Workflow REQUIRED CONFIG HINTS ../workflow) endif () find_package(ZLIB REQUIRED) include_directories( ${OPENSSL_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${Protobuf_INCLUDE_DIR} ${WORKFLOW_INCLUDE_DIR} ${SRPC_INCLUDE_DIR} ) if (WIN32) link_directories(${SRPC_LIB_DIR} ${WORKFLOW_LIB_DIR} ${Protobuf_LIB_DIR}) set(SRPC_GEN_PROGRAM ${SRPC_BIN_DIR}/Debug/srpc_generator.exe) else () get_filename_component(Protobuf_LIB_DIR ${Protobuf_LIBRARY} DIRECTORY) link_directories(${SRPC_LIB_DIR} ${WORKFLOW_LIB_DIR} ${Protobuf_LIB_DIR}) set(SRPC_GEN_PROGRAM ${SRPC_BIN_DIR}/srpc_generator) endif () protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS echo_pb.proto) protobuf_generate_cpp(HELLOWORLD_SRCS HELLOWORLD_HDRS helloworld.proto) add_custom_target( TURORIAL_GEN ALL COMMAND ${SRPC_GEN_PROGRAM} ${PROJECT_SOURCE_DIR}/echo_pb.proto ${PROJECT_SOURCE_DIR} COMMAND ${SRPC_GEN_PROGRAM} ${PROJECT_SOURCE_DIR}/echo_thrift.thrift ${PROJECT_SOURCE_DIR} COMMAND ${SRPC_GEN_PROGRAM} protobuf ${PROJECT_SOURCE_DIR}/helloworld.proto ${PROJECT_SOURCE_DIR} COMMENT "srpc generator..." ) if (WIN32) if (SRPC_TUTORIAL_BUILD_STATIC_RUNTIME) set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL ) foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") endforeach () endif () endif () if (WIN32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP /wd4200") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4200 /Zc:__cplusplus /std:c++14") else () set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fPIC -pipe -std=gnu90") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -pipe -std=c++11 -fno-exceptions") endif () set(TUTORIAL_PB_LIST tutorial-01-srpc_pb_server tutorial-02-srpc_pb_client tutorial-05-brpc_pb_server tutorial-06-brpc_pb_client tutorial-09-client_task tutorial-10-server_async tutorial-15-srpc_pb_proxy tutorial-16-server_with_metrics ) if (APPLE) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ${CMAKE_FIND_LIBRARY_SUFFIXES}) find_library(Workflow_LIB workflow HINTS ../workflow/_lib) find_library(Srpc_LIB srpc HINTS ../_lib) set(SRPC_LIB ${Srpc_LIB} ${Workflow_LIB} pthread OpenSSL::SSL OpenSSL::Crypto protobuf z ) elseif (WIN32) set(SRPC_LIB srpc workflow ws2_32 wsock32 OpenSSL::SSL OpenSSL::Crypto protobuf::libprotobuf ZLIB::ZLIB Snappy::snappy ${LZ4_LIBRARY} ) else () set(SRPC_LIB srpc workflow pthread OpenSSL::SSL OpenSSL::Crypto protobuf z ${SNAPPY_LIB} ${LZ4_LIB} ) endif () foreach(src ${TUTORIAL_PB_LIST}) string(REPLACE "-" ";" arr ${src}) list(GET arr -1 bin_name) add_executable(${bin_name} ${src}.cc ${PROTO_SRCS} ${PROTO_HDRS}) target_link_libraries(${bin_name} ${SRPC_LIB}) add_dependencies(${bin_name} TURORIAL_GEN) endforeach() set(TUTORIAL_THRIFT_LIST tutorial-03-srpc_thrift_server tutorial-04-srpc_thrift_client tutorial-07-thrift_thrift_server tutorial-08-thrift_thrift_client ) foreach(src ${TUTORIAL_THRIFT_LIST}) string(REPLACE "-" ";" arr ${src}) list(GET arr -1 bin_name) add_executable(${bin_name} ${src}.cc) target_link_libraries(${bin_name} ${SRPC_LIB}) add_dependencies(${bin_name} TURORIAL_GEN) endforeach() set(TUTORIAL_HELLOWORLD_LIST tutorial-11-trpc_pb_server tutorial-12-trpc_pb_client tutorial-13-trpc_http_server tutorial-14-trpc_http_client ) foreach(src ${TUTORIAL_HELLOWORLD_LIST}) string(REPLACE "-" ";" arr ${src}) list(GET arr -1 bin_name) add_executable(${bin_name} ${src}.cc ${HELLOWORLD_SRCS} ${HELLOWORLD_HDRS}) target_link_libraries(${bin_name} ${SRPC_LIB}) add_dependencies(${bin_name} TURORIAL_GEN) endforeach() if (NOT WIN32) set(TUTORIAL_HTTP_LIST tutorial-17-http_server tutorial-18-http_client ) foreach(src ${TUTORIAL_HTTP_LIST}) string(REPLACE "-" ";" arr ${src}) list(GET arr -1 bin_name) add_executable(${bin_name} ${src}.cc) target_link_libraries(${bin_name} ${SRPC_LIB}) add_dependencies(${bin_name} TURORIAL_GEN) endforeach() endif () srpc-0.10.1/tutorial/GNUmakefile000066400000000000000000000016161454502251400164630ustar00rootroot00000000000000ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) ALL_TARGETS := all clean MAKE_FILE := Makefile DEFAULT_BUILD_DIR := build.cmake BUILD_DIR := $(shell if [ -f $(MAKE_FILE) ]; then echo "."; else echo $(DEFAULT_BUILD_DIR); fi) CMAKE3 := $(shell if which cmake3>/dev/null ; then echo cmake3; else echo cmake; fi;) .PHONY: $(ALL_TARGETS) all: mkdir -p $(BUILD_DIR) ifeq ($(DEBUG),y) cd $(BUILD_DIR) && $(CMAKE3) -D CMAKE_BUILD_TYPE=Debug $(ROOT_DIR) else ifneq ("${Workflow_DIR}workflow", "workflow") cd $(BUILD_DIR) && $(CMAKE3) -DWorkflow_DIR:STRING=${Workflow_DIR} $(ROOT_DIR) else cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR) endif make -C $(BUILD_DIR) -f Makefile clean: ifeq ($(MAKE_FILE), $(wildcard $(MAKE_FILE))) -make -f Makefile clean else ifeq ($(DEFAULT_BUILD_DIR), $(wildcard $(DEFAULT_BUILD_DIR))) -make -C $(DEFAULT_BUILD_DIR) clean endif rm -rf $(DEFAULT_BUILD_DIR) srpc-0.10.1/tutorial/echo_pb.proto000066400000000000000000000004311454502251400170670ustar00rootroot00000000000000syntax="proto2"; message EchoRequest { required string message = 1; required string name = 2; }; message EchoResponse { required string message = 1; optional int32 state = 2; optional int32 error = 3; }; service Example { rpc Echo(EchoRequest) returns (EchoResponse); }; srpc-0.10.1/tutorial/echo_thrift.thrift000066400000000000000000000002511454502251400201230ustar00rootroot00000000000000struct EchoResult { 1:required string message; 2:optional i32 state; 3:optional i32 error; } service Example { EchoResult Echo(1:string message, 2:string name); } srpc-0.10.1/tutorial/helloworld.proto000066400000000000000000000004101454502251400176400ustar00rootroot00000000000000syntax = "proto3"; package trpc.test.helloworld; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} rpc SayHi (HelloRequest) returns (HelloReply) {} } message HelloRequest { string msg = 1; } message HelloReply { string msg = 1; } srpc-0.10.1/tutorial/tutorial-01-srpc_pb_server.cc000066400000000000000000000027001454502251400220100ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "echo_pb.srpc.h" #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { resp->set_message("Hi back"); printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n", req->DebugString().c_str(), resp->DebugString().c_str()); } }; static void sig_handler(int signo) { wait_group.done(); } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); SRPCServer server; ExampleServiceImpl impl; server.add_service(&impl); if (server.start(1412) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/tutorial/tutorial-02-srpc_pb_client.cc000066400000000000000000000030711454502251400217630ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "workflow/WFFacilities.h" #include "echo_pb.srpc.h" #include "srpc/rpc_types.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); int main() { Example::SRPCClient client("127.0.0.1", 1412); //async EchoRequest req; req.set_message("Hello, srpc!"); req.set_name("1412"); client.Echo(&req, [](EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) printf("%s\n", resp->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); //sync EchoRequest sync_req; EchoResponse sync_resp; RPCSyncContext sync_ctx; sync_req.set_message("Hello, srpc!"); sync_req.set_name("Sync"); client.Echo(&sync_req, &sync_resp, &sync_ctx); if (sync_ctx.success) printf("%s\n", sync_resp.DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); wait_group.wait(); return 0; } srpc-0.10.1/tutorial/tutorial-03-srpc_thrift_server.cc000066400000000000000000000027021454502251400227130ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "echo_thrift.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); class ExampleServiceImpl : public Example::Service { public: void Echo(EchoResult& _return, const std::string& message, const std::string& name) override { _return.message = "Hi back, " + name; printf("Server Echo()\nreq_message:\n%s\nresp_message:\n%s\n", message.c_str(), _return.message.c_str()); } }; static void sig_handler(int signo) { wait_group.done(); } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); SRPCServer server; ExampleServiceImpl impl; server.add_service(&impl); if (server.start(1412) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/tutorial/tutorial-04-srpc_thrift_client.cc000066400000000000000000000036271454502251400226730ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "workflow/WFFacilities.h" #include "echo_thrift.srpc.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); int main() { Example::SRPCClient client("127.0.0.1", 1412); //sync EchoResult sync_res; client.Echo(sync_res, "Hello, srpc!", "1412"); if (client.thrift_last_sync_success()) printf("%s\n", sync_res.message.c_str()); else { const auto& sync_ctx = client.thrift_last_sync_ctx(); printf("status[%d] error[%d] errmsg:%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); } //async Example::EchoRequest req; req.message = "Hello, srpc!"; req.name = "1412"; client.Echo(&req, [](Example::EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) printf("%s\n", resp->result.message.c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); //sync Example::EchoRequest sync_req; Example::EchoResponse sync_resp; RPCSyncContext sync_ctx; sync_req.message = "Hello, srpc!"; sync_req.name = "Sync"; client.Echo(&sync_req, &sync_resp, &sync_ctx); if (sync_ctx.success) printf("%s\n", sync_resp.result.message.c_str()); else printf("status[%d] error[%d] errmsg:%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); wait_group.wait(); return 0; } srpc-0.10.1/tutorial/tutorial-05-brpc_pb_server.cc000066400000000000000000000030241454502251400217730ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "echo_pb.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); char attachment[10] = "ATTACH"; class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { resp->set_message("Hi back, " + req->name()); ctx->set_attachment_nocopy(attachment, strlen(attachment)); printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n", req->DebugString().c_str(), resp->DebugString().c_str()); } }; static void sig_handler(int signo) { wait_group.done(); } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); BRPCServer server; ExampleServiceImpl impl; server.add_service(&impl); if (server.start(1412) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/tutorial/tutorial-06-brpc_pb_client.cc000066400000000000000000000033021454502251400217430ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "workflow/WFFacilities.h" #include "echo_pb.srpc.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); int main() { Example::BRPCClient client("127.0.0.1", 1412); //async EchoRequest req; req.set_message("Hello, srpc!"); req.set_name("1412"); client.Echo(&req, [](EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) printf("%s\n", resp->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); const char *attachment; size_t len; while (ctx->get_attachment(&attachment, &len)) printf("get attachment [%.*s] len=%zu\n", (int)len, attachment, len); }); //sync EchoRequest sync_req; EchoResponse sync_resp; RPCSyncContext sync_ctx; sync_req.set_message("Hello, srpc!"); sync_req.set_name("Sync"); client.Echo(&sync_req, &sync_resp, &sync_ctx); if (sync_ctx.success) printf("%s\n", sync_resp.DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); wait_group.wait(); return 0; } srpc-0.10.1/tutorial/tutorial-07-thrift_thrift_server.cc000066400000000000000000000025641454502251400232560ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "echo_thrift.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); class ExampleServiceImpl : public Example::Service { public: void Echo(EchoResult& _return, const std::string& message, const std::string& name) override { _return.message = "Hi back, " + name; printf("Server Echo()\nreq_message:\n%s\nresp_message:\n%s\n", message.c_str(), _return.message.c_str()); } }; static void sig_handler(int signo) { wait_group.done(); } int main() { signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); ThriftServer server; ExampleServiceImpl impl; server.add_service(&impl); if (server.start(1412) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); return 0; } srpc-0.10.1/tutorial/tutorial-08-thrift_thrift_client.cc000066400000000000000000000042521454502251400232230ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "echo_thrift.srpc.h" using namespace srpc; int main() { Example::ThriftClient client("127.0.0.1", 1412); //sync EchoResult sync_res; client.Echo(sync_res, "Hello, srpc!", "1412"); if (client.thrift_last_sync_success()) printf("%s\n", sync_res.message.c_str()); else { const auto& sync_ctx = client.thrift_last_sync_ctx(); printf("status[%d] error[%d] errmsg:%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); } //send/recv client.send_Echo("Hello, srpc!", "1412"); //do anything you want client.recv_Echo(sync_res); if (client.thrift_last_sync_success()) printf("%s\n", sync_res.message.c_str()); else { const auto& sync_ctx = client.thrift_last_sync_ctx(); printf("status[%d] error[%d] errmsg:%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); } //async Example::EchoRequest req; req.message = "Hello, srpc!"; req.name = "1412"; client.Echo(&req, [](Example::EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) printf("%s\n", resp->result.message.c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); //sync Example::EchoRequest sync_req; Example::EchoResponse sync_resp; RPCSyncContext sync_ctx; sync_req.message = "Hello, srpc!"; sync_req.name = "Sync"; client.Echo(&sync_req, &sync_resp, &sync_ctx); if (sync_ctx.success) printf("%s\n", sync_resp.result.message.c_str()); else printf("status[%d] error[%d] errmsg:%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); return 0; } srpc-0.10.1/tutorial/tutorial-09-client_task.cc000066400000000000000000000036171454502251400213120ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include "echo_pb.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; int main() { Example::SRPCClient client("127.0.0.1", 1412); EchoRequest req; req.set_message("Hello, srpc!"); req.set_name("1412"); auto *rpc_task = client.create_Echo_task([](EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) printf("%s\n", resp->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); auto *http_task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { std::string body; const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); body.assign((const char *)data, len); printf("%s\n\n", body.c_str()); } else printf("Http request fail\n\n"); }); rpc_task->serialize_input(&req); rpc_task->log({{"event", "info"}, {"message", "rpc client task log."}}); WFFacilities::WaitGroup wait_group(1); SeriesWork *series = Workflow::create_series_work(http_task, [&wait_group](const SeriesWork *) { wait_group.done(); }); series->push_back(rpc_task); series->start(); wait_group.wait(); return 0; } srpc-0.10.1/tutorial/tutorial-10-server_async.cc000066400000000000000000000040151454502251400214760ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include "echo_pb.srpc.h" #include "workflow/WFFacilities.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { ctx->set_compress_type(RPCCompressGzip); ctx->log({{"event", "info"}, {"message", "rpc server echo() end."}}); auto *task = WFTaskFactory::create_http_task("https://www.sogou.com", 0, 0, [req, resp](WFHttpTask *task) { if (task->get_state() == WFT_STATE_SUCCESS) { const void *data; size_t len; task->get_resp()->get_parsed_body(&data, &len); resp->mutable_message()->assign((const char *)data, len); } else resp->set_message("Error: " + std::to_string(task->get_error())); printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n", req->DebugString().c_str(), resp->DebugString().c_str()); }); ctx->get_series()->push_back(task); } }; static void sig_handler(int signo) { wait_group.done(); } int main(int argc, char *argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); SRPCServer server; ExampleServiceImpl impl; server.add_service(&impl); if (server.start(1412) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/tutorial/tutorial-11-trpc_pb_server.cc000066400000000000000000000036121454502251400220150ustar00rootroot00000000000000/* Copyright (c) 2021 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "helloworld.srpc.h" #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" using namespace srpc; using namespace trpc::test::helloworld; static WFFacilities::WaitGroup wait_group(1); class GreeterServiceImpl : public Greeter::Service { public: void SayHello(HelloRequest *req, HelloReply *resp, RPCContext *ctx) override { ctx->set_compress_type(RPCCompressGzip); resp->set_msg("This is SRPC framework TRPC protocol. Hello back."); printf("Server SayHello()\nget_req:\n%s\nset_resp:\n%s\n", req->DebugString().c_str(), resp->DebugString().c_str()); } void SayHi(HelloRequest *req, HelloReply *resp, RPCContext *ctx) override { resp->set_msg("This is SRPC framework TRPC protocol. Hi back."); printf("Server SayHi()\nget_req:\n%s\nset_resp:\n%s\n", req->DebugString().c_str(), resp->DebugString().c_str()); } }; static void sig_handler(int signo) { wait_group.done(); } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); TRPCServer server; GreeterServiceImpl impl; server.add_service(&impl); if (server.start(1412) == 0) { printf("SRPC framework TRPC server running on 1412...\n"); wait_group.wait(); server.stop(); } else perror("server start"); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/tutorial/tutorial-12-trpc_pb_client.cc000066400000000000000000000031771454502251400217740ustar00rootroot00000000000000/* Copyright (c) 2021 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "workflow/WFFacilities.h" #include "helloworld.srpc.h" #include "srpc/rpc_types.h" using namespace srpc; using namespace trpc::test::helloworld; static WFFacilities::WaitGroup wait_group(1); int main() { Greeter::TRPCClient client("127.0.0.1", 1412); //async HelloRequest req; req.set_msg("Hello, trpc server. This is srpc framework trpc client!"); client.SayHello(&req, [](HelloReply *resp, RPCContext *ctx) { if (ctx->success()) printf("%s\n", resp->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); //sync HelloRequest sync_req; HelloReply sync_resp; RPCSyncContext sync_ctx; sync_req.set_msg("Hi, trpc server. This is srpc framework trpc client!"); client.SayHi(&sync_req, &sync_resp, &sync_ctx); if (sync_ctx.success) printf("%s\n", sync_resp.DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); wait_group.wait(); return 0; } srpc-0.10.1/tutorial/tutorial-13-trpc_http_server.cc000066400000000000000000000036221454502251400223760ustar00rootroot00000000000000/* Copyright (c) 2021 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "helloworld.srpc.h" #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" using namespace srpc; using namespace trpc::test::helloworld; static WFFacilities::WaitGroup wait_group(1); class GreeterServiceImpl : public Greeter::Service { public: void SayHello(HelloRequest *req, HelloReply *resp, RPCContext *ctx) override { ctx->set_compress_type(RPCCompressGzip); resp->set_msg("This is SRPC framework TRPC protocol. Hello back."); printf("Server SayHello()\nget_req:\n%s\nset_resp:\n%s\n", req->DebugString().c_str(), resp->DebugString().c_str()); } void SayHi(HelloRequest *req, HelloReply *resp, RPCContext *ctx) override { resp->set_msg("This is SRPC framework TRPC protocol. Hi back."); printf("Server SayHi()\nget_req:\n%s\nset_resp:\n%s\n", req->DebugString().c_str(), resp->DebugString().c_str()); } }; static void sig_handler(int signo) { wait_group.done(); } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); TRPCHttpServer server; GreeterServiceImpl impl; server.add_service(&impl); if (server.start(1412) == 0) { printf("SRPC framework TRPCHttp server running on 1412...\n"); wait_group.wait(); server.stop(); } else perror("server start"); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/tutorial/tutorial-14-trpc_http_client.cc000066400000000000000000000032261454502251400223470ustar00rootroot00000000000000/* Copyright (c) 2021 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "workflow/WFFacilities.h" #include "helloworld.srpc.h" #include "srpc/rpc_types.h" using namespace srpc; using namespace trpc::test::helloworld; static WFFacilities::WaitGroup wait_group(1); int main() { Greeter::TRPCHttpClient client("127.0.0.1", 1412); //async HelloRequest req; req.set_msg("Hello, trpc-http server. This is srpc framework trpc-http client!"); client.SayHello(&req, [](HelloReply *resp, RPCContext *ctx) { if (ctx->success()) printf("%s\n", resp->DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", ctx->get_status_code(), ctx->get_error(), ctx->get_errmsg()); }); //sync HelloRequest sync_req; HelloReply sync_resp; RPCSyncContext sync_ctx; sync_req.set_msg("Hi, trpc-http server. This is srpc framework trpc-http client!"); client.SayHi(&sync_req, &sync_resp, &sync_ctx); if (sync_ctx.success) printf("%s\n", sync_resp.DebugString().c_str()); else printf("status[%d] error[%d] errmsg:%s\n", sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str()); wait_group.wait(); return 0; } srpc-0.10.1/tutorial/tutorial-15-srpc_pb_proxy.cc000066400000000000000000000034161454502251400216750ustar00rootroot00000000000000/* Copyright (c) 2020 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "echo_pb.srpc.h" #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); template class ExampleProxyServiceImpl : public Example::Service { public: ExampleProxyServiceImpl(CLIENT *client) { this->client = client; } private: CLIENT *client; public: void Echo(EchoRequest *request, EchoResponse *response, RPCContext *context) override { auto *task = this->client->create_Echo_task([response](EchoResponse *resp, RPCContext *ctx) { if (ctx->success()) *response = std::move(*resp); }); task->serialize_input(request); context->get_series()->push_back(task); } }; static void sig_handler(int signo) { wait_group.done(); } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); SRPCServer server; Example::SRPCClient client("127.0.0.1", 1412); ExampleProxyServiceImpl impl(&client); server.add_service(&impl); if (server.start(61412) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/tutorial/tutorial-16-server_with_metrics.cc000066400000000000000000000041311454502251400230670ustar00rootroot00000000000000/* Copyright (c) 2022 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "echo_pb.srpc.h" #include "workflow/WFFacilities.h" #include "srpc/rpc_types.h" #include "srpc/rpc_metrics_filter.h" using namespace srpc; static WFFacilities::WaitGroup wait_group(1); class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override { resp->set_message("Hi back"); printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n", req->DebugString().c_str(), resp->DebugString().c_str()); this->filter->histogram("echo_request_size")->observe(req->ByteSizeLong()); for (size_t i = 1; i <= 10; i++) this->filter->summary("echo_test_quantiles")->observe(0.01 * i); } void set_filter(RPCMetricsPull *filter) { this->filter = filter; } private: RPCMetricsPull *filter; }; static void sig_handler(int signo) { wait_group.done(); } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); SRPCServer server; ExampleServiceImpl impl; RPCMetricsPull filter; filter.init(8080); /* export port for prometheus */ filter.create_histogram("echo_request_size", "Echo request size", {1, 10, 100}); filter.create_summary("echo_test_quantiles", "Test quantile", {{0.5, 0.05}, {0.9, 0.01}}); impl.set_filter(&filter); server.add_filter(&filter); server.add_service(&impl); if (server.start(1412) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); filter.deinit(); google::protobuf::ShutdownProtobufLibrary(); return 0; } srpc-0.10.1/tutorial/tutorial-17-http_server.cc000066400000000000000000000034311454502251400213500ustar00rootroot00000000000000/* Copyright (c) 2023 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "workflow/WFFacilities.h" #include "srpc/http_server.h" #include "srpc/rpc_types.h" #include "srpc/rpc_metrics_filter.h" #include "srpc/rpc_trace_filter.h" static WFFacilities::WaitGroup wait_group(1); srpc::RPCMetricsPull exporter; srpc::RPCTraceDefault trace_log; //srpc::RPCTraceOpenTelemetry otel("http://127.0.0.1:4318"); static void sig_handler(int signo) { wait_group.done(); } void process(WFHttpTask *task) { fprintf(stderr, "http server get request_uri: %s\n", task->get_req()->get_request_uri()); task->get_resp()->append_output_body("Hello from server!"); } int main() { signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); srpc::HttpServer server(process); exporter.init(8080); /* export port for prometheus */ exporter.create_histogram("echo_request_size", "Echo request size", {1, 10, 100}); exporter.create_summary("echo_test_quantiles", "Test quantile", {{0.5, 0.05}, {0.9, 0.01}}); server.add_filter(&trace_log); // server.add_filter(&otel); server.add_filter(&exporter); if (server.start(1412) == 0) { wait_group.wait(); server.stop(); } else perror("server start"); exporter.deinit(); return 0; } srpc-0.10.1/tutorial/tutorial-18-http_client.cc000066400000000000000000000031541454502251400213230ustar00rootroot00000000000000/* Copyright (c) 2023 sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "workflow/WFFacilities.h" #include "srpc/http_client.h" #include "srpc/rpc_types.h" #include "srpc/rpc_metrics_filter.h" #include "srpc/rpc_trace_filter.h" using namespace srpc; #define REDIRECT_MAX 5 #define RETRY_MAX 2 srpc::RPCTraceDefault trace_log; static WFFacilities::WaitGroup wait_group(1); void callback(WFHttpTask *task) { int state = task->get_state(); int error = task->get_error(); fprintf(stderr, "callback. state = %d error = %d\n", state, error); if (state == WFT_STATE_SUCCESS) // print server response body { const void *body; size_t body_len; task->get_resp()->get_parsed_body(&body, &body_len); fwrite(body, 1, body_len, stdout); fflush(stdout); fprintf(stderr, "\nfinish print body. body_len = %zu\n", body_len); } } int main() { srpc::HttpClient client; client.add_filter(&trace_log); WFHttpTask *task = client.create_http_task("http://127.0.0.1:1412", REDIRECT_MAX, RETRY_MAX, callback); task->start(); wait_group.wait(); return 0; } srpc-0.10.1/workflow/000077500000000000000000000000001454502251400144145ustar00rootroot00000000000000