View a markdown version of this page

Membuat arsitektur berbasis peristiwa dengan Lambda - AWS Lambda

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Membuat arsitektur berbasis peristiwa dengan Lambda

Peristiwa adalah apa pun yang memicu fungsi Lambda untuk dijalankan. Peristiwa dapat memicu fungsi Lambda dalam dua cara: melalui pemanggilan langsung (push) dan pemetaan sumber peristiwa (tarik).

Banyak AWS layanan dapat langsung memanggil fungsi Lambda Anda. Layanan ini mendorong peristiwa ke fungsi Lambda Anda. Peristiwa yang memicu fungsi dapat berupa hampir apa saja, mulai dari permintaan HTTP melalui API Gateway, jadwal yang dikelola oleh EventBridge Scheduler, AWS IoT acara, atau acara Amazon S3. Dengan pemetaan sumber peristiwa, Lambda secara aktif mengambil (atau menarik) peristiwa dari antrian atau aliran. Anda mengonfigurasi Lambda untuk memeriksa peristiwa dari layanan yang didukung, dan Lambda menangani polling dan pemanggilan fungsi Anda.

Ketika diteruskan ke fungsi Anda, peristiwa disusun dalam format JSON. Struktur JSON bervariasi tergantung pada layanan yang menghasilkannya dan jenis peristiwa. Sementara pemanggilan fungsi Lambda standar dapat bertahan hingga 15 menit, Lambda paling cocok untuk pemanggilan singkat yang berlangsung satu detik atau kurang. Hal ini terutama berlaku untuk arsitektur berbasis peristiwa, di mana setiap fungsi Lambda diperlakukan sebagai layanan mikro yang bertanggung jawab untuk melakukan serangkaian instruksi spesifik yang sempit.

catatan

Event-driven arsitektur berkomunikasi di berbagai sistem menggunakan jaringan, yang memperkenalkan latensi variabel. Untuk beban kerja yang membutuhkan latensi sangat rendah, seperti sistem perdagangan real-time, desain ini mungkin bukan pilihan terbaik. Namun, untuk beban kerja yang sangat skalabel dan tersedia, atau yang memiliki pola lalu lintas yang tidak dapat diprediksi, arsitektur berbasis peristiwa dapat memberikan cara yang efektif untuk memenuhi tuntutan ini.

Manfaat arsitektur yang digerakkan oleh peristiwa

Lambda mendukung dua metode pemanggilan dalam arsitektur berbasis peristiwa:

  1. Panggilan langsung (metode push): AWS layanan memicu fungsi Lambda secara langsung. Contoh:

    • Amazon S3 memicu fungsi saat file diunggah

    • API Gateway memicu fungsi saat menerima permintaan HTTP

  2. Pemetaan sumber peristiwa (metode tarik): Lambda mengambil peristiwa dan memanggil fungsi. Contoh:

    • Lambda mengambil pesan dari antrian Amazon SQS dan memanggil fungsi

    • Lambda membaca catatan dari aliran DynamoDB dan memanggil fungsi

Kedua metode berkontribusi pada manfaat arsitektur berbasis peristiwa, seperti yang dijelaskan di bawah ini.

Mengganti polling dan webhook dengan acara

Banyak arsitektur tradisional menggunakan mekanisme polling dan webhook untuk mengkomunikasikan status antara komponen yang berbeda. Polling bisa sangat tidak efisien untuk mengambil pembaruan karena ada jeda antara data baru yang tersedia dan sinkronisasi dengan layanan hilir. Webhook tidak selalu didukung oleh layanan mikro lain yang ingin Anda integrasikan. Mereka mungkin juga memerlukan konfigurasi otorisasi dan otentikasi khusus. Dalam kedua kasus, metode integrasi ini menantang untuk skala sesuai permintaan tanpa pekerjaan tambahan oleh tim pengembangan.

Diagram yang menunjukkan panggilan layanan-ke-layanan langsung diganti dengan perutean berbasis peristiwa dalam arsitektur berbasis peristiwa.

