2011-11-23 74 views
1

该场景是RPC over消息队列 - 由于底层机制是异步的,因此客户端应指定在超时之前他们等待响应的时间。作为客户端,您宁愿使用这两个代码片段中的哪一个?用于超时的API设计:TimeoutException或带out参数的布尔返回值?

最重要的是:作为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; 
} 

回答

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' 
    } 
} 
0

例外的是沉重而凌乱,每个API方法调用应该尝试包裹/ catch /最后到hanle自定义异常。这种方法不适合开发人员,所以我不喜欢它。

考虑到GetResponse()调用本身对API消费者是同步的 - 返回操作值是非常正常的,但我会建议引入一些更抽象和更丰富的事情,而不是简单的布尔状态,因此您可以返回任何状态底层的消息系统,这可能是一个自定义的错误代码,消息,甚至是对象。所以,因为这是API - 把界面和:

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; 
    } 
} 
+0

GetResponseTo()方法的实现会阻塞您的线程 - 使其看起来好像发送请求并同步获得响应 - 即使它发生在异步pub/sub上。 –

+0

另外,如果我露出一个事件像你的建议,这将是由你来决定哪些请求超时(也许你有很多优秀的请求)。 –

+0

@约翰·鲁伊斯:​​EventArgs的可以为您提供关于timeouted要求,BUF TOR syncronous API的信息,非此非彼TPL不适合提供代码意义 – sll

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... :(
} 
+0

由于方法调用确实是同步的,所以使用TPL没有意义,@dtb:你在想什么? – sll

+0

底层机制是异步的;我认为没有价值试图隐藏这个事实背后的抽象,一旦超时,网络错误等发生泄漏。但是,如果使用TPL是有意义的,则它在消息队列库上有一点依赖。 – dtb

+0

据我所知有些企业短信库同时提供了异步和同步方法API,以便调用者可以考虑在每个特定情况下使用哪一个,这样既同步和异步方法很有意义 – sll

1

你可以看看使用AutoResetEvent类,这将处理第二个管道。

尽量避免你的第一个代码段的例外是贵

+0

如果通过通常情况下的例外方法循环很多次,那么例外是昂贵的。如果不是,那么应该使用通常情况下的异常 – dasheddot

+0

是的,EventInterceptor.WaitForResponse()的实现实际上是在等待AutoResetEvent。 –

0

个人而言,我宁愿例外版本。如果我指定了一些超时,我认为这是一个例外,那么如果我不能在指定的时间范围内得到结果。我不认为基于事件的通知是最好的决定。下面的逻辑取决于结果,所以它对我没有意义。 但是,如果你想提供异步方法,任务是一个好主意,就像dtb所述