聊聊rocketmq-proxy的popMessage

本文主要研究一下rocketmq-proxy的popMessage

MessagingProcessor

org/apache/rocketmq/proxy/processor/MessagingProcessor.java

public interface MessagingProcessor extends StartAndShutdown {

	//......

    CompletableFuture<PopResult> popMessage(
        ProxyContext ctx,
        QueueSelector queueSelector,
        String consumerGroup,
        String topic,
        int maxMsgNums,
        long invisibleTime,
        long pollTime,
        int initMode,
        SubscriptionData subscriptionData,
        boolean fifo,
        PopMessageResultFilter popMessageResultFilter,
        String attemptId,
        long timeoutMillis
    );

	//......
}

MessagingProcessor接口定义了popMessage方法

DefaultMessagingProcessor

org/apache/rocketmq/proxy/processor/DefaultMessagingProcessor.java

public class DefaultMessagingProcessor extends AbstractStartAndShutdown implements MessagingProcessor {

	//......

    public CompletableFuture<PopResult> popMessage(
        ProxyContext ctx,
        QueueSelector queueSelector,
        String consumerGroup,
        String topic,
        int maxMsgNums,
        long invisibleTime,
        long pollTime,
        int initMode,
        SubscriptionData subscriptionData,
        boolean fifo,
        PopMessageResultFilter popMessageResultFilter,
        String attemptId,
        long timeoutMillis
    ) {
        return this.consumerProcessor.popMessage(ctx, queueSelector, consumerGroup, topic, maxMsgNums,
            invisibleTime, pollTime, initMode, subscriptionData, fifo, popMessageResultFilter, attemptId, timeoutMillis);
    }

	//......
}

DefaultMessagingProcessor的popMessage方法委托给了consumerProcessor.popMessage

ConsumerProcessor

org/apache/rocketmq/proxy/processor/ConsumerProcessor.java

    public CompletableFuture<PopResult> popMessage(
        ProxyContext ctx,
        QueueSelector queueSelector,
        String consumerGroup,
        String topic,
        int maxMsgNums,
        long invisibleTime,
        long pollTime,
        int initMode,
        SubscriptionData subscriptionData,
        boolean fifo,
        PopMessageResultFilter popMessageResultFilter,
        String attemptId,
        long timeoutMillis
    ) {
        CompletableFuture<PopResult> future = new CompletableFuture<>();
        try {
            AddressableMessageQueue messageQueue = queueSelector.select(ctx, this.serviceManager.getTopicRouteService().getCurrentMessageQueueView(ctx, topic));
            if (messageQueue == null) {
                throw new ProxyException(ProxyExceptionCode.FORBIDDEN, "no readable queue");
            }
            return popMessage(ctx, messageQueue, consumerGroup, topic, maxMsgNums, invisibleTime, pollTime, initMode,
                subscriptionData, fifo, popMessageResultFilter, attemptId, timeoutMillis);
        }  catch (Throwable t) {
            future.completeExceptionally(t);
        }
        return future;
    }

	public CompletableFuture<PopResult> popMessage(
        ProxyContext ctx,
        AddressableMessageQueue messageQueue,
        String consumerGroup,
        String topic,
        int maxMsgNums,
        long invisibleTime,
        long pollTime,
        int initMode,
        SubscriptionData subscriptionData,
        boolean fifo,
        PopMessageResultFilter popMessageResultFilter,
        String attemptId,
        long timeoutMillis
    ) {
        CompletableFuture<PopResult> future = new CompletableFuture<>();
        try {
            if (maxMsgNums > ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST) {
                log.warn("change maxNums from {} to {} for pop request, with info: topic:{}, group:{}",
                    maxMsgNums, ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST, topic, consumerGroup);
                maxMsgNums = ProxyUtils.MAX_MSG_NUMS_FOR_POP_REQUEST;
            }

            PopMessageRequestHeader requestHeader = new PopMessageRequestHeader();
            requestHeader.setConsumerGroup(consumerGroup);
            requestHeader.setTopic(topic);
            requestHeader.setQueueId(messageQueue.getQueueId());
            requestHeader.setMaxMsgNums(maxMsgNums);
            requestHeader.setInvisibleTime(invisibleTime);
            requestHeader.setPollTime(pollTime);
            requestHeader.setInitMode(initMode);
            requestHeader.setExpType(subscriptionData.getExpressionType());
            requestHeader.setExp(subscriptionData.getSubString());
            requestHeader.setOrder(fifo);
            requestHeader.setAttemptId(attemptId);

            future = this.serviceManager.getMessageService().popMessage(
                    ctx,
                    messageQueue,
                    requestHeader,
                    timeoutMillis)
                .thenApplyAsync(popResult -> {
                    if (PopStatus.FOUND.equals(popResult.getPopStatus()) &&
                        popResult.getMsgFoundList() != null &&
                        !popResult.getMsgFoundList().isEmpty() &&
                        popMessageResultFilter != null) {

                        List<MessageExt> messageExtList = new ArrayList<>();
                        for (MessageExt messageExt : popResult.getMsgFoundList()) {
                            try {
                                fillUniqIDIfNeed(messageExt);
                                String handleString = createHandle(messageExt.getProperty(MessageConst.PROPERTY_POP_CK), messageExt.getCommitLogOffset());
                                if (handleString == null) {
                                    log.error("[BUG] pop message from broker but handle is empty. requestHeader:{}, msg:{}", requestHeader, messageExt);
                                    messageExtList.add(messageExt);
                                    continue;
                                }
                                MessageAccessor.putProperty(messageExt, MessageConst.PROPERTY_POP_CK, handleString);

                                PopMessageResultFilter.FilterResult filterResult =
                                    popMessageResultFilter.filterMessage(ctx, consumerGroup, subscriptionData, messageExt);
                                switch (filterResult) {
                                    case NO_MATCH:
                                        this.messagingProcessor.ackMessage(
                                            ctx,
                                            ReceiptHandle.decode(handleString),
                                            messageExt.getMsgId(),
                                            consumerGroup,
                                            topic,
                                            MessagingProcessor.DEFAULT_TIMEOUT_MILLS);
                                        break;
                                    case TO_DLQ:
                                        this.messagingProcessor.forwardMessageToDeadLetterQueue(
                                            ctx,
                                            ReceiptHandle.decode(handleString),
                                            messageExt.getMsgId(),
                                            consumerGroup,
                                            topic,
                                            MessagingProcessor.DEFAULT_TIMEOUT_MILLS);
                                        break;
                                    case MATCH:
                                    default:
                                        messageExtList.add(messageExt);
                                        break;
                                }
                            } catch (Throwable t) {
                                log.error("process filterMessage failed. requestHeader:{}, msg:{}", requestHeader, messageExt, t);
                                messageExtList.add(messageExt);
                            }
                        }
                        popResult.setMsgFoundList(messageExtList);
                    }
                    return popResult;
                }, this.executor);
        } catch (Throwable t) {
            future.completeExceptionally(t);
        }
        return FutureUtils.addExecutor(future, this.executor);
    }    

ConsumerProcessor的popMessage方法先通过queueSelector.select选择messageQueue,之后再执行popMessage;这里限制了每次pop消息的数量不能超过MAX_MSG_NUMS_FOR_POP_REQUEST(32),构建PopMessageRequestHeader之后再委托给了this.serviceManager.getMessageService().popMessage,之后返回popResult,拉取到的消息放到了msgFoundList属性

MessageService

org/apache/rocketmq/proxy/service/message/MessageService.java

    CompletableFuture<PopResult> popMessage(
        ProxyContext ctx,
        AddressableMessageQueue messageQueue,
        PopMessageRequestHeader requestHeader,
        long timeoutMillis
    );

MessageService接口定义了popMessage方法,它有两个实现类,分别是LocalMessageService

LocalMessageService

