WCF异步调用实战:OneWay+Asynchronous Operation
最近,博客园博客评论、博问回答、闪存都会出现同样的问题,有时提交特别慢,甚至一次提交长达6秒左右。
通过日志分析发现,引起速度慢的操作发生在调用WCF服务时。比如,我在一篇博文中发表了一个评论,评论内容被保存到数据库之后,还会调用WCF服务,生成一条“博客评论”动态,这样在园子里关注我的人就能看到这条动态。
由于动态的操作频繁,数据量大,是一个相对耗时的操作,我们希望这个操作不要影响当前程序的执行,所以,采用异步操作是一个合理的选择。而且这是一个不需要返回值的异步操作。
我们想当然地在WCF客户端生成代理类时选择了“Generate asynchronous operations”,生成了客户端异步操作的代理类,然后调用异步方法client.AddAsync()。
可是,采用这样的异步调用之后,发现问题依然存在。
这种异步调用方法叫“异步信道调用”:
客户端通过绑定创建的信道向服务端发送消息,从而实现了对服务的调用,不管消息通过信道向服务端发送的方式是同步的(采用请求-回复MEP进行消息交换)还是异步的(采用单向MEP进行消息交换),客户端程序都可以通过代理对象异步地调用信道,从而实现异步服务调用;
欲解详情,请移步Artech的博客 ...-》WCF技术剖析之十一:异步操作在WCF中的应用(上篇)。
这个异步调用是针对信道(Channel)的,与服务端无关,也就是WCF客户端改为异步之后,服务端不需要作任何修改。
此路不通,只能绕行...
于是,我们改用WCF服务端异步,准确的说法是“异步服务实现”(服务端在具体实现服务操作的时候,采用异步调用的方式。- 引用自异步操作在WCF中的应用(上篇))。怎么实现呢?遇到WCF问题,当然要去园子里当之无愧的WCF专家博客。参考了WCF技术剖析之十一:异步操作在WCF中的应用(下篇),我们服务端的WCF也异步了。
可是,惊喜没有出现,问题依旧。
此路还是不通,只能继续绕行,问题是往哪里绕?
我们通过测试进一步确认了问题,在WCF服务端实现方法中直接将当前线程sleep:
Thread.Sleep(50000);
经过测试发现,不管客户端是同步还异步,服务端是同步还是异步,都跳不过这个Sleep时间。难道是我们对这里的异步理解错了?(后来发现,的确是理解错了)
遇到技术难题找Google,Google一般找StackOverflow(这两人感情很好),找到一个线索:
原来,之前的方法不管用什么步,都要等服务端的方法执行全部完成,而异步只是在发行异步调用之后,不用干等,可以干其他事情。但整个执行还是要等异步方法执行完成并返回。
而我们的需求就发起异步调用之后,就走人,不管执行结果如何,这样才能保证异步调用对当前程序的执行速度影响最小。
而WCF已经考虑了这个需求,提供了解决方案:[OperationContract(IsOneWay = true)],见上图中的代码。
回头看看Artech的文章,也提到了这个:
单向(One-way)消息交换:客户端的信道通过单向的消息交换模式向服务端发送消息,消息一旦抵达传输层马上返回,从而达到异步服务调用的效果。
绕行两次,终于走上正道!
于是,采用OneWay解决方案,在服务端相应的WCF接口上加上[OperationContract(IsOneWay = true)]。
需要注意的地方:
- 标记为[OperationContract(IsOneWay = true)]的方法返回值必须为void。
- WCF客户端要重新生成代理类。
测试下来,发现偶尔还会出现调用WCF速度慢的问题。后来,将客户端改为“异步信道调用”(Asynchronous Operation),也就是前面所述的第一种异步调用方法(client.AddAsync()),问题才解决。
所以,经验实践检验过的解决方案是单向(One-way)消息交换+“异步信道调用”(Asynchronous Operation)。
小结
在遇到问题并进行解决的时候,是学习效率最高、学习热情最高的时候,不要急于求成,将问题相关的知识理解透了,解决方法会突然而至。问题解决之后,写一篇博客,将在解决问题过程中学到的知识整理一下,将零散的知识点组织起来,你将会有更进一步的收获。
在解决问题中学习,在解决问题后分享,在学习与分享中成长!