mirror of
https://gitee.com/sogou/srpc.git
synced 2024-12-21 16:14:54 +08:00
initialize
This commit is contained in:
parent
2d4124efa0
commit
7114f18794
7
.editorconfig
Normal file
7
.editorconfig
Normal file
@ -0,0 +1,7 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# all files
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
62
.gitignore
vendored
62
.gitignore
vendored
@ -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
138
CMakeLists.txt
Normal 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
28
CMakeLists_Headers.txt
Normal 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
60
GNUmakefile
Normal 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
26
LICENSE
@ -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
319
README.md
@ -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
85
benchmark/CMakeLists.txt
Normal 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
28
benchmark/GNUmakefile
Normal 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
|
12
benchmark/benchmark_pb.proto
Normal file
12
benchmark/benchmark_pb.proto
Normal 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);
|
||||
}
|
||||
|
6
benchmark/benchmark_thrift.thrift
Normal file
6
benchmark/benchmark_thrift.thrift
Normal 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
217
benchmark/client.cc
Normal 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
295
benchmark/client_cdf.cc
Normal 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
147
benchmark/server.cc
Normal 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(¶ms);
|
||||
|
||||
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(¶ms);
|
||||
|
||||
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(¶ms);
|
||||
|
||||
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
21
benchmark/test.py
Normal 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")
|
||||
|
40
benchmark/thrift_server.cc
Normal file
40
benchmark/thrift_server.cc
Normal 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
0
buildrpm.mk
Normal file
2
configure
vendored
Normal file
2
configure
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
mkdir -p build
|
||||
cd build && cmake3 .. || cmake ..
|
BIN
docs/images/benchmark1.png
Normal file
BIN
docs/images/benchmark1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
docs/images/benchmark2.png
Normal file
BIN
docs/images/benchmark2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
BIN
docs/images/benchmark3.png
Normal file
BIN
docs/images/benchmark3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
BIN
docs/images/benchmark4.png
Normal file
BIN
docs/images/benchmark4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
docs/images/benchmark5.png
Normal file
BIN
docs/images/benchmark5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
BIN
docs/images/benchmark6.png
Normal file
BIN
docs/images/benchmark6.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
394
docs/rpc.md
Normal file
394
docs/rpc.md
Normal 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连接上的通信seqid,seqid=0代表第1次
|
||||
|
||||
#### ``std::string get_remote_ip() const;``
|
||||
获得对方IP地址,支持ipv4/ipv6
|
||||
|
||||
#### ``int get_peer_addr(struct sockaddr *addr, socklen_t *addrlen) const;``
|
||||
获得对方地址,in/out参数为更底层的数据结构sockaddr
|
||||
|
||||
#### ``const std::string& get_service_name() const;``
|
||||
获取RPC Service Name
|
||||
|
||||
#### ``const std::string& get_method_name() const;``
|
||||
获取RPC Methode Name
|
||||
|
||||
#### ``SeriesWork *get_series() const;``
|
||||
获取当前ServerTask/ClientTask所在series
|
||||
|
||||
### RPCContext API - Only for client done
|
||||
#### ``bool success() const;``
|
||||
client专用。这次请求是否成功
|
||||
|
||||
#### ``int get_status_code() const;``
|
||||
client专用。这次请求的rpc status code
|
||||
|
||||
#### ``const char *get_errmsg() const;``
|
||||
client专用。这次请求的错误信息
|
||||
|
||||
#### ``int get_error() const;``
|
||||
client专用。这次请求的错误码
|
||||
|
||||
#### ``void *get_user_data() const;``
|
||||
client专用。获取ClientTask的user_data
|
||||
|
||||
### 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
28
docs/tutorial-01-idl.md
Normal 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);
|
||||
};
|
||||
~~~
|
27
docs/tutorial-02-service.md
Normal file
27
docs/tutorial-02-service.md
Normal 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());
|
||||
}
|
||||
};
|
||||
~~~
|
50
docs/tutorial-03-server.md
Normal file
50
docs/tutorial-03-server.md
Normal 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;
|
||||
}
|
||||
~~~
|
37
docs/tutorial-04-client.md
Normal file
37
docs/tutorial-04-client.md
Normal 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;
|
||||
}
|
||||
~~~
|
70
docs/tutorial-05-context.md
Normal file
70
docs/tutorial-05-context.md
Normal 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连接上的通信seqid,seqid=0代表第1次
|
||||
|
||||
#### ``std::string get_remote_ip() const;``
|
||||
获得对方IP地址,支持ipv4/ipv6
|
||||
|
||||
#### ``int get_peer_addr(struct sockaddr *addr, socklen_t *addrlen) const;``
|
||||
获得对方地址,in/out参数为更底层的数据结构sockaddr
|
||||
|
||||
#### ``const std::string& get_service_name() const;``
|
||||
获取RPC Service Name
|
||||
|
||||
#### ``const std::string& get_method_name() const;``
|
||||
获取RPC Methode Name
|
||||
|
||||
#### ``SeriesWork *get_series() const;``
|
||||
获取当前ServerTask/ClientTask所在series
|
||||
|
||||
### RPCContext API - Only for client done
|
||||
#### ``bool success() const;``
|
||||
client专用。这次请求是否成功
|
||||
|
||||
#### ``int get_status_code() const;``
|
||||
client专用。这次请求的rpc status code
|
||||
|
||||
#### ``const char *get_errmsg() const;``
|
||||
client专用。这次请求的错误信息
|
||||
|
||||
#### ``int get_error() const;``
|
||||
client专用。这次请求的错误码
|
||||
|
||||
#### ``void *get_user_data() const;``
|
||||
client专用。获取ClientTask的user_data
|
||||
|
||||
### 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代表无限。
|
94
docs/tutorial-06-workflow.md
Normal file
94
docs/tutorial-06-workflow.md
Normal 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
15
example/GNUmakefile
Normal 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
236
example/client.cc
Normal 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
23
example/echo.proto
Normal 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
14
example/msg.thrift
Normal 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
140
example/server.cc
Normal 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(¶ms);
|
||||
|
||||
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(¶ms);
|
||||
|
||||
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
95
src/CMakeLists.txt
Normal 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
|
||||
)
|
||||
|
10
src/compress/CMakeLists.txt
Normal file
10
src/compress/CMakeLists.txt
Normal 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})
|
||||
|
82
src/compress/rpc_compress.cc
Normal file
82
src/compress/rpc_compress.cc
Normal 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
238
src/compress/rpc_compress.h
Normal 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
|
||||
|
405
src/compress/rpc_compress_gzip.h
Normal file
405
src/compress/rpc_compress_gzip.h
Normal 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
|
||||
|
293
src/compress/rpc_compress_lz4.h
Normal file
293
src/compress/rpc_compress_lz4.h
Normal 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
|
||||
|
132
src/compress/rpc_compress_snappy.cc
Normal file
132
src/compress/rpc_compress_snappy.cc
Normal 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
|
67
src/compress/rpc_compress_snappy.h
Normal file
67
src/compress/rpc_compress_snappy.h
Normal 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
|
||||
|
27
src/generator/CMakeLists.txt
Normal file
27
src/generator/CMakeLists.txt
Normal 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
65
src/generator/compiler.cc
Normal 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
192
src/generator/descriptor.h
Normal 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
419
src/generator/generator.cc
Normal 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
83
src/generator/generator.h
Normal 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
856
src/generator/parser.cc
Normal 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
77
src/generator/parser.h
Normal 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
1504
src/generator/printer.h
Normal file
File diff suppressed because it is too large
Load Diff
25
src/message/CMakeLists.txt
Normal file
25
src/message/CMakeLists.txt
Normal 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
173
src/message/rpc_message.h
Normal 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
|
||||
|
456
src/message/rpc_message_brpc.cc
Normal file
456
src/message/rpc_message_brpc.cc
Normal 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
|
272
src/message/rpc_message_brpc.h
Normal file
272
src/message/rpc_message_brpc.h
Normal 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
|
||||
|
885
src/message/rpc_message_srpc.cc
Normal file
885
src/message/rpc_message_srpc.cc
Normal 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
|
||||
|
319
src/message/rpc_message_srpc.h
Normal file
319
src/message/rpc_message_srpc.h
Normal 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
|
||||
|
250
src/message/rpc_message_thrift.cc
Normal file
250
src/message/rpc_message_thrift.cc
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
386
src/message/rpc_message_thrift.h
Normal file
386
src/message/rpc_message_thrift.h
Normal 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
|
||||
|
22
src/message/rpc_meta.proto
Normal file
22
src/message/rpc_meta.proto
Normal 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;
|
||||
};
|
143
src/message/rpc_meta_brpc.proto
Normal file
143
src/message/rpc_meta_brpc.proto
Normal 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
95
src/rpc_basic.h
Normal 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
409
src/rpc_buffer.cc
Normal 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
390
src/rpc_buffer.h
Normal 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
196
src/rpc_client.h
Normal 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
92
src/rpc_context.h
Normal 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
186
src/rpc_context.inl
Normal 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
123
src/rpc_global.cc
Normal 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
52
src/rpc_global.h
Normal 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
83
src/rpc_options.h
Normal 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
168
src/rpc_server.h
Normal 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
99
src/rpc_service.h
Normal 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
447
src/rpc_task.inl
Normal 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
113
src/rpc_types.h
Normal 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
130
src/rpc_zero_copy_stream.h
Normal 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
58
src/sogou_rpc.h
Normal 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
10
src/thrift/CMakeLists.txt
Normal 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})
|
||||
|
196
src/thrift/rpc_thrift_buffer.cc
Normal file
196
src/thrift/rpc_thrift_buffer.cc
Normal 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
|
||||
|
283
src/thrift/rpc_thrift_buffer.h
Normal file
283
src/thrift/rpc_thrift_buffer.h
Normal 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
|
||||
|
77
src/thrift/rpc_thrift_enum.h
Normal file
77
src/thrift/rpc_thrift_enum.h
Normal 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
|
||||
|
596
src/thrift/rpc_thrift_idl.cc
Normal file
596
src/thrift/rpc_thrift_idl.cc
Normal 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
113
src/thrift/rpc_thrift_idl.h
Normal 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
|
||||
|
1043
src/thrift/rpc_thrift_idl.inl
Normal file
1043
src/thrift/rpc_thrift_idl.inl
Normal file
File diff suppressed because it is too large
Load Diff
9
srpc-config.cmake.in
Normal file
9
srpc-config.cmake.in
Normal 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
106
test/CMakeLists.txt
Normal 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
32
test/GNUmakefile
Normal 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
27
test/test_pb.proto
Normal 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
7
test/test_thrift.thrift
Normal 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
292
test/unittest.cc
Normal 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
105
tutorial/CMakeLists.txt
Normal 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
27
tutorial/GNUmakefile
Normal 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
17
tutorial/echo_pb.proto
Normal 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);
|
||||
};
|
||||
|
10
tutorial/echo_thrift.thrift
Normal file
10
tutorial/echo_thrift.thrift
Normal 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);
|
||||
}
|
||||
|
69
tutorial/tutorial-01-srpc_pb_server.cc
Normal file
69
tutorial/tutorial-01-srpc_pb_server.cc
Normal 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;
|
||||
}
|
||||
|
57
tutorial/tutorial-02-srpc_pb_client.cc
Normal file
57
tutorial/tutorial-02-srpc_pb_client.cc
Normal 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;
|
||||
}
|
||||
|
68
tutorial/tutorial-03-srpc_thrift_server.cc
Normal file
68
tutorial/tutorial-03-srpc_thrift_server.cc
Normal 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;
|
||||
}
|
||||
|
71
tutorial/tutorial-04-srpc_thrift_client.cc
Normal file
71
tutorial/tutorial-04-srpc_thrift_client.cc
Normal 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;
|
||||
}
|
||||
|
69
tutorial/tutorial-05-brpc_pb_server.cc
Normal file
69
tutorial/tutorial-05-brpc_pb_server.cc
Normal 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;
|
||||
}
|
||||
|
57
tutorial/tutorial-06-brpc_pb_client.cc
Normal file
57
tutorial/tutorial-06-brpc_pb_client.cc
Normal 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
Loading…
Reference in New Issue
Block a user