Kontrak, Alamat, dan API untuk Microservice

ID Wilayah

REGION_ID adalah kode singkat yang ditetapkan Google berdasarkan region yang Anda pilih saat membuat aplikasi. Kode ini tidak sesuai dengan negara atau provinsi, meskipun beberapa ID region mungkin tampak mirip dengan kode negara dan provinsi yang umum digunakan. Untuk aplikasi yang dibuat setelah Februari 2020, REGION_ID.r disertakan dalam URL App Engine. Untuk aplikasi lama yang dibuat sebelum tanggal tersebut, ID region bersifat opsional dalam URL.

Pelajari ID region lebih lanjut.

Microservice di App Engine biasanya memanggil satu sama lain menggunakan RESTful API berbasis HTTP. Anda juga dapat memanggil microservice di latar belakang menggunakan Task Queue, dan prinsip desain API yang dijelaskan di sini berlaku. Penting untuk mengikuti pola tertentu guna memastikan aplikasi berbasis microservice Anda selalu stabil, aman, dan berperforma baik.

Menggunakan kontrak yang kuat

Salah satu aspek terpenting aplikasi berbasis microservice adalah kemampuan men-deploy microservice yang sepenuhnya independen dari satu sama lain. Agar independen seperti ini, setiap microservice harus memberikan kontrak berversi yang jelas kepada kliennya, yaitu microservice lainnya. Setiap layanan tidak boleh melanggar kontrak versi ini sampai diketahui bahwa tidak ada microservice lain yang mengandalkan kontrak versi tertentu. Perlu diingat bahwa microservice lainnya mungkin perlu melakukan roll back ke versi kode sebelumnya yang memerlukan kontrak sebelumnya, sehingga penting untuk memperhitungkan fakta ini pada kebijakan penghentian penggunaan dan kebijakan penonaktifan Anda.

Budaya seputar kontrak berversi yang kuat mungkin menjadi aspek organisasi yang paling menantang dari aplikasi berbasis microservice yang stabil. Tim pengembangan harus menginternalisasi pemahaman tentang perubahan yang dapat menyebabkan gangguan versus perubahan yang tidak menyebabkan gangguan. Mereka harus mengetahui kapan rilis utama baru diperlukan. Mereka harus memahami bagaimana dan kapan kontrak lama dapat dihentikan. Tim harus menggunakan teknik komunikasi yang sesuai, termasuk pemberitahuan penghentian penggunaan dan pemberitahuan penolakan, untuk memastikan kesadaran terhadap perubahan pada kontrak microservice. Meskipun terdengar sulit, membangun praktik ini ke dalam budaya pengembangan akan menghasilkan peningkatan besar pada kecepatan dan kualitas dari waktu ke waktu.

Menangani microservice

Layanan dan versi kode dapat langsung ditangani. Hasilnya, Anda dapat men-deploy versi kode baru secara berdampingan dengan versi kode yang sudah ada, dan Anda dapat menguji kode baru sebelum menjadikannya versi penayangan default.

Setiap project App Engine memiliki layanan default, dan setiap layanan memiliki versi kode default. Untuk menangani layanan default untuk versi default project, gunakan URL berikut:
https://PROJECT_ID.REGION_ID.r.appspot.com

Jika Anda men-deploy layanan bernama user-service, Anda dapat mengakses versi penayangan default layanan tersebut menggunakan URL berikut:

https://user-service-dot-my-app.REGION_ID.r.appspot.com

Jika Anda men-deploy versi kode kedua non-default bernama banana ke layanan user-service, Anda dapat langsung mengakses versi kode tersebut menggunakan URL berikut:

https://banana-dot-user-service-dot-my-app.REGION_ID.r.appspot.com

Perlu diperhatikan bahwa jika Anda men-deploy versi kode kedua non-default bernama cherry ke layanan default, Anda dapat mengakses versi kode tersebut menggunakan URL berikut:

https://cherry-dot-my-app.REGION_ID.r.appspot.com

