Flink的处理函数——processFunction

目录

一、处理函数概述       

二、Process函数分类——8个

(1)ProcessFunction

(2)KeyedProcessFunction

(3)ProcessWindowFunction

(4)ProcessAllWindowFunction

(5)CoProcessFunction

(6)ProcessJoinFunction

(7)BroadcastProcessFunction

(8)KeyedBroadcastProcessFunction

三、KeyedProcessFunction案例

1.运行processElement方法中的事件时间

(1)输入数据

2.运行processElement方法中的处理时间

(1)先输入一条数据

(2)再快速输入两条数据

3.运行processElement方法中的水位线

(1)输入数据

4.总结:

四、应用案例——求TopN

(一)思路一:hashmap

(二)思路二:keyby

(三)思路三:使用侧输出流——推荐


一、处理函数概述       

        在Flink更底层,我们可以不定义任何具体的算子(比如map,filter,或者window),而只是提炼出一个统一的“处理”(process)操作——它是所有转换算子的一个概括性的表达,可以自定义处理逻辑,所以这一层接口就被叫作“处理函数”(process function)

 

二、Process函数分类——8个

Flink提供了8个不同的处理函数:

(1)ProcessFunction

最基本的处理函数,基于DataStream直接调用.process()时作为参数传入。

(2)KeyedProcessFunction

对流按键分区后的处理函数,基于KeyedStream调用.process()时作为参数传入。要想使用定时器,比如基于KeyedStream。

(3)ProcessWindowFunction

开窗之后的处理函数,也是全窗口函数的代表。基于WindowedStream调用.process()时作为参数传入。

(4)ProcessAllWindowFunction

同样是开窗之后的处理函数,基于AllWindowedStream调用.process()时作为参数传入。

(5)CoProcessFunction

合并(connect)两条流之后的处理函数,基于ConnectedStreams调用.process()时作为参数传入。关于流的连接合并操作,我们会在后续章节详细介绍。

(6)ProcessJoinFunction

间隔连接(interval join)两条流之后的处理函数,基于IntervalJoined调用.process()时作为参数传入。

(7)BroadcastProcessFunction

广播连接流处理函数,基于BroadcastConnectedStream调用.process()时作为参数传入。这里的“广播连接流”BroadcastConnectedStream,是一个未keyBy的普通DataStream与一个广播流(BroadcastStream)做连接(conncet)之后的产物。关于广播流的相关操作,我们会在后续章节详细介绍。

(8)KeyedBroadcastProcessFunction

按键分区的广播连接流处理函数,同样是基于BroadcastConnectedStream调用.process()时作为参数传入。与BroadcastProcessFunction不同的是,这时的广播连接流,是一个KeyedStream与广播流(BroadcastStream)做连接之后的产物。

三、KeyedProcessFunction案例

        基于keyBy之后的KeyedStream,直接调用.process()方法,这时需要传入的参数就是KeyedProcessFunction的实现类。

stream.keyBy( t -> t.f0 ).process(new MyKeyedProcessFunction())

        类似地,KeyedProcessFunction也是继承自AbstractRichFunction的一个抽象类,与ProcessFunction的定义几乎完全一样,区别只是在于类型参数多了一个K,这是当前按键分区的key的类型。同样地,我们必须实现一个.processElement()抽象方法,用来处理流中的每一个数据;另外还有一个非抽象方法.onTimer(),用来定义定时器触发时的回调操作。

代码示例:

