API Kontrak WG: kebun binatang layanan



Dengan meningkatnya jumlah komponen dalam sistem perangkat lunak, jumlah orang yang berpartisipasi dalam pengembangannya biasanya juga bertambah. Akibatnya, untuk mempertahankan laju pengembangan dan kemudahan pemeliharaan, pendekatan pada organisasi API harus menjadi subjek perhatian khusus.

Jika Anda ingin melihat lebih dekat bagaimana tim Platform Wargaming mengatasi kompleksitas sistem lebih dari seratus layanan web yang saling berinteraksi, maka selamat datang di cat.

Halo semuanya! Nama saya Valentine dan saya seorang insinyur di Platform di Wargaming. Bagi mereka yang tidak tahu apa platform itu dan apa fungsinya, saya akan meninggalkan di sini tautan ke publikasi terbaru dari salah satu kolega saya - max_posedon

Saat ini, saya telah bekerja di perusahaan selama lebih dari lima tahun dan sebagian menemukan periode pertumbuhan aktif World of Tanks. Untuk mengungkap masalah yang diangkat dalam artikel ini, saya harus mulai dengan penyimpangan singkat ke dalam sejarah Platform Wargaming.

Sedikit sejarah


Semakin populernya "tank" ternyata seperti longsoran salju, dan seperti biasanya dalam kasus seperti itu, infrastruktur di sekitar permainan mulai berkembang pesat. Akibatnya, permainan dengan cepat bertambah dengan berbagai layanan web, dan pada saat saya bergabung dengan tim, skor mereka sudah mencapai puluhan (sekarang, omong-omong, lebih dari 100 komponen platform bekerja dan menguntungkan perusahaan).

Seiring berjalannya waktu, game-game baru muncul, dan memahami seluk-beluk integrasi antara layanan web tidak lagi mudah. Situasi hanya memburuk ketika tim dari kantor Wargaming lain bergabung dengan pengembangan platform. Perkembangan telah didistribusikan, dengan semua konsekuensi dalam bentuk jarak, zona waktu dan hambatan bahasa. Dan ada lebih banyak layanan. Menemukan seseorang yang mengerti bagaimana platform bekerja secara keseluruhan tidaklah mudah. Informasi sering kali harus dikumpulkan sebagian dari sumber yang berbeda.

Antarmuka berbagai layanan web dapat sangat berbeda dalam kinerja gaya, yang membuat proses integrasi dengan platform lebih sulit. Dan dependensi antar-komponen langsung mengurangi fleksibilitas pengembangan dengan menyulitkan dekomposisi fungsi dalam platform. Yang membuat segalanya menjadi lebih buruk, game - klien platform - mengetahui topologi kami dengan baik, karena mereka harus berintegrasi langsung dengan setiap layanan platform. Ini memberi mereka kesempatan, menggunakan koneksi horisontal, untuk melobi pelaksanaan perbaikan tertentu secara langsung dalam komponen yang mereka integrasikan. Hal ini menyebabkan munculnya fungsi duplikat di berbagai komponen platform, serta ketidakmampuan untuk memperluas fungsionalitas yang ada ke game lain. Menjadi jelas bahwa untuk terus membangun platform di sekitar setiap game tertentu adalah cabang buntu pengembangan. Kami membutuhkan perubahan teknis dan organisasi, sebagai hasilnya kami dapat mengendalikan kompleksitas yang berkembang dari sistem yang berkembang pesat dan membuat semua fungsionalitas platform sesuai untuk digunakan oleh game mana pun.

Dengan ini saya ingin menyelesaikan perjalanan sejarah dan, akhirnya, berbicara tentang salah satu solusi teknis kami, yang membantu untuk tetap mengendalikan kompleksitas yang disebabkan oleh jumlah layanan yang semakin meningkat. Selain itu, ini mengurangi biaya pengembangan fungsionalitas baru dan sangat menyederhanakan integrasi dengan platform.

Memenuhi API Kontrak