App Engine menerapkan aturan bahwa nama versi kode dalam layanan default tidak boleh bertabrakan dengan nama layanan.

Versi kode tertentu yang menangani secara langsung hanya boleh digunakan untuk pengujian asap dan untuk memfasilitasi pengujian A/B, roll maju, dan roll back. Sebagai gantinya, kode klien Anda hanya boleh menangani versi penayangan default dari layanan default atau layanan tertentu:


https://PROJECT_ID.REGION_ID.r.appspot.com

https://SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com

Gaya penanganan ini memungkinkan microservice untuk men-deploy versi baru layanannya, termasuk perbaikan bug, tanpa perlu mengubah klien.

Menggunakan versi API

Setiap API microservice harus memiliki versi API utama di URL, seperti:

/user-service/v1/

Versi API utama ini mengidentifikasi dengan jelas versi API microservice yang dipanggil di log. Yang lebih penting, versi API utama menghasilkan URL yang berbeda, sehingga versi API utama yang baru dapat ditayangkan secara berdampingan dengan versi API utama yang lama:

/user-service/v1/
/user-service/v2/

Anda tidak perlu menyertakan versi API minor di URL karena versi API minor menurut definisi tidak akan menyebabkan perubahan yang dapat menyebabkan gangguan. Bahkan, menyertakan versi API minor di URL akan menyebabkan proliferasi URL dan menyebabkan ketidakpastian mengenai kemampuan klien untuk beralih ke versi API minor yang baru.

Perlu diperhatikan bahwa artikel ini mengasumsikan lingkungan continuous integration dan penayangan, dengan cabang utama selalu di-deploy ke App Engine. Ada dua konsep versi yang berbeda dalam artikel ini:

  • Versi kode, yang memetakan langsung ke versi layanan App Engine dan merepresentasikan tag commit tertentu cabang utama.

  • Versi API, yang memetakan langsung ke URL API dan mewakili bentuk argumen permintaan, bentuk dokumen respons, dan perilaku API.

Artikel ini juga mengasumsikan bahwa satu deployment kode akan menerapkan API versi lama dan baru dari sebuah API dalam versi kode umum. Misalnya, cabang utama yang di-deploy mungkin menerapkan /user-service/v1/ dan /user-service/v2/. Saat meluncurkan versi minor dan patch baru, pendekatan ini memungkinkan Anda membagi traffic antara dua versi kode secara terpisah dari versi API yang sebenarnya diimplementasikan oleh kode tersebut.

Organisasi Anda dapat memilih untuk mengembangkan /user-service/v1/ dan /user-service/v2/ di berbagai cabang kode; yaitu, tidak ada satu deployment kode yang akan menerapkan keduanya secara bersamaan. Model ini juga dapat dibuat di App Engine, tetapi untuk memisahkan traffic, Anda harus memindahkan versi API utama ke nama layanan itu sendiri. Misalnya, klien Anda akan menggunakan URL berikut:

http://user-service-v1.my-app.REGION_ID.r.appspot.com/user-service/v1/
http://user-service-v2.my-app.REGION_IDappspot.com/user-service/v2/

Versi API utama dipindahkan ke nama layanan itu sendiri, seperti user-service-v1 dan user-service-v2. (Bagian /v1/, /v2/ dari jalur bersifat redundan dalam model ini dan dapat dihapus, meskipun masih berguna dalam analisis log.) Model ini memerlukan lebih banyak upaya karena kemungkinan memerlukan update pada skrip deployment Anda untuk men-deploy layanan baru pada perubahan versi API utama. Selain itu, perhatikan jumlah maksimum layanan yang diizinkan per aplikasi App Engine.

Perubahan yang dapat menyebabkan gangguan versus yang tidak dapat menyebabkan gangguan

