Cara mengelola email otomatis yang dikirim dari aplikasi web

12

Saya merancang aplikasi web dan saya bertanya-tanya bagaimana merancang arsitektur untuk mengelola pengiriman email otomatis.

Saat ini saya memiliki fitur ini dibangun di aplikasi web saya dan email dikirim berdasarkan input / interaksi pengguna (seperti membuat pengguna baru). Masalahnya adalah bahwa menghubungkan langsung ke server mail membutuhkan beberapa detik. Meningkatkan aplikasi saya, ini akan menjadi leher botol yang signifikan di masa depan.

Apa cara terbaik untuk mengelola pengiriman sejumlah besar email otomatis dalam arsitektur sistem saya?

Tidak akan ada sejumlah besar email yang dikirim (maks. 2000 per hari). Email tidak perlu segera dikirim, lag hingga 10 menit tidak masalah.

Pembaruan: Antrian pesan telah diberikan sebagai jawaban, tetapi bagaimana ini akan dirancang? Apakah ini akan ditangani di aplikasi dan diproses selama periode tenang, atau apakah saya perlu membuat 'aplikasi email' atau layanan web baru untuk hanya mengelola antrian?

Gaz_Edge
sumber
Bisakah Anda memberi kami skala perasaan? Ratusan, ribuan atau jutaan surat? Juga, haruskah email segera dikirim atau apakah kelambatan kecil dapat diterima?
yannis
Mengirim email melibatkan penyerahan pesan SMTP ke host email penerima, tetapi itu tidak berarti pesan tersebut telah terkirim. Jadi secara efektif, semua pengiriman email tidak sinkron, dan tidak ada gunanya berpura-pura "menunggu kesuksesan".
Kilian Foth
1
Saya tidak "menunggu kesuksesan", tetapi saya harus menunggu server smtp untuk menerima permintaan saya. @YannisRizos lihat perbarui RE komentar Anda
Gaz_Edge
Untuk 2000 (yang merupakan maks Anda), mail akan berfungsi. Ketika mereka terjadi di katakanlah 10 jam kerja, itu adalah 3 mail per menit yang sangat bisa dilakukan. Pastikan Anda menyiapkan data DNS Anda dengan baik dan penyedia menerima Anda mengirimnya dalam jumlah ini. Juga pikirkan: "apa server surat turun?". Beban mengirim 2.000 surat bukanlah sesuatu yang perlu dikhawatirkan.
Luc Franken
Jawaban di mana
CRONTAB

Jawaban:

15

Pendekatan umum, seperti Ozz sudah sebutkan , adalah antrian pesan . Dari perspektif desain, antrian pesan pada dasarnya adalah antrian FIFO , yang merupakan tipe data yang agak mendasar:

Antrian FIFO

Apa yang membuat antrian pesan istimewa adalah bahwa sementara aplikasi Anda bertanggung jawab untuk antrian, proses yang berbeda akan bertanggung jawab untuk antrian. Dalam istilah antrian, aplikasi Anda adalah pengirim pesan, dan proses penghapusan adalah penerima. Keuntungan yang jelas adalah bahwa seluruh proses tidak sinkron, penerima bekerja secara independen dari pengirim, selama ada pesan untuk diproses. Kerugian yang jelas adalah bahwa Anda memerlukan komponen tambahan, pengirim, agar semuanya berfungsi.

Karena arsitektur Anda sekarang bergantung pada dua komponen yang bertukar pesan, Anda dapat menggunakan istilah komunikasi antar-proses yang bagus untuknya.

Bagaimana memperkenalkan antrian mempengaruhi desain aplikasi Anda?

Tindakan tertentu dalam aplikasi Anda menghasilkan email. Memperkenalkan antrian pesan berarti bahwa tindakan tersebut sekarang harus mendorong pesan ke antrian (dan tidak lebih). Pesan-pesan itu harus membawa jumlah minimum informasi mutlak yang diperlukan untuk membangun email ketika penerima Anda memprosesnya.

Format dan isi pesan