import com.atguigu.bean.WaterSensor;
import com.atguigu.split.WaterSensorMapFunction;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.streaming.api.TimerService;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;import java.time.Duration;public class KeyedProcessTimerDemo {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);SingleOutputStreamOperator<WaterSensor> sensorDS = env.socketTextStream("node141", 9999).map(new WaterSensorMapFunction());WatermarkStrategy<WaterSensor> watermarkStrategy = WatermarkStrategy.<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(3)) // 乱序// 提取watermark的时间戳.withTimestampAssigner((element, recordTimestamp) ->element.getTs() * 1000L);SingleOutputStreamOperator<WaterSensor> sensorDSWithWatermark = sensorDS.assignTimestampsAndWatermarks(watermarkStrategy);KeyedStream<WaterSensor, String> sensorKS = sensorDSWithWatermark.keyBy(WaterSensor::getId);// key    输入的类型     输出的类型SingleOutputStreamOperator<String> process = sensorKS.process(new KeyedProcessFunction<String, WaterSensor, String>() {/*** 来一条数据调用一次* @param value* @param ctx* @param out* @throws Exception*/@Overridepublic void processElement(WaterSensor value, Context ctx, Collector<String> out) throws Exception {//获取当前数据的keyString currentKey = ctx.getCurrentKey();// TODO 1.定时器注册TimerService timerService = ctx.timerService();// 1、事件时间的案例
//                        Long currentEventTime = ctx.timestamp(); // 数据中提取出来的事件时间
//                        timerService.registerEventTimeTimer(5000L);
//                        System.out.println("当前key=" + currentKey + ",当前时间=" + currentEventTime + ",注册了一个5s的定时器");// 2、处理时间的案例long currentTs = timerService.currentProcessingTime();timerService.registerProcessingTimeTimer(currentTs + 5000L);System.out.println("当前key=" + currentKey + ",当前时间=" + currentTs + ",注册了一个5s后的定时器");// 3、获取 process的 当前watermark
//                        long currentWatermark = timerService.currentWatermark();
//                        System.out.println("当前数据=" + value + ",当前watermark=" + currentWatermark);// 注册定时器: 处理时间、事件时间
//                        timerService.registerProcessingTimeTimer();
//                        timerService.registerEventTimeTimer();// 删除定时器: 处理时间、事件时间
//                        timerService.deleteEventTimeTimer();
//                        timerService.deleteProcessingTimeTimer();// 获取当前时间进展: 处理时间-当前系统时间,  事件时间-当前watermark
//                        long currentTs = timerService.currentProcessingTime();
//                        long wm = timerService.currentWatermark();}/*** TODO 2.时间进展到定时器注册的时间,调用该方法* @param timestamp 当前时间进展,就是定时器被触发时的时间* @param ctx       上下文* @param out       采集器* @throws Exception*/@Overridepublic void onTimer(long timestamp, OnTimerContext ctx, Collector<String> out) throws Exception {super.onTimer(timestamp, ctx, out);String currentKey = ctx.getCurrentKey();System.out.println("key=" + currentKey + "现在时间是" + timestamp + "定时器触发");}});process.print();env.execute();}
}

1.运行processElement方法中的事件时间

// 1、事件时间的案例
Long currentEventTime = ctx.timestamp(); // 数据中提取出来的事件时间
timerService.registerEventTimeTimer(5000L);
System.out.println("当前key=" + currentKey + ",当前时间=" + currentEventTime + ",注册了一个5s的定时器");
(1)输入数据
[root@node141 ~]# nc -lk 9999
s1,1,1
s1,2,2
s1,3,3
s2,4,4
s2,5,5
s3,9,9

运行结果:注册定时器后,5s后触发

 

2.运行processElement方法中的处理时间

// 2、处理时间的案例
long currentTs = timerService.currentProcessingTime();
timerService.registerProcessingTimeTimer(currentTs + 5000L);
System.out.println("当前key=" + currentKey + ",当前时间=" + currentTs + ",注册了一个5s后的定时器");
(1)先输入一条数据
[root@node141 ~]# nc -lk 9999
s1,1,1

运行结果:相差5s

(2)再快速输入两条数据
[root@node141 ~]# nc -lk 9999
s1,1,1
s1,2,2
s2,2,2

运行结果:相差5s,5s后定时器触发

3.运行processElement方法中的水位线

// 3、获取 process的 当前watermark
long currentWatermark = timerService.currentWatermark();
System.out.println("当前数据=" + value + ",当前watermark=" + currentWatermark);
(1)输入数据
[root@node141 ~]# nc -lk 9999
s1,1,1
s1,5,5
s1,9,9

运行结果:

