Flink之复杂事件处理CEP

复杂事件处理CEP

  • Flink CEP
  • 基本使用
    • 添加依赖
    • 定义匹配模式
    • 定义匹配结果
    • 验证
  • 模式Pattern API
    • 单个模式
      • 量词
      • 条件
    • 组合模式
    • 跳过策略
    • 模式组
    • 匹配结果
  • 应用示例
    • 自定义消息事件
    • 自定义Pattern
    • 测试

Flink CEP

Flink的CEP (Complex Event Processing) 是指Flink提供的一种用于处理复杂事件序列的库。复杂事件通常由多个简单事件组成,这些简单事件在特定的时间窗口内以特定的顺序发生。CEP可以用于检测和识别这些复杂事件,并根据预定义的模式进行操作和处理。

Flink的CEP库提供了一个灵活而强大的编程模型,使用户能够指定不同事件之间的关系模式,并定义事件触发的条件。它能够处理基于时间、顺序和其他属性的复杂事件模式,并支持流式处理和实时数据。CEP可以用于构建基于事件的应用程序,例如金融交易监控、网络流量分析、IoT数据处理等。

应用场景

Flink CEP(Complex Event Processing)是针对处理数据流中复杂事件模式的技术,适用于多种实时数据处理场景,其中包括:

金融交易监控:实时监控金融交易数据流,以识别潜在的欺诈行为,例如检测异常的交易序列或者异常的资金流动模式。网络安全分析:对实时网络日志进行分析,以检测网络攻击、异常行为或者安全威胁,例如识别特定攻击模式或异常的网络通信序列。物联网(IoT)数据处理:处理来自传感器和设备的实时数据,以识别设备故障、异常事件或者预测维护需求,例如发现特定的设备状态序列暗示了潜在的问题。市场营销和个性化推荐:分析客户实时行为数据,识别特定的购买模式或者行为序列,以提供个性化的产品推荐或市场营销策略。生产流程监控:监控工业生产线上的传感器和生产数据,以检测生产异常、预测设备故障或者优化生产调度。医疗健康监控:实时监控病人健康数据或医疗设备数据,以检测潜在的健康危机、预测病情变化或者提供实时的健康监控服务。

基本使用

添加依赖

将Flink CEP依赖项添加到pom.xml中

   <dependency><groupId>org.apache.flink</groupId><artifactId>flink-java</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-streaming-java</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-clients</artifactId><version>${flink.version}</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-cep</artifactId><version>${flink.version}</version></dependency>

定义匹配模式

