├── README.md ├── example ├── sample.proto ├── single_request_async_example.cpp ├── heartbeat_example.cpp ├── single_request_example.cpp ├── http_request_example.cpp └── cloud_rpc_demo.cpp ├── test ├── coverage │ ├── ReportGCovr.sh │ ├── InitialCoverage.sh │ └── ReportCoverage.sh └── unit_test │ ├── io_buffer_unittest.proto │ ├── simple_socket_server.h │ ├── unittest_main.cpp │ ├── socket_posix_unittest.cpp │ ├── endpoint_unittest.cpp │ ├── simple_socket_server.cpp │ ├── utils_unittest.cpp │ ├── uri_unittest.cpp │ ├── lifecyclelock_unittest.cpp │ ├── channel_unittest.cpp │ ├── concurrenthashmap_unittest.cpp │ ├── http_unittest.cpp │ ├── time_thread_unittest.cpp │ └── thread_pool_unittest.cpp ├── include ├── protocol │ ├── request_base.h │ ├── response_base.h │ ├── http │ │ ├── http_header.h │ │ ├── http_request.h │ │ └── http_response.h │ ├── bolt │ │ ├── bolt_request.h │ │ └── bolt_response.h │ └── protocol_define.h ├── tcp │ └── endpoint.h ├── rpc.h ├── common │ ├── time_thread.h │ ├── uri.h │ ├── life_cycle_lock.h │ ├── common_defines.h │ ├── lru_cache.h │ └── utils.h ├── channel │ └── channel.h └── session │ └── session.h ├── src ├── protocol │ ├── response_base.cpp │ ├── http │ │ ├── http_header.cpp │ │ ├── http_protocol.h │ │ ├── http_request.cpp │ │ ├── http_protocol.cpp │ │ └── http_response.cpp │ ├── bolt │ │ ├── bolt_protocol.h │ │ ├── bolt_protocol.cpp │ │ └── bolt_response.cpp │ └── protocol_define.cpp ├── common │ ├── macro.h │ ├── log.cpp │ ├── log.h │ ├── common_defines.cpp │ ├── uri.cpp │ └── io_buffer.h ├── schedule │ ├── loop.h │ ├── loop_thread.h │ ├── schedule.h │ ├── epoll_loop.cpp │ ├── schedule.cpp │ ├── loop_thread.cpp │ └── kqueue_loop.cpp └── tcp │ ├── socket_manager.h │ ├── socket_base.h │ ├── endpoint.cpp │ ├── socket_posix.cpp │ ├── socket.h │ └── socket_manager.cpp ├── Dockerfile ├── second_party └── fmt │ ├── src │ ├── format.cc │ └── posix.cc │ └── include │ └── fmt │ ├── time.h │ └── ostream.h ├── TARGETS └── CMakeLists.txt /README.md: -------------------------------------------------------------------------------- 1 | # sofa-bolt-cpp 2 | 轻量的提供BOLT+PROTOBUF的RPC Client 3 | -------------------------------------------------------------------------------- /example/sample.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message SampleServicePbResult { 4 | string result = 1; 5 | } 6 | 7 | message SampleServicePbRequest { 8 | string name = 1; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /test/coverage/ReportGCovr.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin 2 | rm -f *.html 3 | gcovr -r ../../ --html --gcov-exclude='.*(?:Logger|pb|unit_test|example).*' --html-details -o detail.html 4 | gcovr -r ../../ --html --gcov-exclude='.*(?:Logger|pb|unit_test|example).*' -o summary.html 5 | -------------------------------------------------------------------------------- /test/unit_test/io_buffer_unittest.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message UnitTestMessage { 4 | repeated string msg = 1; 5 | repeated int32 code = 2; 6 | } 7 | 8 | message UnitTestProto { 9 | string name = 1; 10 | int64 id = 2; 11 | uint32 uid = 3; 12 | UnitTestMessage msg = 4; 13 | } 14 | -------------------------------------------------------------------------------- /test/coverage/InitialCoverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "ignore proto and test files" 3 | find build -name "*pb.cc.gcno" -exec rm -rf {} \; 4 | find build -name "*example*.gcno" -exec rm -rf {} \; 5 | find build -name "*unittest*.gcno" -exec rm -rf {} \; 6 | echo "start initialize lcov coverage" 7 | lcov -d build -z 8 | lcov -d build -b . --no-external --initial -c -o rpc_client_c11_init.info 9 | 10 | -------------------------------------------------------------------------------- /test/coverage/ReportCoverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "ignore proto and test files" 4 | find build -name "*pb.cc.gcda" -exec rm -rf {} \; 5 | find build -name "*example*.gcda" -exec rm -rf {} \; 6 | find build -name "*unittest*.gcda" -exec rm -rf {} \; 7 | 8 | echo "start report lcov coverage" 9 | lcov -d build -b . --no-external -c -o rpc_client_c11.info 10 | genhtml -o rpc_client_c11_result --prefix=`pwd` rpc_client_c11_init.info rpc_client_c11.info 11 | 12 | -------------------------------------------------------------------------------- /include/protocol/request_base.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/4. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_REQUEST_BASE_H 6 | #define RPC_INCLUDE_REQUEST_BASE_H 7 | 8 | namespace antflash { 9 | 10 | class IOBuffer; 11 | 12 | //abstract 13 | class RequestBase { 14 | public: 15 | RequestBase() {} 16 | virtual ~RequestBase() {} 17 | }; 18 | 19 | } 20 | 21 | #endif //RPC_INCLUDE_REQUEST_BASE_H 22 | -------------------------------------------------------------------------------- /src/protocol/response_base.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/11. 3 | // 4 | 5 | #include "protocol/response_base.h" 6 | 7 | namespace antflash { 8 | 9 | const char* ParseResultToString(EResParseResult r) { 10 | static const char* msg[] = { 11 | "ok", 12 | "parse error", 13 | "not enough data" 14 | }; 15 | return msg[static_cast(r)]; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/common/macro.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/10. 3 | // 4 | 5 | #ifndef RPC_COMMON_MARCO_H 6 | #define RPC_COMMON_MARCO_H 7 | 8 | #undef LIKELY 9 | #undef UNLIKELY 10 | #if defined(OS_LINUX) 11 | #define LIKELY(expr) (__builtin_expect((bool)(expr), true)) 12 | #define UNLIKELY(expr) (__builtin_expect((bool)(expr), false)) 13 | #else 14 | #define LIKELY(expr) (expr) 15 | #define UNLIKELY(expr) (expr) 16 | #endif 17 | 18 | #endif //RPC_COMMON_MARCO_H 19 | -------------------------------------------------------------------------------- /src/protocol/http/http_header.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #include "protocol/http/http_header.h" 6 | 7 | namespace antflash { 8 | 9 | const std::string HttpHeader::_s_method_name[HTTP_METHOD_TOTAL] = { 10 | "GET", 11 | "POST", 12 | }; 13 | 14 | const std::string HttpHeader::_s_header_key[HTTP_HEADER_KEY_SIZE] = { 15 | "log-id", 16 | "application/json", 17 | "connection", 18 | }; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /test/unit_test/simple_socket_server.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/25. 3 | // 4 | 5 | #ifndef RPC_SIMPLE_SOCKET_SERVER_H 6 | #define RPC_SIMPLE_SOCKET_SERVER_H 7 | 8 | #include 9 | 10 | namespace antflash { 11 | 12 | class SimpleSocketServer { 13 | public: 14 | SimpleSocketServer() : _exit(false) {} 15 | 16 | bool run(int port); 17 | 18 | void stop() { 19 | _exit = true; 20 | } 21 | 22 | private: 23 | bool _exit; 24 | }; 25 | 26 | } 27 | 28 | #endif //RPC_SIMPLE_SOCKET_SERVER_H 29 | -------------------------------------------------------------------------------- /test/unit_test/unittest_main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhenggu.xwt on 18/4/3. 3 | // 4 | #include 5 | #include "../src/common/log.h" 6 | 7 | int main(int argc, char** argv) { 8 | 9 | char* var = nullptr; 10 | var = getenv("ALOG_DEBUG"); 11 | if (nullptr != var) { 12 | std::string level(var); 13 | if (level == "debug") { 14 | antflash::LogMessage::setLogLevel( 15 | antflash::LogLevel::LOG_LEVEL_DEBUG); 16 | } 17 | } 18 | 19 | testing::InitGoogleTest(&argc, argv); 20 | 21 | return RUN_ALL_TESTS(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /include/protocol/response_base.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/11. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_RESPONSE_BASE_H 6 | #define RPC_INCLUDE_RESPONSE_BASE_H 7 | 8 | namespace antflash { 9 | 10 | class IOBuffer; 11 | 12 | enum class EResParseResult { 13 | PARSE_OK = 0, 14 | PARSE_ERROR, 15 | PARSE_NOT_ENOUGH_DATA, 16 | }; 17 | 18 | class ResponseBase { 19 | public: 20 | ResponseBase() {} 21 | virtual ~ResponseBase() {} 22 | 23 | virtual EResParseResult deserialize(IOBuffer &buffer) noexcept = 0; 24 | }; 25 | 26 | const char* ParseResultToString(EResParseResult r); 27 | 28 | } 29 | #endif //RPC_INCLUDE_RESPONSE_BASE_H 30 | -------------------------------------------------------------------------------- /src/schedule/loop.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/9. 3 | // 4 | 5 | #ifndef RPC_SCHEDULE_LOOP_H 6 | #define RPC_SCHEDULE_LOOP_H 7 | 8 | #include "tcp/socket_base.h" 9 | #include 10 | 11 | namespace antflash { 12 | 13 | struct LoopInternalData; 14 | 15 | class Loop { 16 | public: 17 | Loop(); 18 | ~Loop(); 19 | 20 | bool init(); 21 | void destroy(); 22 | void loop_once(); 23 | 24 | bool add_event(int fd, int events, void* handler); 25 | void remove_event(int fd, int events); 26 | 27 | private: 28 | base::FdGuard _backend_fd; 29 | std::unique_ptr _data; 30 | }; 31 | 32 | } 33 | 34 | #endif //RPC_SCHEDULE_LOOP_H 35 | -------------------------------------------------------------------------------- /src/protocol/http/http_protocol.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #ifndef RPC_PROTOCOL_HTTP_PROTOCOL_H 6 | #define RPC_PROTOCOL_HTTP_PROTOCOL_H 7 | 8 | #include 9 | #include "protocol/request_base.h" 10 | #include "protocol/response_base.h" 11 | 12 | namespace antflash { 13 | namespace http { 14 | 15 | bool assembleHttpRequest(const RequestBase& req, size_t request_id, IOBuffer& buffer); 16 | EResParseResult parseHttpProtocol(IOBuffer&, size_t&, size_t&, void**); 17 | EResParseResult parseHttpResponse(ResponseBase& rsp, IOBuffer& buffer, void*); 18 | bool assembleHeartbeatRequest(IOBuffer& buffer); 19 | size_t converseHttpRequest(size_t request_id); 20 | 21 | } 22 | } 23 | 24 | 25 | #endif //RPC_HTTP_PROTOCOL_H 26 | -------------------------------------------------------------------------------- /include/tcp/endpoint.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/2. 3 | // Brief 4 | 5 | #ifndef RPC_INCLUDE_ENDPOINT_H 6 | #define RPC_INCLUDE_ENDPOINT_H 7 | 8 | #include 9 | #include 10 | 11 | namespace antflash { 12 | 13 | static constexpr in_addr IP_ANY = {INADDR_ANY}; 14 | static constexpr in_addr IP_NONE = {INADDR_NONE}; 15 | 16 | struct EndPoint { 17 | EndPoint() : ip(IP_ANY), port(0) {} 18 | EndPoint(const in_addr &i, int p) : ip(i), port(p) {} 19 | EndPoint(const sockaddr_in& in) : ip(in.sin_addr), port(in.sin_port) {} 20 | EndPoint(const EndPoint& right) : ip(right.ip), port(right.port) {} 21 | 22 | bool parseFromString(const char *str); 23 | std::string ipToStr() const; 24 | 25 | in_addr ip; 26 | int port; 27 | }; 28 | 29 | 30 | } 31 | 32 | #endif //RPC_INCLUDE_ENDPOINT_H 33 | -------------------------------------------------------------------------------- /src/protocol/http/http_request.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #include "protocol/http/http_request.h" 6 | #include 7 | #include 8 | #include "common/io_buffer.h" 9 | #include "common/log.h" 10 | 11 | namespace antflash { 12 | 13 | HttpRequest& HttpRequest::attach(const std::string& data) { 14 | _data.reset(new IOBuffer); 15 | _data->append(data); 16 | return *this; 17 | } 18 | 19 | HttpRequest& HttpRequest::attach(const char* data) { 20 | _data.reset(new IOBuffer); 21 | _data->append(data); 22 | return *this; 23 | } 24 | 25 | std::shared_ptr HttpRequest::attach() const { 26 | return _data; 27 | } 28 | 29 | size_t HttpRequest::attach_size() const { 30 | if (_data) { 31 | return _data->size(); 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /test/unit_test/socket_posix_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/25. 3 | // 4 | 5 | #include "tcp/socket_base.h" 6 | #include 7 | #include 8 | 9 | TEST(SocketPosixTest, base) { 10 | ASSERT_FALSE(antflash::base::set_non_blocking(-1)); 11 | ASSERT_FALSE(antflash::base::set_blocking(-1)); 12 | int fd = open("socket_posix", O_WRONLY | O_CREAT | O_TRUNC, 0644); 13 | ASSERT_TRUE(antflash::base::set_non_blocking(fd)); 14 | ASSERT_TRUE(antflash::base::set_non_blocking(fd)); 15 | ASSERT_TRUE(antflash::base::set_blocking(fd)); 16 | ASSERT_TRUE(antflash::base::set_blocking(fd)); 17 | 18 | antflash::base::FdGuard fd_guard(-1); 19 | ASSERT_FALSE(antflash::base::connected(fd_guard)); 20 | antflash::base::FdGuard fd_guard2(fd); 21 | ASSERT_FALSE(antflash::base::connected(fd_guard2)); 22 | } 23 | -------------------------------------------------------------------------------- /src/protocol/bolt/bolt_protocol.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/4. 3 | // 4 | 5 | #ifndef RPC_PROTOCOL_BOLT_PROTOCOL_H 6 | #define RPC_PROTOCOL_BOLT_PROTOCOL_H 7 | 8 | #include 9 | #include 10 | #include "protocol/request_base.h" 11 | #include "protocol/response_base.h" 12 | 13 | namespace antflash { 14 | namespace bolt { 15 | 16 | bool assembleBoltRequest(const RequestBase& req, size_t request_id, IOBuffer& buffer); 17 | EResParseResult parseBoltProtocol(IOBuffer&, size_t&, size_t&, void**); 18 | EResParseResult parseBoltResponse(ResponseBase& rsp, IOBuffer& buffer, void*); 19 | bool assembleHeartbeatRequest(std::shared_ptr&, 20 | std::shared_ptr&); 21 | bool parseHeartbeatResponse(std::shared_ptr&); 22 | size_t converseBoltRequest(size_t request_id); 23 | } 24 | } 25 | 26 | #endif //RPC_PROTOCOL_BOLT_PROTOCOL_H 27 | -------------------------------------------------------------------------------- /include/protocol/http/http_header.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_HTTP_HEADER_H 6 | #define RPC_INCLUDE_HTTP_HEADER_H 7 | 8 | #include 9 | 10 | namespace antflash { 11 | 12 | enum EHttpMethod { 13 | HTTP_METHOD_GET = 0, 14 | HTTP_METHOD_POST, 15 | HTTP_METHOD_TOTAL 16 | }; 17 | 18 | enum EHttpHeaderKey { 19 | LOG_ID = 0, 20 | CONTENT_TYPE_JSON, 21 | CONNECTION, 22 | HTTP_HEADER_KEY_SIZE 23 | }; 24 | 25 | class HttpHeader { 26 | public: 27 | inline static const std::string& getHeaderKey(EHttpHeaderKey key) { 28 | return _s_header_key[key]; 29 | } 30 | 31 | inline static const std::string& getMethod(EHttpMethod key) { 32 | return _s_method_name[key]; 33 | } 34 | 35 | private: 36 | static const std::string _s_header_key[HTTP_HEADER_KEY_SIZE]; 37 | static const std::string _s_method_name[HTTP_METHOD_TOTAL]; 38 | }; 39 | 40 | } 41 | 42 | #endif //RPC_HTTP_HEADER_H 43 | -------------------------------------------------------------------------------- /src/schedule/loop_thread.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/9. 3 | // 4 | 5 | #ifndef RPC_SCHEDULE_LOOP_THREAD_H 6 | #define RPC_SCHEDULE_LOOP_THREAD_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "tcp/socket_base.h" 13 | 14 | namespace antflash { 15 | 16 | class Loop; 17 | 18 | class LoopThread final { 19 | public: 20 | LoopThread(); 21 | ~LoopThread(); 22 | 23 | LoopThread(const LoopThread&) = delete; 24 | LoopThread&operator=(const LoopThread&) = delete; 25 | 26 | LoopThread(LoopThread&& right); 27 | LoopThread& operator=(LoopThread&& right); 28 | 29 | bool start(); 30 | 31 | void stop(); 32 | 33 | bool add_event(int fd, int events, void* handler); 34 | void remove_event(int fd, int events); 35 | 36 | private: 37 | std::atomic _exit; 38 | std::unique_ptr _thread; 39 | std::unique_ptr _loop; 40 | base::FdGuard _wakeup_fds[2]; 41 | }; 42 | 43 | } 44 | 45 | #endif //RPC_SCHEDULE_LOOP_THREAD_H 46 | -------------------------------------------------------------------------------- /src/common/log.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/7/17. 3 | // 4 | 5 | #include "log.h" 6 | #include 7 | #include "rpc.h" 8 | 9 | namespace antflash { 10 | 11 | static const char* s_level_names[] = {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"}; 12 | 13 | inline void DefaultLogHandler( 14 | LogLevel level, const char* filename, int line, 15 | const std::string& message) { 16 | 17 | fprintf(stdout, "[bolt-rpc %s %s:%d] %s\n", 18 | s_level_names[static_cast(level)], 19 | filename, line, message.c_str()); 20 | fflush(stdout); 21 | } 22 | 23 | static LogHandler* s_cur_log_handler = &DefaultLogHandler; 24 | 25 | LogHandler* setLogHandler(LogHandler* handler) { 26 | LogHandler* old = s_cur_log_handler; 27 | s_cur_log_handler = handler; 28 | return old; 29 | } 30 | 31 | void setLogLevel(LogLevel level) { 32 | LogMessage::setLogLevel(level); 33 | } 34 | 35 | LogLevel LogMessage::_s_min_level = LogLevel::LOG_LEVEL_INFO; 36 | 37 | void LogMessage::finish() { 38 | if (_level < _s_min_level) { 39 | return; 40 | } 41 | if (nullptr != s_cur_log_handler) { 42 | s_cur_log_handler(_level, _filename, _line, _message); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /include/rpc.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhenggu.xwt on 18/4/18. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_RPC_H 6 | #define RPC_INCLUDE_RPC_H 7 | 8 | #include "channel/channel.h" 9 | #include "session/session.h" 10 | #include "protocol/http/http_request.h" 11 | #include "protocol/http/http_response.h" 12 | #include "protocol/bolt/bolt_request.h" 13 | #include "protocol/bolt/bolt_response.h" 14 | 15 | namespace antflash { 16 | 17 | /** 18 | * Global init for ant feature rpc client, not thread compatible, 19 | * you should call this method before using rpc client. 20 | * Init action: scheduler threads, timer threads and socket manager 21 | * thread. 22 | * @return true if init success, else false 23 | */ 24 | bool globalInit(); 25 | 26 | /** 27 | * Global destroy for ant feature rpc client, not thread compatible, 28 | * you should call this method before process end, or some exception may 29 | * be abort. 30 | */ 31 | void globalDestroy(); 32 | 33 | /** 34 | * Set Specific rpc log handler, if not set, rpc will log out information to stdout 35 | * @param handler 36 | * @return 37 | */ 38 | LogHandler* setLogHandler(LogHandler* handler); 39 | 40 | /** 41 | * Set rpc log level, info level default. 42 | * @param level 43 | * @return 44 | */ 45 | void setLogLevel(LogLevel level); 46 | 47 | } 48 | 49 | #endif //RPC_INCLUDE_RPC_H 50 | -------------------------------------------------------------------------------- /test/unit_test/endpoint_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/3. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | TEST(EndPointTest, parseFromString) { 10 | std::string str = " I love u "; 11 | antflash::EndPoint point; 12 | GTEST_ASSERT_EQ(point.parseFromString(str.c_str()), false); 13 | str = "127.0.0.1:12345"; 14 | GTEST_ASSERT_EQ(point.parseFromString(str.c_str()), true); 15 | GTEST_ASSERT_EQ(point.ip.s_addr, (unsigned int)16777343); 16 | GTEST_ASSERT_EQ(point.port, 12345); 17 | ASSERT_STREQ(str.c_str(), point.ipToStr().c_str()); 18 | str = "288.2.1.500:12345"; 19 | GTEST_ASSERT_EQ(point.parseFromString(str.c_str()), false); 20 | str = "127.0.0.1: 12345"; 21 | GTEST_ASSERT_EQ(point.parseFromString(str.c_str()), true); 22 | str = " 127.0.0.1:12345"; 23 | GTEST_ASSERT_EQ(point.parseFromString(str.c_str()), false); 24 | str = "127.0.0.1:12345 "; 25 | GTEST_ASSERT_EQ(point.parseFromString(str.c_str()), false); 26 | str = "127.0.0.1:70000"; 27 | GTEST_ASSERT_EQ(point.parseFromString(str.c_str()), false); 28 | str = "127.0.0.1:0"; 29 | GTEST_ASSERT_EQ(point.parseFromString(str.c_str()), true); 30 | GTEST_ASSERT_EQ(point.port, 0); 31 | } 32 | -------------------------------------------------------------------------------- /src/tcp/socket_manager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/13. 3 | // 4 | 5 | #ifndef RPC_TCP_SOCKET_MANAGER_H 6 | #define RPC_TCP_SOCKET_MANAGER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "socket.h" 14 | 15 | namespace antflash { 16 | 17 | class SocketManager { 18 | public: 19 | static SocketManager& getInstance() { 20 | static SocketManager s_socket_mgr; 21 | return s_socket_mgr; 22 | } 23 | 24 | bool init(); 25 | void destroy(); 26 | void addWatch(std::shared_ptr& socket); 27 | 28 | private: 29 | SocketManager() : _exit(false), 30 | _reclaim_counter(0) {} 31 | ~SocketManager() { 32 | destroy(); 33 | } 34 | 35 | void watchConnections(); 36 | 37 | std::atomic _exit; 38 | std::unique_ptr _thread; 39 | std::list> _list; 40 | std::list> _reclaim_list; 41 | base::FdGuard _reclaim_fd[2]; 42 | std::vector> _on_reclaim; 43 | std::mutex _reclaim_mtx; 44 | std::condition_variable _reclaim_notify; 45 | std::vector _reclaim_notify_flag; 46 | size_t _reclaim_counter; 47 | std::mutex _mtx; 48 | }; 49 | 50 | } 51 | 52 | #endif //RPC_TCP_SOCKET_MANAGER_H 53 | -------------------------------------------------------------------------------- /example/single_request_async_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "rpc.h" 5 | #include "sample.pb.h" 6 | #include "../src/common/log.h" 7 | 8 | int main() { 9 | if (!antflash::globalInit()) { 10 | LONG_ERROR("init fail."); 11 | return -1; 12 | } 13 | 14 | antflash::Channel channel; 15 | if (!channel.init("127.0.0.1:12200", nullptr)) { 16 | LONG_ERROR("channel init fail."); 17 | return -1; 18 | } 19 | 20 | antflash::BoltRequest request; 21 | SampleServicePbRequest sub_req; 22 | 23 | SampleServicePbResult sub_rsp; 24 | antflash::BoltResponse response(sub_rsp); 25 | 26 | sub_req.set_name("zhenggu"); 27 | request.service("com.alipay.rpc.common.service.facade.pb.SampleServicePb:1.0") 28 | .method("hello").data(sub_req); 29 | antflash::Session session; 30 | session.send(request).to(channel).receiveTo(response).async( 31 | [&sub_rsp](antflash::ESessionError err, antflash::ResponseBase* rsp) { 32 | if (err != antflash::ESessionError::SESSION_OK) { 33 | LOG_INFO("get response fail:{}", antflash::Session::getErrText(err)); 34 | return; 35 | } 36 | LOG_INFO("response:{}", sub_rsp.result()); 37 | } 38 | ); 39 | 40 | std::this_thread::sleep_for(std::chrono::seconds(1)); 41 | 42 | antflash::globalDestroy(); 43 | 44 | return 0; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcc:4 2 | 3 | RUN echo "deb http://ftp.debian.org/debian jessie-backports main" > /etc/apt/sources.list.d/jessie-backports.list \ 4 | && apt-get update && apt-get -t jessie-backports install -y --no-install-recommends \ 5 | cmake \ 6 | && apt-get clean \ 7 | && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/jessie-backports.list 8 | RUN wget https://cmake.org/files/v3.12/cmake-3.12.1.tar.gz 9 | RUN tar xvf cmake-3.12.1.tar.gz 10 | RUN ls 11 | RUN cd cmake-3.12.1 12 | WORKDIR cmake-3.12.1 13 | RUN gcc --version 14 | RUN ./configure 15 | RUN make 16 | RUN make install 17 | RUN /usr/local/bin/cmake --version 18 | 19 | RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-cpp-3.6.1.tar.gz 20 | RUN tar xvf protobuf-cpp-3.6.1.tar.gz 21 | RUN ls 22 | WORKDIR protobuf-3.6.1 23 | RUN ./configure 24 | RUN make 25 | RUN make install 26 | RUN ldconfig 27 | 28 | 29 | RUN wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz 30 | RUN tar xvf release-1.8.0.tar.gz 31 | RUN cd googletest-release-1.8.0 32 | WORKDIR googletest-release-1.8.0 33 | RUN cmake . 34 | RUN make 35 | RUN make install 36 | 37 | 38 | ENV TIME_ZONE=Asia/Shanghai 39 | 40 | RUN \ 41 | mkdir -p /usr/src/app \ 42 | && echo "${TIME_ZONE}" > /etc/timezone \ 43 | && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime 44 | 45 | RUN protoc --version 46 | RUN gcc --version 47 | RUN cmake --version 48 | COPY . /usr/src/myapp 49 | WORKDIR /usr/src/myapp 50 | RUN ls 51 | RUN cmake . 52 | RUN make 53 | RUN ls 54 | CMD ["./cloud_rpc_demo"] -------------------------------------------------------------------------------- /include/common/time_thread.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/5/9. 3 | // 4 | 5 | 6 | #ifndef RPC_SCHEDULE_TIME_THREAD_H 7 | #define RPC_SCHEDULE_TIME_THREAD_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace antflash { 20 | 21 | struct Task; 22 | struct TaskContainer; 23 | 24 | using TimerTaskFn = std::function; 25 | 26 | class TimeThread { 27 | public: 28 | TimeThread(); 29 | ~TimeThread(); 30 | bool init(); 31 | void destroy(); 32 | size_t schedule(size_t timeout, TimerTaskFn fn); 33 | size_t scheduleAbs(size_t abs_time, TimerTaskFn&& fn); 34 | bool unschedule(size_t task_id); 35 | private: 36 | void loopOnce(); 37 | TaskContainer* getLocalTaskContainer(); 38 | void collectUnschedule(TaskContainer*); 39 | void collectUnschedule(); 40 | 41 | std::unique_ptr _td; 42 | std::atomic _exit; 43 | std::mutex _wake_mtx; 44 | std::condition_variable _wake_cond; 45 | 46 | std::mutex _tasks_mtx; 47 | std::vector _containers; 48 | pthread_key_t _local_task; 49 | 50 | std::atomic _nearest_run_time; 51 | std::atomic _timer_id; 52 | 53 | std::unordered_set _active_ids; 54 | std::unique_ptr> _tasks; 55 | }; 56 | 57 | } 58 | 59 | #endif //RPC_SCHEDULE_TIME_THREAD_H 60 | -------------------------------------------------------------------------------- /test/unit_test/simple_socket_server.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/25. 3 | // 4 | 5 | #include "simple_socket_server.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace antflash { 15 | 16 | bool SimpleSocketServer::run(int port) { 17 | 18 | int ss = socket(AF_INET, SOCK_STREAM, 0); 19 | 20 | struct sockaddr_in server_sockaddr; 21 | server_sockaddr.sin_family = AF_INET; 22 | server_sockaddr.sin_port = htons(port); 23 | 24 | server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 25 | if(bind(ss, (struct sockaddr* ) &server_sockaddr, sizeof(server_sockaddr))==-1) { 26 | return false; 27 | } 28 | if(listen(ss, 20) == -1) { 29 | return false; 30 | } 31 | 32 | struct sockaddr_in client_addr; 33 | socklen_t length = sizeof(client_addr); 34 | ///成功返回非负描述字,出错返回-1 35 | auto conn = accept(ss, (struct sockaddr*)&client_addr, &length); 36 | if( conn < 0 ) { 37 | return false; 38 | } 39 | 40 | //char buffer[1024]; 41 | while(!_exit) { 42 | //memset(buffer, 0 ,sizeof(buffer)); 43 | //int len = recv(conn, buffer, sizeof(buffer), 0); 44 | //if(strcmp(buffer, "exit\n") == 0) break; 45 | //必须要有返回数据, 这样才算一个完整的请求 46 | //send(conn, buffer, len , 0); 47 | std::this_thread::sleep_for(std::chrono::seconds(1)); 48 | } 49 | close(conn); 50 | close(ss); 51 | return true; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /include/common/uri.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #ifndef RPC_COMMON_URI_H 6 | #define RPC_COMMON_URI_H 7 | 8 | #include 9 | 10 | namespace antflash { 11 | 12 | class URI { 13 | public: 14 | URI() : _parse_ok(true) {} 15 | URI(const std::string& url) : _parse_ok(true) { 16 | parseFromString(url); 17 | } 18 | URI(const char* url) : _parse_ok(true) { 19 | parseFromString(url); 20 | } 21 | ~URI() {} 22 | 23 | void parseFromString(const char*); 24 | void parseFromString(const std::string& url) { 25 | parseFromString(url.c_str()); 26 | } 27 | 28 | URI& operator=(const std::string& url) { 29 | parseFromString(url); 30 | return *this; 31 | } 32 | URI& operator=(const char* url) { 33 | parseFromString(url); 34 | return *this; 35 | } 36 | 37 | const std::string& path() const { 38 | return _path; 39 | } 40 | 41 | const std::string& host() const { 42 | return _host; 43 | } 44 | 45 | int port() const { 46 | return _port; 47 | } 48 | 49 | void clear() { 50 | _schema.clear(); 51 | _host.clear(); 52 | _path.clear(); 53 | _user_info.clear(); 54 | _port = -1; 55 | _parse_ok = true; 56 | } 57 | 58 | bool parse_ok() const { 59 | return _parse_ok; 60 | } 61 | 62 | private: 63 | std::string _schema; 64 | std::string _host; 65 | std::string _path; 66 | std::string _user_info; 67 | std::string _query; 68 | std::string _fragment; 69 | int _port; 70 | bool _parse_ok; 71 | }; 72 | 73 | 74 | } 75 | 76 | #endif //RPC_COMMON_URI_H 77 | -------------------------------------------------------------------------------- /src/common/log.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/10. 3 | // 4 | 5 | #ifndef RPC_COMMON_LOG_H 6 | #define RPC_COMMON_LOG_H 7 | 8 | #include 9 | #include "common/common_defines.h" 10 | 11 | namespace antflash { 12 | 13 | class LogMessage { 14 | public: 15 | LogMessage(LogLevel level, const char* filename, int line): 16 | _level(level), _filename(filename), _line(line) {} 17 | ~LogMessage() { 18 | finish(); 19 | } 20 | 21 | template 22 | void print(Args&& ...args) { 23 | _message = fmt::format(std::forward(args)...); 24 | }; 25 | 26 | static void setLogLevel(LogLevel level) { 27 | _s_min_level = level; 28 | } 29 | 30 | private: 31 | void finish(); 32 | 33 | LogLevel _level; 34 | const char* _filename; 35 | int _line; 36 | std::string _message; 37 | 38 | static LogLevel _s_min_level; 39 | }; 40 | 41 | } 42 | 43 | #ifndef __FILENAME__ 44 | #define __FILENAME__ __FILE__ 45 | #endif 46 | 47 | #define LOG(LEVEL, FORMAT, ARGS...) \ 48 | antflash::LogMessage(antflash::LogLevel::LOG_LEVEL_##LEVEL, __FILENAME__, __LINE__).print(FORMAT, ##ARGS) 49 | 50 | #define LOG_IF(LEVEL, CONDITION, FORMAT, ARGS...) \ 51 | !(CONDITION) ? (void)0 : LOG(LEVEL, FORMAT, ##ARGS) 52 | 53 | #define LOG_DEBUG(FORMAT, ARGS...) \ 54 | LOG(DEBUG, FORMAT, ##ARGS) 55 | 56 | #define LOG_INFO(FORMAT, ARGS...) \ 57 | LOG(INFO, FORMAT, ##ARGS) 58 | 59 | #define LOG_WARN(FORMAT, ARGS...) \ 60 | LOG(WARNING, FORMAT, ##ARGS) 61 | 62 | #define LOG_ERROR(FORMAT, ARGS...) \ 63 | LOG(ERROR, FORMAT, ##ARGS) 64 | 65 | #define LOG_FATAL(FORMAT, ARGS...) \ 66 | LOG(FATAL, FORMAT, ##ARGS) 67 | 68 | #endif //RPC_COMMON_LOG_H 69 | -------------------------------------------------------------------------------- /src/schedule/schedule.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/9. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_SCHEDULE_H 6 | #define RPC_INCLUDE_SCHEDULE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "common/time_thread.h" 13 | 14 | namespace antflash { 15 | 16 | class LoopThread; 17 | 18 | class Schedule { 19 | public: 20 | using Handler = std::function; 21 | 22 | //Singleton 23 | static Schedule& getInstance() { 24 | static Schedule schedule; 25 | return schedule; 26 | } 27 | 28 | bool init(int32_t schedule_num = -1); 29 | void destroy_schedule(); 30 | void destroy_time_schedule(); 31 | 32 | //TODO change fd + handler to a class 33 | bool addSchedule(int fd, int events, int idx = -1) { 34 | return addScheduleInternal(fd, events, nullptr, idx); 35 | } 36 | 37 | bool addSchedule(int fd, int events, Handler &handler, int idx = -1) { 38 | return addScheduleInternal(fd, events, (void*)&handler, idx); 39 | } 40 | 41 | void removeSchedule(int fd, int events, int idx = -1); 42 | 43 | size_t addTimeschdule(size_t abs_time, TimerTaskFn&& fn) { 44 | return _time_thread->scheduleAbs(abs_time, std::move(fn)); 45 | } 46 | 47 | bool removeTimeschdule(size_t time_task_id) { 48 | return _time_thread->unschedule(time_task_id); 49 | } 50 | 51 | size_t scheduleThreadSize() const; 52 | 53 | private: 54 | bool addScheduleInternal(int fd, int events, void *handler, int idx); 55 | 56 | Schedule(); 57 | ~Schedule(); 58 | 59 | std::vector _threads; 60 | std::unique_ptr _time_thread; 61 | }; 62 | 63 | 64 | 65 | } 66 | 67 | #endif //RPC_INCLUDE_SCHEDULE_H 68 | -------------------------------------------------------------------------------- /src/protocol/protocol_define.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/4. 3 | // 4 | 5 | #include "protocol/protocol_define.h" 6 | #include 7 | #include "bolt/bolt_protocol.h" 8 | #include "http/http_protocol.h" 9 | 10 | namespace antflash { 11 | 12 | static Protocol s_protocol[static_cast(EProtocolType::PROTOCOL_TOTAL)]; 13 | 14 | void registerProtocol(EProtocolType type, const Protocol& protocol) { 15 | s_protocol[static_cast(type)] = protocol; 16 | } 17 | 18 | const Protocol* getProtocol(EProtocolType type) { 19 | return &s_protocol[static_cast(type)]; 20 | } 21 | 22 | class GlobalProtocolInitializer { 23 | public: 24 | GlobalProtocolInitializer() { 25 | Protocol bolt_protocol = {bolt::assembleBoltRequest, 26 | bolt::parseBoltProtocol, 27 | bolt::parseBoltResponse, 28 | bolt::assembleHeartbeatRequest, 29 | bolt::parseHeartbeatResponse, 30 | bolt::converseBoltRequest, 31 | EProtocolType::PROTOCOL_BOLT}; 32 | 33 | registerProtocol(EProtocolType::PROTOCOL_BOLT, bolt_protocol); 34 | 35 | Protocol http_protocol = {http::assembleHttpRequest, 36 | http::parseHttpProtocol, 37 | http::parseHttpResponse, 38 | nullptr, 39 | nullptr, 40 | nullptr, 41 | EProtocolType::PROTOCOL_HTTP}; 42 | registerProtocol(EProtocolType::PROTOCOL_HTTP, http_protocol); 43 | } 44 | 45 | ~GlobalProtocolInitializer() {} 46 | }; 47 | 48 | static GlobalProtocolInitializer s_global_initializer; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /second_party/fmt/src/format.cc: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #include "fmt/format-inl.h" 9 | 10 | FMT_BEGIN_NAMESPACE 11 | namespace internal { 12 | // Force linking of inline functions into the library. 13 | std::string (*vformat_ref)(string_view, format_args) = vformat; 14 | std::wstring (*vformat_wref)(wstring_view, wformat_args) = vformat; 15 | } 16 | 17 | template struct internal::basic_data; 18 | 19 | // Explicit instantiations for char. 20 | 21 | template FMT_API char internal::thousands_sep(locale_provider *lp); 22 | 23 | template void basic_fixed_buffer::grow(std::size_t); 24 | 25 | template void internal::arg_map::init( 26 | const basic_format_args &args); 27 | 28 | template FMT_API int internal::char_traits::format_float( 29 | char *buffer, std::size_t size, const char *format, int precision, 30 | double value); 31 | 32 | template FMT_API int internal::char_traits::format_float( 33 | char *buffer, std::size_t size, const char *format, int precision, 34 | long double value); 35 | 36 | // Explicit instantiations for wchar_t. 37 | 38 | template FMT_API wchar_t internal::thousands_sep(locale_provider *lp); 39 | 40 | template void basic_fixed_buffer::grow(std::size_t); 41 | 42 | template void internal::arg_map::init( 43 | const basic_format_args &args); 44 | 45 | template FMT_API int internal::char_traits::format_float( 46 | wchar_t *buffer, std::size_t size, const wchar_t *format, 47 | int precision, double value); 48 | 49 | template FMT_API int internal::char_traits::format_float( 50 | wchar_t *buffer, std::size_t size, const wchar_t *format, 51 | int precision, long double value); 52 | FMT_END_NAMESPACE 53 | -------------------------------------------------------------------------------- /include/protocol/http/http_request.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_HTTP_REQUEST_H 6 | #define RPC_INCLUDE_HTTP_REQUEST_H 7 | 8 | #include 9 | #include 10 | #include "protocol/request_base.h" 11 | #include "common/uri.h" 12 | #include "http_header.h" 13 | 14 | namespace antflash { 15 | 16 | class IOBuffer; 17 | namespace http { 18 | class HttpInternalRequest; 19 | } 20 | 21 | class HttpRequest : public RequestBase { 22 | friend class http::HttpInternalRequest; 23 | public: 24 | HttpRequest() { 25 | } 26 | 27 | const URI& uri() const { 28 | return _uri; 29 | } 30 | 31 | HttpRequest& uri(const std::string& uri) { 32 | _uri = uri; 33 | return *this; 34 | } 35 | HttpRequest& method(EHttpMethod method) { 36 | _method = method; 37 | return *this; 38 | } 39 | HttpRequest& attach(const std::string& data); 40 | HttpRequest& attach(const char* data); 41 | std::shared_ptr attach() const; 42 | size_t attach_size() const; 43 | 44 | HttpRequest& attach_type(const std::string& type) { 45 | _attch_type = type; 46 | return *this; 47 | } 48 | HttpRequest& attach_type(EHttpHeaderKey type) { 49 | _attch_type = HttpHeader::getHeaderKey(type); 50 | return *this; 51 | } 52 | 53 | const std::string& attach_type() const { 54 | return _attch_type; 55 | } 56 | 57 | //current 1.1 58 | //HttpRequest& version(int32_t major, int32_t minor) { 59 | // return *this; 60 | //} 61 | 62 | private: 63 | enum class EDataType { 64 | CSTRING, 65 | STRING, 66 | }; 67 | 68 | URI _uri; 69 | EHttpMethod _method; 70 | std::string _attch_type; 71 | std::shared_ptr _data; 72 | }; 73 | 74 | } 75 | 76 | #endif //RPC_INCLUDE_HTTP_REQUEST_H 77 | -------------------------------------------------------------------------------- /src/schedule/epoll_loop.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/9. 3 | // 4 | 5 | #include "loop.h" 6 | #include 7 | #include 8 | #include "common/common_defines.h" 9 | 10 | namespace antflash { 11 | 12 | using ScheduleHandler = std::function; 13 | 14 | struct LoopInternalData { 15 | struct epoll_event events[MAX_POLL_EVENT]; 16 | }; 17 | 18 | Loop::Loop() { 19 | 20 | } 21 | 22 | Loop::~Loop() { 23 | 24 | } 25 | 26 | bool Loop::init() { 27 | _backend_fd = epoll_create(1024*1024); 28 | 29 | 30 | if (!base::set_close_on_exec(_backend_fd.fd())) { 31 | return false; 32 | } 33 | 34 | if (_backend_fd.fd() == -1) { 35 | return false; 36 | } 37 | 38 | _data.reset(new LoopInternalData); 39 | return true; 40 | } 41 | 42 | void Loop::destroy() { 43 | 44 | } 45 | 46 | void Loop::loop_once() { 47 | auto actives = epoll_wait(_backend_fd.fd(), _data->events, MAX_POLL_EVENT, -1); 48 | 49 | if (actives == -1 && errno != EINTR) { 50 | //ERROR 51 | return; 52 | } 53 | 54 | for (auto i = 0; i < actives; ++i) { 55 | struct epoll_event& ke = _data->events[i]; 56 | if (ke.data.ptr) { 57 | auto handler = static_cast(ke.data.ptr); 58 | (*handler)(); 59 | } 60 | } 61 | } 62 | 63 | bool Loop::add_event(int fd, int events, void* handler) { 64 | struct epoll_event ev; 65 | memset(&ev, 0, sizeof(ev)); 66 | ev.events = events | EPOLLET; 67 | ev.data.ptr = handler; 68 | 69 | return 0 == epoll_ctl(_backend_fd.fd(), EPOLL_CTL_ADD, fd, &ev); 70 | } 71 | 72 | void Loop::remove_event(int fd, int events) { 73 | struct epoll_event ev; 74 | memset(&ev, 0, sizeof(ev)); 75 | ev.events = events | EPOLLET; 76 | ev.data.ptr = nullptr; 77 | 78 | epoll_ctl(_backend_fd.fd(), EPOLL_CTL_DEL, fd, &ev); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/tcp/socket_base.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/2. 3 | // 4 | 5 | #ifndef RPC_TCP_SOCKET_BASE_H 6 | #define RPC_TCP_SOCKET_BASE_H 7 | 8 | #include //std::swap before c++11 9 | #include //std::swap in c++11 10 | #include 11 | #include 12 | #include 13 | #include "tcp/endpoint.h" 14 | 15 | namespace antflash { 16 | 17 | namespace base { 18 | //RAII 19 | class FdGuard { 20 | public: 21 | FdGuard() : _fd(-1) {} 22 | 23 | FdGuard(int fd) : _fd(fd) {} 24 | 25 | ~FdGuard() { 26 | release(); 27 | } 28 | 29 | FdGuard(const FdGuard &) = delete; 30 | 31 | FdGuard(FdGuard&& right) : _fd(right._fd) { 32 | right._fd = -1; 33 | } 34 | 35 | FdGuard& operator=(const FdGuard&) = delete; 36 | 37 | FdGuard& operator=(FdGuard&& right) { 38 | if (&right != this) { 39 | _fd = right._fd; 40 | right._fd = -1; 41 | } 42 | 43 | return *this; 44 | } 45 | 46 | FdGuard& operator=(int fd) { 47 | _fd = fd; 48 | return *this; 49 | } 50 | 51 | inline void swap(FdGuard &right) { 52 | std::swap(right._fd, _fd); 53 | } 54 | 55 | inline int fd() const { 56 | return _fd; 57 | } 58 | 59 | void release() { 60 | if (_fd >= 0) { 61 | close(_fd); 62 | } 63 | _fd = -1; 64 | } 65 | 66 | int handover() { 67 | int fd = _fd; 68 | _fd = -1; 69 | return fd; 70 | } 71 | 72 | private: 73 | int _fd; 74 | }; 75 | 76 | FdGuard create_socket(); 77 | bool prepare_socket(FdGuard& fd); 78 | int connect(FdGuard& fd, EndPoint& remote); 79 | bool connected(FdGuard& fd); 80 | 81 | bool set_blocking(int fd); 82 | bool set_non_blocking(int fd); 83 | bool set_close_on_exec(int fd); 84 | bool set_no_delay(int socket); 85 | 86 | } 87 | } 88 | 89 | #endif //RPC_TCP_SOCKET_BASE_H 90 | -------------------------------------------------------------------------------- /src/tcp/endpoint.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/2. 3 | // 4 | 5 | #include "tcp/endpoint.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "common/utils.h" 11 | 12 | namespace antflash { 13 | 14 | bool EndPoint::parseFromString(const char *str) { 15 | constexpr auto STR_MAX_SIZE = 64; 16 | char inner_str[STR_MAX_SIZE]; 17 | const char* p = str; 18 | size_t i = 0; 19 | while (':' != *p) { 20 | inner_str[i++] = *p; 21 | if (i >= STR_MAX_SIZE || '\0' == *p) { 22 | return false; 23 | } 24 | ++p; 25 | } 26 | 27 | inner_str[i] = '\0'; 28 | //Utils::trim(inner_str); 29 | 30 | //try parse ip 31 | int rc = inet_pton(AF_INET, inner_str, &ip); 32 | if (rc <= 0) { 33 | //try parse host name 34 | struct hostent *result = nullptr; 35 | #if defined(OS_MACOSX) 36 | result = gethostbyname(inner_str); 37 | #else 38 | char buf[1024]; 39 | int error = 0; 40 | struct hostent ent; 41 | if (gethostbyname_r(inner_str, &ent, buf, sizeof(buf), 42 | &result, &error) != 0) { 43 | return false; 44 | } 45 | #endif // defined(__APPLE__) 46 | if (nullptr == result) { 47 | return false; 48 | } 49 | memmove(&ip, result->h_addr, result->h_length); 50 | } 51 | 52 | ++p; //skip ':' 53 | char* end = nullptr; 54 | port = std::strtol(p, &end, 10); 55 | if (end == p || *end) { 56 | return false; 57 | } 58 | if (port < 0 || port > 65535) { 59 | return false; 60 | } 61 | 62 | return true; 63 | } 64 | 65 | std::string EndPoint::ipToStr() const { 66 | char tmp_ip[INET_ADDRSTRLEN]; 67 | char tmp[INET_ADDRSTRLEN + 16]; 68 | inet_ntop(AF_INET, &ip, tmp_ip, INET_ADDRSTRLEN); 69 | sprintf(tmp, "%s:%d", tmp_ip, port); 70 | return std::string(tmp); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /include/protocol/http/http_response.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_HTTP_RESPONSE_H 6 | #define RPC_INCLUDE_HTTP_RESPONSE_H 7 | 8 | #include 9 | #include "http_header.h" 10 | #include "protocol/response_base.h" 11 | 12 | struct http_parser; 13 | 14 | namespace antflash { 15 | 16 | enum EHttpStatus { 17 | HTTP_STATUS_CONTINUE = 100, 18 | HTTP_STATUS_OK = 200, 19 | HTTP_STATUS_CREATED = 201, 20 | HTTP_STATUS_ACCEPTED = 202, 21 | HTTP_STATUS_BAD_REQUEST = 400, 22 | HTTP_STATUS_FORBIDDEN = 403, 23 | HTTP_STATUS_NOT_FOUND = 404, 24 | }; 25 | 26 | class HttpResponse : public ResponseBase { 27 | public: 28 | HttpResponse() : _status(HTTP_STATUS_OK), _parser(nullptr) {} 29 | ~HttpResponse(); 30 | 31 | const std::string& body() const { 32 | return _body; 33 | } 34 | 35 | EResParseResult deserialize(IOBuffer &buffer) noexcept override; 36 | static EResParseResult checkHeader( 37 | IOBuffer &buffer, 38 | size_t& data_size, 39 | size_t& request_id, 40 | void** parser); 41 | 42 | void setHttpParser(void*); 43 | 44 | static int onMessageBegin(struct http_parser*); 45 | static int onUrl(struct http_parser *, const char *, size_t); 46 | static int onStatus(struct http_parser*, const char *, size_t); 47 | static int onHeaderField(struct http_parser *, const char *, size_t); 48 | static int onHeaderValue(struct http_parser *, const char *, size_t); 49 | static int onHeadersComplete(struct http_parser *); 50 | static int onBodyCb(struct http_parser*, const char *, size_t); 51 | static int onMessageCompleteCb(struct http_parser *); 52 | 53 | private: 54 | EHttpStatus _status; 55 | struct http_parser* _parser; 56 | 57 | std::string _url; 58 | std::string _body; 59 | std::unordered_map _header; 60 | }; 61 | 62 | } 63 | 64 | 65 | #endif //RPC_INCLUDE_HTTP_RESPONSE_H 66 | -------------------------------------------------------------------------------- /include/protocol/bolt/bolt_request.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/4. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_BOLT_REQUEST_H 6 | #define RPC_INCLUDE_BOLT_REQUEST_H 7 | 8 | #include 9 | #include 10 | #include "protocol/request_base.h" 11 | #include "common/common_defines.h" 12 | 13 | namespace antflash { 14 | 15 | namespace bolt { 16 | class BoltInternalRequest; 17 | } 18 | 19 | class BoltRequest final : public RequestBase { 20 | friend class bolt::BoltInternalRequest; 21 | public: 22 | BoltRequest() : _data_type(EDataType::NONE) { 23 | _data.str = nullptr; 24 | } 25 | 26 | virtual ~BoltRequest() {} 27 | 28 | inline BoltRequest& service(const std::string& service) { 29 | _service = service; 30 | return *this; 31 | } 32 | inline BoltRequest& method(const std::string& method) { 33 | _method = method; 34 | return *this; 35 | } 36 | inline BoltRequest& traceId(const std::string& id) { 37 | _trace_id = id; 38 | return *this; 39 | } 40 | inline BoltRequest& data(const std::string& data) { 41 | _data.str = &data; 42 | _data_type = EDataType::STRING; 43 | return *this; 44 | } 45 | inline BoltRequest& data(const char* data) { 46 | _data.c_str = data; 47 | _data_type = EDataType::CSTRING; 48 | return *this; 49 | } 50 | inline BoltRequest& data(const google::protobuf::Message& data) { 51 | _data.proto = &data; 52 | _data_type = EDataType::PROTOBUF; 53 | return *this; 54 | } 55 | 56 | private: 57 | enum class EDataType { 58 | CSTRING, 59 | STRING, 60 | PROTOBUF, 61 | NONE 62 | }; 63 | 64 | std::string _service; 65 | std::string _method; 66 | std::string _trace_id; 67 | 68 | union { 69 | const char* c_str; 70 | const std::string* str; 71 | const google::protobuf::Message* proto; 72 | } _data; 73 | 74 | EDataType _data_type; 75 | }; 76 | 77 | } 78 | 79 | #endif //RPC_INCLUDE_BOLT_REQUEST_H 80 | -------------------------------------------------------------------------------- /test/unit_test/utils_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/3. 3 | // 4 | 5 | #include "common/utils.h" 6 | #include 7 | 8 | TEST(UtilsTest, leftTrim) { 9 | char str1[64] = " I love u "; 10 | antflash::Utils::leftTrim(str1); 11 | ASSERT_STREQ(str1, "I love u "); 12 | char str2[64] = " I love u "; 13 | antflash::Utils::leftTrim(str2); 14 | ASSERT_STREQ(str2, "I love u "); 15 | char str3[64] = "I love u "; 16 | antflash::Utils::leftTrim(str3); 17 | ASSERT_STREQ(str3, "I love u "); 18 | char str4[64] = "I love u"; 19 | antflash::Utils::leftTrim(str4); 20 | ASSERT_STREQ(str4, "I love u"); 21 | char str5[64] = "I love u "; 22 | antflash::Utils::leftTrim(str5); 23 | ASSERT_STREQ(str5, "I love u "); 24 | char str6[64] = "I love u"; 25 | antflash::Utils::leftTrim(str6); 26 | ASSERT_STREQ(str6, "I love u"); 27 | char str7[64] = "\0"; 28 | antflash::Utils::leftTrim(str7); 29 | ASSERT_STREQ(str7, "\0"); 30 | char* p = nullptr; 31 | antflash::Utils::leftTrim(p); 32 | ASSERT_STREQ(p, nullptr); 33 | } 34 | 35 | TEST(UtilsTest, rightTrim) { 36 | char str1[64] = " I love u "; 37 | antflash::Utils::rightTrim(str1); 38 | ASSERT_STREQ(str1, " I love u"); 39 | char str2[64] = " I love u "; 40 | antflash::Utils::rightTrim(str2); 41 | ASSERT_STREQ(str2, " I love u"); 42 | char str3[64] = "I love u "; 43 | antflash::Utils::rightTrim(str3); 44 | ASSERT_STREQ(str3, "I love u"); 45 | char str4[64] = "I love u"; 46 | antflash::Utils::rightTrim(str4); 47 | ASSERT_STREQ(str4, "I love u"); 48 | char str5[64] = "I love u "; 49 | antflash::Utils::rightTrim(str5); 50 | ASSERT_STREQ(str5, "I love u"); 51 | char str6[64] = "I love u"; 52 | antflash::Utils::rightTrim(str6); 53 | ASSERT_STREQ(str6, "I love u"); 54 | char str7[64] = "\0"; 55 | antflash::Utils::rightTrim(str7); 56 | ASSERT_STREQ(str7, "\0"); 57 | char* p = nullptr; 58 | antflash::Utils::rightTrim(p); 59 | ASSERT_STREQ(p, nullptr); 60 | } 61 | -------------------------------------------------------------------------------- /src/schedule/schedule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/9. 3 | // 4 | 5 | #include "schedule.h" 6 | #include "loop_thread.h" 7 | #include "common/log.h" 8 | 9 | namespace antflash { 10 | 11 | Schedule::Schedule() { 12 | 13 | } 14 | 15 | Schedule::~Schedule() { 16 | destroy_schedule(); 17 | destroy_time_schedule(); 18 | } 19 | 20 | bool Schedule::init(int32_t schedule_num) { 21 | if (schedule_num <= 0) { 22 | schedule_num = std::thread::hardware_concurrency(); 23 | } 24 | _threads.resize(schedule_num); 25 | bool ret = true; 26 | for (auto& thread : _threads) { 27 | if (!thread.start()) { 28 | ret = false; 29 | break; 30 | } 31 | } 32 | 33 | if (!ret) { 34 | destroy_schedule(); 35 | } 36 | 37 | _time_thread.reset(new TimeThread); 38 | ret = _time_thread->init(); 39 | if (!ret) { 40 | destroy_schedule(); 41 | destroy_time_schedule(); 42 | } 43 | 44 | return ret; 45 | } 46 | 47 | void Schedule::destroy_schedule() { 48 | for (auto& thread : _threads) { 49 | thread.stop(); 50 | } 51 | _threads.clear(); 52 | } 53 | void Schedule::destroy_time_schedule() { 54 | if (_time_thread) { 55 | _time_thread->destroy(); 56 | _time_thread.reset(); 57 | } 58 | } 59 | 60 | bool Schedule::addScheduleInternal(int fd, int events, void *handler, int idx) { 61 | if (_threads.size() == 0) { 62 | return false; 63 | } 64 | if (idx > 0) { 65 | return _threads[idx % _threads.size()].add_event(fd, events, handler); 66 | } else { 67 | return _threads[fd % _threads.size()].add_event(fd, events, handler); 68 | } 69 | } 70 | 71 | void Schedule::removeSchedule(int fd, int events, int idx) { 72 | if (_threads.size() == 0) { 73 | return; 74 | } 75 | if (idx > 0) { 76 | _threads[idx % _threads.size()].remove_event(fd, events); 77 | } else { 78 | _threads[fd % _threads.size()].remove_event(fd, events); 79 | } 80 | } 81 | 82 | size_t Schedule::scheduleThreadSize() const { 83 | return _threads.size(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /include/protocol/protocol_define.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/3. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_PROTOCOL_DEFINE_H 6 | #define RPC_INCLUDE_PROTOCOL_DEFINE_H 7 | 8 | #include 9 | #include 10 | #include "request_base.h" 11 | #include "response_base.h" 12 | 13 | namespace antflash { 14 | 15 | class IOBuffer; 16 | 17 | enum class EProtocolType { 18 | PROTOCOL_BOLT, 19 | PROTOCOL_HTTP, 20 | PROTOCOL_TOTAL 21 | }; 22 | 23 | struct Protocol { 24 | public: 25 | //Assemble request with unique request id in process to io buffer 26 | using AssembleRequestFn = bool(*)(const RequestBase&, size_t request_id, IOBuffer&); 27 | AssembleRequestFn assemble_request_fn; 28 | 29 | //Parse io buffer data to check if data fits specific protocol. 30 | //If ok, returns total size of the response data and unique request id. 31 | //If io buffer data is copied in this method, response data means the whole data 32 | //and parse_response_fn will parse from begin. 33 | //Else response data means left data parse_response_fn need to parse, and parsed data 34 | //is transfer by @data 35 | using ParseProtocolFn = EResParseResult (*) (IOBuffer&, size_t&, size_t&, void** data); 36 | ParseProtocolFn parse_protocol_fn; 37 | 38 | //Parse the whole response data from io buffer, @data contains parsed data by parse_protocol_fn 39 | using ParseResponseFn = EResParseResult (*)(ResponseBase&, IOBuffer&, void* data); 40 | ParseResponseFn parse_response_fn; 41 | 42 | //Assemble heartbeat request and response 43 | using AssembleHeartbeatFn = bool(*)( 44 | std::shared_ptr&, std::shared_ptr&); 45 | AssembleHeartbeatFn assemble_heartbeat_fn; 46 | 47 | //Verify heartbeat response 48 | using ParseHeartbeatFn = bool(*)(std::shared_ptr&); 49 | ParseHeartbeatFn parse_heartbeat_fn; 50 | 51 | using ConverseRequestIdFn = size_t (*)(size_t); 52 | ConverseRequestIdFn converse_request_fn; 53 | 54 | EProtocolType type; 55 | }; 56 | 57 | const Protocol* getProtocol(EProtocolType type); 58 | 59 | } 60 | 61 | #endif //RPC_INCLUDE_PROTOCOL_DEFINE_H 62 | -------------------------------------------------------------------------------- /example/heartbeat_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "rpc.h" 7 | #include "../src/common/log.h" 8 | 9 | int main() { 10 | if (!antflash::globalInit()) { 11 | LOG_INFO("global init fail."); 12 | return -1; 13 | } 14 | 15 | antflash::ChannelOptions options; 16 | options.connection_type = 17 | antflash::EConnectionType::CONNECTION_TYPE_POOLED; 18 | options.max_retry = 1; 19 | antflash::Channel channel; 20 | if (!channel.init("127.0.0.1:12200", &options)) { 21 | LOG_INFO("channel init fail."); 22 | return -1; 23 | } 24 | 25 | antflash::BoltRequest request; 26 | std::vector> threads; 27 | std::vector exits; 28 | //size_t total_size = std::thread::hardware_concurrency(); 29 | size_t total_size = 3; 30 | for (size_t i = 0; i < total_size; ++i) { 31 | exits.push_back(false); 32 | threads.emplace_back( 33 | new std::thread([&channel, &request, &exits, i]() { 34 | std::default_random_engine e; 35 | std::uniform_int_distribution<> u(1, 20); 36 | while (!exits[i]) { 37 | antflash::BoltResponse response; 38 | antflash::Session session; 39 | LOG_INFO("thread[{}] begin heartbeat.", i); 40 | session.send(request).to(channel).receiveTo(response).sync(); 41 | 42 | if (session.failed()) { 43 | LOG_INFO("thread[{}] session fail: {}", i, session.getErrText()); 44 | } else if (response.isHeartbeat()) { 45 | LOG_INFO("thread[{}] session success, status[{}]", i, response.status()); 46 | } 47 | 48 | std::this_thread::sleep_for( 49 | std::chrono::seconds(u(e))); 50 | } 51 | })); 52 | } 53 | 54 | std::this_thread::sleep_for(std::chrono::seconds(86400)); 55 | for (size_t i = 0; i < total_size; ++i) { 56 | exits[i] = true; 57 | } 58 | 59 | for (size_t i = 0; i < total_size; ++i) { 60 | threads[i]->join(); 61 | } 62 | 63 | antflash::globalDestroy(); 64 | 65 | return 0; 66 | } 67 | 68 | -------------------------------------------------------------------------------- /src/tcp/socket_posix.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/8. 3 | // 4 | 5 | #include "socket_base.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace antflash { 12 | namespace base { 13 | 14 | bool set_non_blocking(int fd) { 15 | const int flags = fcntl(fd, F_GETFL, 0); 16 | if (flags < 0) { 17 | return false; 18 | } 19 | if (flags & O_NONBLOCK) { 20 | return true; 21 | } 22 | return 0 == fcntl(fd, F_SETFL, flags | O_NONBLOCK); 23 | } 24 | 25 | bool set_blocking(int fd) { 26 | const int flags = fcntl(fd, F_GETFL, 0); 27 | if (flags < 0) { 28 | return false; 29 | } 30 | if (flags & O_NONBLOCK) { 31 | return 0 == fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 32 | } 33 | return true; 34 | } 35 | 36 | bool set_close_on_exec(int fd) { 37 | return 0 == fcntl(fd, F_SETFD, FD_CLOEXEC); 38 | } 39 | 40 | bool set_no_delay(int socket) { 41 | int flag = 1; 42 | return 0 == setsockopt(socket, IPPROTO_TCP, 43 | TCP_NODELAY, (char*)&flag, sizeof(flag)); 44 | } 45 | 46 | 47 | FdGuard create_socket() { 48 | return FdGuard(socket(AF_INET, SOCK_STREAM, 0)); 49 | } 50 | 51 | bool prepare_socket(FdGuard& fd) { 52 | #if defined(OS_MACOSX) 53 | const int value = 1; 54 | setsockopt(fd.fd(), SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(int)); 55 | #endif 56 | return set_non_blocking(fd.fd()) 57 | && set_close_on_exec(fd.fd()) 58 | && set_no_delay(fd.fd()); 59 | } 60 | 61 | int connect(FdGuard& fd, EndPoint& remote) { 62 | struct sockaddr_in addr; 63 | bzero((char*)&addr, sizeof(addr)); 64 | addr.sin_family = AF_INET; 65 | addr.sin_addr = remote.ip; 66 | addr.sin_port = htons(remote.port); 67 | 68 | return connect(fd.fd(), (struct sockaddr*)&addr, sizeof(addr)); 69 | } 70 | 71 | bool connected(FdGuard& fd) { 72 | if (fd.fd() < 0) { 73 | return false; 74 | } 75 | 76 | int err = 0; 77 | socklen_t errlen = sizeof(err); 78 | if (getsockopt(fd.fd(), SOL_SOCKET, SO_ERROR, &err, &errlen) < 0) { 79 | return false; 80 | } 81 | 82 | if (err != 0) { 83 | return false; 84 | } 85 | 86 | return true; 87 | } 88 | 89 | } 90 | } -------------------------------------------------------------------------------- /src/schedule/loop_thread.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/9. 3 | // 4 | 5 | #include "loop_thread.h" 6 | #include 7 | #include 8 | #include "loop.h" 9 | #include "common/log.h" 10 | 11 | namespace antflash { 12 | 13 | LoopThread::LoopThread() : _exit(false) { 14 | } 15 | 16 | LoopThread::~LoopThread() { 17 | } 18 | 19 | 20 | LoopThread::LoopThread(LoopThread&& right) { 21 | _thread = std::move(right._thread); 22 | _exit.store(right._exit.load()); 23 | right._exit.store(false); 24 | } 25 | 26 | LoopThread& LoopThread::operator=(LoopThread&& right) { 27 | if (&right != this) { 28 | _thread = std::move(right._thread); 29 | _exit.store(right._exit.load()); 30 | right._exit.store(false); 31 | } 32 | 33 | return *this; 34 | } 35 | 36 | bool LoopThread::start() { 37 | int wakeup_fd[2]; 38 | wakeup_fd[0] = -1; 39 | wakeup_fd[1] = -1; 40 | if (pipe(wakeup_fd) != 0) { 41 | return false; 42 | } 43 | _wakeup_fds[0] = wakeup_fd[0]; 44 | _wakeup_fds[1] = wakeup_fd[1]; 45 | 46 | std::promise thread_ok; 47 | _thread.reset(new std::thread([this, &thread_ok](){ 48 | try { 49 | _loop.reset(new Loop()); 50 | if (!_loop->init()) { 51 | thread_ok.set_value(false); 52 | return; 53 | } 54 | 55 | thread_ok.set_value(true); 56 | 57 | while (!_exit.load(std::memory_order_acquire)) { 58 | _loop->loop_once(); 59 | } 60 | 61 | _loop->destroy(); 62 | } catch (const std::exception& ex) { 63 | thread_ok.set_exception(std::make_exception_ptr(ex)); 64 | } 65 | })); 66 | 67 | return thread_ok.get_future().get(); 68 | } 69 | 70 | void LoopThread::stop() { 71 | //memory barrier 72 | _exit.store(true, std::memory_order_release); 73 | //wakeup loop 74 | add_event(_wakeup_fds[1].fd(), POLLOUT, nullptr); 75 | 76 | if (_thread && _thread->joinable()) { 77 | _thread->join(); 78 | } 79 | } 80 | 81 | bool LoopThread::add_event(int fd, int events, void* handler) { 82 | return _loop->add_event(fd, events, handler); 83 | } 84 | 85 | void LoopThread::remove_event(int fd, int events) { 86 | _loop->remove_event(fd, events); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /test/unit_test/uri_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/25. 3 | // 4 | 5 | #include "common/uri.h" 6 | #include 7 | 8 | using namespace antflash; 9 | 10 | TEST(URITest, baseFunction) { 11 | { 12 | antflash::URI uri; 13 | ASSERT_TRUE(uri.parse_ok()); 14 | } 15 | { 16 | antflash::URI uri("https://127.0.0.1:8080/test"); 17 | ASSERT_TRUE(uri.parse_ok()); 18 | ASSERT_EQ(uri.path(), "/test"); 19 | ASSERT_EQ(uri.host(), "127.0.0.1"); 20 | ASSERT_EQ(uri.port(), 8080); 21 | uri = "https://127.0.0.1:8000/test2"; 22 | ASSERT_TRUE(uri.parse_ok()); 23 | ASSERT_EQ(uri.path(), "/test2"); 24 | ASSERT_EQ(uri.host(), "127.0.0.1"); 25 | ASSERT_EQ(uri.port(), 8000); 26 | } 27 | { 28 | std::string str("https://127.0.0.1:8080/test? abc "); 29 | antflash::URI uri(str); 30 | ASSERT_FALSE(uri.parse_ok()); 31 | ASSERT_EQ(uri.path(), "/test"); 32 | ASSERT_EQ(uri.host(), "127.0.0.1"); 33 | ASSERT_EQ(uri.port(), 8080); 34 | str = "https://127.0.0.1:8000/test"; 35 | uri = str; 36 | ASSERT_TRUE(uri.parse_ok()); 37 | ASSERT_EQ(uri.path(), "/test"); 38 | ASSERT_EQ(uri.host(), "127.0.0.1"); 39 | ASSERT_EQ(uri.port(), 8000); 40 | } 41 | 42 | { 43 | std::string str("https://127.0.0.1:8080/test? abc "); 44 | antflash::URI uri(str); 45 | ASSERT_FALSE(uri.parse_ok()); 46 | ASSERT_EQ(uri.path(), "/test"); 47 | ASSERT_EQ(uri.host(), "127.0.0.1"); 48 | ASSERT_EQ(uri.port(), 8080); 49 | str = "https://127.0.0.1:8000/test"; 50 | uri = str; 51 | ASSERT_TRUE(uri.parse_ok()); 52 | ASSERT_EQ(uri.path(), "/test"); 53 | ASSERT_EQ(uri.host(), "127.0.0.1"); 54 | ASSERT_EQ(uri.port(), 8000); 55 | } 56 | 57 | { 58 | antflash::URI uri("mailto:xxx@xxx.xx"); 59 | antflash::URI uri1("mailto:xxx@xxx.xx@yyy.yy"); 60 | antflash::URI uri2(" mailto:xxx@xxx.xx"); 61 | antflash::URI uri3(" m "); 62 | antflash::URI uri4("https://127.0.0.1:8080/test?user#a"); 63 | antflash::URI uri5("https://127.0.0.1:8080/test?user#a "); 64 | antflash::URI uri6("https://127.0.0.1:8080/test?user#a b"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /example/single_request_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "rpc.h" 5 | #include "sample.pb.h" 6 | #include "../src/common/log.h" 7 | 8 | int main() { 9 | if (!antflash::globalInit()) { 10 | LOG_ERROR("init fail."); 11 | return -1; 12 | } 13 | 14 | antflash::Channel channel; 15 | antflash::ChannelOptions options; 16 | options.connection_type = antflash::EConnectionType::CONNECTION_TYPE_POOLED; 17 | if (!channel.init("bar1.inc.alipay.net:12200", &options)) { 18 | printf("%s", "channel init fail."); 19 | return -1; 20 | } 21 | 22 | antflash::BoltRequest request; 23 | SampleServicePbRequest sub_req; 24 | 25 | SampleServicePbResult sub_rsp; 26 | antflash::BoltResponse response(sub_rsp); 27 | 28 | sub_req.set_name("zhenggu"); 29 | request.service("com.alipay.rpc.common.service.facade.pb.SampleServicePb:1.0") 30 | .method("hello").data(sub_req); 31 | antflash::Session session; 32 | session.send(request).to(channel).receiveTo(response).sync(); 33 | response.isHeartbeat(); 34 | 35 | if (session.failed()) { 36 | LOG_INFO("get response fail {}", session.getErrText()); 37 | } else { 38 | if (response.status() != antflash::BoltResponse::SUCCESS) { 39 | LOG_INFO("get response fail, response status:{}", response.status()); 40 | } else { 41 | LOG_INFO("get response {}, response:{}", 42 | session.getErrText(), sub_rsp.result()); 43 | } 44 | } 45 | 46 | session.reset(); 47 | 48 | antflash::PipelineSession pipe_session; 49 | constexpr size_t pipe_size = 10; 50 | pipe_session.reserve(pipe_size); 51 | SampleServicePbResult sub_rsps[pipe_size]; 52 | std::vector responses; 53 | responses.reserve(pipe_size); 54 | for (size_t i = 0; i < pipe_size; ++i) { 55 | responses.emplace_back(sub_rsps[i]); 56 | pipe_session.pipe(request, responses[i]); 57 | } 58 | pipe_session.to(channel).sync(); 59 | 60 | if (pipe_session.failed()) { 61 | LOG_INFO("get response fail {}", pipe_session.getErrText()); 62 | } else { 63 | for (size_t i = 0; i < pipe_size; ++i) { 64 | LOG_INFO("response:{}", sub_rsps[i].result()); 65 | } 66 | } 67 | 68 | std::this_thread::sleep_for( 69 | std::chrono::seconds(20)); 70 | 71 | antflash::globalDestroy(); 72 | 73 | return 0; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/schedule/kqueue_loop.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/9. 3 | // 4 | 5 | #include "loop.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "common/common_defines.h" 13 | 14 | namespace antflash { 15 | 16 | using ScheduleHandler = std::function; 17 | 18 | struct LoopInternalData { 19 | struct kevent events[MAX_POLL_EVENT]; 20 | }; 21 | 22 | Loop::Loop() { 23 | 24 | } 25 | 26 | Loop::~Loop() { 27 | 28 | } 29 | 30 | bool Loop::init() { 31 | _backend_fd = kqueue(); 32 | 33 | if (_backend_fd.fd() == -1) { 34 | return false; 35 | } 36 | 37 | if (!base::set_close_on_exec(_backend_fd.fd())) { 38 | return false; 39 | } 40 | 41 | _data.reset(new LoopInternalData); 42 | return true; 43 | } 44 | 45 | void Loop::destroy() { 46 | 47 | } 48 | 49 | void Loop::loop_once() { 50 | auto actives = kevent(_backend_fd.fd(), nullptr, 0, _data->events, MAX_POLL_EVENT, nullptr); 51 | 52 | if (actives == -1 && errno != EINTR) { 53 | //ERROR 54 | return; 55 | } 56 | 57 | for (auto i = 0; i < actives; ++i) { 58 | struct kevent& ke = _data->events[i]; 59 | if (ke.udata) { 60 | auto handler = static_cast(ke.udata); 61 | (*handler)(); 62 | } 63 | } 64 | } 65 | 66 | bool Loop::add_event(int fd, int events, void* handler) { 67 | struct timespec immediatelly; 68 | immediatelly.tv_nsec = 0; 69 | immediatelly.tv_sec = 0; 70 | struct kevent ev[2]; 71 | int n = 0; 72 | if (events & POLLIN) { 73 | EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE|EV_CLEAR, 0, 0, handler); 74 | } 75 | if (events & POLLOUT) { 76 | EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE|EV_CLEAR, 0, 0, handler); 77 | } 78 | 79 | return 0 == kevent(_backend_fd.fd(), ev, n, nullptr, 0, &immediatelly); 80 | } 81 | 82 | void Loop::remove_event(int fd, int events) { 83 | struct timespec immediatelly; 84 | immediatelly.tv_nsec = 0; 85 | immediatelly.tv_sec = 0; 86 | struct kevent ev[2]; 87 | int n = 0; 88 | if (events & POLLIN) { 89 | EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE|EV_DISABLE, 0, 0, nullptr); 90 | } 91 | if (events & POLLOUT) { 92 | EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE|EV_DISABLE, 0, 0, nullptr); 93 | } 94 | 95 | kevent(_backend_fd.fd(), ev, n, nullptr, 0, &immediatelly); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /test/unit_test/lifecyclelock_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/16. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace antflash; 10 | 11 | TEST(LifCycleLockTest, singleThread) { 12 | LifeCycleLock lock; 13 | 14 | ASSERT_TRUE(lock.tryShared()); 15 | ASSERT_EQ(lock.record(), 4); 16 | ASSERT_TRUE(lock.tryUpgrade()); 17 | ASSERT_EQ(lock.record(), 6); 18 | ASSERT_FALSE(lock.tryExclusive()); 19 | ASSERT_EQ(lock.record(), 6); 20 | lock.releaseShared(); 21 | ASSERT_EQ(lock.record(), 2); 22 | ASSERT_TRUE(lock.tryUpgrade()); 23 | ASSERT_EQ(lock.record(), 2); 24 | ASSERT_TRUE(lock.tryExclusive()); 25 | ASSERT_EQ(lock.record(), 1); 26 | ASSERT_FALSE(lock.tryUpgrade()); 27 | ASSERT_EQ(lock.record(), 3); 28 | ASSERT_FALSE(lock.tryShared()); 29 | lock.releaseExclusive(); 30 | ASSERT_EQ(lock.record(), 0); 31 | ASSERT_TRUE(lock.tryShared()); 32 | ASSERT_EQ(lock.record(), 4); 33 | lock.releaseShared(); 34 | ASSERT_EQ(lock.record(), 0); 35 | } 36 | 37 | TEST(LifCycleLockTest, multiThread) { 38 | LifeCycleLock lock; 39 | auto dest_time = std::chrono::system_clock::now() 40 | + std::chrono::milliseconds(50); 41 | 42 | std::thread t1([&lock, &dest_time]() { 43 | std::this_thread::sleep_until(dest_time); 44 | LifeCycleShareGuard guard(lock); 45 | ASSERT_TRUE(guard.shared()); 46 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 47 | }); 48 | std::thread t2([&lock, &dest_time]() { 49 | std::this_thread::sleep_until(dest_time); 50 | std::this_thread::sleep_for(std::chrono::milliseconds(15)); 51 | { 52 | LifeCycleShareGuard guard(lock); 53 | ASSERT_FALSE(guard.shared()); 54 | } 55 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 56 | ASSERT_FALSE(lock.tryUpgrade()); 57 | ASSERT_FALSE(lock.tryExclusive()); 58 | lock.upgrade(); 59 | lock.exclusive(); 60 | lock.releaseExclusive(); 61 | }); 62 | 63 | std::thread t3([&lock, &dest_time]() { 64 | std::this_thread::sleep_until(dest_time); 65 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 66 | ASSERT_TRUE(lock.tryUpgrade()); 67 | ASSERT_FALSE(lock.tryExclusive()); 68 | lock.exclusive(); 69 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 70 | lock.releaseExclusive(); 71 | }); 72 | 73 | t1.join(); 74 | t2.join(); 75 | t3.join(); 76 | 77 | ASSERT_EQ(lock.record(), 0); 78 | } 79 | -------------------------------------------------------------------------------- /src/common/common_defines.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/18. 3 | // 4 | 5 | #include "common/common_defines.h" 6 | #include 7 | #include "rpc.h" 8 | #include "schedule/schedule.h" 9 | #include "tcp/socket_manager.h" 10 | #include "log.h" 11 | #include 12 | 13 | namespace antflash { 14 | 15 | struct RpcErrorInfo { 16 | const char *name; 17 | const char *description; 18 | }; 19 | 20 | #define XX(num, name, string) { "RPC_STATUS_" #name, #string }, 21 | static RpcErrorInfo s_rpc_status_str_tab[] = { 22 | RPC_STATUS_MAP(XX) 23 | }; 24 | #undef XX 25 | 26 | /* 27 | static void ProtoBufLogHandler(google::protobuf::LogLevel level, 28 | const char* filename, int line, 29 | const std::string& message) { 30 | switch (level) { 31 | case google::protobuf::LOGLEVEL_INFO: 32 | LOG_INFO("{}:{} {}", filename, line, message); 33 | return; 34 | case google::protobuf::LOGLEVEL_WARNING: 35 | LOG_WARN("{}:{} {}", filename, line, message); 36 | return; 37 | case google::protobuf::LOGLEVEL_ERROR: 38 | LOG_ERROR("{}:{} {}", filename, line, message); 39 | return; 40 | case google::protobuf::LOGLEVEL_FATAL: 41 | LOG_FATAL("{}:{} {}", filename, line, message); 42 | return; 43 | default: 44 | LOG_DEBUG("{}:{} {}", filename, line, message); 45 | return; 46 | } 47 | } 48 | */ 49 | 50 | bool globalInit() { 51 | //google::protobuf::SetLogHandler(&ProtoBufLogHandler); 52 | 53 | //Init schedule first so that sockets can be connected/read normally 54 | if (!Schedule::getInstance().init()) { 55 | return false; 56 | } 57 | 58 | if (!SocketManager::getInstance().init()) { 59 | return false; 60 | } 61 | 62 | #if defined(OS_MACOSX) 63 | #else 64 | // Ignore SIGPIPE. 65 | struct sigaction oldact; 66 | if (sigaction(SIGPIPE, nullptr, &oldact) != 0 || 67 | (oldact.sa_handler == nullptr && oldact.sa_sigaction == nullptr)) { 68 | LOG_DEBUG("ignore SIGPIPE"); 69 | signal(SIGPIPE, SIG_IGN); 70 | } 71 | #endif 72 | 73 | return true; 74 | } 75 | 76 | void globalDestroy() { 77 | //Destroy schedule first so that sockets can be reclaimed normally 78 | Schedule::getInstance().destroy_schedule(); 79 | 80 | SocketManager::getInstance().destroy(); 81 | 82 | //timeout thread must by destroyed after socket manager 83 | Schedule::getInstance().destroy_time_schedule(); 84 | } 85 | 86 | const char* getRpcStatus(ERpcStatus status) { 87 | return s_rpc_status_str_tab[status].description; 88 | } 89 | 90 | } 91 | 92 | -------------------------------------------------------------------------------- /include/protocol/bolt/bolt_response.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/11. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_BOLT_RESPONSE_H 6 | #define RPC_INCLUDE_BOLT_RESPONSE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "protocol/response_base.h" 12 | #include "common/common_defines.h" 13 | 14 | namespace antflash { 15 | 16 | class BoltResponse final : public ResponseBase { 17 | public: 18 | enum EBoltResponseStatus { 19 | SUCCESS = 0x0000, 20 | ERROR = 0x0001, 21 | SERVER_EXCEPTION = 0x0002, 22 | UNKNOWN = 0x0003, 23 | SERVER_THREADPOOL_BUSY = 0x0004, 24 | ERROR_COMM = 0x0005, 25 | NO_PROCESSOR = 0x0006, 26 | TIMEOUT = 0x0007, 27 | CLIENT_SEND_ERROR = 0x0008, 28 | CODEC_EXCEPTION = 0x0009, 29 | CONNECTION_CLOSED = 0x0010, 30 | SERVER_SERIAL_EXCEPTION = 0x0011, 31 | SERVER_DESERIAL_EXCEPTION = 0x0012 32 | }; 33 | 34 | BoltResponse() { 35 | _data_type = EDataType::NONE; 36 | } 37 | 38 | BoltResponse(google::protobuf::Message& message) { 39 | _data.proto = &message; 40 | _data_type = EDataType::PROTOBUF; 41 | } 42 | 43 | BoltResponse(std::string& message) { 44 | _data.str = &message; 45 | _data_type = EDataType::STRING; 46 | } 47 | 48 | ~BoltResponse() { 49 | } 50 | 51 | EResParseResult deserialize(IOBuffer &buffer) noexcept override; 52 | static EResParseResult checkHeader( 53 | const IOBuffer &buffer, 54 | size_t& data_size, 55 | size_t& request_id); 56 | 57 | bool isHeartbeat() const { 58 | return _header.cmdcode == BOLT_PROTOCOL_CMD_HEARTBEAT; 59 | } 60 | 61 | uint16_t status() const { 62 | return _header.status; 63 | } 64 | 65 | const std::string& msg() const; 66 | 67 | private: 68 | enum class EDataType { 69 | STRING = 0, 70 | PROTOBUF, 71 | NONE 72 | }; 73 | 74 | struct [[gnu::packed]] BoltHeader { 75 | uint8_t proto; 76 | uint8_t type; 77 | uint16_t cmdcode; 78 | uint8_t ver2; 79 | uint32_t request_id; 80 | uint8_t codec; 81 | 82 | uint16_t status; 83 | uint16_t class_len; 84 | uint16_t header_len; 85 | uint32_t content_len; 86 | 87 | void ntoh(); 88 | }; 89 | 90 | mutable BoltHeader _header; 91 | std::string _class_name; 92 | std::unordered_map _header_map; 93 | 94 | union { 95 | std::string* str; 96 | google::protobuf::Message* proto; 97 | } _data; 98 | 99 | EDataType _data_type; 100 | }; 101 | 102 | } 103 | #endif //RPC_INCLUDE_BOLT_RESPONSE_H 104 | -------------------------------------------------------------------------------- /example/http_request_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "rpc.h" 4 | #include "../src/common/log.h" 5 | 6 | int main() { 7 | antflash::globalInit(); 8 | 9 | antflash::Channel channel; 10 | antflash::ChannelOptions options; 11 | options.protocol = antflash::EProtocolType::PROTOCOL_HTTP; 12 | //if (!channel.init("127.0.0.1:8888", &options)) { 13 | if (!channel.init("13330", &options)) { 14 | LOG_ERROR("channel init fail."); 15 | return -1; 16 | } 17 | 18 | { 19 | antflash::HttpRequest request; 20 | antflash::HttpResponse response; 21 | //request.uri("127.0.0.1:8888/configs/application") 22 | request.uri("127.0.0.1:13330/configs/application") 23 | .method(antflash::EHttpMethod::HTTP_METHOD_POST) 24 | .attach_type("application/json") 25 | .attach("{\"DataCenter\":\"dc\",\"AppName\":\"testApp\",\"AntShareCloud\":false}"); 26 | antflash::Session session; 27 | session.send(request).to(channel).receiveTo(response).sync(); 28 | 29 | if (session.failed()) { 30 | LOG_INFO("get response fail"); 31 | } else { 32 | LOG_INFO("response body: {}", response.body()); 33 | } 34 | } 35 | 36 | { 37 | antflash::HttpRequest request; 38 | antflash::HttpResponse response; 39 | //request.uri("127.0.0.1:8888/services/subscribe") 40 | request.uri("127.0.0.1:13330/services/subscribe") 41 | .method(antflash::EHttpMethod::HTTP_METHOD_POST) 42 | .attach_type("application/json") 43 | .attach("{\"serviceName\":\"com.alipay.rpc.common.service.facade.pb.SampleServicePb:1.0@DEFAULT\"}"); 44 | antflash::Session session; 45 | session.send(request).to(channel).receiveTo(response).sync(); 46 | 47 | if (session.failed()) { 48 | LOG_INFO("get response fail"); 49 | } else { 50 | LOG_INFO("response body: {}", response.body()); 51 | } 52 | } 53 | 54 | { 55 | antflash::HttpRequest request; 56 | antflash::HttpResponse response; 57 | //request.uri("127.0.0.1:8888/services/unsubscribe") 58 | request.uri("127.0.0.1:13330/services/unsubscribe") 59 | .method(antflash::EHttpMethod::HTTP_METHOD_POST) 60 | .attach_type("application/json") 61 | .attach("{\"serviceName\":\"com.alipay.rpc.common.service.facade.pb.SampleServicePb:1.0@DEFAULT\"}"); 62 | antflash::Session session; 63 | session.send(request).to(channel).receiveTo(response).sync(); 64 | 65 | if (session.failed()) { 66 | LOG_INFO("get response fail"); 67 | } else { 68 | LOG_INFO("response body: {}", response.body()); 69 | } 70 | } 71 | 72 | LOG_INFO("shut down"); 73 | 74 | antflash::globalDestroy(); 75 | 76 | return 0; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /include/common/life_cycle_lock.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/16. 3 | // 4 | 5 | #ifndef RPC_COMMON_LIFE_CYCLE_LOCK_H 6 | #define RPC_COMMON_LIFE_CYCLE_LOCK_H 7 | 8 | #include 9 | #include 10 | 11 | namespace antflash { 12 | 13 | class LifeCycleLock { 14 | public: 15 | enum : int32_t { 16 | SHARED = 4, 17 | UPGRADED = 2, 18 | EXCLUSIVE = 1 19 | }; 20 | 21 | LifeCycleLock() : _bits(0) {} 22 | ~LifeCycleLock() {} 23 | 24 | LifeCycleLock(LifeCycleLock&) = delete; 25 | LifeCycleLock& operator=(LifeCycleLock&) = delete; 26 | 27 | void share() { 28 | int64_t count = 0; 29 | while (!tryShared()) { 30 | if (++count > 1000) { 31 | count = 0; 32 | std::this_thread::yield(); 33 | } 34 | } 35 | } 36 | 37 | //Try to share this lock, this can be fail if the lock is in exclusive mode 38 | //or someone is trying to upgrade lock to exclusive mode. 39 | bool tryShared() { 40 | int32_t value = _bits.fetch_add(SHARED, std::memory_order_acq_rel); 41 | if (value & (UPGRADED | EXCLUSIVE)) { 42 | _bits.fetch_add(-SHARED, std::memory_order_release); 43 | return false; 44 | } 45 | 46 | return true; 47 | } 48 | 49 | void upgrade() { 50 | int64_t count = 0; 51 | while (!tryUpgrade()) { 52 | if (++count > 1000) { 53 | count = 0; 54 | std::this_thread::yield(); 55 | } 56 | } 57 | } 58 | 59 | //Try to make this lock exclusive, before that we need to upgrade lock 60 | //first to acquire it first, when failed, we cannot flip the UPGRADED 61 | //bit back, as in this case there is either another upgrade lock or a 62 | // exclusive lock, if it's a exclusive lock, the bit will get cleared 63 | // up when that lock's done. 64 | bool tryUpgrade() { 65 | int32_t value = _bits.fetch_or(UPGRADED, std::memory_order_acq_rel); 66 | return ((value & EXCLUSIVE) == 0); 67 | } 68 | 69 | bool tryUpgradeNonReEntrant() { 70 | int32_t value = _bits.fetch_or(UPGRADED, std::memory_order_acq_rel); 71 | return ((value & (UPGRADED | EXCLUSIVE)) == 0); 72 | } 73 | 74 | void exclusive() { 75 | int64_t count = 0; 76 | while (!tryExclusive()) { 77 | if (++count > 1000) { 78 | count = 0; 79 | std::this_thread::yield(); 80 | } 81 | } 82 | } 83 | 84 | //Try to unlock upgrade and make lock exclusive atomically 85 | bool tryExclusive() 86 | { 87 | int32_t expect = UPGRADED; 88 | return _bits.compare_exchange_strong( 89 | expect, EXCLUSIVE, std::memory_order_acq_rel); 90 | } 91 | 92 | void releaseShared() { 93 | _bits.fetch_add(-SHARED, std::memory_order_release); 94 | } 95 | 96 | void releaseExclusive() 97 | { 98 | _bits.fetch_and(~(EXCLUSIVE | UPGRADED), std::memory_order_release); 99 | } 100 | 101 | int32_t record() { 102 | return _bits.load(); 103 | } 104 | 105 | private: 106 | std::atomic _bits; 107 | }; 108 | 109 | 110 | class LifeCycleShareGuard { 111 | public: 112 | LifeCycleShareGuard(LifeCycleLock& lock) : _lock(&lock) { 113 | _shared = _lock->tryShared(); 114 | } 115 | 116 | ~LifeCycleShareGuard() { 117 | if (_shared) { 118 | _lock->releaseShared(); 119 | } 120 | } 121 | 122 | inline bool shared() const { 123 | return _shared; 124 | } 125 | 126 | private: 127 | LifeCycleLock* _lock; 128 | bool _shared; 129 | }; 130 | 131 | } 132 | 133 | #endif //RPC_COMMON_LIFE_CYCLE_LOCK_H 134 | -------------------------------------------------------------------------------- /src/protocol/http/http_protocol.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #include "http_protocol.h" 6 | #include 7 | #include 8 | #include "common/io_buffer.h" 9 | #include "common/log.h" 10 | #include "protocol/http/http_request.h" 11 | #include "protocol/http/http_response.h" 12 | 13 | namespace antflash { 14 | namespace http { 15 | 16 | struct HttpHeaderData { 17 | using HeaderMap = std::unordered_map; 18 | HttpHeaderData() { 19 | _header_map.reserve(HTTP_HEADER_KEY_SIZE); 20 | } 21 | void setHeader(EHttpHeaderKey key, const std::string& value) { 22 | auto p = _header_map.emplace(key, value); 23 | if (!p.second) { 24 | p.first->second = value; 25 | } 26 | } 27 | 28 | HeaderMap _header_map; 29 | }; 30 | 31 | class HttpInternalRequest { 32 | public: 33 | HttpInternalRequest(const HttpRequest& request, size_t request_id) : 34 | _request(request) { 35 | std::stringstream ss; 36 | ss << request_id; 37 | _header.setHeader(LOG_ID, ss.str()); 38 | _header.setHeader(CONNECTION, "keep-alive"); 39 | } 40 | 41 | bool serialize(IOBuffer&); 42 | private: 43 | HttpHeaderData _header; 44 | const HttpRequest& _request; 45 | }; 46 | 47 | bool HttpInternalRequest::serialize(antflash::IOBuffer& buffer) { 48 | 49 | static const char* s_http_line_end = "\r\n"; 50 | 51 | std::stringstream http_ss; 52 | http_ss << HttpHeader::getMethod(_request._method) << " "; 53 | http_ss << _request._uri.path(); 54 | http_ss << " HTTP/1.1"<< s_http_line_end; 55 | if (_request._method != EHttpMethod::HTTP_METHOD_GET) { 56 | http_ss << "Content-Length: " << _request.attach_size() << s_http_line_end; 57 | } 58 | if (!_request._attch_type.empty()) { 59 | http_ss << "Content-Type: " << _request._attch_type << s_http_line_end; 60 | } 61 | 62 | http_ss << "Host: " << _request._uri.host() 63 | << ":" << _request._uri.port() << s_http_line_end; 64 | http_ss << "Accept: */*" << s_http_line_end; 65 | http_ss << "User-Agent: curl/7.0" << s_http_line_end; 66 | 67 | for (auto& p : _header._header_map) { 68 | http_ss << HttpHeader::getHeaderKey((EHttpHeaderKey)p.first) 69 | << ": " << p.second << s_http_line_end; 70 | } 71 | 72 | http_ss << s_http_line_end; 73 | 74 | buffer.append(http_ss.str()); 75 | 76 | if (_request._data) { 77 | //FIXME copy iobuffer after refine iobuffer 78 | buffer.append(std::move(*_request._data)); 79 | } 80 | 81 | LOG_INFO("http info:\n{}", buffer.to_string()); 82 | 83 | return true; 84 | } 85 | 86 | bool assembleHttpRequest( 87 | const RequestBase &req, 88 | size_t request_id, IOBuffer& buffer) { 89 | auto http = static_cast(&req); 90 | if (!http->uri().parse_ok()) { 91 | LOG_ERROR("uri parse fail"); 92 | return false; 93 | } 94 | HttpInternalRequest request(*http, request_id); 95 | return request.serialize(buffer); 96 | } 97 | 98 | EResParseResult parseHttpProtocol ( 99 | IOBuffer& buffer, size_t& data_size, 100 | size_t& request_id, void** p) { 101 | return HttpResponse::checkHeader(buffer, data_size, request_id, p); 102 | } 103 | 104 | EResParseResult parseHttpResponse( 105 | ResponseBase& rsp, IOBuffer& buffer, void* addition_data) { 106 | auto http = static_cast(&rsp); 107 | http->setHttpParser(addition_data); 108 | return rsp.deserialize(buffer); 109 | } 110 | 111 | bool assembleHeartbeatRequest(IOBuffer& buffer) { 112 | return true; 113 | } 114 | 115 | size_t converseHttpRequest(size_t request_id) { 116 | return request_id; 117 | } 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /include/common/common_defines.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/2. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_COMMON_DEFINES_H 6 | #define RPC_INCLUDE_COMMON_DEFINES_H 7 | 8 | #include 9 | 10 | namespace antflash { 11 | 12 | /** 13 | * Log Level enum 14 | */ 15 | enum class LogLevel { 16 | LOG_LEVEL_DEBUG, // Debug. 17 | LOG_LEVEL_INFO, // Informational. 18 | LOG_LEVEL_WARNING, // Warns about issues that, although not technically a 19 | // problem now, could cause problems in the future. 20 | LOG_LEVEL_ERROR, //An error occurred which should never happen during normal use. 21 | LOG_LEVEL_FATAL // An error occurred from which the library cannot recover. 22 | }; 23 | 24 | /** 25 | * Log Handler function pointer 26 | */ 27 | using LogHandler = void(LogLevel level, 28 | const char* filename, int line, 29 | const std::string& message); 30 | 31 | /*Socket Related*/ 32 | static constexpr auto CACHELINE_SIZE = 64; 33 | static constexpr size_t BUFFER_DEFAULT_SLICE_SIZE = 8192; 34 | static constexpr size_t BUFFER_DEFAULT_BLOCK_SIZE = 8192; 35 | static constexpr size_t BUFFER_MAX_SLICE_SIZE = 65536; 36 | static constexpr size_t MAX_POLL_EVENT = 32; 37 | static constexpr int32_t SOCKET_CONNECT_TIMEOUT_MS = 200; 38 | static constexpr int32_t SOCKET_TIMEOUT_MS = 500; 39 | static constexpr int32_t SOCKET_MAX_RETRY = 3; 40 | static constexpr size_t SOCKET_MAX_IDLE_US = 15 * 1000 * 1000; 41 | 42 | static constexpr size_t MAX_PARALLEL_SESSION_SIZE_ON_SOCKET = 1024; 43 | 44 | static constexpr size_t CONNECTION_POOL_MAX_SOCKET_SIZE = 32; 45 | 46 | /*Bolt Protocol Related*/ 47 | static constexpr uint8_t BOLT_PROTOCOL_TYPE = 1; 48 | static constexpr uint8_t BOLT_PROTOCOL_REQUEST = 1; 49 | static constexpr uint8_t BOLT_PROTOCOL_RESPONSE = 0; 50 | static constexpr uint8_t BOLT_PROTOCOL_RESPONSE_ONE_WAY = 2; 51 | static constexpr uint16_t BOLT_PROTOCOL_CMD_HEARTBEAT = 0; 52 | static constexpr uint16_t BOLT_PROTOCOL_CMD_REQUEST = 1; 53 | static constexpr uint16_t BOLT_PROTOCOL_CMD_RESPONSE = 2; 54 | static constexpr uint8_t BOLT_PROTOCOL_VER2 = 1; 55 | static constexpr uint8_t BOLT_PROTOCOL_CODEC_PB = 11; 56 | 57 | static constexpr auto SIZE_OF_AVOID_FALSE_SHARING = 64; 58 | 59 | /* Zookeeper Related */ 60 | static constexpr size_t ZOOKEEPER_RECV_TIMEOUT = 30000; 61 | 62 | /*Rpc Client All Status Codes Related*/ 63 | #define RPC_STATUS_MAP(XX) \ 64 | XX(0, OK, Status ok) \ 65 | XX(1, INIT, Status init) \ 66 | XX(2, CHANNEL_INIT_FAIL, Channel init fail) \ 67 | XX(3, SOCKET_CONNECT_FAIL, Connect server fail) \ 68 | XX(4, SOCKET_CONNECT_TIMEOUT, Connect server timeout) \ 69 | XX(5, SOCKET_WRITE_ERROR, Write to server error) \ 70 | XX(6, SOCKET_READ_ERROR, Read from server error) \ 71 | XX(7, SOCKET_CLOSED_BY_SERVER, Remote server close the connection) \ 72 | XX(8, SOCKET_HEARTBEAT_FAIL, Heartbeat fail) \ 73 | XX(9, SOCKET_TO_RECLAIM, Socket is prepared to be reclaimed) \ 74 | 75 | enum ERpcStatus : int32_t 76 | { 77 | #define XX(num, name, string) RPC_STATUS_##name = num, 78 | RPC_STATUS_MAP(XX) 79 | #undef XX 80 | }; 81 | 82 | const char* getRpcStatus(ERpcStatus status); 83 | 84 | enum class ESessionError { 85 | SESSION_OK = 0, 86 | PROTOCOL_NOT_FOUND, 87 | ASSEMBLE_REQUEST_FAIL, 88 | SOCKET_LOST, 89 | SOCKET_BUSY, 90 | WRITE_FAIL, 91 | READ_FAIL, 92 | READ_TIMEOUT, 93 | PARSE_RESPONSE_FAIL, 94 | TIMER_BUSY 95 | }; 96 | 97 | } 98 | 99 | #endif //RPC_INCLUDE_COMMON_DEFINES_H 100 | -------------------------------------------------------------------------------- /example/cloud_rpc_demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "rpc.h" 5 | #include "sample.pb.h" 6 | #include "../src/common/log.h" 7 | 8 | int main() { 9 | if (!antflash::globalInit()) { 10 | LOG_ERROR("init fail."); 11 | return -1; 12 | } 13 | 14 | antflash::Channel httpChannel; 15 | antflash::ChannelOptions httpOptions; 16 | httpOptions.protocol = antflash::EProtocolType::PROTOCOL_HTTP; 17 | //if (!channel.init("127.0.0.1:8888", &options)) { 18 | if (!httpChannel.init("127.0.0.1:13330", &httpOptions)) { 19 | LOG_ERROR("httpChannel init fail."); 20 | return -1; 21 | } 22 | 23 | { 24 | antflash::HttpRequest applicationHttpRequest; 25 | antflash::HttpResponse applicationHttpResponse; 26 | //request.uri("127.0.0.1:8888/configs/application") 27 | applicationHttpRequest.uri("127.0.0.1:13330/configs/application") 28 | .method(antflash::EHttpMethod::HTTP_METHOD_POST) 29 | .attach_type("application/json") 30 | .attach("{\"DataCenter\":\"dc\",\"AppName\":\"testApp\",\"AntShareCloud\":true}"); 31 | antflash::Session applicationHttpsession; 32 | applicationHttpsession.send(applicationHttpRequest).to(httpChannel).receiveTo(applicationHttpResponse).sync(); 33 | 34 | if (applicationHttpsession.failed()) { 35 | LOG_INFO("get response fail"); 36 | } else { 37 | LOG_INFO("response body: {}", applicationHttpResponse.body()); 38 | } 39 | } 40 | 41 | { 42 | antflash::HttpRequest subscribeHttpRequest; 43 | antflash::HttpResponse subscribeHttpResponse; 44 | //request.uri("127.0.0.1:8888/services/subscribe") 45 | subscribeHttpRequest.uri("127.0.0.1:13330/services/subscribe") 46 | .method(antflash::EHttpMethod::HTTP_METHOD_POST) 47 | .attach_type("application/json") 48 | .attach("{\"serviceName\":\"com.alipay.rpc.common.service.facade.pb.SampleServicePb:1.0\"}"); 49 | antflash::Session subscribeHttpSession; 50 | subscribeHttpSession.send(subscribeHttpRequest).to(httpChannel).receiveTo(subscribeHttpResponse).sync(); 51 | 52 | if (subscribeHttpSession.failed()) { 53 | LOG_INFO("get response fail"); 54 | } else { 55 | LOG_INFO("response body: {}", subscribeHttpResponse.body()); 56 | } 57 | } 58 | 59 | 60 | 61 | antflash::Channel rpcChannel; 62 | antflash::ChannelOptions rpcOptions; 63 | rpcOptions.connection_type = antflash::EConnectionType::CONNECTION_TYPE_POOLED; 64 | if (!rpcChannel.init("127.0.0.1:12220", &rpcOptions)) { 65 | printf("%s", "rpcOptions init fail."); 66 | return -1; 67 | } 68 | 69 | antflash::BoltRequest boltRequest; 70 | SampleServicePbRequest sub_req; 71 | 72 | SampleServicePbResult sub_rsp; 73 | antflash::BoltResponse boltResponse(sub_rsp); 74 | 75 | while (true) { 76 | sub_req.set_name("alipay"); 77 | boltRequest.service("com.alipay.rpc.common.service.facade.pb.SampleServicePb:1.0") 78 | .method("hello").data(sub_req); 79 | antflash::Session rpcSession; 80 | rpcSession.send(boltRequest).to(rpcChannel).receiveTo(boltResponse).sync(); 81 | boltResponse.isHeartbeat(); 82 | 83 | if (rpcSession.failed()) { 84 | LOG_INFO("get response fail {}", rpcSession.getErrText()); 85 | } else { 86 | if (boltResponse.status() != antflash::BoltResponse::SUCCESS) { 87 | LOG_INFO("get response fail, response status:{}", boltResponse.status()); 88 | } else { 89 | LOG_INFO("get response {}, response:{}", 90 | rpcSession.getErrText(), sub_rsp.result()); 91 | } 92 | } 93 | rpcSession.reset(); 94 | 95 | std::this_thread::sleep_for( 96 | std::chrono::seconds(3)); 97 | } 98 | 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/common/uri.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #include "common/uri.h" 6 | 7 | namespace antflash { 8 | 9 | struct URIFastParserMap { 10 | public: 11 | 12 | enum EParseAction { 13 | URI_PARSE_CONTINUE = 0, 14 | URI_PARSE_CHECK = 1, 15 | URI_PARSE_BREAK = 2 16 | }; 17 | 18 | URIFastParserMap() : action_map {URI_PARSE_CONTINUE,} { 19 | action_map['\0'] = URI_PARSE_BREAK; 20 | action_map[' '] = URI_PARSE_CHECK; 21 | action_map['#'] = URI_PARSE_BREAK; 22 | action_map['/'] = URI_PARSE_BREAK; 23 | action_map[':'] = URI_PARSE_CHECK; 24 | action_map['?'] = URI_PARSE_BREAK; 25 | action_map['@'] = URI_PARSE_CHECK; 26 | } 27 | 28 | EParseAction action_map[128]; //ascii 29 | }; 30 | 31 | 32 | static URIFastParserMap s_fast_parser_map; 33 | 34 | static const char *splitHostAndPort( 35 | const char *host_begin, const char *host_end, int *port) { 36 | *port = 0; 37 | int multiply = 1; 38 | for (const char *q = host_end - 1; q > host_begin; --q) { 39 | if (*q >= '0' && *q <= '9') { 40 | *port += (*q - '0') * multiply; 41 | multiply *= 10; 42 | } else if (*q == ':') { 43 | return q; 44 | } else { 45 | break; 46 | } 47 | } 48 | *port = -1; 49 | return host_end; 50 | } 51 | 52 | static bool isAllSpace(const char *p) { 53 | while (*p == ' ') { 54 | ++p; 55 | } 56 | return *p == '\0'; 57 | } 58 | 59 | void URI::parseFromString(const char *url) { 60 | clear(); 61 | const char *p = url; 62 | while (*p == ' ') { 63 | ++p; 64 | } 65 | const char *begin = p; 66 | 67 | bool need_schema = true; 68 | bool need_user_info = true; 69 | do { 70 | auto action = s_fast_parser_map.action_map[int(*p)]; 71 | if (action == URIFastParserMap::URI_PARSE_CONTINUE) { 72 | continue; 73 | } 74 | if (action == URIFastParserMap::URI_PARSE_BREAK) { 75 | break; 76 | } 77 | 78 | if (*p == ':') { 79 | //always suppose that url has '\0' ending. 80 | if (p[1] == '/' && p[2] == '/' && need_schema) { 81 | need_schema = false; 82 | _schema.assign(begin, p - begin); 83 | p += 2; 84 | begin = p + 1; 85 | } 86 | } else if (*p == '@') { 87 | if (need_user_info) { 88 | need_user_info = false; 89 | _user_info.assign(begin, p - begin); 90 | begin = p + 1; 91 | } 92 | } else if (*p == ' ') { 93 | if (isAllSpace(p)) { 94 | _parse_ok = false; 95 | return; 96 | } 97 | break; 98 | } 99 | 100 | } while (++p); 101 | 102 | const char *host_end = splitHostAndPort(begin, p, &_port); 103 | _host.assign(begin, host_end - begin); 104 | if (*p == '/') { 105 | begin = p++; 106 | for (; *p && *p != '?' && *p != '#'; ++p) { 107 | if (*p == ' ') { 108 | if (isAllSpace(p)) { 109 | _parse_ok = false; 110 | return; 111 | } 112 | break; 113 | } 114 | } 115 | _path.assign(begin, p - begin); 116 | } 117 | if (*p == '?') { 118 | begin = ++p; 119 | for (; *p && *p != '#'; ++p) { 120 | if (*p == ' ') { 121 | if (!isAllSpace(p + 1)) { 122 | _parse_ok = false; 123 | return; 124 | } 125 | break; 126 | } 127 | } 128 | _query.assign(begin, p - begin); 129 | } 130 | if (*p == '#') { 131 | begin = ++p; 132 | for (; *p; ++p) { 133 | if (*p == ' ') { 134 | if (!isAllSpace(p + 1)) { 135 | _parse_ok = false; 136 | return; 137 | } 138 | break; 139 | } 140 | } 141 | _fragment.assign(begin, p - begin); 142 | } 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /TARGETS: -------------------------------------------------------------------------------- 1 | targets = { 2 | 'bolt-rpc-client': cpp_library ( 3 | srcs = [ 4 | 'second_party/fmt/src/format.cc', 5 | 'src/common/common_defines.cpp', 6 | 'src/common/log.cpp', 7 | 'src/common/io_buffer.cpp', 8 | 'src/common/uri.cpp', 9 | 'src/protocol/protocol_define.cpp', 10 | 'src/protocol/bolt/bolt_protocol.cpp', 11 | 'src/protocol/bolt/bolt_response.cpp', 12 | 'src/protocol/response_base.cpp', 13 | 'src/protocol/http/http_protocol.cpp', 14 | 'src/protocol/http/http_parser.c', 15 | 'src/protocol/http/http_header.cpp', 16 | 'src/protocol/http/http_request.cpp', 17 | 'src/protocol/http/http_response.cpp', 18 | 'src/tcp/endpoint.cpp', 19 | 'src/tcp/socket_manager.cpp', 20 | 'src/tcp/socket.cpp', 21 | 'src/tcp/socket_posix.cpp', 22 | 'src/schedule/epoll_loop.cpp', 23 | 'src/schedule/loop_thread.cpp', 24 | 'src/schedule/time_thread.cpp', 25 | 'src/schedule/schedule.cpp', 26 | 'src/channel/channel.cpp', 27 | 'src/session/session.cpp', 28 | ], 29 | incs = [ 30 | 'include', 31 | 'src', 32 | 'second_party/fmt/include', 33 | ], 34 | deps = [ 35 | 'protobuf-3.2.0:protobuf', 36 | ], 37 | cxxflags = [ 38 | '-DOS_LINUX', 39 | ], 40 | ldflags = [ 41 | '-L/lib64 -L/usr/lib64', 42 | ], 43 | ), 44 | 'sample_proto_generation': cpp_utility ( 45 | srcs = [ 46 | 'example/sample.proto', 47 | ], 48 | deps = [ 49 | 'protobuf-3.2.0:protobuf', 50 | ], 51 | ), 52 | 'sample': cpp_program ( 53 | srcs = [ 54 | 'example/sample.pb.cc', 55 | 'example/single_request_example.cpp', 56 | ], 57 | deps = [ 58 | ':bolt-rpc-client', 59 | 'protobuf-3.2.0:protobuf', 60 | 'sample_proto_generation', 61 | ], 62 | incs = [ 63 | 'include', 64 | 'example', 65 | 'second_party/fmt/include', 66 | ], 67 | cxxflags = [ 68 | '-DOS_LINUX', 69 | ], 70 | ldflags = [ 71 | '-L/lib64 -L/usr/lib64', 72 | ], 73 | ), 74 | 'ut_proto_generation': cpp_utility ( 75 | srcs = [ 76 | 'test/unit_test/io_buffer_unittest.proto', 77 | ], 78 | deps = [ 79 | 'protobuf-3.2.0:protobuf', 80 | ], 81 | ), 82 | 'bolt-rpc-client_test' : cpp_fast_test ( 83 | srcs = [ 84 | 'test/unit_test/unittest_main.cpp', 85 | 'test/unit_test/endpoint_unittest.cpp', 86 | 'test/unit_test/channel_unittest.cpp', 87 | 'test/unit_test/io_buffer_unittest.pb.cc', 88 | 'test/unit_test/io_buffer_unittest.cpp', 89 | 'test/unit_test/lifecyclelock_unittest.cpp', 90 | 'test/unit_test/mpscqueue_unittest.cpp', 91 | 'test/unit_test/simple_socket_server.cpp', 92 | 'test/unit_test/socket_manager_unittest.cpp', 93 | 'test/unit_test/socket_posix_unittest.cpp', 94 | 'test/unit_test/thread_pool_unittest.cpp', 95 | 'test/unit_test/time_thread_unittest.cpp', 96 | 'test/unit_test/uri_unittest.cpp', 97 | 'test/unit_test/utils_unittest.cpp', 98 | 'test/unit_test/http_unittest.cpp', 99 | 'test/unit_test/intrusivelist_unittest.cpp', 100 | 'test/unit_test/concurrenthashmap_unittest.cpp', 101 | 'test/unit_test/lrucache_unittest.cpp', 102 | ], 103 | incs = [ 104 | 'include', 105 | 'test/unit_test', 106 | 'src', 107 | 'second_party/fmt/include', 108 | ], 109 | testing = [ 110 | 'bolt-rpc-client', 111 | ], 112 | build_deps = [ 113 | 'ut_proto_generation', 114 | ], 115 | deps = [ 116 | 'gtest-1.7.0:gtest', 117 | 'gtest-1.7.0:gtest_main', 118 | ':bolt-rpc-client', 119 | 'protobuf-3.2.0:protobuf', 120 | ] 121 | ), 122 | } 123 | 124 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(bolt-rpc-client) 3 | 4 | set(THREADS_PREFER_PTHREAD_FLAG ON) 5 | find_package(Threads REQUIRED) 6 | find_package(Protobuf REQUIRED) 7 | if(PROTOBUF_FOUND) 8 | message(STATUS "protobuf library found") 9 | else() 10 | message(FATAL_ERROR "protobuf library is needed but cant be found") 11 | endif() 12 | 13 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++1y -fprofile-arcs -ftest-coverage") 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++1y") 15 | 16 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'") 17 | 18 | include_directories(${PROTOBUF_INCLUDE_DIRS}) 19 | include_directories(second_party/fmt/include) 20 | include_directories( 21 | include 22 | src) 23 | 24 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) 25 | PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS 26 | example/sample.proto) 27 | 28 | set(FMT_SOURCE 29 | second_party/fmt/src/format.cc) 30 | 31 | set(RPC_SOURCE 32 | src/tcp/socket.cpp 33 | src/tcp/endpoint.cpp 34 | src/tcp/socket_manager.cpp 35 | src/tcp/socket_posix.cpp 36 | src/session/session.cpp 37 | src/protocol/protocol_define.cpp 38 | src/protocol/bolt/bolt_protocol.cpp 39 | src/protocol/bolt/bolt_response.cpp 40 | src/channel/channel.cpp 41 | src/schedule/loop_thread.cpp 42 | src/schedule/schedule.cpp 43 | src/schedule/time_thread.cpp 44 | src/common/log.cpp 45 | src/common/common_defines.cpp 46 | src/common/io_buffer.cpp 47 | src/common/uri.cpp 48 | src/protocol/http/http_parser.c 49 | src/protocol/http/http_header.cpp 50 | src/protocol/http/http_request.cpp 51 | src/protocol/http/http_response.cpp 52 | src/protocol/http/http_protocol.cpp ) 53 | 54 | SET(POLL_SOURCE 55 | src/schedule/epoll_loop.cpp) 56 | 57 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 58 | add_definitions(-DOS_LINUX) 59 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 60 | add_definitions(-DOS_MACOSX) 61 | SET(POLL_SOURCE 62 | src/schedule/kqueue_loop.cpp) 63 | endif() 64 | 65 | add_library(bolt-rpc-client STATIC ${FMT_SOURCE} ${RPC_SOURCE} ${POLL_SOURCE}) 66 | target_link_libraries(bolt-rpc-client Threads::Threads z ${PROTOBUF_LIBRARIES}) 67 | 68 | add_executable(single_request_example ${PROTO_SRCS} 69 | example/single_request_example.cpp) 70 | #target_link_libraries(single_request_example bolt-rpc-client Threads::Threads alog z tcmalloc profiler ${PROTOBUF_LIBRARIES}) 71 | target_link_libraries(single_request_example bolt-rpc-client Threads::Threads ${PROTOBUF_LIBRARIES}) 72 | 73 | add_executable(bolt_heartbeat ${PROTO_SRCS} 74 | example/heartbeat_example.cpp) 75 | target_link_libraries(bolt_heartbeat bolt-rpc-client Threads::Threads ${PROTOBUF_LIBRARIES}) 76 | 77 | add_executable(cloud_rpc_demo ${PROTO_SRCS} 78 | example/cloud_rpc_demo.cpp) 79 | target_link_libraries(cloud_rpc_demo bolt-rpc-client Threads::Threads ${PROTOBUF_LIBRARIES}) 80 | 81 | set(UNIT_TEST_SOURCE 82 | test/unit_test/unittest_main.cpp 83 | test/unit_test/endpoint_unittest.cpp 84 | test/unit_test/channel_unittest.cpp 85 | test/unit_test/io_buffer_unittest.cpp 86 | test/unit_test/lifecyclelock_unittest.cpp 87 | test/unit_test/mpscqueue_unittest.cpp 88 | test/unit_test/simple_socket_server.cpp 89 | test/unit_test/socket_manager_unittest.cpp 90 | test/unit_test/socket_posix_unittest.cpp 91 | test/unit_test/thread_pool_unittest.cpp 92 | test/unit_test/time_thread_unittest.cpp 93 | test/unit_test/uri_unittest.cpp 94 | test/unit_test/utils_unittest.cpp 95 | test/unit_test/http_unittest.cpp 96 | test/unit_test/intrusivelist_unittest.cpp 97 | test/unit_test/concurrenthashmap_unittest.cpp 98 | test/unit_test/lrucache_unittest.cpp) 99 | 100 | PROTOBUF_GENERATE_CPP(TEST_PROTO_SRCS TEST_PROTO_HDRS 101 | test/unit_test/io_buffer_unittest.proto) 102 | 103 | include_directories( 104 | src) 105 | 106 | add_executable(unit_test ${UNIT_TEST_SOURCE} ${TEST_PROTO_HDRS} ${TEST_PROTO_SRCS}) 107 | target_link_libraries(unit_test gtest bolt-rpc-client ${PROTOBUF_LIBRARIES}) 108 | -------------------------------------------------------------------------------- /test/unit_test/channel_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/25. 3 | // 4 | 5 | #include 6 | #include 7 | #include "rpc.h" 8 | #include "tcp/socket.h" 9 | 10 | using namespace antflash; 11 | 12 | namespace antflash { 13 | class ChannelUnitTest { 14 | public: 15 | ChannelUnitTest(Channel& c) { 16 | _socket.reset(); 17 | c.getSocket(_socket); 18 | } 19 | 20 | std::shared_ptr& socket() { 21 | return _socket; 22 | } 23 | 24 | private: 25 | std::shared_ptr _socket; 26 | }; 27 | } 28 | 29 | TEST(ChannelTest, initSingle) { 30 | ASSERT_TRUE(globalInit()); 31 | 32 | Channel channel; 33 | Channel& channel2 = channel; 34 | channel2 = channel; 35 | 36 | ASSERT_EQ(channel.status(), ERpcStatus::RPC_STATUS_INIT); 37 | ASSERT_STREQ(antflash::getRpcStatus(channel.status()), "Status init"); 38 | 39 | channel.init("127.0.0.1:12345", nullptr); 40 | auto& option = channel.option(); 41 | ASSERT_EQ(option.connection_type, EConnectionType::CONNECTION_TYPE_SINGLE); 42 | ASSERT_EQ(option.protocol, EProtocolType::PROTOCOL_BOLT); 43 | ASSERT_EQ(option.connect_timeout_ms, SOCKET_CONNECT_TIMEOUT_MS); 44 | ASSERT_EQ(option.timeout_ms, SOCKET_TIMEOUT_MS); 45 | ASSERT_EQ(option.max_retry, SOCKET_MAX_RETRY); 46 | 47 | ASSERT_NE(channel.status(), ERpcStatus::RPC_STATUS_INIT); 48 | 49 | ChannelUnitTest channel_test(channel); 50 | ASSERT_FALSE(channel_test.socket()); 51 | 52 | globalDestroy(); 53 | } 54 | 55 | TEST(ChannelTest, initShort) { 56 | ASSERT_TRUE(globalInit()); 57 | 58 | ChannelOptions options; 59 | options.protocol = EProtocolType::PROTOCOL_HTTP; 60 | options.connection_type = EConnectionType::CONNECTION_TYPE_SHORT; 61 | 62 | Channel channel; 63 | ASSERT_EQ(channel.status(), ERpcStatus::RPC_STATUS_INIT); 64 | channel.init("127.0.0.1:12200", &options); 65 | 66 | auto& option = channel.option(); 67 | ASSERT_EQ(option.connection_type, EConnectionType::CONNECTION_TYPE_SHORT); 68 | ASSERT_EQ(option.protocol, EProtocolType::PROTOCOL_HTTP); 69 | ASSERT_EQ(option.connect_timeout_ms, SOCKET_CONNECT_TIMEOUT_MS); 70 | ASSERT_EQ(option.timeout_ms, SOCKET_TIMEOUT_MS); 71 | ASSERT_EQ(option.max_retry, SOCKET_MAX_RETRY); 72 | 73 | ChannelUnitTest channel_test(channel); 74 | ASSERT_TRUE(!!channel_test.socket()); 75 | 76 | globalDestroy(); 77 | } 78 | 79 | TEST(ChannelTest, initPool) { 80 | ASSERT_TRUE(globalInit()); 81 | 82 | ChannelOptions options; 83 | options.protocol = EProtocolType::PROTOCOL_HTTP; 84 | options.connection_type = EConnectionType::CONNECTION_TYPE_POOLED; 85 | 86 | Channel channel; 87 | ASSERT_EQ(channel.status(), ERpcStatus::RPC_STATUS_INIT); 88 | channel.init("127.0.0.1:12200", &options); 89 | 90 | auto& option = channel.option(); 91 | ASSERT_EQ(option.connection_type, EConnectionType::CONNECTION_TYPE_POOLED); 92 | ASSERT_EQ(option.protocol, EProtocolType::PROTOCOL_HTTP); 93 | ASSERT_EQ(option.connect_timeout_ms, SOCKET_CONNECT_TIMEOUT_MS); 94 | ASSERT_EQ(option.timeout_ms, SOCKET_TIMEOUT_MS); 95 | ASSERT_EQ(option.max_retry, SOCKET_MAX_RETRY); 96 | 97 | std::thread td([&channel](){ 98 | ChannelUnitTest channel_test(channel); 99 | ASSERT_TRUE(!!channel_test.socket()); 100 | }); 101 | 102 | td.join(); 103 | 104 | globalDestroy(); 105 | } 106 | 107 | TEST(ChannelTest, session) { 108 | Channel channel; 109 | ASSERT_EQ(channel.status(), ERpcStatus::RPC_STATUS_INIT); 110 | Session session; 111 | session.sync(); 112 | ASSERT_TRUE(session.failed()); 113 | ASSERT_EQ(session.getErrText(), "protocol not found"); 114 | session.async([](ESessionError, ResponseBase*){}); 115 | ASSERT_TRUE(session.failed()); 116 | session.reset(); 117 | channel.init("127.0.0.1:1234", nullptr); 118 | session.to(channel); 119 | session.sync(); 120 | ASSERT_TRUE(session.failed()); 121 | session.reset(); 122 | Channel channel1; 123 | channel1.init("127.0.0.1:12200", nullptr); 124 | session.to(channel1); 125 | session.sync(); 126 | ASSERT_TRUE(session.failed()); 127 | 128 | PipelineSession session1; 129 | session1.sync(); 130 | ASSERT_FALSE(session1.failed()); 131 | HttpRequest req; 132 | HttpResponse rsp; 133 | session1.pipe(req, rsp); 134 | session1.sync(); 135 | ASSERT_TRUE(session1.failed()); 136 | } 137 | -------------------------------------------------------------------------------- /include/channel/channel.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/8. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_CHANNEL_H 6 | #define RPC_INCLUDE_CHANNEL_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "protocol/protocol_define.h" 12 | #include "tcp/endpoint.h" 13 | #include "common/common_defines.h" 14 | 15 | namespace antflash { 16 | 17 | enum class EConnectionType { 18 | /** 19 | * Single connection to server, try to hold the connection by heartbeat if protocol 20 | * supported, default connection type. 21 | */ 22 | CONNECTION_TYPE_SINGLE, 23 | /** 24 | * Pooled connection to server, each thread holding one active connection is recommended. 25 | * If connections reach maximum limits, some threads will share connection and sending 26 | * data will be blocked. 27 | */ 28 | CONNECTION_TYPE_POOLED, 29 | /** 30 | * Single connection to server, try reconnect to endpoint every single session. 31 | */ 32 | CONNECTION_TYPE_SHORT 33 | }; 34 | 35 | struct ChannelOptions { 36 | //Constructions 37 | ChannelOptions(); 38 | ChannelOptions(const ChannelOptions&); 39 | ChannelOptions& operator=(const ChannelOptions&); 40 | 41 | /** 42 | * Connection timeout, -1 means waiting for connection permanently. 43 | */ 44 | int32_t connect_timeout_ms; 45 | /** 46 | * One session timeout, include sending and receiving data, -1 means waiting permanently. 47 | */ 48 | int32_t timeout_ms; 49 | /** 50 | * Max retry time, only retry when write/read error, if read timeout, no retry. 51 | */ 52 | int32_t max_retry; 53 | /** 54 | * socket pool size, set in CONNECTION_TYPE_POOLED case. 55 | */ 56 | int32_t pool_size; 57 | /** 58 | * Protocol type, only one kind of protocol can be hold by each channel 59 | */ 60 | EProtocolType protocol; 61 | /** 62 | * Type of connection to server. CONNECTION_TYPE_SINGLE as default connection type 63 | */ 64 | EConnectionType connection_type; 65 | }; 66 | 67 | class Socket; 68 | class SocketPool; 69 | class LifeCycleLock; 70 | struct SubChannel; 71 | 72 | /** 73 | * Channel for RPC, with RPC remote information, connection type, protocol type and so on. 74 | * Channel should be init before any Session use this channel, after init success, Session 75 | * in varied threads could use one single channel parallel. 76 | */ 77 | class Channel { 78 | friend class Session; 79 | friend class ChannelUnitTest; 80 | public: 81 | Channel() : _status(RPC_STATUS_INIT) {} 82 | ~Channel(); 83 | 84 | /** 85 | * Try connecting channel to a single server whose address is given by @address 86 | * non-thread-safe, make sure init channel before using it by multiple threads. 87 | * 88 | * @param address: endpoint's address. 89 | * @param options: channel options. 90 | * @return 91 | */ 92 | bool init(const EndPoint& address, const ChannelOptions* options); 93 | /** 94 | * 95 | * @param address: endpoint's address. 96 | * @param options: channel options. 97 | * @return 98 | */ 99 | bool init(const char* address, const ChannelOptions* options); 100 | 101 | /** 102 | * get a copy string of the endpoint address of channel 103 | * @return 104 | */ 105 | inline std::string getAddress() const { 106 | return _address.ipToStr(); 107 | } 108 | 109 | /****** For Unit Test Begin ******/ 110 | ERpcStatus status() const { 111 | return _status; 112 | } 113 | const ChannelOptions& option() const { 114 | return _options; 115 | } 116 | /****** For Unit Test End ******/ 117 | private: 118 | bool getSocket(std::shared_ptr& socket); 119 | bool getSocketInternal(std::shared_ptr& socket); 120 | bool getSubSocketInternal(std::shared_ptr& socket); 121 | bool tryConnect(std::shared_ptr& socket); 122 | 123 | static void deleteThreadLocalSubChannel(void* arg); 124 | 125 | EndPoint _address; 126 | 127 | std::shared_ptr _socket; 128 | std::vector> _sub_channels; 129 | 130 | std::shared_ptr _lock; 131 | 132 | const Protocol* _protocol; 133 | ChannelOptions _options; 134 | 135 | ERpcStatus _status; 136 | 137 | //As thread_local in C++ could only be static variable, use pthread_key_t instead 138 | pthread_key_t _local_sub_channel_idx; 139 | }; 140 | 141 | } 142 | 143 | #endif //RPC_INCLUDE_CHANNEL_H 144 | -------------------------------------------------------------------------------- /test/unit_test/concurrenthashmap_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/5/22. 3 | // 4 | 5 | #include "common/concurrent_hash_map.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "common/utils.h" 11 | 12 | constexpr size_t s_bucket_size = 1024; 13 | 14 | TEST(ConcurrentHashMapTest, singleThread) { 15 | antflash::ConcurrentHashMap 16 | hash_map(s_bucket_size); 17 | 18 | ASSERT_EQ(hash_map.bucketSize(), 1031UL); 19 | ASSERT_EQ(hash_map.size(), 0UL); 20 | 21 | ASSERT_EQ(hash_map.getHash(0) % hash_map.bucketSize(), 22 | hash_map.getHash(hash_map.bucketSize()) % hash_map.bucketSize()); 23 | 24 | antflash::ConcurrentHashMap 25 | str_hash_map(s_bucket_size, s_bucket_size); 26 | 27 | ASSERT_EQ(str_hash_map.bucketSize(), 1031UL); 28 | ASSERT_EQ(str_hash_map.size(), 0UL); 29 | 30 | ASSERT_NE(str_hash_map.getHash("") % hash_map.bucketSize(), 31 | str_hash_map.getHash("abc") % hash_map.bucketSize()); 32 | 33 | size_t ret = 0; 34 | ASSERT_FALSE(hash_map.get(1, ret)); 35 | ASSERT_TRUE(hash_map.put(1, 10)); 36 | ASSERT_TRUE(hash_map.get(1, ret)); 37 | ASSERT_FALSE(hash_map.get(1 + hash_map.bucketSize(), ret)); 38 | ASSERT_EQ(ret, 10UL); 39 | ASSERT_TRUE(hash_map.put(1 + hash_map.bucketSize(), 101)); 40 | ASSERT_TRUE(hash_map.get(1 + hash_map.bucketSize(), ret)); 41 | ASSERT_EQ(ret, 101UL); 42 | 43 | ret = 11; 44 | ASSERT_FALSE(hash_map.put(1, ret)); 45 | ASSERT_TRUE(hash_map.exchange(1, ret)); 46 | ASSERT_EQ(ret, 10UL); 47 | ASSERT_TRUE(hash_map.get(1, ret)); 48 | ASSERT_EQ(ret, 11UL); 49 | 50 | ASSERT_FALSE(hash_map.put(1 + hash_map.bucketSize(), ret)); 51 | ASSERT_TRUE(hash_map.exchange(1 + hash_map.bucketSize(), ret)); 52 | ASSERT_EQ(ret, 101UL); 53 | ASSERT_TRUE(hash_map.get(1 + hash_map.bucketSize(), ret)); 54 | ASSERT_EQ(ret, 11UL); 55 | hash_map.erase(1 + hash_map.bucketSize()); 56 | ASSERT_FALSE(hash_map.get(1 + hash_map.bucketSize(), ret)); 57 | 58 | ASSERT_FALSE(hash_map.exchange(2, ret)); 59 | ASSERT_EQ(ret, 11UL); 60 | ret = 9; 61 | ASSERT_TRUE(hash_map.get(2, ret)); 62 | ASSERT_EQ(ret, 11UL); 63 | ASSERT_TRUE(hash_map.erase(2)); 64 | ASSERT_FALSE(hash_map.erase(2)); 65 | 66 | ret = 0; 67 | ASSERT_FALSE(str_hash_map.get("1", ret)); 68 | ASSERT_TRUE(str_hash_map.put("1", 10)); 69 | ASSERT_TRUE(str_hash_map.get("1", ret)); 70 | ASSERT_EQ(ret, 10UL); 71 | 72 | ret = 11; 73 | ASSERT_FALSE(str_hash_map.put("1", ret)); 74 | ASSERT_TRUE(str_hash_map.exchange("1", ret)); 75 | ASSERT_EQ(ret, 10UL); 76 | ASSERT_TRUE(str_hash_map.get("1", ret)); 77 | ASSERT_EQ(ret, 11UL); 78 | 79 | ASSERT_FALSE(str_hash_map.exchange("123", ret)); 80 | ASSERT_EQ(ret, 11UL); 81 | ret = 9; 82 | ASSERT_TRUE(str_hash_map.get("123", ret)); 83 | ASSERT_EQ(ret, 11UL); 84 | } 85 | 86 | TEST(ConcurrentHashMapTest, multiThread) { 87 | 88 | constexpr uint32_t thread_num = 4; 89 | constexpr uint32_t num_inserts = 1000000 / thread_num; 90 | constexpr uint32_t num_ops_perThread = num_inserts; 91 | 92 | antflash::ConcurrentHashMap 93 | hash_map(num_inserts, s_bucket_size); 94 | 95 | antflash::Utils::Timer cost; 96 | 97 | std::vector threads; 98 | for (int64_t j = 0; j < thread_num; j++) { 99 | std::random_device rd; 100 | threads.emplace_back([&hash_map, j]() { 101 | for (uint32_t i = 0; i < num_ops_perThread; ++i) { 102 | hash_map.put(i, i / 3); 103 | } 104 | }); 105 | } 106 | 107 | for (auto& td : threads) { 108 | td.join(); 109 | } 110 | 111 | printf("set time cost: %lu us\n", cost.elapsedMicro()); 112 | ASSERT_EQ(hash_map.size(), num_inserts); 113 | for (uint32_t i = 0; i < num_inserts; ++i) { 114 | uint32_t val = 0; 115 | EXPECT_TRUE(hash_map.get(i, val)); 116 | EXPECT_EQ(val, i / 3); 117 | } 118 | 119 | threads.clear(); 120 | cost.reset(); 121 | for (int64_t j = 0; j < thread_num; j++) { 122 | std::random_device rd; 123 | threads.emplace_back([&hash_map, j]() { 124 | for (uint32_t i = 0; i < num_ops_perThread; ++i) { 125 | uint32_t val = 0; 126 | hash_map.get(0, val); 127 | } 128 | }); 129 | } 130 | printf("find time cost: %lu us\n", cost.elapsedMicro()); 131 | for (auto& td : threads) { 132 | td.join(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/common/io_buffer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/20. 3 | // 4 | 5 | #ifndef RPC_IO_BUFFER_REFINE_H 6 | #define RPC_IO_BUFFER_REFINE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "common/common_defines.h" 13 | 14 | namespace antflash { 15 | 16 | struct Block; 17 | 18 | //IO buffer, not thread safe 19 | class IOBuffer { 20 | friend class IOBufferZeroCopyOutputStream; 21 | friend class IOBufferZeroCopyInputStream; 22 | public: 23 | //IOBuffer with Slice, reference to Block. Slice memory allocation 24 | //is much smaller than Block, and several Slices shared one Block's 25 | // reference with different offset and length. 26 | struct Slice { 27 | std::shared_ptr block; 28 | uint32_t offset; 29 | uint32_t length; 30 | 31 | Slice() = default; 32 | ~Slice() = default; 33 | Slice(const Slice&) = default; 34 | Slice& operator=(const Slice&) = default; 35 | Slice(Slice&&) = default; 36 | Slice& operator=(Slice&&) = default; 37 | Slice(const std::shared_ptr& b, uint32_t o, uint32_t l) : 38 | block(b), offset(o), length(l) {} 39 | }; 40 | 41 | IOBuffer(); 42 | 43 | ~IOBuffer(); 44 | 45 | IOBuffer(const IOBuffer &right); 46 | 47 | IOBuffer(IOBuffer &&right); 48 | 49 | IOBuffer &operator=(const IOBuffer &right); 50 | 51 | IOBuffer &operator=(IOBuffer &&right); 52 | 53 | void swap(IOBuffer &right); 54 | 55 | std::string to_string() const; 56 | 57 | std::string dump() const; 58 | 59 | size_t pop_front(size_t n); 60 | 61 | size_t pop_back(size_t n); 62 | 63 | void append(const IOBuffer &other); 64 | 65 | void append(IOBuffer &&other); 66 | 67 | void append(char c); 68 | 69 | inline void append(const std::string &data) { 70 | append(data.data(), data.size()); 71 | } 72 | 73 | void append(const void *data, size_t count); 74 | 75 | void append(const char *data); 76 | 77 | ssize_t append_from_file_descriptor(int fd, size_t max_size); 78 | 79 | size_t cut(IOBuffer *out, size_t n); 80 | 81 | size_t cut(std::string &out, size_t n); 82 | 83 | size_t cut(void *out, size_t n); 84 | 85 | ssize_t cut_into_file_descriptor(int fd, size_t size_hint); 86 | 87 | size_t copy_to(void *buf, size_t n) const; 88 | 89 | void clear() { 90 | _ref.clear(); 91 | _ref_offset = 0; 92 | } 93 | 94 | bool empty() const { 95 | return length() == 0; 96 | } 97 | 98 | size_t length() const { 99 | size_t total_size = 0; 100 | for (size_t i = _ref_offset; i < _ref.size(); ++i) { 101 | total_size += _ref[i].length; 102 | } 103 | return total_size; 104 | } 105 | 106 | size_t size() const { return length(); } 107 | inline size_t slice_num() const { 108 | return _ref.size() - _ref_offset; 109 | } 110 | std::pair slice(size_t i) const; 111 | 112 | /****** For unit test begin *******/ 113 | std::pair block(size_t i) const; 114 | struct BlockUnitTest { 115 | std::weak_ptr block; 116 | bool exist(); 117 | }; 118 | BlockUnitTest weakBlock(size_t i); 119 | /****** For unit test end *******/ 120 | private: 121 | void tryReleaseBlock(); 122 | 123 | std::vector _ref; 124 | size_t _ref_offset; 125 | }; 126 | 127 | class IOBufferZeroCopyInputStream 128 | : public google::protobuf::io::ZeroCopyInputStream { 129 | public: 130 | IOBufferZeroCopyInputStream(const IOBuffer &); 131 | 132 | bool Next(const void **data, int *size) override; 133 | 134 | ~IOBufferZeroCopyInputStream() override; 135 | 136 | void BackUp(int count) override; 137 | 138 | bool Skip(int count) override; 139 | 140 | google::protobuf::int64 ByteCount() const override; 141 | 142 | private: 143 | const IOBuffer *_buf; 144 | size_t _byte_count; 145 | size_t _add_offset; 146 | size_t _ref_index; 147 | }; 148 | 149 | class IOBufferZeroCopyOutputStream 150 | : public google::protobuf::io::ZeroCopyOutputStream { 151 | public: 152 | IOBufferZeroCopyOutputStream(IOBuffer *); 153 | 154 | ~IOBufferZeroCopyOutputStream() {} 155 | 156 | bool Next(void **data, int *size) override; 157 | 158 | void BackUp(int count) override; 159 | 160 | google::protobuf::int64 ByteCount() const override; 161 | 162 | private: 163 | IOBuffer *_buf; 164 | size_t _byte_count; 165 | }; 166 | 167 | } 168 | 169 | #endif //RPC_IO_BUFFER_REFINE_H 170 | -------------------------------------------------------------------------------- /second_party/fmt/include/fmt/time.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - time formatting 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_TIME_H_ 9 | #define FMT_TIME_H_ 10 | 11 | #include "format.h" 12 | #include 13 | 14 | FMT_BEGIN_NAMESPACE 15 | 16 | namespace internal{ 17 | inline null<> localtime_r(...) { return null<>(); } 18 | inline null<> localtime_s(...) { return null<>(); } 19 | inline null<> gmtime_r(...) { return null<>(); } 20 | inline null<> gmtime_s(...) { return null<>(); } 21 | } 22 | 23 | // Thread-safe replacement for std::localtime 24 | inline std::tm localtime(std::time_t time) { 25 | struct dispatcher { 26 | std::time_t time_; 27 | std::tm tm_; 28 | 29 | dispatcher(std::time_t t): time_(t) {} 30 | 31 | bool run() { 32 | using namespace fmt::internal; 33 | return handle(localtime_r(&time_, &tm_)); 34 | } 35 | 36 | bool handle(std::tm *tm) { return tm != FMT_NULL; } 37 | 38 | bool handle(internal::null<>) { 39 | using namespace fmt::internal; 40 | return fallback(localtime_s(&tm_, &time_)); 41 | } 42 | 43 | bool fallback(int res) { return res == 0; } 44 | 45 | bool fallback(internal::null<>) { 46 | using namespace fmt::internal; 47 | std::tm *tm = std::localtime(&time_); 48 | if (tm) tm_ = *tm; 49 | return tm != FMT_NULL; 50 | } 51 | }; 52 | dispatcher lt(time); 53 | if (lt.run()) 54 | return lt.tm_; 55 | // Too big time values may be unsupported. 56 | FMT_THROW(format_error("time_t value out of range")); 57 | } 58 | 59 | // Thread-safe replacement for std::gmtime 60 | inline std::tm gmtime(std::time_t time) { 61 | struct dispatcher { 62 | std::time_t time_; 63 | std::tm tm_; 64 | 65 | dispatcher(std::time_t t): time_(t) {} 66 | 67 | bool run() { 68 | using namespace fmt::internal; 69 | return handle(gmtime_r(&time_, &tm_)); 70 | } 71 | 72 | bool handle(std::tm *tm) { return tm != FMT_NULL; } 73 | 74 | bool handle(internal::null<>) { 75 | using namespace fmt::internal; 76 | return fallback(gmtime_s(&tm_, &time_)); 77 | } 78 | 79 | bool fallback(int res) { return res == 0; } 80 | 81 | bool fallback(internal::null<>) { 82 | std::tm *tm = std::gmtime(&time_); 83 | if (tm) tm_ = *tm; 84 | return tm != FMT_NULL; 85 | } 86 | }; 87 | dispatcher gt(time); 88 | if (gt.run()) 89 | return gt.tm_; 90 | // Too big time values may be unsupported. 91 | FMT_THROW(format_error("time_t value out of range")); 92 | } 93 | 94 | namespace internal { 95 | inline std::size_t strftime(char *str, std::size_t count, const char *format, 96 | const std::tm *time) { 97 | return std::strftime(str, count, format, time); 98 | } 99 | 100 | inline std::size_t strftime(wchar_t *str, std::size_t count, 101 | const wchar_t *format, const std::tm *time) { 102 | return std::wcsftime(str, count, format, time); 103 | } 104 | } 105 | 106 | template 107 | struct formatter { 108 | template 109 | auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { 110 | auto it = internal::null_terminating_iterator(ctx); 111 | if (*it == ':') 112 | ++it; 113 | auto end = it; 114 | while (*end && *end != '}') 115 | ++end; 116 | tm_format.reserve(end - it + 1); 117 | using internal::pointer_from; 118 | tm_format.append(pointer_from(it), pointer_from(end)); 119 | tm_format.push_back('\0'); 120 | return pointer_from(end); 121 | } 122 | 123 | template 124 | auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) { 125 | internal::basic_buffer &buf = internal::get_container(ctx.out()); 126 | std::size_t start = buf.size(); 127 | for (;;) { 128 | std::size_t size = buf.capacity() - start; 129 | std::size_t count = 130 | internal::strftime(&buf[start], size, &tm_format[0], &tm); 131 | if (count != 0) { 132 | buf.resize(start + count); 133 | break; 134 | } 135 | if (size >= tm_format.size() * 256) { 136 | // If the buffer is 256 times larger than the format string, assume 137 | // that `strftime` gives an empty result. There doesn't seem to be a 138 | // better way to distinguish the two cases: 139 | // https://github.com/fmtlib/fmt/issues/367 140 | break; 141 | } 142 | const std::size_t MIN_GROWTH = 10; 143 | buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); 144 | } 145 | return ctx.out(); 146 | } 147 | 148 | basic_memory_buffer tm_format; 149 | }; 150 | FMT_END_NAMESPACE 151 | 152 | #endif // FMT_TIME_H_ 153 | -------------------------------------------------------------------------------- /include/session/session.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/4. 3 | // 4 | 5 | #ifndef RPC_INCLUDE_SESSION_H 6 | #define RPC_INCLUDE_SESSION_H 7 | 8 | #include 9 | #include "channel/channel.h" 10 | #include "protocol/request_base.h" 11 | #include "protocol/response_base.h" 12 | 13 | namespace antflash { 14 | 15 | using SessionAsyncCallback = std::function; 16 | struct SocketReadSession; 17 | 18 | class Session final { 19 | friend class SocketManager; 20 | friend class PipelineSession; 21 | public: 22 | Session() : _timeout(-1), 23 | _retry(-1), 24 | _protocol(nullptr), 25 | _request(nullptr), 26 | _response(nullptr), 27 | _error_code(ESessionError::SESSION_OK), 28 | _channel(nullptr) {} 29 | ~Session(); 30 | 31 | //Set request data to be sent. Before session sync/async function returns, 32 | //DO NOT release request's memory as session just hold reference of this 33 | //request. 34 | Session& send(const RequestBase& request) { 35 | _request = &request; 36 | return *this; 37 | } 38 | 39 | //Set channel which request data will be sent to, it's safe to set one channel 40 | //to several sessions in multiple threads.Before session sync/async function 41 | //returns, DO NOT release channel's memory. 42 | Session& to(Channel& channel); 43 | 44 | //Set timeout to this session, and this timeout is higher priority than channel's 45 | //timeout, if not set, use channel's timeout by default. 46 | inline Session& timeout(int32_t timeout) { 47 | _timeout = timeout; 48 | return *this; 49 | } 50 | 51 | //NOT USE RIGHT NOW 52 | inline Session& maxRetry(int32_t retry) { 53 | _retry = retry; 54 | return *this; 55 | } 56 | 57 | //Set response to store data that session receive from server. Before session 58 | // sync/async function returns, DO NOT release request's memory as session 59 | // just hold reference of this response. 60 | inline Session& receiveTo(ResponseBase& response) { 61 | _response = &response; 62 | return *this; 63 | } 64 | 65 | //Send data to server synchronized, block the thread calling this sync function. 66 | Session& sync(); 67 | 68 | Session& async(SessionAsyncCallback callback); 69 | 70 | //Check if session sync/async function success, if failed, use getErrText to 71 | //get detail information. 72 | bool failed() { 73 | return _error_code != ESessionError::SESSION_OK; 74 | } 75 | 76 | //return detail information of session. 77 | const std::string& getErrText() const; 78 | 79 | //If a session is used to send data sync/async, remember to reset this session 80 | //before next calling. 81 | void reset(); 82 | 83 | static const std::string& getErrText(ESessionError); 84 | 85 | private: 86 | //Only be used in SocketManager 87 | Session& to(std::shared_ptr& socket); 88 | 89 | void sendInternalWithRetry(SessionAsyncCallback* callback); 90 | void sendInternal(SessionAsyncCallback* callback); 91 | 92 | int32_t _timeout; 93 | int32_t _retry; 94 | size_t _begin_time_us; 95 | 96 | const Protocol* _protocol; 97 | const RequestBase* _request; 98 | ResponseBase* _response; 99 | 100 | ESessionError _error_code; 101 | 102 | Channel* _channel; 103 | std::shared_ptr _socket; 104 | }; 105 | 106 | class PipelineSession { 107 | public: 108 | PipelineSession() : _channel(nullptr) {} 109 | ~PipelineSession() {} 110 | 111 | //Set channel which request data will be sent to, it's safe to set one channel 112 | //to several sessions in multiple threads.Before pipe session sync/async 113 | //returns, DO NOT release channel's memory. 114 | inline PipelineSession& to(Channel& channel) { 115 | _channel = &channel; 116 | return *this; 117 | } 118 | 119 | //Set timeout to this session, and this timeout is higher priority than channel's 120 | //timeout, if not set, use channel's timeout by default. 121 | inline PipelineSession& timeout(int32_t timeout) { 122 | _session.timeout(timeout); 123 | return *this; 124 | } 125 | 126 | inline PipelineSession& reserve(size_t size) { 127 | _requests.reserve(size); 128 | _responses.reserve(size); 129 | return *this; 130 | } 131 | 132 | inline PipelineSession& pipe(const RequestBase &req, ResponseBase &rsp) { 133 | _requests.emplace_back(&req); 134 | _responses.emplace_back(&rsp); 135 | return *this; 136 | } 137 | 138 | PipelineSession& sync(); 139 | 140 | //Check if session sync/async function success, if failed, use getErrText to 141 | //get detail information. 142 | inline bool failed() { 143 | return _session.failed(); 144 | } 145 | 146 | //return detail information of session. 147 | inline std::string getErrText() const { 148 | return _session.getErrText() + _additional_err_text; 149 | } 150 | 151 | private: 152 | std::vector _requests; 153 | std::vector _responses; 154 | Session _session; 155 | Channel* _channel; 156 | std::string _additional_err_text; 157 | }; 158 | 159 | } 160 | 161 | #endif //RPC_INCLUDE_SESSION_H 162 | -------------------------------------------------------------------------------- /second_party/fmt/include/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include "format.h" 12 | #include 13 | 14 | FMT_BEGIN_NAMESPACE 15 | namespace internal { 16 | 17 | template 18 | class formatbuf : public std::basic_streambuf { 19 | private: 20 | typedef typename std::basic_streambuf::int_type int_type; 21 | typedef typename std::basic_streambuf::traits_type traits_type; 22 | 23 | basic_buffer &buffer_; 24 | 25 | public: 26 | formatbuf(basic_buffer &buffer) : buffer_(buffer) {} 27 | 28 | protected: 29 | // The put-area is actually always empty. This makes the implementation 30 | // simpler and has the advantage that the streambuf and the buffer are always 31 | // in sync and sputc never writes into uninitialized memory. The obvious 32 | // disadvantage is that each call to sputc always results in a (virtual) call 33 | // to overflow. There is no disadvantage here for sputn since this always 34 | // results in a call to xsputn. 35 | 36 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 37 | if (!traits_type::eq_int_type(ch, traits_type::eof())) 38 | buffer_.push_back(static_cast(ch)); 39 | return ch; 40 | } 41 | 42 | std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { 43 | buffer_.append(s, s + count); 44 | return count; 45 | } 46 | }; 47 | 48 | template 49 | struct test_stream : std::basic_ostream { 50 | private: 51 | struct null; 52 | // Hide all operator<< from std::basic_ostream. 53 | void operator<<(null); 54 | }; 55 | 56 | // Checks if T has a user-defined operator<< (e.g. not a member of std::ostream). 57 | template 58 | class is_streamable { 59 | private: 60 | template 61 | static decltype( 62 | internal::declval&>() 63 | << internal::declval(), std::true_type()) test(int); 64 | 65 | template 66 | static std::false_type test(...); 67 | 68 | typedef decltype(test(0)) result; 69 | 70 | public: 71 | // std::string operator<< is not considered user-defined because we handle strings 72 | // specially. 73 | static const bool value = result::value && !std::is_same::value; 74 | }; 75 | 76 | // Disable conversion to int if T has an overloaded operator<< which is a free 77 | // function (not a member of std::ostream). 78 | template 79 | class convert_to_int { 80 | public: 81 | static const bool value = 82 | convert_to_int::value && !is_streamable::value; 83 | }; 84 | 85 | // Write the content of buf to os. 86 | template 87 | void write(std::basic_ostream &os, basic_buffer &buf) { 88 | const Char *data = buf.data(); 89 | typedef std::make_unsigned::type UnsignedStreamSize; 90 | UnsignedStreamSize size = buf.size(); 91 | UnsignedStreamSize max_size = 92 | internal::to_unsigned((std::numeric_limits::max)()); 93 | do { 94 | UnsignedStreamSize n = size <= max_size ? size : max_size; 95 | os.write(data, static_cast(n)); 96 | data += n; 97 | size -= n; 98 | } while (size != 0); 99 | } 100 | 101 | template 102 | void format_value(basic_buffer &buffer, const T &value) { 103 | internal::formatbuf format_buf(buffer); 104 | std::basic_ostream output(&format_buf); 105 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 106 | output << value; 107 | buffer.resize(buffer.size()); 108 | } 109 | 110 | // Disable builtin formatting of enums and use operator<< instead. 111 | template 112 | struct format_enum::value>::type> : std::false_type {}; 114 | } // namespace internal 115 | 116 | // Formats an object of type T that has an overloaded ostream operator<<. 117 | template 118 | struct formatter::value>::type> 120 | : formatter, Char> { 121 | 122 | template 123 | auto format(const T &value, Context &ctx) -> decltype(ctx.out()) { 124 | basic_memory_buffer buffer; 125 | internal::format_value(buffer, value); 126 | basic_string_view str(buffer.data(), buffer.size()); 127 | formatter, Char>::format(str, ctx); 128 | return ctx.out(); 129 | } 130 | }; 131 | 132 | template 133 | inline void vprint(std::basic_ostream &os, 134 | basic_string_view format_str, 135 | basic_format_args::type> args) { 136 | basic_memory_buffer buffer; 137 | vformat_to(buffer, format_str, args); 138 | internal::write(os, buffer); 139 | } 140 | /** 141 | \rst 142 | Prints formatted data to the stream *os*. 143 | 144 | **Example**:: 145 | 146 | fmt::print(cerr, "Don't {}!", "panic"); 147 | \endrst 148 | */ 149 | template 150 | inline void print(std::ostream &os, string_view format_str, 151 | const Args & ... args) { 152 | vprint(os, format_str, make_format_args(args...)); 153 | } 154 | 155 | template 156 | inline void print(std::wostream &os, wstring_view format_str, 157 | const Args & ... args) { 158 | vprint(os, format_str, make_format_args(args...)); 159 | } 160 | FMT_END_NAMESPACE 161 | 162 | #endif // FMT_OSTREAM_H_ 163 | -------------------------------------------------------------------------------- /test/unit_test/http_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/25. 3 | // 4 | 5 | #include "protocol/http/http_protocol.h" 6 | #include "protocol/http/http_request.h" 7 | #include "protocol/http/http_response.h" 8 | #include "protocol/bolt/bolt_protocol.h" 9 | #include "protocol/bolt/bolt_request.h" 10 | #include "protocol/bolt/bolt_response.h" 11 | #include "common/io_buffer.h" 12 | #include 13 | #include 14 | 15 | TEST(HttpTest, baseFunction) { 16 | { 17 | antflash::HttpRequest request; 18 | request.uri("127.0.0.1:12200") 19 | .method(antflash::HTTP_METHOD_POST) 20 | .attach("subscribe") 21 | .attach_type("application/json"); 22 | ASSERT_TRUE(request.uri().parse_ok()); 23 | ASSERT_EQ(request.attach_size(), 9UL); 24 | ASSERT_EQ(request.attach()->to_string(), std::string("subscribe")); 25 | ASSERT_EQ(request.attach_type(), std::string("application/json")); 26 | 27 | antflash::IOBuffer buffer; 28 | auto ret = antflash::http::assembleHttpRequest(request, 1, buffer); 29 | ASSERT_TRUE(ret); 30 | 31 | antflash::HttpRequest request2(request); 32 | std::string attach("unsubscribe"); 33 | request2.attach(attach).attach_type(antflash::CONNECTION); 34 | ASSERT_EQ(request2.attach()->to_string(), attach); 35 | ASSERT_EQ(request2.attach_size(), attach.length()); 36 | ASSERT_EQ(request2.attach_type(), std::string("connection")); 37 | } 38 | { 39 | antflash::HttpResponse response; 40 | antflash::IOBuffer buffer; 41 | ASSERT_EQ(response.deserialize(buffer), 42 | antflash::EResParseResult::PARSE_ERROR); 43 | 44 | struct http_parser* parser = nullptr; 45 | void* p = parser; 46 | size_t unused = 0; 47 | antflash::HttpResponse::checkHeader(buffer, unused, unused, nullptr); 48 | antflash::HttpResponse::checkHeader(buffer, unused, unused, &p); 49 | antflash::HttpResponse::checkHeader(buffer, unused, unused, &p); 50 | 51 | response.setHttpParser((void*)parser); 52 | ASSERT_EQ(response.deserialize(buffer), 53 | antflash::EResParseResult::PARSE_ERROR); 54 | } 55 | { 56 | antflash::HttpRequest request; 57 | request.uri("https://127.0.0.1:8080/test? abc ") 58 | .method(antflash::HTTP_METHOD_POST) 59 | .attach("subscribe") 60 | .attach_type("application/json"); 61 | antflash::IOBuffer buffer; 62 | auto ret = antflash::http::assembleHttpRequest(request, 1, buffer); 63 | ASSERT_FALSE(ret); 64 | antflash::http::assembleHeartbeatRequest(buffer); 65 | antflash::http::converseHttpRequest(10UL); 66 | } 67 | { 68 | antflash::IOBuffer buffer; 69 | std::string str("HTTP/1.1 200 OK\n" 70 | "Content-Length: 61\n" 71 | "Content-Type: application/json\n" 72 | "Host: 127.0.0.1:13330\n" 73 | "Accept: */*\n" 74 | "User-Agent: curl/7.0\n" 75 | "connection: keep-alive\n" 76 | "log-id: 1\n" 77 | "\n" 78 | "{\"DataCenter\":\"dc\",\"AppName\":\"testApp\",\"AntShareCloud\":false}"); 79 | buffer.append(str); 80 | antflash::HttpResponse response; 81 | struct http_parser* parser = nullptr; 82 | void* p = (void*)parser; 83 | size_t data_size = 0; 84 | size_t request_id = 0; 85 | ASSERT_EQ(antflash::http::parseHttpProtocol(buffer, data_size, request_id, &p), 86 | antflash::EResParseResult::PARSE_OK); 87 | ASSERT_EQ(antflash::http::parseHttpResponse(response, buffer, p), 88 | antflash::EResParseResult::PARSE_OK); 89 | 90 | ASSERT_EQ(data_size, 0UL); 91 | ASSERT_EQ(request_id, 0UL); 92 | } 93 | { 94 | antflash::IOBuffer buffer; 95 | std::string str("HTTP/1.1 200 OK\n" 96 | "Content-Length: 61\n" 97 | "Content-Type: application/json\n" 98 | "Host: 127.0.0.1:13330\n" 99 | "Accept: */*\n" 100 | ": curl/7.0\n" 101 | "connection: keep-alive" 102 | "log-id: 1\n"); 103 | 104 | buffer.append(str); 105 | antflash::HttpResponse response; 106 | struct http_parser* parser = nullptr; 107 | void* p = (void*)parser; 108 | size_t data_size = 0; 109 | size_t request_id = 0; 110 | ASSERT_EQ(antflash::http::parseHttpProtocol(buffer, data_size, request_id, &p), 111 | antflash::EResParseResult::PARSE_ERROR); 112 | ASSERT_EQ(antflash::http::parseHttpResponse(response, buffer, p), 113 | antflash::EResParseResult::PARSE_ERROR); 114 | 115 | ASSERT_EQ(data_size, 0UL); 116 | ASSERT_EQ(request_id, 0UL); 117 | } 118 | } 119 | 120 | TEST(BoltTest, baseFunction) { 121 | { 122 | antflash::BoltRequest request; 123 | antflash::IOBuffer buffer; 124 | request.data("test"); 125 | auto ret = antflash::bolt::assembleBoltRequest(request, 1, buffer); 126 | ASSERT_TRUE(ret); 127 | 128 | std::string str("test2"); 129 | request.data(str); 130 | ret = antflash::bolt::assembleBoltRequest(request, 1, buffer); 131 | ASSERT_TRUE(ret); 132 | 133 | std::shared_ptr response; 134 | ASSERT_FALSE(antflash::bolt::parseHeartbeatResponse(response)); 135 | response = std::make_shared(); 136 | ASSERT_FALSE(antflash::bolt::parseHeartbeatResponse(response)); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /test/unit_test/time_thread_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/25. 3 | // 4 | 5 | #include 6 | #include 7 | #include "common/time_thread.h" 8 | #include "common/utils.h" 9 | 10 | using namespace antflash; 11 | 12 | TEST(TimeThreadTest, init) { 13 | TimeThread time_thread; 14 | ASSERT_TRUE(time_thread.init()); 15 | time_thread.destroy(); 16 | } 17 | 18 | TEST(TimeThreadTest, schedule) { 19 | TimeThread time_thread; 20 | ASSERT_TRUE(time_thread.init()); 21 | 22 | auto current = Utils::getHighPrecisionTimeStamp(); 23 | ASSERT_GT(time_thread.schedule(10, [current](){ 24 | auto now = Utils::getHighPrecisionTimeStamp(); 25 | EXPECT_EQ((now - current) / 1000, 10UL); 26 | }), 0U); 27 | 28 | std::this_thread::sleep_for(std::chrono::milliseconds(11)); 29 | } 30 | 31 | TEST(TimeThreadTest, multi_schedule) { 32 | TimeThread time_thread; 33 | ASSERT_TRUE(time_thread.init()); 34 | 35 | std::thread td1([&time_thread]{ 36 | auto current = Utils::getHighPrecisionTimeStamp(); 37 | ASSERT_GT(time_thread.schedule(10, [current](){ 38 | auto now = Utils::getHighPrecisionTimeStamp(); 39 | ASSERT_EQ((now - current) / 1000, 10UL); 40 | }), 0U); 41 | 42 | std::this_thread::sleep_for(std::chrono::milliseconds(11)); 43 | }); 44 | 45 | std::thread td2([&time_thread]{ 46 | auto current = Utils::getHighPrecisionTimeStamp(); 47 | ASSERT_GT(time_thread.schedule(10, [current](){ 48 | auto now = Utils::getHighPrecisionTimeStamp(); 49 | ASSERT_EQ((now - current) / 1000, 10UL); 50 | }), 0U); 51 | 52 | std::this_thread::sleep_for(std::chrono::milliseconds(11)); 53 | }); 54 | 55 | std::thread td3([&time_thread]{ 56 | auto current = Utils::getHighPrecisionTimeStamp(); 57 | ASSERT_GT(time_thread.schedule(11, [current](){ 58 | auto now = Utils::getHighPrecisionTimeStamp(); 59 | ASSERT_EQ((now - current) / 1000, 11UL); 60 | }), 0U); 61 | 62 | std::this_thread::sleep_for(std::chrono::milliseconds(12)); 63 | }); 64 | 65 | td1.join(); 66 | td2.join(); 67 | td3.join(); 68 | } 69 | 70 | TEST(TimeThreadTest, unschedule) { 71 | TimeThread time_thread; 72 | ASSERT_TRUE(time_thread.init()); 73 | 74 | bool executed = false; 75 | auto task_id = time_thread.schedule(10, [&executed](){ 76 | executed = true; 77 | }); 78 | ASSERT_GT(task_id, 0UL); 79 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 80 | ASSERT_TRUE(time_thread.unschedule(task_id)); 81 | std::this_thread::sleep_for(std::chrono::milliseconds(6)); 82 | ASSERT_TRUE(!executed); 83 | 84 | executed = false; 85 | auto old_task_id = task_id; 86 | task_id = time_thread.schedule(10, [&executed](){ 87 | executed = true; 88 | }); 89 | ASSERT_GT(task_id, old_task_id); 90 | std::this_thread::sleep_for(std::chrono::microseconds(9800)); 91 | ASSERT_TRUE(time_thread.unschedule(task_id)); 92 | std::this_thread::sleep_for(std::chrono::milliseconds(3)); 93 | ASSERT_TRUE(!executed); 94 | } 95 | 96 | TEST(TimeThreadTest, multi_unschedule) { 97 | TimeThread time_thread; 98 | ASSERT_TRUE(time_thread.init()); 99 | 100 | std::thread td1([&time_thread]{ 101 | bool executed = false; 102 | auto task_id = time_thread.schedule(10, [&executed](){ 103 | executed = true; 104 | }); 105 | ASSERT_GT(task_id, 0UL); 106 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 107 | ASSERT_TRUE(time_thread.unschedule(task_id)); 108 | std::this_thread::sleep_for(std::chrono::milliseconds(6)); 109 | ASSERT_TRUE(!executed); 110 | }); 111 | 112 | std::thread td2([&time_thread]{ 113 | bool executed = false; 114 | auto task_id = time_thread.schedule(10, [&executed](){ 115 | executed = true; 116 | }); 117 | ASSERT_GT(task_id, 0UL); 118 | std::this_thread::sleep_for(std::chrono::microseconds(9800)); 119 | ASSERT_TRUE(time_thread.unschedule(task_id)); 120 | std::this_thread::sleep_for(std::chrono::milliseconds(3)); 121 | ASSERT_TRUE(!executed); 122 | }); 123 | 124 | std::thread td3([&time_thread]{ 125 | bool executed = false; 126 | auto task_id = time_thread.schedule(10, [&executed](){ 127 | executed = true; 128 | }); 129 | ASSERT_GT(task_id, 0UL); 130 | std::this_thread::sleep_for(std::chrono::milliseconds(12)); 131 | ASSERT_TRUE(time_thread.unschedule(task_id)); 132 | ASSERT_TRUE(executed); 133 | }); 134 | 135 | td1.join(); 136 | td2.join(); 137 | td3.join(); 138 | } 139 | 140 | TEST(TimeThreadTest, multi_cross_unschedule) { 141 | TimeThread time_thread; 142 | ASSERT_TRUE(time_thread.init()); 143 | 144 | size_t task_id = 0; 145 | std::thread td1([&time_thread, &task_id]{ 146 | bool executed = false; 147 | task_id = time_thread.schedule(10, [&executed](){ 148 | executed = true; 149 | }); 150 | ASSERT_GT(task_id, 0UL); 151 | std::this_thread::sleep_for(std::chrono::milliseconds(11)); 152 | ASSERT_TRUE(!executed); 153 | }); 154 | 155 | std::thread td2([&time_thread, &task_id]{ 156 | while (task_id == 0UL) { 157 | } 158 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 159 | ASSERT_TRUE(time_thread.unschedule(task_id)); 160 | }); 161 | 162 | std::thread td3([&time_thread, &task_id]{ 163 | while (task_id == 0UL) { 164 | } 165 | std::this_thread::sleep_for(std::chrono::milliseconds(12)); 166 | ASSERT_TRUE(time_thread.unschedule(task_id)); 167 | }); 168 | 169 | td1.join(); 170 | td2.join(); 171 | td3.join(); 172 | } 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/tcp/socket.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/2. 3 | // 4 | 5 | #ifndef RPC_TCP_SOCKET_H 6 | #define RPC_TCP_SOCKET_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "socket_base.h" 14 | #include "common/common_defines.h" 15 | #include "common/io_buffer.h" 16 | #include "common/life_cycle_lock.h" 17 | #include "common/lockfree_queue.h" 18 | 19 | namespace antflash { 20 | 21 | struct Protocol; 22 | class Socket; 23 | class ResponseBase; 24 | 25 | struct SocketReadSession { 26 | enum ESocketReadStatus : int32_t { 27 | INIT = 0, 28 | SUCCESS, 29 | FAIL 30 | }; 31 | //Request ID 32 | size_t request_id; 33 | 34 | //Request time 35 | size_t request_time; 36 | 37 | //Async session callback 38 | std::function callback; 39 | ResponseBase* response; 40 | size_t expire_time; 41 | size_t timer_task_id; 42 | 43 | //Sync Notify specific session data is ready or timeout 44 | std::promise result; 45 | 46 | //Protocol that session used 47 | const Protocol* protocol; 48 | 49 | //life cycle and owner status 50 | LifeCycleLock owners; 51 | 52 | //read buffer 53 | IOBuffer read_buf; 54 | 55 | //other data info 56 | void* data; 57 | 58 | /** 59 | * To notify session and do post process when receive data or timeout. 60 | * As notify could be called by two thread:OnRead thread and Timeout thread, 61 | * and read session memory is always reclaimed by OnRead thread. We should 62 | * make sure timeout event always happen and release shared status in timeout 63 | * event. There are two scenarios: 64 | * 1, OnRead happens before timeout: OnRead upgrade owner success and go on 65 | * dealing post process and timeout thread try upgrade fail, timeout thread 66 | * just release shared status. 67 | * 2, OnRead happens after timeout: OnRead upgrade owner fail and timeout thread 68 | * deals with post process and then release shared status. 69 | * For Sync case: No matter which thread notify waiting work thread, add shared 70 | * num before notify, and make sure memory not be reclaimed during work thread 71 | * doing post process. 72 | * As session memory is reclaimed in OnRead thread, so reclaim action and notify 73 | * wouldn't happen at same time in OnRead thread, we don't need to worry about 74 | * session memory segment in OnRead thread. 75 | * @param err session error code 76 | */ 77 | inline bool notify(ESessionError err) { 78 | //If shared fail, it means the other thread holds upgrade 79 | if (!owners.tryShared()) { 80 | return false; 81 | } 82 | 83 | //If upgrade fail, it means the other thread holds upgrade 84 | bool notify_result = false; 85 | if (owners.tryUpgradeNonReEntrant()) { 86 | notify_result = true; 87 | //For async case 88 | if (callback) { 89 | postProcess(err); 90 | callback(err, response); 91 | } else { 92 | //In sync case, set promise and return, in this step 93 | //owners has two shared owner, one for timeout thread, 94 | //one for sync working thread. 95 | result.set_value(err); 96 | return notify_result; 97 | } 98 | } 99 | //Release sync shared status if sync fail or in async case 100 | owners.releaseShared(); 101 | return notify_result; 102 | } 103 | 104 | /** 105 | * Post process when receive data or timeout. For some protocol like 106 | * bolt, read session may just analyse header of buffer, the whole 107 | * deserialize will be done in this method. The other protocol like 108 | * http, post process do nothing. 109 | * @param error 110 | */ 111 | void postProcess(ESessionError& error); 112 | }; 113 | 114 | class Socket : public std::enable_shared_from_this { 115 | friend class SocketManager; 116 | public: 117 | Socket(const EndPoint& remote) : 118 | _remote(remote), 119 | _status(RPC_STATUS_INIT), 120 | _session_info(MAX_PARALLEL_SESSION_SIZE_ON_SOCKET), 121 | _read_additional_data(nullptr) { 122 | _session_map.reserve(MAX_PARALLEL_SESSION_SIZE_ON_SOCKET); 123 | } 124 | ~Socket(); 125 | 126 | /** 127 | * return socket's file descriptor 128 | * @return file descriptor 129 | */ 130 | int fd() const { 131 | return _fd.fd(); 132 | } 133 | 134 | /** 135 | * return socket's related remote endpoint 136 | * @return endpoint 137 | */ 138 | EndPoint getRemote() const { 139 | return _remote; 140 | } 141 | 142 | void setBindProtocol(const Protocol *protocol); 143 | const Protocol* getProtocol() const; 144 | 145 | bool connect(int32_t connect_timeout_ms = -1); 146 | void disconnect(); 147 | bool active() { 148 | return _status.load(std::memory_order_relaxed) == RPC_STATUS_OK; 149 | } 150 | bool write(IOBuffer& buffer, int32_t timeout_ms); 151 | 152 | size_t get_last_active_time() const { 153 | return _last_active_time_us.load(std::memory_order_acquire); 154 | } 155 | 156 | bool prepareRead(SocketReadSession *val) { 157 | return _session_info.push(val); 158 | } 159 | 160 | inline bool tryShared() { 161 | return _sharers.tryShared(); 162 | } 163 | 164 | inline void releaseShared() { 165 | return _sharers.releaseShared(); 166 | } 167 | 168 | inline bool tryExclusive() { 169 | return _sharers.tryUpgrade() 170 | && _sharers.tryExclusive(); 171 | } 172 | 173 | inline void exclusive() { 174 | _sharers.exclusive(); 175 | } 176 | 177 | inline void upgrade() { 178 | _sharers.upgrade(); 179 | } 180 | 181 | inline void releaseExclusive() { 182 | _sharers.releaseExclusive(); 183 | } 184 | 185 | inline void setStatus(ERpcStatus fail) { 186 | _status.store(fail, std::memory_order_relaxed); 187 | } 188 | 189 | private: 190 | void onRead(); 191 | void tryReclaimSessionMap(); 192 | ssize_t cutIntoMessage(); 193 | 194 | struct SocketConnection { 195 | int32_t timeout_ms; 196 | std::function on_connection; 197 | }; 198 | 199 | EndPoint _remote; 200 | 201 | base::FdGuard _fd; 202 | SocketConnection _connection; 203 | std::atomic _status; 204 | 205 | std::function _on_read; 206 | const Protocol* _protocol; 207 | IOBuffer _read_buf; 208 | 209 | MPSCQueue _session_info; 210 | std::unordered_map _session_map; 211 | 212 | std::atomic _last_active_time_us; 213 | std::recursive_mutex _write_mtx; 214 | LifeCycleLock _sharers; 215 | 216 | //TODO use more effective and proper way to transfer data 217 | void* _read_additional_data; 218 | }; 219 | 220 | } 221 | 222 | #endif //RPC_TCP_SOCKET_H 223 | -------------------------------------------------------------------------------- /src/protocol/bolt/bolt_protocol.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/4. 3 | // 4 | 5 | #include "bolt_protocol.h" 6 | #include 7 | #include "protocol/bolt/bolt_request.h" 8 | #include "protocol/bolt/bolt_response.h" 9 | #include "common/io_buffer.h" 10 | #include "common/log.h" 11 | 12 | namespace antflash { 13 | namespace bolt { 14 | 15 | struct [[gnu::packed]] BoltRequestHeader { 16 | const uint8_t proto = BOLT_PROTOCOL_TYPE; 17 | const uint8_t type = BOLT_PROTOCOL_REQUEST; 18 | uint16_t cmdcode; 19 | uint8_t ver2 = BOLT_PROTOCOL_VER2; 20 | uint32_t request_id; 21 | const uint8_t codec = BOLT_PROTOCOL_CODEC_PB; 22 | 23 | uint32_t timeout; 24 | uint16_t class_len; 25 | uint16_t header_len; 26 | uint32_t content_len; 27 | 28 | void hton() { 29 | cmdcode = htons(cmdcode); 30 | request_id = htonl(request_id); 31 | timeout = htonl(timeout); 32 | class_len = htons(class_len); 33 | header_len = htons(header_len); 34 | content_len = htonl(content_len); 35 | } 36 | }; 37 | 38 | class BoltInternalRequest { 39 | public: 40 | BoltInternalRequest( 41 | const BoltRequest& req, size_t request_id) : 42 | _request(req) { 43 | initHeader(); 44 | _header.request_id = request_id; 45 | if (req._data_type == BoltRequest::EDataType::NONE) { 46 | _header.cmdcode = BOLT_PROTOCOL_CMD_HEARTBEAT; 47 | } 48 | } 49 | 50 | ~BoltInternalRequest() {} 51 | 52 | bool serialize(IOBuffer& buffer); 53 | 54 | void initHeader() noexcept { 55 | _header.cmdcode = BOLT_PROTOCOL_CMD_REQUEST; 56 | _header.request_id = 0; 57 | _header.timeout = -1; 58 | _header.class_len = 0; 59 | _header.header_len = 0; 60 | _header.content_len = 0; 61 | } 62 | 63 | BoltRequestHeader _header; 64 | const BoltRequest& _request; 65 | }; 66 | 67 | class BoltHeartbeatBuffer { 68 | public: 69 | BoltHeartbeatBuffer() : _req(new BoltRequest) { 70 | } 71 | 72 | const std::shared_ptr& data() const { 73 | return _req; 74 | } 75 | private: 76 | std::shared_ptr _req; 77 | }; 78 | 79 | static uint32_t appendBoltHeaderKV( 80 | IOBuffer &buffer, 81 | const char *key, uint32_t key_size, 82 | const char *value, uint32_t value_size) { 83 | constexpr uint32_t KEY_VALUE_SIZE_BTYES = sizeof(uint32_t) * 2; 84 | 85 | uint32_t key_size_network = htonl(key_size); 86 | buffer.append(&key_size_network, sizeof(uint32_t));//key size 87 | buffer.append(key, key_size);//key 88 | uint32_t value_size_network = htonl(value_size); 89 | buffer.append(&value_size_network, sizeof(uint32_t));//value size 90 | buffer.append(value, value_size); 91 | 92 | return KEY_VALUE_SIZE_BTYES + key_size + value_size; 93 | } 94 | 95 | bool BoltInternalRequest::serialize(IOBuffer& buffer) { 96 | if (_header.cmdcode == BOLT_PROTOCOL_CMD_HEARTBEAT) { 97 | _header.hton(); 98 | buffer.append((void*)&_header, sizeof(_header)); 99 | return true; 100 | } 101 | 102 | IOBuffer buffer_body; 103 | 104 | //class name 105 | constexpr char class_name[] = "com.alipay.sofa.rpc.core.request.SofaRequest"; 106 | constexpr uint32_t class_name_size = sizeof(class_name) - 1; 107 | _header.class_len = class_name_size; 108 | buffer_body.append(class_name, class_name_size); 109 | 110 | //service 111 | const char service_key[] = "service"; 112 | constexpr uint32_t service_key_size = sizeof(service_key) - 1; 113 | _header.header_len += appendBoltHeaderKV( 114 | buffer_body, 115 | service_key, service_key_size, 116 | _request._service.data(), 117 | _request._service.size()); 118 | 119 | //header 120 | const char sofa_service_key[] = "sofa_head_target_service"; 121 | constexpr uint32_t sofa_service_key_size = sizeof(sofa_service_key) - 1; 122 | _header.header_len += appendBoltHeaderKV( 123 | buffer_body, sofa_service_key, sofa_service_key_size, 124 | _request._service.data(), 125 | _request._service.size()); 126 | 127 | constexpr char method_key[] = "sofa_head_method_name"; 128 | constexpr uint32_t method_key_size = sizeof(method_key) - 1; 129 | _header.header_len += appendBoltHeaderKV( 130 | buffer_body, method_key, method_key_size, 131 | _request._method.data(), 132 | _request._method.size()); 133 | 134 | constexpr char trace_id_key[] = "rpc_trace_context.sofaTraceId"; 135 | constexpr uint32_t trace_id_key_size = sizeof(trace_id_key) - 1; 136 | _header.header_len += appendBoltHeaderKV( 137 | buffer_body, trace_id_key, trace_id_key_size, 138 | _request._trace_id.data(), 139 | _request._trace_id.size()); 140 | 141 | if (_request._data_type == BoltRequest::EDataType::PROTOBUF 142 | && _request._data.proto) { 143 | //TODO compress if needed 144 | IOBufferZeroCopyOutputStream wrapper(&buffer_body); 145 | _request._data.proto->SerializeToZeroCopyStream(&wrapper); 146 | _header.content_len = buffer_body.size() 147 | - _header.class_len - _header.header_len; 148 | } else if (_request._data_type == BoltRequest::EDataType::STRING 149 | && _request._data.str) { 150 | _header.content_len = _request._data.str->length(); 151 | buffer_body.append(_request._data.str->data(), 152 | _request._data.str->length()); 153 | } else if (_request._data_type == BoltRequest::EDataType::CSTRING 154 | && _request._data.c_str) { 155 | _header.content_len = strlen(_request._data.c_str); 156 | buffer.append(_request._data.c_str); 157 | } 158 | 159 | _header.hton(); 160 | buffer.append((void*)&_header, sizeof(_header)); 161 | buffer.append(std::move(buffer_body)); 162 | 163 | return true; 164 | } 165 | 166 | bool assembleBoltRequest(const RequestBase &req, size_t request_id, IOBuffer& buffer) { 167 | const BoltRequest* breq = static_cast(&req); 168 | BoltInternalRequest inner_request(*breq, request_id); 169 | return inner_request.serialize(buffer); 170 | } 171 | 172 | EResParseResult parseBoltProtocol ( 173 | IOBuffer& buffer, size_t& data_size, size_t& request_id, void**/*unused*/) { 174 | return BoltResponse::checkHeader(buffer, data_size, request_id); 175 | } 176 | 177 | EResParseResult parseBoltResponse(ResponseBase& rsp, IOBuffer& buffer, void*/*unused*/) { 178 | return rsp.deserialize(buffer); 179 | } 180 | 181 | 182 | bool assembleHeartbeatRequest(std::shared_ptr& req, 183 | std::shared_ptr& rsp) { 184 | //only initialize once 185 | static BoltHeartbeatBuffer s_heartbeat; 186 | req = s_heartbeat.data(); 187 | rsp.reset(new BoltResponse()); 188 | return true; 189 | } 190 | bool parseHeartbeatResponse(std::shared_ptr& res) { 191 | if (res) { 192 | BoltResponse* response = static_cast(res.get()); 193 | return response->isHeartbeat() && response->status() == BoltResponse::SUCCESS; 194 | } 195 | return false; 196 | } 197 | 198 | size_t converseBoltRequest(size_t request_id) { 199 | return size_t(uint32_t(request_id)); 200 | } 201 | 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/tcp/socket_manager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/13. 3 | // 4 | 5 | #include "socket_manager.h" 6 | #include 7 | #include 8 | #include "common/utils.h" 9 | #include "session/session.h" 10 | #include "schedule/schedule.h" 11 | #include "common/log.h" 12 | 13 | namespace antflash { 14 | 15 | bool SocketManager::init() { 16 | _exit.store(false, std::memory_order_release); 17 | 18 | int reclaim_fd[2]; 19 | reclaim_fd[0] = -1; 20 | reclaim_fd[1] = -1; 21 | if (pipe(reclaim_fd) != 0) { 22 | return false; 23 | } 24 | _reclaim_fd[0] = reclaim_fd[0]; 25 | _reclaim_fd[1] = reclaim_fd[1]; 26 | 27 | auto size = Schedule::getInstance().scheduleThreadSize(); 28 | _on_reclaim.reserve(size); 29 | _reclaim_notify_flag.resize(size, false); 30 | for (size_t i = 0; i < size; ++i) { 31 | _on_reclaim.emplace_back([this, i]() { 32 | _reclaim_notify_flag[i] = true; 33 | Schedule::getInstance().removeSchedule( 34 | _reclaim_fd[1].fd(), POLLOUT, i); 35 | std::lock_guard guard(_reclaim_mtx); 36 | _reclaim_notify.notify_one(); 37 | }); 38 | } 39 | 40 | 41 | _thread.reset(new std::thread([this](){ 42 | while (!_exit.load(std::memory_order_acquire)) { 43 | watchConnections(); 44 | std::this_thread::sleep_for(std::chrono::seconds(1)); 45 | } 46 | })); 47 | 48 | return true; 49 | } 50 | 51 | void SocketManager::destroy() { 52 | _exit.store(true, std::memory_order_release); 53 | if (_thread && _thread->joinable()) { 54 | _thread->join(); 55 | _thread.reset(); 56 | } 57 | 58 | //reclaim all socket's memory 59 | for (auto& socket : _list) { 60 | //If channel still holds exclusive when socket manager destroy, 61 | //which means channel is still alive when whole process is 62 | //going to shut down, in this case, we granted that no more 63 | //socket request will be sent, and related socket will be 64 | // destroyed in channel. 65 | if (socket->_sharers.tryUpgrade()) { 66 | socket->_sharers.exclusive(); 67 | } 68 | //Just push to reclaim list 69 | _reclaim_list.push_back(socket); 70 | } 71 | _list.clear(); 72 | 73 | //As socket manager is destroyed after schedule manager, clear reclaim list directly 74 | for (auto socket : _reclaim_list) { 75 | socket->_on_read = std::function(); 76 | LOG_DEBUG("reset socket:{}", socket->fd()); 77 | } 78 | _reclaim_list.clear(); 79 | } 80 | 81 | void SocketManager::addWatch(std::shared_ptr& socket) { 82 | std::lock_guard lock_guard(_mtx); 83 | _list.push_back(socket); 84 | } 85 | 86 | void SocketManager::watchConnections() { 87 | std::vector> sockets; 88 | //1. Collect sockets to be reclaimed or to be watched 89 | { 90 | std::lock_guard lock_guard(_mtx); 91 | sockets.reserve(_list.size()); 92 | for (auto itr = _list.begin(); itr != _list.end();) { 93 | //As channel always exclusive it's socket when socket is active 94 | //If socket's exclusive status can be catch in socket manager, 95 | //it means this socket needs to be reclaimed. 96 | if ((*itr)->tryExclusive()) { 97 | LOG_INFO("socket[{}] is going to be reclaimed.", (*itr)->fd()); 98 | //Disconnect socket just remove OnRead handler, we can not reclaim this 99 | // socket directly after disconnect as schedule manager may still call 100 | // this socket's OnRead event in its loop before it receive schedule 101 | // remove message, we could only do it in next loop. 102 | (*itr)->disconnect(); 103 | _reclaim_list.emplace_back(*itr); 104 | itr = _list.erase(itr); 105 | } else { 106 | sockets.emplace_back(*itr); 107 | ++itr; 108 | } 109 | } 110 | } 111 | 112 | //2. Try to reclaim socket 113 | if (!_reclaim_list.empty()) { 114 | size_t schedule_size = Schedule::getInstance().scheduleThreadSize(); 115 | size_t cur_idx = _reclaim_counter++ % schedule_size; 116 | _reclaim_notify_flag[cur_idx] = false; 117 | 118 | Schedule::getInstance().addSchedule( 119 | _reclaim_fd[1].fd(), POLLOUT, 120 | _on_reclaim[cur_idx], cur_idx); 121 | 122 | std::unique_lock lock(_reclaim_mtx); 123 | auto status = _reclaim_notify.wait_for( 124 | lock, std::chrono::milliseconds(500), 125 | [this, cur_idx]() { 126 | return _reclaim_notify_flag[cur_idx]; 127 | }); 128 | 129 | //Receiving notify successfully from schedule must happen in next loop as 130 | //adding schedule of reclaim is after removing schedule of sockets. 131 | //And in this case, sockets can be reclaimed safety. 132 | if (status) { 133 | for (auto itr = _reclaim_list.begin(); itr != _reclaim_list.end();) { 134 | if (((*itr)->fd() % schedule_size) == cur_idx) { 135 | //release socket shared_from_this so that memory can be reclaimed 136 | (*itr)->_on_read = std::function(); 137 | LOG_DEBUG("reset socket:{}", (*itr)->fd()); 138 | itr = _reclaim_list.erase(itr); 139 | } else { 140 | ++itr; 141 | } 142 | } 143 | } 144 | } 145 | 146 | //3. send heartbeat to watch sockets 147 | for (auto& socket : sockets) { 148 | //If socket status is not active, and still in watch list, it means 149 | //this socket is not used by any other session yet, just skip it 150 | if (!socket->active()) { 151 | continue; 152 | } 153 | auto last_active_time = socket->get_last_active_time(); 154 | if (SOCKET_MAX_IDLE_US < 155 | Utils::getHighPrecisionTimeStamp() - last_active_time) { 156 | if (socket->_protocol 157 | && socket->_protocol->assemble_heartbeat_fn) { 158 | std::shared_ptr request; 159 | std::shared_ptr response; 160 | if (socket->_protocol->assemble_heartbeat_fn(request, response)) { 161 | Session ss; 162 | ss.send(*request) 163 | .to(socket) 164 | .timeout(SOCKET_TIMEOUT_MS) 165 | .receiveTo(*response) 166 | .sync(); 167 | if (ss.failed()) { 168 | socket->setStatus(RPC_STATUS_SOCKET_CONNECT_FAIL); 169 | } else { 170 | if (!socket->_protocol->parse_heartbeat_fn(response)) { 171 | socket->setStatus(RPC_STATUS_SOCKET_CONNECT_FAIL); 172 | } else { 173 | LOG_INFO("remote[{}] heartbeat success", 174 | socket->getRemote().ipToStr()); 175 | } 176 | } 177 | } 178 | } 179 | } 180 | } 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /include/common/lru_cache.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/5/21. 3 | // 4 | 5 | #ifndef RPC_COMMON_LRU_CACHE_H 6 | #define RPC_COMMON_LRU_CACHE_H 7 | 8 | #include "common/intrusive_list.h" 9 | #include "common/concurrent_hash_map.h" 10 | 11 | namespace antflash { 12 | 13 | template 14 | struct LRUNode : public Node 15 | { 16 | K key; 17 | LRUNode* hash_next; 18 | size_t used_mem; 19 | 20 | bool is_intrusive() const { 21 | return true; 22 | } 23 | 24 | template 25 | LRUNode(const K& key, size_t mem, Args&& ...args) : 26 | Node(std::forward(args)...), 27 | key(key), hash_next(nullptr), used_mem(mem) {} 28 | }; 29 | 30 | template 31 | class LRUCache : public ConcurrentHashMap> { 32 | public: 33 | using NodePtr = typename ConcurrentHashMap>::NodePtr; 34 | using LRUNodePtr = LRUNode*; 35 | 36 | LRUCache(size_t bucket_hint, size_t max_mem_bytes) : 37 | ConcurrentHashMap>( 38 | bucket_hint, 39 | [this](NodePtr pp) { 40 | LRUNodePtr p = static_cast(pp); 41 | if (p->used_mem > _mem_size) { 42 | return false; 43 | } 44 | 45 | return true; 46 | }, 47 | [this](NodePtr pp, bool ret) { 48 | if (ret) { 49 | _hit_query_times.fetch_add(1, std::memory_order_relaxed); 50 | Node* p = static_cast*>(pp); 51 | if (_list.remove(p)) { 52 | _list.push_front(p); 53 | } 54 | } 55 | }, 56 | [this](NodePtr pp, bool ret) { 57 | if (ret) { 58 | LRUNodePtr p = static_cast(pp); 59 | 60 | if (p->used_mem + _mem_size_used.load( 61 | std::memory_order_acquire) > _mem_size) { 62 | release_nodes(p->used_mem, p->key); 63 | } 64 | 65 | _mem_size_used.fetch_add(p->used_mem, std::memory_order_release); 66 | } 67 | }, 68 | [this](NodePtr new_node, NodePtr ori_node, bool ret) { 69 | LRUNodePtr cur = static_cast(new_node); 70 | if (ret) { 71 | LRUNodePtr ori = static_cast(ori_node); 72 | _mem_size_used.fetch_add( 73 | cur->used_mem - ori->used_mem, std::memory_order_release); 74 | } else { 75 | _mem_size_used.fetch_add( 76 | cur->used_mem, std::memory_order_release); 77 | } 78 | 79 | size_t mem_used = _mem_size_used.load(std::memory_order_acquire); 80 | if (mem_used > _mem_size) { 81 | release_nodes(mem_used - _mem_size, cur->key); 82 | } 83 | }, 84 | [this](NodePtr pp, bool ret) { 85 | if (ret) { 86 | LRUNodePtr p = static_cast(pp); 87 | Node* n = static_cast*>(p); 88 | if (_list.remove(n)) { 89 | delete p; 90 | } 91 | _mem_size_used.fetch_sub(p->used_mem, std::memory_order_release); 92 | } 93 | } 94 | ), 95 | _mem_size(max_mem_bytes), 96 | _mem_size_used(0), 97 | _hit_query_times(0), 98 | _total_query_times(0) { 99 | } 100 | 101 | ~LRUCache() { 102 | this->destroy(); 103 | while (true) { 104 | auto p = _list.pop_back(); 105 | if (p == nullptr) { 106 | break; 107 | } 108 | delete p; 109 | } 110 | } 111 | 112 | bool get(const K& key, V& value) { 113 | _total_query_times.fetch_add(1, std::memory_order_relaxed); 114 | return this->intrusive_get(key, value); 115 | } 116 | 117 | template 118 | bool put(const K& key, size_t mem, Args&& ...args) { 119 | LRUNodePtr p = new LRUNode(key, mem, std::forward(args)...); 120 | if (!this->intrusive_put(key, p)) { 121 | delete p; 122 | return false; 123 | } 124 | 125 | Node* pp = static_cast*>(p); 126 | _list.push_front(pp); 127 | 128 | return true; 129 | } 130 | 131 | template 132 | bool exchange(const K& key, size_t mem, Args&& ...args) { 133 | LRUNodePtr p = new LRUNode(key, mem, std::forward(args)...); 134 | LRUNodePtr ori = p; 135 | bool ret = this->intrusive_exchange(key, p); 136 | 137 | //如果交换成功则更新链表,p为交换后的旧指针 138 | if (ret) { 139 | Node* pp = static_cast*>(p); 140 | if (_list.remove(pp)) { 141 | delete p; 142 | } 143 | } 144 | 145 | Node* pp = static_cast*>(ori); 146 | _list.push_front(pp); 147 | 148 | return ret; 149 | } 150 | 151 | void erase(const K& key) { 152 | this->intrusive_erase(key); 153 | } 154 | 155 | size_t get_hit_query_times() const { 156 | return _hit_query_times.load(std::memory_order_relaxed); 157 | } 158 | 159 | size_t get_total_query_times() const { 160 | return _total_query_times.load(std::memory_order_relaxed); 161 | } 162 | 163 | size_t get_mem_size_used() const { 164 | return _mem_size_used.load(std::memory_order_relaxed); 165 | } 166 | 167 | private: 168 | void release_nodes(size_t mem, const K& locked_bucket_key) { 169 | while (mem > 0) { 170 | Node *pp = _list.pop_back(); 171 | if (pp == nullptr) { 172 | break; 173 | } 174 | LRUNodePtr p = static_cast(pp); 175 | 176 | if (this->get_bucket_idx(locked_bucket_key) 177 | == this->get_bucket_idx(p->key)) { 178 | this->erase_nolock(p->key); 179 | _mem_size_used.fetch_sub(p->used_mem, std::memory_order_release); 180 | } else { 181 | //If key to removed is not in the same bucket with locked bucket, 182 | //try erase key with lock, and then try to remove this p from list 183 | //one more time to make sure that p is succeed removing from list. 184 | this->erase(p->key); 185 | _list.remove(p); 186 | } 187 | 188 | mem -= p->used_mem; 189 | delete p; 190 | } 191 | } 192 | 193 | size_t _mem_size; 194 | std::atomic _mem_size_used; 195 | std::atomic _hit_query_times; 196 | std::atomic _total_query_times; 197 | 198 | IntrusiveList _list; 199 | }; 200 | 201 | } 202 | 203 | #endif //RPC_COMMON_LRU_CACHE_H 204 | -------------------------------------------------------------------------------- /src/protocol/bolt/bolt_response.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/11. 3 | // 4 | 5 | #include "protocol/bolt/bolt_response.h" 6 | #include 7 | #include 8 | #include "common/io_buffer.h" 9 | #include "common/log.h" 10 | 11 | namespace antflash { 12 | 13 | void BoltResponse::BoltHeader::ntoh() { 14 | cmdcode = ntohs(cmdcode); 15 | request_id = ntohl(request_id); 16 | status = ntohs(status); 17 | class_len = ntohs(class_len); 18 | header_len = ntohs(header_len); 19 | content_len = ntohl(content_len); 20 | } 21 | 22 | static std::unordered_map s_rsp_error_info = { 23 | {BoltResponse::SUCCESS, "success"}, 24 | {BoltResponse::ERROR, "error"}, 25 | {BoltResponse::SERVER_EXCEPTION, "server exception"}, 26 | {BoltResponse::UNKNOWN, "unknown"}, 27 | {BoltResponse::SERVER_THREADPOOL_BUSY, "server thread pool busy"}, 28 | {BoltResponse::ERROR_COMM, "error communication"}, 29 | {BoltResponse::NO_PROCESSOR, "no processor"}, 30 | {BoltResponse::TIMEOUT, "timeout"}, 31 | {BoltResponse::CLIENT_SEND_ERROR, "client send error"}, 32 | {BoltResponse::CODEC_EXCEPTION, "codec exception"}, 33 | {BoltResponse::CONNECTION_CLOSED, "connection closed"}, 34 | {BoltResponse::SERVER_SERIAL_EXCEPTION, "server serial exception"}, 35 | {BoltResponse::SERVER_DESERIAL_EXCEPTION, "server deserial exception"} 36 | }; 37 | 38 | const std::string& BoltResponse::msg() const { 39 | static const std::string unknown = "unknown"; 40 | auto itr = s_rsp_error_info.find(_header.status); 41 | if (itr == s_rsp_error_info.end()) { 42 | return unknown; 43 | } 44 | return itr->second; 45 | } 46 | 47 | EResParseResult BoltResponse::deserialize(IOBuffer &buffer) noexcept { 48 | auto cn = buffer.cut(&_header, sizeof(BoltHeader)); 49 | if (cn < sizeof(BoltHeader)) { 50 | LOG_ERROR("not enough data for header"); 51 | return EResParseResult::PARSE_NOT_ENOUGH_DATA; 52 | } 53 | 54 | _header.ntoh(); 55 | 56 | if (_header.proto != BOLT_PROTOCOL_TYPE) { 57 | LOG_ERROR("proto type is {}, not {}", _header.proto, BOLT_PROTOCOL_TYPE); 58 | return EResParseResult::PARSE_ERROR; 59 | } 60 | 61 | if (_header.type != BOLT_PROTOCOL_RESPONSE) { 62 | LOG_ERROR("message type is {}, not {}", _header.type, BOLT_PROTOCOL_RESPONSE); 63 | return EResParseResult::PARSE_ERROR; 64 | } 65 | 66 | if (_header.cmdcode == BOLT_PROTOCOL_CMD_HEARTBEAT) { 67 | LOG_DEBUG("heartbeat received"); 68 | return EResParseResult::PARSE_OK; 69 | } 70 | 71 | if (_header.cmdcode != BOLT_PROTOCOL_CMD_RESPONSE) { 72 | LOG_ERROR("protocol cmd {}, not {}", (int)_header.cmdcode, BOLT_PROTOCOL_CMD_RESPONSE); 73 | return EResParseResult::PARSE_ERROR; 74 | } 75 | 76 | LOG_DEBUG("receive data status: {}", (int)_header.status); 77 | if (_header.status != EBoltResponseStatus::SUCCESS) { 78 | //如果status不是SUCCESS,则header和content内容不一定能解析,直接丢弃 79 | auto data_size = _header.class_len 80 | + _header.header_len 81 | + _header.content_len; 82 | buffer.pop_front(data_size); 83 | 84 | return EResParseResult::PARSE_OK; 85 | } 86 | 87 | _class_name.resize(_header.class_len); 88 | cn = buffer.cut(&_class_name[0], _header.class_len); 89 | if (cn < _header.class_len) { 90 | LOG_ERROR("parse class name fail"); 91 | return EResParseResult::PARSE_NOT_ENOUGH_DATA; 92 | } 93 | 94 | uint16_t left_size = _header.header_len; 95 | while (left_size > 0) { 96 | std::string key_str; 97 | uint32_t header_key_size = 0; 98 | cn = buffer.cut(&header_key_size, sizeof(uint32_t)); 99 | if (cn < sizeof(uint32_t)) { 100 | LOG_ERROR("parse header key size fail"); 101 | return EResParseResult::PARSE_NOT_ENOUGH_DATA; 102 | } 103 | header_key_size = ntohl(header_key_size); 104 | left_size -= sizeof(uint32_t); 105 | 106 | if (header_key_size > 0) { 107 | key_str.resize(header_key_size); 108 | cn = buffer.cut(&key_str[0], header_key_size); 109 | if (cn < header_key_size) { 110 | LOG_ERROR("parse header key name fail"); 111 | return EResParseResult::PARSE_NOT_ENOUGH_DATA; 112 | } 113 | left_size -= header_key_size; 114 | } 115 | 116 | uint32_t header_value_size = 0; 117 | std::string value_str; 118 | cn = buffer.cut(&header_value_size, sizeof(uint32_t)); 119 | if (cn < sizeof(uint32_t)) { 120 | LOG_ERROR("parse header value size fail"); 121 | return EResParseResult::PARSE_NOT_ENOUGH_DATA; 122 | } 123 | header_value_size = ntohl(header_value_size); 124 | left_size -= sizeof(uint32_t); 125 | 126 | if (header_value_size > 0) { 127 | value_str.resize(header_value_size); 128 | cn = buffer.cut(&value_str[0], header_value_size); 129 | if (cn < header_value_size) { 130 | LOG_ERROR("parse header value name fail"); 131 | return EResParseResult::PARSE_NOT_ENOUGH_DATA; 132 | } 133 | left_size -= header_value_size; 134 | } 135 | _header_map.emplace(std::move(key_str), std::move(value_str)); 136 | } 137 | 138 | if (_header.content_len > 0) { 139 | if (_data_type == EDataType::STRING) { 140 | _data.str->resize(_header.content_len); 141 | cn = buffer.cut(&(*_data.str)[0], _header.content_len); 142 | if (cn < _header.content_len) { 143 | LOG_ERROR("parse string content fail"); 144 | return EResParseResult::PARSE_NOT_ENOUGH_DATA; 145 | } 146 | } else if (_data_type == EDataType::PROTOBUF) { 147 | IOBufferZeroCopyInputStream stream(buffer); 148 | google::protobuf::io::ZeroCopyInputStream *input = &stream; 149 | google::protobuf::io::CodedInputStream decoder(input); 150 | if (!(_data.proto->ParseFromCodedStream(&decoder) 151 | && decoder.ConsumedEntireMessage())) { 152 | LOG_ERROR("parse protobuf content fail, data info:{}", buffer.to_string()); 153 | return EResParseResult::PARSE_ERROR; 154 | } 155 | if (stream.ByteCount() != _header.content_len) { 156 | LOG_ERROR("parse protobuf content fail, size not correct"); 157 | return EResParseResult::PARSE_ERROR; 158 | } 159 | buffer.pop_front(_header.content_len); 160 | } 161 | } 162 | 163 | return EResParseResult::PARSE_OK; 164 | } 165 | 166 | EResParseResult BoltResponse::checkHeader( 167 | const IOBuffer &buffer, 168 | size_t& data_size, 169 | size_t& request_id) { 170 | constexpr size_t MAX_BOLT_BODY_SIZE = 64 * 1024 * 1024; 171 | 172 | BoltHeader header; 173 | auto cn = buffer.copy_to(&header, sizeof(BoltHeader)); 174 | header.ntoh(); 175 | 176 | if (cn < sizeof(BoltHeader)) { 177 | return EResParseResult::PARSE_NOT_ENOUGH_DATA; 178 | } 179 | 180 | request_id = header.request_id; 181 | data_size = header.class_len + header.header_len + header.content_len; 182 | if (data_size > MAX_BOLT_BODY_SIZE) { 183 | return EResParseResult::PARSE_ERROR; 184 | } 185 | data_size += sizeof(header); 186 | if (data_size > buffer.size()) { 187 | return EResParseResult::PARSE_NOT_ENOUGH_DATA; 188 | } 189 | 190 | return EResParseResult::PARSE_OK; 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /src/protocol/http/http_response.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/17. 3 | // 4 | 5 | #include "protocol/http/http_response.h" 6 | #include 7 | #include 8 | #include 9 | #include "http_parser.h" 10 | #include "common/io_buffer.h" 11 | #include "common/log.h" 12 | 13 | namespace antflash { 14 | 15 | static const std::unordered_map s_http_status = { 16 | { EHttpStatus::HTTP_STATUS_CONTINUE, "Continue" }, 17 | { EHttpStatus::HTTP_STATUS_OK, "OK" }, 18 | { EHttpStatus::HTTP_STATUS_CREATED, "Created" }, 19 | { EHttpStatus::HTTP_STATUS_ACCEPTED, "Accepted" }, 20 | { EHttpStatus::HTTP_STATUS_BAD_REQUEST, "Bad Request" }, 21 | { EHttpStatus::HTTP_STATUS_FORBIDDEN, "Forbidden" }, 22 | { EHttpStatus::HTTP_STATUS_NOT_FOUND, "Not Found" }, 23 | }; 24 | 25 | enum HttpParserStage { 26 | HTTP_ON_MESSAGE_BEGIN, 27 | HTTP_ON_URL, 28 | HTTP_ON_STATUS, 29 | HTTP_ON_HEADER_FIELD, 30 | HTTP_ON_HEADER_VALUE, 31 | HTTP_ON_HEADERS_COMPLELE, 32 | HTTP_ON_BODY, 33 | HTTP_ON_MESSAGE_COMPLELE 34 | }; 35 | 36 | struct HttpMessageInfo { 37 | HttpParserStage stage; 38 | std::string url; 39 | std::string cur_header; 40 | std::string body; 41 | std::unordered_map header; 42 | }; 43 | 44 | const http_parser_settings g_parser_settings = { 45 | HttpResponse::onMessageBegin, 46 | HttpResponse::onUrl, 47 | HttpResponse::onStatus, 48 | HttpResponse::onHeaderField, 49 | HttpResponse::onHeaderValue, 50 | HttpResponse::onHeadersComplete, 51 | HttpResponse::onBodyCb, 52 | HttpResponse::onMessageCompleteCb 53 | }; 54 | 55 | int HttpResponse::onMessageBegin(struct http_parser* parser) { 56 | LOG_DEBUG("onMessageBegin:{}", fmt::ptr(parser)); 57 | HttpMessageInfo* msg = static_cast(parser->data); 58 | msg->stage = HTTP_ON_MESSAGE_BEGIN; 59 | return 0; 60 | } 61 | 62 | int HttpResponse::onUrl( 63 | struct http_parser* parser, const char * data, size_t size) { 64 | HttpMessageInfo* msg = static_cast(parser->data); 65 | msg->stage = HTTP_ON_URL; 66 | msg->url.append(data, size); 67 | return 0; 68 | } 69 | 70 | int HttpResponse::onStatus( 71 | struct http_parser* parser, const char *, size_t) { 72 | LOG_DEBUG("onStatus:{}", fmt::ptr(parser)); 73 | HttpMessageInfo* msg = static_cast(parser->data); 74 | msg->stage = HTTP_ON_STATUS; 75 | return 0; 76 | } 77 | 78 | int HttpResponse::onHeaderField( 79 | struct http_parser* parser, const char* data, size_t size) { 80 | LOG_DEBUG("onHeaderField:{}", fmt::ptr(parser)); 81 | HttpMessageInfo* msg = static_cast(parser->data); 82 | if (msg->stage != HTTP_ON_HEADER_FIELD) { 83 | msg->stage = HTTP_ON_HEADER_FIELD; 84 | msg->cur_header.clear(); 85 | } 86 | 87 | msg->cur_header.append(data, size); 88 | return 0; 89 | } 90 | 91 | int HttpResponse::onHeaderValue( 92 | struct http_parser* parser, const char* data, size_t size) { 93 | LOG_DEBUG("onHeaderValue:{}", fmt::ptr(parser)); 94 | HttpMessageInfo* msg = static_cast(parser->data); 95 | if (msg->stage != HTTP_ON_HEADER_VALUE) { 96 | msg->stage = HTTP_ON_HEADER_VALUE; 97 | if (msg->cur_header.empty()) { 98 | LOG_ERROR("Header name is empty"); 99 | return -1; 100 | } 101 | } 102 | 103 | std::string value(data, size); 104 | auto itr = msg->header.emplace(msg->cur_header, value); 105 | if (!itr.second) { 106 | itr.first->second.append(","); 107 | itr.first->second.append(value); 108 | } 109 | 110 | return 0; 111 | } 112 | 113 | int HttpResponse::onHeadersComplete(struct http_parser* parser) { 114 | LOG_DEBUG("onHeadersComplete:{}", fmt::ptr(parser)); 115 | HttpMessageInfo* msg = static_cast(parser->data); 116 | msg->stage = HTTP_ON_HEADERS_COMPLELE; 117 | 118 | return 0; 119 | } 120 | 121 | int HttpResponse::onBodyCb( 122 | struct http_parser* parser, const char* data, size_t size) { 123 | LOG_DEBUG("onBodyCb:{}", fmt::ptr(parser)); 124 | HttpMessageInfo* msg = static_cast(parser->data); 125 | msg->stage = HTTP_ON_BODY; 126 | msg->body.append(data, size); 127 | 128 | return 0; 129 | } 130 | 131 | int HttpResponse::onMessageCompleteCb(struct http_parser* parser) { 132 | LOG_DEBUG("onMessageCompleteCb:{}", fmt::ptr(parser)); 133 | HttpMessageInfo* msg = static_cast(parser->data); 134 | msg->stage = HTTP_ON_MESSAGE_COMPLELE; 135 | 136 | return 0; 137 | } 138 | 139 | HttpResponse::~HttpResponse() { 140 | if (_parser) { 141 | HttpMessageInfo* msg = 142 | static_cast(_parser->data); 143 | delete msg; 144 | delete _parser; 145 | } 146 | } 147 | 148 | void HttpResponse::setHttpParser(void* p) { 149 | _parser = static_cast(p); 150 | } 151 | 152 | EResParseResult HttpResponse::deserialize(IOBuffer& /*unused*/) noexcept { 153 | auto ret = EResParseResult::PARSE_OK; 154 | do { 155 | if (_parser == nullptr) { 156 | ret = EResParseResult::PARSE_ERROR; 157 | break; 158 | } 159 | auto info = (HttpMessageInfo*)_parser->data; 160 | if (info == nullptr || 161 | info->stage != HTTP_ON_MESSAGE_COMPLELE) { 162 | ret = EResParseResult::PARSE_ERROR; 163 | break; 164 | } 165 | _url = std::move(info->url); 166 | _body = std::move(info->body); 167 | _header = std::move(info->header); 168 | delete info; 169 | _parser->data = nullptr; 170 | } while (0); 171 | 172 | delete _parser; 173 | _parser = nullptr; 174 | return ret; 175 | } 176 | 177 | EResParseResult HttpResponse::checkHeader( 178 | IOBuffer &buffer, 179 | size_t& data_size, 180 | size_t& request_id, 181 | void** p) { 182 | EResParseResult ret = EResParseResult::PARSE_NOT_ENOUGH_DATA; 183 | struct http_parser* parser = nullptr; 184 | HttpMessageInfo* info = nullptr; 185 | 186 | if (nullptr != p && nullptr != *p) { 187 | parser = (struct http_parser*)(*p); 188 | info = (HttpMessageInfo*)parser->data; 189 | } else { 190 | parser = new struct http_parser; 191 | http_parser_init(parser, HTTP_BOTH); 192 | info = new HttpMessageInfo; 193 | parser->data = info; 194 | } 195 | 196 | size_t nr = 0; 197 | for (size_t i = 0; i < buffer.slice_num(); ++i) { 198 | auto data = buffer.slice(i); 199 | if (data.second > 0) { 200 | nr += http_parser_execute( 201 | parser, &g_parser_settings, 202 | data.first, data.second); 203 | if (parser->http_errno != 0) { 204 | //FIXME http parse fail handle 205 | LOG_ERROR("http parse error:{}", (int)parser->http_errno); 206 | ret = EResParseResult::PARSE_ERROR; 207 | delete info; 208 | info = nullptr; 209 | delete parser; 210 | parser = nullptr; 211 | break; 212 | } 213 | if (info->stage == HTTP_ON_MESSAGE_COMPLELE) { 214 | ret = EResParseResult::PARSE_OK; 215 | break; 216 | } 217 | } 218 | } 219 | 220 | buffer.pop_front(nr); 221 | data_size = 0; 222 | 223 | if (p != nullptr) { 224 | *p = (void*)parser; 225 | } else { 226 | delete info; 227 | info = nullptr; 228 | delete parser; 229 | parser = nullptr; 230 | } 231 | 232 | return ret; 233 | } 234 | 235 | } 236 | -------------------------------------------------------------------------------- /second_party/fmt/src/posix.cc: -------------------------------------------------------------------------------- 1 | // A C++ interface to POSIX functions. 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | // Disable bogus MSVC warnings. 9 | #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) 10 | # define _CRT_SECURE_NO_WARNINGS 11 | #endif 12 | 13 | #include "fmt/posix.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #ifndef _WIN32 20 | # include 21 | #else 22 | # ifndef WIN32_LEAN_AND_MEAN 23 | # define WIN32_LEAN_AND_MEAN 24 | # endif 25 | # include 26 | # include 27 | 28 | # define O_CREAT _O_CREAT 29 | # define O_TRUNC _O_TRUNC 30 | 31 | # ifndef S_IRUSR 32 | # define S_IRUSR _S_IREAD 33 | # endif 34 | 35 | # ifndef S_IWUSR 36 | # define S_IWUSR _S_IWRITE 37 | # endif 38 | 39 | # ifdef __MINGW32__ 40 | # define _SH_DENYNO 0x40 41 | # endif 42 | 43 | #endif // _WIN32 44 | 45 | #ifdef fileno 46 | # undef fileno 47 | #endif 48 | 49 | namespace { 50 | #ifdef _WIN32 51 | // Return type of read and write functions. 52 | typedef int RWResult; 53 | 54 | // On Windows the count argument to read and write is unsigned, so convert 55 | // it from size_t preventing integer overflow. 56 | inline unsigned convert_rwcount(std::size_t count) { 57 | return count <= UINT_MAX ? static_cast(count) : UINT_MAX; 58 | } 59 | #else 60 | // Return type of read and write functions. 61 | typedef ssize_t RWResult; 62 | 63 | inline std::size_t convert_rwcount(std::size_t count) { return count; } 64 | #endif 65 | } 66 | 67 | FMT_BEGIN_NAMESPACE 68 | 69 | buffered_file::~buffered_file() FMT_NOEXCEPT { 70 | if (file_ && FMT_SYSTEM(fclose(file_)) != 0) 71 | report_system_error(errno, "cannot close file"); 72 | } 73 | 74 | buffered_file::buffered_file(cstring_view filename, cstring_view mode) { 75 | FMT_RETRY_VAL(file_, 76 | FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL); 77 | if (!file_) 78 | FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); 79 | } 80 | 81 | void buffered_file::close() { 82 | if (!file_) 83 | return; 84 | int result = FMT_SYSTEM(fclose(file_)); 85 | file_ = FMT_NULL; 86 | if (result != 0) 87 | FMT_THROW(system_error(errno, "cannot close file")); 88 | } 89 | 90 | // A macro used to prevent expansion of fileno on broken versions of MinGW. 91 | #define FMT_ARGS 92 | 93 | int buffered_file::fileno() const { 94 | int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); 95 | if (fd == -1) 96 | FMT_THROW(system_error(errno, "cannot get file descriptor")); 97 | return fd; 98 | } 99 | 100 | file::file(cstring_view path, int oflag) { 101 | int mode = S_IRUSR | S_IWUSR; 102 | #if defined(_WIN32) && !defined(__MINGW32__) 103 | fd_ = -1; 104 | FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 105 | #else 106 | FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); 107 | #endif 108 | if (fd_ == -1) 109 | FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); 110 | } 111 | 112 | file::~file() FMT_NOEXCEPT { 113 | // Don't retry close in case of EINTR! 114 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 115 | if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 116 | report_system_error(errno, "cannot close file"); 117 | } 118 | 119 | void file::close() { 120 | if (fd_ == -1) 121 | return; 122 | // Don't retry close in case of EINTR! 123 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 124 | int result = FMT_POSIX_CALL(close(fd_)); 125 | fd_ = -1; 126 | if (result != 0) 127 | FMT_THROW(system_error(errno, "cannot close file")); 128 | } 129 | 130 | long long file::size() const { 131 | #ifdef _WIN32 132 | // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT 133 | // is less than 0x0500 as is the case with some default MinGW builds. 134 | // Both functions support large file sizes. 135 | DWORD size_upper = 0; 136 | HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); 137 | DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); 138 | if (size_lower == INVALID_FILE_SIZE) { 139 | DWORD error = GetLastError(); 140 | if (error != NO_ERROR) 141 | FMT_THROW(windows_error(GetLastError(), "cannot get file size")); 142 | } 143 | unsigned long long long_size = size_upper; 144 | return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; 145 | #else 146 | typedef struct stat Stat; 147 | Stat file_stat = Stat(); 148 | if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) 149 | FMT_THROW(system_error(errno, "cannot get file attributes")); 150 | static_assert(sizeof(long long) >= sizeof(file_stat.st_size), 151 | "return type of file::size is not large enough"); 152 | return file_stat.st_size; 153 | #endif 154 | } 155 | 156 | std::size_t file::read(void *buffer, std::size_t count) { 157 | RWResult result = 0; 158 | FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); 159 | if (result < 0) 160 | FMT_THROW(system_error(errno, "cannot read from file")); 161 | return internal::to_unsigned(result); 162 | } 163 | 164 | std::size_t file::write(const void *buffer, std::size_t count) { 165 | RWResult result = 0; 166 | FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); 167 | if (result < 0) 168 | FMT_THROW(system_error(errno, "cannot write to file")); 169 | return internal::to_unsigned(result); 170 | } 171 | 172 | file file::dup(int fd) { 173 | // Don't retry as dup doesn't return EINTR. 174 | // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html 175 | int new_fd = FMT_POSIX_CALL(dup(fd)); 176 | if (new_fd == -1) 177 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); 178 | return file(new_fd); 179 | } 180 | 181 | void file::dup2(int fd) { 182 | int result = 0; 183 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 184 | if (result == -1) { 185 | FMT_THROW(system_error(errno, 186 | "cannot duplicate file descriptor {} to {}", fd_, fd)); 187 | } 188 | } 189 | 190 | void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT { 191 | int result = 0; 192 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 193 | if (result == -1) 194 | ec = error_code(errno); 195 | } 196 | 197 | void file::pipe(file &read_end, file &write_end) { 198 | // Close the descriptors first to make sure that assignments don't throw 199 | // and there are no leaks. 200 | read_end.close(); 201 | write_end.close(); 202 | int fds[2] = {}; 203 | #ifdef _WIN32 204 | // Make the default pipe capacity same as on Linux 2.6.11+. 205 | enum { DEFAULT_CAPACITY = 65536 }; 206 | int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); 207 | #else 208 | // Don't retry as the pipe function doesn't return EINTR. 209 | // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html 210 | int result = FMT_POSIX_CALL(pipe(fds)); 211 | #endif 212 | if (result != 0) 213 | FMT_THROW(system_error(errno, "cannot create pipe")); 214 | // The following assignments don't throw because read_fd and write_fd 215 | // are closed. 216 | read_end = file(fds[0]); 217 | write_end = file(fds[1]); 218 | } 219 | 220 | buffered_file file::fdopen(const char *mode) { 221 | // Don't retry as fdopen doesn't return EINTR. 222 | FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); 223 | if (!f) 224 | FMT_THROW(system_error(errno, 225 | "cannot associate stream with file descriptor")); 226 | buffered_file bf(f); 227 | fd_ = -1; 228 | return bf; 229 | } 230 | 231 | long getpagesize() { 232 | #ifdef _WIN32 233 | SYSTEM_INFO si; 234 | GetSystemInfo(&si); 235 | return si.dwPageSize; 236 | #else 237 | long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); 238 | if (size < 0) 239 | FMT_THROW(system_error(errno, "cannot get memory page size")); 240 | return size; 241 | #endif 242 | } 243 | FMT_END_NAMESPACE 244 | 245 | -------------------------------------------------------------------------------- /include/common/utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/2. 3 | // 4 | 5 | #ifndef RPC_COMMON_UTILS_H 6 | #define RPC_COMMON_UTILS_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace antflash { 16 | 17 | class Utils { 18 | public: 19 | class Timer { 20 | public: 21 | Timer() : _begin(std::chrono::steady_clock::now()) { 22 | } 23 | 24 | ~Timer() {} 25 | 26 | void reset() { 27 | _begin = std::chrono::steady_clock::now(); 28 | } 29 | 30 | size_t elapsed() { 31 | return std::chrono::duration_cast( 32 | std::chrono::steady_clock::now() - _begin).count(); 33 | } 34 | 35 | size_t elapsedMicro() { 36 | return std::chrono::duration_cast( 37 | std::chrono::steady_clock::now() - _begin).count(); 38 | } 39 | 40 | size_t elapsedSecond() { 41 | return std::chrono::duration_cast( 42 | std::chrono::steady_clock::now() - _begin).count(); 43 | } 44 | 45 | private: 46 | std::chrono::steady_clock::time_point _begin; 47 | }; 48 | 49 | static size_t getTimeStamp() { 50 | using namespace std::chrono; 51 | return duration_cast( 52 | system_clock::now().time_since_epoch()).count(); 53 | } 54 | 55 | static size_t getHighPrecisionTimeStamp() { 56 | using namespace std::chrono; 57 | return duration_cast( 58 | system_clock::now().time_since_epoch()).count(); 59 | } 60 | 61 | static void leftTrim(char *str) { 62 | char* p = str; 63 | if (nullptr != p) { 64 | while (*p && std::isspace(*p)) { 65 | ++p; 66 | } 67 | if (p == str) { 68 | return; 69 | } 70 | size_t len = strlen(p); 71 | std::memmove(str, p, len); 72 | str[len] = '\0'; 73 | } 74 | } 75 | 76 | static void rightTrim(char *str) { 77 | char *p = str; 78 | if (nullptr != p) { 79 | if ('\0' == *p) { 80 | return; 81 | } 82 | 83 | while (*(p + 1)) { 84 | ++p; 85 | } 86 | 87 | while (p != str && std::isspace(*p)) { 88 | *(p--) = '\0'; 89 | } 90 | } 91 | } 92 | 93 | static void trim(char *str) { 94 | leftTrim(str); 95 | rightTrim(str); 96 | } 97 | 98 | static void trim(std::experimental::string_view& view) { 99 | //Left trim views 100 | size_t length = view.length(); 101 | const char* data = view.data(); 102 | while (std::isspace(*data)) { 103 | ++data; 104 | --length; 105 | } 106 | //Right trim 107 | const char* p = data + length; 108 | while (p != data && std::isspace(*--p)) { 109 | --length; 110 | } 111 | 112 | view = std::experimental::string_view(data, length); 113 | } 114 | 115 | static std::vector stringSplit( 116 | const std::string& src, char delimiter) { 117 | std::vector vec; 118 | std::string::size_type pos1, pos2; 119 | pos2 = src.find(delimiter); 120 | pos1 = 0; 121 | while (std::string::npos != pos2) { 122 | vec.emplace_back(src.substr(pos1, pos2 - pos1)); 123 | 124 | pos1 = pos2 + 1; 125 | pos2 = src.find(delimiter, pos1); 126 | } 127 | if(pos1 != src.length()) { 128 | vec.emplace_back(src.substr(pos1)); 129 | } 130 | 131 | return vec; 132 | } 133 | 134 | static std::vector stringSplit( 135 | const std::string& src, const std::string& delimiter) { 136 | std::vector vec; 137 | std::string::size_type pos1, pos2; 138 | pos2 = src.find(delimiter); 139 | pos1 = 0; 140 | while (std::string::npos != pos2) { 141 | vec.emplace_back(src.substr(pos1, pos2 - pos1)); 142 | 143 | pos1 = pos2 + delimiter.length(); 144 | pos2 = src.find(delimiter, pos1); 145 | } 146 | if(pos1 != src.length()) { 147 | vec.emplace_back(src.substr(pos1)); 148 | } 149 | 150 | return vec; 151 | } 152 | 153 | static std::vector 154 | stringSlice(const std::string& src, char delimiter) { 155 | std::vector vec; 156 | std::string::size_type pos1, pos2; 157 | pos2 = src.find(delimiter); 158 | pos1 = 0; 159 | while (std::string::npos != pos2) { 160 | vec.emplace_back(&src[pos1], pos2 - pos1); 161 | 162 | pos1 = pos2 + 1; 163 | pos2 = src.find(delimiter, pos1); 164 | } 165 | if(pos1 != src.length()) { 166 | vec.emplace_back(&src[pos1], src.length() - pos1); 167 | } 168 | 169 | return vec; 170 | } 171 | 172 | static std::vector 173 | stringSlice(const std::string& src, const std::string& delimiter) { 174 | std::vector vec; 175 | std::string::size_type pos1, pos2; 176 | pos2 = src.find(delimiter); 177 | pos1 = 0; 178 | while (std::string::npos != pos2) { 179 | vec.emplace_back(&src[pos1], pos2 - pos1); 180 | 181 | pos1 = pos2 + delimiter.length(); 182 | pos2 = src.find(delimiter, pos1); 183 | } 184 | if(pos1 != src.length()) { 185 | vec.emplace_back(&src[pos1], src.length() - pos1); 186 | } 187 | 188 | return vec; 189 | } 190 | 191 | static size_t splitNum(const std::string& src, char delimiter) { 192 | size_t num = 0; 193 | std::string::size_type pos1, pos2; 194 | pos2 = src.find(delimiter); 195 | pos1 = 0; 196 | while (std::string::npos != pos2) { 197 | ++num; 198 | pos1 = pos2 + 1; 199 | pos2 = src.find(delimiter, pos1); 200 | } 201 | if(pos1 != src.length()) { 202 | ++num; 203 | } 204 | 205 | return num; 206 | } 207 | 208 | static uint32_t MurmurHash2(const void* key, int len) { 209 | return MurmurHash2(key, len, 97); 210 | } 211 | 212 | static uint32_t MurmurHash2(const void* key, int len, uint32_t seed) { 213 | // 'm' and 'r' are mixing constants generated offline. 214 | // They're not really 'magic', they just happen to work well. 215 | 216 | const uint32_t m = 0x5bd1e995; 217 | const int r = 24; 218 | 219 | // Initialize the hash to a 'random' value 220 | uint32_t h = seed ^ len; 221 | 222 | // Mix 4 bytes at a time into the hash 223 | const unsigned char * data = (const unsigned char *)key; 224 | while(len >= 4) { 225 | uint32_t k = *(uint32_t *)data; 226 | 227 | k *= m; 228 | k ^= k >> r; 229 | k *= m; 230 | 231 | h *= m; 232 | h ^= k; 233 | 234 | data += 4; 235 | len -= 4; 236 | } 237 | 238 | // Handle the last few bytes of the input array 239 | const int8_t *idata = (const int8_t *)data; 240 | switch(len) { 241 | case 3: h ^= idata[2] << 16; 242 | case 2: h ^= idata[1] << 8; 243 | case 1: h ^= idata[0]; 244 | h *= m; 245 | }; 246 | 247 | // Do a few final mixes of the hash to ensure the last few 248 | // bytes are well-incorporated. 249 | h ^= h >> 13; 250 | h *= m; 251 | h ^= h >> 15; 252 | 253 | return h; 254 | } 255 | }; 256 | 257 | } 258 | 259 | #endif //RPC_COMMON_UTILS_H 260 | -------------------------------------------------------------------------------- /test/unit_test/thread_pool_unittest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Ant Financial, Inc. All Rights Reserved 2 | // Created by zhenggu.xwt on 18/4/25. 3 | // 4 | 5 | #include "common/thread_pool.h" 6 | #include 7 | #include 8 | #include 9 | #include "common/utils.h" 10 | 11 | using namespace antflash; 12 | 13 | TEST(ThreadPoolTest, init) { 14 | using Task = std::function; 15 | ThreadPool thread_pool(2, 128); 16 | ASSERT_TRUE(thread_pool.init()); 17 | } 18 | 19 | TEST(ThreadPoolTest, singleTask) { 20 | using Task = std::function; 21 | ThreadPool thread_pool(2, 8); 22 | ASSERT_TRUE(thread_pool.init()); 23 | auto call_td_id = std::this_thread::get_id(); 24 | auto ret = thread_pool.submit([&call_td_id]() -> int { 25 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 26 | auto run_td_id = std::this_thread::get_id(); 27 | if (run_td_id == call_td_id) { 28 | return 0; 29 | } 30 | return 1; 31 | }); 32 | 33 | ASSERT_EQ(ret.get(), 1); 34 | } 35 | 36 | TEST(ThreadPoolTest, singleVoidTask) { 37 | using Task = std::function; 38 | ThreadPool thread_pool(2, 8); 39 | ASSERT_TRUE(thread_pool.init()); 40 | auto run_td_id = std::this_thread::get_id(); 41 | auto ret = thread_pool.submit([&run_td_id]() { 42 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 43 | run_td_id = std::this_thread::get_id(); 44 | }); 45 | ASSERT_TRUE(ret); 46 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 47 | ASSERT_NE(run_td_id, std::this_thread::get_id()); 48 | } 49 | 50 | TEST(ThreadPoolTest, singleTaskNoPool) { 51 | using Task = std::function; 52 | ThreadPool thread_pool(0, 0); 53 | ASSERT_TRUE(thread_pool.init()); 54 | auto call_td_id = std::this_thread::get_id(); 55 | auto ret = thread_pool.submit([&call_td_id]() -> int { 56 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 57 | auto run_td_id = std::this_thread::get_id(); 58 | if (run_td_id == call_td_id) { 59 | return 0; 60 | } 61 | return 1; 62 | }); 63 | 64 | ASSERT_EQ(ret.get(), 0); 65 | } 66 | 67 | TEST(ThreadPoolTest, twoTaskParallel) { 68 | using Task = std::function; 69 | ThreadPool thread_pool(2, 4); 70 | ASSERT_TRUE(thread_pool.init()); 71 | 72 | auto dest_time = std::chrono::system_clock::now() 73 | + std::chrono::milliseconds(50); 74 | 75 | auto td1_pool_id = std::this_thread::get_id(); 76 | std::thread td1([&dest_time, &thread_pool, &td1_pool_id]() { 77 | std::this_thread::sleep_until(dest_time); 78 | auto call_td_id = std::this_thread::get_id(); 79 | auto ret = thread_pool.submit([&call_td_id, &td1_pool_id]() -> int { 80 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 81 | td1_pool_id = std::this_thread::get_id(); 82 | if (td1_pool_id == call_td_id) { 83 | return 0; 84 | } 85 | return 1; 86 | }); 87 | 88 | ASSERT_EQ(ret.get(), 1); 89 | }); 90 | auto td2_pool_id = std::this_thread::get_id(); 91 | std::thread td2([&dest_time, &thread_pool, &td2_pool_id]() { 92 | std::this_thread::sleep_until(dest_time); 93 | auto call_td_id = std::this_thread::get_id(); 94 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 95 | auto ret = thread_pool.submit([&call_td_id, &td2_pool_id]() -> int { 96 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 97 | td2_pool_id = std::this_thread::get_id(); 98 | if (td2_pool_id == call_td_id) { 99 | return 0; 100 | } 101 | return 1; 102 | }); 103 | 104 | ASSERT_EQ(ret.get(), 1); 105 | }); 106 | td1.join(); 107 | td2.join(); 108 | ASSERT_NE(td1_pool_id, td2_pool_id); 109 | } 110 | 111 | TEST(ThreadPoolTest, taskSteal) { 112 | using Task = std::function; 113 | ThreadPool thread_pool(2, 4); 114 | ASSERT_TRUE(thread_pool.init()); 115 | 116 | auto td1_pool_id = std::this_thread::get_id(); 117 | auto td2_pool_id = std::this_thread::get_id(); 118 | std::thread td1([&thread_pool, &td1_pool_id, &td2_pool_id]() { 119 | auto call_td_id = std::this_thread::get_id(); 120 | auto ret1 = thread_pool.submit([&call_td_id, &td1_pool_id]() -> int { 121 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 122 | td1_pool_id = std::this_thread::get_id(); 123 | if (td1_pool_id == call_td_id) { 124 | return 0; 125 | } 126 | return 1; 127 | }); 128 | 129 | auto ret2 = thread_pool.submit([&call_td_id, &td2_pool_id]() -> int { 130 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 131 | td2_pool_id = std::this_thread::get_id(); 132 | if (td2_pool_id == call_td_id) { 133 | return 0; 134 | } 135 | return 1; 136 | }); 137 | 138 | ASSERT_EQ(ret1.get(), 1); 139 | ASSERT_EQ(ret2.get(), 1); 140 | }); 141 | td1.join(); 142 | ASSERT_NE(td1_pool_id, td2_pool_id); 143 | } 144 | 145 | TEST(ThreadPoolTest, taskPerformance) { 146 | using Task = std::function; 147 | constexpr size_t task_num = 100000; 148 | { 149 | ThreadPool thread_pool(2, 1024); 150 | ASSERT_TRUE(thread_pool.init()); 151 | std::vector> rets; 152 | rets.reserve(task_num); 153 | bool* results = new bool[task_num]; 154 | Utils::Timer cost; 155 | for (size_t i = 0; i < task_num; ++i) { 156 | results[i] = false; 157 | rets.emplace_back(thread_pool.submit([&results, i]()-> size_t { 158 | results[i] = true; 159 | return i;})); 160 | } 161 | for (auto& ret : rets) { 162 | ASSERT_GE(ret.get(), 0UL); 163 | } 164 | std::cout << "time cost:" << cost.elapsed() << "ms." << std::endl; 165 | for (size_t i = 0; i < task_num; ++i) { 166 | ASSERT_TRUE(results[i]); 167 | } 168 | delete [] results; 169 | } 170 | { 171 | ThreadPool thread_pool(4, 1024); 172 | ASSERT_TRUE(thread_pool.init()); 173 | std::vector> rets; 174 | rets.reserve(task_num); 175 | bool* results = new bool[task_num]; 176 | Utils::Timer cost; 177 | for (size_t i = 0; i < task_num; ++i) { 178 | results[i] = false; 179 | rets.emplace_back(thread_pool.submit([&results, i]()-> size_t { 180 | results[i] = true; 181 | return i;})); 182 | } 183 | for (auto& ret : rets) { 184 | ASSERT_GE(ret.get(), 0UL); 185 | } 186 | std::cout << "time cost:" << cost.elapsed() << "ms." << std::endl; 187 | for (size_t i = 0; i < task_num; ++i) { 188 | ASSERT_TRUE(results[i]); 189 | } 190 | delete [] results; 191 | } 192 | { 193 | using VoidTask = std::function; 194 | ThreadPool thread_pool(2, 1024); 195 | ASSERT_TRUE(thread_pool.init()); 196 | bool* results = new bool[task_num]; 197 | std::atomic counter(0); 198 | Utils::Timer cost; 199 | for (size_t i = 0; i < task_num; ++i) { 200 | results[i] = false; 201 | thread_pool.submit([&counter, &results, i]()-> void { 202 | results[i] = true; 203 | counter.fetch_add(1); 204 | }); 205 | } 206 | while (counter.load() < task_num) { 207 | ; 208 | } 209 | std::cout << "time cost:" << cost.elapsed() << "ms." << std::endl; 210 | for (size_t i = 0; i < task_num; ++i) { 211 | ASSERT_TRUE(results[i]); 212 | } 213 | 214 | delete [] results; 215 | } 216 | } 217 | --------------------------------------------------------------------------------