Bagaimana kami memastikan pertumbuhan CityMobile

gambar

Nama saya Ivan, saya kepala pengembangan server di Citimobil. Hari ini saya akan berbicara tentang apa itu pengembangan server, masalah apa yang kami temui dan bagaimana kami berencana untuk mengembangkannya.

Mulai dari pertumbuhan


Hanya sedikit orang yang tahu bahwa CityMobile telah ada sejak lama, 13 tahun. Dulunya adalah perusahaan kecil yang hanya bekerja di Moskow. Ada sangat sedikit pengembang, dan mereka tahu betul bagaimana sistem bekerja - karena mereka sendiri yang membuatnya. Pasar taksi baru saja mulai berkembang, bebannya sangat kecil. Kami bahkan tidak memiliki tugas untuk memberikan toleransi kesalahan dan penskalaan.

Pada tahun 2018, Grup Mail.Ru berinvestasi di CityMobile, dan kami mulai tumbuh dengan cepat. Tidak ada waktu untuk menulis ulang platform atau setidaknya refactoring yang signifikan, perlu untuk mengembangkan kemampuan fungsionalnya untuk mengejar ketinggalan dengan pesaing utama kami, dan dengan cepat merekrut orang. Ketika saya bergabung dengan perusahaan, hanya 20 pengembang yang terlibat dalam backend, dan hampir semua dari mereka diadili. Karena itu, kami memutuskan untuk "memetik buah-buahan yang menggantung rendah": membuat perubahan sederhana yang memberikan hasil besar.

Pada saat itu, kami memiliki monolit dalam PHP dan tiga layanan di Go, serta satu pangkalan utama di MySQL yang diakses monolith (layanan tersebut digunakan sebagai repositori Redis dan EasticSearch). Dan secara bertahap, dengan meningkatnya beban, permintaan sistem yang berat mulai memperlambat basis.

Apa yang bisa dilakukan dengan ini?

Pertama, kami mengambil langkah yang jelas: menempatkan seorang budak dalam produksi. Tetapi jika banyak permintaan berat datang kepadanya, apakah dia akan tahan? Jelas juga bahwa dengan banyak permintaan laporan analitik, budak akan mulai ketinggalan. Tumpukan budak yang kuat dapat memengaruhi kinerja CityMobile secara keseluruhan. Akibatnya, kami menempatkan budak lain untuk analis. Kelambatannya tidak pernah menyebabkan masalah pada prod. Tetapi bahkan bagi kami ini tampaknya tidak cukup. Kami menulis replikator tabel dari MySQL di Clickhouse. Dan hari ini, analytics hidup sendiri, menggunakan tumpukan yang lebih dirancang untuk OLAP.

Dalam bentuk ini, sistem bekerja untuk beberapa waktu, kemudian fungsi mulai muncul yang lebih menuntut pada perangkat keras. Semakin banyak permintaan setiap minggu: bahkan satu minggu berlalu tanpa catatan baru. Selain itu, kami menempatkan bom waktu di bawah sistem kami. Sebelumnya, kami hanya memiliki satu titik kegagalan - basis master MySQL, tetapi dengan penambahan seorang budak ada dua poin seperti itu: master dan budak. Kegagalan salah satu dari mesin ini akan menyebabkan kegagalan sistem.

Untuk melindungi dari ini, kami mulai menggunakan proxy lokal untuk melakukan pemeriksaan kesehatan budak. Ini memungkinkan kami untuk menggunakan banyak budak tanpa mengubah kode. Kami memperkenalkan pemeriksaan otomatis berkala tentang status setiap budak dan metrik umumnya:

  • la;
  • slave lag;
  • ketersediaan pelabuhan;
  • jumlah kunci, dll.

Jika ambang tertentu terlampaui, sistem menghapus budak dari beban. Tetapi pada saat yang sama, tidak lebih dari setengah dari budak dapat ditarik, sehingga, karena peningkatan beban pada yang tersisa, mereka tidak dapat mengatur waktu henti untuk diri mereka sendiri. Sebagai proxy, kami menggunakan HAProxy dan segera memasukkan rencana untuk beralih ke ProxySQL di backlog. Pilihannya agak aneh, tetapi admin kami sudah memiliki pengalaman yang baik bekerja dengan HAProxy, dan masalahnya adalah akut dan membutuhkan solusi awal. Karena itu, kami menciptakan sistem gagal-aman untuk budak, yang diskalakan dengan cukup mudah. Untuk semua kesederhanaannya, dia tidak pernah mengecewakan kita.

