initialize

This commit is contained in:
liyingxin 2020-08-09 15:54:33 +08:00
parent 2d4124efa0
commit 7114f18794
104 changed files with 16333 additions and 52 deletions

7
.editorconfig Normal file
View File

@ -0,0 +1,7 @@
# top-most EditorConfig file
root = true
# all files
[*]
indent_style = tab
indent_size = 4

62
.gitignore vendored
View File

@ -1,32 +1,42 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.a
*.bak
*.gz
*.zip
*.tar
*.la
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.rpm
*.so
*.dylib
*.dll
*.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
# Fortran module files
*.mod
*.smod
_bin/
_include/
_lib/
.deps/
build/
build_pkg/
CMakeFiles/
Debug/
Release/
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
missing
SRCINFO
SRCNUMVER
SRCVERSION
CMakeCache.txt
Makefile
# Executables
*.exe
*.out
*.app

138
CMakeLists.txt Normal file
View File

@ -0,0 +1,138 @@
cmake_minimum_required(VERSION 3.6)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "build type")
set(CMAKE_INSTALL_PREFIX /opt/sogou CACHE PATH "install prefix")
project(srpc
VERSION 1.0.3
LANGUAGES C CXX)
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 create_symlink ${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()
add_subdirectory(src)
####CONFIG
include(CMakePackageConfigHelpers)
set(CONFIG_INC_DIR ${INC_DIR})
set(CONFIG_LIB_DIR ${LIB_DIR})
set(CONFIG_BIN_DIR ${BIN_DIR})
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
)
install(
FILES ${CMAKE_CONFIG_INSTALL_FILE}
DESTINATION ${CMAKE_CONFIG_INSTALL_DIR}
COMPONENT devel
RENAME ${PROJECT_NAME}-config.cmake
)
#### PACK
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Sogou RPC")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
# set(CPACK_RPM_PRE_INSTALL_SCRIPT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/pre_script.sh)
# set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/post_script.sh)
execute_process(
COMMAND git log -n1 --format=%ad --date=short
RESULT_VARIABLE GIT_LOG_RET
OUTPUT_VARIABLE GIT_YMD
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (NOT GIT_LOG_RET EQUAL 0)
set(GIT_YMD 0)
endif()
string(REPLACE "-" "" RELEASE_LABEL ${GIT_YMD})
set(CPACK_RPM_PACKAGE_RELEASE ${RELEASE_LABEL})
set(CPACK_RPM_COMPONENT_INSTALL ON)
set(CPACK_COMPONENTS_ALL devel)
set(CPACK_RPM_FILE_NAME RPM-DEFAULT)
set(CPACK_RPM_PACKAGE_RELEASE_DIST ON)
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/lib64/cmake)
set(PB_PACKAGE_REQUIRED "protobuf-devel")
execute_process(COMMAND
grep VERSION /etc/os-release
OUTPUT_VARIABLE OS_RELEASE_CORE OUTPUT_STRIP_TRAILING_WHITESPACE)
if (NOT ${OS_RELEASE_CORE} EQUAL "")
string(SUBSTRING ${OS_RELEASE_CORE} 9 1 OS_RELEASE_CORE_NUMBER)
if (${OS_RELEASE_CORE_NUMBER} EQUAL "7")
set(PB_PACKAGE_REQUIRED "protobuf3-devel")
endif()
endif()
set(CPACK_RPM_DEVEL_PACKAGE_REQUIRES "workflow-devel >= 1.12.3, ${PB_PACKAGE_REQUIRED}")
install(
FILES ${INCLUDE_HEADERS}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
COMPONENT devel
)
install(
FILES README.md
DESTINATION "${CMAKE_INSTALL_DOCDIR}-${PROJECT_VERSION}"
COMPONENT devel
)
set(CPACK_RPM_SPEC_MORE_DEFINE "%define __spec_install_post /bin/true") # disable strip
set(CPACK_GENERATOR "RPM")
include(CPack)

28
CMakeLists_Headers.txt Normal file
View File

@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.6)
set(INCLUDE_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/thrift/rpc_thrift_buffer.h
src/thrift/rpc_thrift_enum.h
src/thrift/rpc_thrift_idl.h
src/thrift/rpc_thrift_idl.inl
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/sogou_rpc.h
)

60
GNUmakefile Normal file
View File

@ -0,0 +1,60 @@
ROOT_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
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: base
make -C $(BUILD_DIR) -f Makefile
base:
mkdir -p $(BUILD_DIR)
ifeq ($(DEBUG),y)
cd $(BUILD_DIR) && $(CMAKE3) -D CMAKE_BUILD_TYPE=Debug $(ROOT_DIR)
else
cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR)
endif
tutorial: all
make -C tutorial
example: all
make -C example
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 (build, $(wildcard build))
-make -C build clean
endif
-make -C test clean
-make -C benchmark clean
-make -C example clean
-make -C tutorial 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

26
LICENSE
View File

@ -1,3 +1,4 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@ -174,28 +175,3 @@
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

319
README.md
View File

@ -1 +1,318 @@
# srpc
# Sogou RPC (Sogou Remote Procedure Call)
## Introduction
#### 搜狗自研RPC简称srpc主要功能和特点
* 这是一个基于Sogou C++ Workflow的项目兼具
* 高性能
* 低开发和接入门槛
* 完美兼容workflow的串并联任务流
* 对于已有pb/thrift描述文件的项目可以做到一键迁移
* 支持多种IDL格式包括
* Protobuf
* Thrift
* 支持多种数据布局,使用上完全透明,包括:
* Protobuffer serialize
* Thrift Binary serialize
* json serialize
* 支持多种压缩,使用上完全透明,包括:
* gzip
* zlib
* snappy
* lz4
* 支持多种通信协议,使用上完全透明,包括:
* tcp
* http
* 用户可以通过http+json实现跨语言
* 如果自己是server提供方用任何语言的http server接受post请求解析若干http header即可
* 如果自己是client调用方用任何语言的http client发送post请求添加若干http header即可
* 内置了可以与其他RPC框架的server/client无缝互通的client/server包括
* BPRC
* ~~GRPC~~
* Thrift Framed Binary
* Thrift Http Binary
* 兼容workflow的使用方式
* 提供创建任务的接口来创建一个rpc任务
* 可以把rpc任务放到任务流图中回调函数里也可以拿到当前的任务流
* 其他workflow所支持的功能包括upstream等
## Install
* srpc是一个静态库libsrpc.a只有开发环境需要依赖libsrpc编译后二进制发布不需要依赖libsrpc库
* srpc依赖workflow和protobuf3
#### 1. yum安装
##### centos7
~~~sh
yum install srpc-devel workflow-devel protobuf3 protobuf3-devel
~~~
##### centos8
~~~sh
yum install srpc-devel workflow-devel protobuf protobuf-devel
~~~
#### 2. 源码安装
##### sogou centos7
~~~sh
yum install workflow-devel protobuf3 protobuf3-devel cmake3 gtest-devel valgrind
git clone https://git.sogou-inc.com/liyingxin/srpc.git
scl enable protobuf3 -- bash
make
make check
make install
~~~
##### sogou centos8
~~~sh
yum install workflow-devel protobuf protobuf-devel cmake gtest-devel valgrind
git clone https://git.sogou-inc.com/liyingxin/srpc.git
make
make check
make install
~~~
## Quick Start
#### 1. example.proto
~~~proto
syntax="proto2";//这里proto2和proto3都可以srpc都支持
message EchoRequest {
optional string message = 1;
optional string name = 2;
};
message EchoResponse {
optional 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 <stdio.h>
#include <signal.h>
#include "example.srpc.h"
using namespace sogou;
class ExampleServiceImpl : public Example::Service
{
public:
void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override
{
response->set_message("Hi, " + request->name());
/*
// gzip/zlib/snappy/lz4/none
ctx->set_compress_type(RPCCompressGzip);
// protobuf/json
ctx->set_data_type(RPCDataJson);
*/
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);
pause();
server_http.stop();
server_tcp.stop();
return 0;
}
~~~
#### 4. client.cc
~~~cpp
#include <stdio.h>
#include "example.srpc.h"
using namespace sogou;
int main()
{
Example::SRPCClient client("127.0.0.1", 1412);
EchoRequest req;
req.set_message("Hello, sogou rpc!");
req.set_name("Li Yingxin");
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());
});
pause();
return 0;
}
~~~
#### 5. make
~~~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 127.0.0.1:8811/Example/Echo -H 'Content-Type: application/json' -d '{message:"from curl",name:"CURL"}'
~~~
终端1输出
~~~sh
get_req:
message: "Hello, sogou rpc!"
name: "Li Yingxin"
set_resp:
message: "Hi, Li Yingxin"
get_req:
message: "from curl"
name: "CURL"
set_resp:
message: "Hi, CURL"
~~~
终端2输出
~~~sh
message: "Hi, Li Yingxin"
{"message":"Hi, CURL"}
~~~
## Tutorial
* [第1步设计IDL描述文件](docs/tutorial-01-idl.md)
* [第2步实现ServiceIMPL](docs/tutorial-02-service.md)
* [第3步启动Server](docs/tutorial-03-server.md)
* [第4步使用Client](docs/tutorial-04-client.md)
* [第5步了解异步Context](docs/tutorial-05-context.md)
* [第6步与workflow的结合使用](docs/tutorial-06-workflow.md)
## 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)
## Authors
* **Li Yingxin** - *[liyingxin@sogou-inc.com](mailto:liyingxin@sogou-inc.com)* - *main author*
* **Wu Jiaxu** - *[wujiaxu@sogou-inc.com](mailto:wujiaxu@sogou-inc.com)* - *c++ template interface/implementation*

85
benchmark/CMakeLists.txt Normal file
View File

@ -0,0 +1,85 @@
cmake_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)
else ()
find_package(Protobuf REQUIRED)
endif ()
find_package(Workflow REQUIRED)
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} protobuf ${PROJECT_SOURCE_DIR}/benchmark_pb.proto ${PROJECT_SOURCE_DIR}
COMMAND ${SRPC_GEN_PROGRAM} thrift ${PROJECT_SOURCE_DIR}/benchmark_thrift.thrift ${PROJECT_SOURCE_DIR}
COMMENT "sprc 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(SRPC_LIB srpc)
elseif (WIN32)
set(SRPC_LIB srpc workflow ws2_32 wsock32 OpenSSL::SSL OpenSSL::Crypto)
else ()
set(SRPC_LIB srpc)
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)

28
benchmark/GNUmakefile Normal file
View File

@ -0,0 +1,28 @@
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
ALL_TARGETS := all clean
MAKE_FILE := Makefile
DEFAULT_BUILD_DIR := build
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
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 (build, $(wildcard build))
-make -C build clean
endif
rm -rf build
#g++ -o thrift_server thrift_server.cc gen-cpp/*.cpp -O2 -g -lthrift -lthriftnb -levent -lpthread -std=c++11

View File

@ -0,0 +1,12 @@
syntax="proto3";
message EmptyPBMsg { }
message FixLengthPBMsg { bytes msg = 1; }
service BenchmarkPB
{
rpc echo_pb(FixLengthPBMsg) returns (EmptyPBMsg);
rpc slow_pb(FixLengthPBMsg) returns (EmptyPBMsg);
}

View File

@ -0,0 +1,6 @@
service BenchmarkThrift
{
void echo_thrift(1:string msg);
void slow_thrift(1:string msg);
}

217
benchmark/client.cc Normal file
View File

@ -0,0 +1,217 @@
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include <string>
#include <atomic>
#include <chrono>
#include "benchmark_pb.srpc.h"
#include "benchmark_thrift.srpc.h"
using namespace sogou;
#define TEST_SECOND 20
#define GET_CURRENT_MS std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()
#define GET_CURRENT_NS std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()
std::atomic<int> query_count(0);
std::atomic<int> success_count(0);
std::atomic<int> error_count(0);
std::atomic<int64_t> latency_sum(0);
volatile bool stop_flag = false;
int PARALLEL_NUMBER;
std::string request_msg;
template<class CLIENT>
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>(client);
//printf("echo done. seq_id=%d\n", ctx->get_task_seq());
});
}
template<class CLIENT>
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>(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 <IP> <PORT> <srpc|brpc|thrift> <pb|thrift> <PARALLEL_NUMBER> <REQUEST_BYTES>\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;
}

295
benchmark/client_cdf.cc Normal file
View File

