45、Flink 的指标体系介绍及验证(1)-指标类型及指标实现示例
Flink 系列文章
1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接
13、Flink 的table api与sql的基本概念、通用api介绍及入门示例
14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性
15、Flink 的table api与sql之流式概念-详解的介绍了动态表、时间属性配置(如何处理更新结果)、时态表、流上的join、流上的确定性以及查询配置
16、Flink 的table api与sql之连接外部系统: 读写外部系统的连接器和格式以及FileSystem示例(1)
16、Flink 的table api与sql之连接外部系统: 读写外部系统的连接器和格式以及Elasticsearch示例(2)
16、Flink 的table api与sql之连接外部系统: 读写外部系统的连接器和格式以及Apache Kafka示例(3)
16、Flink 的table api与sql之连接外部系统: 读写外部系统的连接器和格式以及JDBC示例(4)
16、Flink 的table api与sql之连接外部系统: 读写外部系统的连接器和格式以及Apache Hive示例(6)
17、Flink 之Table API: Table API 支持的操作(1)
17、Flink 之Table API: Table API 支持的操作(2)
18、Flink的SQL 支持的操作和语法
19、Flink 的Table API 和 SQL 中的内置函数及示例(1)
19、Flink 的Table API 和 SQL 中的自定义函数及示例(2)
19、Flink 的Table API 和 SQL 中的自定义函数及示例(3)
19、Flink 的Table API 和 SQL 中的自定义函数及示例(4)
20、Flink SQL之SQL Client: 不用编写代码就可以尝试 Flink SQL,可以直接提交 SQL 任务到集群上
21、Flink 的table API与DataStream API 集成(1)- 介绍及入门示例、集成说明
21、Flink 的table API与DataStream API 集成(2)- 批处理模式和inser-only流处理
21、Flink 的table API与DataStream API 集成(3)- changelog流处理、管道示例、类型转换和老版本转换示例
21、Flink 的table API与DataStream API 集成(完整版)
22、Flink 的table api与sql之创建表的DDL
24、Flink 的table api与sql之Catalogs(介绍、类型、java api和sql实现ddl、java api和sql操作catalog)-1
24、Flink 的table api与sql之Catalogs(java api操作数据库、表)-2
24、Flink 的table api与sql之Catalogs(java api操作视图)-3
24、Flink 的table api与sql之Catalogs(java api操作分区与函数)-4
25、Flink 的table api与sql之函数(自定义函数示例)
26、Flink 的SQL之概览与入门示例
27、Flink 的SQL之SELECT (select、where、distinct、order by、limit、集合操作和去重)介绍及详细示例(1)
27、Flink 的SQL之SELECT (SQL Hints 和 Joins)介绍及详细示例(2)
27、Flink 的SQL之SELECT (窗口函数)介绍及详细示例(3)
27、Flink 的SQL之SELECT (窗口聚合)介绍及详细示例(4)
27、Flink 的SQL之SELECT (Group Aggregation分组聚合、Over Aggregation Over聚合 和 Window Join 窗口关联)介绍及详细示例(5)
27、Flink 的SQL之SELECT (Top-N、Window Top-N 窗口 Top-N 和 Window Deduplication 窗口去重)介绍及详细示例(6)
27、Flink 的SQL之SELECT (Pattern Recognition 模式检测)介绍及详细示例(7)
28、Flink 的SQL之DROP 、ALTER 、INSERT 、ANALYZE 语句
29、Flink SQL之DESCRIBE、EXPLAIN、USE、SHOW、LOAD、UNLOAD、SET、RESET、JAR、JOB Statements、UPDATE、DELETE(1)
29、Flink SQL之DESCRIBE、EXPLAIN、USE、SHOW、LOAD、UNLOAD、SET、RESET、JAR、JOB Statements、UPDATE、DELETE(2)
30、Flink SQL之SQL 客户端(通过kafka和filesystem的例子介绍了配置文件使用-表、视图等)
31、Flink的SQL Gateway介绍及示例
32、Flink table api和SQL 之用户自定义 Sources & Sinks实现及详细示例
33、Flink 的Table API 和 SQL 中的时区
35、Flink 的 Formats 之CSV 和 JSON Format
36、Flink 的 Formats 之Parquet 和 Orc Format
41、Flink之Hive 方言介绍及详细示例
40、Flink 的Apache Kafka connector(kafka source的介绍及使用示例)-1
40、Flink 的Apache Kafka connector(kafka sink的介绍及使用示例)-2
40、Flink 的Apache Kafka connector(kafka source 和sink 说明及使用示例) 完整版
42、Flink 的table api与sql之Hive Catalog
43、Flink之Hive 读写及详细验证示例
44、Flink之module模块介绍及使用示例和Flink SQL使用hive内置函数及自定义函数详细示例–网上有些说法好像是错误的
45、Flink 的指标体系介绍及验证(1)-指标类型及指标实现示例
45、Flink 的指标体系介绍及验证(2)-指标的scope、报告、系统指标以及追踪、api集成示例和dashboard集成
45、Flink 的指标体系介绍及验证(3)- 完整版
46、Flink 的table api与sql之配项列表及示例
文章目录
- Flink 系列文章
- 一、Flink 指标体系
- 1、Registering metrics 注册指标
- 1)、指标类型
- 2)、计数器
- 3)、Gauge
- 4)、Histogram
- 5)、Meter
本文简单的介绍了Flink 的指标体系的第一部分,即指标类型以及四种类型的代码实现示例。
本专题分为三部分,即:
45、Flink 的指标体系介绍及验证(1)-指标类型及指标实现示例
45、Flink 的指标体系介绍及验证(2)-指标的scope、报告、系统指标以及追踪、api集成示例和dashboard集成
45、Flink 的指标体系介绍及验证(3)- 完整版
本文依赖nc能正常使用。
本文分为5个部分,即指标分类、计数器、gauge、histogram和meter四个指标的代码实现。
本文的示例是在Flink 1.17版本中运行。
一、Flink 指标体系
Flink暴露了一个度量系统,允许收集度量并将其公开给外部系统。
本文涉及的maven依赖
<properties><encoding>UTF-8</encoding><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><java.version>1.8</java.version><scala.version>2.12</scala.version><flink.version>1.17.0</flink.version></properties><dependencies><!-- https://mvnrepository.com/artifact/org.apache.flink/flink-clients --><dependency><groupId>org.apache.flink</groupId><artifactId>flink-clients</artifactId><version>${flink.version}</version><scope>provided</scope></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-java</artifactId><version>${flink.version}</version><scope>provided</scope></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-streaming-java</artifactId><version>${flink.version}</version><scope>provided</scope></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-csv</artifactId><version>${flink.version}</version><scope>provided</scope></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-json</artifactId><version>${flink.version}</version><scope>provided</scope></dependency><!-- flink连接器 --><!-- https://mvnrepository.com/artifact/org.apache.flink/flink-connector-kafka --><dependency><groupId>org.apache.flink</groupId><artifactId>flink-connector-kafka</artifactId><version>${flink.version}</version></dependency></dependencies>
1、Registering metrics 注册指标
通过调用getRuntimeContext().getMetricGroup(),您可以从任何扩展RichFunction的用户函数访问度量系统。此方法返回一个MetricGroup对象,您可以在该对象上创建和注册新度量。
1)、指标类型
Flink支持计数器、仪表盘、柱状图和计量表。Counters, Gauges, Histograms and Meters.
2)、计数器
计数器是用来统计数量的。当前值可以是in-或使用 inc()/inc(long n)或dec()/dec(long n)增减。您可以通过调用MetricGroup上的 counter(String name)来创建和注册计数器。
本示例提供了多种实现方式,供参考。
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.metrics.Counter;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;/*** @author alanchan**/
public class TestMetricsDemo {// public class LineMapper extends RichMapFunction<String, String> {
// private transient Counter counter;
//
// @Override
// public void open(Configuration config) {
// this.counter = getRuntimeContext().getMetricGroup().counter("result2LineCounter");
// }
//
// @Override
// public String map(String value) throws Exception {
// this.counter.inc();
// return value;
// }
// }public static void test1() throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);env.setParallelism(1);// sourceDataStream<String> lines = env.socketTextStream("192.168.10.42", 9999);// transformationDataStream<Tuple2<String, Integer>> result = lines.flatMap(new FlatMapFunction<String, String>() {@Overridepublic void flatMap(String value, Collector<String> out) throws Exception {String[] arr = value.split(",");for (String word : arr) {out.collect(word);}}}).map(new MapFunction<String, Tuple2<String, Integer>>() {@Overridepublic Tuple2<String, Integer> map(String value) throws Exception {return Tuple2.of(value, 1);}}).keyBy(t -> t.f0).sum(1);// SingleOutputStreamOperator<Tuple2<Integer, Integer>> result1 = lines.map(new RichMapFunction<String, Tuple2<Integer, Integer>>() {
//
// @Override
// public Tuple2<Integer, Integer> map(String value) throws Exception {
// int subTaskId = getRuntimeContext().getIndexOfThisSubtask();// 子任务id/分区编号
// return new Tuple2(subTaskId, 1);
// }
// // 按照子任务id/分区编号分组,并统计每个子任务/分区中有几个元素
// }).keyBy(t -> t.f0).sum(1);// RichFlatMapFunction<IN, OUT>// Tuple3<String, Long, Integer> 输入的字符串,行数,统计单词的总数DataStream<Tuple3<String, Long, Integer>> result2 = lines.flatMap(new RichFlatMapFunction<String, Tuple2<String, Long>>() {
// private transient Counter counter;private long result2LineCounter = 0;@Overridepublic void open(Configuration config) {
// this.counter = getRuntimeContext().getMetricGroup().counter("result2LineCounter:");result2LineCounter = getRuntimeContext().getMetricGroup().counter("result2LineCounter:").getCount();}@Overridepublic void flatMap(String value, Collector<Tuple2<String, Long>> out) throws Exception {
// this.counter.inc();result2LineCounter++;System.out.println("计数器行数:" + result2LineCounter);String[] arr = value.split(",");for (String word : arr) {out.collect(Tuple2.of(word, result2LineCounter));}}}).map(new MapFunction<Tuple2<String, Long>, Tuple3<String, Long, Integer>>() {@Overridepublic Tuple3<String, Long, Integer> map(Tuple2<String, Long> value) throws Exception {
// Tuple3<String, Long, Integer> t = Tuple3.of(value.f0, value.f1, 1);return Tuple3.of(value.f0, value.f1, 1);}}).keyBy(t -> t.f0).sum(2);// sinkresult.print("result:");result2.print("result2:");env.execute();}public static void main(String[] args) throws Exception {test1();
// StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// env.setParallelism(1);
// DataStream<String> input = env.fromElements("a", "b", "c", "a", "b", "c");
//
// input.keyBy(value -> value).map(new RichMapFunction<String, String>() {
// private long count = 0;
//
// @Override
// public void open(Configuration parameters) throws Exception {
super.open(parameters);
// count = getRuntimeContext().getMetricGroup().counter("myCounter").getCount();
// }
//
// @Override
// public String map(String value) throws Exception {
// count++;
// return value + ": " + count;
// }
// }).print();
//
// env.execute("Flink Count Counter Example");}}
///验证数据///
// 输入数据
[alanchan@server2 bin]$ nc -lk 9999
hello,123
alan,flink,good
alan_chan,hi,flink//控制台输出:
计数器行数:1
result:> (hello,1)
result2:> (hello,1,1)
result:> (123,1)
result2:> (123,1,1)
计数器行数:2
result2:> (alan,2,1)
result:> (alan,1)
result2:> (flink,2,1)
result:> (flink,1)
result2:> (good,2,1)
result:> (good,1)
计数器行数:3
result:> (alan_chan,1)
result2:> (alan_chan,3,1)
result:> (hi,1)
result2:> (hi,3,1)
result:> (flink,2)
result2:> (flink,2,2)
或者,您也可以使用自己的Counter实现:
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.metrics.Counter;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;/*** @author alanchan**/
public class TestMetricsDemo {public static void test2() throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);env.setParallelism(1);// sourceDataStream<String> lines = env.socketTextStream("192.168.10.42", 9999);// transformation// Tuple3<String, Long, Integer> 输入的字符串,行数,统计单词的总数DataStream<Tuple3<String, Long, Integer>> result = lines.flatMap(new RichFlatMapFunction<String, Tuple2<String, Long>>() {private transient Counter counter;@Overridepublic void open(Configuration config) {this.counter = getRuntimeContext().getMetricGroup().counter("result2LineCounter", new AlanCustomCounter());}@Overridepublic void flatMap(String value, Collector<Tuple2<String, Long>> out) throws Exception {this.counter.inc();
// result2LineCounter++;System.out.println("计数器行数:" + this.counter.getCount());String[] arr = value.split(",");for (String word : arr) {out.collect(Tuple2.of(word, this.counter.getCount()));}}}).map(new MapFunction<Tuple2<String, Long>, Tuple3<String, Long, Integer>>() {@Overridepublic Tuple3<String, Long, Integer> map(Tuple2<String, Long> value) throws Exception {return Tuple3.of(value.f0, value.f1, 1);}}).keyBy(t -> t.f0).sum(2);// sinkresult.print("result:");env.execute();}public static class AlanCustomCounter implements Counter {private long count;@Overridepublic void inc() {count += 2;}@Overridepublic void inc(long n) {count += n;}@Overridepublic void dec() {count -= 2;}@Overridepublic void dec(long n) {count -= n;}@Overridepublic long getCount() {return count;}}public static void main(String[] args) throws Exception {test2();}}///验证数据///
// 输入数据
[alanchan@server2 bin]$ nc -lk 9999
hello,123
alan,flink,good
alan_chan,hi,flink//控制台输出:
计数器行数:2
result:> (hello,2,1)
result:> (123,2,1)
计数器行数:4
result:> (alan,4,1)
result:> (flink,4,1)
result:> (good,4,1)
计数器行数:6
result:> (alan_chan,6,1)
result:> (hi,6,1)
result:> (flink,4,2)
3)、Gauge
仪表可根据需要提供任何类型的值。为了使用Gauge,您必须首先创建一个实现org.apache.flink.metrics.Guge接口的类。返回值的类型没有限制。您可以通过调用MetricGroup上的gauge(String name, Gauge gauge) 来注册gauge。
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.metrics.Counter;
import org.apache.flink.metrics.Gauge;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;/*** @author alanchan**/
public class TestMetricsGaugeDemo {
// public class MyMapper extends RichMapFunction<String, String> {
// private transient int valueToExpose = 0;
//
// @Override
// public void open(Configuration config) {
// getRuntimeContext().getMetricGroup().gauge("MyGauge", new Gauge<Integer>() {
// @Override
// public Integer getValue() {
// return valueToExpose;
// }
// });
// }
//
// @Override
// public String map(String value) throws Exception {
// valueToExpose++;
// return value;
// }
// }public static void test1() throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);env.setParallelism(1);// sourceDataStream<String> lines = env.socketTextStream("192.168.10.42", 9999);// transformation// RichFlatMapFunction<IN, OUT>// Tuple3<String, String, Integer> 输入的字符串,alan lines[行数],统计单词的总数DataStream<Tuple3<String, String, Integer>> result = lines.flatMap(new RichFlatMapFunction<String, Tuple2<String, String>>() {private long result2LineCounter = 0;private Gauge<String> gauge = null;@Overridepublic void open(Configuration config) {result2LineCounter = getRuntimeContext().getMetricGroup().counter("resultLineCounter:").getCount();gauge = getRuntimeContext().getMetricGroup().gauge("alanGauge", new Gauge<String>() {@Overridepublic String getValue() {return "alan lines[" + result2LineCounter + "]";}});}@Overridepublic void flatMap(String value, Collector<Tuple2<String, String>> out) throws Exception {result2LineCounter++;System.out.println("计数器行数:" + result2LineCounter);String[] arr = value.split(",");for (String word : arr) {out.collect(Tuple2.of(word, gauge.getValue()));}}}).map(new MapFunction<Tuple2<String, String>, Tuple3<String, String, Integer>>() {@Overridepublic Tuple3<String, String, Integer> map(Tuple2<String, String> value) throws Exception {return Tuple3.of(value.f0, value.f1, 1);}}).keyBy(t -> t.f0).sum(2);// sinkresult.print("result:");env.execute();}public static void main(String[] args) throws Exception {test1();}}///验证数据///
// 输入数据
[alanchan@server2 bin]$ nc -lk 9999
hello,123
alan,flink,good
alan_chan,hi,flink//控制台输出:
计数器行数:1
result:> (hello,alan lines[1],1)
result:> (123,alan lines[1],1)
计数器行数:2
result:> (alan,alan lines[2],1)
result:> (flink,alan lines[2],1)
result:> (good,alan lines[2],1)
计数器行数:3
result:> (alan_chan,alan lines[3],1)
result:> (hi,alan lines[3],1)
result:> (flink,alan lines[2],2)
报告器会将暴露的对象转换为String,这意味着需要一个有意义的toString()实现。
4)、Histogram
直方图测量长值的分布。您可以通过调用MetricGroup上的histogram(String name, Histogram histogram) 来注册一个对象。
下面的示例是自己实现的Histogram接口,仅仅用于演示实现过程。
import java.io.Serializable;import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.metrics.Gauge;
//import com.codahale.metrics.Histogram;
import org.apache.flink.metrics.Histogram;
import org.apache.flink.metrics.HistogramStatistics;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;/*** @author alanchan**/
public class TestMetricsHistogramDemo {// public class MyMapper extends RichMapFunction<Long, Long> {
// private transient Histogram histogram;
//
// @Override
// public void open(Configuration config) {
// this.histogram = getRuntimeContext().getMetricGroup().histogram("alanHistogram", new AlanHistogram());
// }
//
// @Override
// public Long map(Long value) throws Exception {
// this.histogram.update(value);
// return value;
// }
// }public static class AlanHistogram implements Histogram {private CircularDoubleArray descriptiveStatistics = new CircularDoubleArray(10);;public AlanHistogram() {}public AlanHistogram(int windowSize) {this.descriptiveStatistics = new CircularDoubleArray(windowSize);}@Overridepublic void update(long value) {this.descriptiveStatistics.addValue(value);}@Overridepublic long getCount() {return this.descriptiveStatistics.getElementsSeen();}@Overridepublic HistogramStatistics getStatistics() {
// return new DescriptiveStatisticsHistogramStatistics(this.descriptiveStatistics);return null;}class CircularDoubleArray implements Serializable {private static final long serialVersionUID = 1L;private final double[] backingArray;private int nextPos = 0;private boolean fullSize = false;private long elementsSeen = 0;CircularDoubleArray(int windowSize) {this.backingArray = new double[windowSize];}synchronized void addValue(double value) {backingArray[nextPos] = value;++elementsSeen;++nextPos;if (nextPos == backingArray.length) {nextPos = 0;fullSize = true;}}synchronized double[] toUnsortedArray() {final int size = getSize();double[] result = new double[size];System.arraycopy(backingArray, 0, result, 0, result.length);return result;}private synchronized int getSize() {return fullSize ? backingArray.length : nextPos;}private synchronized long getElementsSeen() {return elementsSeen;}}}public static void test1() throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);env.setParallelism(1);// sourceDataStream<String> lines = env.socketTextStream("192.168.10.42", 9999);// transformation// RichFlatMapFunction<IN, OUT>// Tuple3<String, String, Integer> 输入的字符串,alan lines[行数],统计单词的总数DataStream<Tuple3<String, String, Integer>> result = lines.flatMap(new RichFlatMapFunction<String, Tuple2<String, String>>() {private long result2LineCounter = 0;private Gauge<String> gauge = null;private Histogram histogram = null;;@Overridepublic void open(Configuration config) {result2LineCounter = getRuntimeContext().getMetricGroup().counter("resultLineCounter:").getCount();gauge = getRuntimeContext().getMetricGroup().gauge("alanGauge", new Gauge<String>() {@Overridepublic String getValue() {return "alan lines[" + result2LineCounter + "]";}});this.histogram = getRuntimeContext().getMetricGroup().histogram("alanHistogram", new AlanHistogram());}@Overridepublic void flatMap(String value, Collector<Tuple2<String, String>> out) throws Exception {result2LineCounter++;this.histogram.update(result2LineCounter * 3);// 此处仅仅示例this.histogram.getCount()的值,没有实际的意义System.out.println("计数器行数:" + result2LineCounter + " histogram:" + this.histogram.getCount());String[] arr = value.split(",");for (String word : arr) {out.collect(Tuple2.of(word, gauge.getValue()));}}}).map(new MapFunction<Tuple2<String, String>, Tuple3<String, String, Integer>>() {@Overridepublic Tuple3<String, String, Integer> map(Tuple2<String, String> value) throws Exception {return Tuple3.of(value.f0, value.f1, 1);}}).keyBy(t -> t.f0).sum(2);// sinkresult.print("result:");env.execute();}public static void main(String[] args) throws Exception {test1();}}///验证数据///
// 输入数据
[alanchan@server2 bin]$ nc -lk 9999
hello,123
alan,flink,good
alan_chan,hi,flink//控制台输出:
计数器行数:1 histogram:1
result:> (hello,alan lines[1],1)
result:> (123,alan lines[1],1)
计数器行数:2 histogram:2
result:> (alan,alan lines[2],1)
result:> (flink,alan lines[2],1)
result:> (good,alan lines[2],1)
计数器行数:3 histogram:3
result:> (alan_chan,alan lines[3],1)
result:> (hi,alan lines[3],1)
result:> (flink,alan lines[2],2)
Flink没有提供直方图的默认实现,但提供了一个允许使用Codahale/DropWizard直方图的包装器。要使用此包装器,
在pom.xml中添加以下依赖项:
<dependency><groupId>org.apache.flink</groupId><artifactId>flink-metrics-dropwizard</artifactId><version>1.17.1</version>
</dependency>
下面的示例是使用 Codahale/DropWizard直方图,如下所示:
import java.io.Serializable;import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.dropwizard.metrics.DropwizardHistogramWrapper;
import org.apache.flink.metrics.Gauge;
//import com.codahale.metrics.Histogram;
import org.apache.flink.metrics.Histogram;
import org.apache.flink.metrics.HistogramStatistics;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;import com.codahale.metrics.SlidingWindowReservoir;/*** @author alanchan**/
public class TestMetricsHistogramDemo {public static void test2() throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);env.setParallelism(1);// sourceDataStream<String> lines = env.socketTextStream("192.168.10.42", 9999);// transformation// RichFlatMapFunction<IN, OUT>// Tuple3<String, String, Integer> 输入的字符串,alan lines[行数],统计单词的总数DataStream<Tuple3<String, String, Integer>> result = lines.flatMap(new RichFlatMapFunction<String, Tuple2<String, String>>() {private long result2LineCounter = 0;private Gauge<String> gauge = null;private Histogram histogram = null;;@Overridepublic void open(Configuration config) {result2LineCounter = getRuntimeContext().getMetricGroup().counter("resultLineCounter:").getCount();gauge = getRuntimeContext().getMetricGroup().gauge("alanGauge", new Gauge<String>() {@Overridepublic String getValue() {return "alan lines[" + result2LineCounter + "]";}});com.codahale.metrics.Histogram dropwizardHistogram = new com.codahale.metrics.Histogram(new SlidingWindowReservoir(500));
// this.histogram = getRuntimeContext().getMetricGroup().histogram("alanHistogram", new AlanHistogram());this.histogram = getRuntimeContext().getMetricGroup().histogram("alanHistogram", new DropwizardHistogramWrapper(dropwizardHistogram));}@Overridepublic void flatMap(String value, Collector<Tuple2<String, String>> out) throws Exception {result2LineCounter++;this.histogram.update(result2LineCounter * 3);// 此处仅仅示例this.histogram.getCount()的值,没有实际的意义System.out.println("计数器行数:" + result2LineCounter + " histogram:" + this.histogram.getCount());String[] arr = value.split(",");for (String word : arr) {out.collect(Tuple2.of(word, gauge.getValue()));}}}).map(new MapFunction<Tuple2<String, String>, Tuple3<String, String, Integer>>() {@Overridepublic Tuple3<String, String, Integer> map(Tuple2<String, String> value) throws Exception {return Tuple3.of(value.f0, value.f1, 1);}}).keyBy(t -> t.f0).sum(2);// sinkresult.print("result:");env.execute();}public static void main(String[] args) throws Exception {test2();}}///验证数据///
// 输入数据
[alanchan@server2 bin]$ nc -lk 9999
hello,123
alan,flink,good
alan_chan,hi,flink//控制台输出://控制台输出:
计数器行数:1 histogram:1
result:> (hello,alan lines[1],1)
result:> (123,alan lines[1],1)
计数器行数:2 histogram:2
result:> (alan,alan lines[2],1)
result:> (flink,alan lines[2],1)
result:> (good,alan lines[2],1)
计数器行数:3 histogram:3
result:> (alan_chan,alan lines[3],1)
result:> (hi,alan lines[3],1)
result:> (flink,alan lines[2],2)
5)、Meter
仪表测量平均吞吐量。可以使用markEvent()方法注册事件的发生。可以使用markEvent(long n)方法注册同时发生多个事件。您可以通过在MetricGroup上调用meter(String name, Meter meter)来注册meter。
下面的示例展示了自定义的Meter实现,可能很不严谨,实际上应用更多的是本部分的第二个示例。
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.dropwizard.metrics.DropwizardHistogramWrapper;
import org.apache.flink.metrics.Counter;
import org.apache.flink.metrics.Gauge;
import org.apache.flink.metrics.Histogram;
import org.apache.flink.metrics.Meter;
import org.apache.flink.metrics.SimpleCounter;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;//import com.codahale.metrics.Meter;
import com.codahale.metrics.SlidingWindowReservoir;/*** @author alanchan**/
public class TestMetricsMeterDemo {public class MyMapper extends RichMapFunction<Long, Long> {private transient Meter meter;@Overridepublic void open(Configuration config) {this.meter = getRuntimeContext().getMetricGroup().meter("myMeter", new AlanMeter());}@Overridepublic Long map(Long value) throws Exception {this.meter.markEvent();return value;}}public static class AlanMeter implements Meter {/** The underlying counter maintaining the count. */private final Counter counter = new SimpleCounter();;/** The time-span over which the average is calculated. */private final int timeSpanInSeconds = 0;/** Circular array containing the history of values. */private final long[] values = null;;/** The index in the array for the current time. */private int time = 0;/** The last rate we computed. */private double currentRate = 0;@Overridepublic void markEvent() {this.counter.inc();}@Overridepublic void markEvent(long n) {this.counter.inc(n);}@Overridepublic long getCount() {return counter.getCount();}@Overridepublic double getRate() {return currentRate;}public void update() {time = (time + 1) % values.length;values[time] = counter.getCount();currentRate = ((double) (values[time] - values[(time + 1) % values.length]) / timeSpanInSeconds);}}public static void test1() throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);env.setParallelism(1);// sourceDataStream<String> lines = env.socketTextStream("192.168.10.42", 9999);// transformation// RichFlatMapFunction<IN, OUT>// Tuple3<String, String, Integer> 输入的字符串,alan lines[行数],统计单词的总数DataStream<Tuple3<String, String, Integer>> result = lines.flatMap(new RichFlatMapFunction<String, Tuple2<String, String>>() {private long result2LineCounter = 0;private Gauge<String> gauge = null;private Histogram histogram = null;private Meter meter;@Overridepublic void open(Configuration config) {result2LineCounter = getRuntimeContext().getMetricGroup().counter("resultLineCounter:").getCount();gauge = getRuntimeContext().getMetricGroup().gauge("alanGauge", new Gauge<String>() {@Overridepublic String getValue() {return "alan lines[" + result2LineCounter + "]";}});com.codahale.metrics.Histogram dropwizardHistogram = new com.codahale.metrics.Histogram(new SlidingWindowReservoir(500));this.histogram = getRuntimeContext().getMetricGroup().histogram("alanHistogram", new DropwizardHistogramWrapper(dropwizardHistogram));this.meter = getRuntimeContext().getMetricGroup().meter("alanMeter", new AlanMeter());}@Overridepublic void flatMap(String value, Collector<Tuple2<String, String>> out) throws Exception {result2LineCounter++;this.histogram.update(result2LineCounter * 3);this.meter.markEvent();// 此处仅仅示例this.histogram.getCount()、this.meter.getRate()的值,没有实际的意义,具体使用以实际使用场景为准System.out.println("计数器行数:" + result2LineCounter + ", histogram:" + this.histogram.getCount() + ", meter.getRate:" + this.meter.getRate());String[] arr = value.split(",");for (String word : arr) {out.collect(Tuple2.of(word, gauge.getValue()));}}}).map(new MapFunction<Tuple2<String, String>, Tuple3<String, String, Integer>>() {@Overridepublic Tuple3<String, String, Integer> map(Tuple2<String, String> value) throws Exception {return Tuple3.of(value.f0, value.f1, 1);}}).keyBy(t -> t.f0).sum(2);// sinkresult.print("result:");env.execute();}public static void main(String[] args) throws Exception {test1();}}///验证数据///
// 输入数据
[alanchan@server2 bin]$ nc -lk 9999
hello,123
alan,flink,good
alan_chan,hi,flink//控制台输出:
计数器行数:1, histogram:1, meter.getRate:0.0
result:> (hello,alan lines[1],1)
result:> (123,alan lines[1],1)
计数器行数:2, histogram:2, meter.getRate:0.0
result:> (alan,alan lines[2],1)
result:> (flink,alan lines[2],1)
result:> (good,alan lines[2],1)
计数器行数:3, histogram:3, meter.getRate:0.0
result:> (alan_chan,alan lines[3],1)
result:> (hi,alan lines[3],1)
result:> (flink,alan lines[2],2)
Flink提供了一个允许使用Codahale/DropWizard仪表的包装器。要使用此包装器,
在pom.xml中添加以下依赖项:
<dependency><groupId>org.apache.flink</groupId><artifactId>flink-metrics-dropwizard</artifactId><version>1.17.1</version>
</dependency>
下面使用Codahale/DropWizard注册的示例,如下所示:
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.dropwizard.metrics.DropwizardHistogramWrapper;
import org.apache.flink.dropwizard.metrics.DropwizardMeterWrapper;
import org.apache.flink.metrics.Counter;
import org.apache.flink.metrics.Gauge;
import org.apache.flink.metrics.Histogram;
import org.apache.flink.metrics.Meter;
import org.apache.flink.metrics.SimpleCounter;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;//import com.codahale.metrics.Meter;
import com.codahale.metrics.SlidingWindowReservoir;/*** @author alanchan**/
public class TestMetricsMeterDemo {public static void test2() throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);env.setParallelism(1);// sourceDataStream<String> lines = env.socketTextStream("192.168.10.42", 9999);// transformation// RichFlatMapFunction<IN, OUT>// Tuple3<String, String, Integer> 输入的字符串,alan lines[行数],统计单词的总数DataStream<Tuple3<String, String, Integer>> result = lines.flatMap(new RichFlatMapFunction<String, Tuple2<String, String>>() {private long result2LineCounter = 0;private Gauge<String> gauge = null;private Histogram histogram = null;private Meter meter;@Overridepublic void open(Configuration config) {result2LineCounter = getRuntimeContext().getMetricGroup().counter("resultLineCounter:").getCount();gauge = getRuntimeContext().getMetricGroup().gauge("alanGauge", new Gauge<String>() {@Overridepublic String getValue() {return "alan lines[" + result2LineCounter + "]";}});com.codahale.metrics.Histogram dropwizardHistogram = new com.codahale.metrics.Histogram(new SlidingWindowReservoir(500));this.histogram = getRuntimeContext().getMetricGroup().histogram("alanHistogram", new DropwizardHistogramWrapper(dropwizardHistogram));// this.meter = getRuntimeContext().getMetricGroup().meter("alanMeter", new AlanMeter());com.codahale.metrics.Meter dropwizardMeter = new com.codahale.metrics.Meter();this.meter = getRuntimeContext().getMetricGroup().meter("alanMeter", new DropwizardMeterWrapper(dropwizardMeter));}@Overridepublic void flatMap(String value, Collector<Tuple2<String, String>> out) throws Exception {result2LineCounter++;this.histogram.update(result2LineCounter * 3);this.meter.markEvent();// 此处仅仅示例this.histogram.getCount()、this.meter.getRate()的值,没有实际的意义,具体使用以实际使用场景为准System.out.println("计数器行数:" + result2LineCounter + ", histogram:" + this.histogram.getCount() + ", meter.getRate:" + this.meter.getRate());String[] arr = value.split(",");for (String word : arr) {out.collect(Tuple2.of(word, gauge.getValue()));}}}).map(new MapFunction<Tuple2<String, String>, Tuple3<String, String, Integer>>() {@Overridepublic Tuple3<String, String, Integer> map(Tuple2<String, String> value) throws Exception {return Tuple3.of(value.f0, value.f1, 1);}}).keyBy(t -> t.f0).sum(2);// sinkresult.print("result:");env.execute();}public static void main(String[] args) throws Exception {test2();}}//控制台输出:
计数器行数:1, histogram:1, meter.getRate:0.0
result:> (hello,alan lines[1],1)
result:> (123,alan lines[1],1)
计数器行数:2, histogram:2, meter.getRate:0.0
result:> (alan,alan lines[2],1)
result:> (flink,alan lines[2],1)
result:> (good,alan lines[2],1)
计数器行数:3, histogram:3, meter.getRate:0.0
result:> (alan_chan,alan lines[3],1)
result:> (hi,alan lines[3],1)
result:> (flink,alan lines[2],2)
以上,本文简单的介绍了Flink 的指标体系的第一部分,即指标类型以及四种类型的代码实现示例。
本专题分为三部分,即:
45、Flink 的指标体系介绍及验证(1)-指标类型及指标实现示例
45、Flink 的指标体系介绍及验证(2)-指标的scope、报告、系统指标以及追踪、api集成示例和dashboard集成
45、Flink 的指标体系介绍及验证(3)- 完整版
相关文章:
45、Flink 的指标体系介绍及验证(1)-指标类型及指标实现示例
Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…...