Anda harus memahami perbedaan antara perubahan yang dapat menyebabkan gangguan dan perubahan yang tidak dapat menyebabkan gangguan. Perubahan yang dapat menyebabkan gangguan sering kali bersifat subtraktif, yang berarti perubahan tersebut mengambil beberapa bagian dari dokumen permintaan atau respons. Mengubah bentuk dokumen atau mengubah nama kunci dapat menyebabkan perubahan yang dapat menyebabkan gangguan. Argumen baru yang diperlukan selalu menyebabkan perubahan yang dapat menyebabkan gangguan. Perubahan yang dapat menyebabkan gangguan juga terjadi jika perilaku microservice berubah.

Perubahan yang tidak dapat menyebabkan gangguan cenderung bersifat tambahan. Argumen permintaan opsional baru, atau bagian tambahan baru dalam dokumen respons adalah perubahan yang tidak dapat menyebabkan gangguan. Untuk mencapai perubahan yang tidak dapat menyebabkan gangguan, pilihan serialisasi melalui kabel sangat penting. Banyak serialisasi cocok untuk perubahan yang tidak dapat menyebabkan gangguan: JSON, Buffering Protokol, atau Thrift. Saat deserialisasi dilakukan, serialisasi ini diam-diam mengabaikan informasi tambahan yang tidak terduga. Dalam bahasa dinamis, informasi tambahan hanya muncul dalam objek yang dideserialisasi.

Pertimbangkan definisi JSON berikut untuk layanan /user-service/v1/:

{
  "userId": "UID-123",
  "firstName": "Jake",
  "lastName": "Cole",
  "username": "jcole@example.com"
}

Perubahan yang dapat menyebabkan gangguan berikut memerlukan pembuatan versi ulang layanan sebagai /user-service/v2/:

{
  "userId": "UID-123",
  "name": "Jake Cole",  # combined fields
  "email": "jcole@example.com"  # key change
}

Namun, perubahan yang tidak dapat menyebabkan gangguan berikut tidak memerlukan versi baru:

{
  "userId": "UID-123",
  "firstName": "Jake",
  "lastName": "Cole",
  "username": "jcole@example.com",
  "company": "Acme Corp."  # new key
}

Men-deploy versi API minor baru yang tidak dapat menyebabkan gangguan

Saat men-deploy versi API minor baru, App Engine memungkinkan versi kode baru dirilis secara berdampingan dengan versi kode lama. Di App Engine, meskipun Anda dapat langsung menangani salah satu versi yang di-deploy, hanya satu versi yang merupakan versi penyajian default; mengingat bahwa ada versi penyajian default untuk setiap layanan. Dalam contoh ini, kami memiliki versi kode lama, bernama apple yang merupakan versi penyajian default, dan kami men-deploy versi kode baru sebagai versi berdampingan, yang bernama banana. Perlu diperhatikan bahwa URL microservice untuk keduanya adalah /user-service/v1/ yang sama karena kita men-deploy perubahan API kecil yang tidak dapat menyebabkan gangguan.

App Engine menyediakan mekanisme untuk memigrasikan traffic secara otomatis dari apple ke banana dengan menandai kode versi baru banana sebagai versi penyajian default. Jika versi penyajian default baru sudah ditetapkan, tidak ada permintaan baru yang akan dirutekan ke apple, dan semua permintaan baru akan dirutekan ke banana. Ini adalah cara melanjutkan ke versi kode baru yang mengimplementasikan versi API minor atau patch baru tanpa memengaruhi microservice klien.

Jika terjadi error, rollback dapat dilakukan dengan membalik proses di atas: tetapkan versi penayangan default kembali ke versi lama, apple dalam contoh kami. Semua permintaan baru akan dirutekan kembali ke versi kode lama dan tidak ada permintaan baru yang akan dirutekan ke banana. Perhatikan bahwa permintaan yang sedang berlangsung diizinkan untuk diselesaikan.

