RabbitMQ-Stream(高级详解)

在这里插入图片描述

文章目录

  • 什么是流
  • 何时使用 RabbitMQ Stream?
  • 在 RabbitMQ 中使用流的其他方式
  • 基本使用
    • Offset参数
    • chunk
  • Stream 插件
    • 服务端消息偏移量追踪
    • 示例
  • 示例应用程序
  • RabbitMQ 流 Java API
    • 概述
    • 环境
      • 创建具有所有默认值的环境
      • 使用 URI 创建环境
      • 创建具有多个 URI 的环境
    • 启用 TLS
      • 什么是TLS
      • 创建使用 TLS 的环境
      • 创建信任所有服务器证书进行开发的 TLS 环境
    • 负载均衡
      • 使用自定义地址解析程序始终使用负载均衡器
  • 管理流
    • 创建流
    • 删除流
    • 创建流时设置保留策略
    • 创建流时设置基于时间的保留策略
  • 服务端的偏移量跟踪
    • 自动跟踪
      • 使用默认值的自动跟踪策略
      • 配置自动跟踪策略
    • 手动跟踪
      • 配置手动跟踪策略
  • Kafka简单对比

更多相关内容可查看

什么是流

附官方文档:https://www.rabbitmq.com/docs/streams#overview

RabbitMQ Streams 是一种持久复制的数据结构,可以完成与队列相同的任务:它们缓冲来自生产者的消息,供消费者读取。 但是,流在两个重要方面与队列不同:消息的存储和使用方式。

流对消息的仅追加日志进行建模,这些消息可以重复读取,直到它们过期。 流始终是持久和复制的。对这种流行为的更技术性的描述是“非破坏性消费者语义”。

要从 RabbitMQ 中的流中读取消息,一个或多个使用者订阅该流并根据需要多次读取相同的消息。

流中的数据可以通过 RabbitMQ 客户端库或专用二进制协议插件和关联的客户端使用。 强烈建议使用后一种选项,因为它提供对所有特定于流的功能的访问,并提供最佳吞吐量(性能)。

对于流队列的描述是:高性能、可持久化、可复制、非破坏性消费、只追加写入的日志

何时使用 RabbitMQ Stream?

RabbitMQ Stream被开发用于满足以下消息传递使用情况:

  • 大规模广播(Large fan-outs):当多个消费者应用程序需要读取相同的消息时。
  • 回放/时光旅行(Replay / Time-traveling):当消费者应用程序需要读取整个数据历史记录或从流中的特定点开始时。
  • 吞吐量性能(Throughput performance):当需要比其他协议(AMQP、STOMP、MQTT)更高的吞吐量时。
  • 大型日志(Large logs):当需要存储大量数据,并且内存开销最小化时。

在 RabbitMQ 中使用流的其他方式

使用AMQP 0-9-1协议,可以在RabbitMQ中使用流抽象。与使用流协议从流中消费不同,使用AMQP 0-9-1协议时,可以从“流驱动”的队列中进行消费。所谓的“流驱动”队列是一种特殊类型的队列,它由流基础架构层支持,并经过调整以提供流语义(主要是非破坏性读取)。

使用这样的队列具有以下优点:可以利用流抽象固有的特性(仅追加结构,非破坏性读取),并与任何AMQP 0-9-1客户端库一起使用。考虑到AMQP 0-9-1客户端库的成熟度以及AMQP 0-9-1周围的生态系统,这显然是很有趣的。

但是,通过使用它,无法获得流协议的性能优势,因为流协议是专为性能而设计的,而AMQP 0-9-1是一种更通用的协议。

使用“流驱动”队列无法与流Java客户端一起使用,您需要使用AMQP 0-9-1客户端库。

基本使用

生产消息:

import pika
from pika import BasicProperties
from pika.adapters.blocking_connection import BlockingChannel
from pika.spec import Basic
​
​
STREAM_QUEUE = "stream_queue"
​
connection = pika.BlockingConnection(pika.ConnectionParameters("localhost", 5672, "/"))
channel = connection.channel()
//创建了一个到 RabbitMQ 代理的连接,然后创建了一个通道,并声明了一个持久化的流队列(stream queue),该队列名为 "stream_queue",参数为 {"x-queue-type": "stream"}。
channel.queue_declare(queue=STREAM_QUEUE, durable=True, arguments={"x-queue-type": "stream"})//在循环中,将数字 500 到 599 发布到 "stream_queue" 队列中。
for i in range(500, 600):
    msg = f"{i}".encode()
    channel.basic_publish("", STREAM_QUEUE, msg)
