private async Task ListeningThread(string host, Func <ICommand, Task> handlerAsync, Func <ICommand, Task> handlerAwaitAsync)
            {
                canceller = new CancellationTokenSource();

retry:

                IConsumer <string, byte[]> consumer = null;

                try
                {
                    await KafkaCommon.AssureTopic(host, topic);

                    var consumerConfig = new ConsumerConfig();
                    consumerConfig.BootstrapServers = host;
                    consumerConfig.GroupId          = topic;
                    consumerConfig.EnableAutoCommit = false;

                    consumer = new ConsumerBuilder <string, byte[]>(consumerConfig).Build();
                    consumer.Subscribe(topic);

                    for (; ;)
                    {
                        Exception error         = null;
                        bool      awaitResponse = false;
                        string    ackTopic      = null;
                        string    ackKey        = null;
                        try
                        {
                            if (canceller.Token.IsCancellationRequested)
                            {
                                break;
                            }

                            var consumerResult = consumer.Consume(canceller.Token);
                            consumer.Commit(consumerResult);

                            awaitResponse = consumerResult.Message.Key == KafkaCommon.MessageWithAckKey;
                            if (awaitResponse)
                            {
                                ackTopic = Encoding.UTF8.GetString(consumerResult.Message.Headers.GetLastBytes(KafkaCommon.AckTopicHeader));
                                ackKey   = Encoding.UTF8.GetString(consumerResult.Message.Headers.GetLastBytes(KafkaCommon.AckKeyHeader));
                            }

                            if (consumerResult.Message.Key == KafkaCommon.MessageKey || awaitResponse)
                            {
                                var stopwatch = new Stopwatch();
                                stopwatch.Start();

                                byte[] body = consumerResult.Message.Value;
                                if (encryptionKey != null)
                                {
                                    body = SymmetricEncryptor.Decrypt(encryptionAlgorithm, encryptionKey, body);
                                }

                                var message = KafkaCommon.Deserialize <KafkaCommandMessage>(body);

                                if (message.Claims != null)
                                {
                                    var claimsIdentity = new ClaimsIdentity(message.Claims.Select(x => new Claim(x[0], x[1])), "CQRS");
                                    Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentity);
                                }

                                if (awaitResponse)
                                {
                                    await handlerAwaitAsync(message.Message);
                                }
                                else
                                {
                                    await handlerAsync(message.Message);
                                }

                                stopwatch.Stop();
                                _ = Log.TraceAsync($"Received Await: {topic}  {stopwatch.ElapsedMilliseconds}");
                            }
                            else
                            {
                                _ = Log.ErrorAsync($"{nameof(KafkaServer)} unrecognized message key {consumerResult.Message.Key}");
                            }
                        }
                        catch (TaskCanceledException)
                        {
                            break;
                        }
                        catch (Exception ex)
                        {
                            _     = Log.TraceAsync($"Error: Received Await: {topic}");
                            _     = Log.ErrorAsync(ex);
                            error = ex;
                        }
                        if (awaitResponse)
                        {
                            IProducer <string, byte[]> producer = null;
                            try
                            {
                                var producerConfig = new ProducerConfig();
                                producerConfig.BootstrapServers = host;
                                producerConfig.ClientId         = clientID;

                                producer = new ProducerBuilder <string, byte[]>(producerConfig).Build();

                                var ack = new Acknowledgement()
                                {
                                    Success      = error == null,
                                    ErrorMessage = error?.Message
                                };
                                var body = KafkaCommon.Serialize(ack);
                                if (encryptionKey != null)
                                {
                                    body = SymmetricEncryptor.Encrypt(encryptionAlgorithm, encryptionKey, body);
                                }

                                await producer.ProduceAsync(ackTopic, new Message <string, byte[]>() { Key = ackKey, Value = body });
                            }

                            catch (Exception ex)
                            {
                                _ = Log.ErrorAsync(ex);
                            }
                            finally
                            {
                                if (producer != null)
                                {
                                    producer.Dispose();
                                }
                            }
                        }
                    }

                    consumer.Unsubscribe();
                }
                catch (Exception ex)
                {
                    _ = Log.ErrorAsync(ex);

                    if (consumer != null)
                    {
                        consumer.Dispose();
                    }
                    consumer = null;

                    if (!canceller.IsCancellationRequested)
                    {
                        await Task.Delay(retryDelay);

                        goto retry;
                    }
                }
                canceller.Dispose();
                canceller = null;
                if (consumer != null)
                {
                    consumer.Dispose();
                }
                IsOpen = false;
            }
Esempio n. 2
0
        private async Task SendAsync(ICommand command, bool requireAcknowledgement)
        {
            if (requireAcknowledgement)
            {
                if (!listenerStarted)
                {
                    await listenerStartedLock.WaitAsync();

                    if (!listenerStarted)
                    {
                        _ = Task.Run(AckListeningThread);
                        listenerStarted = true;
                    }
                    listenerStartedLock.Release();
                }
            }

            var topic = command.GetType().GetNiceName();

            string[][] claims = null;
            if (Thread.CurrentPrincipal is ClaimsPrincipal principal)
            {
                claims = principal.Claims.Select(x => new string[] { x.Type, x.Value }).ToArray();
            }

            var message = new KafkaCommandMessage()
            {
                Message = command,
                Claims  = claims
            };

            var body = KafkaCommon.Serialize(message);

            if (encryptionKey != null)
            {
                body = SymmetricEncryptor.Encrypt(encryptionAlgorithm, encryptionKey, body);
            }

            if (requireAcknowledgement)
            {
                var ackKey = Guid.NewGuid().ToString();

                var headers = new Headers();
                headers.Add(new Header(KafkaCommon.AckTopicHeader, Encoding.UTF8.GetBytes(ackTopic)));
                headers.Add(new Header(KafkaCommon.AckKeyHeader, Encoding.UTF8.GetBytes(ackKey)));
                var key = KafkaCommon.MessageWithAckKey;

                var waiter = new SemaphoreSlim(0, 1);

                try
                {
                    Acknowledgement ack = null;
                    ackCallbacks.TryAdd(ackKey, (ackFromCallback) =>
                    {
                        ack = ackFromCallback;
                        waiter.Release();
                    });

                    var producerResult = await producer.ProduceAsync(topic, new Message <string, byte[]> {
                        Headers = headers, Key = key, Value = body
                    });

                    if (producerResult.Status != PersistenceStatus.Persisted)
                    {
                        throw new Exception($"{nameof(KafkaClient)} failed: {producerResult.Status}");
                    }

                    await waiter.WaitAsync();

                    if (!ack.Success)
                    {
                        throw new AcknowledgementException(ack, topic);
                    }
                }
                finally
                {
                    if (waiter != null)
                    {
                        waiter.Dispose();
                    }
                }
            }
            else
            {
                var key = KafkaCommon.MessageKey;

                var producerResult = await producer.ProduceAsync(topic, new Message <string, byte[]> {
                    Key = key, Value = body
                });

                if (producerResult.Status != PersistenceStatus.Persisted)
                {
                    throw new Exception($"{nameof(KafkaClient)} failed: {producerResult.Status}");
                }
            }
        }