Kedua mekanisme ini dapat digantikan oleh peristiwa, yang dapat disaring, dirutekan, dan didorong ke hilir untuk menggunakan layanan mikro. Pendekatan ini dapat menghasilkan konsumsi bandwidth yang lebih sedikit, pemanfaatan CPU, dan biaya yang berpotensi lebih rendah. Arsitektur ini juga dapat mengurangi kompleksitas, karena setiap unit fungsional lebih kecil dan seringkali ada lebih sedikit kode.

Diagram yang menunjukkan peristiwa yang difilter, dirutekan, dan didorong ke beberapa target tanpa layanan kopling.

Event-driven arsitektur juga dapat membuatnya lebih mudah untuk merancang sistem yang mendekati waktu nyata, membantu organisasi menjauh dari pemrosesan berbasis batch. Peristiwa dihasilkan pada saat status dalam aplikasi berubah, sehingga kode khusus dari layanan mikro harus dirancang untuk menangani pemrosesan satu peristiwa. Karena penskalaan ditangani oleh layanan Lambda, arsitektur ini dapat menangani peningkatan lalu lintas yang signifikan tanpa mengubah kode khusus. Saat peristiwa meningkat, begitu juga lapisan komputasi yang memproses peristiwa.

Mengurangi kompleksitas

Layanan mikro memungkinkan pengembang dan arsitek untuk menyederhanakan alur kerja yang kompleks. Misalnya, monolit e-commerce dapat dipecah menjadi proses penerimaan pesanan dan pembayaran dengan inventaris, pemenuhan, dan layanan akuntansi terpisah. Apa yang mungkin rumit untuk dikelola dan diatur dalam monolit menjadi serangkaian layanan terpisah yang berkomunikasi secara asinkron dengan peristiwa.

Diagram yang menunjukkan layanan memproses data pada tingkat yang berbeda menggunakan antrian peristiwa sebagai buffer.

Pendekatan ini juga memungkinkan untuk mengumpulkan layanan yang memproses data pada tingkat yang berbeda. Dalam hal ini, layanan mikro penerimaan pesanan dapat menyimpan volume tinggi pesanan masuk dengan menyangga pesan dalam antrian Amazon SQS.

Layanan pemrosesan pembayaran, yang biasanya lebih lambat karena kompleksitas penanganan pembayaran, dapat mengambil aliran pesan yang stabil dari antrian Amazon SQS. Ini dapat mengatur logika percobaan ulang dan penanganan kesalahan yang kompleks menggunakan AWS Step Functions, dan mengoordinasikan alur kerja pembayaran aktif untuk ratusan ribu pesanan.

Pendekatan alternatif: Untuk orkestrasi menggunakan bahasa pemrograman standar, Anda dapat menggunakan fungsi tahan lama Lambda. Fungsi tahan lama memungkinkan Anda menulis penerimaan pesanan, pemrosesan pembayaran, dan logika notifikasi dalam kode dengan checkpointing otomatis dan coba lagi. Pendekatan ini bekerja dengan baik ketika alur kerja terutama melibatkan fungsi Lambda dan Anda lebih suka menjaga logika orkestrasi dalam kode.

Meningkatkan skalabilitas dan ekstensibilitas

Layanan mikro menghasilkan peristiwa yang biasanya dipublikasikan ke layanan pesan seperti Amazon SNS dan Amazon SQS. Ini berperilaku seperti buffer elastis antara layanan mikro dan membantu menangani penskalaan saat lalu lintas meningkat. Layanan seperti Amazon kemudian EventBridge dapat memfilter dan merutekan pesan tergantung pada konten acara, seperti yang didefinisikan dalam aturan. Akibatnya, aplikasi berbasis peristiwa dapat lebih skalabel dan menawarkan redundansi yang lebih besar daripada aplikasi monolitik.

