Ejemplo n.º 1
0
        public ReceiverChannel(
            IChannelInformation channelInformation,
            ITransport transport,
            IChannelCommandProcessor channelCommandProcessor,
            ICollection <IChannelModule <Message> > messageModules,
            ICollection <IChannelModule <Notification> > notificationModules,
            ICollection <IChannelModule <Command> > commandModules,
            Func <Exception, Task> exceptionHandler,
            int envelopeBufferSize,
            TimeSpan?consumeTimeout)
        {
            if (consumeTimeout != null && consumeTimeout.Value == default)
            {
                throw new ArgumentException("Invalid consume timeout", nameof(consumeTimeout));
            }

            _channelInformation      = channelInformation;
            _transport               = transport;
            _channelCommandProcessor = channelCommandProcessor;
            _messageModules          = messageModules;
            _notificationModules     = notificationModules;
            _commandModules          = commandModules;
            _exceptionHandler        = exceptionHandler;
            _consumeTimeout          = consumeTimeout;
            _sessionSemaphore        = new SemaphoreSlim(1);
            _startStopSemaphore      = new SemaphoreSlim(1);
            _consumerCts             = new CancellationTokenSource();
            _messageBuffer           = ChannelUtil.CreateForCapacity <Message>(envelopeBufferSize, false, true);
            _notificationBuffer      = ChannelUtil.CreateForCapacity <Notification>(envelopeBufferSize, false, true);
            _commandBuffer           = ChannelUtil.CreateForCapacity <Command>(envelopeBufferSize, false, true);
            _sessionBuffer           = ChannelUtil.CreateForCapacity <Session>(envelopeBufferSize, false, true);
        }
Ejemplo n.º 2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ClientChannel" /> class.
 /// </summary>
 /// <param name="transport">The transport to be used by the channel.</param>
 /// <param name="sendTimeout">The channel send timeout. Each send operation must be completed in the specified timeout or it will be canceled.</param>
 /// <param name="envelopeBufferSize">The number of envelopes to be buffered internally by the channel in the receive operations. If this limit is reached, the channel will not consume the transport until the buffer is consumed by the receive operations.</param>
 /// <param name="fillEnvelopeRecipients">if set to <c>true</c> [fill envelope recipients].</param>
 /// <param name="autoReplyPings">Indicates if the channel should reply automatically to ping request commands. In this case, the ping command are not returned by the ReceiveCommandAsync method.</param>
 /// <param name="autoNotifyReceipt">Indicates if the client should automatically send 'received' notifications for messages.</param>
 /// <param name="remotePingInterval">The interval to ping the remote party.</param>
 /// <param name="remoteIdleTimeout">The timeout to close the channel due to inactivity.</param>
 /// <param name="consumeTimeout">The channel consume timeout. Each envelope received from the transport must be consumed in the specified interval or it will cause the channel to be closed.</param>
 /// <param name="closeTimeout">The channel close timeout.</param>
 /// <param name="channelCommandProcessor">The channel command processor.</param>
 public ClientChannel(
     ITransport transport,
     TimeSpan sendTimeout,
     int envelopeBufferSize      = 1,
     bool fillEnvelopeRecipients = false,
     bool autoReplyPings         = true,
     bool autoNotifyReceipt      = false,
     TimeSpan?remotePingInterval = null,
     TimeSpan?remoteIdleTimeout  = null,
     TimeSpan?consumeTimeout     = null,
     TimeSpan?closeTimeout       = null,
     IChannelCommandProcessor channelCommandProcessor = null)
     : base(transport,
            sendTimeout,
            consumeTimeout,
            closeTimeout ?? sendTimeout,
            envelopeBufferSize,
            fillEnvelopeRecipients,
            autoReplyPings,
            remotePingInterval,
            remoteIdleTimeout,
            channelCommandProcessor)
 {
     if (autoNotifyReceipt)
     {
         MessageModules.Add(new NotifyReceiptChannelModule(this));
     }
 }
