Flume进阶学习!

news2024/10/9 10:22:00

本文图片来自于8.flume实时监控文件hdfs sink使用演示_哔哩哔哩_bilibili

Apache Flume 的启动过程及其配置文件和脚本

在官网下载的Flume的压缩包中,.lib文件有大量的jar包,按道理说只有.lib文件就可以运行Flume程序了。只不过需要java -jar命令还要加非常多的参数,这时.conf配置文件夹和.bin启动脚本命令夹就有用了:.bin中的命令本质上就是对于java -jar命令一系列参数的封装,给.bin命令传递参数,它会进一步变成含有参数的java-jar命令,进而启动jar包中的进程。然后一些参数的设置会在.conf中,和.bin配合执行,.bin脚本会读配置文件中的信息

Flume 启动过程

  1. 依赖库(.lib 文件夹):Flume 分发包中的 lib 目录包含了 Flume 运行所需的所有 Java 库(.jar 文件)。这些库包括 Flume 自身的代码、用于处理不同数据源和目的地(如 Kafka、HDFS)的插件,以及其他必要的支持库(如日志库、网络通信库等)。

  2. 配置文件(.conf 文件夹):Flume 的配置文件通常存放在 conf 目录中。这些 .conf 配置文件包含了定义 Flume Agent 的各种属性和参数,如使用的 Source、Channel 和 Sink 类型及其具体配置。这些配置决定了 Flume 如何收集、传输和存储数据。

  3. 启动脚本(.bin 文件夹)bin 目录包含了用于启动 Flume 服务的脚本。这些脚本简化了启动过程,因为手动通过 java -jar 命令启动 Flume 不仅需要指定 Flume 应用程序的主 .jar 文件,还需要包含 lib 目录下所有依赖库的路径,以及必要的运行参数和 JVM 选项

启动脚本的作用

启动脚本(例如,flume-ng 脚本)的主要作用是:

  • 设置类路径(Classpath):脚本会自动将 lib 目录下的所有 .jar 文件添加到 Java 类路径中,确保 Flume 运行时可以找到所有必要的类和库。
  • 读取配置文件脚本允许用户通过命令行参数指定配置文件(位于 conf 目录)。脚本将确保 Flume 启动时使用正确的配置。
  • 简化命令行参数:用户只需要提供简单的命令行参数(如要使用的配置文件名和 Flume Agent 的名称),而无需手动指定所有 .jar 文件和其他复杂的 JVM 参数。
  • 定制 JVM 选项:脚本还可以包含默认的或自定义的 JVM 启动选项,如内存设置,这对于优化 Flume 的性能和行为非常重要。

实际运行

实际上,当运行 Flume 的启动脚本时(如 bin/flume-ng agent --conf conf --conf-file example.conf --name a1),脚本会解析这些参数,并构建一个完整的 java 命令,其中包括所有必要的类路径设置、JVM 选项和 Flume 的主类。这个命令最终会启动一个 JVM 实例,其中运行 Flume Agent,根据指定的 .conf 文件中的配置来执行数据收集和传输任务


注意事项

这老师讲的太好了6.flume查看源码的基础方法_哔哩哔哩_bilibili,一些注意点:

  • Flume的配置文件是很重要的,基本上就是在配置文件上配置信息,然后直接启动Flume即可。
  • 一个sink只能接收一个channel,但是一个Source可以连接多个channel
  • 监控本地文件,在启动Flume之前一定确保文件存在,否则报错无法正常启动。

断点续传

Apache Flume 提供了一种机制,允许在处理文件时实现断点续传功能,这在使用如 Spooling Directory Source 来监控并读取本地文件时尤为重要。这种机制确保了即使 Flume 实例发生故障或重启,也能从上次停止的位置继续读取文件,避免了数据重复或丢失

实现断点续传的关键:Position File

Flume 通过维护一个偏移量文件(position file)来实现断点续传。这个偏移量文件记录了 Flume 读取每个文件的当前位置,如下图所示。

  • 文件偏移量(pos):表示 Flume 在文件中的读取位置。每次 Flume 读取一部分数据后,它会更新这个偏移量,以反映下一次读取应该开始的位置。

  • 位置文件(Position File):是一个特殊的文件,Flume 用它来存储每个正在被监控和读取的文件的偏移量。通过这种方式,即使 Flume 服务重启,它也能通过读取位置文件来确定上次读取停止的位置,并从那里继续读取数据。

