当前位置: 首页 > news >正文

Flink--7、窗口(窗口的概念、分类、API、分配器、窗口函数)、触发器、移除器

在这里插入图片描述
                       星光下的赶路人star的个人主页

                      内心的平静始于不再让他人掌控你的感情

文章目录

  • 0、前言
  • 1、窗口(Window)
    • 1.1 窗口的概念
    • 1.2 窗口的分类
    • 1.3 窗口API概览
    • 1.4 窗口分配器(Window Assigner)
      • 1.4.1 时间窗口
      • 1.4.2 计数窗口
    • 1.5 窗口函数
      • 1.5.1 增量聚合函数(ReduceFunction/AggregateFunction)
      • 1.5.2 全窗口函数(Full Window Functions)
      • 1.5.3 增量聚合和全窗口函数的结合使用
    • 1.6 其它API
      • 1.6.1 触发器(Trigger)
      • 1.6.2 移除器(Evictor)

0、前言

在批处理统计中,我们可以等待一批数据都到齐后,统一处理。但是在实时处理统计中,我们是来一条就得处理一条,那么我们怎么统计最近一段时间内的数据呢?引入“窗口”。
所谓的“窗口”,一般就是划定的一段时间范围,也就是“时间窗”;对在这范围内的数据进行处理,就是所谓的窗口计算。所以窗口和时间往往是分不开的。接下来我们就深入了解一下Flink中的时间语义和窗口的应用。

1、窗口(Window)

1.1 窗口的概念

Flink是一种流式计算引擎,主要是来处理无界数据流的,数据源源不断、无穷无尽。想要更加方便高效地处理无界流,一种方式就是将无限数据切割成有限的“数据块”进行处理,这就是所谓的“窗口”(Window)。
在这里插入图片描述
注意:Flink中窗口并不是静态准备好的,而是动态创建——当有落在这个窗口区间范围的数据达到时,才创建对应的窗口。另外,这里我们认为到达窗口结束时间时,窗口就触发计算并关闭,事实上“触发计算”和“窗口关闭”两个行为也可以分开.

1.2 窗口的分类

上面的其实是最简单的例子,是最简单的一种时间窗口。在Flink中,窗口的应用非常灵活,我们可以使用各种不同类型的窗口来实现需求。接下来我们就从不同的角度,对Flink中内置的窗口做一个分类说明。

1、按照驱动类型分类
在这里插入图片描述

2、按照窗口分配数据的规则分类
根据分配数据的规则,窗口的具体实现可以分为四类:滚动窗口(Tumbling Window)、滑动窗口(Sliding Window)、会话窗口(Session Window)以及全局窗口(Global Window)。

(1)滚动窗口(Tumbling Window)
滚动窗口有固定的大小,是一种对数据进行“均匀切片”的划分方式。窗口之间没有重叠,也不会有间隔,是“首尾相接”的状态。这是最简单的窗口形式,每一个数据都会被分配到一个窗口,而且只会属于一个窗口。
在这里插入图片描述
(2)滑动窗口(Sliding Window)

在这里插入图片描述
(3)会话窗口
在这里插入图片描述
(4)全局窗口
就是把所有数据当做在同一个窗口。这种窗口没有结束的时候,默认是不会做触发计算的。如果希望它能对数据进行计算处理,还需要自定义触发器。
在这里插入图片描述

1.3 窗口API概览

1、按键分区(Keyed)和非按键分区(Non-Keyed)
在定义窗口操作之前,首先要确定,到底是基于按键分区(Keyed)的数据流KeyedStream来开窗,还是直接在没有按键分区的DataStream上开窗。也就是说,在调用窗口算子之前,是否有keyBy操作
(1)按键分区窗口(Keyed Window)
经过按键分区KeyBy操作后,数据流会按照key被分为多条逻辑流(Logical Streams),这就是KeyedStream。基于KeyedStream进行窗口操作时,窗口计算会在多个并行子任务上同时执行。相同key的数据会被发送到一个并行子任务,而窗口操作会基于每个key进行单独的处理。所以可以认为,每个key上都定义了一组窗口,各自独立地进行统计计算。
在代码实现上,我们需要先对DataStream调用.keyBy()进行按键分区,然后再调用.window()定义窗口。

stream.keyBy(...).window(...)

(2)非按键分区
如果没有进行keyBy,那么原始的DataStream就不会分成多条逻辑流。这时窗口逻辑只能在一个任务(task)上执行,就相当于并行度变成了1。
在代码中,直接基于DataStream调用.windowAll()定义窗口。

stream.windowAll(...)

注意:对于非按键分区的窗口操作,手动调大窗口算子的并行度也是无效的,windowAll本身就是一个非并行的操作。
2、代码中窗口API的调用
窗口操作主要有两个部分:窗口分配器(Window Assigners)和窗口函数(Window Functions)。

stream.keyBy(<key selector>).window(<window assigner>).aggregate(<window function>)

其中.window()方法需要传入一个窗口分配器,它指明了窗口的类型;而后面的.aggregate()方法传入一个窗口函数作为参数,它用来定义窗口具体的处理逻辑。窗口分配器有各种形式,而窗口函数的调用方法也不止.aggregate()一种。

1.4 窗口分配器(Window Assigner)

定义窗口分配器(Window Assaginer)是构建窗口算子的第一步,它的作用就是定义数据应该被分配到哪个窗口。所以可以说,窗口分配器其实就是在指定窗口的类型。
窗口分配器最通用的定义方式,就是调用.window()方法。这个方法需要传入一个WindowAssiger作为参数,返回WindowedStream。如果是非按键分区窗口,那么直接调用.windowAll()方法,同样传入一个WindowAssigner,返回的是AllWindowedStream。
窗口按照驱动类型可以分成时间窗口和计数窗口,而按照具体的分配规则,又有滚动窗口、滑动窗口、会话窗口、全局窗口四种。除去需要自定义的全局窗口外,其他常用的类型Flink中都给出了内置的分配器的实现。

1.4.1 时间窗口

时间窗口是最常用的窗口类型,又可以细分为滚动、滑动和会话三种。
(1)滚动处理时间窗口
窗口分配器由类TumblingProcessingTimeWindows提供,需要调用它的静态方法.of()。

stream.keyBy(...).window(TumblingProcessingTimeWindows.of(Time.seconds(5))).aggregate(...)

这里.of()方法需要传入一个Time类型的参数size,表示滚动窗口的大小,我们这里创建了一个长度为5秒的滚动窗口。
另外,.of()还有一个重载方法,可以传入两个Time类型的参数:size和offset。第一个参数当然还是窗口大小,第二个参数则表示窗口起始点的偏移量。
(2)滑动处理时间窗口
窗口分配器由类SlidingProcessingTimeWindows提供,同样需要调用它的静态方法.of()。

stream.keyBy(...).window(SlidingProcessingTimeWindows.of(Time.seconds(10)Time.seconds(5))).aggregate(...)

这里.of()方法需要传入两个Time类型的参数:size和slide,前者表示滑动窗口的大小,后者表示滑动窗口的滑动步长。我们这里创建了一个长度为10秒、滑动步长为5秒的滑动窗口。
滑动窗口同样可以追加第三个参数,用于指定窗口起始点的偏移量,用法与滚动窗口完全一致。
(3)处理时间会话窗口
窗口分配器由类ProcessingTimeSessionWindows提供,需要调用它的静态方法.withGap()或者.withDynamicGap()。

stream.keyBy(...).window(ProcessingTimeSessionWindows.withGap(Time.seconds(10))).aggregate(...)

这里.withGap()方法需要传入一个Time类型的参数size,表示会话的超时时间,也就是最小间隔session gap。我们这里创建了静态会话超时时间为10秒的会话窗口。
另外,还可以调用withDynamicGap()方法定义session gap的动态提取逻辑。
(4)滚动事件时间窗口
窗口分配器由类TumblingEventTimeWindows提供,用法与滚动处理事件窗口完全一致。

stream.keyBy(...).window(TumblingEventTimeWindows.of(Time.seconds(5))).aggregate(...)

(5)滑动事件窗口
窗口分配器由类SlidingEventTimeWindows提供,用法与滑动处理事件窗口完全一致。

stream.keyBy(...).window(SlidingEventTimeWindows.of(Time.seconds(10)Time.seconds(5))).aggregate(...)

(6)事件时间会话窗口
窗口分配器由类EventTimeSessionWindows提供,用法与处理事件会话窗口完全一致。

stream.keyBy(...).window(EventTimeSessionWindows.withGap(Time.seconds(10))).aggregate(...)

1.4.2 计数窗口

计数窗口概念非常简单,本身底层是基于全局窗口(Global Window)实现的。Flink为我们提供了非常方便的接口:直接调用.countWindow()方法。根据分配规则的不同,又可以分为滚动计数窗口和滑动计数窗口两类,下面我们就来看它们的具体实现。
(1)滚动计数窗口
滚动计数窗口只需要传入一个长整型的参数size,表示窗口的大小。

stream.keyBy(...).countWindow(10)

我们定义了一个长度为10的滚动计数窗口,当窗口中元素数量达到10的时候,就会触发计算执行并关闭窗口。
(2)滚动计数窗口
与滚动计数窗口类似,不过需要在.countWindow()调用时传入两个参数:size和slide,前者表示窗口大小,后者表示滑动步长。

stream.keyBy(...).countWindow(103)

