AliMQ(RockertMQ)源码(四)创建消费者

一般创建AliMQ(RocketMQ)的Consumer的方式都是如下:

AliMQHandler.buildConsumer(accessKey, secretKey,
                systemCancelOrder.get("topicId"),
              "CANCEL_ORDER"+"||"+systemCancelOrder.get("tagPolicy"), // systemCancelOrder.get("tag"),
                systemCancelOrder.get("consumerId"),
                systemCancelOrderListener);

里面如下:

    public static Consumer buildConsumer(
            String accessKey, String secretKey,
            String topicId, String tag, String consumerId,
            MessageListener messageListener) {
        Properties properties = new Properties();
        properties.put("ConsumerId", consumerId);
        properties.put("AccessKey", accessKey);
        properties.put("SecretKey", secretKey);
        Consumer mqConsumer = ONSFactory.createConsumer(properties);
        mqConsumer.subscribe(topicId, tag, messageListener);
        mqConsumer.start();
        return mqConsumer;
    }

可以看到真正执行的是下面三个方法,至于创建mq的原理是什么,里面是怎么实现的呢,一起看下。

创建consumer

Consumer mqConsumer = ONSFactory.createConsumer(properties);

在这个方法中创建了consumer,实则是用Properties创建了一个新的Consumer。

    public ConsumerImpl(final Properties properties) {
        super(properties);
        boolean postSubscriptionWhenPull = Boolean.parseBoolean(properties.getProperty(PropertyKeyConst.PostSubscriptionWhenPull, "false"));
        this.defaultMQPushConsumer.setPostSubscriptionWhenPull(postSubscriptionWhenPull);

        String messageModel = properties.getProperty(PropertyKeyConst.MessageModel, PropertyValueConst.CLUSTERING);
        this.defaultMQPushConsumer.setMessageModel(MessageModel.valueOf(messageModel));
    }

这个方法中先调用了父类ONSConsumerAbstract中的构造方法,在ONSConsumerAbstract的构造方法中又调用了ONSClientAbstract的构造方法。

一、ONSClientAbstract的构造方法

  • 连接mq的认证参数:
this.sessionCredentials.updateContent(properties);

然后检测参数有效性。

  • 设置那么Server地址:
// 用户指定了Name Server
        // 私有云模式有可能需要
        this.nameServerAddr = this.properties.getProperty(PropertyKeyConst.NAMESRV_ADDR);
        if (nameServerAddr != null) {
            return;
        }
        this.nameServerAddr = fetchNameServerAddr();
  • 然后有一个轮询的线程,每30秒检测一次nameServer地址是否已变更:
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            public void run() {
                try {
                    String nsAddrs = fetchNameServerAddr();
                    if (nsAddrs != null && !ONSClientAbstract.this.nameServerAddr.equals(nsAddrs)) {
                        ONSClientAbstract.this.nameServerAddr = nsAddrs;
                        if (isStarted()) {
                            updateNameServerAddr(nsAddrs);
                        }
                    }
                } catch (Exception e) {
                    log.error("update name server periodically failed.", e);
                }
            }
        }, 10 * 1000L, 30 * 1000L, TimeUnit.MILLISECONDS);
  • 这块逻辑其实是consumer与producer共用的。

二、ONSConsumerAbstract的构造方法

  • 创建Consumer
this.defaultMQPushConsumer = new DefaultMQPushConsumer(new OnsClientRPCHook(sessionCredentials));

