예제 #1
0
        public void Notify(string @event, object @internal, NotifyData data, byte[] payload)
        {
            _logger.LogDebug($"notify() [event:{@event}]");

            if (_closed)
            {
                throw new InvalidStateException("PayloadChannel closed");
            }

            var notification = new { @event, @internal, data };
            var ns1Bytes     = Netstring.Encode(notification.ToCamelCaseJson());
            var ns2Bytes     = Netstring.Encode(payload);

            if (ns1Bytes.Length > NsMessageMaxLen)
            {
                throw new Exception("PayloadChannel notification too big");
            }
            if (ns2Bytes.Length > NsMessageMaxLen)
            {
                throw new Exception("PayloadChannel payload too big");
            }

            Loop.Default.Sync(() =>
            {
                try
                {
                    // This may throw if closed or remote side ended.
                    _producerSocket.Write(ns1Bytes, ex =>
                    {
                        if (ex != null)
                        {
                            _logger.LogError(ex, "_producerSocket.Write() | error");
                        }
                    });
                }
                catch (Exception ex)
                {
                    _logger.LogWarning($"notify() | sending notification failed: {ex}");
                    return;
                }

                try
                {
                    // This may throw if closed or remote side ended.
                    _producerSocket.Write(ns2Bytes, ex =>
                    {
                        if (ex != null)
                        {
                            _logger.LogError(ex, "_producerSocket.Write() | error");
                        }
                    });
                }
                catch (Exception ex)
                {
                    _logger.LogWarning($"notify() | sending notification failed: {ex}");
                    return;
                }
            });
        }
