/// <summary> /// Returns an instance of transport suitable for a call. The implementation may return existing transport or allocate a new instance. /// The call is thread-safe /// </summary> protected virtual ClientTransport AcquireClientTransportForCall(ClientEndPoint client, RequestMsg request) { var transport = TryGetExistingAcquiredTransportPerRemoteNode(client.Node); if (transport != null) { return(transport); } //otherwise we need to create a new transport if (m_ClientTransportMaxCount > 0) { var alock = m_ClientTransportAllocatorLocks[(client.Node.GetHashCode() & CoreConsts.ABS_HASH_MASK) % m_ClientTransportAllocatorLocks.Length]; lock (alock) { transport = TryGetExistingAcquiredTransportPerRemoteNode(client.Node); if (transport != null) { return(transport); } return(makeAndLaunchClientTransportForCall(client, request)); } } return(makeAndLaunchClientTransportForCall(client, request)); }
/// <summary> /// Dispatches a call allocating new or re-using existing transport if needed. The strategy depends on particular Binding implementation. /// This call is thread-safe /// </summary> public CallSlot DispatchCall(ClientEndPoint client, RequestMsg request) { CheckRunningState(); //Binding level inspectors foreach (var insp in m_ClientMsgInspectors.OrderedValues) { request = insp.ClientDispatchCall(client, request); } //Glue level inspectors request = Glue.ClientDispatchingRequest(client, request); //============================================================== CallOptions options; options.DispatchTimeoutMs = client.DispatchTimeoutMs; options.TimeoutMs = client.TimeoutMs; var reserve = client.ReserveTransport; var transport = client.m_ReservedTransport ?? AcquireClientTransportForCall(client, request); CallSlot slot = null; try { if (reserve) { client.m_ReservedTransport = transport; } slot = transport.SendRequest(client, request, options); // If execution pauses here and server sends response BEFORE call //then Glue.Deliver response will retry to fimnd the missing call slot for some fixed interval if (slot != null) { Glue.ClientDispatchedRequest(client, request, slot); } } finally { if (!reserve) { ReleaseClientTransportAfterCall(transport); } } return(slot); }
private ClientTransport makeAndLaunchClientTransportForCall(ClientEndPoint client, RequestMsg request) { var transport = MakeNewClientTransport(client); try { ConfigureAndStartNewClientTransport(transport); } catch { transport.Dispose(); throw; } return(transport); }
/// <summary> /// INTERNAL METHOD. Developers do not call! /// This constructor is used by an Async binding that delivers response after call slot was created /// </summary> public CallSlot(ClientEndPoint client, ClientTransport clientTransport, RequestMsg request, CallStatus status, int timeoutMs = 0) { m_Client = client; m_ClientTransport = clientTransport; m_RequestID = request.RequestID; m_CallStatus = status; m_OneWay = request.OneWay; m_StartTime = DateTime.UtcNow; m_TimeoutMs = timeoutMs > 0 ? timeoutMs : DEFAULT_TIMEOUT_MS; if (!m_OneWay && client.Binding.MeasureStatTimes) { m_StatStartTimeTicks = client.Binding.StatTimeTicks; m_StatRoundtripTimeKey = client.Binding.GetClientCallStatTimeKey(client, request); } }
/// <summary> /// INTERNAL METHOD. Developers do not call! /// This constructor is used by a synchronous binding that delivers response right after sending it. /// ONLY for OneWayCall = false /// </summary> public CallSlot(ClientEndPoint client, ClientTransport clientTransport, long actualStartTimeTicks, DateTime actualStartTimeUtc, RequestMsg request, ResponseMsg response, int timeoutMs) { m_Client = client; m_ClientTransport = clientTransport; m_RequestID = request.RequestID; m_OneWay = false; m_StartTime = actualStartTimeUtc; m_TimeoutMs = timeoutMs > 0 ? timeoutMs : DEFAULT_TIMEOUT_MS; if (client.Binding.MeasureStatTimes) { m_StatStartTimeTicks = actualStartTimeTicks; m_StatRoundtripTimeKey = client.Binding.GetClientCallStatTimeKey(client, request); } m_ResponseMsg = response; m_CallStatus = m_ResponseMsg.OK ? CallStatus.ResponseOK : CallStatus.ResponseError; var remoteInstance = response.RemoteInstance; if (remoteInstance.HasValue) { m_Client.__setRemoteInstance(remoteInstance.Value); } if (m_StatRoundtripTimeKey != null && m_Client.Binding.MeasureStatTimes) { m_StatRoundtripEndTimeTicks = m_Client.Binding.StatTimeTicks; m_ClientTransport.stat_Time(m_StatRoundtripTimeKey, m_StatRoundtripEndTimeTicks - m_StatStartTimeTicks); } }
/// <summary> /// Factory method - Override to make an instance of a new client transport suitable for particular binding type /// </summary> protected abstract ClientTransport MakeNewClientTransport(ClientEndPoint client);
/// <summary> /// Extracts necessary information from client:request pair that characterizes the particular call /// for the purpose of call timing /// </summary> public virtual string GetClientCallStatTimeKey(ClientEndPoint client, RequestMsg request) { //todo In future may add property StatCallTimesLevel {Transport, Contract, Method} return(client.Node.ToString() + " -> " + request.Contract.FullName + '.' + request.MethodName + "()"); }
public void ClientDispatchedRequest(ClientEndPoint client, RequestMsg request, CallSlot callSlot) { }
public RequestMsg ClientDispatchingRequest(ClientEndPoint client, RequestMsg request) { return(request); }
/// <summary> /// Override to send a client request into remote endpoint. /// This is a blocking call for bindings that are OperationFlow.Synchronous and /// result arrives immediately into CallSlot. /// </summary> protected abstract CallSlot DoSendRequest(ClientEndPoint endpoint, RequestMsg request, CallOptions options);
/// <summary> /// Sends a client request into remote endpoint. /// This is a blocking call for bindings that are OperationFlow.Synchronous and /// result arrives immediately into CallSlot. /// </summary> public CallSlot SendRequest(ClientEndPoint endpoint, RequestMsg request, CallOptions options) { CheckRunningState(); return(DoSendRequest(endpoint, request, options)); }