App Engine juga memberikan kemampuan untuk mengarahkan persentase tertentu saja dari traffic ke versi kode baru; proses ini sering kali disebut sebagai proses rilis canary, dan mekanismenya disebut sebagai pemisahan traffic di App Engine. Anda dapat mengarahkan 1%, 10%, 50%, atau berapa pun persentase traffic yang Anda inginkan ke versi kode baru, dan Anda dapat menyesuaikan jumlah ini dari waktu ke waktu. Misalnya, Anda dapat meluncurkan versi kode baru selama 15 menit, yang secara perlahan meningkatkan traffic dan mengamati masalah yang mungkin mengidentifikasi bahwa rollback diperlukan. Mekanisme yang sama ini memungkinkan Anda melakukan pengujian A/B pada dua versi kode: menetapkan pemisahan traffic ke 50% serta membandingkan karakteristik performa dan tingkat error kedua versi kode untuk mengonfirmasi peningkatan yang diharapkan.

Gambar berikut menunjukkan setelan pemisahan traffic di konsol Google Cloud:

Setelan pemisahan traffic di konsol Google Cloud

Men-deploy versi API utama baru yang dapat menyebabkan gangguan

Saat Anda men-deploy versi API utama yang dapat menyebabkan gangguan, proses roll forward dan roll back akan sama seperti proses untuk versi API minor yang tidak menyebabkan gangguan. Namun, biasanya Anda tidak akan melakukan pemisahan traffic atau pengujian A/B karena versi API yang dapat menyebabkan gangguan adalah URL yang baru dirilis, seperti /user-service/v2/. Tentu saja, jika Anda telah mengubah implementasi dasar versi API utama yang lama, Anda mungkin masih ingin menggunakan pemisahan traffic untuk menguji apakah versi API utama lama terus berfungsi seperti yang diharapkan.

Saat men-deploy versi utama API baru, perlu diingat bahwa versi API utama yang lama mungkin juga masih ditayangkan. Misalnya, /user-service/v1/ mungkin masih disajikan saat /user-service/v2/ dirilis. Fakta ini adalah bagian penting dalam perilisan kode independen. Anda mungkin hanya menonaktifkan versi API utama yang lama setelah memastikan bahwa tidak ada microservice lain yang memerlukannya, termasuk microservice lain yang mungkin perlu melakukan roll back ke versi kode yang lebih lama.

Sebagai contoh konkret, bayangkan Anda memiliki microservice, bernama web-app, yang bergantung pada microservice lainnya, bernama user-service. Bayangkan user-service perlu mengubah beberapa implementasi yang mendasarinya yang akan membuat dukungan terhadap versi API utama yang lama web-app dan saat ini digunakan tidak dapat lagi digunakan, seperti menciutkan firstName dan lastName ke dalam satu kolom yang disebut name. Artinya, user-service perlu menonaktifkan versi API utama yang lama.

Untuk melakukan perubahan ini, Anda harus melakukan tiga deployment terpisah:

  • Pertama, user-service harus men-deploy /user-service/v2/ sekaligus tetap mendukung /user-service/v1/. Deployment ini mungkin memerlukan kode sementara yang ditulis untuk mendukung kompatibilitas mundur, yang merupakan konsekuensi umum dalam aplikasi berbasis microservice

  • Selanjutnya, web-app harus men-deploy kode terbaru yang mengubah dependensinya dari /user-service/v1/ menjadi /user-service/v2/

  • Terakhir, setelah tim user-service memverifikasi bahwa web-app tidak lagi memerlukan /user-service/v1/ dan web-app tidak perlu melakukan roll back, tim dapat men-deploy kode yang menghapus endpoint /user-service/v1/ lama dan kode sementara yang diperlukan untuk mendukungnya.

Meskipun mungkin tampak berlebihan, aktivitas ini adalah proses penting dalam aplikasi berbasis microservice, dan merupakan proses yang memungkinkan siklus rilis pengembangan independen. Untuk lebih jelasnya, proses ini tampaknya cukup dependen, tetapi yang penting adalah setiap langkah di atas dapat terjadi pada linimasa yang independen, serta roll forward dan rollback terjadi dalam cakupan satu microservice. Hanya urutan langkah yang tetap dan langkah-langkahnya dapat berlangsung selama berjam-jam, berhari-hari, atau bahkan berminggu-minggu.

Langkah selanjutnya