| // 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. |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #include <cstdint> |
| #include <vector> |
| |
| #include <gflags/gflags.h> |
| |
| #include "butil/atomicops.h" |
| #include "butil/fast_rand.h" |
| #include "butil/logging.h" |
| #include "brpc/channel.h" |
| #include "brpc/controller.h" |
| #include "bthread/bthread.h" |
| #include "bvar/latency_recorder.h" |
| #include "bvar/variable.h" |
| #include "test.pb.h" |
| |
| #if BRPC_WITH_URMA |
| |
| DEFINE_string(server, "127.0.0.1:8003", "IP Port of urma performance server"); |
| DEFINE_int32(thread_num, 0, "How many threads are used"); |
| DEFINE_int32(queue_depth, 1, "How many requests can be pending in the queue"); |
| DEFINE_int32(expected_qps, 0, "The expected QPS"); |
| DEFINE_int32(max_thread_num, 16, "The max number of threads are used"); |
| DEFINE_int32(attachment_size, -1, "Attachment size is used (in Bytes)"); |
| DEFINE_int32(rpc_timeout_ms, 5000, "Timeout for each RPC in milliseconds"); |
| DEFINE_bool(echo_attachment, false, "Select whether attachment should be echo"); |
| DEFINE_bool(use_urma, true, "Use URMA transport (true) or TCP (false)"); |
| |
| bvar::LatencyRecorder g_latency("client"); |
| bvar::Adder<int64_t> g_error_count("client_error_count"); |
| |
| static void* worker(void* arg) { |
| test::PerfTestService_Stub* stub = |
| static_cast<test::PerfTestService_Stub*>(arg); |
| int qps = FLAGS_expected_qps; |
| while (!brpc::IsAskedToQuit()) { |
| butil::FastRandSeed seed; |
| butil::init_fast_rand_seed(&seed); |
| std::vector<brpc::Controller> cntls(FLAGS_queue_depth); |
| std::vector<test::PerfTestRequest> reqs(FLAGS_queue_depth); |
| std::vector<test::PerfTestResponse> resps(FLAGS_queue_depth); |
| std::vector<brpc::CallId> ids(FLAGS_queue_depth); |
| for (int i = 0; i < FLAGS_queue_depth; ++i) { |
| cntls[i].set_log_id(butil::fast_rand(&seed) & 0x7fffffff); |
| reqs[i].set_echo_attachment(FLAGS_echo_attachment); |
| if (FLAGS_attachment_size >= 0) { |
| cntls[i].request_attachment().resize(FLAGS_attachment_size, 'a'); |
| } |
| ids[i] = cntls[i].call_id(); |
| stub->Test(&cntls[i], &reqs[i], &resps[i], brpc::DoNothing()); |
| } |
| for (int i = 0; i < FLAGS_queue_depth; ++i) { |
| brpc::Join(ids[i]); |
| if (cntls[i].Failed()) { |
| g_error_count << 1; |
| LOG_EVERY_SECOND(WARNING) |
| << "RPC failed: " << cntls[i].ErrorText(); |
| } else { |
| g_latency << cntls[i].latency_us(); |
| } |
| } |
| if (qps > 0) { |
| usleep(FLAGS_queue_depth * 1000000 / qps); |
| } |
| } |
| return nullptr; |
| } |
| |
| int main(int argc, char* argv[]) { |
| gflags::ParseCommandLineFlags(&argc, &argv, true); |
| brpc::ChannelOptions options; |
| options.socket_mode = FLAGS_use_urma ? brpc::SOCKET_MODE_URMA |
| : brpc::SOCKET_MODE_TCP; |
| options.connect_timeout_ms = FLAGS_rpc_timeout_ms; |
| options.timeout_ms = FLAGS_rpc_timeout_ms; |
| options.max_retry = 0; |
| brpc::Channel channel; |
| if (channel.Init(FLAGS_server.c_str(), &options) != 0) { |
| LOG(ERROR) << "Fail to init channel to " << FLAGS_server; |
| return -1; |
| } |
| test::PerfTestService_Stub stub(&channel); |
| |
| // Complete one RPC before starting all workers. This makes handshake and |
| // data-path failures visible instead of looking like a hung benchmark. |
| brpc::Controller warmup_cntl; |
| warmup_cntl.set_timeout_ms(FLAGS_rpc_timeout_ms); |
| test::PerfTestRequest warmup_req; |
| test::PerfTestResponse warmup_resp; |
| warmup_req.set_echo_attachment(false); |
| stub.Test(&warmup_cntl, &warmup_req, &warmup_resp, nullptr); |
| if (warmup_cntl.Failed()) { |
| LOG(ERROR) << "Warm-up RPC failed after timeout_ms=" |
| << FLAGS_rpc_timeout_ms << ": " |
| << warmup_cntl.ErrorText(); |
| return -1; |
| } |
| LOG(INFO) << "Warm-up RPC to " << FLAGS_server |
| << " succeeded, latency=" << warmup_cntl.latency_us() << "us"; |
| |
| int thread_num = FLAGS_thread_num; |
| if (thread_num == 0) { |
| thread_num = FLAGS_max_thread_num; |
| } |
| if (thread_num <= 0 || FLAGS_queue_depth <= 0) { |
| LOG(ERROR) << "thread_num and queue_depth must be positive"; |
| return -1; |
| } |
| std::vector<bthread_t> tids(thread_num); |
| for (int i = 0; i < thread_num; ++i) { |
| bthread_start_background(&tids[i], nullptr, worker, &stub); |
| } |
| LOG(INFO) << "URMA performance client started (server=" << FLAGS_server |
| << ", use_urma=" << FLAGS_use_urma |
| << ", threads=" << thread_num |
| << ", rpc_timeout_ms=" << FLAGS_rpc_timeout_ms << ")"; |
| while (!brpc::IsAskedToQuit()) { |
| sleep(1); |
| LOG(INFO) << "qps=" << g_latency.qps(1) |
| << " latency=" << g_latency.latency(1) << "us" |
| << " errors=" << g_error_count.get_value(); |
| } |
| for (int i = 0; i < thread_num; ++i) { |
| bthread_join(tids[i], nullptr); |
| } |
| return 0; |
| } |
| |
| #else |
| |
| #include <cstdio> |
| int main() { |
| printf("This example requires brpc built with -DWITH_URMA=ON.\n"); |
| return 0; |
| } |
| |
| #endif // BRPC_WITH_URMA |