​
channel.close()
connection.close()

消费消息:

import pika
from pika import BasicProperties
from pika.adapters.blocking_connection import BlockingChannel
from pika.spec import Basic
​//channel:通道对象,用于确认消息
//method:Basic.Deliver 对象,包含有关传递消息的元数据。
//properties:BasicProperties 对象,包含消息的属性。
//body:消息的内容,以字节形式表示。
​
def msg_handler(channel: BlockingChannel, method: Basic.Deliver, properties: BasicProperties, body: bytes):
    msg = f"获取消息:{body.decode()}"
    print(msg)
    channel.basic_ack(method.delivery_tag)
​
​
STREAM_QUEUE = "stream_queue"

connection = pika.BlockingConnection(pika.ConnectionParameters("localhost", 5672, "/"))
channel = connection.channel()
channel.queue_declare(queue=STREAM_QUEUE, durable=True, arguments={"x-queue-type": "stream"})
​​//创建了一个到 RabbitMQ 代理的连接,然后创建了一个通道,并声明了一个持久化的流队列(stream queue),该队列名为 "stream_queue",参数为 {"x-queue-type": "stream"}。
channel.basic_qos(prefetch_count=50)
//设置了消费者的 QoS(Quality of Service),限制了每次从队列中获取的消息数量为 50 条。
channel.basic_consume(STREAM_QUEUE, on_message_callback=msg_handler, arguments={"x-stream-offset": 290})
//订阅了 "stream_queue" 队列,并指定了消息处理函数 msg_handler,同时设置了消费者的流偏移量为 290。
channel.start_consuming()//开始消费消息
channel.close()
connection.close()
//关闭了通道和连接。

Offset参数

附官网地址:https://www.rabbitmq.com/blog/2021/09/13/rabbitmq-streams-offset-tracking

偏移量是描述某种位置或相对位置的数值

绝对偏移量没有任何实际意义,只是一种技术概念。因此,当应用程序首次连接到流时,它不太可能使用偏移量,而更倾向于使用高级概念,如流的开头或结尾,甚至流中的某个时间点。

RabbitMQ Streams 支持除绝对偏移量之外的不同偏移量规范:.first、.last、.next、.next 和 timestamp。

对于流的“结尾”,有两种偏移量规范:.next 表示下一个将被写入的偏移量。如果消费者在 .next 处连接到流,而且没有人发布消息,那么消费者将不会接收到任何消息。只有当新消息到来时,消费者才会开始接收消息。

.last 表示“从最后一批消息开始”。,因为出于性能考虑,消息是批量处理的。

下图显示了流中的偏移量规范。

可以通过x-stream-offset来控制读取消息的位置

在这里插入图片描述

chunk

chunk就是stream队列中用于存储和传输消息的单元,一个chunk包含几条到几千条不等的消息。

Stream 插件

以上只是对Stream类型队列的简单使用,API和普通队列没有差异。若要体验完整的Stream队列特性,如:服务端消息偏移量追踪,需要启用stream插件,不启用和启用流插件功能特性对比
可参考: Stream Core vs Stream Plugin。

服务端消息偏移量追踪

Stream提供了服务端消息偏移量追踪,客户端断开重连后可以从上次消费的下一个位置开始消费消息。

示例

使用docker启动一个rabbitmq服务并启用stream插件:

docker run \
 -d --name rabbitmq \
 --hostname=node1 \
 --env=RABBITMQ_NODENAME=r1 \
 --env=RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS='-rabbitmq_stream advertised_host localhost' \
 --volume=rabbit_erl:/var/lib/rabbitmq \
 -p 15672:15672 -p 5672:5672 -p 5552:5552 \
 rabbitmq:3-management
 
docker exec rabbitmq rabbitmq-plugins enable rabbitmq_stream

这里使用rstream客户端来收发消息:

import asyncio
​
from rstream import (
    Producer
)STREAM_QUEUE = "stream_queue"
CONSUMER_NAME = "py"
​
​
async def pub():
    async with Producer("localhost", 5552, username="guest", password="guest") as producer:
        await producer.create_stream(STREAM_QUEUE)
        for i in range(100, 300):
            await producer.send(STREAM_QUEUE, f"{i}".encode())
​
​
if __name__ == "__main__":
    asyncio.run(pub())

消费消息:

import asyncio
​
from rstream import (
    AMQPMessage,
    Consumer,
    ConsumerOffsetSpecification,
    MessageContext,
    OffsetType, OffsetNotFound
)STREAM_QUEUE = "stream_queue"
CONSUMER_NAME = "py"
​
​
async def msg_handler(msg: AMQPMessage, context: MessageContext):
    print(msg)
    await context.consumer.store_offset(STREAM_QUEUE, CONSUMER_NAME, context.offset)
​
​
async def sub():
    consumer = Consumer("localhost", 5552, username="guest", password="guest")
    await consumer.start()
    try:
        offset = await consumer.query_offset(STREAM_QUEUE, CONSUMER_NAME)
    except OffsetNotFound:
        offset = 1
    await consumer.subscribe(STREAM_QUEUE, msg_handler,
                             offset_specification=ConsumerOffsetSpecification(OffsetType.OFFSET, offset),
                             subscriber_name=CONSUMER_NAME)
    await consumer.run()
​
​
if __name__ == "__main__":
    asyncio.run(sub())

示例应用程序

发布一些消息,然后注册 消费者对它们进行一些计算

创建环境

System.out.println("Connecting...");
//用于创建环境Environment#builder
Environment environment = Environment.builder().build();  
String stream = UUID.randomUUID().toString();
//创建流
environment.streamCreator().stream(stream).create();  

发布消息

System.out.println("Starting publishing...");
int messageCount = 10000;
CountDownLatch publishConfirmLatch = new CountDownLatch(messageCount);
//创建ProducerEnvironment#producerBuilder
Producer producer = environment.producerBuilder()  
        .stream(stream)
        .build();
IntStream.range(0, messageCount)
        .forEach(i -> producer.send(  //发送消息Producer#send(Message, ConfirmationHandler)
                producer.messageBuilder()                    
                    .addData(String.valueOf(i).getBytes())   
                    .build(),                                
                confirmationStatus -> publishConfirmLatch.countDown()  //	消息发布确认倒计时
        ));
publishConfirmLatch.await(10, TimeUnit.SECONDS);  //等待所有发布确认到达
producer.close();  //	关闭生产者
System.out.printf("Published %,d messages%n", messageCount);

消费消息

System.out.println("Starting consuming...");
AtomicLong sum = new AtomicLong(0);
CountDownLatch consumeLatch = new CountDownLatch(messageCount);
//创建ConsumerEnvironment#consumerBuilder
Consumer consumer = environment.consumerBuilder()  
        .stream(stream)
        .offset(OffsetSpecification.first()) //从流的开头开始消费
        .messageHandler((offset, message) -> {  //设置处理消息的逻辑
            //将消息正文中的值添加到总和
            sum.addAndGet(Long.parseLong(new String(message.getBodyAsBinary())));  
      		//每条消息倒计时
            consumeLatch.countDown();  
        })
        .build();
//等待所有消息到达
consumeLatch.await(10, TimeUnit.SECONDS);  

System.out.println("Sum: " + sum.get());  
//关闭消费者
consumer.close();

删除流并关闭环境

environment.deleteStream(stream);  //删除流
environment.close();  //关闭环境

RabbitMQ 流 Java API

概述

RabbitMQ Stream 插件、发布消息和 使用消息。有 3 个主要接口:

  • com.rabbitmq.stream.Environment用于连接到节点,并可选择管理流。
  • com.rabbitmq.stream.Producer以发布消息。
  • com.rabbitmq.stream.Consumer以使用消息。

环境

创建具有所有默认值的环境

Environment environment = Environment.builder().build();  //创建将连接到 localhost:5552 的环境
// ...
environment.close(); //使用后关闭环境

使用 URI 创建环境

Environment environment = Environment.builder()
        .uri("rabbitmq-stream://guest:guest@localhost:5552/%2f")  
        .build();//	使用该方法指定要连接到的 URIuri

创建具有多个 URI 的环境

Environment environment = Environment.builder()
        .uris(Arrays.asList(                     
                "rabbitmq-stream://host1:5552",
                "rabbitmq-stream://host2:5552",
                "rabbitmq-stream://host3:5552")
        )
        .build();//		使用该方法指定多个 URIuris

启用 TLS

什么是TLS