@ -0,0 +1,295 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <thread>
#include <string>
#include <atomic>
#include <chrono>
#include <thread>
#include <vector>
#include <mutex>
#include "benchmark_pb.srpc.h"
#include "benchmark_thrift.srpc.h"
using namespace sogou;
#define TEST_SECOND 20
#define GET_CURRENT_MS std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()
#define GET_CURRENT_NS std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()
std::atomic<int> query_count(0);
std::atomic<int> slow_count(0);
std::atomic<int> success_count(0);
std::atomic<int> error_count(0);
//std::atomic<int64_t> latency_sum(0);
std::vector<std::vector<int64_t>> latency_lists;
volatile bool stop_flag = false;
int PARALLEL_NUMBER;
std::string request_msg;
int QPS;
template<class CLIENT>
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, [client, 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, [client, ns_st](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<class CLIENT>
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, [client, 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, [client, ns_st](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 <IP> <PORT> <srpc|brpc|thrift> <pb|thrift> <PARALLEL_NUMBER> <REQUEST_BYTES> <QPS>\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<std::thread *> 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<BenchmarkPB::SRPCClient>, 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<BenchmarkThrift::SRPCClient>, 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<BenchmarkPB::BRPCClient>, 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<BenchmarkThrift::ThriftClient>, 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<BenchmarkPB::SRPCHttpClient>, 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<BenchmarkThrift::SRPCHttpClient>, 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<BenchmarkThrift::ThriftHttpClient>, 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<int64_t> 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;
}

147
benchmark/server.cc Normal file
View File

@ -0,0 +1,147 @@
#include <stdio.h>
#include <signal.h>
#include "benchmark_pb.srpc.h"
#include "benchmark_thrift.srpc.h"
#ifndef _WIN32
#include <unistd.h>
#endif
using namespace sogou;
class BenchmarkPBServiceImpl : public BenchmarkPB::Service
{
public:
void echo_pb(FixLengthPBMsg *request, EmptyPBMsg *response, RPCContext *ctx) override { }
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 { }
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)
{
}
static void run_srpc_server(unsigned short port)
{
RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT;
params.max_connections = 2048;
SRPCServer server(&params);
BenchmarkPBServiceImpl pb_impl;
BenchmarkThriftServiceImpl thrift_impl;
server.add_service(&pb_impl);
server.add_service(&thrift_impl);
if (server.start(port) == 0)
{
#ifndef _WIN32
pause();
#else
getchar();
#endif
server.stop();
}
else
perror("server start");
}
template<class SERVER>
static void run_pb_server(unsigned short port)
{
RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT;
params.max_connections = 2048;
SERVER server(&params);
BenchmarkPBServiceImpl pb_impl;
server.add_service(&pb_impl);
if (server.start(port) == 0)
{
#ifndef _WIN32
pause();
#else
getchar();
#endif
server.stop();
}
else
perror("server start");
}
template<class SERVER>
static void run_thrift_server(unsigned short port)
{
RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT;
params.max_connections = 2048;
SERVER server(&params);
BenchmarkThriftServiceImpl thrift_impl;
server.add_service(&thrift_impl);
if (server.start(port) == 0)
{
#ifndef _WIN32
pause();
#else
getchar();
#endif
server.stop();
}
else
perror("server start");
}
int main(int argc, char* argv[])
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 3)
{
fprintf(stderr, "Usage: %s <PORT> <srpc|brpc|thrift>\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);
else if (server_type == "brpc")
run_pb_server<BRPCServer>(port);
else if (server_type == "thrift")
run_thrift_server<ThriftServer>(port);
else if (server_type == "srpc_http")
run_pb_server<SRPCHttpServer>(port);
else if (server_type == "thrift_http")
run_thrift_server<ThriftHttpServer>(port);
else
abort();
google::protobuf::ShutdownProtobufLibrary();
return 0;
}

21
benchmark/test.py Normal file
View File

@ -0,0 +1,21 @@
import 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")

View File

@ -0,0 +1,40 @@
#include "gen-cpp/BenchmarkThrift.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/server/TNonblockingServer.h>
#include <thrift/concurrency/ThreadManager.h>
#include <thrift/concurrency/PosixThreadFactory.h>
#include <thrift/transport/TBufferTransports.h>
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<BenchmarkThriftHandler> handler(new BenchmarkThriftHandler());
shared_ptr<TProcessor> processor(new BenchmarkThriftProcessor(handler));
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
boost::shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(16);
boost::shared_ptr<PosixThreadFactory> threadFactory = boost::shared_ptr<PosixThreadFactory>(new PosixThreadFactory());
TNonblockingServer server(processor, protocolFactory, port, threadManager);
server.setMaxConnections(2048);
server.setNumIOThreads(16);
threadManager->threadFactory(threadFactory);
threadManager->start();
server.serve();
return 0;
}

0
buildrpm.mk Normal file
View File

2
configure vendored Normal file
View File

@ -0,0 +1,2 @@
mkdir -p build
cd build && cmake3 .. || cmake ..

BIN
docs/images/benchmark1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
docs/images/benchmark2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/images/benchmark3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
docs/images/benchmark4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/images/benchmark5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/images/benchmark6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

394
docs/rpc.md Normal file
View File

@ -0,0 +1,394 @@
## 基础功能对比
|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| 支持 | 不支持 | 支持 | 支持 |
|SogouRPC Std | PB/Thrift | tcp | 二进制/JSON |gzip/zlib/lz4/snappy| 支持 | 支持 | 支持 | 不支持 |
|SogouRPC Std Http | PB/Thrift | http | 二进制/JSON |gzip/zlib/lz4/snappy| 支持 | 支持 | 支持 | 不支持 |
## 基础概念
- 通信层TCP/TPC_SSL/HTTP/HTTPS/HTTP2
- 协议层Thrift-binary/BRPC-std/SogouRPC-std
- 压缩层:不压缩/gzip/zlib/lz4/snappy
- 数据层PB binary/Thrift binary/Json string
- IDL序列化层PB/Thrift serialization
- RPC调用层Service/Client IMPL
## RPC Global
- 获取srpc版本号``sogou::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
- 组成sogouRPC服务的基本单元
- 每一个Service一定由某一种IDL生成
- Service只与IDL有关与网络通信具体协议无关
### 示例
下面我们通过一个具体例子来呈现
- 沿用上面的``example.proto``IDL描述文件
- 执行官方的``protoc example.proto --cpp_out=./ --proto_path=./``获得``example.pb.h``和``example.pb.cpp``两个文件
- 执行SogouRPC的``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、需要确定协议、监听端口号
- 然后我们可以创建任意个数的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、SogouRPC-STD、SogouRPC-Http
- 甚至我们可以将1个PB的``ExampleServiceImpl``和1个Thrift的``AnotherThriftServiceImpl````add_service``到同一个SogouRPC-STD Server两种IDL在同一个端口上完美工作
~~~cpp
int main()
{
SRPCServer server_srpc;
SRPCHttpServer server_srpc_http;
BRPCServer server_brpc;
ThriftServer server_thrift;
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_srpc.start(1412);
server_srpc_http.start(8811);
server_brpc.start(2020);
server_thrift.start(9090);
pause();
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用法请看下一个段落
~~~cpp
#include <stdio.h>
#include "example.srpc.h"
using namespace sogou;
int main()
{
Example::SRPCClient client("127.0.0.1", 1412);
EchoRequest req;
req.set_message("Hello, sogou rpc!");
req.set_name("Li Yingxin");
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());
});
pause();
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连接上的通信seqidseqid=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
### RPCContext API - Only for server process
#### ``void set_data_type(RPCDataType type);``
Server专用。设置数据打包类型
- RPCDataProtobuf
- RPCDataThrift
- RPCDataJson
#### ``void set_compress_type(RPCCompressType type);``
Server专用。设置数据压缩类型
- 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<void (RPCContext *ctx)> cb);``
Server专用。设置reply callback操作系统写入socket缓冲区成功后被调用。
#### ``void set_send_timeout(int timeout);``
Server专用。设置发送超时单位毫秒。-1代表无限。
#### ``void set_keep_alive(int timeout);``
Server专用。设置连接保活时间单位毫秒。-1代表无限。
## 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 | NaN | 发送写超时,默认无限 |
|keep_alive_timeout | NaN | 空闲连接保活,-1代表永远不断开默认0短连接 |
|retry_max | NaN | 最大重试次数默认0不重试 |
|compress_type | NaN | 压缩类型,默认不压缩 |
|data_type | NaN | 网络包数据类型默认与RPC默认值一致SRPC-Http协议为json其余为对应IDL的类型 |
## 与workflow异步框架的结合
### Server
下面我们通过一个具体例子来呈现
- Echo RPC在接收到请求时像下游发起一次http请求
- 对下游请求完成后我们将http response的body信息填充到response的message里回复给客户端
- 我们不希望阻塞/占据着Handler的线程所以对下游的请求一定是一次异步请求
- 首先我们通过Workflow框架的工厂``WFTaskFactory::create_http_task``创建一个异步任务http_task
- 然后我们利用RPCContext的``ctx->get_series()``获取到ServerTask所在的SeriesWork
- 最后我们使用SeiresWork的``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);
}
};
~~~
### 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, sogou rpc!");
req.set_name("1412");
rpc_task->serialize_input(&req);
((*http_task * rpc_task) > calc_task).start();
pause();
return 0;
}
~~~

28
docs/tutorial-01-idl.md Normal file
View File

@ -0,0 +1,28 @@
## 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);
};
~~~

View File

@ -0,0 +1,27 @@
## RPC Service
- 组成sogouRPC服务的基本单元
- 每一个Service一定由某一种IDL生成
- Service只与IDL有关与网络通信具体协议无关
### 示例
下面我们通过一个具体例子来呈现
- 沿用上面的``example.proto``IDL描述文件
- 执行官方的``protoc example.proto --cpp_out=./ --proto_path=./``获得``example.pb.h``和``example.pb.cpp``两个文件
- 执行SogouRPC的``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());
}
};
~~~

View File

@ -0,0 +1,50 @@
## 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、SogouRPC-STD、SogouRPC-Http
- 甚至我们可以将1个PB的``ExampleServiceImpl``和1个Thrift的``AnotherThriftServiceImpl````add_service``到同一个SogouRPC-STD Server两种IDL在同一个端口上完美工作
~~~cpp
int main()
{
SRPCServer server_srpc;
SRPCHttpServer server_srpc_http;
BRPCServer server_brpc;
ThriftServer server_thrift;
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_srpc.start(1412);
server_srpc_http.start(8811);
server_brpc.start(2020);
server_thrift.start(9090);
pause();
server_thrift.stop();
server_brpc.stop();
server_srpc_http.stop();
server_srpc.stop();
return 0;
}
~~~

View File

@ -0,0 +1,37 @@
## RPC Client
- 每一个Client对应着一个确定的目标/一个确定的集群
- 每一个Client对应着一个确定的网络通信协议
- 每一个Client对应着一个确定的IDL
### 示例
下面我们通过一个具体例子来呈现
- 沿用上面的例子client相对简单直接调用即可
- 通过``Example::XXXClient``创建某种RPC的client实例需要目标的ip+port或url
- 利用client实例直接调用rpc函数``Echo``即可,这是一次异步请求,请求完成后会进入回调函数
- 具体的RPC Context用法请看下一个段落
~~~cpp
#include <stdio.h>
#include "example.srpc.h"
using namespace sogou;
int main()
{
Example::SRPCClient client("127.0.0.1", 1412);
EchoRequest req;
req.set_message("Hello, sogou rpc!");
req.set_name("Li Yingxin");
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());
});
pause();
return 0;
}
~~~

View File

@ -0,0 +1,70 @@
## 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连接上的通信seqidseqid=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
### RPCContext API - Only for server process
#### ``void set_data_type(RPCDataType type);``
Server专用。设置数据打包类型
- RPCDataProtobuf
- RPCDataThrift
- RPCDataJson
#### ``void set_compress_type(RPCCompressType type);``
Server专用。设置数据压缩类型
- 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<void (RPCContext *ctx)> cb);``
Server专用。设置reply callback操作系统写入socket缓冲区成功后被调用。
#### ``void set_send_timeout(int timeout);``
Server专用。设置发送超时单位毫秒。-1代表无限。
#### ``void set_keep_alive(int timeout);``
Server专用。设置连接保活时间单位毫秒。-1代表无限。

View File

@ -0,0 +1,94 @@
## 与workflow异步框架的结合
### Server
下面我们通过一个具体例子来呈现
- Echo RPC在接收到请求时像下游发起一次http请求
- 对下游请求完成后我们将http response的body信息填充到response的message里回复给客户端
- 我们不希望阻塞/占据着Handler的线程所以对下游的请求一定是一次异步请求
- 首先我们通过Workflow框架的工厂``WFTaskFactory::create_http_task``创建一个异步任务http_task
- 然后我们利用RPCContext的``ctx->get_series()``获取到ServerTask所在的SeriesWork
- 最后我们使用SeiresWork的``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);
}
};
~~~
### 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, sogou rpc!");
req.set_name("1412");
rpc_task->serialize_input(&req);
((*http_task * rpc_task) > calc_task).start();
pause();
return 0;
}
~~~

15
example/GNUmakefile Normal file
View File

@ -0,0 +1,15 @@
workflow_inc = -I/search/data2/wujiaxu/sos/SubTask/_include
workflow_lib = -L/search/data2/wujiaxu/sos/SubTask/_lib
includes = -I../_include $(workflow_inc)
libs = -L../_lib $(workflow_lib)
all:
protoc echo.proto --cpp_out=./ --proto_path=./
../_bin/srpc_generator protobuf ./echo.proto ./
../_bin/srpc_generator thrift ./msg.thrift ./
g++ -o client client.cc echo.pb.cc -g -O2 -std=c++11 -fno-exceptions -lsrpc -ljemalloc $(includes) $(libs)
g++ -o server server.cc echo.pb.cc -g -O2 -std=c++11 -fno-exceptions -lsrpc -ljemalloc $(includes) $(libs)
clean:
rm -f client server

236
example/client.cc Normal file
View File