positionFile原本是默认创建新目录的,为了方便可以修改配置文件直接更换positionFile的位置,这个文件中记录了监控的文件,每一个都包含绝对目录和inode文件唯一标识,pos是核心表示字节偏移量,根据这个偏移量实现断点续传的位置搜索所以如果将文件改名,或者使用vim编辑(vim是先将原本文件删除然后再新建一个文件),Flume都会重新读取文件的所有内容。此时断点续传所依赖的positionfile中的文件inode会不一样,(如果是改名的话inode和file路径都会不一样)表示新的文件。

工作流程

  1. 启动:Flume 启动时,Spooling Directory Source 会检查其位置文件,以确定每个文件的读取位置。

  2. 读取和更新当 Flume 从一个文件中读取数据时,它会实时更新该文件在位置文件中对应的偏移量。这确保了 Flume 始终知道下一次应从哪里开始读取。有点向Kafka消费者的消费位置。

  3. 故障和重启:如果 Flume 发生故障或被重启,Spooling Directory Source 会再次读取位置文件,并根据文件中的偏移量信息继续读取数据,从而实现断点续传。

优势和局限

  • 数据一致性:这种机制最大的优势是提高了数据处理的一致性和可靠性,确保数据不会因为系统故障而丢失或重复处理。

  • 局限:值得注意的是,这种机制依赖于位置文件的准确性和完整性。如果位置文件被损坏或丢失,Flume 可能无法正确地继续之前的读取过程,可能会导致数据重复或遗漏。因此,保证位置文件的安全和完整性是实现断点续传机制的重要条件。


事务

Apache Flume 在处理数据流时采用了事务机制来保证数据的可靠性。在 Flume 的架构中,数据从 Source 流向 Channel,再从 Channel 流向 Sink。为了保证数据不会因为处理过程中的失败而丢失,Flume 对这个流程中的数据传输采用了事务控制。具体来说,Flume 中有两种基本的事务类型:put 事务和 take 事务。理解这两种事务的工作方式对于掌握 Flume 的数据一致性和可靠性保证机制至关重要。

Put 事务

  • 定义put 事务是指 Source 将数据放入(put)Channel 的过程。在这个过程中,数据的传输被包裹在一个事务中

  • 事务提交失败与数据回滚当 Source 尝试将数据放入 Channel 时,如果 Channel 已满(即没有足够的空间接收更多的数据),则 put 事务会提交失败。在这种情况下,事务会被回滚,这意味着数据不会被放入 Channel,并且 Source 需要重新尝试这个操作。这里的“回滚”指的是事务的回滚,而不是数据本身被回滚或删除。换句话说,事务的回滚意味着尝试放入数据的操作被取消,数据保持在 Source 端,等待下一次尝试。

  • 重试机制:在实践中,Source 会不断重试 put 事务,直到 Channel 有足够的空间接收新数据为止。这个过程确保了即使在短暂的系统压力或资源限制下,数据也不会丢失,只是其传输可能会延迟。

具体而言,Flume 会根据 Source 的配置决定重试机制:

  • 持久化 Source如果 Source 是持久化的(比如监控文件系统的 Source),则数据不会丢失,因为它仍然保存在源系统中(如文件中)。Flume 会在稍后重试 put 操作,这通常涉及等待直到 Channel 有足够空间可用。
  • 非持久化 Source如果 Source 不是持久化的(比如接收内存中数据流的 Source),则在 put 事务持续失败的情况下,数据可能会丢失,因为没有其他地方存储这些数据直到 Channel 可用。

Take 事务

  • 定义take 事务是指 Sink 从 Channel 中取出(take)数据的过程。同样,这个过程也被包裹在一个事务中,以确保数据的可靠传输。

  • 事务确认与回滚:如果在尝试处理(take)数据时成功,Sink 将提交事务,确认数据被成功处理。如果处理过程中遇到问题(比如目的地无法写入数据),则事务会被回滚,这意味着数据会保留在 Channel 中,Sink 需要重新尝试这个操作。

