1. 消费者对于分配给它的队列如果发起拉消息的

首先,每个队列的消费进度是存储在它归属的 Broker 节点上的,对于新分配给自己的队列,需要到队列归属的 Broker 上进行 RPC 请求,获取该队列的消费进度,存储到消费者本地的 offsetStore 对象里。
有了这个消费进度之后,会创建一个 PullRequest 对象,保存队列信息和拉消息位点信息,之后将 PullRequest 对象交给 PullMessageService(拉消息服务)
PullMessageService 内部有个 LinkedBlockingQueue 去存放 PullRequest 对象,并且有自己的线程资源,线程启动后消费 LinkedBlockingQueue,异步的去读取 PullRequest 对象,根据对象信息发起拉消息请求
拉下一批消息后,会更新 PullRequest 下一次拉消息的位点信息,然后把 PullRequest 再一次存储到 **LinkedBlockingQueue**,形成一个拉消息的闭环。

2. 对于 Broker 上拉下来的消息,消费者如何消费

拉消息和消息消费两个逻辑是异步的,分别有各自独立的线程去完成。以并发消息服务为例,拉取下来的每条消息会被封装成【消息消费任务】,提交到 并发消费服务 的线程池,被线程池内线程拿走执行
消息消费任务最核心的逻辑是调用用户注册在 Consumer 上的 MessageListener 对象,里面封装了用户处理消息的具体逻辑,处理完后返回消费任务的处理结果,是成功还是失败
对于不同的消费结果,消费端需要做不同处理。消费成功的话,直接更新消费者本地的 offsetStore 的消费进度,让其 +1;消费失败的话,失败的消息是需要回退给 broker 节点,这些回退的消息进入到重试主题
每个消费者都有一个专属的重试主题(%Group%RetryTopic),而且消费者启动的时候会去订阅该组的重试主题。
进入到重试主题的消息不会立马被再次消费,broker 接收到重试消息后,并没有直接将重试消息投放到重试主题,而是将重试主题和队列保存到消息属性内,修改主题为延迟主题(schedule_topic_xxx),队列 ID根据当前消息的重试次数决定,不同的队列对应不同的延迟级别,从小到大(比如第一次重试的时候,队列 ID 为 0,第二次重试为 1,以此类推)。延迟队列是由本地 Broker 服务去消费的,每个延迟队列在 Broker 端都有一个消费任务去处理,消费任务执行时,会从消费位点获取延迟消息,检查消息有没有达到交付时间,达到交付时间就 clone 这条消息,修改消息主题和队列 ID,修改为消息属性读取出的 原topicId 和原 queueId,然后再次存储到 commitLog。
之后监听程序读取出来新消息,构建 ConsumeQueue 队列的数据。到此为止,消费者端才能从 Broker 获取到待重试消费的消息

3. 如果拉消息服务已经赶上队列进度了,拉消息服务会不会不停的尝试拉取

客户端看源码 确实是无脑拉
服务端进行了控制,使用的技术是长轮询