Рубрики
Без рубрики

Весеннее облако: AWS SNS

AWS SNS-это система обмена сообщениями издателя/подписчика, которая является популярным выбором для многих разработчиков. В этом уроке мы создадим приложение Spring Cloud с поддержкой обмена сообщениями по SMS и электронной почте.

Автор оригинала: 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-сообщений:

  1. Рекламные : Как следует из названия, эти типы сообщений используются только в рекламных целях. Эти сообщения доставляются с 9 утра до 9 вечера и должны содержать только рекламные материалы.
  2. Транзакционные : Эти сообщения используются для важных и важных уведомлений. Например, для проверки 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 .