Skip to content

Commit 9a26fa9

Browse files
committed
feat tools: update and simplify the sample
Tests: протестировано CI commit_hash:915e1ed533c2f209048a5df479a7aa94eef8eaf6
1 parent e96270d commit 9a26fa9

5 files changed

Lines changed: 92 additions & 77 deletions

File tree

core/src/server/net/connection_config.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,17 @@ ConnectionConfig Parse(const yaml_config::YamlConfig& value, formats::parse::To<
2727
}
2828

2929
config.http_version = value["http-version"].As<USERVER_NAMESPACE::http::HttpVersion>(config.http_version);
30+
if (config.http_version == USERVER_NAMESPACE::http::HttpVersion::kDefault) {
31+
config.http_version = USERVER_NAMESPACE::http::HttpVersion::k11;
32+
}
3033

3134
config.http2_session_config = value["http2-session"].As<Http2SessionConfig>(config.http2_session_config);
35+
if (config.http_version != http::HttpVersion::k11 && config.http_version != http::HttpVersion::k2) {
36+
throw std::runtime_error(fmt::format(
37+
"Only 1.1 and 2 versions of HTTP are supported for static config 'http-version', but '{}' was provided",
38+
value["http-version"].As<std::string>()
39+
));
40+
}
3241

3342
return config;
3443
}
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
project(userver-tool-http-client-perf CXX)
22

3-
file(GLOB_RECURSE SOURCES *.cpp)
4-
53
find_package(Boost REQUIRED CONFIG COMPONENTS program_options)
64

7-
add_executable(${PROJECT_NAME} ${SOURCES})
5+
add_executable(${PROJECT_NAME} "httpclient_perf.cpp")
86
target_link_libraries(${PROJECT_NAME}
9-
userver-core
7+
userver::core
108
Boost::program_options
119
)
Lines changed: 52 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#include <fstream>
22
#include <iostream>
3-
#include <list>
43
#include <thread>
54

65
#include <boost/program_options.hpp>
76

87
#include <userver/clients/http/client.hpp>
98
#include <userver/engine/async.hpp>
9+
#include <userver/engine/get_all.hpp>
1010
#include <userver/engine/run_standalone.hpp>
1111
#include <userver/http/http_version.hpp>
1212
#include <userver/logging/log.hpp>
@@ -21,101 +21,88 @@ namespace http = clients::http;
2121
struct Config {
2222
std::string log_level = "error";
2323
std::string logfile = "";
24-
size_t count = 1000;
25-
size_t coroutines = 1;
26-
size_t worker_threads = 1;
27-
size_t io_threads = 1;
24+
std::size_t count = 1000;
25+
std::size_t coroutines = 1;
26+
std::size_t worker_threads = 1;
27+
std::size_t io_threads = 1;
2828
long timeout_ms = 1000;
2929
bool multiplexing = false;
30-
size_t max_host_connections = 0;
30+
std::size_t max_host_connections = 0;
3131
USERVER_NAMESPACE::http::HttpVersion http_version = USERVER_NAMESPACE::http::HttpVersion::k11;
3232
std::string url_file;
3333
};
3434

3535
struct WorkerContext {
36-
std::atomic<uint64_t> counter{0};
37-
const uint64_t print_each_counter;
38-
uint64_t response_len;
36+
std::atomic<std::uint64_t> counter{0};
37+
const std::uint64_t print_each_counter;
38+
std::uint64_t response_len;
3939

4040
http::Client& http_client;
4141
const Config& config;
4242
const std::vector<std::string>& urls;
4343
};
4444

