Flink的双流join理解
如何保证Flink双流Join准确性和及时性、除了窗口join还存在哪些实现方式、究竟如何回答才能完全打动面试官呢。。你将在文中找到答案。
1 引子
1.1 数据库SQL中的JOIN
我们先来看看数据库SQL中的JOIN操作。如下所示的订单查询SQL,通过将订单表的id和订单详情表order_id关联,获取所有订单下的商品信息。
select a.id as '订单id',a.order_date as '下单时间',a.order_amount as '订单金额',b.order_detail_id as '订单详情id',b.goods_name as '商品名称',b.goods_price as '商品价格',b.order_id as '订单id'
from dwd_order_info_pfd a
right join dwd_order_detail_pfd b
on a.id = b.order_id
这是一段很简单的SQL代码,就不详细展开叙述了。此处主要引出SQL中的JOIN类型,这里用到的是 right join , 即右连接。
- left join: 保留左表全部数据和右表关联数据,右表非关联数据置NULL
- right join: 保留右表全部数据和左表关联数据,左表非关联数据置NULL
- inner join: 保留左表关联数据和右边关联数据
- cross join: 保留左表和右表数据笛卡尔积
基于关联键值逐行关联匹配,过滤表数据并生成最终结果,提供给下游数据分析使用。
就此打住,关于数据库SQL中的JOIN原理不再多赘述,感兴趣的话大家可自行研究,下面我们将目光转移到大数据领域看看吧。
1.2 离线场景下的JOIN
假设存在这样一个场景:
已知Mysql数据库中订单表和订单明细表,且满足一对多的关系,统计T-1天所有订单的商品分布详情。
聪明的大家肯定已经给出了答案,没错~就是上面的SQL:
select a.*, b.*
from dwd_order_info_pfd a
right join dwd_order_detail_pfd b
on a.id = b.order_id
现在修改下条件:已知订单表和订单明细表均为亿级别数据,求相同场景下的分析结果。
咋办?此时关系型数据库貌似不大合适了~开始放大招:使用大数据计算引擎来解决。
考虑到T-1统计场景对时效性要求很低,可以使用Hive SQL来处理,底层跑Mapreduce任务。如果想提高运行速度,换成Flink或Spark计算引擎,使用内存计算。
至于查询SQL和上面一样,并将其封装成一个定时调度任务, 等系统调度运行。如果结果不正确的话,由于数据源和数据静态不变,大不了重跑,看起来感觉皆大欢喜~
可是好景不长,产品冤家此时又给了你一个无法拒绝的需求:我要实时统计!!
2 实时场景下的JOIN
还是上面的场景,此时数据源换成了实时订单流和实时订单明细流,比如Kafka的两个topic,要求实时统计每分钟内所有订单下的商品分布详情。
现在情况貌似变得复杂了起来,简单分析下:
- 数据源。实时数据流,和静态流不同,数据是实时流入的且动态变化,需要计算程序支持实时处理机制。
- 关联性。前面提到静态数据执行多次join操作,左表和右表能关联的数据是很恒定的;而实时数据流(左右表)如果进入时机不一致,原本可以关联的数据会关联不上或者发生错误。
- 延迟性。实时统计,提供分钟甚至秒级别响应结果。
由于流数据join的特殊性,在满足实时处理机制、低延迟、强关联性的前提下,看来需要制定完善的数据方案,才能实现真正的流数据JOIN。
2.1 方案思路
我们知道订单数据和订单明细数据是一对多的关系,即一条订单数据对应着多条商品明细数据,毕竟买一件商品也是那么多邮费,不如打包团购。。而一条明细数据仅对应一条订单数据。
这样,双流join策略可以考虑如下思路:
- 当数据流为订单数据时。无条件保留,无论当前是否关联到明细数据,均留作后续join使用。
- 当数据流为明细数据时。在关联到其订单数据后,就可以say goodbye了,否则暂时保留等待下一次与订单数据的邂逅。
- 完成所有处于同一时段内的订单数据和订单明细数据join, 清空存储状态
实际生产场景中,需要考虑更多的复杂情况,包括JOIN过程的数据丢失等异常情况的处理,此处仅示意。
好了,看起来我们已经有了一个马马虎虎的实时流JOIN方案雏形。
貌似可以准备动手大干一场了~ 别着急,有人已经帮我们偷偷的实现了:Apache Flink
3 Flink的双流JOIN
Apache Flink 是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。Flink 被设计在所有常见的集群环境中运行,以内存执行速度和任意规模来执行计算。
——来自Flink官网定义
这里我们只需要知道Flink是一个实时计算引擎就行了,主要关注其如何实现双流JOIN。
3.1 内部运行机制
- 内存计算:Flink任务优先在内存中计算,内存不够时保存到访问高效的磁盘上,提供秒级延迟响应。
- 状态强一致性:Flink使用一致性快照保存状态,并定期检查本地状态到持久存储来保证状态一致性。
- 分布式执行:Flink应用程序可以划分为无数个并行任务在集群中执行,几乎无限量使用CPU、主内存、磁盘和网络IO。
- 内置高级编程模型:Flink编程模型抽象为SQL、Table、DataStream|DataSet API、Process四层,并封装成丰富功能的算子,其中就包含JOIN类型的算子。
仔细看看,我们前面章节讨论的实时流JOIN方案的前提是否都满足了呢?
- 实时处理机制: Flink天生即实时计算引擎
- 低延迟: Flink内存计算秒级延迟
- 强关联性: Flink状态一致性和join类算子
不由感叹, 这个Flink果然强啊~
保持好奇心,我们去瞅瞅Flink双流join的真正奥义!!
3.2 JOIN实现机制
Flink双流JOIN主要分为两大类。一类是基于原生State的Connect算子操作,另一类是基于窗口的JOIN操作。其中基于窗口的JOIN可细分为window join和interval join两种。
- 实现原理:底层原理依赖Flink的State状态存储,通过将数据存储到State中进行关联join, 最终输出结果。
恍然大悟, Flink原来是通过State状态来缓存等待join的实时流。
4 基于Window Join的双流JOIN实现机制
顾名思义,此类方式利用Flink的窗口机制实现双流join。通俗理解,将两条实时流中元素分配到同一个时间窗口内完成Join。
- 底层原理: 两条实时流数据缓存在Window State中,当窗口触发计算时,执行join操作。
4.1 join算子
先看看Window join实现方式之一的join算子。这里涉及到Flink中的窗口(window)概念,因此Window Join按照窗口类型区分的话某种程度来说可以细分出3种:
- Tumbling Window Join (滚动窗口)
- Sliding Window Join (滑动窗口)
- Session Widnow Join(会话窗口)
两条流数据按照关联主键在(滚动、滑动、会话)窗口内进行inner join, 底层基于State存储,并支持处理时间和事件时间两种时间特征,看下源码:
源码核心总结:windows窗口 + state存储 + 双层for循环执行join()
现在让我们把时间轴往回拉一点点,在实时场景JOIN那里我们收到了这样的需求:统计每分钟内所有订单下的商品明细分布。
OK, 使用join算子小试牛刀一下。我们定义60秒的滚动窗口,将订单流和订单明细流通过order_id关联,得到如下的程序:
val env = ...
// kafka 订单流
val orderStream = ...
// kafka 订单明细流
val orderDetailStream = ...orderStream.join(orderDetailStream).where(r => r._1) //订单id.equalTo(r => r._2) //订单id.window(TumblingProcessTimeWindows.of(Time.seconds(60))).apply {(r1, r2) => r1 + " : " + r2}.print()
整个代码其实很简单,概要总结下:
- 定义两条输入实时流A、B
- A流调用join(b流)算子
- 关联关系定义: where为A流关联键,equalTo为B流关联键,都是订单id
- 定义window窗口(60s间隔)
- apply方法定义逻辑输出
这样只要程序稳定运行,就能够持续不断的计算每分钟内订单分布详情,貌似解决问题了奥~
还是别高兴太早,别忘了此时的join类型是inner join。复习一下知识:inner join指的是仅保留两条流关联上的数据。
这样双流中没关联上的数据岂不是都丢掉了?别担心,Flink还提供了另一个window join操作: coGroup算子。
4.2 coGroup算子
coGroup算子也是基于window窗口机制,不过coGroup算子比Join算子更加灵活,可以按照用户指定的逻辑匹配左流或右流数据并输出。
换句话说,我们通过自己指定双流的输出来达到left join和right join的目的。
现在来看看在相同场景下coGroup算子是如何实现left join:
#这里看看java的写法
orderDetailStream.coGroup(orderStream).where(r -> r.getOrderId()).equalTo(r -> r.getOrderId()).window(TumblingProcessingTimeWindows.of(Time.seconds(60))).apply(new CoGroupFunction<OrderDetail, Order, Tuple2<String, Long>>() {@Overridepublic void coGroup(Iterable<OrderDetail> orderDetailRecords, Iterable<Order> orderRecords, Collector<Tuple2<String, Long>> collector) {for (OrderDetail orderDetaill : orderDetailRecords) {boolean flag = false;for (Order orderRecord : orderRecords) {// 右流中有对应的记录collector.collect(new Tuple2<>(orderDetailRecords.getGoods_name(), orderDetailRecords.getGoods_price()));flag = true;}if (!flag) {// 右流中没有对应的记录collector.collect(new Tuple2<>(orderDetailRecords.getGoods_name(), null));}}}}).print();
这里需要说明几点:
- join算子替换为coGroup算子
- 两条流依然需要在一个window中且定义好关联条件
- apply方法中自定义判断,此处对右值进行判断:如果有值则进行连接输出,否则右边置为NULL。
可以这么说,现在我们已经彻底搞定了窗口双流JOIN。
只要你给我提供具体的窗口大小,我就能通过join或coGroup算子鼓捣出各种花样join,而且使用起来特别简单。
但是假如此时我们亲爱的产品又提出了一个小小条件:
大促高峰期,商品数据某时段会写入不及时,时间可能比订单早也可能比订单晚,同样计算每分钟内的订单商品分布详情,没问题吧~
当然有问题:两条流如果步调不一致,还用窗口来控制能join的上才怪了~ 很容易等不到join流窗口就自动关闭了。
还好,我知道Flink提供了Interval join
机制。
5 基于Interval Join的双流JOIN实现机制
Interval Join根据右流相对左流偏移的时间区间(interval)作为关联窗口,在偏移区间窗口中完成join操作。
有点不好理解,我画个图看下:
stream2.time ∈ (stream1.time +low, stream1.time +high)
满足数据流stream2在数据流stream1的 interval(low, high)偏移区间内关联join。interval越大,关联上的数据就越多,超出interval的数据不再关联。
- 实现原理:interval join也是利用Flink的state存储数据,不过此时存在state失效机制ttl,触发数据清理操作。
这里再引出一个问题:
state的ttl机制需要怎么设置?不合理的ttl设置会不会撑爆内存?
下面简单看下interval join的代码实现过程:
val env = ...
// kafka 订单流
val orderStream = ...
// kafka 订单明细流
val orderDetailStream = ...orderStream.keyBy(_.1)// 调用intervalJoin关联.intervalJoin(orderDetailStream._2)// 设定时间上限和下限.between(Time.milliseconds(-30), Time.milliseconds(30)) .process(new ProcessWindowFunction())class ProcessWindowFunction extends ProcessJoinFunction...{override def processElement(...) {collector.collect((r1, r2) => r1 + " : " + r2)}
}
订单流在流入程序后,等候(low,high)时间间隔内的订单明细流数据进行join, 否则继续处理下一个流。
从代码中我们发现,interval join需要在两个KeyedStream
之上操作,即keyBy(),并在between()方法中指定偏移区间的上下界。
需要注意的是interval join实现的也是inner join
,且目前只支持事件时间。
6 基于Connect的双流JOIN实现机制
前面在使用Window join或者Interval Join来实现双流join的时候,我发现了其中的共性:
无论哪种实现方式,Flink内部都将join过程透明化,在算子中封装了所有的实现细节。
无论哪种实现方式,Flink内部都将join过程透明化,在算子中封装了所有的实现细节。
可是这样会引来一个问题:如果程序报错或者数据异常,如何快速进行调优排查,直接看源码吗?不大现实。。
这里介绍基于Connect算子实现的双流JOIN方法,我们可自己控制双流JOIN处理逻辑,同时保持过程时效性和准确性。
6.1 Connect算子原理
对两个DataStream执行connect操作,将其转化为ConnectedStreams, 生成的Streams可以调用不同方法在两个实时流上执行,且双流之间可以共享状态。
图上我们可以看到,两个数据流被connect之后,只是被放在了同一个流中,内部依然保持各自的数据和形式,两个流相互独立。
[DataStream1, DataStream2] -> ConnectedStreams[1,2]
这样,我们可以在Connect算子底层的ConnectedStreams基础上编写代码,自行实现双流JOIN的逻辑处理。
6.2 技术实现
1.调用connect算子,根据orderid进行分组,并使用process算子分别对两条流进行处理。
orderStream.connect(orderDetailStream).keyBy("orderId", "orderId").process(new orderProcessFunc());
2.process方法内部进行状态编程, 初始化订单、订单明细和定时器的ValueState状态。
private ValueState<OrderEvent> orderState;
private ValueState<TxEvent> orderDetailState;
private ValueState<Long> timeState;// 初始化状态Value
orderState = getRuntimeContext().getState(new ValueStateDescriptor<Order>("order-state",Order.class));
····
3.为每个进入的数据流保存state状态并创建定时器。在时间窗口内另一个流到达时进行join并输出,完成后删除定时器。
@Override
public void processElement1(Order value, Context ctx, Collector<Tuple2<Order, OrderDetail>> out){if (orderDetailState.value() == null){//明细数据未到,先把订单数据放入状态orderState.update(value);//建立定时器,60秒后触发Long ts = (value.getEventTime()+60)*1000L;ctx.timerService().registerEventTimeTimer(ts);timeState.update(ts);}else{//明细数据已到,直接输出到主流out.collect(new Tuple2<>(value,orderDetailState.value()));//删除定时器ctx.timerService().deleteEventTimeTimer(timeState.value());//清空状态,注意清空的是订单明细状态orderDetailState.clear();timeState.clear();}
}
...
@Override
public void processElement2(){...
}
4.未及时到达的数据流触发定时器输出到侧输出流,左流先到而右流未到,则输出左流,反之输出右连流。
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<Tuple2<Order, OrderDetail>> out) {// 实现左连接if (orderState.value() != null){ctx.output(new OutputTag<String>("left-jo in") {}, orderState.value().getOrderId());// 实现右连接}else{ctx.output(new OutputTag<String>("right-jo in") {}, orderDetailState.value().getOrderId());}orderState.clear();orderDetailState.clear();timeState.clear();
}
总体思想:基于数据时间实现订单数据及订单明细数据的关联,超时或者缺失则由侧输出流输出。
在connect中针对订单流和订单明细流,先创建定时器并保存state状态,处于窗口内就进行join, 否则进入侧输出流。
7 双流JOIN的优化与总结
-
为什么我的双流join时间到了却不触发,一直没有输出
检查一下watermark的设置是否合理,数据时间是否远远大于watermark和窗口时间,导致窗口数据经常为空
-
state数据保存多久,会内存爆炸吗
state自带有ttl机制,可以设置ttl过期策略,触发Flink清理过期state数据。建议程序中的state数据结构用完后手动clear掉。
-
我的双流join倾斜怎么办
join倾斜三板斧: 过滤异常key、拆分表减少数据、打散key分布。当然可以的话我建议加内存!加内存!加内存!!
-
想实现多流join怎么办
目前无法一次实现,可以考虑先union然后再二次处理;或者先进行connnect操作再进行join操作,仅建议~
-
join过程延迟、没关联上的数据会丢失吗
这个一般来说不会,join过程可以使用侧输出流存储延迟流;如果出现节点网络等异常,Flink checkpoint也可以保证数据不丢失。
相关文章:

Flink的双流join理解
如何保证Flink双流Join准确性和及时性、除了窗口join还存在哪些实现方式、究竟如何回答才能完全打动面试官呢。。你将在文中找到答案。 1 引子 1.1 数据库SQL中的JOIN 我们先来看看数据库SQL中的JOIN操作。如下所示的订单查询SQL,通过将订单表的id和订单详情表ord…...

《使用Python进行数据挖掘:理论、应用与案例研究》
嘿,今天我要给你们介绍一本使用Python进行数据挖掘的好书。这本书是由吴迪博士撰写的,他是雷曼学院商学院的助理教授,也是数据科学的实战派。 在这个时代,数据多得让人眼花缭乱,要从中找出有用的信息,那可不…...
Go语言技巧:快速统一字符串中的换行符,解决跨平台问题
统一字符串中的 Windows \r\n 换行符 — Go语言实现 在编程中,尤其是处理跨平台的文本数据时,换行符的处理是一个常见的问题。Windows 系统使用 \r\n 作为换行符,而 Unix-like 系统(如 Linux 和 macOS)使用 \n。在 Go…...
算法训练营day20(二叉树06:最大二叉树,合并二叉树,搜索二叉树,验证搜索二叉树)
第六章 二叉树 part06 今日内容 ● 654.最大二叉树 ● 617.合并二叉树 ● 700.二叉搜索树中的搜索 ● 98.验证二叉搜索树 详细布置 654.最大二叉树 又是构造二叉树,昨天大家刚刚做完 中序后序确定二叉树,今天做这个 应该会容易一些, 先看视…...
Leetcode(区间合并习题思路总结,持续更新。。。)
讲解题目:合并区间 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间, 并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。示例 1:输入&a…...

『python爬虫』使用docling 将pdf或html网页转为MD (保姆级图文)
目录 预览效果安装下载模型测试代码总结 欢迎关注 『python爬虫』 专栏,持续更新中 欢迎关注 『python爬虫』 专栏,持续更新中 预览效果 支持转化pdf的表格 安装 Docling 本身是专注于文档转换的工具,通常用于将文件(如 PDF&…...

elasticsearch现有集群扩展节点
原文地址:elasticsearch现有集群扩展节点 – 无敌牛 欢迎参观我的个人博客:无敌牛 – 技术/著作/典籍/分享等 给现有的 elasticsearch 集群扩展节点比较容易,已有的集群不需要做任何修改,也不用对服务做任何处理,只需…...
力扣162:寻找峰值
峰值元素是指其值严格大于左右相邻值的元素。 给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。 你可以假设 nums[-1] nums[n] -∞ 。 你必须实现时间复杂度为 O(…...
Kafka-Connect
一、概述 Kafka Connect是一个在Apache Kafka和其他系统之间可扩展且可靠地流式传输数据的工具。细心的你会发现,我们编写的producer、consumer都有很多重复的代码,KafkaConnect就是将这些通用的api进行了封装。让我们可以只关心业务部分(数…...

递归、搜索与回溯算法 - 3 ( floodfill 记忆化搜素 9000 字详解 )
一:floodfill 算法 1.1 图像渲染 题目链接:图像渲染 class Solution {// 首先先定义四个方向的向量int[] dx {0, 0, 1, -1};int[] dy {1, -1, 0, 0};// 接着用 m 记录行数,n 记录列数,prev 记录 (sr, sc) 位置的…...

YOLOv9改进,YOLOv9引入CAS-ViT(卷积加自注意力视觉变压器)中AdditiveBlock模块,二次创新RepNCSPELAN4结构
摘要 CAS-ViT 是一种为高效移动应用设计的视觉Transformer。模型通过结合卷积操作与加性自注意机制,在保持高性能的同时显著减少计算开销,适合资源受限的设备如手机。其核心组件 AdditiveBlock 通过多维度信息交互和简化的加性相似函数,实现了高效的上下文信息整合,避免了…...

HDLCPPP原理与配置
前言: 广域网中经常会使用串行链路来提供远距离的数据传输,高级数据链路控制HDLC( High-Level Data Link Control )和点对点协议PPP( Point to Point Protocol)是两种典型的串口封装协议。 HDLC协议: 原理…...
react + vite 中的环境变量怎么获取
一、Vite 环境变量基础 创建一个.env文件,Vite 定义的环境变量需要以VITE_开头。 VITE_API_URL "http://localhost:3000/api" 生产模式创建.env.production。 VITE_API_URL "https://production-api-url.com/api" 二、在 React 组件中获…...

知识蒸馏中有哪些经验| 目标检测 |mobile-yolov5-pruning-distillation项目中剪枝知识分析
项目地址:https://github.com/Syencil/mobile-yolov5-pruning-distillation 项目时间:2022年 mobile-yolov5-pruning-distillation是一个以yolov5改进为主的开源项目,主要包含3中改进方向:更改backbone、模型剪枝、知识蒸馏。这里…...
Oracle 19c RAC单节点停机维护硬件
背景 RAC 环境下一台主机硬件光纤卡不定时重启,造成链路会间断几秒,期间数据库会话响应时间随之变长,该光纤卡在硬件厂商的建议下,决定停机更换备件,为保证生产影响最小,决定停掉该节点,另外节…...

Linux系统 进程
Linux系统 进程 进程私有地址空间用户模式和内核模式上下文切换 进程控制系统调用错误处理进程控制函数获取进程 ID创建和终止进程回收子进程让进程休眠加载并运行程序 进程 异常是允许操作系统内核提供进程(process)概念的基本构造块,进程是…...
机载视频流回传+编解码方案
无线网络,低带宽场景。不能直接转发ROS raw image(10MB/s),而要压缩(编码)后再传输。可以用rtsp的udp传输或者直接传输话题,压缩方法有theora(ROS image_transport默认支持ÿ…...

Ubuntu 20.04 Server版连接Wifi
前言 有时候没有网线口插网线或者摆放电脑位置不够时,需要用Wifi联网。以下记录Wifi联网过程。 环境:Ubuntu 20.04 Server版,无UI界面 以下操作均为root用户,如果是普通用户,请切换到root用户,或者在需要权…...

【VRChat 改模】开发环境搭建:VCC、VRChat SDK、Unity 等环境配置
一、配置 Unity 相关 1.下载 UnityHub 下载地址:https://unity.com/download 安装打开后如图所示: 2.下载 VRChat 官方推荐版本的 Unity 跳转界面(VRChat 官方推荐页面):https://creators.vrchat.com/sdk/upgrade/…...

人工智能的微积分基础
目录 编辑 引言 微积分的基本概念 1. 导数 2. 积分 3. 微分方程 微积分在人工智能中的应用 1. 机器学习中的优化 2. 反向传播算法 3. 概率与统计 4. 控制理论 5. 自然语言处理中的梯度 6. 计算机视觉中的积分 7. 优化算法中的微积分 8. 微分几何在深度学习中的…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...

在Zenodo下载文件 用到googlecolab googledrive
方法:Figshare/Zenodo上的数据/文件下载不下来?尝试利用Google Colab :https://zhuanlan.zhihu.com/p/1898503078782674027 参考: 通过Colab&谷歌云下载Figshare数据,超级实用!!࿰…...

简单聊下阿里云DNS劫持事件
阿里云域名被DNS劫持事件 事件总结 根据ICANN规则,域名注册商(Verisign)认定aliyuncs.com域名下的部分网站被用于非法活动(如传播恶意软件);顶级域名DNS服务器将aliyuncs.com域名的DNS记录统一解析到shado…...

基于微信小程序的作业管理系统源码数据库文档
作业管理系统 摘 要 随着社会的发展,社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景,运用软件工程原理和开发方法,它主要是采用java语言技术和微信小程序来完成对系统的…...
【设计模式】1.简单工厂、工厂、抽象工厂模式
every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 以下是 简单工厂模式、工厂方法模式 和 抽象工厂模式 的 Python 实现与对比,结合代码示例和实际应用场景说明: 1. 简单工厂模式&a…...