TLS的主要功能包括:

  • 加密(Encryption):TLS使用加密算法对传输的数据进行加密,使其在传输过程中不易被窃听或解读。常见的加密算法包括对称加密算法(如AES)和非对称加密算法(如RSA)。
  • 身份验证(Authentication):TLS通过数字证书验证通信双方的身份,确保与对方建立安全连接的是预期的实体,而不是攻击者。
  • 完整性保护(IntegrityProtection):TLS使用消息摘要算法(如HMAC)对传输的数据进行签名,以确保数据在传输过程中未被篡改或损坏。

创建使用 TLS 的环境

X509Certificate certificate;
try (FileInputStream inputStream =
            new FileInputStream("/path/to/ca_certificate.pem")) {
    CertificateFactory fact = CertificateFactory.getInstance("X.509");
    certificate = (X509Certificate) fact.generateCertificate(inputStream); 
//这部分代码加载了一个X.509格式的CA证书文件(/path/to/ca_certificate.pem),这通常是由可信的证书颁发机构(CA)签发的。CA证书用于验证服务器的身份,并建立信任关系。
}

SslContext sslContext = SslContextBuilder
    .forClient()
    .trustManager(certificate)  //	将 Netty 配置为信任 CA 证书SslContext
    .build();
//在这里,我们使用加载的CA证书构建了一个SSL上下文(SslContext),该上下文用于客户端的SSL/TLS通信。我们将加载的CA证书作为信任管理器传递给SslContextBuilder,以便客户端能够验证服务器证书的有效性。

Environment environment = Environment.builder()
    .uri("rabbitmq-stream+tls://guest:guest@localhost:5551/%2f")  //在环境 URI 中使用 TLS 方案
    .tls().sslContext(sslContext)  //	在环境配置中设置SslContext
    .environmentBuilder()
    .build();
//在这里,我们创建了RabbitMQ Stream的环境配置。通过URI指定了连接地址和凭据信息。通过.tls().sslContext(sslContext)配置了TLS环境,将之前创建的SSL上下文应用于RabbitMQ Stream连接,确保了安全的通信。

创建信任所有服务器证书进行开发的 TLS 环境

Environment environment = Environment.builder()
    .uri("rabbitmq-stream+tls://guest:guest@localhost:5551/%2f")
    .tls().trustEverything()  //信任所有服务器证书
    .environmentBuilder()
    .build();

负载均衡

使用自定义地址解析程序始终使用负载均衡器

Address entryPoint = new Address("my-load-balancer", 5552);  //设置负载均衡器地址
Environment environment = Environment.builder()
    .host(entryPoint.host())  //使用负载均衡器地址进行初始连接
    .port(entryPoint.port())  //使用负载均衡器地址进行初始连接
    .addressResolver(address -> entryPoint)  //略元数据提示,始终使用负载均衡器
    .build();

管理流

创建流

environment.streamCreator().stream("my-stream").create();

删除流

environment.deleteStream("my-stream");

创建流时设置保留策略

environment.streamCreator()
        .stream("my-stream")
        .maxLengthBytes(ByteCapacity.GB(10))  //将最大大小设置为 10 GB
        .maxSegmentSizeBytes(ByteCapacity.MB(500))  //将段大小设置为 500 MB
        .create();

创建流时设置基于时间的保留策略

environment.streamCreator()
        .stream("my-stream")
        .maxAge(Duration.ofHours(6))  //将最长期限设置为 6 小时
        .maxSegmentSizeBytes(ByteCapacity.MB(500))  //将段大小设置为 500 MB
        .create();

服务端的偏移量跟踪

RabbitMQ Stream 提供了服务器端的偏移量跟踪功能。这意味着消费者可以跟踪它在流中所达到的偏移量。它允许消费者的新实例在其离开的地方重新开始消费。所有这些操作都不需要额外的数据存储,因为代理服务器存储了偏移量跟踪信息。

偏移量跟踪分为两个步骤:

  • 消费者必须具有名称。名称是通过 ConsumerBuilder#name(String)方法设置的。名称可以是任意值(长度不超过256个字符),并且应该是唯一的(从应用程序的角度来看)。需要注意的是,无论是客户端库还是代理服务器都不强制名称的唯一性:如果两个
    Java 实例共享相同的名称,它们的偏移量跟踪可能会交错,这通常不符合应用程序的预期。
  • 消费者必须定期存储其到目前为止已达到的偏移量。偏移量存储的方式取决于跟踪策略:自动或手动

