jar.exe的encode问题

在HP-UX 11i系统中使用Java进行FTP文件更新时遇到问题,特别是使用ZIPInputStream解压含有中文字符的文件名时抛出异常。该问题源于Java的一个已知BUG,在不同平台间处理中文文件名时出现错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

— 作者 wonder @ 05:38

 

 

 

 

说起来我好像和小型机有仇,今天晚上更新程序死活也更新不上去,这个hpux11i真实麻烦。大多数ftp程序都不好用,后来只能用jar打包,打完后把程序放到hpux上用jar解压缩,见鬼,这都能出问题,结果报了一个异常。java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:298),唉咋办,上网查了查,发现居然是sun的一个bug,而且六年都没有改了,TNND,sun你就不能勤快点啊。

没办法,只能试了试,结果发现只要在同一个平台就没有问题,后来估计是平台的encode的问题,不过没找到在什么地方设置。解决办法:在windows下用jar压缩一个jar文件,然后用windows的ftp工具把这个文件传到hpux上,然后再在windows下用telnet工具连接到hpux上,执行jar xvf就好用了。

最后再鄙视一下sun的懒惰!

 

Bug ID:4820807

 
Votes 9
Synopsis java.util.zip.ZipInputStream cannot extract files with Chinese chars in name
Category java:classes_util_jarzip
Reported Against 1.2.1 , 1.4.1 , tiger
Release Fixed  
State In progress, request for enhancement
Related Bugs 4885817 , 4244499
Submit Date 19-FEB-2003
Description
FULL PRODUCT VERSION :
java version "1.4.1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-b21)
Java HotSpot(TM) Client VM (build 1.4.1-b21, mixed mode)


FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]
Service Pack 3

A DESCRIPTION OF THE PROBLEM :
If ZipInputStream is used to read a zip file containing one
or more files with Chinese, Japanese or Korean names, the
getNextEntry method throws an IllegalArgumentException.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a zip file containing at least one file with a
Chinese, Japanese or Korean filename.
2. Try to read using a ZipInputStream.

EXPECTED VERSUS ACTUAL BEHAVIOR :
Should return a valid entry with the correct filename
instead of throwing an exception.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.IllegalArgumentException

    at java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:291)

    at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:230)

    at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:75)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.FileInputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public final class TestCase {
    public static void main(String[] args) throws IOException {
        ZipInputStream zis = new ZipInputStream(new FileInputStream
("myfile.zip"));
        ZipEntry entry;
        while ((entry = zis.getNextEntry()) != null) {
            System.out.println("found " + entry.getName());
        }
    }
}

---------- END SOURCE ----------

CUSTOMER WORKAROUND :
Do not use CJK filenames in zip files.
(Review ID: 181382) 
======================================================================


 xxxxx@xxxxx  2003-09-02

Same problem reported by a CAP member from Germany:

J2SE Version (please include all output from java -version flag):
  java version "1.4.1"
  Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-b21)
  Java HotSpot(TM) Client VM (build 1.4.1-b21, mixed mode)

and

  java version "1.5.0-beta"
  Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b16)
  Java HotSpot(TM) Client VM (build 1.5.0-beta-b16, mixed mode)


Does this problem occur on J2SE 1.3, 1.4 or 1.4.1?  Yes / No (pick one)
  Yes

Operating System Configuration Information (be specific):
  English Linux and German Win2K

Bug Description:
  A ZIP file with entries that contain german umlauts. When read
  read these entries using ZipInputStream.getNextEntry() it throws an 
  IllegalArgumentException at:

Exception in thread "main" java.lang.IllegalArgumentException
         at 
java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:298)
         at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:237)
         at 
java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:73)
         at ZipUmlauts.main(ZipUmlauts.java:22)

  It would be better, if the getUTF8String() method would just ignore 
  these "illegal" characters or add them "as-is".

Test Program: (ZipUmlauts.java umlauts.zip)
-------------

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/*
 *  ZipUmlauts.java created on Sep 1, 2003 8:45:08 AM
 */

/**
 * @version ${Id:}
 * @author rs
 * @since pirobase®CB 1.0
 */
public final class ZipUmlauts {

    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("umlauts.zip");
        ZipInputStream zis=new ZipInputStream(fis);
        ZipEntry ze;
        while ((ze=zis.getNextEntry())!=null) {
            System.out.println(ze.getName());
        }
    }

}
Work Around
N/A
Evaluation
Unfortunately, fixing this in a backward-compatible way may be impossible.
At least, for non-ASCII file names, Java should be able to create files
on one system and extract them on a different system, even if the
encodings are different.

The suggestion of adding an encoding attribute is a good one.
That should have been done when the decision to encode file names
in UTF-8 was first made.
 xxxxx@xxxxx  2003-09-04

I have confirmed that, as long as one uses Sun's J2SE zip
implementation consistently, in a environment where file.encoding
supports the character set of interest,
that one can create, list and extract
jar/zip files containing non-ASCII characters (including Chinese
characters) correctly.   Other zip implementations also have
character encoding interoperability problems, so J2SE's
implementation is not alone.

The suggestion of falling back to file.encoding is an appealing one,
but it's quite dangerous to go down that route.

Encoding "autodetection" is a good interactive feature for users, but
it's not so good for file formats.  To have a file be properly readable
depending fairly randomly on the data bit patterns stored within it
is a reliability disaster.  It's much better to have consistent failure
than intermittent "success".

Re-architecting zip to record the encoding of the file names will
hopefully get done for J2SE 1.6.

 xxxxx@xxxxx  2003-11-25


I believe this is a duplicate of 4244499. See the evaluation of that bug report for a relatively simple proposed solution.
 xxxxx@xxxxx  2005-1-29 00:28:38 GMT