Sistem ini juga sangat dapat diperluas, memungkinkan tim lain untuk memperluas fitur dan menambahkan fungsionalitas tanpa mempengaruhi pemrosesan pesanan dan layanan mikro pemrosesan pembayaran. Dengan menerbitkan acara menggunakan EventBridge, aplikasi ini terintegrasi dengan sistem yang ada, seperti layanan mikro inventaris, tetapi juga memungkinkan aplikasi masa depan untuk berintegrasi sebagai konsumen acara. Produsen acara tidak memiliki pengetahuan tentang konsumen acara, yang dapat membantu menyederhanakan logika layanan mikro.

Trade-offs arsitektur yang digerakkan oleh peristiwa

Variabel latensi

Tidak seperti aplikasi monolitik, yang mungkin memproses segala sesuatu dalam ruang memori yang sama pada satu perangkat, aplikasi berbasis peristiwa berkomunikasi di seluruh jaringan. Desain ini memperkenalkan latensi variabel. Meskipun memungkinkan untuk merekayasa aplikasi untuk meminimalkan latensi, aplikasi monolitik hampir selalu dapat dioptimalkan untuk latensi yang lebih rendah dengan mengorbankan skalabilitas dan ketersediaan.

Beban kerja yang membutuhkan kinerja latensi rendah yang konsisten, seperti aplikasi perdagangan frekuensi tinggi di bank atau otomatisasi robotika sub-milidetik di gudang, bukanlah kandidat yang baik untuk arsitektur berbasis peristiwa.

Konsistensi akhirnya

Suatu peristiwa mewakili perubahan status, dan dengan banyak peristiwa yang mengalir melalui layanan yang berbeda dalam arsitektur pada titik waktu tertentu, beban kerja tersebut seringkali pada akhirnya konsisten. Ini membuatnya lebih kompleks untuk memproses transaksi, menangani duplikat, atau menentukan keadaan keseluruhan sistem yang tepat.

Beberapa beban kerja berisi kombinasi persyaratan yang pada akhirnya konsisten (misalnya, total pesanan dalam jam saat ini) atau sangat konsisten (misalnya, inventaris saat ini). Untuk beban kerja yang membutuhkan konsistensi data yang kuat, ada pola arsitektur untuk mendukung ini. Contoh:

  • DynamoDB dapat memberikan pembac aan yang sangat konsisten, kadang-kadang pada latensi yang lebih tinggi, menghabiskan throughput yang lebih besar daripada mode default. DynamoDB juga dapat mendukung transaksi untuk membantu menjaga konsistensi data.

  • Anda dapat menggunakan Amazon RDS untuk fitur yang membutuhkan properti AC ID, meskipun database relasional umumnya kurang terukur daripada database NoSQL seperti DynamoDB. Amazon RDS Proxy dapat membantu mengelola pengumpulan koneksi dan penskalaan dari konsumen sementara seperti fungsi Lambda.

Event-based Arsitektur biasanya dirancang di sekitar peristiwa individu alih-alih kumpulan data yang besar. Umumnya, alur kerja dirancang untuk mengelola langkah-langkah dari peristiwa individu atau alur eksekusi alih-alih beroperasi pada beberapa peristiwa secara bersamaan. Dalam tanpa server, pemrosesan peristiwa real-time lebih disukai daripada pemrosesan batch: batch harus diganti dengan banyak pembaruan tambahan yang lebih kecil. Meskipun ini dapat membuat beban kerja lebih tersedia dan dapat diskalakan, itu juga membuatnya lebih menantang bagi acara untuk memiliki kesadaran akan peristiwa lain.

Mengembalikan nilai ke pemanggil

Dalam banyak kasus, aplikasi berbasis peristiwa bersifat asinkron. Ini berarti bahwa layanan penelepon tidak menunggu permintaan dari layanan lain sebelum melanjutkan pekerjaan lain. Ini adalah karakteristik mendasar dari arsitektur berbasis peristiwa yang memungkinkan skalabilitas dan fleksibilitas. Ini berarti bahwa meneruskan nilai pengembalian atau hasil alur kerja lebih kompleks daripada dalam aliran eksekusi sinkron.

