public async Task WriteAsync(string topicName, string servicePath, HttpContext httpContext) { var gateConsumer = _gateTemporaryConsumers.GetOrAdd(topicName, _ => { var serviceName = Guid.NewGuid().ToString(); return(_gateFactory.CreateConsumer <GateRequest, GateResponse>(serviceName, topicName, async(genericContext, cancellationToken) => { if (servicePath != genericContext.Message.ServicePath) { return; } var message = Encoding.UTF8.GetString(genericContext.Message.Content); var messageParts = message.Split('\n'); if (_consumerContexts.TryGetValue(topicName, out var topicContexts)) { foreach (var context in topicContexts.Values) { if (!string.IsNullOrEmpty(servicePath)) { await context.Response.WriteAsync($"event: {servicePath}\n", cancellationToken); } foreach (var messagePart in messageParts) { await context.Response.WriteAsync($"data: {messagePart}\n", cancellationToken); } await context.Response.WriteAsync($"\n", cancellationToken); await context.Response.Body.FlushAsync(cancellationToken); } } }, () => { }, true, 1)); });
public async Task Subscribe_Test() { var gateConsumer = A.Fake <IGateConsumer>(); A.CallTo(() => _gateFactory.CreateConsumer("service-name", "topic-name", A <Func <GateContext, CancellationToken, Task> > .Ignored, A <Action> .Ignored, false, 10)).Returns(gateConsumer); var consumer = await _toTest.SubscribeConsumerAsync("service-name", "topic-name", new Uri("http://test-host"), 10, 20, 30); Assert.AreEqual(new Uri("http://test-host"), consumer.Endpoint); Assert.AreEqual(gateConsumer, consumer.GateConsumer); Assert.AreEqual(20, consumer.RestartDelayInSeconds); Assert.AreEqual(30, consumer.RestartCount); }
public static IGateConsumer CreateConsumer <TMessage, TReply>( this IGateFactory brokerFactory, string serviceName, string topicName, Func <GateContext <TMessage, TReply>, CancellationToken, Task> messageHandler, Action abortedHandler, bool autoDelete, int maxConcurrentMessages) { serviceName = serviceName ?? throw new ArgumentNullException(nameof(serviceName)); topicName = topicName ?? throw new ArgumentNullException(nameof(topicName)); return(brokerFactory.CreateConsumer(serviceName, topicName, async(context, cancellationToken) => { var genericContext = new GateContext <TMessage, TReply>( message: Serializer.Deserialize <TMessage>(context.Message), isRequest: context.IsRequest ); await messageHandler(genericContext, cancellationToken); if (genericContext.IsRequest) { context.Reply = Serializer.Serialize(genericContext.Reply); } }, abortedHandler, autoDelete, maxConcurrentMessages)); }
public async Task <ProxyConsumer> SubscribeConsumerAsync( string serviceName, string topicName, Uri endpoint, int maxConcurrentMessages, int restartDelayInSeconds, int restartCount, CancellationToken cancellationToken = default) { serviceName = serviceName ?? throw new ArgumentNullException(nameof(serviceName)); topicName = topicName ?? throw new ArgumentNullException(nameof(topicName)); endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint)); if (!endpoint.IsAbsoluteUri) { throw new ArgumentException("Endpoint uri must be absolute", nameof(endpoint)); } var consumerId = $"{serviceName}_{topicName}_{endpoint}".ToSha256(); var proxyConsumer = new ProxyConsumer(); if (!_proxyConsumers.TryAdd(consumerId, proxyConsumer)) { throw new InvalidOperationException("Consumer already exists"); } try { proxyConsumer.Id = consumerId; proxyConsumer.Endpoint = endpoint; proxyConsumer.AbortTime = null; proxyConsumer.RestartDelayInSeconds = restartDelayInSeconds; proxyConsumer.RestartCount = restartCount; proxyConsumer.HttpClient = _httpClientFactory.CreateClient(); proxyConsumer.UnsubscribeCompletionSource = null; proxyConsumer.GateConsumer = _gateFactory.CreateConsumer <GateRequest, GateResponse>(serviceName, topicName, async(genericContext, ct) => { var httpRequestMessage = genericContext.Message.ToHttpRequestMessage(endpoint); var httpResponseMessage = await proxyConsumer.HttpClient.SendAsync(httpRequestMessage, ct); if (!httpResponseMessage.IsSuccessStatusCode && !genericContext.IsRequest) { if (!_fallbackServices.Any()) { httpResponseMessage.EnsureSuccessStatusCode(); } var fallbackExceptions = new List <Exception>(); foreach (var fallbackService in _fallbackServices) { try { await fallbackService.SendAsync(serviceName, topicName, genericContext.Message, ct); break; } catch (Exception ex) { fallbackExceptions.Add(ex); } } if (fallbackExceptions.Any()) { throw new AggregateException("Can not send message to service or fallbacks", fallbackExceptions); } } if (genericContext.IsRequest) { genericContext.Reply = await httpResponseMessage.ToGateResponseAsync(); } }, () => { proxyConsumer.AbortTime = DateTime.Now; if (proxyConsumer.UnsubscribeCompletionSource != null) { proxyConsumer.UnsubscribeCompletionSource.TrySetResult(true); } else { proxyConsumer.RestartCancellationTokenSource = new CancellationTokenSource(); proxyConsumer.RestartTask = RestartHandler(consumerId); } }, false, maxConcurrentMessages); await proxyConsumer.GateConsumer.StartConsumeAsync(cancellationToken); return(proxyConsumer); } catch (Exception ex) { _logger.LogError(ex, "Error starting consumer"); _proxyConsumers.TryRemove(consumerId, out _); throw; } }