在创建consumer的时候创建了一个钩子,用于填写与broker连接前的校验参数。
consumer中有许多参数,包括客户端ip,消费的策略(默认是CONSUME_FROM_LAST_OFFSET)等。

  • Consumer的配置项设置
        boolean isVipChannelEnabled = Boolean.parseBoolean(properties.getProperty(PropertyKeyConst.isVipChannelEnabled, "false"));
        this.defaultMQPushConsumer.setVipChannelEnabled(isVipChannelEnabled);

        this.defaultMQPushConsumer.setConsumerGroup(consumerGroup);
        String instanceName = properties.getProperty(PropertyKeyConst.InstanceName, this.buildIntanceName());
        this.defaultMQPushConsumer.setInstanceName(instanceName);
        this.defaultMQPushConsumer.setNamesrvAddr(this.getNameServerAddr());

        String consumeThreadNums = properties.getProperty(PropertyKeyConst.ConsumeThreadNums);
        if (!UtilAll.isBlank(consumeThreadNums)) {
            this.defaultMQPushConsumer.setConsumeThreadMin(Integer.valueOf(consumeThreadNums));
            this.defaultMQPushConsumer.setConsumeThreadMax(Integer.valueOf(consumeThreadNums));
        }

        String configuredCachedMessageAmount = properties.getProperty(PropertyKeyConst.MaxCachedMessageAmount);
        if (!UtilAll.isBlank(configuredCachedMessageAmount)) {
            maxCachedMessageAmount = Math.min(MAX_CACHED_MESSAGE_AMOUNT, Integer.valueOf(configuredCachedMessageAmount));
            maxCachedMessageAmount = Math.max(MIN_CACHED_MESSAGE_AMOUNT, maxCachedMessageAmount);
            this.defaultMQPushConsumer.setPullThresholdForTopic(maxCachedMessageAmount);

        }

        String configuredCachedMessageSizeInMiB = properties.getProperty(PropertyKeyConst.MaxCachedMessageSizeInMiB);
        if (!UtilAll.isBlank(configuredCachedMessageSizeInMiB)) {
            maxCachedMessageSizeInMiB = Math.min(MAX_CACHED_MESSAGE_SIZE_IN_MIB, Integer.valueOf(configuredCachedMessageSizeInMiB));
            maxCachedMessageSizeInMiB = Math.max(MIN_CACHED_MESSAGE_SIZE_IN_MIB, maxCachedMessageSizeInMiB);
            this.defaultMQPushConsumer.setPullThresholdSizeForTopic(maxCachedMessageSizeInMiB);
        }

包括是否走vipChannel,为什么叫做vip通道呢?因为rocketmq监听的这个端口的消息少一点,特别是存储消息到磁盘的那块逻辑,vip通道是不会做的。所以个人觉得这个通道响应速度会快点。

设置consumerGroup,实例名称,nameServer地址,消费者线程的最小和最大线程数,包括一些在PropertyKeyConst类中的配置(客户端缓存消息数量,客户端缓存最大内容)。

  • 拉取消息的前置和后置处理类的创建
        // 为Consumer增加消息轨迹回发模块
        String msgTraceSwitch = properties.getProperty(PropertyKeyConst.MsgTraceSwitch);
        if (!UtilAll.isBlank(msgTraceSwitch) && (!Boolean.parseBoolean(msgTraceSwitch))) {
            log.info("MQ Client Disable the Trace Hook!");
        } else {
            try {
                Properties tempProperties = new Properties();
                tempProperties.put(OnsTraceConstants.AccessKey, sessionCredentials.getAccessKey());
                tempProperties.put(OnsTraceConstants.SecretKey, sessionCredentials.getSecretKey());
                tempProperties.put(OnsTraceConstants.MaxMsgSize, "128000");
                tempProperties.put(OnsTraceConstants.AsyncBufferSize, "2048");
                tempProperties.put(OnsTraceConstants.MaxBatchNum, "100");
                tempProperties.put(OnsTraceConstants.NAMESRV_ADDR, this.getNameServerAddr());
                tempProperties.put(OnsTraceConstants.InstanceName, "PID_CLIENT_INNER_TRACE_PRODUCER");
                tempProperties.put(OnsTraceConstants.TraceDispatcherType, OnsTraceDispatcherType.CONSUMER.name());
                AsyncArrayDispatcher dispatcher = new AsyncArrayDispatcher(tempProperties, sessionCredentials);
                dispatcher.setHostConsumer(defaultMQPushConsumer.getDefaultMQPushConsumerImpl());
                traceDispatcher = dispatcher;
                this.defaultMQPushConsumer.getDefaultMQPushConsumerImpl().registerConsumeMessageHook(
                    new OnsConsumeMessageHookImpl(traceDispatcher));
            } catch (Throwable e) {
                log.error("system mqtrace hook init failed ,maybe can't send msg trace data", e);
            }
        }

三、ConsumerImpl的创建

boolean postSubscriptionWhenPull = Boolean.parseBoolean(properties.getProperty(PropertyKeyConst.PostSubscriptionWhenPull, "false"));
        this.defaultMQPushConsumer.setPostSubscriptionWhenPull(postSubscriptionWhenPull);
  • 判断每次请求是否都带上最新的订阅关系。
        String messageModel = properties.getProperty(PropertyKeyConst.MessageModel, PropertyValueConst.CLUSTERING);
        this.defaultMQPushConsumer.setMessageModel(MessageModel.valueOf(messageModel));
  • 设置发送的集群消息还是广播消息。

你可能感兴趣的:(rocketmq,RocketMQ源码解析)