예제 #2
0
        private void ConsumerSocketOnData(ArraySegment <byte> data)
        {
            if (_recvBuffer == null)
            {
                _recvBuffer = data;
            }
            else
            {
                var newBuffer = new byte[_recvBuffer.Value.Count + data.Count];
                Array.Copy(_recvBuffer.Value.Array, _recvBuffer.Value.Offset, newBuffer, 0, _recvBuffer.Value.Count);
                Array.Copy(data.Array, data.Offset, newBuffer, _recvBuffer.Value.Count, data.Count);
                _recvBuffer = new ArraySegment <byte>(newBuffer);
            }

            if (_recvBuffer.Value.Count > NsPayloadMaxLen)
            {
                _logger.LogError("ConsumerSocketOnData() | receiving buffer is full, discarding all data into it");
                // Reset the buffer and exit.
                _recvBuffer = null;
                return;
            }

            //_logger.LogError($"ConsumerSocketOnData: {buffer}");
            var netstring = new Netstring(_recvBuffer.Value);

            try
            {
                var nsLength = 0;
                foreach (var payload in netstring)
                {
                    nsLength += payload.NetstringLength;
                    ProcessMessage(payload);
                }

                if (nsLength > 0)
                {
                    if (nsLength == _recvBuffer.Value.Count)
                    {
                        // Reset the buffer.
                        _recvBuffer = null;
                    }
                    else
                    {
                        _recvBuffer = new ArraySegment <byte>(_recvBuffer.Value.Array, _recvBuffer.Value.Offset + nsLength, _recvBuffer.Value.Count - nsLength);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"ConsumerSocketOnData() | invalid netstring data received from the worker process:{ex}");
                // Reset the buffer and exit.
                _recvBuffer = null;
                return;
            }
        }
예제 #3
0
        private void ConsumerSocketOnData(ArraySegment <byte> data)
        {
            // 数据回调通过单一线程进入,所有 _recvBuffer 是线程安全的。
            if (_recvBuffer == null)
            {
                _recvBuffer = data;
            }
            else
            {
                var newBuffer = new byte[_recvBuffer.Value.Count + data.Count];
                Array.Copy(_recvBuffer.Value.Array, _recvBuffer.Value.Offset, newBuffer, 0, _recvBuffer.Value.Count);
                Array.Copy(data.Array, data.Offset, newBuffer, _recvBuffer.Value.Count, data.Count);
                _recvBuffer = new ArraySegment <byte>(newBuffer);
            }

            if (_recvBuffer.Value.Count > NsPayloadMaxLen)
            {
                _logger.LogError("ConsumerSocketOnData() | Receiving buffer is full, discarding all data into it");
                // Reset the buffer and exit.
                _recvBuffer = null;
                return;
            }

            var netstring = new Netstring(_recvBuffer.Value);

            try
            {
                var nsLength = 0;
                foreach (var payload in netstring)
                {
                    nsLength += payload.NetstringLength;
                    var payloadString = Encoding.UTF8.GetString(payload.Data.Array, payload.Data.Offset, payload.Data.Count);
                    try
                    {
                        // We can receive JSON messages (Channel messages) or log strings.
                        switch (payloadString[0])
                        {
                        // 123 = '{' (a Channel JSON messsage).
                        case '{':
                            ThreadPool.QueueUserWorkItem(_ =>
                            {
                                ProcessMessage(payloadString);
                            });
                            break;

                        // 68 = 'D' (a debug log).
                        case 'D':
                            if (!payloadString.Contains("(trace)"))
                            {
                                _logger.LogDebug($"ConsumerSocketOnData() | [pid:{_processId}] { payloadString }");
                            }
                            break;

                        // 87 = 'W' (a warn log).
                        case 'W':
                            if (!payloadString.Contains("no suitable Producer"))
                            {
                                _logger.LogWarning($"ConsumerSocketOnData() | [pid:{_processId}] { payloadString }");
                            }
                            break;

                        // 69 = 'E' (an error log).
                        case 'E':
                            _logger.LogError($"ConsumerSocketOnData() | [pid:{_processId}] { payloadString }");
                            break;

                        // 88 = 'X' (a dump log).
                        case 'X':
                            _logger.LogDebug($"ConsumerSocketOnData() | [pid:{_processId}] { payloadString }");
                            break;

                        default:
                            _logger.LogWarning($"ConsumerSocketOnData() | Worker [pid:{_processId}] unexpected data, payload:{ payloadString }");
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"ConsumerSocketOnData() | Received invalid message from the worker process, payload: {payloadString}");
                        // Reset the buffer and exit.
                        _recvBuffer = null;
                        return;
                    }
                }

                if (nsLength > 0)
                {
                    if (nsLength == _recvBuffer.Value.Count)
                    {
                        // Reset the buffer.
                        _recvBuffer = null;
                    }
                    else
                    {
                        _recvBuffer = new ArraySegment <byte>(_recvBuffer.Value.Array, _recvBuffer.Value.Offset + nsLength, _recvBuffer.Value.Count - nsLength);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"ConsumerSocketOnData() | Invalid netstring data received from the worker process.");
                // Reset the buffer and exit.
                _recvBuffer = null;
                return;
            }
        }
예제 #4
0
        public Task <string?> RequestAsync(MethodId methodId, object? @internal = null, object?data = null)
        {
            if (_closed)
            {
                throw new InvalidStateException("Channel closed");
            }

            var method = methodId.GetEnumStringValue();
            var id     = InterlockedExtensions.Increment(ref _nextId);
            // NOTE: For testinng
            //_logger.LogDebug($"RequestAsync() | [Method:{method}, Id:{id}]");

            var requestMesssge = new RequestMessage
            {
                Id       = id,
                Method   = method,
                Internal = @internal,
                Data     = data,
            };
            var nsBytes = Netstring.Encode(requestMesssge.ToCamelCaseJson());

            if (nsBytes.Length > NsMessageMaxLen)
            {
                throw new Exception("Channel request too big");
            }

            var tcs = new TaskCompletionSource <string?>();

            var sent = new Sent
            {
                RequestMessage = requestMesssge,
                Resolve        = data =>
                {
                    if (!_sents.TryRemove(id, out var _))
                    {
                        tcs.TrySetException(new Exception($"Received response does not match any sent request [id:{id}]"));
                        return;
                    }
                    tcs.TrySetResult(data);
                },
                Reject = e =>
                {
                    if (!_sents.TryRemove(id, out var _))
                    {
                        tcs.TrySetException(new Exception($"Received response does not match any sent request [id:{id}]"));
                        return;
                    }
                    tcs.TrySetException(e);
                },
                Close = () =>
                {
                    tcs.TrySetException(new InvalidStateException("Channel closed"));
                },
            };

            if (!_sents.TryAdd(id, sent))
            {
                throw new Exception($"Error add sent request [id:{id}]");
            }

            tcs.WithTimeout(TimeSpan.FromSeconds(15 + (0.1 * _sents.Count)), () => _sents.TryRemove(id, out var _));

            Loop.Default.Sync(() =>
            {
                try
                {
                    // This may throw if closed or remote side ended.
                    _producerSocket.Write(nsBytes, ex =>
                    {
                        if (ex != null)
                        {
                            _logger.LogError(ex, "_producerSocket.Write() | Error");
                            sent.Reject(ex);
                        }
                    });
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "_producerSocket.Write() | Error");
                    sent.Reject(ex);
                }
            });

            return(tcs.Task);
        }
예제 #5
0
        private void ConsumerSocketOnData(ArraySegment <byte> data)
        {
            if (_recvBuffer == null)
            {
                _recvBuffer = data;
            }
            else
            {
                var newBuffer = new byte[_recvBuffer.Value.Count + data.Count];
                Array.Copy(_recvBuffer.Value.Array, _recvBuffer.Value.Offset, newBuffer, 0, _recvBuffer.Value.Count);
                Array.Copy(data.Array, data.Offset, newBuffer, _recvBuffer.Value.Count, data.Count);
                _recvBuffer = new ArraySegment <byte>(newBuffer);
            }

            if (_recvBuffer.Value.Count > NsPayloadMaxLen)
            {
                _logger.LogError("ConsumerSocketOnData() | receiving buffer is full, discarding all data into it");
                // Reset the buffer and exit.
                _recvBuffer = null;
                return;
            }

            //_logger.LogError($"ConsumerSocketOnData: {buffer}");
            var netstring = new Netstring(_recvBuffer.Value);

            try
            {
                var nsLength = 0;
                foreach (var payload in netstring)
                {
                    nsLength += payload.NetstringLength;
                    var payloadString = Encoding.UTF8.GetString(payload.Data.Array, payload.Data.Offset, payload.Data.Count);
                    try
                    {
                        // We can receive JSON messages (Channel messages) or log strings.
                        switch (payloadString[0])
                        {
                        // 123 = '{' (a Channel JSON messsage).
                        case '{':
                            ProcessMessage(payloadString);
                            break;

                        // 68 = 'D' (a debug log).
                        case 'D':
                            //if (!payloadString.Contains("(trace)"))
                            _logger.LogError($"ConsumerSocketOnData() | [pid:{_processId}] { payloadString }");
                            break;

                        // 87 = 'W' (a warn log).
                        case 'W':
                            _logger.LogWarning($"ConsumerSocketOnData() | [pid:{_processId}] { payloadString }");
                            break;

                        // 69 = 'E' (an error log).
                        case 'E':
                            _logger.LogError($"ConsumerSocketOnData() | [pid:{_processId}] { payloadString }");
                            break;

                        // 88 = 'X' (a dump log).
                        case 'X':
                            // eslint-disable-next-line no-console
                            _logger.LogDebug($"ConsumerSocketOnData() | [pid:{_processId}] { payloadString }");
                            break;

                        default:
                            // eslint-disable-next-line no-console
                            _logger.LogWarning($"ConsumerSocketOnData() | worker[pid:{_processId}] unexpected data:{ payloadString }");
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError($"ConsumerSocketOnData() | received invalid message from the worker process:{ex}\ndata: {payloadString}");
                        // Reset the buffer and exit.
                        _recvBuffer = null;
                        return;
                    }
                }

                if (nsLength > 0)
                {
                    if (nsLength == _recvBuffer.Value.Count)
                    {
                        // Reset the buffer.
                        _recvBuffer = null;
                    }
                    else
                    {
                        _recvBuffer = new ArraySegment <byte>(_recvBuffer.Value.Array, _recvBuffer.Value.Offset + nsLength, _recvBuffer.Value.Count - nsLength);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"ConsumerSocketOnData() | invalid netstring data received from the worker process:{ex}");
                // Reset the buffer and exit.
                _recvBuffer = null;
                return;
            }
        }
예제 #6
0
        public Task <string?> RequestAsync(MethodId methodId, object? @internal = null, object?data = null)
        {
            var method = methodId.GetEnumStringValue();
            var id     = _nextId < Int32.MaxValue ? ++_nextId : (_nextId = 1); // TODO: (alby)线程同步

            _logger.LogDebug($"RequestAsync() | [method:{method}, id:{id}]");

            if (_closed)
            {
                throw new InvalidStateException("Channel closed");
            }

            var requestMesssge = new RequestMessage
            {
                Id       = id,
                Method   = method,
                Internal = @internal,
                Data     = data,
            };
            var nsBytes = Netstring.Encode(requestMesssge.ToCamelCaseJson());

            if (nsBytes.Length > NsMessageMaxLen)
            {
                throw new Exception("Channel request too big");
            }

            var tcs = new TaskCompletionSource <string?>();

            var sent = new Sent
            {
                RequestMessage = requestMesssge,
                Close          = () =>
                {
                    if (!_sents.Remove(id))
                    {
                        return;
                    }
                    tcs.TrySetException(new InvalidStateException("Channel closed"));
                },
                Resolve = data =>
                {
                    if (!_sents.Remove(id))
                    {
                        return;
                    }
                    tcs.TrySetResult(data);
                },
                Reject = e =>
                {
                    if (!_sents.Remove(id))
                    {
                        return;
                    }
                    tcs.TrySetException(e);
                },
            };

            _sents.Add(id, sent);

            tcs.WithTimeout(TimeSpan.FromSeconds(15 + (0.1 * _sents.Count)), () => _sents.Remove(id));

            Loop.Default.Sync(() =>
            {
                try
                {
                    // This may throw if closed or remote side ended.
                    _producerSocket.Write(nsBytes, ex =>
                    {
                        if (ex != null)
                        {
                            _logger.LogError(ex, "_producerSocket.Write() | error");
                            tcs.TrySetException(ex);
                        }
                    });
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "_producerSocket.Write() | error");
                    tcs.TrySetException(ex);
                }
            });

            return(tcs.Task);
        }