
8æ11æ¥ãMail.Ruã¯æ¬¡ã®HighloadCupãçºè¡šããŸã ã·ã¹ãã ããã°ã©ã㌠ããã¯ãšã³ãéçºè
ã
ç°¡åã«èšããšãã¿ã¹ã¯ã¯æ¬¡ã®ãšããã§ããïŒdockerã4ã³ã¢ã4GBã®ã¡ã¢ãªã10GBã®HDDã apiã®ã»ãã ããããŠããªãã¯æçæéã§ã¯ãšãªã«çããå¿
èŠããããŸãã èšèªãšãã¯ãããžãŒã®ã¹ã¿ãã¯ã¯ç¡å¶éã§ãã ãã¡ã³ãã ãšã³ãžã³ãæèŒããYandexã¿ã³ã¯ããã¹ãã·ã¹ãã ã§ããã
ãã®ãããªç¶æ³ã§ãã¡ã€ãã«ã§13äœã«éããæ¹æ³ã«ã€ããŠã¯ããã®èšäºã«ãªããŸãã
åé¡ã®å£°æ
ã¿ã¹ã¯ã®è©³çްãªèª¬æã¯ãããã®åå è
ã® 1人ã®åºçç© ããŸãã¯ç«¶æã®å
¬åŒããŒãžã«ãããŸãã ãã¹ãã®å®è¡æ¹æ³ã¯ããã«æžãããŠããŸã ã
Apiã¯ãããŒã¿ããŒã¹ããåçŽã«ã¬ã³ãŒããè¿ãããã®3ã€ã®getãªã¯ãšã¹ããããŒã¿éçŽã®ããã®2ã€ã®getãªã¯ãšã¹ããããŒã¿å€æŽã®ããã®3ã€ã®postãªã¯ãšã¹ããããã³ããŒã¿ã远å ããããã®3 postãªã¯ãšã¹ãã§æ§æãããŸããã
æ¬¡ã®æ¡ä»¶ãããã«åæãããçæŽ»ã倧ããä¿é²ããŸããã
- ãããã¯ãŒã¯æå€±ãªãïŒããŒã¿ã¯ã«ãŒãããã¯ãééããŸãïŒ
- ã¡ã¢ãªã«åãŸãããšãä¿èšŒãããããŒã¿
- ç«¶åããæçš¿ãªã¯ãšã¹ãã¯ãããŸããïŒã€ãŸããåãã¬ã³ãŒããåæã«å€æŽããããšããããšã¯ãããŸããïŒ
- ãªã¯ãšã¹ãã®æçš¿ãšååŸãåæã«è¡ãããããšã¯ãããŸããïŒãªã¯ãšã¹ãã®ååŸã®ã¿ãŸãã¯ãªã¯ãšã¹ãã®æçš¿ã®ã¿ïŒ
- ããŒã¿ã¯åé€ãããã倿ŽãŸãã¯è¿œå ãããã ãã§ã
ãªã¯ãšã¹ãã¯3ã€ã®éšåã«åããããŸããããŸãããœãŒã¹ããŒã¿ã«å¯Ÿããget-requestãæ¬¡ã«ããŒã¿ã®è¿œå /倿Žã«å¯Ÿããäžé£ã®post-requestã倿ŽãããããŒã¿ã«å¯ŸããæåŸã®æã匷åãªäžé£ã®get-requestã§ãã ç¬¬äºæ®µéã¯éåžžã«éèŠã§ããããªããªã 誀ã£ãŠããŒã¿ã远å ãŸãã¯å€æŽãããšã第3ãã§ãŒãºã§å€ãã®ãšã©ãŒãçºçããå¯èœæ§ããããçµæãšããŠå€§ããªçœ°éã«ãªããŸãã ãã®æ®µéã§ã¯ãæåŸã®æ®µéã§ã¯ããªã¯ãšã¹ãæ°ã100ãã2000rpsã«çŽç·çã«å¢å ããŸããã
å¥ã®æ¡ä»¶ã¯ã1ã€ã®èŠæ±ã1ã€ã®æ¥ç¶ã§ãããšããããšã§ããã ããŒãã¢ã©ã€ãã¯ãããŸããããããæç¹ã§æåŠããŸãããget-requestã¯ãã¹ãŠããŒãã¢ã©ã€ãããã®ãã®ã§ããã¹ããªã¯ãšã¹ãã¯ããããæ°ããæ¥ç¶ã«å¯Ÿãããã®ã§ããã
ç§ã®å€¢ã¯ãLinuxãC ++ãããã³ã·ã¹ãã ããã°ã©ãã³ã°ïŒçŸå®ã¯ã²ã©ããã®ã§ããïŒã§ãããç§èªèº«ã¯äœãåŠå®ãããé ããé£ã³èŸŒãããšã«ããŸããã
è¡ãã
ãªããªã é«è² è·ã«ã€ããŠã¯äœãç¥ããªããããå°ãç¥ã£ãŠããŸããããæè¿ãŸã§ãŠã§ããµãŒããŒãäœæããå¿
èŠããªãããšãæãã§ããŸããããåé¡ã解決ããããã®æåã®ã¹ãããã¯é©åãªãŠã§ããµãŒããŒãèŠã€ããããšã§ããã ç§ã®èŠç·ã¯proxygenãã€ãã¿ãŸããã äžè¬çã«ããµãŒããŒã¯è¯å¥œã§ãããšæ³å®ãããŠããŸãã-Facebookãšåãããããã€ãã€ãã«ã€ããŠç¥ã£ãŠãã人ã¯ä»ã«ããŸããïŒ
ãœãŒã¹ã³ãŒãã«ã¯ããã®ãµãŒããŒã®äœ¿ç𿹿³ã®äŸãããã€ãå«ãŸããŠããŸãã
HTTPServerOptions options; options.threads = static_cast<size_t>(FLAGS_threads); options.idleTimeout = std::chrono::milliseconds(60000); options.shutdownOn = {SIGINT, SIGTERM}; options.enableContentCompression = false; options.handlerFactories = RequestHandlerChain() .addThen<EchoHandlerFactory>() .build(); HTTPServer server(std::move(options)); server.bind(IPs);
ãããŠãåãå
¥ããããæ¥ç¶ããšã«ããã¡ã¯ããªã¡ãœãããåŒã³åºãããŸã
class EchoHandlerFactory : public RequestHandlerFactory { public: // ... RequestHandler* onRequest(RequestHandler*, HTTPMessage*) noexcept override { return new EchoHandler(stats_.get()); }
new EchoHandler()
ããã¹ãŠã®ãªã¯ãšã¹ãã«å¯ŸããŠnew EchoHandler()
ããç§ã¯ãããéèŠèŠããŸããã§ããã
EchoHandler
èªäœã¯ã proxygen::RequestHandler
å®è£
ããå¿
èŠãããproxygen::RequestHandler
ã
class EchoHandler : public proxygen::RequestHandler { public: void onRequest(std::unique_ptr<proxygen::HTTPMessage> headers) noexcept override; void onBody(std::unique_ptr<folly::IOBuf> 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; };
ãã¹ãŠãçŸããçŸããèŠããŸãããåä¿¡ããŒã¿ãåŠçããã ãã§ãã
APIã»ããã¯åçŽã§å°ããããã 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 T> class InMemoryStorage { public: typedef std::unordered_map<uint32_t, T*> Map; InMemoryStorage(); bool Add(uint32_t id, T&& data, T** pointer); T* Get(uint32_t id); private: std::vector<std::forward_list<T>> buckets_; std::vector<Map> bucket_indexes_; std::vector<std::mutex> bucket_mutexes_; }; template <class T> InMemoryStorage<T>::InMemoryStorage() : buckets_(BUCKETS_COUNT), bucket_indexes_(BUCKETS_COUNT), bucket_mutexes_(BUCKETS_COUNT) { } template <class T> bool InMemoryStorage<T>::Add(uint32_t id, T&& data, T** pointer) { int bucket_id = id % BUCKETS_COUNT; std::lock_guard<std::mutex> 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 <class T> T* InMemoryStorage<T>::Get(uint32_t id) { int bucket_id = id % BUCKETS_COUNT; std::lock_guard<std::mutex> 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ãããæ°ã§ããã€ãŸãããã®ç¯å²å
ã§ä»»æã®ã€ã³ããã¯ã¹ãæã€ããŒã¿ã远å ããå¿
èŠã¯ãããŸããïŒããããªããŠééã£ãŠããã®ã§ãããïŒïŒã ãããã£ãŠãã¹ã¬ããã®mutexã®ã¿ã€ã ã¢ãŠããæžããããã«ãBUCKETS_COUNTïŒ= 10ïŒã¹ãã¬ãŒãžããããŸããã
ãªããªã ããŒã¿ãµã³ããªã³ã°ã®ãªã¯ãšã¹ããããããŠãŒã¶ãŒãusers -> visits
ãã¹ãŠã®å Žæãããã³ãã®å Žæã«æ®ã£ããã¹ãŠã®ã¬ãã¥ãŒããã°ããæ€çŽ¢ããå¿
èŠãããã users -> visits
ãšlocations -> visits
ã€ã³ããã¯ã¹ãå¿
èŠã§ããã
åãã€ããªãã®ãŒã§ãã€ã³ããã¯ã¹çšã«æ¬¡ã®ã³ãŒããäœæãããŸããã
template<class T> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(bucket_mutexes_[bucket_id]); buckets_[bucket_id].insert(std::make_pair(new_id, val)); } std::vector<T*> GetValues(uint32_t id) { int bucket_id = id % BUCKETS_COUNT; std::lock_guard<std::mutex> lock(bucket_mutexes_[bucket_id]); auto range = buckets_[bucket_id].equal_range(id); auto it = range.first; std::vector<T*> result; while (it != range.second) { result.push_back(it->second); ++it; } return std::move(result); } private: std::vector<std::unordered_multimap<uint32_t, T*>> buckets_; std::vector<std::mutex> bucket_mutexes_; };
åœåããªãŒã¬ãã€ã¶ãŒã¯ããŒã¿ããŒã¹ãåæåãããã¹ãããŒã¿ã®ã¿ãã¬ã€ã¢ãŠãããŸãããããµã³ãã«ãªã¯ãšã¹ãã¯ãªãã£ãããã Insomniaã䜿çšããŠã³ãŒãã®ãã¹ããéå§ããŸãããããã«ããããªã¯ãšã¹ãã®éä¿¡ãšå€æŽãããã³åçã®ç¢ºèªã容æã«ãªããŸããã
å°ãåŸã«ãäž»å¬è
ã¯åå è
ã«åæ
ããæŠè»ã®åŒŸè¬ãšå®å
šãªè©äŸ¡ãšç ²æããŒã¿ãæçš¿ããŸãããç§ã¯ç°¡åãªã¹ã¯ãªãããæžããŠãããŒã«ã«ã§ãœãªã¥ãŒã·ã§ã³ã®æ£ç¢ºæ§ããã¹ãã§ããããã«ããå°æ¥çã«éåžžã«åœ¹ç«ã¡ãŸããã
é«è² è·ã¹ã¿ãŒã
ãããŠæåŸã«ããããã¿ã€ãã宿ããããŒã«ã«ã§ã¯ãã¹ã¿ãŒããã¹ãŠã¯åé¡ãªããšèšããæ±ºå®ãéä¿¡ããæãæ¥ãŸããã æåã®æã¡äžãã¯éåžžã«åºæ¿çã§ãäœããééã£ãŠããããšã瀺ããŸãã...


ç§ã®ãµãŒããŒã¯2000rpsã®è² è·ãä¿æããŠããŸããã§ããã ç§ãèŠããŠããéãããã®æç¹ã§ã®ãªãŒããŒã¯çŽæ°çŸç§ã§ããã
次ã«ããµãŒããŒãè² è·ã«ãŸã£ãã察åŠããŠãããã©ããããŸãã¯ããããå®è£
ã®åé¡ã§ãããã©ããã確èªããããšã«ããŸããã ç§ã¯ãã¹ãŠã®ãªã¯ãšã¹ãã«ç©ºã®jsonãåã«äžããè©äŸ¡æ»æãéå§ããproxygenèªäœãè² è·ã«å¯ŸåŠã§ããªãããšã確èªããã³ãŒããæžããŸããã
void onEOM() noexcept override { proxygen::ResponseBuilder(downstream_) .status(200, "OK") .header("Content-Type", "application/json") .body("{}") .sendWithEOM(); }
ãããã3çªç®ã®ãã§ãŒãºãã£ãŒãã®å€èгã§ãã

äžæ¹ã§ããããç§ã®ã³ãŒãã®åé¡ã§ã¯ãªãããšã¯è¯ãã£ãã®ã§ããã仿¹ã§ã¯ããµãŒããŒã§äœããã¹ãããšããçåãçããŸããã ç§ã¯ãŸã èªåã§æžãã®ãé¿ããããšæã£ãŠããŸããã
次ã®ãã¹ãã¯ã«ã©ã¹ã§ããã ç§ã¯ãããæ¬åœã«å¥œãã ã£ããšããã«èšããªããã°ãªããŸããããããŠãçªç¶ãå°æ¥ãç§ããã©ã¹http-serverãå¿
èŠãšãããªãã°ãããã¯åœŒã§ãã ããããŒããŒã¹ã®ãµãŒããŒãproxygenã®ä»£ããã«ãããžã§ã¯ãã«è¿œå ãããªã¯ãšã¹ããã³ãã©ãŒãå°ãæžãçŽããŠãæ°ãããµãŒããŒã§åäœããããã«ããŸããã
äœ¿ãæ¹ã¯ãšãŠãç°¡åã§ãã
crow::SimpleApp app; CROW_ROUTE(app, "/users/<uint>").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ãè¿ããè©äŸ¡æ»æã®ããã«éä¿¡ããããšãèšè¿°ããŸããã
ã«ã©ã¹ã管çãã圌ã¯è² è·ãä¿æããä»ç§ã¯ç§ã®ããžãã¯ã远å ããå¿
èŠããããŸããã

ãªããªã ããžãã¯ã¯æ¢ã«èšè¿°ãããŠãããããã³ãŒããæ°ãããµãŒããŒã«é©å¿ãããã®ã¯éåžžã«ç°¡åã§ããã ãã¹ãŠãããŸããããŸããïŒ

100ç§ã¯ãã§ã«äœããããŸãããµãŒããŒæ€çŽ¢ã§ã¯ãªããããžãã¯ã®æé©åãéå§ã§ããŸãã
ãªããªã ç§ã®ãµãŒããŒã¯ãŸã std::stringstream
ã䜿çšããŠå¿çãçæããŠããŸããããæåã¯ãããåãé€ãããšã«ããŸããã ã¬ã³ãŒããããŒã¿ããŒã¹ã«è¿œå ããæç¹ã§ãããããŒä»ãã®å®å
šãªå¿çãå«ãè¡ãããã«åœ¢æãããèŠæ±æã«è¿ãããŸããã
ãã®æç¹ã§ã次ã®2ã€ã®çç±ã«ãããããããŒãå®å
šã«è¿œå ããããšã«ããŸããã
- äœåãª
write()
ãåé€ãããŸã - ããŒã¿ã¯ããã200MBã§ãååãªã¡ã¢ãªããããŸãã
é»å ±ã®ãã£ããã«ãŒã ã§ç§èªèº«ãå€ãã®ã³ããŒãå£ããå¥ã®åé¡ã¯ã幎霢ã«ãããŠãŒã¶ãŒã®ãã£ã«ã¿ãŒã§ãã ãã®åé¡ã«ãããšããŠãŒã¶ãŒã®å¹Žéœ¢ã¯UNIXã¿ã€ã ã¹ã¿ã³ãã«ä¿åããããªã¯ãšã¹ãã§ã¯fromAge=30&toAge=70
ã®ããã«äžž1幎ã®åœ¢åŒã§æ ŒçŽãããŠããŸããã 幎ã¯ç§ã«ã©ã®ããã«ã€ãªããã®ã§ããïŒ ãããå¹Žãæ€èšãããã©ããïŒ ãŸãããŠãŒã¶ãŒã2æ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ç§ã«2åã«å¢å ããŸããã
äžèŠæªãã¯ãããŸãããããã®æç¹ã§ãªãŒããŒã¯20ç§æªæºã§ãçµæã¯20ã40äœã§ããã
ãã®æç¹ã§ãããã«2ã€ã®èгå¯ãè¡ãããŸããã
- ããŒã¿ã€ã³ããã¯ã¹ã¯åžžã«ã®ã£ãããªãã§1ããé ã«ãªããŸãã
- ã€ã³ããã¯ã¹ãµã€ãºã¯ããŠãŒã¶ãŒãšå Žæã§çŽ1Mã蚪åã§çŽ10Mã§ããã
ããŒã¿ã¹ãã¬ãŒãžçšã®ããã·ã¥ããã¥ãŒããã¯ã¹ããã±ããã¯äžèŠã§ãããããŒã¿ã¯ãã¯ã¿ãŒå
ã®ã€ã³ããã¯ã¹ã«ãã£ãŠå®å
šã«ä¿åã§ããããšãæããã«ãªããŸããã æçµããŒãžã§ã³ã¯ããã«ãããŸã ïŒã€ã³ããã¯ã¹ãåŠçããããã®ã³ãŒãã®äžéšã¯ãããããçªç¶å¶éãè¶
ããå Žåã«æçµã«è¿œå ãããŸããïŒã
ç§ã®ããžãã¯ã®ããã©ãŒãã³ã¹ã«åŒ·ã圱é¿ããæãããªç¬éã¯ãªãããã«èŠããŸããã ã»ãšãã©ã®å Žåãæé©åã®å©ããåããŠãããã«æ°ç§é
ããå¯èœæ§ããããŸãããååã¯æšãŠãªããã°ãªããŸããã§ããã
åã³ãç§ã¯ãããã¯ãŒã¯/ãµãŒããŒã§ã®äœæ¥ã«äŒã¿å§ããŸããã ãµãŒããŒã®ãœãŒã¹ãé§ãæããŠãæ®å¿µãªçµè«ã«è³ããŸãã-éä¿¡æã«ã2ã€ã®äžå¿
èŠãªããŒã¿ã³ããŒãçºçããŸãããæåã«ãµãŒããŒã®å
éšãããã¡ãŒãžã次ã«éä¿¡ãããã¡ãŒãžã
èªè»¢è»ã«ä¹ããã...
ããªãã®ãŠã§ããµãŒããŒãæžãå§ããããšã»ã©ãäžçã§ç¡åã§ç¡è²¬ä»»ã§äžé埳ãªãã®ã¯ãããŸããã ãããŠãç§ã¯ããã«ããã«çªå
¥ããããšãç¥ã£ãŠããŸããã
ãããŠããã®ç¬éãæ¥ãŸããã
ãµãŒããŒãäœæããéã«ãããã€ãã®ä»®å®ãè¡ãããŸããã
- ã¿ã³ã¯ã¯ã«ãŒãããã¯ãä»ããŠçºç«ããŸããã€ãŸããæå€±ã¯ãããŸããã
- ã¿ã³ã¯ããã®ããã±ãŒãžã¯éåžžã«å°ããããã1åã®èªã¿åãã§å·®ãåŒãããšãã§ããŸãïŒïŒ
- ç§ã®çããå°ããã®ã§ããããã¯writeïŒïŒã®1åã®åŒã³åºãã§èšé²ãããæžã蟌ã¿åŒã³åºãã¯ãããã¯ãããŸãã
- æå¹ãªgetããã³postãªã¯ãšã¹ãã®ã¿ãã¿ã³ã¯ããéä¿¡ãããŸã
äžè¬ã«ãããã€ãã®éšåã§readïŒïŒããã³writeïŒïŒãæ£çŽã«ãµããŒãããããšã¯ã§ããŸããããçŸåšã®ããŒãžã§ã³ã¯æ©èœãããããããã¯ãåŸã§ãã®ãŸãŸã§ããã
äžé£ã®å®éšã®åŸãã¡ã€ã³ã¹ã¬ããã§acceptïŒïŒããããã¯ããepollã«æ°ãããœã±ããã远å ãã1ã€ã®epollfdã§ãªãã¹ã³ããŠããŒã¿ãåŠçããstd::thread::hardware_concurrency()
ã¹ã¬ããããããã¯ããŸããã
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
ã¯ã1ã€ã®ã¹ããªãŒã ã®ã¿ãEPOLLET
ãããããšãä¿èšŒããŸãããŸãããœã±ããã«æªèªããŒã¿ãããå Žåããã¹ãŠãEAGAIN
åã«èªã¿åããããŸã§ã epoll
ã¯ãœã±ããã§åäœããŸããã ãããã£ãŠããã®ãã©ã°ã䜿çšããããã®æ¬¡ã®æšå¥šäºé
ã¯ããœã±ãããéããããã³ã°ã«ãããšã©ãŒãè¿ããããŸã§èªã¿åãããšã§ãã ããããä»®å®ã«ç€ºãããŠããããã«ãèŠæ±ã¯å°ããã read()
1åã®åŒã³åºãã§read()
ããŸãããœã±ããã«ã¯ããŒã¿ããªãã epoll
ã¯éåžžãæ°ããããŒã¿ãå°çãããšãã«æ©èœããŸããã
ããã§1ã€ã®ééããç¯ããŸããïŒ std::thread::hardware_concurrency()
ã䜿çšããŠäœ¿çšå¯èœãªã¹ã¬ããã®æ°ã決å®ããŸãããããã®åŒã³åºãã¯10ïŒãµãŒããŒäžã®ã³ã¢ã®æ°ïŒãè¿ããDockerã§ã¯4ã€ã®ã³ã¢ãã䜿çšã§ããªãã£ããããããã¯æªãèãã§ããã ãã ããããã¯çµæã«ã»ãšãã©åœ±é¿ããŸããã§ããã
httpã®è§£æã«ã¯ã http-parser ïŒCrowã䜿çšïŒãurl- libyuarelã®è§£æãããã³query-request- qs_parseã®ãã©ã¡ãŒã¿ãŒã®ãã³ãŒãã䜿çšããŸããã qs_parseã¯Crowã§ã䜿çšãããURLãè§£æã§ããŸãããããŸããŸãã©ã¡ãŒã¿ãŒã®ãã³ãŒãã«ã®ã¿äœ¿çšããŸããã
ãã®æ¹æ³ã§å®è£
ãæžãçŽããåŸããªããšã10ç§ãæããããšãã§ããä»ã§ã¯40ç§ã«ãªããŸããã
ã¢ãŒã¢é«è² è·
ç«¶æäŒã®çµãããŸã§1é±éåãæ®ã£ãŠããŸããããäž»å¬è
ã¯200 MBã®ããŒã¿ãš2000 rpã§ååã§ã¯ãªããšå€æããããŒã¿ãµã€ãºãšè² è·ã5åã«å¢ãããŸããïŒããŒã¿ã¯ã¢ã³ããã¯åœ¢åŒã§1 GBãå æãå§ãã第3ãã§ãŒãºã§çºç«çã¯10,000 rpsã«å¢å ããŸããã
å
šäœã®çããä¿æããå®è£
ã¯ãã¡ã¢ãªã«å
¥ãã®ããããéšåçã«çããæžãããã®å€ãã®writeïŒïŒåŒã³åºããè¡ãããšãæªãèãã®ããã«æãããç§ã¯ writevïŒïŒã䜿çšããæ±ºå®ãæžãçŽããŸãã ïŒã¹ãã¬ãŒãžäžã«ããŒã¿ãè€è£œããå¿
èŠã¯ãããŸããã§ããèšé²ã¯1ã€ã®ã·ã¹ãã ã³ãŒã«ã§è¡ãããŸããïŒæçµçã®æ¹åã远å ãããŸããïŒwritevã¯iovecé
åã®1024èŠçŽ ãäžåºŠã«æžã蟌ãããšãã§ãã /users/<id>/locations
å®è£
ã¯éåžžã«é«äŸ¡ã§ãããããŒã¿ã¬ã³ãŒããåå²ããæ©èœã远å ããŸãã2åã«ïŒã
å®è£
ãéå§ããŠãã245ç§ã®çŽ æŽãããæéãïŒè¯ãæå³ã§ïŒåŸãŸããããã®çµæãçµæããŒãã«ã§2äœã«ãªããŸããã
ãã¹ãã·ã¹ãã ã¯éåžžã«ã©ã³ãã ã§ãããåããœãªã¥ãŒã·ã§ã³ã§ã¯æ°åå€åããæéã瀺ãããå¯èœæ§ããããè©äŸ¡æ»æã¯1æ¥ã«2åããå®è¡ã§ããªããããæ¬¡ã®æ¹åã®ã©ããæçµçµæã«ã€ãªãã£ãã®ããã©ããããã§ãªãã£ãã®ãã¯æç¢ºã§ã¯ãããŸããã
æ®ãã®é±ã«ããã£ãŠãæ¬¡ã®æé©åãè¡ããŸããã
ãã©ãŒã ã®ã³ãŒã«ãã§ãŒã³ã眮æ
DBInstance::GetDbInstance()->GetLocations()
ãã€ã³ã¿ãŒãž
g_location_storage
ãããããç§ã¯æ£çŽãªhttpããŒãµãŒã¯getãªã¯ãšã¹ãã«ã¯å¿
èŠãªããšæããŸãããgetãªã¯ãšã¹ãã®å Žåãããã«URLãååŸã§ããä»ã®ããšã¯æ°ã«ããŸããã 幞ããªããšã«ãä¿®æ£ãããã¿ã³ã¯ãªã¯ãšã¹ãã«ãããããèš±å¯ãããŸããã ãŸãããããã¡ãå°ç¡ãã«ã§ããããšã泚ç®ã«å€ããŸãïŒããšãã°ãURLã®æåŸã«\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äœã§ããã

ãœãªã¥ãŒã·ã§ã³ã³ãŒãã¯æ¬¡ã®å Žæã«ãããŸãïŒ https : //github.com/evgsid/highload_solution
æçµæç¹ã®æ±ºå®ã³ãŒãïŒ https : //github.com/evgsid/highload_solution/tree/final
éæ³ã®è¬
ããã§ã¯ãéæ³ã«ã€ããŠå°ã話ããŸãããã
ã©ã³ãã³ã°ã®æåã®6äœã¯ãµã³ãããã¯ã¹ã§ç¹ã«éç«ã£ãŠããŸãããæéã¯çŽ140ç§ã§ã次ã¯çŽ190ç§ãããã«æéã¯åŸã
ã«å¢å ããŸããã
æåã®6人ãäœããã®éæ³ã®è¬ãèŠã€ããããšã¯æããã§ããã
ãŠãŒã¶ãŒã¹ããŒã¹->ã«ãŒãã«ã¹ããŒã¹ããã®ã³ããŒãé€å€ããå®éšãšããŠsendfileãšmmapã詊ããŸãããããã¹ãã§ã¯ããã©ãŒãã³ã¹ã®åäžã¯èŠãããŸããã§ããã
ãããŠä»ãæ±ºåæŠã®æåŸã®æ°åã§ãæææ±ºå®ã¯çµäºãããªãŒããŒãã¡ã¯éæ³ã®è¬BUSY WAITãå
±æããŸãã
ä»ã®ãã¹ãŠãçããå Žåã epoll(x, y, z, 0)
ã䜿çšããå Žåã«epoll(x, y, z, -1)
180ç§ãäžãããœãªã¥ãŒã·ã§ã³ã¯ããã«150ç§ä»¥äžãäžããŸããã ãã¡ãããããã¯éçšãœãªã¥ãŒã·ã§ã³ã§ã¯ãããŸããããé
å»¶ã倧å¹
ã«åæžããŸãã
ãã®ããŒãã«é¢ããåªããèšäºã¯ã10Gbpsã€ãŒãµãããã§äœã¬ã€ãã³ã·ãå®çŸããæ¹æ³ãã芧ãã ããã
ç§ã®è§£æ±ºçã¯ãããžãŒåŸ
æ©ã䜿çšãããšãã®æçµçµæã§188ã§ããããããã«136ç§ã瀺ããŸãããããã¯ãæçµèšäºã§4çªç®ããã®èšäºãæžããŠããæç¹ã§ãµã³ãããã¯ã¹ã§8äœã§ãã
ãããæé©ãªãœãªã¥ãŒã·ã§ã³ã®ã°ã©ãã§ãã

å
責äºé
å®éãããžãŒåŸ
æ©ã¯éåžžã«æ
éã«äœ¿çšããå¿
èŠããããŸãã ç§ã®è§£æ±ºçã¯ãã¡ã€ã³ã¹ã¬ãããæ¥ç¶ãåãå
¥ãã4ã€ã®ã¹ã¬ããããœã±ããããã®ããŒã¿ã®ã¿ãåŠçããå ŽåãããžãŒåŸ
æ©ã䜿çšãããšããã¹ããã§ãŒãºã§åŒ·ãäœäžãå§ããŸãããåãå
¥ãã«ååãªããã»ããµæéããªãã解決çãéåžžã«é
ãã£ãããã§ãã åŠçã¹ã¬ããã®æ°ã3ã«æžãããšããã®åé¡ã¯å®å
šã«è§£æ±ºãããŸããã
ãããã«
äž»å¬è
ã¯çŽ æŽããããç«¶æã¯é¢çœãã§ãã
ãšãŠãã¯ãŒã«ã§ããïŒ ãããã¯å¿ããããªã3é±éã®ã·ã¹ãã ããã°ã©ãã³ã°ã§ããã 劻ãšåäŸãã¡ãäŒæäžã ã£ãããšã«æè¬ããŸããããã§ãªããã°ãå®¶æã¯é¢å©ã«è¿ã¥ããŸãã å€å¯ãªãã£ãäž»å¬è
ã®ãããã§ãåå è
ãå©ããæŠè»ãšæŠã£ãã å€ãã®æ°ããã¢ã€ãã¢ãæäŸããæ°ãããœãªã¥ãŒã·ã§ã³ãæ¢ãæ±ããåå è
ã«æè¬ããŸãã
次ã®ã³ã³ãã¹ããæ¥œãã¿ã«ããŠããŸãïŒ