超时的API设计:TimeoutException或带有out参数的布尔返回?

时间:2011-11-23 22:07:58

标签: c# .net api timeout

场景是RPC over消息队列 - 由于底层机制是异步的,因此客户端应指定在超时之前等待响应的时间。作为客户端,您更愿意使用这两个代码片段中的哪一个?

最重要的是:作为GetResponseTo()方法的用户,为什么你更喜欢一个?您的选择如何使您的代码更具可扩展性,更易读,更可测试等?

try
{
    IEvent response = _eventMgr.GetResponseTo(myRequest, myTimeSpan);
    // I have my response!
}
catch(TimeoutException te)
{
    // I didn't get a response to 'myRequest' within 'myTimeSpan'
}

OR

IEvent myResponse = null;

if (_eventMgr.GetResponseTo(myRequest, myTimeSpan, out myResponse)
{
    // I got a response!
}
else
{
    // I didn't get a response... :(
}

有关您的信息,请参阅GetResponseTo()的当前实现:

public IEvent GetResponseTo(IEvent request, TimeSpan timeout)
{
    if (null == request) { throw new ArgumentNullException("request"); }

    // create an interceptor for the request
    IEventInterceptor interceptor = new EventInterceptor(request, timeout);

    // tell the dispatcher to watch for a response to this request
    _eventDispatcher.AddInterceptor(interceptor);

    // send the request
    _queueManager.SendRequest(request);

    // block this thread while we wait for a response.  If the timeout elapses,
    // this will throw a TimeoutException
    interceptor.WaitForResponse();

    // return the intercepted response
    return interceptor.Response;
}

5 个答案:

答案 0 :(得分:1)

无论是第一个还是第二个,我都想使用Task Parallel Library,这是从.NET 4.5开始异步执行所有事情的推荐方法:

Task<IEvent> task = _eventMgr.GetResponseToAsync(myRequest);

if (task.Wait(myTimeSpan))
{
    // I got a response!
}
else
{
    // I didn't get a response... :(
}

答案 1 :(得分:1)

你可以使用AutoResetEvent类来处理第二个的管道。

尽量避免使用您的第一个代码段,因为异常费用很高

答案 2 :(得分:0)

异常繁重且杂乱,每个API方法调用都应该被try / catch / finally包装以处理自定义异常。这种方法不适合开发人员,所以我不喜欢它。

考虑到GetResponse()调用本身对于API使用者来说是同步的 - 返回操作值是很正常的,但我建议引入更抽象和信息量更强的东西而不是简单的bool状态,这样你就可以返回任何由底层消息传递系统提供的状态,这可以是自定义错误代码,消息甚至对象。因此,这也是API - put接口:

enum OperationStatus
{
   Unknown,
   Timeout,
   Ok
}

// pretty simple, only message and status code
interface IOperationResult<T>
{
      OperationStatus Status { get; }
      string Message { get; }
      T Item { get; }      
}


class GetResponseResult : IOperationResult<IEvent>
{
   ...
} 

class EventManager
{
     public IOperationResult<IEvent> GetResponseTo(
                                      IRequest request, 
                                      TimeSpan timeInterval)
    {    
        GetResponseResult result;  

        // wait for async request                 
        // ...

        if (timeout)
        {
          result = new GetResponseResult 
                        { 
                           Status = OperationStatus.Timeout,
                           Message = underlyingMessagingLib.ErrorMessage
                        };
        }
        else
        {
          result = new GetResponseResult 
                        { 
                           Status = OperationStatus.Ok,
                           Item = response
                        };
        }

        return result;
    }
}

答案 3 :(得分:0)

我个人更喜欢异常版本。如果我指定一些超时,我的意见是,如果我无法在指定的时间范围内得到结果,这是一个例外。我不认为基于事件的通知是最好的决定。以下逻辑取决于结果,因此它不会对我有意义。 但是如果你也想提供异步方法,那么任务就像dtb

所说的那样好

答案 4 :(得分:0)

我选择使用out参数。

我想将其他人标记为答案,但我无法这样做。我尝试实施基于TPL的方法,但根据我在评论中链接的问题/答案,我无法这样做。

我不想通过引入更多概念来混淆我的事件模型,正如@sll建议的那样。

尽管@dasheddot更喜欢异常版本,但@sll有一个很好的观点,即有人试图发送大量请求并在循环中获得大量响应可能需要处理很多异常。

// potentially 10 exceptions?  meh... let's not go down this road.
for(int i=0;i<10;i++)
{
  try
  {
     IEvent response = _eventMgr.GetResponseTo(myRequest, myTimeSpan);

     // I have my response!
  }
  catch(TimeoutException te)
  {
     // I didn't get a response to 'myRequest' within 'myTimeSpan'
  } 
}