org/apache/rocketmq/proxy/service/message/LocalMessageService.java

    public CompletableFuture<PopResult> popMessage(ProxyContext ctx, AddressableMessageQueue messageQueue,
        PopMessageRequestHeader requestHeader, long timeoutMillis) {
        requestHeader.setBornTime(System.currentTimeMillis());
        RemotingCommand request = LocalRemotingCommand.createRequestCommand(RequestCode.POP_MESSAGE, requestHeader, ctx.getLanguage());
        CompletableFuture<RemotingCommand> future = new CompletableFuture<>();
        SimpleChannel channel = channelManager.createInvocationChannel(ctx);
        InvocationContext invocationContext = new InvocationContext(future);
        channel.registerInvocationContext(request.getOpaque(), invocationContext);
        ChannelHandlerContext simpleChannelHandlerContext = channel.getChannelHandlerContext();
        try {
            RemotingCommand response = brokerController.getPopMessageProcessor().processRequest(simpleChannelHandlerContext, request);
            if (response != null) {
                invocationContext.handle(response);
                channel.eraseInvocationContext(request.getOpaque());
            }
        } catch (Exception e) {
            future.completeExceptionally(e);
            channel.eraseInvocatio
在Maven构建过程中,若出现`Could not resolve dependencies for project`错误,并且涉及`rocketmq-spring-boot-starter`和`grpc-core`的版本冲突,通常表明项目依赖解析失败,可能是由于依赖版本不兼容、依赖未正确发布至仓库,或者本地/远程仓库缓存问题所致。 以下是对该问题的详细分析和解决方案: ### 1. 检查依赖版本兼容性 `rocketmq-spring-boot-starter`可能依赖特定版本的gRPC库(如`grpc-core`),而项目中显式声明的gRPC版本与其不兼容,导致版本冲突。应查阅`rocketmq-spring-boot-starter`官方文档或Maven Central,确认其推荐的gRPC版本,并在`pom.xml`中统一指定该版本。 例如,若发现`rocketmq-spring-boot-starter`依赖`grpc-core:1.26.0`,则应在`dependencyManagement`中显式声明该版本: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-bom</artifactId> <version>1.26.0</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> ``` ### 2. 清理本地Maven缓存 如果本地仓库中存在损坏或旧版本的依赖包,可能导致解析失败。可通过删除本地Maven仓库中的相关目录强制重新下载依赖。例如: ```bash rm -rf ~/.m2/repository/io/grpc rm -rf ~/.m2/repository/org/apache/rocketmq ``` 然后执行以下命令重新构建项目: ```bash mvn clean install -U ``` 其中,`-U`参数用于强制更新快照依赖。 ### 3. 配置正确的Maven仓库 确保`pom.xml`或`settings.xml`中配置的仓库地址正确且可访问,尤其是使用私有仓库(如Nexus)时。例如在`pom.xml`中配置仓库: ```xml <repositories> <repository> <id>central</id> <url>https://repo.maven.apache.org/maven2</url> </repository> <repository> <id>alimaven</id> <url>https://maven.aliyun.com/repository/public</url> </repository> </repositories> ``` 如果使用Nexus私有仓库,确保`settings.xml`中配置了正确的认证信息和镜像设置。 ### 4. 使用Maven Exclusion排除冲突依赖 如果`rocketmq-spring-boot-starter`引入了不兼容的gRPC版本,可以通过`<exclusions>`排除其自带的gRPC依赖,并显式声明兼容版本。例如: ```xml <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot-starter</artifactId> <version>2.2.3</version> <exclusions> <exclusion> <groupId>io.grpc</groupId> <artifactId>grpc-netty</artifactId> </exclusion> </exclusions> </dependency> ``` ### 5. 检查网络和代理设置 在内网开发环境中,需确保Maven能够通过代理访问外部仓库,或已将所需依赖上传至内网Nexus仓库。在`settings.xml`中配置代理: ```xml <proxies> <proxy> <id>example-proxy</id> <active>true</active> <protocol>http</protocol> <host>proxy.example.com</host> <port>8080</port> <username>proxyuser</username> <password>somepassword</password> <nonProxyHosts>localhost|127.0.0.1</nonProxyHosts> </proxy> </proxies> ``` ### 6. 使用Maven命令行调试 使用`-X`参数启用调试日志,查看依赖解析的详细过程,有助于定位问题根源: ```bash mvn clean install -X ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值