数据经过map算子,然后再到process方法中。

        当第一条数据输入时,进入map时的watermark是1s-3s-1ms=-2001ms,数据随后进入process方法,调用了processElement方法,在processElement方法中获取当前的watermark,此时-2001ms这一watermark还没有进入process中,所以当前process的watermark是long的最小值;

        第一条数据处理完成后,第二条继续输入到map中,此时process中的watermark变为-2001ms,而map中的watermark是5s-3s-1ms=1999ms,同样,数据再进入process中,调用processElement方法,此时1999ms这一watermark仍然还没有进入process中,所以当前process中的watermark是之前的-2001ms;

       第二条数据处理完成后,第三条数据继续输入到map中,此时process中的watermark变为1999ms,而map中的watermark是9s-3s-1ms=5999ms,同样,数据再进入process中,调用processElement方法,此时5999ms这一watermark也没有来得及进入process中,所以当前process中的watermark是之前的1999ms。

4.总结:

TODO 定时器
1、keyed才有
2、事件时间定时器,通过watermark来触发的
      watermark >= 注册的时间
      注意: watermark = 当前最大事件时间 - 等待时间 -1ms, 因为 -1ms,所以会推迟一条数据
       比如, 5s的定时器,
       如果 等待=3s, watermark = 8s - 3s -1ms = 4999ms,不会触发5s的定时器
       需要 watermark = 9s -3s -1ms = 5999ms ,才能去触发 5s的定时器
3、在process中获取当前watermark,显示的是上一次的watermark
      ==> 因为process还没接收到这条数据对应生成的新watermark

四、应用案例——求TopN

(一)思路一:hashmap

使用所有数据到一起,用hashmap来存储,key=vc,value=count值

import com.atguigu.bean.WaterSensor;
import com.atguigu.split.WaterSensorMapFunction;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.ProcessAllWindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;import java.time.Duration;
import java.util.*;public class TopNDemo {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);SingleOutputStreamOperator<WaterSensor> sensorDS = env.socketTextStream("node141", 9999).map(new WaterSensorMapFunction()).assignTimestampsAndWatermarks(WatermarkStrategy.<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(3)) // 乱序.withTimestampAssigner((element, recordTimestamp) ->element.getTs() * 1000L));// 最近10s=窗口长度  每5s输出=滑动步长// TODO 思路一:使用所有数据到一起,用hashmap来存储,key=vc,value=count值sensorDS.windowAll(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5))).process(new MyTopNPawf()).print();env.execute();}public static class MyTopNPawf extends ProcessAllWindowFunction<WaterSensor, String, TimeWindow> {@Overridepublic void process(ProcessAllWindowFunction<WaterSensor, String, TimeWindow>.Context context, Iterable<WaterSensor> elements, Collector<String> out) throws Exception {// 定义一个hashmap用来存,key=vc,value=count值Map<Integer, Integer> vcCountMap = new HashMap<>();// 1.遍历数据,统计各个vc出现的次数for (WaterSensor element : elements) {Integer vc = element.getVc();if (vcCountMap.containsKey(vc)) {// 1.1 key存在,不是这个key的第一条数据,直接累加vcCountMap.put(vc, vcCountMap.get(vc) + 1);} else {// 1.2 如果key不存在,则初始化vcCountMap.put(vc, 1);}}// 2.对count值进行排序,利用list来实现排序List<Tuple2<Integer, Integer>> datas = new ArrayList<>();// 2.1 对list进行排序,根据count值进行降序for (Integer vc : vcCountMap.keySet()) {datas.add(Tuple2.of(vc, vcCountMap.get(vc)));}// count值相减datas.sort((o1, o2) -> o2.f1 - o1.f1);// 3.取出count最大的2个vcStringBuffer outStr = new StringBuffer();outStr.append("\n=======================\n");for (int i = 0; i < Math.min(2, datas.size()); i++) {Tuple2<Integer, Integer> vcCount = datas.get(i);outStr.append("窗口开始时间:" + DateFormatUtils.format(context.window().getStart(), "yyyy-MM-dd HH:mm:ss.SSS"));outStr.append("\n");outStr.append("top:" + (i + 1));outStr.append("\n");outStr.append("vc=" + vcCount.f0);outStr.append("\n");outStr.append("count=" + vcCount.f1);outStr.append("\n");outStr.append("窗口结束时间:" + DateFormatUtils.format(context.window().getEnd(), "yyyy-MM-dd HH:mm:ss.SSS\n"));}outStr.append("\n=======================\n");// 输出out.collect(outStr.toString());}}
}

