// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // A brpc based redis-server. Currently just implement set and // get, but it's sufficient that you can get the idea how to // implement brpc::RedisCommandHandler. #include <brpc/server.h> #include <brpc/redis.h> #include <butil/crc32c.h> #include <butil/strings/string_split.h> #include <gflags/gflags.h> #include <unordered_map> #include <butil/time.h> DEFINE_int32(port, 6379, "TCP Port of this server"); class RedisServiceImpl : public brpc::RedisService { public: bool Set(const std::string& key, const std::string& value) { int slot = butil::crc32c::Value(key.c_str(), key.size()) % kHashSlotNum; _mutex[slot].lock(); _map[slot][key] = value; _mutex[slot].unlock(); return true; } bool Get(const std::string& key, std::string* value) { int slot = butil::crc32c::Value(key.c_str(), key.size()) % kHashSlotNum; _mutex[slot].lock(); auto it = _map[slot].find(key); if (it == _map[slot].end()) { _mutex[slot].unlock(); return false; } *value = it->second; _mutex[slot].unlock(); return true; } private: const static int kHashSlotNum = 32; std::unordered_map<std::string, std::string> _map[kHashSlotNum]; butil::Mutex _mutex[kHashSlotNum]; }; class GetCommandHandler : public brpc::RedisCommandHandler { public: explicit GetCommandHandler(RedisServiceImpl* rsimpl) : _rsimpl(rsimpl) {} brpc::RedisCommandHandlerResult Run(const std::vector<butil::StringPiece>& args, brpc::RedisReply* output, bool /*flush_batched*/) override { if (args.size() != 2ul) { output->FormatError("Expect 1 arg for 'get', actually %lu", args.size()-1); return brpc::REDIS_CMD_HANDLED; } const std::string key(args[1].data(), args[1].size()); std::string value; if (_rsimpl->Get(key, &value)) { output->SetString(value); } else { output->SetNullString(); } return brpc::REDIS_CMD_HANDLED; } private: RedisServiceImpl* _rsimpl; }; class SetCommandHandler : public brpc::RedisCommandHandler { public: explicit SetCommandHandler(RedisServiceImpl* rsimpl) : _rsimpl(rsimpl) {} brpc::RedisCommandHandlerResult Run(const std::vector<butil::StringPiece>& args, brpc::RedisReply* output, bool /*flush_batched*/) override { if (args.size() != 3ul) { output->FormatError("Expect 2 args for 'set', actually %lu", args.size()-1); return brpc::REDIS_CMD_HANDLED; } const std::string key(args[1].data(), args[1].size()); const std::string value(args[2].data(), args[2].size()); _rsimpl->Set(key, value); output->SetStatus("OK"); return brpc::REDIS_CMD_HANDLED; } private: RedisServiceImpl* _rsimpl; }; int main(int argc, char* argv[]) { google::ParseCommandLineFlags(&argc, &argv, true); RedisServiceImpl* rsimpl = new RedisServiceImpl; rsimpl->AddCommandHandler("get", new GetCommandHandler(rsimpl)); rsimpl->AddCommandHandler("set", new SetCommandHandler(rsimpl)); brpc::Server server; brpc::ServerOptions server_options; server_options.redis_service = rsimpl; if (server.Start(FLAGS_port, &server_options) != 0) { LOG(ERROR) << "Fail to start server"; return -1; } server.RunUntilAskedToQuit(); return 0; }