@ -0,0 +1,236 @@
#include <string>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <atomic>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include "echo.srpc.h"
#include "msg.srpc.h"
using namespace sogou;
using namespace example;
#define PARALLEL_NUM 100
#define GET_CURRENT_MS std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()
#define GET_CURRENT_NS std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()
std::atomic<int> query_count(0);
std::atomic<int> success_count(0);
std::atomic<int> error_count(0);
std::atomic<int64_t> latency_sum(0);
int64_t start, end;
volatile bool stop_flag = false;
EchoRequest g_echo_req;
ExampleThrift::MessageRequest g_msg_req;
template<class CLIENT>
static void do_echo(CLIENT *client)
{
EchoRequest req;
req.set_message("Hello, sogou rpc!");
req.set_name("Jeff Dean");
int64_t ns_st = GET_CURRENT_NS;
client->Echo(&req, [client, ns_st](EchoResponse *response, RPCContext *ctx) {
if (!stop_flag)
do_echo<CLIENT>(client);
++query_count;
if (ctx->success())
{
//printf("%s\n", response->message().c_str());
//printf("%s\n", ctx->get_remote_ip().c_str());
++success_count;
latency_sum += GET_CURRENT_NS - ns_st;
}
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());
});
}
template<class CLIENT>
static void do_msg(CLIENT *client)
{
ExampleThrift::MessageRequest req;
req.message = "Hello, sogou rpc!";
req.name = "Jeff Dean";
int64_t ns_st = GET_CURRENT_NS;
client->Message(&req, [client, ns_st](ExampleThrift::MessageResponse *response, RPCContext *ctx) {
if (!stop_flag)
do_msg<CLIENT>(client);
++query_count;
if (ctx->success())
{
//printf("%s\n", response->result.message.c_str());
//printf("%s\n", ctx->get_remote_ip().c_str());
++success_count;
latency_sum += GET_CURRENT_NS - ns_st;
}
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());
});
}
void sig_handler(int signo)
{
stop_flag = true;
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);
}
int main(int argc, char* argv[])
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc < 4 || argc > 5)
{
fprintf(stderr, "Usage: %s <srpc|brpc|thrift> <echo|msg> <IP> <PORT> or %s <srpc|brpc|thrift> <echo|msg> <URL>\n",
argv[0], argv[0]);
return 0;
}
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
WFGlobalSettings setting = GLOBAL_SETTINGS_DEFAULT;
setting.endpoint_params.max_connections = 1000;
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.task_params.data_type = RPCDataJson;
// client_params.task_params.compress_type = RPCCompressGzip;
// UpstreamManager::upstream_create_weighted_random("echo_server", true);
// UpstreamManager::upstream_add_server("echo_server", "sostest12.web.gd.ted:1412");
// UpstreamManager::upstream_add_server("echo_server", "sostest11.web.gd.ted:1412");
// UpstreamManager::upstream_add_server("echo_server", "sostest11.web.gd.ted:1411");
std::string server_type = argv[1];
std::string service_name = argv[2];
if (argc == 4)
{
// client_params.url = "http://echo_server";//for upstream
client_params.url = argv[3];
} else {
client_params.host = argv[3];
client_params.port = atoi(argv[4]);
}
start = GET_CURRENT_MS;
if (server_type == "srpc")
{
if (service_name == "echo")
{
ExamplePB::SRPCClient client(&client_params);
for (int i = 0; i < PARALLEL_NUM; i++)
do_echo(&client);
}
else if (service_name == "msg")
{
ExampleThrift::SRPCClient client(&client_params);
for (int i = 0; i < PARALLEL_NUM; i++)
do_msg(&client);
}
else
abort();
}
else if (server_type == "brpc")
{
ExamplePB::BRPCClient client(&client_params);
for (int i = 0; i < PARALLEL_NUM; i++)
{
if (service_name == "echo")
do_echo(&client);
else if (service_name == "msg")
abort();
else
abort();
}
}
else if (server_type == "thrift")
{
ExampleThrift::ThriftClient client(&client_params);
for (int i = 0; i < PARALLEL_NUM; i++)
{
if (service_name == "echo")
abort();
else if (service_name == "msg")
do_msg(&client);
else
abort();
}
}
else if (server_type == "srpc_http")
{
if (service_name == "echo")
{
ExamplePB::SRPCHttpClient client(&client_params);
for (int i = 0; i < PARALLEL_NUM; i++)
do_echo(&client);
}
else if (service_name == "msg")
{
ExampleThrift::SRPCHttpClient client(&client_params);
for (int i = 0; i < PARALLEL_NUM; i++)
do_msg(&client);
}
else
abort();
}
else if (server_type == "thrift_http")
{
ExampleThrift::ThriftHttpClient client(&client_params);
for (int i = 0; i < PARALLEL_NUM; i++)
{
if (service_name == "echo")
abort();
else if (service_name == "msg")
do_msg(&client);
else
abort();
}
}
else
abort();
pause();
sleep(2);
google::protobuf::ShutdownProtobufLibrary();
return 0;
}

23
example/echo.proto Normal file
View File

@ -0,0 +1,23 @@
syntax="proto2";
package example;
message EchoRequest
{
optional string message = 1;
optional string name = 2;
//repeated int32 arr = 3;
};
message EchoResponse
{
optional string message = 1;
optional int32 state = 2;
optional int32 error = 3;
//repeated int32 arr = 4;
};
service ExamplePB
{
rpc Echo(EchoRequest) returns (EchoResponse);
};

14
example/msg.thrift Normal file
View File

@ -0,0 +1,14 @@
namespace cpp example
struct MessageResult
{
1:required string message;
2:optional i32 state;
3:optional i32 error;
}
service ExampleThrift
{
MessageResult Message(1:string message, 2:string name);
}

140
example/server.cc Normal file
View File

@ -0,0 +1,140 @@
#include <string>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <vector>
#include <string>
#include "echo.srpc.h"
#include "msg.srpc.h"
using namespace sogou;
using namespace example;
class ExamplePBServiceImpl : public ExamplePB::Service
{
public:
void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override
{
// ctx->set_compress_type(RPCCompressGzip);
response->set_message("Hi back");
//response->set_state(1);
//response->set_error(2);
// printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n",
// request->DebugString().c_str(),
// response->DebugString().c_str());
}
};
class ExampleThriftServiceImpl : public ExampleThrift::Service
{
/*
public:
void Message(MessageResult& _return, const std::string& message, const std::string& name)
{
_return.message = "Hi back";
}*/
public:
void Message(ExampleThrift::MessageRequest *request, ExampleThrift::MessageResponse *response, RPCContext *ctx) override
{
//ctx->set_data_type(RPCDataJson);
response->result.message = "Hi back";
//response->result.__isset.arr = true;
//response->result.state = 1;
//response->result.__isset.state = true;
//response->result.error = 0;
//response->result.__isset.error = true;
// printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n",
// request->message.c_str(),
// response->result.message.c_str());
}
};
static void sig_handler(int signo)
{
}
template<class SERVER>
static void run_server(unsigned short port)
{
RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT;
params.max_connections = 2048;
SERVER server(&params);
ExamplePBServiceImpl pb_impl;
ExampleThriftServiceImpl thrift_impl;
server.add_service(&pb_impl);
server.add_service(&thrift_impl);
// some server options
if (server.start(port) == 0)
{
pause();
server.stop();
}
else
perror("server start");
}
template<class SERVER>
static void run_thrift_server(unsigned short port)
{
RPCServerParams params = RPC_SERVER_PARAMS_DEFAULT;
params.max_connections = 2048;
SERVER server(&params);
ExampleThriftServiceImpl thrift_impl;
server.add_service(&thrift_impl);
// some server options
if (server.start(port) == 0)
{
pause();
server.stop();
}
else
perror("server start");
}
int main(int argc, char* argv[])
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 3)
{
fprintf(stderr, "Usage: %s <PORT> <srpc|brpc|thrift>\n", argv[0]);
return 0;
}
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_server<SRPCServer>(port);
else if (server_type == "brpc")
run_server<BRPCServer>(port);
else if (server_type == "thrift")
run_thrift_server<ThriftServer>(port);
else if (server_type == "srpc_http")
run_server<SRPCHttpServer>(port);
else if (server_type == "thrift_http")
run_thrift_server<ThriftHttpServer>(port);
else
abort();
google::protobuf::ShutdownProtobufLibrary();
return 0;
}

95
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,95 @@
cmake_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 CONFIG REQUIRED)
else ()
find_package(Protobuf REQUIRED)
endif ()
find_package(Workflow REQUIRED CONFIG HINTS /usr/lib64/cmake/workflow)
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_global.cc
)
add_subdirectory(thrift)
add_subdirectory(compress)
add_subdirectory(message)
add_dependencies(thrift LINK_HEADERS)
add_dependencies(compress LINK_HEADERS)
add_dependencies(message LINK_HEADERS)
add_library(
${PROJECT_NAME} STATIC
${SRC}
$<TARGET_OBJECTS:thrift>
$<TARGET_OBJECTS:compress>
$<TARGET_OBJECTS:message>
)
add_dependencies(${PROJECT_NAME} LINK_HEADERS)
if (WIN32)
target_compile_definitions(
${PROJECT_NAME} PRIVATE
strdup=_strdup
strcasecmp=_stricmp
strncasecmp=_strnicmp
)
endif ()
install(
TARGETS ${PROJECT_NAME}
ARCHIVE
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT devel
)
if (APPLE)
set(LIBSO ${LIB_DIR}/libsrpc.a)
else ()
set(LIBSO ${LIB_DIR}/libsrpc.so)
add_custom_target(
SCRIPT_SHARED_LIB ALL
COMMAND ${CMAKE_COMMAND} -E echo 'GROUP ( libsrpc.a AS_NEEDED ( libz.so libworkflow.so libprotobuf.so ) ) ' > ${LIBSO}
)
add_dependencies(SCRIPT_SHARED_LIB ${PROJECT_NAME})
endif ()
install(
FILES ${LIBSO}
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT devel
)

View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.6)
project(compress)
set(SRC
rpc_compress.cc
rpc_compress_snappy.cc
)
add_library(${PROJECT_NAME} OBJECT ${SRC})

View File