Pertumbuhan lebih lanjut


Ketika bisnis berkembang, kami menemukan hambatan lain dalam sistem kami. Dengan perubahan kondisi eksternal - misalnya, curah hujan mulai di wilayah yang luas - jumlah pesanan taksi meningkat dengan cepat. Dalam situasi seperti itu, pengemudi tidak punya waktu untuk bereaksi cukup cepat, dan ada kekurangan mobil. Sementara pesanan didistribusikan, mereka menciptakan beban pada budak MySQL dalam satu lingkaran.

Kami menemukan solusi yang sukses - Tarantool. Sulit untuk menulis ulang sistem untuk itu, jadi kami memecahkan masalahnya secara berbeda: menggunakan alat replikasi mysql-tarantoolmembuat replikasi beberapa tabel dari MySQL ke Tarantool. Semua permintaan bacaan yang muncul selama kekurangan mobil, kami mulai mengudara di Tarantool dan sejak itu kami tidak lagi khawatir tentang badai dan badai! Dan kami memecahkan masalah dengan titik kegagalan dengan lebih mudah: kami segera menginstal beberapa replika yang kami akses ke cek kesehatan melalui HAProxy. Setiap instance Tarantool direplikasi oleh replikator terpisah. Sebagai bonus yang bagus, kami juga memecahkan masalah lagging slave di bagian kode ini: replikasi dari MySQL ke Tarantool bekerja lebih cepat daripada dari MySQL ke MySQL.

Namun, pangkalan utama kami masih merupakan titik kegagalan dan tidak memperhitungkan operasi perekaman. Kami mulai memecahkan masalah ini dengan cara ini.

Pertama, pada saat itu kami sudah mulai secara aktif menciptakan layanan baru (misalnya, anti-penipuan, yang telah ditulis oleh rekan-rekan saya ). Selain itu, layanan segera membutuhkan skalabilitas penyimpanan. Untuk Redis, kami mulai menggunakan hanya Redis-cluster, dan untuk Tarantool - Vshard. Di mana kami menggunakan MySQL, kami mulai menggunakan Vitess untuk logika baru . Basis data semacam itu segera shardable, sehingga hampir tidak ada masalah dengan pencatatan, dan jika tiba-tiba muncul, akan mudah untuk menyelesaikannya dengan menambahkan server. Sekarang kami menggunakan Vitess hanya untuk layanan non-kritis dan mempelajari jebakan, tetapi, di masa depan, itu akan ada di semua database MySQL.

Kedua, karena itu sulit dan lama untuk menerapkan Vitess untuk logika yang ada, kami pergi dengan cara yang lebih sederhana, meskipun kurang universal: kami mulai mendistribusikan pangkalan utama di berbagai server, tabel demi tabel. Kami sangat beruntung: ternyata beban utama pada catatan dibuat oleh tabel yang tidak penting untuk fungsi utama. Dan ketika kita membuat tabel seperti itu, kita tidak membuat poin tambahan dari kegagalan bisnis. Musuh utama bagi kami adalah keterhubungan yang kuat dari tabel-tabel dalam kode menggunakan GABUNG (masing-masing GABUNG dan 50-60 tabel). Kami memotong mereka tanpa ampun.

Sekarang saatnya untuk mengingat dua pola yang sangat penting untuk merancang sistem beban tinggi:

  • Graceful degradation. , - . , , , , .. , .
  • Circuit breaker. , . , , , . ? ( - graceful degradation). , FPM- , . - ( ) , . , - , ( ).

Jadi, kami mulai skala setidaknya, tetapi masih ada poin kegagalan.

Kemudian kami memutuskan untuk beralih ke replikasi semi-sinkron (dan berhasil menerapkannya). Apa fitur-fiturnya? Jika selama replikasi asinkron normal, pembersih di pusat data menuangkan seember air di server, maka transaksi terakhir tidak akan punya waktu untuk mereplikasi ke budak dan akan hilang. Dan kita harus yakin bahwa dalam hal ini kita tidak akan memiliki masalah serius setelah salah satu budak menjadi tuan baru. Akibatnya, kami memutuskan untuk tidak kehilangan transaksi sama sekali, dan untuk ini kami menggunakan replikasi semi-sinkron. Sekarang para budak dapat tertinggal, tetapi bahkan jika server database master dihancurkan, informasi tentang semua transaksi akan disimpan pada setidaknya satu budak.

