RocketMQ原理—5.高可用+高并发+高性能架构
大纲
1.RocketMQ的整体架构与运行流程
2.基于NameServer管理Broker集群的架构
3.Broker集群的主从复制架构
4.基于Topic和Queue实现的数据分片架构
5.Broker基于Pull模式的主从复制原理
6.Broker层面到底如何做到数据0丢失
7.数据0丢失与写入高并发的取舍
8.RocketMQ读写分离主从漂移设计
9.RocketMQ为什么采取惰性读写分离模式
10.Broker数据与服务是否都实现高可用了
11.Broker数据与服务的数据一致性设计
12.Broker基于Raft协议的主从架构设计
13.Raft协议的Leader选举算法介绍
14.Broker基于状态机实现的Leader选举
15.Broker基于DLedger的数据写入流程
16.Broker引入DLedger后的存储兼容设计
17.Broker主从节点之间的元数据同步
18.Broker基于Raft协议的主从切换机制
19.Consumer端队列负载均衡分配机制
20.Consumer消息拉取的挂起机制分析
21.Consumer的处理队列与并发消费
22.Consumer处理成功后的消费进度管理
23.Consumer消息重复消费原理剖析
24.Consumer处理失败时的延迟消费机制
25.ConsumerGroup变动时的重平衡机制
1.RocketMQ的整体架构与运行流程
2.基于NameServer管理Broker集群的架构
3.Broker集群的主从复制架构
4.基于Topic和Queue实现的数据分片架构
5.Broker基于Pull模式的主从复制原理
(1)Broker主从复制的Push模式和Pull模式
(2)Broker基于Pull模式的主从复制原理
(3)Push消费模式 vs Pull消费模式
(1)Broker主从复制的Push模式和Pull模式
Push同步模式:Producer往Broker主节点写入数据后,Broker主节点会主动把数据推送Push到Broker从节点里。
Pull同步模式:Producer往Broker主节点写入数据后,Broker主节点会等待从节点发送拉取数据的Pull请求。Broker主节点收到从节点的拉取数据请求后,才会把数据发送给从节点。
其实,Broker发送消息给Consumer进行消费,同样也是有两种模式:Push模式和Pull模式。
Push消费模式:就是Broker会主动把消息发送给消费者,消费者是被动接收Broker推送过来的消息,然后进行处理。
Pull消费模式:就是Broker不会主动推送消息给消费者,而是消费者主动发送请求到Broker去拉取消息,然后进行处理。
(2)Broker基于Pull模式的主从复制原理
Broker主节点在启动后,会监听来自从节点的连接请求。Broker从节点在启动后,会主动向主节点发起连接请求。
首先,当Broker主节点和从节点建立好网络连接后,各自会初始化一些组件。Broker主节点会创建并初始化一个HAConnection组件,专门用于处理从节点的同步请求。Broker从节点会创建并初始化两个组件,一个是HAClient主从同步请求线程、一个是HAClient主从同步响应线程。
然后,Broker从节点的HAClient主从同步请求线程,会不断发送主从同步请求,到主节点的HAConnection组件,期间会带上从节点向主节点已拉取的最大的物理偏移量max offset。
接着,主节点的HAConnection组件便会到磁盘文件取出最大物理偏移量max offset之后的数据,然后返回给从节点。
之后,从节点的HAClient主从同步响应线程便会对收到的这些max offset之后的数据进行处理,写入到其磁盘文件中。
(3)Push消费模式 vs Pull消费模式
一个消费者组内的多台机器会分别负责一部分MessageQueue的消费。那么每台机器就必须要连接到对应的Broker,尝试消费里面的MessageQueue对应的消息。此时就涉及到两种消费模式了:Push消费模式和Pull消费模式。
实际上,这两个消费模式本质是一样的,都是消费者机器主动发送请求到Broker机器去拉取一批消息来处理。Push消费模式底层是基于Pull消费模式来实现的,只不过它的名字叫做Push而已。意思是Broker会尽可能实时的把新消息交给消费者机器来进行处理,它的消息时效性会更好。
一般使用RocketMQ时,消费模式通常选择Push模式,因为Pull模式的代码写起来更加复杂和繁琐,而且Push模式底层本身就是基于消息拉取的方式来实现的,只不过时效性更好而已。
Push模式的实现思路:当消费者发送请求到Broker去拉取消息时,如果有新的消息可以消费就马上返回一批消息到消费机器去处理,处理完之后会接着立刻发送请求到Broker机器去拉取下一批消息。
所以消费者在Push模式下处理完一批消息,会马上发起请求拉取下一批消息。消息处理的时效性非常好,看起来就像Broker一直不停地推送消息给消费者一样。
此外,Push模式下有一个请求挂起和长轮询机制。当拉取消息的请求发送到Broker,结果发现没有新的消息给处理时,就会让请求线程挂起,默认是挂起15秒。然后这个期间Broker会有后台线程每隔一会儿就去检查一下是否有新的消息。另外在这个挂起过程中,如果有新的消息到达了会主动唤醒挂起的线程,然后把消息返回给消费者。
当然消费者进行消息拉取的底层源码是比较复杂的,涉及大量细节,但核心思路大致如此。需要注意:哪怕是常用的Push消费模式,本质也是消费者不停地发送请求到Broker去拉取一批一批的消息。
6.Broker层面到底如何做到数据0丢失
第一种场景:Broker主节点JVM进程崩溃 ==> 数据在PageCache,即便异步刷盘也不影响。
第二种场景:Broker主节点服务器崩溃 ==> 数据在PageCache,异步刷盘会丢失,同步刷盘不丢失。
第三种场景:Broker主节点服务器硬盘坏掉 ==> 如果Broker从节点数据还没同步最新的,就会丢失。
Broker要做到数据0丢失,需要同步刷盘 + 同步复制。也就是Producer向Broker主节点写数据时,Broker主节点返回写入成功响应的前提是:主节点落盘成功 + 从节点同步成功。
7.数据0丢失与写入高并发的取舍
Producer将一条消息发送给Broker:
若Broker采用异步刷盘 + 异步复制,那么基本几ms ~ 几十ms即可返回写入成功的响应。
若Broker采用同步刷盘 + 异步复制,那么需要几十ms ~ 几百ms才可返回写入成功的响应。
若Broker采用同步刷盘 + 同步复制,那么需要几百ms ~ 几s才可返回写入成功的响应。
同步复制需要等待:从节点发送Pull请求 + 读磁盘数据 + 网络发送数据到从节点 + 从节点写数据到磁盘 + 等下一次Pull请求。
Broker默认采用的是异步刷盘 + 异步复制。
8.RocketMQ读写分离主从漂移设计
(1)优先从Broker主节点消费消息
(2)读写分离主从漂移的规则
(1)优先从Broker主节点消费消息
RocketMQ是不倾向让Producer和Consumer进行读写分离的,而是倾向让写和读都由主节点来负责。从节点则用于进行数据复制和同步来实现热备份,如果主节点挂了才会选择从节点进行数据读取。所以Consumer默认下会消费Broker主节点的ConsumeQueue里的消息。
(2)读写分离主从漂移的规则
如果Broker主节点过于繁忙,比如积压了大量写和读的消息超过本地内存的40%,那么当Consumer向Broker主节点发起一次拉取消息的请求后,Broker主节点会通知Consumer下一次去该Broker的某个从节点拉取消息。
而当Consumer向Broker从节点拉取消息一段时间后,从节点发现自己本地消息积压小于本地内存30%,拉取消息很顺利,那么Broker从节点会通知Consumer下一次回到Broker主节点去拉取消息。
9.RocketMQ为什么采取惰性读写分离模式
(1)什么是惰性读写分离模式
(2)MQ要实现真正的读写分离比较麻烦
(3)Broker从节点每隔10秒同步消费进度
(1)什么是惰性读写分离模式
惰性读写分离其实就是上面说的读写分离主从漂移。这种漂移指的是:主从机器对外提供一个完整的服务,客户端有时候访问主、有时候访问从。
惰性读写分离不属于彻底的读写分离,从节点的数据更多时候是用于备用。在以下两种情况下,Consumer才会选择到从节点去读取消费数据。
情况一:如果主节点过于繁忙,积压没有消费的消息太多,已经超过本地内存40%。此时主节点可能出现大量读写线程并发运行,机器运行效率可能已经降低,来不及处理这么多的请求,那么主节点就会让消费请求漂移到从节点去读取消费数据。如果在从节点消费得非常好,消息的积压数量很快下降到从节点本地内存的30%以内,就又会让Consumer漂移回主节点消费。
情况二:如果主节点崩溃了,那么Consumer也只能到从节点去读取数据进行消费。
(2)MQ要实现真正的读写分离比较麻烦
RocketMQ作为一个MQ,一个Topic对应多个Queue,可以认为支持去从节点读取数据进行消费。
Kafka作为一个MQ,一个Topic对应多个Partition,不同节点组成Leader和Follower主从结构进行数据复制,不支持去从节点读取数据进行进行。
MQ作为一个特殊的中间件系统,它要维护每个Consumer对一个Queue/Partition的消费进度。如果要实现真正的读写分离,那么维护这个消费进度就会非常麻烦。
比如在从节点上进行读取消费时,一旦这个从节点宕机,此时主节点和其他从节点是不知道该从节点的消费进度的,消费进度要进行集中式保存就比较麻烦。
所以考虑到消费进度的维护和保存,通常各个MQ都会让消费者在主节点进行读和写,这样就可以简单地对消费进度进行集中式维护和存储。
(3)Broker从节点每隔10秒同步消费进度
由于Consumer向RocketMQ的Broker主节点进行消费时,有时候会漂移到Broker从节点进行消费。所以Broker从节点会每隔10s去Broker主节点进行元数据同步,比如同步给主节点最新的消费进度。
由此可见,RocketMQ采用惰性读写分离,主要是为了避免维护不同消费者组去不同从节点消费时产生的复杂的消费进度。
10.Broker数据与服务是否都实现高可用了
(1)RocketMQ4.5.0之前
(2)RocketMQ4.5.0之后
(1)RocketMQ4.5.0之前
Broker主节点崩溃后,是没有高可用主从切换机制的,从节点只用于热备份,保证大部分的数据不会丢失而已。
由于Broker提供的服务就两个:一个是写数据、一个是读数据。所以此时主节点崩溃后,只能靠从节点提供有限的数据和服务了,即只能提供读数据服务而不能提供写数据服务。对应的Producer都会出现写数据失败,但是Consumer可以继续去从节点读取数据进行有限的消费,数据消费完就没了。
此外主节点崩溃后,从节点可能存在有些最新的数据没来得及同步过来,出现数据丢失的问题,所以数据和服务没有实现高可用。
(2)RocketMQ4.5.0之后
实现了主从同步 + 主从切换的高可用机制,保证数据和服务都是高可用的。
注意:在RocketMQ4.5.0以前的老版本,只是实现了单纯的主从复制,只能做到大部分数据不丢失,效果不是特别好。某个Broker分组内的主节点挂掉后,从节点是没法接管主节点的工作的。
11.Broker数据与服务的数据一致性设计
要实现主从数据强一致同步:
情况一:如果主从同步采用Pull模式
那么Broker主节点就要等待从节点过来Pull数据,从而增加Producer的写请求耗时,此时整个写请求的性能损耗比较大。
情况二:如果主从同步采用Push模式
Broker主节点将消息写入PageCache后,就Push给从节点进行同步,那么写请求只要等待从节点的Push成功即可返回。Broker从节点继续采取异步刷盘的策略,它收到主节点Push的消息后,直接写入PageCache就返回给主节点。此时整个写请求的性能损耗比较小。
12.Broker基于Raft协议的主从架构设计
如果基于Raft协议,那么一组Broker最少需要使用三台机器。
一.当这3台Broker启动后
会基于Raft协议进行Leader选举,选举出的Leader便会成为Broker主节点。
二.当Producer往Broker主节点发起写请求时
Broker主节点首先会将新消息先写入到OS的PageCache中,接着将新消息同步Push到其余两台从节点。由于基于Raft协议,所以只要Broker主节点发现过半数Broker节点(包括它自己)写入新消息成功,那么Broker主节点就可以返回写入成功。而Broker从节点收到主节点的Push新消息请求后,也是首先写入OS的PageCache,然后就直接返回写入成功给Broker主节点。
(3)当Broker主节点宕机后
剩余的两台Broker从节点便会根据Raft协议进行Leader选举,选举其中一台Broker作为新的主节点。这样一个Broker主节点 + 一个Broker从节点,依然可以满足Raft协议,继续提供写服务和保证数据及服务的高可用。
13.Raft协议的Leader选举算法介绍
说明一:各个节点在启动时都是Follower。
说明二:每个Follower都会给自己设置一个150ms~300ms之间的一个随机时间,可以理解为一个随机的倒计时时间。也就是说,有的Follower可能倒计时是150ms、有的Follower可能倒计时是200ms,每个Follower的倒计时一般不一样。这时必然会存在一个Follower,它的倒计时是最小的,它会最先到达倒计时的时间。
说明三:第一个完成倒计时的Follower会把自己的身份转变为Candidate,变成一个Leader候选者,会开始竞选Leader。于是它会给自己投票想成为Leader,此外它还会发送请求给其他节点表示它完成了一轮投票,希望对方也投票给自己。
说明四:其他还处于倒计时中的Follower节点收到这个请求后,如果发现自己还没给其他Candidate投过票,那么就把它自己的票投给这个Candidate,并发送请求给其他节点进行通知。如果发现自己已经给其他Candidate投过票,那么就忽略这个Candidate发送过来的请求。
说明五:当某个Candidate发现自己的得票数已超半数quorum,那么它就成为Leader了,这时它会向其他节点发送Leader心跳。那些节点收到这个Leader的心跳后,就会重置自己的倒计时,比如原来的倒计时还剩10ms,收到Leader心跳时就重置为200ms。Follower节点通过Leader的心跳去不断重置自己的倒计时,不让倒计时到达,以此来维持Leader的地位,否则倒计时一到达,它就会从Follower转变为Candidate发起新一轮的Leader选举。
14.Broker基于状态机实现的Leader选举
(1)什么是状态设计模式
(2)什么是状态机
(3)使用状态机机制实现Leader选举
(1)什么是状态设计模式
就是系统可以维护多个State状态,多个State状态之间可以进行切换。每次切换到一个新的State状态后,执行的行为是不同的,行为是跟State状态是绑定在一起的。状态机就是状态设计模式的一个运用。
(2)什么是状态机
状态设计模式可以演变成一个状态机,即StateMachine。状态机和状态设计模式一样,可以维护多个State状态,不同的State状态可以对应不同的行为。RocketMQ的Broker就是采取状态机机制来实现Leader选举的。
(3)使用状态机机制实现Leader选举
说明一:同一个组的每个Broker节点在启动时都会有一个状态机StateMachine,这个状态机会对节点状态进行判断。而这些节点在启动时的初始化状态都是Follower,所以状态机就会根据Follower状态让节点执行maintainAsFollower行为。
说明二:maintainAsFollower行为会判断是否收到Leader的心跳包。如果没收到心跳包就等待倒计时结束,节点切换成Candidate状态。如果收到心跳包就重置倒计时,节点切换成Follower状态。
说明三:当状态机发现节点的状态由Follower变成了Candidate,那么就会让节点执行maintainAsCandidate行为。
说明四:刚开始启动时各个节点都没有收到心跳包,都在等待各自的随机倒计时的结束。假设节点A的倒计时先结束,其节点状态由Follower切换为Candidate。
说明五:maintainAsCandidate行为会发起一轮新的投票,比如节点A会先投票给自己,然后发送该投票结果给组内的其他节点。组内其他节点收到该投票结果后会进行投票响应,如果发现其状态为Follower且没投过票,响应就是投票给节点A。
说明六:节点A收到其他节点的投票响应后,其状态机就会判断是否有超过半数的节点都对自己投票了。如果没有收到半数投票,就重置倒计时,等待下轮选举投票。如果收到半数投票,就开始把自己切换为Leader状态。当状态机发现节点的状态由Candidate变成了Leader,就会让节点执行maintainAsLeader行为。
说明七:maintainAsLeader行为会定时发送HeartBeat心跳请求给其他Follower节点。当其他Follower节点收到心跳请求包后,就会重置倒计时,并且返回心跳结果响应给Leader节点,然后等待倒计时结束。
说明八:当Leader节点收到这些心跳结果响应后,会判断是否超过半数节点进行了心跳响应。如果是则继续定时发送HeartBeat心跳请求给其他Follower节点,如果不是则把状态切换为Candidate状态。
注意关键点:是否超半数投票 + 是否超半数心跳响应。
15.Broker基于DLedger的数据写入流程
在Raft协议下,RocketMQ的Leader可以对外提供读和写服务,Follower则一般不对外提供服务,仅仅进行数据复制和同步,以及在Leader故障时完成Leader重新选举继续对外服务。
DLedger是一个实现了Raft协议的框架,它实现了Leader如何选举、数据如何复制、主从如何切换等功能。当Broker拿到一条消息准备写入时,就会切换为基于DLedger来进行写入,不过DLedger里写的不叫消息,而叫日志。
Broker节点的DLedger也是先往PageCache里写日志,然后会有后台线程进行异步刷盘将日志写入磁盘。而Leader节点的DLedger在往PageCache写完日志后,会异步复制日志到其他Follower节点,然后Leader节点会同步阻塞等待这些Follower节点写入日志的结果。当Leader节点发现过半Follower节点写入消息成功后,才会向Producer返回写入成功的响应,代表这条消息写入成功。
如下是Broker基于DLedger的数据写入流程:
16.Broker引入DLedger后的存储兼容设计
(1)过半节点写成功才返回写入成功
(2)DLedger的日志格式
(1)过半节点写成功才返回写入成功
在基于DLedger的数据写入过程中,消息仅仅写入Leader上DLedger的PageCache时,还不能代表这条消息的写入已经成功。还需要等待超过半数节点写入成功后,Leader向Producer返回这条消息已经写入成功了,才能让消费者去消费该条消息。
(2)DLedger的日志格式
消息被写入DLedger的PageCache时,由于数据会被调整为DLedger的日志格式,不再是没有使用DLedger时写入PageCache的CommitLog消息格式,那么该如何兼容这个DLedger的日志格式?
DLedger的日志会分成Body和Header两部分:Header部分中会包含很多的Header头字段,Body部分会包含长度不固定的Body体。
所以DLedger的一条日志中,会把CommitLog原始的一条数据放入到其Body部分,也就是:一条DLedger日志 = Header(多个头字段) + Body(CommitLog原始数据)。
因此使用DLedger写入消息到PageCache后,后台线程异步刷盘到CommitLog文件的每一条数据都会有"Header + Body"。此时如果从ConsumeQueue获取到偏移量后继续从Header开始去计算就找不到原始的CommitLog数据了。
所以需要对ConsumeQueue的数据也进行设计兼容:ConsumeQueue的一条数据里的offset物理偏移量,需要更改为CommitLog里一条数据的Body的起始物理偏移量。
17.Broker主从节点之间的元数据同步
元数据包括:Topic路由信息(比如Topic在当前的Broker组里有几个Queue)、消费进度数据。
因为这些元数据都是存储在Broker的Leader节点上的,也需要同步到Broker的Follower节点,所以Follower节点会启动一个定时任务每10s去Leader节点同步元数据。
18.Broker基于Raft协议的主从切换机制
Broker基于Raft协议的主从切换机制如下:
说明一:Broker组内的各个节点一开始启动时都是Follower状态,都会判断自己是否有收到Leader心跳包。由于刚开始启动时没有Leader,所以各个节点不会收到心跳包,于是都会等待随机倒计时结束,准备切换成Candidate状态。
说明二:其中的一个节点必然会优先结束随机倒计时并切换成Candidate状态。该节点切换成Candidate状态后,就会发起一轮新的选举,也就是给自己进行投票,并且把该投票也发送给其他节点。
说明三:其他节点收到该投票后,就会判断自己是否已给某节点投票。此时这些节点并没有给某节点投过票,并且都还处于Follower状态,其倒计时还没有结束,于是这些节点便会把票投给第一个结束随机倒计时的节点。否则,就忽略该投票。
说明四:第一个结束随机倒计时的节点收到其他节点的投票信息后,会判断投自己的票是否已超半数。如果是,则把Candidate状态切换成Leader状态。
说明五:第一个结束随机倒计时的节点把状态切换成Leader后,就会定时给其他节点发送HeartBeat心跳。其他节点收到心跳后,就会重置倒计时。
所以只要Leader正常运行,定时发送心跳过来重置倒计时,那么这些节点的倒计时永远不会结束,从而这些节点会一直维持着Follower状态。
说明六:这样,处于Leader状态的节点,和一直维持Follower状态的那些节点,就会正常工作。Producer的消息会往Leader节点进行写入,然后会被复制到Follower节点。Consumer消费消息也会从Leader节点进行读取,然后其Leader节点的元数据也会定时同步到Follower节点上。
之所以Leader节点能一直维持其Leader地位,是因为Leader节点会一直定时给Follower节点发送HeartBeat心跳,然后让这些Follower节点一直在重置自己的随机倒计时,让倒计时永远无法结束。否则,一旦Follower节点的随机倒计时结束,它就会将自己的状态切换成Candidate,并发起一轮新的Leader选举。
说明七:假设此时Leader节点崩溃了,比如Broker JVM进程进行了正常的重启。那么该Leader节点就无法给Follower节点定时发送HeartBeat心跳了。于是那些Follower节点便会判断出没有收到心跳包,从而会等待其倒计时结束,切换成Candidate状态。
其中必定会有一个Follower节点先结束倒计时切换成Candidate状态,然后发起新的Leader选举。当它发现有过半数节点给自己投票了之后,便会切换成Leader状态,完成主从切换,恢复工作。
说明八:由于新的Leader之前是有完整的消息数据和元数据,所以新Leader只要切换成功,Consumer和Producer继续往新Leader读写即可。新的Leader会继续给其他Follower节点同步数据、定时发送Leader心跳包让Follower节点无法切换成Candidate状态。
注意:基于Raft协议的Broker集群,每一组Broker至少需要部署3个节点。
19.Consumer端如何负载均衡分配Queue
(1)关于Topic的Queue分配问题
(2)Consumer的RebalanceService组件和算法
一个Topic会有多个Queue,而且会分布在不同的Broker上。而一个ConsumerGroup会有多个Consumer,所以需要把多个Queue分配给多个Consumer,这样每个Consumer都会分配到一部分的Queue。
(1)关于Topic的Queue分配问题
问题1:谁来负责把一个Topic的多个Queue分配到多个Consumer
Consumer自己可以负责进行分配。每个Consumer都可以获取到一个Topic有多少个Queue,以及自己所处的ConsumerGroup里有多少个Consumer,每个Consumer都可以按照相同的算法去做一次分配。
问题2:一个Topic里的Queue信息应该从哪里获取
从NameServer获取Topic里的Queue信息。Broker往NameServer进行注册和发送心跳时,都会带上该Broker上的Topic路由信息,可以理解为通过Broker也能获取完整的Topic路由信息(只需向所有Broker查询即可)。
问题3:如何知道一个ConsumerGroup里到底有多少个Consumer
每个Broker都可以知道一个ConsumerGroup的所有Consumer。Consumer启动时,会向所有的Broker进行注册。所以每个Broker都可以知道一个ConsumerGroup的所有Consumer都有哪些,可以通过随便一个Broker来获取Topic的路由信息 + ConsumerGroup信息。
(2)Consumer的RebalanceService组件和算法
Consumer中会有一个RebalanceService组件,负责每隔20秒去拉取Topic的Queue信息、ConsumerGroup信息,然后根据算法分配Queue,最后确认自己要拉取哪些Queue上的信息。
这些算法有:平均分配算法(热门)、轮询分配算法(热门)、一致性Hash(冷门)、机房分配(冷门)、配置分配(冷门)。
假设有两个Broker组、某个Topic有8个Queue:q1、q2、q3、q4、q5、q6、q7、q8,还有2个Consumer:Consumer1、Consumer2。
如果按照平均分配算法进行分配,那么Consumer1可能会分配到这4个Queue:q1、q2、q3、q4,Consumer2可能会分配到这4个Queue:q5、q6、q7、q8。
如果按照轮询分配算法进行分配,那么Consumer1可能会分配到这4个Queue:q1、q3、q5、q7,Consumer2可能会分配到这4个Queue:q2、q4、q6、q8。
20.Consumer消息拉取的挂起机制分析
(1)短轮询机制(Short Polling)
(2)长轮询机制(Long Polling)
(3)总结Consumer的Push模式和Pull模式
Consumer拉取消息时会有两种机制:长轮询机制(Long Polling)和短轮询机制(Short Polling)。Consumer如果没有开启长轮询机制(Long Polling),那么就会使用短轮询机制(Short Polling)去拉取消息。
(1)短轮询机制(Short Polling)
短轮询指的是短时间(默认1秒)挂起去进行消息拉取,这个1秒可以由shortPollingMillis参数进行控制。当Consumer发起请求去Leader节点拉取消息时,默认会采用短轮询机制。
如果Leader节点上处理该请求的线程时,发现没有消息就会挂起1秒,挂起过程中并不会有响应返回给Consumer。1秒后该线程会苏醒,然后再去检查Leader节点是否有消息了,如果还是没有消息,就返回Not Found Message给Consumer。
(2)长轮询机制(Long Polling)
长轮询其实指的就是长时间挂起去进行消息拉取。在开启了长轮询机制的情况下,当Consumer发起请求去Leader节点拉取消息时,如果Leader节点上处理该请求的线程发现没有消息,那么就会直接挂起,挂起过程中并不会有响应返回给Consumer。
同时,Leader节点中会有一个长轮询后台线程,每隔5秒去检查Leader节点是否有新的消息进来。如果检查到有新消息则唤醒挂起的线程,并判断该消息是否是Consumer所感兴趣的。如果不是Consumer感兴趣的,则再判断是否长轮询超时,如果超时则返回Not Found Message给Consumer。
当Consumer采用Push模式去拉取消息时,那么会:挂起 + 每隔5秒检查 + 超时时间为15秒,15秒都没拉到消息就超时返回。
当Consumer采用Pull模式去拉取消息时,那么会:挂起 + 每隔5秒检查 + 超时时间为20秒,20秒都没拉到消息就超时返回。
(3)总结Consumer的Push模式和Pull模式
实际上,这两个消费模式本质是一样的,都是消费者机器主动发送请求到Broker机器去拉取一批消息来进行处理。
Push消费模式底层也是基于消费者Pull模式来实现的,只不过它的名字叫做Push而已。意思是Broker会尽可能实时的把新消息交给消费者机器来进行处理,它的消息时效性会更好。
一般使用RocketMQ时,消费模式通常都是选择Push模式,因为Pull模式的代码写起来更加的复杂和繁琐,而且Push模式底层本身就是基于消息拉取的方式来实现的,只不过时效性更好而已。
Push模式的实现思路:当消费者发送请求到Broker去拉取消息时,如果有新的消息可以消费就马上返回一批消息到消费机器去处理,处理完之后会接着立刻发送请求到Broker机器去拉取下一批消息。
所以,消费机器在Push模式下,会处理完一批消息,马上发起请求拉取下一批消息,消息处理的时效性非常好,看起来就像Broker一直不停的推送消息到消费机器一样。
此外,Push模式下有一个请求挂起和长轮询的机制:当拉取消息的请求发送到Broker,结果发现没有新的消息给处理时,就会让请求线程挂起,默认是挂起15秒。然后在这个期间,Broker会有一个后台线程,每隔5秒就去检查一下是否有新的消息。另外在这个挂起过程中,如果有新的消息到达了会主动唤醒挂起的线程,然后把消息返回给消费者。
21.Consumer的处理队列与并发消费
(1)PullMessageService线程和ProcessQueue
(2)ConsumeMessageThread消息消费线程
(3)消费者并发消费总结
(1)PullMessageService线程和ProcessQueue
Consumer中负责拉取消息的线程只有一个,就是PullMessageService线程。Consumer从Broker拉取到消息后,会有一个ProcessQueue处理队列,用于进行消息中转。
Consumer的PullMessageService线程拉取到消息后,会将消息写入一个叫ProcessQueue的内存数据结构中,这个ProcessQueue数据结构的作用其实是用来对消息进行中转用的。
由于Consumer负责消费的会是Broker中的某几个ConsumeQueue里的消息,所以Consumer拉取到的ConsumeQueue数据都会写到其内存的某几个ProcessQueue里面。也就是Consumer从Broker中拉取了几个ConsumeQueue的数据,就会对应有几个ProcessQueue,可以理解ProcessQueue和ConsumeQueue之间存在一一对应的映射关系。
(2)ConsumeMessageThread消息消费线程
Consumer把拉取到的消息写入ProcessQueue完成中转后,就会提交消费任务到一个线程池里。通过这个线程池,就可以开辟多个ConsumeMessageThread线程(即消息消费线程),来对消息进行并发消费。线程池里的每个线程处理消费消息完毕后,就会回调用户自己写代码实现的回调监听处理函数,处理具体业务。
(3)消费者并发消费总结
说明一:Consumer在启动时会往Broker注册,会通过RebalanceService组件获取Topic路由信息和ConsumerGroup信息。然后RebalanceService组件会通过负载均衡算法实现Queue到Consumer的分配,确定自己要拉取哪些Queue。
说明二:Consumer在拉取Queue的消息时会有长轮询和短轮询两种模式,默认采用短轮询拉取消息。
说明三:当Consumer拉取到消息后,就会写入在内存中和ConsumeQueue一一对应的ProcessQueue队列,并提交任务到线程池,由线程池里的线程并发地从ProcessQueue获取消息进行处理。
说明四:这些线程从ProcessQueue获取到消息后,就会回调用户实现的回调监听处理函数listener.consumeMessage()。当回调监听处理函数执行完毕后,便会返回SUCCESS给线程,线程便会删除ProcessQueue里的该消息,这样线程又可以继续从ProcessQueue里获取下一条消息进行处理。
22.Consumer处理成功后的消费进度管理
(1)消息从ProcessQueue中删除后要提交消费进度
(2)消费进度先存本地内存再异步提交
(1)消息被处理完后要提交消费进度
当线程池里的线程从ProcessQueue获取到某消息,并回调用户实现的回调监听处理函数listener.consumeMessage(),然后执行成功返回线程SUCCESS后,就可以将该消息从ProcessQueue中删掉了。
当消息从ProcessQueue中删掉后,Consumer需要向Broker的Leader节点提交消息对应的ConsumeQueue的消费进度。因为Broker的Leader节点需要维护和管理:每个ConsumeQueue被各个ConsumeGroup消费的进度。
(2)消费进度先存本地内存再异步提交
当回调监听处理函数返回SUCCESS后,Consumer本地的内存里会存储该Consumer对ConsumeQueue的消费进度。然后Consumer端会有一个后台线程,异步提交这个消费进度到Broker的Leader节点。即Consumer会先将消费进度提交到自己的本地内存里,接着有一个后台线程异步提交消费进度到Leader节点。
Broker的Leader节点收到Consumer提交的消费进度后,也会先存放到自己的内存中。然后Broker也会有一个后台线程将消费进度异步刷入磁盘文件里。
23.Consumer消息重复消费原理剖析
(1)消息被重复消费的消费端原因
(2)造成消费端重复消费消息的场景
(1)消息被重复消费的消费端原因
由于一条消息被消费后,消费进度不管在Consumer端还是在Broker端,都会先进入内存。所以当消费进度还在内存时机器崩溃了或者系统重启,那么就会导致消息重复消费。
(2)造成消费端重复消费消息的场景
主要就是如下两个情景造成消费被重复消息:
情景一:Consumer端消费完消息后,消费进度还没进入内存或已经写入内存但还没提交给Broker,机器宕机或系统重启。
情景二:Broker端收到Consumer提交的消费进度还没写入内存或刚写入内存,还没刷入磁盘,机器宕机或系统重启。
对于Consumer消息重复消费的问题,在Consumer端需要实现一套严格的分布式锁和幂等性保障机制来进行处理。
24.Consumer处理失败时的延迟消费机制
(1)处理失败返回RECONSUME_LATER
(2)消息进入RETRY_Topic并检查延迟时间
(3)消息到达延迟时间再次进入原Topic重新消费
(1)处理失败返回RECONSUME_LATER
Consumer端线程池里的线程从ProcessQueue获取到某消息后:如果在回调用户实现的回调监听处理函数listener.consumeMessage()时,消费失败返回了RECONSUME_LATER。那么Consumer也会把ProcessQueue里的这条消息进行删除,然后返回一个处理消息失败的ACK给Broker。
(2)消息进入RETRY_Topic并检查延迟时间
Broker收到这个处理消息失败的ACK后,会对该消息的Topic进行改写,改写成RETRY_Topic_%。该消息也就成为了延迟消息,接着将消息写入到RETRY_Topic_%对应的CommitLog和ConsumeQueue中。然后Broker端会有一个延迟消息的后台线程对改写Topic的ConsumeQueue进行检查,检查里面的消息是否达到延迟时间。其中延迟时间会有多个,并且可以进行配置。
(3)消息到达延迟时间再次进入原Topic重新消费
如果达到延迟时间,就会把该消息取出来再次进行改写Topic,改写为原来的Topic。这样该消息会被写入到原Topic对应的CommitLog对应的CommitLog和ConsumeQueue中,从而让该消息被Consumer在后续的消费中拉取到,进行重新消费。
25.ConsumerGroup变动时的重平衡机制
每当一个ConsumerGroup中少了一个Consumer(机器宕机或重启)、或者多了一个Consumer(新增机器)时,就需要重新分配Topic的那些Queue给Consumer,而这部分工作会由Consumer端的RebalanceService组件完成。
RebalanceService组件会每隔20秒去Broker拉取最新的Topic路由信息 + ConsumerGroup信息。
当某个Consumer宕机后,Broker是知道该宕机的Consumer对其负责的ConsumeQueue的消费进度的。所以在最多20秒后,其他Consumer就会进行重新的负载均衡,将宕机Consumer负责的ConsumeQueue分配好。
当ConsumerGroup新增一个Consumer时,由于新增的Consumer会往Broker进行注册,所以Broker能知道新增Consumer。新老Consumer都会每隔20秒拉取最新的Topic路由信息 + ConsumerGroup信息。这样新老Consumer都可以通过RebalanceService重平衡组件重新分配ConsumeQueue。
相关文章:
RocketMQ原理—5.高可用+高并发+高性能架构
大纲 1.RocketMQ的整体架构与运行流程 2.基于NameServer管理Broker集群的架构 3.Broker集群的主从复制架构 4.基于Topic和Queue实现的数据分片架构 5.Broker基于Pull模式的主从复制原理 6.Broker层面到底如何做到数据0丢失 7.数据0丢失与写入高并发的取舍 8.RocketMQ读…...
适配器模式——C++实现
目录 1. 适配器模式简介 2. 角色组成 3. 代码示例 4. 适配器模式、装饰器模式、外观模式的辨析 1. 适配器模式简介 适配器模式是一种结构型模式。 适配器模式的定义:适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不可兼容…...
C语言自定义数据类型详解(一)——结构体类型(上)
什么是自定义数据类型呢?顾名思义,就是我们用户自己定义和设置的类型。 在C语言中,我们的自定义数据类型一共有三种,它们分别是:结构体(struct),枚举(enum),联合(union)。接下来,我…...
C语言基础4
sizeof和strlen的区别 ①sizeof是运算符而strlen是函数 ②sizeof可以用类型做参数,strlen只能用char*做参数 ③数组做sizeof参数不退化,而传递给strlen则退化成指针 ④strlen结果是运行时候才能计算出来,而且计算出来的是字符串的长度不是内…...
【Elasticsearch】Elasticsearch的查询
Elasticsearch的查询 DSL查询基础语句叶子查询全文检索查询matchmulti_match 精确查询termrange 复合查询算分函数查询bool查询 排序分页基础分页深度分页 高亮高亮原理实现高亮 RestClient查询基础查询叶子查询复合查询排序和分页高亮 数据聚合DSL实现聚合Bucket聚合带条件聚合…...
第 5 章:声音与音乐系统
5.1 声音效果的应用 在游戏中,声音效果是增强游戏沉浸感和趣味性的重要元素。Pygame 提供了强大的音频处理功能,使得添加各种声音效果变得相对简单。声音效果可以包括角色的动作音效,如跳跃、攻击、受伤时的声音;环境音效&#x…...
第十四讲 JDBC数据库
1. 什么是JDBC JDBC(Java Database Connectivity,Java数据库连接),它是一套用于执行SQL语句的Java API。应用程序可通过这套API连接到关系型数据库,并使用SQL语句来完成对数据库中数据的查询、新增、更新和删除等操作…...
2024年除夕
多少年前的除夕,一如今天这样的除夕;多少年后的除夕,也一如多少年前的除夕。 无数个这样的除夕下午,我打开电脑,望着窗外安静的小区,车声渐渐稀疏的马路,想写下一些新的感受时,多少…...
虚幻基础07:蓝图接口
能帮到你的话,就给个赞吧 😘 文章目录 作用原理事件函数 作用 实现对象间的通知。 A 通知 B 做什么。 原理 将接口抽象为蓝图,使得任意蓝图都能直接访问。 只需要再传入对象地址,就能执行对象的功能。 事件 黄色:…...
7. 马科维茨资产组合模型+金融研报AI长文本智能体(Qwen-Long)增强方案(理论+Python实战)
目录 0. 承前1. 深度金融研报准备2. 核心AI函数代码讲解2.1 函数概述2.2 输入参数2.3 主要流程2.4 异常处理2.5 清理工作2.7 get_ai_weights函数汇总 3. 汇总代码4. 反思4.1 不足之处4.2 提升思路 5. 启后 0. 承前 本篇博文是对前两篇文章,链接: 5. 马科维茨资产组…...
如何在本地部署deepseek r1模型?
DeepSeek(深度求索)正式发布了其最新推理模型DeepSeek-R1,引发业界广泛关注。这款模型不仅在性能上与OpenAI的GPT-4相媲美,更以其开源策略和创新的训练方法,为AI发展带来了新的可能性。DeepSeek-R1 在后训练阶段大规模…...
HarmonyOS:状态管理最佳实践
一、概述 在声明式UI编程范式中,UI是应用程序状态的函数,应用程序状态的修改会更新相应的UI界面。ArkUI采用了MVVM模式,其中ViewModel将数据与视图绑定在一起,更新数据的时候直接更新视图。如下图所示: ArkUI的MVVM模式…...
当AI风暴来袭:中美科技商业版图的迥异走向
当AI风暴来袭:中美科技商业版图的迥异走向 美国科技巨头的 AI 豪赌:Stargate 公司的诞生 2025 年,科技界被一则重磅消息所震动:软银、NVIDIA、Oracle 与 OpenAI 共同组建了 Stargate 公司。这一合作堪称豪华阵容,软银作为全球知名的投资巨头,拥有雄厚的资金实力和广泛的…...
马尔科夫模型和隐马尔科夫模型区别
我用一个天气预报和海藻湿度观测的比喻来解释,保证你秒懂! 1. 马尔可夫模型(Markov Model, MM) 特点:状态直接可见 场景:天气预报(晴天→雨天→阴天…)核心假设: 下一个…...
面向对象设计原则 - SOLID原则 (基于C++)
SOLID 是面向对象编程中的一组五个设计原则,这些原则旨在帮助开发者创建更灵活、可维护和可扩展的软件系统。它们最初由 Robert C. Martin 提出,并在 2000 年左右被广泛接受。每个字母代表一个不同的原则: 单一职责原则 (Single Responsibil…...
ChatGPT 搜索测试整合记忆功能
据 TestingCatalog 报道,OpenAI 正在测试 ChatGPT 搜索的整合记忆功能,被命名为 “Memory in search”2。以下是关于该功能的具体情况123: 功能特点 个性化搜索:启用该功能后,ChatGPT 能利用存储的记忆数据࿰…...
PWM频率测量方法
测量PWM(脉宽调制)信号的频率是嵌入式系统中的常见需求,尤其是在电机控制、LED调光、传感器信号处理等场景中。 在这里介绍两种测量PWM频率的方法:测频法与测周法。 1、测频(率)法 原理:在闸门…...
【B站保姆级视频教程:Jetson配置YOLOv11环境(一)镜像下载与烧录】
b站同步视频教程:https://www.bilibili.com/video/BV11r6oYkEFb/ 一、引言 在人工智能与计算机视觉快速发展的当下,Jetson系列开发板凭借强大的性能,成为众多开发者进行深度学习项目的热门选择。YOLOv11作为目标检测领域的先进算法…...
使用QSqlQueryModel创建交替背景色的表格模型
class UserModel(QSqlQueryModel):def __init__(self):super().__init__()self._query "SELECT name, age FROM users"self.refresh()def refresh(self):self.setQuery(self._query)# 重新定义data()方法def data(self, index, role): if role Qt.BackgroundRole…...
计算机网络__基础知识问答
Question: 1)在计算机网络的5层结构中,每一层的功能大概是什么? 2)交换机的功能?https://www.bilibili.com/video/BV1na4y1L7Ev 3)路由器的功能?https://www.bilibili.com/video/BV1hv411k7n…...
C语言数组详解:从基础到进阶的全面解析
在C语言中,数组是一种基本的数据结构,用于存储多个相同类型的数据。数组的引入使得C语言能够高效地存储和操作大量数据。在任何一个C语言程序中,数组都发挥着极其重要的作用。无论是在算法实现、数据存储、还是在复杂程序的设计中,…...
微前端架构在前端开发中的实践与挑战
随着单页面应用(SPA)和前端框架如 React、Vue、Angular 的快速发展,现代前端应用的复杂度日益提升。尤其是当应用规模逐渐增大时,单一的代码库往往难以应对不同团队的协作和版本管理问题。为了应对这一挑战,微前端架构…...
国内flutter环境部署(记录篇)
设置系统环境变量 export PUB_HOSTED_URLhttps://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URLhttps://storage.flutter-io.cn使用以下命令下载flutter镜像 git clone -b stable https://mirror.ghproxy.com/https://github.com/<github仓库地址>#例如flutter仓…...
Julia DataFrames.jl:深入理解和使用
随着数据科学和机器学习的发展,数据框架广泛应用于数据处理与分析工作中。在 Julia 语言中,DataFrames.jl 是一个强大且灵活的数据框库,为数据操作提供了丰富的功能。本文旨在系统地介绍 DataFrames.jl 的基础概念、使用方法、常见实践和最佳…...
上位机知识篇---DDSSDK
文章目录 前言第一部分:DDS核心特性1.以数据为中心2.发布-订阅模型3.质量服务4.多语言支持 关键概念1.主题2.发布者3.订阅者4. 数据写入者5.数据读取者6.域参与者7.域 DDS的优势1.可伸缩性2.实时性3.可靠性4.容错性 DDS的应用场景1.军事通信系统2.航空航天3.工业自动…...
基于DeepSeek在藏语学习推广和藏语信息化方面可以做哪些工作?
基于DeepSeek对藏语的技术优势,您可在以下三大方向开展创新性工作,以下是20具体落地方案: 一、藏语智能教育工具开发 《三十颂》AI语法教练 开发虚拟助教自动解析藏文句子结构(标注格助词/时态变化)错误检测系统&…...
如何把obsidian的md文档导出成图片,并加上文档属性
上篇关于这个插件PKMer_Obsidian 插件:Export Image plugin 一键将笔记转换为图片分享的文章 如何把obsidian的md文档导出成图片,并加上水印-CSDN博客 如何导出图片的时候让文档属性也显示出来,啊啊,这个功能找了一晚上…...
AUTOSAR从入门到精通-车身控制系统BCM(三)
目录 前言 算法原理 什么是车身控制模块BCM 1. BCM ECU的工作原理 a. 硬件架构 b. 控制逻辑 BCM带来的好处 车身控制模块(BCM)的功用 车身控制模块(BCM)能够控制的车身功能系统 BCM的各项功能 1.1内外部灯光控制 1.2 雨刮系统 1.3 车身防盗报警系统 1.4 车锁…...
删除全表数据sql
-- 删除 employees 表中的所有数据 DELETE FROM employees;-- 清空 employees 表中的所有数据 TRUNCATE TABLE employees;TRUNCATE 操作不记录每一行的删除操作,而是直接释放数据页,所以执行速度通常比 DELETE 快。不过它不能和 WHERE 子句一起使用&…...
Winform如何取消叉号,减号和放大(两种)
方法一: 找到窗体属性 MaximizeBoxFalse; MinimizeBoxFalse; ControlBoxFALSE; 方法二: 点击Form 在From里面找到Form-Closing 这个事件 写入 if(e.CloseReasonCloseReason.UserClosing) { MessageBox.Show("对不起,你不能关闭") e.Cancel true; }...
Couchbase UI: Query
Couchbase 的 Query 页面在 UI 中是一个核心功能模块,支持 N1QL 查询的编写、调试和优化。同时,它也扩展了与查询相关的功能,比如 Workbench、Monitor 和 UDF 管理。这些功能让开发者和管理员能够更加高效地管理数据查询和性能优化。 以下是…...
Word 中实现方框内点击自动打 √ ☑
注: 本文为 “Word 中方框内点击打 √ ☑ / 打 ☒” 相关文章合辑。 对第一篇增加了打叉部分,第二篇为第一篇中方法 5 “控件” 实现的详解。 在 Word 方框内打 √ 的 6 种技巧 2020-03-09 12:38 使用 Word 制作一些调查表、检查表等,通常…...
Go优雅实现redis分布式锁
前言 系统为了保证高可用,通常会部署多实例,并且会存在同时对共享资源并发读写,这时候为了保证读写的安全,常规手段是会引入分布式锁,本文将介绍如何使用redis设计一个优雅的Go分布式锁。 设计 redis分布式锁是借助…...
A7. Jenkins Pipeline自动化构建过程,可灵活配置多项目、多模块服务实战
服务容器化构建的环境配置构建前需要解决什么下面我们带着问题分析构建的过程:1. 如何解决jenkins执行环境与shell脚本执行环境不一致问题?2. 构建之前动态修改项目的环境变量3. 在通过容器打包时避免不了会产生比较多的不可用的镜像资源,这些资源要是不及时删除掉时会导致服…...
Controller 层优化四步曲
Controller 层优化四步曲 前言 在开发过程中,Controller 层作为系统与外界交互的桥梁,承担着接收请求、解析参数、调用业务逻辑、处理异常等职责。 然而,随着业务复杂度的增加,Controller 层的代码往往会变得臃肿且难以维护。 …...
自动化运维在云环境中的完整实践指南
随着云计算的普及,越来越多的企业将业务迁移到云上。云环境的高动态性和复杂性使得传统的手动运维方式难以应对,自动化运维成为提升效率、降低成本、保障系统稳定性的关键。本文将详细介绍如何在云环境中实施自动化运维,涵盖工具选择、实施步骤和最佳实践。 © ivwdcwso…...
electron typescript运行并设置eslint检测
目录 一、初始化package.json 二、安装依赖 三、项目结构 四、配置启动项 五、补充:ts转js别名问题 一、初始化package.json 我的:这里的"main"没太大影响,看后面的步骤。 {"name": "xloda-cloud-ui-pc"…...
DiffuEraser: 一种基于扩散模型的视频修复技术
视频修复算法结合了基于流的像素传播与基于Transformer的生成方法,利用光流信息和相邻帧的信息来恢复纹理和对象,同时通过视觉Transformer完成被遮挡区域的修复。然而,这些方法在处理大范围遮挡时常常会遇到模糊和时序不一致的问题࿰…...
《Operating System Concepts》阅读笔记:p1-p1
《Operating System Concepts》学习第 1 天,p1-p1 总结,总计 1 页。 一、技术总结 无。 二、英语总结(生词:1) 1.intermediary (1)intermediary: inter-(“between, among”) medius(“middle”) c.intermediary originally referred …...
9.8 实战:使用 GPT Builder 开发定制化 ChatGPT 应用
实战:使用 GPT Builder 开发定制化 ChatGPT 应用 引言:打造属于你的智能助手 定制化 ChatGPT 应用正在成为解决具体问题和提升生产力的关键工具。GPT Builder 提供了一个灵活、直观的平台,让开发者和非技术用户都能快速创建满足特定需求的智能助手。本文将通过一个实战案例…...
团体程序设计天梯赛-练习集——L1-024 后天
前言 首先祝大家新年快乐,然后博主今点炮让炮崩了一下,水一天 这道题5分非常简单,有不少的做法 L1-024 后天 如果今天是星期三,后天就是星期五;如果今天是星期六,后天就是星期一。我们用数字1到7对应星期…...
基于STM32的智能语音控制灯光系统设计
目录 引言系统设计 硬件设计软件设计 系统功能模块 语音识别模块灯光控制模块模式切换与场景管理模块用户交互与显示模块远程控制与数据上传模块 控制算法 语音识别与命令解析算法灯光强度与颜色调节算法数据记录与远程反馈算法 代码实现 语音识别与灯光控制代码场景模式与定时…...
Redis部署方式全解析:优缺点大对比
Redis部署方式全解析:优缺点大对比 一、引言 Redis作为一款高性能的内存数据库,在分布式系统、缓存、消息队列等众多场景中都有着广泛的应用。选择合适的Redis部署方式,对于系统的性能、可用性、可扩展性以及成本等方面都有着至关重要的影响…...
Java实现FIFO缓存策略实战
实现FIFO模型选择FIFO模型实现过程FIFO模型完整代码下面看一下先进先出的示例过程总结FIFO(First In First Out,先进先出)策略是一种基本的数据处理和存储管理方法,在Java中,这种策略通常用于管理那些需要按照顺序处理的数据项,比如任务的队列、数据的传输缓冲区等。在Ja…...
把markdown转换为pdf的方法
将 Markdown 文件转换为 PDF 有多种方法,以下是几种常见的方式: 1. 使用 VS Code 和 Markdown 插件 VS Code 是一款流行的代码编辑器,支持通过插件将 Markdown 转换为 PDF。 步骤: 安装 VS Code: 下载地址ÿ…...
Java Web 开发基础介绍
Java学习资料 Java学习资料 Java学习资料 一、引言 在当今数字化时代,Web 应用无处不在。Java 凭借其强大的功能、良好的跨平台性和丰富的开发框架,成为 Web 开发领域的热门选择之一。Java Web 开发允许开发者构建动态、交互式的 Web 应用程序&#x…...
自定义数据集,使用 PyTorch 框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测
在本文中,我们将展示如何使用 NumPy 创建自定义数据集,利用 PyTorch 实现一个简单的逻辑回归模型,并在训练完成后保存该模型,最后加载模型并用它进行预测。 1. 创建自定义数据集 首先,我们使用 NumPy 创建一个简单的…...
LangChain概述
文章目录 为什么需要LangChainLLM应用开发的最后1公里LangChain的2个关键词LangChain的3个场景LangChain的6大模块 为什么需要LangChain 首先想象一个开发者在构建一个LLM应用时的常见场景。当你开始构建一个新项目时,你可能会遇到许多API接口、数据格式和工具。对于…...
Ubuntu 16.04安装Lua
个人博客地址:Ubuntu 16.04安装Lua | 一张假钞的真实世界 在Linux系统上使用以下命令编译安装Lua: curl -R -O http://www.lua.org/ftp/lua-5.3.3.tar.gz tar zxf lua-5.3.3.tar.gz cd lua-5.3.3 make linux test 安装make 编译过程如果提示以下信息…...
独立开发者产品日刊:将 Figma 设计转化为全栈应用、对话 PDF生成思维导图、视频转 AI 笔记、AI问答引擎、Mac 应用启动器切换器
独立开发者产品日刊,每日汇集 ProductHunt 热榜产品介绍,用一个 Slogan 帮你概括产品内容,期望能够让你快速浏览get最新产品创意,激发在产品上的灵感。 Lovable Builder.io Slogan:将 Figma 设计转化为全栈应用 类别…...