Автор оригинала: Guest Contributor.
Вступление
Отправка уведомлений пользователям является довольно распространенной задачей – будь то электронная почта, SMS-сообщения или даже почтовые запросы HTTP/HTTPS.
Служба простых уведомлений (SNS) – это система обмена сообщениями издателя/подписчика, предоставляемая Amazon Web Services (AWS). Это популярный выбор для многих разработчиков и очень надежный.
В этой статье мы создадим облачное приложение Spring с поддержкой обмена сообщениями (SMS и электронная почта) с помощью AWS SNS.
Почему Вы выбрали AWS SNS?
Простой сервис уведомлений AWS позволяет издателю (обычно микросервису) отправлять (публиковать) уведомления по определенным темам получателям (подписчикам) с помощью различных средств – SMS, электронной почты, HTTP, AWS Lambda и AWS SQS.
Эти получатели намеренно подписываются на тему, от которой они хотят получать уведомления:
Вот некоторые из причин, по которым AWS SNS чрезвычайно популярен:
- Позволяет публиковать на конечных точках HTTP и других сервисах AWS
- Поддерживает более 205 стран для доставки SMS и электронной почты. Этот уровень доступности особенно важен, если ваши пользователи будут глобального происхождения.
- Гарантирует доставку сообщений до тех пор, пока адрес SMS/электронной почты действителен.
- AWS предоставляет многофункциональный и хорошо написанный SDK для Java для SNS с отличной документацией .
- Благодаря легко интегрируемым модулям Spring интеграция Aws SDK для Java становится чрезвычайно простой.
- Если вы уже используете другие сервисы AWS для хранения или развертывания, то вам не составит труда оставаться в той же экосистеме и использовать SNS.
Примеры использования Spring Boot для AWS SNS
Существует множество областей, в которых вы можете использовать уведомления по SMS, электронной почте или HTTP/S в веб-приложении Spring:
- Уведомите все микросервисы о событии в масштабах всего приложения.
- Уведомляйте администраторов/разработчиков о критических ошибках или отключенных службах.
- Проверка номера телефона с помощью OTP (Одноразовый пароль) во время регистрации пользователя или сброса пароля.
- Уведомлять пользователей о событии, непосредственно связанном с пользователем (например: заявка принята).
- Увеличьте вовлеченность пользователей, так как уведомления по электронной почте и SMS могут вернуть пользователя в ваше приложение.
Учетная запись AWS
Как и в случае с любым сервисом AWS, нам нужно получить Идентификатор ключа доступа и Секретный ключ из нашей учетной записи AWS. Войдите в свою консоль AWS и посетите страницу ” Мои учетные данные безопасности “, указанную в раскрывающемся меню вашей учетной записи:
Разверните вкладку ” Ключи доступа (идентификатор ключа доступа и секретный ключ доступа) “и нажмите” Создать новый ключ доступа “:
Загрузите файл с вашими учетными данными и храните его в надежном месте! Никто не должен иметь доступа к этому файлу, так как тогда у них также будет полная авторизация для использования вашей учетной записи AWS:
Вам необходимо выбрать регион AWS для использования в качестве места обработки ваших запросов на услуги SNS. Обратите внимание, что цены на SMS могут отличаться в зависимости от выбранного региона и что не все регионы поддерживают SMS-сообщения.
Обязательно выберите местоположение, поддерживаемое SMS, из здесь .
Для краткости мы использовали учетную запись root для создания идентификатора ключа AWS
и Секретного ключа
, но эта практика настоятельно не рекомендуется, и AWS рекомендует вместо этого использовать Роли пользователей IAM .
Проект весенней загрузки
Как всегда, для быстрого загрузочного проекта Spring Boot мы будем использовать Spring Initializr :
В качестве альтернативы мы можем использовать интерфейс командной строки Spring Boot :
$ spring init --dependencies=web sns-demo
Зависимости
Используя выбранный вами инструмент сборки, добавьте необходимые зависимости:
Грейдер
dependencies { implementation platform('software.amazon.awssdk:bom:2.5.29') // BOM for AWS SDK For Java implementation 'software.amazon.awssdk:sns' // We only need to get SNS SDK in our case implementation 'software.amazon.awssdk:ses' // Needed for sending emails with attachment implementation 'com.sun.mail:javax.mail' // Needed for sending emails with attachment compile group: 'org.springframework.cloud', name: 'spring-cloud-aws-messaging', version: '2.2.1.RELEASE' compile group: 'org.springframework.cloud', name: 'spring-cloud-aws-autoconfigure', version: '2.2.1.RELEASE' }
Maven
org.springframework.cloud spring-cloud-aws-messaging {version}
Отправка электронных писем с помощью SNS
Создайте тему SNS
Тема SNS-это точка доступа, которая объединяет различные конечные точки между издателем (наш проект Spring Boot) и подписчиками. Издатель публикует сообщение в теме, и это сообщение затем будет доставлено всем подписчикам этой темы.
Во-первых, давайте определим вспомогательный метод, который позволит нам получить клиента SNS:
private SnsClient getSnsClient() throws URISyntaxException { return SnsClient.builder() .credentialsProvider(getAwsCredentials( "Access Key ID", "Secret Key")) .region(Region.US_EAST_1) // Set your selected region .build(); }
Этот метод использует другой вспомогательный метод, получение учетных данных AWS()
:
private AwsCredentialsProvider getAwsCredentials(String accessKeyID, String secretAccessKey { AwsBasicCredentials awsBasicCredentials = AwsBasicCredentials.create(accessKeyID, secretAccessKey); AwsCredentialsProvider awsCredentialsProvider = () -> awsBasicCredentials; return awsCredentialsProvider; }
Действительно, вы можете настроить клиент, когда используете его, но вспомогательные методы немного более элегантны. С этим покончено, давайте создадим конечную точку для создания темы:
@RequestMapping("/createTopic") private String createTopic(@RequestParam("topic_name") String topicName) throws URISyntaxException { // Topic name cannot contain spaces final CreateTopicRequest topicCreateRequest = CreateTopicRequest.builder().name(topicName).build(); // Helper method makes the code more readable SnsClient snsClient = getSnsClient(); final CreateTopicResponse topicCreateResponse = snsClient.createTopic(topicCreateRequest); if (topicCreateResponse.sdkHttpResponse().isSuccessful()) { System.out.println("Topic creation successful"); System.out.println("Topic ARN: " + topicCreateResponse.topicArn()); System.out.println("Topics: " + snsClient.listTopics()); } else { throw new ResponseStatusException( HttpStatus.INTERNAL_SERVER_ERROR, topicCreateResponse.sdkHttpResponse().statusText().get() ); } snsClient.close(); return "Topic ARN: " + topicCreateResponse.topicArn(); }
Примечание: Если ваша система находится за прокси-сервером, вам необходимо настроить Dns-клиент
с помощью пользовательского HttpClient, настроенного для работы с вашим прокси-сервером:
SnsClient snsClient = SnsClient.builder() .credentialsProvider(getAwsCredentials( "Access Key ID", "Secret Key")) .httpClient(getProxyHTTPClient("http://host:port")) .region(Region.US_EAST_1) // Set your selected region .build();
Git Essentials
Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!
private SdkHttpClient getProxyHTTPClient(String proxy) throws URISyntaxException { URI proxyURI = new URI(proxy); // This HTTP Client supports custom proxy final SdkHttpClient sdkHttpClient = ApacheHttpClient.builder() .proxyConfiguration(ProxyConfiguration.builder() .endpoint(proxyURI) .build()) .build(); return sdkHttpClient; }
Или вы можете использовать системный прокси-сервер:
private SdkHttpClient getProxyHTTPClient() throws URISyntaxException { // This HTTP Client supports system proxy final SdkHttpClient sdkHttpClient = ApacheHttpClient.builder() .proxyConfiguration(ProxyConfiguration.builder() .useSystemPropertyValues(true) .build()) .build(); return sdkHttpClient; }
Наконец, давайте сделаем запрос curl
, чтобы проверить, работает ли наша тема при создании:
$ curl http://localhost:8080/createTopic?topic_name=Stack-Abuse-Demo Topic ARN: arn:aws:sns:us-east-1:123456789:Stack-Abuse-Demo
Вы также можете подтвердить, была ли тема создана или нет с помощью консоли AWS:
Пожалуйста, сохраните тему ARN (Имя ресурса Amazon) где-нибудь (например, в базе данных вместе с записями пользователей), так как она нам понадобится позже.
Подписка на тему
Теперь, когда тема настроена в стороне, давайте создадим конечную точку для подписки. Поскольку мы занимаемся электронной почтой, мы установим протокол
для "электронная почта"
. Пожалуйста, обратите внимание, что в терминах AWS “подписчик” упоминается как “конечная точка” , поэтому мы будем использовать наш адрес электронной почты для конечной точки
свойства:
@RequestMapping("/addSubscribers") private String addSubscriberToTopic(@RequestParam("arn") String arn) throws URISyntaxException { SnsClient snsClient = getSnsClient(); final SubscribeRequest subscribeRequest = SubscribeRequest.builder() .topicArn(arn) .protocol("email") .endpoint("[email protected]") .build(); SubscribeResponse subscribeResponse = snsClient.subscribe(subscribeRequest); if (subscribeResponse.sdkHttpResponse().isSuccessful()) { System.out.println("Subscriber creation successful"); } else { throw new ResponseStatusException( HttpStatus.INTERNAL_SERVER_ERROR, subscribeResponse.sdkHttpResponse().statusText().get() ); } snsClient.close(); return "Subscription ARN request is pending. To confirm the subscription, check your email."; }
Давайте отправим еще один запрос на завиток:
$ curl http://localhost:8080/addSubscribers?arn=arn:aws:sns:us-east-1:123456789:Stack-Abuse-Demo Subscription ARN request is pending. To confirm the subscription, check your email.
Примечание: Подписчику необходимо подтвердить подписку, посетив свой адрес электронной почты и нажав на электронное письмо с подтверждением, отправленное AWS:
Отправка Электронных писем
Теперь вы можете публиковать электронные письма в своей теме, и все получатели, подтвердившие свою подписку, должны получить это сообщение:
@RequestMapping("/sendEmail") private String sendEmail(@RequestParam("arn") String arn) throws URISyntaxException { SnsClient snsClient = getSnsClient(); final SubscribeRequest subscribeRequest = SubscribeRequest.builder() .topicArn(arn) .protocol("email") .endpoint("[email protected]") .build(); final String msg = "This Stack Abuse Demo email works!"; final PublishRequest publishRequest = PublishRequest.builder() .topicArn(arn) .subject("Stack Abuse Demo email") .message(msg) .build(); PublishResponse publishResponse = snsClient.publish(publishRequest); if (publishResponse.sdkHttpResponse().isSuccessful()) { System.out.println("Message publishing successful"); } else { throw new ResponseStatusException( HttpStatus.INTERNAL_SERVER_ERROR, publishResponse.sdkHttpResponse().statusText().get()); } snsClient.close(); return "Email sent to subscribers. Message-ID: " + publishResponse.messageId(); }
Давайте отправим еще один запрос на завиток:
$ curl http://localhost:8080/sendEmail?arn=arn:aws:sns:us-east-1:650924441247:Stack-Abuse-Demo Email sent to subscribers. Message-ID: abdcted-8bf8-asd54-841b-5e0be960984c
И, проверив нашу электронную почту, нас встречают:
Обработка Вложений Электронной Почты
AWS SNS поддерживает размеры сообщений только до 256 КБ и не поддерживает вложения. Основной функцией SNS является отправка уведомлений сообщений , а не вложений .
Если вам нужно отправить вложения по электронной почте, вам необходимо использовать Простой почтовый сервис AWS (SES) вместе с его SendRawEmail
для достижения этой функциональности. Мы будем создавать электронные письма с помощью библиотеки javax.mail
.
Если вы не знакомы с ним, не стесняйтесь проверить, как отправлять электронные письма на Java .
Во-первых, давайте настроим клиент Ses
так же , как мы настроили клиент Sns
, и добавим адрес электронной почты:
SesClient sesClient = SesClient.builder() .credentialsProvider(getAwsCredentials( "Access Key ID", "Secret Key")) .region(Region.US_EAST_1) //Set your selected region .build(); VerifyEmailAddressRequest verifyEmailAddressRequest = VerifyEmailAddressRequest.builder() .emailAddress("[email protected]").build(); sesClient.verifyEmailAddress(verifyEmailAddressRequest);
На адреса электронной почты, которые вы добавляете здесь, будет отправлено сообщение с подтверждением, и владельцам адреса электронной почты необходимо подтвердить подписку.
А затем давайте создадим объект электронной почты и используем AWS’ SendRawEmail
для их отправки:
@RequestMapping("/sendEmailWithAttachment") private String sendEmailWithAttachment(@RequestParam("arn") String arn) throws URISyntaxException, MessagingException, IOException { String subject = "Stack Abuse AWS SES Demo"; String attachment = "{PATH_TO_ATTACHMENT}"; String body = "" + "" + "Hello!
" + "Please check your email for an attachment." + "
" + ""; Session session = Session.getDefaultInstance(new Properties(), null); MimeMessage message = new MimeMessage(session); // Setting subject, sender and recipient message.setSubject(subject, "UTF-8"); message.setFrom(new InternetAddress("[email protected]")); // AWS Account Email message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("[email protected]")); // Recipient email MimeMultipart msg_body = new MimeMultipart("alternative"); MimeBodyPart wrap = new MimeBodyPart(); MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setContent(body, "text/html; charset=UTF-8"); msg_body.addBodyPart(htmlPart); wrap.setContent(msg_body); MimeMultipart msg = new MimeMultipart("mixed"); message.setContent(msg); msg.addBodyPart(wrap); MimeBodyPart att = new MimeBodyPart(); DataSource fds = new FileDataSource(attachment); att.setDataHandler(new DataHandler(fds)); att.setFileName(fds.getName()); msg.addBodyPart(att); // Build SesClient SesClient sesClient = SesClient.builder() .credentialsProvider(getAwsCredentials( "Access Key ID", "Secret Key")) .region(Region.US_EAST_1) // Set your preferred region .build(); // Send the email ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); message.writeTo(outputStream); RawMessage rawMessage = RawMessage.builder().data(SdkBytes.fromByteArray(outputStream.toByteArray())).build(); SendRawEmailRequest rawEmailRequest = SendRawEmailRequest.builder().rawMessage(rawMessage).build(); // The .sendRawEmail method is the one that actually sends the email SendRawEmailResponse sendRawEmailResponse = sesClient.sendRawEmail(rawEmailRequest); if (sendRawEmailResponse.sdkHttpResponse().isSuccessful()) { System.out.println("Message publishing successful"); } else { throw new ResponseStatusException( HttpStatus.INTERNAL_SERVER_ERROR, sendRawEmailResponse.sdkHttpResponse().statusText().get() ); } return "Email sent to subscribers. Message-ID: " + sendRawEmailResponse.messageId(); }
И, наконец, давайте отправим запрос, чтобы проверить, работает ли это:
$ curl http://localhost:8080/sendEmailWithAttachment?arn=arn:aws:sns:Stack-Abuse-Demo Email sent to subscribers. Message-ID: 0100016fa375071f-4824-2b69e1050efa-000000
Примечание: Если вы не можете найти письмо, обязательно проверьте папку со спамом:
Отправка SMS-сообщений
Некоторые предпочитают отправлять SMS-сообщения вместо электронной почты, главным образом потому, что SMS-сообщения с большей вероятностью будут замечены. Существует два типа SMS-сообщений:
- Рекламные : Как следует из названия, эти типы сообщений используются только в рекламных целях. Эти сообщения доставляются с 9 утра до 9 вечера и должны содержать только рекламные материалы.
- Транзакционные : Эти сообщения используются для важных и важных уведомлений. Например, для проверки OTP и номера телефона. Сообщения такого типа нельзя использовать в рекламных целях, поскольку это нарушает правила, установленные для транзакционных сообщений.
Отправка SMS на один телефонный номер
@RequestMapping("/sendSMS") private String sendSMS(@RequestParam("phone") String phone) throws URISyntaxException { SnsClient snsClient = getSnsClient(); final PublishRequest publishRequest = PublishRequest.builder() .phoneNumber(phone) .message("This is Stack Abuse SMS Demo") .build(); PublishResponse publishResponse = snsClient.publish(publishRequest); if (publishResponse.sdkHttpResponse().isSuccessful()) { System.out.println("Message publishing to phone successful"); } else { throw new ResponseStatusException( HttpStatus.INTERNAL_SERVER_ERROR, publishResponse.sdkHttpResponse().statusText().get() ); } snsClient.close(); return "SMS sent to " + phone + ". Message-ID: " + publishResponse.messageId(); }
Давайте проверим это с помощью запроса curl
:
$ curl http://localhost:8080/sendSMS?phone=%2B9112345789 SMS sent to +919538816148. Message-ID: 82cd26aa-947c-a978-703d0841fa7b
Отправляйте SMS массово
Массовая отправка SMS не выполняется простым повторением предыдущего подхода. На этот раз мы создадим тему SNS и вместо email
будем использовать протокол sms
. Когда мы захотим отправить массовое сообщение, все подписанные телефонные номера получат уведомление:
@RequestMapping("/sendBulkSMS") private String sendBulkSMS(@RequestParam("arn") String arn) throws URISyntaxException { SnsClient snsClient = getSnsClient(); String[] phoneNumbers = new String[]{"+917760041698", "917760041698", "7760041698" }; for (String phoneNumber: phoneNumbers) { final SubscribeRequest subscribeRequest = SubscribeRequest.builder() .topicArn(arn) .protocol("sms") .endpoint(phoneNumber) .build(); SubscribeResponse subscribeResponse = snsClient.subscribe(subscribeRequest); if (subscribeResponse.sdkHttpResponse().isSuccessful()) { System.out.println(phoneNumber + " subscribed to topic "+arn); } } final PublishRequest publishRequest = PublishRequest.builder() .topicArn(arn) .message("This is Stack Abuse SMS Demo") .build(); PublishResponse publishResponse = snsClient.publish(publishRequest); if (publishResponse.sdkHttpResponse().isSuccessful()) { System.out.println("Bulk Message sending successful"); System.out.println(publishResponse.messageId()); } else { throw new ResponseStatusException( HttpStatus.INTERNAL_SERVER_ERROR, publishResponse.sdkHttpResponse().statusText().get() ); } snsClient.close(); return "Done"; }
Вывод
Spring Cloud AWS чрезвычайно упрощает интеграцию сервисов AWS в проект Spring Boot.
AWS SNS-это надежная и простая служба издателя/подписчика, используемая многими разработчиками по всему миру для отправки простых уведомлений другим конечным точкам HTTP, электронной почте, телефонам и другим сервисам AWS.
Мы создали простое приложение Spring Boot, которое генерирует тему SNS, может добавлять в нее подписчиков и отправлять им сообщения по электронной почте и SMS.
Исходный код доступен на GitHub .