@ -0,0 +1,82 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#include "rpc_compress.h"
#include "rpc_compress_gzip.h"
#include "rpc_compress_snappy.h"
#include "rpc_compress_lz4.h"
namespace sogou
{
int RPCCompresser::add(RPCCompressType type)
{
if (type >= SRPC_COMPRESS_TYPE_MAX || 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 sogou

238
src/compress/rpc_compress.h Normal file
View File

@ -0,0 +1,238 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_COMPRESS_H__
#define __RPC_COMPRESS_H__
#include "rpc_basic.h"
#include "rpc_options.h"
namespace sogou
{
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 RPCCompresser
{
public:
static RPCCompresser *get_instance()
{
static RPCCompresser 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:
RPCCompresser()
{
this->add(RPCCompressGzip);
this->add(RPCCompressZlib);
this->add(RPCCompressSnappy);
this->add(RPCCompressLz4);
}
CompressHandler handler[SRPC_COMPRESS_TYPE_MAX];
};
////////
// inl
inline int RPCCompresser::add_handler(int type, CompressHandler&& handler)
{
if (type >= SRPC_COMPRESS_TYPE_MAX || 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 *RPCCompresser::find_handler(int type) const
{
if (type >= SRPC_COMPRESS_TYPE_MAX || 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 RPCCompresser::parse_from_compressed(const char *buf, size_t buflen,
char *msg, size_t msglen,
int type) const
{
if (type >= SRPC_COMPRESS_TYPE_MAX
|| type <= RPCCompressNone
|| !this->handler[type].decompress)
{
return -2;
}
return this->handler[type].decompress(buf, buflen, msg, msglen);
}
inline int RPCCompresser::parse_from_compressed(RPCBuffer *src, RPCBuffer *dest,
int type) const
{
if (type >= SRPC_COMPRESS_TYPE_MAX
|| type <= RPCCompressNone
|| !this->handler[type].decompress_iovec)
{
return -2;
}
return this->handler[type].decompress_iovec(src, dest);
}
inline int RPCCompresser::serialize_to_compressed(const char *msg, size_t msglen,
char *buf, size_t buflen,
int type) const
{
if (type >= SRPC_COMPRESS_TYPE_MAX
|| type <= RPCCompressNone
|| !this->handler[type].compress)
{
return -2;
}
return this->handler[type].compress(msg, msglen, buf, buflen);
}
inline int RPCCompresser::serialize_to_compressed(RPCBuffer *src, RPCBuffer *dest,
int type) const
{
if (type >= SRPC_COMPRESS_TYPE_MAX
|| type <= RPCCompressNone
|| !this->handler[type].compress)
{
return -2;
}
return this->handler[type].compress_iovec(src, dest);
}
inline int RPCCompresser::lease_compressed_size(int type, size_t origin_size) const
{
if (type >= SRPC_COMPRESS_TYPE_MAX
|| type <= RPCCompressNone
|| !this->handler[type].lease_size)
{
return -2;
}
return this->handler[type].lease_size(origin_size);
}
inline void RPCCompresser::clear()
{
for (int i = 0; i < SRPC_COMPRESS_TYPE_MAX; i++)
{
//this->handler[i].type = RPCCompressNone;
this->handler[i].compress = nullptr;
this->handler[i].decompress = nullptr;
this->handler[i].lease_size = nullptr;
}
}
} // namespace sogou
#endif

View File

@ -0,0 +1,405 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_COMPRESS_GZIP_H__
#define __RPC_COMPRESS_GZIP_H__
//#include <google/protobuf/io/gzip_stream.h>
//#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <zlib.h>
#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 sogou
{
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->ByteSize();
}
*/
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<Bytef *>(const_cast<void *>(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<Bytef *>(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<Bytef *>(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<Bytef *>(const_cast<void *>(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<Bytef *>(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 sogou
#endif

View File

@ -0,0 +1,293 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_COMPRESS_LZ4_H__
#define __RPC_COMPRESS_LZ4_H__
#include <workflow/lz4.h>
#include <workflow/lz4frame.h>
#include "rpc_basic.h"
namespace sogou
{
//#define IN_CHUNK_SIZE (16*1024)
static const 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 sogou
#endif

View File

@ -0,0 +1,132 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#include <workflow/snappy.h>
#include <workflow/snappy-sinksource.h>
#include "rpc_compress_snappy.h"
namespace sogou
{
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 sogou

View File

@ -0,0 +1,67 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_COMPRESS_SNAPPY_H__
#define __RPC_COMPRESS_SNAPPY_H__
#include "rpc_basic.h"
namespace sogou
{
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 sogou
#endif

View File

@ -0,0 +1,27 @@
cmake_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
)

65
src/generator/compiler.cc Normal file
View File

@ -0,0 +1,65 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <stdio.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "generator.h"
int main(int argc, const char *argv[])
{
if (argc != 4)
{
fprintf(stderr, "Usage:\n\t%s protobuf proto_file output_dir\n"
"\t%s thrift thrift_file output_dir\n", argv[0], argv[0]);
return 0;
}
char *idl = const_cast<char *>(argv[1]);
bool is_thrift = (strcasecmp(idl, "thrift") == 0);
const char *idl_file = argv[2];
struct GeneratorParams params;
Generator gen(is_thrift);
// Generate(file, parameter, generator_context, error);
params.out_dir = argv[3];
char file_name[DIRECTORY_LENGTH] = { 0 };
size_t pos = 0;
if (idl_file[0] != '/' && idl_file[1] != ':')
{
getcwd(file_name, DIRECTORY_LENGTH);
pos = strlen(file_name) + 1;
file_name[strlen(file_name)] = '/';
}
if (strlen(idl_file) >= 2 && idl_file[0] == '.' && idl_file[1] == '/')
idl_file += 2;
snprintf(file_name + pos, DIRECTORY_LENGTH - pos, "%s", idl_file);
fprintf(stdout, "[Generator Begin]\n");
gen.generate(file_name, params);
fprintf(stdout, "[Generator Done]\n");
return 0;
}

192
src/generator/descriptor.h Normal file
View File

@ -0,0 +1,192 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_DESCRIPTOR_H__
#define __RPC_DESCRIPTOR_H__
#include <vector>
#include <string>
#include <set>
struct rpc_param
{
std::string type_name;
std::string var_name;
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<rpc_param> req_params;
std::vector<rpc_param> resp_params;
};
struct struct_descriptor
{
std::vector<rpc_param> params;
};
struct Descriptor
{
std::string block_name;
std::string block_type;
// serivce
std::vector<rpc_descriptor> rpcs;
// struct
struct_descriptor st;
// enum
std::vector<std::string> enum_lines;
};
//class ServiceDescrpitor : public Descriptor
//{};
struct idl_info
{
std::string file_name;
std::string absolute_file_path;
std::string file_name_prefix;
std::string package_name;
std::list<Descriptor> desc_list;
std::list<idl_info> include_list;
};
class SGenUtil
{
public:
static std::vector<std::string> split_filter_empty(const std::string& str, char sep)
{
std::vector<std::string> 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::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 std::set<std::string> *get_enum_set()
{
static std::set<std::string> kInstance;
return &kInstance;
}
};
#endif

419
src/generator/generator.cc Normal file
View File

@ -0,0 +1,419 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#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(const std::string& idl_file, struct GeneratorParams params)
{
if (this->parser.parse(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;
}
this->generate_skeleton(this->info.file_name);
return true;
}
bool Generator::generate_header(idl_info& cur_info, struct GeneratorParams params)
{
if (strlen(params.out_dir) + this->suffix.length() > 100)
{
fprintf(stderr, "[Generator Error] params out file name and dir are longer than 100.\n");
return false;
}
for (auto& sub_info : cur_info.include_list)
{
fprintf(stdout, "[Generator] auto generate include file [%s]\n",
sub_info.absolute_file_path.c_str());
this->generate_header(sub_info, params);
}
// 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] parse proto output dir 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)
this->generate_thrift_type_file(cur_info);//[prefix].thrift.h
for (const auto& desc : this->info.desc_list)
{
if (desc.block_type == "service")
{
//has_service = true;
this->generate_srpc_file(cur_info);// [prefix].srpc.h
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);
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");
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");
// 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<rpc_param>& params)
{
for (auto& param : params)
{
if (param.data_type == sogou::TDT_LIST
|| param.data_type == sogou::TDT_MAP
|| param.data_type == sogou::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, "::" + sub_info.package_name + "::");
}
}
}
}
void Generator::generate_thrift_type_file(idl_info& cur_info)
{
this->printer.open(this->thrift_type_file);
this->printer.print_thrift_include(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);
this->thrift_replace_include(cur_info, rpc.resp_params);
this->printer.print_rpc_thrift_struct_class(rpc.response_name, rpc.resp_params);
}
this->printer.print_service_namespace_end(desc.block_name);
}
else if (desc.block_type == "struct")
{
this->thrift_replace_include(cur_info, desc.st.params);
this->printer.print_rpc_thrift_struct_class(desc.block_name, desc.st.params);
}
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();
}
void Generator::generate_srpc_file(const idl_info& cur_info)
{
this->printer.open(this->srpc_file);
this->printer.print_srpc_include(this->prefix, cur_info.package_name);
std::vector<std::string> rpc_list;
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");
}
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(cur_info.package_name, desc.block_name, desc.rpcs);
this->printer.print_client_comment();
for (const auto& rpc : desc.rpcs)
{
this->printer.print_client_define_done(cur_info.package_name,
rpc.method_name,
rpc.response_name);
}
this->printer.print_empty_line();
for (const auto& type : rpc_list)
this->printer.print_client_class(type, cur_info.package_name, desc.block_name, desc.rpcs);
this->printer.print_implement_comments();
this->printer.print_server_constructor(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);
this->printer.print_client_methods(type, cur_info.package_name, desc.block_name, desc.rpcs);
this->printer.print_client_create_task(type, desc.block_name, desc.rpcs);
}
this->printer.print_service_namespace_end(desc.block_name);
}
this->printer.print_end(cur_info.package_name);
this->printer.close();
}
void Generator::generate_server_cpp_file(const idl_info& cur_info, const std::string& idl_file_name)
{
this->printer.open(this->server_cpp_file);
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();
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.close();
}
void Generator::generate_client_cpp_file(const idl_info& cur_info, const std::string& idl_file_name)
{
this->printer.open(this->client_cpp_file);
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();
for (const auto& desc : cur_info.desc_list)
{
if (desc.block_type != "service")
continue;
this->printer.print_client_main_service("SRPC", cur_info.package_name, desc.block_name);
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);
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();
}

83
src/generator/generator.h Normal file
View File

@ -0,0 +1,83 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_GENERATOR_H__
#define __RPC_GENERATOR_H__
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <map>
#include <list>
#include <string>
#include <algorithm>
#include "printer.h"
#include "parser.h"
struct GeneratorParams
{
const char *out_dir;
const char *out_file;
};
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(const std::string& idl_file, struct GeneratorParams params);
private:
bool generate_header(idl_info& cur_info, struct GeneratorParams params);
void generate_skeleton(const std::string& idl_file);
void generate_srpc_file(const idl_info& cur_info);
void generate_thrift_type_file(idl_info& cur_info);
void generate_server_cpp_file(const idl_info& cur_info, const std::string& idle_file_name);
void generate_client_cpp_file(const idl_info& cur_info, const std::string& idle_file_name);
void thrift_replace_include(const idl_info& cur_info, std::vector<rpc_param>& 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;
std::string server_cpp_file;
std::string client_cpp_file;
bool is_thrift;
};
#endif

856
src/generator/parser.cc Normal file
View File

@ -0,0 +1,856 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <ctype.h>
#include "parser.h"
#include "thrift/rpc_thrift_enum.h"
#define LINE_MAX 2048
bool Parser::parse(const std::string& proto_file, idl_info& info)
{
std::string dir_prefix;
auto pos = proto_file.find_last_of('/');
if (pos == std::string::npos)
info.file_name = proto_file;
else
{
info.file_name = proto_file.substr(pos + 1);
dir_prefix = proto_file.substr(0, 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 = proto_file;
FILE *in = fopen(proto_file.c_str(), "r");
if (!in)
{
fprintf(stderr, "[Parser] proto file: [%s] not exists.\n", proto_file.c_str());
return false;
}
fprintf(stdout, "proto file: [%s]\n", proto_file.c_str());
char line_buffer[LINE_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;
bool succ;
while (fgets(line_buffer, LINE_MAX, in))
{
std::string line = SGenUtil::lstrip(line_buffer);
if (line.empty())
continue;
this->check_single_comments(line);
if (line.empty())
continue;
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;
else
continue;
} else if (this->check_multi_comments_begin(line)) {
state = (state & PARSER_ST_OUTSIDE_COMMENT_MASK) + PARSER_ST_INSIDE_COMMENT;
}
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", proto_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 (file_path[0] != '/')
file_path = dir_prefix + file_path;
info.include_list.resize(info.include_list.size() + 1);
succ = this->parse(file_path, info.include_list.back());
if (!succ)
{
info.include_list.pop_back();
return false;
}
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) == 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)
// if (this->check_block_begin(line) == false)
block.append(line);
if (this->check_block_end(line) == true)
{
if (--stack_count == 0)
{
state = PARSER_ST_OUTSIDE_BLOCK;
//state &= PARSER_ST_OUTSIDE_BLOCK_MASK;
if (block_type == "service")
{
Descriptor desc;
if (this->is_thrift)
succ = this->parse_service_thrift(info.file_name_prefix, block, desc);
else
succ = this->parse_service_pb(block, desc);
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 == "struct" && this->is_thrift)
{
Descriptor desc;
succ = this->parse_struct_thrift(info.file_name_prefix, block, desc);
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 == "enum" && this->is_thrift)
{
Descriptor desc;
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));
}
}
}
}
}
fclose(in);
return true;
}
// 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_dir_prefix(const std::string& file_name, char *dir_prefix)
{
size_t pos = file_name.length() - 1;
while (file_name[pos] != '/' && pos != 0)
pos--;
if (pos == 0)
return false;
snprintf(dir_prefix, pos + 2, "%s", file_name.c_str());
// fprintf(stderr, "[%s]------------[%s]\n", file_name.c_str(), dir_prefix);
return true;
}
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;
/*
pos += include_prefix.length();
while (line[pos] == ' ')
pos++;
size_t begin = pos;
//TODO: special logic for thrift
while (pos < line.length() && !isspace(line[pos])
&& line[pos] != ';')
pos++;
if (line[begin] != '\"' || line[line.length() - 2] != ';'
|| line[line.length() - 3] != '\"')
return false;
file_name = line.substr(begin + 1, line.length() - 4 - begin);
return true;*/
}
bool Parser::parse_package_name(const std::string& line, std::string& 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++;
package_name = line.substr(begin, pos - begin);
return true;
}
static std::string gen_param_var(const std::string& type_name, size_t& cur)
{
size_t st = 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 (type_name[cur] == ',' || type_name[cur] == '>')
cur++;
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);
auto val_type = gen_param_var(type_name, ++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);
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);
return "std::vector<" + val_type + ">";
}
return idl_type;
}
static void fill_rpc_param_type(const std::string& file_name_prefix,
const std::string& idl_type, rpc_param& param)
{
if (idl_type == "bool")
{
param.data_type = sogou::TDT_BOOL;
param.type_name = "bool";
}
else if (idl_type == "i8" || idl_type == "byte")
{
param.data_type = sogou::TDT_I08;
param.type_name = "int8_t";
}
else if (idl_type == "i16")
{
param.data_type = sogou::TDT_I16;
param.type_name = "int16_t";
}
else if (idl_type == "i32")
{
param.data_type = sogou::TDT_I32;
param.type_name = "int32_t";
}
else if (idl_type == "i64")
{
param.data_type = sogou::TDT_I64;
param.type_name = "int64_t";
}
else if (idl_type == "u64")
{
param.data_type = sogou::TDT_U64;
param.type_name = "uint64_t";
}
else if (idl_type == "double")
{
param.data_type = sogou::TDT_DOUBLE;
param.type_name = "double";
}
else if (idl_type == "string" || idl_type == "binary")
{
param.data_type = sogou::TDT_STRING;
param.type_name = "std::string";
}
else if (SGenUtil::start_with(idl_type, "list"))
{
size_t cur = 0;
param.data_type = sogou::TDT_LIST;
param.type_name = gen_param_var(idl_type, cur);
}
else if (SGenUtil::start_with(idl_type, "map"))
{
size_t cur = 0;
param.data_type = sogou::TDT_MAP;
param.type_name = gen_param_var(idl_type, cur);
}
else if (SGenUtil::start_with(idl_type, "set"))
{
size_t cur = 0;
param.data_type = sogou::TDT_SET;
param.type_name = gen_param_var(idl_type, cur);
}
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 = sogou::TDT_I32;
param.type_name = "int32_t";
}
else
{
//struct
param.data_type = sogou::TDT_STRUCT;
param.type_name = idl_type;
}
}
}
bool Parser::parse_service_thrift(const std::string& file_name_prefix, const std::string& block, Descriptor& desc)
{
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 = SGenUtil::split_filter_empty(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_filter_empty(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 = sogou::THRIFT_STRUCT_FIELD_REQUIRED;
param.field_id = 0;
if (idl_type != "void")
{
fill_rpc_param_type(file_name_prefix, idl_type, param);
rpc_desc.resp_params.push_back(param);
}
if (left_b + 1 < line.size())
{
auto bb = SGenUtil::split_filter_empty(line.substr(left_b + 1), ',');
for (const auto& ele : bb)
{
auto filedparam = SGenUtil::split_filter_empty(ele, ':');
if (filedparam.size() != 2)
continue;
auto typevar = SGenUtil::split_filter_empty(filedparam[1], ' ');
if (typevar.size() != 2)
continue;
param.var_name = typevar[1];
param.required_state = sogou::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);
rpc_desc.req_params.push_back(param);
}
}
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);
}
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_filter_empty(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)
{
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);
int deep = 0;
for (size_t i = 0; i < valid_block.size(); i++)
{
if (valid_block[i] == '\n' || valid_block[i] == '\r')
valid_block[i] = ';';
else if (valid_block[i] == ',' && deep == 0)
valid_block[i] = ';';
else if (valid_block[i] == '<')
deep++;
else if (valid_block[i] == '>')
deep--;
}
auto arr = SGenUtil::split_filter_empty(valid_block, ';');
for (const auto& ele : arr)
{
auto line = SGenUtil::strip(ele);
if (line.empty())
continue;
auto aabb = SGenUtil::split_filter_empty(line, ':');
if (aabb.size() != 2)
continue;
auto aa = SGenUtil::strip(aabb[0]);
if (aa.empty())
continue;
param.field_id = atoi(aa.c_str());
auto bb = SGenUtil::strip(aabb[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 = sogou::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") ? sogou::THRIFT_STRUCT_FIELD_REQUIRED
: sogou::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);
fprintf(stdout, "Successfully parse struct param: %s %s\n",
param.type_name.c_str(), param.var_name.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]);
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)
{
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_MAX];
if (fgets(line_buffer, LINE_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_end(const std::string& line)
{
if (line.find("}") == std::string::npos
&& line.find("};") == std::string::npos)
return false;
return true;
}
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;
}

77
src/generator/parser.h Normal file
View File

@ -0,0 +1,77 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <map>
#include <list>
#include <string>
#include <algorithm>
#include <unordered_map>
#include "descriptor.h"
class Parser
{
public:
bool parse(const std::string& proto_file, idl_info& info);
int find_valid(const std::string& line);
bool check_block_begin(FILE *in, 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);
bool parse_service_pb(const std::string& block, Descriptor& desc);
bool parse_service_thrift(const std::string& file_name_prefix, const std::string& block, Descriptor& desc);
bool parse_struct_thrift(const std::string& file_name_prefix, const std::string& block, Descriptor& desc);
bool parse_enum_thrift(const std::string& block, Descriptor& desc);
bool parse_package_name(const std::string& line, std::string& 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);
bool parse_dir_prefix(const std::string& file_name, char *dir_prefix);
int parse_pb_rpc_option(const std::string& line);
Parser(bool is_thrift) { this->is_thrift = is_thrift; }
private:
bool is_thrift;
};
#endif

1504
src/generator/printer.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.6)
project(message)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(PROTO_LIST rpc_meta.proto rpc_meta_brpc.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_LIST})
set(SRC
rpc_message_brpc.cc
rpc_message_srpc.cc
rpc_message_thrift.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 ()

173
src/message/rpc_message.h Normal file
View File

@ -0,0 +1,173 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_MESSAGE_H__
#define __RPC_MESSAGE_H__
#include <string.h>
#include <string>
#include <workflow/ProtocolMessage.h>
#include "rpc_basic.h"
namespace sogou
{
static const char *srpc_error_string(int srpc_status_code);
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 void set_seqid(long long seqid) {}
};
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
{
return srpc_error_string(get_status_code());
}
virtual void set_status_code(int code) = 0;
virtual void set_error(int error) = 0;
};
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;
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;
}
};
static const char *srpc_error_string(int srpc_status_code)
{
switch (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";
}
}
} // namespace sogou
#endif

View File

@ -0,0 +1,456 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#include <errno.h>
#include <vector>
#include <string>
#include <workflow/HttpUtil.h>
#include <workflow/StringUtil.h>
#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 sogou
{
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<BrpcMeta *>(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->message->cut(this->message_len, this->attachment);
}
if (meta->has_request())
{
BrpcRequestMeta *meta_req = meta->mutable_request();
std::string::size_type pos = meta_req->service_name().find(".");
if (pos != std::string::npos)
meta_req->set_service_name(meta_req->service_name().c_str() + pos + 1);
}
return true;
}
return false;
}
bool BRPCResponse::deserialize_meta()
{
BrpcMeta *meta = static_cast<BrpcMeta *>(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->message->cut(this->message_len, this->attachment);
}
this->srpc_status_code = RPCStatusOK;
if (meta->has_response())
{
if (meta->mutable_response()->error_code() != 0)
{
this->srpc_status_code = RPCStatusMetaError;
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->ByteSize();
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<BrpcMeta *>(this->meta);
this->meta_len = meta->ByteSize();
this->meta_buf = new char[this->meta_len];
if (this->srpc_status_code != RPCStatusOK)
{
meta->mutable_response()->set_error_code(2001);
meta->mutable_response()->set_error_text(this->srpc_error_msg);
}
return meta->SerializeToArray(this->meta_buf, (int)this->meta_len);
}
int BRPCMessage::get_compress_type() const
{
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
return meta->compress_type();
}
void BRPCMessage::set_compress_type(int type)
{
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
meta->set_compress_type(type);
}
void BRPCMessage::set_attachment_nocopy(const char *attachment, size_t len)
{
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
this->attachment_len += len;
meta->set_attachment_size(this->attachment_len);
this->attachment->append(attachment, len, BUFFER_MODE_NOCOPY);
}
bool BRPCMessage::get_attachment(const char **attachment, size_t *len) const
{
size_t tmp_len = *len;
const void *tmp_buf;
if (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<BrpcMeta *>(this->meta);
return meta->mutable_request()->service_name();
}
const std::string& BRPCRequest::get_method_name() const
{
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
return meta->mutable_request()->method_name();
}
void BRPCRequest::set_service_name(const std::string& service_name)
{
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
meta->mutable_request()->set_service_name(service_name);
}
void BRPCRequest::set_method_name(const std::string& method_name)
{
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
meta->mutable_request()->set_method_name(method_name);
}
int64_t BRPCRequest::get_correlation_id() const
{
const BrpcMeta *meta = static_cast<const BrpcMeta *>(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 = srpc_error_string(code);
}
int BRPCResponse::get_error() const
{
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
return meta->mutable_response()->error_code();
}
const char *BRPCResponse::get_errmsg() const
{
return this->srpc_error_msg.c_str();
}
void BRPCResponse::set_error(int error)
{
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
meta->mutable_response()->set_error_code(error);
}
void BRPCResponse::set_correlation_id(int64_t cid)
{
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
meta->set_correlation_id(cid);
}
int BRPCMessage::serialize(const ProtobufIDLMessage *pb_msg)
{
if (!pb_msg)
return RPCStatusOK;
BrpcMeta *meta = static_cast<BrpcMeta *>(this->meta);
bool is_resp = !meta->has_request();
int msg_len = pb_msg->ByteSize();
RPCOutputStream stream(this->message, pb_msg->ByteSize());
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<const BrpcMeta *>(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<BrpcMeta *>(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 RPCCompresser *compresser = RPCCompresser::get_instance();
int ret = compresser->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 = compresser->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<const BrpcMeta *>(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 RPCCompresser *compresser = RPCCompresser::get_instance();
int ret = compresser->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;
}
} // namesapce sogou

View File

@ -0,0 +1,272 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_MESSAGE_BRPC_H__
#define __RPC_MESSAGE_BRPC_H__
#ifdef _WIN32
#include <workflow/PlatformSocket.h>
#else
#include <arpa/inet.h>
#endif
#include "rpc_message.h"
#include "rpc_basic.h"
namespace sogou
{
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(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 { }
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;
};
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 BaiduStdRequest : 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);
}
public:
BaiduStdRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; }
};
class BaiduStdResponse : 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);
}
public:
BaiduStdResponse() { 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 sogou
#endif

View File

@ -0,0 +1,885 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <errno.h>
#include <vector>
#include <string>
#include <google/protobuf/util/json_util.h>
#include <google/protobuf/util/type_resolver_util.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <workflow/HttpUtil.h>
#include <workflow/StringUtil.h>
#include "rpc_basic.h"
#include "rpc_compress.h"
#include "rpc_meta.pb.h"
#include "rpc_message_srpc.h"
#include "rpc_zero_copy_stream.h"
namespace sogou
{
struct SogouHttpHeadersString
{
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";
};
static const struct SogouHttpHeadersString SogouHttpHeaders;
static const std::map<const std::string, int> SogouHttpHeadersCode =
{
{SogouHttpHeaders.RPCCompressType, 1},
{SogouHttpHeaders.OriginSize, 2},
{SogouHttpHeaders.CompressdSize, 3},
{SogouHttpHeaders.DataType, 4},
{SogouHttpHeaders.SRPCStatus, 5},
{SogouHttpHeaders.SRPCError, 6}
};
static const std::vector<std::string> RPCDataTypeString =
{
"application/x-protobuf",
"application/x-thrift",
"application/json"
};
static const std::vector<std::string> RPCRPCCompressTypeString =
{
"identity",
"x-snappy",
"gzip",
"deflate",
"x-lz4"
};
static constexpr const char* kTypeUrlPrefix = "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(kTypeUrlPrefix, 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(kTypeUrlPrefix) + "/" + 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<RPCMeta *>(this->meta);
return meta->compress_type();
}
int SRPCMessage::get_data_type() const
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
return meta->data_type();
}
void SRPCMessage::set_compress_type(int type)
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
meta->set_compress_type(type);
}
void SRPCMessage::set_data_type(int type)
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
meta->set_data_type(type);
}
void SRPCMessage::set_attachment_nocopy(const char *attachment, size_t len)
{
}
bool SRPCMessage::get_attachment(const char **attachment, size_t *len) const
{
return false;
}
const std::string& SRPCRequest::get_service_name() const
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
return meta->mutable_request()->service_name();
}
const std::string& SRPCRequest::get_method_name() const
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
return meta->mutable_request()->method_name();
}
void SRPCRequest::set_service_name(const std::string& service_name)
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
meta->mutable_request()->set_service_name(service_name);
}
void SRPCRequest::set_method_name(const std::string& method_name)
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
meta->mutable_request()->set_method_name(method_name);
}
int SRPCResponse::get_status_code() const
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
return meta->mutable_response()->status_code();
}
void SRPCResponse::set_status_code(int code)
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
meta->mutable_response()->set_status_code(code);
}
int SRPCResponse::get_error() const
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
return meta->mutable_response()->error();
}
void SRPCResponse::set_error(int error)
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
meta->mutable_response()->set_error(error);
}
int SRPCMessage::serialize(const ProtobufIDLMessage *pb_msg)
{
RPCMeta *meta = static_cast<RPCMeta *>(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->ByteSize());
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();
google::protobuf::io::ArrayInputStream input_stream(binary_input.data(), (int)binary_input.size());
const auto* pool = pb_msg->GetDescriptor()->file()->pool();
auto* resolver = (pool == google::protobuf::DescriptorPool::generated_pool()
? ResolverInstance::get_resolver()
: google::protobuf::util::NewTypeResolverForDescriptorPool(kTypeUrlPrefix, pool));
ret = BinaryToJsonStream(resolver, GetTypeUrl(pb_msg), &input_stream, &output_stream).ok() ? 0 : -1;
if (pool != google::protobuf::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)
{
const RPCMeta *meta = static_cast<const RPCMeta *>(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;
google::protobuf::io::StringOutputStream output_stream(&binary_output);
const auto* pool = pb_msg->GetDescriptor()->file()->pool();
auto* resolver = (pool == google::protobuf::DescriptorPool::generated_pool()
? ResolverInstance::get_resolver()
: google::protobuf::util::NewTypeResolverForDescriptorPool(kTypeUrlPrefix, 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 != google::protobuf::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<RPCMeta *>(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<const RPCMeta *>(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<RPCMeta *>(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 RPCCompresser *compresser = RPCCompresser::get_instance();
int ret = compresser->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 = compresser->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<const RPCMeta *>(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 RPCCompresser *compresser = RPCCompresser::get_instance();
int ret = compresser->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;
}
bool SogouHttpRequest::serialize_meta()
{
if (this->buf->size() > 0x7FFFFFFF)
return false;
RPCMeta *meta = static_cast<RPCMeta *>(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(SogouHttpHeaders.DataType,
RPCDataTypeString[data_type]);
set_header_pair(SogouHttpHeaders.RPCCompressType,
RPCRPCCompressTypeString[compress_type]);
if (compress_type != RPCCompressNone)
{
set_header_pair(SogouHttpHeaders.CompressdSize,
std::to_string(meta->compressed_size()));
set_header_pair(SogouHttpHeaders.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 SogouHttpRequest::deserialize_meta()
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
protocol::HttpHeaderCursor cursor(this);
std::string key, value;
while (cursor.next(key, value))
{
const auto it = SogouHttpHeadersCode.find(key);
if (it != SogouHttpHeadersCode.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;
}
}
}
const char *uri = get_request_uri();
if (uri[0] == '/')
{
int pos1, pos2;
pos1 = pos2 = 0;
int i = 1;
while (uri[i])
{
if (pos1 == 0)
{
if (uri[i] == '/')
pos1 = i;
}
else if (uri[i] == '/' || uri[i] == '?' || uri[i] == '#')
{
pos2 = i;
break;
}
i++;
}
if (pos1)
{
if (!uri[i])
pos2 = i;
if (pos2)
{
meta->mutable_request()->set_service_name(uri + 1, pos1 - 1);
meta->mutable_request()->set_method_name(uri + pos1 + 1, pos2 - pos1 - 1);
}
}
}
const void *body;
size_t body_len;
get_parsed_body(&body, &body_len);
if (body_len > 0x7FFFFFFF)
return false;
this->SRPCRequest::buf->append((const char *)body, body_len, BUFFER_MODE_NOCOPY);
this->SRPCRequest::message_len = body_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 (body_len == 0 && meta->data_type() == RPCDataJson)
{
this->SRPCRequest::buf->append("{}", 2, BUFFER_MODE_NOCOPY);
this->SRPCRequest::message_len = 2;
}
}
else
meta->set_compressed_size(body_len);
return true;
}
bool SogouHttpResponse::serialize_meta()
{
if (this->buf->size() > 0x7FFFFFFF)
return false;
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
int data_type = meta->data_type();
int compress_type = meta->compress_type();
int rpc_status_code = this->get_status_code();
set_http_version("HTTP/1.1");
if (rpc_status_code == RPCStatusOK)
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(SogouHttpHeaders.SRPCStatus,
std::to_string(meta->mutable_response()->status_code()));
set_header_pair(SogouHttpHeaders.SRPCError,
std::to_string(meta->mutable_response()->error()));
set_header_pair(SogouHttpHeaders.DataType,
RPCDataTypeString[data_type]);
set_header_pair(SogouHttpHeaders.RPCCompressType,
RPCRPCCompressTypeString[compress_type]);
if (compress_type != RPCCompressNone)
{
set_header_pair(SogouHttpHeaders.CompressdSize,
std::to_string(meta->compressed_size()));
set_header_pair(SogouHttpHeaders.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 SogouHttpResponse::deserialize_meta()
{
RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
protocol::HttpHeaderCursor cursor(this);
std::string key, value;
while (cursor.next(key, value))
{
const auto it = SogouHttpHeadersCode.find(key);
if (it != SogouHttpHeadersCode.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;
case 5:
meta->mutable_response()->set_status_code(atoi(value.c_str()));
break;
case 6:
meta->mutable_response()->set_error(atoi(value.c_str()));
break;
default:
continue;
}
}
}
const void *body;
size_t body_len;
get_parsed_body(&body, &body_len);
if (body_len > 0x7FFFFFFF)
return false;
this->SRPCResponse::buf->append((const char *)body, body_len, BUFFER_MODE_NOCOPY);
this->SRPCResponse::message_len = body_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 (body_len == 0 && meta->data_type() == RPCDataJson)
{
this->SRPCResponse::buf->append("{}", 2, BUFFER_MODE_NOCOPY);
this->SRPCResponse::message_len = 2;
}
}
else
meta->set_compressed_size(body_len);
return true;
}
} // namespace sogou

View File

@ -0,0 +1,319 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_MESSAGE_SRPC_H__
#define __RPC_MESSAGE_SRPC_H__
#ifdef _WIN32
#include <workflow/PlatformSocket.h>
#else
#include <arpa/inet.h>
#endif
#include <workflow/HttpMessage.h>
#include "rpc_message.h"
#include "rpc_basic.h"
#include "rpc_thrift_idl.h"
#include "rpc_buffer.h"
namespace sogou
{
static constexpr int SRPC_HEADER_SIZE = 16;
// define sogou rpc 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(const char **attachment, size_t *len) const;
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;
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;
void set_status_code(int code);
void set_error(int error);
};
class SogouStdRequest : 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);
}
public:
SogouStdRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; }
};
class SogouStdResponse : 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();
}
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);
}
public:
SogouStdResponse() { this->size_limit = RPC_BODY_SIZE_LIMIT; }
};
class SogouHttpRequest : 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);
}
public:
SogouHttpRequest() { this->size_limit = RPC_BODY_SIZE_LIMIT; }
};
class SogouHttpResponse : 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();
}
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);
}
public:
SogouHttpResponse() { 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->ByteSize();
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 sogou
#endif

View File

@ -0,0 +1,250 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <workflow/HttpUtil.h>
#include "rpc_message_thrift.h"
namespace sogou
{
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)
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
{
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;
//memcpy(TBuffer->readbuf + TBuffer->readbuf_size, (const char *)buf, read_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;
}
}
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();
}
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_ = srpc_error_string(status_code_);
}
}
return true;
}
return false;
}
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();
set_http_version("HTTP/1.1");
if (rpc_status_code == RPCStatusOK)
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();
}
}

