Mengirim Email Secara Asinkron Melalui AWS SES

Diterbitkan: 2022-03-10
Ringkasan cepat Mengirim banyak email transaksional sekaligus, jika tidak dirancang dengan benar, dapat menjadi penghambat aplikasi dan menurunkan pengalaman pengguna. Sebagian dari masalahnya adalah menghubungkan ke server SMTP dari dalam aplikasi, secara sinkron. Dalam artikel ini kita akan mengeksplorasi cara mengirim email dari luar aplikasi, secara asinkron, menggunakan kombinasi AWS S3, Lambda, dan SES.

Sebagian besar aplikasi mengirim email untuk berkomunikasi dengan penggunanya. Email transaksional adalah email yang dipicu oleh interaksi pengguna dengan aplikasi, seperti saat menyambut pengguna baru setelah mendaftar di situs, memberi pengguna tautan untuk mereset kata sandi, atau melampirkan faktur setelah pengguna melakukan pembelian. Semua kasus sebelumnya ini biasanya hanya memerlukan pengiriman satu email ke pengguna. Namun, dalam beberapa kasus lain, aplikasi perlu mengirim lebih banyak email, seperti ketika pengguna memposting konten baru di situs, dan semua pengikutnya (yang, dalam platform seperti Twitter, mungkin berjumlah jutaan pengguna) akan menerima pemberitahuan. Dalam situasi terakhir ini, tidak dirancang dengan benar, pengiriman email dapat menjadi hambatan dalam aplikasi.

Itulah yang terjadi dalam kasus saya. Saya memiliki situs yang mungkin perlu mengirim 20 email setelah beberapa tindakan yang dipicu pengguna (seperti pemberitahuan pengguna ke semua pengikutnya). Awalnya, ini mengandalkan pengiriman email melalui penyedia SMTP berbasis cloud yang populer (seperti SendGrid, Mandrill, Mailjet dan Mailgun), namun tanggapan kembali ke pengguna akan memakan waktu beberapa detik. Jelas, menghubungkan ke server SMTP untuk mengirim 20 email itu memperlambat proses secara signifikan.

Setelah diperiksa, saya menemukan sumber masalahnya:

  1. Koneksi sinkron
    Aplikasi terhubung ke server SMTP dan menunggu pengakuan, secara sinkron, sebelum melanjutkan eksekusi proses.
  2. Latensi tinggi
    Meskipun server saya berlokasi di Singapura, penyedia SMTP yang saya gunakan memiliki server yang berlokasi di AS, sehingga koneksi pulang pergi membutuhkan waktu yang cukup lama.
  3. Tidak dapat digunakan kembali koneksi SMTP
    Saat memanggil fungsi untuk mengirim email, fungsi mengirim email segera, membuat koneksi SMTP baru pada saat itu (tidak menawarkan untuk mengumpulkan semua email dan mengirim semuanya bersama-sama di akhir permintaan, di bawah satu SMTP koneksi).

Karena #1, waktu yang harus ditunggu pengguna untuk mendapatkan tanggapan terkait dengan waktu yang diperlukan untuk mengirim email. Karena #2, waktu untuk mengirim satu email relatif tinggi. Dan karena #3, waktu untuk mengirim 20 email adalah 20 kali waktu yang dibutuhkan untuk mengirim satu email. Meskipun mengirim hanya satu email mungkin tidak membuat aplikasi menjadi sangat lambat, mengirim 20 email tentu saja bisa, memengaruhi pengalaman pengguna.

Mari kita lihat bagaimana kita bisa menyelesaikan masalah ini.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Memperhatikan Sifat Email Transaksional

Sebelum apa pun, kita harus memperhatikan bahwa tidak semua email sama pentingnya. Kami secara luas dapat mengkategorikan email menjadi dua kelompok: email prioritas dan non-prioritas. Misalnya, jika pengguna lupa kata sandi untuk mengakses akun, dia akan mengharapkan email dengan tautan pengaturan ulang kata sandi segera di kotak masuknya; itu adalah email prioritas. Sebaliknya, mengirim email yang memberitahukan bahwa seseorang yang kita ikuti telah memposting konten baru tidak perlu langsung masuk ke kotak masuk pengguna; itu adalah email non-prioritas.