自动跟踪

自动跟踪策略具有以下可用设置:

  • 存储前的消息计数:客户端将在指定数量的消息之后存储偏移量,即在消息处理程序执行之后。默认值是每10,000条消息存储一次。
  • 刷新间隔:客户端将确保在指定的间隔内存储最后接收到的偏移量。这可以避免在空闲时存在未存储的待处理偏移量。默认值为5秒。

使用默认值的自动跟踪策略

Consumer consumer =
    environment.consumerBuilder()
        .stream("my-stream")
        .name("application-1")   //设置使用者名称
        .autoTrackingStrategy()   //使用默认值的自动跟踪策略
        .builder()
        .messageHandler((context, message) -> {
          // message handling code...
        })
        .build();

配置自动跟踪策略

Consumer consumer =
    environment.consumerBuilder()
        .stream("my-stream")
        .name("application-1") //设置使用者名称  
        .autoTrackingStrategy()  //使用自动跟踪策略 
            .messageCountBeforeStorage(50_000)  //存储每 50,000 条消息 
            .flushInterval(Duration.ofSeconds(10)) //确保至少每 10 秒存储一次偏移量  
        .builder()
        .messageHandler((context, message) -> {
          // message handling code...
        })
        .build();

手动跟踪

配置手动跟踪策略

Consumer consumer =
    environment.consumerBuilder()
        .stream("my-stream")
        .name("application-1")   //设置使用者名称
        .manualTrackingStrategy()   //使用默认值的手动跟踪
            .checkInterval(Duration.ofSeconds(10))  //每 10 秒检查一次上次请求的偏移量 
        .builder()
        .messageHandler((context, message) -> {
          // message handling code...

          if (conditionToStore()) {
            context.storeOffset();  //在某种条件下存储电流偏移 
          }
        })
        .build();

Kafka简单对比

rabbitmqkafka
生产/消费者queuetopic
底层消息存储chunkpartition

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

青否数字人直播源码代理端后台操作步骤!

青否数字人直播源码代理端后台,我们将详细介绍一下数字人的代理端后台的详细操作步骤! 1.代理端入口 2.代理后台预览 基本设置,账号管理,资金管理,克隆端 。 2.1基本设置 设置一些账号的基本信息包括名称,l…

时尚解决方案来袭:几分钟即可生成高清商拍大片

在时尚行业,视觉展示的重要性不可小觑。商品图片不仅代表品牌的风格调性,而且直接影响消费者的购买行为。可以说,视觉营销在服装行业中的地位至关重要。 尽管如此,视觉营销的传统产出渠道——商业摄影,因其高成本、复杂…

安全生产信息化平台:高效构建安全台账管理体系

随着科技的飞速发展,信息化已成为推动企业安全生产管理的重要手段。传统的安全台账管理方式,涉及记录、整理、汇编等多个繁琐环节,不仅耗时费力,而且在查找和检索时也不便。安全生产信息化平台的出现,为企业提供了全新…

实验12 路由重分布

实验12 路由重分布 一、 原理描述二、 实验目的三、 实验内容四、 实验配置五、 实验步骤 一、 原理描述 在大型网络的组建过程中,隶属不同机构的网络部分往往会根据自身的实际情况来选用路由协议。例如,有些网络规模很小,为了管理简单&…

yolov8摔倒检测(包含数据集+训练好的模型)

基于先进的YOLOv8模型,实现了一套高效可靠的人体摔倒检测系统。YOLOv8作为YOLO系列的最新成员,以其卓越的检测速度和准确性,在计算机视觉领域尤其是目标检测任务中表现出色。本系统不仅能够实时处理视频流或监控画面,还能对静态图…

RAG 进阶 LlamaIndex多文档

前言 我们一起来学习LlamaIndex功能更完善的多文档RAG,大家可以参照官方文档来对比学习。 Advanced RAG - LlamaIndex Multi-Doc Agent 单个文档RAG Agent流程 我们结合上图来理解一下文档的处理流程。Document就是文档,它会被解析成多个数据节点(Node),类似RAG …

小程序开发中部署SSL证书的详细指南

在小程序开发中部署SSL证书主要是为了实现HTTPS安全连接,确保用户与服务器之间的数据传输是加密的。以下是部署SSL证书的一般步骤,具体操作可能会根据你的服务器提供商和所选的SSL证书类型有所不同: 1. 获取SSL证书 PC点此申请:…

