AWS SES를 통해 비동기식으로 이메일 보내기
게시 됨: 2022-03-10대부분의 애플리케이션은 사용자와 통신하기 위해 이메일을 보냅니다. 거래 이메일은 사이트에 등록한 후 새 사용자를 환영할 때, 사용자에게 암호를 재설정할 수 있는 링크를 제공할 때 또는 사용자가 구매한 후 청구서를 첨부할 때와 같이 사용자가 애플리케이션과 상호 작용할 때 트리거되는 이메일입니다. 이러한 모든 이전 사례에서는 일반적으로 사용자에게 하나의 이메일만 보내면 됩니다. 하지만 다른 경우에는 사용자가 사이트에 새 콘텐츠를 게시하는 경우와 같이 애플리케이션에서 더 많은 이메일을 보내야 하며 모든 팔로워(트위터와 같은 플랫폼에서는 수백만 명의 사용자에 달할 수 있음)가 이메일을 수신하게 됩니다. 공고. 이 후자의 상황에서 적절하게 설계되지 않으면 이메일을 보내는 것이 애플리케이션에서 병목 현상이 될 수 있습니다.
그것이 제 경우에 일어난 일입니다. 일부 사용자 트리거 작업(예: 모든 팔로워에 대한 사용자 알림) 후에 20개의 이메일을 보내야 하는 사이트가 있습니다. 처음에는 널리 사용되는 클라우드 기반 SMTP 공급자(예: SendGrid, Mandrill, Mailjet 및 Mailgun)를 통해 이메일을 보내는 데 의존했지만 사용자에게 다시 응답하는 데 몇 초가 걸립니다. 분명히 SMTP 서버에 연결하여 이 20개의 이메일을 보내면 프로세스 속도가 크게 느려졌습니다.
검사 후 문제의 원인을 찾았습니다.
- 동기 연결
애플리케이션은 SMTP 서버에 연결하고 프로세스 실행을 계속하기 전에 동기적으로 승인을 기다립니다. - 높은 대기 시간
내 서버는 싱가포르에 있지만 사용 중인 SMTP 공급자는 서버가 미국에 있어 왕복 연결에 상당한 시간이 걸립니다. - SMTP 연결을 재사용할 수 없음 이메일을 보내기 위해 함수를 호출할 때 함수는 즉시 이메일을 보내고 그 순간에 새로운 SMTP 연결을 생성합니다(모든 이메일을 수집하고 요청이 끝날 때 단일 SMTP 아래에서 모두 함께 보내는 것을 제안하지 않습니다. 연결).
#1 때문에 사용자가 응답을 기다려야 하는 시간은 이메일을 보내는 데 걸리는 시간과 관련이 있습니다. #2 때문에 이메일 하나를 보내는 시간이 상대적으로 길다. 그리고 #3 때문에 20개의 이메일을 보내는 데 걸리는 시간은 1개의 이메일을 보내는 데 걸리는 시간의 20배입니다. 이메일을 하나만 보내도 애플리케이션이 크게 느려지지는 않지만 20개 이메일을 보내면 확실히 느려지므로 사용자 경험에 영향을 줍니다.
이 문제를 해결하는 방법을 살펴보겠습니다.
거래 이메일의 특성에 주의하기
무엇보다도 모든 이메일의 중요성이 동일하지는 않다는 점을 알아야 합니다. 이메일을 우선순위 이메일과 비우선순위 이메일의 두 그룹으로 크게 분류할 수 있습니다. 예를 들어 사용자가 계정에 액세스하기 위해 비밀번호를 잊어버린 경우 받은 편지함에 비밀번호 재설정 링크가 포함된 이메일이 즉시 표시됩니다. 그것은 우선 순위 이메일입니다. 대조적으로, 우리가 팔로우하는 누군가가 새로운 콘텐츠를 게시했음을 알리는 이메일을 보내는 것은 사용자의 받은 편지함에 즉시 도착할 필요가 없습니다. 우선순위가 아닌 이메일입니다.
솔루션은 이러한 두 가지 범주의 이메일이 전송되는 방식을 최적화해야 합니다. 프로세스 중에 전송될 우선순위 이메일이 몇 개(아마도 1 또는 2개)이고 이메일의 대부분이 우선순위가 아닌 이메일이라고 가정하면 다음과 같이 솔루션을 설계합니다.
- 우선 순위 이메일 은 애플리케이션이 배포된 동일한 지역에 위치한 SMTP 공급자를 사용하여 대기 시간이 긴 문제를 간단히 피할 수 있습니다. 좋은 연구 외에도 여기에는 우리 애플리케이션을 공급자의 API와 통합하는 작업이 포함됩니다.
- 우선 순위가 아닌 전자 메일 은 비동기식으로 보낼 수 있으며 많은 전자 메일이 함께 전송되는 일괄 처리로 보낼 수 있습니다. 애플리케이션 수준에서 구현하려면 적절한 기술 스택이 필요합니다.
다음으로 이메일을 비동기식으로 보내는 기술 스택을 정의해 보겠습니다.
기술 스택 정의
참고: 내 웹 사이트는 이미 AWS EC2에서 호스팅되기 때문에 AWS 서비스를 기반으로 스택을 사용하기로 결정했습니다. 그렇지 않으면 여러 회사의 네트워크 간에 데이터를 이동하는 데 오버헤드가 발생합니다. 그러나 다른 클라우드 서비스 제공업체를 사용하여 솔루션을 구현할 수도 있습니다.
첫 번째 접근 방식은 대기열을 설정하는 것이었습니다. 대기열을 통해 애플리케이션이 더 이상 이메일을 보내지 않고 대신 대기열에 이메일 콘텐츠와 메타데이터가 포함된 메시지를 게시한 다음 다른 프로세스가 대기열에서 메시지를 선택하고 이메일을 보내도록 할 수 있습니다.
그러나 SQS라는 AWS의 대기열 서비스를 확인할 때 다음과 같은 이유로 적절한 솔루션이 아니라고 판단했습니다.
- 설정이 다소 복잡합니다.
- 표준 대기열 메시지는 최대 256kb의 정보만 저장할 수 있으며, 이메일에 첨부 파일(예: 송장)이 있는 경우 충분하지 않을 수 있습니다. 그리고 큰 메시지를 작은 메시지로 분할하는 것이 가능하더라도 복잡성은 훨씬 더 커집니다.
그런 다음 설정이 훨씬 쉬운 다른 AWS 서비스인 S3와 Lambda를 결합하여 대기열의 동작을 완벽하게 모방할 수 있다는 것을 깨달았습니다. 데이터를 저장하고 검색하는 클라우드 객체 스토리지 솔루션인 S3는 메시지 업로드를 위한 리포지토리 역할을 할 수 있으며, 이벤트에 대한 응답으로 코드를 실행하는 컴퓨팅 서비스인 Lambda는 메시지를 선택하고 작업을 실행할 수 있습니다.
즉, 이메일 전송 프로세스를 다음과 같이 설정할 수 있습니다.
- 애플리케이션은 이메일 콘텐츠 + 메타데이터가 포함된 파일을 S3 버킷에 업로드합니다.
- 새 파일이 S3 버킷에 업로드될 때마다 S3는 새 파일에 대한 경로가 포함된 이벤트를 트리거합니다.
- Lambda 함수는 이벤트를 선택하고 파일을 읽고 이메일을 보냅니다.
마지막으로 이메일을 보내는 방법을 결정해야 합니다. 이미 가지고 있는 SMTP 공급자를 계속 사용하여 Lambda 함수가 API와 상호 작용하도록 하거나 SES라고 하는 이메일 전송에 AWS 서비스를 사용할 수 있습니다. SES를 사용하면 다음과 같은 장점과 단점이 있습니다.
혜택:
- AWS Lambda 내에서 사용하기가 매우 간단합니다(코드 2줄만 필요).
- 더 저렴합니다. Lambda 요금은 함수를 실행하는 데 걸리는 시간을 기준으로 계산되므로 AWS 네트워크 내에서 SES에 연결하는 것이 외부 서버에 연결하는 것보다 시간이 더 짧기 때문에 함수가 더 일찍 완료되고 비용이 적게 듭니다. . (애플리케이션이 호스팅되는 동일한 지역에서 SES를 사용할 수 없는 경우가 아니면 제 경우에는 EC2 서버가 있는 아시아 태평양(싱가포르) 지역에서 SES가 제공되지 않기 때문에 일부 서버에 연결하는 것이 더 나을 수 있습니다. 아시아 기반 외부 SMTP 공급자).
단점:
- 전송된 이메일을 모니터링하기 위한 많은 통계가 제공되지 않으며 더 강력한 통계를 추가하려면 추가 노력이 필요합니다(예: 이메일의 몇 퍼센트를 열었는지 또는 어떤 링크를 클릭했는지 추적하려면 AWS CloudWatch를 통해 설정해야 함).
- 우선 순위 이메일을 보내기 위해 SMTP 공급자를 계속 사용하면 통계를 한 곳에서 모두 볼 수 없습니다.
단순화를 위해 아래 코드에서는 SES를 사용합니다.
그런 다음 프로세스와 스택의 논리를 다음과 같이 정의했습니다. 애플리케이션은 평소와 같이 우선 순위 이메일을 보내지만 우선 순위가 아닌 이메일의 경우 이메일 콘텐츠와 메타데이터가 포함된 파일을 S3에 업로드합니다. 이 파일은 SES에 연결하여 이메일을 보내는 Lambda 함수에 의해 비동기식으로 처리됩니다.
솔루션 구현을 시작하겠습니다.
우선순위 이메일과 비우선순위 이메일 구별하기
요컨대, 이것은 모두 애플리케이션에 따라 다르므로 이메일 기반으로 이메일을 결정해야 합니다. wp_mail
함수의 제약 조건에 대한 몇 가지 해킹이 필요한 WordPress용으로 구현한 솔루션을 설명하겠습니다. 다른 플랫폼의 경우 아래 전략도 작동하지만 해킹이 필요하지 않은 더 나은 전략이 있을 가능성이 큽니다.
워드프레스에서 이메일을 보내는 방법은 wp_mail
함수를 호출하는 것이고 우리는 그것을 변경하고 싶지 않습니다(예: wp_mail_synchronous
또는 wp_mail_asynchronous
함수 호출) wp_mail
이메일이 속한 그룹을 알아야 합니다. 불행히도 wp_mail
은 서명에서 볼 수 있듯이 이 정보를 평가할 수 있는 추가 매개변수를 제공하지 않습니다.
function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() )
그런 다음 이메일의 범주를 찾기 위해 해킹 솔루션을 추가합니다. 기본적으로 이메일을 우선순위 그룹에 속하게 만들고 $to
에 특정 이메일(예: [email protected])이 포함된 경우 또는 $subject
가 특수 문자열(예: "[Non-priority!]")로 시작하는 경우, 이는 비우선 순위 그룹에 속합니다(그리고 제목에서 해당 이메일 또는 문자열을 제거합니다). wp_mail
은 플러그인 가능한 기능이므로 우리의 functions.php 파일에 동일한 서명을 가진 새 기능을 구현함으로써 간단히 재정의할 수 있습니다. 처음에는 모든 매개변수를 추출하기 위해 wp-includes/pluggable.php 파일에 있는 원래 wp_mail
함수의 동일한 코드를 포함합니다.
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;
그런 다음 우선 순위가 아닌지 확인합니다. 이 경우 send_asynchronous_mail
함수에서 별도의 논리로 분기합니다. 그렇지 않은 경우 원래 wp_mail
함수에서와 동일한 코드를 계속 실행합니다.
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 // ... }
send_asynchronous_mail
함수에서 이메일을 S3에 직접 업로드하는 대신 이메일을 전역 변수 $emailqueue
에 추가하기만 하면 여기에서 요청이 끝날 때 단일 연결로 모든 이메일을 S3에 함께 업로드할 수 있습니다.
function send_asynchronous_mail($to, $subject, $message, $headers, $attachments) { global $emailqueue; if (!$emailqueue) { $emailqueue = array(); } // Add email to queue. Code continues below... }
이메일당 하나의 파일을 업로드하거나 하나의 파일에 많은 이메일이 포함되도록 번들할 수 있습니다. $headers
에는 이메일 메타(from, content-type 및 charset, CC, BCC 및 회신 필드)가 포함되어 있으므로 동일한 $headers
가 있을 때마다 이메일을 함께 그룹화할 수 있습니다. 이렇게 하면 이러한 이메일을 모두 동일한 파일로 S3에 업로드할 수 있으며 $headers
메타 정보는 이메일당 한 번이 아니라 파일에 한 번만 포함됩니다.
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 }
마지막으로 send_asynchronous_mail
함수는 true
를 반환합니다. 이 코드는 해키입니다. true
는 일반적으로 이메일이 성공적으로 전송되었음을 의미하지만 이 경우에는 아직 전송조차 되지 않았으므로 완벽하게 실패할 수 있습니다. 이 때문에 wp_mail
을 호출하는 함수는 true
응답을 "이메일이 성공적으로 전송되었습니다."로 처리해서는 안 되지만 대기열에 추가되었다는 승인으로 처리해야 합니다. 그렇기 때문에 이 기술을 우선 순위가 아닌 이메일로 제한하는 것이 중요합니다. 그러면 실패할 경우 프로세스가 백그라운드에서 계속 재시도할 수 있고 사용자는 이메일이 이미 받은 편지함에 있을 것이라고 기대하지 않을 것입니다.

function send_asynchronous_mail($to, $subject, $message, $headers, $attachments) { // Continued from above... // That's it! return true; }
S3에 이메일 업로드
이전 기사 "AWS S3를 통해 여러 서버 간에 데이터 공유"에서 S3에서 버킷을 생성하는 방법과 SDK를 통해 버킷에 파일을 업로드하는 방법에 대해 설명했습니다. 아래의 모든 코드는 WordPress용 솔루션 구현을 계속하므로 PHP용 SDK를 사용하여 AWS에 연결합니다.
우리는 추상 클래스 AWS_S3
(이전 기사에서 소개됨)에서 확장하여 S3에 연결하고 요청 끝에서 이메일을 버킷 "async-emails"에 업로드할 수 있습니다( wp_footer
후크를 통해 트리거됨). 이메일이 인터넷에 노출되는 것을 원하지 않으므로 ACL을 "비공개"로 유지해야 합니다.
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();
전역 변수 $emailqueue
에 저장된 headers => emaildata 쌍을 반복하기 시작하고 헤더가 비어 있는 경우 get_default_email_meta
함수에서 기본 구성을 가져옵니다. 아래 코드에서는 헤더에서 "from" 필드만 검색합니다(모든 헤더를 추출하는 코드는 원래 함수 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... } } }
마지막으로 이메일을 S3에 업로드합니다. 우리는 비용을 절감하기 위해 파일당 업로드할 이메일 수를 결정합니다. Lambda 함수는 100ms 범위에서 계산된 실행에 필요한 시간을 기준으로 요금을 부과합니다. 기능에 필요한 시간이 많을수록 비용이 더 많이 듭니다.
이메일당 1개의 파일을 업로드하여 모든 이메일을 전송하는 것은 여러 이메일당 1개의 파일을 업로드하는 것보다 비용이 더 많이 듭니다. 기능 실행으로 인한 오버헤드는 여러 이메일에 대해 한 번만 계산되는 것이 아니라 이메일당 한 번 계산되고 또한 많은 이메일을 보내기 때문입니다. 함께 100ms 범위를 더 철저하게 채웁니다.
따라서 파일당 많은 이메일을 업로드합니다. 얼마나 많은 이메일? Lambda 함수에는 최대 실행 시간(기본값 3초)이 있으며 작업이 실패하면 실패한 위치가 아닌 처음부터 계속 재시도합니다. 따라서 파일에 100개의 이메일이 포함되어 있고 최대 시간이 다 되기 전에 Lambda가 50개의 이메일을 보낼 수 있으면 실패하고 작업 실행을 다시 시도하여 처음 50개의 이메일을 다시 한 번 보냅니다. 이를 방지하려면 최대 시간이 다 되기 전에 처리하기에 충분하다고 확신하는 파일당 이메일 수를 선택해야 합니다. 우리 상황에서는 파일당 25개의 이메일을 보내도록 선택할 수 있습니다. 이메일 수는 애플리케이션에 따라 다르므로(크기가 큰 이메일은 전송 시간이 더 오래 걸리고 이메일 전송 시간은 인프라에 따라 다름) 올바른 숫자를 찾기 위해 몇 가지 테스트를 수행해야 합니다.
파일의 내용은 "meta" 속성 아래에 이메일 메타를 포함하고 "emails" 속성 아래에 이메일 청크를 포함하는 단순한 JSON 객체입니다.
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), ]); } } } }
간단하게 하기 위해 위의 코드에서는 첨부 파일을 S3에 업로드하지 않습니다. 이메일에 첨부 파일이 포함되어야 하는 경우 SendEmail
(아래 Lambda 스크립트에서 사용됨) 대신 SES 함수 SendRawEmail
을 사용해야 합니다.
이메일이 포함된 파일을 S3에 업로드하는 로직을 추가했으면 다음으로 Lambda 함수 코딩으로 넘어갈 수 있습니다.
Lambda 스크립트 코딩
Lambda 함수는 서버에서 실행되지 않기 때문에가 아니라 개발자가 서버에 대해 걱정할 필요가 없기 때문에 서버리스 함수라고도 합니다. 개발자는 단순히 스크립트를 제공하고 클라우드는 서버 프로비저닝, 배포 및 스크립트를 실행합니다. 따라서 앞서 언급했듯이 Lambda 함수는 함수 실행 시간을 기준으로 요금이 부과됩니다.
다음 Node.js 스크립트는 필요한 작업을 수행합니다. 버킷에 새 객체가 생성되었음을 나타내는 S3 "Put" 이벤트에 의해 호출되는 함수:
- 새 객체의 경로(변수
srcKey
아래)와 버킷(변수srcBucket
아래)을 가져옵니다. -
s3.getObject
를 통해 객체를 다운로드합니다. -
JSON.parse(response.Body.toString())
를 통해 객체의 내용을 구문 분석하고 이메일과 이메일 메타를 추출합니다. - 모든 이메일을 반복하고
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); }); };
다음으로 다음과 같은 Lambda 함수를 AWS에 업로드하고 구성해야 합니다.
- S3에 액세스할 수 있는 Lambda 권한을 부여하는 실행 역할 생성.
- 모든 코드를 포함하는 .zip 패키지 생성, 즉 우리가 생성하는 Lambda 함수 + 모든 필수 Node.js 모듈.
- CLI 도구를 사용하여 이 패키지를 AWS에 업로드합니다.
이러한 작업을 수행하는 방법은 AWS 사이트의 Amazon S3에서 AWS Lambda 사용 자습서에 적절하게 설명되어 있습니다.
Lambda 함수로 S3 연결하기
마지막으로 버킷과 Lambda 함수가 생성되면 두 객체를 함께 연결해야 버킷에 새 객체가 생성될 때마다 Lambda 함수를 실행하는 이벤트가 트리거됩니다. 이를 위해 S3 대시보드로 이동하여 버킷 행을 클릭하면 해당 속성이 표시됩니다.

그런 다음 속성을 클릭하고 "이벤트" 항목으로 스크롤한 다음 알림 추가를 클릭하고 다음 필드를 입력합니다.
- 이름: 알림 이름, 예: "EmailSender";
- 이벤트: 버킷에 새 객체가 생성될 때 트리거되는 이벤트인 "Put"
- 보내기: "람다 함수";
- Lambda: 새로 생성된 Lambda의 이름, 예: "LambdaEmailSender".

마지막으로 일정 시간이 지나면 이메일 데이터가 포함된 파일을 자동으로 삭제하도록 S3 버킷을 설정할 수도 있습니다. 이를 위해 버킷의 관리 탭으로 이동하여 이메일이 만료되어야 하는 일수를 정의하는 새 수명 주기 규칙을 생성합니다.

그게 다야 이 순간부터 이메일의 콘텐츠와 메타가 포함된 새 객체를 S3 버킷에 추가하면 파일을 읽고 SES에 연결하여 이메일을 전송하는 Lambda 함수가 트리거됩니다.
내 사이트에서 이 솔루션을 구현했고 다시 한 번 빨라졌습니다. 외부 프로세스로 이메일 전송을 오프로드하여 애플리케이션이 20개 또는 5000개 이메일을 보내든 상관없이 작업을 트리거한 사용자에 대한 응답은 다음과 같습니다. 즉각적인.
결론
이 기사에서는 단일 요청으로 많은 트랜잭션 이메일을 보내는 것이 애플리케이션에서 병목 현상이 될 수 있는 이유를 분석하고 이 문제를 처리하는 솔루션을 만들었습니다. 애플리케이션 내에서(동기식으로) SMTP 서버에 연결하는 대신 AWS S3 + Lambda + SES의 스택을 기반으로 비동기적으로 외부 기능에서 이메일을 보냅니다.
이메일을 비동기식으로 보내면 애플리케이션에서 수천 개의 이메일을 보낼 수 있지만 작업을 트리거한 사용자에 대한 응답은 영향을 받지 않습니다. 그러나 사용자가 이메일이 받은 편지함에 도착할 때까지 기다리지 않도록 하기 위해 이메일을 우선순위와 비우선순위의 두 그룹으로 나누고 우선순위가 아닌 이메일만 비동기식으로 보내기로 결정했습니다. 이메일 전송을 위한 wp_mail
기능의 제한으로 인해 다소 해킹된 WordPress용 구현을 제공했습니다.
이 기사의 교훈은 서버 기반 응용 프로그램의 서버리스 기능이 꽤 잘 작동한다는 것입니다. WordPress와 같은 CMS에서 실행되는 사이트는 클라우드에서 특정 기능만 구현하여 성능을 향상하고 마이그레이션으로 인해 발생하는 많은 복잡성을 피할 수 있습니다. 고도로 동적인 사이트를 완전한 서버리스 아키텍처로 전환합니다.