View File

@ -0,0 +1,386 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_MESSAGE_THRIFT_H__
#define __RPC_MESSAGE_THRIFT_H__
#include <string>
#include <workflow/HttpMessage.h>
#include "rpc_message.h"
#include "rpc_thrift_idl.h"
namespace sogou
{
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<ThriftException>::get_elements_instance();
this->descriptor = ThriftDescriptorImpl<ThriftException, TDT_STRUCT, void, void>::get_instance();
}
static void StaticElementsImpl(std::list<struct_element> *elements)
{
const ThriftException *st = (const ThriftException *)0;
const char *base = (const char *)st;
using subtype_1 = ThriftDescriptorImpl<std::string, TDT_STRING, void, void>;
using subtype_2 = ThriftDescriptorImpl<int32_t, TDT_I32, void, void>;
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(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; }
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();
public:
int get_status_code() const { return status_code_; }
int get_error() const { return error_; }
const char *get_errmsg() const { return errmsg_.c_str(); }
void set_status_code(int code)
{
status_code_ = code;
if (code != RPCStatusOK)
errmsg_ = srpc_error_string(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);
}
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);
}
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);
}
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);
}
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<char *>(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;
}
}
#endif

View File

@ -0,0 +1,22 @@
syntax="proto2";
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;
};