在 Flume 中,“数据回滚”实际上是指“事务回滚”,意味着由于某些原因(如资源限制),尝试的数据传输操作(无论是 put 还是 take)被取消,数据保持在原位置不变,等待重新操作。这种事务机制确保了 Flume 能够在面对系统故障或资源瓶颈时,通过重试保证数据不丢失,最终可靠地完成数据传输任务。

事务中的事件

在 Apache Flume 中,一个事务可以包含多个事件(Event),其数量由配置中的参数决定。对于如何配置一个事务中可以处理的事件数量,这主要取决于使用的 Channel 类型以及相关的配置参数。每种 Channel 类型(如 Memory Channel、File Channel 等)都有自己的特定参数,用于控制事务的行为,包括一个事务中可以包含的事件数量

Memory Channel

对于 Memory Channel,有两个主要的配置参数影响一个事务中可以处理的事件数量:

  • capacity:这个参数设置了 Memory Channel 总的容量,即它可以存储的最大事件数。
  • transactionCapacity:这个参数设置了每个事务最多可以包含的事件数量。这直接影响了每次 puttake 操作可以处理的最大事件数。

例如,如果你设置 transactionCapacity 为 100,那么每个 puttake 事务最多处理 100 个事件。

File Channel

对于 File Channel,关键的配置参数包括:

  • capacity:定义了 File Channel 可以持有的最大事件数。
  • transactionCapacity:和 Memory Channel 类似,这个参数定义了每个事务可以处理的最大事件数。

配置示例

对于 Memory Channel,配置可能如下所示:

agent.channels.memChannel.type = memory agent.channels.memChannel.capacity = 10000 
agent.channels.memChannel.transactionCapacity = 100

这意味着 Memory Channel 的总容量为 10000 个事件,而每个事务最多可以处理 100 个事件。

默认参数

对于 Memory Channel 和 File Channel,如果没有明确指定 transactionCapacity,它们的默认值可能因 Flume 的版本和具体实现而异,因此查阅当前使用版本的 Flume 文档是确定这些默认值的最佳方式。


Flume Agent 内部原理

ChannelSelector 概述

Flume 的 ChannelSelector 是用于决定一个事件 (Event) 应该被发送到哪一个或哪些 Channel 的组件。

ChannelSelector 的类型

  • ReplicatingSelector:这种类型的 ChannelSelector 会将接收到的每个事件复制并发送到所有配置的 Channel。这主要用于数据的冗余存储,当你希望同一份数据被送到多个目的地进行处理或备份时非常有用。

  • MultiplexingSelector:MultiplexingSelector 允许基于事件的某些属性(通常是事件头部的键值对)来决定事件应该被发送到哪个 Channel。这种选择器支持更复杂的路由策略,比如基于事件类型或来源将数据发送到不同的处理管道。

Channel 是分布式管道集群吗?

不完全是。在 Flume 中,Channel 更像是连接 Source(数据来源)和 Sink(数据目的地)的内部队列或缓冲区。每个 Channel 都独立存在,负责暂存数据直到数据被 Sink 处理。尽管 Channel 可以在不同的 Flume Agent 上分布运行,但每个 Channel 通常是指单个实例中的一个组件,而不是一个分布式系统中的多个组件集群。


ChannelSelector之一:MultiplexingSelector

在多个channel的情况下,由于一个sink只能连接一个channel,所以使用MultiplexingSelector本质上就是根据事件的性质来为不同的sink(即不同的目的地)发送事件

MultiplexingSelector 描述为一种“多路复用”确实可能引起一些混淆,尤其是在通常理解的网络或通信协议中的多路复用概念上。在那种情况下,“多路复用”通常指的是在单一通信通道上同时传输多个信号或数据流的技术,以提高效率或带宽利用率。

在 Apache Flume 的上下文中,MultiplexingSelector 的使用确实更接近于一个“分类选择器”或“路由器”。它的主要作用是基于事件的属性(例如,事件头部的键值对)来决定事件应该发送到哪个或哪些 Channel,从而实现对事件流的逻辑分割或路由到不同的处理流程。这种机制允许不同类型的事件被处理和存储在不同的目的地。