Ejemplo n.º 3
0
 /// <summary>
 /// Initializes a new instance of the <a href="ServerChannel" /> class.
 /// </summary>
 /// <param name="sessionId">The session identifier.</param>
 /// <param name="localNode">The server node.</param>
 /// <param name="transport">The transport to be used by the channel.</param>
 /// <param name="sendTimeout">The channel send timeout. Each send operation must be completed in the specified timeout or it will be canceled.</param>
 /// <param name="envelopeBufferSize">The number of envelopes to be buffered internally by the channel in the receive operations. If this limit is reached, the channel will not consume the transport until the buffer is consumed by the receive operations.</param>
 /// <param name="fillEnvelopeRecipients">if set to <c>true</c> [fill envelope recipients].</param>
 /// <param name="autoReplyPings">Indicates if the channel should reply automatically to ping request commands. In this case, the ping command are not returned by the ReceiveCommandAsync method.</param>
 /// <param name="remotePingInterval">The interval to ping the remote party.</param>
 /// <param name="remoteIdleTimeout">The timeout to close the channel due to inactivity.</param>
 /// <param name="consumeTimeout">The channel consume timeout. Each envelope received from the transport must be consumed in the specified interval or it will cause the channel to be closed.</param>
 /// <param name="closeTimeout">The channel close timeout.</param>
 /// <param name="channelCommandProcessor">The channel command processor.</param>
 public ServerChannel(
     string sessionId,
     Node localNode,
     ITransport transport,
     TimeSpan sendTimeout,
     int envelopeBufferSize      = 1,
     bool fillEnvelopeRecipients = false,
     bool autoReplyPings         = false,
     TimeSpan?remotePingInterval = null,
     TimeSpan?remoteIdleTimeout  = null,
     TimeSpan?consumeTimeout     = null,
     TimeSpan?closeTimeout       = null,
     IChannelCommandProcessor channelCommandProcessor = null)
     : base(transport,
            sendTimeout,
            consumeTimeout,
            closeTimeout ?? sendTimeout,
            envelopeBufferSize,
            fillEnvelopeRecipients,
            autoReplyPings,
            remotePingInterval,
            remoteIdleTimeout,
            channelCommandProcessor)
 {
     SessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId));
     LocalNode = localNode ?? throw new ArgumentNullException(nameof(localNode));
 }
Ejemplo n.º 4
0
 /// <summary>
 /// Initializes a new instance of the <a href="ServerChannel" /> class.
 /// </summary>
 /// <param name="sessionId">The session identifier.</param>
 /// <param name="serverNode">The server node.</param>
 /// <param name="transport">The transport to be used by the channel.</param>
 /// <param name="sendTimeout">The channel send timeout. Each send operation must be completed in the specified timeout or it will be canceled.</param>
 /// <param name="envelopeBufferSize">The number of envelopes to be buffered internally by the channel in the receive operations. If this limit is reached, the channel will not consume the transport until the buffer is consumed by the receive operations.</param>
 /// <param name="fillEnvelopeRecipients">if set to <c>true</c> [fill envelope recipients].</param>
 /// <param name="autoReplyPings">Indicates if the channel should reply automatically to ping request commands. In this case, the ping command are not returned by the ReceiveCommandAsync method.</param>
 /// <param name="remotePingInterval">The interval to ping the remote party.</param>
 /// <param name="remoteIdleTimeout">The timeout to close the channel due to inactivity.</param>
 /// <param name="consumeTimeout">The channel consume timeout. Each envelope received from the transport must be consumed in the specified interval or it will cause the channel to be closed.</param>
 /// <param name="closeTimeout">The channel close timeout.</param>
 /// <param name="channelCommandProcessor">The channel command processor.</param>
 public ServerChannel(
     string sessionId,
     Node serverNode,
     ITransport transport,
     TimeSpan sendTimeout,
     int envelopeBufferSize      = 1,
     bool fillEnvelopeRecipients = false,
     bool autoReplyPings         = false,
     TimeSpan?remotePingInterval = null,
     TimeSpan?remoteIdleTimeout  = null,
     TimeSpan?consumeTimeout     = null,
     TimeSpan?closeTimeout       = null,
     IChannelCommandProcessor channelCommandProcessor = null)
     : base(transport, sendTimeout, consumeTimeout, closeTimeout ?? sendTimeout, envelopeBufferSize, fillEnvelopeRecipients, autoReplyPings, remotePingInterval, remoteIdleTimeout, channelCommandProcessor)
 {
     LocalNode = serverNode;
     SessionId = sessionId;
 }
Ejemplo n.º 5
0
        /// <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);
        }
Ejemplo n.º 6
0
 /// <summary>
 /// Sets the channel command processor to be used.
 /// </summary>
 /// <param name="channelCommandProcessor">The channel command processor.</param>
 /// <returns></returns>
 public IClientChannelBuilder WithChannelCommandProcessor(IChannelCommandProcessor channelCommandProcessor)
 {
     ChannelCommandProcessor = channelCommandProcessor;
     return(this);
 }