输入数据:

[root@node141 ~]# nc -lk 9999
s1,1,1
s1,2,1
s1,3,3
s1,6,1
s1,8,2
s1,9,3
s1,10,2
s1,11,1
s1,13,2

运行结果:

 

思路一不推荐,因为要将数据攒到一起才会计算,效果不好。

(二)思路二:keyby

代码思路:

 

实现步骤:
1、按照vc做keyby,开窗,分别count
   ==》 增量聚合,计算 count
   ==》 全窗口,对计算结果 count值封装 ,  带上 窗口结束时间的 标签
         ==》 为了让同一个窗口时间范围的计算结果到一起去
2、对同一个窗口范围的count值进行处理: 排序、取前N个
   =》 按照 windowEnd做keyby
   =》 使用process, 来一条调用一次,需要先存,分开存,用HashMap进行存储,key=windowEnd,value=List
     =》 使用定时器,对 存起来的结果 进行 排序、取前N个

import com.atguigu.bean.WaterSensor;
import com.atguigu.split.WaterSensorMapFunction;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.AggregateFunction;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class KeyedProcessFunctionTopNDemo {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);SingleOutputStreamOperator<WaterSensor> sensorDS = env.socketTextStream("node141", 9999).map(new WaterSensorMapFunction()).assignTimestampsAndWatermarks(WatermarkStrategy.<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(3)) // 乱序.withTimestampAssigner((element, recordTimestamp) ->element.getTs() * 1000L));// 最近10s=窗口长度  每5s输出=滑动步长// TODO 使用keyedProcessFunction实现// 1.按照vc分组、开窗、聚合(增量计算+全量打标签)// 开窗聚合后,就是普通的流,没有了窗口信息,需要自己打上窗口的标记 windowEndSingleOutputStreamOperator<Tuple3<Integer, Integer, Long>> windowAgg = sensorDS.keyBy(WaterSensor::getVc).window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5))).aggregate(new VcCountAgg(),new WindowResult());// 2.按照窗口标签(窗口结束时间)keyby,保证同一个窗口时间范围的结果,到一起去,排序、取TopNwindowAgg.keyBy(r -> r.f2).process(new TopN(2)).print();env.execute();}public static class VcCountAgg implements AggregateFunction<WaterSensor, Integer, Integer> {@Overridepublic Integer createAccumulator() {return 0;}@Overridepublic Integer add(WaterSensor value, Integer accumulator) {return accumulator + 1;}@Overridepublic Integer getResult(Integer accumulator) {return accumulator;}@Overridepublic Integer merge(Integer a, Integer b) {return null;}}/*** 泛型如下:* 第一个:输入类型=增量函数的输出 count值,Integer* 第二个:输出类型=Tuple3<vc,count,windowEnd>,带上 窗口结束时间的标签* 第三个:key的类型:vc,Integer* 第四个:窗口类型*/public static class WindowResult extends ProcessWindowFunction<Integer, Tuple3<Integer, Integer, Long>, Integer, TimeWindow> {@Overridepublic void process(Integer key, Context context, Iterable<Integer> elements, Collector<Tuple3<Integer, Integer, Long>> out) throws Exception {// 迭代器里面只有一条数据,next一次即可Integer count = elements.iterator().next();long windowEnd = context.window().getEnd();out.collect(Tuple3.of(key, count, windowEnd));}}/*** 泛型如下:* 第一个:key的类型,是windowEnd* 第二个:输入的类型,三元组Tuple3<vc,count,windowEnd>,带上 窗口结束时间的标签* 第三个:打印输出结果*/public static class TopN extends KeyedProcessFunction<Long, Tuple3<Integer, Integer, Long>, String> {private Map<Long, List<Tuple3<Integer, Integer, Long>>> dataListMap;// 要取的top数量private int threshold;public TopN(int threshold) {this.threshold = threshold;dataListMap = new HashMap<>();}@Overridepublic void processElement(Tuple3<Integer, Integer, Long> value, KeyedProcessFunction<Long, Tuple3<Integer, Integer, Long>, String>.Context ctx, Collector<String> out) throws Exception {// 进入这个方法,只是一条数据,要排序,必须等数据到齐才行,将不同窗口的数据用hashmap分开存起来,// todo  1.存到hashmap中  注意此时的key是窗口的结束时间Long windowEnd = value.f2;if (dataListMap.containsKey(windowEnd)) {// 1.1 包含vc,不是该vc的第一条,直接添加到list中List<Tuple3<Integer, Integer, Long>> dataList = dataListMap.get(windowEnd);dataList.add(value);// 也就是说只需要把value的值添加到dataListMap中即可} else {// 1.2 不包含,是该vc的第一条,需要初始化listList<Tuple3<Integer, Integer, Long>> dataList = new ArrayList<>();dataList.add(value);dataListMap.put(windowEnd, dataList);}// todo 2.注册一个定时器,windowEnd+1ms即可// 同一个窗口范围,应该同时输出ctx.timerService().registerEventTimeTimer(windowEnd + 1);}@Overridepublic void onTimer(long timestamp, KeyedProcessFunction<Long, Tuple3<Integer, Integer, Long>, String>.OnTimerContext ctx, Collector<String> out) throws Exception {super.onTimer(timestamp, ctx, out);// 定时器触发,同一个窗口范围的计算结果攒齐了,开始  排序、取TopNLong windowEnd = ctx.getCurrentKey();// 1.排序List<Tuple3<Integer, Integer, Long>> dataList = dataListMap.get(windowEnd);dataList.sort((o1, o2) -> o2.f1 - o1.f1);// 2.取TopNStringBuffer outStr = new StringBuffer();outStr.append("\n=======================\n");for (int i = 0; i < Math.min(threshold, dataList.size()); i++) {Tuple3<Integer, Integer, Long> vcCount = dataList.get(i);outStr.append("top:" + (i + 1));outStr.append("\n");outStr.append("vc=" + vcCount.f0);outStr.append("\n");outStr.append("count=" + vcCount.f1);outStr.append("\n");outStr.append("窗口结束时间:" + DateFormatUtils.format(vcCount.f2, "yyyy-MM-dd HH:mm:ss.SSS" + "\n"));outStr.append("=======================\n");}// 用完的List,及时清理dataList.clear();// 输出out.collect(outStr.toString());}}
}