多路复用的命名

  • 逻辑上的多路复用:虽然 Flume 中的多路复用不增加单个 Channel 的带宽或处理能力,它通过允许单个 Source 向多个 Channel 分发事件,实现了逻辑上的多路复用。这样,基于事件特性的不同处理逻辑可以并行运行,而无需为每种事件类型配置独立的 Source

  • 效率提高的另一层意义:虽然直观上看似乎没有提高传输效率,但通过允许更细粒度的数据流管理和处理,MultiplexingSelector 实际上提高了整个数据处理流程的效率和灵活性。它使得数据可以根据需求被送往最适合处理该数据的 Sink,从而优化了资源的使用,例如,可以避免将所有数据都通过一个高成本的处理流程。

实践意义

在实践中,MultiplexingSelector 允许开发人员和系统设计者基于数据内容或类型实现复杂的数据路由策略,例如:

  • 将特定类型的日志发送到专门的分析系统。
  • 根据事件严重性级别将事件分发到不同的存储或处理队列。
  • 实现数据分流,将部分数据送往实时处理系统,而将其他数据归档存储。

作者总结

这种逻辑上的多路复用,其实就是在同一个Source源数据路径中,实现不同性质或者不同处理逻辑的事件同时传输,这种复用相当于将Selector和多个Channel结合形成多种逻辑上的Source源数据路径,但其实只有一个Source源数据路径。实现了数据按需分发,极大地增强了大数据处理和日志收集系统的能力。

其实这个MultiplexingSelector是最能体现Event事件中Header的作用的组件了,因为有了Header,就可以通过Selector选择最合适的Sink,这样的话Sink对于事件的一致性处理逻辑会减少很多,也在变相地优化了资源的使用!

实际应用场景

考虑一个实际的应用场景:一个系统产生了多种类型的日志,包括系统日志、应用日志和访问日志。这些日志被发送到同一个 Flume Source,通过在事件的 Header 中标识日志类型,MultiplexingSelector 可以将它们路由到专门处理这三种日志的不同 Sinks —— 例如,系统日志被存储于 HDFS 以便长期分析,应用日志被发送到一个实时监控系统,而访问日志则被归档到外部存储以供未来审计。

通过这样的设计,不仅优化了资源的使用,也确保了数据处理流程的清晰和高效,同时减轻了后续处理环节的负担,因为每个 Sink 都明确知道自己将处理哪种类型的数据,不需要再做额外的数据分类处理。这种基于 Header 的智能路由策略是 Flume 架构灵活性和强大功能的一个重要体现。


本地数据即时同步到HDFS就用Flume

使用 Apache Flume 同步本地数据到 HDFS 是一个常见且有效的用例。Flume 提供了灵活的配置选项来适应不同的数据流场景,包括文件滚动策略,这对于避免在 HDFS 上产生大量小文件非常关键。HDFS 中的小文件问题是因为每个文件在 NameNode 上都会占用内存来维护文件系统的命名空间和块映射信息,过多的小文件会消耗大量的 NameNode 内存资源,影响其性能

配置参数调整

为了解决小文件问题,可以调整以下几个与文件滚动相关的 Flume 配置参数:

  1. rollSize:这个参数设置了触发滚动(创建新文件)之前,一个文件可以达到的最大大小(以字节为单位,通常在HDFS中设置为128MB的字节数量)。增加这个值可以减少生成的文件数量,但也可能导致数据在写入 HDFS 之前在 Flume 中停留更长时间。

  2. rollCount:这个参数设置在触发滚动之前,一个文件中可以包含的最大事件(Event)数。调整这个值也可以帮助控制文件数量和大小。

  3. rollInterval:这个参数设置了滚动的时间间隔(以秒为单位)。设置为0禁用基于时间的滚动,可能有助于减少由于时间间隔到达而生成的小文件数量,尤其是在数据流量较低时

示例配置

agent.sinks.hdfsSink.type = hdfs
agent.sinks.hdfsSink.hdfs.path = hdfs://namenode/path/to/data
agent.sinks.hdfsSink.hdfs.filePrefix = events-
agent.sinks.hdfsSink.hdfs.rollSize = 134217728 # 128MB
agent.sinks.hdfsSink.hdfs.rollCount = 0
agent.sinks.hdfsSink.hdfs.rollInterval = 0