Format dan isi pesan Anda sepenuhnya terserah Anda, tetapi Anda harus mengingat semakin kecil semakin baik. Antrian Anda harus secepat mungkin untuk menulis dan memprosesnya, melemparkan sejumlah besar data ke dalamnya mungkin akan membuat kemacetan.

Selain itu beberapa layanan antrian berbasis cloud memiliki batasan ukuran pesan dan dapat membagi pesan yang lebih besar. Anda tidak akan melihat, pesan perpecahan akan disajikan sebagai satu ketika Anda memintanya, tetapi Anda akan dikenakan biaya untuk beberapa pesan (dengan asumsi tentu saja Anda menggunakan layanan yang memerlukan biaya).

Desain penerima

Karena kita berbicara tentang aplikasi web, pendekatan umum untuk receiver Anda adalah skrip cron sederhana. Itu akan berjalan setiap xmenit (atau detik) dan itu akan:

  • Pop njumlah pesan dari antrian,
  • Memproses pesan (yaitu mengirim email).

Perhatikan bahwa saya mengatakan pop daripada mengambil atau mengambil, itu karena penerima Anda tidak hanya mendapatkan item dari antrian, tetapi juga menghapusnya (yaitu menghapusnya dari antrian atau menandainya sebagai diproses). Bagaimana persisnya itu akan terjadi tergantung pada implementasi Anda pada antrian pesan dan kebutuhan spesifik aplikasi Anda.

Tentu saja apa yang saya jelaskan pada dasarnya adalah operasi batch , cara paling sederhana untuk memproses antrian. Bergantung pada kebutuhan Anda, Anda mungkin ingin memproses pesan dengan cara yang lebih rumit (yang juga membutuhkan antrian yang lebih rumit).

Lalu lintas

Penerima Anda dapat mempertimbangkan lalu lintas pertimbangan dan menyesuaikan jumlah pesan yang diproses berdasarkan lalu lintas pada saat berjalan. Pendekatan sederhana adalah untuk memprediksi jam lalu lintas tinggi Anda berdasarkan data lalu lintas masa lalu dan dengan asumsi Anda pergi dengan skrip cron yang berjalan setiap xmenit Anda bisa melakukan sesuatu seperti ini:

if( 
    now() > 2pm && now() < 7pm
) {
    process(10);
} else {
    process(100);
}

function process(count) {
    for(i=0; i<=count; i++) {
        message = dequeue();
        mail(message)
    }
}

Pendekatan yang sangat naif & kotor, tetapi berhasil. Jika tidak, baik, pendekatan lain adalah mencari tahu lalu lintas server Anda saat ini di setiap iterasi dan menyesuaikan jumlah item proses yang sesuai. Tolong jangan mengoptimalkan mikro jika itu tidak benar-benar diperlukan, Anda akan membuang-buang waktu.

Penyimpanan antrian

Jika aplikasi Anda sudah menggunakan database, maka satu tabel di atasnya akan menjadi solusi paling sederhana:

CREATE TABLE message_queue (
  id int(11) NOT NULL AUTO_INCREMENT,
  timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  processed enum('0','1') NOT NULL DEFAULT '0',
  message varchar(255) NOT NULL,
  PRIMARY KEY (id),
  KEY timestamp (timestamp),
  KEY processed (processed)
) 

Sebenarnya tidak lebih rumit dari itu. Anda tentu saja dapat menjadikannya serumit yang Anda butuhkan, misalnya, Anda dapat menambahkan bidang prioritas (yang berarti bahwa ini bukan lagi antrian FIFO, tetapi jika Anda benar-benar membutuhkannya, siapa yang peduli?). Anda juga bisa membuatnya lebih sederhana, dengan melewatkan bidang yang telah diproses (tetapi kemudian Anda harus menghapus baris setelah Anda memprosesnya).

Tabel basis data akan ideal untuk 2000 pesan per hari, tetapi mungkin tidak akan baik untuk jutaan pesan per hari. Ada sejuta faktor untuk dipertimbangkan, segala sesuatu di infrastruktur Anda berperan dalam skalabilitas keseluruhan aplikasi Anda.