输入数据:

[root@node141 ~]# nc -lk 9999
s1,1,1
s1,2,2
s1,8,1
s1,10,1

运行结果:

(三)思路三:使用侧输出流——推荐

import com.atguigu.bean.WaterSensor;
import com.atguigu.split.WaterSensorMapFunction;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.util.Collector;
import org.apache.flink.util.OutputTag;import java.time.Duration;public class SideOutputDemo {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);SingleOutputStreamOperator<WaterSensor> sensorDS = env.socketTextStream("node141", 9999).map(new WaterSensorMapFunction()).assignTimestampsAndWatermarks(WatermarkStrategy.<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(3)) // 乱序.withTimestampAssigner((element, recordTimestamp) ->element.getTs() * 1000L));OutputTag<String> warnTag = new OutputTag<>("warn", Types.STRING);SingleOutputStreamOperator<WaterSensor> process = sensorDS.keyBy(WaterSensor::getId).process(new KeyedProcessFunction<String, WaterSensor, WaterSensor>() {@Overridepublic void processElement(WaterSensor value, KeyedProcessFunction<String, WaterSensor, WaterSensor>.Context ctx, Collector<WaterSensor> out) throws Exception {// 使用侧输出流告警if (value.getVc() > 10) {ctx.output(warnTag, "当前水位=" + value.getVc() + ",大于阈值10!!!");}// 主流正常发送数据out.collect(value);}});process.print("主流");process.getSideOutput(warnTag).printToErr("warn");env.execute();}
}