DataStream要应用模式匹配的 事件必须实现正确的equals()和hash Code()方法,因为 Flink CEP 使用它们来比较和匹配事件。

    public static void main(String[] args) throws Exception {// 设置执行环境StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();// 准备事件流DataStream<Tuple2<String, Integer>> inputEventStream = env.fromElements(new Tuple2<>("event", 1),new Tuple2<>("event", 2),new Tuple2<>("event", 3),new Tuple2<>("event", 4),new Tuple2<>("event", 5),new Tuple2<>("event", 6),new Tuple2<>("event", 7),new Tuple2<>("event", 8)).assignTimestampsAndWatermarks(WatermarkStrategy.<Tuple2<String, Integer>>forMonotonousTimestamps().withTimestampAssigner(new SerializableTimestampAssigner<Tuple2<String, Integer>>() {@Overridepublic long extractTimestamp(Tuple2<String, Integer> event, long recordTimestamp) {return event.f1 * 1000;}})).keyBy(event -> event.f0);/*** 定义复杂事件处理模式* 先匹配元素是偶数的事件,然后匹配元素>3的事件,然后继续匹配元素是8的元素*/// 声明并初始化一个模式,用于表示要在事件流中检测的模式。这个模式匹配的是一个包含String和Integer类型元素的元组. begin("start") 来定义模式的起始点Pattern<Tuple2<String, Integer>, ?> pattern = Pattern.<Tuple2<String, Integer>>begin("start")// 对模式的起始点应用条件,条件是一个简单的过滤条件: 事件的第二个元素是偶数。.where(new SimpleCondition<Tuple2<String, Integer>>() {@Overridepublic boolean filter(Tuple2<String, Integer> event) {return event.f1 % 2 == 0;}})// 定义了紧接在起始点后的第一个元素命名为 "middle".next("middle")// 这个元素是一个Tuple2类型的子类型.subtype(Tuple2.class)// 对第二个元素应用了迭代条件,这里使用了一个迭代条件(IterativeCondition),来检查第二个元素是否为奇数.where(new SimpleCondition<Tuple2>() {@Overridepublic boolean filter(Tuple2 event) {// return (Integer) event.f1 > 5;return (Integer) event.f1 > 3;}})// 规定了前面定义的模式必须发生N次.times(2)// 定义了这N次发生必须是连续的.consecutive()// 定义了在之后紧跟的元素命名为 "end",用于表示模式的结束.followedBy("end")// 对模式的结束点应用了一个简单的条件,确保事件的第二个元素等于8.where(SimpleCondition.of((Tuple2<String, Integer> event) -> event.f1 == 8)).within(Time.seconds(5));// 在事件流上应用模式PatternStream<Tuple2<String, Integer>> patternStream = CEP.pattern(inputEventStream.keyBy(event -> event.f0), pattern);// 选择匹配结果并输出// DataStream<String> result = patternStream.select(new MyPatternSelectFunction());DataStream<String> result = patternStream.process(new MyPatternProcessFunction());result.print();// 执行任务env.execute("CEP Example");}

定义匹配结果

 /*** PatternSelectFunction定义匹配结果的处理函数*/public static class MyPatternSelectFunction implements PatternSelectFunction<Tuple2<String, Integer>, String> {@Overridepublic String select(Map<String, List<Tuple2<String, Integer>>> pattern) throws Exception {StringBuilder builder = new StringBuilder();builder.append("找到匹配项: ");pattern.forEach((key, value) -> builder.append(key).append(" => ").append(value).append("; "));return builder.toString();}}/*** PatternProcessFunction定义匹配结果的处理函数*/public static class MyPatternProcessFunction extends PatternProcessFunction<Tuple2<String, Integer>, String> {@Overridepublic void processMatch(Map<String, List<Tuple2<String, Integer>>> pattern, Context context, Collector<String> collector) throws Exception {StringBuilder builder = new StringBuilder();builder.append("找到匹配项: ");pattern.forEach((key, value) -> builder.append(key).append(" => ").append(value).append("; "));collector.collect(builder.toString());}}

验证

1> 找到匹配项: start => [(event,4)]; middle => [(event,5), (event,6)]; end => [(event,8)]; 

模式Pattern API

模式 API允许定义要从输入流中提取的复杂模式序列

每个复杂模式序列由多个简单模式组成,即寻找具有相同属性的单个事件的模式每个模式必须有一个唯一的名称,可以使用该名称来标识匹配的事件模式名称不能包含字符":"

单个模式

复杂规则中的每一个单独的模式定义,就是个体模式。我们既可以定义一个给定事件出现的次数(量词),也可以定义一个条件来决定一个进来的事件是否被接受进入这个模式(条件)。

量词

默认情况下,模式是单例模式,可以使用量词将其转换为循环模式。

API说明
pattern.oneOrMore()模式发生1次或N次
pattern.times(#ofTimes)发生一次或多次的模式
pattern.times(#fromTimes, #toTimes)出现特定次数的模式
pattern.greedy()模式变得贪婪,匹配越多越好
pattern.optional()模式可以不匹配

使用示例:

// 期望出现4次
pattern.times(4);// 期望出现0次或者4次
pattern.times(4).optional();// 期望出现2次、3次或者4次
pattern.times(2, 4);// 期望出现2次、3次或者4次,尽可能多地重复
pattern.times(2, 4).greedy();// 期望出现0次、2次、3次或者4次
pattern.times(2, 4).optional();// 期望出现0次、2次、3次或者4次,尽可能多地重复
pattern.times(2, 4).optional().greedy();// 期望出现1次或者更多次
pattern.oneOrMore();// 期望出现1次或者更多次,尽可能多地重复
pattern.oneOrMore().greedy();// 期望出现0次或者更多次
pattern.oneOrMore().optional();// 期望出现0次或者更多次,尽可能多地重复
pattern.oneOrMore().optional().greedy();// 期望出现2次或者更多次
pattern.timesOrMore(2);// 期望出现2次或者更多次,尽可能多地重复
pattern.timesOrMore(2).greedy();// 期望出现0次、2次或者更多次
pattern.timesOrMore(2).optional()// 期望出现0次、2次或者更多次,尽可能多地重复
pattern.timesOrMore(2).optional().greedy();

条件

对于每个模式,可以指定传入事件必须满足的条件才能被“接受”到模式中

API描述示例说明
pattern.where()定义当前模式的条件。为了匹配模式,事件必须满足条件。多个连续的 where() 子句会导致它们的条件被AND编辑pattern.where(SimpleCondition.of((Tuple2<String, Integer> event) -> event.f1 == 8))匹配f1==8
pattern.or()添加一个与现有条件相结合的新条件OR。仅当事件至少满足其中一个条件时,它才能与模式匹配pattern.where(SimpleCondition.of((Tuple2<String, Integer> event) -> event.f1 ==1)).or(SimpleCondition.of((Tuple2<String, Integer> event) -> event.f1 == 2))匹配f1 == 1 或者 f1==2
pattern.until()指定循环模式的停止条件。如果发生与给定条件匹配的事件,则该模式将不再接受任何事件。仅适用于结合oneOrMore() NOTE:它允许在基于事件的条件下清除相应模式的状态pattern.until(SimpleCondition.of((Tuple2<String, Integer> event) -> event.f1 == 2))匹配1次或多次,直到f1==2

如果名称以foo开头,则接受名为middle的模式的下一个事件,并且如果该模式先前接受的事件的价格之和加上当前事件的价格事件不超过5.0的值

middle.oneOrMore().subtype(SubEvent.class).where(new IterativeCondition<SubEvent>() {@Overridepublic boolean filter(SubEvent value, Context<SubEvent> ctx) throws Exception {if (!value.getName().startsWith("foo")) {return false;}double sum = value.getPrice();for (Event event : ctx.getEventsForPattern("middle")) {sum += event.getPrice();}return Double.compare(sum, 5.0) < 0;}});

组合模式

把很多单个模式组合起来,就形成了组合模式。Flink CEP支持事件之间如下形式的连续策略:

严格连续性:期望所有匹配事件严格一个接一个地出现,中间没有任何不匹配的事件。宽松连续性:忽略匹配事件之间出现的不匹配事件。非确定性宽松连续性:进一步放松连续性,允许忽略某些匹配事件的其他匹配。

要在连续模式之间应用它们,可以使用:

next():对于严格的
followedBy():对于宽松的
followedByAny():对于非确定性松弛连续性
notNext():如果您不希望某个事件类型直接跟随另一个事件类型
notFollowedBy():如果您不希望某个事件类型介于其他两个事件类型之间

模式序列必须以初始模式开始

Pattern<Event, ?> start = Pattern.<Event>begin("start");Pattern<Event, ?> start = Pattern.<Event>begin(Pattern.<Event>begin("start").where(...).followedBy("middle").where(...)
);
API说明示例
begin(#name)定义起始模式Pattern<Event, ?> start = Pattern.begin(“start”);
begin(#pattern_sequence)定义起始模式Pattern.begin(Pattern.begin(“start”).where(…).followedBy(“middle”).where(…));
next(#name)附加新模式。匹配事件必须直接继承前一个匹配事件(严格连续性)Pattern<Event, ?> next = start.next(“middle”)
next(#pattern_sequence)附加新模式。一系列匹配事件必须直接接续前一个匹配事件(严格连续性)start.next(Pattern.begin(“start”).where(…).followedBy(“middle”).where(…));
followedBy(#name)附加新模式。其他事件可以发生在匹配事件和前一个匹配事件之间(宽松的连续性)Pattern<Event, ?> followedBy = start.followedBy(“middle”);
followedBy(#pattern_sequence)附加新模式。其他事件可以发生在匹配事件和前一个匹配事件之间(宽松的连续性)start.followedBy(Pattern.begin(“start”).where(…).followedBy(“middle”).where(…));
followedByAny(#name)附加新模式。其他事件可以发生在匹配事件和前一个匹配事件之间,并且将为每个替代匹配事件呈现替代匹配(非确定性宽松连续性)Pattern<Event, ?> followedByAny = start.followedByAny(“middle”);
followedByAny(#pattern_sequence)附加新模式。其他事件可以发生在匹配事件和前一个匹配事件之间,并且将为每个替代匹配事件呈现替代匹配(非确定性宽松连续性)start.next(Pattern.begin(“start”).where(…).followedBy(“middle”).where(…));
notNext()附加新的否定模式。匹配(否定)事件必须直接继承前一个匹配事件(严格连续性),才能丢弃部分匹配Pattern<Event, ?> notNext = start.notNext(“not”);
notFollowedBy()附加新的否定模式。即使匹配(负)事件和前一个匹配事件(宽松连续性)之间发生其他事件,部分匹配的事件序列也将被丢弃Pattern<Event, ?> notFollowedBy = start.notFollowedBy(“not”);
within(time)定义事件序列与模式匹配的最大时间间隔。如果未完成的事件序列超过此时间,则将其丢弃pattern.within(Time.seconds(10));

使用示例:

// 严格的连续性模式
Pattern<Event, ?> strict = start.next("middle").where(...);// 宽松的连续性模式
Pattern<Event, ?> relaxed = start.followedBy("middle").where(...);// 非确定性的宽松连续性模式
Pattern<Event, ?> nonDetermin = start.followedByAny("middle").where(...);// 使用严格连续性的NOT模式
Pattern<Event, ?> strictNot = start.notNext("not").where(...);// 使用宽松连续性的NOT模式
Pattern<Event, ?> relaxedNot = start.notFollowedBy("not").where(...);

跳过策略

对于给定的模式,同一事件可以分配给多个成功的匹配。要控制一个事件将分配多少个匹配项,需要指定跳过策略AfterMatchSkipStrategy

跳跃策略有五种类型

API说明
AfterMatchSkipStrategy.noSkip()创建NO_SKIP跳过策略
AfterMatchSkipStrategy.skipToNext()创建SKIP_TO_NEXT跳过策略
AfterMatchSkipStrategy.skipPastLastEvent()创建SKIP_PAST_LAST_EVENT跳过策略
AfterMatchSkipStrategy.skipToFirst(patternName)使用引用的模式名称patternName创建SKIP_TO_FIRST跳过策略
AfterMatchSkipStrategy.skipToLast(patternName)使用引用的模式名称patternName创建SKIP_TO_LAST跳过策略

注意:

当使用SKIP_TO_FIRST和SKIP_TO_LAST跳过策略时,还应指定有效的PatternName

SkipToFirstStrategy skipToFirstStrategy = AfterMatchSkipStrategy.skipToFirst("patternName");
Pattern.begin("patternName", skipToFirstStrategy);

模式组

将一个模式作为条件嵌套在单个模式里,就是模式组。

Pattern<Event, ?> start = Pattern.begin(
Pattern.begin("start").where(...).followedBy("start_middle").where(...)
);// 严格的连续性模式
Pattern<Event, ?> strict = start.next(
Pattern.begin("next_start").where(...).followedBy("next_middle").where(...)
).times(3);// 宽松的连续性模式
Pattern<Event, ?> relaxed = start.followedBy(
Pattern.begin("followedby_start").where(...).followedBy("followedby_middle").where(...)
).oneOrMore();// 非确定性的宽松连续性模式
Pattern<Event, ?> nonDetermin = start.followedByAny(
Pattern.begin("followedbyany_start").where(...).followedBy("followedbyany_middle").where(...)
).optional();

匹配结果

指定要查找的模式序列后,就可以将其应用到输入流以检测潜在的匹配项。

要针对模式序列运行事件流,必须创建一个PatternStream. 给定一个输入流input、一个模式pattern和一个可选的比较器,comparator用于对具有相同时间戳的事件(在 EventTime的情况下或在同一时刻到达)进行排序

可以使用PatternProcessFunction、也可以使用旧式API,例如PatternSelectFunction

1.PatternProcessFunction

PatternProcessFunction有一个processMatch为每个匹配事件序列调用的方法。

PatternStream<Event> patternStream = CEP.pattern(input, pattern, comparator);
    public static class MyPatternProcessFunction extends PatternProcessFunction<Tuple2<String, Integer>, String> {/**** @param pattern Map<String, List<IN>>其中键是模式序列中每个模式的名称,值是该模式的所有已接受事件的列表(IN是输入元素的类型)*/@Overridepublic void processMatch(Map<String, List<Tuple2<String, Integer>>> pattern, Context context, Collector<String> collector) throws Exception {StringBuilder builder = new StringBuilder();builder.append("找到匹配项: ");pattern.forEach((key, value) -> builder.append(key).append(" => ").append(value).append("; "));collector.collect(builder.toString());}}

使用:

DataStream<String> result = patternStream.process(new MyPatternProcessFunction());

2.TimedOutPartialMatchHandler

每当模式具有通过within关键字附加的窗口长度时,部分事件序列就有可能被丢弃,因为它们超出了窗口长度。要对超时的部分匹配采取行动,可以使用TimedOutPartialMatchHandler接口。

TimedOutPartialMatchHandler提供了额外的processTimedOutMatch方法,每次超时的部分匹配都会调用该方法。

    public static class MyPatternProcessFunction extends PatternProcessFunction<Tuple2<String, Integer>, String> implements TimedOutPartialMatchHandler<Tuple2<String, Integer>> {/**** @param pattern Map<String, List<IN>>其中键是模式序列中每个模式的名称,值是该模式的所有已接受事件的列表(IN是输入元素的类型)*/@Overridepublic void processMatch(Map<String, List<Tuple2<String, Integer>>> pattern, Context context, Collector<String> collector) throws Exception {StringBuilder builder = new StringBuilder();builder.append("找到匹配项: ");pattern.forEach((key, value) -> builder.append(key).append(" => ").append(value).append("; "));collector.collect(builder.toString());}@Overridepublic void processTimedOutMatch(Map<String, List<Tuple2<String, Integer>>> map, Context context) throws Exception {StringBuilder builder = new StringBuilder();builder.append("处理超时的部分模式: ");map.forEach((key, value) -> builder.append(key).append(" => ").append(value).append("; "));System.out.println(builder.toString());}}
  1. PatternSelectFunction
public static class MyPatternSelectFunction implements PatternSelectFunction<Tuple2<String, Integer>, String> {@Overridepublic String select(Map<String, List<Tuple2<String, Integer>>> pattern) throws Exception {StringBuilder builder = new StringBuilder();builder.append("找到匹配项: ");pattern.forEach((key, value) -> builder.append(key).append(" => ").append(value).append("; "));return builder.toString();}}

使用:

DataStream<String> result = patternStream.select(new MyPatternSelectFunction());

应用示例

模拟查找匹配5秒钟内连续登录失败在3次以上的用户

自定义消息事件

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginEvent {/*** 用户id*/private Integer uid;/*** 是否登录成功*/private Boolean success;/*** 时间戳*/private Long timeStamp; 
}

自定义Pattern

 public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);DataStream<LoginEvent> streamSource = env.fromElements(new LoginEvent(1, false, 1000L),new LoginEvent(2, true, 2000L),new LoginEvent(3, true, 3000L),new LoginEvent(1, false, 4000L),new LoginEvent(1, false, 5000L),new LoginEvent(4, false, 5000L)).assignTimestampsAndWatermarks(WatermarkStrategy.<LoginEvent>forMonotonousTimestamps().withTimestampAssigner(new SerializableTimestampAssigner<LoginEvent>() {@Overridepublic long extractTimestamp(LoginEvent loginEvent, long l) {return loginEvent.getTimeStamp();}})).keyBy(r -> r.getUid());Pattern<LoginEvent, LoginEvent> pattern = Pattern.<LoginEvent>begin("first").where(new SimpleCondition<LoginEvent>() {@Overridepublic boolean filter(LoginEvent loginEvent) throws Exception {return !loginEvent.getSuccess();}}).next("second").where(new SimpleCondition<LoginEvent>() {@Overridepublic boolean filter(LoginEvent loginEvent) throws Exception {return !loginEvent.getSuccess();}}).next("third").where(new SimpleCondition<LoginEvent>() {@Overridepublic boolean filter(LoginEvent loginEvent) throws Exception {return !loginEvent.getSuccess();}}).within(Time.seconds(5));PatternStream<LoginEvent> patternedStream = CEP.pattern(streamSource, pattern);patternedStream.select(new PatternSelectFunction<LoginEvent, String>() {@Overridepublic String select(Map<String, List<LoginEvent>> map) throws Exception {LoginEvent first = map.get("first").iterator().next();LoginEvent second = map.get("second").iterator().next();LoginEvent third = map.get("third").iterator().next();return String.format("uid:%d 连续3次登录失败,登录时间: first:%d, second:%d, third:%d", first.getUid(), first.getTimeStamp(), second.getTimeStamp(), third.getTimeStamp());}}).print();env.execute();}

测试

uid:1 5秒钟内连续3次登录失败,登录时间: first:1000, second:4000, third:5000

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu1804安装jupyter中的js环境

参考&#xff1a; https://github.com/n-riesco/ijavascript http://n-riesco.github.io/ijavascript/#installation https://github.com/n-riesco/ijavascript/issues/241 https://www.tomche.space/post/using-javascript-kernel-in-vscode-jupyter-notebooks/ https://w…

淘宝商品详情接口在电商运营中的应用实例

一、背景 某电商企业A在运营过程中&#xff0c;发现手动更新商品信息效率低下&#xff0c;且容易出现信息不一致的情况。为了解决这个问题&#xff0c;企业A决定采用淘宝商品详情接口&#xff0c;实现商品信息的自动获取和更新。 二、目标 通过集成淘宝商品详情接口&#xf…

HNU-计算机网络-讨论课2

第二次 有关网络智能、安全以及未来网络的讨论 一、必选问题&#xff08;每组自由选择N个&#xff0c;保证组内每人负责1个&#xff09; 网络的发展促进信息的传播&#xff0c;极大提高了人类的感知能力&#xff0c;整个世界都被纳入人类的感知范围。但人们对信息系统以及数据…

手持式安卓主板_PDA安卓板_智能手持终端方案

手持式安卓主板方案是一种智能终端设备&#xff0c;具备自动对焦和闪光灯功能&#xff0c;可以在昏暗的环境下快速扫描二维码并轻松采集数据。该方案还提供多渠道支付和数据采集功能&#xff0c;为用户提供了便捷的体验。 该方案的产品基于手持式安卓主板&#xff0c;并搭载了八…

海南省某部队实现资产管理和IP地址管理

在快速发展的网络环境中&#xff0c;如何有效管理资产和IP地址已成为众多组织面临的挑战。海南省某部队&#xff0c;作为一个肩负重要使命的单位&#xff0c;对此有着更为迫切的需求。为了应对这一挑战&#xff0c;他们选择了一个备受赞誉的系统管理平台——监控易&#xff0c;…

利用jQuery实现AJAX定时刷新局部页面实例

通过两种方法实例讲解ajax定时刷新局部页面&#xff0c;当然方法有很多种&#xff0c;也可以不使用ajax来刷新页面&#xff0c;可以使用jquery中的append来给指定内容加东西&#xff0c;但是都不太实用&#xff0c;最实用的方法还是ajax加载数据了。 方法一&#xff1a; 局部刷…

JAVA优化: MD5 加密的工具类

MD5 加密的工具类&#xff0c;提供了对字符串进行 MD5 加密的功能。主要包括以下几个方法&#xff1a; md5(String s)&#xff1a;对传入的字符串进行 MD5 加密&#xff0c;并返回加密后的字节数组。toHex(byte hash[])&#xff1a;将字节数组转换为十六进制字符串表示。hash(…

ApplicationRunner 类

优质博文&#xff1a;IT-BLOG-CN 在开发中可能会有这样的情景。需要在容器启动的时候执行一些内容。比如读取配置文件&#xff0c;数据库连接之类的。SpringBoot给我们提供了两个接口来帮助我们实现这种需求。这两个接口分别为CommandLineRunner和ApplicationRunner。他们的执…

JavaScript 代码整洁技巧

为什么代码要整洁&#xff1f; 代码质量与整洁度成正比。有的团队在赶工期的时候&#xff0c;不注重代码的整洁&#xff0c;代码写的越来越糟糕&#xff0c;项目越来越混乱&#xff0c;生产力也跟着下降&#xff0c;那就必须找更多人来提高生产力&#xff0c;开发成本越来越高…

C/C++,图算法——求强联通的Tarjan算法之源程序

1 文本格式 #include <bits/stdc.h> using namespace std; const int maxn 1e4 5; const int maxk 5005; int n, k; int id[maxn][5]; char s[maxn][5][5], ans[maxk]; bool vis[maxn]; struct Edge { int v, nxt; } e[maxn * 100]; int head[maxn], tot 1; vo…

Unity UGUI控件之Horizontal Layout Group

Horizontal Layout Group是Unity中的UGUI控件&#xff0c;用于在水平方向上对子对象进行布局。 主要有一下作用&#xff1a; 水平布局&#xff1a;Horizontal Layout Group将子对象按照水平方向进行布局&#xff0c;可以控制子对象的排列顺序和间距。自动调整尺寸&#xff1a…

不到1000行代码,PyTorch团队让Llama 7B提速10倍

在过去的一年里&#xff0c;生成式 AI 发展迅猛&#xff0c;在这当中&#xff0c;文本生成一直是一个特别受欢迎的领域&#xff0c;很多开源项目如 llama.cpp、vLLM 、 MLC-LLM 等&#xff0c;为了取得更好的效果&#xff0c;都在进行不停的优化。 作为机器学习社区中最受欢迎框…

面试就是这么简单,offer拿到手软(四)—— 常见java152道基础面试题

面试就是这么简单&#xff0c;offer拿到手软&#xff08;一&#xff09;—— 常见非技术问题回答思路 面试就是这么简单&#xff0c;offer拿到手软&#xff08;二&#xff09;—— 常见65道非技术面试问题 面试就是这么简单&#xff0c;offer拿到手软&#xff08;三&#xff…

WIN10下解决HIVE 初始化MYSQL表报错:Unknown version specified for initialization

今天本地WINDOWS装HIVE&#xff0c;走到最后一步初始化数据库死活不通过&#xff1a; D:\hive\hive-rel-release-3.1.3\bin\ext>hive --service schematool -dbType mysql -initSchema --verbose SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found bind…

flask 上传文件

from flask import Flask, request, render_template,redirect, url_for from werkzeug.utils import secure_filename import os from flask import send_from_directory # send_from_directory可以从目录加载文件app Flask(__name__)#UPLOAD_FOLDER media # 注意&#xff…

深入理解强化学习——马尔可夫决策过程:占用度量-[代码实现]

分类目录&#xff1a;《深入理解强化学习》总目录 在文章《深入理解强化学习——马尔可夫决策过程&#xff1a;占用度量-[基础知识]》我们介绍了占用度量的基础知识&#xff0c;本文我们编写代码来近似估计占用度量。这里我们采用近似估计&#xff0c;即设置一个较大的采样轨迹…

会声会影2024购买多少钱 会声会影在哪里购买

掌握视频编辑技术&#xff0c;能为我们的工作和生活带来很多帮助。例如&#xff1a;将我们精心编辑的视频&#xff0c;上传到抖音、快手等平台进行变现&#xff1b;通过天马行空的视频创意&#xff0c;摇身一变成为B站up主。因此&#xff0c;拥有一款像会声会影这样的视频编辑软…

信号可靠性剖析

问题 基于信号发送的进程间通信方式可靠吗&#xff1f;&#xff1f;&#xff1f; 信号查看(kill -l) 信号的分类 不可靠信号 (传统信号) 信号值在 [1, 31] 之间的所有信号 可靠信号 (实时信号) 信号值在 [SIGRTMIN&#xff0c;SIGRTMAX]&#xff0c;即&#xff1a;[34&…

计算机组成原理学习-总线总结

复习本章时&#xff0c;思考以下问题&#xff1a; 1)引入总线结构有什么好处&#xff1f;2)引入总线结构会导致什么问题&#xff1f;如何解决&#xff1f;

Squid安装与配置(ip代理)

继前面一篇Tinyproxy安装与配置(ip代理)&#xff0c;在实际使用中会发现在请求一些网站时会被拒绝&#xff0c;那是因为Tinyproxy其实不支持所谓的高匿代理。所以这次用功能更加丰富的squid试试。 1、安装 yum install squid -y yum install httpd-tools -y2、配置 1、备份原…