-

   rss_rss_hh_new

 - e-mail

 

 -

 LiveInternet.ru:
: 17.03.2011
:
:
: 51

:


[ ] 13 Highload Cup 2017

, 12 2017 . 10:59 +
reatfly 10:59

13 Highload Cup 2017

Image


11 Mail.Ru HighloadCup backend-.


: , 4 , 4 , 10 HDD, api, . . - phantom.


, 13 , .



, . , , .


Api 3 get- , 2 get- , 3 post- , 3 post- .


, :


  • ( loopback)
  • post- (.. )
  • post- get- ( get-, 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 bucket_indexes_;
  std::vector bucket_mutexes_;
};

template 
InMemoryStorage::InMemoryStorage()
    : buckets_(BUCKETS_COUNT), bucket_indexes_(BUCKETS_COUNT),
      bucket_mutexes_(BUCKETS_COUNT) {
}

template 
bool InMemoryStorage::Add(uint32_t id, T&& data, T** pointer) {
  int bucket_id = id % BUCKETS_COUNT;
  std::lock_guard lock(bucket_mutexes_[bucket_id]);

  Map& bucket_index = bucket_indexes_[bucket_id];
  auto it = bucket_index.find(id);
  if (it != bucket_index.end()) {
    return false;
  }

  buckets_[bucket_id].emplace_front(data);
  bucket_index.emplace(id, &buckets_[bucket_id].front());
  if (pointer)
    *pointer = &buckets_[bucket_id].front();
  return true;
}

template 
T* InMemoryStorage::Get(uint32_t id) {
  int bucket_id = id % BUCKETS_COUNT;
  std::lock_guard lock(bucket_mutexes_[bucket_id]);

  Map& bucket = bucket_indexes_[bucket_id];
  auto it = bucket.find(id);
  if (it != bucket.end()) {
    return it->second;
  }
  return nullptr;
}

: , , 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, .


, , .


Highload,


- , , , . , , - ...


first attempt


graph


2000rps. , , .


, , . , json, , , proxygen .


void onEOM() noexcept override {
  proxygen::ResponseBuilder(downstream_)
    .status(200, "OK")
    .header("Content-Type", "application/json")
    .body("{}")
    .sendWithEOM();
}

3- .


proxygen fail


, , : ? , - .


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 , , .


Crow win


.. , . !


Crow first result


100 -, , .


.. std::stringstream, . , , .
:


  • write()
  • 200,

, , . , 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 .


:


  • - 1
  • 1 Users Locations, 10 Visits.

, , , , . ( , ).


- , , , . , .


/. , 2 : , .


I want to ride my bicycle...


, , web-. , .


.
:


  • loopback,
  • , read()
  • , write(),
  • get- post-

, - 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 .


MOAR HIGHLOAD


, , 200 2000rps , 5 : 1 , 10000rps 3- .


, , , write(), , , writev(): ( : writev 1024 iovec, /users//locations 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: ";

, , . , , - .


, 197 , 16 , 188 , 13 .


final


: https://github.com/evgsid/highload_solution


: https://github.com/evgsid/highload_solution/tree/final


Magic pill


.


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- .


:


ideal


Disclaimer

, busy wait . , , 4 , busy wait post-, accept' , . 3 .



, .


! 3 . , , . , , . , .


!

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/337710/

:  

: [1] []
 

:
: 

: ( )

:

  URL