Di dalam platform, kami menyebutnya API Kontrak. Pada intinya, ini adalah kerangka kerja integrasi yang diwakili oleh seperangkat dokumentasi dan pustaka klien untuk setiap teknologi dari tumpukan kami (Erlang / Elixir, Java / Scala, Python). Ini sedang dikembangkan, pertama-tama, untuk menyederhanakan integrasi komponen platform satu sama lain. Kedua, untuk membantu kami memecahkan sejumlah masalah berikut:

  • perbedaan gaya antarmuka program
  • adanya ketergantungan antar-komponen langsung
  • menjaga dokumentasi tetap terbaru
  • introspeksi dan debugging fungsi ujung ke ujung

Jadi, hal pertama yang pertama.

Perbedaan gaya dalam antarmuka perangkat lunak


Menurut pendapat saya, masalah ini muncul sebagai akibat dari kombinasi beberapa faktor:

  • Kurangnya standar ketat tentang bagaimana seharusnya API. Serangkaian rekomendasi sering tidak memiliki efek yang diinginkan, API masih berbeda. Terutama jika pengembangan dilakukan oleh tim dari berbagai kantor perusahaan. Setiap tim memiliki kebiasaan dan praktiknya sendiri. Secara kolektif, API semacam itu seringkali tidak terlihat seperti bagian dari keseluruhan.
  • Kurangnya direktori tunggal dengan nama dan format entitas khusus bisnis. Sebagai aturan, Anda tidak dapat mengambil entitas dari hasil satu API dan meneruskannya ke API layanan lain. Ini membutuhkan transformasi.
  • Kurangnya sistem peninjauan terpusat wajib untuk API. Selalu ada tenggat waktu dan tidak ada waktu untuk mengumpulkan pembaruan dan, terlebih lagi, membuat perubahan pada API, yang pada kenyataannya seringkali ternyata sudah setengah diuji.

Hal pertama yang kami lakukan ketika mendesain API Kontrak adalah untuk mengatakan bahwa mulai sekarang API milik platform, dan bukan ke satu komponen. Ini mengarah pada fakta bahwa pengembangan fungsionalitas baru dimulai dengan permintaan tarik ke API penyimpanan terpusat. Saat ini, kami menggunakan repositori GIT sebagai penyimpanan. Untuk kenyamanan, kami membagi seluruh API menjadi fungsi bisnis yang terpisah, memformalkan struktur fungsi ini dan menyebutnya Kontrak.

Sejak itu, setiap fungsi bisnis baru di API kontrak kami harus diuraikan dalam format khusus dan melalui permintaan tarik dengan peninjauan wajib. Tidak ada cara lain untuk menerbitkan API baru ke API Kontrak. Dalam repositori yang sama, kami mendefinisikan direktori entitas khusus bisnis dan menyarankan agar pengembang kontrak menggunakannya kembali alih-alih mendeskripsikan entitas ini sendiri.

Jadi kami mendapat platform API terintegrasi secara konseptual yang tampak seperti produk tunggal, meskipun faktanya sebenarnya diimplementasikan pada banyak komponen platform menggunakan berbagai tumpukan teknologi.

Adanya dependensi antar-komponen langsung


Masalah kita ini memanifestasikan dirinya dalam kenyataan bahwa setiap komponen platform diperlukan untuk mengetahui siapa yang secara spesifik melayani fungsionalitas yang dibutuhkannya.

Dan itu bahkan bukan kesulitan mempertahankan direktori ini mutakhir, tetapi fakta bahwa ketergantungan langsung secara signifikan mempersulit migrasi fungsionalitas bisnis dari satu komponen platform ke yang lain. Masalahnya sangat akut ketika kami memulai dekomposisi monolit kami menjadi komponen yang lebih kecil. Ternyata meyakinkan klien untuk mengganti integrasi kerja dengan fungsionalitas apa pun dengan yang sama dari sudut pandang bisnis, tetapi yang lain dari sudut pandang teknis, bukanlah tugas manajemen yang sepele. Klien sama sekali tidak mengerti maksudnya, karena semuanya berjalan baik baginya. Akibatnya, lapisan backward berbau busuk ditulis yang hanya mempersulit dukungan platform dan berdampak buruk pada kualitas layanan. Dan karena kami sudah membuat standardisasi platform API, maka perlu untuk secara bersamaan menyelesaikan masalah ini.

