Projektuję aplikację internetową i zastanawiam się, jak zaprojektować architekturę do zarządzania wysyłaniem automatycznych wiadomości e-mail.
Obecnie mam tę funkcję wbudowaną w moją aplikację internetową, a e-maile są wysyłane na podstawie danych wejściowych / interakcji użytkownika (takich jak utworzenie nowego użytkownika). Problem polega na tym, że bezpośrednie połączenie z serwerem poczty zajmuje kilka sekund. Zwiększając moją aplikację, będzie to znacząca szyjka butelki w przyszłości.
Jaki jest najlepszy sposób zarządzania wysyłaniem dużej liczby automatycznych wiadomości e-mail w ramach architektury mojego systemu?
Nie będzie wysyłanej dużej liczby wiadomości e-mail (maksymalnie 2000 dziennie). E-maile nie muszą być wysyłane natychmiast, opóźnienie do 10 minut jest w porządku.
Aktualizacja: Kolejkowanie wiadomości podano jako odpowiedź, ale jak by to zaprojektować? Czy zostanie to załatwione w aplikacji i przetworzone w ciszy, czy też muszę utworzyć nową „aplikację pocztową” lub usługę internetową, aby po prostu zarządzać kolejką?
Odpowiedzi:
Powszechnym podejściem, jak już wspomniano Ozz , jest kolejka komunikatów . Z perspektywy projektowej kolejka komunikatów jest zasadniczo kolejką FIFO , która jest raczej podstawowym typem danych:
Tym, co sprawia, że kolejka komunikatów jest wyjątkowa, jest to, że chociaż twoja aplikacja jest odpowiedzialna za kolejkowanie, inny proces byłby odpowiedzialny za de-kolejkowanie. W kolejce lingo aplikacja jest nadawcą wiadomości, a proces usuwania z kolejki jest odbiorcą. Oczywistą zaletą jest to, że cały proces jest asynchroniczny, odbiorca działa niezależnie od nadawcy, o ile istnieją komunikaty do przetworzenia. Oczywistą wadą jest to, że potrzebujesz dodatkowego komponentu, nadawcy, aby całość działała.
Ponieważ twoja architektura opiera się teraz na dwóch komponentach wymieniających wiadomości, możesz użyć do tego fantazyjnego terminu komunikacji międzyprocesowej .
W jaki sposób wprowadzenie kolejki wpływa na projekt aplikacji?
Niektóre działania w aplikacji generują wiadomości e-mail. Wprowadzenie kolejki komunikatów oznaczałoby, że te akcje powinny teraz wypychać wiadomości do kolejki (i nic więcej). Wiadomości te powinny zawierać absolutnie minimalną ilość informacji niezbędnych do zbudowania wiadomości e-mail, gdy odbiorca je przetworzy.
Format i treść wiadomości
Format i treść wiadomości zależy wyłącznie od Ciebie, ale należy pamiętać, że im mniejsze, tym lepiej. Twoja kolejka powinna być zapisywana i przetwarzana tak szybko, jak to możliwe, a rzucenie na nią dużej ilości danych prawdopodobnie stworzy wąskie gardło.
Ponadto kilka usług kolejkowania opartych na chmurze ma ograniczenia dotyczące rozmiarów wiadomości i może dzielić większe wiadomości. Nie zauważysz, podzielone wiadomości będą podawane jako jedna, gdy o nie poprosisz, ale zostaniesz obciążony za wiele wiadomości (zakładając oczywiście, że korzystasz z usługi, która wymaga opłaty).
Konstrukcja odbiornika
Ponieważ mówimy o aplikacji internetowej, powszechnym podejściem dla twojego odbiornika byłby prosty skrypt cron. Działa co
x
minutę (lub sekundy) i:n
ilość wiadomości z kolejki,Zauważ, że mówię „pop” zamiast „pobierz” lub „pobierz”, ponieważ odbiorca nie tylko pobiera elementy z kolejki, ale także je usuwa (tj. Usuwa je z kolejki lub oznacza jako przetworzone). To, jak dokładnie to nastąpi, zależy od implementacji kolejki komunikatów i specyficznych potrzeb aplikacji.
Oczywiście to, co opisuję, jest zasadniczo operacją wsadową , najprostszym sposobem przetwarzania kolejki. W zależności od potrzeb możesz chcieć przetwarzać wiadomości w bardziej skomplikowany sposób (wymagałoby to również bardziej skomplikowanej kolejki).
ruch drogowy
Twój odbiornik może wziąć pod uwagę ruch i dostosować liczbę przetwarzanych wiadomości na podstawie ruchu w czasie jego działania. Uproszczone podejście polegałoby na przewidywaniu godzin intensywnego ruchu na podstawie przeszłych danych o ruchu i przy założeniu, że korzystasz ze skryptu cron, który uruchamia się co
x
minutę, możesz zrobić coś takiego:Bardzo naiwne i brudne podejście, ale działa. Jeśli tak się nie stanie, drugim podejściem byłoby sprawdzenie aktualnego ruchu serwera na każdej iteracji i odpowiednie dostosowanie liczby elementów procesu. Nie dokonuj mikrooptymalizacji, jeśli nie jest to absolutnie konieczne, tracisz czas.
Miejsce w kolejce
Jeśli Twoja aplikacja korzysta już z bazy danych, najprostszym rozwiązaniem będzie pojedyncza tabela na niej:
To naprawdę nie jest bardziej skomplikowane. Możesz oczywiście uczynić to tak skomplikowanym, jak potrzebujesz, możesz na przykład dodać pole priorytetowe (co oznaczałoby, że nie jest to już kolejka FIFO, ale jeśli naprawdę jej potrzebujesz, kogo to obchodzi?). Możesz również uprościć to, pomijając przetworzone pole (ale wtedy będziesz musiał usunąć wiersze po ich przetworzeniu).
Tabela bazy danych byłaby idealna dla 2000 wiadomości dziennie, ale prawdopodobnie nie byłaby dobrze skalowana dla milionów wiadomości dziennie. Należy wziąć pod uwagę milion czynników, wszystko w twojej infrastrukturze odgrywa rolę w ogólnej skalowalności twojej aplikacji.
W każdym razie, zakładając, że już zidentyfikowałeś kolejkę opartą na bazie danych jako wąskie gardło, następnym krokiem byłoby przyjrzenie się usłudze opartej na chmurze. Amazon SQS to jedyna usługa, z której korzystałem i zrobił to, co obiecał. Jestem pewien, że istnieje wiele podobnych usług.
Kolejki oparte na pamięci również należy rozważyć, szczególnie w przypadku kolejek krótkotrwałych. memcached doskonale nadaje się do przechowywania w kolejce wiadomości.
Niezależnie od miejsca, w którym zdecydujesz się zbudować kolejkę, bądź sprytny i abstrakcyjny. Ani nadawca, ani odbiorca nie powinni być przywiązani do konkretnego magazynu, w przeciwnym razie przejście na inny magazyn w późniejszym czasie byłoby kompletną PITA.
Prawdziwe podejście do życia
Zbudowałem kolejkę wiadomości e-mail, która jest bardzo podobna do tego, co robisz. To było na projekcie PHP i zbudowałem go wokół Zend Queue , komponentu Zend Framework, który oferuje kilka adapterów do różnych magazynów. Moje magazyny, w których:
Moje wiadomości były tak proste, jak tylko mogą być, moja aplikacja utworzyła małe tablice z niezbędnymi informacjami (
[user_id, reason]
). Magazyn komunikatów był serializowaną wersją tej tablicy (najpierw był to wewnętrzny format serializacji PHP, potem JSON, nie pamiętam, dlaczego się zmieniłem).reason
Jest stała i oczywiście mam duży gdzieś tabeli, który mapujereason
do pełniejszych wyjaśnień (I udało się wysłać e-maile do około 500 klientów z tajemniczyreason
zamiast pełniejszego wiadomości raz).Dalsza lektura
Standardy:
Przybory:
Ciekawe czyta:
źródło
Potrzebujesz jakiegoś systemu kolejkowania.
Jednym prostym sposobem może być zapisanie w tabeli bazy danych i utworzenie w tej tabeli kolejnych wierszy procesu aplikacji zewnętrznej, ale istnieje wiele innych technologii kolejkowania, których można użyć.
Możesz mieć znaczenie w wiadomościach e-mail, aby niektóre z nich były wykonywane niemal natychmiast (na przykład resetowanie hasła), a te o mniejszym znaczeniu mogły być grupowane w celu wysłania później.
źródło
Oprócz kolejki drugą rzeczą, którą powinieneś rozważyć, jest wysyłanie wiadomości e-mail za pośrednictwem wyspecjalizowanych usług: na przykład MailChimp (nie jestem powiązany z tą usługą). W przeciwnym razie wiele usług pocztowych, takich jak Gmail, wkrótce wyśle Twoje listy do folderu ze spamem.
źródło
Modelowałem mój system kolejek w innej tabeli 2 jako;
Istnieje relacja 1-1 między tymi tabelami.
Tabela wiadomości do przechowywania treści wiadomości. Rzeczywista treść (Do, CC, BCC, Temat, Treść itp.) Jest serializowana do pola Wykres w formacie XML. Inne informacje From, To służą tylko do zgłaszania problemów bez deserializacji wykresu. Oddzielenie tej tabeli pozwala podzielić zawartość tabeli na inną pamięć dyskową. Gdy będziesz gotowy do wysłania wiadomości, musisz przeczytać wszystkie informacje, dlatego nie ma nic złego w serializacji całej zawartości do jednej kolumny z indeksem klucza podstawowego.
Tabela MessageState do przechowywania stanu zawartości wiadomości z dodatkowymi informacjami opartymi na dacie. Oddzielenie tej tabeli pozwala na szybki dostęp do mechanizmu z dodatkowymi indeksami szybkiego przechowywania IO. Inne kolumny są już oczywiste.
Możesz użyć oddzielnej puli wątków, która skanuje te tabele. Jeśli aplikacja i pula znajdują się na tym samym komputerze, można użyć klasy EventWaitHandle do zasygnalizowania puli z aplikacji o czymś wstawionym do tych tabel, w przeciwnym razie najlepsze jest okresowe skanowanie z przekroczeniem limitu czasu.
źródło