Solusinya harus mengoptimalkan bagaimana dua kategori email ini dikirim. Dengan asumsi bahwa hanya akan ada beberapa (mungkin 1 atau 2) email prioritas yang akan dikirim selama proses, dan sebagian besar email akan menjadi email non-prioritas, maka kami merancang solusi sebagai berikut:

  • Email prioritas dapat dengan mudah menghindari masalah latensi tinggi dengan menggunakan penyedia SMTP yang terletak di wilayah yang sama tempat aplikasi disebarkan. Selain riset yang baik, ini melibatkan pengintegrasian aplikasi kita dengan API penyedia.
  • Email non-prioritas dapat dikirim secara asinkron, dan dalam kumpulan di mana banyak email dikirim bersamaan. Diimplementasikan pada tingkat aplikasi, membutuhkan tumpukan teknologi yang sesuai.

Mari kita tentukan tumpukan teknologi untuk mengirim email secara asinkron selanjutnya.

Mendefinisikan Tumpukan Teknologi

Catatan: Saya telah memutuskan untuk mendasarkan tumpukan saya pada layanan AWS karena situs web saya sudah dihosting di AWS EC2. Jika tidak, saya akan mengalami overhead dari memindahkan data di antara beberapa jaringan perusahaan. Namun, kami juga dapat menerapkan solusi kami menggunakan penyedia layanan cloud lainnya.

Pendekatan pertama saya adalah mengatur antrian. Melalui antrian, saya dapat membuat aplikasi tidak mengirim email lagi, tetapi malah menerbitkan pesan dengan konten email dan metadata dalam antrian, dan kemudian meminta proses lain mengambil pesan dari antrian dan mengirim email.

Namun, ketika memeriksa layanan antrian dari AWS, yang disebut SQS, saya memutuskan bahwa itu bukan solusi yang tepat, karena:

  • Pengaturannya agak rumit;
  • Pesan antrian standar hanya dapat menyimpan informasi 256 kb teratas, yang mungkin tidak cukup jika email memiliki lampiran (misalnya faktur). Dan meskipun dimungkinkan untuk membagi pesan besar menjadi pesan yang lebih kecil, kompleksitasnya semakin bertambah.

Kemudian saya menyadari bahwa saya dapat dengan sempurna meniru perilaku antrean melalui kombinasi layanan AWS lainnya, S3 dan Lambda, yang jauh lebih mudah disiapkan. S3, solusi penyimpanan objek cloud untuk menyimpan dan mengambil data, dapat bertindak sebagai repositori untuk mengunggah pesan, dan Lambda, layanan komputasi yang menjalankan kode sebagai respons terhadap peristiwa, dapat memilih pesan dan menjalankan operasi dengannya.

Dengan kata lain, kita dapat mengatur proses pengiriman email kita seperti ini:

  1. Aplikasi mengunggah file dengan konten email + metadata ke bucket S3.
  2. Setiap kali file baru diunggah ke dalam ember S3, S3 memicu peristiwa yang berisi jalur ke file baru.
  3. Fungsi Lambda mengambil acara, membaca file, dan mengirim email.

Akhirnya, kita harus memutuskan bagaimana cara mengirim email. Kita dapat tetap menggunakan penyedia SMTP yang sudah kita miliki, memiliki fungsi Lambda yang berinteraksi dengan API mereka, atau menggunakan layanan AWS untuk mengirim email, yang disebut SES. Menggunakan SES memiliki kelebihan dan kekurangan:

Manfaat:

  • Sangat mudah digunakan dari dalam AWS Lambda (hanya membutuhkan 2 baris kode).
  • Lebih murah: Biaya Lambda dihitung berdasarkan jumlah waktu yang diperlukan untuk menjalankan fungsi, jadi menghubungkan ke SES dari dalam jaringan AWS akan memakan waktu lebih singkat daripada menghubungkan ke server eksternal, membuat fungsi selesai lebih awal dan biaya lebih murah . (Kecuali SES tidak tersedia di wilayah yang sama tempat aplikasi dihosting; dalam kasus saya, karena SES tidak ditawarkan di wilayah Asia Pasifik (Singapura), tempat server EC2 saya berada, maka saya mungkin lebih baik menghubungkan ke beberapa Penyedia SMTP eksternal berbasis di Asia).

Kekurangan:

  • Tidak banyak statistik untuk memantau email terkirim kami yang disediakan, dan menambahkan yang lebih kuat memerlukan upaya ekstra (misalnya: melacak persentase email yang dibuka, atau tautan apa yang diklik, harus disiapkan melalui AWS CloudWatch).
  • Jika kami terus menggunakan penyedia SMTP untuk mengirim email prioritas, maka statistik kami tidak akan ada di 1 tempat.

Untuk mempermudah, dalam kode di bawah ini kita akan menggunakan SES.

Kami kemudian mendefinisikan logika proses dan tumpukan sebagai berikut: Aplikasi mengirimkan email prioritas seperti biasa, tetapi untuk yang bukan prioritas, aplikasi mengunggah file dengan konten email dan metadata ke S3; file ini diproses secara asinkron oleh fungsi Lambda, yang terhubung ke SES untuk mengirim email.

Mari kita mulai menerapkan solusinya.

Membedakan Antara Email Prioritas dan Non-Prioritas

Singkatnya, ini semua tergantung pada aplikasinya, jadi kita perlu memutuskan email demi email. Saya akan menjelaskan solusi yang saya terapkan untuk WordPress, yang memerlukan beberapa peretasan di sekitar batasan dari fungsi wp_mail . Untuk platform lain, strategi di bawah ini juga akan berhasil, tetapi sangat mungkin akan ada strategi yang lebih baik, yang tidak memerlukan peretasan untuk bekerja.

Cara mengirim email di WordPress adalah dengan memanggil fungsi wp_mail , dan kami tidak ingin mengubahnya (misalnya: dengan memanggil fungsi wp_mail_synchronous atau wp_mail_asynchronous ), jadi implementasi wp_mail kami perlu menangani kasus sinkron dan asinkron, dan perlu mengetahui ke grup mana email tersebut berada. Sayangnya, wp_mail tidak menawarkan parameter tambahan apa pun yang dapat digunakan untuk menilai informasi ini, karena dapat dilihat dari tanda tangannya:

 function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() )

Kemudian, untuk mengetahui kategori email, kami menambahkan solusi peretasan: secara default, kami menjadikan email milik grup prioritas, dan jika $to berisi email tertentu (misalnya: [email protected]), atau jika $subject dimulai dengan string khusus (misalnya: “[Non-priority!]“), maka itu milik grup non-priority (dan kami menghapus email atau string yang sesuai dari subjek). wp_mail adalah fungsi yang dapat dicolokkan, jadi kita dapat menimpanya hanya dengan mengimplementasikan fungsi baru dengan tanda tangan yang sama pada file functions.php kita. Awalnya, ini berisi kode yang sama dari fungsi wp_mail asli, yang terletak di file wp-includes/pluggable.php, untuk mengekstrak semua parameter:

 if ( !function_exists( 'wp_mail' ) ) : function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) { $atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) ); if ( isset( $atts['to'] ) ) { $to = $atts['to']; } if ( !is_array( $to ) ) { $to = explode( ',', $to ); } if ( isset( $atts['subject'] ) ) { $subject = $atts['subject']; } if ( isset( $atts['message'] ) ) { $message = $atts['message']; } if ( isset( $atts['headers'] ) ) { $headers = $atts['headers']; } if ( isset( $atts['attachments'] ) ) { $attachments = $atts['attachments']; } if ( ! is_array( $attachments ) ) { $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) ); } // Continue below... } endif;

