简体   繁体   English

Java电子邮件发送队列 - 固定数量的线程,发送尽可能多的消息

[英]Java email sending queue - fixed number of threads sending as many messages as are available

I'm writing a message processing application (email) that I want to have an outgoing queue. 我正在编写一个消息处理应用程序(电子邮件),我想要一个传出队列。 The way I've designed this is having a singleton queue class, ThreadedQueueSender, backed by an Executor Service and a BlockingQueue. 我设计的方式是拥有一个单例队列类ThreadedQueueSender,由Executor Service和BlockingQueue支持。 Additionally, a thread pool of javax.mail.Transport objects is used to obtain and release connections to the outgoing SMTP server. 此外,javax.mail.Transport对象的线程池用于获取和释放与传出SMTP服务器的连接。

This class exposes a method, add(MimeMessage) , that adds messages to the work queue ( BlockingQueue ). 此类公开了一个方法add(MimeMessage) ,它将消息添加到工作队列( BlockingQueue )。

At instantiation of the class, the ExecutorService is initialized to a ThreadPoolExecutor with a fixed number of threads, lets say 5. Each thread's run() method is in infinite loop that only exits when it detects interrupt (when ExecutorService.shutdownNow() is called). 在类的实例化中, ExecutorService被初始化为具有固定线程数的ThreadPoolExecutor ,比方说5.每个线程的run()方法都处于无限循环中,只有在检测到中断时才会退出(当调用ExecutorService.shutdownNow()时)。

This run method uses BlockingQueue.poll() to take messsages from the work queue until no more are available without blocking, then requests a Transport object from the connection pool, opens the connection, sends all the messages its retrieved, closes the connection and returns the Transport object. 此运行方法使用BlockingQueue.poll()从工作队列中获取消息,直到没有阻塞不再可用,然后从连接池请求Transport对象,打开连接,发送其检索的所有消息,关闭连接并返回Transport对象。

This works, but I feel I am not taking full advantage of the ExecutorService by having a fixed number of threads that run for the life of the application. 这有效,但我觉得我没有充分利用ExecutorService,因为它有一定数量的线程在应用程序的生命周期内运行。 Also, I am managing the work queue myself instead of letting the concurrency frameworks handle it. 此外,我自己管理工作队列,而不是让并发框架处理它。 How would others implement this functionality? 其他人如何实现此功能? Is it better to wrap each incoming message in a Runnable, then execute the sending logic? 将每个传入消息包装在Runnable中然后执行发送逻辑会更好吗?

Thank you, any comments are appreciated. 谢谢,任何意见表示赞赏。

Ryan 瑞安

You should create tasks for every piece of work that should be done by your executor service. 您应该为执行程序服务应该完成的每项工作创建任务。

For example you could create a callable "MailSendingTask" that holds the MimeMessage and wraps the mail sending. 例如,您可以创建一个可调用的“MailSendingTask”来保存MimeMessage并包装邮件发送。 Queue these MailSendingTasks by submitting them to your executor. 通过将这些MailSendingTasks提交给您的遗嘱执行人来排队。 Now your Executor decides how many threads will be created (Config it by setting lower and upper thread pool bounds) 现在,您的执行程序决定将创建多少个线程(通过设置较低和较高的线程池边界来配置它)

You only need to create 2 or 3 classes/interfaces 您只需要创建2个或3个类/接口

  • one MailService Interface that provides a simple send(MimeMessage msg) method 一个MailService接口,提供简单的发送(MimeMessage msg)方法
  • one MailServiceImplementation Class that implements MailService and holds a reference to a configured executor 一个MailServiceImplementation类,它实现MailService并保存对已配置的执行程序的引用
  • one class MailSenderTask implementing the callable interface that holds a reference to the MimeMessage object and which does the mail sending. 一个类MailSenderTask实现可调用接口,该接口保存对MimeMessage对象的引用并执行邮件发送。

You could even go futher by creating an extra service that manages the mail socket connections which could be used by the MailSenderTask. 您甚至可以通过创建管理MailSenderTask可以使用的邮件套接字连接的额外服务来进一步发展。

If you want to add "cancelation" you should look at the classes Future and FutureTask 如果要添加“取消”,您应该查看Future和FutureTask类

Wrapping up the messages in a Runnable would force you to either make the work queue unbounded or deal with what happens when the queue is full. Runnable包装消息将强制您使工作队列无限制或处理队列已满时发生的情况。 ThreadPoolExecutor gives you a few policies for dealing with this situation - See ThreadPoolExecutor javadoc for details. ThreadPoolExecutor为您提供了一些处理这种情况的策略 - 有关详细信息,请参阅ThreadPoolExecutor javadoc - Give up, run it yourself / discard something - 放弃,自己运行/丢弃一些东西

Another thing you can do is allow the thread pool to create threads beyond its core size, how threads are spawned and when they are reaped is described by the first 4 arguments to the ThreadPoolExecutor constructor. 您可以做的另一件事是允许线程池创建超出其核心大小的线程,如何生成线程以及何时获取线程由ThreadPoolExecutor构造函数的前4个参数描述。 How well this works in reality will depend on the resource bottleneck. 这在现实中如何运作将取决于资源瓶颈。

Also, what's the advantage of BlockingQueue.poll in your situation, rather than BlockingQueue.take ? 另外, BlockingQueue.poll在你的情况下有什么优势,而不是BlockingQueue.take Both are interruptible, and your thread only has one task, so blocking is not undesirable. 两者都是可中断的,并且您的线程只有一个任务,因此阻塞不是不可取的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM