|
|
@ -5,10 +5,10 @@ import com.alibaba.fastjson.JSONObject; |
|
|
import com.influxdb.client.DeleteApi; |
|
|
import com.influxdb.client.DeleteApi; |
|
|
import com.influxdb.client.InfluxDBClient; |
|
|
import com.influxdb.client.InfluxDBClient; |
|
|
import com.influxdb.client.InfluxDBClientFactory; |
|
|
import com.influxdb.client.InfluxDBClientFactory; |
|
|
import com.influxdb.client.WriteApi; |
|
|
|
|
|
import com.influxdb.client.domain.WritePrecision; |
|
|
import com.influxdb.client.domain.WritePrecision; |
|
|
import com.influxdb.query.FluxRecord; |
|
|
import com.influxdb.query.FluxRecord; |
|
|
import com.influxdb.query.FluxTable; |
|
|
import com.influxdb.query.FluxTable; |
|
|
|
|
|
import com.topsail.influxdb.config.InfluxDBConfig; |
|
|
import com.topsail.influxdb.entity.*; |
|
|
import com.topsail.influxdb.entity.*; |
|
|
import com.topsail.influxdb.mapper.DeviceInfoMapper; |
|
|
import com.topsail.influxdb.mapper.DeviceInfoMapper; |
|
|
import com.topsail.influxdb.pojo.History; |
|
|
import com.topsail.influxdb.pojo.History; |
|
|
@ -33,7 +33,8 @@ import java.util.*; |
|
|
@Service |
|
|
@Service |
|
|
public class DeviceDataService { |
|
|
public class DeviceDataService { |
|
|
public static final Logger LOG = LoggerFactory.getLogger(DeviceDataService.class); |
|
|
public static final Logger LOG = LoggerFactory.getLogger(DeviceDataService.class); |
|
|
public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
|
|
|
|
|
|
|
|
// 使用 ThreadLocal 解决 SimpleDateFormat 线程安全问题 |
|
|
|
|
|
private static final ThreadLocal<SimpleDateFormat> dateFormatLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); |
|
|
// InfluxDB基础配置 |
|
|
// InfluxDB基础配置 |
|
|
private static final String DEVICEDATA_BUCKET_NAME = "iot"; |
|
|
private static final String DEVICEDATA_BUCKET_NAME = "iot"; |
|
|
@Value("${shengdilan.influxdb.token}") |
|
|
@Value("${shengdilan.influxdb.token}") |
|
|
@ -54,24 +55,54 @@ public class DeviceDataService { |
|
|
DeviceInfoMapper deviceInfoMapper; |
|
|
DeviceInfoMapper deviceInfoMapper; |
|
|
@Autowired |
|
|
@Autowired |
|
|
AmqpService amqpService; |
|
|
AmqpService amqpService; |
|
|
private InfluxDBClient influxDBClient; |
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
|
InfluxDBClient influxDBClient; |
|
|
|
|
|
@Autowired |
|
|
|
|
|
InfluxDBConfig influxDBConfig; |
|
|
|
|
|
|
|
|
|
|
|
// 旧InfluxDB客户端(用于数据迁移) |
|
|
private InfluxDBClient oldInfluxDBClient; |
|
|
private InfluxDBClient oldInfluxDBClient; |
|
|
|
|
|
|
|
|
|
|
|
/** 备份队列名称 */ |
|
|
|
|
|
private static final String BACKUP_QUEUE_NAME = "shengdilandevicedataback"; |
|
|
|
|
|
|
|
|
@PostConstruct |
|
|
@PostConstruct |
|
|
public void init() { |
|
|
public void init() { |
|
|
this.influxDBClient = InfluxDBClientFactory.create(url, token.toCharArray(), org); |
|
|
|
|
|
this.oldInfluxDBClient = InfluxDBClientFactory.create(oldurl, oldtoken.toCharArray(), oldorg); |
|
|
this.oldInfluxDBClient = InfluxDBClientFactory.create(oldurl, oldtoken.toCharArray(), oldorg); |
|
|
|
|
|
// 注册连接恢复回调:当InfluxDB恢复时,报告备份队列积压情况 |
|
|
|
|
|
influxDBConfig.addRecoveryCallback(this::onConnectionRecovered); |
|
|
|
|
|
LOG.info("DeviceDataService初始化完成,已注册InfluxDB连接恢复回调"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@PreDestroy |
|
|
@PreDestroy |
|
|
public void destroy() { |
|
|
public void destroy() { |
|
|
if (influxDBClient != null) { |
|
|
|
|
|
influxDBClient.close(); |
|
|
|
|
|
} |
|
|
|
|
|
if (oldInfluxDBClient != null) { |
|
|
if (oldInfluxDBClient != null) { |
|
|
oldInfluxDBClient.close(); |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
oldInfluxDBClient.close(); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
LOG.warn("关闭旧InfluxDB客户端时发生异常: {}", e.getMessage()); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* InfluxDB连接恢复后的回调处理 |
|
|
|
|
|
* 报告备份队列积压情况,@RabbitListener会自动重试处理备份队列中的消息 |
|
|
|
|
|
*/ |
|
|
|
|
|
private void onConnectionRecovered() { |
|
|
|
|
|
try { |
|
|
|
|
|
long backupQueueSize = amqpService.getQueueMessageCount(BACKUP_QUEUE_NAME); |
|
|
|
|
|
if (backupQueueSize > 0) { |
|
|
|
|
|
LOG.info("InfluxDB连接已恢复,备份队列 [{}] 当前积压{}条消息,@RabbitListener将自动重试处理", |
|
|
|
|
|
BACKUP_QUEUE_NAME, backupQueueSize); |
|
|
|
|
|
} else { |
|
|
|
|
|
LOG.info("InfluxDB连接已恢复,备份队列无积压消息"); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
LOG.warn("InfluxDB连接已恢复,但检查备份队列积压情况时发生异常: {}", e.getMessage()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 根据设备号查询设备历史数据 |
|
|
* 根据设备号查询设备历史数据 |
|
|
* |
|
|
* |
|
|
@ -85,60 +116,61 @@ public class DeviceDataService { |
|
|
*/ |
|
|
*/ |
|
|
public List<DeviceHistoryVo> getDeviceHistoryData(String uid, Integer pageNo, Integer pageSize, String startTime, String endTime, String imei) { |
|
|
public List<DeviceHistoryVo> getDeviceHistoryData(String uid, Integer pageNo, Integer pageSize, String startTime, String endTime, String imei) { |
|
|
InfluxDBClient client = InfluxDBClientFactory.create(url, token.toCharArray(), org); |
|
|
InfluxDBClient client = InfluxDBClientFactory.create(url, token.toCharArray(), org); |
|
|
StringBuffer query = new StringBuffer(); |
|
|
|
|
|
query.append("from(bucket: \"iot\") "); |
|
|
|
|
|
if (startTime != null && endTime != null) { |
|
|
|
|
|
SimpleDateFormat oldFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
|
|
|
|
|
SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); |
|
|
|
|
|
newFormat.setTimeZone(TimeZone.getTimeZone("UTC"));//时区转换 |
|
|
|
|
|
String start = null; |
|
|
|
|
|
try { |
|
|
|
|
|
start = newFormat.format(oldFormat.parse(startTime)); |
|
|
|
|
|
String stop = newFormat.format(oldFormat.parse(endTime)); |
|
|
|
|
|
query.append(String.format(" |> range(start:%s, stop:%s)", start, stop)); |
|
|
|
|
|
} catch (ParseException e) { |
|
|
|
|
|
e.printStackTrace(); |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
StringBuffer query = new StringBuffer(); |
|
|
|
|
|
query.append("from(bucket: \"iot\") "); |
|
|
|
|
|
if (startTime != null && endTime != null) { |
|
|
|
|
|
SimpleDateFormat oldFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
|
|
|
|
|
SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); |
|
|
|
|
|
newFormat.setTimeZone(TimeZone.getTimeZone("UTC"));//时区转换 |
|
|
|
|
|
String start = null; |
|
|
|
|
|
try { |
|
|
|
|
|
start = newFormat.format(oldFormat.parse(startTime)); |
|
|
|
|
|
String stop = newFormat.format(oldFormat.parse(endTime)); |
|
|
|
|
|
query.append(String.format(" |> range(start:%s, stop:%s)", start, stop)); |
|
|
|
|
|
} catch (ParseException e) { |
|
|
|
|
|
e.printStackTrace(); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
query.append("|> range(start: -1d)"); |
|
|
} |
|
|
} |
|
|
} else { |
|
|
|
|
|
query.append("|> range(start: -1d)"); |
|
|
|
|
|
} |
|
|
|
|
|
query.append(String.format(" |> filter(fn: (r) => r[\"_measurement\"] == \"history\") |> filter(fn: (r) => r[\"imei\"] == \"%s\") |> filter(fn: (r) => r[\"_field\"] == \"jsondata\" ) |> sort(columns:[\"_time\"], desc:true) ", imei)); |
|
|
|
|
|
if (pageNo != null && pageSize != null) { |
|
|
|
|
|
query.append(String.format(" |> limit(n: %s,offset:%s)", pageSize, pageNo - 1)); |
|
|
|
|
|
} |
|
|
|
|
|
query.append(" |> yield(name: \"last\")"); |
|
|
|
|
|
System.out.println("查询语句==========:" + query); |
|
|
|
|
|
List<FluxTable> tables = client.getQueryApi().query(query.toString()); |
|
|
|
|
|
// List<DeviceHistoryData> returnList = new ArrayList<>(); |
|
|
|
|
|
List<DeviceHistoryVo> resultSet = new ArrayList<>(); |
|
|
|
|
|
for (FluxTable fluxTable : tables) { |
|
|
|
|
|
List<FluxRecord> records = fluxTable.getRecords(); |
|
|
|
|
|
for (FluxRecord fluxRecord : records) { |
|
|
|
|
|
String value = (String) fluxRecord.getValueByKey("_value"); |
|
|
|
|
|
DeviceHistoryData data = JSONObject.parseObject(value, DeviceHistoryData.class); |
|
|
|
|
|
Integer singalstrength = (Integer) JSONObject.parseObject(value).get("singalstrength"); |
|
|
|
|
|
data.setSignalStrength(singalstrength); |
|
|
|
|
|
data.setTime(dateFormat.format((Long.parseLong(data.getTime())))); |
|
|
|
|
|
DeviceHistoryVo dataVo = DeviceHistoryVo.builder() |
|
|
|
|
|
.deviceType(String.valueOf(data.getDeviceType())) |
|
|
|
|
|
.imei(data.getImei()) |
|
|
|
|
|
.batteryLevel(data.getBatteryLevel()) |
|
|
|
|
|
.singalStrength(data.getSignalStrength()) |
|
|
|
|
|
.sampleData(data.getSampleData()) |
|
|
|
|
|
.passNum(data.getPassNum()) |
|
|
|
|
|
.alarmType(data.getAlarmType()) |
|
|
|
|
|
.unit(data.getUnit()) |
|
|
|
|
|
.sendTime(data.getTime()) |
|
|
|
|
|
.dataBody(data.getDataBody()) |
|
|
|
|
|
.value(data.getValue()).build(); |
|
|
|
|
|
dataVo = analysisSampleData(dataVo, data.getValue(), data.getUnit()); |
|
|
|
|
|
resultSet.add(dataVo); |
|
|
|
|
|
// returnList.add(data); |
|
|
|
|
|
|
|
|
query.append(String.format(" |> filter(fn: (r) => r[\"_measurement\"] == \"history\") |> filter(fn: (r) => r[\"imei\"] == \"%s\") |> filter(fn: (r) => r[\"_field\"] == \"jsondata\" ) |> sort(columns:[\"_time\"], desc:true) ", imei)); |
|
|
|
|
|
if (pageNo != null && pageSize != null) { |
|
|
|
|
|
query.append(String.format(" |> limit(n: %s,offset:%s)", pageSize, pageNo - 1)); |
|
|
|
|
|
} |
|
|
|
|
|
query.append(" |> yield(name: \"last\")"); |
|
|
|
|
|
System.out.println("查询语句==========:" + query); |
|
|
|
|
|
List<FluxTable> tables = client.getQueryApi().query(query.toString()); |
|
|
|
|
|
List<DeviceHistoryVo> resultSet = new ArrayList<>(); |
|
|
|
|
|
for (FluxTable fluxTable : tables) { |
|
|
|
|
|
List<FluxRecord> records = fluxTable.getRecords(); |
|
|
|
|
|
for (FluxRecord fluxRecord : records) { |
|
|
|
|
|
String value = (String) fluxRecord.getValueByKey("_value"); |
|
|
|
|
|
DeviceHistoryData data = JSONObject.parseObject(value, DeviceHistoryData.class); |
|
|
|
|
|
Integer singalstrength = (Integer) JSONObject.parseObject(value).get("singalstrength"); |
|
|
|
|
|
data.setSignalStrength(singalstrength); |
|
|
|
|
|
data.setTime(dateFormatLocal.get().format((Long.parseLong(data.getTime())))); |
|
|
|
|
|
DeviceHistoryVo dataVo = DeviceHistoryVo.builder() |
|
|
|
|
|
.deviceType(String.valueOf(data.getDeviceType())) |
|
|
|
|
|
.imei(data.getImei()) |
|
|
|
|
|
.batteryLevel(data.getBatteryLevel()) |
|
|
|
|
|
.singalStrength(data.getSignalStrength()) |
|
|
|
|
|
.sampleData(data.getSampleData()) |
|
|
|
|
|
.passNum(data.getPassNum()) |
|
|
|
|
|
.alarmType(data.getAlarmType()) |
|
|
|
|
|
.unit(data.getUnit()) |
|
|
|
|
|
.sendTime(data.getTime()) |
|
|
|
|
|
.dataBody(data.getDataBody()) |
|
|
|
|
|
.value(data.getValue()).build(); |
|
|
|
|
|
dataVo = analysisSampleData(dataVo, data.getValue(), data.getUnit()); |
|
|
|
|
|
resultSet.add(dataVo); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
Collections.reverse(resultSet); |
|
|
|
|
|
return resultSet; |
|
|
|
|
|
} finally { |
|
|
|
|
|
client.close(); |
|
|
} |
|
|
} |
|
|
client.close(); |
|
|
|
|
|
Collections.reverse(resultSet); |
|
|
|
|
|
return resultSet; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -184,17 +216,13 @@ public class DeviceDataService { |
|
|
*/ |
|
|
*/ |
|
|
public List<DeviceDataInfluxData> getOldInfluxdbData(String imei) { |
|
|
public List<DeviceDataInfluxData> getOldInfluxdbData(String imei) { |
|
|
DeviceBelongInfo deviceBelongInfo = deviceInfoMapper.queryDeviceBelongInfo(imei); |
|
|
DeviceBelongInfo deviceBelongInfo = deviceInfoMapper.queryDeviceBelongInfo(imei); |
|
|
// InfluxDBClient client = InfluxDBClientFactory.create(oldurl, oldtoken.toCharArray(), oldorg); |
|
|
|
|
|
StringBuffer query = new StringBuffer(); |
|
|
StringBuffer query = new StringBuffer(); |
|
|
query.append("from(bucket: \"iot\") "); |
|
|
query.append("from(bucket: \"iot\") "); |
|
|
query.append("|> range(start: -1y)"); |
|
|
query.append("|> range(start: -1y)"); |
|
|
query.append(String.format(" |> filter(fn: (r) => r[\"_measurement\"] == \"history\") |> filter(fn: (r) => r[\"imei\"] == \"%s\") |> filter(fn: (r) => r[\"_field\"] == \"jsondata\" ) |> sort(columns:[\"_time\"], desc:true) ", imei)); |
|
|
query.append(String.format(" |> filter(fn: (r) => r[\"_measurement\"] == \"history\") |> filter(fn: (r) => r[\"imei\"] == \"%s\") |> filter(fn: (r) => r[\"_field\"] == \"jsondata\" ) |> sort(columns:[\"_time\"], desc:true) ", imei)); |
|
|
// if (pageNo != null && pageSize != null) { |
|
|
|
|
|
// query.append(String.format(" |> limit(n: %s,offset:%s)", pageSize, pageNo - 1)); |
|
|
|
|
|
// } |
|
|
|
|
|
query.append(" |> yield(name: \"last\")"); |
|
|
query.append(" |> yield(name: \"last\")"); |
|
|
System.out.println("查询语句==========:" + query); |
|
|
System.out.println("查询语句==========:" + query); |
|
|
if(oldInfluxDBClient==null){ |
|
|
|
|
|
|
|
|
if (oldInfluxDBClient == null) { |
|
|
oldInfluxDBClient = InfluxDBClientFactory.create(oldurl, oldtoken.toCharArray(), oldorg); |
|
|
oldInfluxDBClient = InfluxDBClientFactory.create(oldurl, oldtoken.toCharArray(), oldorg); |
|
|
} |
|
|
} |
|
|
List<FluxTable> tables = oldInfluxDBClient.getQueryApi().query(query.toString()); |
|
|
List<FluxTable> tables = oldInfluxDBClient.getQueryApi().query(query.toString()); |
|
|
@ -204,10 +232,25 @@ public class DeviceDataService { |
|
|
List<FluxRecord> records = fluxTable.getRecords(); |
|
|
List<FluxRecord> records = fluxTable.getRecords(); |
|
|
for (FluxRecord fluxRecord : records) { |
|
|
for (FluxRecord fluxRecord : records) { |
|
|
String value = (String) fluxRecord.getValueByKey("_value"); |
|
|
String value = (String) fluxRecord.getValueByKey("_value"); |
|
|
History history = JSONObject.parseObject(value, History.class); |
|
|
|
|
|
|
|
|
History history = null; |
|
|
|
|
|
try { |
|
|
|
|
|
history = JSONObject.parseObject(value, History.class); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
LOG.warn("直接解析历史数据JSON失败,尝试清理后解析。IMEI: {}, 错误: {}", imei, e.getMessage()); |
|
|
|
|
|
try { |
|
|
|
|
|
String cleanedValue = clearDataBodyValue(value); |
|
|
|
|
|
history = JSONObject.parseObject(cleanedValue, History.class); |
|
|
|
|
|
String databody = extractAndClearDataBody(value); |
|
|
|
|
|
history.setDatabody(databody); |
|
|
|
|
|
LOG.info("成功清理并解析历史数据JSON。IMEI: {}", imei); |
|
|
|
|
|
} catch (Exception cleanEx) { |
|
|
|
|
|
LOG.error("清理后仍无法解析历史数据JSON,跳过该条记录。IMEI: {}, 原始数据前200字符: {}", |
|
|
|
|
|
imei, value != null && value.length() > 200 ? value.substring(0, 200) + "..." : value); |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
if (history != null) { |
|
|
if (history != null) { |
|
|
Date createtime = history.getSenddate(); |
|
|
Date createtime = history.getSenddate(); |
|
|
//将时间转换成Instant |
|
|
|
|
|
Instant time = null; |
|
|
Instant time = null; |
|
|
if (createtime != null) { |
|
|
if (createtime != null) { |
|
|
time = createtime.toInstant(); |
|
|
time = createtime.toInstant(); |
|
|
@ -221,7 +264,6 @@ public class DeviceDataService { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// client.close(); |
|
|
|
|
|
Collections.reverse(rerurnList); |
|
|
Collections.reverse(rerurnList); |
|
|
return rerurnList; |
|
|
return rerurnList; |
|
|
} |
|
|
} |
|
|
@ -232,14 +274,21 @@ public class DeviceDataService { |
|
|
* @param history |
|
|
* @param history |
|
|
*/ |
|
|
*/ |
|
|
public void saveDeviceDataToInfluxdb(History history) { |
|
|
public void saveDeviceDataToInfluxdb(History history) { |
|
|
|
|
|
// 先进行null检查,避免空指针异常 |
|
|
|
|
|
if (history == null || (StringUtils.isEmpty(history.getImei()))) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
if (history.getSenddate() == null) { |
|
|
if (history.getSenddate() == null) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
if (history == null || (StringUtils.isEmpty(history.getImei()))) { |
|
|
|
|
|
|
|
|
// 检查InfluxDB连接是否可用,不可用时直接将数据发送到MQ备份 |
|
|
|
|
|
if (!influxDBConfig.isConnectionHealthy()) { |
|
|
|
|
|
LOG.warn("InfluxDB连接不可用,数据直接发送到MQ备份。设备: {}", history.getImei()); |
|
|
|
|
|
amqpService.SendMessage("shengdilandevicedataback", JSON.toJSONString(history)); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
//查询设备的所属信息 |
|
|
//查询设备的所属信息 |
|
|
if (history != null && (history.getDeviceBelongInfo() == null || history.getHouseId() == null)) { |
|
|
|
|
|
|
|
|
if (history.getDeviceBelongInfo() == null || history.getHouseId() == null) { |
|
|
DeviceBelongInfo deviceBelongInfo = getDeviceBelongInfo(history.getImei().trim()); |
|
|
DeviceBelongInfo deviceBelongInfo = getDeviceBelongInfo(history.getImei().trim()); |
|
|
if (deviceBelongInfo == null) { |
|
|
if (deviceBelongInfo == null) { |
|
|
return; |
|
|
return; |
|
|
@ -248,21 +297,72 @@ public class DeviceDataService { |
|
|
history.setHouseId(deviceBelongInfo.getHouseId()); |
|
|
history.setHouseId(deviceBelongInfo.getHouseId()); |
|
|
} |
|
|
} |
|
|
Date createtime = history.getSenddate(); |
|
|
Date createtime = history.getSenddate(); |
|
|
//将时间转换成Instant |
|
|
|
|
|
Instant time = null; |
|
|
Instant time = null; |
|
|
if (createtime != null) { |
|
|
if (createtime != null) { |
|
|
time = createtime.toInstant(); |
|
|
time = createtime.toInstant(); |
|
|
} |
|
|
} |
|
|
// 复用已创建的客户端实例 |
|
|
|
|
|
DeviceDataInfluxData deviceDataInfluxData = rebuildDeviceDataInfluxData(history, time); |
|
|
DeviceDataInfluxData deviceDataInfluxData = rebuildDeviceDataInfluxData(history, time); |
|
|
try (WriteApi writeApi = influxDBClient.getWriteApi()) { |
|
|
|
|
|
writeApi.writeMeasurement(DEVICEDATA_BUCKET_NAME, org, WritePrecision.NS, deviceDataInfluxData); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
e.printStackTrace(); |
|
|
|
|
|
LOG.error("设备数据写入influxdb失败:{}", history.getImei()); |
|
|
|
|
|
amqpService.SendMessage("shengdilandevicedataback", JSON.toJSONString(history)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int maxRetries = 3; |
|
|
|
|
|
boolean success = false; |
|
|
|
|
|
for (int retry = 0; retry < maxRetries && !success; retry++) { |
|
|
|
|
|
try { |
|
|
|
|
|
influxDBConfig.getCurrentWriteApi().writeMeasurement(DEVICEDATA_BUCKET_NAME, org, WritePrecision.NS, deviceDataInfluxData); |
|
|
|
|
|
success = true; |
|
|
|
|
|
LOG.info("设备数据写入influxdb成功:{}", history.getImei()); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
LOG.error("设备数据写入influxdb失败(重试 {}/{}):{},错误:{}", |
|
|
|
|
|
retry + 1, maxRetries, history.getImei(), e.getMessage()); |
|
|
|
|
|
|
|
|
|
|
|
boolean isTimeout = isInfluxDBTimeoutException(e); |
|
|
|
|
|
|
|
|
|
|
|
// 如果是服务器内部错误(如shard损坏),不重试,直接发送到MQ |
|
|
|
|
|
if (e.getMessage() != null && (e.getMessage().contains("InternalServerErrorException") || |
|
|
|
|
|
e.getMessage().contains("not attempting to open shard") || |
|
|
|
|
|
e.getMessage().contains("short buffer"))) { |
|
|
|
|
|
LOG.error("InfluxDB服务器内部错误,可能是shard损坏,数据发送到MQ备份。错误详情: {}", e.getMessage()); |
|
|
|
|
|
// 标记连接不健康,避免其他线程继续尝试无效写入 |
|
|
|
|
|
influxDBConfig.markConnectionUnhealthy(); |
|
|
|
|
|
try { |
|
|
|
|
|
amqpService.SendMessage("shengdilandevicedataback", JSON.toJSONString(history)); |
|
|
|
|
|
} catch (Exception sendEx) { |
|
|
|
|
|
LOG.error("发送备份队列失败,异常将抛回给MQ监听器,消息将重新入队: {}", sendEx.getMessage()); |
|
|
|
|
|
throw new RuntimeException("发送备份队列失败", sendEx); |
|
|
|
|
|
} |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 如果是超时异常,立即标记连接不健康,发送到备份队列 |
|
|
|
|
|
if (isTimeout) { |
|
|
|
|
|
LOG.warn("检测到 InfluxDB 超时异常,标记连接不健康,将数据发送到备份队列: {}", e.getMessage()); |
|
|
|
|
|
influxDBConfig.markConnectionUnhealthy(); |
|
|
|
|
|
try { |
|
|
|
|
|
amqpService.SendMessage("shengdilandevicedataback", JSON.toJSONString(history)); |
|
|
|
|
|
} catch (Exception sendEx) { |
|
|
|
|
|
LOG.error("发送备份队列失败,异常将抛回给MQ监听器,消息将重新入队: {}", sendEx.getMessage()); |
|
|
|
|
|
throw new RuntimeException("发送备份队列失败", sendEx); |
|
|
|
|
|
} |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 最后一次重试失败,发送到MQ |
|
|
|
|
|
if (retry == maxRetries - 1) { |
|
|
|
|
|
LOG.error("设备数据写入influxdb最终失败:{}", history.getImei()); |
|
|
|
|
|
try { |
|
|
|
|
|
amqpService.SendMessage("shengdilandevicedataback", JSON.toJSONString(history)); |
|
|
|
|
|
} catch (Exception sendEx) { |
|
|
|
|
|
LOG.error("发送备份队列失败,异常将抛回给MQ监听器,消息将重新入队: {}", sendEx.getMessage()); |
|
|
|
|
|
throw new RuntimeException("发送备份队列失败", sendEx); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
try { |
|
|
|
|
|
Thread.sleep(2000 * (retry + 1)); |
|
|
|
|
|
} catch (InterruptedException ie) { |
|
|
|
|
|
Thread.currentThread().interrupt(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
LOG.info("设备数据写入influxdb成功:{}", history.getImei()); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -281,23 +381,22 @@ public class DeviceDataService { |
|
|
deviceDataInfluxData.value3 = 0; |
|
|
deviceDataInfluxData.value3 = 0; |
|
|
deviceDataInfluxData.value4 = 0; |
|
|
deviceDataInfluxData.value4 = 0; |
|
|
try { |
|
|
try { |
|
|
if (values.length > 0) { |
|
|
|
|
|
deviceDataInfluxData.value1 = Double.parseDouble(values[0]); |
|
|
|
|
|
|
|
|
if (values.length > 0 && values[0] != null && !"null".equals(values[0])) { |
|
|
|
|
|
deviceDataInfluxData.value1 = parseDoubleValue(values[0]); |
|
|
} |
|
|
} |
|
|
if (values.length > 1) { |
|
|
|
|
|
deviceDataInfluxData.value2 = Double.parseDouble(values[1]); |
|
|
|
|
|
|
|
|
if (values.length > 1 && values[1] != null && !"null".equals(values[1])) { |
|
|
|
|
|
deviceDataInfluxData.value2 = parseDoubleValue(values[1]); |
|
|
} |
|
|
} |
|
|
if (values.length > 2) { |
|
|
|
|
|
deviceDataInfluxData.value3 = Double.parseDouble(values[2]); |
|
|
|
|
|
|
|
|
if (values.length > 2 && values[2] != null && !"null".equals(values[2])) { |
|
|
|
|
|
deviceDataInfluxData.value3 = parseDoubleValue(values[2]); |
|
|
} |
|
|
} |
|
|
if (values.length > 3) { |
|
|
|
|
|
deviceDataInfluxData.value4 = Double.parseDouble(values[4]); |
|
|
|
|
|
|
|
|
if (values.length > 3 && values[3] != null && !"null".equals(values[3])) { |
|
|
|
|
|
deviceDataInfluxData.value4 = parseDoubleValue(values[3]); |
|
|
} |
|
|
} |
|
|
} catch (Exception e) { |
|
|
} catch (Exception e) { |
|
|
LOG.info(e.getMessage()); |
|
|
|
|
|
|
|
|
LOG.warn("解析设备数据值失败,imei: {}, value: {}, 错误: {}", history.getImei(), value, e.getMessage()); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
//从内部移动处出来,value有没有数值都进行插入 |
|
|
|
|
|
deviceDataInfluxData.battery = history.getBatterylevel(); |
|
|
deviceDataInfluxData.battery = history.getBatterylevel(); |
|
|
deviceDataInfluxData.sigal = history.getSingalstrength(); |
|
|
deviceDataInfluxData.sigal = history.getSingalstrength(); |
|
|
deviceDataInfluxData.jsondata = JSON.toJSONString(history); |
|
|
deviceDataInfluxData.jsondata = JSON.toJSONString(history); |
|
|
@ -308,6 +407,32 @@ public class DeviceDataService { |
|
|
return deviceDataInfluxData; |
|
|
return deviceDataInfluxData; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 解析数值字符串 |
|
|
|
|
|
* 支持格式:"0.2496Mpa", "25℃", "80%RH", "118.0", "-50.0", "null"等 |
|
|
|
|
|
* - 包含单位(Mpa、℃、%RH)的字符串:提取数值部分 |
|
|
|
|
|
* - 纯数字字符串:直接解析 |
|
|
|
|
|
* |
|
|
|
|
|
* @param value 待解析的字符串 |
|
|
|
|
|
* @return 解析后的double值,解析失败返回0 |
|
|
|
|
|
*/ |
|
|
|
|
|
private double parseDoubleValue(String value) { |
|
|
|
|
|
if (value == null || value.trim().isEmpty() || "null".equals(value)) { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
// 去除所有非数字、小数点、负号的字符(如单位:Mpa, ℃, %RH等) |
|
|
|
|
|
String numericStr = value.replaceAll("[^\\d.\\-]", "").trim(); |
|
|
|
|
|
if (numericStr.isEmpty()) { |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
return Double.parseDouble(numericStr); |
|
|
|
|
|
} catch (NumberFormatException e) { |
|
|
|
|
|
LOG.debug("无法解析数值: {}", value); |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 查询设备的所属信息 |
|
|
* 查询设备的所属信息 |
|
|
*/ |
|
|
*/ |
|
|
@ -320,16 +445,10 @@ public class DeviceDataService { |
|
|
*/ |
|
|
*/ |
|
|
public void deleteDeviceData() { |
|
|
public void deleteDeviceData() { |
|
|
InfluxDBClient client = InfluxDBClientFactory.create(url, token.toCharArray()); |
|
|
InfluxDBClient client = InfluxDBClientFactory.create(url, token.toCharArray()); |
|
|
StringBuffer query = new StringBuffer(); |
|
|
|
|
|
query.append("from(bucket: \"iot\") "); |
|
|
|
|
|
query.append(String.format(" |> filter(fn: (r) => r[\"_measurement\"] == \"history\")")); |
|
|
|
|
|
query.append("|> range(start: -36d)"); |
|
|
|
|
|
System.out.println("查询语句==========:" + query); |
|
|
|
|
|
DeleteApi deleteApi = client.getDeleteApi(); |
|
|
|
|
|
OffsetDateTime start = OffsetDateTime.of(2020, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); |
|
|
|
|
|
OffsetDateTime stop = OffsetDateTime.of(2026, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); |
|
|
|
|
|
try { |
|
|
try { |
|
|
// Delete data with specific time range |
|
|
|
|
|
|
|
|
DeleteApi deleteApi = client.getDeleteApi(); |
|
|
|
|
|
OffsetDateTime start = OffsetDateTime.of(2020, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); |
|
|
|
|
|
OffsetDateTime stop = OffsetDateTime.of(2026, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); |
|
|
deleteApi.delete(start, stop, "", DEVICEDATA_BUCKET_NAME, org); |
|
|
deleteApi.delete(start, stop, "", DEVICEDATA_BUCKET_NAME, org); |
|
|
System.out.println("Data deleted successfully"); |
|
|
System.out.println("Data deleted successfully"); |
|
|
} catch (Exception e) { |
|
|
} catch (Exception e) { |
|
|
@ -345,45 +464,69 @@ public class DeviceDataService { |
|
|
* @param imei |
|
|
* @param imei |
|
|
*/ |
|
|
*/ |
|
|
public void transferDeviceData(String imei) { |
|
|
public void transferDeviceData(String imei) { |
|
|
//1.查询所有的设备编号 |
|
|
|
|
|
List<SyncDataFlag> syncDataFlags = deviceInfoMapper.querySyncDeviceDataFlagInfo(imei); |
|
|
List<SyncDataFlag> syncDataFlags = deviceInfoMapper.querySyncDeviceDataFlagInfo(imei); |
|
|
//2.根据设备编号查询历史Influxdb所有的设备数据 |
|
|
|
|
|
Boolean flag = true; |
|
|
Boolean flag = true; |
|
|
if (syncDataFlags != null && syncDataFlags.size() > 0) { |
|
|
if (syncDataFlags != null && syncDataFlags.size() > 0) { |
|
|
for (int i = 0; i < syncDataFlags.size(); i++) { |
|
|
for (int i = 0; i < syncDataFlags.size(); i++) { |
|
|
if (!flag) { |
|
|
if (!flag) { |
|
|
syncDataFlags = deviceInfoMapper.querySyncDeviceDataFlagInfo(null); |
|
|
syncDataFlags = deviceInfoMapper.querySyncDeviceDataFlagInfo(null); |
|
|
|
|
|
if (syncDataFlags == null || syncDataFlags.isEmpty()) { |
|
|
|
|
|
LOG.warn("重新查询同步标志列表为空,终止同步"); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
int index = new Random().nextInt(syncDataFlags.size()); |
|
|
int index = new Random().nextInt(syncDataFlags.size()); |
|
|
SyncDataFlag syncDataFlag = syncDataFlags.get(index); |
|
|
SyncDataFlag syncDataFlag = syncDataFlags.get(index); |
|
|
Boolean syncDeviceData = false; |
|
|
Boolean syncDeviceData = false; |
|
|
|
|
|
Long dataCount = 0L; |
|
|
List<DeviceDataInfluxData> influxdbDataList = getOldInfluxdbData(syncDataFlag.getImei().toLowerCase(Locale.ROOT)); |
|
|
List<DeviceDataInfluxData> influxdbDataList = getOldInfluxdbData(syncDataFlag.getImei().toLowerCase(Locale.ROOT)); |
|
|
if (influxdbDataList != null && influxdbDataList.size() > 0) { |
|
|
if (influxdbDataList != null && influxdbDataList.size() > 0) { |
|
|
for (DeviceDataInfluxData influxData : influxdbDataList) { |
|
|
for (DeviceDataInfluxData influxData : influxdbDataList) { |
|
|
// InfluxDBClient client = InfluxDBClientFactory.create(url, token.toCharArray()); |
|
|
|
|
|
//3.将设备数据保存到influxdb中 |
|
|
|
|
|
if(influxDBClient== null){ |
|
|
|
|
|
influxDBClient = InfluxDBClientFactory.create(url, token.toCharArray()); |
|
|
|
|
|
} |
|
|
|
|
|
try (WriteApi writeApi = influxDBClient.getWriteApi()) { |
|
|
|
|
|
writeApi.writeMeasurement(DEVICEDATA_BUCKET_NAME, org, WritePrecision.NS, influxData); |
|
|
|
|
|
syncDeviceData = true; |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
LOG.error("保存设备数据到influxdb失败:{}", e.getMessage()); |
|
|
|
|
|
syncDeviceData = false; |
|
|
|
|
|
if (influxData != null && influxData.jsondata != null && !influxData.jsondata.equals("")) { |
|
|
|
|
|
String message = influxData.jsondata; |
|
|
|
|
|
History history = JSON.parseObject(message, History.class); |
|
|
|
|
|
if (history != null && history.getImei() != null && !history.getImei().equals("")) { |
|
|
|
|
|
amqpService.SendMessage("shengdilandevicedataback", JSON.toJSONString(history)); |
|
|
|
|
|
|
|
|
boolean writeSuccess = false; |
|
|
|
|
|
int maxRetries = 3; |
|
|
|
|
|
for (int retry = 0; retry < maxRetries && !writeSuccess; retry++) { |
|
|
|
|
|
try { |
|
|
|
|
|
influxDBConfig.getCurrentWriteApi().writeMeasurement(DEVICEDATA_BUCKET_NAME, org, WritePrecision.NS, influxData); |
|
|
|
|
|
syncDeviceData = true; |
|
|
|
|
|
dataCount = dataCount + 1; |
|
|
|
|
|
writeSuccess = true; |
|
|
|
|
|
LOG.info("设备数据保存到influxdb成功:{}", influxData.imei); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
LOG.error("保存设备数据到influxdb失败(重试 {}/{}):{},错误:{}", |
|
|
|
|
|
retry + 1, maxRetries, influxData.imei, e.getMessage()); |
|
|
|
|
|
|
|
|
|
|
|
if (e.getMessage() != null && (e.getMessage().contains("InternalServerErrorException") || |
|
|
|
|
|
e.getMessage().contains("not attempting to open shard") || |
|
|
|
|
|
e.getMessage().contains("short buffer"))) { |
|
|
|
|
|
LOG.error("InfluxDB服务器内部错误,可能是shard损坏,数据发送到MQ备份。错误详情: {}", e.getMessage()); |
|
|
|
|
|
if (influxData != null && influxData.jsondata != null && !influxData.jsondata.equals("")) { |
|
|
|
|
|
History history = JSON.parseObject(influxData.jsondata, History.class); |
|
|
|
|
|
if (history != null && history.getImei() != null && !history.getImei().equals("")) { |
|
|
|
|
|
amqpService.SendMessage("shengdilandevicedataback", JSON.toJSONString(history)); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (retry == maxRetries - 1) { |
|
|
|
|
|
if (influxData != null && influxData.jsondata != null && !influxData.jsondata.equals("")) { |
|
|
|
|
|
History history = JSON.parseObject(influxData.jsondata, History.class); |
|
|
|
|
|
if (history != null && history.getImei() != null && !history.getImei().equals("")) { |
|
|
|
|
|
amqpService.SendMessage("shengdilandevicedataback", JSON.toJSONString(history)); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
try { |
|
|
|
|
|
Thread.sleep(2000 * (retry + 1)); |
|
|
|
|
|
} catch (InterruptedException ie) { |
|
|
|
|
|
Thread.currentThread().interrupt(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
if (syncDeviceData) { |
|
|
if (syncDeviceData) { |
|
|
//4.更新同步设备数据状态 |
|
|
|
|
|
deviceInfoMapper.updateSyncDeviceDataFlagInfo(syncDataFlag.getId(), 1); |
|
|
deviceInfoMapper.updateSyncDeviceDataFlagInfo(syncDataFlag.getId(), 1); |
|
|
} |
|
|
} |
|
|
flag = false; |
|
|
flag = false; |
|
|
@ -396,35 +539,126 @@ public class DeviceDataService { |
|
|
*/ |
|
|
*/ |
|
|
public Long getDeviceDataCount(String imei) { |
|
|
public Long getDeviceDataCount(String imei) { |
|
|
InfluxDBClient client = InfluxDBClientFactory.create(oldurl, oldtoken.toCharArray(), oldorg); |
|
|
InfluxDBClient client = InfluxDBClientFactory.create(oldurl, oldtoken.toCharArray(), oldorg); |
|
|
// 1. 拼接公共过滤条件片段(总条数和分页查询共用) |
|
|
|
|
|
StringBuilder filterFragment = new StringBuilder(); |
|
|
|
|
|
filterFragment.append("from(bucket: \"iot\") "); |
|
|
|
|
|
filterFragment.append("|> range(start: -400d)"); |
|
|
|
|
|
// 2. 拼接固定过滤条件(_measurement和_field,必选) |
|
|
|
|
|
filterFragment.append(" |> filter(fn: (r) => r[\"_measurement\"] == \"history\")"); |
|
|
|
|
|
filterFragment.append(" |> filter(fn: (r) => r[\"_field\"] == \"jsondata\")"); |
|
|
|
|
|
// 3. 动态拼接imei过滤条件(仅当imei非空且非空白字符串时拼接) |
|
|
|
|
|
if (imei != null && !imei.trim().isEmpty()) { |
|
|
|
|
|
filterFragment.append(String.format(" |> filter(fn: (r) => r[\"imei\"] == \"%s\")", imei.trim())); |
|
|
|
|
|
} |
|
|
|
|
|
// 5. 拼接固定排序条件(按时间倒序) |
|
|
|
|
|
// 2. 构建总条数查询子句(yield命名为total) |
|
|
|
|
|
StringBuilder totalQuery = new StringBuilder(); |
|
|
|
|
|
totalQuery.append(filterFragment); // 复用过滤条件 |
|
|
|
|
|
totalQuery.append(" |> count(column: \"_value\")"); // 统计总条数 |
|
|
|
|
|
totalQuery.append(" |> yield(name: \"total\")\n"); // 标记结果集为total |
|
|
|
|
|
System.out.println("查询数量语句:" + totalQuery.toString()); |
|
|
|
|
|
//查询总条数 |
|
|
|
|
|
long totalCount = 0; |
|
|
|
|
|
List<FluxTable> totalTables = client.getQueryApi().query(totalQuery.toString()); |
|
|
|
|
|
for (FluxTable table : totalTables) { |
|
|
|
|
|
for (FluxRecord record : table.getRecords()) { |
|
|
|
|
|
Object total = record.getValue(); |
|
|
|
|
|
if (total != null) { |
|
|
|
|
|
totalCount = totalCount + ((Number) total).longValue(); |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
StringBuilder filterFragment = new StringBuilder(); |
|
|
|
|
|
filterFragment.append("from(bucket: \"iot\") "); |
|
|
|
|
|
filterFragment.append("|> range(start: -400d)"); |
|
|
|
|
|
filterFragment.append(" |> filter(fn: (r) => r[\"_measurement\"] == \"history\")"); |
|
|
|
|
|
filterFragment.append(" |> filter(fn: (r) => r[\"_field\"] == \"jsondata\")"); |
|
|
|
|
|
if (imei != null && !imei.trim().isEmpty()) { |
|
|
|
|
|
filterFragment.append(String.format(" |> filter(fn: (r) => r[\"imei\"] == \"%s\")", imei.trim())); |
|
|
|
|
|
} |
|
|
|
|
|
StringBuilder totalQuery = new StringBuilder(); |
|
|
|
|
|
totalQuery.append(filterFragment); |
|
|
|
|
|
totalQuery.append(" |> count(column: \"_value\")"); |
|
|
|
|
|
totalQuery.append(" |> yield(name: \"total\")\n"); |
|
|
|
|
|
System.out.println("查询数量语句:" + totalQuery.toString()); |
|
|
|
|
|
long totalCount = 0; |
|
|
|
|
|
List<FluxTable> totalTables = client.getQueryApi().query(totalQuery.toString()); |
|
|
|
|
|
for (FluxTable table : totalTables) { |
|
|
|
|
|
for (FluxRecord record : table.getRecords()) { |
|
|
|
|
|
Object total = record.getValue(); |
|
|
|
|
|
if (total != null) { |
|
|
|
|
|
totalCount = totalCount + ((Number) total).longValue(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return totalCount; |
|
|
|
|
|
} finally { |
|
|
|
|
|
client.close(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 检查异常是否为 InfluxDB 超时、连接失败或 shard 损坏异常 |
|
|
|
|
|
* @param e 异常对象 |
|
|
|
|
|
* @return 如果是超时、连接失败或 shard 损坏异常返回 true,否则返回 false |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean isInfluxDBTimeoutException(Exception e) { |
|
|
|
|
|
String message = e.getMessage(); |
|
|
|
|
|
|
|
|
|
|
|
// 检查 shard 损坏相关的错误 |
|
|
|
|
|
if (message != null && (message.contains("not attempting to open shard") || |
|
|
|
|
|
message.contains("short buffer") || |
|
|
|
|
|
message.contains("InternalServerErrorException"))) { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查连接失败相关的错误 |
|
|
|
|
|
if (message != null && (message.contains("Failed to connect") || |
|
|
|
|
|
message.contains("ConnectException") || |
|
|
|
|
|
message.contains("Connection refused") || |
|
|
|
|
|
message.contains("Connection timed out"))) { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查直接异常类型 |
|
|
|
|
|
if (e instanceof com.influxdb.exceptions.InfluxException) { |
|
|
|
|
|
if (message != null && (message.contains("Read timed out") || |
|
|
|
|
|
message.contains("SocketTimeoutException") || |
|
|
|
|
|
message.contains("timeout") || |
|
|
|
|
|
message.contains("connect timed out"))) { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查根本原因(使用HashSet防止循环引用导致死循环) |
|
|
|
|
|
Throwable cause = e.getCause(); |
|
|
|
|
|
Set<Throwable> visitedCauses = new HashSet<>(); |
|
|
|
|
|
while (cause != null && visitedCauses.add(cause)) { |
|
|
|
|
|
if (cause instanceof java.net.SocketTimeoutException || |
|
|
|
|
|
cause instanceof java.net.ConnectException) { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
if (cause.getMessage() != null && |
|
|
|
|
|
(cause.getMessage().contains("Read timed out") || |
|
|
|
|
|
cause.getMessage().contains("SocketTimeoutException") || |
|
|
|
|
|
cause.getMessage().contains("connect timed out") || |
|
|
|
|
|
cause.getMessage().contains("Failed to connect") || |
|
|
|
|
|
cause.getMessage().contains("ConnectException") || |
|
|
|
|
|
cause.getMessage().contains("Connection timed out"))) { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
cause = cause.getCause(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 清理databody字段值(置为null)以尝试修复JSON解析问题 |
|
|
|
|
|
*/ |
|
|
|
|
|
private String clearDataBodyValue(String value) { |
|
|
|
|
|
if (value == null || value.isEmpty()) { |
|
|
|
|
|
return value; |
|
|
|
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
// 使用正则替换databody字段值为null |
|
|
|
|
|
return value.replaceAll("\"databody\"\\s*:\\s*\"[^\"]*\"", "\"databody\":null"); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
LOG.warn("清理databody字段失败: {}", e.getMessage()); |
|
|
|
|
|
return value; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 提取并清理databody字段内容 |
|
|
|
|
|
* 使用正则从原始字符串中提取databody值,避免JSON解析失败的问题 |
|
|
|
|
|
*/ |
|
|
|
|
|
private String extractAndClearDataBody(String value) { |
|
|
|
|
|
if (value == null || value.isEmpty()) { |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
// 使用正则从原始字符串中提取databody字段值 |
|
|
|
|
|
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\"databody\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\""); |
|
|
|
|
|
java.util.regex.Matcher matcher = pattern.matcher(value); |
|
|
|
|
|
if (matcher.find()) { |
|
|
|
|
|
return matcher.group(1); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
LOG.warn("提取databody字段失败: {}", e.getMessage()); |
|
|
} |
|
|
} |
|
|
return totalCount; |
|
|
|
|
|
|
|
|
return null; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |