Pulsar官方文档学习笔记——消息机制

news2024/11/28 8:50:36

pulsar

基于3.x最新官方文档学习记录

概念与架构

典型的推送订阅模式。生产者发送消息,消费者订阅topic消费信息并回应ACK。订阅创建后,Pulsar会保留所有消息。仅消息被所有订阅 成功消费了才会丢弃(可以配置消息保留机制保留一定量) 而且还可以重新给broker投递重新消费

消息

消息是pulsar传递的基本单元。有如下元素组成

组成成分描述
value/data payload消息携带的数据
key消息的键或分区键。消息可以用键标记,对于topic压缩有用
properties消息属性。key-value对的map组成
producer name生产者名
topic nametopic名称
schema version生产消息所用的schema版本
sequence ID每个消息在自己的topic里都有一个有序的序列id,由生产者赋值。ID可以用于去重。如果brokerDeduplicationEnabled设置为true。那么每个消息的sequencee id在主题里是唯一的
message idbookies赋值的,持久化的消息id。在pulsar集群中是唯一的
publish time消息发布时间戳,生产者自动赋值的
event time一个应用程序附加到消息的可选时间戳信息。(业务一半也很少用,自己在消息里用一个字段就完了)

消息有默认大小5MB。可以通过配置修改

  • In the broker.conf file.

    # The max size of a message (in bytes).
    maxMessageSize=5242880
    
  • In the bookkeeper.conf file.

    # The max size of the netty frame (in bytes). Any messages received larger than this value are rejected. The default value is 5 MB.
    nettyMaxFrameSizeBytes=5253120
    
响应

当应用消费并处理完消息后应该回复一个ack代表改消息消费成功。消息会在所有订阅消费完后删除。当然可以配置消息保留策略保留一定量的消息。对于批量的信息,也可以批量ACK

ack方式
  • 单独确认

​ 仅确认选择的消息

consumer.acknowledge(msg);
  • 累计确认

    确认后,该消息之前的消息不会再消费到。但是注意 这个不能用再shared和key_shared 订阅类型。因为这种订阅类型下有多个消费者

consumer.acknowledgeCumulative(msg);
NACK

NACK机制,消费者可以通知broker自己没有处理该消息,需要重新消费。broker会将该消息重新投递给consumer消费

  • 在独占和故障转移订阅下,只能nack最近一条消息
  • 在share和key_shared下可以选择nack某一条消息

对有序订阅类型的订阅(独占,key_shared) nack会导致乱序

可以设置重新投递次数以及投递延迟

例如配置延时在1s~60s

Consumer<byte[]> consumer = pulsarClient.newConsumer()
        .topic(topic)
        .subscriptionName("sub-negative-ack")
        .subscriptionInitialPosition(SubscriptionInitialPosition.Earliest)
        .negativeAckRedeliveryBackoff(MultiplierRedeliveryBackoff.builder()
            .minDelayMs(1000)
            .maxDelayMs(60 * 1000)
            .multiplier(2) // 重试延迟乘数
            .build())
        .subscribe();

消息重新投递和投递延迟如下

Redelivery countRedelivery delay
11 seconds
22 seconds
34 seconds
48 seconds
516 seconds
632 seconds
760 seconds
860 seconds

如果启用了批处理,那么一批数据都会重新投递

ack timeout

默认关闭。超时机制,设置客户端跟踪 消费的时间,如果超过时间限制,会给broker回nack触发重新 投递

可以配合重新投递的设置使用

consumer.ackTimeout(10, TimeUnit.SECOND)
        .ackTimeoutRedeliveryBackoff(MultiplierRedeliveryBackoff.builder()
            .minDelayMs(1000)
            .maxDelayMs(60 * 1000)
            .multiplier(2)
            .build());

The message redelivery behavior should be as follows.

Redelivery countRedelivery delay
110 + 1 seconds
210 + 2 seconds
310 + 4 seconds
410 + 8 seconds
510 + 16 seconds
610 + 32 seconds
710 + 60 seconds
810 + 60 seconds

注意:

  • 如果启用批处理,那么一批数据都会重新投递

  • 自己单独NACK比超时NACK更可取。超时值不好设置,容易重复消费不需要的消息

Retry letter topic

重试队列 用来存储消费失败的消息,并且稍后重试消费。用户可以自定义重试延迟时间。重试队列重试次数超过阈值后,消息就回到死信队列了。

在这里插入图片描述

重试队列消息有一些特殊proerties

REAL_TOPIC = persistent://public/default/my-topic # 源topic
ORIGIN_MESSAGE_ID = 1:0:-1:0 # 原始Message ID
RECONSUMETIMES = 6 # 重试次数
DELAY_TIME = 3000 # 重试间隔

重试队列与延迟投递目的是不一样的,重试队列主要是为了处理失败的消息。延迟投递则是想要在指定时间消费消息

重试队列默认名字<subscriptionname>-RETRY,官方不推荐用。因为如果同一命名空间下的topicx消费有多相同订阅名,那么不同topic他的重试队列名字会是一样的。会导致互相消费

重试队列操作

Map<String, String> customProperties = new HashMap<String, String>();
customProperties.put("custom-key-1", "custom-value-1");
customProperties.put("custom-key-2", "custom-value-2");
consumer.reconsumeLater(msg, customProperties, 3, TimeUnit.SECONDS);

比起nack。重试队列更适合大量重新消费的场景 。重试丢列的消费会持久化到bookie,而nack只会缓存到客户端

dead letter topic

死信队列里一般存储了消费失败的消息。客户可以自定义如何处理这些失败的消息。例如存储到ES里供后面查询,指标监控等

Consumer<byte[]> consumer = pulsarClient.newConsumer(Schema.BYTES)
                .topic("my-topic")
                .subscriptionName("my-subscription")
                .subscriptionType(SubscriptionType.Shared)
                .deadLetterPolicy(DeadLetterPolicy.builder()
                      .maxRedeliverCount(maxRedeliveryCount)
                      .deadLetterTopic("my-dead-letter-topic-name")
                      .build())
                .subscribe();

死信队列默认格式为<subscriptionname>-DLQ与重试队列一样,建议是自定义topic名字

压缩

pulsar支持消息压缩。lz4 zlib zstd snappy都行。比较推荐启用

batching

批处理开启后,生产者会累计发送 。此时backlog siez 代表该批次总数而不是消息总数。批次也会作为一个单元进行存储。

在这里插入图片描述

一般来说,该批次所有消息被确认成功则该批次 确认成功,否则认为失败进行重新投。但pulsar 2.6后就有批量确认。可以将当前索引之前的消息 都Ack了。

chunking

分块,Pulsar可以通过分块来处理大消息。当消息分块启用(对持久化topic才生效),当消息超过指定大小 maxMessageSize消息将会被如下处理。消息结尾默认为OFF

  • 将源消息拆分,并将他们与分块元数据(chunked metadata)按顺序发送
  • broker会将分块的消息存储在一个ledgere中。并用chunkedMessageRate参数来记录该主题分块消息速率。
  • 消费者会缓存所有消息的分块并聚合到接收队列中
  • 从接受队列消费消息

注意:

分块和批处理不能同时开启

消费示例

单个消费者消费连续 分块的消息

在这里插入图片描述

单个消费者消费乱序分块的消息

在这里插入图片描述

Topic

pulsar主题是将数据组织成流的单元。名称为一个特定结构的url

{persistent|non-persistent}://tenant/namespace/topic
Topic name componentDescription
persistent / non-persistent标识主题类型,pulsar支持两种主题,持久化的和非持久化的。默认是持久化的
tenant租户名,pulsar多租户概念
namespace命名空间,每个租户都有多个命名空间
topictopic名字,没什么别的含义

pulsar客户端如果生产或消费一个不存在的topic。pulsar会尝试建立。 但一般不推荐,Pulsar会变得不好维护。最好是部署脚本啥的提前键好,方便统计维护topic

namespace

命名空间是租户下对topic的逻辑分组

Subscriptions

订阅名。每个消费者去订阅topic的时候都需要有个订阅名(如果没有puslar也会默认创建一个)

订阅类型
  • 独占exclusive

    默认的消费方式。消费者独占该订阅名的消费

  • 共享shared

    多个消费者可以使用一个订阅名消费该topic。适合需要动态扩容的消费业务

  • 灾备failover

    主消费者断开后,消息的消费会交给队列中下一个消费者。对于分区的topic。新消费者可能会消费乱序

    对于分区的topic.broker会按照优先级和消费者name字典序进行排序。代理尝试将分区平均分给具有最高优先级的消费者。

    如果分区数小于消费者数,有如下情况。例如2个分区四个消费者。那么每个分区都会有一个激活的消费者消费和三个待消费的消费者

    在这里插入图片描述

    如果分区数大于消费者数。例如九个分区三个消费者。如下图,A负责P0 P3 P6 那么BC就是P0 P3 P6 候选消费的消费者。其余同理

    在这里插入图片描述

    对于未分区的topic

    topic比消费者少

    在这里插入图片描述

    topic比消费者多。例如 A消费 topic1和topic4 那么B就是topic1和topic4的候选

  • key共享key_shared在这里插入图片描述

​ 几个消费者共享消费,但是根据哈希key(可以自己指定,有几种)分流。相同key或ordering key只会 投递给同一个消费者无论如何重新投递。那么当有新消费者加入时,pulsar会记录当前已经读取的消息位置,只有当该位置前的消息都被ack了,才会给新消费者投递消息。那万一其他消费者卡住了怎么办,可以开启allowOutOfOrderDelivery放松下条件,只会有短暂的不符合key_shared的消费情况

​ key_shared 形势下得批处理。如果启用key_shared,要么关闭批处理,要么shengchanzhe 需要启用 key-based batching .起始就是给批次打key。给单条消息打key没啥用,默认得处理方法可能没办法将相同Key的数据打包到相同批次。而消费者消费时,会将第一条数据的key当作该批数据的key从而导致上下文错误

大致消费形式总结如下

在这里插入图片描述

订阅模式

按游标是否是持久型来划分

Subscription modeDescriptionNote
Durable持久型游标,订阅重启,broker重启后会从最近消费的消息消费默认的订阅模式
NonDurable非持久型游标,broker重启后游标会丢失读者的订阅模式本质上是非持久的,因为他无法阻止topic数据的删除

多topic订阅

  • 正则形式 persistent://public/default/finance-.*
  • 列表形式

topic分区

单分区的topic只会被一个broker持有,这限制了该topic的吞吐量。 分区可以将topic分布给多个broker。从而提高吞吐量

在这里插入图片描述

topic分区会均匀分布在集群broker上。分区topic(只能由admin api创建。分区数需要在创建topic时指定)和普通topic工作方式没啥区别。

路由方式

当推送消息给分区的topic时,必须指定路由模式,这决定了该消息推给哪个分区

当前有三种消息路由方式

ModeDescription
RoundRobinPartition如果没有提供key,那么生产者以轮询 的方式在分区上发布消息。但这个轮询不是指针对单个消息。而是设置成一个相同 的批处理延迟边界,一批一批的发给不同分区,确保批处理有效。如果消息有key,那么就会按key散列给不同分区
SinglePartition如果消息没key,那么就随机一个分区发送。如果指定key,那么就会散列到特定分区
CustomPartition自定义路由分区,通过重写客户端接口实现(java的话是MessageRouter)
顺序保证

消息的顺序和路由方式以及msg的key有关,通常情况下,用户希望每个分区保持顺序

如果msg有key,那么 在使用RoundRobin和single模式时候,消息 都会根据哈希(JavaStringHash, murmur3_32Hash,推荐是用后者)路由到特定分区

消息顺序

rdering guaranteeDescriptionRouting Mode and Key
Per-key-partition所有 相同key的消息 会在一个分区按顺序排列使用RoundRobin和single模式并提供key
Per-producer从同一个生产者出来的消息会按序排列需要使用single模式并且消息不附加key

非持久topic

默认情况下,Pulsar会持久化所有没ack的消息。消息 会交给BookKeeper bookies存储。 但pulsar也提供非持久化的 topic。一旦broker挂了或者订阅挂了,期间的消息都找不回来

格式

*non-persistent*://tenant/namespace/topic

非持久化topic一系列行为都只基于内存。非持久化的topic也不会持久化到zk,意味着该元数据zk是不知道的。如果对应broker挂了,非持久化的topic不会分给新的broker。解决方案是将allowAUtoTopicCreation设置为true,allowAutoTOpicCreationTYpe设置为non-partitioned

性能

因为持久化topic需要存磁盘,存状态等操作,总之会比非持久化的topic性能差一些

client api
PulsarClient client = PulsarClient.builder()
        .serviceUrl("pulsar://localhost:6650")
        .build();
String npTopic = "non-persistent://public/default/my-topic";
String subscriptionName = "my-subscription-name";

