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
+21
View File
@@ -0,0 +1,21 @@
# 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.
file(GLOB SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/tools/rpc_press/*.cpp")
add_executable(rpc_press ${SOURCES})
target_link_libraries(rpc_press PRIVATE brpc-static ${DYNAMIC_LIB})
install(TARGETS rpc_press RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+28
View File
@@ -0,0 +1,28 @@
# 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.
import json
import urllib2
data = { "message" : "hello world" }
request_json = json.dumps(data)
req = urllib2.Request("http://127.0.0.1:8000/EchoService/Echo")
try:
response = urllib2.urlopen(req, request_json, 1)
print response.read()
except urllib2.HTTPError as e:
print e.exception.code
+130
View File
@@ -0,0 +1,130 @@
// 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 "info_thread.h"
namespace brpc {
InfoThread::InfoThread()
: _stop(false)
, _tid(0) {
pthread_mutex_init(&_mutex, NULL);
pthread_cond_init(&_cond, NULL);
}
InfoThread::~InfoThread() {
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
void InfoThread::run() {
int64_t i = 0;
int64_t last_sent_count = 0;
int64_t last_succ_count = 0;
int64_t last_error_count = 0;
int64_t start_time = butil::cpuwide_time_us();
while (!_stop) {
int64_t end_time = 0;
while (!_stop &&
(end_time = butil::cpuwide_time_us()) < start_time + 1000000L) {
BAIDU_SCOPED_LOCK(_mutex);
if (!_stop) {
timespec ts = butil::microseconds_to_timespec(end_time);
pthread_cond_timedwait(&_cond, &_mutex, &ts);
}
}
start_time = butil::cpuwide_time_us();
char buf[64];
const time_t tm_s = start_time / 1000000L;
struct tm lt;
strftime(buf, sizeof(buf), "%Y/%m/%d-%H:%M:%S",
localtime_r(&tm_s, &lt));
const int64_t cur_sent_count = _options.sent_count->get_value();
const int64_t cur_succ_count = _options.latency_recorder->count();
const int64_t cur_error_count = _options.error_count->get_value();
printf("%s\tsent:%-10dsuccess:%-10derror:%-6dtotal_error:%-10lldtotal_sent:%-10lld\n",
buf,
(int)(cur_sent_count - last_sent_count),
(int)(cur_succ_count - last_succ_count),
(int)(cur_error_count - last_error_count),
(long long)cur_error_count,
(long long)cur_sent_count);
last_sent_count = cur_sent_count;
last_succ_count = cur_succ_count;
last_error_count = cur_error_count;
if (_stop || ++i % 10 == 0) {
printf("[Latency]\n"
" avg %10lld us\n"
" 50%% %10lld us\n"
" 70%% %10lld us\n"
" 90%% %10lld us\n"
" 95%% %10lld us\n"
" 97%% %10lld us\n"
" 99%% %10lld us\n"
" 99.9%% %10lld us\n"
" 99.99%% %10lld us\n"
" max %10lld us\n",
(long long)_options.latency_recorder->latency(),
(long long)_options.latency_recorder->latency_percentile(0.5),
(long long)_options.latency_recorder->latency_percentile(0.7),
(long long)_options.latency_recorder->latency_percentile(0.9),
(long long)_options.latency_recorder->latency_percentile(0.95),
(long long)_options.latency_recorder->latency_percentile(0.97),
(long long)_options.latency_recorder->latency_percentile(0.99),
(long long)_options.latency_recorder->latency_percentile(0.999),
(long long)_options.latency_recorder->latency_percentile(0.9999),
(long long)_options.latency_recorder->max_latency());
}
}
}
static void* run_info_thread(void* arg) {
((InfoThread*)arg)->run();
return NULL;
}
bool InfoThread::start(const InfoThreadOptions& options) {
if (options.latency_recorder == NULL ||
options.error_count == NULL ||
options.sent_count == NULL) {
LOG(ERROR) << "Some required options are NULL";
return false;
}
_options = options;
_stop = false;
if (pthread_create(&_tid, NULL, run_info_thread, this) != 0) {
LOG(ERROR) << "Fail to create info_thread";
return false;
}
return true;
}
void InfoThread::stop() {
{
BAIDU_SCOPED_LOCK(_mutex);
if (_stop) {
return;
}
_stop = true;
pthread_cond_signal(&_cond);
}
pthread_join(_tid, NULL);
}
} // namespace brpc
+55
View File
@@ -0,0 +1,55 @@
// 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.
#ifndef BRPC_RPC_REPLAY_INFO_THREAD_H
#define BRPC_RPC_REPLAY_INFO_THREAD_H
#include <pthread.h>
#include <bvar/bvar.h>
namespace brpc {
struct InfoThreadOptions {
bvar::LatencyRecorder* latency_recorder;
bvar::Adder<int64_t>* sent_count;
bvar::Adder<int64_t>* error_count;
InfoThreadOptions()
: latency_recorder(NULL)
, sent_count(NULL)
, error_count(NULL) {}
};
class InfoThread {
public:
InfoThread();
~InfoThread();
void run();
bool start(const InfoThreadOptions&);
void stop();
private:
bool _stop;
InfoThreadOptions _options;
pthread_mutex_t _mutex;
pthread_cond_t _cond;
pthread_t _tid;
};
} // namespace brpc
#endif //BRPC_RPC_REPLAY_INFO_THREAD_H
+229
View File
@@ -0,0 +1,229 @@
// 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 <algorithm>
#include <butil/logging.h>
#include <json2pb/pb_to_json.h>
#include <json2pb/json_to_pb.h>
#include "pb_util.h"
#include "json_loader.h"
#include <errno.h>
namespace brpc {
class JsonLoader::Reader {
public:
explicit Reader(int fd2)
: _fd(fd2)
, _brace_depth(0)
, _quoted(false)
, _quote_char(0)
{}
explicit Reader(const std::string& jsons)
: _fd(-1)
, _brace_depth(0)
, _quoted(false)
, _quote_char(0) {
_file_buf.append(jsons);
}
bool get_next_json(butil::IOBuf* json1);
bool read_some();
private:
int _fd;
int _brace_depth;
bool _quoted; // quoted by " or '
char _quote_char;
butil::IOPortal _file_buf;
};
// Load data from the file.
bool JsonLoader::Reader::read_some() {
if (_fd < 0) { // loading from a string.
return false;
}
ssize_t nr = _file_buf.append_from_file_descriptor(_fd, 65536);
if (nr < 0) {
if (errno == EINTR) {
return read_some();
}
PLOG(ERROR) << "Fail to read fd=" << _fd;
return false;
} else if (nr == 0) {
return false;
} else {
return true;
}
}
// Ignore json only with spaces and newline
static bool possibly_valid_json(const butil::IOBuf& json) {
butil::IOBufAsZeroCopyInputStream it(json);
const void* data = NULL;
for (int size = 0; it.Next(&data, &size); ) {
for (int i = 0; i < size; ++i) {
char c = ((const char*)data)[i];
if (!isspace(c) && c != '\n') {
return true;
}
}
}
return false;
}
// Separate jsons with closed braces.
bool JsonLoader::Reader::get_next_json(butil::IOBuf* json1) {
if (_file_buf.empty()) {
if (!read_some()) {
return false;
}
}
json1->clear();
while (1) {
butil::IOBufAsZeroCopyInputStream it(_file_buf);
const void* data = NULL;
int size = 0;
int total_size = 0;
int skipped = 0;
for (; it.Next(&data, &size); total_size += size) {
for (int i = 0; i < size; ++i) {
const char c = ((const char*)data)[i];
if (_brace_depth == 0) {
// skip any character until the first left brace is found.
if (c != '{') {
++skipped;
continue;
}
}
switch (c) {
case '{':
if (!_quoted) {
++_brace_depth;
} else {
VLOG(1) << "Quoted left brace";
}
break;
case '}':
if (!_quoted) {
--_brace_depth;
if (_brace_depth == 0) {
// the braces are closed, a complete object.
_file_buf.cutn(json1, total_size + i + 1);
json1->pop_front(skipped);
return possibly_valid_json(*json1);
} else if (_brace_depth < 0) {
LOG(ERROR) << "More right braces than left braces";
return false;
}
} else {
VLOG(1) << "Quoted right brace";
}
break;
case '"':
if (_quoted) {
if (_quote_char == '"') {
_quoted = false;
}
} else {
_quoted = true;
_quote_char = '"';
}
break;
case '\'':
if (_quoted) {
if (_quote_char == '\'') {
_quoted = false;
}
} else {
_quoted = true;
_quote_char = '\'';
}
break;
default:
break;
// just skip
}
}
}
if (!_file_buf.empty()) {
json1->append(_file_buf);
_file_buf.clear();
}
if (!read_some()) {
json1->pop_front(skipped);
if (!json1->empty()) {
return possibly_valid_json(*json1);
}
return false;
}
}
return false;
}
JsonLoader::JsonLoader(google::protobuf::compiler::Importer* importer,
google::protobuf::DynamicMessageFactory* factory,
const std::string& service_name,
const std::string& method_name)
: _importer(importer)
, _factory(factory)
, _service_name(service_name)
, _method_name(method_name)
{
_request_prototype = pbrpcframework::get_prototype_by_name(
_service_name, _method_name, true, _importer, _factory);
}
void JsonLoader::load_messages(
JsonLoader::Reader* ctx,
std::deque<google::protobuf::Message*>* out_msgs) {
out_msgs->clear();
butil::IOBuf request_json;
while (ctx->get_next_json(&request_json)) {
VLOG(1) << "Load " << out_msgs->size() + 1 << "-th json=`"
<< request_json << '\'';
std::string error;
google::protobuf::Message* request = _request_prototype->New();
butil::IOBufAsZeroCopyInputStream wrapper(request_json);
if (!json2pb::JsonToProtoMessage(&wrapper, request, &error)) {
LOG(WARNING) << "Fail to convert to pb: " << error << ", json=`"
<< request_json << '\'';
delete request;
continue;
}
out_msgs->push_back(request);
LOG_IF(INFO, (out_msgs->size() % 10000) == 0)
<< "Loaded " << out_msgs->size() << " jsons";
}
}
void JsonLoader::load_messages(
int fd,
std::deque<google::protobuf::Message*>* out_msgs) {
JsonLoader::Reader ctx(fd);
load_messages(&ctx, out_msgs);
}
void JsonLoader::load_messages(
const std::string& jsons,
std::deque<google::protobuf::Message*>* out_msgs) {
JsonLoader::Reader ctx(jsons);
load_messages(&ctx, out_msgs);
}
} // namespace brpc
+63
View File
@@ -0,0 +1,63 @@
// 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.
#ifndef BRPC_JSON_LOADER_H
#define BRPC_JSON_LOADER_H
#include <string>
#include <deque>
#include <google/protobuf/message.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
#include <butil/iobuf.h>
namespace brpc {
// This utility loads pb messages in json format from a file or string.
class JsonLoader {
public:
JsonLoader(google::protobuf::compiler::Importer* importer,
google::protobuf::DynamicMessageFactory* factory,
const std::string& service_name,
const std::string& method_name);
~JsonLoader() {}
// TODO(gejun): messages should be lazily loaded.
// Load jsons from fd or string, convert them into pb messages, then insert
// them into `out_msgs'.
void load_messages(int fd, std::deque<google::protobuf::Message*>* out_msgs);
void load_messages(const std::string& jsons,
std::deque<google::protobuf::Message*>* out_msgs);
private:
class Reader;
void load_messages(
JsonLoader::Reader* ctx,
std::deque<google::protobuf::Message*>* out_msgs);
google::protobuf::compiler::Importer* _importer;
google::protobuf::DynamicMessageFactory* _factory;
std::string _service_name;
std::string _method_name;
const google::protobuf::Message* _request_prototype;
};
} // namespace brpc
#endif // BRPC_JSON_LOADER_H
+72
View File
@@ -0,0 +1,72 @@
// 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 <butil/logging.h>
#include "pb_util.h"
using google::protobuf::ServiceDescriptor;
using google::protobuf::Descriptor;
using google::protobuf::DescriptorPool;
using google::protobuf::MessageFactory;
using google::protobuf::Message;
using google::protobuf::MethodDescriptor;
using google::protobuf::compiler::Importer;
using google::protobuf::DynamicMessageFactory;
using google::protobuf::DynamicMessageFactory;
using std::string;
namespace pbrpcframework {
const MethodDescriptor* find_method_by_name(const string& service_name,
const string& method_name,
Importer* importer) {
const ServiceDescriptor* descriptor =
importer->pool()->FindServiceByName(service_name);
if (NULL == descriptor) {
LOG(FATAL) << "Fail to find service=" << service_name;
return NULL;
}
return descriptor->FindMethodByName(method_name);
}
const Message* get_prototype_by_method_descriptor(
const MethodDescriptor* descripter,
bool is_input,
DynamicMessageFactory* factory) {
if (NULL == descripter) {
LOG(FATAL) <<"Param[descripter] is NULL";
return NULL;
}
const Descriptor* message_descriptor = NULL;
if (is_input) {
message_descriptor = descripter->input_type();
} else {
message_descriptor = descripter->output_type();
}
return factory->GetPrototype(message_descriptor);
}
const Message* get_prototype_by_name(const string& service_name,
const string& method_name, bool is_input,
Importer* importer,
DynamicMessageFactory* factory){
const MethodDescriptor* descripter = find_method_by_name(
service_name, method_name, importer);
return get_prototype_by_method_descriptor(descripter, is_input, factory);
}
} // pbrpcframework
+44
View File
@@ -0,0 +1,44 @@
// 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.
#ifndef UTIL_PB_UTIL_H
#define UTIL_PB_UTIL_H
#include "google/protobuf/message.h"
#include "google/protobuf/descriptor.h"
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/compiler/importer.h>
namespace pbrpcframework {
const google::protobuf::MethodDescriptor* find_method_by_name(
const std::string& service_name,
const std::string& method_name,
google::protobuf::compiler::Importer* importer);
const google::protobuf::Message* get_prototype_by_method_descriptor(
const google::protobuf::MethodDescriptor* descripter,
bool is_input,
google::protobuf::DynamicMessageFactory* factory);
const google::protobuf::Message* get_prototype_by_name(
const std::string& service_name,
const std::string& method_name,
bool is_input,
google::protobuf::compiler::Importer* importer,
google::protobuf::DynamicMessageFactory* factory);
}
#endif //UTIL_PB_UTIL_H
+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.
#include <gflags/gflags.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/compiler/importer.h>
#include <brpc/server.h>
#include <butil/logging.h>
#include <butil/string_splitter.h>
#include <string.h>
#include "rpc_press_impl.h"
DEFINE_int32(dummy_port, 8888, "Port of dummy server");
DEFINE_string(proto, "", " user's proto files with path");
DEFINE_string(inc, "", "Include paths for proto, separated by semicolon(;)");
DEFINE_string(method, "example.EchoService.Echo", "The full method name");
DEFINE_string(server, "0.0.0.0:8002", "ip:port of the server when -load_balancer is empty, the naming service otherwise");
DEFINE_string(input, "", "The file containing requests in json format");
DEFINE_string(output, "", "The file containing responses in json format");
DEFINE_string(lb_policy, "", "The load balancer algorithm: rr, random, la, p2c, c_murmurhash, c_md5");
DEFINE_int32(thread_num, 0, "Number of threads to send requests. 0: automatically chosen according to -qps");
DEFINE_string(protocol, "baidu_std", "baidu_std hulu_pbrpc sofa_pbrpc http public_pbrpc nova_pbrpc ubrpc_compack...");
DEFINE_string(connection_type, "", "Type of connections: single, pooled, short");
DEFINE_int32(timeout_ms, 1000, "RPC timeout in milliseconds");
DEFINE_int32(connection_timeout_ms, 500, " connection timeout in milliseconds");
DEFINE_int32(max_retry, 3, "Maximum retry times by RPC framework");
DEFINE_int32(request_compress_type, 0, "Snappy:1 Gzip:2 Zlib:3 LZ4:4 None:0");
DEFINE_int32(response_compress_type, 0, "Snappy:1 Gzip:2 Zlib:3 LZ4:4 None:0");
DEFINE_int32(attachment_size, 0, "Carry so many byte attachment along with requests");
DEFINE_int32(duration, 0, "how many seconds the press keep");
DEFINE_int32(qps, 100 , "how many calls per seconds");
DEFINE_bool(pretty, true, "output pretty jsons");
bool set_press_options(pbrpcframework::PressOptions* options){
size_t dot_pos = FLAGS_method.find_last_of('.');
if (dot_pos == std::string::npos) {
LOG(ERROR) << "-method must be in form of: package.service.method";
return false;
}
options->service = FLAGS_method.substr(0, dot_pos);
options->method = FLAGS_method.substr(dot_pos + 1);
options->lb_policy = FLAGS_lb_policy;
options->test_req_rate = FLAGS_qps;
if (FLAGS_thread_num > 0) {
options->test_thread_num = FLAGS_thread_num;
} else {
if (FLAGS_qps <= 0) { // unlimited qps
options->test_thread_num = 50;
} else {
options->test_thread_num = FLAGS_qps / 10000;
if (options->test_thread_num < 1) {
options->test_thread_num = 1;
}
if (options->test_thread_num > 50) {
options->test_thread_num = 50;
}
}
}
const int rate_limit_per_thread = 1000000;
double req_rate_per_thread = options->test_req_rate / options->test_thread_num;
if (req_rate_per_thread > rate_limit_per_thread) {
LOG(ERROR) << "req_rate: " << (int64_t) req_rate_per_thread << " is too large in one thread. The rate limit is "
<< rate_limit_per_thread << " in one thread";
return false;
}
options->input = FLAGS_input;
options->output = FLAGS_output;
options->connection_type = FLAGS_connection_type;
options->connect_timeout_ms = FLAGS_connection_timeout_ms;
options->timeout_ms = FLAGS_timeout_ms;
options->max_retry = FLAGS_max_retry;
options->protocol = FLAGS_protocol;
options->request_compress_type = FLAGS_request_compress_type;
options->response_compress_type = FLAGS_response_compress_type;
options->attachment_size = FLAGS_attachment_size;
options->host = FLAGS_server;
options->proto_file = FLAGS_proto;
options->proto_includes = FLAGS_inc;
return true;
}
int main(int argc, char* argv[]) {
// Parse gflags. We recommend you to use gflags as well
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
// set global log option
if (FLAGS_dummy_port >= 0) {
brpc::StartDummyServerAt(FLAGS_dummy_port);
}
pbrpcframework::PressOptions options;
if (!set_press_options(&options)) {
return -1;
}
pbrpcframework::RpcPress* rpc_press = new pbrpcframework::RpcPress;
if (0 != rpc_press->init(&options)) {
LOG(FATAL) << "Fail to init rpc_press";
return -1;
}
rpc_press->start();
if (FLAGS_duration <= 0) {
while (!brpc::IsAskedToQuit()) {
sleep(1);
}
} else {
sleep(FLAGS_duration);
}
rpc_press->stop();
// NOTE(gejun): Can't delete rpc_press on exit. It's probably
// used by concurrently running done.
return 0;
}
+297
View File
@@ -0,0 +1,297 @@
// 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 <stdio.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <bthread/bthread.h>
#include <butil/file_util.h> // butil::FilePath
#include <butil/time.h>
#include <brpc/channel.h>
#include <brpc/controller.h>
#include <butil/logging.h>
#include <json2pb/pb_to_json.h>
#include "json_loader.h"
#include "rpc_press_impl.h"
using google::protobuf::Message;
using google::protobuf::Closure;
namespace pbrpcframework {
class ImportErrorPrinter
: public google::protobuf::compiler::MultiFileErrorCollector {
public:
// Line and column numbers are zero-based. A line number of -1 indicates
// an error with the entire file (e.g. "not found").
virtual void AddError(const std::string& filename, int line,
int /*column*/, const std::string& message) {
LOG_AT(ERROR, filename.c_str(), line) << message;
}
#if GOOGLE_PROTOBUF_VERSION >= 5026000
void RecordError(absl::string_view filename, int line, int column,
absl::string_view message) override {
LOG_AT(ERROR, filename.data(), line) << message;
}
#endif
};
int PressClient::init() {
brpc::ChannelOptions rpc_options;
rpc_options.connect_timeout_ms = _options->connect_timeout_ms;
rpc_options.timeout_ms = _options->timeout_ms;
rpc_options.max_retry = _options->max_retry;
rpc_options.protocol = _options->protocol;
rpc_options.connection_type = _options->connection_type;
if (_options->attachment_size > 0) {
_attachment.clear();
_attachment.assign(_options->attachment_size, 'a');
}
if (_rpc_client.Init(_options->host.c_str(), _options->lb_policy.c_str(),
&rpc_options) != 0){
LOG(ERROR) << "Fail to initialize channel";
return -1;
}
_method_descriptor = find_method_by_name(
_options->service, _options->method, _importer);
if (NULL == _method_descriptor) {
LOG(ERROR) << "Fail to find method=" << _options->service << '.'
<< _options->method;
return -1;
}
_response_prototype = get_prototype_by_method_descriptor(
_method_descriptor, false, _factory);
return 0;
}
void PressClient::call_method(brpc::Controller* cntl, Message* request,
Message* response, Closure* done) {
if (!_attachment.empty()) {
cntl->request_attachment().append(_attachment);
}
_rpc_client.CallMethod(_method_descriptor, cntl, request, response, done);
}
RpcPress::RpcPress()
: _pbrpc_client(NULL)
, _started(false)
, _stop(false)
, _output_json(NULL) {
}
RpcPress::~RpcPress() {
if (_output_json) {
fclose(_output_json);
_output_json = NULL;
}
delete _importer;
}
int RpcPress::init(const PressOptions* options) {
if (NULL == options) {
LOG(ERROR) << "Param[options] is NULL" ;
return -1;
}
_options = *options;
// Import protos.
if (_options.proto_file.empty()) {
LOG(ERROR) << "-proto is required";
return -1;
}
int pos = _options.proto_file.find_last_of('/');
std::string proto_file(_options.proto_file.substr(pos + 1));
std::string proto_path(_options.proto_file.substr(0, pos));
google::protobuf::compiler::DiskSourceTree sourceTree;
// look up .proto file in the same directory
sourceTree.MapPath("", proto_path.c_str());
// Add paths in -inc
if (!_options.proto_includes.empty()) {
butil::StringSplitter sp(_options.proto_includes.c_str(), ';');
for (; sp; ++sp) {
sourceTree.MapPath("", std::string(sp.field(), sp.length()));
}
}
ImportErrorPrinter error_printer;
_importer = new google::protobuf::compiler::Importer(&sourceTree, &error_printer);
if (_importer->Import(proto_file.c_str()) == NULL) {
LOG(ERROR) << "Fail to import " << proto_file;
return -1;
}
_pbrpc_client = new PressClient(&_options, _importer, &_factory);
if (!_options.output.empty()) {
butil::File::Error error;
butil::FilePath path(_options.output);
butil::FilePath dir = path.DirName();
if (!butil::CreateDirectoryAndGetError(dir, &error)) {
LOG(ERROR) << "Fail to create directory=`" << dir.value()
<< "', " << error;
return -1;
}
_output_json = fopen(_options.output.c_str(), "w");
LOG_IF(ERROR, !_output_json) << "Fail to open " << _options.output;
}
int ret = _pbrpc_client->init();
if (0 != ret) {
LOG(ERROR) << "Fail to initialize rpc client";
return ret;
}
if (_options.input.empty()) {
LOG(ERROR) << "-input is empty";
return -1;
}
brpc::JsonLoader json_util(_importer, &_factory,
_options.service, _options.method);
if (butil::PathExists(butil::FilePath(_options.input))) {
int fd = open(_options.input.c_str(), O_RDONLY);
if (fd < 0) {
PLOG(ERROR) << "Fail to open " << _options.input;
return -1;
}
json_util.load_messages(fd, &_msgs);
} else {
json_util.load_messages(_options.input, &_msgs);
}
if (_msgs.empty()) {
LOG(ERROR) << "Fail to load requests";
return -1;
}
LOG(INFO) << "Loaded " << _msgs.size() << " requests";
_latency_recorder.expose("rpc_press");
_error_count.expose("rpc_press_error_count");
return 0;
}
void* RpcPress::sync_call_thread(void* arg) {
((RpcPress*)arg)->sync_client();
return NULL;
}
void RpcPress::handle_response(brpc::Controller* cntl,
Message* request,
Message* response,
int64_t start_time){
if (!cntl->Failed()){
int64_t rpc_call_time_us = butil::cpuwide_time_us() - start_time;
_latency_recorder << rpc_call_time_us;
if (_output_json) {
std::string response_json;
std::string error;
if (!json2pb::ProtoMessageToJson(*response, &response_json, &error)) {
LOG(WARNING) << "Fail to convert to json: " << error;
}
fprintf(_output_json, "%s\n", response_json.c_str());
}
} else {
LOG(WARNING) << "error_code=" << cntl->ErrorCode() << ", "
<< cntl->ErrorText();
_error_count << 1;
}
delete response;
delete cntl;
}
static butil::atomic<int> g_thread_count(0);
void RpcPress::sync_client() {
double req_rate = _options.test_req_rate / _options.test_thread_num;
//max make up time is 5 s
if (_msgs.empty()) {
LOG(ERROR) << "nothing to send!";
return;
}
const int thread_index = g_thread_count.fetch_add(1, butil::memory_order_relaxed);
int msg_index = thread_index;
int64_t last_expected_time = butil::monotonic_time_ns();
const int64_t interval = (int64_t) (1000000000L / req_rate);
// the max tolerant delay between end_time and expected_time. 10ms or 10 intervals
int64_t max_tolerant_delay = std::max((int64_t) 10000000L, 10 * interval);
while (!_stop) {
brpc::Controller* cntl = new brpc::Controller;
msg_index = (msg_index + _options.test_thread_num) % _msgs.size();
Message* request = _msgs[msg_index];
Message* response = _pbrpc_client->get_output_message();
const int64_t start_time = butil::cpuwide_time_us();
google::protobuf::Closure* done = brpc::NewCallback<
RpcPress,
RpcPress*,
brpc::Controller*,
Message*,
Message*, int64_t>
(this, &RpcPress::handle_response, cntl, request, response, start_time);
const brpc::CallId cid1 = cntl->call_id();
_pbrpc_client->call_method(cntl, request, response, done);
_sent_count << 1;
if (_options.test_req_rate <= 0) {
brpc::Join(cid1);
} else {
int64_t end_time = butil::monotonic_time_ns();
int64_t expected_time = last_expected_time + interval;
if (end_time < expected_time) {
usleep((expected_time - end_time)/1000);
}
if (end_time - expected_time > max_tolerant_delay) {
expected_time = end_time;
}
last_expected_time = expected_time;
}
}
}
int RpcPress::start() {
_ttid.resize(_options.test_thread_num);
int ret = 0;
for (int i = 0; i < _options.test_thread_num; i++) {
if ((ret = pthread_create(&_ttid[i], NULL, sync_call_thread, this)) != 0) {
LOG(ERROR) << "Fail to create sending threads";
return -1;
}
}
brpc::InfoThreadOptions info_thr_opt;
info_thr_opt.latency_recorder = &_latency_recorder;
info_thr_opt.error_count = &_error_count;
info_thr_opt.sent_count = &_sent_count;
if (!_info_thr.start(info_thr_opt)) {
LOG(ERROR) << "Fail to create stats thread";
return -1;
}
_started = true;
return 0;
}
int RpcPress::stop() {
if (!_started) {
return -1;
}
_stop = true;
for (size_t i = 0; i < _ttid.size(); i++) {
pthread_join(_ttid[i], NULL);
}
_info_thr.stop();
return 0;
}
} //namespace
+141
View File
@@ -0,0 +1,141 @@
// 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.
#ifndef PBRPCPRESS_PBRPC_PRESS_H
#define PBRPCPRESS_PBRPC_PRESS_H
#include <stdio.h>
#include <deque>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
#include <bvar/bvar.h>
#include <brpc/channel.h>
#include "info_thread.h"
#include "pb_util.h"
namespace pbrpcframework {
class JsonUtil;
struct PressOptions {
std::string service; //service name (packet.rpcservice)
std::string method; //method name (rpc service method)
int server_type; // server type: 0 = hulu server, 1 = old pbrpc server, 2 = sofa server
double test_req_rate; // 0 = no limit
int test_thread_num;
std::string input;
std::string output;
std::string host; // server's ip:port, used by hulu server and sofa server
std::string channel; // server's channel, used by old pbrpc server
//comcfg::Configure conf;
std::string conf_dir;
std::string conf_file;
std::string connection_type; // connection type 0:SINGLE 1:POOLED 2:SHORT
int connect_timeout_ms; // connection timeout in milliseconds
int timeout_ms; // RPC timeout in milliseconds
int max_retry; // Maximum retry times by RPC framework
std::string protocol;
int request_compress_type; // Snappy:1 Gzip:2 Zlib:3 LZ4:4 None:0
int response_compress_type; // Snappy:1 Gzip:2 Zlib:3 LZ4:4 None:0
int attachment_size; // Snappy:1 Gzip:2 Zlib:3 LZ4:4 None:0
bool auth;// Enable Giano authentication
std::string auth_group;
std::string lb_policy; // "rr", "Policy of load balance rr ||random"
std::string proto_file;
std::string proto_includes;
PressOptions() :
server_type(0),
test_req_rate(0),
test_thread_num(1),
connect_timeout_ms(1000),
timeout_ms(1000),
max_retry(3),
request_compress_type(0),
response_compress_type(0),
attachment_size(0),
auth(false)
{}
};
class PressClient {
public:
PressClient(const PressOptions* options,
google::protobuf::compiler::Importer* importer,
google::protobuf::DynamicMessageFactory* factory) {
_method_descriptor = NULL;
_response_prototype = NULL;
_options = options;
_importer = importer;
_factory = factory;
}
google::protobuf::Message* get_output_message() {
return _response_prototype->New();
}
int init();
void call_method(brpc::Controller* cntl,
google::protobuf::Message* request,
google::protobuf::Message* response,
google::protobuf::Closure* done);
public:
brpc::Channel _rpc_client;
std::string _attachment;
const PressOptions* _options;
const google::protobuf::MethodDescriptor* _method_descriptor;
const google::protobuf::Message* _response_prototype;
google::protobuf::compiler::Importer* _importer;
google::protobuf::DynamicMessageFactory* _factory;
};
class RpcPress {
public:
RpcPress();
~RpcPress();
int init(const PressOptions* options);
int start();
int stop();
const PressOptions* options() { return &_options; }
private:
DISALLOW_COPY_AND_ASSIGN(RpcPress);
bool new_pbrpc_press_client_by_client_type(int client_type);
void sync_client();
void handle_response(brpc::Controller* cntl,
google::protobuf::Message* request,
google::protobuf::Message* response,
int64_t start_time_ns);
static void* sync_call_thread(void* arg);
bvar::LatencyRecorder _latency_recorder;
bvar::Adder<int64_t> _error_count;
bvar::Adder<int64_t> _sent_count;
std::deque<google::protobuf::Message*> _msgs;
PressClient* _pbrpc_client;
PressOptions _options;
bool _started;
bool _stop;
FILE* _output_json;
google::protobuf::compiler::Importer* _importer;
google::protobuf::DynamicMessageFactory _factory;
std::vector<pthread_t> _ttid;
brpc::InfoThread _info_thr;
};
}
#endif // PBRPCPRESS_PBRPC_PRESS_H