您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

232 行
9.1KB

  1. using Confluent.Kafka;
  2. using DotNetty.Transport.Bootstrapping;
  3. using DotNetty.Transport.Channels;
  4. using Microsoft.Extensions.Hosting;
  5. using Microsoft.Extensions.Logging;
  6. using Microsoft.Extensions.Options;
  7. using NearCardAttendance.Common.helper;
  8. using NearCardAttendance.Model;
  9. using Newtonsoft.Json.Linq;
  10. using Newtonsoft.Json;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Linq;
  14. using System.Net;
  15. using System.Text;
  16. using System.Threading.Tasks;
  17. using NearCardAttendance.Service.MessageQueue.Model;
  18. namespace NearCardAttendance.TcpServer
  19. {
  20. public class Server : BackgroundService
  21. {
  22. private readonly ILogger<Server> _logger;
  23. //private readonly IServiceProvider _serviceProvider;
  24. private readonly ServerBootstrap _serverBootstrap;
  25. // private IChannel _serverChannel = default!;
  26. private CancellationTokenSource _tokenSource = null!;
  27. private readonly ServiceConfig _configService;
  28. private readonly HttpHelper _httpHelper = default!;
  29. private int _messageCount = 0;
  30. public Server(
  31. ILogger<Server> logger,
  32. ServerBootstrap serverBootstrap,
  33. IServiceProvider serviceProvider, HttpHelper httpHelper, IOptions<ServiceConfig> _optConfigService)
  34. {
  35. _logger = logger;
  36. //_serviceProvider = serviceProvider;
  37. _configService = _optConfigService.Value;
  38. _serverBootstrap = serverBootstrap;
  39. _httpHelper = httpHelper;
  40. }
  41. public override Task StartAsync(CancellationToken cancellationToken)
  42. {
  43. _logger.LogInformation("------StartAsync");
  44. _tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
  45. return base.StartAsync(cancellationToken);
  46. }
  47. public override Task StopAsync(CancellationToken cancellationToken)
  48. {
  49. _logger.LogInformation("------StopAsync");
  50. _tokenSource.Cancel(); //停止工作线程
  51. return base.StopAsync(cancellationToken);
  52. }
  53. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  54. {
  55. _logger.LogInformation("DotNetty server starting...");
  56. //var address = new IPEndPoint(IPAddress.Any, 12345);
  57. //IChannel _serverChannel = await _serverBootstrap.BindAsync(address);
  58. string ipAddress = "0.0.0.0";
  59. int port = 16662;
  60. var endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
  61. IChannel _serverChannel = await _serverBootstrap.BindAsync(endPoint);
  62. // _serverChannel.GetAttribute(MessageIdAttribute.Key).Set("SomeRequestId");
  63. //_logger.LogInformation("DotNetty server started on {0}.", address);
  64. _logger.LogInformation("DotNetty server started on {0}.", endPoint);
  65. #region kafka
  66. var kafkaConsumer = CreateKafkaConsumer();
  67. kafkaConsumer.Subscribe("topics.storage.near_card_attendance");
  68. var tasks = new List<Task>();
  69. List<ConsumeResult<Ignore, string>> consumeBatchResult = new List<ConsumeResult<Ignore, string>>();
  70. try
  71. {
  72. while (!stoppingToken.IsCancellationRequested)
  73. {
  74. var consumeResult = kafkaConsumer.Consume(stoppingToken);
  75. if (consumeResult != null)
  76. {
  77. _messageCount++;
  78. consumeBatchResult.Add(consumeResult);
  79. //// 30条消息为一批
  80. #if DEBUG
  81. if (!await ProcessBatchMessageAsync(consumeBatchResult, kafkaConsumer))
  82. { // 返回结果错误暂停5分钟
  83. await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
  84. }
  85. #else
  86. if (_messageCount % 30 == 0)
  87. {
  88. if (!await ProcessBatchMessageAsync(consumeBatchResult, kafkaConsumer))
  89. { // 返回结果错误暂停5分钟
  90. await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
  91. }
  92. }
  93. #endif
  94. }
  95. }
  96. }
  97. catch (OperationCanceledException)
  98. {
  99. _logger.LogWarning("Worker exit");
  100. }
  101. catch (Exception ex)
  102. {
  103. _logger.LogError($"An error occurred: {ex.Message}");
  104. }
  105. #endregion
  106. // Wait until the service is stopped
  107. stoppingToken.WaitHandle.WaitOne();
  108. _logger.LogInformation("DotNetty server stopping...");
  109. // Close the server channel and release resources
  110. await _serverChannel.CloseAsync();
  111. _logger.LogInformation("DotNetty server stopped.");
  112. }
  113. private async Task<bool> ProcessBatchMessageAsync(List<ConsumeResult<Ignore, string>> consumeBatchResult, IConsumer<Ignore, string> kafkaConsumer)
  114. {
  115. try
  116. {
  117. var url = $"{_configService.XinHuaLeYuUrl}/user/electronicCardAttendance/receiveTbAttendanceRecordException";
  118. var list = new List<object>();
  119. //consumeBatchResult.ForEach(x => {
  120. // JObject msg = (JObject)JsonConvert.DeserializeObject(x.Message.Value)!;
  121. // list.Add(new
  122. // {
  123. // attendanceStatus = int.TryParse(msg["data"]!["attendanceStatus"]!.ToString(), out int status) ? status : 0,
  124. // attendanceTime = msg["data"]!["attendanceTime"]!.ToString(),
  125. // imei = msg["data"]!["imei"]!.ToString()
  126. // }) ;
  127. //});
  128. consumeBatchResult.ForEach(x => {
  129. EventData msg = JsonConvert.DeserializeObject<EventData>(x.Message.Value)!;
  130. JObject content = (JObject)JsonConvert.DeserializeObject(msg.Content)!;
  131. list.Add(new
  132. {
  133. attendanceStatus = int.TryParse(content!["attendanceStatus"]!.ToString(), out int status) ? status : 0,
  134. attendanceTime = content!["attendanceTime"]!.ToString(),
  135. imei = content!["imei"]!.ToString()
  136. });
  137. });
  138. var data = new
  139. {
  140. data = list
  141. };
  142. var res = await _httpHelper.HttpToPostAsync(url, data);
  143. if (!string.IsNullOrEmpty(res))
  144. {
  145. JObject resObj = (JObject)JsonConvert.DeserializeObject(res!)!;
  146. if ((bool)resObj["success"]!)
  147. {
  148. _logger.LogInformation($"{nameof(ProcessBatchMessageAsync)} 推送 {JsonConvert.SerializeObject(data)} 结果,{res}");
  149. consumeBatchResult.ForEach(x =>
  150. {
  151. kafkaConsumer.Commit(x);
  152. _logger.LogInformation($"完成消费:{JsonConvert.SerializeObject(x.Message.Value)}");
  153. });
  154. return true;
  155. }
  156. }
  157. }
  158. catch (Exception ex)
  159. {
  160. _logger.LogError($"处理消息出错 \n{ex.Message}\n{ex.StackTrace}");
  161. }
  162. return false;
  163. }
  164. private IConsumer<Ignore, string> CreateKafkaConsumer()
  165. {
  166. var consumerConfig = new ConsumerConfig
  167. {
  168. GroupId = "near_card_attendance",
  169. BootstrapServers = _configService.KafkaServerAddress,
  170. AutoOffsetReset = AutoOffsetReset.Earliest,
  171. EnableAutoCommit = false, // 关闭自动提交偏移量
  172. CancellationDelayMaxMs = 1//set CancellationDelayMaxMs
  173. };
  174. return new ConsumerBuilder<Ignore, string>(consumerConfig)
  175. .SetErrorHandler((_, e) =>
  176. {
  177. //Console.WriteLine($"消费者创建出错,代码:{e.Code} |原因: {e.Reason}");
  178. _logger.LogInformation($"消费者创建出错,代码:{e.Code} |原因: {e.Reason}");
  179. })
  180. .SetPartitionsAssignedHandler((c, partitions) =>
  181. {
  182. //// 在这里手动指定要消费的分区
  183. //var partitionsToConsume = new List<TopicPartitionOffset>
  184. //{
  185. // new TopicPartitionOffset("topics.storage.near_card_attendance", partitionIndex, Offset.Unset)
  186. //};
  187. ////c.Assign(partitionsToConsume);
  188. //Console.WriteLine($"Assigned partitions: {string.Join(", ", partitionsToConsume)}");
  189. //return partitionsToConsume;
  190. })
  191. .SetPartitionsRevokedHandler((c, partitions) =>
  192. {
  193. })
  194. .Build();
  195. }
  196. }
  197. }