Consumer<byte[]> consumer = client.newConsumer()
        .topic(npTopic)
        .subscriptionName(subscriptionName)
        .subscribe();

system topic

system topic 主要用于实现某些功能并消除对第三方组件的依赖。例如心跳检测,事务,主题策略,资源组服务等。以心跳检测为例子 ,系统主题进行健康检查 ,可以让生产者,消费者生产/读取heartberat ns下的消息来探测服务是否存活

NamespaceTopicNameDomainCountUsage
pulsar/systemtransaction_coordinator_assign_${id}PersistentDefault 16事务协调
pulsar/system__transaction_log_${tc_id}PersistentDefault 16事务日志
pulsar/systemresource-usageNon-persistentDefault 4资源组服务
host/portheartbeatPersistent1心跳检测
User-defined-ns__change_eventsPersistentDefault 4topic相关事件
User-defined-ns__transaction_buffer_snapshotPersistentOne per namespace事务缓存快照
User-defined-ns${topicName}__transaction_pending_ackPersistent每个订阅有事务的都会有一个事务ack管理

消息重新投递

看前面ack、nack、ack timeout 和重试队列

消息保留和过期

默认情况情况下会立即删除消费者已确认过的消息,并将未确认的消息持久化在消息积压中。不过puslar可以设置更改这种行为。pulsar中的消息保留有两种定义,一种用于非积压消息的持久化。一种用于积压的消息

在puslar中 ,可以设置两种 方式进行消息保留(命名空间级)

  • 进行消息的持久化存储,可以存储 已经ack的消息(针对已ack数据,保留X小时Y大小的数据)
  • 设置消息TTL,过期数据丢弃(针对未ack的数据,ACK)
消息保留策略

在这里插入图片描述

在这里插入图片描述

看这两张图就知道了 。深蓝色是还在保留范围内的数据。浅蓝是不在保留范围内的数据,可以删除的 。白色是 未ack积压的数据

开启消息保留 的时候,必须设置默认的大小限制(defaultRetentionSizeInMB)和时间限制(defaultRetentionTimeInMinutes)

Time limitSize limitMessage retention
-1-1无限制 保留
-1>0在范围内保留
>0-1在范围内保留
00不开启消息保留(默认 )
0>0Invalid
>00Invalid
>0>0超过一个就丢

当然也可以手动设置 topic下消息的保留期限和大小

int retentionTime = 10; // 10 minutes
int retentionSize = 500; // 500 megabytes
RetentionPolicies policies = new RetentionPolicies(retentionTime, retentionSize);
admin.namespaces().setRetention(namespace, policies);
积压配额

backlogs是用来存未ACK数据的。pulsar默认会存储所有 未ACK数据。你也可以控制存储数据的大小和时间。pulsar用配额来限制backlog

在这里插入图片描述

该图展示了pulsar如何限制backlog的数据。超过backlog配额的会启用一定消息保留策略。策略如下

PolicyAction
producer_request_hold超出配额时生产者会保留消息重试直到超出客户端配置的sendTimeoutMs
producer_exception发消息时抛异常,告知已经超出配额
consumer_backlog_eviction超出配额时broker开始丢弃积压数据

设置积压配额

admin.namespaces().setBacklogQuota(namespace, BacklogQuota.builder()
        .retentionPolicy(RetentionPolicy.producer_request_hold)
        .limitSize(2147483648L)
        .limitTime(60 * 60)
        .build());
TTL

默认情况下,puslar会持久化所有未ack的数据。但可以设置TTL来决定什么时候丢弃未ack的数据

pulsar-admin namespaces set-message-ttl my-tenant/my-ns \
--messageTTL 120 # TTL of 2 minutes
admin.namespaces().setNamespaceMessageTTL(namespace, ttlInSeconds);

消息去重

当消息被持久化多次时会进行去重。确保消息在磁盘只存一次,即使该消息被生产者重复发送。

在这里插入图片描述

前两个图是没开启去重,后两个图是开启去重。消息去重有broker层级,ns层级和topic层级

该功能默认是关闭的。如果启用,建议生产者无限尝试发送消息。将sendTImeout配置为0

生产者幂等性

该方式也是消息去重的一种。意味着每条消息仅生成一次。缺点是会将重复数据删除的工作推到应用程序。

重复数据删除和effectively-once语义

消息 重复数据删除使pulsar成为理想的消息传递系统,可以与流处理引擎和其他要提供一次有效处理语义的系统结合使用

消息延迟传递

消息延迟传递可以让消费者延迟一定时间 再消费到消息 。在这个机制中 ,消息存在bookkeeper中,消息发到broker后,DelayedDeliveryTracker在内存中维护时间索引。(timemessageId) 一旦指定延迟结束,消息就会传递给消费者(只能在shared可key-shared订阅使用)。该机制默认启用

注意:

  • 在消息保留机制下,pulsar会 删除 topic前面的ledger(可以理解为一个存储单元), 但不会删除中间的ledgere。意味着如果你发了一条延迟很长的消息。即使该消息后面的消息都被消费了(到期被消费)。由于这条消息卡在中间,那么后面的消息对应的ledger都不会被删除
  • 在启用积压配额的机制下,如果超过配额依旧会触发对应的消息保留策略
  • 在启用TTL机制下,消息到期时,即使是延迟消息也会被删除

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

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

相关文章

裸金属服务器和裸金属云服务器:区别、优势与选择

首先&#xff0c;必须肯定的是&#xff1a;裸金属服务器和裸金属云服务器是有区别的。 ‌ 二者的概述 裸金属服务器&#xff08;‌Bare Metal Server&#xff09;‌是一种物理服务器&#xff0c;‌它直接在硬件上运行&#xff0c;‌没有额外的虚拟化层。‌这意味着每个应用程…

封装通用第三方平台用户表(微信开放平台)

文章目录 一. 注册微信开放平台1.1 开发者资质认证1.2 应用申请1.3 配置应用 二.通用数据库表设计三.入库实体类四. 对接第三方平台4.1 微信开放平台VO对象4.2 通用方法 我们的系统可能要对接很多第三方系统&#xff0c;为了便利用户授权使用和对多平台账户的管理。有必要设计通…

2024 江苏省第二届数据安全技术应用职业技能竞赛 初赛 部分wp

文章目录 一、前言二、参考文章三、题目&#xff08;解析&#xff09;数据安全解题赛1、ds_0602&#xff08;30分&#xff09;2、333.file&#xff08;45分&#xff09;3、pf文件分析&#xff08;35分&#xff09;4、丢失的资料&#xff08;45分&#xff09;5、greatphp&#x…

基于SpringBoot的来访管理系统的设计与实现-

TOC springboot600基于SpringBoot的来访管理系统的设计与实现---论文 绪 论 1.1项目研究的背景 随着科学技术发展&#xff0c;计算机已成为人们生活中必不可少的生活办公工具&#xff0c;在这样的背景下&#xff0c;网络技术被应用到各个方面&#xff0c;为了提高办公生活效…

Android类加载机制简介

一、前言 随着 Android 技术的不断发展&#xff0c;对其内部机制的探索也日益深入。类加载机制作为 Android 运行时环境的核心组成部分之一&#xff0c;影响着应用的性能、安全性以及可扩展性。通过对 Android 类加载机制的研究&#xff0c;开发者可以更好地优化代码结构、提高…

requests请求库入门使用

requests 库是一个功能强大且易于使用的 HTTP 请求库&#xff0c;适用于各种网络请求任务。它简化了 HTTP 请求的发送过程&#xff0c;并提供了丰富的功能来处理各种网络请求和响应。 1.安装 首先&#xff0c;你需要安装 requests 库。如果你还没有安装&#xff0c;可以使用 …

网优学习干货:2.6G仿真操作(1)

2.6G工程建立——整体仿真过程 针对覆盖仿真、速率仿真及蒙特卡洛仿真的说明 覆盖仿真&#xff1a;覆盖仿真主要用于评估网络覆盖性能。基于MassiveMIMO天线、射线传模或经验传模进行覆盖预测&#xff0c;计算链路损耗后&#xff0c;基于栅格分析估算小区覆盖预测的各项指标&am…

前端不同项目使用不同的node版本(Volta管理切换)

前端不同项目使用不同的node版本(Volta管理切换) 使用volta自动切换前端项目的node版本&#xff0c; 每个不同的前端项目&#xff0c;可以使用不同的node版本。Volta这个工具&#xff0c;它允许用户方便地安装、切换和管理不同版本的Node.js&#xff0c;避免了为每个项目手动配…

QT6聊天室项目 核心类与主窗口设计逻辑分析

