private async Task SendAsync(IEvent @event) { var topic = @event.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 KafkaEventMessage() { Message = @event, Claims = claims }; var body = KafkaCommon.Serialize(message); if (encryptionKey != null) { body = SymmetricEncryptor.Encrypt(encryptionAlgorithm, encryptionKey, body); } var producerResult = await producer.ProduceAsync(topic, new Message <string, byte[]> { Key = KafkaCommon.MessageKey, Value = body }); if (producerResult.Status != PersistenceStatus.Persisted) { throw new Exception($"{nameof(KafkaClient)} failed: {producerResult.Status}"); } }
public async Task ListeningThread(string host, Func <IEvent, Task> handlerAsync) { canceller = new CancellationTokenSource(); retry: IConsumer <string, byte[]> consumer = null; try { await KafkaCommon.AssureTopic(host, topic); var consumerConfig = new ConsumerConfig(); consumerConfig.BootstrapServers = host; consumerConfig.GroupId = Guid.NewGuid().ToString(); consumerConfig.EnableAutoCommit = false; consumer = new ConsumerBuilder <string, byte[]>(consumerConfig).Build(); consumer.Subscribe(topic); for (; ;) { try { if (canceller.Token.IsCancellationRequested) { break; } var consumerResult = consumer.Consume(canceller.Token); consumer.Commit(consumerResult); if (consumerResult.Message.Key == KafkaCommon.MessageKey) { var stopwatch = new Stopwatch(); stopwatch.Start(); byte[] body = consumerResult.Message.Value; if (encryptionKey != null) { body = SymmetricEncryptor.Decrypt(encryptionAlgorithm, encryptionKey, body); } var message = KafkaCommon.Deserialize <KafkaEventMessage>(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); } 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); } } 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; }
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; }
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}"); } } }
private async Task AckListeningThread() { await KafkaCommon.AssureTopic(host, ackTopic); var consumerConfig = new ConsumerConfig(); consumerConfig.BootstrapServers = host; consumerConfig.GroupId = ackTopic; consumerConfig.EnableAutoCommit = false; IConsumer <string, byte[]> consumer = null; try { consumer = new ConsumerBuilder <string, byte[]>(consumerConfig).Build(); consumer.Subscribe(ackTopic); for (; ;) { try { if (canceller.Token.IsCancellationRequested) { break; } var consumerResult = consumer.Consume(canceller.Token); consumer.Commit(consumerResult); if (!ackCallbacks.TryRemove(consumerResult.Message.Key, out Action <Acknowledgement> callback)) { continue; } var response = consumerResult.Message.Value; if (encryptionKey != null) { response = SymmetricEncryptor.Decrypt(encryptionAlgorithm, encryptionKey, response); } var ack = KafkaCommon.Deserialize <Acknowledgement>(response); callback(ack); } catch (TaskCanceledException) { break; } catch { } } consumer.Unsubscribe(); await KafkaCommon.DeleteTopic(host, ackTopic); } finally { canceller.Dispose(); if (consumer != null) { consumer.Dispose(); } } }