Ejemplo n.º 7
0
        /// <summary>
        /// Creates a new instance of ChannelBase
        /// </summary>
        /// <param name="transport">The transport.</param>
        /// <param name="sendTimeout">The channel send timeout.</param>
        /// <param name="consumeTimeout">The channel consume timeout. Each envelope received from the transport must be consumed in the specified timeout or it will cause the channel to be closed.</param>
        /// <param name="closeTimeout">The channel close timeout.</param>
        /// <param name="envelopeBufferSize">Size of the envelope buffer.</param>
        /// <param name="fillEnvelopeRecipients">Indicates if the from and to properties of sent and received envelopes should be filled with the session information if not defined.</param>
        /// <param name="autoReplyPings">Indicates if the channel should reply automatically to ping request commands. In this case, the ping command are not returned by the ReceiveCommandAsync method.</param>
        /// <param name="remotePingInterval">The interval to ping the remote party.</param>
        /// <param name="remoteIdleTimeout">The timeout to close the channel due to inactivity.</param>
        /// <param name="channelCommandProcessor">The channel command processor.</param>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.ArgumentException">
        /// Invalid send timeout
        /// or
        /// Invalid consume timeout
        /// or
        /// Invalid close timeout
        /// </exception>
        /// <exception cref="System.ArgumentOutOfRangeException"></exception>
        protected ChannelBase(
            ITransport transport,
            TimeSpan sendTimeout,
            TimeSpan?consumeTimeout,
            TimeSpan closeTimeout,
            int envelopeBufferSize,
            bool fillEnvelopeRecipients,
            bool autoReplyPings,
            TimeSpan?remotePingInterval,
            TimeSpan?remoteIdleTimeout,
            IChannelCommandProcessor channelCommandProcessor)
        {
            if (sendTimeout == default(TimeSpan))
            {
                throw new ArgumentException("Invalid send timeout", nameof(sendTimeout));
            }
            if (consumeTimeout != null && consumeTimeout.Value == default(TimeSpan))
            {
                throw new ArgumentException("Invalid consume timeout", nameof(consumeTimeout));
            }
            if (closeTimeout == default(TimeSpan))
            {
                throw new ArgumentException("Invalid close timeout", nameof(closeTimeout));
            }
            if (envelopeBufferSize <= 0)
            {
                envelopeBufferSize = DataflowBlockOptions.Unbounded;
            }
            Transport          = transport ?? throw new ArgumentNullException(nameof(transport));
            Transport.Closing += Transport_Closing;
            _sendTimeout       = sendTimeout;
            _consumeTimeout    = consumeTimeout;
            _closeTimeout      = closeTimeout;
            _consumerCts       = new CancellationTokenSource();
            _syncRoot          = new object();
            var dataflowBlockOptions = new ExecutionDataflowBlockOptions()
            {
                BoundedCapacity        = envelopeBufferSize,
                MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded,
                EnsureOrdered          = false
            };

            _transportBuffer           = new BufferBlock <Envelope>(dataflowBlockOptions);
            _messageConsumerBlock      = new TransformBlock <Envelope, Message>(e => ConsumeMessageAsync(e), dataflowBlockOptions);
            _commandConsumerBlock      = new TransformBlock <Envelope, Command>(e => ConsumeCommandAsync(e), dataflowBlockOptions);
            _notificationConsumerBlock = new TransformBlock <Envelope, Notification>(e => ConsumeNotificationAsync(e), dataflowBlockOptions);
            _sessionConsumerBlock      = new TransformBlock <Envelope, Session>(e => ConsumeSession(e), dataflowBlockOptions);
            _messageBuffer             = new BufferBlock <Message>(dataflowBlockOptions);
            _commandBuffer             = new BufferBlock <Command>(dataflowBlockOptions);
            _notificationBuffer        = new BufferBlock <Notification>(dataflowBlockOptions);
            _sessionBuffer             = new BufferBlock <Session>(dataflowBlockOptions);
            _drainEnvelopeBlock        = DataflowBlock.NullTarget <Envelope>();
            _transportBuffer.LinkTo(_messageConsumerBlock, PropagateCompletionLinkOptions, e => e is Message);
            _transportBuffer.LinkTo(_commandConsumerBlock, PropagateCompletionLinkOptions, e => e is Command);
            _transportBuffer.LinkTo(_notificationConsumerBlock, PropagateCompletionLinkOptions, e => e is Notification);
            _transportBuffer.LinkTo(_sessionConsumerBlock, PropagateCompletionLinkOptions, e => e is Session);
            _messageConsumerBlock.LinkTo(_messageBuffer, PropagateCompletionLinkOptions, e => e != null);
            _messageConsumerBlock.LinkTo(_drainEnvelopeBlock, e => e == null);
            _commandConsumerBlock.LinkTo(_commandBuffer, PropagateCompletionLinkOptions, e => e != null);
            _commandConsumerBlock.LinkTo(_drainEnvelopeBlock, e => e == null);
            _notificationConsumerBlock.LinkTo(_notificationBuffer, PropagateCompletionLinkOptions, e => e != null);
            _notificationConsumerBlock.LinkTo(_drainEnvelopeBlock, e => e == null);
            _sessionConsumerBlock.LinkTo(_sessionBuffer, PropagateCompletionLinkOptions, e => e != null);
            _sessionConsumerBlock.LinkTo(_drainEnvelopeBlock, e => e == null);
            _channelCommandProcessor = channelCommandProcessor ?? new ChannelCommandProcessor();
            MessageModules           = new List <IChannelModule <Message> >();
            NotificationModules      = new List <IChannelModule <Notification> >();
            CommandModules           = new List <IChannelModule <Command> >();

            if (autoReplyPings)
            {
                CommandModules.Add(new ReplyPingChannelModule(this));
            }
            if (fillEnvelopeRecipients)
            {
                FillEnvelopeRecipientsChannelModule.CreateAndRegister(this);
            }
            if (remotePingInterval != null)
            {
                RemotePingChannelModule.CreateAndRegister(this, remotePingInterval.Value, remoteIdleTimeout);
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Creates a new instance of ChannelBase
        /// </summary>
        /// <param name="transport">The transport.</param>
        /// <param name="sendTimeout">The channel send timeout.</param>
        /// <param name="consumeTimeout">The channel consume timeout. Each envelope received from the transport must be consumed in the specified timeout or it will cause the channel to be closed.</param>
        /// <param name="closeTimeout">The channel close timeout.</param>
        /// <param name="envelopeBufferSize">Size of the envelope buffer.</param>
        /// <param name="fillEnvelopeRecipients">Indicates if the from and to properties of sent and received envelopes should be filled with the session information if not defined.</param>
        /// <param name="autoReplyPings">Indicates if the channel should reply automatically to ping request commands. In this case, the ping command are not returned by the ReceiveCommandAsync method.</param>
        /// <param name="remotePingInterval">The interval to ping the remote party.</param>
        /// <param name="remoteIdleTimeout">The timeout to close the channel due to inactivity.</param>
        /// <param name="channelCommandProcessor">The channel command processor.</param>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="System.ArgumentException">
        /// Invalid send timeout
        /// or
        /// Invalid consume timeout
        /// or
        /// Invalid close timeout
        /// </exception>
        /// <exception cref="System.ArgumentOutOfRangeException"></exception>
        protected ChannelBase(
            ITransport transport,
            TimeSpan sendTimeout,
            TimeSpan?consumeTimeout,
            TimeSpan closeTimeout,
            int envelopeBufferSize,
            bool fillEnvelopeRecipients,
            bool autoReplyPings,
            TimeSpan?remotePingInterval,
            TimeSpan?remoteIdleTimeout,
            IChannelCommandProcessor channelCommandProcessor)
        {
            if (closeTimeout == default)
            {
                throw new ArgumentException("Invalid close timeout", nameof(closeTimeout));
            }
            if (envelopeBufferSize <= 0)
            {
                envelopeBufferSize = -1;
            }
            Transport                = transport ?? throw new ArgumentNullException(nameof(transport));
            Transport.Closing       += Transport_Closing;
            _closeTimeout            = closeTimeout;
            _channelCommandProcessor = channelCommandProcessor ?? new ChannelCommandProcessor();

            // Modules
            MessageModules      = new List <IChannelModule <Message> >();
            NotificationModules = new List <IChannelModule <Notification> >();
            CommandModules      = new List <IChannelModule <Command> >();
            if (autoReplyPings)
            {
                CommandModules.Add(new ReplyPingChannelModule(this));
            }
            if (fillEnvelopeRecipients)
            {
                FillEnvelopeRecipientsChannelModule.CreateAndRegister(this);
            }
            if (remotePingInterval != null)
            {
                _remotePingChannelModule = RemotePingChannelModule.CreateAndRegister(this, remotePingInterval.Value, remoteIdleTimeout);
            }

            _receiverChannel = new ReceiverChannel(
                this,
                Transport,
                _channelCommandProcessor,
                MessageModules,
                NotificationModules,
                CommandModules,
                HandleConsumerExceptionAsync,
                envelopeBufferSize,
                consumeTimeout,
                _closeTimeout);

            _senderChannel = new SenderChannel(
                this,
                Transport,
                MessageModules,
                NotificationModules,
                CommandModules,
                HandleSenderExceptionAsync,
                envelopeBufferSize,
                sendTimeout,
                _closeTimeout);
        }