上面提出DA-LT-4BT0007点位离线,但未生成离线异常,请分析原因后生成完整修改后代码。生成package com.tongchuang.realtime.mds; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject; import com.tongchuang.realtime.bean.ULEParamConfig; import com.tongchuang.realtime.util.KafkaUtils; import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.api.common.state.*; import org.apache.flink.api.common.time.Time; import org.apache.flink.api.common.typeinfo.BasicTypeInfo; import org.apache.flink.api.common.typeinfo.TypeHint; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.configuration.Configuration; import org.apache.flink.connector.kafka.source.KafkaSource; import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer; import org.apache.flink.streaming.api.datastream.*; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.functions.co.KeyedBroadcastProcessFunction; import org.apache.flink.streaming.api.functions.source.RichSourceFunction; import org.apache.flink.util.Collector; import org.apache.flink.util.OutputTag; import java.io.Serializable; import java.sql.*; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Duration; import java.util.*; import java.util.Date; import java.util.concurrent.TimeUnit; public class ULEDataanomalyanalysis { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); env.getConfig().setAutoWatermarkInterval(1000); // Kafka消费者配置 KafkaSource<String> kafkaConsumer = KafkaUtils.getKafkaConsumer( "realdata_minute", "minutedata_uledataanomalyanalysis", OffsetsInitializer.latest() ); // 修改Watermark策略 DataStreamSource<String> kafkaDS = env.fromSource( kafkaConsumer, WatermarkStrategy.<String>forBoundedOutOfOrderness(Duration.ofMinutes(1)) .withTimestampAssigner((event, timestamp) -> { try { JSONObject json = JSON.parseObject(event); return new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(json.getString("times")).getTime(); } catch (ParseException e) { return System.currentTimeMillis(); } }), "realdata_uledataanomalyanalysis" ); kafkaDS.print("分钟数据流"); // 解析JSON并拆分tag SingleOutputStreamOperator<JSONObject> splitStream = kafkaDS .map(JSON::parseObject) .returns(TypeInformation.of(JSONObject.class)) .flatMap((JSONObject value, Collector<JSONObject> out) -> { JSONObject data = value.getJSONObject("datas"); String time = value.getString("times"); for (String tag : data.keySet()) { JSONObject tagData = data.getJSONObject(tag); JSONObject newObj = new JSONObject(); newObj.put("time", time); newObj.put("tag", tag); newObj.put("ontime", tagData.getDouble("ontime")); newObj.put("avg", tagData.getDouble("avg")); out.collect(newObj); } }) .returns(TypeInformation.of(JSONObject.class)) .name("Split-By-Tag"); // 配置数据源 DataStream<ConfigCollection> configDataStream = env .addSource(new MysqlConfigSource()) .setParallelism(1) .filter(Objects::nonNull) .name("Config-Source"); // 标签值数据流 DataStream<Map<String, Object>> tagValueStream = splitStream .map(json -> { Map<String, Object> valueMap = new HashMap<>(); valueMap.put("tag", json.getString("tag")); valueMap.put("value", "436887485805570949".equals(json.getString("datatype")) ? json.getDouble("ontime") : json.getDouble("avg")); return valueMap; }) .returns(new TypeHint<Map<String, Object>>() {}) .name("Tag-Value-Stream"); // 合并配置流和标签值流 DataStream<Object> broadcastStream = configDataStream .map(config -> (Object) config) .returns(TypeInformation.of(Object.class)) .union( tagValueStream.map(tagValue -> (Object) tagValue) .returns(TypeInformation.of(Object.class)) ); // 广播流 BroadcastStream<Object> finalBroadcastStream = broadcastStream .broadcast(Descriptors.configStateDescriptor, Descriptors.tagValuesDescriptor); // 按tag分组 KeyedStream<JSONObject, String> keyedStream = splitStream .keyBy(json -> json.getString("tag")); BroadcastConnectedStream<JSONObject, Object> connectedStream = keyedStream.connect(finalBroadcastStream); // 异常检测处理 SingleOutputStreamOperator<JSONObject> anomalyStream = connectedStream .process(new OptimizedAnomalyDetectionFunction()) .name("Anomaly-Detection"); anomalyStream.print("异常检测结果"); // 离线检测结果侧输出 DataStream<String> offlineCheckStream = anomalyStream.getSideOutput(OptimizedAnomalyDetectionFunction.OFFLINE_CHECK_TAG); offlineCheckStream.print("离线检测结果"); env.execute("uledataanomalyanalysis"); } // 配置集合类 public static class ConfigCollection implements Serializable { private static final long serialVersionUID = 1L; public final Map<String, List<ULEParamConfig>> tagToConfigs; public final Map<String, ULEParamConfig> encodeToConfig; public final Set<String> allTags; public final long checkpointTime; public ConfigCollection(Map<String, List<ULEParamConfig>> tagToConfigs, Map<String, ULEParamConfig> encodeToConfig) { this.tagToConfigs = new HashMap<>(tagToConfigs); this.encodeToConfig = new HashMap<>(encodeToConfig); this.allTags = new HashSet<>(tagToConfigs.keySet()); this.checkpointTime = System.currentTimeMillis(); } } // MySQL配置源 public static class MysqlConfigSource extends RichSourceFunction<ConfigCollection> { private volatile boolean isRunning = true; private final long interval = TimeUnit.MINUTES.toMillis(5); @Override public void run(SourceContext<ConfigCollection> ctx) throws Exception { while (isRunning) { ConfigCollection newConfig = loadParams(); if (newConfig != null) { ctx.collect(newConfig); System.out.println("配置加载完成,检查点时间: " + new Date(newConfig.checkpointTime)); } else { System.out.println("配置加载失败"); } Thread.sleep(interval); } } private ConfigCollection loadParams() { Map<String, List<ULEParamConfig>> tagToConfigs = new HashMap<>(5000); Map<String, ULEParamConfig> encodeToConfig = new HashMap<>(5000); String url = "jdbc:mysql://10.51.37.73:3306/eps?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"; String user = "root"; String password = "6CKIm5jDVsLrahSw"; String query = "SELECT F_tag AS tag, F_enCode AS encode, F_dataTypes AS datatype, " + "F_isConstantValue AS constantvalue, F_isOnline AS isonline, " + "F_isSync AS issync, F_syncParaEnCode AS syncparaencode, " + "F_isZero AS iszero, F_isHigh AS ishigh, F_highThreshold AS highthreshold, " + "F_isLow AS islow, F_lowThreshold AS lowthreshold, F_duration AS duration " + "FROM t_equipmentparameter " + "WHERE F_enabledmark = '1' AND (F_isConstantValue='1' OR F_isZero='1' " + "OR F_isHigh='1' OR F_isLow='1' OR F_isOnline='1' OR F_isSync='1')"; try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query)) { while (rs.next()) { ULEParamConfig config = new ULEParamConfig(); config.tag = rs.getString("tag"); config.encode = rs.getString("encode"); config.datatype = rs.getString("datatype"); config.constantvalue = rs.getInt("constantvalue"); config.iszero = rs.getInt("iszero"); config.ishigh = rs.getInt("ishigh"); config.highthreshold = rs.getDouble("highthreshold"); config.islow = rs.getInt("islow"); config.lowthreshold = rs.getDouble("lowthreshold"); config.duration = rs.getLong("duration"); config.isonline = rs.getInt("isonline"); config.issync = rs.getInt("issync"); config.syncparaencode = rs.getString("syncparaencode"); if (config.encode == null || config.encode.isEmpty()) { System.err.println("忽略无效配置: 空encode"); continue; } String tag = config.tag; tagToConfigs.computeIfAbsent(tag, k -> new ArrayList<>(10)).add(config); encodeToConfig.put(config.encode, config); } System.out.println("加载配置: " + encodeToConfig.size() + " 个参数"); return new ConfigCollection(tagToConfigs, encodeToConfig); } catch (SQLException e) { System.err.println("加载参数配置错误:"); e.printStackTrace(); return null; } } @Override public void cancel() { isRunning = false; } } // 状态描述符 public static class Descriptors { public static final MapStateDescriptor<Void, ConfigCollection> configStateDescriptor = new MapStateDescriptor<>( "configState", TypeInformation.of(Void.class), TypeInformation.of(ConfigCollection.class) ); public static final MapStateDescriptor<String, Double> tagValuesDescriptor = new MapStateDescriptor<>( "tagValuesBroadcastState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.DOUBLE_TYPE_INFO ); } // 优化后的异常检测函数(重点修复离线检测) public static class OptimizedAnomalyDetectionFunction extends KeyedBroadcastProcessFunction<String, JSONObject, Object, JSONObject> { public static final OutputTag<String> OFFLINE_CHECK_TAG = new OutputTag<String>("offline-check"){}; // 状态管理 private transient MapState<String, AnomalyState> stateMap; private transient MapState<String, Double> lastValuesMap; private transient MapState<String, Long> lastDataTimeMap; private transient MapState<String, Long> offlineTimerState; private transient MapState<String, Long> tagInitTimeState; // 新增:标签初始化时间状态 private transient SimpleDateFormat timeFormat; private transient long lastSyncLogTime = 0; @Override public void open(Configuration parameters) { StateTtlConfig ttlConfig = StateTtlConfig.newBuilder(Time.days(30)) .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) .cleanupFullSnapshot() .build(); // 异常状态存储 MapStateDescriptor<String, AnomalyState> stateDesc = new MapStateDescriptor<>( "anomalyState", BasicTypeInfo.STRING_TYPE_INFO, TypeInformation.of(AnomalyState.class) ); stateDesc.enableTimeToLive(ttlConfig); stateMap = getRuntimeContext().getMapState(stateDesc); // 最新值存储 MapStateDescriptor<String, Double> valuesDesc = new MapStateDescriptor<>( "lastValuesState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.DOUBLE_TYPE_INFO ); valuesDesc.enableTimeToLive(ttlConfig); lastValuesMap = getRuntimeContext().getMapState(valuesDesc); // 最后数据时间存储 MapStateDescriptor<String, Long> timeDesc = new MapStateDescriptor<>( "lastDataTimeState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timeDesc.enableTimeToLive(ttlConfig); lastDataTimeMap = getRuntimeContext().getMapState(timeDesc); // 离线检测定时器状态 MapStateDescriptor<String, Long> timerDesc = new MapStateDescriptor<>( "offlineTimerState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timerDesc.enableTimeToLive(ttlConfig); offlineTimerState = getRuntimeContext().getMapState(timerDesc); // 新增:标签初始化时间状态 MapStateDescriptor<String, Long> initTimeDesc = new MapStateDescriptor<>( "tagInitTimeState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); initTimeDesc.enableTimeToLive(ttlConfig); tagInitTimeState = getRuntimeContext().getMapState(initTimeDesc); timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); } @Override public void processElement(JSONObject data, ReadOnlyContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); String timeStr = data.getString("time"); long eventTime = timeFormat.parse(timeStr).getTime(); // 更新最后数据时间 lastDataTimeMap.put(tag, eventTime); // 获取广播配置 ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) return; List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null || configs.isEmpty()) return; // 清理无效状态 List<String> keysToRemove = new ArrayList<>(); for (String encode : stateMap.keys()) { boolean found = false; for (ULEParamConfig cfg : configs) { if (cfg.encode.equals(encode)) { found = true; break; } } if (!found) { System.out.println("清理过期状态: " + encode); keysToRemove.add(encode); } } for (String encode : keysToRemove) { stateMap.remove(encode); } double value = 0; boolean valueSet = false; // 检查是否需要离线检测 boolean hasOnlineConfig = false; long minDuration = Long.MAX_VALUE; for (ULEParamConfig config : configs) { if (config.isonline == 1) { hasOnlineConfig = true; minDuration = Math.min(minDuration, config.duration); // 初始化标签检测时间(如果尚未初始化) if (!tagInitTimeState.contains(tag)) { tagInitTimeState.put(tag, System.currentTimeMillis()); } } } // 管理离线检测定时器 if (hasOnlineConfig) { // 删除现有定时器 Long currentTimer = offlineTimerState.get(tag); if (currentTimer != null) { ctx.timerService().deleteEventTimeTimer(currentTimer); } // 注册新定时器(使用最小duration) long offlineTimeout = eventTime + minDuration * 60 * 1000; ctx.timerService().registerEventTimeTimer(offlineTimeout); offlineTimerState.put(tag, offlineTimeout); // 重置离线状态(数据到达表示恢复) for (ULEParamConfig config : configs) { if (config.isonline == 1) { AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(6); if (status.reported) { reportAnomaly(6, 0, 0.0, timeStr, config, out); status.reset(); stateMap.put(config.encode, state); System.out.println("离线恢复: tag=" + tag + ", encode=" + config.encode); } } } } // 遍历配置项进行异常检测 for (ULEParamConfig config : configs) { if (!valueSet) { value = "436887485805570949".equals(config.datatype) ? data.getDouble("ontime") : data.getDouble("avg"); lastValuesMap.put(tag, value); valueSet = true; } AnomalyState state = getOrCreateState(config.encode); // 处理异常类型 checkConstantValueAnomaly(config, value, timeStr, state, out); // 1. 恒值检测 checkZeroValueAnomaly(config, value, timeStr, state, out); // 2. 零值检测 checkThresholdAnomaly(config, value, timeStr, state, out); // 3. 上阈值, 4. 下阈值 checkSyncAnomaly(config, value, timeStr, state, configCollection, ctx, out); // 5. 同步检测 stateMap.put(config.encode, state); } } @Override public void onTimer(long timestamp, OnTimerContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); Long lastEventTime = lastDataTimeMap.get(tag); ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) return; List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null) return; // 检查所有需要离线检测的配置项 boolean hasOnlineConfig = false; for (ULEParamConfig config : configs) { if (config.isonline == 1) { hasOnlineConfig = true; AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(6); // 核心修复:处理从未收到数据的情况 if (lastEventTime == null) { // 获取标签初始化时间 Long initTime = tagInitTimeState.get(tag); if (initTime == null) { // 如果状态中不存在,使用配置加载时间 initTime = configCollection.checkpointTime; tagInitTimeState.put(tag, initTime); } // 检查是否超过配置的duration long elapsed = System.currentTimeMillis() - initTime; long durationMs = config.duration * 60 * 1000; if (elapsed >= durationMs) { if (!status.reported) { String triggerTime = timeFormat.format(new Date()); reportAnomaly(6, 1, 0.0, triggerTime, config, out); status.reported = true; // 输出到侧输出流 ctx.output(OFFLINE_CHECK_TAG, String.format("离线异常(从未收到数据): tag=%s, encode=%s, 初始化时间=%s, 当前时间=%s, 时间差=%dms (阈值=%dms)", config.tag, config.encode, timeFormat.format(new Date(initTime)), triggerTime, elapsed, durationMs) ); System.out.println("检测到从未接收数据的离线异常: " + config.tag); } } } // 处理已有数据但超时的情况 else if (timestamp - lastEventTime >= config.duration * 60 * 1000) { if (!status.reported) { String triggerTime = timeFormat.format(new Date(timestamp)); reportAnomaly(6, 1, 0.0, triggerTime, config, out); status.reported = true; ctx.output(OFFLINE_CHECK_TAG, String.format("离线异常: tag=%s, encode=%s, 最后数据时间=%s, 超时时间=%s, 时间差=%dms (阈值=%dms)", config.tag, config.encode, timeFormat.format(new Date(lastEventTime)), triggerTime, timestamp - lastEventTime, config.duration * 60 * 1000) ); System.out.println("检测到数据中断的离线异常: " + config.tag); } } else { // 数据已恢复,重置状态 if (status.reported) { reportAnomaly(6, 0, 0.0, timeFormat.format(new Date()), config, out); status.reset(); System.out.println("离线状态恢复: " + config.tag); } } stateMap.put(config.encode, state); } } // 重新注册定时器(每分钟检查一次) if (hasOnlineConfig) { long newTimeout = timestamp + TimeUnit.MINUTES.toMillis(1); ctx.timerService().registerEventTimeTimer(newTimeout); offlineTimerState.put(tag, newTimeout); } else { offlineTimerState.remove(tag); } } // 恒值检测 - 异常类型1 private void checkConstantValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.constantvalue != 1) return; try { AnomalyStatus status = state.getStatus(1); long durationThreshold = config.duration * 60 * 1000; Date timestamp = timeFormat.parse(timeStr); if (status.lastValue == null) { status.lastValue = currentValue; status.lastChangeTime = timestamp; return; } if (Math.abs(currentValue - status.lastValue) > 0.001) { status.lastValue = currentValue; status.lastChangeTime = timestamp; if (status.reported) { reportAnomaly(1, 0, currentValue, timeStr, config, out); } status.reset(); return; } long elapsed = timestamp.getTime() - status.lastChangeTime.getTime(); if (elapsed > durationThreshold) { if (!status.reported) { reportAnomaly(1, 1, currentValue, timeStr, config, out); status.reported = true; } } } catch (Exception e) { System.err.println("恒值检测错误: " + config.encode + " - " + e.getMessage()); } } // 零值检测 - 异常类型2 private void checkZeroValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.iszero != 1) return; try { AnomalyStatus status = state.getStatus(2); Date timestamp = timeFormat.parse(timeStr); boolean isZero = Math.abs(currentValue) < 0.001; if (isZero) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(2, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(2, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("零值检测错误: " + config.encode + " - " + e.getMessage()); } } // 阈值检测 - 异常类型3(上阈值)和4(下阈值) private void checkThresholdAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { try { if (config.ishigh == 1) { AnomalyStatus highStatus = state.getStatus(3); processThresholdAnomaly(highStatus, currentValue, timeStr, currentValue > config.highthreshold, config, 3, out); } if (config.islow == 1) { AnomalyStatus lowStatus = state.getStatus(4); processThresholdAnomaly(lowStatus, currentValue, timeStr, currentValue < config.lowthreshold, config, 4, out); } } catch (Exception e) { System.err.println("阈值检测错误: " + config.encode + " - " + e.getMessage()); } } private void processThresholdAnomaly(AnomalyStatus status, double currentValue, String timeStr, boolean isAnomaly, ULEParamConfig config, int anomalyType, Collector<JSONObject> out) { try { Date timestamp = timeFormat.parse(timeStr); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(anomalyType, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(anomalyType, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("阈值处理错误: " + config.encode + " - " + e.getMessage()); } } // 同步检测 - 异常类型5(优化日志输出) private void checkSyncAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, ConfigCollection configCollection, ReadOnlyContext ctx, Collector<JSONObject> out) { if (config.issync != 1 || config.syncparaencode == null || config.syncparaencode.isEmpty()) { return; } try { // 通过encode获取关联配置 ULEParamConfig relatedConfig = configCollection.encodeToConfig.get(config.syncparaencode); if (relatedConfig == null) { if (System.currentTimeMillis() - lastSyncLogTime > 60000) { System.out.println("同步检测错误: 未找到关联配置, encode=" + config.syncparaencode); lastSyncLogTime = System.currentTimeMillis(); } return; } // 获取关联配置的tag String relatedTag = relatedConfig.tag; if (relatedTag == null || relatedTag.isEmpty()) { if (System.currentTimeMillis() - lastSyncLogTime > 60000) { System.out.println("同步检测错误: 关联配置没有tag, encode=" + config.syncparaencode); lastSyncLogTime = System.currentTimeMillis(); } return; } // 从广播状态获取关联值 ReadOnlyBroadcastState<String, Double> tagValuesState = ctx.getBroadcastState(Descriptors.tagValuesDescriptor); Double relatedValue = tagValuesState.get(relatedTag); if (relatedValue == null) { if (System.currentTimeMillis() - lastSyncLogTime > 60000) { // 优化日志:添加当前标签信息 System.out.printf("同步检测警告: 关联值未初始化 [主标签=%s(%s), 关联标签=%s(%s)]%n", config.tag, config.encode, relatedTag, config.syncparaencode); lastSyncLogTime = System.currentTimeMillis(); } return; } // 同步检测逻辑 AnomalyStatus status = state.getStatus(5); Date timestamp = timeFormat.parse(timeStr); // 业务逻辑:当前值接近1且关联值接近0时异常 boolean isAnomaly = (currentValue >= 0.99) && (Math.abs(relatedValue) < 0.01); // 日志记录 if (System.currentTimeMillis() - lastSyncLogTime > 60000) { System.out.printf("同步检测: %s (%.4f) vs %s (%.4f) -> %b%n", config.tag, currentValue, relatedTag, relatedValue, isAnomaly); lastSyncLogTime = System.currentTimeMillis(); } // 处理异常状态 if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(5, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(5, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (ParseException e) { System.err.println("同步检测时间解析错误: " + config.encode + " - " + e.getMessage()); } catch (Exception e) { System.err.println("同步检测错误: " + config.encode + " - " + e.getMessage()); } } // 报告异常(添加详细日志) private void reportAnomaly(int anomalyType, int statusFlag, double value, String time, ULEParamConfig config, Collector<JSONObject> out) { JSONObject event = new JSONObject(); event.put("tag", config.tag); event.put("paracode", config.encode); event.put("abnormaltype", anomalyType); event.put("statusflag", statusFlag); event.put("datavalue", value); event.put("triggertime", time); out.collect(event); // 添加详细日志输出 String statusDesc = statusFlag == 1 ? "异常开始" : "异常结束"; System.out.printf("报告异常: 类型=%d, 状态=%s, 标签=%s, 编码=%s, 时间=%s%n", anomalyType, statusDesc, config.tag, config.encode, time); } @Override public void processBroadcastElement(Object broadcastElement, Context ctx, Collector<JSONObject> out) throws Exception { // 处理配置更新 if (broadcastElement instanceof ConfigCollection) { ConfigCollection newConfig = (ConfigCollection) broadcastElement; BroadcastState<Void, ConfigCollection> configState = ctx.getBroadcastState(Descriptors.configStateDescriptor); // 获取旧配置 ConfigCollection oldConfig = configState.get(null); // 处理配置变更:清理不再启用的报警 if (oldConfig != null) { for (Map.Entry<String, ULEParamConfig> entry : oldConfig.encodeToConfig.entrySet()) { String encode = entry.getKey(); ULEParamConfig oldCfg = entry.getValue(); ULEParamConfig newCfg = newConfig.encodeToConfig.get(encode); if (newCfg == null || !isAlarmEnabled(newCfg, oldCfg)) { // 发送恢复事件 sendRecoveryEvents(encode, oldCfg, ctx, out); } } } // 更新广播状态 configState.put(null, newConfig); System.out.println("广播配置更新完成, 配置项: " + newConfig.encodeToConfig.size()); // 新增:初始化所有需要在线检测的标签 for (String tag : newConfig.allTags) { List<ULEParamConfig> configs = newConfig.tagToConfigs.get(tag); if (configs != null) { for (ULEParamConfig config : configs) { if (config.isonline == 1) { // 初始化标签检测时间 if (!tagInitTimeState.contains(tag)) { long initTime = System.currentTimeMillis(); tagInitTimeState.put(tag, initTime); System.out.println("初始化在线检测: tag=" + tag + ", 时间=" + timeFormat.format(new Date(initTime))); } } } } } } // 处理标签值更新 else if (broadcastElement instanceof Map) { @SuppressWarnings("unchecked") Map<String, Object> tagValue = (Map<String, Object>) broadcastElement; String tag = (String) tagValue.get("tag"); Double value = (Double) tagValue.get("value"); if (tag != null && value != null) { BroadcastState<String, Double> tagValuesState = ctx.getBroadcastState(Descriptors.tagValuesDescriptor); tagValuesState.put(tag, value); } } } // 检查报警是否启用 private boolean isAlarmEnabled(ULEParamConfig newCfg, ULEParamConfig oldCfg) { return (oldCfg.constantvalue == 1 && newCfg.constantvalue == 1) || (oldCfg.iszero == 1 && newCfg.iszero == 1) || (oldCfg.ishigh == 1 && newCfg.ishigh == 1) || (oldCfg.islow == 1 && newCfg.islow == 1) || (oldCfg.isonline == 1 && newCfg.isonline == 1) || (oldCfg.issync == 1 && newCfg.issync == 1); } // 发送恢复事件 private void sendRecoveryEvents(String encode, ULEParamConfig config, Context ctx, Collector<JSONObject> out) { try { AnomalyState state = stateMap.get(encode); if (state == null) return; // 遍历所有可能的报警类型 for (int type = 1; type <= 6; type++) { AnomalyStatus status = state.getStatus(type); if (status.reported) { JSONObject recoveryEvent = new JSONObject(); recoveryEvent.put("tag", config.tag); recoveryEvent.put("paracode", config.encode); recoveryEvent.put("abnormaltype", type); recoveryEvent.put("statusflag", 0); // 恢复事件 recoveryEvent.put("datavalue", 0.0); recoveryEvent.put("triggertime", timeFormat.format(new Date())); out.collect(recoveryEvent); System.out.println("发送恢复事件: 类型=" + type + ", 标签=" + config.tag); status.reset(); } } // 更新状态 stateMap.put(encode, state); } catch (Exception e) { System.err.println("发送恢复事件失败: " + e.getMessage()); } } // 辅助方法 private ConfigCollection getBroadcastConfig(ReadOnlyContext ctx) throws Exception { return ctx.getBroadcastState(Descriptors.configStateDescriptor).get(null); } private AnomalyState getOrCreateState(String encode) throws Exception { AnomalyState state = stateMap.get(encode); if (state == null) { state = new AnomalyState(); } return state; } } // 异常状态类 public static class AnomalyState implements Serializable { private static final long serialVersionUID = 1L; private final Map<Integer, AnomalyStatus> statusMap = new HashMap<>(); public AnomalyStatus getStatus(int type) { return statusMap.computeIfAbsent(type, k -> new AnomalyStatus()); } } // 异常状态详情 public static class AnomalyStatus implements Serializable { private static final long serialVersionUID = 1L; public Date startTime; // 异常开始时间 public Double lastValue; // 用于恒值检测 public Date lastChangeTime; // 值最后变化时间 public boolean reported; // 是否已报告 public void reset() { startTime = null; lastValue = null; lastChangeTime = null; reported = false; } } } 运行时日志为:"C:\Program Files (x86)\Java\jdk1.8.0_102\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:21096,suspend=y,server=n -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.2\captureAgent\debugger-agent.jar=file:/C:/Users/Administrator/AppData/Local/Temp/capture.props -Dfile.encoding=UTF-8 -classpath C:\Users\Administrator\AppData\Local\Temp\classpath849000959.jar com.tongchuang.realtime.mds.ULEDataanomalyanalysis 已连接到目标 VM, 地址: ''127.0.0.1:21096',传输: '套接字'' SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory] 加载配置: 30 个参数 配置加载完成,检查点时间: Wed Aug 06 07:55:06 CST 2025 广播配置更新完成, 配置项: 30 Exception in thread "main" org.apache.flink.runtime.client.JobExecutionException: Job execution failed. at org.apache.flink.runtime.jobmaster.JobResult.toJobExecutionResult(JobResult.java:144) at org.apache.flink.runtime.minicluster.MiniClusterJobClient.lambda$getJobExecutionResult$3(MiniClusterJobClient.java:137) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:602) at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:577) at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474) at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1962) at org.apache.flink.runtime.rpc.akka.AkkaInvocationHandler.lambda$invokeRpc$0(AkkaInvocationHandler.java:237) at java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:760) at java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:736) at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474) at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1962) at org.apache.flink.runtime.concurrent.FutureUtils$1.onComplete(FutureUtils.java:1081) at akka.dispatch.OnComplete.internal(Future.scala:264) at akka.dispatch.OnComplete.internal(Future.scala:261) at akka.dispatch.japi$CallbackBridge.apply(Future.scala:191) at akka.dispatch.japi$CallbackBridge.apply(Future.scala:188) at scala.concurrent.impl.CallbackRunnable.run$$$capture(Promise.scala:60) at scala.concurrent.impl.CallbackRunnable.run(Promise.scala) at org.apache.flink.runtime.concurrent.Executors$DirectExecutionContext.execute(Executors.java:73) at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:68) at scala.concurrent.impl.Promise$DefaultPromise.$anonfun$tryComplete$1(Promise.scala:284) at scala.concurrent.impl.Promise$DefaultPromise.$anonfun$tryComplete$1$adapted(Promise.scala:284) at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:284) at akka.pattern.PromiseActorRef.$bang(AskSupport.scala:573) at akka.pattern.PipeToSupport$PipeableFuture$$anonfun$pipeTo$1.applyOrElse(PipeToSupport.scala:22) at akka.pattern.PipeToSupport$PipeableFuture$$anonfun$pipeTo$1.applyOrElse(PipeToSupport.scala:21) at scala.concurrent.Future.$anonfun$andThen$1(Future.scala:532) at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:29) at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:29) at scala.concurrent.impl.CallbackRunnable.run$$$capture(Promise.scala:60) at scala.concurrent.impl.CallbackRunnable.run(Promise.scala) at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:55) at akka.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:91) at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12) at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:81) at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:91) at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40) at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(ForkJoinExecutorConfigurator.scala:44) at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) Caused by: org.apache.flink.runtime.JobException: Recovery is suppressed by NoRestartBackoffTimeStrategy at org.apache.flink.runtime.executiongraph.failover.flip1.ExecutionFailureHandler.handleFailure(ExecutionFailureHandler.java:138) at org.apache.flink.runtime.executiongraph.failover.flip1.ExecutionFailureHandler.getFailureHandlingResult(ExecutionFailureHandler.java:82) at org.apache.flink.runtime.scheduler.DefaultScheduler.handleTaskFailure(DefaultScheduler.java:216) at org.apache.flink.runtime.scheduler.DefaultScheduler.maybeHandleTaskFailure(DefaultScheduler.java:206) at org.apache.flink.runtime.scheduler.DefaultScheduler.updateTaskExecutionStateInternal(DefaultScheduler.java:197) at org.apache.flink.runtime.scheduler.SchedulerBase.updateTaskExecutionState(SchedulerBase.java:682) at org.apache.flink.runtime.scheduler.SchedulerNG.updateTaskExecutionState(SchedulerNG.java:79) at org.apache.flink.runtime.jobmaster.JobMaster.updateTaskExecutionState(JobMaster.java:435) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.flink.runtime.rpc.akka.AkkaRpcActor.handleRpcInvocation(AkkaRpcActor.java:305) at org.apache.flink.runtime.rpc.akka.AkkaRpcActor.handleRpcMessage(AkkaRpcActor.java:212) at org.apache.flink.runtime.rpc.akka.FencedAkkaRpcActor.handleRpcMessage(FencedAkkaRpcActor.java:77) at org.apache.flink.runtime.rpc.akka.AkkaRpcActor.handleMessage(AkkaRpcActor.java:158) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:26) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:21) at scala.PartialFunction.applyOrElse(PartialFunction.scala:123) at scala.PartialFunction.applyOrElse$(PartialFunction.scala:122) at akka.japi.pf.UnitCaseStatement.applyOrElse(CaseStatements.scala:21) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:171) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:172) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:172) at akka.actor.Actor.aroundReceive(Actor.scala:517) at akka.actor.Actor.aroundReceive$(Actor.scala:515) at akka.actor.AbstractActor.aroundReceive(AbstractActor.scala:225) at akka.actor.ActorCell.receiveMessage$$$capture(ActorCell.scala:592) at akka.actor.ActorCell.receiveMessage(ActorCell.scala) at akka.actor.ActorCell.invoke(ActorCell.scala:561) at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:258) at akka.dispatch.Mailbox.run(Mailbox.scala:225) at akka.dispatch.Mailbox.exec(Mailbox.scala:235) ... 4 more Caused by: java.lang.NullPointerException: No key set. This method should not be called outside of a keyed context. at org.apache.flink.util.Preconditions.checkNotNull(Preconditions.java:76) at org.apache.flink.runtime.state.heap.StateTable.checkKeyNamespacePreconditions(StateTable.java:270) at org.apache.flink.runtime.state.heap.StateTable.get(StateTable.java:260) at org.apache.flink.runtime.state.heap.StateTable.get(StateTable.java:143) at org.apache.flink.runtime.state.heap.HeapMapState.get(HeapMapState.java:86) at org.apache.flink.runtime.state.ttl.TtlMapState.lambda$getWrapped$0(TtlMapState.java:64) at org.apache.flink.runtime.state.ttl.AbstractTtlDecorator.getWrappedWithTtlCheckAndUpdate(AbstractTtlDecorator.java:97) at org.apache.flink.runtime.state.ttl.TtlMapState.getWrapped(TtlMapState.java:63) at org.apache.flink.runtime.state.ttl.TtlMapState.contains(TtlMapState.java:96) at org.apache.flink.runtime.state.UserFacingMapState.contains(UserFacingMapState.java:72) at com.tongchuang.realtime.mds.ULEDataanomalyanalysis$OptimizedAnomalyDetectionFunction.processBroadcastElement(ULEDataanomalyanalysis.java:767) at org.apache.flink.streaming.api.operators.co.CoBroadcastWithKeyedOperator.processElement2(CoBroadcastWithKeyedOperator.java:133) at org.apache.flink.streaming.runtime.io.StreamTwoInputProcessorFactory.processRecord2(StreamTwoInputProcessorFactory.java:221) at org.apache.flink.streaming.runtime.io.StreamTwoInputProcessorFactory.lambda$create$1(StreamTwoInputProcessorFactory.java:190) at org.apache.flink.streaming.runtime.io.StreamTwoInputProcessorFactory$StreamTaskNetworkOutput.emitRecord(StreamTwoInputProcessorFactory.java:291) at org.apache.flink.streaming.runtime.io.AbstractStreamTaskNetworkInput.processElement(AbstractStreamTaskNetworkInput.java:134) at org.apache.flink.streaming.runtime.io.AbstractStreamTaskNetworkInput.emitNext(AbstractStreamTaskNetworkInput.java:105) at org.apache.flink.streaming.runtime.io.StreamOneInputProcessor.processInput(StreamOneInputProcessor.java:66) at org.apache.flink.streaming.runtime.io.StreamTwoInputProcessor.processInput(StreamTwoInputProcessor.java:98) at org.apache.flink.streaming.runtime.tasks.StreamTask.processInput(StreamTask.java:423) at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.runMailboxLoop(MailboxProcessor.java:204) at org.apache.flink.streaming.runtime.tasks.StreamTask.runMailboxLoop(StreamTask.java:684) at org.apache.flink.streaming.runtime.tasks.StreamTask.executeInvoke(StreamTask.java:639) at org.apache.flink.streaming.runtime.tasks.StreamTask.runWithCleanUpOnFail(StreamTask.java:650) at org.apache.flink.streaming.runtime.tasks.StreamTask.invoke(StreamTask.java:623) at org.apache.flink.runtime.taskmanager.Task.doRun(Task.java:779) at org.apache.flink.runtime.taskmanager.Task.run(Task.java:566) at java.lang.Thread.run(Thread.java:745) 与目标 VM 断开连接, 地址为: ''127.0.0.1:21096',传输: '套接字'' 进程已结束,退出代码为 1 请生成完整修复后代码。
最新发布
08-07
package com.tongchuang.realtime.mds; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.tongchuang.realtime.bean.ULEParamConfig; import com.tongchuang.realtime.util.KafkaUtils; import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.api.common.state.*; import org.apache.flink.api.common.time.Time; import org.apache.flink.api.common.typeinfo.BasicTypeInfo; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.configuration.Configuration; import org.apache.flink.connector.kafka.source.KafkaSource; import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer; import org.apache.flink.streaming.api.datastream.*; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.functions.co.KeyedBroadcastProcessFunction; import org.apache.flink.streaming.api.functions.source.RichSourceFunction; import org.apache.flink.util.Collector; import org.apache.flink.util.OutputTag; import java.io.Serializable; import java.sql.*; import java.text.SimpleDateFormat; import java.util.*; import java.util.Date; import java.util.concurrent.TimeUnit; public class ULEDataanomalyanalysis { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); // 创建Kafka消费者 KafkaSource<String> kafkaConsumer = KafkaUtils.getKafkaConsumer( "realdata_minute", "minutedata_uledataanomalyanalysis", OffsetsInitializer.latest() ); DataStreamSource<String> kafkaDS = env.fromSource( kafkaConsumer, WatermarkStrategy.noWatermarks(), "realdata_uledataanomalyanalysis" ); // 解析JSON并拆分每个tag的数据 SingleOutputStreamOperator<JSONObject> splitStream = kafkaDS .map(JSON::parseObject) .flatMap((JSONObject value, Collector<JSONObject> out) -> { JSONObject data = value.getJSONObject("datas"); String time = value.getString("times"); for (String tag : data.keySet()) { JSONObject tagData = data.getJSONObject(tag); JSONObject newObj = new JSONObject(); newObj.put("time", time); newObj.put("tag", tag); newObj.put("ontime", tagData.getDouble("ontime")); newObj.put("avg", tagData.getDouble("avg")); out.collect(newObj); } }) .returns(TypeInformation.of(JSONObject.class)) .name("Split-By-Tag"); // 每5分钟加载参数配置 DataStream<ConfigCollection> configDataStream = env .addSource(new MysqlConfigSource()) .setParallelism(1) .filter(Objects::nonNull) .name("Config-Source"); // 将配置流转换为广播流 BroadcastStream<ConfigCollection> configBroadcastStream = configDataStream .broadcast(Descriptors.configStateDescriptor); // 按tag分组并连接广播流 KeyedStream<JSONObject, String> keyedStream = splitStream .keyBy(json -> json.getString("tag")); BroadcastConnectedStream<JSONObject, ConfigCollection> connectedStream = keyedStream.connect(configBroadcastStream); // 异常检测处理 SingleOutputStreamOperator<JSONObject> anomalyStream = connectedStream .process(new OptimizedAnomalyDetectionFunction()) .name("Anomaly-Detection"); anomalyStream.print("异常检测结果"); // anomalyStream.map(JSON::toString).addSink(KafkaUtils.getKafkaSink("minutedata_uleanomaly")); env.execute("uledataanomalyanalysis"); } // 配置集合类 - 添加检查点时间 public static class ConfigCollection implements Serializable { private static final long serialVersionUID = 1L; public final Map<String, List<ULEParamConfig>> tagToConfigs; public final Map<String, ULEParamConfig> encodeToConfig; public final Set<String> allTags; public final long checkpointTime; // 配置加载的时间戳 public ConfigCollection(Map<String, List<ULEParamConfig>> tagToConfigs, Map<String, ULEParamConfig> encodeToConfig) { this.tagToConfigs = new HashMap<>(tagToConfigs); this.encodeToConfig = new HashMap<>(encodeToConfig); this.allTags = new HashSet<>(tagToConfigs.keySet()); this.checkpointTime = System.currentTimeMillis(); // 记录配置加载时间 } } // MySQL配置源 public static class MysqlConfigSource extends RichSourceFunction<ConfigCollection> { private volatile boolean isRunning = true; private final long interval = TimeUnit.MINUTES.toMillis(5); @Override public void run(SourceContext<ConfigCollection> ctx) throws Exception { while (isRunning) { ConfigCollection newConfig = loadParams(); if (newConfig != null) { ctx.collect(newConfig); System.out.println("配置加载完成,检查点时间: " + new Date(newConfig.checkpointTime)); } else { System.out.println("配置加载失败"); } Thread.sleep(interval); } } private ConfigCollection loadParams() { Map<String, List<ULEParamConfig>> tagToConfigs = new HashMap<>(5000); Map<String, ULEParamConfig> encodeToConfig = new HashMap<>(5000); String url = "jdbc:mysql://10.51.37.73:3306/eps?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"; String user = "root"; String password = "6CKIm5jDVsLrahSw"; String query = "SELECT F_tag AS tag, F_enCode AS encode, F_dataTypes AS datatype, " + "F_isConstantValue AS constantvalue, F_isOnline AS isonline, " + "F_isSync AS issync, F_syncParaEnCode AS syncparaencode, " + "F_isZero AS iszero, F_isHigh AS ishigh, F_highThreshold AS highthreshold, " + "F_isLow AS islow, F_lowThreshold AS lowthreshold, F_duration AS duration " + "FROM t_equipmentparameter " + "WHERE F_enabledmark = '1' AND (F_isConstantValue ='1' OR F_isZero= '1' " + "OR F_isHigh = '1' OR F_isLow = '1' OR F_isOnline = '1' OR F_isSync = '1')"; try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query)) { while (rs.next()) { ULEParamConfig config = new ULEParamConfig(); config.tag = rs.getString("tag"); config.encode = rs.getString("encode"); config.datatype = rs.getString("datatype"); config.constantvalue = rs.getInt("constantvalue"); config.iszero = rs.getInt("iszero"); config.ishigh = rs.getInt("ishigh"); config.highthreshold = rs.getDouble("highthreshold"); config.islow = rs.getInt("islow"); config.lowthreshold = rs.getDouble("lowthreshold"); config.duration = rs.getLong("duration"); config.isonline = rs.getInt("isonline"); config.issync = rs.getInt("issync"); config.syncparaencode = rs.getString("syncparaencode"); String tag = config.tag; tagToConfigs.computeIfAbsent(tag, k -> new ArrayList<>(10)).add(config); encodeToConfig.put(config.encode, config); } System.out.println("加载配置: " + encodeToConfig.size() + " 个参数"); return new ConfigCollection(tagToConfigs, encodeToConfig); } catch (SQLException e) { System.err.println("加载参数配置错误:"); e.printStackTrace(); return null; } } @Override public void cancel() { isRunning = false; } } // 状态描述符 public static class Descriptors { public static final MapStateDescriptor<Void, ConfigCollection> configStateDescriptor = new MapStateDescriptor<>( "configState", TypeInformation.of(Void.class), TypeInformation.of(ConfigCollection.class) ); } // 优化后的异常检测函数(无定时器实现) public static class OptimizedAnomalyDetectionFunction extends KeyedBroadcastProcessFunction<String, JSONObject, ConfigCollection, JSONObject> { // 状态管理 private transient MapState<String, AnomalyState> stateMap; // key=encode private transient MapState<String, Double> lastValuesMap; // key=tag private transient MapState<String, Long> lastDataTimeMap; // key=tag private transient ValueState<Long> lastCheckpointState; // 记录该tag上次处理的检查点时间 private transient SimpleDateFormat timeFormat; // 侧输出标签用于离线检测 private static final OutputTag<String> OFFLINE_CHECK_TAG = new OutputTag<String>("offline-check"){}; @Override public void open(Configuration parameters) { // 状态TTL配置(30天自动清理) StateTtlConfig ttlConfig = StateTtlConfig.newBuilder(Time.days(30)) .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) .cleanupFullSnapshot() .build(); // 初始化异常状态存储(启用TTL) MapStateDescriptor<String, AnomalyState> stateDesc = new MapStateDescriptor<>( "anomalyState", BasicTypeInfo.STRING_TYPE_INFO, TypeInformation.of(AnomalyState.class) ); stateDesc.enableTimeToLive(ttlConfig); stateMap = getRuntimeContext().getMapState(stateDesc); // 初始化最新值存储(启用TTL) MapStateDescriptor<String, Double> valuesDesc = new MapStateDescriptor<>( "lastValuesState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.DOUBLE_TYPE_INFO ); valuesDesc.enableTimeToLive(ttlConfig); lastValuesMap = getRuntimeContext().getMapState(valuesDesc); // 初始化最后数据时间存储(启用TTL) MapStateDescriptor<String, Long> timeDesc = new MapStateDescriptor<>( "lastDataTimeState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timeDesc.enableTimeToLive(ttlConfig); lastDataTimeMap = getRuntimeContext().getMapState(timeDesc); // 初始化检查点状态(记录上次处理的配置时间) ValueStateDescriptor<Long> checkpointDesc = new ValueStateDescriptor<>( "lastCheckpointState", BasicTypeInfo.LONG_TYPE_INFO ); checkpointDesc.enableTimeToLive(ttlConfig); lastCheckpointState = getRuntimeContext().getState(checkpointDesc); timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); } @Override public void processElement(JSONObject data, ReadOnlyContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); String timeStr = data.getString("time"); long eventTime = timeFormat.parse(timeStr).getTime(); // 更新最后数据时间 lastDataTimeMap.put(tag, eventTime); // 获取广播配置 ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) { return; } List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null || configs.isEmpty()) { return; } // ========== 清理无效状态 ========== Iterator<String> stateKeys = stateMap.keys().iterator(); while (stateKeys.hasNext()) { String encode = stateKeys.next(); boolean found = false; for (ULEParamConfig cfg : configs) { if (cfg.encode.equals(encode)) { found = true; break; } } if (!found) { stateMap.remove(encode); } } // ========== 检查离线状态(基于配置检查点) ========== Long lastCP = lastCheckpointState.value(); if (lastCP == null || configCollection.checkpointTime > lastCP) { // 新配置到达,检查离线状态 for (ULEParamConfig config : configs) { if (config.isonline == 1) { // 获取该tag的最后数据时间 Long lastEventTime = lastDataTimeMap.get(tag); if (lastEventTime == null) { // 从未收到数据,触发离线报警 AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(5); if (!status.reported) { reportAnomaly(5, 1, 0.0, timeFormat.format(new Date(configCollection.checkpointTime)), config, out); status.reported = true; stateMap.put(config.encode, state); } } else { // 计算超时时间点:检查点时间 - duration long timeoutPoint = configCollection.checkpointTime - config.duration * 60 * 1000; if (lastEventTime < timeoutPoint) { // 触发离线报警 AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(5); if (!status.reported) { reportAnomaly(5, 1, 0.0, timeFormat.format(new Date(configCollection.checkpointTime)), config, out); status.reported = true; stateMap.put(config.encode, state); } } } } } // 更新检查点状态 lastCheckpointState.update(configCollection.checkpointTime); } double value = 0; boolean valueSet = false; // 遍历配置项进行异常检测 for (ULEParamConfig config : configs) { if (!valueSet) { value = "436887485805570949".equals(config.datatype) ? data.getDouble("ontime") : data.getDouble("avg"); lastValuesMap.put(tag, value); valueSet = true; } // 获取或初始化状态 AnomalyState state = getOrCreateState(config.encode); // ========== 离线恢复检测 ========== if (config.isonline == 1) { AnomalyStatus status = state.getStatus(5); if (status.reported) { // 设备重新上线,发送恢复事件 reportAnomaly(5, 0, 0.0, timeStr, config, out); status.reset(); System.out.println("设备 " + tag + " 恢复在线,发送恢复事件"); } } // 处理异常类型 checkConstantValueAnomaly(config, value, timeStr, state, out); checkZeroValueAnomaly(config, value, timeStr, state, out); checkThresholdAnomaly(config, value, timeStr, state, out); checkSyncAnomaly(config, value, timeStr, state, configCollection, out); // 保存状态 stateMap.put(config.encode, state); } } // 恒值检测 private void checkConstantValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.constantvalue != 1) return; try { AnomalyStatus status = state.getStatus(1); long durationThreshold = config.duration * 60 * 1000; Date timestamp = timeFormat.parse(timeStr); if (status.lastValue == null) { status.lastValue = currentValue; status.lastChangeTime = timestamp; return; } if (Math.abs(currentValue - status.lastValue) > 0.001) { status.lastValue = currentValue; status.lastChangeTime = timestamp; if (status.reported) { reportAnomaly(1, 0, currentValue, timeStr, config, out); } status.reset(); return; } long elapsed = timestamp.getTime() - status.lastChangeTime.getTime(); if (elapsed > durationThreshold) { if (!status.reported) { reportAnomaly(1, 1, currentValue, timeStr, config, out); status.reported = true; } } } catch (Exception e) { System.err.println("恒值检测错误: " + config.encode + " - " + e.getMessage()); } } // 零值检测 private void checkZeroValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.iszero != 1) return; try { AnomalyStatus status = state.getStatus(2); Date timestamp = timeFormat.parse(timeStr); boolean isZero = Math.abs(currentValue) < 0.001; if (isZero) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(2, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(2, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("零值检测错误: " + config.encode + " - " + e.getMessage()); } } // 阈值检测 private void checkThresholdAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { try { if (config.ishigh == 1) { AnomalyStatus highStatus = state.getStatus(3); processThresholdAnomaly(highStatus, currentValue, timeStr, currentValue > config.highthreshold, config, 3, out); } if (config.islow == 1) { AnomalyStatus lowStatus = state.getStatus(4); processThresholdAnomaly(lowStatus, currentValue, timeStr, currentValue < config.lowthreshold, config, 4, out); } } catch (Exception e) { System.err.println("阈值检测错误: " + config.encode + " - " + e.getMessage()); } } private void processThresholdAnomaly(AnomalyStatus status, double currentValue, String timeStr, boolean isAnomaly, ULEParamConfig config, int anomalyType, Collector<JSONObject> out) { try { Date timestamp = timeFormat.parse(timeStr); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(anomalyType, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(anomalyType, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("阈值处理错误: " + config.encode + " - " + e.getMessage()); } } // 同步检测 private void checkSyncAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, ConfigCollection configCollection, Collector<JSONObject> out) { if (config.issync != 1 || config.syncparaencode == null) return; try { AnomalyStatus status = state.getStatus(6); Date timestamp = timeFormat.parse(timeStr); ULEParamConfig relatedConfig = configCollection.encodeToConfig.get(config.syncparaencode); System.out.printf("同步"+relatedConfig.toString()); if (relatedConfig == null) return; String relatedTag = null; for (Map.Entry<String, List<ULEParamConfig>> entry : configCollection.tagToConfigs.entrySet()) { if (entry.getValue().contains(relatedConfig)) { relatedTag = entry.getKey(); break; } } if (relatedTag == null) return; Double relatedValue = lastValuesMap.get(relatedTag); System.out.printf("relatedValue"+relatedConfig.toString()); if (relatedValue == null) return; boolean isAnomaly = (Math.abs(currentValue - 1.0) < 0.001) && (Math.abs(relatedValue) < 0.001); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(6, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(6, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("同步检测错误: " + config.encode + " - " + e.getMessage()); } } // 报告异常 private void reportAnomaly(int anomalyType, int statusFlag, double value, String time, ULEParamConfig config, Collector<JSONObject> out) { JSONObject event = new JSONObject(); event.put("tag", config.tag); event.put("paracode", config.encode); event.put("abnormaltype", anomalyType); event.put("statusflag", statusFlag); event.put("datavalue", value); event.put("triggertime", time); out.collect(event); } @Override public void processBroadcastElement(ConfigCollection newConfig, Context ctx, Collector<JSONObject> out) { BroadcastState<Void, ConfigCollection> state = ctx.getBroadcastState(Descriptors.configStateDescriptor); try { // 获取旧配置 ConfigCollection oldConfig = state.get(null); // 处理配置变更:清理不再启用的报警 if (oldConfig != null) { for (Map.Entry<String, ULEParamConfig> entry : oldConfig.encodeToConfig.entrySet()) { String encode = entry.getKey(); ULEParamConfig oldCfg = entry.getValue(); // 检查配置是否被删除或禁用 ULEParamConfig newCfg = newConfig.encodeToConfig.get(encode); if (newCfg == null || !isAlarmEnabled(newCfg, oldCfg)) { // 发送恢复事件 sendRecoveryEvents(encode, oldCfg, ctx, out); } } } // 更新广播状态 state.put(null, newConfig); System.out.println("广播配置更新完成, 配置项: " + newConfig.encodeToConfig.size()); System.out.println("配置项: " + newConfig.toString()); // 触发离线检测 for (String tag : newConfig.allTags) { ctx.output(OFFLINE_CHECK_TAG, tag); } } catch (Exception e) { e.printStackTrace(); } } // 检查报警是否启用 private boolean isAlarmEnabled(ULEParamConfig newCfg, ULEParamConfig oldCfg) { // 检查所有报警类型是否被禁用 return (oldCfg.constantvalue == 1 && newCfg.constantvalue == 1) || (oldCfg.iszero == 1 && newCfg.iszero == 1) || (oldCfg.ishigh == 1 && newCfg.ishigh == 1) || (oldCfg.islow == 1 && newCfg.islow == 1) || (oldCfg.isonline == 1 && newCfg.isonline == 1) || (oldCfg.issync == 1 && newCfg.issync == 1); } // 发送恢复事件 private void sendRecoveryEvents(String encode, ULEParamConfig config, Context ctx, Collector<JSONObject> out) { try { AnomalyState state = stateMap.get(encode); if (state == null) return; // 遍历所有可能的报警类型 for (int type = 1; type <= 6; type++) { AnomalyStatus status = state.getStatus(type); if (status.reported) { JSONObject recoveryEvent = new JSONObject(); recoveryEvent.put("tag", config.tag); recoveryEvent.put("paracode", config.encode); recoveryEvent.put("abnormaltype", type); recoveryEvent.put("statusflag", 0); // 恢复事件 recoveryEvent.put("datavalue", 0.0); recoveryEvent.put("triggertime", timeFormat.format(new Date())); out.collect(recoveryEvent); status.reset(); } } // 更新状态 stateMap.put(encode, state); } catch (Exception e) { System.err.println("发送恢复事件失败: " + e.getMessage()); } } // ========== 辅助方法 ========== private ConfigCollection getBroadcastConfig(ReadOnlyContext ctx) throws Exception { return ctx.getBroadcastState(Descriptors.configStateDescriptor).get(null); } private AnomalyState getOrCreateState(String encode) throws Exception { AnomalyState state = stateMap.get(encode); if (state == null) { state = new AnomalyState(); } return state; } } // 异常状态类 public static class AnomalyState implements Serializable { private static final long serialVersionUID = 1L; private final Map<Integer, AnomalyStatus> statusMap = new HashMap<>(); public AnomalyStatus getStatus(int type) { return statusMap.computeIfAbsent(type, k -> new AnomalyStatus()); } } // 异常状态详情 public static class AnomalyStatus implements Serializable { private static final long serialVersionUID = 1L; public Date startTime; // 异常开始时间 public Double lastValue; // 用于恒值检测 public Date lastChangeTime; // 值最后变化时间 public boolean reported; // 是否已报告 public void reset() { startTime = null; lastValue = null; lastChangeTime = null; reported = false; } } } "C:\Program Files (x86)\Java\jdk1.8.0_102\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:38688,suspend=y,server=n -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.2\captureAgent\debugger-agent.jar=file:/C:/Users/Administrator/AppData/Local/Temp/capture.props -Dfile.encoding=UTF-8 -classpath C:\Users\Administrator\AppData\Local\Temp\classpath176308487.jar com.tongchuang.realtime.mds.ULEDataanomalyanalysis 已连接到目标 VM, 地址: ''127.0.0.1:38688',传输: '套接字'' SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory] 加载配置: 30 个参数 配置加载完成,检查点时间: Sat Aug 02 09:09:51 CST 2025 广播配置更新完成, 配置项: 30 配置项: com.tongchuang.realtime.mds.ULEDataanomalyanalysis$ConfigCollection@eb409 同步ULEParamConfig(tag=DA-NY-LG1ZL-2-001, encode=EP000022, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1)relatedValueULEParamConfig(tag=DA-NY-LG1ZL-2-001, encode=EP000022, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1)异常检测结果> {"abnormaltype":2,"paracode":"EP000022","datavalue":0.0,"tag":"DA-NY-LG1ZL-2-001","triggertime":"2025-08-02 09:10","statusflag":1} 异常检测结果> {"abnormaltype":4,"paracode":"EP000022","datavalue":0.0,"tag":"DA-NY-LG1ZL-2-001","triggertime":"2025-08-02 09:10","statusflag":1} 异常检测结果> {"abnormaltype":4,"paracode":"EP000001","datavalue":1.0,"tag":"DA-DB195-RH-B-0201","triggertime":"2025-08-02 09:10","statusflag":1} 同步ULEParamConfig(tag=DA-NY-LG1ZL-2-001, encode=EP000022, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1)relatedValueULEParamConfig(tag=DA-NY-LG1ZL-2-001, encode=EP000022, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1)异常检测结果> {"abnormaltype":1,"paracode":"EP000022","datavalue":0.0,"tag":"DA-NY-LG1ZL-2-001","triggertime":"2025-08-02 09:11","statusflag":1} 异常检测结果> {"abnormaltype":1,"paracode":"EP000004","datavalue":1209.0,"tag":"DA-LT-4BT0004","triggertime":"2025-08-02 09:11","statusflag":1} 异常检测结果> {"abnormaltype":1,"paracode":"EP000018","datavalue":1209.0,"tag":"DA-LT-4BT0004","triggertime":"2025-08-02 09:11","statusflag":1} 异常检测结果> {"abnormaltype":1,"paracode":"EP000001","datavalue":1.0,"tag":"DA-DB195-RH-B-0201","triggertime":"2025-08-02 09:11","statusflag":1} 同步ULEParamConfig(tag=DA-NY-LG1ZL-2-001, encode=EP000022, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1)relatedValueULEParamConfig(tag=DA-NY-LG1ZL-2-001, encode=EP000022, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1),同步异常检测,未找到relatedTag
08-03
package com.tongchuang.realtime.mds; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.tongchuang.realtime.bean.ULEParamConfig; import com.tongchuang.realtime.util.KafkaUtils; import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.api.common.state.; import org.apache.flink.api.common.time.Time; import org.apache.flink.api.common.typeinfo.BasicTypeInfo; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.configuration.Configuration; import org.apache.flink.connector.kafka.source.KafkaSource; import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer; import org.apache.flink.streaming.api.datastream.; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.functions.co.KeyedBroadcastProcessFunction; import org.apache.flink.streaming.api.functions.source.RichSourceFunction; import org.apache.flink.util.Collector; import org.apache.flink.util.OutputTag; import java.io.Serializable; import java.sql.; import java.text.SimpleDateFormat; import java.util.; import java.util.Date; import java.util.concurrent.TimeUnit; public class ULEDataanomalyanalysis { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); // 创建Kafka消费者 KafkaSource kafkaConsumer = KafkaUtils.getKafkaConsumer( “realdata_minute”, “minutedata_uledataanomalyanalysis”, OffsetsInitializer.latest() ); DataStreamSource kafkaDS = env.fromSource( kafkaConsumer, WatermarkStrategy.noWatermarks(), “realdata_uledataanomalyanalysis” ); kafkaDS.print(“分钟数据流”); // 解析JSON并拆分每个tag的数据 SingleOutputStreamOperator splitStream = kafkaDS .map(JSON::parseObject) .flatMap((JSONObject value, Collector out) -> { JSONObject data = value.getJSONObject(“datas”); String time = value.getString(“times”); for (String tag : data.keySet()) { JSONObject tagData = data.getJSONObject(tag); JSONObject newObj = new JSONObject(); newObj.put(“time”, time); newObj.put(“tag”, tag); newObj.put(“ontime”, tagData.getDouble(“ontime”)); newObj.put(“avg”, tagData.getDouble(“avg”)); out.collect(newObj); } }) .returns(TypeInformation.of(JSONObject.class)) .name(“Split-By-Tag”); // 每5分钟加载参数配置 DataStream configDataStream = env .addSource(new MysqlConfigSource()) .setParallelism(1) .filter(Objects::nonNull) .name(“Config-Source”); // 将配置流转换为广播流 BroadcastStream configBroadcastStream = configDataStream .broadcast(Descriptors.configStateDescriptor); // 按tag分组并连接广播流 KeyedStream<JSONObject, String> keyedStream = splitStream .keyBy(json -> json.getString(“tag”)); BroadcastConnectedStream<JSONObject, ConfigCollection> connectedStream = keyedStream.connect(configBroadcastStream); // 异常检测处理 SingleOutputStreamOperator anomalyStream = connectedStream .process(new OptimizedAnomalyDetectionFunction()) .name(“Anomaly-Detection”); anomalyStream.print(“异常检测结果”); // anomalyStream.map(JSON::toString).addSink(KafkaUtils.getKafkaSink(“minutedata_uleanomaly”)); env.execute(“uledataanomalyanalysis”); } // 配置集合类 - 添加检查点时间 public static class ConfigCollection implements Serializable { private static final long serialVersionUID = 1L; public final Map<String, List> tagToConfigs; public final Map<String, ULEParamConfig> encodeToConfig; public final Set allTags; public final long checkpointTime; // 配置加载的时间戳 public ConfigCollection(Map<String, List> tagToConfigs, Map<String, ULEParamConfig> encodeToConfig) { this.tagToConfigs = new HashMap<>(tagToConfigs); this.encodeToConfig = new HashMap<>(encodeToConfig); this.allTags = new HashSet<>(tagToConfigs.keySet()); this.checkpointTime = System.currentTimeMillis(); // 记录配置加载时间 } } // MySQL配置源 public static class MysqlConfigSource extends RichSourceFunction { private volatile boolean isRunning = true; private final long interval = TimeUnit.MINUTES.toMillis(5); @Override public void run(SourceContext ctx) throws Exception { while (isRunning) { ConfigCollection newConfig = loadParams(); if (newConfig != null) { ctx.collect(newConfig); System.out.println("配置加载完成,检查点时间: " + new Date(newConfig.checkpointTime)); } else { System.out.println(“配置加载失败”); } Thread.sleep(interval); } } private ConfigCollection loadParams() { Map<String, List> tagToConfigs = new HashMap<>(5000); Map<String, ULEParamConfig> encodeToConfig = new HashMap<>(5000); String url = “jdbc:mysql://10.51.37.73:3306/eps?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8”; String user = “root”; String password = “6CKIm5jDVsLrahSw”; String query = "SELECT F_tag AS tag, F_enCode AS encode, F_dataTypes AS datatype, " + "F_isConstantValue AS constantvalue, F_isOnline AS isonline, " + "F_isSync AS issync, F_syncParaEnCode AS syncparaencode, " + "F_isZero AS iszero, F_isHigh AS ishigh, F_highThreshold AS highthreshold, " + "F_isLow AS islow, F_lowThreshold AS lowthreshold, F_duration AS duration " + "FROM t_equipmentparameter " + "WHERE F_enabledmark = ‘1’ AND (F_isConstantValue =‘1’ OR F_isZero= ‘1’ " + “OR F_isHigh = ‘1’ OR F_isLow = ‘1’ OR F_isOnline = ‘1’ OR F_isSync = ‘1’)”; try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query)) { while (rs.next()) { ULEParamConfig config = new ULEParamConfig(); config.tag = rs.getString(“tag”); config.encode = rs.getString(“encode”); config.datatype = rs.getString(“datatype”); config.constantvalue = rs.getInt(“constantvalue”); config.iszero = rs.getInt(“iszero”); config.ishigh = rs.getInt(“ishigh”); config.highthreshold = rs.getDouble(“highthreshold”); config.islow = rs.getInt(“islow”); config.lowthreshold = rs.getDouble(“lowthreshold”); config.duration = rs.getLong(“duration”); config.isonline = rs.getInt(“isonline”); config.issync = rs.getInt(“issync”); config.syncparaencode = rs.getString(“syncparaencode”); // 跳过无效配置 if (config.encode == null || config.encode.isEmpty()) { System.err.println(“忽略无效配置: 空encode”); continue; } String tag = config.tag; tagToConfigs.computeIfAbsent(tag, k -> new ArrayList<>(10)).add(config); encodeToConfig.put(config.encode, config); } System.out.println(“加载配置: " + encodeToConfig.size() + " 个参数”); return new ConfigCollection(tagToConfigs, encodeToConfig); } catch (SQLException e) { System.err.println(“加载参数配置错误:”); e.printStackTrace(); return null; } } @Override public void cancel() { isRunning = false; } } // 状态描述符 public static class Descriptors { public static final MapStateDescriptor<Void, ConfigCollection> configStateDescriptor = new MapStateDescriptor<>( “configState”, TypeInformation.of(Void.class), TypeInformation.of(ConfigCollection.class) ); } // 优化后的异常检测函数(无定时器实现) public static class OptimizedAnomalyDetectionFunction extends KeyedBroadcastProcessFunction<String, JSONObject, ConfigCollection, JSONObject> { // 状态管理 private transient MapState<String, AnomalyState> stateMap; // key=encode private transient MapState<String, Double> lastValuesMap; // key=tag private transient MapState<String, Long> lastDataTimeMap; // key=tag private transient ValueState lastCheckpointState; // 记录该tag上次处理的检查点时间 private transient SimpleDateFormat timeFormat; // 日志频率控制 private transient long lastSyncLogTime = 0; // 侧输出标签用于离线检测 private static final OutputTag OFFLINE_CHECK_TAG = new OutputTag(“offline-check”){}; @Override public void open(Configuration parameters) { // 状态TTL配置(30天自动清理) StateTtlConfig ttlConfig = StateTtlConfig.newBuilder(Time.days(300)) .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) .cleanupFullSnapshot() .build(); // 初始化异常状态存储(启用TTL) MapStateDescriptor<String, AnomalyState> stateDesc = new MapStateDescriptor<>( “anomalyState”, BasicTypeInfo.STRING_TYPE_INFO, TypeInformation.of(AnomalyState.class) ); stateDesc.enableTimeToLive(ttlConfig); stateMap = getRuntimeContext().getMapState(stateDesc); // 初始化最新值存储(启用TTL) MapStateDescriptor<String, Double> valuesDesc = new MapStateDescriptor<>( “lastValuesState”, BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.DOUBLE_TYPE_INFO ); valuesDesc.enableTimeToLive(ttlConfig); lastValuesMap = getRuntimeContext().getMapState(valuesDesc); // 初始化最后数据时间存储(启用TTL) MapStateDescriptor<String, Long> timeDesc = new MapStateDescriptor<>( “lastDataTimeState”, BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timeDesc.enableTimeToLive(ttlConfig); lastDataTimeMap = getRuntimeContext().getMapState(timeDesc); // 初始化检查点状态(记录上次处理的配置时间) ValueStateDescriptor checkpointDesc = new ValueStateDescriptor<>( “lastCheckpointState”, BasicTypeInfo.LONG_TYPE_INFO ); checkpointDesc.enableTimeToLive(ttlConfig); lastCheckpointState = getRuntimeContext().getState(checkpointDesc); timeFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm”); } @Override public void processElement(JSONObject data, ReadOnlyContext ctx, Collector out) throws Exception { String tag = ctx.getCurrentKey(); String timeStr = data.getString(“time”); long eventTime = timeFormat.parse(timeStr).getTime(); // 更新最后数据时间 lastDataTimeMap.put(tag, eventTime); // 获取广播配置 ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) { return; } List configs = configCollection.tagToConfigs.get(tag); if (configs == null || configs.isEmpty()) { return; } // 跟踪已恢复的tag Set recoveredTags = new HashSet<>(); // ========== 清理无效状态 ========== // 修复:使用临时列表避免ConcurrentModificationException List keysToRemove = new ArrayList<>(); for (String encode : stateMap.keys()) { boolean found = false; for (ULEParamConfig cfg : configs) { if (cfg.encode.equals(encode)) { found = true; break; } } if (!found) { System.out.println("清理过期状态: " + encode); keysToRemove.add(encode); } } for (String encode : keysToRemove) { stateMap.remove(encode); } // ========== 检查离线状态(基于配置检查点) ========== Long lastCP = lastCheckpointState.value(); if (lastCP == null || configCollection.checkpointTime > lastCP) { // 新配置到达,检查离线状态 for (ULEParamConfig config : configs) { if (config.isonline == 1) { // 获取该tag的最后数据时间 Long lastEventTime = lastDataTimeMap.get(tag); if (lastEventTime == null) { // 从未收到数据,触发离线报警 AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(5); if (!status.reported) { // 使用超时时间点作为触发时间 long timeoutPoint = configCollection.checkpointTime; String triggerTime = timeFormat.format(new Date(timeoutPoint)); reportAnomaly(5, 1, 0.0, triggerTime, config, out); status.reported = true; stateMap.put(config.encode, state); } } else { // 计算超时时间点:检查点时间 - duration long timeoutPoint = configCollection.checkpointTime - config.duration * 60 * 1000; if (lastEventTime < timeoutPoint) { // 触发离线报警 AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(5); if (!status.reported) { // 使用超时时间点作为触发时间 String triggerTime = timeFormat.format(new Date(timeoutPoint)); reportAnomaly(5, 1, 0.0, triggerTime, config, out); status.reported = true; stateMap.put(config.encode, state); } } } } } // 更新检查点状态 lastCheckpointState.update(configCollection.checkpointTime); } double value = 0; boolean valueSet = false; // 遍历配置项进行异常检测 for (ULEParamConfig config : configs) { if (!valueSet) { value = “436887485805570949”.equals(config.datatype) ? data.getDouble(“ontime”) : data.getDouble(“avg”); lastValuesMap.put(tag, value); valueSet = true; } // 获取或初始化状态 AnomalyState state = getOrCreateState(config.encode); // ========== 离线恢复检测 ========== if (config.isonline == 1) { AnomalyStatus status = state.getStatus(5); if (status.reported) { // 设备重新上线,发送恢复事件 if (!recoveredTags.contains(tag)) { // 使用当前系统时间作为恢复时间 String recoveryTime = timeFormat.format(new Date()); reportAnomaly(5, 0, 0.0, recoveryTime, config, out); status.reset(); recoveredTags.add(tag); // 标记该tag已恢复 System.out.println(“设备 " + tag + " 恢复在线”); } } } // 处理异常类型 checkConstantValueAnomaly(config, value, timeStr, state, out); checkZeroValueAnomaly(config, value, timeStr, state, out); checkThresholdAnomaly(config, value, timeStr, state, out); checkSyncAnomaly(config, value, timeStr, state, configCollection, out); // 保存状态 stateMap.put(config.encode, state); } } // 恒值检测 private void checkConstantValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector out) { if (config.constantvalue != 1) return; try { AnomalyStatus status = state.getStatus(1); long durationThreshold = config.duration * 60 * 1000; Date timestamp = timeFormat.parse(timeStr); if (status.lastValue == null) { status.lastValue = currentValue; status.lastChangeTime = timestamp; return; } if (Math.abs(currentValue - status.lastValue) > 0.001) { status.lastValue = currentValue; status.lastChangeTime = timestamp; if (status.reported) { reportAnomaly(1, 0, currentValue, timeStr, config, out); } status.reset(); return; } long elapsed = timestamp.getTime() - status.lastChangeTime.getTime(); if (elapsed > durationThreshold) { if (!status.reported) { reportAnomaly(1, 1, currentValue, timeStr, config, out); status.reported = true; } } } catch (Exception e) { System.err.println("恒值检测错误: " + config.encode + " - " + e.getMessage()); } } // 零值检测 private void checkZeroValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector out) { if (config.iszero != 1) return; try { AnomalyStatus status = state.getStatus(2); Date timestamp = timeFormat.parse(timeStr); boolean isZero = Math.abs(currentValue) < 0.001; if (isZero) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(2, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(2, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("零值检测错误: " + config.encode + " - "+e.getMessage()); } } // 阈值检测 private void checkThresholdAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector out) { try { if (config.ishigh == 1) { AnomalyStatus highStatus = state.getStatus(3); processThresholdAnomaly(highStatus, currentValue, timeStr, currentValue > config.highthreshold, config, 3, out); } if (config.islow == 1) { AnomalyStatus lowStatus = state.getStatus(4); processThresholdAnomaly(lowStatus, currentValue, timeStr, currentValue < config.lowthreshold, config, 4, out); } } catch (Exception e) { System.err.println("阈值检测错误: " + config.encode + " - " + e.getMessage()); } } private void processThresholdAnomaly(AnomalyStatus status, double currentValue, String timeStr, boolean isAnomaly, ULEParamConfig config, int anomalyType, Collector out) { try { Date timestamp = timeFormat.parse(timeStr); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(anomalyType, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(anomalyType, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("阈值处理错误: " + config.encode + " - " + e.getMessage()); } } // 同步检测 - 优化实现 private void checkSyncAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, ConfigCollection configCollection, Collector out) { if (config.issync != 1 || config.syncparaencode == null) return; try { // 日志频率控制:每分钟最多打印一次 long now = System.currentTimeMillis(); if (now - lastSyncLogTime > 60000) { System.out.println("同步检测 - 当前配置: " + config); lastSyncLogTime = now; } AnomalyStatus status = state.getStatus(6); Date timestamp = timeFormat.parse(timeStr); // 直接获取关联配置 ULEParamConfig relatedConfig = configCollection.encodeToConfig.get(config.syncparaencode); if (relatedConfig == null) { System.out.println(“同步检测: 未找到关联配置, encode=” + config.syncparaencode); return; } // 查找关联tag String relatedTag = null; for (Map.Entry<String, List> entry : configCollection.tagToConfigs.entrySet()) { for (ULEParamConfig cfg : entry.getValue()) { if (cfg.encode.equals(relatedConfig.encode)) { relatedTag = entry.getKey(); break; } } if (relatedTag != null) break; } if (relatedTag == null) { System.out.println(“同步检测: 未找到关联Tag,关联encode=” + relatedConfig.encode); return; } // 安全获取关联值 Double relatedValue = null; if (lastValuesMap.contains(relatedTag)) { relatedValue = lastValuesMap.get(relatedTag); } if (relatedValue == null) { System.out.println(“同步检测: 关联值不可用, tag=” + relatedTag); return; } // 优化比较逻辑:使用更精确的阈值 boolean isAnomaly = (currentValue >= 0.99) && (Math.abs(relatedValue) < 0.01); // 调试信息 System.out.printf(“同步检测: %s (%.4f) vs %s (%.4f) -> %b%n”, config.tag, currentValue, relatedTag, relatedValue, isAnomaly); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(6, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(6, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("同步检测错误: " + config.encode + " - " + e.getMessage()); } } // 报告异常 private void reportAnomaly(int anomalyType, int statusFlag, double value, String time, ULEParamConfig config, Collector out) { JSONObject event = new JSONObject(); event.put(“tag”, config.tag); event.put(“paracode”, config.encode); event.put(“abnormaltype”, anomalyType); event.put(“statusflag”, statusFlag); event.put(“datavalue”, value); event.put(“triggertime”, time); out.collect(event); } @Override public void processBroadcastElement(ConfigCollection newConfig, Context ctx, Collector out) { BroadcastState<Void, ConfigCollection> state = ctx.getBroadcastState(Descriptors.configStateDescriptor); try { // 获取旧配置 ConfigCollection oldConfig = state.get(null); // 处理配置变更:清理不再启用的报警 if (oldConfig != null) { for (Map.Entry<String, ULEParamConfig> entry : oldConfig.encodeToConfig.entrySet()) { String encode = entry.getKey(); ULEParamConfig oldCfg = entry.getValue(); // 检查配置是否被删除或禁用 ULEParamConfig newCfg = newConfig.encodeToConfig.get(encode); if (newCfg == null || !isAlarmEnabled(newCfg, oldCfg)) { // 发送恢复事件 sendRecoveryEvents(encode, oldCfg, ctx, out); } } } // 更新广播状态 state.put(null, newConfig); System.out.println("广播配置更新完成, 配置项: " + newConfig.encodeToConfig.size()); System.out.println("配置摘要: " + newConfig.encodeToConfig.keySet()); // 触发离线检测 for (String tag : newConfig.allTags) { ctx.output(OFFLINE_CHECK_TAG, tag); } } catch (Exception e) { e.printStackTrace(); } } // 检查报警是否启用 private boolean isAlarmEnabled(ULEParamConfig newCfg, ULEParamConfig oldCfg) { // 检查所有报警类型是否被禁用 return (oldCfg.constantvalue == 1 && newCfg.constantvalue == 1) || (oldCfg.iszero == 1 && newCfg.iszero == 1) || (oldCfg.ishigh == 1 && newCfg.ishigh == 1) || (oldCfg.islow == 1 && newCfg.islow == 1) || (oldCfg.isonline == 1 && newCfg.isonline == 1) || (oldCfg.issync == 1 && newCfg.issync == 1); } // 发送恢复事件 private void sendRecoveryEvents(String encode, ULEParamConfig config, Context ctx, Collector out) { try { AnomalyState state = stateMap.get(encode); if (state == null) return; // 遍历所有可能的报警类型 for (int type = 1; type <= 6; type++) { AnomalyStatus status = state.getStatus(type); if (status.reported) { JSONObject recoveryEvent = new JSONObject(); recoveryEvent.put(“tag”, config.tag); recoveryEvent.put(“paracode”, config.encode); recoveryEvent.put(“abnormaltype”, type); recoveryEvent.put(“statusflag”, 0); // 恢复事件 recoveryEvent.put(“datavalue”, 0.0); recoveryEvent.put(“triggertime”, timeFormat.format(new Date())); out.collect(recoveryEvent); status.reset(); } } // 更新状态 stateMap.put(encode, state); } catch (Exception e) { System.err.println(“发送恢复事件失败: " + e.getMessage()); } } // ========== 辅助方法 ========== private ConfigCollection getBroadcastConfig(ReadOnlyContext ctx) throws Exception { return ctx.getBroadcastState(Descriptors.configStateDescriptor).get(null); } private AnomalyState getOrCreateState(String encode) throws Exception { AnomalyState state = stateMap.get(encode); if (state == null) { state = new AnomalyState(); } return state; } } // 异常状态类 public static class AnomalyState implements Serializable { private static final long serialVersionUID = 1L; private final Map<Integer, AnomalyStatus> statusMap = new HashMap<>(); public AnomalyStatus getStatus(int type) { return statusMap.computeIfAbsent(type, k -> new AnomalyStatus()); } } // 异常状态详情 public static class AnomalyStatus implements Serializable { private static final long serialVersionUID = 1L; public Date startTime; // 异常开始时间 public Double lastValue; // 用于恒值检测 public Date lastChangeTime; // 值最后变化时间 public boolean reported; // 是否已报告 public void reset() { startTime = null; lastValue = null; lastChangeTime = null; reported = false; } } } 运行日志为"C:\Program Files (x86)\Java\jdk1.8.0_102\bin\java.exe” -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:12725,suspend=y,server=n -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.2\captureAgent\debugger-agent.jar=file:/C:/Users/Administrator/AppData/Local/Temp/capture.props -Dfile.encoding=UTF-8 -classpath C:\Users\Administrator\AppData\Local\Temp\classpath679239090.jar com.tongchuang.realtime.mds.ULEDataanomalyanalysis 已连接到目标 VM, 地址: ‘‘127.0.0.1:12725’,传输: ‘套接字’’ SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory] 加载配置: 30 个参数 配置加载完成,检查点时间: Mon Aug 04 10:51:26 CST 2025 广播配置更新完成, 配置项: 30 配置摘要: [EP000015, EP000014, EP000013, EP000012, EP000011, EP000010, EP100013, EP100014, EP000019, EP000018, EP100010, EP000017, EP100011, EP000016, EP100012, EP000004, EP000003, EP000002, EP000001, EP000022, EP000021, EP100007, EP000020, EP100008, EP100009, EP000009, EP000008, EP000007, EP000006, EP000005] 分钟数据流> {“times”:“2025-08-04 10:51”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3093.452,“avg”:3128.6265,“min”:3083.741,“max”:3185.4487},“DA-LT-6BT008”:{“ontime”:174.2086,“avg”:172.546,“min”:171.8335,“max”:174.2086},“DA-LT-5BT0005”:{“ontime”:408.24,“avg”:407.208,“min”:404.94,“max”:409.2},“DA-LT-6BT004”:{“ontime”:1219.4089,“avg”:1219.8833,“min”:1219.3755,“max”:1220.4434},“DA-LT-5BT0004”:{“ontime”:1200.4,“avg”:1200.045,“min”:1199.5,“max”:1200.5},“DA-LT-6BT005”:{“ontime”:389.8511,“avg”:390.6297,“min”:389.8511,“max”:391.9514},“DA-LT-5BT0008”:{“ontime”:193.6,“avg”:190.7653,“min”:186.94,“max”:193.82},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:124.2625,“avg”:124.46,“min”:123.1875,“max”:125.7875},“DA-LT-4BT0007”:{“ontime”:169.4,“avg”:169.715,“min”:168.9,“max”:170.3},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1211.0,“avg”:1211.0,“min”:1211.0,“max”:1211.0},“DA-LT-6BT001”:{“ontime”:174443.58,“avg”:173604.8032,“min”:172657.02,“max”:174912.67},“DA-LT-4BT0005”:{“ontime”:293.0625,“avg”:293.5583,“min”:292.1875,“max”:294.6875},“DA-NY-LG2ZL-2-003”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA_DB195_RH_R_0281”:{“ontime”:283.58,“avg”:310.297,“min”:283.58,“max”:384.99},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:120682.57,“avg”:120638.2251,“min”:119844.39,“max”:121553.055}}} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {“times”:“2025-08-04 10:52”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3105.4685,“avg”:3127.2034,“min”:3085.331,“max”:3208.071},“DA-LT-6BT008”:{“ontime”:172.8935,“avg”:173.5736,“min”:172.8935,“max”:174.0123},“DA-LT-5BT0005”:{“ontime”:404.88,“avg”:405.671,“min”:404.76,“max”:407.34},“DA-LT-5BT0004”:{“ontime”:1199.4,“avg”:1198.915,“min”:1198.2001,“max”:1199.6},“DA-LT-6BT004”:{“ontime”:1220.4434,“avg”:1220.8577,“min”:1220.3433,“max”:1221.2441},“DA-LT-6BT005”:{“ontime”:391.971,“avg”:392.889,“min”:391.971,“max”:393.3843},“DA-LT-5BT0008”:{“ontime”:186.88,“avg”:188.0343,“min”:186.8,“max”:189.34},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:124.6625,“avg”:124.4888,“min”:123.825,“max”:125.3625},“DA-LT-4BT0007”:{“ontime”:170.0,“avg”:170.085,“min”:169.3,“max”:170.4},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1211.0,“avg”:1210.3333,“min”:1210.0,“max”:1211.0},“DA-LT-6BT001”:{“ontime”:173081.9,“avg”:173387.4525,“min”:172657.1,“max”:174215.6},“DA-LT-4BT0005”:{“ontime”:294.0625,“avg”:293.925,“min”:292.625,“max”:294.5},“DA-NY-LG2ZL-2-003”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA_DB195_RH_R_0281”:{“ontime”:311.85,“avg”:298.3053,“min”:226.51,“max”:340.17},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:120709.81,“avg”:121436.6195,“min”:120241.945,“max”:123212.08}}} 异常检测结果> {“abnormaltype”:2,“paracode”:“EP000022”,“datavalue”:0.0,“tag”:“DA-NY-LG1ZL-2-001”,“triggertime”:“2025-08-04 10:52”,“statusflag”:1} 异常检测结果> {“abnormaltype”:4,“paracode”:“EP000022”,“datavalue”:0.0,“tag”:“DA-NY-LG1ZL-2-001”,“triggertime”:“2025-08-04 10:52”,“statusflag”:1} 异常检测结果> {“abnormaltype”:4,“paracode”:“EP000001”,“datavalue”:1.0,“tag”:“DA-DB195-RH-B-0201”,“triggertime”:“2025-08-04 10:52”,“statusflag”:1} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {“times”:“2025-08-04 10:53”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3088.489,“avg”:3114.5305,“min”:3066.6252,“max”:3183.479},“DA-LT-6BT008”:{“ontime”:173.9338,“avg”:174.8619,“min”:173.9141,“max”:176.7015},“DA-LT-5BT0005”:{“ontime”:407.34,“avg”:408.787,“min”:407.34,“max”:409.62},“DA-LT-5BT0004”:{“ontime”:1198.2001,“avg”:1197.635,“min”:1197.0,“max”:1198.3},“DA-LT-6BT004”:{“ontime”:1221.1775,“avg”:1221.4828,“min”:1221.144,“max”:1221.7448},“DA-LT-6BT005”:{“ontime”:393.345,“avg”:393.5502,“min”:393.031,“max”:394.1498},“DA-LT-5BT0008”:{“ontime”:189.26,“avg”:190.819,“min”:189.26,“max”:192.74},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:124.2125,“avg”:124.3077,“min”:123.5125,“max”:125.1125},“DA-LT-4BT0007”:{“ontime”:169.4,“avg”:169.0767,“min”:167.7,“max”:169.8},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1210.0,“avg”:1209.3167,“min”:1209.0,“max”:1210.0},“DA-LT-6BT001”:{“ontime”:173364.6,“avg”:173578.1482,“min”:172720.47,“max”:174390.27},“DA-LT-4BT0005”:{“ontime”:292.8125,“avg”:292.6771,“min”:291.1875,“max”:293.6875},“DA-NY-LG2ZL-2-003”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA_DB195_RH_R_0281”:{“ontime”:295.82,“avg”:294.4385,“min”:273.98,“max”:322.66},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:122562.12,“avg”:122467.8892,“min”:120901.414,“max”:124176.8}}} 异常检测结果> {“abnormaltype”:1,“paracode”:“EP000022”,“datavalue”:0.0,“tag”:“DA-NY-LG1ZL-2-001”,“triggertime”:“2025-08-04 10:53”,“statusflag”:1} 异常检测结果> {“abnormaltype”:1,“paracode”:“EP000001”,“datavalue”:1.0,“tag”:“DA-DB195-RH-B-0201”,“triggertime”:“2025-08-04 10:53”,“statusflag”:1} 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {“times”:“2025-08-04 10:54”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3078.615,“avg”:3082.1354,“min”:3039.7373,“max”:3216.3108},“DA-LT-6BT008”:{“ontime”:175.5434,“avg”:173.7907,“min”:172.8149,“max”:175.5434},“DA-LT-5BT0005”:{“ontime”:409.38,“avg”:408.7688,“min”:407.94,“max”:409.68},“DA-LT-5BT0004”:{“ontime”:1196.9,“avg”:1196.2543,“min”:1195.3,“max”:1197.1},“DA-LT-6BT004”:{“ontime”:1221.7448,“avg”:1221.7742,“min”:1221.6447,“max”:1221.8448},“DA-LT-6BT005”:{“ontime”:393.1291,“avg”:392.4774,“min”:392.1673,“max”:393.1291},“DA-LT-5BT0008”:{“ontime”:191.14,“avg”:191.1912,“min”:190.66,“max”:191.74},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:123.825,“avg”:123.6761,“min”:122.225,“max”:124.7875},“DA-LT-4BT0007”:{“ontime”:167.5,“avg”:167.35,“min”:167.1,“max”:167.5},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1209.0,“avg”:1209.0,“min”:1209.0,“max”:1209.0},“DA-LT-6BT001”:{“ontime”:173662.19,“avg”:173854.9536,“min”:172842.14,“max”:175016.47},“DA-LT-4BT0005”:{“ontime”:290.625,“avg”:291.1727,“min”:290.25,“max”:291.9375},“DA-NY-LG2ZL-2-003”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA_DB195_RH_R_0281”:{“ontime”:316.75,“avg”:294.1453,“min”:263.4,“max”:318.26},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:123655.62,“avg”:122644.2382,“min”:121316.24,“max”:123656.664},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0}}} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {“times”:“2025-08-04 10:55”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3218.5786,“avg”:3046.705,“min”:2944.5356,“max”:3218.5786},“DA-LT-6BT008”:{“ontime”:174.4442,“avg”:173.5075,“min”:171.5587,“max”:174.719},“DA-LT-5BT0005”:{“ontime”:407.76,“avg”:410.281,“min”:406.38,“max”:412.92},“DA-LT-5BT0004”:{“ontime”:1195.5,“avg”:1194.82,“min”:1194.4,“max”:1195.5},“DA-LT-6BT004”:{“ontime”:1221.8115,“avg”:1221.7748,“min”:1221.678,“max”:1221.8448},“DA-LT-6BT005”:{“ontime”:392.2654,“avg”:391.9164,“min”:391.4803,“max”:392.344},“DA-LT-5BT0008”:{“ontime”:191.16,“avg”:193.6457,“min”:190.1,“max”:195.76},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:122.225,“avg”:123.2477,“min”:121.95,“max”:125.275},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1209.0,“avg”:1209.1833,“min”:1209.0,“max”:1210.0},“DA-LT-6BT001”:{“ontime”:173967.0,“avg”:173799.209,“min”:172448.05,“max”:174773.38},“DA-LT-4BT0005”:{“ontime”:290.625,“avg”:292.1531,“min”:290.625,“max”:294.1875},“DA-NY-LG2ZL-2-003”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA_DB195_RH_R_0281”:{“ontime”:295.73,“avg”:299.1413,“min”:258.3,“max”:335.65},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:123094.66,“avg”:121517.155,“min”:120457.766,“max”:123094.66},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0}}} 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 加载配置: 30 个参数 配置加载完成,检查点时间: Mon Aug 04 10:56:27 CST 2025 广播配置更新完成, 配置项: 30 配置摘要: [EP000015, EP000014, EP000013, EP000012, EP000011, EP000010, EP100013, EP100014, EP000019, EP000018, EP100010, EP000017, EP100011, EP000016, EP100012, EP000004, EP000003, EP000002, EP000001, EP000022, EP000021, EP100007, EP000020, EP100008, EP100009, EP000009, EP000008, EP000007, EP000006, EP000005] 分钟数据流> {“times”:“2025-08-04 10:56”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3057.6804,“avg”:3012.5768,“min”:2949.3123,“max”:3078.372},“DA-LT-6BT008”:{“ontime”:171.8335,“avg”:172.1848,“min”:171.8335,“max”:172.5597},“DA-LT-5BT0005”:{“ontime”:412.74,“avg”:413.215,“min”:412.02,“max”:415.14},“DA-LT-6BT004”:{“ontime”:1221.678,“avg”:1221.6897,“min”:1221.6113,“max”:1221.7448},“DA-LT-5BT0004”:{“ontime”:1194.8,“avg”:1196.8434,“min”:1194.8,“max”:1199.3},“DA-LT-6BT005”:{“ontime”:391.5392,“avg”:390.6559,“min”:389.8314,“max”:391.5392},“DA-LT-5BT0008”:{“ontime”:195.66,“avg”:195.8223,“min”:195.08,“max”:197.42},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:125.275,“avg”:124.2504,“min”:123.45,“max”:126.2625},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1210.0,“avg”:1210.0,“min”:1210.0,“max”:1210.0},“DA-LT-6BT001”:{“ontime”:173206.77,“avg”:174326.7505,“min”:173206.77,“max”:175154.61},“DA-LT-4BT0005”:{“ontime”:293.875,“avg”:293.4646,“min”:292.5625,“max”:295.0625},“DA-NY-LG2ZL-2-003”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA_DB195_RH_R_0281”:{“ontime”:288.86,“avg”:301.8983,“min”:268.97,“max”:349.89},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:120710.516,“avg”:120748.8062,“min”:119682.57,“max”:122023.83},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0}}} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {“times”:“2025-08-04 10:57”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3005.6284,“avg”:3004.7748,“min”:2962.2488,“max”:3059.464},“DA-LT-6BT008”:{“ontime”:171.9316,“avg”:171.3516,“min”:170.5968,“max”:172.0494},“DA-LT-5BT0005”:{“ontime”:415.14,“avg”:416.542,“min”:415.14,“max”:417.24},“DA-LT-5BT0004”:{“ontime”:1199.3,“avg”:1200.7184,“min”:1199.3,“max”:1201.3},“DA-LT-6BT004”:{“ontime”:1221.7114,“avg”:1221.5724,“min”:1221.411,“max”:1221.7448},“DA-LT-6BT005”:{“ontime”:389.8118,“avg”:389.4774,“min”:389.1444,“max”:389.8118},“DA-LT-5BT0008”:{“ontime”:197.42,“avg”:198.3487,“min”:197.42,“max”:198.68},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:124.2,“avg”:123.2256,“min”:121.8625,“max”:126.1},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1210.0,“avg”:1210.0,“min”:1210.0,“max”:1210.0},“DA-LT-6BT001”:{“ontime”:174333.06,“avg”:173710.917,“min”:172544.47,“max”:174776.31},“DA-NY-LG2ZL-2-003”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0005”:{“ontime”:294.0,“avg”:293.3229,“min”:291.5625,“max”:296.0},“DA_DB195_RH_R_0281”:{“ontime”:289.38,“avg”:297.7735,“min”:273.12,“max”:343.61},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:121024.04,“avg”:120971.4221,“min”:120043.63,“max”:122208.27},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0}}} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001。其中1、同步分析功能为实现。2、DA-LT-4BT0007的初始离线和流数据出现数据又离线均未报出离线异常。请生成完整优化后代码
08-05
package com.tongchuang.realtime.mds; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.tongchuang.realtime.bean.ULEParamConfig; import com.tongchuang.realtime.util.KafkaUtils; import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.api.common.state.*; import org.apache.flink.api.common.time.Time; import org.apache.flink.api.common.typeinfo.BasicTypeInfo; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.configuration.Configuration; import org.apache.flink.connector.kafka.source.KafkaSource; import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer; import org.apache.flink.streaming.api.datastream.*; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.functions.co.KeyedBroadcastProcessFunction; import org.apache.flink.streaming.api.functions.source.RichSourceFunction; import org.apache.flink.util.Collector; import org.apache.flink.util.OutputTag; import java.io.Serializable; import java.sql.*; import java.text.SimpleDateFormat; import java.util.*; import java.util.Date; import java.util.concurrent.TimeUnit; public class ULEDataanomalyanalysis { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); // 创建Kafka消费者 KafkaSource<String> kafkaConsumer = KafkaUtils.getKafkaConsumer( "realdata_minute", "minutedata_uledataanomalyanalysis", OffsetsInitializer.latest() ); DataStreamSource<String> kafkaDS = env.fromSource( kafkaConsumer, WatermarkStrategy.noWatermarks(), "realdata_uledataanomalyanalysis" ); kafkaDS.print("分钟数据流"); // 解析JSON并拆分每个tag的数据 SingleOutputStreamOperator<JSONObject> splitStream = kafkaDS .map(JSON::parseObject) .flatMap((JSONObject value, Collector<JSONObject> out) -> { JSONObject data = value.getJSONObject("datas"); String time = value.getString("times"); for (String tag : data.keySet()) { JSONObject tagData = data.getJSONObject(tag); JSONObject newObj = new JSONObject(); newObj.put("time", time); newObj.put("tag", tag); newObj.put("ontime", tagData.getDouble("ontime")); newObj.put("avg", tagData.getDouble("avg")); out.collect(newObj); } }) .returns(TypeInformation.of(JSONObject.class)) .name("Split-By-Tag"); // 每5分钟加载参数配置 DataStream<ConfigCollection> configDataStream = env .addSource(new MysqlConfigSource()) .setParallelism(1) .filter(Objects::nonNull) .name("Config-Source"); // 将配置流转换为广播流 BroadcastStream<ConfigCollection> configBroadcastStream = configDataStream .broadcast(Descriptors.configStateDescriptor); // 按tag分组并连接广播流 KeyedStream<JSONObject, String> keyedStream = splitStream .keyBy(json -> json.getString("tag")); BroadcastConnectedStream<JSONObject, ConfigCollection> connectedStream = keyedStream.connect(configBroadcastStream); // 异常检测处理 SingleOutputStreamOperator<JSONObject> anomalyStream = connectedStream .process(new OptimizedAnomalyDetectionFunction()) .name("Anomaly-Detection"); anomalyStream.print("异常检测结果"); // anomalyStream.map(JSON::toString).addSink(KafkaUtils.getKafkaSink("minutedata_uleanomaly")); env.execute("uledataanomalyanalysis"); } // 配置集合类 - 添加检查点时间 public static class ConfigCollection implements Serializable { private static final long serialVersionUID = 1L; public final Map<String, List<ULEParamConfig>> tagToConfigs; public final Map<String, ULEParamConfig> encodeToConfig; public final Set<String> allTags; public final long checkpointTime; // 配置加载的时间戳 public ConfigCollection(Map<String, List<ULEParamConfig>> tagToConfigs, Map<String, ULEParamConfig> encodeToConfig) { this.tagToConfigs = new HashMap<>(tagToConfigs); this.encodeToConfig = new HashMap<>(encodeToConfig); this.allTags = new HashSet<>(tagToConfigs.keySet()); this.checkpointTime = System.currentTimeMillis(); // 记录配置加载时间 } } // MySQL配置源 public static class MysqlConfigSource extends RichSourceFunction<ConfigCollection> { private volatile boolean isRunning = true; private final long interval = TimeUnit.MINUTES.toMillis(5); @Override public void run(SourceContext<ConfigCollection> ctx) throws Exception { while (isRunning) { ConfigCollection newConfig = loadParams(); if (newConfig != null) { ctx.collect(newConfig); System.out.println("配置加载完成,检查点时间: " + new Date(newConfig.checkpointTime)); } else { System.out.println("配置加载失败"); } Thread.sleep(interval); } } private ConfigCollection loadParams() { Map<String, List<ULEParamConfig>> tagToConfigs = new HashMap<>(5000); Map<String, ULEParamConfig> encodeToConfig = new HashMap<>(5000); String url = "jdbc:mysql://10.51.37.73:3306/eps?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"; String user = "root"; String password = "6CKIm5jDVsLrahSw"; String query = "SELECT F_tag AS tag, F_enCode AS encode, F_dataTypes AS datatype, " + "F_isConstantValue AS constantvalue, F_isOnline AS isonline, " + "F_isSync AS issync, F_syncParaEnCode AS syncparaencode, " + "F_isZero AS iszero, F_isHigh AS ishigh, F_highThreshold AS highthreshold, " + "F_isLow AS islow, F_lowThreshold AS lowthreshold, F_duration AS duration " + "FROM t_equipmentparameter " + "WHERE F_enabledmark = '1' AND (F_isConstantValue ='1' OR F_isZero= '1' " + "OR F_isHigh = '1' OR F_isLow = '1' OR F_isOnline = '1' OR F_isSync = '1')"; try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query)) { while (rs.next()) { ULEParamConfig config = new ULEParamConfig(); config.tag = rs.getString("tag"); config.encode = rs.getString("encode"); config.datatype = rs.getString("datatype"); config.constantvalue = rs.getInt("constantvalue"); config.iszero = rs.getInt("iszero"); config.ishigh = rs.getInt("ishigh"); config.highthreshold = rs.getDouble("highthreshold"); config.islow = rs.getInt("islow"); config.lowthreshold = rs.getDouble("lowthreshold"); config.duration = rs.getLong("duration"); config.isonline = rs.getInt("isonline"); config.issync = rs.getInt("issync"); config.syncparaencode = rs.getString("syncparaencode"); // 跳过无效配置 if (config.encode == null || config.encode.isEmpty()) { System.err.println("忽略无效配置: 空encode"); continue; } String tag = config.tag; tagToConfigs.computeIfAbsent(tag, k -> new ArrayList<>(10)).add(config); encodeToConfig.put(config.encode, config); } System.out.println("加载配置: " + encodeToConfig.size() + " 个参数"); return new ConfigCollection(tagToConfigs, encodeToConfig); } catch (SQLException e) { System.err.println("加载参数配置错误:"); e.printStackTrace(); return null; } } @Override public void cancel() { isRunning = false; } } // 状态描述符 public static class Descriptors { public static final MapStateDescriptor<Void, ConfigCollection> configStateDescriptor = new MapStateDescriptor<>( "configState", TypeInformation.of(Void.class), TypeInformation.of(ConfigCollection.class) ); } // 优化后的异常检测函数(无定时器实现) public static class OptimizedAnomalyDetectionFunction extends KeyedBroadcastProcessFunction<String, JSONObject, ConfigCollection, JSONObject> { // 状态管理 private transient MapState<String, AnomalyState> stateMap; // key=encode private transient MapState<String, Double> lastValuesMap; // key=tag private transient MapState<String, Long> lastDataTimeMap; // key=tag private transient ValueState<Long> lastCheckpointState; // 记录该tag上次处理的检查点时间 private transient SimpleDateFormat timeFormat; // 日志频率控制 private transient long lastSyncLogTime = 0; // 侧输出标签用于离线检测 private static final OutputTag<String> OFFLINE_CHECK_TAG = new OutputTag<String>("offline-check"){}; @Override public void open(Configuration parameters) { // 状态TTL配置(30天自动清理) StateTtlConfig ttlConfig = StateTtlConfig.newBuilder(Time.days(300)) .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) .cleanupFullSnapshot() .build(); // 初始化异常状态存储(启用TTL) MapStateDescriptor<String, AnomalyState> stateDesc = new MapStateDescriptor<>( "anomalyState", BasicTypeInfo.STRING_TYPE_INFO, TypeInformation.of(AnomalyState.class) ); stateDesc.enableTimeToLive(ttlConfig); stateMap = getRuntimeContext().getMapState(stateDesc); // 初始化最新值存储(启用TTL) MapStateDescriptor<String, Double> valuesDesc = new MapStateDescriptor<>( "lastValuesState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.DOUBLE_TYPE_INFO ); valuesDesc.enableTimeToLive(ttlConfig); lastValuesMap = getRuntimeContext().getMapState(valuesDesc); // 初始化最后数据时间存储(启用TTL) MapStateDescriptor<String, Long> timeDesc = new MapStateDescriptor<>( "lastDataTimeState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timeDesc.enableTimeToLive(ttlConfig); lastDataTimeMap = getRuntimeContext().getMapState(timeDesc); // 初始化检查点状态(记录上次处理的配置时间) ValueStateDescriptor<Long> checkpointDesc = new ValueStateDescriptor<>( "lastCheckpointState", BasicTypeInfo.LONG_TYPE_INFO ); checkpointDesc.enableTimeToLive(ttlConfig); lastCheckpointState = getRuntimeContext().getState(checkpointDesc); timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); } @Override public void processElement(JSONObject data, ReadOnlyContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); String timeStr = data.getString("time"); long eventTime = timeFormat.parse(timeStr).getTime(); // 更新最后数据时间 lastDataTimeMap.put(tag, eventTime); // 获取广播配置 ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) { return; } List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null || configs.isEmpty()) { return; } // 跟踪已恢复的tag Set<String> recoveredTags = new HashSet<>(); // ========== 清理无效状态 ========== // 修复:使用临时列表避免ConcurrentModificationException List<String> keysToRemove = new ArrayList<>(); for (String encode : stateMap.keys()) { boolean found = false; for (ULEParamConfig cfg : configs) { if (cfg.encode.equals(encode)) { found = true; break; } } if (!found) { System.out.println("清理过期状态: " + encode); keysToRemove.add(encode); } } for (String encode : keysToRemove) { stateMap.remove(encode); } // ========== 检查离线状态(基于配置检查点) ========== Long lastCP = lastCheckpointState.value(); if (lastCP == null || configCollection.checkpointTime > lastCP) { // 新配置到达,检查离线状态 for (ULEParamConfig config : configs) { if (config.isonline == 1) { // 获取该tag的最后数据时间 Long lastEventTime = lastDataTimeMap.get(tag); if (lastEventTime == null) { // 从未收到数据,触发离线报警 AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(5); if (!status.reported) { // 使用超时时间点作为触发时间 long timeoutPoint = configCollection.checkpointTime; String triggerTime = timeFormat.format(new Date(timeoutPoint)); reportAnomaly(5, 1, 0.0, triggerTime, config, out); status.reported = true; stateMap.put(config.encode, state); } } else { // 计算超时时间点:检查点时间 - duration long timeoutPoint = configCollection.checkpointTime - config.duration * 60 * 1000; if (lastEventTime < timeoutPoint) { // 触发离线报警 AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(5); if (!status.reported) { // 使用超时时间点作为触发时间 String triggerTime = timeFormat.format(new Date(timeoutPoint)); reportAnomaly(5, 1, 0.0, triggerTime, config, out); status.reported = true; stateMap.put(config.encode, state); } } } } } // 更新检查点状态 lastCheckpointState.update(configCollection.checkpointTime); } double value = 0; boolean valueSet = false; // 遍历配置项进行异常检测 for (ULEParamConfig config : configs) { if (!valueSet) { value = "436887485805570949".equals(config.datatype) ? data.getDouble("ontime") : data.getDouble("avg"); lastValuesMap.put(tag, value); valueSet = true; } // 获取或初始化状态 AnomalyState state = getOrCreateState(config.encode); // ========== 离线恢复检测 ========== if (config.isonline == 1) { AnomalyStatus status = state.getStatus(5); if (status.reported) { // 设备重新上线,发送恢复事件 if (!recoveredTags.contains(tag)) { // 使用当前系统时间作为恢复时间 String recoveryTime = timeFormat.format(new Date()); reportAnomaly(5, 0, 0.0, recoveryTime, config, out); status.reset(); recoveredTags.add(tag); // 标记该tag已恢复 System.out.println("设备 " + tag + " 恢复在线"); } } } // 处理异常类型 checkConstantValueAnomaly(config, value, timeStr, state, out); checkZeroValueAnomaly(config, value, timeStr, state, out); checkThresholdAnomaly(config, value, timeStr, state, out); checkSyncAnomaly(config, value, timeStr, state, configCollection, out); // 保存状态 stateMap.put(config.encode, state); } } // 恒值检测 private void checkConstantValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.constantvalue != 1) return; try { AnomalyStatus status = state.getStatus(1); long durationThreshold = config.duration * 60 * 1000; Date timestamp = timeFormat.parse(timeStr); if (status.lastValue == null) { status.lastValue = currentValue; status.lastChangeTime = timestamp; return; } if (Math.abs(currentValue - status.lastValue) > 0.001) { status.lastValue = currentValue; status.lastChangeTime = timestamp; if (status.reported) { reportAnomaly(1, 0, currentValue, timeStr, config, out); } status.reset(); return; } long elapsed = timestamp.getTime() - status.lastChangeTime.getTime(); if (elapsed > durationThreshold) { if (!status.reported) { reportAnomaly(1, 1, currentValue, timeStr, config, out); status.reported = true; } } } catch (Exception e) { System.err.println("恒值检测错误: " + config.encode + " - " + e.getMessage()); } } // 零值检测 private void checkZeroValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.iszero != 1) return; try { AnomalyStatus status = state.getStatus(2); Date timestamp = timeFormat.parse(timeStr); boolean isZero = Math.abs(currentValue) < 0.001; if (isZero) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(2, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(2, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("零值检测错误: " + config.encode + " - "+e.getMessage()); } } // 阈值检测 private void checkThresholdAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { try { if (config.ishigh == 1) { AnomalyStatus highStatus = state.getStatus(3); processThresholdAnomaly(highStatus, currentValue, timeStr, currentValue > config.highthreshold, config, 3, out); } if (config.islow == 1) { AnomalyStatus lowStatus = state.getStatus(4); processThresholdAnomaly(lowStatus, currentValue, timeStr, currentValue < config.lowthreshold, config, 4, out); } } catch (Exception e) { System.err.println("阈值检测错误: " + config.encode + " - " + e.getMessage()); } } private void processThresholdAnomaly(AnomalyStatus status, double currentValue, String timeStr, boolean isAnomaly, ULEParamConfig config, int anomalyType, Collector<JSONObject> out) { try { Date timestamp = timeFormat.parse(timeStr); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(anomalyType, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(anomalyType, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("阈值处理错误: " + config.encode + " - " + e.getMessage()); } } // 同步检测 - 优化实现 private void checkSyncAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, ConfigCollection configCollection, Collector<JSONObject> out) { if (config.issync != 1 || config.syncparaencode == null) return; try { // 日志频率控制:每分钟最多打印一次 long now = System.currentTimeMillis(); if (now - lastSyncLogTime > 60000) { System.out.println("同步检测 - 当前配置: " + config); lastSyncLogTime = now; } AnomalyStatus status = state.getStatus(6); Date timestamp = timeFormat.parse(timeStr); // 直接获取关联配置 ULEParamConfig relatedConfig = configCollection.encodeToConfig.get(config.syncparaencode); if (relatedConfig == null) { System.out.println("同步检测: 未找到关联配置, encode=" + config.syncparaencode); return; } // 查找关联tag String relatedTag = null; for (Map.Entry<String, List<ULEParamConfig>> entry : configCollection.tagToConfigs.entrySet()) { for (ULEParamConfig cfg : entry.getValue()) { if (cfg.encode.equals(relatedConfig.encode)) { relatedTag = entry.getKey(); break; } } if (relatedTag != null) break; } if (relatedTag == null) { System.out.println("同步检测: 未找到关联Tag,关联encode=" + relatedConfig.encode); return; } // 安全获取关联值 Double relatedValue = null; if (lastValuesMap.contains(relatedTag)) { relatedValue = lastValuesMap.get(relatedTag); } if (relatedValue == null) { System.out.println("同步检测: 关联值不可用, tag=" + relatedTag); return; } // 优化比较逻辑:使用更精确的阈值 boolean isAnomaly = (currentValue >= 0.99) && (Math.abs(relatedValue) < 0.01); // 调试信息 System.out.printf("同步检测: %s (%.4f) vs %s (%.4f) -> %b%n", config.tag, currentValue, relatedTag, relatedValue, isAnomaly); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(6, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(6, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("同步检测错误: " + config.encode + " - " + e.getMessage()); } } // 报告异常 private void reportAnomaly(int anomalyType, int statusFlag, double value, String time, ULEParamConfig config, Collector<JSONObject> out) { JSONObject event = new JSONObject(); event.put("tag", config.tag); event.put("paracode", config.encode); event.put("abnormaltype", anomalyType); event.put("statusflag", statusFlag); event.put("datavalue", value); event.put("triggertime", time); out.collect(event); } @Override public void processBroadcastElement(ConfigCollection newConfig, Context ctx, Collector<JSONObject> out) { BroadcastState<Void, ConfigCollection> state = ctx.getBroadcastState(Descriptors.configStateDescriptor); try { // 获取旧配置 ConfigCollection oldConfig = state.get(null); // 处理配置变更:清理不再启用的报警 if (oldConfig != null) { for (Map.Entry<String, ULEParamConfig> entry : oldConfig.encodeToConfig.entrySet()) { String encode = entry.getKey(); ULEParamConfig oldCfg = entry.getValue(); // 检查配置是否被删除或禁用 ULEParamConfig newCfg = newConfig.encodeToConfig.get(encode); if (newCfg == null || !isAlarmEnabled(newCfg, oldCfg)) { // 发送恢复事件 sendRecoveryEvents(encode, oldCfg, ctx, out); } } } // 更新广播状态 state.put(null, newConfig); System.out.println("广播配置更新完成, 配置项: " + newConfig.encodeToConfig.size()); System.out.println("配置摘要: " + newConfig.encodeToConfig.keySet()); // 触发离线检测 for (String tag : newConfig.allTags) { ctx.output(OFFLINE_CHECK_TAG, tag); } } catch (Exception e) { e.printStackTrace(); } } // 检查报警是否启用 private boolean isAlarmEnabled(ULEParamConfig newCfg, ULEParamConfig oldCfg) { // 检查所有报警类型是否被禁用 return (oldCfg.constantvalue == 1 && newCfg.constantvalue == 1) || (oldCfg.iszero == 1 && newCfg.iszero == 1) || (oldCfg.ishigh == 1 && newCfg.ishigh == 1) || (oldCfg.islow == 1 && newCfg.islow == 1) || (oldCfg.isonline == 1 && newCfg.isonline == 1) || (oldCfg.issync == 1 && newCfg.issync == 1); } // 发送恢复事件 private void sendRecoveryEvents(String encode, ULEParamConfig config, Context ctx, Collector<JSONObject> out) { try { AnomalyState state = stateMap.get(encode); if (state == null) return; // 遍历所有可能的报警类型 for (int type = 1; type <= 6; type++) { AnomalyStatus status = state.getStatus(type); if (status.reported) { JSONObject recoveryEvent = new JSONObject(); recoveryEvent.put("tag", config.tag); recoveryEvent.put("paracode", config.encode); recoveryEvent.put("abnormaltype", type); recoveryEvent.put("statusflag", 0); // 恢复事件 recoveryEvent.put("datavalue", 0.0); recoveryEvent.put("triggertime", timeFormat.format(new Date())); out.collect(recoveryEvent); status.reset(); } } // 更新状态 stateMap.put(encode, state); } catch (Exception e) { System.err.println("发送恢复事件失败: " + e.getMessage()); } } // ========== 辅助方法 ========== private ConfigCollection getBroadcastConfig(ReadOnlyContext ctx) throws Exception { return ctx.getBroadcastState(Descriptors.configStateDescriptor).get(null); } private AnomalyState getOrCreateState(String encode) throws Exception { AnomalyState state = stateMap.get(encode); if (state == null) { state = new AnomalyState(); } return state; } } // 异常状态类 public static class AnomalyState implements Serializable { private static final long serialVersionUID = 1L; private final Map<Integer, AnomalyStatus> statusMap = new HashMap<>(); public AnomalyStatus getStatus(int type) { return statusMap.computeIfAbsent(type, k -> new AnomalyStatus()); } } // 异常状态详情 public static class AnomalyStatus implements Serializable { private static final long serialVersionUID = 1L; public Date startTime; // 异常开始时间 public Double lastValue; // 用于恒值检测 public Date lastChangeTime; // 值最后变化时间 public boolean reported; // 是否已报告 public void reset() { startTime = null; lastValue = null; lastChangeTime = null; reported = false; } } } 运行日志为"C:\Program Files (x86)\Java\jdk1.8.0_102\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:12725,suspend=y,server=n -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.2\captureAgent\debugger-agent.jar=file:/C:/Users/Administrator/AppData/Local/Temp/capture.props -Dfile.encoding=UTF-8 -classpath C:\Users\Administrator\AppData\Local\Temp\classpath679239090.jar com.tongchuang.realtime.mds.ULEDataanomalyanalysis 已连接到目标 VM, 地址: ''127.0.0.1:12725',传输: '套接字'' SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory] 加载配置: 30 个参数 配置加载完成,检查点时间: Mon Aug 04 10:51:26 CST 2025 广播配置更新完成, 配置项: 30 配置摘要: [EP000015, EP000014, EP000013, EP000012, EP000011, EP000010, EP100013, EP100014, EP000019, EP000018, EP100010, EP000017, EP100011, EP000016, EP100012, EP000004, EP000003, EP000002, EP000001, EP000022, EP000021, EP100007, EP000020, EP100008, EP100009, EP000009, EP000008, EP000007, EP000006, EP000005] 分钟数据流> {"times":"2025-08-04 10:51","datas":{"DA-LT-5BT0001":{"ontime":3093.452,"avg":3128.6265,"min":3083.741,"max":3185.4487},"DA-LT-6BT008":{"ontime":174.2086,"avg":172.546,"min":171.8335,"max":174.2086},"DA-LT-5BT0005":{"ontime":408.24,"avg":407.208,"min":404.94,"max":409.2},"DA-LT-6BT004":{"ontime":1219.4089,"avg":1219.8833,"min":1219.3755,"max":1220.4434},"DA-LT-5BT0004":{"ontime":1200.4,"avg":1200.045,"min":1199.5,"max":1200.5},"DA-LT-6BT005":{"ontime":389.8511,"avg":390.6297,"min":389.8511,"max":391.9514},"DA-LT-5BT0008":{"ontime":193.6,"avg":190.7653,"min":186.94,"max":193.82},"DA-NY-LG1ZL-2-001":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA-LT-4BT0008":{"ontime":124.2625,"avg":124.46,"min":123.1875,"max":125.7875},"DA-LT-4BT0007":{"ontime":169.4,"avg":169.715,"min":168.9,"max":170.3},"DB5701P250A00_101":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0004":{"ontime":1211.0,"avg":1211.0,"min":1211.0,"max":1211.0},"DA-LT-6BT001":{"ontime":174443.58,"avg":173604.8032,"min":172657.02,"max":174912.67},"DA-LT-4BT0005":{"ontime":293.0625,"avg":293.5583,"min":292.1875,"max":294.6875},"DA-NY-LG2ZL-2-003":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA_DB195_RH_R_0281":{"ontime":283.58,"avg":310.297,"min":283.58,"max":384.99},"DA-DB195-RH-B-0200":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-DB195-RH-B-0201":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0001":{"ontime":120682.57,"avg":120638.2251,"min":119844.39,"max":121553.055}}} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {"times":"2025-08-04 10:52","datas":{"DA-LT-5BT0001":{"ontime":3105.4685,"avg":3127.2034,"min":3085.331,"max":3208.071},"DA-LT-6BT008":{"ontime":172.8935,"avg":173.5736,"min":172.8935,"max":174.0123},"DA-LT-5BT0005":{"ontime":404.88,"avg":405.671,"min":404.76,"max":407.34},"DA-LT-5BT0004":{"ontime":1199.4,"avg":1198.915,"min":1198.2001,"max":1199.6},"DA-LT-6BT004":{"ontime":1220.4434,"avg":1220.8577,"min":1220.3433,"max":1221.2441},"DA-LT-6BT005":{"ontime":391.971,"avg":392.889,"min":391.971,"max":393.3843},"DA-LT-5BT0008":{"ontime":186.88,"avg":188.0343,"min":186.8,"max":189.34},"DA-NY-LG1ZL-2-001":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA-LT-4BT0008":{"ontime":124.6625,"avg":124.4888,"min":123.825,"max":125.3625},"DA-LT-4BT0007":{"ontime":170.0,"avg":170.085,"min":169.3,"max":170.4},"DB5701P250A00_101":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0004":{"ontime":1211.0,"avg":1210.3333,"min":1210.0,"max":1211.0},"DA-LT-6BT001":{"ontime":173081.9,"avg":173387.4525,"min":172657.1,"max":174215.6},"DA-LT-4BT0005":{"ontime":294.0625,"avg":293.925,"min":292.625,"max":294.5},"DA-NY-LG2ZL-2-003":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA_DB195_RH_R_0281":{"ontime":311.85,"avg":298.3053,"min":226.51,"max":340.17},"DA-DB195-RH-B-0200":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-DB195-RH-B-0201":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0001":{"ontime":120709.81,"avg":121436.6195,"min":120241.945,"max":123212.08}}} 异常检测结果> {"abnormaltype":2,"paracode":"EP000022","datavalue":0.0,"tag":"DA-NY-LG1ZL-2-001","triggertime":"2025-08-04 10:52","statusflag":1} 异常检测结果> {"abnormaltype":4,"paracode":"EP000022","datavalue":0.0,"tag":"DA-NY-LG1ZL-2-001","triggertime":"2025-08-04 10:52","statusflag":1} 异常检测结果> {"abnormaltype":4,"paracode":"EP000001","datavalue":1.0,"tag":"DA-DB195-RH-B-0201","triggertime":"2025-08-04 10:52","statusflag":1} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {"times":"2025-08-04 10:53","datas":{"DA-LT-5BT0001":{"ontime":3088.489,"avg":3114.5305,"min":3066.6252,"max":3183.479},"DA-LT-6BT008":{"ontime":173.9338,"avg":174.8619,"min":173.9141,"max":176.7015},"DA-LT-5BT0005":{"ontime":407.34,"avg":408.787,"min":407.34,"max":409.62},"DA-LT-5BT0004":{"ontime":1198.2001,"avg":1197.635,"min":1197.0,"max":1198.3},"DA-LT-6BT004":{"ontime":1221.1775,"avg":1221.4828,"min":1221.144,"max":1221.7448},"DA-LT-6BT005":{"ontime":393.345,"avg":393.5502,"min":393.031,"max":394.1498},"DA-LT-5BT0008":{"ontime":189.26,"avg":190.819,"min":189.26,"max":192.74},"DA-NY-LG1ZL-2-001":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA-LT-4BT0008":{"ontime":124.2125,"avg":124.3077,"min":123.5125,"max":125.1125},"DA-LT-4BT0007":{"ontime":169.4,"avg":169.0767,"min":167.7,"max":169.8},"DB5701P250A00_101":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0004":{"ontime":1210.0,"avg":1209.3167,"min":1209.0,"max":1210.0},"DA-LT-6BT001":{"ontime":173364.6,"avg":173578.1482,"min":172720.47,"max":174390.27},"DA-LT-4BT0005":{"ontime":292.8125,"avg":292.6771,"min":291.1875,"max":293.6875},"DA-NY-LG2ZL-2-003":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA_DB195_RH_R_0281":{"ontime":295.82,"avg":294.4385,"min":273.98,"max":322.66},"DA-DB195-RH-B-0200":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-DB195-RH-B-0201":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0001":{"ontime":122562.12,"avg":122467.8892,"min":120901.414,"max":124176.8}}} 异常检测结果> {"abnormaltype":1,"paracode":"EP000022","datavalue":0.0,"tag":"DA-NY-LG1ZL-2-001","triggertime":"2025-08-04 10:53","statusflag":1} 异常检测结果> {"abnormaltype":1,"paracode":"EP000001","datavalue":1.0,"tag":"DA-DB195-RH-B-0201","triggertime":"2025-08-04 10:53","statusflag":1} 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {"times":"2025-08-04 10:54","datas":{"DA-LT-5BT0001":{"ontime":3078.615,"avg":3082.1354,"min":3039.7373,"max":3216.3108},"DA-LT-6BT008":{"ontime":175.5434,"avg":173.7907,"min":172.8149,"max":175.5434},"DA-LT-5BT0005":{"ontime":409.38,"avg":408.7688,"min":407.94,"max":409.68},"DA-LT-5BT0004":{"ontime":1196.9,"avg":1196.2543,"min":1195.3,"max":1197.1},"DA-LT-6BT004":{"ontime":1221.7448,"avg":1221.7742,"min":1221.6447,"max":1221.8448},"DA-LT-6BT005":{"ontime":393.1291,"avg":392.4774,"min":392.1673,"max":393.1291},"DA-LT-5BT0008":{"ontime":191.14,"avg":191.1912,"min":190.66,"max":191.74},"DA-NY-LG1ZL-2-001":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA-LT-4BT0008":{"ontime":123.825,"avg":123.6761,"min":122.225,"max":124.7875},"DA-LT-4BT0007":{"ontime":167.5,"avg":167.35,"min":167.1,"max":167.5},"DB5701P250A00_101":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0004":{"ontime":1209.0,"avg":1209.0,"min":1209.0,"max":1209.0},"DA-LT-6BT001":{"ontime":173662.19,"avg":173854.9536,"min":172842.14,"max":175016.47},"DA-LT-4BT0005":{"ontime":290.625,"avg":291.1727,"min":290.25,"max":291.9375},"DA-NY-LG2ZL-2-003":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA_DB195_RH_R_0281":{"ontime":316.75,"avg":294.1453,"min":263.4,"max":318.26},"DA-DB195-RH-B-0200":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0001":{"ontime":123655.62,"avg":122644.2382,"min":121316.24,"max":123656.664},"DA-DB195-RH-B-0201":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0}}} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {"times":"2025-08-04 10:55","datas":{"DA-LT-5BT0001":{"ontime":3218.5786,"avg":3046.705,"min":2944.5356,"max":3218.5786},"DA-LT-6BT008":{"ontime":174.4442,"avg":173.5075,"min":171.5587,"max":174.719},"DA-LT-5BT0005":{"ontime":407.76,"avg":410.281,"min":406.38,"max":412.92},"DA-LT-5BT0004":{"ontime":1195.5,"avg":1194.82,"min":1194.4,"max":1195.5},"DA-LT-6BT004":{"ontime":1221.8115,"avg":1221.7748,"min":1221.678,"max":1221.8448},"DA-LT-6BT005":{"ontime":392.2654,"avg":391.9164,"min":391.4803,"max":392.344},"DA-LT-5BT0008":{"ontime":191.16,"avg":193.6457,"min":190.1,"max":195.76},"DA-NY-LG1ZL-2-001":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA-LT-4BT0008":{"ontime":122.225,"avg":123.2477,"min":121.95,"max":125.275},"DB5701P250A00_101":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0004":{"ontime":1209.0,"avg":1209.1833,"min":1209.0,"max":1210.0},"DA-LT-6BT001":{"ontime":173967.0,"avg":173799.209,"min":172448.05,"max":174773.38},"DA-LT-4BT0005":{"ontime":290.625,"avg":292.1531,"min":290.625,"max":294.1875},"DA-NY-LG2ZL-2-003":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA_DB195_RH_R_0281":{"ontime":295.73,"avg":299.1413,"min":258.3,"max":335.65},"DA-DB195-RH-B-0200":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0001":{"ontime":123094.66,"avg":121517.155,"min":120457.766,"max":123094.66},"DA-DB195-RH-B-0201":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0}}} 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 加载配置: 30 个参数 配置加载完成,检查点时间: Mon Aug 04 10:56:27 CST 2025 广播配置更新完成, 配置项: 30 配置摘要: [EP000015, EP000014, EP000013, EP000012, EP000011, EP000010, EP100013, EP100014, EP000019, EP000018, EP100010, EP000017, EP100011, EP000016, EP100012, EP000004, EP000003, EP000002, EP000001, EP000022, EP000021, EP100007, EP000020, EP100008, EP100009, EP000009, EP000008, EP000007, EP000006, EP000005] 分钟数据流> {"times":"2025-08-04 10:56","datas":{"DA-LT-5BT0001":{"ontime":3057.6804,"avg":3012.5768,"min":2949.3123,"max":3078.372},"DA-LT-6BT008":{"ontime":171.8335,"avg":172.1848,"min":171.8335,"max":172.5597},"DA-LT-5BT0005":{"ontime":412.74,"avg":413.215,"min":412.02,"max":415.14},"DA-LT-6BT004":{"ontime":1221.678,"avg":1221.6897,"min":1221.6113,"max":1221.7448},"DA-LT-5BT0004":{"ontime":1194.8,"avg":1196.8434,"min":1194.8,"max":1199.3},"DA-LT-6BT005":{"ontime":391.5392,"avg":390.6559,"min":389.8314,"max":391.5392},"DA-LT-5BT0008":{"ontime":195.66,"avg":195.8223,"min":195.08,"max":197.42},"DA-NY-LG1ZL-2-001":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA-LT-4BT0008":{"ontime":125.275,"avg":124.2504,"min":123.45,"max":126.2625},"DB5701P250A00_101":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0004":{"ontime":1210.0,"avg":1210.0,"min":1210.0,"max":1210.0},"DA-LT-6BT001":{"ontime":173206.77,"avg":174326.7505,"min":173206.77,"max":175154.61},"DA-LT-4BT0005":{"ontime":293.875,"avg":293.4646,"min":292.5625,"max":295.0625},"DA-NY-LG2ZL-2-003":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA_DB195_RH_R_0281":{"ontime":288.86,"avg":301.8983,"min":268.97,"max":349.89},"DA-DB195-RH-B-0200":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0001":{"ontime":120710.516,"avg":120748.8062,"min":119682.57,"max":122023.83},"DA-DB195-RH-B-0201":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0}}} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001 分钟数据流> {"times":"2025-08-04 10:57","datas":{"DA-LT-5BT0001":{"ontime":3005.6284,"avg":3004.7748,"min":2962.2488,"max":3059.464},"DA-LT-6BT008":{"ontime":171.9316,"avg":171.3516,"min":170.5968,"max":172.0494},"DA-LT-5BT0005":{"ontime":415.14,"avg":416.542,"min":415.14,"max":417.24},"DA-LT-5BT0004":{"ontime":1199.3,"avg":1200.7184,"min":1199.3,"max":1201.3},"DA-LT-6BT004":{"ontime":1221.7114,"avg":1221.5724,"min":1221.411,"max":1221.7448},"DA-LT-6BT005":{"ontime":389.8118,"avg":389.4774,"min":389.1444,"max":389.8118},"DA-LT-5BT0008":{"ontime":197.42,"avg":198.3487,"min":197.42,"max":198.68},"DA-NY-LG1ZL-2-001":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA-LT-4BT0008":{"ontime":124.2,"avg":123.2256,"min":121.8625,"max":126.1},"DB5701P250A00_101":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0004":{"ontime":1210.0,"avg":1210.0,"min":1210.0,"max":1210.0},"DA-LT-6BT001":{"ontime":174333.06,"avg":173710.917,"min":172544.47,"max":174776.31},"DA-NY-LG2ZL-2-003":{"ontime":0.0,"avg":0.0,"min":0.0,"max":0.0},"DA-LT-4BT0005":{"ontime":294.0,"avg":293.3229,"min":291.5625,"max":296.0},"DA_DB195_RH_R_0281":{"ontime":289.38,"avg":297.7735,"min":273.12,"max":343.61},"DA-DB195-RH-B-0200":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0},"DA-LT-4BT0001":{"ontime":121024.04,"avg":120971.4221,"min":120043.63,"max":122208.27},"DA-DB195-RH-B-0201":{"ontime":1.0,"avg":1.0,"min":1.0,"max":1.0}}} 同步检测 - 当前配置: ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=1000000.0, islow=1, lowthreshold=10.0, duration=1) 同步检测: 关联值不可用, tag=DA-NY-LG1ZL-2-001。其中同步分析功能为实现,DA-LT-4BT0007的离线检测为实现
08-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值