Sebagian besar pemanggilan Lambda dalam sistem produksi bersifat asinkron, menanggapi peristiwa dari layanan seperti Amazon S3 atau Amazon SQS. Dalam kasus ini, keberhasilan atau kegagalan memproses suatu peristiwa seringkali lebih penting daripada mengembalikan nilai. Fitur seperti antrean surat mati (DLQ) di Lambda disediakan untuk memastikan Anda dapat mengidentifikasi dan mencoba kembali peristiwa yang gagal, tanpa perlu memberi tahu penelepon.

Debugging di seluruh layanan dan fungsi

Debugging sistem berbasis peristiwa juga berbeda dibandingkan dengan aplikasi monolitik. Dengan berbagai sistem dan layanan yang melewati peristiwa, tidak mungkin untuk merekam dan mereproduksi keadaan yang tepat dari beberapa layanan ketika kesalahan terjadi. Karena setiap pemanggilan layanan dan fungsi memiliki file log terpisah, mungkin lebih rumit untuk menentukan apa yang terjadi pada peristiwa tertentu yang menyebabkan kesalahan.

Ada tiga persyaratan penting untuk membangun pendekatan debugging yang sukses dalam sistem berbasis peristiwa. Pertama, sistem logging yang kuat sangat penting, dan ini disediakan di seluruh AWS layanan dan disematkan dalam fungsi Lambda oleh Amazon CloudWatch. Kedua, dalam sistem ini, penting untuk memastikan bahwa setiap peristiwa memiliki pengenal transaksi yang dicatat pada setiap langkah selama transaksi, untuk membantu saat mencari log.

Akhirnya, sangat disarankan untuk mengotomatiskan penguraian dan analisis log dengan menggunakan layanan debugging dan pemantauan seperti AWS X-Ray. Ini dapat menggunakan log di beberapa pemanggilan dan layanan Lambda, membuatnya lebih mudah untuk menentukan akar penyebab masalah. Lihat Panduan Pemecahan Masalah untuk cakupan mendalam tentang penggunaan X-Ray untuk pemecahan masalah.

Anti-patterns dalam aplikasi yang Lambda-based digerakkan oleh peristiwa

Saat membangun arsitektur berbasis peristiwa dengan Lambda, hindari anti-pola umum berikut. Pola-pola ini bekerja tetapi dapat meningkatkan biaya dan kompleksitas.

Monolit Lambda

Dalam banyak aplikasi yang dimigrasi dari server tradisional, seperti instans Amazon EC2 atau aplikasi Elastic Beanstalk, pengembang “mengangkat dan menggeser” kode yang ada. Seringkali, ini menghasilkan fungsi Lambda tunggal yang berisi semua logika aplikasi yang dipicu untuk semua peristiwa. Untuk aplikasi web dasar, fungsi Lambda monolitik akan menangani semua rute Gateway API dan berintegrasi dengan semua sumber daya hilir yang diperlukan.

Diagram menunjukkan pola orkestrasi sinkron di mana satu fungsi memanggil beberapa layanan secara berurutan.

Pendekatan ini memiliki beberapa kelemahan:

  • Ukuran paket — Fungsi Lambda mungkin jauh lebih besar karena berisi semua kode yang mungkin untuk semua jalur, yang membuatnya lebih lambat untuk menjalankan layanan Lambda.

  • Sulit untuk menerapkan hak istimewa terkecil - Peran eksekusi fungsi harus mengizinkan izin ke semua sumber daya yang diperlukan untuk semua jalur, membuat izin sangat luas. Ini adalah masalah keamanan. Banyak jalur dalam monolit fungsional tidak memerlukan semua izin yang telah diberikan.

  • Lebih sulit untuk ditingkatkan - Dalam sistem produksi, peningkatan apa pun ke fungsi tunggal lebih berisiko dan dapat merusak seluruh aplikasi. Meningkatkan jalur tunggal dalam fungsi Lambda adalah peningkatan ke seluruh fungsi.

  • Lebih sulit untuk mempertahankan — Lebih sulit untuk memiliki beberapa pengembang yang mengerjakan layanan karena ini adalah repositori kode monolitik. Ini juga meningkatkan beban kognitif pada pengembang dan membuatnya lebih sulit untuk membuat cakupan pengujian yang sesuai untuk kode.

  • Lebih sulit untuk menggunakan kembali kode — Lebih sulit untuk memisahkan pustaka yang dapat digunakan kembali dari monolit, membuat penggunaan kembali kode menjadi lebih sulit. Saat Anda mengembangkan dan mendukung lebih banyak proyek, ini dapat membuat lebih sulit untuk mendukung kode dan meningkatkan kecepatan tim Anda.

  • Lebih sulit untuk diuji — Seiring bertambahnya baris kode, menjadi lebih sulit untuk menguji semua kemungkinan kombinasi input dan titik masuk dalam basis kode. Umumnya lebih mudah untuk menerapkan pengujian unit untuk layanan yang lebih kecil dengan kode yang lebih sedikit.