我们定义了一个长度为10、滑动步长为3的滑动计数窗口。每个窗口统计10个数据,每隔3个数据就统计输出一次结果。
(3)全局窗口
全局窗口是计数窗口的底层实现,一般在需要自定义窗口时使用。它的定义同样是直接调用.window(),分配器由GlobalWindows类提供。

stream.keyBy(...).window(GlobalWindows.create());

需要注意使用全局窗口,必须自行定义触发器才能实现窗口计算,否则起不到任何作用。

1.5 窗口函数

定义了窗口分配器,我们只是知道了数据属于哪个窗口,可以将数据收集起来了;至于收集起来到底是要做什么,还得看窗口函数。所以在窗口分配器之后,必须再接上一个定义窗口如何计算的操作,这就是所谓的“窗口函数”(Window Functions)。
在这里插入图片描述
窗口函数定义了要对窗口中收集的数据做的计算操作,根据处理的方式可以分为两类:增量聚合函数和全窗口函数。

1.5.1 增量聚合函数(ReduceFunction/AggregateFunction)

窗口将数据收集起来,最基本的处理操作当然就是进行聚合。我们可以每来一个数据就在之前结果上聚合一次,这就是“增量聚合”。
典型的增量聚合函数有两个:ReduceFunction和AggregateFunction。
1、归约聚合(ReduceFunction)
代码示例(求水位累加值):

/*** 特点:*      两两聚合*      输入和输出的类型一样*/
public class Demo06_Reduce {public static void main(String[] args) throws Exception {//创建Flink配置类(空参创建的话都是默认值)Configuration configuration = new Configuration();//修改配置类中的WebUI端口号configuration.setInteger("rest.port",3333);//创建Flink环境(并且传入配置对象)StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(configuration);env.socketTextStream("hadoop102",9999)//映射.map(new WaterSensorFunction())//全局计数滑动窗口.countWindowAll(3)//累加.reduce((waterSensor, t1) -> {t1.setVc(waterSensor.getVc()+t1.getVc());return t1;}).print();env.execute();}}

测试截图:
在这里插入图片描述

2、聚合函数(AggregateFunction)
ReduceFunction可以解决大多数归约聚合的问题,但是这个接口有一个限制,就是聚合状态的类型、数据结果的类型必须和输入数据类型一样。
Flink Window API中的aggragate就突破了这个限制,可以定义更加灵活的窗口聚合操作。这个方法需要传入一个AggregateFunction的实现类作为参数。
AggregateFunction可以看作是ReduceFunction通用版本,这里有三种类型:输入类型(IN)、累加器类型(ACC)和输出类型(OUT)。输入类型IN就是输入流中元素的数据类型;累加器类型ACC则是我们进行聚合的中间状态类型;而数据类型当然就是最终计算结果的类型了。
接口中有四个方法:

  • createAccumulator():创建一个累加器,这就是为聚合创建一个初始状态,每个聚合任务只会调用一次。
  • add():将输入的元素添加到累加器中。
  • getResult():从累加器中提取聚合的输出结果
  • merge():合并两个累加器,并将合并后的状态作为一个累加器返回

所以可以看到,AggregateFunction的工作原理是:先调用createAccumulator()方法为任务初始化一个状态(累加器);而后每来一个数据就调用一次add()方法,对数据进行聚合,得到的结果保存在状态中;等到了窗口需要输出时,再调用getResult方法得到计算结果。很明显,与ReduceFunction相同,AggregateFunction也是增量式的聚合;而由于输入、中间状态、输出的类型可以不同,使得应用更加灵活方便。
代码实现:

/*** 输入和输出的类型不一样,sum、max、min、minBy、maxBy、reduce就不行了**可以考虑用aggregate*/
public class Demo07_Aggregate {public static void main(String[] args) throws Exception {//创建Flink配置类(空参创建的话都是默认值)Configuration configuration = new Configuration();//修改配置类中的WebUI端口号configuration.setInteger("rest.port",3333);//创建Flink环境(并且传入配置对象)StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(configuration);env.socketTextStream("hadoop102",9999).map(new WaterSensorFunction())//全局计数滚动窗口.countWindowAll(3)/*** 统计窗口中所有数据的vc之和* AggregateFunction<IN,ACC,OUT>*     IN:输入,窗口中元素的类型*     ACC:累加器。聚合中使用的中间的缓存类型*     OUT:输出的类型**    以上三种类型都可以不一致*///输出: vc之和:xxx.aggregate(new AggregateFunction<WaterSensor, Integer, String>() {//创建一个累加器对象 (在一个窗口创建时执行一次)@Overridepublic Integer createAccumulator() {System.out.println("我是一个累加器");return 0;}//把输入的每个元素累加到累加器上@Overridepublic Integer add(WaterSensor waterSensor, Integer integer) {return integer+waterSensor.getVc();}//输出最终结果(在窗口关闭执行时执行一次)@Overridepublic String getResult(Integer integer) {System.out.println("我是输出最终结果");return "vc之和:"+integer;}//不用写。在DataSetAPI(批处理)中才要实现@Overridepublic Integer merge(Integer integer, Integer acc1) {return null;}}).print();env.execute();}
}

测试截图:
在这里插入图片描述
另外,Flink也为窗口的聚合提供了一系列预定义的简单聚合方法,可以直接基于WindowedStream调用。主要包括.sum()/max()/maxBy()/min()/minBy(),与KeyedStream的简单聚合非常相似。它们的底层,其实都是通过AggregateFunction来实现的。

1.5.2 全窗口函数(Full Window Functions)

有些场景下,我们要做的计算必须基于全部的数据才有效,这时做增量聚合就没什么意义了;另外,输出的结果可能要包含上下文中的一些信息(比如窗口的起始时间),这是增量聚合函数做不到的。
所以我们还需要有更丰富的窗口计算方式。窗口操作中的另一大类就是全窗口函数。与增量函数不同,全局窗口函数首先需要收集窗口中的数据,并在内部缓存起来,等到窗口要输出结果的时候再取出数据进行计算。
在Flink中,全局窗口函数也是有两种:WindowFunction和ProcessWindowFunction。
1、窗口函数(WindowFunction)
WindowFunction字面上就是“窗口函数”,它其实是老版本的通用窗口函数接口。我们可以基于WindowedStream调用.apply()方法,传入一个WindowFunction的实现类。

stream.keyBy(<key selector>).window(<window assigner>).apply(new MyWindowFunction());

这个类中可以获取到包含窗口所有数据的可迭代集合(Iterable),还可以拿到窗口(Window)本身的信息。
不过WindowFunction能提供的上下文信息比较少,也没有更高级的功能。事实上,它的作用可以被ProcessWindowFunction全覆盖,所以之后可能会逐渐弃用。
2、处理窗口函数(ProcessWindowFunction)
ProcessWindowFunction是Window API中最底层的通用窗口函数接口。之所以说它“最底层”,是因为除了可以拿到窗口中的所有数据之外,ProcessWindowFunction还可以获取到一个“上下文对象”(Context)。这个上下文对象非常强大,不仅能够获取窗口信息,还可以访问当前的时间和状态信息。这里的时间就包括了处理时间(processing time)和事件时间水位线(event time watermark)。这就使得ProcessWindowFunction更加灵活、功能更加丰富,其实就是一个增强版的WindowFunction。

public class WindowProcessDemo {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);SingleOutputStreamOperator<WaterSensor> sensorDS = env.socketTextStream("hadoop102", 9999).map(new WaterSensorFunction());KeyedStream<WaterSensor, String> sensorKS = sensorDS.keyBy(sensor -> sensor.getId());// 1. 窗口分配器WindowedStream<WaterSensor, String, TimeWindow> sensorWS = sensorKS.window(TumblingProcessingTimeWindows.of(Time.seconds(10)));SingleOutputStreamOperator<String> process = sensorWS.process(new ProcessWindowFunction<WaterSensor, String, String, TimeWindow>() {@Overridepublic void process(String s, Context context, Iterable<WaterSensor> elements, Collector<String> out) throws Exception {long count = elements.spliterator().estimateSize();long windowStartTs = context.window().getStart();long windowEndTs = context.window().getEnd();String windowStart = DateFormatUtils.format(windowStartTs, "yyyy-MM-dd HH:mm:ss.SSS");String windowEnd = DateFormatUtils.format(windowEndTs, "yyyy-MM-dd HH:mm:ss.SSS");out.collect("key=" + s + "的窗口[" + windowStart + "," + windowEnd + ")包含" + count + "条数据===>" + elements.toString());}});process.print();env.execute();}
}

测试截图:
在这里插入图片描述

1.5.3 增量聚合和全窗口函数的结合使用

在实际应用中,我们往往希望兼具这两者的优点,把它们结合在一起使用。Flink的Window API就给我们实现了这样的用法。
我们之前在调用WindowedStream的.reduce()和.aggregate()方法时,只是简单地直接传入了一个ReduceFunction或AggregateFunction进行增量聚合。除此之外,其实还可以传入第二个参数:一个全窗口函数,可以是WindowFunction或者ProcessWindowFunction。

// ReduceFunction与WindowFunction结合
public <R> SingleOutputStreamOperator<R> reduce(ReduceFunction<T> reduceFunction,WindowFunction<TRKW> function) // ReduceFunction与ProcessWindowFunction结合
public <R> SingleOutputStreamOperator<R> reduce(ReduceFunction<T> reduceFunction,ProcessWindowFunction<TRKW> function)// AggregateFunction与WindowFunction结合
public <ACCVR> SingleOutputStreamOperator<R> aggregate(AggregateFunction<TACCV> aggFunction,WindowFunction<VRKW> windowFunction)// AggregateFunction与ProcessWindowFunction结合
public <ACCVR> SingleOutputStreamOperator<R> aggregate(AggregateFunction<TACCV> aggFunction,ProcessWindowFunction<VRKW> windowFunction)

这样调用的处理机制是:基于第一个参数(增量聚合函数)来处理窗口数据,每来一个数据就做一次聚合;等到窗口需要触发计算的时候,就调用第二个参数(全局窗口函数)的处理逻辑输出结果。需要注意的是,这里的全窗口函数就不再缓存所有数据了,而是直接将增量聚合函数的结果拿来当做了Iterable类型的输入。

public class WindowAggregateAndProcessDemo {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);SingleOutputStreamOperator<WaterSensor> sensorDS = env.socketTextStream("hadoop102", 9999).map(new WaterSensorFunction());KeyedStream<WaterSensor, String> sensorKS = sensorDS.keyBy(sensor -> sensor.getId());// 1. 窗口分配器WindowedStream<WaterSensor, String, TimeWindow> sensorWS = sensorKS.window(TumblingProcessingTimeWindows.of(Time.seconds(10)));// 2. 窗口函数:/*** 增量聚合 Aggregate + 全窗口 process* 1、增量聚合函数处理数据: 来一条计算一条* 2、窗口触发时, 增量聚合的结果(只有一条) 传递给 全窗口函数* 3、经过全窗口函数的处理包装后,输出** 结合两者的优点:* 1、增量聚合: 来一条计算一条,存储中间的计算结果,占用的空间少* 2、全窗口函数: 可以通过 上下文 实现灵活的功能*///        sensorWS.reduce()   //也可以传两个SingleOutputStreamOperator<String> result = sensorWS.aggregate(new MyAgg(),new MyProcess());result.print();env.execute();}public static class MyAgg implements AggregateFunction<WaterSensor, Integer, String> {@Overridepublic Integer createAccumulator() {System.out.println("创建累加器");return 0;}@Overridepublic Integer add(WaterSensor value, Integer accumulator) {System.out.println("调用add方法,value="+value);return accumulator + value.getVc();}@Overridepublic String getResult(Integer accumulator) {System.out.println("调用getResult方法");return accumulator.toString();}@Overridepublic Integer merge(Integer a, Integer b) {System.out.println("调用merge方法");return null;}}// 全窗口函数的输入类型 = 增量聚合函数的输出类型public static class MyProcess extends ProcessWindowFunction<String,String,String,TimeWindow> {@Overridepublic void process(String s, Context context, Iterable<String> elements, Collector<String> out) throws Exception {long startTs = context.window().getStart();long endTs = context.window().getEnd();String windowStart = DateFormatUtils.format(startTs, "yyyy-MM-dd HH:mm:ss.SSS");String windowEnd = DateFormatUtils.format(endTs, "yyyy-MM-dd HH:mm:ss.SSS");long count = elements.spliterator().estimateSize();out.collect("key=" + s + "的窗口[" + windowStart + "," + windowEnd + ")包含" + count + "条数据===>" + elements.toString());}}
}

测试截图:
在这里插入图片描述

1.6 其它API

对于一个窗口算子而言,窗口分配器和窗口函数是必不可少的。除此之外,Flink还提供了其他一些可选的API,让我们可以更加灵活地控制窗口行为。
触发器和移除器在日常使用中很少会用到,这里仅仅简单介绍其语法格式。

1.6.1 触发器(Trigger)

触发器主要是用来控制窗口什么时候触发计算。所谓的“触发计算”,本质上就是执行窗口函数,所以可以认为是计算得到的结果并输出的过程。
基于WindowedStream调用.triggrt()方法,就可以传入一个自定义的窗口触发器(Trigger·)。

stream.keyBy(...).window(...).trigger(new MyTrigger())

1.6.2 移除器(Evictor)

移除器主要用来定义移除某些数据的逻辑。基于WindowedStream调用.evictor()方法,就可以传人一个自定义的移除器(Evictor)。Evictor是一个接口,不同的窗口类型都有各自预实现的移除器。

stream.keyBy(...).window(...).evictor(new MyEvictor())

在这里插入图片描述
                      您的支持是我创作的无限动力

在这里插入图片描述
                      希望我能为您的未来尽绵薄之力

在这里插入图片描述
                      如有错误,谢谢指正;若有收获,谢谢赞美

相关文章:

Flink--7、窗口(窗口的概念、分类、API、分配器、窗口函数)、触发器、移除器

星光下的赶路人star的个人主页 内心的平静始于不再让他人掌控你的感情 文章目录 0、前言1、窗口&#xff08;Window&#xff09;1.1 窗口的概念1.2 窗口的分类1.3 窗口API概览1.4 窗口分配器&#xff08;Window Assigner&#xff09;1.4.1 时间窗口1.4.2 计数窗口 1.5 窗口函数…...

vscode 注释插件koroFileHeader

https://blog.51cto.com/u_15785499/5664323 https://blog.csdn.net/weixin_67697081/article/details/129004675...

Centos7安装php-fpm

目录 第一步&#xff1a;查看系统IP地址和网卡名称 第二步&#xff1a;更改网络配置模式 第三步、重启network 查看iptablies ,将第十行&#xff0c;十一行删除 第四步&#xff1a;关闭config 第五步&#xff1a;创建nginx 文件夹 查看目录下的文件 进入nginx文件夹 第…...

计算机网络(五):运输层

参考引用 计算机网络微课堂-湖科大教书匠计算机网络&#xff08;第7版&#xff09;-谢希仁 1. 运输层概述 之前所介绍的计算机网络体系结构中的物理层、数据链路层以及网络层它们共同解决了将主机通过异构网络互联起来所面临的问题&#xff0c;实现了主机到主机的通信&#xff…...

适合在校学生的云服务器有哪些?

随着云计算技术的发展&#xff0c;越来越多的学生开始使用云服务器来进行学习和实践。对于学生来说&#xff0c;选择一款便宜的云服务器不仅可以帮助他们降低成本&#xff0c;还可以提高学习和实践的效率。本文将介绍几款适合学生使用的便宜云服务器。 1、腾讯云学生服务器【点…...

计算机竞赛 深度学习驾驶行为状态检测系统(疲劳 抽烟 喝水 玩手机) - opencv python

文章目录 1 前言1 课题背景2 相关技术2.1 Dlib人脸识别库2.2 疲劳检测算法2.3 YOLOV5算法 3 效果展示3.1 眨眼3.2 打哈欠3.3 使用手机检测3.4 抽烟检测3.5 喝水检测 4 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的驾…...

想要精通算法和SQL的成长之路 - 验证二叉搜索树和不同的二叉搜索树

想要精通算法和SQL的成长之路 - 验证二叉搜索树和不同的二叉搜索树 前言一. 验证二叉搜索树二. 不同的二叉搜索树三. 不同的二叉搜索树II 前言 想要精通算法和SQL的成长之路 - 系列导航 二叉搜索树的定义&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包…...

SpringCloudAlibaba 相关组件的学习一

目录 前言 系统架构演变 1、单体架构 2、垂直架构 3、分布式架构 4、SOA架构 5、微服务架构 一、微服务架构的介绍 1、微服务架构的常见问题 2 微服务架构的常见概念 2.1 服务治理 2.2 服务调用 2.3 服务网关 2.4 服务容错 2.5 链路追踪 3、微服务架构的常用解决…...

【C语言 模拟实现strncpy函数、strncat函数、strncmp函数、strstr函数】

C语言程序设计笔记---026 C语言之模拟实现strncpy函数、strncat函数、strncmp函数、strstr函数1、介绍strncpy函数1.1、模拟实现strncpy函数 2、介绍strncat函数2.1、模拟实现strncat函数 3、介绍strncmp函数3.1、模拟实现strncmp函数 4、介绍strstr函数4.1、模拟实现strstr函数…...

Mongodb7启动报错排除解决方案

一&#xff1a; 报错信息: [rootwww log]# journalctl -xe -- Unit mongodb.service has begun starting up. /usr/local/mongodb/mongdb7/bin/mongod --help for more information 10月 03 13:47:39 www.yhchange.com systemd[1]: mongodb.service: control process exited, …...

王杰国庆作业day5

...

QT、C++实现地图导航系统(mapSystem)

文章目录 地图导航系统项目应用背景技术栈选择数据处理算法实现界面实现源码展示成果展示源码下载 &#xff08;免费&#xff09; 地图导航系统 项目应用背景 电子地图导航系统的主要目的是为用户提供精确、实时的导航和位置信息&#xff0c;以帮助他们在城市或地区内轻松找到…...

STM32 定时器介绍--通用、高级定时器

目录 高级定时器 1.功能框图 1-时钟源 2-时基单元 3-输入捕获 4-输出比较 2.输入捕获的应用 3.输出比较的应用 4.初始化结构体 1-时基初始化结构体 2-输出比较结构体 3-PWM信号 周期和占空比的计算--以通用定时器为例 4-输入捕获结构体 5-断路和死区初始化结构体…...

淘宝天猫渠道会员购是什么意思?如何开通天猫淘宝渠道会员购有什么用?

淘宝天猫渠道会员购是什么意思&#xff1f; 淘宝天猫渠道会员购与淘宝天猫粉丝福利购意思基本相同&#xff0c;都可以领取淘宝天猫大额内部隐藏优惠券、通过草柴APP开通绑定渠道会员还可以获得购物返利。 草柴APP如何绑定开通淘宝天猫渠道会员&#xff1f; 1、手机下载安装「…...

(Note)机器学习面试题

机器学习 1.两位同事从上海出发前往深圳出差&#xff0c;他们在不同时间出发&#xff0c;搭乘的交通工具也不同&#xff0c;能准确描述两者“上海到深圳”距离差别的是&#xff1a; A.欧式距离 B.余弦距离 C.曼哈顿距离 D.切比雪夫距离 S:D 1. 欧几里得距离 计算公式&#x…...

思科:iOS和iOSXe软件存在漏洞

思科警告说,有人试图利用iOS软件和iOSXe软件中的一个安全缺陷,这些缺陷可能会让一个经过认证的远程攻击者在受影响的系统上实现远程代码执行。 中严重程度的脆弱性被追踪为 CVE-2023-20109 ,并以6.6分得分。它会影响启用Gdoi或G-Ikev2协议的软件的所有版本。 国际知名白帽黑客…...

CCF CSP认证 历年题目自练Day19

题目一 试题编号&#xff1a; 201812-1 试题名称&#xff1a; 小明上学 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题描述&#xff1a; 题目背景   小明是汉东省政法大学附属中学的一名学生&#xff0c;他每天都要骑自行车往返于家和学校。为了能尽可能充…...

Java 开发环境配置

在本章节中我们将为大家介绍如何搭建Java开发环境。 目录 window系统安装java 下载JDK 配置环境变量 JAVA_HOME 设置 PATH设置 CLASSPATH 设置 测试JDK是否安装成功 Linux&#xff0c;UNIX&#xff0c;Solaris&#xff0c;FreeBSD环境变量设置 流行 Java 开发工具 使…...

[2023.09.26]: JsValue的转换体验与as关键字的浅析

昨天解决了焦点问题&#xff0c;今天就开始搬砖了。本以为可以一帆风顺&#xff0c;但是还是遇到了几个问题&#xff0c;不过还好&#xff0c;都被一一解决&#xff0c;这里我分享一下JsValue的转换体验以及关键字as的使用浅析。 场景描述 我是在什么情况下遇到JsValue的转换…...

SpringBoot Validation入参校验国际化

在 Spring Boot 中&#xff0c;可以使用 Validation 和国际化来实现对入参的校验。 常用的校验 NotNull验证字段值不能为 nullNotEmpty验证字段值不能为 null 或空字符串NotBlank验证字符串字段值不能为空、null&#xff0c;并且必须至少包含一个非空白字符Size验证字符串、…...

树莓集团涉足直播产业园区运营,成都直播产业园区再添黑马

树莓集团涉足成都直播产业园运营领域&#xff0c;这一消息引起了业界的广泛关注。在这个无限可能的直播领域中&#xff0c;树莓集团将与上市公司德商产投紧密合作&#xff0c;立志为成都直播行业的发展注入新的活力。成都天府蜂巢直播产业园推行着一系列创新的政策措施&#xf…...

中小学教师ChatGPT的23种用法

原文&#xff1a;中小学教师ChatGPT的23种用法 近日&#xff0c;ChatGPT引发舆论风暴&#xff0c;火遍全球。作为一款生成式人工智能软件&#xff0c;ChatGPT可以就任何议题生成文本&#xff0c;完成包括回答问题&#xff0c;撰写文章、论文、诗歌在内的多种工作。各界盛赞其“…...

Ubuntu性能分析-ftrace 底层驱动

1、框架介绍 ftrace内核驱动可以分为几部分:ftrace framework,RingBuffer,debugfs,Tracepoint,各种Tracer。 ftrace框架是整个ftrace功能的纽带,包括对内和的修改,Tracer的注册,RingBuffer的控制等等。 RingBuffer是静态动态ftrace的载体。 debugfs则提供了用户空间…...

网盘搜索引擎:点亮知识星空,畅享数字宝藏!

大家好&#xff01;作为一名资深的网络产品运营人员&#xff0c;我今天要向大家介绍一款让你受益匪浅的神奇工具——网盘搜索引擎&#xff01;它可以帮助你免费搜索查询各种云盘共享资源&#xff0c;包括影视作品、纪录片、小说、动漫等等。现在&#xff0c;我们急需网络流量&a…...

Mysql以key-val存储、正常存储的区别

场景 你作为一个服务端工程师&#xff0c;假设产品要求设计这么一个页面&#xff0c;页面上包含很多模块&#xff0c;每个模块都可以单独进行变更&#xff0c;有些模块是富文本。 实现方式有很多&#xff0c;我们来聊比较常用的两种&#xff0c;看看mysql的表如何设计。 第一…...

MySQL 索引优化实践(单表)

目录 一、前言二、表数据准备三、常见业务无索引查询耗时测试3.1、通过订单ID / 订单编号 查询指定订单3.2、查询订单列表 四、订单常见业务索引优化实践4.1、通过唯一索引和普通索引优化通过订单编号查询订单信息4.2、通过普通联合索引优化订单列表查询4.2.1、分析查询字段的查…...

react create-react-app v5配置 px2rem (暴露 eject方式)

环境信息&#xff1a; create-react-app v5 “react”: “^18.2.0” “postcss-plugin-px2rem”: “^0.8.1” 配置步骤&#xff1a; 我这个方式是 npm run eject 暴露 webpack配置的方法 1.安装 postcss-plugin-px2rem 和 lib-flexible cnpm install postcss-plugin-px2rem…...

AVL树的实现及原理

目录 AVL树的由来 AVL的实现原理 左单旋 右单旋 先左后右 先右后左 总结 AVL树的由来 查找&#xff0c;无论在什么情况下都与我们息息相关。在我们学习数组阶段学习到了线性查找&#xff0c;可是它的效率很低下&#xff0c;又演变出来了二分查找&#xff0c;它的效率非常…...

NestJs和Vite使用monorepo管理项目中,需要使用共享的文件夹步骤

NestJs和Vite使用monorepo管理项目中,需要使用共享的文件夹步骤 1 首先需要将nest-cli打包的功能通过webpack接管 nest-cli.json文件内容 {"$schema": "https://json.schemastore.org/nest-cli","collection": "nestjs/schematics",…...

我用PYQT5做的第一个实用的上位机项目(三)

基本的程序框架&#xff1a; 因为自己不是专业的程序员&#xff0c;只是一个搞电气控制的“票友”&#xff0c;所以尽量减少手动输入 代码量&#xff0c;能在Qt Dsigner里面完成的组态就不要放在代码里面完成。 在框架的建设方面&#xff0c;尽量做到集中和整合&#xff0c;位…...

网站编辑怎么做内容分类/谷歌浏览器官网下载安装

我们在日常开发中少不了和JSON数据打交道&#xff0c;那么我们来看看JAVA中常用的JSON解析方式。1、JSON官方2、GSON3、FastJSON4、jacksonJSON操作涉及到的类&#xff1a;public class Student {private int id;private String name;private int age;public int getId() {retu…...

中卫市住房和城乡建设局网站/域名查询平台

北京三年级语文不好怎么办-有什么方法,在学习之前不妨自己定下一个规则&#xff0c;先完成当晚的计划和目标&#xff0c;然后再上网看电影或玩游戏。如果你遇到特殊的情况&#xff0c;不得不上网&#xff0c;给自己定下时间&#xff0c;时间到了之后&#xff0c;坚决断线&#…...

杭州商城型网站建设/怎么样自己创建网站

在eclipse目录下建个批处理文件eclipse.bat,用文本编辑器打开,写入如下内容:eclipse.exe -vmargs -Xms128m -Xmx512m -XX:PermSize128m -XX:PermSize256m然后保存.以后运行eclipse的时候就执行这个批处理就行了.解释下参数的意思:-vmargs 说…...

怎么做网站免费的/常州网站建设制作

AAA认证及RADIUS配置 AAA简介 AAA是Authentication&#xff0c;Authorization and Accounting&#xff08;认证、授权和计费&#xff09;的简称&#xff0c;它提供了一个对认证、授权和计费这三种安全功能进行配置的一致性框架&#xff0c;实际上是对网络安全的一种管理。 这里…...

巫山网站设计/内存优化大师

每日任务内容&#xff1a; 本次会议为第四次Scrum Meeting会议~由于本次会议女生今日因身体不适未参与会议&#xff0c;故在宿舍楼开会&#xff0c;大家集会15分钟。队员 昨日完成任务 明日要完成任务 刘乾 #130 学习python的单元测试&#xff0c;并撰写一个demo或者blog&…...

如何申请邮箱免费注册/枣庄网络推广seo

任何电脑系统都会有瑕疵&#xff0c;win10系统也不例外。今天有用户遇到了win10隐藏防火墙和网络保护部分方法的问题&#xff0c;如果对于老鸟来说就很简单就可以处理&#xff0c;但是对于新手处理win10隐藏防火墙和网络保护部分方法的问题就很难&#xff0c;要是你同样也被win…...