使用线程/通知父线程有关Java中错误的异常处理最佳实践解决方案

huangapple go评论72阅读模式
英文:

Best-practice solution for exception-handling using threads/notifying parent thread about error in Java

问题

关于在线程中的异常处理最佳实践解决方案,我有一个问题:
我的任务是在TransferServer上实现一个简化版本的SMTP,该服务器接收消息并将其分发到与收件人域相关联的不同邮件服务器。

首先,我有一个共享的BlockedQueue,每个客户端连接(在单独的线程中)将要发送的消息添加到其中,还有另一个ConsumerThread,它通过BlockedQueue处理消息,查找域并将每条消息分发到正确的IP地址。

在这个消费者线程中,我有一个ExecutorService,我将发送消息到指定邮件服务器的任务提交给它。现在问题是:当向邮件服务器交付消息失败时,我需要向发件人发送一条消息,通知其交付失败。此外,在消费者线程中还可能发生域查找失败且未找到邮件服务器的情况。

因此,我有两种可能的错误情况:

  1. 域查找失败
  2. 消息交付失败

第一种情况发生在消费者线程中,因此很容易尝试发送另一条包含错误信息的消息给发件人。另一方面,第二种情况让我陷入困境。

在单独的消息发送线程/任务中实现此错误消息发送的最佳实践是什么?

我考虑了三种不同的可能解决方案:

  1. 使我的SenderTask实现Callable,而不是Runnable,并返回错误代码,然后定期检查成功情况(在这种情况下,我不知何故需要跟踪所有的Futures),如果发生错误,则父ConsumerThread将启动一个新的SenderTask,将错误消息发送回发件人。
  2. 将ExecutorService传递给每个SenderTask,并让SenderTask自己向执行器提交一个新的错误消息SenderTask(对我来说感觉有些奇怪)。
  3. 使用UncaughtExceptionHandler处理错误消息的发送,并创建一个ThreadFactory来将处理程序与执行器一起使用。

哪种方法最适合,或者是否甚至有更简单的方法来实现这一点?

英文:

I got a question regarding what is the best-practice solution for exception handling in threads:
My task is to implement a simplified version of SMTP on a TransferServer which receives a message and distributes it to the different mail servers associated with the recipient's domain.

So first I got a shared BlockedQueue where each client connection (in a separate thread) adds messages to send, and another ConsumerThread that works through the BlockedQueue, looks up domains and distributes each message to the right IP address.

In this consumer thread I have an ExecutorService which I submit the tasks of sending a message to a specified mail server. The problem now is the following: When message delivery to a mail server somehow fails, I need to send back a message to the sender informing them that the delivery failed. Additionally it can also happen in the consumer thread that a domain lookup fails and no mail server is found.

So I got two possible error cases:

  1. Domain lookup fails
  2. Message delivery fails

The first case happens right in the consumer thread and therefore it is very easy to just try to send another message back to the sender containing the error. The second case on the other hand is the one that keeps me stuck.

What would be best-practice to implement this error message sending in a separate message sending thread/task?

I thought of three different possible solutions:

  1. Make my SenderTask implement Callable, not Runnable and return an error code, then periodically check for success (i somehow need to keep track of all Futures in this scenario) and if an error occurs, the parent ConsumerThread will initiate a new SenderTask which sends the error message back to the sender.
  2. Pass ExecutorService to each SenderTask and let SenderTask submit a new error message SenderTask to the executor itself (feels weird to me)
  3. Use an UncaughtExceptionHandler which handles the error message sending and create a ThreadFactory to use the handler with the executor

Which approach is the best suitable or is there even an easier way to achieve this?

答案1

得分: 0

根据您的情况,最好的选择是根本不在线程之间传递异常。您可以在异步操作本身中进行错误处理,这样就不需要任何线程同步。错误处理程序可以作为接口注入到异步操作中。例如:

public interface ErrorHandler {
  void handleDomainLookup(DomainLookupException exc);
  void handleMessageDeliveryFails(MessageDeliveryFailsException exc);
}
.....
public class AsynchronousEmailTask implements Runnable {
   private final ErrorHandler errorHandler;
   // 您可能需要其他参数来发送电子邮件
   public AsynchronousEmailTask(ErrorHandler errorHandler) {
     this.errorHandler = errorHandler;
   }
   @Override
   public void run() {
      try {
        sendEmail();
      } catch(MessageDeliveryFailsException exc) {
        this.errorHandler.handleMessageDeliveryFails(exc);
      } catch(DomainLookupException exc) {
        this.errorHandler.handleDomainLookup(exc);
      }
   }
   void sendEmail() throws DomainLookupException, MessageDeliveryFailsException {
    // 待实现部分
   }
}
......
class Consumer implements ErrorHandler {
...
 public void handleDomainLookup(DomainLookupException exc) {
   // 待实现部分
 }
 public void handleMessageDeliveryFails(MessageDeliveryFailsException exc) {
   // 待实现部分
 }
...
 void consume() {
   ...
     pool.submit(new AsynchronousEmailTask(this) );
 }
}
英文:

For your case the best option is not to transfer exceptions between threads at all. What you can do is make error handling with the asynchronous operation it self, so that you need no any thread synchronization. Error handler can be injected to asynchronous operation as an interface. For example:

public interface ErrorHandler {
  void handleDomainLookup(DomainLookupException exc);
  void handleMessageDeliveryFails(MessageDeliveryFailsException exc);
}
.....    
public class AsynchronousEmailTask implements Runnable {
   private final ErrorHandler errorHandler;
   // you may need some another arguments for sending email
   public AsynchronousEmailTask(ErrorHandler errorHandler) {
     this.errorHandler = errorHandler;
   }
   @Override
   public void run() {
      try {
        sendEmail();
      } catch(MessageDeliveryFailsException exc) {
        this.errorHandler.handleMessageDeliveryFails(exc);
      } catch(DomainLookupException exc) {
        this.errorHandler.handleDomainLookup(exc);
      }
   }
   void sendEmail() throws DomainLookupException, MessageDeliveryFailsException {
    // TODO: implement me
   }
}
......
class Consumer implements ErrorHandler {
...
 public void handleDomainLookup(DomainLookupException exc) {
   // TODO: implement me
 }
 public void handleMessageDeliveryFails(MessageDeliveryFailsException exc) {
   // TODO: implement me
 }
...
 void consume() {
   ...
     pool.submit(new AsynchronousEmailTask(this) );
 }

}

huangapple
  • 本文由 发表于 2020年10月18日 22:32:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/64414504.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定