/* * 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 mysql-server #include #include #include #include #include "brpc/policy/mysql/mysql.h" #include "brpc/policy/mysql/mysql_authenticator.h" #include #include #include DEFINE_string(connection_type, "pooled", "Connection type. Available values: pooled, short"); DEFINE_string(server, "127.0.0.1", "IP Address of server"); DEFINE_int32(port, 3306, "Port of server"); DEFINE_string(user, "brpcuser", "user name"); DEFINE_string(password, "12345678", "password"); DEFINE_string(schema, "brpc_test", "schema"); DEFINE_string(params, "", "params"); DEFINE_string(collation, "utf8mb4_general_ci", "collation"); DEFINE_string(data, "ABCDEF", "data"); DEFINE_int32(timeout_ms, 5000, "RPC timeout in milliseconds"); DEFINE_int32(connect_timeout_ms, 5000, "RPC timeout in milliseconds"); DEFINE_int32(max_retry, 3, "Max retries(not including the first RPC)"); DEFINE_int32(thread_num, 50, "Number of threads to send requests"); DEFINE_bool(use_bthread, false, "Use bthread to send requests"); DEFINE_int32(dummy_port, -1, "port of dummy server(for monitoring)"); DEFINE_int32(op_type, 0, "CRUD operation, 0:INSERT, 1:SELECT, 2:UPDATE"); DEFINE_bool(dont_fail, false, "Print fatal when some call failed"); bvar::LatencyRecorder g_latency_recorder("client"); bvar::Adder g_error_count("client_error_count"); struct SenderArgs { int base_index; brpc::Channel* mysql_channel; }; const std::string insert = "insert into brpc_press(col1,col2,col3,col4) values " "('" "ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCA" "BCABCABCABCABCABCABCA', '" + FLAGS_data + "' ,1.5, " "now())"; // Send `command' to mysql-server via `channel' static void* sender(void* void_args) { SenderArgs* args = (SenderArgs*)void_args; std::stringstream command; if (FLAGS_op_type == 0) { command << insert; } else if (FLAGS_op_type == 1) { command << "select * from brpc_press where id = " << args->base_index + 1; } else if (FLAGS_op_type == 2) { command << "update brpc_press set col2 = '" + FLAGS_data + "' where id = " << args->base_index + 1; } else { LOG(ERROR) << "wrong op type " << FLAGS_op_type; } brpc::MysqlRequest request; if (!request.Query(command.str())) { LOG(ERROR) << "Fail to execute command"; return NULL; } while (!brpc::IsAskedToQuit()) { brpc::MysqlResponse response; brpc::Controller cntl; args->mysql_channel->CallMethod(NULL, &cntl, &request, &response, NULL); const int64_t elp = cntl.latency_us(); if (!cntl.Failed()) { g_latency_recorder << elp; if (FLAGS_op_type == 0) { CHECK_EQ(response.reply(0).is_ok(), true); } else if (FLAGS_op_type == 1) { CHECK_EQ(response.reply(0).row_count(), 1); } else if (FLAGS_op_type == 2) { CHECK_EQ(response.reply(0).is_ok(), true); } } 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_NS::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_MYSQL; options.connection_type = FLAGS_connection_type; options.timeout_ms = FLAGS_timeout_ms /*milliseconds*/; options.connect_timeout_ms = FLAGS_connect_timeout_ms; options.max_retry = FLAGS_max_retry; options.auth = new brpc::policy::MysqlAuthenticator( FLAGS_user, FLAGS_password, FLAGS_schema, FLAGS_params, FLAGS_collation); if (channel.Init(FLAGS_server.c_str(), FLAGS_port, &options) != 0) { LOG(ERROR) << "Fail to initialize channel"; return -1; } // create table brpc_press { brpc::MysqlRequest request; if (!request.Query( "CREATE TABLE IF NOT EXISTS `brpc_press`(`id` INT UNSIGNED AUTO_INCREMENT, `col1` " "VARCHAR(100) NOT NULL, `col2` VARCHAR(1024) NOT NULL, `col3` decimal(10,0) NOT " "NULL, `col4` DATE, PRIMARY KEY ( `id` )) ENGINE=InnoDB DEFAULT CHARSET=utf8;")) { LOG(ERROR) << "Fail to create table"; return -1; } brpc::MysqlResponse response; brpc::Controller cntl; channel.CallMethod(NULL, &cntl, &request, &response, NULL); if (!cntl.Failed()) { std::cout << response << std::endl; } else { LOG(ERROR) << "Fail to access mysql, " << cntl.ErrorText(); return -1; } } // truncate table { brpc::MysqlRequest request; if (!request.Query("truncate table brpc_press")) { LOG(ERROR) << "Fail to truncate table"; return -1; } brpc::MysqlResponse response; brpc::Controller cntl; channel.CallMethod(NULL, &cntl, &request, &response, NULL); if (!cntl.Failed()) { std::cout << response << std::endl; } else { LOG(ERROR) << "Fail to access mysql, " << cntl.ErrorText(); return -1; } } // prepare data for select, update if (FLAGS_op_type != 0) { for (int i = 0; i < FLAGS_thread_num; ++i) { brpc::MysqlRequest request; if (!request.Query(insert)) { LOG(ERROR) << "Fail to execute command"; return -1; } brpc::MysqlResponse response; brpc::Controller cntl; channel.CallMethod(NULL, &cntl, &request, &response, NULL); if (cntl.Failed()) { LOG(ERROR) << cntl.ErrorText(); return -1; } if (!response.reply(0).is_ok()) { LOG(ERROR) << "prepare data failed"; return -1; } } } if (FLAGS_dummy_port >= 0) { brpc::StartDummyServerAt(FLAGS_dummy_port); } // test CRUD operations std::vector bids; std::vector pids; bids.resize(FLAGS_thread_num); pids.resize(FLAGS_thread_num); std::vector args; args.resize(FLAGS_thread_num); for (int i = 0; i < FLAGS_thread_num; ++i) { args[i].base_index = i; args[i].mysql_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 mysql-server at qps=" << g_latency_recorder.qps(1) << " latency=" << g_latency_recorder.latency(1); } LOG(INFO) << "mysql_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; }