Dan kemudian kami memeriksa apakah itu bukan prioritas, dalam hal ini kami kemudian beralih ke logika terpisah di bawah fungsi send_asynchronous_mail atau, jika tidak, kami tetap menjalankan kode yang sama seperti pada fungsi wp_mail asli:

 function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) { // Continued from above... $hacky_email = "[email protected]"; if (in_array($hacky_email, $to)) { // Remove the hacky email from $to array_splice($to, array_search($hacky_email, $to), 1); // Fork to asynchronous logic return send_asynchronous_mail($to, $subject, $message, $headers, $attachments); } // Continue all code from original function in wp-includes/pluggable.php // ... }

Dalam fungsi kami send_asynchronous_mail , alih-alih mengunggah email langsung ke S3, kami cukup menambahkan email ke variabel global $emailqueue , dari mana kami dapat mengunggah semua email bersama ke S3 dalam satu koneksi di akhir permintaan:

 function send_asynchronous_mail($to, $subject, $message, $headers, $attachments) { global $emailqueue; if (!$emailqueue) { $emailqueue = array(); } // Add email to queue. Code continues below... }

Kita bisa mengupload satu file per email, atau kita bisa bundle sehingga dalam 1 file kita berisi banyak email. Karena $headers berisi meta email (dari, tipe konten dan charset, CC, BCC, dan bidang balas-ke), kami dapat mengelompokkan email bersama-sama kapan pun mereka memiliki $headers yang sama. Dengan cara ini, semua email ini dapat diunggah dalam file yang sama ke S3, dan informasi meta $headers hanya akan disertakan sekali dalam file, bukan sekali per email:

 function send_asynchronous_mail($to, $subject, $message, $headers, $attachments) { // Continued from above... // Add email to the queue $emailqueue[$headers] = $emailqueue[$headers] ?? array(); $emailqueue[$headers][] = array( 'to' => $to, 'subject' => $subject, 'message' => $message, 'attachments' => $attachments, ); // Code continues below }

Terakhir, fungsi send_asynchronous_mail mengembalikan true . Harap perhatikan bahwa kode ini diretas: true biasanya berarti email berhasil dikirim, tetapi dalam kasus ini, email tersebut bahkan belum terkirim, dan bisa saja gagal. Karena itu, fungsi yang memanggil wp_mail tidak boleh memperlakukan respons yang true sebagai "email berhasil dikirim," tetapi pengakuan bahwa itu telah diantrekan. Itulah mengapa penting untuk membatasi teknik ini pada email non-prioritas sehingga jika gagal, proses dapat terus mencoba lagi di latar belakang, dan pengguna tidak akan mengharapkan email sudah ada di kotak masuknya:

 function send_asynchronous_mail($to, $subject, $message, $headers, $attachments) { // Continued from above... // That's it! return true; }

Mengunggah Email Ke S3

Dalam artikel saya sebelumnya “Berbagi Data Di Antara Beberapa Server Melalui AWS S3”, saya menjelaskan cara membuat bucket di S3, dan cara mengunggah file ke bucket melalui SDK. Semua kode di bawah ini melanjutkan implementasi solusi untuk WordPress, oleh karena itu kami terhubung ke AWS menggunakan SDK untuk PHP.

Kami dapat memperluas dari kelas abstrak AWS_S3 (diperkenalkan dalam artikel saya sebelumnya) untuk terhubung ke S3 dan mengunggah email ke "async-emails" ember di akhir permintaan (dipicu melalui kait wp_footer ). Harap perhatikan bahwa kami harus menjaga ACL sebagai "pribadi" karena kami tidak ingin email tersebut terekspos ke internet:

 class AsyncEmails_AWS_S3 extends AWS_S3 { function __construct() { // Send all emails at the end of the execution add_action("wp_footer", array($this, "upload_emails_to_s3"), PHP_INT_MAX); } protected function get_acl() { return "private"; } protected function get_bucket() { return "async-emails"; } function upload_emails_to_s3() { $s3Client = $this->get_s3_client(); // Code continued below... } } new AsyncEmails_AWS_S3();

Kami mulai mengulangi melalui pasangan header => emaildata yang disimpan dalam variabel global $emailqueue , dan mendapatkan konfigurasi default dari fungsi get_default_email_meta jika header kosong. Dalam kode di bawah ini, saya hanya mengambil bidang "dari" dari header (kode untuk mengekstrak semua header dapat disalin dari fungsi asli wp_mail ):

 class AsyncEmails_AWS_S3 extends AWS_S3 { public function get_default_email_meta() { // Code continued from above... return array( 'from' => sprintf( '%s <%s>', get_bloginfo('name'), get_bloginfo('admin_email') ), 'contentType' => 'text/html', 'charset' => strtolower(get_option('blog_charset')) ); } public function upload_emails_to_s3() { // Code continued from above... global $emailqueue; foreach ($emailqueue as $headers => $emails) { $meta = $this->get_default_email_meta(); // Retrieve the "from" from the headers $regexp = '/From:\s*(([^\<]*?) <)? ?\s*\n/i'; if(preg_match($regexp, $headers, $matches)) { $meta['from'] = sprintf( '%s <%s>', $matches[2], $matches[3] ); } // Code continued below... } } } class AsyncEmails_AWS_S3 extends AWS_S3 { public function get_default_email_meta() { // Code continued from above... return array( 'from' => sprintf( '%s <%s>', get_bloginfo('name'), get_bloginfo('admin_email') ), 'contentType' => 'text/html', 'charset' => strtolower(get_option('blog_charset')) ); } public function upload_emails_to_s3() { // Code continued from above... global $emailqueue; foreach ($emailqueue as $headers => $emails) { $meta = $this->get_default_email_meta(); // Retrieve the "from" from the headers $regexp = '/From:\s*(([^\<]*?) <)? ?\s*\n/i'; if(preg_match($regexp, $headers, $matches)) { $meta['from'] = sprintf( '%s <%s>', $matches[2], $matches[3] ); } // Code continued below... } } }

Akhirnya, kami mengunggah email ke S3. Kami memutuskan berapa banyak email yang akan diunggah per file dengan tujuan untuk menghemat uang. Fungsi Lambda membebankan biaya berdasarkan jumlah waktu yang mereka perlukan untuk dijalankan, dihitung dalam rentang 100 md. Semakin banyak waktu yang dibutuhkan suatu fungsi, semakin mahal harganya.

Mengirim semua email dengan mengunggah 1 file per email, kemudian, lebih mahal daripada mengunggah 1 file per banyak email, karena overhead dari menjalankan fungsi dihitung sekali per email, bukan hanya sekali untuk banyak email, dan juga karena mengirim banyak email bersama-sama mengisi rentang 100 ms lebih teliti.

Jadi kami mengunggah banyak email per file. Berapa banyak email? Fungsi Lambda memiliki waktu eksekusi maksimum (3 detik secara default), dan jika operasi gagal, ia akan terus mencoba lagi dari awal, bukan dari tempat gagalnya. Jadi, jika file berisi 100 email, dan Lambda berhasil mengirim 50 email sebelum waktu maksimum habis, maka file tersebut gagal dan mencoba menjalankan operasi lagi, mengirim 50 email pertama sekali lagi. Untuk menghindarinya, kita harus memilih sejumlah email per file yang kita yakini cukup untuk diproses sebelum waktu maksimal habis. Dalam situasi kami, kami dapat memilih untuk mengirim 25 email per file. Jumlah email tergantung pada aplikasi (email yang lebih besar akan memakan waktu lebih lama untuk dikirim, dan waktu untuk mengirim email akan tergantung pada infrastruktur), jadi kita harus melakukan beberapa pengujian untuk mendapatkan nomor yang tepat.

Konten file hanyalah objek JSON, berisi meta email di bawah properti "meta", dan potongan email di bawah properti "email":

 class AsyncEmails_AWS_S3 extends AWS_S3 { public function upload_emails_to_s3() { // Code continued from above... foreach ($emailqueue as $headers => $emails) { // Code continued from above... // Split the emails into chunks of no more than the value of constant EMAILS_PER_FILE: $chunks = array_chunk($emails, EMAILS_PER_FILE); $filename = time().rand(); for ($chunk_count = 0; $chunk_count < count($chunks); $chunk_count++) { $body = array( 'meta' => $meta, 'emails' => $chunks[$chunk_count], ); // Upload to S3 $s3Client->putObject([ 'ACL' => $this->get_acl(), 'Bucket' => $this->get_bucket(), 'Key' => $filename.$chunk_count.'.json', 'Body' => json_encode($body), ]); } } } }

Untuk mempermudah, dalam kode di atas, saya tidak mengunggah lampiran ke S3. Jika email kita perlu menyertakan lampiran, maka kita harus menggunakan fungsi SES SendRawEmail alih-alih SendEmail (yang digunakan dalam skrip Lambda di bawah).

Setelah menambahkan logika untuk mengunggah file dengan email ke S3, kita dapat pindah ke pengkodean fungsi Lambda.

Mengkodekan Skrip Lambda

Fungsi Lambda juga disebut fungsi tanpa server, bukan karena tidak berjalan di server, tetapi karena pengembang tidak perlu mengkhawatirkan server: pengembang cukup menyediakan skrip, dan cloud menangani penyediaan server, penerapan, dan menjalankan skrip. Oleh karena itu, seperti yang disebutkan sebelumnya, fungsi Lambda dibebankan berdasarkan waktu eksekusi fungsi.

Skrip Node.js berikut melakukan pekerjaan yang diperlukan. Dipanggil oleh acara "Put" S3, yang menunjukkan bahwa objek baru telah dibuat di bucket, fungsinya:

  1. Memperoleh jalur objek baru (di bawah variabel srcKey ) dan ember (di bawah variabel srcBucket ).
  2. Mengunduh objek, melalui s3.getObject .
  3. Mengurai konten objek, melalui JSON.parse(response.Body.toString()) , dan mengekstrak email dan meta email.
  4. Iterasi melalui semua email, dan mengirimkannya melalui ses.sendEmail .
 var async = require('async'); var aws = require('aws-sdk'); var s3 = new aws.S3(); exports.handler = function(event, context, callback) { var srcBucket = event.Records[0].s3.bucket.name; var srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")); // Download the file from S3, parse it, and send the emails async.waterfall([ function download(next) { // Download the file from S3 into a buffer. s3.getObject({ Bucket: srcBucket, Key: srcKey }, next); }, function process(response, next) { var file = JSON.parse(response.Body.toString()); var emails = file.emails; var emailsMeta = file.meta; // Check required parameters if (emails === null || emailsMeta === null) { callback('Bad Request: Missing required data: ' + response.Body.toString()); return; } if (emails.length === 0) { callback('Bad Request: No emails provided: ' + response.Body.toString()); return; } var totalEmails = emails.length; var sentEmails = 0; for (var i = 0; i < totalEmails; i++) { var email = emails[i]; var params = { Destination: { ToAddresses: email.to }, Message: { Subject: { Data: email.subject, Charset: emailsMeta.charset } }, Source: emailsMeta.from }; if (emailsMeta.contentType == 'text/html') { params.Message.Body = { Html: { Data: email.message, Charset: emailsMeta.charset } }; } else { params.Message.Body = { Text: { Data: email.message, Charset: emailsMeta.charset } }; } // Send the email var ses = new aws.SES({ "region": "us-east-1" }); ses.sendEmail(params, function(err, data) { if (err) { console.error('Unable to send email due to an error: ' + err); callback(err); } sentEmails++; if (sentEmails == totalEmails) { next(); } }); } } ], function (err) { if (err) { console.error('Unable to send emails due to an error: ' + err); callback(err); } // Success callback(null); }); };

Selanjutnya, kita harus mengunggah dan mengonfigurasi fungsi Lambda ke AWS, yang melibatkan:

  1. Membuat peran eksekusi yang memberikan izin Lambda untuk mengakses S3.
  2. Membuat paket .zip yang berisi semua kode, yaitu fungsi Lambda yang kita buat + semua modul Node.js yang diperlukan.
  3. Mengunggah paket ini ke AWS menggunakan alat CLI.

Cara melakukan hal-hal ini dijelaskan dengan benar di situs AWS, pada Tutorial Menggunakan AWS Lambda dengan Amazon S3.

Menghubungkan S3 Dengan Fungsi Lambda

Terakhir, setelah ember dan fungsi Lambda dibuat, kita perlu menghubungkan keduanya bersama-sama, sehingga setiap kali ada objek baru yang dibuat di ember, itu akan memicu sebuah peristiwa untuk menjalankan fungsi Lambda. Untuk melakukan ini, kita pergi ke dasbor S3 dan klik pada baris ember, yang akan menampilkan propertinya:

Menampilkan properti bucket di dalam dasbor S3
Mengklik baris bucket akan menampilkan properti bucket. (Pratinjau besar)

Kemudian mengklik Properties, kami menggulir ke bawah ke item "Acara", dan di sana kami mengklik Tambahkan pemberitahuan, dan masukkan bidang berikut:

  • Nama: nama notifikasi, misalnya: “EmailSender”;
  • Peristiwa: “Put”, yang merupakan peristiwa yang dipicu saat objek baru dibuat di ember;
  • Kirim ke: “Fungsi Lambda”;
  • Lambda: nama Lambda kami yang baru dibuat, misalnya: "LambdaEmailSender".
Menyiapkan S3 dengan Lambda
Menambahkan pemberitahuan di S3 untuk memicu acara untuk Lambda. (Pratinjau besar)

Terakhir, kita juga dapat mengatur ember S3 untuk secara otomatis menghapus file yang berisi data email setelah beberapa waktu. Untuk ini, kami pergi ke tab Manajemen ember, dan membuat aturan Siklus Hidup baru, yang menentukan setelah berapa hari email harus kedaluwarsa:

Aturan siklus hidup
Menyiapkan aturan Siklus Hidup untuk menghapus file secara otomatis dari bucket. (Pratinjau besar)

Itu dia. Mulai saat ini, ketika menambahkan objek baru pada ember S3 dengan konten dan meta untuk email, itu akan memicu fungsi Lambda, yang akan membaca file dan terhubung ke SES untuk mengirim email.

Saya menerapkan solusi ini di situs saya, dan itu menjadi cepat sekali lagi: dengan membongkar pengiriman email ke proses eksternal, apakah aplikasi mengirim 20 atau 5000 email tidak ada bedanya, respons terhadap pengguna yang memicu tindakan akan menjadi segera.

Kesimpulan

Dalam artikel ini kami telah menganalisis mengapa mengirim banyak email transaksional dalam satu permintaan dapat menjadi hambatan dalam aplikasi, dan menciptakan solusi untuk mengatasi masalah tersebut: alih-alih menghubungkan ke server SMTP dari dalam aplikasi (secara serempak), kami dapat kirim email dari fungsi eksternal, secara asinkron, berdasarkan tumpukan AWS S3 + Lambda + SES.

Dengan mengirim email secara asinkron, aplikasi dapat mengatur untuk mengirim ribuan email, namun respons pengguna yang memicu tindakan tidak akan terpengaruh. Namun, untuk memastikan bahwa pengguna tidak menunggu email masuk ke kotak masuk, kami juga memutuskan untuk membagi email menjadi dua grup, prioritas dan non-prioritas, dan hanya mengirim email non-prioritas secara asinkron. Kami menyediakan implementasi untuk WordPress, yang agak hacky karena keterbatasan fungsi wp_mail untuk mengirim email.

Pelajaran dari artikel ini adalah bahwa fungsionalitas tanpa server pada aplikasi berbasis server bekerja dengan cukup baik: situs yang berjalan pada CMS seperti WordPress dapat meningkatkan kinerjanya dengan hanya menerapkan fitur tertentu di cloud, dan menghindari banyak kerumitan yang diakibatkan oleh migrasi situs yang sangat dinamis ke arsitektur tanpa server sepenuhnya.