View File

@ -0,0 +1,143 @@
// 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;
optional int32 status_code = 3 [default = 0];
}
////// 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];
}
*/

95
src/rpc_basic.h Normal file
View File

@ -0,0 +1,95 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_BASIC_H__
#define __RPC_BASIC_H__
#include <google/protobuf/message.h>
#include "rpc_thrift_idl.h"
#include "rpc_buffer.h"
namespace sogou
{
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 int SRPC_COMPRESS_TYPE_MAX = 10;
static constexpr int INT_UNSET = 0x7FFFFFFF;
static constexpr size_t RPC_BODY_SIZE_LIMIT = 2LL * 1024 * 1024 * 1024;
using ProtobufIDLMessage = google::protobuf::Message;
enum RPCDataType
{
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
};
} // end namespace sogou
#endif

409
src/rpc_buffer.cc Normal file
View File

@ -0,0 +1,409 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <errno.h>
#include <stdlib.h>
#include "rpc_buffer.h"
namespace sogou
{
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 = fetch(&buf);
if (sz > 0)
{
bool keep_left = false;
void *left_buf;
if (last_piece_left_ > 0)
{
const auto it = buffer_list_.rbegin();
left_buf = (char *)it->buf + it->buflen;
}
out->last_piece_left_ = 0; // drop
if (sz < cur_.second)
{
out->append(buf, sz, BUFFER_MODE_COPY);
cutsize += sz;
cur_.first->buflen = cur_.second;
++cur_.first;
if (std::next(cur_.first, 1) == buffer_list_.end())
keep_left = true;
}
while (cur_.first != buffer_list_.end())
{
auto it = cur_.first;
if (it->is_nocopy)
out->append(it->buf, it->buflen, BUFFER_MODE_NOCOPY);
else
out->append(it->buf, it->buflen,
it->is_new ? BUFFER_MODE_GIFT_NEW
: BUFFER_MODE_GIFT_MALLOC);
cutsize += it->buflen;
++cur_.first;
buffer_list_.erase(it);
}
if (last_piece_left_ > 0)
{
if (keep_left)
{
buffer_t ele;
ele.buflen = 0;
ele.buf = left_buf;
ele.is_nocopy = true;
ele.is_new = false;
buffer_list_.emplace_back(std::move(ele));
}
else
{
out->append(left_buf, last_piece_left_, BUFFER_MODE_NOCOPY);
out->backup(last_piece_left_);
}
}
}
rewind();
return cutsize;
}
} // namespace sogou

390
src/rpc_buffer.h Normal file
View File

