0

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

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

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

我在某处读到了错误的做法,并使数据库服务器过载。

是的,我会使用智能 crons,这样就不会有 2 个 crons 重叠,或者为偶数和奇数等设置一个 crons。我还在寻找第 3 方替代方案,例如http://www.iron.io/用于 crons。

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

4

2 回答 2

0

我必须做类似的事情,并按照查尔斯·恩格尔克的建议做了——我使用了 SQS。

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

这是将消息排队的示例代码:

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 调用后回滚的数据库事务,则您的业务流程将被撤消,但电子邮件将被发送。

深思熟虑。

于 2013-02-26T18:29:27.663 回答
0

感谢迈克的详细回复。我最终为我的应用程序实现了一个 REST API,它具有安全的用户名+密码+密钥访问权限,并从 3rd Party Service Iron.io 运行它

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

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

www.example.com/rest/messagesposted

我之所以采用这种方法,是因为我必须将消息安排为 90 天以上的间隔,而队列只保留 14 天的消息。

你侦察什么?

于 2013-03-03T02:35:12.403 回答