在这个例子中,hdfsSink 配置为当文件大小达到128MB时进行滚动,禁用了基于事件数量和时间间隔的滚动,这有助于减少小文件的生成。但是在实际开发过程中,基本上只需要设置事件间隔的滚动,通常为3600s即1h。而不基于事件数量,将其设置为0。原因如下:

在处理大数据流时,控制文件的大小或写入间隔比控制每个文件的事件数量通常更有意义。同时,在许多情况下,数据源的事件产生率是变化的,基于时间(rollInterval)的滚动提供了一种简单的机制来周期性地生成文件,而不需要根据数据流的变化动态调整事件计数阈值rollCount)。这简化了配置管理,尤其是在数据量大且变化不定的生产环境中

注意事项

  • 数据延迟:增加文件大小或事件计数的阈值可能会导致数据在达到滚动条件之前在内存中保留更长时间,这可能会稍微增加数据到达 HDFS 的延迟。
  • 资源管理:确保 Flume Agent 有足够的资源(如内存)来处理较大的滚动文件大小。

通过合理配置这些参数,可以有效地减轻 HDFS 小文件问题,同时保持数据同步的及时性和准确性。不过,最佳的配置参数取决于具体的使用场景,包括数据产生的速率、预期的延迟要求以及 HDFS 集群的容量,可能需要一些实验和调整来找到最优的平衡点。

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

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

相关文章

二维码:技术、商业与未来

title: 二维码:技术、商业与未来 date: 2024/4/3 19:12:28 updated: 2024/4/3 19:12:28 tags: 二维码技术商业应用移动支付物联网AR/VR融合智能家居数字化社会 第一章:引言 1. 二维码在数字化时代的重要性和普及程度 在数字化时代,二维码作…

【吊打面试官系列】Redis篇 -使用过 Redis 做异步队列么,你是怎么用的?

大家好,我是锋哥。今天分享关于 【使用过 Redis 做异步队列么,你是怎么用的?】面试题,希望对大家有帮助; 使用过 Redis 做异步队列么,你是怎么用的? 一般使用 list 结构作为队列,rpu…

31. UE5 RPG使用增强输入激活GameplayAbility(一)

在前面文章中,我们实现了对技能添加并直接激活功能,介绍了GA的相关参数配置。现在,我们还不能通过键位触发技能,正常在游戏时,我们需要通过键位触发技能,实现技能的激活。 在UE5里面添加了增强输入&#xf…

简历复印--原型模式

1.1 夸张的简历 简历的打印。"对编程来说,简单的复制粘贴极有可能造成重复代码的灾难。我所说的意思你根本还没听懂。那就以刚才的例子,我出个需求你写写看,要求有一个简历类,必须要有姓名,可以设置性别和年龄&am…

Unity类银河恶魔城学习记录12-2 p124 Character Stats UI源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili UI_Statslot.cs using System.Collections; using System.Collections.Gen…

root@localhost‘s password: Permission denied, please try again.

编辑、etc/ssh/sshd_config文件 ,将PermitRootLogin这行改为yes rootubuntu:/home/ubuntu# vim /etc/ssh/sshd_config 重新加载改文件 /etc/init.d/ssh restart

uniapp 设置globalStyle navigationBarTitleText 不显示

设置全局的navigationBarTitleText但是没有显示 没效果: 原因: 这里实际上设置了navigationBarTitleText 为"" 所以不会使用全局的设置 解决方法就是直接将这一行代码删除

【逆向思考 】【拓扑排序】1591. 奇怪的打印机 II

本文涉及的知识点 逆向思考 拓扑排序 LeetCode1591. 奇怪的打印机 II 给你一个奇怪的打印机,它有如下两个特殊的打印规则: 每一次操作时,打印机会用同一种颜色打印一个矩形的形状,每次打印会覆盖矩形对应格子里原本的颜色。 一…

【教程】宝塔default.db占用空间几十g解决方法|宝塔占用磁盘空间特别大解决方法|宝塔磁盘被占满怎么清理

目录 一、前言二、排查问题三、解决方法 一、前言 用过宝塔创建网站,大家应该都非常熟悉,但是用随着用的时间越来越多,宝塔所占用的空间也越来越多,不停的加大数据盘都没有用,我原先买了30G够用了,随着时间…

