来自数据库的SMS和电子邮件队列

时间:2013-02-11 21:10:30

标签: email amazon-web-services rds

我只是想讨论一下我面临的情况。

我想向用户发送电子邮件 - 很多电子邮件 - 但如果我在应用程序运行时发送它们,AWS SDK对于电子邮件来说速度慢 - 用户体验不佳 - 至少对我的应用程序而言。

所以我打算做的是在数据库中输入数据(电子邮件地址,要发送的内容,0)并启动一个cron作业来读取表格并开始发送电子邮件 - 一旦发送电子邮件 - 它标记了数据库行为1。

我在某处读到了一个错误的做法,并在数据库服务器上放置了重载。

是的,我会使用智能crons,以便没有2个crons重叠或为偶数和奇数等设置一个cron。我也在寻找第三方替代品,如crons的http://www.iron.io/

有人可以分享他们在类似情况下的经验等等。我只是想使用智能解决方案,而不仅仅是在数据库上投入大量资源并在交易上花费大量......

2 个答案:

答案 0 :(得分:0)

我必须做类似的事情,并且像Charles Engelke建议的那样 - 我使用了SQS。

通过将整个消息内容放在SQS消息中,我完全消除了数据库。你被限制在64k in an SQS message,所以只要这不是问题,这种方法是可行的。

以下是排队邮件的示例代码:

package com.softwareconfidence.bsp.sending;

import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.model.SendMessageRequest;
import com.googlecode.funclate.json.Json;

import java.util.HashMap;
import java.util.Map;

public class EmailQueuer  {
    private final AmazonSQS sqs;
    private final String sendQueueUrl;

    public EmailQueuer(AmazonSQS sqs,String sendQueueUrl) {
        this.sqs = sqs;
        this.sendQueueUrl = sendQueueUrl;
    }

    public void queue() {
        Map<String,String> emailModel = new HashMap<String, String>(){{
            put("from","me@me.com");
            put("to","you@you.com");
            put("cc","her@them.com");
            put("subject","Greetings");
            put("body","Hello World");
        }}; 
        sqs.sendMessage(new SendMessageRequest(sendQueueUrl, Json.toJson(emailModel)));
    }
}

然后在您的应用程序中,您需要有一个执行器服务来轮询队列并处理消息:

new ScheduledThreadPoolExecutor(1).scheduleAtFixedRate(sendEmails(), 0, 1, MINUTES)

当应用程序退出时,您需要确保在此执行程序上调用shutdown()。无论如何,这一行将每分钟发送一次电子邮件,其中sendEmails()返回此Runnable类的一个实例:

package com.softwareconfidence.bsp.standalone.sending;

import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.model.*;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.model.DeleteMessageRequest;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.sqs.model.ReceiveMessageResult;
import com.amazonaws.services.sqs.model.SendMessageRequest;
import com.googlecode.funclate.json.Json;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;

public class FromSqsEmailer implements Runnable {
    private final AmazonSQS sqs;
    private final String sendQueueUrl;
    private final String deadLetterQueueUrl;
    private final AmazonSimpleEmailService emailService;

    public FromSqsEmailer(AmazonSimpleEmailService emailService, String deadLetterQueueUrl, String sendQueueUrl, AmazonSQS sqs) {
        this.emailService = emailService;
        this.deadLetterQueueUrl = deadLetterQueueUrl;
        this.sendQueueUrl = sendQueueUrl;
        this.sqs = sqs;
    }

    public void run() {
        int batchSize = 10;
        int numberHandled;
        do {
            ReceiveMessageResult receiveMessageResult =
                    sqs.receiveMessage(new ReceiveMessageRequest(sendQueueUrl).withMaxNumberOfMessages(batchSize));
            final List<com.amazonaws.services.sqs.model.Message> toSend = receiveMessageResult.getMessages();
            for (com.amazonaws.services.sqs.model.Message message : toSend) {
                SendEmailResult sendResult = sendMyEmail(Json.parse(message.getBody()));
                if(sendResult != null) {
                    sqs.deleteMessage(new DeleteMessageRequest(sendQueueUrl, message.getReceiptHandle()));
                }
            }
            numberHandled = toSend.size();
        } while (numberHandled > 0);
    }

    private SendEmailResult sendMyEmail(Map<String, Object> emailModel) {
        Destination to = new Destination()
                .withToAddresses(get("to", emailModel))
                .withCcAddresses(get("cc", emailModel));
        try {
            return emailService.sendEmail(new SendEmailRequest(get("from", emailModel), to, body(emailModel)));
        } catch (Exception e){
            StringWriter stackTrace = new StringWriter();
            e.printStackTrace(new PrintWriter(stackTrace));
            sqs.sendMessage(new SendMessageRequest(deadLetterQueueUrl, "while sending email " + stackTrace));
        }
        return null;
    }

    private String get(String propertyName, Map<String, Object> emailModel) {
        return emailModel.get(propertyName).toString();
    }

    private Message body(Map<String, Object> emailModel) {
        Message message = new Message().withSubject(new Content(get("subject", emailModel)));
        Body body = new Body().withText(new Content(get("body", emailModel)));
        message.setBody(body);
        return message;
    }
}

如果您使用数据库,这种方法的一个缩小是电子邮件发送步骤是HTTP调用。如果您有一个在此HTTP调用后回滚的数据库事务,则会撤消您的业务流程,但会发送该电子邮件。

深思熟虑。

答案 1 :(得分:0)

感谢Mike的详细回复。我最终为我的应用程序实现了一个REST API,它具有安全的用户名+密码+密钥访问权限,并从获得的第三方服务Iron.io运行

www.example.com/rest/messages/format/json

它迭代并发送收集状态的消息在一个数组中 - 然后它回发到

www.example.com/rest/messagesposted
  

我遵循这种方法,因为我不得不安排消息   间隔90天,队列只保留14天的消息。

你在做什么调查?