Flink编程——风险欺诈检测
Flink 风险欺诈检测
文章目录
- Flink 风险欺诈检测
- 背景
- 准备条件
- FraudDetectionJob.java
- FraudDetector.java
- 代码分析
- 执行环境
- 创建数据源
- 对事件分区 & 欺诈检测
- 输出结果
- 运行作业
- 欺诈检测器
- 欺诈检测器 v1:状态
- 欺诈检测器 v2:状态 + 时间
- 完整的程序
- 期望的结果
Apache Flink 提供了 DataStream API 来实现稳定可靠的、有状态的流处理应用程序。 Flink 支持对状态和时间的细粒度控制,以此来实现复杂的事件驱动数据处理系统。 这个入门指导手册讲述了如何通过 Flink DataStream API 来实现一个有状态流处理程序。
背景
在当今数字时代,信用卡欺诈行为越来越被重视。 罪犯可以通过诈骗或者入侵安全级别较低系统来盗窃信用卡卡号。 用盗得的信用卡进行很小额度的例如一美元或者更小额度的消费进行测试。 如果测试消费成功,那么他们就会用这个信用卡进行大笔消费,来购买一些他们希望得到的,或者可以倒卖的财物。
在这个教程中,你将会建立一个针对可疑信用卡交易行为的反欺诈检测系统。 通过使用一组简单的规则,你将了解到 Flink 如何为我们实现复杂业务逻辑并实时执行。
准备条件
这个代码练习假定你对 Java 有一定的了解,当然,如果你之前使用的是其他开发语言,你也应该能够跟随本教程进行学习。
Flink提供了一个准备好的 Flink Maven Archetype 能够快速创建一个包含了必要依赖的 Flink 程序骨架,基于此,你可以把精力集中在编写业务逻辑上即可。 这些已包含的依赖包括 flink-streaming-java
、flink-walkthrough-common
等,他们分别是 Flink 应用程序的核心依赖项和这个代码练习需要的数据生成器
mvn archetype:generate \-DarchetypeGroupId=org.apache.flink \-DarchetypeArtifactId=flink-walkthrough-datastream-java \-DarchetypeVersion=1.18.0 \-DgroupId=frauddetection \-DartifactId=frauddetection \-Dversion=0.1 \-Dpackage=spendreport \-DinteractiveMode=false
可以在命令上里执行执行上述命令
‘
你可以根据自己的情况修改 groupId
、 artifactId
和 package
。通过这三个参数, Maven 将会创建一个名为 frauddetection
的文件夹,包含了所有依赖的整个工程项目将会位于该文件夹下。 将工程目录导入到你的开发环境之后,你可以找到 FraudDetectionJob.java
代码文件,文件中的代码如下所示。你可以在 IDE 中直接运行这个文件。 同时,你可以试着在数据流中设置一些断点或者以 DEBUG 模式来运行程序,体验 Flink 是如何运行的。
创建好之后可以在IDEA 中打开该项目尝试运行
在 IDE 中运行该项目可能会遇到 java.langNoClassDefFoundError
的异常。这很可能是因为运行所需要的 Flink 的依赖库没有默认被全部加载到类路径(classpath)里。
’
IntelliJ IDE:前往 运行 > 编辑配置 > 修改选项 > 选中 将带有 “provided” 范围的依赖项添加到类路径。这样的话,运行配置将会包含所有在 IDE 中运行所必须的类。
‘
FraudDetectionJob.java
package spendreport;import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.walkthrough.common.sink.AlertSink;
import org.apache.flink.walkthrough.common.entity.Alert;
import org.apache.flink.walkthrough.common.entity.Transaction;
import org.apache.flink.walkthrough.common.source.TransactionSource;public class FraudDetectionJob {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStream<Transaction> transactions = env.addSource(new TransactionSource()).name("transactions");DataStream<Alert> alerts = transactions.keyBy(Transaction::getAccountId).process(new FraudDetector()).name("fraud-detector");alerts.addSink(new AlertSink()).name("send-alerts");env.execute("Fraud Detection");}
}
FraudDetector.java
package spendreport;import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.util.Collector;
import org.apache.flink.walkthrough.common.entity.Alert;
import org.apache.flink.walkthrough.common.entity.Transaction;public class FraudDetector extends KeyedProcessFunction<Long, Transaction, Alert> {private static final long serialVersionUID = 1L;private static final double SMALL_AMOUNT = 1.00;private static final double LARGE_AMOUNT = 500.00;private static final long ONE_MINUTE = 60 * 1000;@Overridepublic void processElement(Transaction transaction,Context context,Collector<Alert> collector) throws Exception {Alert alert = new Alert();alert.setId(transaction.getAccountId());collector.collect(alert);}
}
代码分析
让我们一步步地来分析一下这两个代码文件。FraudDetectionJob
类定义了程序的数据流,而 FraudDetector
类定义了欺诈交易检测的业务逻辑。
下面我们开始讲解整个 Job 是如何组装到 FraudDetectionJob
类的 main
函数中的。
执行环境
第一行的 StreamExecutionEnvironment
用于设置你的执行环境。 任务执行环境用于定义任务的属性、创建数据源以及最终启动任务的执行。
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
创建数据源
数据源从外部系统例如 Apache Kafka、Rabbit MQ 或者 Apache Pulsar 接收数据,然后将数据送到 Flink 程序中。 这个代码练习使用的是一个能够无限循环生成信用卡模拟交易数据的数据源。 每条交易数据包括了信用卡 ID (accountId
),交易发生的时间 (timestamp
) 以及交易的金额(amount
)。 绑定到数据源上的 name
属性是为了调试方便,如果发生一些异常,我们能够通过它快速定位问题发生在哪里。
DataStream<Transaction> transactions = env.addSource(new TransactionSource()).name("transactions");
对事件分区 & 欺诈检测
transactions
这个数据流包含了大量的用户交易数据,需要被划分到多个并发上进行欺诈检测处理。由于欺诈行为的发生是基于某一个账户的,所以,必须要保证同一个账户的所有交易行为数据要被同一个并发的 task 进行处理。
为了保证同一个 task 处理同一个 key 的所有数据,你可以使用 DataStream#keyBy
对流进行分区。 process()
函数对流绑定了一个操作,这个操作将会对流上的每一个消息调用所定义好的函数。 通常,一个操作会紧跟着 keyBy
被调用,在这个例子中,这个操作是FraudDetector
,该操作是在一个 keyed context 上执行的。
DataStream<Alert> alerts = transactions.keyBy(Transaction::getAccountId).process(new FraudDetector()).name("fraud-detector");
输出结果
sink 会将 DataStream
写出到外部系统,例如 Apache Kafka、Cassandra 或者 AWS Kinesis 等。 AlertSink
使用 INFO 的日志级别打印每一个 Alert
的数据记录,而不是将其写入持久存储,以便你可以方便地查看结果。
alerts.addSink(new AlertSink())
运行作业
Flink 程序是懒加载的,并且只有在完全搭建好之后,才能够发布到集群上执行。 调用 StreamExecutionEnvironment#execute
时给任务传递一个任务名参数,就可以开始运行任务。
env.execute("Fraud Detection");
欺诈检测器
欺诈检查类 FraudDetector
是 KeyedProcessFunction
接口的一个实现。 他的方法 KeyedProcessFunction#processElement
将会在每个交易事件上被调用。 这个程序里边会对每笔交易发出警报,有人可能会说这做报过于保守了。
本教程的后续步骤将指导你对这个欺诈检测器进行更有意义的业务逻辑扩展。
public class FraudDetector extends KeyedProcessFunction<Long, Transaction, Alert> {private static final double SMALL_AMOUNT = 1.00;private static final double LARGE_AMOUNT = 500.00;private static final long ONE_MINUTE = 60 * 1000;@Overridepublic void processElement(Transaction transaction,Context context,Collector<Alert> collector) throws Exception {Alert alert = new Alert();alert.setId(transaction.getAccountId());collector.collect(alert);}
}
这里我们的逻辑非常简单,其实就是直接传递给下游了,也就是每个数据我们都无区别的做了告警
欺诈检测器 v1:状态
我们先实现第一版报警程序,对于一个账户,如果出现小于 $1 美元的交易后紧跟着一个大于 $500 的交易,就输出一个报警信息。
假设你的欺诈检测器所处理的交易数据如下:
交易 3 和交易 4 应该被标记为欺诈行为,因为交易 3 是一个 $0.09 的小额交易,而紧随着的交易 4 是一个 $510 的大额交易。 另外,交易 7、8 和 交易 9 就不属于欺诈交易了,因为在交易 7 这个 $0.02 的小额交易之后,并没有跟随一个大额交易,而是一个金额适中的交易,这使得交易 7 到 交易 9 不属于欺诈行为。
欺诈检测器需要在多个交易事件之间记住一些信息。仅当一个大额的交易紧随一个小额交易的情况发生时,这个大额交易才被认为是欺诈交易。 在多个事件之间存储信息就需要使用到 状态,这也是我们选择使用 KeyedProcessFunction 的原因。 它能够同时提供对状态和时间的细粒度操作,这使得我们能够在接下来的代码练习中实现更复杂的算法。
最直接的实现方式是使用一个 boolean 型的标记状态来表示是否刚处理过一个小额交易。 当处理到该账户的一个大额交易时,你只需要检查这个标记状态来确认上一个交易是是否小额交易即可。
然而,仅使用一个标记作为 FraudDetector
的类成员来记录账户的上一个交易状态是不准确的。 Flink 会在同一个 FraudDetector
的并发实例中处理多个账户的交易数据,假设,当账户 A 和账户 B 的数据被分发的同一个并发实例上处理时,账户 A 的小额交易行为可能会将标记状态设置为真,随后账户 B 的大额交易可能会被误判为欺诈交易。 当然,我们可以使用如 Map
这样的数据结构来保存每一个账户的状态,但是常规的类成员变量是无法做到容错处理的,当任务失败重启后,之前的状态信息将会丢失。 这样的话,如果程序曾出现过失败重启的情况,将会漏掉一些欺诈报警。
为了应对这个问题,Flink 提供了一套支持容错状态的原语,这些原语几乎与常规成员变量一样易于使用。
Flink 中最基础的状态类型是 ValueState,这是一种能够为被其封装的变量添加容错能力的类型。 ValueState
是一种 keyed state,也就是说它只能被用于 keyed context 提供的 operator 中,即所有能够紧随 DataStream#keyBy
之后被调用的operator。 一个 operator 中的 keyed state 的作用域默认是属于它所属的 key 的。 这个例子中,key 就是当前正在处理的交易行为所属的信用卡账户(key 传入 keyBy() 函数调用),而 FraudDetector
维护了每个帐户的标记状态。 ValueState
需要使用 ValueStateDescriptor
来创建,ValueStateDescriptor
包含了 Flink 如何管理变量的一些元数据信息。状态在使用之前需要先被注册。 状态需要使用 open()
函数来注册状态。
public class FraudDetector extends KeyedProcessFunction<Long, Transaction, Alert> {private static final long serialVersionUID = 1L;private transient ValueState<Boolean> flagState;@Overridepublic void open(Configuration parameters) {ValueStateDescriptor<Boolean> flagDescriptor = new ValueStateDescriptor<>("flag",Types.BOOLEAN);flagState = getRuntimeContext().getState(flagDescriptor);}
ValueState
是一个包装类,类似于 Java 标准库里边的 AtomicReference
和 AtomicLong
。 它提供了三个用于交互的方法。update
用于更新状态,value
用于获取状态值,还有 clear
用于清空状态。 如果一个 key 还没有状态,例如当程序刚启动或者调用过 ValueState#clear
方法时,ValueState#value
将会返回 null
。 如果需要更新状态,需要调用 ValueState#update
方法,直接更改 ValueState#value
的返回值可能不会被系统识别。 容错处理将在 Flink 后台自动管理,你可以像与常规变量那样与状态变量进行交互。
下边的示例,说明了如何使用标记状态来追踪可能的欺诈交易行为。
@Override
public void processElement(Transaction transaction,Context context,Collector<Alert> collector) throws Exception {// Get the current state for the current keyBoolean lastTransactionWasSmall = flagState.value();// Check if the flag is setif (lastTransactionWasSmall != null) {if (transaction.getAmount() > LARGE_AMOUNT) {// Output an alert downstreamAlert alert = new Alert();alert.setId(transaction.getAccountId());collector.collect(alert);}// Clean up our stateflagState.clear();}if (transaction.getAmount() < SMALL_AMOUNT) {// Set the flag to trueflagState.update(true);}
}
对于每笔交易,欺诈检测器都会检查该帐户的标记状态。 请记住,ValueState
的作用域始终限于当前的 key,即信用卡帐户。 如果标记状态不为空,则该帐户的上一笔交易是小额的,因此,如果当前这笔交易的金额很大,那么检测程序将输出报警信息。
在检查之后,不论是什么状态,都需要被清空。 不管是当前交易触发了欺诈报警而造成模式的结束,还是当前交易没有触发报警而造成模式的中断,都需要重新开始新的模式检测。
最后,检查当前交易的金额是否属于小额交易。 如果是,那么需要设置标记状态,以便可以在下一个事件中对其进行检查。 注意,ValueState<Boolean>
实际上有 3 种状态:unset (null
),true
,和 false
,ValueState
是允许空值的。 我们的程序只使用了 unset (null
) 和 true
两种来判断标记状态被设置了与否。
欺诈检测器 v2:状态 + 时间
骗子们在小额交易后不会等很久就进行大额消费,这样可以降低小额测试交易被发现的几率。 比如,假设你为欺诈检测器设置了一分钟的超时,对于上边的例子,交易 3 和 交易 4 只有间隔在一分钟之内才被认为是欺诈交易。 Flink 中的 KeyedProcessFunction
允许您设置计时器,该计时器在将来的某个时间点执行回调函数。
让我们看看如何修改程序以符合我们的新要求:
- 当标记状态被设置为
true
时,设置一个在当前时间一分钟后触发的定时器。 - 当定时器被触发时,重置标记状态。
- 当标记状态被重置时,删除定时器。
要删除一个定时器,你需要记录这个定时器的触发时间,这同样需要状态来实现,所以你需要在标记状态后也创建一个记录定时器时间的状态。
Java
private transient ValueState<Boolean> flagState;
private transient ValueState<Long> timerState;@Override
public void open(Configuration parameters) {ValueStateDescriptor<Boolean> flagDescriptor = new ValueStateDescriptor<>("flag",Types.BOOLEAN);flagState = getRuntimeContext().getState(flagDescriptor);ValueStateDescriptor<Long> timerDescriptor = new ValueStateDescriptor<>("timer-state",Types.LONG);timerState = getRuntimeContext().getState(timerDescriptor);
}
KeyedProcessFunction#processElement
需要使用提供了定时器服务的 Context
来调用。 定时器服务可以用于查询当前时间、注册定时器和删除定时器。 使用它,你可以在标记状态被设置时,也设置一个当前时间一分钟后触发的定时器,同时,将触发时间保存到 timerState
状态中。
if (transaction.getAmount() < SMALL_AMOUNT) {// set the flag to trueflagState.update(true);// set the timer and timer statelong timer = context.timerService().currentProcessingTime() + ONE_MINUTE;context.timerService().registerProcessingTimeTimer(timer);timerState.update(timer);
}
处理时间是本地时钟时间,这是由运行任务的服务器的系统时间来决定的。
当定时器触发时,将会调用 KeyedProcessFunction#onTimer
方法。 通过重写这个方法来实现一个你自己的重置状态的回调逻辑。
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<Alert> out) {// remove flag after 1 minutetimerState.clear();flagState.clear();
}
最后,如果要取消定时器,你需要删除已经注册的定时器,并同时清空保存定时器的状态。 你可以把这些逻辑封装到一个助手函数中,而不是直接调用 flagState.clear()
。
private void cleanUp(Context ctx) throws Exception {// delete timerLong timer = timerState.value();ctx.timerService().deleteProcessingTimeTimer(timer);// clean up all statetimerState.clear();flagState.clear();
}
这就是一个功能完备的,有状态的分布式流处理程序了。
完整的程序
package spendreport;import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.util.Collector;
import org.apache.flink.walkthrough.common.entity.Alert;
import org.apache.flink.walkthrough.common.entity.Transaction;public class FraudDetector extends KeyedProcessFunction<Long, Transaction, Alert> {private static final long serialVersionUID = 1L;private static final double SMALL_AMOUNT = 1.00;private static final double LARGE_AMOUNT = 500.00;private static final long ONE_MINUTE = 60 * 1000;private transient ValueState<Boolean> flagState;private transient ValueState<Long> timerState;@Overridepublic void open(Configuration parameters) {ValueStateDescriptor<Boolean> flagDescriptor = new ValueStateDescriptor<>("flag",Types.BOOLEAN);flagState = getRuntimeContext().getState(flagDescriptor);ValueStateDescriptor<Long> timerDescriptor = new ValueStateDescriptor<>("timer-state",Types.LONG);timerState = getRuntimeContext().getState(timerDescriptor);}@Overridepublic void processElement(Transaction transaction,Context context,Collector<Alert> collector) throws Exception {// Get the current state for the current keyBoolean lastTransactionWasSmall = flagState.value();// Check if the flag is setif (lastTransactionWasSmall != null) {if (transaction.getAmount() > LARGE_AMOUNT) {//Output an alert downstreamAlert alert = new Alert();alert.setId(transaction.getAccountId());collector.collect(alert);}// Clean up our statecleanUp(context);}if (transaction.getAmount() < SMALL_AMOUNT) {// set the flag to trueflagState.update(true);long timer = context.timerService().currentProcessingTime() + ONE_MINUTE;context.timerService().registerProcessingTimeTimer(timer);timerState.update(timer);}}@Overridepublic void onTimer(long timestamp, OnTimerContext ctx, Collector<Alert> out) {// remove flag after 1 minutetimerState.clear();flagState.clear();}private void cleanUp(Context ctx) throws Exception {// delete timerLong timer = timerState.value();ctx.timerService().deleteProcessingTimeTimer(timer);// clean up all statetimerState.clear();flagState.clear();}
}
期望的结果
使用已准备好的 TransactionSource
数据源运行这个代码,将会检测到账户 3 的欺诈行为,并输出报警信息。 你将能够在你的 task manager 的日志中看到下边输出:
2019-08-19 14:22:06,220 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
2019-08-19 14:22:11,383 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
2019-08-19 14:22:16,551 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
2019-08-19 14:22:21,723 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
2019-08-19 14:22:26,896 INFO org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
相关文章:

Flink编程——风险欺诈检测
Flink 风险欺诈检测 文章目录 Flink 风险欺诈检测背景准备条件FraudDetectionJob.javaFraudDetector.java 代码分析执行环境创建数据源对事件分区 & 欺诈检测输出结果运行作业欺诈检测器 欺诈检测器 v1:状态欺诈检测器 v2:状态 时间完整的程序期望的…...
Day37 贪心算法 part06 738. 单调递增的数字 968. 监控二叉树
贪心算法 part06 738. 单调递增的数字 968. 监控二叉树 738. 单调递增的数字 class Solution { public:int monotoneIncreasingDigits(int n) {string strNum to_string(n);int tag strNum.size();for(int i strNum.size()-1; i>1; i--){if(strNum[i]<strNum[i-1]){…...

SpringBoot Redis入门(四)——Redis单机、哨兵、集群模式
单机模式:单台缓存服务器,开发、测试环境下使用;哨兵模式:主-从模式,提高缓存服务器的高可用和安全性。所有缓存的数据在每个节点上都一致。每个节点添加监听器,不断监听节点可用状态,一旦主节点…...
获取数组中的第一个、第二个、第三个......元素
常规操作可以直接使用索引(下标)获取: const arr [5,8,6,9,10] const first arr[0] //5 const second arr[1] //8 const third arr[2] //6 不使用索引,如何获取: const [first] [5,8,6,9,10] //…...

前端面试题(持续更新~~)
文章目录 一、基础1、数组常用的方法2、数组有哪几种循环方式?分别有什么作用?3、字符串常用的方法4、原型链5、闭包6、常见的继承7、cookie 、localstorage 、 sessionstrorage区别8、数组去重方法9、http 的请求方式10、数据类型的判断方法11、cookie …...
ubuntu下无法访问和ping通github的一种解决方法
近期在ubuntu下突然无法访问github了,ping也无法ping通,尝试过更换不同的网络也无济于事。后来在https://blog.csdn.net/weixin_48544978/article/details/133899687 这个文章中找到了解决办法。 运气比较好,只按照文章中的第一步将http://…...

C#,入门教程(28)——文件夹(目录)、文件读(Read)与写(Write)的基础知识
上一篇: C#,入门教程(27)——应用程序(Application)的基础知识https://blog.csdn.net/beijinghorn/article/details/125094837 C#知识比你的预期简单的多,但也远远超乎你的想象! 与文件相关的知识…...

开源大数据集群部署(六)Keytab文件生成
作者:櫰木 Keytab文件用于在不输入密码的情况下对主体(用户或服务)进行身份验证。以下是创建Kerberos身份验证的步骤。 1、创建keytab文件 除了使用明文密码登录之外,Kerberos还可以使用keytab密码文件登陆,现在为te…...

图神经网络X项目|基于图神经网络的电商行为的预测(5%)
文章目录 Jupyter Notebook 学习人工智能的好帮手数据集数据集下载数据集调用数据集应用技巧——获取不重复的编号数据集应用技巧——随机采样数据集应用技巧——抽取前N项进行模拟测试 数据集构建技巧一——查看数据集构建进度 Jupyter Notebook 学习人工智能的好帮手 【Jupy…...

仰暮计划|“说是操场,那就是个土坡,我们在那儿上边种种树啊,拔拔草,有的时候还会有同学来喂喂羊啥的,这都是我们的娱乐”
我是1948年农历二月份在河南省许昌市五女店镇的一个乡村里边出生的。从我记事的时候,中华人民共和国就已经成立了。当时是好多年,经历了三大改造呀、生产队呀、大队呀,乱七八糟的很多,估计你们现在这些孩子们啊,都没有…...
Java【代码 16】将word、excel文件转换为pdf格式和将pdf文档转换为image格式工具类分享
1.感谢 感谢小伙伴儿的分享: ● 不羁 ● 郭中天 整合调整后的工具类Gitee地址:https://gitee.com/yuanzhengme/java_application_aspose_demo 2.包含的工具类 ● WordToPdfUtil用于将word文档转换为pdf格式的工具类 ● ExcelToPdfUtil用于将excel文档…...

8亿日活的抖音,用“自我设限”谋求长期主义
文|新熔财经 作者|寒蝉鸣 随着手机近乎全民化的普及,在互联网上“冲浪”的人是越来越多了。 根据QuestMobile发布的《中国互联网核心趋势年度报告(2023)》,2023年,中国移动互联网月活跃用户规…...

Final Cut Pro v10.7.1中文版 专业级视频剪辑软件 兼容M
Final Cut Pro 是 macOS平台上最好的视频剪辑软件,基于Cocoa编写,支持多路多核心处理器,支持GPU加速,支持后台渲染,可编辑从标清到4K的各种分辨率视频,ColorSync管理的色彩流水线则可保证全片色彩的一致性。…...
Chrome扩展之通信
Chrome扩展通信 chrome扩展的5种js js类型介绍popup单击插件图标后的弹窗中的js,由于单击图标打开popup,焦点离开又立即关闭,生命周期一般很短。content-script与页面共享DOM,但是不共享JS,可访问部分chrome扩展API。…...

Appium 环境配置
Appium 是一个开源的、跨平台的测试框架,可以用来测试 Native App、混合应用、移动 Web 应用(H5 应用)等,也是当下互联网企业实现移动自动化测试的重要工具。Appium 坚持的测试理念: •无需用户对 App 进行任何修改或…...
前端JavaScript篇之Javscript数组的常用方法有哪些?JavaScript判断数组的方式有哪些?
目录 Javscript数组的常用方法有哪些?JavaScript判断数组的方式有哪些?1. Array.isArray() 方法:2. instanceof 操作符:3. Object.prototype.toString.call() 方法:4. Array.from() 方法: Javscript数组的常…...

【Spring Boot 3】【Redis】基本数据类型操作
【Spring Boot 3】【Redis】基本数据类型操作 背景介绍开发环境开发步骤及源码工程目录结构 背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工…...

[MySQL]关于表的增删改查
目录 1.插入 1.1单行数据全列插入 1.2多行插入,指定列插入 编辑2.查询 2.1全列查询 2.2指定列查询 3.3查询字段为表达式 2.4别名 编辑2.5去重 2.6排序 2.7条件查询 2.7.1基本查询: 2.7.2 AND 和OR 2.7.3范围查询 2.7.4模糊查询 2.7.5分页查询 limit …...

编译和链接(翻译环境:预编译+编译+汇编+链接、运行环境)
一、翻译环境和运行环境 在ANSI C的任何一种实现中,存在两个不同的环境。 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境,它用于实际执行代码。 VS中编译器:cl.exe ;Linux中…...

洛谷 P1364 医院设置
题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...