chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:29:29 +08:00
commit f52cf32e56
1718 changed files with 366293 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
# 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.
cmake_minimum_required(VERSION 3.16...3.28)
project(redis_c++ C CXX)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/BrpcExample.cmake)
# Install dependencies:
# With apt:
# sudo apt-get install libreadline-dev
# sudo apt-get install ncurses-dev
# With yum:
# sudo yum install readline-devel
# sudo yum install ncurses-devel
option(LINK_SO "Whether examples are linked dynamically" OFF)
execute_process(
COMMAND bash -c "find ${PROJECT_SOURCE_DIR}/../.. -type d -regex \".*output/include$\" | head -n1 | xargs dirname | tr -d '\n'"
OUTPUT_VARIABLE OUTPUT_PATH
)
if(OUTPUT_PATH)
list(PREPEND CMAKE_PREFIX_PATH ${OUTPUT_PATH})
endif()
find_package(Threads REQUIRED)
find_package(Protobuf REQUIRED)
find_path(GPERFTOOLS_INCLUDE_DIR NAMES gperftools/heap-profiler.h)
find_library(GPERFTOOLS_LIBRARIES NAMES tcmalloc_and_profiler)
# Search for libthrift* by best effort. If it is not found and brpc is
# compiled with thrift protocol enabled, a link error would be reported.
find_library(THRIFT_LIB NAMES thrift)
if (NOT THRIFT_LIB)
set(THRIFT_LIB "")
endif()
find_path(BRPC_INCLUDE_PATH NAMES brpc/server.h)
if(LINK_SO)
find_library(BRPC_LIB NAMES brpc)
else()
find_library(BRPC_LIB NAMES libbrpc.a brpc)
endif()
if((NOT BRPC_INCLUDE_PATH) OR (NOT BRPC_LIB))
message(FATAL_ERROR "Fail to find brpc")
endif()
find_path(GFLAGS_INCLUDE_PATH gflags/gflags.h)
find_library(GFLAGS_LIBRARY NAMES gflags libgflags)
if((NOT GFLAGS_INCLUDE_PATH) OR (NOT GFLAGS_LIBRARY))
message(FATAL_ERROR "Fail to find gflags")
endif()
find_path(LEVELDB_INCLUDE_PATH NAMES leveldb/db.h)
find_library(LEVELDB_LIB NAMES leveldb)
if ((NOT LEVELDB_INCLUDE_PATH) OR (NOT LEVELDB_LIB))
message(FATAL_ERROR "Fail to find leveldb")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT OPENSSL_ROOT_DIR)
set(OPENSSL_ROOT_DIR
"/usr/local/opt/openssl" # Homebrew installed OpenSSL
)
endif()
find_package(OpenSSL REQUIRED)
set(DYNAMIC_LIB
Threads::Threads
${GFLAGS_LIBRARY}
${PROTOBUF_LIBRARIES}
${LEVELDB_LIB}
${OPENSSL_CRYPTO_LIBRARY}
${OPENSSL_SSL_LIBRARY}
${THRIFT_LIB}
${GPERFTOOLS_LIBRARIES}
dl
)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(DYNAMIC_LIB ${DYNAMIC_LIB}
pthread
"-framework CoreFoundation"
"-framework CoreGraphics"
"-framework CoreData"
"-framework CoreText"
"-framework Security"
"-framework Foundation"
"-Wl,-U,_MallocExtension_ReleaseFreeMemory"
"-Wl,-U,_ProfilerStart"
"-Wl,-U,_ProfilerStop"
"-Wl,-U,__Z13GetStackTracePPvii"
"-Wl,-U,_mallctl"
"-Wl,-U,_malloc_stats_print"
)
endif()
add_executable(redis_cli redis_cli.cpp)
brpc_example_configure_target(redis_cli)
add_executable(redis_press redis_press.cpp)
brpc_example_configure_target(redis_press)
add_executable(redis_server redis_server.cpp)
brpc_example_configure_target(redis_server)
add_executable(redis_cluster_client redis_cluster_client.cpp)
brpc_example_configure_target(redis_cluster_client)
set(AUX_LIB readline ncurses)
target_link_libraries(redis_cli PRIVATE ${BRPC_LIB} ${DYNAMIC_LIB} ${AUX_LIB})
target_link_libraries(redis_press PRIVATE ${BRPC_LIB} ${DYNAMIC_LIB})
target_link_libraries(redis_server PRIVATE ${BRPC_LIB} ${DYNAMIC_LIB})
target_link_libraries(redis_cluster_client PRIVATE ${BRPC_LIB} ${DYNAMIC_LIB})
+159
View File
@@ -0,0 +1,159 @@
// 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 command-line interface to talk with redis-server
#include <signal.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <gflags/gflags.h>
#include <butil/logging.h>
#include <brpc/channel.h>
#include <brpc/redis.h>
DEFINE_string(connection_type, "", "Connection type. Available values: single, pooled, short");
DEFINE_string(server, "127.0.0.1:6379", "IP Address of server");
DEFINE_int32(timeout_ms, 1000, "RPC timeout in milliseconds");
DEFINE_int32(max_retry, 3, "Max retries(not including the first RPC)");
namespace brpc {
const char* logo();
}
// Send `command' to redis-server via `channel'
static bool access_redis(brpc::Channel& channel, const char* command) {
brpc::RedisRequest request;
if (!request.AddCommand(command)) {
LOG(ERROR) << "Fail to add command";
return false;
}
brpc::RedisResponse response;
brpc::Controller cntl;
channel.CallMethod(NULL, &cntl, &request, &response, NULL);
if (cntl.Failed()) {
LOG(ERROR) << "Fail to access redis, " << cntl.ErrorText();
return false;
} else {
std::cout << response << std::endl;
return true;
}
}
// For freeing the memory returned by readline().
struct Freer {
void operator()(char* mem) {
free(mem);
}
};
static void dummy_handler(int) {}
// The getc for readline. The default getc retries reading when meeting
// EINTR, which is not what we want.
static bool g_canceled = false;
static int cli_getc(FILE *stream) {
int c = getc(stream);
if (c == EOF && errno == EINTR) {
g_canceled = true;
return '\n';
}
return c;
}
int main(int argc, char* argv[]) {
// Parse gflags. We recommend you to use gflags as well.
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
// A Channel represents a communication line to a Server. Notice that
// Channel is thread-safe and can be shared by all threads in your program.
brpc::Channel channel;
// Initialize the channel, NULL means using default options.
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_REDIS;
options.connection_type = FLAGS_connection_type;
options.timeout_ms = FLAGS_timeout_ms/*milliseconds*/;
options.max_retry = FLAGS_max_retry;
if (channel.Init(FLAGS_server.c_str(), &options) != 0) {
LOG(ERROR) << "Fail to initialize channel";
return -1;
}
if (argc <= 1) { // interactive mode
// We need this dummy signal hander to interrupt getc (and returning
// EINTR), SIG_IGN did not work.
signal(SIGINT, dummy_handler);
// Hook getc of readline.
rl_getc_function = cli_getc;
// Print welcome information.
printf("%s\n", brpc::logo());
printf("This command-line tool mimics the look-n-feel of official "
"redis-cli, as a demostration of brpc's capability of"
" talking to redis-server. The output and behavior is "
"not exactly same with the official one.\n\n");
for (;;) {
char prompt[64];
snprintf(prompt, sizeof(prompt), "redis %s> ", FLAGS_server.c_str());
std::unique_ptr<char, Freer> command(readline(prompt));
if (command == NULL || *command == '\0') {
if (g_canceled) {
// No input after the prompt and user pressed Ctrl-C,
// quit the CLI.
return 0;
}
// User entered an empty command by just pressing Enter.
continue;
}
if (g_canceled) {
// User entered sth. and pressed Ctrl-C, start a new prompt.
g_canceled = false;
continue;
}
// Add user's command to history so that it's browse-able by
// UP-key and search-able by Ctrl-R.
add_history(command.get());
if (!strcmp(command.get(), "help")) {
printf("This is a redis CLI written in brpc.\n");
continue;
}
if (!strcmp(command.get(), "quit")) {
// Although quit is a valid redis command, it does not make
// too much sense to run it in this CLI, just quit.
return 0;
}
access_redis(channel, command.get());
}
} else {
std::string command;
command.reserve(argc * 16);
for (int i = 1; i < argc; ++i) {
if (i != 1) {
command.push_back(' ');
}
command.append(argv[i]);
}
if (!access_redis(channel, command.c_str())) {
return -1;
}
}
return 0;
}
+119
View File
@@ -0,0 +1,119 @@
// 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 basic client for native Redis Cluster using brpc::RedisClusterChannel.
#include <gflags/gflags.h>
#include <bthread/countdown_event.h>
#include <butil/logging.h>
#include <brpc/controller.h>
#include <brpc/redis.h>
#include <brpc/redis_cluster.h>
DEFINE_string(seeds, "127.0.0.1:7000,127.0.0.1:7001",
"Comma-separated redis cluster seed endpoints");
DEFINE_string(key_prefix, "brpc_cluster_demo", "Prefix for demo keys");
DEFINE_int32(timeout_ms, 1000, "RPC timeout in milliseconds");
DEFINE_int32(rpc_max_retry, 1, "Max retries for a single sub RPC");
DEFINE_int32(max_redirect, 5, "Max MOVED/ASK redirect retries");
DEFINE_int32(refresh_interval_s, 30, "Periodic topology refresh interval");
DEFINE_int32(topology_refresh_timeout_ms, 1000,
"Timeout of CLUSTER SLOTS/NODES request");
DEFINE_bool(disable_periodic_refresh, false, "Disable periodic topology refresh");
namespace {
class Done : public google::protobuf::Closure {
public:
explicit Done(bthread::CountdownEvent* event) : _event(event) {}
void Run() override { _event->signal(); }
private:
bthread::CountdownEvent* _event;
};
int PrintResponse(const brpc::RedisResponse& response) {
for (int i = 0; i < response.reply_size(); ++i) {
const brpc::RedisReply& reply = response.reply(i);
if (reply.is_error()) {
LOG(ERROR) << "reply[" << i << "] error=" << reply.error_message();
return -1;
}
LOG(INFO) << "reply[" << i << "] " << reply;
}
return 0;
}
} // namespace
int main(int argc, char* argv[]) {
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
brpc::RedisClusterChannelOptions options;
options.max_redirect = FLAGS_max_redirect;
options.refresh_interval_s = FLAGS_refresh_interval_s;
options.enable_periodic_refresh = !FLAGS_disable_periodic_refresh;
options.topology_refresh_timeout_ms = FLAGS_topology_refresh_timeout_ms;
options.channel_options.timeout_ms = FLAGS_timeout_ms;
options.channel_options.max_retry = FLAGS_rpc_max_retry;
brpc::RedisClusterChannel channel;
if (channel.Init(FLAGS_seeds, &options) != 0) {
LOG(ERROR) << "Fail to init redis cluster channel, seeds=" << FLAGS_seeds;
return -1;
}
const std::string key1 = FLAGS_key_prefix + "_1";
const std::string key2 = FLAGS_key_prefix + "_2";
// Sync pipeline.
brpc::RedisRequest request;
brpc::RedisResponse response;
brpc::Controller cntl;
CHECK(request.AddCommand("set %s v1", key1.c_str()));
CHECK(request.AddCommand("set %s v2", key2.c_str()));
CHECK(request.AddCommand("mget %s %s", key1.c_str(), key2.c_str()));
channel.CallMethod(NULL, &cntl, &request, &response, NULL);
if (cntl.Failed()) {
LOG(ERROR) << "Sync call failed: " << cntl.ErrorText();
return -1;
}
if (PrintResponse(response) != 0) {
return -1;
}
// Async single request.
brpc::RedisRequest async_request;
brpc::RedisResponse async_response;
brpc::Controller async_cntl;
CHECK(async_request.AddCommand("get %s", key1.c_str()));
bthread::CountdownEvent event(1);
Done done(&event);
channel.CallMethod(NULL, &async_cntl, &async_request, &async_response, &done);
event.wait();
if (async_cntl.Failed()) {
LOG(ERROR) << "Async call failed: " << async_cntl.ErrorText();
return -1;
}
if (PrintResponse(async_response) != 0) {
return -1;
}
LOG(INFO) << "Redis cluster demo finished";
return 0;
}
+188
View File
@@ -0,0 +1,188 @@
// 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 multi-threaded client getting keys from a redis-server constantly.
#include <gflags/gflags.h>
#include <bthread/bthread.h>
#include <butil/logging.h>
#include <butil/string_printf.h>
#include <bvar/bvar.h>
#include <brpc/channel.h>
#include <brpc/server.h>
#include <brpc/redis.h>
DEFINE_int32(thread_num, 50, "Number of threads to send requests");
DEFINE_bool(use_bthread, false, "Use bthread to send requests");
DEFINE_string(connection_type, "", "Connection type. Available values: single, pooled, short");
DEFINE_string(server, "0.0.0.0:6379", "IP Address of server");
DEFINE_int32(timeout_ms, 100, "RPC timeout in milliseconds");
DEFINE_int32(max_retry, 3, "Max retries(not including the first RPC)");
DEFINE_bool(dont_fail, false, "Print fatal when some call failed");
DEFINE_string(key, "hello", "The key to be get");
DEFINE_string(value, "world", "The value associated with the key");
DEFINE_int32(batch, 1, "Pipelined Operations");
DEFINE_int32(dummy_port, -1, "port of dummy server(for monitoring)");
DEFINE_int32(backup_request_ms, -1, "Timeout for sending a backup request");
bvar::LatencyRecorder g_latency_recorder("client");
bvar::Adder<int> g_error_count("client_error_count");
struct SenderArgs {
int base_index;
brpc::Channel* redis_channel;
};
static void* sender(void* void_args) {
SenderArgs* args = (SenderArgs*)void_args;
std::string value;
std::vector<std::pair<std::string, std::string> > kvs;
kvs.resize(FLAGS_batch);
for (int i = 0; i < FLAGS_batch; ++i) {
kvs[i].first = butil::string_printf(
"%s_%04d", FLAGS_key.c_str(), args->base_index + i);
kvs[i].second = butil::string_printf(
"%s_%04d", FLAGS_value.c_str(), args->base_index + i);
}
brpc::RedisRequest request;
for (int i = 0; i < FLAGS_batch; ++i) {
CHECK(request.AddCommand("GET %s", kvs[i].first.c_str()));
}
while (!brpc::IsAskedToQuit()) {
// We will receive response synchronously, safe to put variables
// on stack.
brpc::RedisResponse response;
brpc::Controller cntl;
// Because `done'(last parameter) is NULL, this function waits until
// the response comes back or error occurs(including timedout).
args->redis_channel->CallMethod(NULL, &cntl, &request, &response, NULL);
const int64_t elp = cntl.latency_us();
if (!cntl.Failed()) {
g_latency_recorder << elp;
CHECK_EQ(response.reply_size(), FLAGS_batch);
for (int i = 0; i < FLAGS_batch; ++i) {
CHECK_EQ(kvs[i].second.c_str(), response.reply(i).data())
<< "base=" << args->base_index << " i=" << i;
}
} else {
g_error_count << 1;
CHECK(brpc::IsAskedToQuit() || !FLAGS_dont_fail)
<< "error=" << cntl.ErrorText() << " latency=" << elp;
// We can't connect to the server, sleep a while. Notice that this
// is a specific sleeping to prevent this thread from spinning too
// fast. You should continue the business logic in a production
// server rather than sleeping.
bthread_usleep(50000);
}
}
return NULL;
}
int main(int argc, char* argv[]) {
// Parse gflags. We recommend you to use gflags as well.
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
// A Channel represents a communication line to a Server. Notice that
// Channel is thread-safe and can be shared by all threads in your program.
brpc::Channel channel;
// Initialize the channel, NULL means using default options.
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_REDIS;
options.connection_type = FLAGS_connection_type;
options.timeout_ms = FLAGS_timeout_ms/*milliseconds*/;
options.max_retry = FLAGS_max_retry;
options.backup_request_ms = FLAGS_backup_request_ms;
if (channel.Init(FLAGS_server.c_str(), &options) != 0) {
LOG(ERROR) << "Fail to initialize channel";
return -1;
}
// Pipeline #batch * #thread_num SET requests into redis so that we
// have keys to get.
brpc::RedisRequest request;
brpc::RedisResponse response;
brpc::Controller cntl;
for (int i = 0; i < FLAGS_batch * FLAGS_thread_num; ++i) {
if (!request.AddCommand("SET %s_%04d %s_%04d",
FLAGS_key.c_str(), i,
FLAGS_value.c_str(), i)) {
LOG(ERROR) << "Fail to SET " << i << "th request";
return -1;
}
}
channel.CallMethod(NULL, &cntl, &request, &response, NULL);
if (cntl.Failed()) {
LOG(ERROR) << "Fail to access redis, " << cntl.ErrorText();
return -1;
}
if (FLAGS_batch * FLAGS_thread_num != response.reply_size()) {
LOG(ERROR) << "Fail to set";
return -1;
}
for (int i = 0; i < FLAGS_batch * FLAGS_thread_num; ++i) {
CHECK_EQ("OK", response.reply(i).data());
}
LOG(INFO) << "Set " << FLAGS_batch * FLAGS_thread_num << " values";
if (FLAGS_dummy_port >= 0) {
brpc::StartDummyServerAt(FLAGS_dummy_port);
}
std::vector<bthread_t> bids;
std::vector<pthread_t> pids;
bids.resize(FLAGS_thread_num);
pids.resize(FLAGS_thread_num);
std::vector<SenderArgs> args;
args.resize(FLAGS_thread_num);
for (int i = 0; i < FLAGS_thread_num; ++i) {
args[i].base_index = i * FLAGS_batch;
args[i].redis_channel = &channel;
if (!FLAGS_use_bthread) {
if (pthread_create(&pids[i], NULL, sender, &args[i]) != 0) {
LOG(ERROR) << "Fail to create pthread";
return -1;
}
} else {
if (bthread_start_background(
&bids[i], NULL, sender, &args[i]) != 0) {
LOG(ERROR) << "Fail to create bthread";
return -1;
}
}
}
while (!brpc::IsAskedToQuit()) {
sleep(1);
LOG(INFO) << "Accessing redis-server at qps=" << g_latency_recorder.qps(1)
<< " latency=" << g_latency_recorder.latency(1);
}
LOG(INFO) << "redis_client is going to quit";
for (int i = 0; i < FLAGS_thread_num; ++i) {
if (!FLAGS_use_bthread) {
pthread_join(pids[i], NULL);
} else {
bthread_join(bids[i], NULL);
}
}
return 0;
}
+219
View File
@@ -0,0 +1,219 @@
// 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 <memory>
#include <unordered_map>
#include <butil/time.h>
DEFINE_int32(port, 6379, "TCP Port of this server");
class AuthSession : public brpc::Destroyable {
public:
explicit AuthSession(const std::string& user_name, const std::string& password)
: _user_name(user_name), _password(password) {}
void Destroy() override {
delete this;
}
const std::string _user_name;
const std::string _password;
};
class RedisServiceImpl : public brpc::RedisService {
public:
RedisServiceImpl() {
_user_password["db1"] = "123456";
_user_password["db2"] = "123456";
_db_map["db1"].resize(kHashSlotNum);
_db_map["db2"].resize(kHashSlotNum);
}
bool Set(const std::string& db_name, const std::string& key, const std::string& value) {
int slot = butil::crc32c::Value(key.c_str(), key.size()) % kHashSlotNum;
_mutex[slot].lock();
auto& kv = _db_map[db_name];
kv[slot][key] = value;
_mutex[slot].unlock();
return true;
}
bool Auth(const std::string& db_name, const std::string& password) {
if (_user_password.find(db_name) == _user_password.end()) {
return false;
} else {
if (_user_password[db_name] != password) {
return false;
}
}
return true;
}
bool Get(const std::string& db_name, const std::string& key, std::string* value) {
int slot = butil::crc32c::Value(key.c_str(), key.size()) % kHashSlotNum;
_mutex[slot].lock();
auto& kv = _db_map[db_name];
auto it = kv[slot].find(key);
if (it == kv[slot].end()) {
_mutex[slot].unlock();
return false;
}
*value = it->second;
_mutex[slot].unlock();
return true;
}
private:
const static int kHashSlotNum = 32;
typedef std::unordered_map<std::string, std::string> KVStore;
std::unordered_map<std::string, std::vector<KVStore>> _db_map;
std::unordered_map<std::string, std::string> _user_password;
butil::Mutex _mutex[kHashSlotNum];
};
class GetCommandHandler : public brpc::RedisCommandHandler {
public:
explicit GetCommandHandler(RedisServiceImpl* rsimpl)
: _rsimpl(rsimpl) {}
brpc::RedisCommandHandlerResult Run(brpc::RedisConnContext* ctx,
const std::vector<butil::StringPiece>& args,
brpc::RedisReply* output,
bool /*flush_batched*/) override {
AuthSession* session = static_cast<AuthSession*>(ctx->get_session());
if (session == nullptr) {
output->FormatError("No auth session");
return brpc::REDIS_CMD_HANDLED;
}
if (session->_user_name.empty()) {
output->FormatError("No user name");
return brpc::REDIS_CMD_HANDLED;
}
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(session->_user_name, 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(brpc::RedisConnContext* ctx,
const std::vector<butil::StringPiece>& args,
brpc::RedisReply* output,
bool /*flush_batched*/) override {
AuthSession* session = static_cast<AuthSession*>(ctx->get_session());
if (session == nullptr) {
output->FormatError("No auth session");
return brpc::REDIS_CMD_HANDLED;
}
if (session->_user_name.empty()) {
output->FormatError("No user name");
return brpc::REDIS_CMD_HANDLED;
}
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(session->_user_name, key, value);
output->SetStatus("OK");
return brpc::REDIS_CMD_HANDLED;
}
private:
RedisServiceImpl* _rsimpl;
};
class AuthCommandHandler : public brpc::RedisCommandHandler {
public:
explicit AuthCommandHandler(RedisServiceImpl* rsimpl)
: _rsimpl(rsimpl) {}
brpc::RedisCommandHandlerResult Run(brpc::RedisConnContext* ctx,
const std::vector<butil::StringPiece>& args,
brpc::RedisReply* output,
bool /*flush_batched*/) override {
if (args.size() != 3ul) {
output->FormatError("Expect 2 args for 'auth', actually %lu", args.size()-1);
return brpc::REDIS_CMD_HANDLED;
}
const std::string db_name(args[1].data(), args[1].size());
const std::string password(args[2].data(), args[2].size());
if (_rsimpl->Auth(db_name, password)) {
output->SetStatus("OK");
auto auth_session = new AuthSession(db_name, password);
ctx->reset_session(auth_session);
} else {
output->FormatError("Invalid password for database '%s'", db_name.c_str());
}
return brpc::REDIS_CMD_HANDLED;
}
private:
RedisServiceImpl* _rsimpl;
};
int main(int argc, char* argv[]) {
google::ParseCommandLineFlags(&argc, &argv, true);
RedisServiceImpl *rsimpl = new RedisServiceImpl;
auto get_handler =std::unique_ptr<GetCommandHandler>(new GetCommandHandler(rsimpl));
auto set_handler =std::unique_ptr<SetCommandHandler>( new SetCommandHandler(rsimpl));
auto auth_handler = std::unique_ptr<AuthCommandHandler>(new AuthCommandHandler(rsimpl));
rsimpl->AddCommandHandler("get", get_handler.get());
rsimpl->AddCommandHandler("set", set_handler.get());
rsimpl->AddCommandHandler("auth", auth_handler.get());
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;
}