Kami menghadapi beberapa pilihan. Dari jumlah tersebut, kami secara khusus mempertimbangkan:

  • Implementasi protokol penemuan layanan pada masing-masing komponen.
  • Menggunakan mediator untuk mengarahkan permintaan klien ke komponen platform yang benar.
  • Menggunakan broker pesan sebagai bus perpesanan.

Sebagai hasil dari beberapa pemikiran dan percobaan, pilihan jatuh pada broker pesan, terlepas dari kenyataan bahwa ia melihat kami sebagai satu-satunya titik kegagalan dan meningkatkan overhead pengoperasian platform. Peran penting dalam pemilihan dimainkan oleh fakta bahwa platform pada waktu itu sudah memiliki keahlian dalam bekerja dengan RabbitMQ. Dan broker itu sendiri berskala baik dan memiliki mekanisme bawaan untuk memastikan toleransi kesalahan. Sebagai bonus, kami mendapat kesempatan untuk mengimplementasikan arsitektur berbasis-event ( event-driven architecture atau EDA ) "di bawah tenda". Yang kemudian terbuka di hadapan kita kemungkinan yang lebih luas dari interaksi antar layanan, dibandingkan dengan interaksi point-to-point.

Jadi, secara topologi, platform mulai berubah dari grafik dengan konektivitas acak menjadi bintang. Dan komponen platform membalikkan dependensi mereka dan mendapat kesempatan untuk berinteraksi satu sama lain secara eksklusif melalui kontrak yang terdaftar di repositori terpusat, tanpa perlu tahu siapa yang secara spesifik mengimplementasikan kontrak tertentu. Dengan kata lain, semua komponen dalam platform dapat berinteraksi satu sama lain menggunakan titik integrasi tunggal, yang sangat menyederhanakan kehidupan pengembang.

Menjaga dokumentasi tetap mutakhir


Masalah yang terkait dengan kurangnya dokumentasi atau hilangnya relevansinya hampir selalu ditemui. Dan semakin tinggi laju perkembangan, semakin sering ia memanifestasikan dirinya. Dan setelah itu, mengumpulkan semua spesifikasi API di satu tempat dan format untuk lebih dari seratus layanan dalam tim yang terdistribusi dan multinasional adalah tugas yang sulit.

Saat mengembangkan API Kontrak, kami juga menetapkan tujuan untuk menyelesaikan masalah ini. Dan kami berhasil. Format yang ditentukan secara ketat untuk deskripsi kontrak memungkinkan kami untuk membangun proses yang sesuai dengan itu, segera setelah munculnya kontrak baru, perakitan dokumentasi otomatis dimulai. Ini memberi kami keyakinan bahwa dokumentasi API kami selalu terbarui. Proses ini sepenuhnya otomatis dan tidak memerlukan upaya pengembangan atau pengelolaan.

Introspeksi dan debugging fungsi ujung ke ujung


Ketika kami membagi monolit kami menjadi komponen yang lebih kecil, secara alami, kesulitan mulai timbul dalam debugging fungsi ujung ke ujung. Jika layanan fungsi bisnis didistribusikan di beberapa komponen platform, maka seringkali untuk melokalisasi dan men-debug masalah, orang harus mencari perwakilan dari masing-masing komponen. Yang kadang-kadang dapat dicapai dengan kesulitan, mengingat perbedaan waktu 11 jam dengan beberapa rekan kami.

Dengan munculnya API Kontrak, dan khususnya terima kasih kepada pialang pesan yang mendasarinya, kami dapat menerima salinan pesan yang terlibat dalam pelaksanaan fungsi bisnis, tanpa efek samping pada peserta interaksi. Untuk melakukan ini, bahkan tidak perlu mengetahui komponen dari platform mana yang bertanggung jawab untuk memproses kontrak tertentu. Dan setelah lokalisasi masalah, kita bisa mendapatkan pengenal komponen yang rusak dari metadata dari pesan masalah.

Apa lagi yang kami kembangkan di atas API Kontrak