@ -0,0 +1,390 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_BUFFER_H__
#define __RPC_BUFFER_H__
#ifdef _WIN32
#include <workflow/PlatformSocket.h>
#else
#include <sys/uio.h>
#endif
#include <stddef.h>
#include <string.h>
#include <list>
namespace sogou
{
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;
/**
* @brief Cut current buffer at absolutely offset, current buffer keep first part, give second part to 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_t> buffer_list_;
std::pair<std::list<buffer_t>::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;
};
////////
// inl
inline size_t RPCBuffer::size() const { return size_; }
inline 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);
}
}
}
inline 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;
}
inline 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;
}
inline bool RPCBuffer::append(const void *buf, size_t buflen, int mode)
{
if (mode == BUFFER_MODE_COPY)
return write(buf, buflen);
return append(const_cast<void *>(buf), buflen, mode);
}
inline 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;
}
inline 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;
}
inline 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;
}
inline void RPCBuffer::rewind()
{
cur_.first = buffer_list_.begin();
cur_.second = 0;
init_read_over_ = true;
}
inline long RPCBuffer::seek(long offset)
{
if (offset > 0)
return read_skip(offset);
else if (offset < 0)
return read_back(offset);
return 0;
}
inline size_t RPCBuffer::peek(const void **buf)
{
return internal_fetch(buf, BUFFER_FETCH_STAY);
}
inline size_t RPCBuffer::fetch(const void **buf)
{
return internal_fetch(buf, BUFFER_FETCH_MOVE);
}
inline RPCBuffer::~RPCBuffer()
{
clear_list_buffer();
}
} // namespace sogou
#endif

196
src/rpc_client.h Normal file
View File

@ -0,0 +1,196 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_CLIENT_H__
#define __RPC_CLIENT_H__
#include "rpc_types.h"
#include "rpc_context.h"
#include "rpc_options.h"
#include "rpc_global.h"
namespace sogou
{
template<class RPCTYPE>
class RPCClient
{
public:
using TASK = RPCClientTask<typename RPCTYPE::REQ, typename RPCTYPE::RESP>;
protected:
using COMPLEXTASK = WFComplexClientTask<typename RPCTYPE::REQ, typename RPCTYPE::RESP>;
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);
protected:
template<class OUTPUT>
TASK *create_rpc_client_task(const std::string& method_name,
std::function<void (OUTPUT *, RPCContext *)>&& done)
{
auto *task = new TASK(this->service_name, method_name, &this->params.task_params,
[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;
RPCClientParams params;
ParsedURI uri;
struct sockaddr_storage ss;
socklen_t ss_len;
bool has_addr_info;
};
////////
// inl
template<class RPCTYPE>
inline RPCClient<RPCTYPE>::RPCClient(const std::string& service_name):
params(RPC_CLIENT_PARAMS_DEFAULT),
has_addr_info(false)
{
SRPCGlobal::get_instance();
this->service_name = service_name;
}
template<class RPCTYPE>
inline const RPCTaskParams *RPCClient<RPCTYPE>::get_task_params() const
{
return &this->params.task_params;
}
template<class RPCTYPE>
inline const std::string& RPCClient<RPCTYPE>::get_service_name() const
{
return this->service_name;
}
template<class RPCTYPE>
inline void RPCClient<RPCTYPE>::set_keep_alive(int timeout)
{
this->params.task_params.keep_alive_timeout = timeout;
}
template<class RPCTYPE>
inline void RPCClient<RPCTYPE>::init(const RPCClientParams *params)
{
this->params = *params;
if (this->params.task_params.data_type == INT_UNSET)
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<class RPCTYPE>
inline void RPCClient<RPCTYPE>::__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_type(this->params.is_ssl ? TT_TCP_SSL : TT_TCP);
}
}
template<class RPCTYPE>
inline void RPCClient<RPCTYPE>::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<RPCTYPESRPCHttp>::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<RPCTYPEThriftHttp>::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 sogou
#endif

92
src/rpc_context.h Normal file
View File

@ -0,0 +1,92 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_CONTEXT_H__
#define __RPC_CONTEXT_H__
#ifdef _WIN32
#include <workflow/PlatformSocket.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#endif
#include <string>
#include <functional>
#include <workflow/Workflow.h>
#include "rpc_basic.h"
namespace sogou
{
struct RPCSyncContext
{
long long seqid;
std::string errmsg;
std::string remote_ip;
int status_code;
int error;
bool success;
};
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;
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;
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<void (RPCContext *ctx)> cb) = 0;
virtual void set_send_timeout(int timeout) = 0;
virtual void set_keep_alive(int timeout) = 0;
//virtual void noreply();
//virtual WFConnection *get_connection();
public:
virtual ~RPCContext() { }
};
} // namespace sogou
////////
// inl
#include "rpc_context.inl"
#include "rpc_task.inl"
#endif

186
src/rpc_context.inl Normal file
View File

@ -0,0 +1,186 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include "rpc_message.h"
#include <workflow/WFTask.h>
#include <mutex>
#include <condition_variable>
namespace sogou
{
template<class T>
struct ThriftReceiver
{
std::mutex mutex;
std::condition_variable cond;
RPCSyncContext ctx;
T output;
bool is_done = false;
};
template<class RPCREQ, class RPCRESP>
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, addrlen);
}
else if (addr.ss_family == AF_INET6)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(&addr);
inet_ntop(AF_INET6, &sin6->sin6_addr, ip_str, addrlen);
}
}
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(attachment, len);
else
return task_->get_resp()->get_attachment(attachment, len);
}
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;
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<void (RPCContext *ctx)> cb) override
{
if (this->is_server_task())
{
if (cb)
{
task_->set_callback([this, cb](SubTask *task) {
cb(this);
});
}
else
task_->set_callback(nullptr);
}
}
//void noreply() override;
//WFConnection *get_connection() override;
RPCContextImpl(WFNetworkTask<RPCREQ, RPCRESP> *task) : task_(task) { }
protected:
bool is_server_task() const
{
return task_->get_state() == WFT_STATE_TOREPLY
|| task_->get_state() == WFT_STATE_NOREPLY;
}
private:
WFNetworkTask<RPCREQ, RPCRESP> *task_;
};
} // namespace sogou

123
src/rpc_global.cc Normal file
View File

@ -0,0 +1,123 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifdef _WIN32
#include <workflow/PlatformSocket.h>
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include <workflow/WFGlobal.h>
#include <workflow/URIParser.h>
#include "rpc_basic.h"
#include "rpc_global.h"
namespace sogou
{
const char *SRPC_VERSION = "SRPC Library Version: 1.0.3";
const char *SRPCGlobal::get_srpc_version() const { return SRPC_VERSION; }
SRPCGlobal::SRPCGlobal()
{
WFGlobal::register_scheme_port(SRPC_SCHEME, SRPC_DEFAULT_PORT);
WFGlobal::register_scheme_port(SRPC_SSL_SCHEME, SRPC_SSL_DEFAULT_PORT);
}
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;
}
static const SRPCGlobal *srpc_global = SRPCGlobal::get_instance();
} // namespace sogou

52
src/rpc_global.h Normal file
View File

@ -0,0 +1,52 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_GLOBAL_H__
#define __RPC_GLOBAL_H__
#include <workflow/URIParser.h>
#include "rpc_options.h"
namespace sogou
{
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;
private:
SRPCGlobal();
};
} // namespace sogou
#endif

83
src/rpc_options.h Normal file
View File

@ -0,0 +1,83 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_OPTIONS_H__
#define __RPC_OPTIONS_H__
#include <workflow/WFServer.h>
#include <workflow/URIParser.h>
#include "rpc_message.h"
#include "rpc_basic.h"
namespace sogou {
struct RPCTaskParams
{
int send_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;
};
using RPCServerParams = WFServerParams;
static constexpr RPCTaskParams RPC_TASK_PARAMS_DEFAULT =
{
/* .send_timeout = */ INT_UNSET,
/* .keep_alive_timeout = */ INT_UNSET,
/* .retry_max = */ INT_UNSET,
/* .compress_type = */ INT_UNSET,
/* .data_type = */ INT_UNSET
};
static const RPCClientParams RPC_CLIENT_PARAMS_DEFAULT =
{
/* .task_params = */ RPC_TASK_PARAMS_DEFAULT,
/* .host = */ "",
/* .port = */ SRPC_DEFAULT_PORT,
/* .is_ssl = */ false,
/* .url = */ ""
};
static constexpr RPCServerParams RPC_SERVER_PARAMS_DEFAULT =
{
/* .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
};
} // end namespace
#endif

168
src/rpc_server.h Normal file
View File

@ -0,0 +1,168 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_SERVER_H__
#define __RPC_SERVER_H__
#include <string>
#include <map>
#include <errno.h>
#include <workflow/WFServer.h>
#include <workflow/WFHttpServer.h>
#include "rpc_types.h"
#include "rpc_service.h"
#include "rpc_options.h"
namespace sogou
{
template<class RPCTYPE>
class RPCServer : public WFServer<typename RPCTYPE::REQ, typename RPCTYPE::RESP>
{
public:
using TASK = RPCServerTask<typename RPCTYPE::REQ, typename RPCTYPE::RESP>;
protected:
using NETWORKTASK = WFNetworkTask<typename RPCTYPE::REQ, typename RPCTYPE::RESP>;
public:
RPCServer();
RPCServer(const struct WFServerParams *params);
int add_service(RPCService *service);
const RPCService* find_service(const std::string& name) const;
protected:
RPCServer(const struct WFServerParams *params, std::function<void (NETWORKTASK *)>&& process);
CommSession *new_session(long long seq, CommConnection *conn) override;
void server_process(NETWORKTASK *task) const;
private:
std::map<std::string, RPCService *> service_map;
};
////////
// inl
template<class RPCTYPE>
inline RPCServer<RPCTYPE>::RPCServer():
WFServer<typename RPCTYPE::REQ, typename RPCTYPE::RESP>(&RPC_SERVER_PARAMS_DEFAULT, std::bind(&RPCServer::server_process, this, std::placeholders::_1))
{}
template<class RPCTYPE>
inline RPCServer<RPCTYPE>::RPCServer(const struct WFServerParams *params):
WFServer<typename RPCTYPE::REQ, typename RPCTYPE::RESP>(params, std::bind(&RPCServer::server_process, this, std::placeholders::_1))
{}
template<class RPCTYPE>
inline RPCServer<RPCTYPE>::RPCServer(const struct WFServerParams *params, std::function<void (NETWORKTASK *)>&& process):
WFServer<typename RPCTYPE::REQ, typename RPCTYPE::RESP>(params, std::move(process))
{}
template<class RPCTYPE>
inline int RPCServer<RPCTYPE>::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<class RPCTYPE>
inline const RPCService *RPCServer<RPCTYPE>::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<class RPCTYPE>
inline CommSession *RPCServer<RPCTYPE>::new_session(long long seq, CommConnection *conn)
{
auto *task = new TASK(this->process);
task->set_keep_alive(this->params.keep_alive_timeout);
task->get_req()->set_size_limit(this->params.request_size_limit);
return task;
}
template<class RPCTYPE>
void RPCServer<RPCTYPE>::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
{
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
{
status_code = req->decompress();
if (status_code == RPCStatusOK)
status_code = (*rpc)(static_cast<TASK *>(task)->worker);
}
}
}
resp->set_status_code(status_code);
}
template<>
inline const RPCService *RPCServer<RPCTYPEThrift>::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<RPCTYPEThriftHttp>::find_service(const std::string& name) const
{
if (this->service_map.empty())
return NULL;
return this->service_map.cbegin()->second;
}
} // namespace sogou
#endif

99
src/rpc_service.h Normal file
View File

@ -0,0 +1,99 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_SERVICE_H__
#define __RPC_SERVICE_H__
#include <string>
#include <unordered_map>
#include <functional>
#include "rpc_context.h"
#include "rpc_options.h"
namespace sogou
{
class RPCService
{
protected:
using rpc_method_t = std::function<int (RPCWorker&)>;
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<std::string, rpc_method_t> methods_;
std::string name_;
};
////////
// inl
template<class INPUT, class OUTPUT, class SERVICE>
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 sogou
#endif

447
src/rpc_task.inl Normal file
View File

@ -0,0 +1,447 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <string>
#include <functional>
#include <workflow/WFGlobal.h>
#include <workflow/WFTask.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFuture.h>
#include "rpc_basic.h"
#include "rpc_message.h"
#include "rpc_options.h"
namespace sogou
{
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 RPCREQ, class RPCRESP>
class RPCClientTask : public WFComplexClientTask<RPCREQ, RPCRESP>
{
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 serialize_input(const ProtobufIDLMessage *in);
int serialize_input(const ThriftIDLMessage *in);
protected:
using user_done_t = std::function<int (int, RPCWorker&)>;
using WFComplexClientTask<RPCREQ, RPCRESP>::get_req;
using WFComplexClientTask<RPCREQ, RPCRESP>::get_resp;
using WFComplexClientTask<RPCREQ, RPCRESP>::set_callback;
void init_failed() override;
bool check_request() override;
CommMessageOut *message_out() override;
bool finish_once() override;
void rpc_callback(WFNetworkTask<RPCREQ, RPCRESP> *task);
public:
RPCClientTask(const std::string& service_name,
const std::string& method_name,
const RPCTaskParams *params,
user_done_t&& user_done);
private:
template<class IDL>
int __serialize_input(const IDL *in);
user_done_t user_done_;
bool init_failed_;
};
template<class RPCREQ, class RPCRESP>
class RPCServerTask : public WFServerTask<RPCREQ, RPCRESP>
{
public:
RPCServerTask(std::function<void (WFNetworkTask<RPCREQ, RPCRESP> *)>& process) :
WFServerTask<RPCREQ, RPCRESP>(WFGlobal::get_scheduler(), process),
worker(new RPCContextImpl<RPCREQ, RPCRESP>(this), &this->req, &this->resp)
{}
RPCWorker worker;
protected:
CommMessageOut *message_out() override;
};
template<class OUTPUT>
static void RPCAsyncFutureCallback(OUTPUT *output, sogou::RPCContext *ctx)
{
using RESULT = std::pair<OUTPUT, sogou::RPCSyncContext>;
auto *pr = static_cast<WFPromise<RESULT> *>(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();
if (res.second.success)
res.first = std::move(*output);
pr->set_value(std::move(res));
delete pr;
}
template<class OUTPUT>
static void ThriftSendCallback(OUTPUT *output, sogou::RPCContext *ctx)
{
auto *receiver = static_cast<ThriftReceiver<OUTPUT> *>(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<class OUTPUT>
static inline int
ClientRPCDoneImpl(int status_code,
RPCWorker& worker,
const std::function<void (OUTPUT *, RPCContext *)>& 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<class RPCREQ, class RPCRESP>
CommMessageOut *RPCServerTask<RPCREQ, RPCRESP>::message_out()
{
int status_code = this->worker.server_serialize();
if (status_code == RPCStatusOK)
{
status_code = this->resp.compress();
if (status_code == RPCStatusOK)
{
if (this->resp.serialize_meta())
return this->WFServerTask<RPCREQ, RPCRESP>::message_out();
status_code = RPCStatusMetaError;
}
}
this->resp.set_status_code(status_code);
errno = EBADMSG;
return NULL;
}
template<class RPCREQ, class RPCRESP>
inline void RPCClientTask<RPCREQ, RPCRESP>::set_data_type(RPCDataType type)
{
this->req.set_data_type(type);
}
template<class RPCREQ, class RPCRESP>
inline void RPCClientTask<RPCREQ, RPCRESP>::set_compress_type(RPCCompressType type)
{
this->req.set_compress_type(type);
}
template<class RPCREQ, class RPCRESP>
inline void RPCClientTask<RPCREQ, RPCRESP>::set_attachment_nocopy(const char *attachment, size_t len)
{
this->req.set_attachment_nocopy(attachment, len);
}
template<class RPCREQ, class RPCRESP>
inline void RPCClientTask<RPCREQ, RPCRESP>::set_retry_max(int retry_max)
{
this->retry_max_ = retry_max;
}
template<class RPCREQ, class RPCRESP>
inline int RPCClientTask<RPCREQ, RPCRESP>::serialize_input(const ProtobufIDLMessage *in)
{
return __serialize_input<ProtobufIDLMessage>(in);
}
template<class RPCREQ, class RPCRESP>
inline int RPCClientTask<RPCREQ, RPCRESP>::serialize_input(const ThriftIDLMessage *in)
{
return __serialize_input<ThriftIDLMessage>(in);
}
template<class RPCREQ, class RPCRESP>
template<class IDL>
inline int RPCClientTask<RPCREQ, RPCRESP>::__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<class RPCREQ, class RPCRESP>
inline RPCClientTask<RPCREQ, RPCRESP>::RPCClientTask(
const std::string& service_name,
const std::string& method_name,
const RPCTaskParams *params,
user_done_t&& user_done):
WFComplexClientTask<RPCREQ, RPCRESP>(0, nullptr),
user_done_(std::move(user_done)),
init_failed_(false)
{
if (user_done_)
this->set_callback(std::bind(&RPCClientTask::rpc_callback, this, std::placeholders::_1));
if (params->send_timeout != INT_UNSET)
this->set_send_timeout(params->send_timeout);
if (params->keep_alive_timeout != INT_UNSET)
this->set_keep_alive(params->keep_alive_timeout);
if (params->retry_max != INT_UNSET)
this->set_retry_max(params->retry_max);
if (params->compress_type != INT_UNSET)
this->req.set_compress_type(params->compress_type);
if (params->data_type != INT_UNSET)
this->req.set_data_type(params->data_type);
this->req.set_service_name(service_name);
this->req.set_method_name(method_name);
}
template<class RPCREQ, class RPCRESP>
void RPCClientTask<RPCREQ, RPCRESP>::init_failed()
{
init_failed_ = true;
}
template<class RPCREQ, class RPCRESP>
bool RPCClientTask<RPCREQ, RPCRESP>::check_request()
{
int status_code = this->resp.get_status_code();
return status_code == RPCStatusOK || status_code == RPCStatusUndefined;
}
template<class RPCREQ, class RPCRESP>
CommMessageOut *RPCClientTask<RPCREQ, RPCRESP>::message_out()
{
this->req.set_seqid(this->get_task_seq());
int status_code = this->req.compress();
if (status_code == RPCStatusOK)
{
if (this->req.serialize_meta())
return this->WFClientTask<RPCREQ, RPCRESP>::message_out();
status_code = RPCStatusMetaError;
}
this->disable_retry();
this->resp.set_status_code(status_code);
errno = EBADMSG;
return NULL;
}
template<class RPCREQ, class RPCRESP>
bool RPCClientTask<RPCREQ, RPCRESP>::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<class RPCREQ, class RPCRESP>
void RPCClientTask<RPCREQ, RPCRESP>::rpc_callback(WFNetworkTask<RPCREQ, RPCRESP> *task)
{
RPCWorker worker(new RPCContextImpl<RPCREQ, RPCRESP>(this), &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)
return;
}
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
{
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);
user_done_(status_code, worker);
}
} // namespace sogou

113
src/rpc_types.h Normal file
View File

@ -0,0 +1,113 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#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"
namespace sogou
{
struct RPCTYPESRPC
{
using REQ = SogouStdRequest;
using RESP = SogouStdResponse;
static const 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 = SogouHttpRequest;
using RESP = SogouHttpResponse;
static const 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 const RPCDataType default_data_type = RPCDataProtobuf;
};
struct RPCTYPEBRPC
{
using REQ = BaiduStdRequest;
using RESP = BaiduStdResponse;
static const 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 const 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 const 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;
}
};
} // end namespace sogou
#endif

130
src/rpc_zero_copy_stream.h Normal file
View File

@ -0,0 +1,130 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_ZERO_COPY_STREAM_H__
#define __RPC_ZERO_COPY_STREAM_H__
#include <google/protobuf/io/zero_copy_stream.h>
namespace sogou
{
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 sogou
#endif

58
src/sogou_rpc.h Normal file
View File

@ -0,0 +1,58 @@
/*
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.
Authors: Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __SOGOU_RPC_H__
#define __SOGOU_RPC_H__
#if __cplusplus < 201100
#error CPLUSPLUS VERSION required at least C++11. Please use "-std=c++11".
#include <C++11_REQUIRED>
#endif
#include "rpc_basic.h"
#include "rpc_types.h"
#include "rpc_server.h"
#include "rpc_client.h"
namespace sogou
{
using SRPCServer = RPCServer<RPCTYPESRPC>;
using SRPCClient = RPCClient<RPCTYPESRPC>;
using SRPCClientTask = SRPCClient::TASK;
using SRPCHttpServer = RPCServer<RPCTYPESRPCHttp>;
using SRPCHttpClient = RPCClient<RPCTYPESRPCHttp>;
using SRPCHttpClientTask = SRPCHttpClient::TASK;
using BRPCServer = RPCServer<RPCTYPEBRPC>;
using BRPCClient = RPCClient<RPCTYPEBRPC>;
using BRPCClientTask = BRPCClient::TASK;
using ThriftServer = RPCServer<RPCTYPEThrift>;
using ThriftClient = RPCClient<RPCTYPEThrift>;
using ThriftClientTask = ThriftClient::TASK;
using ThriftHttpServer = RPCServer<RPCTYPEThriftHttp>;
using ThriftHttpClient = RPCClient<RPCTYPEThriftHttp>;
using ThriftHttpClientTask = ThriftHttpClient::TASK;
} // namespace sogou
#endif

10
src/thrift/CMakeLists.txt Normal file
View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.6)
project(thrift)
set(SRC
rpc_thrift_buffer.cc
rpc_thrift_idl.cc
)
add_library(${PROJECT_NAME} OBJECT ${SRC})

View File

@ -0,0 +1,196 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include "rpc_thrift_buffer.h"
namespace sogou
{
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 sogou

View File

@ -0,0 +1,283 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_THRIFT_BUFFER_H__
#define __RPC_THRIFT_BUFFER_H__
#ifdef _WIN32
#include <workflow/PlatformSocket.h>
#else
#include <arpa/inet.h>
#ifndef htonll
# define htonll(x) ((((uint64_t)htonl((x) & 0xFFFFFFFF)) << 32) + htonl((x) >> 32))
# define ntohll(x) ((((uint64_t)ntohl((x) & 0xFFFFFFFF)) << 32) + ntohl((x) >> 32))
#endif
#endif
#include <stdint.h>
#include <string.h>
#include <string>
#include "rpc_thrift_enum.h"
#include "rpc_buffer.h"
namespace sogou
{
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);
};
////////
// inl
inline bool ThriftBuffer::readI08(int8_t& val)
{
return this->buffer->read((char *)&val, 1);
}
inline bool ThriftBuffer::readI16(int16_t& val)
{
if (!this->buffer->read((char *)&val, 2))
return false;
val = ntohs(val);
return true;
}
inline bool ThriftBuffer::readI32(int32_t& val)
{
if (!this->buffer->read((char *)&val, 4))
return false;
val = ntohl(val);
return true;
}
inline bool ThriftBuffer::readI64(int64_t& val)
{
if (!this->buffer->read((char *)&val, 8))
return false;
val = ntohll(val);
return true;
}
inline bool ThriftBuffer::readU64(uint64_t& val)
{
if (!this->buffer->read((char *)&val, 8))
return false;
val = ntohll(val);
return true;
}
inline 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;
}
inline bool ThriftBuffer::readString(std::string& str)
{
int32_t slen;
if (!readI32(slen) || slen < 0)
return false;
if (!readStringBody(str, slen))
return false;
return true;
}
inline bool ThriftBuffer::readStringBody(std::string& str, int32_t slen)
{
if (slen < 0)
return false;
str.resize(slen);
return this->buffer->read(const_cast<char *>(str.c_str()), slen);
}
inline bool ThriftBuffer::writeFieldStop()
{
return writeI08((int8_t)TDT_STOP);
}
inline bool ThriftBuffer::writeI08(int8_t val)
{
return this->buffer->write((char *)&val, 1);
}
inline bool ThriftBuffer::writeI16(int16_t val)
{
int16_t x = htons(val);
return this->buffer->write((char *)&x, 2);
}
inline bool ThriftBuffer::writeI32(int32_t val)
{
int32_t x = htonl(val);
return this->buffer->write((char *)&x, 4);
}
inline bool ThriftBuffer::writeI64(int64_t val)
{
int64_t x = htonll(val);
return this->buffer->write((char *)&x, 8);
}
inline bool ThriftBuffer::writeU64(uint64_t val)
{
uint64_t x = htonll(val);
return this->buffer->write((char *)&x, 8);
}
inline bool ThriftBuffer::writeFieldBegin(int8_t field_type, int16_t field_id)
{
if (!writeI08(field_type))
return false;
return writeI16(field_id);
}
inline bool ThriftBuffer::writeString(const std::string& str)
{
int32_t slen = (int32_t)str.size();
if (!writeI32(slen))
return false;
return writeStringBody(str);
}
inline bool ThriftBuffer::writeStringBody(const std::string& str)
{
return this->buffer->write(str.c_str(), str.size());
}
inline bool ThriftMeta::writeI08(int8_t val)
{
this->writebuf.append(1, (char)val);
return true;
}
inline bool ThriftMeta::writeI32(int32_t val)
{
int32_t x = htonl(val);
this->writebuf.append((const char *)&x, 4);
return true;
}
inline 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;
}
} // end namespace sogou
#endif

View File

@ -0,0 +1,77 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#ifndef __RPC_THRIFT_ENUM_H__
#define __RPC_THRIFT_ENUM_H__
namespace sogou
{
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 sogou
#endif

View File

@ -0,0 +1,596 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <ctype.h>
#include <errno.h>
#include "rpc_thrift_idl.h"
namespace sogou
{
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 sogou

113
src/thrift/rpc_thrift_idl.h Normal file
View File

@ -0,0 +1,113 @@
/*
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.
Authors: Wu Jiaxu (wujiaxu@sogou-inc.com)
Li Yingxin (liyingxin@sogou-inc.com)
*/
#ifndef __RPC_THRIFT_IDL_H__
#define __RPC_THRIFT_IDL_H__
#include <stddef.h>
#include <list>
#include <vector>
#include <map>
#include <set>
#include "rpc_thrift_buffer.h"
namespace sogou
{
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 T>
class ThriftElementsImpl
{
public:
static const std::list<struct_element> *get_elements_instance()
{
static const ThriftElementsImpl<T> kInstance;
return &kInstance.elements;
}
private:
ThriftElementsImpl<T>()
{
T::StaticElementsImpl(&this->elements);
}
std::list<struct_element> elements;
};
class ThriftIDLMessage
{
public:
const ThriftDescriptor *descriptor = nullptr;
const std::list<struct_element> *elements = nullptr;
std::string debug_string() const { return ""; }
virtual ~ThriftIDLMessage() { }
};
} // end namespace sogou
#include "rpc_thrift_idl.inl"
#endif

File diff suppressed because it is too large Load Diff

9
srpc-config.cmake.in Normal file
View File

@ -0,0 +1,9 @@
@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@")
check_required_components(srpc)

106
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,106 @@
cmake_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)
set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "")
if (WIN32)
find_package(Protobuf CONFIG REQUIRED)
else ()
find_package(Protobuf REQUIRED)
endif ()
find_package(Workflow REQUIRED)
find_package(SRPC REQUIRED CONFIG HINTS ..)
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 ()
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(TEST_LIST
unittest
)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS test_pb.proto)
add_custom_target(
SRPC_GEN ALL
COMMAND ${SRPC_GEN_PROGRAM} protobuf ${PROJECT_SOURCE_DIR}/test_pb.proto ${PROJECT_SOURCE_DIR}
COMMAND ${SRPC_GEN_PROGRAM} thrift ${PROJECT_SOURCE_DIR}/test_thrift.thrift ${PROJECT_SOURCE_DIR}
COMMENT "sprc generator..."
)
set(GTEST_LIB GTest::GTest GTest::Main)
if (APPLE)
set(SRPC_LIB srpc)
elseif (WIN32)
set(SRPC_LIB srpc workflow ws2_32 wsock32 OpenSSL::SSL OpenSSL::Crypto)
set(GTEST_LIB GTest::gtest GTest::gtest_main)
else ()
set(SRPC_LIB srpc)
endif ()
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()
if (WIN32)
set(memcheck nothing)
else ()
foreach(src ${TEST_LIST})
add_test(${src}-memory-check ${memcheck_command} ./${src})
endforeach()
endif ()