45-
Config ParseConfig(int argc, char* argv[]) {
45+
Config ParseConfig(int argc, const char* const argv[]) {
4646
namespace po = boost::program_options;
4747

48-
Config config;
48+
Config c;
4949
po::options_description desc("Allowed options");
50-
desc.add_options()("help,h", "produce help message")(
51-
"log-level",
52-
po::value(&config.log_level)->default_value(config.log_level),
53-
"log level (trace, debug, info, warning, error)"
54-
)("log-file",
55-
po::value(&config.logfile)->default_value(config.logfile),
56-
"log filename (empty for synchronous stderr)")(
57-
"url-file,f", po::value(&config.url_file)->default_value(config.url_file), "input file"
58-
)("count,c", po::value(&config.count)->default_value(config.count), "request count")(
59-
"coroutines", po::value(&config.coroutines)->default_value(config.coroutines), "client coroutine count"
60-
)("worker-threads", po::value(&config.worker_threads)->default_value(config.worker_threads), "worker thread count")(
61-
"io-threads", po::value(&config.io_threads)->default_value(config.io_threads), "io thread count"
62-
)("timeout,t", po::value(&config.timeout_ms)->default_value(config.timeout_ms), "request timeout in ms")(
63-
"multiplexing", "enable HTTP/2 multiplexing"
64-
)("http-version,V", po::value<std::string>(), "http version, possible values: 1.0, 1.1, 2.0-prior"
65-
)("max-host-connections",
66-
po::value(&config.max_host_connections)->default_value(config.max_host_connections),
67-
"maximum HTTP connection number to a single host");
50+
51+
// clang-format off
52+
desc.add_options()
53+
("help,h", "produce help message")
54+
("log-level", po::value(&c.log_level)->default_value(c.log_level), "One of: trace, debug, info, warning, error")
55+
("log-file", po::value(&c.logfile)->default_value(c.logfile), "log filename (empty for synchronous stderr)")
56+
("url-file,f", po::value(&c.url_file)->default_value(c.url_file), "input file")
57+
("count,c", po::value(&c.count)->default_value(c.count), "request count")
58+
("coroutines", po::value(&c.coroutines)->default_value(c.coroutines), "client coroutine count")
59+
("worker-threads", po::value(&c.worker_threads)->default_value(c.worker_threads), "worker thread count")
60+
("io-threads", po::value(&c.io_threads)->default_value(c.io_threads), "IO thread count")
61+
("timeout,t", po::value(&c.timeout_ms)->default_value(c.timeout_ms), "request timeout in ms")
62+
("multiplexing", "enable HTTP/2 multiplexing")
63+
("http-version,V", po::value<std::string>(), "http version, possible values: 1.0, 1.1, 2, 2-prior")
64+
("max-host-connections", po::value(&c.max_host_connections)->default_value(c.max_host_connections)
65+
, "maximum HTTP connection number to a single host")
66+
;
67+
// clang-format on
6868

6969
po::variables_map vm;
7070
po::store(po::parse_command_line(argc, argv, desc), vm);
7171
po::notify(vm);
7272

7373
if (vm.count("help")) {
7474
std::cout << desc << std::endl;
75-
exit(0);
75+
std::exit(0);
7676
}
7777

78-
if (vm.count("multiplexing")) config.multiplexing = true;
78+
c.multiplexing = vm.count("multiplexing");
7979
if (vm.count("http-version")) {
80-
auto value = vm["http-version"].as<std::string>();
81-
if (value == "1.0")
82-
config.http_version = USERVER_NAMESPACE::http::HttpVersion::k10;
83-
else if (value == "1.1")
84-
config.http_version = USERVER_NAMESPACE::http::HttpVersion::k11;
85-
else if (value == "2")
86-
config.http_version = USERVER_NAMESPACE::http::HttpVersion::k2;
87-
else if (value == "2tls")
88-
config.http_version = USERVER_NAMESPACE::http::HttpVersion::k2Tls;
89-
else if (value == "2-prior")
90-
config.http_version = USERVER_NAMESPACE::http::HttpVersion::k2PriorKnowledge;
91-
else {
92-
std::cerr << "--http-version value is unknown" << std::endl;
93-
exit(1);
94-
}
80+
c.http_version = USERVER_NAMESPACE::http::HttpVersionFromString(vm["http-version"].as<std::string>());
9581
}
9682

97-
if (config.url_file.empty()) {
83+
if (c.url_file.empty()) {
9884
std::cerr << "url-file is undefined" << std::endl;
9985
std::cout << desc << std::endl;
100-
exit(1);
86+
std::exit(1);
10187
}
10288

103-
return config;
89+
return c;
10490
}
10591

10692
std::vector<std::string> ReadUrls(const Config& config) {
10793
std::vector<std::string> urls;
10894

10995
std::ifstream infile(config.url_file);
11096
if (!infile.is_open()) {
111-
std::cerr << "failed to open url-file\n";
112-
exit(1);
97+
throw std::runtime_error("Failed to open url-file\n");
11398
}
11499

115100
std::string line;
116101
while (std::getline(infile, line)) urls.emplace_back(std::move(line));
117102

118-
if (urls.empty()) throw std::runtime_error("No URL in URL file!");
103+
if (urls.empty()) {
104+
throw std::runtime_error("No URL in URL file!");
105+
}
119106

120107
return urls;
121108
}
@@ -139,15 +126,15 @@ void Worker(WorkerContext& context) {
139126
const std::string& url = context.urls[idx % context.urls.size()];
140127

141128
try {
142-
auto ts1 = std::chrono::system_clock::now();
129+
const auto ts1 = std::chrono::system_clock::now();
143130
auto request = CreateRequest(context.http_client, context.config, url);
144-
auto ts2 = std::chrono::system_clock::now();
131+
const auto ts2 = std::chrono::system_clock::now();
145132
LOG_DEBUG() << "CreateRequest";
146133

147134
auto response = request.perform();
148135
context.response_len += response->body().size();
149136
LOG_DEBUG() << "Got response body_size=" << response->body().size();
150-
auto ts3 = std::chrono::system_clock::now();
137+
const auto ts3 = std::chrono::system_clock::now();
151138
LOG_INFO() << "timings create=" << std::chrono::duration_cast<std::chrono::microseconds>(ts2 - ts1).count()
152139
<< "us "
153140
<< "response=" << std::chrono::duration_cast<std::chrono::microseconds>(ts3 - ts2).count()
@@ -171,20 +158,20 @@ void DoWork(const Config& config, const std::vector<std::string>& urls) {
171158
http_client.SetMultiplexingEnabled(config.multiplexing);
172159
if (config.max_host_connections > 0) http_client.SetMaxHostConnections(config.max_host_connections);
173160

174-
WorkerContext worker_context{{0}, 2000, 0, std::ref(http_client), config, urls};
161+
WorkerContext worker_context{{0}, 2000, 0, http_client, config, urls};
175162

176163
std::vector<engine::TaskWithResult<void>> tasks;
177164
tasks.resize(config.coroutines);
178-
auto tp1 = std::chrono::system_clock::now();
165+
const auto tp1 = std::chrono::system_clock::now();
179166
LOG_WARNING() << "Creating workers...";
180-
for (size_t i = 0; i < config.coroutines; ++i) {
167+
for (std::size_t i = 0; i < config.coroutines; ++i) {
181168
tasks[i] = engine::AsyncNoSpan(tp, &Worker, std::ref(worker_context));
182169
}
183170
LOG_WARNING() << "All workers are started " << std::this_thread::get_id();
184171

185-
for (auto& task : tasks) task.Get();
172+
engine::GetAll(tasks);
186173

187-
auto tp2 = std::chrono::system_clock::now();
174+
const auto tp2 = std::chrono::system_clock::now();
188175
auto rps = config.count * 1000 / (std::chrono::duration_cast<std::chrono::milliseconds>(tp2 - tp1).count() + 1);
189176

190177
std::cerr << std::endl;
@@ -194,26 +181,21 @@ void DoWork(const Config& config, const std::vector<std::string>& urls) {
194181

195182
} // namespace
196183

197-
int main(int argc, char* argv[]) {
184+
int main(int argc, const char* const argv[]) {
198185
const Config config = ParseConfig(argc, argv);
199186

200-
logging::LoggerPtr logger;
201187
const auto level = logging::LevelFromString(config.log_level);
202-
if (!config.logfile.empty()) {
203-
logger = logging::MakeFileLogger("default", config.logfile, logging::Format::kTskv, level);
204-
} else {
205-
logger = logging::MakeStderrLogger("default", logging::Format::kTskv, level);
206-
}
207-
logging::DefaultLoggerGuard guard{logger};
188+
const logging::DefaultLoggerGuard guard{
189+
config.logfile.empty() ? logging::MakeStderrLogger("default", logging::Format::kTskv, level)
190+
: logging::MakeFileLogger("default", config.logfile, logging::Format::kTskv, level)};
208191

209192
LOG_WARNING() << "Starting using requests=" << config.count << " coroutines=" << config.coroutines
210193
<< " timeout=" << config.timeout_ms << "ms";
211194
LOG_WARNING() << "multiplexing =" << (config.multiplexing ? "enabled" : "disabled")
212195
<< " max_host_connections=" << config.max_host_connections;
213196

214197
const std::vector<std::string> urls = ReadUrls(config);
215-
216198
engine::RunStandalone(config.worker_threads, [&]() { DoWork(config, urls); });
217199

218-
LOG_WARNING() << "Exit";
200+
LOG_WARNING() << "Finished";
219201
}

universal/include/userver/http/http_version.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
/// @file userver/http/http_version.hpp
44
/// @brief @copybrief http::HttpVersion
55

6+
#include <string_view>
7+
68
#include <userver/yaml_config/fwd.hpp>
79

810
USERVER_NAMESPACE_BEGIN
@@ -19,6 +21,10 @@ enum class HttpVersion {
1921
k2PriorKnowledge, ///< HTTP/2 only (without Upgrade)
2022
};
2123

24+
std::string_view ToString(HttpVersion version);
25+
26+
HttpVersion HttpVersionFromString(std::string_view version);
27+
2228
HttpVersion Parse(const yaml_config::YamlConfig& value, formats::parse::To<HttpVersion>);
2329

2430
} // namespace http

universal/src/http/http_version.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,30 @@ USERVER_NAMESPACE_BEGIN
77

88
namespace http {
99

10+
static constexpr utils::TrivialBiMap kMap([](auto selector) {
11+
return selector()
12+
.Case(HttpVersion::kDefault, "default")
13+
.Case(HttpVersion::k10, "1.0")
14+
.Case(HttpVersion::k11, "1.1")
15+
.Case(HttpVersion::k2, "2")
16+
.Case(HttpVersion::k2Tls, "2tls")
17+
.Case(HttpVersion::k2PriorKnowledge, "2-prior");
18+
});
19+
20+
std::string_view ToString(HttpVersion version) { return utils::impl::EnumToStringView(version, kMap); }
21+
22+
HttpVersion HttpVersionFromString(std::string_view version) {
23+
const auto result = kMap.TryFindBySecond(version);
24+
if (result.has_value()) {
25+
return *result;
26+
}
27+
28+
throw std::runtime_error(
29+
fmt::format("Invalid enum value ({}) for HttpVersion. Allowed values: {}", version, kMap.DescribeSecond())
30+
);
31+
}
32+
1033
HttpVersion Parse(const yaml_config::YamlConfig& value, formats::parse::To<HttpVersion>) {
11-
static constexpr utils::TrivialBiMap kMap([](auto selector) {
12-
return selector().Case(HttpVersion::k2, "2").Case(HttpVersion::k11, "1.1");
13-
});
1434
return utils::ParseFromValueString(value, kMap);
1535
}
1636

0 commit comments

Comments
 (0)