Selain tujuan utamanya dan menyelesaikan masalah di atas, API Kontrak memungkinkan kami untuk mengimplementasikan sejumlah layanan yang bermanfaat.

Gerbang untuk mengakses fungsionalitas platform


Standarisasi API dalam bentuk kontrak memungkinkan kami untuk mengembangkan satu titik akses ke fungsionalitas platform melalui HTTP. Selain itu, dengan munculnya fungsionalitas baru (kontrak), kita tidak perlu mengubah jalur akses ini dengan cara apa pun. Ini maju kompatibel dengan semua kontrak masa depan. Ini memungkinkan Anda untuk bekerja dengan platform sebagai produk tunggal menggunakan antarmuka HTTP biasa.

Layanan Operasi Massal


Setiap kontrak dapat diluncurkan sebagai bagian dari operasi massal, dengan kemampuan untuk melacak statusnya dan kemudian menerima laporan tentang hasil operasi ini. Layanan ini, seperti yang sebelumnya, kompatibel dengan semua kontrak di masa depan.

Penanganan kesalahan platform terpadu


Protokol Kontrak API juga membakukan kesalahan. Ini memungkinkan kami untuk mengimplementasikan pencegat kesalahan, yang menganalisis tingkat keparahannya dan memberi tahu sistem pemantauan tentang potensi masalah pada komponen platform. Dan di masa depan, ia akan dapat secara mandiri memutuskan penemuan bug pada komponen platform. Pencegat kesalahan menangkap mereka langsung dari pialang pesan dan tidak tahu apa-apa tentang tujuan kontrak atau kesalahan, hanya bertindak berdasarkan meta-informasi. Ini memungkinkannya, serta semua layanan yang dijelaskan dalam bagian ini, untuk maju kompatibel dengan semua kontrak masa depan.

Auto Hasilkan Antarmuka Pengguna


Kontrak yang diformalkan dengan ketat memungkinkan Anda membangun komponen antarmuka pengguna secara otomatis. Kami telah mengembangkan layanan yang memungkinkan Anda untuk menghasilkan antarmuka administratif berdasarkan kumpulan kontrak, dan kemudian menyematkan antarmuka ini di salah satu alat platform kami. Dengan demikian, admin yang sebelumnya kami tulis dengan tangan kami sekarang dapat dihasilkan (walaupun hanya sebagian sejauh ini) dalam mode otomatis.

Pencatatan Platform


Komponen ini belum diimplementasikan dan sedang dikembangkan. Tetapi di masa depan, itu akan memungkinkan "on the fly" untuk menghidupkan dan mematikan pencatatan fungsi bisnis apa pun di platform, mengekstraksi informasi ini langsung dari broker pesan, tanpa efek samping apa pun yang berdampak buruk terhadap komponen yang berinteraksi.

Tujuan utama API Kontrak


Tapi tetap saja, tujuan utama API Kontrak adalah untuk mengurangi biaya mengintegrasikan komponen platform.

Pengembang disarikan dari tingkat transportasi oleh perpustakaan yang kami kembangkan untuk setiap tumpukan teknologi kami. Ini memberi kita ruang untuk bermanuver jika kita harus mengubah broker pesan atau bahkan beralih ke interaksi point-to-point. Antarmuka eksternal perpustakaan akan tetap tidak berubah.

Perpustakaan di bawah tenda menghasilkan pesan sesuai dengan aturan tertentu dan mengirimkannya ke broker, setelah itu, setelah menunggu pesan tanggapan, ia mengembalikan hasilnya kepada pengembang. Di luar, sepertinya permintaan sinkron (atau asinkron, tergantung pada implementasi) biasa. Sebagai demonstrasi, saya akan memberikan beberapa contoh.