Ini adalah langkah pertama menuju kesuksesan. Langkah kedua adalah menggunakan utilitas orkestra. Kami juga terus memantau semua MySQL dalam sistem. Jika basis induk gagal, maka otomatisasi akan menjadikan master sebagai budak terbaru (dan dengan mempertimbangkan replikasi semi-sinkron, itu akan berisi semua transaksi) dan mengalihkan seluruh beban tulis ke dalamnya. Jadi kita sekarang dapat menghidupkan kembali kisah tentang seorang wanita pembersih dan seember air.

Apa berikutnya?

Ketika saya datang ke CityMobile, kami memiliki tiga layanan dan satu monolit. Saat ini ada lebih dari 20. Dan hal utama yang menghambat pertumbuhan kami adalah bahwa kami masih memiliki satu induk induk. Kami dengan gagah melawan ini dan membaginya menjadi pangkalan yang terpisah.

Bagaimana kita berkembang lebih jauh?


Perluas rangkaian layanan microser. Ini akan menyelesaikan banyak masalah yang kita hadapi saat ini. Misalnya, tim tidak lagi memiliki satu orang yang mengetahui perangkat seluruh sistem. Dan karena pertumbuhan yang cepat, kami tidak selalu memiliki dokumentasi terbaru, dan sangat sulit untuk mempertahankannya, sulit bagi pemula untuk mempelajari jalannya urusan. Dan jika sistem akan terdiri dari banyak layanan, maka menulis dokumentasi untuk masing-masingnya akan jauh lebih mudah. Dan jumlah kode untuk studi satu kali sangat berkurang.

Pergi ke Pergi.Saya sangat menyukai bahasa ini, tetapi saya selalu percaya bahwa tidak praktis untuk menulis ulang kode kerja dari satu bahasa ke bahasa lain. Tetapi baru-baru ini, pengalaman menunjukkan bahwa perpustakaan PHP, bahkan yang standar dan paling populer, tidak berkualitas tinggi. Artinya, kami menambal banyak perpustakaan. Katakanlah tim SRE menambal perpustakaan standar untuk berinteraksi dengan RabbitMQ: ternyata fungsi dasar seperti waktu tidak berfungsi. Dan semakin dalam tim SRE dan saya memahami masalah ini, semakin jelas bahwa beberapa orang berpikir tentang timeout dalam PHP, sedikit orang yang peduli tentang pengujian perpustakaan, beberapa orang berpikir tentang kunci. Mengapa ini menjadi masalah bagi kami? Karena solusi Go jauh lebih mudah dipelihara.

Apa lagi yang mengesankan saya dengan Go? Cukup aneh, menulis di atasnya sangat sederhana. Selain itu, Go membuatnya mudah untuk membuat berbagai solusi platform. Bahasa ini memiliki seperangkat alat standar yang sangat kuat. Jika karena alasan tertentu backend kami mulai melambat tiba-tiba, buka saja URL tertentu dan Anda dapat melihat semua statistik - diagram alokasi memori, untuk memahami di mana prosesnya idle. Dan di PHP lebih sulit untuk mengidentifikasi masalah kinerja.

Selain itu, Go memiliki linter yang sangat baik - program yang secara otomatis menemukan kesalahan paling umum untuk Anda. Sebagian besar dari mereka dijelaskan dalam artikel "50 shades of Go," dan linter mendeteksi mereka dengan sempurna.

Lanjutkan sharding the base. Kami akan beralih ke Vitess di semua layanan.

Terjemahan dari PHP monolith menjadi redis-cluster.Dalam layanan kami, redis-cluster terbukti sangat baik. Sayangnya, mengimplementasikannya dalam PHP lebih sulit. Monolith menggunakan perintah yang tidak didukung oleh redis-cluster (yang bagus, perintah seperti itu membawa lebih banyak masalah daripada manfaat).

Kami akan menyelidiki masalah RabbitMQ. Diyakini bahwa RabbitMQ bukan perangkat lunak yang paling dapat diandalkan. Kami akan mempelajari masalah ini, menemukan dan menyelesaikan masalah. Mungkin kita akan berpikir tentang beralih ke Kafka atau Tarantool.

All Articles