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

Kafka-Consumer理论知识

一、上下文

之前的博客我们分析了Kafka的设计思想、Kafka的Producer端、Kafka的Server端的分析,为了完整性,我们接下来分析下Kafka的Consumer。《Kafka-代码示例》中有对应的Consumer示例代码,我们以它为入口进行分析

二、KafkaConsumer是什么?

一个从Kafka集群中消费记录的客户端,KafkaConsumer与broker交互,会随着获取的TopicPartition在集群中因故障迁移而自己调整。

KafkaConsumer允许消费者组使用consumer groups对消费进行负载平衡

三、offset

Kafka为分区中的每条记录维护一个offset,该offset充当该分区内记录的唯一标识符,也表示consumer的消费进度。例如:如果offset=5就表示offset为0-4的记录已经被消费。

consumer的position(TopicPartition)给出了下一条记录的offset,这将比消费者在该分区中看到的最高偏移量大一个。每次consumer在调用poll(Duration) 收到消息时,都会推进offset的增长。

consumer可以交由 kafka 定期自动提交offset 。也可以 调用 commitSync() 手动提交。

如果consumer进程失败并重新启动,会从维护的offset处开始继续消费。

1、自动提交offset

如下的示例依赖于自动提交offset

Properties props = new Properties();
//指定broker列表,用于连接kafka集群,1个或多个都可以,尽量多个
props.setProperty("bootstrap.servers", "localhost:9092");
//定义的组为test
props.setProperty("group.id", "test");
//配置自动提交
props.setProperty("enable.auto.commit", "true");
//自动提交频率,1000表示1秒提交1次
props.setProperty("auto.commit.interval.ms", "1000");
props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
//订阅 foo bar 两个topic的数据
consumer.subscribe(Arrays.asList("foo", "bar"));
while (true) {ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));for (ConsumerRecord<String, String> record : records)System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
}

 当程序正常运行,offset会随着consumer的消费自动向前推进,如果consumer发生故障,会出现重复消费或数据丢失的情况。

如果Kafka以auto.commit.interval.ms的频率提交了offset,consumer在下一次自动提交前出现故障,当consumer再次启动后会出现重复消费的现象。

如果consumer刚刚拉回来一批数据准备处理,此刻Kafka正好自动提交offset,但是consumer出现了故障,当consumer再次启动后会出现这批数据丢失的现象。

2、手动提交offset

如下的示例依赖于手动提交offset

Properties props = new Properties();
props.setProperty("bootstrap.servers", "localhost:9092");
props.setProperty("group.id", "test");
//关闭自动提交
props.setProperty("enable.auto.commit", "false");
props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("foo", "bar"));
final int minBatchSize = 200;
List<ConsumerRecord<String, String>> buffer = new ArrayList<>();
while (true) {ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));for (ConsumerRecord<String, String> record : records) {buffer.add(record);}if (buffer.size() >= minBatchSize) {insertIntoDb(buffer);//手动提交consumer.commitSync();buffer.clear();}
}

用户可以根据自己处理数据的逻辑,控制何时提交offset。

在这个例子中,我们需要积累一定数量的数据后再批量插入数据库中。因此只有插入数据库后才需要提交offset。

这里还会发生一个小概率事件:这批数据插入数据库成功,但是再手动提交offset时,consumer发生故障。这会导致consumer重启后发生重复消费现象。因此最好把插入数据和提交offset放入到一个事务中,要么都成功,要么都失败。

上面的示例使用 commitSync() 将所有接收到的记录标记为已提交。在某些情况下,可能希望通过明确指定offset来更好地控制已提交的记录。比如下面示例:

try {while(running) {ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(Long.MAX_VALUE));for (TopicPartition partition : records.partitions()) {List<ConsumerRecord<String, String>> partitionRecords = records.records(partition);for (ConsumerRecord<String, String> record : partitionRecords) {System.out.println(record.offset() + ": " + record.value());}long lastOffset = partitionRecords.get(partitionRecords.size() - 1).offset();//提交的偏移量应始终是应用程序将读取的下一条消息的偏移量//因此这里给的是lastOffset + 1consumer.commitSync(Collections.singletonMap(partition, new OffsetAndMetadata(lastOffset + 1)));}}
} finally {consumer.close();
}

四、consumer groups

Kafka使用consumer groups的概念来允许一个进程池来划分消费和处理记录的工作。这些进程可以在同一台机器上运行,也可以分布在许多机器上,为处理提供可扩展性和容错性。

共享统一 group.id  的所有consumer 都将属于同一消费者组。组中的每个消费者都可以通过其中一个subscribe(Collection, ConsumerRebalanceListener) API动态设置要订阅的topic列表。Kafka会将订阅topic中的每条消息传递给每个消费者组中的一个进程。这是通过平衡消费者组中所有成员之间的分区来实现的,这样每个分区都被分配给组中的一个消费者。

如果一个topic有四个分区,一个消费者组有两个进程,那么每个进程会被分配两个分区来消费。

consumer group中的consumer是动态维护的,如果要给consumer出现故障,分配给它的分区将被重新分配给同一组中的其他consumer。同样,如果新的consumer加入该组,分区将从现有consumer移动到新的consumer。这被称 重新平衡 组。

当topic中的分区增多时也会触发 组的 重新平衡

当组重新分配自动发生时,可以通过ConsumerRebalanceListener通知消费者,这允许他们完成必要的应用程序级逻辑,如状态清理、手动偏移提交等。

消费者也可以使用assign()手动分配特定分区。在这种情况下,动态分区分配和消费者组协调将被禁用。

五、监测consumer故障

consumer订阅了一组topic后,当调用poll(Duration)  时,消费者将自动加入一个组。poll(Duration) 可以让Kafka认为该consumer是活动状态,consumer就会留在组中,并继续从分配给它的分区接收消息。

consumer定期向服务器发送心跳,如果consumer崩溃或在 session.timeout.ms  的持续时间内无法发送心跳,则consumer将被视为已死亡,其分区将被重新分配。

consumer也可能遇到“livelock”情况,即它继续发送心跳,但没有调用poll(Duration)

为了防止consumer在这种情况下无限期地保留其分区,Kafka使用 max.poll.interval.ms 设置提供了一种活性检测机制。如果consumer在max.poll.interval.ms内没有执行poll(Duration)消费数据,那么consumer将主动离开该组,以便另一个consumer可以接管其分区。

当这种情况发生时,可能会看到偏移提交失败(如调用commitSync() 时抛出的CommitFailedException 异常。这是一种安全机制,保证只有组中的活跃成员才能提交偏移。因此,要想留在组中,就必须持续调用 poll(Duration)

consumer提供两个配置来控制轮询循环的行为:

max.poll.interval.ms:

        增加轮询之间的间隔,让consumer可以有更多的事件来处理拉取的数据,缺点:增加此值可能会延迟组重新平衡。

max.poll.records:

        使用此设置可限制单次轮询调用返回的总记录数,通过调整此值,您可以减少轮询间隔,这将减少组重新平衡的影响

如果拉回的消息处理时间变化不可预测,这两种选项可能都不能完美的解决。处理这种情况的推荐方法是将消息处理转移到另一个线程或者线程池。此consumer只负责拉取数据。如果使用这种方法,需要关闭自动提交offset,当数据处理完成进行手动提交offset。

六、为consumer手动指定分区

一般情况下,consumer只要订阅topic或topic列表,并让Kafka根据组中的活动consumer为这些主题动态分配公平的分区份额。但某些情况下,可能需要对特定分区进行更精细的控制。

1、如果进程正在维护与该分区相关联的某种本地状态(如本地磁盘键值存储),那么它应该只获取它在磁盘上维护的分区的记录。

2、如果进程本身具有高可用性,并且在失败时将重新启动(可能使用YARN、Mesos或AWS设施等集群管理框架,或者作为流处理框架的一部分)。在这种情况下,Kafka不需要检测故障并重新分配分区,因为消费进程将在另一台机器上重新启动。

使用示例:

String topic = "foo";
TopicPartition partition0 = new TopicPartition(topic, 0);
TopicPartition partition1 = new TopicPartition(topic, 1);
consumer.assign(Arrays.asList(partition0, partition1));

手动分区分配不使用组协调,因此消费者故障不会导致分配的分区重新平衡。每个消费者独立行动,即使它与另一个消费者共享一个groupId。为了避免偏移提交冲突,通常应该确保每个消费者实例的groupId都是唯一的。

请注意:不可能将手动分区分配和订阅topic混合使用。

七、将offset维护转移到Kafka外部

Kafka自身会维护一个内部topic,用于存储offset。当然我们也可以选择将offset存储在外部存储系统,例如mysql。也可以将消费结果和offset存放在同一个存储系统,以原子方式存储结果和偏移量(用数据库事务 将 数据消费和offset提交绑定在一起)。这将使消费完全原子化,并给出“恰好一次”的语义,该语义比您使用Kafka的偏移提交功能获得的默认“至少一次”语义更强。

此时的做法是:

1、配置手动提交 enable.auto.commit=false

2、使用每个 ConsumerRecord提供的偏移量来保存您的位置

3、重新启动时,使用seek(TopicPartition, long)恢复消费者的位置

如果consumer手动指定分区,那么将offset维护到外部是简单的,因为指定的这个分区一直都是归属与你这个consumer来消费的。如果consumer消费的分区时Kafka给你自动分配的,则会因为consumer的变化等因素导致你当下的consumer被分到的分区变化。这时,需要通过对subscribe(Collection, ConsumerRebalanceListener)和subscribe(Pattern, ConsumerRebalanceListener)的调用提供一个ConsumerRebalanceListener

例如:当consumer获取分区时,consumer希望通过实现ConsumerRebalanceListener.onPartitionsRevoked(Collection)来提交这些分区的偏移量。当将分区分配给消费者时,消费者将希望查找这些新分区的偏移量,并通过实现ConsumerRebalanceListener.onPartitionsAssigned(Collection)将consumer正确初始化到该位置。

ConsumerRebalanceListener的另一个常见用途是清除应用程序为移动到其他地方的分区维护的任何缓存

八、控制consumer的消费位置

在大多数用例中,consumer只需从头到尾消费记录,定期提交其位置(自动或手动)。然而,Kafka允许consumer手动控制其位置,在分区中随意向前或向后移动。这意味着consumer可以重新消费旧记录,或者跳到最近的记录,而无需实际消费中间记录。

例如对于时间敏感的记录处理,如果数据已经生产了很长时间,对于一个新的consumer来说就很有意义。它可以直接处理最新的数据,或者它只关心中午12点之后的数据。

九、流量控制

如果一个consumer被分配了多个分区来获取数据,它将尝试同时从所有分区中消费,从而有效地赋予这些分区相同的消费优先级。然而,在某些情况下,consumer可能希望首先全速处理某一个分区。当这个分区没有数据时才处理其他分区。

还有一种情况,那就是流处理,一个consumer从两个topic消费数据,并对这两个流进行连接。当其中一个topic远远落后与另一个topic时,consumer会暂停从速度快的topic获取数据,以便让滞后的topic跟上。

Kafka支持消费流的动态控制,在未来的 poll(Duration)调用中,分别使用pause(Collection)和resume()来暂停指定分配分区上的消费和恢复指定暂停分区上的消费。

十、消费事务性消息

Kafka 0.11.0引入了事务,其中producer可以原子地写入多个topic和partition。为了实现这一点,从这些分区读取的consumer应该被配置为只读取已提交的数据。既:isolation.level=read_committed

在read_committed模式下,consumer将只读取已成功提交的事务消息。consumer的分区结束偏移量将是分区中属于打开事务的第一条消息的偏移量。这种偏移被称为“最后稳定偏移”(LSO)

LSO 是 Last Stable Offset 的缩写

consumer只会读取LSO并过滤掉任何已中止的事务消息。LSO还影响consumer的seekToEnd(Collection)和endOffsets(Collection)行为。

带有事务消息的分区将包括表示事务结果的提交或中止标记。这些标记不会返回给consumer,但在日志中有偏移。

因此,从具有事务性消息的topic中读取的应用程序将看到所消耗的偏移量存在缺口。这些缺失的消息将成为事务标记,并在两个隔离级别中为consumer过滤掉。此外,消费者也可能会看到由于事务中止而导致的空白,因为这些消息不会被consumer返回,但会有有效的偏移量。

十一、多线程消费

Kafka消费者不是线程安全的。我们有责任确保多线程访问正确同步。不同步访问将导致ConcurrentModificationException

此规则的唯一例外是 wakeup(),它可以从外部线程安全地用于中断活动操作。在这种情况下,将从操作的线程阻塞中抛出org.apache.kafka.common.errors.WakeupException这可用于从另一个线程关闭消费者。

如下示例:

public class KafkaConsumerRunner implements Runnable {private final AtomicBoolean closed = new AtomicBoolean(false);private final KafkaConsumer consumer;public KafkaConsumerRunner(KafkaConsumer consumer) {this.consumer = consumer;}{@literal}@Overridepublic void run() {try {consumer.subscribe(Arrays.asList("topic"));while (!closed.get()) {ConsumerRecords records = consumer.poll(Duration.ofMillis(10000));// 处理新记录 ......}} catch (WakeupException e) {// 如果关闭,忽略异常if (!closed.get()) throw e;} finally {consumer.close();}}// Shutdown hook which can be called from a separate thread// 关闭钩子,可以从单独的线程调用public void shutdown() {closed.set(true);consumer.wakeup();}
}

然后在一个单独的线程中,可以通过设置closed标志并唤醒消费者来关闭消费者。

closed.set(true);
consumer.wakeup();

我们看以下几种情况:

1、1个consumer一个线程

优点:

1、最容易实施

2、它通常是最快的,因为不需要线程间的协调

3、它使得基于每个分区的按顺序处理非常容易实现(每个线程只按接收消息的顺序处理消息)。

缺点:

1、更多的consumer意味着更多的TCP连接到集群(每个线程一个)。一般来说,Kafka非常有效地处理连接,因此这通常是一个很小的成本。

2、多个consumer意味着向服务器发送的请求更多,数据批处理略有减少,这可能会导致I/O吞吐量下降。

3、所有进程中的线程总数将受到分区总数的限制

2、将消费和处理解耦

consumer只负责接收数据,处理数据的逻辑让另一个线程池来处理

优点:

        此选项允许独立扩展consumer和处理器的数量。这使得有可能有一个为多个处理器线程提供数据的单一consumer,从而避免了对分区的任何限制。

缺点:

        1、保证处理器之间的顺序需要特别小心,因为线程将独立执行,由于线程执行时间的运气,较早的数据块实际上可能会在较晚的数据块之后被处理。对于没有订购要求的加工,这不是问题。

        2、手动提交 offset 变得更加困难,因为它要求所有线程协调以确保该分区的处理完成。

相关文章:

Kafka-Consumer理论知识

一、上下文 之前的博客我们分析了Kafka的设计思想、Kafka的Producer端、Kafka的Server端的分析&#xff0c;为了完整性&#xff0c;我们接下来分析下Kafka的Consumer。《Kafka-代码示例》中有对应的Consumer示例代码&#xff0c;我们以它为入口进行分析 二、KafkaConsumer是什…...

Js-对象-04-Array

重点关注&#xff1a;Array String JSON BOM DOM Array Array对象时用来定义数组的。常用语法格式有如下2种&#xff1a; 方式1&#xff1a; var 变量名 new Array(元素列表); 例如&#xff1a; var arr new Array(1,2,3,4); //1,2,3,4 是存储在数组中的数据&#xff0…...

React 第八节组件生命周期钩子-类式组件,函数式组件模拟生命周期用法

概述 React组件的生命周期可以分为三个主要阶段&#xff1a; 挂载阶段&#xff08;Mounting&#xff09;&#xff1a;组件被创建&#xff0c;插入到DOM 树的过程&#xff1b; 更新阶段&#xff08;Updating&#xff09;&#xff1a;是组件中 props 以及state 发生变化时&#…...

Dubbo源码解析-服务调用(七)

一、服务调用流程 服务在订阅过程中&#xff0c;把notify 过来的urls 都转成了invoker&#xff0c;不知道大家是否还记得前面的rpc 过程&#xff0c;protocol也是在服务端和消费端各连接子一个invoker&#xff0c;如下图&#xff1a; 这张图主要展示rpc 主流程&#xff0c;消费…...

svn 崩溃、 cleanup失败 怎么办

在使用svn的过程中&#xff0c;可能出现整个svn崩溃&#xff0c; 例如cleanup 失败的情况&#xff0c;类似于 这时可以下载本贴资源文件并解压。 或者直接访问网站 SQLite Download Page 进行下载 解压后得到 sqlite3.exe 放到发生问题的svn根目录的.svn路径下 右键呼出pow…...

【Linux系列】NTP时间同步服务器搭建完整指南

在分布式系统和高可用环境中&#xff0c;时间同步是至关重要的。特别是对于银行、金融等关键业务系统&#xff0c;精准的时间同步不仅关系到系统的稳定性&#xff0c;还直接影响交易处理、日志管理、日终结算等功能。本文将介绍NTP&#xff08;Network Time Protocol&#xff0…...

go 结构体方法

在 Go 语言中&#xff0c;结构体方法是指附加到结构体类型上的函数。这些方法可以通过结构体的实例来调用。方法的接收者&#xff08;receiver&#xff09;指定了该方法属于哪个结构体类型。接收者可以是一个值类型或指针类型。 定义结构体方法 下面是如何为一个结构体定义方…...

DHCP服务(包含配置过程)

目录 一、 DHCP的定义 二、 使用DHCP的好处 三、 DHCP的分配方式 四、 DHCP的租约过程 1. 客户机请求IP 2. 服务器响应 3. 客户机选择IP 4. 服务器确定租约 5. 重新登录 6. 更新租约 五、 DHCP服务配置过程 一、 DHCP的定义 DHCP&#xff08;Dynamic Host Configur…...

uniapp内嵌的webview H5与应用通信

H5端&#xff1a; 1、找到index.html引入依赖 <script type"text/javascript" src"https://unpkg.com/dcloudio/uni-webview-js0.0.3/index.js"></script> 2、在需要通讯处发送消息 uni.postMessage({data:{code:200,msg:"处理完成&q…...

Android OpenGL ES详解——绘制圆角矩形

1、绘制矩形 代码如下&#xff1a; renderer类&#xff1a; package com.example.roundrectimport android.content.Context import android.opengl.GLES30 import android.opengl.GLSurfaceView.Renderer import com.opengllib.data.VertexArray import com.opengllib.prog…...

网络基础二

文章目录 协议定制&#xff0c;序列化和反序列化应用层网络版计算器协议的定制序列反序列化序列化未复用版 反序列化 TCP是面向字节流的&#xff0c;你怎么保证&#xff0c;你读取上来的数据&#xff0c;是‘’一个“ “完整””的报文呢&#xff1f; 我们没有区分字符串里面有…...

从Full-Text Search全文检索到RAG检索增强

从Full-Text Search全文检索到RAG检索增强 时光飞逝&#xff0c;转眼间六年过去了&#xff0c;六年前铁蛋优化单表千万级数据查询性能的场景依然历历在目&#xff0c;铁蛋也从最开始做CRUD转行去了大数据平台开发&#xff0c;混迹包装开源的业务&#xff0c;机缘巧合下做了实时…...

springMVC 全局异常统一处理

全局异常处理⽅式⼀: 1、配置简单异常处理器 配置 SimpleMappingExceptionResolver 对象: <!-- 配置全局异常统⼀处理的 Bean &#xff08;简单异常处理器&#xff09; --> <bean class"org.springframework.web.servlet.handler.SimpleMappingExceptionReso…...

qt ubuntu i386 系统

sudo ln -s cmake-3.31.0-linux-x86_64/bin/* /usr/local/bin 【Ubuntu20.4安装QT6 - CSDN App】Ubuntu20.4安装QT6_ubuntu安装qt6-CSDN博客 sudo ../configure -release -platform linux-g-64 -static -nomake examples -nomake demos -no-qt3support -no-script -no-scriptt…...

BUUCTF—Reverse—helloword(6)

一道安卓逆向的签到题 下载附件 使用JADX-gui反编译工具打开&#xff08;注意配环境&#xff09;&#xff0c;找到主函数 jadx 本身就是一个开源项目&#xff0c;源代码已经在 Github 上开源了 官方地址&#xff1a;GitHub - skylot/jadx: Dex to Java decompiler 发现flag …...

深入解析下oracle date底层存储方式

之前我们介绍了varchar2和char的数据库底层存储格式&#xff0c;今天我们介绍下date类型的数据存储格式&#xff0c;并通过测试程序快速获取一个日期。 一、环境搭建 1.1&#xff0c;创建表 我们还是创建一个测试表t_code&#xff0c;并插入数据&#xff1a; 1.2&#xff0c;…...

Elasticsearch 开放推理 API 增加了对 IBM watsonx.ai Slate 嵌入模型的支持

作者&#xff1a;来自 Elastic Saikat Sarkar 使用 Elasticsearch 向量数据库构建搜索 AI 体验时如何使用 IBM watsonx™ Slate 文本嵌入。 Elastic 很高兴地宣布&#xff0c;通过集成 IBM watsonx™ Slate 嵌入模型&#xff0c;我们的开放推理 API 功能得以扩展&#xff0c;这…...

如何搭建一个小程序:从零开始的详细指南

在当今数字化时代&#xff0c;小程序以其轻便、无需下载安装即可使用的特点&#xff0c;成为了连接用户与服务的重要桥梁。无论是零售、餐饮、教育还是娱乐行业&#xff0c;小程序都展现了巨大的潜力。如果你正考虑搭建一个小程序&#xff0c;本文将为你提供一个从零开始的详细…...

NFS搭建

NFS搭建 单节点安装配置服务器安装配置启动并使NFS服务开机自启客户端挂载查看是否能发现服务器的共享文件夹创建挂载目录临时挂载自动挂载 双节点安装配置服务器安装配置服务端配置NFS服务端配置Keepalived编辑nfs_check.sh监控脚本安装部署RsyncInofity 客户端 单节点安装配置…...

RNN与LSTM,通过Tensorflow在手写体识别上实战

简介&#xff1a;本文从RNN与LSTM的原理讲起&#xff0c;在手写体识别上进行代码实战。同时列举了优化思路与优化结果&#xff0c;都是基于Tensorflow1.14.0的环境下&#xff0c;希望能给您的神经网络学习带来一定的帮助。如果您觉得我讲的还行&#xff0c;希望可以得到您的点赞…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...

软件工程 期末复习

瀑布模型&#xff1a;计划 螺旋模型&#xff1a;风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合&#xff1a;模块内部功能紧密 模块之间依赖程度小 高内聚&#xff1a;指的是一个模块内部的功能应该紧密相关。换句话说&#xff0c;一个模块应当只实现单一的功能…...