核心类 核心类设计逻辑 数据结构设计&#xff08;data.h&#xff09; 用户信息 用户ID用户网名用户个人签名用户手机号码用户头像聊天会话信息 会话编号会话名称&#xff08;单聊则是对方网名&#xff0c;群聊则是群名&#xff09;最新消息会话图标&#xff08;单聊对方头像&a…

sortable中el-table拖拽及点击箭头上下移动row

效果 安装 npm install sortablejs --save 引入 import Sortable from "sortablejs"; <el-table:data"tableBody"borderref"tableRef":stripe"true":key"tableKey"><el-table-column type"index" la…

driver.find_element 找不到元素的大坑

前端使用element框架。 登录进去使用开发人员工具能看到元素&#xff0c;复制xpath使用find_element死活找不到。 其中一次复制的xpath 注意红色部分: #先点击一下输入框 driver.find_element(By.XPATH,/html/body/div[1]/section/section/section/main/div/div[1]/div/form/…

迎接 Apple Intelligence.一:首次啟用

年 Apple 各平台的重點新功能&#xff0c;相信非 Apple Intelligence 莫屬。Apple 將會將人工智能整合到 iOS 18、iPadOS 18 和 macOS Squoia。在機械學習和大型語言模型加持下&#xff0c;預計 Siri 的對答會更加自然流暢&#xff0c;而且能直接提供答案&#xff0c;而不像過去…

作业8/21

client cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), socket(new QTcpSocket(this)) {ui->setupUi(this); // 设置 UI 界面// 控件初始状态设置为禁用&#xff0c;防止未连接…

储能系统---德业、禾迈、昱能产品布局及对比

一、公司介绍 德业、昱能、禾迈作为微逆行业的佼佼者&#xff0c;同样代表了中国制造行业的三种技术形态。 德业&#xff1a;代表传统制造行业&#xff0c;转型电力电子领域的代表。 昱能&#xff1a;代表海归人员回国创业&#xff0c;致力于技术本土化的代表。 禾迈&#…

socket 验证字符串是否是有效ip地址

import socketdef is_valid_ip(ip):try:# 尝试将字符串解析为IP地址socket.inet_aton(ip)return Trueexcept socket.error:return False# 测试 print(is_valid_ip("192.168.1.1")) # True print(is_valid_ip("256.256.256.256")) # False print(is_valid…

计算机存储原理——基础

文章目录 存储介质简介1. 光介质存储2. 磁介质存储3. flash(闪存) 硬盘存储原理磁盘组件磁盘是如何存储文件的磁盘的性能 存储接口与存储协议接口类型SCSISCSI的寻址方式windows下查看SCSI寻址 接口与总线与协议的对应关系cpu是如何控制存储的 文件系统常见的文件系统文件系统中…

《数据结构》顺序表+算法代码+动画演示-C语言版

目录 顺序表概念 顺序表初始化 顺序表销毁 顺序表尾插 顺序表尾删 顺序表头删 顺序表头插 顺序表pos位置插入 顺序表pos位置删除 顺序表全部代码如下&#xff1a; 顺序表概念 顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下…

「OC」CAShapeLayer和UIBezierPath —— 抽屉视图之中圆角cell的实现

「OC」CAShapeLayer和UIBezierPath —— 抽屉视图之中圆角cell的实现 文章目录 「OC」CAShapeLayer和UIBezierPath —— 抽屉视图之中圆角cell的实现前言CALayer的子类CAShapeLayerpath 贝塞尔曲线应用实践参考资料 前言 上次我们学习了CALayer的相关知识 &#xff0c;但好像忘…

2024计算机软考报名流程(电脑报名)

1.24年下半年软考报名时间&#xff0c;各省报名时间不一样&#xff0c; 报名时间大概集中在&#xff1a;24年8月19日&#xff5e;24年9月15日&#xff1b; 报名网站&#xff1a;中国计算机技术职业资格网&#xff1b; 广东&#xff1a;2024年8月21日9:00至29日17:00 安徽&#…

全面调研:19类AI Agent框架的对比与分析

代理&#xff08;Agent&#xff09;指能自主感知环境并采取行动实现目标的智能体&#xff0c;即AI作为一个人或一个组织的代表&#xff0c;进行某种特定行为和交易&#xff0c;降低一个人或组织的工作复杂程度&#xff0c;减少工作量和沟通成本。 背景 目前&#xff0c;我们在…