在使用 JavaMail API 时,如何确保多服务器环境中的一台服务器仅读取一次电子邮件

Posted

技术标签:

【中文标题】在使用 JavaMail API 时,如何确保多服务器环境中的一台服务器仅读取一次电子邮件【英文标题】:how to ensure that an email is read only once by one of the server in multiserver environment while using JavaMail API 【发布时间】:2019-09-23 05:16:24 【问题描述】:

我有一个要求,作为其中的一部分,我想读取来自 Outlook 的传入电子邮件,然后进行一些处理。我正在使用 JavaMail API 和 IMAP 协议来实现这一点。我编写了一个 java 类,它可以读取消息中的电子邮件添加事件。

它在单服务器环境中与以下代码完美配合,但是当我将它部署到我们有 2 台服务器的生产环境中时,我最终会处理每封电子邮件两次,因为在两台服务器上都部署了相同的代码,两者都尝试阅读电子邮件一旦它在邮箱中收到。

下面是我用来连接邮箱和阅读邮件的代码sn-p:

 try 
    Properties props = System.getProperties();
    // Get a Session object
    Session session = Session.getInstance(props, null);
    // session.setDebug(true);

    // Get a Store object
    Store store = session.getStore("imap");

    // Connect
    store.connect(argv[0], argv[1], argv[2]);

    // Open a Folder
    Folder folder = store.getFolder(argv[3]);
    if (folder == null || !folder.exists()) 
    System.out.println("Invalid folder");
    System.exit(1);
    

    folder.open(Folder.READ_WRITE);

    // Add messageCountListener to listen for new messages
    folder.addMessageCountListener(new MessageCountAdapter() 
    public void messagesAdded(MessageCountEvent ev) 
        Message[] msgs = ev.getMessages();
        System.out.println("Got " + msgs.length + " new messages");
        // Process incoming mail.

 catch (Exception ex) 
    ex.printStackTrace();

关于我应该如何限制电子邮件在多服务器环境中只处理一次的任何建议?

【问题讨论】:

您的服务器需要协调它们的访问,例如使用分布式锁。只有当前持有锁的服务器才会从邮箱中读取。 谢谢大家的回答。在我的情况下,分布式锁定似乎不可行。我终于设法通过使用数据库表作为并发控制器来解决我的问题。以下是有关此方法的更多详细信息的链接:dzone.com/articles/using-relational-dbms-multi 【参考方案1】:

也许解决它最简单的方法是使用分布式锁;有很好的图书馆。但是如果你想在 javamail 中得到答案,那么有两种方法。

首先,你可以使用Flag,调用message.isSet()检查其他服务器是否设置了标志,然后message.setFlags()锁定。不幸的是,比赛。这场比赛可以通过一些涉及另外两个标志或an IMAP extension called condstore javamail 显然不支持的不漂亮黑客来解决——setFlags() 需要一个新的long 参数来设置标志,只有在标志没有改变的情况下自客户上次注意到以来。

其次,您可以使用一系列邮箱并在其中移动邮件。您需要四个邮箱,即收件箱和另外三个可能称为“处理-a”、“处理-b”和“已处理”的邮箱。服务器 A 处理“处理-a”中的所有消息,然后将每个消息移动到“已处理”,B 负责“处理-b”。当每个服务器完成其“处理-foo”时,它会在收件箱中查找新邮件并调用moveMessages() 以原子方式将一条或几条消息移动到自己的邮箱中。 moveMessages() 使用an IMAP extension called move 以原子方式移动消息,大多数服务器都支持,但不是全部。

【讨论】:

JavaMail 在IMAPFolder class 中支持CONDSTORE;诀窍是找到支持它的服务器。 JavaMail 还支持MOVE。 @BillShannon:是吗?您将如何发送诸如a uid store 1 (unchangedsince 1234) +flags (locked) 之类的条件存储?我认为这需要setFlags(…, 1234)。不过,IMO 基于 MOVE 的解决方案更好。 MOVE 是一个更好的扩展,包括更好的部署。 是的,抱歉,我已经多年没有添加 CONDSTORE 和 QRESYNC 支持了。它主要用于支持在重新联机时重新同步脱机缓存。对于该用例,不需要“存储未更改”,尽管如果需要此用例,可以轻松添加它。但您可能说 MOVE 更好。

以上是关于在使用 JavaMail API 时,如何确保多服务器环境中的一台服务器仅读取一次电子邮件的主要内容,如果未能解决你的问题,请参考以下文章

在android开发中如何使用JavaMail程序

使用 JavaMail API 时出现 SSLException

使用 javamail API 发送带有附件的电子邮件

JavaMail读取邮件,如何过滤需要的邮件

JavaMail API的应用

使用带有 smtp 但没有 SSL 的 JavaMail API 在 android 中发送电子邮件