32
test/GNUmakefile Normal file
View File

@ -0,0 +1,32 @@
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
ALL_TARGETS := all check clean
MAKE_FILE := Makefile
DEFAULT_BUILD_DIR := build
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
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
else ifeq (build, $(wildcard build))
-make -C build clean
endif
rm -rf build

27
test/test_pb.proto Normal file
View File

@ -0,0 +1,27 @@
syntax="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);
};

7
test/test_thrift.thrift Normal file
View File

@ -0,0 +1,7 @@
namespace cpp unit;
service TestThrift {
i32 add(1:i32 a, 2:i32 b);
string substr(1:string str, 2:i32 idx, 3:i32 length);
};

292
test/unittest.cc Normal file
View File

@ -0,0 +1,292 @@
/*
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.
Author: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <string>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <gtest/gtest.h>
#include <workflow/WFOperator.h>
#include "test_pb.srpc.h"
#include "test_thrift.srpc.h"
using namespace sogou;
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<class SERVER, class CLIENT>
void test_pb(SERVER& server)
{
std::mutex mutex;
std::condition_variable cond;
bool done = false;
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, [&](AddResponse *response, RPCContext *ctx) {
EXPECT_EQ(ctx->success(), true);
EXPECT_EQ(response->c(), 123 + 456);
SubstrRequest req2;
req2.set_str("hello world!");
req2.set_idx(6);
client.Substr(&req2, [&](SubstrResponse *response, RPCContext *ctx) {
EXPECT_EQ(ctx->success(), true);
EXPECT_TRUE(response->str() == "world!");
mutex.lock();
done = true;
mutex.unlock();
cond.notify_one();
});
});
std::unique_lock<std::mutex> lock(mutex);
while (!done)
cond.wait(lock);
lock.unlock();
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<class SERVER, class CLIENT>
void test_thrift(SERVER& server)
{
std::mutex mutex;
std::condition_variable cond;
bool done = false;
RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT;
TestThriftServiceImpl 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);
TestThrift::addRequest req1;
req1.a = 123;
req1.b = 456;
client.add(&req1, [&](TestThrift::addResponse *response, RPCContext *ctx) {
EXPECT_EQ(ctx->success(), true);
EXPECT_EQ(response->result, 123 + 456);
TestThrift::substrRequest req2;
req2.str = "hello world!";
req2.idx = 6;
req2.length = -1;
client.substr(&req2, [&](TestThrift::substrResponse *response, RPCContext *ctx) {
EXPECT_EQ(ctx->success(), true);
EXPECT_TRUE(response->result == "world!");
mutex.lock();
done = true;
mutex.unlock();
cond.notify_one();
});
});
std::unique_lock<std::mutex> lock(mutex);
while (!done)
cond.wait(lock);
lock.unlock();
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<SRPCServer, TestPB::SRPCClient>(server);
test_thrift<SRPCServer, TestThrift::SRPCClient>(server);
}
TEST(SRPCHttp, unittest)
{
RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT;
SRPCHttpServer server(&server_params);
test_pb<SRPCHttpServer, TestPB::SRPCHttpClient>(server);
}
TEST(BRPC, unittest)
{
RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT;
BRPCServer server(&server_params);
test_pb<BRPCServer, TestPB::BRPCClient>(server);
}
TEST(Thrift, unittest)
{
RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT;
ThriftServer server(&server_params);
test_thrift<ThriftServer, TestThrift::ThriftClient>(server);
}
TEST(ThriftHttp, unittest)
{
RPCServerParams server_params = RPC_SERVER_PARAMS_DEFAULT;
ThriftHttpServer server(&server_params);
test_thrift<ThriftHttpServer, TestThrift::ThriftHttpClient>(server);
}
TEST(SRPC_COMPRESS, unittest)
{
std::mutex mutex;
std::condition_variable cond;
bool done = false;
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([&](const ParallelWork *par) {
mutex.lock();
done = true;
mutex.unlock();
cond.notify_one();
});
par.start();
std::unique_lock<std::mutex> lock(mutex);
while (!done)
cond.wait(lock);
lock.unlock();
server.stop();
}

105
tutorial/CMakeLists.txt Normal file
View File

@ -0,0 +1,105 @@
cmake_minimum_required(VERSION 3.6)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "build type")
project(srpc_tutorial
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)
else ()
find_package(Protobuf REQUIRED)
endif ()
find_package(Workflow REQUIRED)
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 echo_pb.proto)
add_custom_target(
TURORIAL_GEN ALL
COMMAND ${SRPC_GEN_PROGRAM} protobuf ${PROJECT_SOURCE_DIR}/echo_pb.proto ${PROJECT_SOURCE_DIR}
COMMAND ${SRPC_GEN_PROGRAM} thrift ${PROJECT_SOURCE_DIR}/echo_thrift.thrift ${PROJECT_SOURCE_DIR}
COMMENT "sprc 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 ()
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
)
if (APPLE)
set(SRPC_LIB srpc)
elseif (WIN32)
set(SRPC_LIB srpc workflow ws2_32 wsock32 OpenSSL::SSL OpenSSL::Crypto)
else ()
set(SRPC_LIB srpc)
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()

27
tutorial/GNUmakefile Normal file
View File

@ -0,0 +1,27 @@
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
ALL_TARGETS := all clean
MAKE_FILE := Makefile
DEFAULT_BUILD_DIR := build
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
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 (build, $(wildcard build))
-make -C build clean
endif
rm -rf build

17
tutorial/echo_pb.proto Normal file
View File

@ -0,0 +1,17 @@
syntax="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);
};

View File

@ -0,0 +1,10 @@
struct EchoResult {
1:required string message;
2:optional i32 state;
3:optional i32 error;
}
service Example {
EchoResult Echo(1:string message, 2:string name);
}

View File

@ -0,0 +1,69 @@
/*
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.
Author: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <signal.h>
#include "echo_pb.srpc.h"
#ifndef _WIN32
#include <unistd.h>
#endif
using namespace sogou;
class ExampleServiceImpl : public Example::Service
{
public:
void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override
{
// ctx->set_compress_type(RPCCompressGzip);
response->set_message("Hi back");
printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n",
request->DebugString().c_str(),
response->DebugString().c_str());
}
};
static void sig_handler(int signo) { }
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)
{
#ifndef _WIN32
pause();
#else
getchar();
#endif
server.stop();
}
else
perror("server start");
google::protobuf::ShutdownProtobufLibrary();
return 0;
}

View File

@ -0,0 +1,57 @@
/*
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.
Author: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <stdio.h>
#include "echo_pb.srpc.h"
using namespace sogou;
int main()
{
Example::SRPCClient client("127.0.0.1", 1412);
//async
EchoRequest req;
req.set_message("Hello, sogou rpc!");
req.set_name("1412");
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());
});
//sync
EchoRequest sync_req;
EchoResponse sync_resp;
RPCSyncContext sync_ctx;
req.set_message("Hello, sogou rpc!");
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());
return 0;
}

View File

@ -0,0 +1,68 @@
/*
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.
Author: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <signal.h>
#include "echo_thrift.srpc.h"
#ifndef _WIN32
#include <unistd.h>
#endif
using namespace sogou;
class ExampleServiceImpl : public Example::Service
{
public:
void Echo(EchoResult& _return, const std::string& message, const std::string& name) override
{
_return.message = "Hi back";
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) { }
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)
{
#ifndef _WIN32
pause();
#else
getchar();
#endif
server.stop();
}
else
perror("server start");
google::protobuf::ShutdownProtobufLibrary();
return 0;
}

View File

@ -0,0 +1,71 @@
/*
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.
Author: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <stdio.h>
#include "echo_thrift.srpc.h"
using namespace sogou;
int main()
{
Example::SRPCClient client("127.0.0.1", 1412);
//sync
EchoResult sync_res;
client.Echo(sync_res, "Hello, sogou rpc!", "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, sogou rpc!";
req.name = "1412";
client.Echo(&req, [](Example::EchoResponse *response, RPCContext *ctx) {
if (ctx->success())
printf("%s\n", response->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;
req.message = "Hello, sogou rpc!";
req.name = "Sync";
client.Echo(&sync_req, &sync_resp, &sync_ctx);
if (sync_ctx.success)
printf("%s\n", sync_res.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;
}

View File

@ -0,0 +1,69 @@
/*
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.
Author: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <signal.h>
#include "echo_pb.srpc.h"
#ifndef _WIN32
#include <unistd.h>
#endif
using namespace sogou;
class ExampleServiceImpl : public Example::Service
{
public:
void Echo(EchoRequest *request, EchoResponse *response, RPCContext *ctx) override
{
// ctx->set_compress_type(RPCCompressGzip);
response->set_message("Hi back");
printf("Server Echo()\nget_req:\n%s\nset_resp:\n%s\n",
request->DebugString().c_str(),
response->DebugString().c_str());
}
};
static void sig_handler(int signo) { }
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)
{
#ifndef _WIN32
pause();
#else
getchar();
#endif
server.stop();
}
else
perror("server start");
google::protobuf::ShutdownProtobufLibrary();
return 0;
}

View File

@ -0,0 +1,57 @@
/*
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.
Author: Wu Jiaxu (wujiaxu@sogou-inc.com)
*/
#include <stdio.h>
#include "echo_pb.srpc.h"
using namespace sogou;
int main()
{
Example::BRPCClient client("127.0.0.1", 1412);
//async
EchoRequest req;
req.set_message("Hello, sogou rpc!");
req.set_name("1412");
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());
});
//sync
EchoRequest sync_req;
EchoResponse sync_resp;
RPCSyncContext sync_ctx;
req.set_message("Hello, sogou rpc!");
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());
return 0;
}

Some files were not shown because too many files have changed in this diff Show More