Wcf消息交换模式
1. 请求/回复(Request/Reply)
(1) 默认的消息交换模式
(2) 图例:
2. 单向(One-way)
(1) 使用OperationContractAttribute将操作标记为one-way
(2) 没有回应(response),不会报告异常
(3) 客户端虽然不关心消息响应,但是有些情况下希望保证消息能够确实送达:可靠性消息提供暂时性的可靠性,Microsoft® Message Queuing (MSMQ) 能够提供可持久的可靠性
(4) 如果服务器端信道发生了错误,客户端只有在下次调用时才能发现这个错误
(5) 主要应用场景:日志活动或者事件发布
(6) 被设置为单向操作的方法不能包含返回值,即它的返回值只能为void,否则会抛出InvalidOperationException异常
(7) 图例:
3. 双向(Duplex)
(1) 操作可以是request/reply类型或者one-way类型
(2) 回调契约无须标记ServiceContract特性,但是在回调契约中必须为服务的操作标记OperationContract特性
(3) 服务对回调的调用可能会产生死锁。例如,当客户端执行服务操作时,向客户端发出的调用会阻塞服务端进程,以等待服务操作执行完毕。而在该服务操作中,又获得了回调契约对象的引用(或者获得保存的回调契约副本),并执行回调操作。由于服务类被配置为单线程访问,则服务实例是与锁相关联的。如果回调对象也需要返回同一个锁的所有权,简单的说,就是指当回调的应答消息也需要获得与服务实例关联的相同的锁时,就会导致死锁。因为此时服务线程已经被阻塞,服务操作正在等待回调操作执行完毕,而回调操作却又在等待服务释放锁,自然会产生锁的争用。解决死锁的三种方式:一个是将服务配置为允许多线程访问,但这会增加服务开发者管理多线程的负担。第二个方案是将回调设置为重入(Reentrancy),所谓“重入”,是指对同步域拥有独占访问权的线程A调用了同步域之外对象的方法,此时,另外的线程B若要访问该同步域,则线程A将释放对同步域的锁,允许线程B进入。直到线程B执行完毕并释放对同步域的锁后,线程A将重新进入该同步域。配置回调为重入时,因为服务对象是与线程关联的,属于同步域的对象,而回调对象则属于同步域之外的对象。由于服务被配置为重入,则服务调用回调引用时会释放锁。然后将回调返回给客户端,控制权则返回给服务,服务会重入并重新获取锁。这样就解决了死锁的问题。 第三种方案则是将回调操作设置为单向操作。此时,回调调用不会产生应答消息,服务操作一旦执行了回调操作,就会继续执行,回调对象不会争用与服务实例关联的锁,从而解决了死锁问题。
(4) 图例:
Wcf大数据量传输
1. 在绑定上指定消息大小的配额,能够增加对于较大消息的支持
2. 具体解决方式:
(1) 消息传输优化机制(MTOM)
<wsHttpBinding>
<binding name="wsHttpLargeMessage"maxReceivedMessageSize="5000000" messageEncoding="Mtom">
<readerQuotas maxArrayLength="5000000"/>
</binding>
</wsHttpBinding>
(2) MTOM处理传输整个消息仍然被加载到内存中,对于大量数据传输,数据流传递降低了内存的使用量。
Wcf实例模型
1. 通过ServiceBehaviorAttribute控制InstanceContextMod枚举的percall,persession和single,代码:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class CounterServicePerCall:ICounterServicePerCall
2. Percall服务
(1) 为每个调用创建新的服务对象
(2) 增加了整体的吞吐量
(3) 状态不会在多次调用中存在
(4) 内存开销较小
(5) 不会产生并发性问题
3. Persession服务
(1) 和asp.net中的session相比较,wcf中的persession是客户端发起的,而asp.net中的session是服务端初始化的。这样wcf中的session效率要高。
(2) 为每个客户端/代理创建新的服务对象(默认)
(3) 吞吐量较少,内存开销增大
(4) 状态由服务实例维护
(5) 引发多线程客户端的并发
(6) 仅当绑定支持会话时,才能够支持会话
(7) 在服务契约上需要设置能够提供会话功能, SessionMode 枚举(Allow(默认), NotAllowed, Required),代码:
[ServiceContract(SessionMode=SessionMode.Required)]
public interface ICounterServiceSession
{
[OperationContract]
int IncrementCounter();
}
(8) SessionId信道的属性:
SessionServiceClient proxy=new SessionServiceClient ();
string s = proxy.InnerChannel.SessionId;
(9) 会话的生命周期默认是10分钟,如需要设置,代码如下:
<netTcpBinding>
<binding name="netTcp" receiveTimeout="00:10:00" />
</netTcpBinding>
还可以通过操作显式地控制生命周期,设置OperationContractAttribute的
IsInitiating(是否创建) 与IsTerminating(是否终止)属性,代码:
[ServiceContract(SessionMode = SessionMode.Required)]
public interface ISessionService
{
[OperationContract(IsInitiating = true, IsTerminating =false)]
void StartSession();
[OperationContract(IsInitiating = false, IsTerminating =false)]
void IncrementCounter();
[OperationContract(IsInitiating = false, IsTerminating =false)]
int GetCounter();
[OperationContract(IsInitiating = false, IsTerminating =false)]
string GetSessionId();
[OperationContract(IsInitiating = false, IsTerminating =true)]
void StopSession();
}
4. 单件模式single
(1) 为所有客户端的所有调用创建单一的服务对象
(2) 需要处理并发性问题。
(3) 通常要避免使用单件模型