Contoh panggilan kontrak python
from platform_client import Client client = Client(contracts_path=CONTRACTS_PATH, url=AMQP_URL, app_id='client') client.call("ban-management.create-ban.v1", { "wgid": 1234567890, "reason": "Fraudulent activity", "title": "ru.wot", "component": "game", "bantype": "access_denied", "author_id": "v_nikonovich", "expires_at": "2038-01-19 03:14:07Z" }) { u'ban_id': 31415926, u'wgid': 1234567890, u'title': u'ru.wot', u'component': u'game', u'reason': u'Fraudulent activity', u'bantype': u'access_denied', u'status': u"active", u'started_at': u"2019-02-15T15:15:15Z", u'expires_at': u"2038-01-19 03:14:07Z" } 

Panggilan kontrak yang sama, tetapi menggunakan Elixir
 :platform_client.call("ban-management.create-ban.v1", %{ "wgid" => 1234567890, "reason" => "Fraudulent activity", "title" => "ru.wot", "component" => "game", "bantype" => "access_denied", "author_id" => "v_nikonovich", "expires_at" => "2038-01-19 03:14:07Z" }) {:ok, %{ "ban_id" => 31415926, "wgid" => 1234567890, "title" => "ru.wot", "conponent" => "game", "reason" => "Fraudulent activity", "bantype" => "access_denied", "status" => "active", "started_at" => "2019-02-15T15:15:15Z", "expires_at" => "2038-01-19 03:14:07Z" }} 

Di tempat kontrak "ban-management.create-ban.v1" dapat ada fungsionalitas platform lainnya, misalnya: "akun-management.rename-account.v1" atau "notification-center.create-sms-notification.v1". Dan semuanya akan tersedia melalui titik integrasi dengan platform ini.

Tinjauan umum tidak akan lengkap jika Anda tidak menunjukkan API Kontrak dari sudut pandang pengembang server. Pertimbangkan situasi di mana pengembang perlu menerapkan penangan untuk kontrak ban-management.create-ban.v1 yang sama.

 from platform_server import BlockingServer, handler class CustomServer(BlockingServer): @handler('ban-management.create-ban.v1') def handle_create_ban(self, params, context): response = do_some_usefull_job(params) return response d = CustomServer(app_id="server", amqp_url=AMQP_URL, contracts_path=CONTRACTS_PATH) d.serve() 

Kode ini akan cukup untuk mulai melayani kontrak yang diberikan. Pustaka server akan membongkar dan memeriksa parameter permintaan untuk kebenaran, dan kemudian memanggil penangan kontrak dengan parameter permintaan siap untuk diproses. Dengan demikian, pengembang server dilindungi oleh perpustakaan, yang, dalam hal menerima parameter permintaan yang salah, itu sendiri akan mengirim kesalahan validasi kepada klien dan mendaftarkan fakta masalah.

Karena kenyataan bahwa di bawah tenda API Kontrak diimplementasikan berdasarkan peristiwa, kami mendapatkan kesempatan untuk melampaui ruang lingkup skrip Permintaan / Respons dan menerapkan serangkaian interaksi antar-layanan yang lebih luas.

Sebagai contoh:

  • membuat permintaan dan melupakan (tanpa menunggu jawaban)
  • membuat permintaan ke beberapa kontrak secara bersamaan (bahkan tanpa menggunakan loop acara)
  • membuat permintaan dan menerima jawaban dari beberapa penangan sekaligus (jika disediakan oleh skrip integrasi)
  • mendaftarkan penangan respons (dipicu jika penangan kontrak melaporkan penyelesaian, menerima hasil pekerjaan penangan kontrak, yaitu tanggapannya)

Dan ini bukan daftar skenario lengkap yang dapat diekspresikan melalui model peristiwa interaksi. Ini adalah daftar yang sedang kami gunakan.

Alih-alih sebuah kesimpulan


Kami telah menggunakan API Kontrak selama beberapa tahun. Oleh karena itu, tidak mungkin untuk berbicara tentang semua skenario penggunaannya dalam kerangka satu artikel ulasan. Untuk alasan yang sama, saya tidak membebani artikel dengan detail teknis. Dia sudah menjadi sangat produktif. Ajukan pertanyaan, dan saya akan mencoba menjawabnya langsung di komentar. Jika suatu topik sangat menarik, akan mungkin untuk mengungkapkannya secara lebih rinci dalam artikel terpisah.

Source: https://habr.com/ru/post/id441708/


All Articles