SAP创建ODATA服务-Structure
SAP创建ODATA服务-Structure 1、创建数据字典 进入se11创建透明表ZRICO_USR,并创建对应字段 2、创建OData service 首先创建Gateway service project,事务码:SEGW,点击Create Project 按钮 Gateway service Project分四个部分:…...

【开源】基于JAVA的车险自助理赔系统
项目编号: S 018 ,文末获取源码。 \color{red}{项目编号:S018,文末获取源码。} 项目编号:S018,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 车…...
单例模式-C++实现
目录 饿汉式懒汉式双检查锁,线程安全的版本什么是reorder?解决内存读写reorder不安全方法代码解释懒汉式的优缺点 单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局的访问点来获取该实例。它常用于需要在整个应…...
一种模板类实现和声明分开在生成的.a文件被使用时出现undefined reference时的一种解决方法
一种模板类实现和声明分开在生成的.a文件被使用时出现undefined reference时的一种解决方法 模板类头文件格式如下: test.h // test.h namespace test { namespace _testspace { class base { public: base(); ~base(); };template<bool T> class base_impl…...

js用到的算法
1.对象数组中,对象中有对象,数组根据对象中的对象打平 [{indexValueMap: { 68443: 0, 68457: 0 },rowName1: 固定收益类,rowName2: 交易类,rowName3: 次级},{indexValueMap: { 68443: 0, 68457: 0 },rowName1: 固定收益类,rowName2: 交易类,rowName3: 中…...
【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷9
1、商标也属于知识产权的一种。一个商标在注册之后,将会在()的时间受到保护 A、20 年内 B、50 年内 C、直至注册人去世 D、10 年内 答案:D 2、人类史上第一位进入太空的宇航员是(),他/她是…...
如何使用抖音直播调试入口扫码进行调试
使用抖音直播调试入口扫码进行调试的步骤如下: 确保你已经安装了抖音调试助手。打开调试助手,并在主界面点击“连接”按钮。在连接向导页面,根据提示连接你的抖音直播间。请确保你已经获取了直播间的token和scheme。连接成功后,你…...

AI智能人机对话小程序系统源码 附带完整的搭建教程
移动互联网的普及和快速发展,小程序已经成为了一种非常流行的应用形态。小程序具有即用即走、轻量级的特点,非常适合用于提供各种便捷服务。下面罗峰来给大家分享一款AI智能人机对话小程序系统源码,带有完整的搭建教程。 以下是部分代码示例…...

【腾讯云云上实验室】用向量数据库在金融信数据库分析中的实战运用
一、前言 这篇文章将带领读者探索数据库的多样化解决方案及其演进历程,特别关注向量数据库的重要性和在实际项目中的应用。 通过深入剖析腾讯云向量数据库及其在金融信用数据库分析中的实战运用,为读者提供全面而实用的指南,帮助他们理解、…...

2015年五一杯数学建模A题不确定性条件下的最优路径问题解题全过程文档及程序
2015年五一杯数学建模 A题 不确定性条件下的最优路径问题 原题再现 目前,交通拥挤和事故正越来越严重的困扰着城市交通。随着我国交通运输事业的迅速发展,交通“拥塞”已经成为很多城市的“痼疾”。在复杂的交通环境下,如何寻找一条可靠、快…...

5、Qt:项目中包含多个子项目(.pro)/子模块(.pri)
一、说明: 在进行项目开发过程中,会涉及子项目/子模块的问题 Qt中使用TEMPLATE subdirs添加多个子项目;子项目可以单独编译生成可执行文件(exe)或者动态链接库(dll)等,供其他模块…...

Facebook的特点优势
Facebook作为全球最大的社交媒体平台之一,同时也是最受欢迎的社交网站之一,Facebook具有许多独特的特点和优势。本文小编将说一些关于Facebook的特点及优势。 1、全球化 Facebook拥有数十亿的全球用户,覆盖了几乎所有国家和地区。这使得人们…...

Spring框架体系及Spring IOC思想
目录 Spring简介Spring体系结构SpringIOC控制反转思想自定义对象容器Spring实现IOCSpring容器类型容器接口容器实现类对象的创建方式使用构造方法使用工厂类的方法使用工厂类的静态方法对象的创建策略对象的销毁时机生命周期方法获取Bean对象的方式通过id/name获取通过类型获取…...
WT588F02B-8S语音芯片:16位DSP技术引领个性化功能产品新时代
随着科技的快速发展,语音芯片作为人机交互的核心组件,在各个领域的应用越来越广泛。唯创知音推出的WT588F02B-8S语音芯片,以其强大的16位DSP技术和丰富的内置资源,正成为行业内的翘楚。 首先,唯创知音WT588F02B-8S是一…...

数字逻辑电路基础-时序逻辑电路之移位寄存器
文章目录 一、移位寄存器定义二、verilog源码三、仿真结果 一、移位寄存器定义 移位寄存器定义 A shift register is a type of digital circuit using a cascade of flip flops where the output of one flip-flop is connected to the input of the next. 移位寄存器是一种将…...

DEM分析
一、实验名称: DEM分析 二、实验目的: 通过本实验练习,掌握DEM的建立与应用基本方法。 三、实验内容和要求: 实验内容: 利用ARCGIS软件相关分析工具及实验数据,创建DEM,并计算相应坡度的区…...

全面探讨HTTP协议从0.9到3.0版本的发展和特点
前言: 最近的几场面试都问到了http的相关知识点,博主在此结合书籍和网上资料做下总结。本篇文章讲收录到秋招专题,该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出,对大佬有帮…...

中通快递查询入口,根据物流更新量筛选出需要的单号记录
批量中通快递单号的物流信息,根据物流更新量将需要的单号记录筛选出来。 所需工具: 一个【快递批量查询高手】软件 中通快递单号若干 操作步骤: 步骤1:运行【快递批量查询高手】软件,并登录 步骤2:点击主…...

Arraylist案例
Arraylist是使用最频繁的一个集合,它与数组类似,不同之处在于它可以动态改变长度,不够了可以扩容。 案例: 我的思考: 首先多个菜品信息可以用Arraylist 来存储,那我们需要再创建一个菜品类Food࿰…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...