├── .clang-format ├── CMakeLists.txt ├── Config.lua ├── LICENSE ├── README.md ├── src ├── Backend.cpp ├── Backend.h ├── BaseSession.cpp ├── BaseSession.h ├── BaseWaitReply.cpp ├── BaseWaitReply.h ├── Client.cpp ├── Client.h ├── DBProxyServer.cpp ├── RedisWaitReply.cpp ├── RedisWaitReply.h ├── SSDBWaitReply.cpp ├── SSDBWaitReply.h ├── defer.h └── protocol │ ├── RedisParse.h │ ├── RedisRequest.h │ ├── SSDBProtocol.cpp │ └── SSDBProtocol.h └── vcpkg.json /.clang-format: -------------------------------------------------------------------------------- 1 | # Generated from CLion C/C++ Code Style settings 2 | BasedOnStyle: Google 3 | Language: Cpp 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignOperands: true 8 | AllowAllArgumentsOnNextLine: false 9 | AllowAllConstructorInitializersOnNextLine: false 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: false 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLambdasOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakTemplateDeclarations: Yes 19 | BreakBeforeBraces: Custom 20 | BraceWrapping: 21 | AfterCaseLabel: true 22 | AfterClass: true 23 | AfterControlStatement: Always 24 | AfterEnum: true 25 | AfterFunction: true 26 | AfterNamespace: false 27 | AfterUnion: false 28 | BeforeCatch: true 29 | BeforeElse: true 30 | BeforeLambdaBody: false 31 | IndentBraces: false 32 | SplitEmptyFunction: false 33 | SplitEmptyRecord: true 34 | BreakBeforeBinaryOperators: None 35 | BreakBeforeTernaryOperators: true 36 | BreakConstructorInitializers: BeforeColon 37 | BreakInheritanceList: BeforeColon 38 | ColumnLimit: 0 39 | CompactNamespaces: true 40 | ContinuationIndentWidth: 8 41 | IndentCaseLabels: true 42 | IndentPPDirectives: None 43 | IndentWidth: 4 44 | KeepEmptyLinesAtTheStartOfBlocks: true 45 | MaxEmptyLinesToKeep: 2 46 | NamespaceIndentation: None 47 | ObjCSpaceAfterProperty: false 48 | ObjCSpaceBeforeProtocolList: true 49 | PointerAlignment: Right 50 | ReflowComments: false 51 | SpaceAfterCStyleCast: true 52 | SpaceAfterLogicalNot: false 53 | SpaceAfterTemplateKeyword: false 54 | SpaceBeforeAssignmentOperators: true 55 | SpaceBeforeCpp11BracedList: false 56 | SpaceBeforeCtorInitializerColon: true 57 | SpaceBeforeInheritanceColon: true 58 | SpaceBeforeParens: ControlStatements 59 | SpaceBeforeRangeBasedForLoopColon: true 60 | SpaceInEmptyParentheses: false 61 | SpacesBeforeTrailingComments: 0 62 | SpacesInAngles: false 63 | SpacesInCStyleCastParentheses: false 64 | SpacesInContainerLiterals: false 65 | SpacesInParentheses: false 66 | SpacesInSquareBrackets: false 67 | TabWidth: 4 68 | UseTab: Never 69 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project(dbproxy) 3 | 4 | if(WIN32) 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest") 6 | elseif(UNIX) 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") 8 | endif() 9 | 10 | find_package(Lua REQUIRED) 11 | find_path(BRYNET_INCLUDE_DIRS "brynet/Version.hpp") 12 | find_package(sol2 CONFIG REQUIRED) 13 | 14 | set(proxy_src 15 | ${PROJECT_SOURCE_DIR}/src/DBProxyServer.cpp 16 | ${PROJECT_SOURCE_DIR}/src/BaseSession.cpp 17 | ${PROJECT_SOURCE_DIR}/src/Backend.cpp 18 | ${PROJECT_SOURCE_DIR}/src/BaseWaitReply.cpp 19 | ${PROJECT_SOURCE_DIR}/src/Client.cpp 20 | ${PROJECT_SOURCE_DIR}/src/RedisWaitReply.cpp 21 | ${PROJECT_SOURCE_DIR}/src/SSDBWaitReply.cpp 22 | ${PROJECT_SOURCE_DIR}/src/protocol/SSDBProtocol.cpp 23 | ) 24 | 25 | add_executable(dbproxy ${proxy_src}) 26 | 27 | target_include_directories(dbproxy PRIVATE ${LUA_INCLUDE_DIR}) 28 | target_link_libraries(dbproxy PRIVATE sol2::sol2) 29 | target_include_directories(dbproxy PRIVATE ${BRYNET_INCLUDE_DIRS}) 30 | if(WIN32) 31 | target_link_libraries(dbproxy ws2_32 ${LUA_LIBRARIES} ${BRYNET_LIBRARY}) 32 | elseif(UNIX) 33 | find_package(Threads REQUIRED) 34 | target_link_libraries(dbproxy Threads::Threads ${LUA_LIBRARIES} ${BRYNET_LIBRARY}) 35 | endif() 36 | -------------------------------------------------------------------------------- /Config.lua: -------------------------------------------------------------------------------- 1 | ProxyConfig = { 2 | backends = { 3 | {id = 0, ip = "127.0.0.1", port = 6379} 4 | }, 5 | 6 | listenPort = 9999, --对外(提供代理功能)的监听端口 7 | sharding_function = "test_sharding" -- 指定sharding函数,其根据key返回对应的backend id 8 | } 9 | 10 | function test_sharding(key) 11 | return 0 12 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 IronsDu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DBProxy 2 | ## 介绍 3 | dbproxy是一个采用C++11编写的代理服务器(不过现在已经使用了一些C++17特性),支持[redis](https://github.com/antirez/redis)和 [ssdb](https://github.com/ideawu/ssdb)数据库。 4 | 其主要用于扩容和提高系统负载。使用lua控制sharding,把不同的key-value映射到不同的后端redis或ssdb服务器。 5 | 6 | ## 构建 7 | dbproxy支持windwos和linux,需要支持 C++ 17的编译器,且需要使用`vcpkg`安装`brynet`、`lua`、`sol`. 8 | 如果使用vcpkg,则可以直接打开文件夹工程即可构建! 9 | 10 | ## 配置文件 11 | dbproxy的配置文件是[Config.lua](https://github.com/IronsDu/DBProxy/blob/master/Config.lua) 12 | 其`ProxyConfig`的`backends`key配置后端服务器列表,其中的`sharding_function`指示sharding函数。 13 | 作为示例,`test_sharding`就是被指定的sharding函数,其根据key参数,返回对应的服务器号,这里返回0,意思是将key映射到`127.0.0.1` : `6379`这个服务器。 14 | 15 | ## 补充 16 | 目前dbproxy只作为代理映射,不包含读写分离以及额外缓存,也不解决分布式等问题。 17 | 当然其服务器C++代码主体并不涉及任何sharding方案,必须由用户自己在Config.lua里自己实现sharding函数 (当然,也可以从网上找现成的,譬如lua版的一致性hash [lua-consistent-hash](https://github.com/jaderhs/lua-consistent-hash)) 18 | 19 | ## 感谢 20 | 一定程度上借鉴了[redis-shatter](https://github.com/fuzziqersoftware/redis-shatter)和[codis](https://github.com/wandoulabs/codis)。 21 | -------------------------------------------------------------------------------- /src/Backend.cpp: -------------------------------------------------------------------------------- 1 | #include "Backend.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Client.h" 9 | #include "SSDBWaitReply.h" 10 | #include "protocol/RedisParse.h" 11 | #include "protocol/SSDBProtocol.h" 12 | 13 | using namespace std; 14 | 15 | class ClientSession; 16 | thread_local std::vector> gBackendClients; 17 | 18 | BackendSession::BackendSession(brynet::net::TcpConnection::Ptr session, int id) 19 | : BaseSession(session), 20 | mID(id) 21 | { 22 | mCache = nullptr; 23 | } 24 | 25 | void BackendSession::onEnter() 26 | { 27 | gBackendClients.push_back(shared_from_this()); 28 | } 29 | 30 | void BackendSession::onClose() 31 | { 32 | { 33 | const auto oldEnd = gBackendClients.end(); 34 | const auto newEnd = std::remove_if(gBackendClients.begin(), 35 | gBackendClients.end(), 36 | [=](const shared_ptr& v) { 37 | return v.get() == this; 38 | }); 39 | if (newEnd != oldEnd) 40 | { 41 | gBackendClients.erase(newEnd, oldEnd); 42 | } 43 | } 44 | 45 | while (!mPendingWaitReply.empty()) 46 | { 47 | std::shared_ptr client = nullptr; 48 | auto wp = mPendingWaitReply.front().lock(); 49 | if (wp != nullptr) 50 | { 51 | client = wp->getClient(); 52 | } 53 | mPendingWaitReply.pop(); 54 | 55 | if (client == nullptr) 56 | { 57 | continue; 58 | } 59 | 60 | auto eventLoop = client->getEventLoop(); 61 | if (eventLoop == nullptr) 62 | { 63 | continue; 64 | } 65 | if (eventLoop->isInLoopThread()) 66 | { 67 | wp->setError("backend error"); 68 | client->processCompletedReply(); 69 | } 70 | else 71 | { 72 | eventLoop->runAsyncFunctor([clientCapture = std::move(client), wpCapture = std::move(wp)]() { 73 | wpCapture->setError("backend error"); 74 | clientCapture->processCompletedReply(); 75 | }); 76 | } 77 | } 78 | } 79 | 80 | size_t BackendSession::onMsg(const char* buffer, size_t len) 81 | { 82 | size_t totalLen = 0; 83 | 84 | const char c = buffer[0]; 85 | if (mRedisParse != nullptr || 86 | !IS_NUM(c)) 87 | { 88 | /* redis reply */ 89 | char* parseEndPos = (char*) buffer; 90 | char* parseStartPos = parseEndPos; 91 | 92 | while (totalLen < len) 93 | { 94 | if (mRedisParse == nullptr) 95 | { 96 | mRedisParse = std::shared_ptr(parse_tree_new(), [](parse_tree* parse) { 97 | parse_tree_del(parse); 98 | }); 99 | } 100 | 101 | int parseRet = parse(mRedisParse.get(), &parseEndPos, (char*) buffer + len); 102 | totalLen += (parseEndPos - parseStartPos); 103 | 104 | if (parseRet == REDIS_OK) 105 | { 106 | if (mCache == nullptr) 107 | { 108 | processReply(mRedisParse, mCache, parseStartPos, parseEndPos - parseStartPos); 109 | } 110 | else 111 | { 112 | mCache->append(parseStartPos, parseEndPos - parseStartPos); 113 | processReply(mRedisParse, mCache, mCache->c_str(), mCache->size()); 114 | mCache = nullptr; 115 | } 116 | 117 | parseStartPos = parseEndPos; 118 | mRedisParse = nullptr; 119 | } 120 | else if (parseRet == REDIS_RETRY) 121 | { 122 | if (mCache == nullptr) 123 | { 124 | mCache.reset(new std::string); 125 | } 126 | mCache->append(parseStartPos, parseEndPos - parseStartPos); 127 | break; 128 | } 129 | else 130 | { 131 | break; 132 | } 133 | } 134 | } 135 | else 136 | { 137 | /* ssdb reply */ 138 | char* parseStartPos = (char*) buffer; 139 | int leftLen = len; 140 | int packetLen = 0; 141 | while ((packetLen = SSDBProtocolResponse::check_ssdb_packet(parseStartPos, leftLen)) > 0) 142 | { 143 | processReply(nullptr, mCache, parseStartPos, packetLen); 144 | 145 | totalLen += packetLen; 146 | leftLen -= packetLen; 147 | parseStartPos += packetLen; 148 | } 149 | } 150 | 151 | return totalLen; 152 | } 153 | 154 | void BackendSession::processReply(const std::shared_ptr& redisReply, 155 | std::shared_ptr& responseBinary, 156 | const char* replyBuffer, 157 | size_t replyLen) 158 | { 159 | assert(!mPendingWaitReply.empty()); 160 | if (mPendingWaitReply.empty()) 161 | { 162 | return; 163 | } 164 | 165 | auto reply = mPendingWaitReply.front().lock(); 166 | mPendingWaitReply.pop(); 167 | if (reply == nullptr) 168 | { 169 | return; 170 | } 171 | 172 | auto client = reply->getClient(); 173 | if (client == nullptr) 174 | { 175 | return; 176 | } 177 | 178 | auto netParseMsg = std::make_shared(); 179 | netParseMsg->redisReply = redisReply; 180 | if (responseBinary != nullptr) 181 | { 182 | netParseMsg->responseMemory = responseBinary; 183 | } 184 | else 185 | { 186 | netParseMsg->responseMemory = std::make_shared(replyBuffer, replyLen); 187 | } 188 | 189 | auto eventLoop = client->getEventLoop(); 190 | if (eventLoop == nullptr) 191 | { 192 | return; 193 | } 194 | 195 | if (eventLoop->isInLoopThread()) 196 | { 197 | if (netParseMsg->redisReply != nullptr && netParseMsg->redisReply->type == REDIS_REPLY_ERROR) 198 | { 199 | reply->setError(netParseMsg->redisReply->reply->str); 200 | } 201 | reply->onBackendReply(getSession(), netParseMsg); 202 | client->processCompletedReply(); 203 | } 204 | else 205 | { 206 | eventLoop->runAsyncFunctor([clientCapture = std::move(client), 207 | netParseMsgCapture = std::move(netParseMsg), 208 | replyCapture = std::move(reply), 209 | session = getSession()]() { 210 | if (netParseMsgCapture->redisReply != nullptr && netParseMsgCapture->redisReply->type == REDIS_REPLY_ERROR) 211 | { 212 | replyCapture->setError(netParseMsgCapture->redisReply->reply->str); 213 | } 214 | replyCapture->onBackendReply(session, netParseMsgCapture); 215 | 216 | clientCapture->processCompletedReply(); 217 | }); 218 | } 219 | } 220 | 221 | void BackendSession::forward(const std::shared_ptr& waitReply, 222 | const std::shared_ptr& sharedStr, 223 | const char* b, 224 | size_t len) 225 | { 226 | auto tmp = sharedStr; 227 | if (sharedStr == nullptr) 228 | { 229 | tmp = std::make_shared(b, len); 230 | } 231 | 232 | waitReply->addWaitServer(getSession()); 233 | 234 | auto eventLoop = getEventLoop(); 235 | if (eventLoop == nullptr) 236 | { 237 | return; 238 | } 239 | 240 | if (eventLoop->isInLoopThread()) 241 | { 242 | mPendingWaitReply.push(waitReply); 243 | send(tmp); 244 | } 245 | else 246 | { 247 | eventLoop->runAsyncFunctor([sharedThis = shared_from_this(), waitReply, sharedStrCaptupre = std::move(tmp)]() mutable { 248 | sharedThis->mPendingWaitReply.push(std::move(waitReply)); 249 | sharedThis->send(sharedStrCaptupre); 250 | }); 251 | } 252 | } 253 | 254 | int BackendSession::getID() const 255 | { 256 | return mID; 257 | } 258 | 259 | std::shared_ptr randomServer() 260 | { 261 | if (gBackendClients.empty()) 262 | { 263 | return nullptr; 264 | } 265 | return gBackendClients[std::rand() % gBackendClients.size()]; 266 | } 267 | 268 | shared_ptr findBackendByID(int id) 269 | { 270 | auto it = std::find_if(gBackendClients.begin(), gBackendClients.end(), [=](const shared_ptr& v) { 271 | return v->getID() == id; 272 | }); 273 | if (it != gBackendClients.end()) 274 | { 275 | return *it; 276 | } 277 | 278 | return nullptr; 279 | } 280 | -------------------------------------------------------------------------------- /src/Backend.h: -------------------------------------------------------------------------------- 1 | #ifndef _BACKEND_CLIENT_H 2 | #define _BACKEND_CLIENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "BaseSession.h" 12 | 13 | class BaseWaitReply; 14 | struct parse_tree; 15 | struct BackendParseMsg; 16 | 17 | class BackendSession : public BaseSession, public std::enable_shared_from_this 18 | { 19 | public: 20 | BackendSession(brynet::net::TcpConnection::Ptr session, int id); 21 | ~BackendSession() = default; 22 | 23 | void forward( 24 | const std::shared_ptr& waitReply, 25 | const std::shared_ptr& sharedStr, 26 | const char* b, 27 | size_t len); 28 | int getID() const; 29 | 30 | private: 31 | size_t onMsg(const char* buffer, size_t len) override; 32 | void onEnter() override; 33 | void onClose() override; 34 | 35 | void processReply( 36 | const std::shared_ptr& redisReply, 37 | std::shared_ptr& responseBinary, 38 | const char* replyBuffer, 39 | size_t replyLen); 40 | 41 | private: 42 | const int mID; 43 | std::shared_ptr mRedisParse; 44 | std::shared_ptr mCache; 45 | 46 | std::queue> mPendingWaitReply; 47 | }; 48 | 49 | std::shared_ptr randomServer(); 50 | std::shared_ptr findBackendByID(int id); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/BaseSession.cpp: -------------------------------------------------------------------------------- 1 | #include "BaseSession.h" 2 | 3 | BaseSession::BaseSession(brynet::net::TcpConnection::Ptr session) 4 | : mSession(session) 5 | { 6 | } 7 | 8 | BaseSession::~BaseSession() 9 | {} 10 | 11 | brynet::net::EventLoop::Ptr BaseSession::getEventLoop() const 12 | { 13 | auto session = getSession(); 14 | if (session == nullptr) 15 | { 16 | return nullptr; 17 | } 18 | return session->getEventLoop(); 19 | } 20 | 21 | void BaseSession::send(const std::shared_ptr& data) 22 | { 23 | auto session = getSession(); 24 | if (session != nullptr) 25 | { 26 | session->send(data->c_str(), data->size()); 27 | } 28 | } 29 | 30 | void BaseSession::send(const std::string& data) 31 | { 32 | send(data.data(), data.size()); 33 | } 34 | 35 | void BaseSession::send(const char* buffer, size_t len) 36 | { 37 | auto session = getSession(); 38 | if (session != nullptr) 39 | { 40 | session->send(buffer, len); 41 | } 42 | } 43 | 44 | brynet::net::TcpConnection::Ptr BaseSession::getSession() const 45 | { 46 | return mSession.lock(); 47 | } 48 | -------------------------------------------------------------------------------- /src/BaseSession.h: -------------------------------------------------------------------------------- 1 | #ifndef _BASE_SESSION_H 2 | #define _BASE_SESSION_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class BaseSession 9 | { 10 | public: 11 | using PTR = std::shared_ptr; 12 | 13 | public: 14 | BaseSession(brynet::net::TcpConnection::Ptr session); 15 | virtual ~BaseSession(); 16 | 17 | brynet::net::EventLoop::Ptr getEventLoop() const; 18 | 19 | void send(const std::shared_ptr& data); 20 | void send(const std::string& data); 21 | void send(const char* buffer, size_t len); 22 | brynet::net::TcpConnection::Ptr getSession() const; 23 | 24 | virtual size_t onMsg(const char* buffer, size_t len) = 0; 25 | virtual void onEnter() = 0; 26 | virtual void onClose() = 0; 27 | 28 | private: 29 | const std::weak_ptr mSession; 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/BaseWaitReply.cpp: -------------------------------------------------------------------------------- 1 | #include "protocol/SSDBProtocol.h" 2 | #include "protocol/RedisParse.h" 3 | 4 | #include "BaseWaitReply.h" 5 | 6 | BaseWaitReply::BaseWaitReply(const ClientSession::PTR& client) : mClient(client) 7 | {} 8 | 9 | BaseWaitReply::~BaseWaitReply() 10 | { 11 | } 12 | 13 | const std::shared_ptr& BaseWaitReply::getClient() const 14 | { 15 | return mClient; 16 | } 17 | 18 | void BaseWaitReply::addWaitServer(brynet::net::TcpConnection::Ptr server) 19 | { 20 | PendingResponseStatus tmp; 21 | tmp.dbServerSocket = server; 22 | mWaitResponses.push_back(tmp); 23 | } 24 | 25 | void BaseWaitReply::setError(const char* errorCode) 26 | { 27 | mErrorCode = std::string(errorCode); 28 | } 29 | 30 | bool BaseWaitReply::hasError() const 31 | { 32 | return !mErrorCode.empty(); 33 | } 34 | 35 | bool BaseWaitReply::isAllCompleted() const 36 | { 37 | bool ret = true; 38 | 39 | for (auto& v : mWaitResponses) 40 | { 41 | if (v.forceOK == false && v.redisReply == nullptr && v.ssdbReply == nullptr && v.responseBinary == nullptr) 42 | { 43 | ret = false; 44 | break; 45 | } 46 | } 47 | 48 | return ret; 49 | } -------------------------------------------------------------------------------- /src/BaseWaitReply.h: -------------------------------------------------------------------------------- 1 | #ifndef _BASE_WAIT_REPLY_H 2 | #define _BASE_WAIT_REPLY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Client.h" 10 | 11 | class SSDBProtocolResponse; 12 | struct parse_tree; 13 | 14 | struct BackendParseMsg { 15 | using PTR = std::shared_ptr; 16 | 17 | std::shared_ptr redisReply; 18 | std::shared_ptr responseMemory; 19 | }; 20 | 21 | class BaseWaitReply 22 | { 23 | public: 24 | using PTR = std::shared_ptr; 25 | using WEAK_PTR = std::weak_ptr; 26 | 27 | BaseWaitReply(const ClientSession::PTR& client); 28 | virtual ~BaseWaitReply(); 29 | 30 | const ClientSession::PTR& getClient() const; 31 | 32 | public: 33 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr, const BackendParseMsg::PTR&) = 0; 34 | virtual void mergeAndSend(const ClientSession::PTR&) = 0; 35 | 36 | public: 37 | bool isAllCompleted() const; 38 | void addWaitServer(brynet::net::TcpConnection::Ptr); 39 | 40 | bool hasError() const; 41 | void setError(const char* errorCode); 42 | 43 | protected: 44 | struct PendingResponseStatus { 45 | brynet::net::TcpConnection::Ptr dbServerSocket; 46 | std::shared_ptr responseBinary; 47 | std::shared_ptr ssdbReply; 48 | std::shared_ptr redisReply; 49 | bool forceOK = false; 50 | }; 51 | 52 | std::vector mWaitResponses; 53 | 54 | const ClientSession::PTR mClient; 55 | std::string mErrorCode; 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/Client.cpp: -------------------------------------------------------------------------------- 1 | #include "Client.h" 2 | 3 | #include 4 | 5 | #include 6 | #include "Backend.h" 7 | #include "RedisWaitReply.h" 8 | #include "SSDBWaitReply.h" 9 | #include "defer.h" 10 | #include "protocol/RedisParse.h" 11 | #include "protocol/RedisRequest.h" 12 | #include "protocol/SSDBProtocol.h" 13 | 14 | using namespace std; 15 | 16 | ClientSession::ClientSession(brynet::net::TcpConnection::Ptr session, 17 | sol::state state, 18 | std::string shardingFunction) 19 | : BaseSession(session), 20 | mLuaState(std::move(state)), 21 | mShardingFunction(std::move(shardingFunction)) 22 | { 23 | mRedisParse = nullptr; 24 | mNeedAuth = false; 25 | mIsAuth = false; 26 | } 27 | 28 | RedisProtocolRequest& ClientSession::getCacheRedisProtocol() 29 | { 30 | return mCacheRedisProtocol; 31 | } 32 | 33 | SSDBProtocolRequest& ClientSession::getCacheSSDBProtocol() 34 | { 35 | return mCacheSSDBProtocol; 36 | } 37 | 38 | void ClientSession::onEnter() 39 | { 40 | } 41 | 42 | void ClientSession::onClose() 43 | { 44 | } 45 | 46 | size_t ClientSession::onMsg(const char* buffer, size_t len) 47 | { 48 | size_t totalLen = 0; 49 | 50 | if (mRedisParse != nullptr || 51 | !IS_NUM(buffer[0])) 52 | { 53 | totalLen = onRedisRequestMsg(buffer, len); 54 | } 55 | else 56 | { 57 | totalLen = onSSDBRequestMsg(buffer, len); 58 | } 59 | 60 | return totalLen; 61 | } 62 | 63 | size_t ClientSession::onRedisRequestMsg(const char* buffer, size_t len) 64 | { 65 | size_t totalLen = 0; 66 | 67 | char* parseEndPos = (char*) buffer; 68 | char* parseStartPos = parseEndPos; 69 | while (totalLen < len) 70 | { 71 | if (mRedisParse == nullptr) 72 | { 73 | const static std::vector notDataRequest = {"PING\r\n", "COMMAND\r\n"}; 74 | 75 | bool isWaitDataCompleteRequest = false; 76 | bool isFindCompleteRequest = false; 77 | 78 | for (const auto& v : notDataRequest) 79 | { 80 | const size_t leftLen = len - totalLen; 81 | if (leftLen < v.size()) 82 | { 83 | size_t findPos = v.find(parseStartPos, 0, leftLen); 84 | if (findPos != string::npos) 85 | { 86 | isWaitDataCompleteRequest = true; 87 | break; 88 | } 89 | } 90 | else 91 | { 92 | if (v.compare(0, v.size(), parseStartPos, v.size()) == 0) 93 | { 94 | processRedisRequest(mCache, parseStartPos, v.size()); 95 | totalLen += v.size(); 96 | parseStartPos += v.size(); 97 | 98 | isFindCompleteRequest = true; 99 | break; 100 | } 101 | } 102 | } 103 | 104 | if (isWaitDataCompleteRequest) 105 | { 106 | break; 107 | } 108 | 109 | if (isFindCompleteRequest) 110 | { 111 | continue; 112 | } 113 | 114 | mRedisParse = std::shared_ptr(parse_tree_new(), [](parse_tree* parse) { 115 | parse_tree_del(parse); 116 | }); 117 | } 118 | 119 | int parseRet = parse(mRedisParse.get(), &parseEndPos, (char*) buffer + len); 120 | totalLen += (parseEndPos - parseStartPos); 121 | 122 | if (parseRet == REDIS_OK) 123 | { 124 | if (mCache == nullptr) 125 | { 126 | processRedisRequest(mCache, parseStartPos, parseEndPos - parseStartPos); 127 | } 128 | else 129 | { 130 | mCache->append(parseStartPos, parseEndPos - parseStartPos); 131 | auto tmp = mCache->c_str(); 132 | auto tmpLen = mCache->size(); 133 | processRedisRequest(mCache, tmp, mCache->size()); 134 | mCache = nullptr; 135 | } 136 | 137 | parseStartPos = parseEndPos; 138 | mRedisParse = nullptr; 139 | } 140 | else if (parseRet == REDIS_RETRY) 141 | { 142 | if (mCache == nullptr) 143 | { 144 | mCache.reset(new std::string); 145 | } 146 | mCache->append(parseStartPos, parseEndPos - parseStartPos); 147 | break; 148 | } 149 | else 150 | { 151 | assert(false); 152 | break; 153 | } 154 | } 155 | 156 | return totalLen; 157 | } 158 | 159 | size_t ClientSession::onSSDBRequestMsg(const char* buffer, size_t len) 160 | { 161 | size_t totalLen = 0; 162 | 163 | char* parseStartPos = (char*) buffer; 164 | int leftLen = len; 165 | int packetLen = 0; 166 | 167 | while ((packetLen = SSDBProtocolResponse::check_ssdb_packet(parseStartPos, leftLen)) > 0) 168 | { 169 | auto ssdbQuery = std::make_shared(); 170 | ssdbQuery->parse(parseStartPos); 171 | 172 | processSSDBRequest(ssdbQuery, mCache, parseStartPos, packetLen); 173 | 174 | totalLen += packetLen; 175 | leftLen -= packetLen; 176 | parseStartPos += packetLen; 177 | } 178 | 179 | return totalLen; 180 | } 181 | 182 | void ClientSession::processRedisRequest(const std::shared_ptr& requestBinary, 183 | const char* requestBuffer, 184 | size_t requestLen) 185 | { 186 | defer(mRedisParse = nullptr); 187 | 188 | if (strncmp(requestBuffer, "PING\r\n", 6) == 0) 189 | { 190 | pushRedisStatusReply("PONG"); 191 | return; 192 | } 193 | 194 | const char* op = mRedisParse->reply->element[0]->str; 195 | const size_t oplen = mRedisParse->reply->element[0]->len; 196 | 197 | bool isSuccess = false; 198 | defer( 199 | if (!isSuccess) { 200 | pushRedisErrorReply("no error for key"); 201 | }); 202 | 203 | if (strncmp(op, "ping", 4) == 0 || strncmp(op, "PING", 4) == 0) 204 | { 205 | pushRedisStatusReply("PONG"); 206 | isSuccess = true; 207 | } 208 | else if (strncmp(op, "COMMAND", 7) == 0 || strncmp(op, "command", 7) == 0) 209 | { 210 | isSuccess = true; 211 | auto server = randomServer(); 212 | if (server == nullptr) 213 | { 214 | pushRedisErrorReply("not have any backend redis server"); 215 | return; 216 | } 217 | 218 | BaseWaitReply::PTR waitReply = std::make_shared(shared_from_this()); 219 | server->forward(waitReply, requestBinary, requestBuffer, requestLen); 220 | mPendingReply.push_back(waitReply); 221 | return; 222 | } 223 | else if (strncmp(op, "mget", oplen) == 0 || 224 | strncmp(op, "del", oplen) == 0) 225 | { 226 | isSuccess = processRedisCommandOfMultiKeys(std::make_shared(shared_from_this()), 227 | mRedisParse, 228 | requestBinary, 229 | requestBuffer, 230 | requestLen, 231 | op); 232 | } 233 | else if (strncmp(op, "mset", oplen) == 0) 234 | { 235 | isSuccess = processRedisMset(mRedisParse, requestBinary, requestBuffer, requestLen); 236 | } 237 | else 238 | { 239 | isSuccess = processRedisSingleCommand(mRedisParse, requestBinary, requestBuffer, requestLen); 240 | } 241 | } 242 | 243 | void ClientSession::processSSDBRequest(const std::shared_ptr& ssdbQuery, 244 | const std::shared_ptr& requestBinary, 245 | const char* requestBuffer, 246 | size_t requestLen) 247 | { 248 | bool isSuccess = false; 249 | 250 | defer( 251 | if (!isSuccess) { 252 | pushSSDBErrorReply("command not process"); 253 | }); 254 | 255 | Bytes* op = ssdbQuery->getByIndex(0); 256 | if (op == nullptr) 257 | { 258 | return; 259 | } 260 | 261 | if (strncmp("auth", op->buffer, op->len) == 0) 262 | { 263 | isSuccess = procSSDBAuth(ssdbQuery, requestBuffer, requestLen); 264 | return; 265 | } 266 | 267 | if (mNeedAuth && !mIsAuth) 268 | { 269 | pushSSDBStrListReply({"noauth", "authentication required"}); 270 | isSuccess = true; 271 | } 272 | else if (strncmp("ping", op->buffer, op->len) == 0) 273 | { 274 | isSuccess = procSSDBPing(ssdbQuery, requestBuffer, requestLen); 275 | } 276 | else if (strncmp("multi_set", op->buffer, op->len) == 0) 277 | { 278 | isSuccess = procSSDBMultiSet(ssdbQuery, requestBinary, requestBuffer, requestLen); 279 | } 280 | else if (strncmp("multi_get", op->buffer, op->len) == 0) 281 | { 282 | isSuccess = procSSDBCommandOfMultiKeys(std::make_shared(shared_from_this()), 283 | ssdbQuery, 284 | requestBinary, 285 | requestBuffer, 286 | requestLen, 287 | "multi_get"); 288 | } 289 | else if (strncmp("multi_del", op->buffer, op->len) == 0) 290 | { 291 | isSuccess = procSSDBCommandOfMultiKeys(std::make_shared(shared_from_this()), 292 | ssdbQuery, 293 | requestBinary, 294 | requestBuffer, 295 | requestLen, 296 | "multi_del"); 297 | } 298 | else 299 | { 300 | isSuccess = procSSDBSingleCommand(ssdbQuery, requestBinary, requestBuffer, requestLen); 301 | } 302 | } 303 | 304 | void ClientSession::pushSSDBStrListReply(const std::vector& strlist) 305 | { 306 | auto reply = std::make_shared(shared_from_this()); 307 | for (const auto& v : strlist) 308 | { 309 | reply->pushStr(v); 310 | } 311 | mPendingReply.push_back(reply); 312 | processCompletedReply(); 313 | } 314 | 315 | void ClientSession::pushSSDBErrorReply(const char* error) 316 | { 317 | pushSSDBStrListReply({"error", error}); 318 | } 319 | 320 | void ClientSession::pushRedisErrorReply(const char* error) 321 | { 322 | mPendingReply.push_back(std::make_shared(shared_from_this(), error)); 323 | processCompletedReply(); 324 | } 325 | 326 | void ClientSession::pushRedisStatusReply(const char* status) 327 | { 328 | mPendingReply.push_back(std::make_shared(shared_from_this(), status)); 329 | processCompletedReply(); 330 | } 331 | 332 | bool ClientSession::procSSDBAuth(const std::shared_ptr& request, 333 | const char* requestBuffer, 334 | size_t requestLen) 335 | { 336 | if (request->getBuffersLen() != 2) 337 | { 338 | pushSSDBStrListReply({"client_error"}); 339 | return true; 340 | } 341 | 342 | Bytes* p = request->getByIndex(1); 343 | if (mNeedAuth && strncmp(p->buffer, mPassword.c_str(), p->len) != 0) 344 | { 345 | pushSSDBErrorReply("invalid password"); 346 | return true; 347 | } 348 | 349 | mIsAuth = true; 350 | pushSSDBStrListReply({"ok"}); 351 | 352 | return true; 353 | } 354 | 355 | bool ClientSession::procSSDBPing(const std::shared_ptr&, 356 | const char* requestBuffer, 357 | size_t requestLen) 358 | { 359 | pushSSDBStrListReply({"ok"}); 360 | return true; 361 | } 362 | 363 | bool ClientSession::procSSDBMultiSet(const std::shared_ptr& request, 364 | const std::shared_ptr& requestBinary, 365 | const char* requestBuffer, 366 | size_t requestLen) 367 | { 368 | bool isSuccess = (request->getBuffersLen() - 1) % 2 == 0 && request->getBuffersLen() > 1; 369 | if (!isSuccess) 370 | { 371 | return isSuccess; 372 | } 373 | 374 | BaseWaitReply::PTR waitReply = std::make_shared(shared_from_this()); 375 | 376 | defer( 377 | clearShardingKVS(); 378 | if (isSuccess) { 379 | mPendingReply.push_back(waitReply); 380 | }); 381 | 382 | for (size_t i = 1; i < request->getBuffersLen(); i += 2) 383 | { 384 | const Bytes* b = request->getByIndex(i); 385 | int serverID; 386 | if (!shardingKey(b->buffer, b->len, serverID)) 387 | { 388 | return isSuccess = false; 389 | } 390 | 391 | auto it = mShardingTmpKVS.find(serverID); 392 | if (it == mShardingTmpKVS.end()) 393 | { 394 | std::vector tmp; 395 | tmp.push_back(*b); 396 | tmp.push_back(*(request->getByIndex(i + 1))); 397 | mShardingTmpKVS[serverID] = std::move(tmp); 398 | } 399 | else 400 | { 401 | (*it).second.push_back(*b); 402 | (*it).second.push_back(*(request->getByIndex(i + 1))); 403 | } 404 | } 405 | 406 | if (mShardingTmpKVS.size() == 1) 407 | { 408 | auto server = findBackendByID((*mShardingTmpKVS.begin()).first); 409 | if (server == nullptr) 410 | { 411 | return isSuccess = false; 412 | } 413 | 414 | server->forward(waitReply, requestBinary, requestBuffer, requestLen); 415 | } 416 | else 417 | { 418 | SSDBProtocolRequest& request2Backend = getCacheSSDBProtocol(); 419 | 420 | for (const auto& [k, v] : mShardingTmpKVS) 421 | { 422 | if (v.empty()) 423 | { 424 | continue; 425 | } 426 | 427 | request2Backend.init(); 428 | auto server = findBackendByID(k); 429 | if (server == nullptr) 430 | { 431 | return isSuccess = false; 432 | } 433 | 434 | request2Backend.appendStr("multi_set"); 435 | for (const auto& k : v) 436 | { 437 | request2Backend.appendStr(k.buffer, k.len); 438 | } 439 | request2Backend.endl(); 440 | 441 | server->forward(waitReply, 442 | nullptr, 443 | request2Backend.getResult(), 444 | request2Backend.getResultLen()); 445 | } 446 | } 447 | 448 | return isSuccess; 449 | } 450 | 451 | bool ClientSession::procSSDBCommandOfMultiKeys(const std::shared_ptr& waitReply, 452 | const std::shared_ptr& request, 453 | const std::shared_ptr& requestBinary, 454 | const char* requestBuffer, 455 | size_t requestLen, 456 | const char* command) 457 | { 458 | bool isSuccess = request->getBuffersLen() > 1; 459 | if (!isSuccess) 460 | { 461 | return false; 462 | } 463 | 464 | defer( 465 | clearShardingKVS(); 466 | if (isSuccess) { 467 | mPendingReply.push_back(waitReply); 468 | }); 469 | 470 | for (size_t i = 1; i < request->getBuffersLen(); ++i) 471 | { 472 | Bytes* b = request->getByIndex(i); 473 | int serverID; 474 | if (!shardingKey(b->buffer, b->len, serverID)) 475 | { 476 | return isSuccess = false; 477 | } 478 | 479 | auto it = mShardingTmpKVS.find(serverID); 480 | if (it == mShardingTmpKVS.end()) 481 | { 482 | std::vector tmp; 483 | tmp.push_back(*b); 484 | mShardingTmpKVS[serverID] = std::move(tmp); 485 | } 486 | else 487 | { 488 | (*it).second.push_back(*b); 489 | } 490 | } 491 | 492 | if (mShardingTmpKVS.size() == 1) 493 | { 494 | auto server = findBackendByID((*mShardingTmpKVS.begin()).first); 495 | if (server == nullptr) 496 | { 497 | return isSuccess = false; 498 | } 499 | 500 | server->forward(waitReply, requestBinary, requestBuffer, requestLen); 501 | } 502 | else 503 | { 504 | SSDBProtocolRequest& request2Backend = getCacheSSDBProtocol(); 505 | 506 | for (const auto& [k, v] : mShardingTmpKVS) 507 | { 508 | if (v.empty()) 509 | { 510 | continue; 511 | } 512 | 513 | request2Backend.init(); 514 | auto server = findBackendByID(k); 515 | if (server == nullptr) 516 | { 517 | return isSuccess = false; 518 | } 519 | 520 | request2Backend.appendStr(command); 521 | for (const auto& k : v) 522 | { 523 | request2Backend.appendStr(k.buffer, k.len); 524 | } 525 | request2Backend.endl(); 526 | 527 | server->forward(waitReply, nullptr, request2Backend.getResult(), request2Backend.getResultLen()); 528 | } 529 | } 530 | 531 | return true; 532 | } 533 | 534 | bool ClientSession::procSSDBSingleCommand(const std::shared_ptr& request, 535 | const std::shared_ptr& requestBinary, 536 | const char* requestBuffer, size_t requestLen) 537 | { 538 | Bytes* b = request->getByIndex(1); 539 | int serverID; 540 | if (!shardingKey(b->buffer, b->len, serverID)) 541 | { 542 | return false; 543 | } 544 | 545 | auto server = findBackendByID(serverID); 546 | if (server == nullptr) 547 | { 548 | return false; 549 | } 550 | 551 | BaseWaitReply::PTR waitReply = std::make_shared(shared_from_this()); 552 | server->forward(waitReply, requestBinary, requestBuffer, requestLen); 553 | mPendingReply.push_back(waitReply); 554 | 555 | return true; 556 | } 557 | 558 | bool ClientSession::processRedisSingleCommand(const std::shared_ptr& parse, 559 | const std::shared_ptr& requestBinary, 560 | const char* requestBuffer, 561 | size_t requestLen) 562 | { 563 | if (parse->reply->elements < 1) 564 | { 565 | return false; 566 | } 567 | 568 | int serverID; 569 | if (!shardingKey(parse->reply->element[1]->str, parse->reply->element[1]->len, serverID)) 570 | { 571 | return false; 572 | } 573 | 574 | auto server = findBackendByID(serverID); 575 | if (server == nullptr) 576 | { 577 | return false; 578 | } 579 | 580 | BaseWaitReply::PTR waitReply = std::make_shared(shared_from_this()); 581 | server->forward(waitReply, requestBinary, requestBuffer, requestLen); 582 | mPendingReply.push_back(waitReply); 583 | 584 | return true; 585 | } 586 | 587 | bool ClientSession::processRedisMset(const std::shared_ptr& parse, 588 | const std::shared_ptr& requestBinary, 589 | const char* requestBuffer, 590 | size_t requestLen) 591 | { 592 | if (parse->reply->elements <= 1 || (parse->reply->elements - 1) % 2 != 0) 593 | { 594 | return false; 595 | } 596 | 597 | defer( 598 | clearShardingKVS();); 599 | 600 | for (size_t i = 1; i < parse->reply->elements; i += 2) 601 | { 602 | int serverID; 603 | 604 | const char* key = parse->reply->element[i]->str; 605 | int keyLen = parse->reply->element[i]->len; 606 | const char* value = parse->reply->element[i + 1]->str; 607 | int valueLen = parse->reply->element[i + 1]->len; 608 | 609 | if (!shardingKey(key, keyLen, serverID)) 610 | { 611 | return false; 612 | } 613 | 614 | auto it = mShardingTmpKVS.find(serverID); 615 | if (it == mShardingTmpKVS.end()) 616 | { 617 | std::vector tmp; 618 | tmp.push_back({key, keyLen}); 619 | tmp.push_back({value, valueLen}); 620 | mShardingTmpKVS[serverID] = std::move(tmp); 621 | } 622 | else 623 | { 624 | (*it).second.push_back({key, keyLen}); 625 | (*it).second.push_back({value, valueLen}); 626 | } 627 | } 628 | 629 | BaseWaitReply::PTR waitReply = std::make_shared(shared_from_this()); 630 | auto isSuccess = true; 631 | 632 | defer( 633 | if (isSuccess) { 634 | mPendingReply.push_back(waitReply); 635 | }); 636 | 637 | if (mShardingTmpKVS.size() == 1) 638 | { 639 | auto server = findBackendByID((*mShardingTmpKVS.begin()).first); 640 | if (server == nullptr) 641 | { 642 | return isSuccess = false; 643 | } 644 | 645 | server->forward(waitReply, requestBinary, requestBuffer, requestLen); 646 | } 647 | else 648 | { 649 | RedisProtocolRequest& request2Backend = getCacheRedisProtocol(); 650 | 651 | for (const auto& [k, v] : mShardingTmpKVS) 652 | { 653 | if (v.empty()) 654 | { 655 | continue; 656 | } 657 | 658 | auto server = findBackendByID(k); 659 | if (server == nullptr) 660 | { 661 | return isSuccess = false; 662 | } 663 | 664 | request2Backend.init(); 665 | request2Backend.writev("mset"); 666 | for (const auto& k : v) 667 | { 668 | request2Backend.appendBinary(k.buffer, k.len); 669 | } 670 | request2Backend.endl(); 671 | 672 | 673 | server->forward(waitReply, nullptr, request2Backend.getResult(), request2Backend.getResultLen()); 674 | } 675 | } 676 | 677 | return true; 678 | } 679 | 680 | bool ClientSession::processRedisCommandOfMultiKeys(const std::shared_ptr& waitReply, 681 | const std::shared_ptr& parse, 682 | const std::shared_ptr& requestBinary, 683 | const char* requestBuffer, 684 | size_t requestLen, 685 | const char* command) 686 | { 687 | if (parse->reply->elements <= 1) 688 | { 689 | return false; 690 | } 691 | 692 | defer( 693 | clearShardingKVS();); 694 | 695 | for (size_t i = 1; i < parse->reply->elements; ++i) 696 | { 697 | int serverID; 698 | const char* key = parse->reply->element[i]->str; 699 | int keyLen = parse->reply->element[i]->len; 700 | 701 | if (!shardingKey(key, keyLen, serverID)) 702 | { 703 | return false; 704 | } 705 | 706 | auto it = mShardingTmpKVS.find(serverID); 707 | if (it == mShardingTmpKVS.end()) 708 | { 709 | std::vector tmp; 710 | tmp.push_back({key, keyLen}); 711 | mShardingTmpKVS[serverID] = std::move(tmp); 712 | } 713 | else 714 | { 715 | (*it).second.push_back({key, keyLen}); 716 | } 717 | } 718 | 719 | auto isSuccess = true; 720 | defer( 721 | if (isSuccess) { 722 | mPendingReply.push_back(waitReply); 723 | };); 724 | 725 | if (mShardingTmpKVS.size() == 1) 726 | { 727 | auto server = findBackendByID((*mShardingTmpKVS.begin()).first); 728 | if (server == nullptr) 729 | { 730 | return isSuccess = false; 731 | } 732 | 733 | server->forward(waitReply, requestBinary, requestBuffer, requestLen); 734 | } 735 | else 736 | { 737 | RedisProtocolRequest& request2Backend = getCacheRedisProtocol(); 738 | 739 | for (const auto& [k, v] : mShardingTmpKVS) 740 | { 741 | if (v.empty()) 742 | { 743 | continue; 744 | } 745 | 746 | auto server = findBackendByID(k); 747 | if (server == nullptr) 748 | { 749 | return isSuccess = false; 750 | } 751 | 752 | request2Backend.init(); 753 | request2Backend.appendBinary(command, strlen(command)); 754 | for (const auto& k : v) 755 | { 756 | request2Backend.appendBinary(k.buffer, k.len); 757 | } 758 | request2Backend.endl(); 759 | 760 | server->forward(waitReply, nullptr, request2Backend.getResult(), request2Backend.getResultLen()); 761 | } 762 | } 763 | 764 | return true; 765 | } 766 | 767 | void ClientSession::clearShardingKVS() 768 | { 769 | for (auto& [_, v] : mShardingTmpKVS) 770 | { 771 | v.clear(); 772 | } 773 | } 774 | 775 | bool ClientSession::shardingKey(const char* str, int len, int& serverID) 776 | { 777 | serverID = mLuaState[mShardingFunction](std::string(str, len)); 778 | return true; 779 | } 780 | 781 | void ClientSession::processCompletedReply() 782 | { 783 | auto sharedThis = shared_from_this(); 784 | while (!mPendingReply.empty()) 785 | { 786 | const auto& waitReply = mPendingReply.front(); 787 | if (!waitReply->isAllCompleted() && !waitReply->hasError()) 788 | { 789 | break; 790 | } 791 | 792 | waitReply->mergeAndSend(sharedThis); 793 | mPendingReply.pop_front(); 794 | } 795 | } 796 | -------------------------------------------------------------------------------- /src/Client.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLIENT_H 2 | #define _CLIENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "BaseSession.h" 13 | #include "protocol/RedisRequest.h" 14 | #include "protocol/SSDBProtocol.h" 15 | 16 | struct parse_tree; 17 | class BaseWaitReply; 18 | class SSDBProtocolResponse; 19 | 20 | class ClientSession : public BaseSession, public std::enable_shared_from_this 21 | { 22 | public: 23 | using PTR = std::shared_ptr; 24 | 25 | public: 26 | ClientSession(brynet::net::TcpConnection::Ptr session, sol::state state, std::string shardingFunction); 27 | ~ClientSession() = default; 28 | void processCompletedReply(); 29 | 30 | RedisProtocolRequest& getCacheRedisProtocol(); 31 | SSDBProtocolRequest& getCacheSSDBProtocol(); 32 | 33 | private: 34 | virtual size_t onMsg(const char* buffer, size_t len) override; 35 | void onEnter() override; 36 | void onClose() override; 37 | 38 | private: 39 | size_t onRedisRequestMsg(const char* buffer, size_t len); 40 | size_t onSSDBRequestMsg(const char* buffer, size_t len); 41 | 42 | void processRedisRequest( 43 | const std::shared_ptr& requestBinary, 44 | const char* requestBuffer, 45 | size_t requestLen); 46 | void processSSDBRequest( 47 | const std::shared_ptr& ssdbQuery, 48 | const std::shared_ptr& requestBinary, 49 | const char* requestBuffer, 50 | size_t requestLen); 51 | 52 | private: 53 | void pushSSDBStrListReply(const std::vector& strlist); 54 | void pushSSDBErrorReply(const char* error); 55 | void pushRedisErrorReply(const char* error); 56 | void pushRedisStatusReply(const char* status); 57 | 58 | private: 59 | bool procSSDBAuth( 60 | const std::shared_ptr&, 61 | const char* requestBuffer, 62 | size_t requestLen); 63 | bool procSSDBPing( 64 | const std::shared_ptr&, 65 | const char* requestBuffer, 66 | size_t requestLen); 67 | bool procSSDBMultiSet(const std::shared_ptr&, 68 | const std::shared_ptr& requestBinary, 69 | const char* requestBuffer, 70 | size_t requestLen); 71 | bool procSSDBCommandOfMultiKeys( 72 | const std::shared_ptr&, 73 | const std::shared_ptr&, 74 | const std::shared_ptr& requestBinary, 75 | const char* requestBuffer, 76 | size_t requestLen, 77 | const char* command); 78 | bool procSSDBSingleCommand( 79 | const std::shared_ptr&, 80 | const std::shared_ptr& requestBinary, 81 | const char* requestBuffer, 82 | size_t requestLen); 83 | 84 | bool processRedisSingleCommand( 85 | const std::shared_ptr& parse, 86 | const std::shared_ptr& requestBinary, 87 | const char* requestBuffer, 88 | size_t requestLen); 89 | bool processRedisMset( 90 | const std::shared_ptr& parse, 91 | const std::shared_ptr& requestBinary, 92 | const char* requestBuffer, 93 | size_t requestLen); 94 | bool processRedisCommandOfMultiKeys( 95 | const std::shared_ptr&, 96 | const std::shared_ptr& parse, 97 | const std::shared_ptr& requestBinary, 98 | const char* requestBuffer, 99 | size_t requestLen, 100 | const char* command); 101 | 102 | private: 103 | void clearShardingKVS(); 104 | bool shardingKey(const char* str, int len, int& serverID); 105 | 106 | private: 107 | const sol::state mLuaState; 108 | const std::string mShardingFunction; 109 | 110 | std::shared_ptr mRedisParse; 111 | std::shared_ptr mCache; 112 | 113 | std::deque> mPendingReply; 114 | bool mNeedAuth; 115 | bool mIsAuth; 116 | std::string mPassword; 117 | 118 | RedisProtocolRequest mCacheRedisProtocol; 119 | SSDBProtocolRequest mCacheSSDBProtocol; 120 | 121 | std::unordered_map> mShardingTmpKVS; 122 | }; 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /src/DBProxyServer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Backend.h" 13 | #include "Client.h" 14 | 15 | using namespace std; 16 | using namespace brynet::net; 17 | 18 | static void OnSessionEnter(BaseSession::PTR session) 19 | { 20 | session->onEnter(); 21 | auto tcpSession = session->getSession(); 22 | tcpSession->setDataCallback([session](brynet::base::BasePacketReader& reader) { 23 | session->onMsg(reader.currentBuffer(), reader.getLeft()); 24 | reader.consumeAll(); 25 | }); 26 | 27 | tcpSession->setDisConnectCallback([session](const TcpConnection::Ptr& tcpSession) { 28 | session->onClose(); 29 | }); 30 | } 31 | 32 | int main(int argc, const char** argv) 33 | { 34 | if (argc != 2) 35 | { 36 | std::cerr << "usage: path-to-config" << std::endl; 37 | exit(-1); 38 | } 39 | 40 | brynet::net::base::InitSocket(); 41 | 42 | srand(static_cast(time(nullptr))); 43 | 44 | int listenPort; 45 | string shardingFunction; 46 | std::string luaConfigFile; 47 | std::vector> backendConfigs; 48 | 49 | try 50 | { 51 | sol::state luaState; 52 | luaState.do_file(argv[1]); 53 | 54 | auto proxyConfig = luaState.get("ProxyConfig"); 55 | 56 | luaConfigFile = argv[1]; 57 | listenPort = proxyConfig["listenPort"]; 58 | shardingFunction = proxyConfig["sharding_function"].get(); 59 | sol::table backendList = proxyConfig["backends"]; 60 | 61 | for (const auto& [_, v] : backendList) 62 | { 63 | auto backend = v.as(); 64 | int id = backend["id"]; 65 | string dbServerIP = backend["ip"]; 66 | int port = backend["port"]; 67 | backendConfigs.push_back({id, dbServerIP, port}); 68 | 69 | std::cout << "backend :" << id << ", ip:" << dbServerIP << ", port:" << port << endl; 70 | } 71 | } 72 | catch (const std::exception& e) 73 | { 74 | std::cerr << "exception:" << e.what() << endl; 75 | exit(-1); 76 | } 77 | 78 | 79 | auto connector = AsyncConnector::Create(); 80 | connector->startWorkerThread(); 81 | 82 | auto tcpService = brynet::net::IOThreadTcpService::Create(); 83 | int netWorkerThreadNum = std::thread::hardware_concurrency(); 84 | 85 | auto eventLoops = tcpService->startWorkerThread(netWorkerThreadNum, nullptr); 86 | for (const auto& eventLoop : eventLoops) 87 | { 88 | auto eventLoopTspService = brynet::net::EventLoopTcpService::Create(eventLoop); 89 | for (const auto& [id, ip, port] : backendConfigs) 90 | { 91 | auto enterCallback = [id](const TcpConnection::Ptr& session) { 92 | auto bserver = std::make_shared(session, id); 93 | OnSessionEnter(bserver); 94 | }; 95 | 96 | try 97 | { 98 | wrapper::ConnectionBuilder connectionBuilder; 99 | connectionBuilder 100 | .WithService(eventLoopTspService) 101 | .WithConnector(connector) 102 | .WithMaxRecvBufferSize(1024 * 1024) 103 | .AddEnterCallback(enterCallback) 104 | .WithAddr(ip, port) 105 | .asyncConnect(); 106 | } 107 | catch (std::exception& e) 108 | { 109 | std::cout << "exception :" << e.what() << std::endl; 110 | } 111 | } 112 | } 113 | 114 | wrapper::ListenerBuilder listener; 115 | listener.WithService(tcpService) 116 | .AddSocketProcess([](TcpSocket& socket) { 117 | socket.setNodelay(); 118 | }) 119 | .WithMaxRecvBufferSize(1024 * 1024) 120 | .WithAddr(false, std::string("0.0.0.0"), static_cast(listenPort)) 121 | .AddEnterCallback([=](const TcpConnection::Ptr& session) { 122 | sol::state state; 123 | state.do_file(luaConfigFile); 124 | auto client = std::make_shared(session, std::move(state), shardingFunction); 125 | OnSessionEnter(client); 126 | }) 127 | .asyncRun(); 128 | 129 | 130 | while (true) 131 | { 132 | if (brynet::base::app_kbhit()) 133 | { 134 | string input; 135 | std::getline(std::cin, input); 136 | 137 | if (input == "quit") 138 | { 139 | std::cerr << "You enter quit will exit proxy" << std::endl; 140 | break; 141 | } 142 | } 143 | 144 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 145 | } 146 | 147 | listener.stop(); 148 | tcpService->stopWorkerThread(); 149 | 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /src/RedisWaitReply.cpp: -------------------------------------------------------------------------------- 1 | #include "RedisWaitReply.h" 2 | 3 | #include 4 | 5 | #include "Client.h" 6 | #include "protocol/RedisParse.h" 7 | #include "protocol/RedisRequest.h" 8 | 9 | using namespace std; 10 | 11 | static void HelpSendError(const shared_ptr& client, const string& error) 12 | { 13 | BaseWaitReply::PTR tmp = std::make_shared(client, error.c_str()); 14 | tmp->mergeAndSend(client); 15 | } 16 | 17 | RedisSingleWaitReply::RedisSingleWaitReply(const ClientSession::PTR& client) 18 | : BaseWaitReply(client) 19 | { 20 | } 21 | 22 | void RedisSingleWaitReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR& msg) 23 | { 24 | assert(mWaitResponses.size() == 1); 25 | for (auto& v : mWaitResponses) 26 | { 27 | if (v.dbServerSocket == dbServerSocket) 28 | { 29 | v.responseBinary = std::move(msg->responseMemory); 30 | break; 31 | } 32 | } 33 | } 34 | 35 | void RedisSingleWaitReply::mergeAndSend(const ClientSession::PTR& client) 36 | { 37 | if (!mErrorCode.empty()) 38 | { 39 | HelpSendError(client, mErrorCode); 40 | } 41 | else if (!mWaitResponses.empty()) 42 | { 43 | client->send(mWaitResponses.front().responseBinary); 44 | } 45 | } 46 | 47 | RedisStatusReply::RedisStatusReply(const ClientSession::PTR& client, const char* status) 48 | : BaseWaitReply(client), 49 | mStatus(status) 50 | { 51 | } 52 | 53 | void RedisStatusReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) 54 | { 55 | } 56 | 57 | void RedisStatusReply::mergeAndSend(const ClientSession::PTR& client) 58 | { 59 | std::shared_ptr tmp = std::make_shared("+"); 60 | tmp->append(mStatus); 61 | tmp->append("\r\n"); 62 | client->send(tmp); 63 | } 64 | 65 | RedisErrorReply::RedisErrorReply(const ClientSession::PTR& client, const char* error) 66 | : BaseWaitReply(client), 67 | mErrorCode(error) 68 | { 69 | } 70 | 71 | void RedisErrorReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) 72 | { 73 | } 74 | 75 | void RedisErrorReply::mergeAndSend(const ClientSession::PTR& client) 76 | { 77 | std::shared_ptr tmp = std::make_shared("-ERR "); 78 | tmp->append(mErrorCode); 79 | tmp->append("\r\n"); 80 | client->send(tmp); 81 | } 82 | 83 | RedisWrongTypeReply::RedisWrongTypeReply(const ClientSession::PTR& client, const char* wrongType, const char* detail) 84 | : BaseWaitReply(client), 85 | mWrongType(wrongType), 86 | mWrongDetail(detail) 87 | { 88 | } 89 | 90 | void RedisWrongTypeReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) 91 | { 92 | } 93 | 94 | void RedisWrongTypeReply::mergeAndSend(const ClientSession::PTR& client) 95 | { 96 | std::shared_ptr tmp = std::make_shared("-WRONGTYPE "); 97 | tmp->append(mWrongType); 98 | tmp->append(" "); 99 | tmp->append(mWrongDetail); 100 | tmp->append("\r\n"); 101 | client->send(tmp); 102 | } 103 | 104 | RedisMgetWaitReply::RedisMgetWaitReply(const ClientSession::PTR& client) 105 | : BaseWaitReply(client) 106 | { 107 | } 108 | 109 | void RedisMgetWaitReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR& msg) 110 | { 111 | for (auto& v : mWaitResponses) 112 | { 113 | if (v.dbServerSocket != dbServerSocket) 114 | { 115 | continue; 116 | } 117 | 118 | if (mWaitResponses.size() != 1) 119 | { 120 | v.redisReply = std::move(msg->redisReply); 121 | msg->redisReply = nullptr; 122 | } 123 | else 124 | { 125 | v.responseBinary = std::move(msg->responseMemory); 126 | } 127 | break; 128 | } 129 | } 130 | 131 | void RedisMgetWaitReply::mergeAndSend(const ClientSession::PTR& client) 132 | { 133 | if (!mErrorCode.empty()) 134 | { 135 | HelpSendError(client, mErrorCode); 136 | return; 137 | } 138 | if (mWaitResponses.size() == 1) 139 | { 140 | client->send(mWaitResponses.front().responseBinary); 141 | return; 142 | } 143 | 144 | RedisProtocolRequest& strsResponse = client->getCacheRedisProtocol(); 145 | strsResponse.init(); 146 | 147 | for (const auto& v : mWaitResponses) 148 | { 149 | for (size_t i = 0; i < v.redisReply->reply->elements; ++i) 150 | { 151 | strsResponse.appendBinary(v.redisReply->reply->element[i]->str, v.redisReply->reply->element[i]->len); 152 | } 153 | } 154 | 155 | strsResponse.endl(); 156 | client->send(strsResponse.getResult(), strsResponse.getResultLen()); 157 | } 158 | 159 | RedisMsetWaitReply::RedisMsetWaitReply(const ClientSession::PTR& client) 160 | : BaseWaitReply(client) 161 | { 162 | } 163 | 164 | void RedisMsetWaitReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) 165 | { 166 | for (auto& v : mWaitResponses) 167 | { 168 | if (v.dbServerSocket != dbServerSocket) 169 | { 170 | continue; 171 | } 172 | 173 | v.forceOK = true; 174 | break; 175 | } 176 | } 177 | 178 | void RedisMsetWaitReply::mergeAndSend(const ClientSession::PTR& client) 179 | { 180 | if (!mErrorCode.empty()) 181 | { 182 | HelpSendError(client, mErrorCode); 183 | return; 184 | } 185 | 186 | const static char* OK = "+OK\r\n"; 187 | const static int OK_LEN = strlen(OK); 188 | 189 | client->send(OK, OK_LEN); 190 | } 191 | 192 | RedisDelWaitReply::RedisDelWaitReply(const ClientSession::PTR& client) 193 | : BaseWaitReply(client) 194 | { 195 | } 196 | 197 | void RedisDelWaitReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR& msg) 198 | { 199 | for (auto& v : mWaitResponses) 200 | { 201 | if (v.dbServerSocket != dbServerSocket) 202 | { 203 | continue; 204 | } 205 | 206 | if (mWaitResponses.size() != 1) 207 | { 208 | v.redisReply = std::move(msg->redisReply); 209 | msg->redisReply = nullptr; 210 | } 211 | else 212 | { 213 | v.responseBinary = std::move(msg->responseMemory); 214 | } 215 | break; 216 | } 217 | } 218 | 219 | void RedisDelWaitReply::mergeAndSend(const ClientSession::PTR& client) 220 | { 221 | if (!mErrorCode.empty()) 222 | { 223 | HelpSendError(client, mErrorCode); 224 | return; 225 | } 226 | if (mWaitResponses.size() == 1) 227 | { 228 | client->send(mWaitResponses.front().responseBinary); 229 | return; 230 | } 231 | 232 | int64_t num = 0; 233 | for (const auto& v : mWaitResponses) 234 | { 235 | num += v.redisReply->reply->integer; 236 | } 237 | 238 | char tmp[100]; 239 | int len = sprintf(tmp, ":%lld\r\n", num); 240 | client->send(tmp, len); 241 | } 242 | -------------------------------------------------------------------------------- /src/RedisWaitReply.h: -------------------------------------------------------------------------------- 1 | #ifndef _REDIS_WAIT_REPLY_H 2 | #define _REDIS_WAIT_REPLY_H 3 | 4 | #include "BaseWaitReply.h" 5 | 6 | class RedisSingleWaitReply : public BaseWaitReply 7 | { 8 | public: 9 | RedisSingleWaitReply(const ClientSession::PTR& client); 10 | 11 | private: 12 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 13 | virtual void mergeAndSend(const ClientSession::PTR&) override; 14 | }; 15 | 16 | class RedisStatusReply : public BaseWaitReply 17 | { 18 | public: 19 | RedisStatusReply(const ClientSession::PTR& client, const char* status); 20 | 21 | private: 22 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 23 | virtual void mergeAndSend(const ClientSession::PTR&) override; 24 | 25 | private: 26 | const std::string mStatus; 27 | }; 28 | 29 | class RedisErrorReply : public BaseWaitReply 30 | { 31 | public: 32 | RedisErrorReply(const ClientSession::PTR& client, const char* error); 33 | 34 | private: 35 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 36 | virtual void mergeAndSend(const ClientSession::PTR&) override; 37 | 38 | private: 39 | const std::string mErrorCode; 40 | }; 41 | 42 | class RedisWrongTypeReply : public BaseWaitReply 43 | { 44 | public: 45 | RedisWrongTypeReply(const ClientSession::PTR& client, const char* wrongType, const char* detail); 46 | 47 | private: 48 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 49 | virtual void mergeAndSend(const ClientSession::PTR&) override; 50 | 51 | private: 52 | const std::string mWrongType; 53 | const std::string mWrongDetail; 54 | }; 55 | 56 | class RedisMgetWaitReply : public BaseWaitReply 57 | { 58 | public: 59 | RedisMgetWaitReply(const ClientSession::PTR& client); 60 | 61 | private: 62 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 63 | virtual void mergeAndSend(const ClientSession::PTR&) override; 64 | }; 65 | 66 | class RedisMsetWaitReply : public BaseWaitReply 67 | { 68 | public: 69 | RedisMsetWaitReply(const ClientSession::PTR& client); 70 | 71 | private: 72 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 73 | virtual void mergeAndSend(const ClientSession::PTR&) override; 74 | }; 75 | 76 | class RedisDelWaitReply : public BaseWaitReply 77 | { 78 | public: 79 | RedisDelWaitReply(const ClientSession::PTR& client); 80 | 81 | private: 82 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 83 | virtual void mergeAndSend(const ClientSession::PTR&) override; 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/SSDBWaitReply.cpp: -------------------------------------------------------------------------------- 1 | #include "SSDBWaitReply.h" 2 | 3 | #include 4 | 5 | #include "Client.h" 6 | #include "protocol/SSDBProtocol.h" 7 | 8 | static const std::string SSDB_OK = "ok"; 9 | static const std::string SSDB_ERROR = "error"; 10 | 11 | static void syncSSDBStrList(const ClientSession::PTR& client, const std::vector& strList) 12 | { 13 | SSDBProtocolRequest& strsResponse = client->getCacheSSDBProtocol(); 14 | strsResponse.init(); 15 | 16 | strsResponse.writev(strList); 17 | strsResponse.endl(); 18 | 19 | client->send(strsResponse.getResult(), strsResponse.getResultLen()); 20 | } 21 | 22 | StrListSSDBReply::StrListSSDBReply(const ClientSession::PTR& client) 23 | : BaseWaitReply(client) 24 | { 25 | } 26 | 27 | void StrListSSDBReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) 28 | { 29 | } 30 | 31 | void StrListSSDBReply::mergeAndSend(const ClientSession::PTR& client) 32 | { 33 | mStrListResponse.endl(); 34 | client->send(mStrListResponse.getResult(), mStrListResponse.getResultLen()); 35 | } 36 | 37 | void StrListSSDBReply::pushStr(std::string&& str) 38 | { 39 | mStrListResponse.writev(std::move(str)); 40 | } 41 | 42 | void StrListSSDBReply::pushStr(const std::string& str) 43 | { 44 | mStrListResponse.writev(str); 45 | } 46 | 47 | void StrListSSDBReply::pushStr(const char* str) 48 | { 49 | mStrListResponse.appendStr(str); 50 | } 51 | 52 | SSDBSingleWaitReply::SSDBSingleWaitReply(const ClientSession::PTR& client) 53 | : BaseWaitReply(client) 54 | { 55 | } 56 | 57 | void SSDBSingleWaitReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR& msg) 58 | { 59 | for (auto& v : mWaitResponses) 60 | { 61 | if (v.dbServerSocket == dbServerSocket) 62 | { 63 | v.responseBinary = std::move(msg->responseMemory); 64 | break; 65 | } 66 | } 67 | } 68 | 69 | void SSDBSingleWaitReply::mergeAndSend(const ClientSession::PTR& client) 70 | { 71 | if (!mErrorCode.empty()) 72 | { 73 | syncSSDBStrList(client, {SSDB_ERROR, mErrorCode}); 74 | } 75 | else 76 | { 77 | client->send(mWaitResponses.front().responseBinary); 78 | } 79 | } 80 | 81 | SSDBMultiSetWaitReply::SSDBMultiSetWaitReply(const ClientSession::PTR& client) 82 | : BaseWaitReply(client) 83 | { 84 | } 85 | 86 | void SSDBMultiSetWaitReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR& msg) 87 | { 88 | for (auto& v : mWaitResponses) 89 | { 90 | if (v.dbServerSocket != dbServerSocket) 91 | { 92 | continue; 93 | } 94 | 95 | v.responseBinary = std::move(msg->responseMemory); 96 | 97 | if (mWaitResponses.size() != 1) 98 | { 99 | v.ssdbReply = std::make_shared(); 100 | v.ssdbReply->parse(v.responseBinary->c_str()); 101 | } 102 | break; 103 | } 104 | } 105 | 106 | void SSDBMultiSetWaitReply::mergeAndSend(const ClientSession::PTR& client) 107 | { 108 | if (!mErrorCode.empty()) 109 | { 110 | syncSSDBStrList(client, {SSDB_ERROR, mErrorCode}); 111 | return; 112 | } 113 | if (mWaitResponses.size() == 1) 114 | { 115 | client->send(mWaitResponses.front().responseBinary); 116 | return; 117 | } 118 | 119 | std::shared_ptr errorReply = nullptr; 120 | int64_t num = 0; 121 | 122 | for (auto& v : mWaitResponses) 123 | { 124 | int64_t tmp; 125 | if (read_int64(v.ssdbReply.get(), tmp).ok()) 126 | { 127 | num += tmp; 128 | } 129 | else 130 | { 131 | errorReply = std::move(v.responseBinary); 132 | break; 133 | } 134 | } 135 | 136 | if (errorReply != nullptr) 137 | { 138 | client->send(errorReply); 139 | } 140 | else 141 | { 142 | SSDBProtocolRequest& response = client->getCacheSSDBProtocol(); 143 | response.init(); 144 | 145 | response.writev(SSDB_OK, num); 146 | response.endl(); 147 | client->send(response.getResult(), response.getResultLen()); 148 | } 149 | } 150 | 151 | SSDBMultiGetWaitReply::SSDBMultiGetWaitReply(const ClientSession::PTR& client) 152 | : BaseWaitReply(client) 153 | { 154 | } 155 | 156 | void SSDBMultiGetWaitReply::onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR& msg) 157 | { 158 | for (auto& v : mWaitResponses) 159 | { 160 | if (v.dbServerSocket != dbServerSocket) 161 | { 162 | continue; 163 | } 164 | 165 | v.responseBinary = std::move(msg->responseMemory); 166 | 167 | if (mWaitResponses.size() != 1) 168 | { 169 | v.ssdbReply = std::make_shared(); 170 | v.ssdbReply->parse(v.responseBinary->c_str()); 171 | } 172 | break; 173 | } 174 | } 175 | 176 | void SSDBMultiGetWaitReply::mergeAndSend(const ClientSession::PTR& client) 177 | { 178 | if (!mErrorCode.empty()) 179 | { 180 | syncSSDBStrList(client, {SSDB_ERROR, mErrorCode}); 181 | return; 182 | } 183 | if (mWaitResponses.size() == 1) 184 | { 185 | client->send(mWaitResponses.front().responseBinary); 186 | return; 187 | } 188 | 189 | std::shared_ptr errorReply = nullptr; 190 | std::vector kvs; 191 | 192 | for (auto& v : mWaitResponses) 193 | { 194 | if (!read_bytes(v.ssdbReply.get(), kvs).ok()) 195 | { 196 | errorReply = std::move(v.responseBinary); 197 | break; 198 | } 199 | } 200 | 201 | if (errorReply != nullptr) 202 | { 203 | client->send(std::move(errorReply)); 204 | } 205 | else 206 | { 207 | SSDBProtocolRequest& strsResponse = client->getCacheSSDBProtocol(); 208 | strsResponse.init(); 209 | 210 | strsResponse.writev(SSDB_OK); 211 | for (auto& v : kvs) 212 | { 213 | strsResponse.appendStr(v.buffer, v.len); 214 | } 215 | 216 | strsResponse.endl(); 217 | client->send(strsResponse.getResult(), strsResponse.getResultLen()); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/SSDBWaitReply.h: -------------------------------------------------------------------------------- 1 | #ifndef _SSDB_WAIT_REPLY_H 2 | #define _SSDB_WAIT_REPLY_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "BaseWaitReply.h" 9 | 10 | class StrListSSDBReply : public BaseWaitReply 11 | { 12 | public: 13 | StrListSSDBReply(const ClientSession::PTR& client); 14 | void pushStr(std::string&& str); 15 | void pushStr(const std::string& str); 16 | void pushStr(const char* str); 17 | 18 | private: 19 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 20 | virtual void mergeAndSend(const ClientSession::PTR&) override; 21 | 22 | private: 23 | SSDBProtocolRequest mStrListResponse; 24 | }; 25 | 26 | class SSDBSingleWaitReply : public BaseWaitReply 27 | { 28 | public: 29 | SSDBSingleWaitReply(const ClientSession::PTR& client); 30 | 31 | private: 32 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 33 | virtual void mergeAndSend(const ClientSession::PTR&) override; 34 | }; 35 | 36 | class SSDBMultiSetWaitReply : public BaseWaitReply 37 | { 38 | public: 39 | SSDBMultiSetWaitReply(const ClientSession::PTR& client); 40 | 41 | private: 42 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 43 | virtual void mergeAndSend(const ClientSession::PTR&) override; 44 | }; 45 | 46 | class SSDBMultiGetWaitReply : public BaseWaitReply 47 | { 48 | public: 49 | SSDBMultiGetWaitReply(const ClientSession::PTR& client); 50 | 51 | private: 52 | virtual void onBackendReply(brynet::net::TcpConnection::Ptr dbServerSocket, const BackendParseMsg::PTR&) override; 53 | virtual void mergeAndSend(const ClientSession::PTR&) override; 54 | }; 55 | 56 | typedef SSDBMultiSetWaitReply SSDBMultiDelWaitReply; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/defer.h: -------------------------------------------------------------------------------- 1 | #ifndef _DBPROXY_DEFER_H 2 | #define _DBPROXY_DEFER_H 3 | 4 | template 5 | struct privDefer { 6 | F f; 7 | privDefer(F f) 8 | : f(std::move(f)) 9 | {} 10 | ~privDefer() 11 | { 12 | f(); 13 | } 14 | }; 15 | 16 | template 17 | privDefer defer_func(F f) 18 | { 19 | return privDefer(std::move(f)); 20 | } 21 | 22 | #define DEFER_1(x, y) x##y 23 | #define DEFER_2(x, y) DEFER_1(x, y) 24 | #define DEFER_3(x) DEFER_2(x, __COUNTER__) 25 | #define defer(code) auto DEFER_3(_defer_) = defer_func([&]() { \ 26 | code; \ 27 | }) 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/protocol/RedisParse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) <2015> 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | */ 17 | 18 | #ifndef __PROTOCOL_H 19 | #define __PROTOCOL_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define REDIS_REPLY_STRING 1 28 | #define REDIS_REPLY_ARRAY 2 29 | #define REDIS_REPLY_INTEGER 3 30 | #define REDIS_REPLY_NIL 4 31 | #define REDIS_REPLY_STATUS 5 32 | #define REDIS_REPLY_ERROR 6 33 | 34 | 35 | #define REDIS_OK 0 36 | #define REDIS_RETRY -1 37 | #define REDIS_ERR -2 38 | 39 | struct redisReply { 40 | int32_t type; /* REDIS_REPLY_* */ 41 | int64_t integer; /* The integer when type is REDIS_REPLY_INTEGER */ 42 | int32_t len; /* Length of string */ 43 | char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ 44 | size_t elements;/* number of elements, for REDIS_REPLY_ARRAY */ 45 | redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ 46 | }; 47 | 48 | 49 | 50 | #define SIZE_TMP_BUFF 512 //size must enough for status/error string 51 | 52 | struct parse_tree { 53 | redisReply *reply; 54 | parse_tree **childs; 55 | size_t want; 56 | size_t pos; 57 | char type; 58 | char break_; 59 | char tmp_buff[SIZE_TMP_BUFF]; 60 | }; 61 | 62 | #define IS_NUM(CC) (CC >= '0' && CC <= '9') 63 | 64 | #define PARSE_SET(FIELD) \ 65 | reply->FIELD = (reply->FIELD*10)+(c - '0'); \ 66 | 67 | #define PARSE_NUM(FIELD) ({\ 68 | int ret = 0; \ 69 | do{ \ 70 | if(IS_NUM(c)) { \ 71 | reply->FIELD = (reply->FIELD*10)+(c - '0'); \ 72 | ret = 1; \ 73 | } \ 74 | }while(0);ret; }) 75 | 76 | static int32_t parse_string(parse_tree *current, char **str, char *end) { 77 | char c, termi; 78 | redisReply *reply = current->reply; 79 | if (!reply->str) reply->str = current->tmp_buff; 80 | if (current->want) { 81 | for (c = **str; *str != end && current->want; ++(*str), c = **str, --current->want) 82 | if (current->want > 2) reply->str[current->pos++] = c;//½áβµÄ\r\n²»ÐèÒª 83 | if (current->want) return REDIS_RETRY; 84 | } 85 | else { 86 | for (;;) { 87 | termi = current->break_; 88 | for (c = **str; *str != end && c != termi; ++(*str), c = **str) 89 | reply->str[current->pos++] = c; 90 | if (*str == end) return REDIS_RETRY; 91 | ++(*str); 92 | if (termi == '\n') break; 93 | else current->break_ = '\n'; 94 | } 95 | reply->len = current->pos; 96 | } 97 | assert(reply->len == current->pos); 98 | reply->str[current->pos] = 0; 99 | return REDIS_OK; 100 | } 101 | 102 | static int32_t parse_integer(parse_tree *current, char **str, char *end) { 103 | char c, termi; 104 | redisReply *reply = current->reply; 105 | for (;;) { 106 | termi = current->break_; 107 | for (c = **str; *str != end && c != termi; ++(*str), c = **str) 108 | if (c == '-') current->want = -1; 109 | else if (IS_NUM(c)) 110 | { 111 | PARSE_SET(integer); 112 | } 113 | else 114 | return REDIS_ERR; 115 | if (*str == end) return REDIS_RETRY; 116 | ++(*str); 117 | if (termi == '\n') break; 118 | else current->break_ = '\n'; 119 | } 120 | reply->integer *= current->want; 121 | return REDIS_OK; 122 | } 123 | 124 | static int32_t parse_breply(parse_tree *current, char **str, char *end) { 125 | char c, termi; 126 | redisReply *reply = current->reply; 127 | if (!current->want) { 128 | for (;;) { 129 | termi = current->break_; 130 | for (c = **str; *str != end && c != termi; ++(*str), c = **str) 131 | if (c == '-') reply->type = REDIS_REPLY_NIL; 132 | else if (IS_NUM(c)) 133 | { 134 | PARSE_SET(len); 135 | } 136 | else 137 | { 138 | return REDIS_ERR; 139 | } 140 | 141 | if (*str == end) return REDIS_RETRY; 142 | ++(*str); 143 | if (termi == '\n') { 144 | current->break_ = '\r'; 145 | break; 146 | } 147 | else current->break_ = '\n'; 148 | }; 149 | if (reply->type == REDIS_REPLY_NIL) return REDIS_OK; 150 | current->want = reply->len + 2;//¼ÓÉÏ\r\n 151 | } 152 | 153 | if (!reply->str && reply->len + 1 > SIZE_TMP_BUFF) 154 | reply->str = (char*)calloc(1, reply->len + 1); 155 | return parse_string(current, str, end); 156 | } 157 | 158 | static parse_tree *parse_tree_new() { 159 | parse_tree *tree = (parse_tree *)calloc(1, sizeof(*tree)); 160 | tree->reply = (redisReply *)calloc(1, sizeof(*tree->reply)); 161 | tree->break_ = '\r'; 162 | return tree; 163 | } 164 | 165 | static void parse_tree_del(parse_tree *tree) { 166 | size_t i; 167 | if (tree->childs) { 168 | for (i = 0; i < tree->want; ++i) 169 | parse_tree_del(tree->childs[i]); 170 | free(tree->childs); 171 | free(tree->reply->element); 172 | } 173 | if (tree->reply->str != tree->tmp_buff) free(tree->reply->str); 174 | free(tree->reply); 175 | free(tree); 176 | } 177 | 178 | static int32_t parse(parse_tree *current, char **str, char *end); 179 | 180 | static int32_t parse_mbreply(parse_tree *current, char **str, char *end) { 181 | size_t i; 182 | int32_t ret; 183 | char c, termi; 184 | redisReply *reply = current->reply; 185 | if (!current->want) { 186 | for (;;) { 187 | termi = current->break_; 188 | for (c = **str; *str != end && c != termi; ++(*str), c = **str) 189 | if (c == '-') reply->type = REDIS_REPLY_NIL; 190 | else if (IS_NUM(c)) 191 | { 192 | PARSE_SET(elements); 193 | } 194 | else 195 | { 196 | return REDIS_ERR; 197 | } 198 | 199 | if (*str == end) return REDIS_RETRY; 200 | ++(*str); 201 | if (termi == '\n'){ 202 | current->break_ = '\r'; 203 | break; 204 | } 205 | else current->break_ = '\n'; 206 | }; 207 | current->want = reply->elements; 208 | } 209 | 210 | if (current->want > 0 && !current->childs) { 211 | current->childs = (parse_tree**)calloc(current->want, sizeof(*current->childs)); 212 | reply->element = (redisReply**)calloc(current->want, sizeof(*reply->element)); 213 | for (i = 0; i < current->want; ++i){ 214 | current->childs[i] = parse_tree_new(); 215 | reply->element[i] = current->childs[i]->reply; 216 | } 217 | } 218 | 219 | for (; current->pos < current->want; ++current->pos) { 220 | if ((*str) == end) return REDIS_RETRY; 221 | if (REDIS_OK != (ret = parse(current->childs[current->pos], str, end))) 222 | return ret; 223 | } 224 | return REDIS_OK; 225 | } 226 | 227 | #define IS_OP_CODE(CC)\ 228 | (CC == '+' || CC == '-' || CC == ':' || CC == '$' || CC == '*') 229 | 230 | static int32_t parse(parse_tree *current, char **str, char *end) { 231 | int32_t ret = REDIS_RETRY; 232 | redisReply *reply = current->reply; 233 | if (!current->type) { 234 | char c = *(*str)++; 235 | if (IS_OP_CODE(c)) current->type = c; 236 | else return REDIS_ERR; 237 | } 238 | switch (current->type) { 239 | case '+':{ 240 | if (!reply->type) reply->type = REDIS_REPLY_STATUS; 241 | ret = parse_string(current, str, end); 242 | break; 243 | } 244 | case '-':{ 245 | if (!reply->type) reply->type = REDIS_REPLY_ERROR; 246 | ret = parse_string(current, str, end); 247 | break; 248 | } 249 | case ':':{ 250 | if (!reply->type) reply->type = REDIS_REPLY_INTEGER; 251 | current->want = 1; 252 | ret = parse_integer(current, str, end); 253 | break; 254 | } 255 | case '$':{ 256 | if (!reply->type) reply->type = REDIS_REPLY_STRING; 257 | ret = parse_breply(current, str, end); 258 | break; 259 | } 260 | case '*':{ 261 | if (!reply->type) reply->type = REDIS_REPLY_ARRAY; 262 | ret = parse_mbreply(current, str, end); 263 | break; 264 | } 265 | default: 266 | return REDIS_ERR; 267 | } 268 | return ret; 269 | } 270 | 271 | 272 | static inline size_t digitcount(uint32_t num) { 273 | if (num < 10) return 1; 274 | else if (num < 100) return 2; 275 | else if (num < 1000) return 3; 276 | else if (num < 10000) return 4; 277 | else if (num < 100000) return 5; 278 | else if (num < 1000000) return 6; 279 | else if (num < 10000000) return 7; 280 | else if (num < 100000000) return 8; 281 | else if (num < 1000000000) return 9; 282 | else return 10; 283 | } 284 | 285 | static inline void u2s(uint32_t num, char **ptr) { 286 | char *tmp = *ptr + digitcount(num); 287 | do { 288 | *--tmp = '0' + (char)(num % 10); 289 | (*ptr)++; 290 | } while (num /= 10); 291 | } 292 | 293 | 294 | #endif 295 | -------------------------------------------------------------------------------- /src/protocol/RedisRequest.h: -------------------------------------------------------------------------------- 1 | #ifndef _REDIS_PROTOCOL_H 2 | #define _REDIS_PROTOCOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | class RedisProtocolRequest final 13 | { 14 | public: 15 | RedisProtocolRequest() = default; 16 | 17 | void init() 18 | { 19 | mArgc = 0; 20 | mResult.clear(); 21 | } 22 | 23 | template 24 | void writev(const Arg1& arg1, const Args&... args) 25 | { 26 | this->operator<<(arg1); 27 | writev(args...); 28 | } 29 | 30 | void endl() 31 | { 32 | std::string tmp; 33 | tmp.push_back('*'); 34 | tmp += std::to_string(mArgc); 35 | tmp += "\r\n"; 36 | tmp += mResult; 37 | mResult = std::move(tmp); 38 | } 39 | 40 | void appendBinary(const char* buffer, size_t len) 41 | { 42 | addStr(buffer, len); 43 | } 44 | 45 | void writev() 46 | { 47 | } 48 | 49 | const char* getResult() const 50 | { 51 | return mResult.c_str(); 52 | } 53 | 54 | size_t getResultLen() const 55 | { 56 | return mResult.size(); 57 | } 58 | 59 | private: 60 | RedisProtocolRequest& operator<<(const std::vector& keys) 61 | { 62 | for (const auto& v : keys) 63 | { 64 | addStr(v); 65 | } 66 | return *this; 67 | } 68 | 69 | RedisProtocolRequest& operator<<(const std::unordered_map& kvs) 70 | { 71 | for (const auto& [k, v] : kvs) 72 | { 73 | addStr(k); 74 | addStr(v); 75 | } 76 | return *this; 77 | } 78 | 79 | RedisProtocolRequest& operator<<(int64_t v) 80 | { 81 | addStr(std::to_string(v)); 82 | return *this; 83 | } 84 | 85 | RedisProtocolRequest& operator<<(const char* const v) 86 | { 87 | addStr(v, strlen(v)); 88 | return *this; 89 | } 90 | 91 | RedisProtocolRequest& operator<<(const std::string& v) 92 | { 93 | addStr(v); 94 | return *this; 95 | } 96 | 97 | private: 98 | void addStr(const std::string& arg) 99 | { 100 | addStr(arg.c_str(), arg.size()); 101 | } 102 | 103 | void addStr(const char* buffer, size_t len) 104 | { 105 | mResult.push_back('$'); 106 | mResult += (std::to_string(len)); 107 | mResult += "\r\n"; 108 | mResult.append(buffer, len); 109 | mResult += "\r\n"; 110 | mArgc += 1; 111 | } 112 | 113 | private: 114 | int mArgc = {0}; 115 | std::string mResult; 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /src/protocol/SSDBProtocol.cpp: -------------------------------------------------------------------------------- 1 | #include "SSDBProtocol.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #if defined PLATFORM_WINDOWS 11 | #define snprintf _snprintf 12 | #endif 13 | 14 | using namespace brynet::base; 15 | 16 | Status::Status() 17 | : mCacheStatus(STATUS_TYPE::STATUS_NONE) 18 | { 19 | } 20 | 21 | Status::Status(const std::string& code) 22 | : mCode(code), 23 | mCacheStatus(STATUS_TYPE::STATUS_NONE) 24 | { 25 | cacheCodeType(); 26 | } 27 | 28 | Status::Status(std::string&& code) 29 | : mCode(std::move(code)), 30 | mCacheStatus(STATUS_TYPE::STATUS_NONE) 31 | { 32 | cacheCodeType(); 33 | } 34 | 35 | Status::Status(Status&& s) noexcept 36 | : mCode(std::move(s.mCode)), 37 | mCacheStatus(s.mCacheStatus) 38 | { 39 | } 40 | 41 | Status& Status::operator=(Status&& s) noexcept 42 | { 43 | if (this != &s) 44 | { 45 | mCode = std::move(s.mCode); 46 | mCacheStatus = s.mCacheStatus; 47 | } 48 | 49 | return *this; 50 | } 51 | 52 | void Status::cacheCodeType() 53 | { 54 | if (mCacheStatus == STATUS_TYPE::STATUS_NONE) 55 | { 56 | if (mCode == "ok") 57 | { 58 | mCacheStatus = STATUS_TYPE::STATUS_OK; 59 | } 60 | else if (mCode == "not_found") 61 | { 62 | mCacheStatus = STATUS_TYPE::STATUS_NOTFOUND; 63 | } 64 | else 65 | { 66 | mCacheStatus = STATUS_TYPE::STATUS_ERROR; 67 | } 68 | } 69 | } 70 | 71 | bool Status::not_found() const 72 | { 73 | return mCacheStatus == STATUS_TYPE::STATUS_NOTFOUND; 74 | } 75 | 76 | bool Status::ok() const 77 | { 78 | return mCacheStatus == STATUS_TYPE::STATUS_OK; 79 | } 80 | 81 | bool Status::error() const 82 | { 83 | return mCacheStatus == STATUS_TYPE::STATUS_ERROR; 84 | } 85 | 86 | const std::string& Status::code() const 87 | { 88 | return mCode; 89 | } 90 | 91 | SSDBProtocolRequest::SSDBProtocolRequest() 92 | { 93 | m_request = brynet::base::buffer_new(DEFAULT_SSDBPROTOCOL_LEN); 94 | } 95 | 96 | SSDBProtocolRequest::~SSDBProtocolRequest() 97 | { 98 | buffer_delete(m_request); 99 | m_request = nullptr; 100 | } 101 | 102 | void SSDBProtocolRequest::appendStr(const char* str) 103 | { 104 | size_t len = strlen(str); 105 | char lenstr[16]; 106 | int num = snprintf(lenstr, sizeof(len), "%lld\n", len); 107 | appendBlock(lenstr, num); 108 | appendBlock(str, len); 109 | appendBlock("\n", 1); 110 | } 111 | 112 | void SSDBProtocolRequest::appendStr(const char* str, size_t len) 113 | { 114 | char lenstr[16]; 115 | int num = snprintf(lenstr, sizeof(len), "%lld\n", len); 116 | appendBlock(lenstr, num); 117 | appendBlock(str, len); 118 | appendBlock("\n", 1); 119 | } 120 | 121 | void SSDBProtocolRequest::appendInt64(int64_t val) 122 | { 123 | char str[30]; 124 | snprintf(str, sizeof(str), "%lld", val); 125 | appendStr(str); 126 | } 127 | 128 | void SSDBProtocolRequest::appendStr(const std::string& str) 129 | { 130 | char len[16]; 131 | int num = snprintf(len, sizeof(len), "%lld\n", str.size()); 132 | appendBlock(len, num); 133 | appendBlock(str.c_str(), str.length()); 134 | appendBlock("\n", 1); 135 | } 136 | 137 | void SSDBProtocolRequest::endl() 138 | { 139 | appendBlock("\n", 1); 140 | } 141 | 142 | void SSDBProtocolRequest::appendBlock(const char* data, size_t len) 143 | { 144 | if (buffer_getwritevalidcount(m_request) < len) 145 | { 146 | brynet::base::buffer_s* temp = buffer_new(buffer_getsize(m_request) + len); 147 | memcpy(buffer_getwriteptr(temp), buffer_getreadptr(m_request), buffer_getreadvalidcount(m_request)); 148 | buffer_addwritepos(temp, buffer_getreadvalidcount(m_request)); 149 | buffer_delete(m_request); 150 | m_request = temp; 151 | } 152 | 153 | buffer_write(m_request, data, len); 154 | } 155 | 156 | const char* SSDBProtocolRequest::getResult() 157 | { 158 | return buffer_getreadptr(m_request); 159 | } 160 | 161 | size_t SSDBProtocolRequest::getResultLen() 162 | { 163 | return buffer_getreadvalidcount(m_request); 164 | } 165 | 166 | void SSDBProtocolRequest::init() 167 | { 168 | buffer_init(m_request); 169 | } 170 | 171 | SSDBProtocolResponse::~SSDBProtocolResponse() 172 | { 173 | } 174 | 175 | void SSDBProtocolResponse::init() 176 | { 177 | mBuffers.clear(); 178 | } 179 | 180 | void SSDBProtocolResponse::parse(const char* buffer) 181 | { 182 | const char* current = buffer; 183 | while (true) 184 | { 185 | char* temp; 186 | int datasize = strtol(current, &temp, 10); 187 | current = temp; 188 | current += 1; 189 | Bytes tmp = {current, datasize}; 190 | mBuffers.push_back(tmp); 191 | current += datasize; 192 | 193 | current += 1; 194 | 195 | if (*current == '\n') 196 | { 197 | current += 1; 198 | break; 199 | } 200 | } 201 | } 202 | 203 | Bytes* SSDBProtocolResponse::getByIndex(size_t index) 204 | { 205 | if (mBuffers.size() > index) 206 | { 207 | return &mBuffers[index]; 208 | } 209 | else 210 | { 211 | const static char* nullstr = "null"; 212 | static Bytes nullbuffer = {nullstr, strlen(nullstr) + 1}; 213 | return &nullbuffer; 214 | } 215 | } 216 | 217 | void SSDBProtocolResponse::pushByte(const char* buffer, size_t len) 218 | { 219 | Bytes tmp = {buffer, len}; 220 | mBuffers.push_back(tmp); 221 | } 222 | 223 | size_t SSDBProtocolResponse::getBuffersLen() const 224 | { 225 | return mBuffers.size(); 226 | } 227 | 228 | Status SSDBProtocolResponse::getStatus() 229 | { 230 | if (mBuffers.empty()) 231 | { 232 | return Status("error"); 233 | } 234 | 235 | return std::string(mBuffers[0].buffer, mBuffers[0].len); 236 | } 237 | 238 | int SSDBProtocolResponse::check_ssdb_packet(const char* buffer, size_t len) 239 | { 240 | const char* end = buffer + len; 241 | const char* current = buffer; 242 | 243 | while (true) 244 | { 245 | char* temp; 246 | int datasize = strtol(current, &temp, 10); 247 | if (datasize == 0 && temp == current) 248 | { 249 | break; 250 | } 251 | current = temp; 252 | 253 | if (current >= end || *current != '\n') 254 | { 255 | break; 256 | } 257 | current += 1; 258 | current += datasize; 259 | 260 | if (current >= end || *current != '\n') 261 | { 262 | break; 263 | } 264 | 265 | current += 1; 266 | 267 | if (current >= end) 268 | { 269 | break; 270 | } 271 | else if (*current == '\n') 272 | { 273 | current += 1; 274 | return (current - buffer); 275 | } 276 | } 277 | 278 | return 0; 279 | } 280 | 281 | Status read_bytes(SSDBProtocolResponse* response, std::vector& ret) 282 | { 283 | Status status = response->getStatus(); 284 | if (status.ok()) 285 | { 286 | for (size_t i = 1; i < response->getBuffersLen(); ++i) 287 | { 288 | ret.push_back(*(response->getByIndex(i))); 289 | } 290 | } 291 | 292 | return status; 293 | } 294 | 295 | Status read_list(SSDBProtocolResponse* response, std::vector& ret) 296 | { 297 | Status status = response->getStatus(); 298 | if (status.ok()) 299 | { 300 | for (size_t i = 1; i < response->getBuffersLen(); ++i) 301 | { 302 | Bytes* buffer = response->getByIndex(i); 303 | ret.emplace_back(std::string(buffer->buffer, buffer->len)); 304 | } 305 | } 306 | 307 | return status; 308 | } 309 | 310 | Status read_int64(SSDBProtocolResponse* response, int64_t& ret) 311 | { 312 | Status status = response->getStatus(); 313 | if (status.ok()) 314 | { 315 | if (response->getBuffersLen() >= 2) 316 | { 317 | Bytes* buf = response->getByIndex(1); 318 | std::string temp(buf->buffer, buf->len); 319 | (void) sscanf(temp.c_str(), "%lld", &ret); 320 | } 321 | else 322 | { 323 | status = Status("server_error"); 324 | } 325 | } 326 | 327 | return status; 328 | } 329 | 330 | Status read_byte(SSDBProtocolResponse* response, Bytes& ret) 331 | { 332 | Status status = response->getStatus(); 333 | if (status.ok()) 334 | { 335 | if (response->getBuffersLen() >= 2) 336 | { 337 | ret = *(response->getByIndex(1)); 338 | } 339 | else 340 | { 341 | status = Status("server_error"); 342 | } 343 | } 344 | 345 | return status; 346 | } 347 | 348 | Status read_str(SSDBProtocolResponse* response, std::string& ret) 349 | { 350 | Status status = response->getStatus(); 351 | if (status.ok()) 352 | { 353 | if (response->getBuffersLen() >= 2) 354 | { 355 | Bytes* buf = response->getByIndex(1); 356 | ret = std::string(buf->buffer, buf->len); 357 | } 358 | else 359 | { 360 | status = Status("server_error"); 361 | } 362 | } 363 | 364 | return status; 365 | } 366 | -------------------------------------------------------------------------------- /src/protocol/SSDBProtocol.h: -------------------------------------------------------------------------------- 1 | #ifndef _SSDB_PROTOCOL_H 2 | #define _SSDB_PROTOCOL_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | constexpr int DEFAULT_SSDBPROTOCOL_LEN = 1024; 13 | 14 | struct buffer_s; 15 | 16 | class Status 17 | { 18 | enum class STATUS_TYPE 19 | { 20 | STATUS_NONE, 21 | STATUS_OK, 22 | STATUS_NOTFOUND, 23 | STATUS_ERROR, 24 | }; 25 | 26 | public: 27 | Status(); 28 | Status(std::string&&); 29 | Status(const std::string& code); 30 | Status(Status&&) noexcept; 31 | 32 | Status& operator=(Status&&) noexcept; 33 | 34 | bool not_found() const; 35 | bool ok() const; 36 | bool error() const; 37 | 38 | const std::string& code() const; 39 | 40 | private: 41 | void cacheCodeType(); 42 | 43 | private: 44 | std::string mCode; 45 | STATUS_TYPE mCacheStatus; 46 | }; 47 | 48 | class SSDBProtocolRequest : private brynet::base::NonCopyable 49 | { 50 | public: 51 | SSDBProtocolRequest(); 52 | 53 | virtual ~SSDBProtocolRequest(); 54 | 55 | void appendStr(const char* str); 56 | void appendStr(const char* str, size_t len); 57 | void appendInt64(int64_t val); 58 | void appendStr(const std::string& str); 59 | 60 | void endl(); 61 | 62 | const char* getResult(); 63 | size_t getResultLen(); 64 | 65 | void init(); 66 | 67 | template 68 | void writev(const Arg1& arg1, const Args&... args) 69 | { 70 | this->operator<<(arg1); 71 | writev(args...); 72 | } 73 | 74 | void writev() 75 | { 76 | } 77 | 78 | private: 79 | void appendBlock(const char* data, size_t len); 80 | 81 | SSDBProtocolRequest& operator<<(const std::vector& keys) 82 | { 83 | for (auto& v : keys) 84 | { 85 | appendStr(v); 86 | } 87 | return *this; 88 | } 89 | 90 | SSDBProtocolRequest& operator<<(const std::unordered_map& kvs) 91 | { 92 | for (const auto& [k, v] : kvs) 93 | { 94 | appendStr(k); 95 | appendStr(v); 96 | } 97 | return *this; 98 | } 99 | 100 | SSDBProtocolRequest& operator<<(int64_t v) 101 | { 102 | appendInt64(v); 103 | return *this; 104 | } 105 | 106 | SSDBProtocolRequest& operator<<(const char* const v) 107 | { 108 | appendStr(v); 109 | return *this; 110 | } 111 | 112 | SSDBProtocolRequest& operator<<(const std::string& v) 113 | { 114 | appendStr(v); 115 | return *this; 116 | } 117 | 118 | private: 119 | struct brynet::base::buffer_s* m_request = nullptr; 120 | }; 121 | 122 | struct Bytes { 123 | const char* buffer = nullptr; 124 | int len = 0; 125 | }; 126 | 127 | class SSDBProtocolResponse 128 | { 129 | public: 130 | ~SSDBProtocolResponse(); 131 | 132 | void init(); 133 | 134 | void parse(const char* buffer); 135 | Bytes* getByIndex(size_t index); 136 | void pushByte(const char* buffer, size_t len); 137 | 138 | size_t getBuffersLen() const; 139 | 140 | Status getStatus(); 141 | 142 | static int check_ssdb_packet(const char* buffer, size_t len); 143 | 144 | private: 145 | std::vector mBuffers; 146 | }; 147 | 148 | Status read_bytes(SSDBProtocolResponse* response, std::vector& ret); 149 | Status read_list(SSDBProtocolResponse* response, std::vector& ret); 150 | Status read_int64(SSDBProtocolResponse* response, int64_t& ret); 151 | Status read_byte(SSDBProtocolResponse* response, Bytes& ret); 152 | Status read_str(SSDBProtocolResponse* response, std::string& ret); 153 | 154 | 155 | #endif 156 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dbproxy", 3 | "version-string": "0.0.1", 4 | "dependencies": [ 5 | { 6 | "name": "brynet", 7 | "version>=": "1.11.1#1" 8 | }, 9 | { 10 | "name": "lua", 11 | "version>=": "5.3.3" 12 | }, 13 | { 14 | "name": "sol2", 15 | "version>=": "3.2.2" 16 | } 17 | ], 18 | "overrides": [ 19 | { 20 | "name": "lua", 21 | "version": "5.3.3" 22 | }, 23 | { 24 | "name": "brynet", 25 | "version": "1.11.2" 26 | } 27 | ], 28 | "builtin-baseline": "81c0acae22f0b7ef251f82e184aca34d2f5bd949" 29 | } --------------------------------------------------------------------------------