输入数据:

[root@node141 ~]# nc -lk 9999
s1,1,5
s1,10,1
s1,6,11
s1,7,20

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/150035.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vue +element 批量删除

1.拿到当前勾选状态 在el-table里边去写 单选用第一个 多选用第二个 select"selectHandle" :当用户手动勾选数据行的 Checkbox 时触发的事件 select-all"selectHandle":当用户手动勾选全选 Checkbox 时触发的事件// 点击勾选选择器selectHandle(selection…

华为云云耀云服务器L实例评测|云耀云服务器L实例部署DjangoBlog个人博客系统

华为云云耀云服务器L实例评测&#xff5c;云耀云服务器L实例部署DjangoBlog个人博客系统 一、云耀云服务器L实例介绍1.1 云耀云服务器L实例简介1.2 云耀云服务器L实例特点 二、DjangoBlog介绍2.1 DjangoBlog介绍2.2 DjangoBlog特点 三、本次实践介绍3.1 本次实践简介3.2 本次环…

【2023双非保研】信管跨保计算机大类的记录(东南、川大、重大、东北、西电、南理工、杭高院、河海、东华、天大等)

以此篇博客记录我的保研之旅 目录 一、个人情况 二、夏令营 1、国科大杭高院&#xff08;线下&#xff09; 2、南信工&#xff08;线下&#xff09; 3、华中师范&#xff08;线上or线下&#xff09; 4、浙大软件&#xff08;线上&#xff09; 5、东华大学&#xff08;线…

哈希应用之位图

文章目录 1.位图概念2.面试题引入3.代码解决[配注释]4.位图应用4.1找到100亿个整数里只出现一次的整数4.2找两个分别有100亿个整数的文件的交集[只有1G内存]1.法一[使用于数据量<42亿]2.法二[适用于数据量大>42亿]3.在一个有100亿个int的文件中找到出现次数不超过2次的所…

假期AI新闻热点:亚运会Al技术亮点;微软GPT-4V论文精读;Perplexity推出pplx-api;DALL-E 3多渠道测评 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f525; 科技感拉满&#xff0c;第19届杭州亚运会中的Al技术亮点 八年筹备&#xff0c;杭州第19届亚运会开幕式于9月23日晚隆重举行&#xff0…

vue3中使用return语句返回this.$emit(),在同一行不执行,换行后才执行,好奇怪!

今天练习TodoList任务列表案例,该案例效果如图所示&#xff1a; 此案例除了根组件App.vue&#xff0c;还有TodoList、TodoInput、TodoButton三个子组件。 因为有视频讲解&#xff0c;在制作TodoList、TodoInput时很顺利&#xff0c;只是在完成TodoButton这个组件时出了点问题…

<二>Qt斗地主游戏开发:过场动画的实现

1. 过场动画效果 2. 思路分析 过场动画较为简单&#xff0c;只有一个进度条在进行滚动&#xff0c;因此实现起来不需要动画相关处理&#xff0c;仅需要图片和定时器设定&#xff0c;让进度条动起来即可。我们可以创建一个对话框&#xff0c;设定背景图片以及对话框透明无边框&a…

口袋参谋:如何有效地监测你的竞争对手!

​在淘宝天猫上开店&#xff0c;竞争是非常大的&#xff0c;那么就会出现许多同样的产品&#xff0c;如果想要在竞争中胜出&#xff0c;就需要多去研究同行的数据&#xff0c;知己知彼&#xff0c;百战百胜。 掌握竞争对手数据目的主要是有2个&#xff1a; 1、掌握对手是怎么起…

操作符 | C语言中操作符详解 | 操作符的优先级 | 移位操作法的使用方式

一、算术操作符&#xff1a;、-、*、/、% 算术操作符其实在平时生活中&#xff0c;也遇到很多&#xff0c;并且这五类操作符基本很常见&#xff0c;而他们的作用与数学所学习的功能是一样的。但是“/”除号操作符与“%”取模操作符有些不同。下面就以这两个的操作符为主要说起…