基本数据类型、包装类与字符串间的转换

(1)基本数据类型转为字符串 方式1:调用字符串重载的valueOf()方法 int a 10; //String str a;//错误的String str String.valueOf(a);方式2:更直接的方式 int a 10;String str a "";(2)…

汽车EDI:如何与奔驰建立EDI连接?

梅赛德斯-奔驰是世界闻名的豪华汽车品牌,无论是技术实力还是历史底蕴都在全球汽车主机厂中居于领先位置。奔驰拥有多种车型,多元化的产品布局不仅满足了不同用户画像的需求,也对其供应链体系有着极大的考验。 本文将为大家介绍梅赛德斯-奔驰乘…

【深入理解计算机系统第3版】有符号数和无符号数转换以及移位运算练习题2.23

题目 考虑下面的C函数&#xff1a; int fun1(unsigned word) {return (int) ((word << 24) >> 24); }int fun2(unsigned word) {return ((int) word << 24) >> 24; } 假设一个采用补码运算的机器上以32位程序来执行这些函数。还假设有符号数值的右移…

代码随想录算法训练营第30天|LeetCode236.二叉树的最小公共祖先

代码随想录算法训练营第30天|LeetCode236.二叉树的最小公共祖先 1、LeetCode236.二叉树的最小公共祖先 236. 二叉树的最近公共祖先 - 力扣&#xff08;LeetCode&#xff09; 自底向上查找&#xff0c;有点难度&#xff01; | LeetCode&#xff1a;236. 二叉树的最近公共祖先_哔…

如何通过ArkTS卡片的Canvas自定义绘制能力实现五子棋游戏卡片

介绍 本示例展示了如何通过ArkTS卡片的Canvas自定义绘制能力实现一个简单的五子棋游戏卡片。 使用Canvas绘制棋盘和黑白棋子的落子。通过卡片支持的点击事件进行交互&#xff0c;让用户在棋盘上进行黑白棋子的对局。通过TS的逻辑代码实现五子棋输赢判定、回退等逻辑计算&…

7 X 24h智能安全运维再升级!Fortinet 全面集成全新 FortiGuard SOCaaS

数字化时代网络安全威胁层出不穷&#xff0c;网络犯罪分子的狡诈攻击手段不断翻新&#xff0c;传统安全防御手段亟需进化。更为棘手的是&#xff0c;网络安全专业人才的匮乏&#xff0c;让众多企业陷入安全运营的困境。为了有效应对这一挑战&#xff0c;Fortinet全新推出FortiG…

3DGS实时高质量大规模场景渲染最新SOTA!

作者&#xff1a;小柠檬 | 来源&#xff1a;3DCV 在公众号「3DCV」后台&#xff0c;回复「原论文」可获取论文pdf 添加微信&#xff1a;dddvision&#xff0c;备注&#xff1a;3D高斯&#xff0c;拉你入群。文末附行业细分群 详细内容请关注3DCV 3D视觉精品课程&#xff1a;…

Qt自定义控件之Battery电池控件

文章目录 前言一、BasicBattery二、Battery控件三、效果总结 前言 在Qt应用程序开发中&#xff0c;自定义控件是一种常见的需求&#xff0c;开发者经常需要根据特定的需求创建定制化的控件来增强用户界面的交互性和美观性。Battery电池控件是一种常见的自定义控件&#xff0c;…

Codeforces Round 932 (Div. 2) ---- F. Andrey‘s Tree ---- 题解

F. Andreys Tree&#xff1a; 题目描述&#xff1a; 思路解析&#xff1a; 我们假设删除任意一个结点后&#xff0c;我们会将整个树切分为k个联通块&#xff0c;那么可以明确的知道我们只需要连接(k-1)条边就可以将这k个联通块重新连为一棵树。 那么最小代价是啥呢? 图解分…

代码随想录day35--动态规划的应用2||01背包理论基础、携带研究材料

01背包理论基础 有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值为 value[i]。每件物品只能用一次&#xff0c;将这些物品装入背包里物品价值总和最大。 这是很标准的背包问题&#xff0c;很多同学看到后很自然的就想到了背包&#xff0c;我们…

ruoyi-nbcio-plus基于vue3的flowable流程设计器主界面升级修改

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…