public OnDemandClientChannelFactory(IEstablishedClientChannelBuilder channelBuilder) { if (channelBuilder == null) { throw new ArgumentNullException(nameof(channelBuilder)); } ChannelBuilder = channelBuilder; }
private IOnDemandClientChannel CreateOnDemandClientChannel(IEstablishedClientChannelBuilder establishedClientChannelBuilder) { IOnDemandClientChannel onDemandClientChannel; // Avoid the overhead of the MultiplexerClientChannel for a single connection if (ChannelCount == 1) { onDemandClientChannel = new OnDemandClientChannel(establishedClientChannelBuilder); } else { onDemandClientChannel = new MultiplexerClientChannel(establishedClientChannelBuilder, ChannelCount); } return(onDemandClientChannel); }
/// <summary> /// Initializes a new instance of the <see cref="OnDemandClientChannel"/> class. /// </summary> /// <param name="builder">The builder.</param> /// <exception cref="System.ArgumentNullException"></exception> public OnDemandClientChannel(IEstablishedClientChannelBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (builder.ChannelBuilder == null) { throw new ArgumentException("The specified builder is invalid", nameof(builder)); } _builder = builder; _semaphore = new SemaphoreSlim(1, 1); ChannelCreatedHandlers = new List <Func <ChannelInformation, Task> >(); ChannelDiscardedHandlers = new List <Func <ChannelInformation, Task> >(); ChannelCreationFailedHandlers = new List <Func <FailedChannelInformation, Task <bool> > >(); ChannelOperationFailedHandlers = new List <Func <FailedChannelInformation, Task <bool> > >(); }
/// <summary> /// Initializes a new instance of the <see cref="MultiplexerClientChannel"/> class. /// </summary> /// <param name="builder">The channel builder.</param> /// <param name="count">The number of channels to create.</param> /// <param name="inputBufferSize">The input buffer bounded capacity.</param> /// <param name="outputBufferSize">The output buffer bounded capacity.</param> /// <param name="channelCommandProcessor">The workflow for processing commands.</param> /// <exception cref="System.ArgumentNullException"></exception> /// <exception cref="System.ArgumentOutOfRangeException"></exception> public MultiplexerClientChannel( IEstablishedClientChannelBuilder builder, int count = 5, int inputBufferSize = 1, int outputBufferSize = 1, IChannelCommandProcessor channelCommandProcessor = null) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (count <= 0) { throw new ArgumentOutOfRangeException(nameof(count)); } // Create observable collections to allow synchronization var channelCreatedHandlers = new ObservableCollection <Func <ChannelInformation, Task> >(); var channelDiscardedHandlers = new ObservableCollection <Func <ChannelInformation, Task> >(); var channelCreationFailedHandlers = new ObservableCollection <Func <FailedChannelInformation, Task <bool> > >(); var channelOperationFailedHandlers = new ObservableCollection <Func <FailedChannelInformation, Task <bool> > >(); ChannelCreatedHandlers = channelCreatedHandlers; ChannelDiscardedHandlers = channelDiscardedHandlers; ChannelCreationFailedHandlers = channelCreationFailedHandlers; ChannelOperationFailedHandlers = channelOperationFailedHandlers; // Global input buffers var inputOptions = new ExecutionDataflowBlockOptions() { BoundedCapacity = inputBufferSize, MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded, EnsureOrdered = false }; _inputMessageBufferBlock = new BufferBlock <Message>(inputOptions); _inputNotificationBufferBlock = new BufferBlock <Notification>(inputOptions); _channelCommandProcessor = channelCommandProcessor ?? new ChannelCommandProcessor(); // Uses the same channel command processor for all instances // to avoid problems with commands responses being received on different channels. builder.ChannelBuilder.WithChannelCommandProcessor(_channelCommandProcessor); _processCommandTransformBlock = new TransformBlock <Command, Command>(c => { if (_channelCommandProcessor.TrySubmitCommandResult(c)) { return(null); } return(c); }, inputOptions); _inputCommandBufferBlock = new BufferBlock <Command>(inputOptions); _processCommandTransformBlock.LinkTo(_inputCommandBufferBlock, c => c != null); _processCommandTransformBlock.LinkTo(DataflowBlock.NullTarget <Command>(), c => c == null); // The global output buffer _outputBufferBlock = new BufferBlock <BufferedEnvelope>(new DataflowBlockOptions() { BoundedCapacity = outputBufferSize, EnsureOrdered = false }); // An output action block per channel _outputActionBlocks = new ActionBlock <BufferedEnvelope> [count]; _channels = new IOnDemandClientChannel[count]; _listeners = new IChannelListener[count]; for (var i = 0; i < _channels.Length; i++) { // Add an instance suffix to the builder var currentBuilder = builder .Copy() .WithInstance($"{builder.Instance}-{i+1}"); var channel = new OnDemandClientChannel(currentBuilder); // Synchronize the handlers AttachCollection(channelCreatedHandlers, channel.ChannelCreatedHandlers); AttachCollection(channelDiscardedHandlers, channel.ChannelDiscardedHandlers); AttachCollection(channelCreationFailedHandlers, channel.ChannelCreationFailedHandlers); AttachCollection(channelOperationFailedHandlers, channel.ChannelOperationFailedHandlers); // Setup the listener for the channel _listeners[i] = new DataflowChannelListener( _inputMessageBufferBlock, _inputNotificationBufferBlock, _processCommandTransformBlock); // Create a single bounded action block for each channel _outputActionBlocks[i] = new ActionBlock <BufferedEnvelope>( async e => await SendToChannelAsync(channel, e.Envelope).ConfigureAwait(false), new ExecutionDataflowBlockOptions() { BoundedCapacity = 1, MaxDegreeOfParallelism = 1 }); var channelId = i; _outputBufferBlock.LinkTo( _outputActionBlocks[i], new DataflowLinkOptions() { PropagateCompletion = true }, e => e.ChannelId == channelId); _channels[i] = channel; } _semaphore = new SemaphoreSlim(1, 1); }
private void SubstituteEstablishedClientChannelBuilder() { EstablishedClientChannelBuilder = Substitute.For <IEstablishedClientChannelBuilder>(); }