Bagaimanapun, dengan asumsi Anda telah mengidentifikasi antrian berbasis basis data sebagai hambatan, langkah selanjutnya adalah melihat layanan berbasis cloud. Amazon SQS adalah layanan yang saya gunakan, dan melakukan apa yang dijanjikan. Saya yakin ada beberapa layanan serupa di luar sana.

Antrian berdasarkan memori juga sesuatu yang perlu dipertimbangkan, terutama untuk antrian yang berumur pendek. memcached sangat baik sebagai penyimpanan antrian pesan.

Penyimpanan apa pun yang Anda putuskan untuk dijadikan antrian, cerdas dan abstraksi. Baik pengirim maupun penerima Anda tidak terikat pada penyimpanan tertentu, jika tidak beralih ke penyimpanan lain di lain waktu akan menjadi PITA lengkap.

Pendekatan kehidupan nyata

Saya membuat antrian pesan untuk email yang sangat mirip dengan apa yang Anda lakukan. Itu pada proyek PHP dan saya membangunnya di sekitar Zend Queue , komponen dari Zend Framework yang menawarkan beberapa adapter untuk penyimpanan yang berbeda. Penyimpanan saya di mana:

  • Array PHP untuk pengujian unit,
  • Amazon SQS pada produksi,
  • MySQL pada dev dan lingkungan pengujian.

Pesan saya sesederhana mungkin, aplikasi saya membuat array kecil dengan informasi penting ( [user_id, reason]). Toko pesan adalah versi serial array itu (pertama itu adalah format serialisasi internal PHP, kemudian JSON, saya tidak ingat mengapa saya beralih). Ini reasonadalah konstanta dan tentu saja saya punya meja besar di suatu tempat yang memetakan reasonpenjelasan yang lebih lengkap (saya berhasil mengirim sekitar 500 email ke klien dengan cryptic reasondaripada pesan yang lebih lengkap satu kali).

Bacaan lebih lanjut

Standar:

Alat:

Berbunyi menarik:

yannis
sumber
Wow. Hanya tentang jawaban terbaik yang pernah saya terima di sini! Tidak cukup terima kasih!
Gaz_Edge
Saya, dan saya yakin jutaan orang lain menggunakan FIFO ini dengan Gmail & Google Apps Script. sebuah filter Gmail memberi label setiap email masuk berdasarkan kriteria, dan hanya itu, yang mengantri. Google Apps Script menjalankan setiap durasi X, mendapatkan pesan pertama, mengirimnya, membagikannya. Bilas & Ulangi.
DavChana
6

Anda membutuhkan semacam sistem antrian.

Salah satu cara sederhana bisa dengan menulis ke tabel database dan memiliki baris proses aplikasi eksternal lain dalam tabel ini, tetapi ada banyak teknologi antrian lain yang bisa Anda gunakan.

Anda bisa memiliki kepentingan pada email sehingga yang tertentu segera ditindaklanjuti (misalnya pengaturan ulang kata sandi), dan yang kurang penting dapat dikumpulkan untuk dikirim nanti.

ozz
sumber
apakah Anda memiliki diagram arsitektur atau contoh yang menunjukkan cara kerjanya? Sebagai contoh, apakah antrian duduk di 'aplikasi' yang berbeda mengatakan aplikasi email, atau apakah itu mendapatkan proses dari dalam aplikasi web selama periode tenang. Atau haruskah saya membuat semacam layanan web untuk memprosesnya?
Gaz_Edge
1
@Gaz_Edge Aplikasi Anda mendorong item ke antrian. Proses latar belakang (skrip cron kemungkinan besar) muncul x item dari antrian setiap n detik dan memprosesnya (dalam kasus Anda, mengirim email). Tabel database tunggal berfungsi dengan baik sebagai penyimpanan antrian untuk sejumlah kecil item, tetapi secara umum operasi penulisan pada database mahal dan untuk jumlah yang lebih besar Anda mungkin ingin melihat layanan seperti SQS Amazon .
yannis
1
@Gaz_Edge Saya tidak yakin saya dapat diagram itu lebih sederhana daripada apa yang saya tulis "... menulis ke tabel database dan memiliki baris proses aplikasi eksternal lain dalam tabel ini ...." dan untuk tabel, baca "antrian apa pun "Teknologi apa pun itu.
ozz
1
(lanjutan ...) Anda dapat membangun proses latar belakang yang menghapus antrian dengan cara yang mempertimbangkan lalu lintas Anda, misalnya Anda dapat memerintahkannya untuk memproses lebih sedikit item (atau tidak sama sekali) di saat server Anda sedang dalam tekanan . Anda harus memprediksi saat-saat penuh tekanan dengan melihat data lalu lintas masa lalu (lebih mudah daripada kedengarannya, tetapi dengan margin kesalahan yang besar) atau dengan meminta proses latar belakang memeriksa status lalu lintas setiap kali berjalan (lebih akurat, tetapi biaya tambahan yang ditambahkan jarang diperlukan).
yannis
@YannisRizos ingin menggabungkan komentar Anda menjadi jawaban? Juga, diagram dan desain arsitektur akan sangat membantu (saya bertekad untuk mendapatkannya dari pertanyaan ini kali ini! ;-))
Gaz_Edge
2

