Môi trường triển khai
- Loại dự án: Windows Service
- Phiên bản .NET: .NET Framework 4.8
- Ngôn ngữ lập trình: C#
- Thư viện InfluxDB: InfluxDB.Client v4.15.0, InfluxDB.Client.Core v4.15.0
Mã tái tạo sự cố
Thiết lập Timer để gọi hàm ghi dữ liệu theo chu kỳ:
_periodicTimer = new Timer((o) => SaveMetricData(), null, intervalMinutes * 1000, 60 * 1000); // mỗi 1 phút
Cài đặt hàm SaveMetricData:
private async void SaveMetricData()
{
try
{
lock (_dataLock)
{
string currentTime = DateTime.Now.ToString("HH:mm:ss");
foreach (DataRow row in productionSchedule.Rows)
{
if (string.Compare(currentTime, row["startTime"].ToString()) >= 0
&& string.Compare(currentTime, row["endTime"].ToString()) < 0)
{
var metricPoint = PointData.Measurement("production_metrics")
.Tag("factoryId", row["factoryId"].ToString())
.Tag("workshopId", row["workshopId"].ToString())
.Tag("lineId", row["lineId"].ToString())
.Tag("teamId", row["teamId"].ToString())
.Tag("shiftType", row["shiftType"].ToString())
.Field("scheduledDuration", 60); // ghi mỗi phút, tức 60s mỗi lần
var writeApi = _influxDBClient.GetWriteApi();
writeApi.WritePoint(metricPoint, bucket, org);
_logger.Info($"Đã ghi dữ liệu cho team: {row["teamId"]}");
}
}
}
}
catch (Exception ex)
{
_logger.Error(ex.ToString());
}
}
Giải pháp khắc phục
Chuyển đổi từ phương thức đồng bộ GetWriteApi sang phương thức bất đồng bộ GetWriteApiAsync:
private void SaveMetricData()
{
try
{
lock (_dataLock)
{
string currentTime = DateTime.Now.ToString("HH:mm:ss");
List<PointData> metricPoints = new List<PointData>();
foreach (DataRow row in productionSchedule.Rows)
{
if (string.Compare(currentTime, row["startTime"].ToString()) >= 0
&& string.Compare(currentTime, row["endTime"].ToString()) < 0)
{
var metricPoint = PointData.Measurement("production_metrics")
.Tag("factoryId", row["factoryId"].ToString())
.Tag("workshopId", row["workshopId"].ToString())
.Tag("lineId", row["lineId"].ToString())
.Tag("teamId", row["teamId"].ToString())
.Tag("shiftType", row["shiftType"].ToString())
.Field("scheduledDuration", 60); // ghi mỗi phút, tức 60s mỗi lần
metricPoints.Add(metricPoint);
}
}
var writeApi = _influxDBClient.GetWriteApiAsync();
writeApi.WritePointsAsync(metricPoints, bucket, org);
}
}
catch (Exception ex)
{
_logger.Error(ex.ToString());
}
}
Kết luận
Đây là một lỗi tiềm ẩn trong thư viện InfluxDB.Client. Việc sử dụng phương thức GetWriteApi() đồng bộ trong môi trường đa luồng với Timer gây ra tình trạng rò rỉ bộ nhớ theo thời gian. Giải pháp thay thế bằng GetWriteApiAsync() kết hợp với việc thu thập các điểm dữ liệu vào danh sách trước khi ghi sẽ khắc phục được vấn đề này.