diff --git a/src/Microsoft.Actions.Actors/ActionsHttpInteractor.cs b/src/Microsoft.Actions.Actors/ActionsHttpInteractor.cs index d72cac61..79288739 100644 --- a/src/Microsoft.Actions.Actors/ActionsHttpInteractor.cs +++ b/src/Microsoft.Actions.Actors/ActionsHttpInteractor.cs @@ -17,13 +17,14 @@ namespace Microsoft.Actions.Actors using System.Threading; using System.Threading.Tasks; using Microsoft.Actions.Actors.Communication; + using Microsoft.Actions.Actors.Runtime; using Newtonsoft.Json; /// /// Class to interact with actions runtime over http. /// internal class ActionsHttpInteractor : IActionsInteractor - { + { private const string ActionsEndpoint = Constants.ActionsDefaultEndpoint; private readonly string actionsPort = Constants.ActionsDefaultPort; private readonly HttpClientHandler innerHandler; diff --git a/src/Microsoft.Actions.Actors/Client/ActorProxyFactory.cs b/src/Microsoft.Actions.Actors/Client/ActorProxyFactory.cs index a28a4c41..069600e3 100644 --- a/src/Microsoft.Actions.Actors/Client/ActorProxyFactory.cs +++ b/src/Microsoft.Actions.Actors/Client/ActorProxyFactory.cs @@ -16,15 +16,19 @@ namespace Microsoft.Actions.Actors.Client /// internal class ActorProxyFactory : IActorProxyFactory { - // Used only for Remoting based communication - private static readonly ActorCommunicationClientFactory DefaultActorCommunicationClientFactory = new ActorCommunicationClientFactory(); + private readonly object thisLock; + private volatile IActorCommunicationClientFactory actorCommunicationClientFactory; + /// /// Initializes a new instance of the class. /// TODO: Accept Retry settings. /// public ActorProxyFactory() { + this.thisLock = new object(); + + this.actorCommunicationClientFactory = null; } /// @@ -35,7 +39,7 @@ namespace Microsoft.Actions.Actors.Client } /// - /// Create a proxy, this method is also used by ActorReference also to create proxy. + /// Create a proxy, this method is also sued by ACtorReference also to create proxy. /// /// Actor Id. /// Actor Interface Type. @@ -43,14 +47,49 @@ namespace Microsoft.Actions.Actors.Client /// Returns Actor Proxy. internal object CreateActorProxy(ActorId actorId, Type actorInterfaceType, string actorType) { - // TODO factory/client level settings - var actorCommunicationClient = DefaultActorCommunicationClientFactory.GetClient(actorId, actorType); + var factory = this.GetOrCreateActorCommunicationClientFactory(); + + // TODO factory level settings or method level parameter, default http + var actorCommunicationClient = new ActorCommunicationClient( + factory, + actorId, + actorType); var proxyGenerator = ActorCodeBuilder.GetOrCreateProxyGenerator(actorInterfaceType); return proxyGenerator.CreateActorProxy( actorCommunicationClient, - DefaultActorCommunicationClientFactory.GetRemotingMessageBodyFactory()); + factory.GetRemotingMessageBodyFactory()); } + + private IActorCommunicationClientFactory GetOrCreateActorCommunicationClientFactory() + { + if (this.actorCommunicationClientFactory != null) + { + return this.actorCommunicationClientFactory; + } + + lock (this.thisLock) + { + if (this.actorCommunicationClientFactory == null) + { + this.actorCommunicationClientFactory = this.CreateActorCommunicationClientFactory(); + } + } + + return this.actorCommunicationClientFactory; + } + + private IActorCommunicationClientFactory CreateActorCommunicationClientFactory() + { + // TODO factory settings + var factory = new ActorCommunicationClientFactory(); + if (factory == null) + { + throw new NotSupportedException("ClientFactory can't be null"); + } + + return factory; + } } } \ No newline at end of file diff --git a/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClient.cs b/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClient.cs index 2afc20bf..f1ec0dde 100644 --- a/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClient.cs +++ b/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClient.cs @@ -11,16 +11,21 @@ namespace Microsoft.Actions.Actors.Communication.Client internal class ActorCommunicationClient : IActorCommunicationClient { - private readonly IActionsInteractor actionsInteractor; + private readonly SemaphoreSlim communicationClientLock; + private readonly IActorCommunicationClientFactory communicationClientFactory; + private readonly IActorMessageBodyFactory messageBodyFactory; + private IActionsInteractor actionsInteractor; public ActorCommunicationClient( - IActionsInteractor actionsInteractor, + IActorCommunicationClientFactory remotingClientFactory, ActorId actorId, string actorType) { this.ActorId = actorId; this.ActorType = actorType; - this.actionsInteractor = actionsInteractor; + this.communicationClientFactory = remotingClientFactory; + this.communicationClientLock = new SemaphoreSlim(1); + this.messageBodyFactory = remotingClientFactory.GetRemotingMessageBodyFactory(); } /// @@ -41,7 +46,33 @@ namespace Microsoft.Actions.Actors.Communication.Client string methodName, CancellationToken cancellationToken) { - return await this.actionsInteractor.InvokeActorMethodWithRemotingAsync(remotingRequestMessage); + var client = await this.GetCommunicationClientAsync(cancellationToken); + return await client.InvokeActorMethodWithRemotingAsync(remotingRequestMessage); + } + + private async Task GetCommunicationClientAsync(CancellationToken cancellationToken) + { + IActionsInteractor client; + await this.communicationClientLock.WaitAsync(cancellationToken); + try + { + if (this.actionsInteractor == null) + { + this.actionsInteractor = await this.communicationClientFactory.GetClientAsync(); + } + + client = this.actionsInteractor; + } + finally + { + // Release the lock incase of exceptions from the GetClientAsync method, which can + // happen if there are non retriable exceptions in that method. Eg: There can be + // ServiceNotFoundException if the GetClientAsync client is called before the + // service creation completes. + this.communicationClientLock.Release(); + } + + return client; } } } diff --git a/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClientFactory.cs b/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClientFactory.cs index 0afccfa3..4c44516a 100644 --- a/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClientFactory.cs +++ b/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClientFactory.cs @@ -5,19 +5,24 @@ namespace Microsoft.Actions.Actors.Communication.Client { + using System; + using System.Globalization; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Actions.Actors.Runtime; + /// /// An that uses /// http protocol to create that communicate with actors. /// internal class ActorCommunicationClientFactory : IActorCommunicationClientFactory { - private static readonly IActionsInteractor ActionsInteractor = new ActionsHttpInteractor(); private readonly ActorMessageSerializersManager serializersManager; - private readonly IActorMessageBodyFactory actorMessageBodyFactory = null; + private readonly IActorMessageBodyFactory remotingMessageBodyFactory = null; /// /// Initializes a new instance of the class. - /// Constructs actor remoting communication client factory. + /// Constructs a fabric transport based service remoting client factory. /// /// IActorCommunicationMessageSerializationProvider provider. public ActorCommunicationClientFactory( @@ -25,7 +30,19 @@ namespace Microsoft.Actions.Actors.Communication.Client { // TODO Add settings, exception handlers, serialization provider this.serializersManager = IntializeSerializationManager(serializationProvider); - this.actorMessageBodyFactory = this.serializersManager.GetSerializationProvider().CreateMessageBodyFactory(); + this.remotingMessageBodyFactory = this.serializersManager.GetSerializationProvider().CreateMessageBodyFactory(); + } + + /// + /// Returns a client to communicate. + /// + /// + /// A Task that represents outstanding operation. The result of the Task is + /// the CommunicationClient() object. + /// + public async Task GetClientAsync() + { + return await this.CreateClientAsync(); } /// @@ -34,12 +51,7 @@ namespace Microsoft.Actions.Actors.Communication.Client /// A factory for creating the remoting message bodies. public IActorMessageBodyFactory GetRemotingMessageBodyFactory() { - return this.actorMessageBodyFactory; - } - - public ActorCommunicationClient GetClient(ActorId actorId, string actorType) - { - return new ActorCommunicationClient(ActionsInteractor, actorId, actorType); + return this.remotingMessageBodyFactory; } private static ActorMessageSerializersManager IntializeSerializationManager( @@ -50,5 +62,28 @@ namespace Microsoft.Actions.Actors.Communication.Client serializationProvider, new ActorMessageHeaderSerializer()); } + + /// + /// Creates a communication client for the given endpoint address. + /// + /// The communication client that was created. + private Task CreateClientAsync() + { + try + { + // TODO add retries and error handling - add CreateClientWithRetriesAsync version + var client = new ActionsHttpInteractor( + this.serializersManager); + return Task.FromResult((IActionsInteractor)client); + } + catch (Exception ex) + { + // TODO specific error handling + throw new Exception( + string.Format( + CultureInfo.CurrentCulture, + ex.ToString())); + } + } } } diff --git a/src/Microsoft.Actions.Actors/Communication/Client/IActorCommunicationClientFactory.cs b/src/Microsoft.Actions.Actors/Communication/Client/IActorCommunicationClientFactory.cs index 2923e03f..5f130d51 100644 --- a/src/Microsoft.Actions.Actors/Communication/Client/IActorCommunicationClientFactory.cs +++ b/src/Microsoft.Actions.Actors/Communication/Client/IActorCommunicationClientFactory.cs @@ -5,6 +5,10 @@ namespace Microsoft.Actions.Actors.Communication.Client { + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Actions.Actors.Runtime; + /// /// A factory for creating actions communication clients.. /// @@ -17,11 +21,12 @@ namespace Microsoft.Actions.Actors.Communication.Client IActorMessageBodyFactory GetRemotingMessageBodyFactory(); /// - /// Gets actor communication client. + /// Get a communication client. /// - /// Actor Id. - /// Actor Type. - /// A factory for creating the remoting message bodies. - ActorCommunicationClient GetClient(ActorId actorId, string actorType); + /// + /// A Task that represents outstanding operation. The result of the Task is + /// the CommunicationClient() object. + /// + Task GetClientAsync(); } }