[ ] 13 Highload Cup 2017 |
11 Mail.Ru HighloadCup backend-.
: , 4 , 4 , 10 HDD, api, . . - phantom.
, 13 , .
Api 3 get- , 2 get- , 3 post- , 3 post- .
, :
3 : get- , post- / , get- . , .. , , . 100 2000rps.
, , .. keep-alive, - , get- keep-alive, post- .
Linux, C++ ( ), .
.. highload'e , , , web- , web-. proxygen. , , facebook?
HTTPServerOptions options;
options.threads = static_cast(FLAGS_threads);
options.idleTimeout = std::chrono::milliseconds(60000);
options.shutdownOn = {SIGINT, SIGTERM};
options.enableContentCompression = false;
options.handlerFactories = RequestHandlerChain()
.addThen()
.build();
HTTPServer server(std::move(options));
server.bind(IPs);
// Start HTTPServer mainloop in a separate thread
std::thread t([&] () {
server.start();
});
class EchoHandlerFactory : public RequestHandlerFactory {
public:
// ...
RequestHandler* onRequest(RequestHandler*, HTTPMessage*) noexcept override {
return new EchoHandler(stats_.get());
}
// ...
private:
folly::ThreadLocalPtr stats_;
};
new EchoHandler()
, .
EchoHandler
proxygen::RequestHandler
:
class EchoHandler : public proxygen::RequestHandler {
public:
void onRequest(std::unique_ptr headers)
noexcept override;
void onBody(std::unique_ptr body) noexcept override;
void onEOM() noexcept override;
void onUpgrade(proxygen::UpgradeProtocol proto) noexcept override;
void requestComplete() noexcept override;
void onError(proxygen::ProxygenError err) noexcept override;
};
, .
std::regex
, . std::stringstream
. , .
, !
:
struct Location {
std::string place;
std::string country;
std::string city;
uint32_t distance = 0;
std::string Serialize(uint32_t id) const {
std::stringstream data;
data <<
"{" <<
"\"id\":" << id << "," <<
"\"place\":\"" << place << "\"," <<
"\"country\":\"" << country << "\"," <<
"\"city\":\"" << city << "\"," <<
"\"distance\":" << distance <<
"}";
return std::move(data.str());
}
};
" " :
template
class InMemoryStorage {
public:
typedef std::unordered_map Map;
InMemoryStorage();
bool Add(uint32_t id, T&& data, T** pointer);
T* Get(uint32_t id);
private:
std::vector> buckets_;
std::vector
: , , 32- , (, !). BUCKETS_COUNT (=10) , .
.. , , , , , users -> visits
locations -> visits
.
, :
template
class MultiIndex {
public:
MultiIndex() : buckets_(BUCKETS_COUNT), bucket_mutexes_(BUCKETS_COUNT) {
}
void Add(uint32_t id, T* pointer) {
int bucket_id = id % BUCKETS_COUNT;
std::lock_guard lock(bucket_mutexes_[bucket_id]);
buckets_[bucket_id].insert(std::make_pair(id, pointer));
}
void Replace(uint32_t old_id, uint32_t new_id, T* val) {
int bucket_id = old_id % BUCKETS_COUNT;
{
std::lock_guard lock(bucket_mutexes_[bucket_id]);
auto range = buckets_[bucket_id].equal_range(old_id);
auto it = range.first;
while (it != range.second) {
if (it->second == val) {
buckets_[bucket_id].erase(it);
break;
}
++it;
}
}
bucket_id = new_id % BUCKETS_COUNT;
std::lock_guard lock(bucket_mutexes_[bucket_id]);
buckets_[bucket_id].insert(std::make_pair(new_id, val));
}
std::vector GetValues(uint32_t id) {
int bucket_id = id % BUCKETS_COUNT;
std::lock_guard lock(bucket_mutexes_[bucket_id]);
auto range = buckets_[bucket_id].equal_range(id);
auto it = range.first;
std::vector result;
while (it != range.second) {
result.push_back(it->second);
++it;
}
return std::move(result);
}
private:
std::vector> buckets_;
std::vector bucket_mutexes_;
};
, , , Insomnia, .
- , , , . , , - ...
2000rps. , , .
, , . , json, , , proxygen .
void onEOM() noexcept override {
proxygen::ResponseBuilder(downstream_)
.status(200, "OK")
.header("Content-Type", "application/json")
.body("{}")
.sendWithEOM();
}
3- .
, , : ? , - .
Crow. , , http-, . header-based , proxygen, , .
:
crow::SimpleApp app;
CROW_ROUTE(app, "/users/").methods("GET"_method, "POST"_method) (
[](const crow::request& req, crow::response& res, uint32_t id) {
if (req.method == crow::HTTPMethod::GET) {
get_handlers::GetUsers(req, res, id);
} else {
post_handlers::UpdateUsers(req, res, id);
}
});
app.bindaddr("0.0.0.0").port(80).multithreaded().run();
, api, 404, , uint- .
, , , , . , , json , .
Crow , , .
.. , . !
100 -, , .
.. std::stringstream
, . , , .
:
write()
, , . , unix timestamp, : fromAge=30&toAge=70
. ? ? 29 ?
, :
static time_t t = g_generate_time; // get time now
static struct tm now = (*localtime(&t));
if (search_flags & QueryFlags::FROM_AGE) {
tm from_age_tm = now;
from_age_tm.tm_year -= from_age;
time_t from_age_t = mktime(&from_age_tm);
if (user->birth_date > from_age_t) {
continue;
}
}
, 100 50 .
, 20 , - 20-40 .
:
- , , , . , .
/. , 2 : , .
, , web-. , .
.
:
, - read() write() , , " ".
: ccept() , epoll, std::thread::hardware_concurrency()
epollfd .
unsigned int thread_nums = std::thread::hardware_concurrency();
for (unsigned int i = 0; i < thread_nums; ++i) {
threads.push_back(std::thread([epollfd, max_events]() {
epoll_event events[max_events];
int nfds = 0;
while (true) {
nfds = epoll_wait(epollfd, events, max_events, 0);
// ...
for (int n = 0; n < nfds; ++n) {
// ...
}
}
}));
}
while (true) {
int sock = accept(listener, NULL, NULL);
// ...
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
ev.data.fd = sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
}
EPOLLET
, - , , epoll
, EAGAIN
. , . , read()
, , epoll
.
: std::thread::hardware_concurrency()
, , 10 ( ), 4 . , .
http http-parser (Crow ), libyuarel, query- qs_parse. qs_parse Crow, , , .
, 10 , 40 .
, , 200 2000rps , 5 : 1 , 10000rps 3- .
, , , write(), , , writev(): ( : writev 1024 iovec, /users/
iovec-, 2 ).
, ( ) 245 , 2 .
, , 2 , , , .
:
DBInstance::GetDbInstance()->GetLocations()
g_location_storage
, http- get- , get- . , . , ( \0
, ). libyuarel.
HttpData http_data;
if (buf[0] == 'G') {
char* url_start = buf + 4;
http_data.url = url_start;
char* it = url_start;
int url_len = 0;
while (*it++ != ' ') {
++url_len;
}
http_data.url_length = url_len;
http_data.method = HTTP_GET;
} else {
http_parser_init(parser.get(), HTTP_REQUEST);
parser->data = &http_data;
int nparsed = http_parser_execute(
parser.get(), &settings, buf, readed);
if (nparsed != readed) {
close(sock);
continue;
}
http_data.method = parser->method;
}
Route(http_data);
, , , :
const char* header = "HTTP/1.1 200 OK\r\n"
"S: b\r\n"
"C: k\r\n"
"B: a\r\n"
"Content-Length: ";
, , . , , - .
: https://github.com/evgsid/highload_solution
: https://github.com/evgsid/highload_solution/tree/final
.
6 : ~140 , ~ 190 .
, 6 - .
sendfile mmap, userspace -> kernelspace, .
, , : BUSY WAIT.
, , 180 epoll(x, y, z, -1)
epoll(x, y, z, 0)
150 . , -, .
: How to achieve low latency with 10Gbps Ethernet.
, 188 , busy wait 136 , 4- , 8- .
:
, busy wait . , , 4 , busy wait post-, accept' , . 3 .
, .
! 3 . , , . , , . , .
!