Alternatif yang lebih disukai adalah memecah fungsi Lambda monolitik menjadi layanan mikro individu, memetakan fungsi Lambda tunggal ke tugas tunggal yang terdefinisi dengan baik. Dalam aplikasi web sederhana ini dengan beberapa titik akhir API, arsitektur berbasis layanan mikro yang dihasilkan dapat didasarkan pada rute API Gateway.

Diagram yang menunjukkan layanan decoupling pola berbasis peristiwa asinkron melalui bus acara.

Pola rekursif yang menyebabkan fungsi Lambda yang melarikan diri

AWS layanan menghasilkan peristiwa yang memanggil fungsi Lambda, dan fungsi Lambda dapat mengirim pesan ke layanan. AWS Umumnya, layanan atau sumber daya yang memanggil fungsi Lambda harus berbeda dengan layanan atau sumber daya yang dikeluarkan fungsi tersebut. Kegagalan untuk mengelola ini dapat mengakibatkan loop tak terbatas.

Misalnya, fungsi Lambda menulis objek ke objek Amazon S3, yang pada gilirannya memanggil fungsi Lambda yang sama dengan menggunakan event put. Pemanggilan menyebabkan objek kedua ditulis ke bucket, yang memanggil fungsi Lambda yang sama:

Diagram menunjukkan loop tak terbatas di mana fungsi Lambda menulis ke Amazon S3 memicu dirinya sendiri berulang kali.

Sementara potensi loop tak terbatas ada di sebagian besar bahasa pemrograman, anti-pola ini memiliki potensi untuk mengkonsumsi lebih banyak sumber daya dalam aplikasi tanpa server. Baik Lambda maupun Amazon S3 secara otomatis menskalakan berdasarkan lalu lintas, sehingga loop dapat menyebabkan Lambda menskalakan untuk menggunakan semua konkurensi yang tersedia dan Amazon S3 terus menulis objek dan menghasilkan lebih banyak peristiwa untuk Lambda.

Contoh ini menggunakan S3, tetapi risiko loop rekursif juga ada di Amazon SNS, Amazon SQS, DynamoDB, dan layanan lainnya. Anda dapat menggunakan deteksi loop rekursif untuk menemukan dan menghindari anti-pola ini.

Fungsi lambda memanggil fungsi Lambda

Fungsi memungkinkan enkapsulasi dan penggunaan kembali kode. Sebagian besar bahasa pemrograman mendukung konsep kode yang secara serempak memanggil fungsi dalam basis kode. Dalam hal ini, pemanggil menunggu sampai fungsi mengembalikan respons.

catatan

Sementara fungsi Lambda yang secara langsung memanggil fungsi Lambda lainnya umumnya merupakan anti-pola karena masalah biaya dan kompleksitas, ini tidak berlaku untuk fungsi tahan lama, yang dirancang khusus untuk mengatur alur kerja multi-langkah dengan memanggil fungsi lain.

Ketika ini terjadi pada server tradisional atau instance virtual, penjadwal sistem operasi beralih ke pekerjaan lain yang tersedia. Apakah CPU berjalan pada 0% atau 100% tidak mempengaruhi biaya keseluruhan aplikasi, karena Anda membayar biaya tetap untuk memiliki dan mengoperasikan server.

