OperationContext
DispatchRuntime
EndpointDispatcher
DispatcherBuilder
ChannelDispatcher
ChannelHandler
IChannelListener
MessageRpc
ServiceHost
ServiceThrottle
InstanceContextProvider
Other
Extensible interfaces
我突然有个想法,希望能够找出Server端的OperationContext是在何时何处被初始化,这样我就可以准确判断出合适能够访问OperationContext.Current。但是,随着研究的深入,我似乎发现的一条server端extension points的主线。本人写作水平有限,希望大家能看明白。J
当调用ServiceHost.Open的时候,会发生一系列初始化操作,包括DispatchRuntime、EndpointDispatcher等等。这些都是通过DispatcherBuilder来做到的。
首先看看DispatcherBuilder.InitializeServiceHost。这个方法处理了许多逻辑,主要包括:
1. 根据ListenUri对endpoint进行分组,相同ListenUri的endpoint会使用同一个ChannelDispatcher;
2. 处理IServiceBehavior、IContractBehavior、IEndpointBehavior以及IOperationBehavior;
3. 根据Channel Type创建IChannelListener;
4. 使用上一步创建的IChannelListener创建ChannelDispatcher;简单地说,ChannelDispatcher 对象将在特定 URI(称为侦听 URI)的 IChannelListener 与服务的实例相关联。 每个 ServiceHost 对象都可以拥有多个 ChannelDispatcher 对象,这多个对象每个都可与该服务的不同侦听器和侦听 URI 相关联。 当消息到达时,ChannelDispatcher查询每个相关的 EndpointDispatcher 对象以确定终结点是否可以接受消息,以及将该消息传递到可以接受消息的终结点。 当消息的目标地址与 AddressFilter 属性相匹配并且消息操作与 ContractFilter 属性相匹配时,EndpointDispatcher 对象负责处理来自ChannelDispatcher的消息。控制通道会话的生存期和行为的所有属性都可以在ChannelDispatcher对象上检查或修改。 除了 EndpointDispatcher 之外,这些属性还包括自定义 IChannelInitializer 对象、IChannelListener、ServiceHost 以及相关的 InstanceContext。
5. 为每一个endpoint创建EndpointDispatcher,并将其添加到对应的ChannelDispatcher中;
这些完成以后,IChannelListener开始监听注册进去的地址,关于这一步是如何做到的可以继续深入研究TransportManager。
在这里,我假设现在使用的netTcpBinding,那么对应的ChannelListener是TcpDuplexChannelListener。这个类里面使用了一个类,InputQueueChannelAcceptor,当有channel需要建立时,这个类被用来建立channel同时它的方法会被调用以便从下层取出数据。这样,server端就开始正常运行了。
前面提到了ChannelDispatcher的创建过程,但是对于它的作用只是简单的一个描述,下面开始详细介绍。
在ChannelDispatcher中,一个ListenHandler会被创建出来,这个类包含了前面提到的ChannelListener。当收到一个请求以后,ListenHandler.Dispatch会被调用;然后ChannelHandler会被创建,同时将其注册,从此它将替代ListenHandler来处理接收到的request。
ChannelHandler提供了一系列callback,当有request进来,某些方法会被调用用来处理。例如OnAsyncReceiveComplete,当一个request异步接收完成时,它会被调用。
下面是一个大体的调用过程。
OnAsyncReceiveComplete
AsyncMessagePump
HandleRequest
TryAcquireCallThrottle
ServiceThrottle.TryAcquireCallThrottle
TryRetrievingInstanceContext
GetSessionChannel
InitializeServiceChannel
InstanceContextProvider.GetExistingInstanceContext
TryAcquireThrottle
ServiceThrottle.AcquireInstanceContextAndDynamic
DispatchAndReleasePump
dispatchRuntime.GetOperation
Init OperationContext
Create MessageRpc
ImmutableDispatchRuntime.Dispatch
MessageRpc.Process
这就是一个request被处理的大体过程,每个列出来的方法或许都可以是一篇文章,这里不会详细介绍每个细节,而是集中在MessageRpc.Process以及内部的处理。剩下的内容或许以后再慢慢深入研究吧。
以下是MessageRpc.Process的大体过程。
ImmutableDispatchRuntime.ProcessMsg1
ImmutableDispatchRuntime.ProcessMsg11
Check Request Msg
ConcurrencyBehavior.IsConcurrency
AuthenticationBehavior.Authenticate
ServiceAuthenticationManager.Authenticate
AuthorizationBehavior.Authorize
ServiceAuthorizationManager.Authorize
InstanceBehavior.EnsureInstanceContext
AcquireDynamicInstanceContext
ImmutableDispatchRuntime.ProcessMsg2
AfterReceiveRequest
IDispatchMessageInspector.AfterReceiveRequest
ConcurrencyBehavior.LockInstance
ImmutableDispatchRuntime.ProcessMsg3
TransactionBehavior.ResolveTransaction
ImmutableDispatchRuntime.ProcessMsg31
ImmutableDispatchRuntime.ProcessMsg4
ThreadBehavior.BindThread
ImmutableDispatchRuntime.ProcessMsg41
ChannelHandler.Register
InstanceContext.GetServiceInstance
IInstanceProvider.GetInstance
ImmutableDispatchRuntime.ProcessMsg5
DispatchOperationRuntime.InvokeBegin
DispatchOperationRuntime.InitializeCallContextCore
ICallContextInitializer.BeforeInvoke
DispatchOperationRuntime.DeserializeInputs
IDispatchMessageFormatter.DeserializeRequest
DispatchOperationRuntime.InspectInputs
IParameterInspector.BeforeCall
DispatchOperationRuntime.ValidateMustUnderstand
SecurityImpersonationBehavior.StartImpersonation
SecurityImpersonationBehavior.SetCurrentThreadPrinciple
IOperationInvoker.Invoke (Sync)
IOperationInvoker.InvokeBegin (Async)
SecurityImpersonationBehavior.StopImpersonation
DispatchOperationRuntime.InspectOutputs (Sync)
IParameterInspector.AfterCall
DispatchOperationRuntime.SerializeOutputs (Sync)
IDispatchMessageFormatter.SerializeReply
DispatchOperationRuntime.UninitializeCallContext (Sync)
ICallContextInitializer.AfterInvoke
ImmutableDispatchRuntime.ProcessMsg6 (Async)
ThreadBehavior.BindEndThread
ImmutableDispatchRuntime.ProcessMsg7
DispatchOperationRuntime.InvokeEnd
Same like Sync way
ImmutableDispatchRuntime.ProcessMsg8 (如果是Sync,直接从5进入8)
ImmutableDispatchRuntime.PrepareReply
ImmutableDispatchRuntime.PrepareAndAddressReply
ImmutableDispatchRuntime.BeforeSendReply
IDispatchMessageInspectors.BeforeSendReply
ImmutableDispatchRuntime.ProcessMsg9
ImmutableDispatchRuntime.Reply
ImmutableDispatchRuntime.ProcessMsgCleanup
OperationContext.FireOperationComplete
InstanceBehavior.ReleaseServiceIntance
IInstanceProvider.ReleaseInstance
ConcurrencyBehavior.UnlockInstance
ServiceThrottle.DeactivateInstanceContext
一口气看下来,可能会觉得不那么好理解,其实每个ProcessMsg方法都是request处理过程中的一个步骤,下面会简单阐述每个步骤做了些什么。黄色背景代表了常见的扩展点。
ProcessMsg1,主要作用是对message进行验证和授权,另外它还会根据concurrency的设置对InstanceContext进行一些处理;
ProcessMsg2,除了提供了一个扩展点以外,它会根据concurrency来lock当前的InstanceContext;
ProcessMsg3,主要是用在transaction中,这里不做介绍;
ProcessMsg4,暂时没有明白BindThread的作用,在这一步它会获取service instance;
ProcessMsg5,真正开始处理request;
ProcessMsg6和ProcessMsg7,采用Async方式时才会调用;
ProcessMsg8,对reply进行处理;
ProcessMsg9,发出reply;
ProcessMsgCleanup,整个request处理完以后,释放一些资源,例如service instance,instance context。
这篇文章只涉及了request的处理,还有一些问题没有解决:
1. ChannelListener如何监听端口;
2. Request收到以后如何分发到不同的endpoint;
3. 在ProcessMsg4中,ThreadBehavior.BindThread的作用;
Update: 经过进一步研究,我发现BindThread实际上是在当前thread的SynchronizationContext上继续处理request message。同时,这个SynchronizationContext是从EndpointDispatcher.DispatchRuntime获得。通过这些,我认为service operation在被调用之前,同时只有一个thread会处理request;operation在哪个thread上调用,取决于它是sync还是async。
4. 最后,我们在config或通过其他方式进行配置时,是如何影响到后面的处理。