ArduPilot固件自定义参数

文章目录 前言一、修改Parameters.h二、修改Parameters.cpp三、修改config.h四、添加打印五、测试 前言 ROVER4.4 本教程是根据apm官网wiki操作的,有英语基础的可以直接参考官方教程 一、修改Parameters.h 修改Parameters.h,添加如下: k_…

深度遍历-求“岛屿数量”

一、问题描述 二、解题思路 1.设置一个对应的boolean二维数组 isfind[][] ,用来标记已经遍历过的“岛屿” 2.使用双层循环遍历岛屿(grid)二维数组,当遇到 isfind[i][j]false 时表示遇到一个新岛屿 3.当遇到新岛屿时进行深度递归…

小程序中如何设置实体会员卡和线上会员卡一样

在小程序中给客户发电子会员卡,是非常方便和快捷的。除了发放电子会员卡,有些商家还希望能够发放实体会员卡。但实体会员卡如何与小程序中的会员卡号一一对应,是一个重要的问题。下面就具体介绍怎么设置实体会员卡和线上会员卡一样。 1. 领取…

包装类:基本数据类型对应的对象

integer 底层原理: 自动装箱与拆箱(JDK5以后) 成员方法:类型转换最重要 改写键盘录入:利用nextline

HX519 防倒流数据线芯片IC

一般概述 苹果iPhone防倒流数据线芯片,可完美支持iPhone、iPad、iPod等8针闪电接口的数据传输同步功能及充电功能。 特点 ❥集成度高,极少的外围元器件。 ❥电路简单,价格优势明显。 ❥稳定性高,兼容性强。 ❥与市面上普通…

新渠道+1!TDengine Cloud 入驻 Azure Marketplace

近日,TDengine Cloud 正式入驻微软云 Marketplace,为全球更多用户带来全托管的时序数据处理服务。这一举措也丰富了 TDengine 的订阅渠道,为用户提供了极大的便捷性。现在,您可以通过微软云 Marketplace 轻松订阅并部署 TDengine …

生产环境部署meilisearch(Running a self-hosted Meilisearch project in production)

官网的第一手资料学新技术:meilisearch官方文档 安装的官网地址:meilisearch安装的官网 部署在生产环境的指导:meilisearch部署在生产环境的指导 Elasticsearch 做为老牌搜索引擎,功能基本满足,但复杂,重…

导出 Whisper 模型到 ONNX

前言 在语音识别领域,Whisper 模型因其出色的性能和灵活性备受关注。为了在更多平台和环境中部署 Whisper 模型,导出为 ONNX 格式是一个有效的途径。ONNX(Open Neural Network Exchange)是一个开放格式,支持不同的深度…

TPM管理对于提高设备综合效率(OEE)有哪些帮助?

在当今高度自动化的生产环境中,设备综合效率(OEE)是衡量企业生产效率的关键指标。而TPM(全面生产维护)设备管理作为一种先进的设备管理方法,正成为众多企业提升OEE、优化生产流程的重要工具。本文将详细探讨…

爱奇艺万能联播无法启动的方法(好用)

winR输入 %appdata%\IQIYI Video 会打开爱奇艺的打开文件夹 点开就能打开爱奇艺万能联播啦啦啦啦啦

DDei在线设计器-属性编辑器

DDei-Core-属性编辑器 DDei-Core-属性编辑器插件包含了文本、大文本、数值、下拉、单选、勾选以及颜色等属性编辑。 图形和属性共同构成一个完整的定义,属性编辑器就是编辑属性值的控件。当选中图形实例时,属性面板就会展现当前实例的所有属性以及属性编…

攻防世界:Misc 解析(一)

前言 攻防世界是一个CTF(Capture The Flag)比赛平台,提供了一系列网络安全挑战题目,供安全爱好者进行实战演练和技术提升。 攻防世界的题目种类丰富多样,涵盖了网络安全领域的多个方面,包括但不限于Web安…

PWN环境配置

虚拟机安装 镜像下载网站(http://old-releases.ubuntu.com/releases/)虚拟机建议硬盘 256 G 以上,内存也尽量大一些。硬盘大小只是上界,256 G 不是真就占了 256G,而后期如果硬盘空间不足会很麻烦。lsb_release -a查看版本更换 ubuntu 镜像源…