Model ini sering tidak beradaptasi dengan baik untuk pengembangan tanpa server. Misalnya, pertimbangkan aplikasi e-commerce sederhana yang terdiri dari tiga fungsi Lambda yang memproses pesanan:

Diagram menunjukkan rangkaian fungsi sinkron di mana satu fungsi langsung memanggil yang lain.

Dalam hal ini, fungsi Buat pesanan memanggil fungsi Proses pembayaran, yang pada gilirannya memanggil fungsi Buat faktur. Meskipun aliran sinkron ini mungkin bekerja dalam satu aplikasi di server, ini memperkenalkan beberapa masalah yang dapat dihindari dalam arsitektur tanpa server terdistribusi:

  • Biaya — Dengan Lambda, Anda membayar untuk durasi pemanggilan. Dalam contoh ini, saat fungsi Buat faktur berjalan, dua fungsi lain juga berjalan dalam keadaan tunggu, ditunjukkan dengan warna merah pada diagram.

  • Penanganan kesalahan - Dalam pemanggilan bersarang, penanganan kesalahan bisa menjadi jauh lebih kompleks. Misalnya, kesalahan dalam Membuat faktur mungkin memerlukan fungsi Proses pembayaran untuk membalikkan tagihan, atau mungkin mencoba lagi proses Buat faktur.

  • Kopling ketat — Memproses pembayaran biasanya membutuhkan waktu lebih lama daripada membuat faktur. Dalam model ini, ketersediaan seluruh alur kerja dibatasi oleh fungsi paling lambat.

  • Penskalaan — Konkur ensi dari ketiga fungsi harus sama. Dalam sistem yang sibuk, ini menggunakan lebih banyak konkurensi daripada yang seharusnya dibutuhkan.

Dalam aplikasi tanpa server, ada dua pendekatan umum untuk menghindari pola ini. Pertama, gunakan antrian Amazon SQS antara fungsi Lambda. Jika proses hilir lebih lambat daripada proses hulu, antrian akan mempertahankan pesan secara tahan lama dan memisahkan kedua fungsi. Dalam contoh ini, fungsi Buat pesanan akan menerbitkan pesan ke antrian Amazon SQS, dan fungsi Proses pembayaran menggunakan pesan dari antrian.

Pendekatan kedua adalah menggunakan AWS Step Functions. Untuk proses kompleks dengan beberapa jenis kegagalan dan logika coba ulang, Fungsi Langkah dapat membantu mengurangi jumlah kode khusus yang diperlukan untuk mengatur alur kerja. Akibatnya, Step Functions mengatur pekerjaan dan menangani kesalahan dan percobaan ulang dengan kuat, dan fungsi Lambda hanya berisi logika bisnis.

Menunggu sinkron dalam satu fungsi Lambda

Pastikan bahwa aktivitas yang berpotensi bersamaan tidak dijadwalkan secara sinkron dalam satu fungsi Lambda. Misalnya, fungsi Lambda mungkin menulis ke bucket S3 dan kemudian menulis ke tabel DynamoDB:

Diagram yang menunjukkan pemrosesan berurutan dengan waktu tunggu gabungan di beberapa panggilan fungsi.

Dalam desain ini, waktu tunggu diperparah karena aktivitasnya berurutan. Dalam kasus di mana tugas kedua tergantung pada penyelesaian tugas pertama, Anda dapat mengurangi total waktu tunggu dan biaya eksekusi dengan memiliki dua fungsi Lambda terpisah:

Diagram menunjukkan pemrosesan asinkron di mana fungsi pertama merespons segera setelah menulis ke Amazon S3.

Dalam desain ini, fungsi Lambda pertama merespons segera setelah meletakkan objek ke bucket Amazon S3. Layanan S3 memanggil fungsi Lambda kedua, yang kemudian menulis data ke tabel DynamoDB. Pendekatan ini meminimalkan total waktu tunggu dalam eksekusi fungsi Lambda.