Goの次のHighLoadCupソリューション

Habrの多くのユーザーは、先週Mail.ruのHighLoadCupが終了したことを既に知っていると思います(参加者からの記事の数が多かったため)。 また、私の決定をコミュニティと共有したいと思います。



タスクの説明



エンティティには、ユーザー、場所、訪問の3種類があります。 それらにアクセスするには、REST-APIを記述する必要があります。 6つのリクエストを処理する必要があることがわかりました。





他のサービスと同様に、要求は無効になる可能性があり、これも処理する必要がありますが、一般にHTTPパケットが常に有効であるという事実によってタスクが簡素化されます。



開始する



最初はC ++で書き始めましたが、リリースには至りませんでした。 Goのトップで多数のソリューションを見たので、私も試してみることにしました。 この言語は、サーバーアプリケーションの開発に非常に適しているように思われ、必要な機能はすべてそのままで、非常に高品質のパフォーマンスを備えています。 ただし、最初のテストの後、大量のゴミが生成されるため、net / httpもencoding / jsonもこのコンテストに適していないことが明らかになりました。



ラウンド1



最初は、アンパック形式で200 mbのデータしかないため、エンティティごとに既製のJSON文字列を保存することもできると考えました。 コンテストのgoshnikovのアドバイスに基づいて、httpサーバーとしてJSON buger / jsonparserを解析するためにfasthttpを選択しました (割り当てなしで解析し、必要な情報のみを処理します)ロシア語の文字列を処理する必要がなかったため、手作業で作成されました。



type User struct { id uint email string first_name string last_name string gender bool birth_date int64 age int visits Visits json []byte } type Location struct { id uint place string country string city string distance int64 visits Visits json []byte } type Visit struct { id uint location *Location user *User visited_at int64 mark int64 json []byte }
      
      





そのようなエンティティは、最初に出てきたので、速度を上げるために、対応するユーザーと場所へのポインタをすぐに保存することにしました。 結果は非常に満足のいくもので、すべてのデータが完全に適合し、最終的にはトップ10に到達することさえできました(ただし、長くは続きませんでした)。



年齢



訪問者の年齢を計算する問題に特に注意を払いたいです。 よくある質問の例があるにもかかわらず、この質問は多くの参加者にとって鋭いものでした。また、私はこの運命を逃れませんでした。 また、生成されたデータには、誕生日がテスト当日にあるユーザーが数回いたため、いくつかの困難が生じました。



結果は次のコードになりました。



 func countAge(timestamp *int64) int { now := time.Now() t := time.Unix(*timestamp, 0) years := now.Year() - t.Year() if now.Month() > t.Month() || now.Month() == t.Month() && now.Day() >= t.Day() { years += 1 } return years }
      
      





負荷増加



決勝の数日前、民主的で開かれた投票の後、コンテストの作成者はデータ量を10倍、最大RPSを2倍に増やしました。 その後、私の決定は記憶に収まりなくなり、変更が必要になりました。 事前に生成されたJSONを構造から削除し、要求に応じてオンザフライで作成する必要がありました。これはもちろん、リアリズムを向上させました。 同時に、訪問者とロケーション構造に関連付けられている訪問へのリンクをすぐに追加してみませんか。 これにより、毎回一連の訪問(現在は1,000万のエントリが含まれている)をすべて処理する必要がなくなったため、プログラムの速度が大幅に向上しました。



その後の解決策は機能しましたが、速さで他の解決策に譲り始め、最終段階に進まないリスクがありました。 ためらうことなく、私はfasthttpを投げ、tcpソケットとepollに切り替えました。 私たちのシステムのウィンドウのサイズは約65kbであり、パケットは完全に行き来し、これは松葉杖に大きなフィールドを与えました。



ファイナル



私は39位で決勝に達しましたが、間違いなくとても嬉しかったです。 これは、このような競争への私の最初の参加であり、Goとhighloadを初めて知っています(highloadとは呼びませんが)。 フィナーレの開始は不十分で、同時読み取りおよび書き込みアクセスエラーのためにサーバーがクラッシュしました(フィナーレとロッカーが切断される前にそのような問題はありませんでした)、それにもかかわらず、波の1つはすべての起動で最高の結果を示し、28位になりました。



一般的に、それは非常に興味深く有益な(少なくとも私にとっては)競争でした。 主催者に感謝の意を表し、現在のすべての間違いと「機能」を考慮して次のイベントを楽しみにしています(たとえば、ビジーポーリング)。 確かに、将来的には、ネットワークスタックよりもロジックに重点が置かれました。これはより興味深いものになります。



PS私はTシャツを待っています:)



コード(突然、誰かにとって興味深いコードになります )は、私のリポジトリで見ることができます



All Articles