Tidak akan ada sejumlah besar email yang dikirim (maks. 2000 per hari).

Sebagai tambahan untuk antrian, hal kedua yang harus Anda pertimbangkan adalah mengirim email melalui layanan khusus: MailChimp, misalnya (saya tidak berafiliasi dengan layanan ini). Kalau tidak, banyak layanan surat, seperti gmail, akan segera mengirimkan surat Anda ke folder spam.

ONS_
sumber
2

Saya telah memodelkan sistem antrian saya di 2 tabel berbeda sebagai;

CREATE TABLE [dbo].[wMessages](
  [Id] [uniqueidentifier]  NOT NULL,
  [FromAddress] [nvarchar](255) NOT NULL,
  [FromDisplayName] [nvarchar](255) NULL,
  [ToAddress] [nvarchar](255) NOT NULL,
  [ToDisplayName] [nvarchar](255) NULL,
  [Graph] [xml] NOT NULL,
  [Priority] [int] NOT NULL,
  PRIMARY KEY CLUSTERED ( [Id] ASC ))

CREATE TABLE [dbo].[wMessageStates](
  [MessageId] [uniqueidentifier] NOT NULL,
  [Status] [int] NOT NULL,
  [LastChange] [datetimeoffset](7) NOT NULL,
  [SendAfter] [datetimeoffset](7) NULL,
  [SendBefore] [datetimeoffset](7) NULL,
  [DeleteAfter] [datetimeoffset](7) NULL,
  [SendDate] [datetimeoffset](7) NULL,
  PRIMARY KEY CLUSTERED ( [MessageId] ASC )) ON [PRIMARY]
) ON [PRIMARY]

Ada 1-1 hubungan antara tabel-tabel ini.

Tabel pesan untuk menyimpan konten pesan. Konten aktual (Kepada, CC, BCC, Subjek, Badan, dll.) Diserialisasi ke bidang Grafik dalam format XML. Lainnya Dari, Informasi hanya digunakan untuk melaporkan masalah tanpa deserializing grafik. Memisahkan tabel ini memungkinkan untuk mempartisi konten tabel ke penyimpanan disk yang berbeda. Setelah Anda siap untuk mengirim pesan, Anda perlu membaca semua informasi karena itu tidak ada salahnya membuat cerita bersambung semua konten ke satu kolom dengan indeks kunci utama.

MessageState meja untuk menyimpan keadaan isi pesan dengan tambahan informasi berdasarkan tanggal. Memisahkan tabel ini memungkinkan mekanisme akses cepat dengan indeks tambahan pada penyimpanan IO cepat. Kolom lain sudah cukup jelas.

Anda bisa menggunakan kumpulan utas terpisah yang memindai tabel ini. Jika aplikasi dan kumpulan hidup dalam mesin yang sama Anda bisa menggunakan kelas EventWaitHandle untuk memberi sinyal kumpulan dari aplikasi tentang sesuatu yang dimasukkan tabel ini, jika tidak secara berkala memindai dengan batas waktu adalah yang terbaik.

ertan
sumber