伟大不能被计划

假期清理书单&#xff0c;把这个书读完了&#xff0c;结果发现出奇的好&#xff0c;可以说是值得亲身去读的书&#xff0c;中间的一些论述提供了人工智能专业方面的视角来论证这这个通识观点&#xff0c;可信度很不错&#xff1b; 这篇blog也不是对书的总结&#xff0c;更多的是…

asp.net班级管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net班级管理系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语言开发 asp.net班级管理系统 二、功能介绍 1…

Pikachu靶场——目录遍历漏洞和敏感信息泄露

文章目录 1. 目录遍历漏洞1.1 源码分析1.2 漏洞防御 2. 敏感信息泄露2.1 漏洞防御 1. 目录遍历漏洞 漏洞描述 目录遍历漏洞发生在应用程序未能正确限制用户输入的情况下。攻击者可以利用这个漏洞&#xff0c;通过在请求中使用特殊的文件路径字符&#xff08;如 …/ 或 %2e%2e…

Electronjs入门-Electron中的主要模块

在本节中&#xff0c;我们将了解在Electron中创建任何应用程序时的一些基本模块&#xff1b;这些模块多种多样&#xff0c;使我们能够轻松地进行进程通信&#xff0c;创建操作系统的本地菜单。 为了利用Electron模块&#xff0c;以及任何第三方或Node模块&#xff0c;不仅在主流…

SSM - Springboot - MyBatis-Plus 全栈体系(二十一)

第四章 SpringMVC 四、RESTFUL 风格设计和实战 1. RESTFul 风格概述 1.1 RESTFul 风格简介 RESTful&#xff08;Representational State Transfer&#xff09;是一种软件架构风格&#xff0c;用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量…

visual studio解决bug封装dll库

1.速度最大化 O2 2.设置输出目录 配置属性/常规/输出目录 链接器/常规/输出dll文件 链接器/调试/输出程序数据库pdb文件 链接器/高级/导入库 3.输出X86 X64分别对应的dll、lib、pdb 然后修改更新说明 更新说明格式如下&#xff1a; 4.将库提交到FTP每日更新库文档下 和测试交接…

[NISACTF 2022]hardsql - quine注入

题目描述&#xff1a;$password$_POST[passwd]; $sql"SELECT passwd FROM users WHERE usernamebilala and passwd$password;"; 从描述看出是quine注入&#xff0c;且用户名要是bilala 1、经测试&#xff0c;参数为&#xff1a;username&passwd&login登录&a…

36.骑士周游算法及其基于贪心算法的优化

概述 骑士周游算法&#xff0c;叫做“马踏棋盘算法”或许更加直观。在国际象棋8x8的棋盘中&#xff0c;马也是走“日字”进行移动&#xff0c;相应的产生了一个问题&#xff1a;“如果要求马 在每个方格只能进入一次&#xff0c;走遍全部的64个方格需要如何行进&#xff1f;”…

1147. 段式回文

链接&#xff1a; ​​​​​​1147. 段式回文 题解&#xff1a; class Solution { public:int longestDecomposition(string text) {MOD 1e9 7;if (text.size() < 0) {return 0;}// 初始化26的n次方的表pow26.resize(text.size());pow26[0] 1;for (int i 1; i < …

【C语言】利用数组处理批量数据(字符数组)

前言:前面已经介绍了&#xff0c;字符数据是以字符的ASCII代码存储在存储单元中的&#xff0c;一般占一个字节。由于ASCII代码也属于整数形式&#xff0c;因此在C99标准中&#xff0c;把字符类型归纳为整型类型中的一种。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x…

数据结构——红黑树(详解性质+C++模拟)

文章目录 前言红黑树的概念红黑树的性质红黑树结点的定义红黑树的插入操作1. **按照二叉搜索树的规则插入新结点**2. 检测新节点插入后&#xff0c;红黑树的性质是否遭到破坏 红黑树的验证总结 前言 本篇博客将为大家重点讲述红黑树这一数据结构&#xff0c;讲解其实现的方式即…