blob: 57f39dde32f131ed92ed3269ee85ccc30dfa3c09 [file]
// 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 <gflags/gflags.h>
#include <chrono>
#include <iostream>
#include <thread>
#include "butil/config.h"
#include "butil/endpoint.h"
#include "brpc/closure_guard.h"
#include "brpc/controller.h"
#include "brpc/errno.pb.h"
#include "brpc/server.h"
#include "bthread/bthread.h"
#include "echo.brpc.fb.h"
#include "echo_pb.pb.h"
#if !BRPC_WITH_FLATBUFFERS
#error "benchmark_fb requires a FlatBuffers-enabled brpc library"
#endif
DEFINE_string(listen_addr, "127.0.0.1:0", "IPv4 address; port 0 selects a free port. "
"A non-loopback address also needs --allow_remote");
DEFINE_bool(allow_remote, false, "Allow a non-loopback --listen_addr for cross-host benchmarks");
DEFINE_int32(duration_s, 60, "Exit after 1..86400 seconds, or earlier on SIGINT/SIGTERM");
DEFINE_int32(max_concurrency, 32, "Maximum concurrent requests, 1..4096");
DEFINE_int32(reply_delay_ms, 0, "Test aid: delay every reply by 0..10000 milliseconds");
namespace {
const size_t kMaxBytes = 1024 * 1024;
void DelayReply() {
if (FLAGS_reply_delay_ms > 0) {
// Sleep the handling bthread, not a worker pthread.
bthread_usleep(static_cast<uint64_t>(FLAGS_reply_delay_ms) * 1000);
}
}
// Reserve the echoed string plus table overhead up front so a large reply
// does not grow the builder by repeated reallocate-and-copy.
size_t BuilderSize(const benchmark_fb::Request* input) {
const size_t text = input->message() ? input->message()->size() : 0;
return text + 256;
}
class BenchmarkServiceImpl : public benchmark_fb::BenchmarkService {
public:
void Echo(google::protobuf::RpcController* controller,
const brpc::flatbuffers::Message* request,
brpc::flatbuffers::Message* response,
google::protobuf::Closure* done) override {
brpc::ClosureGuard done_guard(done);
if (!controller) {
return;
}
auto* cntl = static_cast<brpc::Controller*>(controller);
// The generated dispatcher has already verified the request schema,
// so verifying it again here would only add cost to the fb_rpc side.
if (!request || !response) {
cntl->SetFailed(brpc::EREQUEST, "Invalid FlatBuffers Request");
return;
}
const auto* input = request->GetRoot<benchmark_fb::Request>();
if ((input->message() && input->message()->size() > kMaxBytes) ||
cntl->request_attachment().size() > kMaxBytes) {
cntl->SetFailed(brpc::EREQUEST, "Example payload limit is 1 MiB per field");
return;
}
brpc::flatbuffers::MessageBuilder builder(BuilderSize(input));
// Preserve absent versus empty strings, including embedded zero bytes.
const auto text = input->message() ?
builder.CreateString(input->message()->data(), input->message()->size()) :
::flatbuffers::Offset<::flatbuffers::String>();
builder.Finish(benchmark_fb::CreateResponse(
builder, input->request_id(), text, cntl->request_attachment().size()));
*response = builder.ReleaseMessage();
cntl->response_attachment().append(cntl->request_attachment());
DelayReply();
}
};
// Protobuf control service over baidu_std with the same echo semantics.
class PbBenchmarkServiceImpl : public benchmark_fb_pb::BenchmarkService {
public:
void Echo(google::protobuf::RpcController* controller,
const benchmark_fb_pb::Request* request,
benchmark_fb_pb::Response* response,
google::protobuf::Closure* done) override {
brpc::ClosureGuard done_guard(done);
auto* cntl = static_cast<brpc::Controller*>(controller);
if (request->message().size() > kMaxBytes ||
cntl->request_attachment().size() > kMaxBytes) {
cntl->SetFailed(brpc::EREQUEST, "Example payload limit is 1 MiB per field");
return;
}
response->set_request_id(request->request_id());
if (request->has_message()) {
response->set_message(request->message());
}
response->set_attachment_size(cntl->request_attachment().size());
cntl->response_attachment().append(cntl->request_attachment());
DelayReply();
}
};
} // namespace
int main(int argc, char* argv[]) {
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
butil::EndPoint endpoint;
if ((FLAGS_listen_addr.compare(0, 10, "127.0.0.1:") != 0 && !FLAGS_allow_remote) ||
butil::str2endpoint(FLAGS_listen_addr.c_str(), &endpoint) != 0 ||
FLAGS_duration_s < 1 || FLAGS_duration_s > 86400 ||
FLAGS_max_concurrency < 1 || FLAGS_max_concurrency > 4096 ||
FLAGS_reply_delay_ms < 0 || FLAGS_reply_delay_ms > 10000) {
std::cerr << "Use --listen_addr=127.0.0.1:PORT (or --allow_remote), "
"--duration_s=1..86400, --max_concurrency=1..4096 "
"and --reply_delay_ms=0..10000\n";
return 2;
}
// brpc defaults SIGTERM to immediate termination. This example always
// enables graceful shutdown, including the smoke's owned-process cleanup.
if (GFLAGS_NAMESPACE::SetCommandLineOption(
"graceful_quit_on_sigterm", "true").empty()) {
std::cerr << "Cannot enable graceful SIGTERM handling\n";
return 1;
}
BenchmarkServiceImpl service;
brpc::Server server;
if (server.AddFlatBuffersService(&service, brpc::SERVER_DOESNT_OWN_SERVICE) != 0) {
std::cerr << "Cannot register FlatBuffers service\n";
return 1;
}
PbBenchmarkServiceImpl pb_service;
if (server.AddService(&pb_service, brpc::SERVER_DOESNT_OWN_SERVICE) != 0) {
std::cerr << "Cannot register protobuf control service\n";
return 1;
}
brpc::ServerOptions options;
options.has_builtin_services = false;
options.enabled_protocols = "fb_rpc baidu_std";
options.max_concurrency = FLAGS_max_concurrency;
if (server.Start(endpoint, &options) != 0) {
std::cerr << "Cannot start FlatBuffers server\n";
return 1;
}
// Install brpc's termination handlers before publishing readiness.
brpc::IsAskedToQuit();
std::cout << "BRPC_FB_READY " << server.listen_address() << std::endl;
const auto deadline = std::chrono::steady_clock::now() +
std::chrono::seconds(FLAGS_duration_s);
while (!brpc::IsAskedToQuit() && std::chrono::steady_clock::now() < deadline) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
if (!brpc::IsAskedToQuit()) {
std::cerr << "--duration_s=" << FLAGS_duration_s << " elapsed, stopping\n";
}
const int stop_result = server.Stop(0);
const int join_result = server.Join();
return stop_result == 0 && join_result == 0 ? 0 : 1;
}