项目场景:
应用中需要连接两个RocketMQ集群,即就是两个不同的NameServer,消费两个集群里的消息。(RocketMQ Client 版本4.4.0)
问题描述:
应用启动后在RocketMQ控制台发现,两个消费都注册到同一个集群(期望是根据配置分别注册到不同的NameServer)
DefaultMQPushConsumer consumer1 = new DefaultMQPushConsumer();
consumer1.setConsumerGroup(groupName);
consumer1.setNamesrvAddr(namesServer1);
consumer1.start();
DefaultMQPushConsumer consumer2 = new DefaultMQPushConsumer();
consumer2.setConsumerGroup(groupName);
consumer2.setNamesrvAddr(namesServer2);
consumer2.start();
原因分析:
初步估计是配置的两个NameServer只有一个生效,把两个配置改为一致后,消费者注册正常,后面查看了RocketMQ源码,发现了问题所在。
org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#start();
//获取MQ客户端实例
this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook);
/**
* 构建MQ客户端id,规则是IP@instanceName@unitName
* instanceName默认为进程id
*/
public String buildMQClientId() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClientIP());
sb.append("@");
sb.append(this.getInstanceName());
if (!UtilAll.isBlank(this.unitName)) {
sb.append("@");
sb.append(this.unitName);
}
return sb.toString();
}
if (this.instanceName.equals("DEFAULT")) {
this.instanceName = String.valueOf(UtilAll.getPid());
}
public MQClientInstance getAndCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) {
String clientId = clientConfig.buildMQClientId();
MQClientInstance instance = this.factoryTable.get(clientId);
if (null == instance) {
instance =
new MQClientInstance(clientConfig.cloneClientConfig(),
this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook);
MQClientInstance prev = this.factoryTable.putIfAbsent(clientId, instance);
if (prev != null) {
instance = prev;
log.warn("Returned Previous MQClientInstance for clientId:[{}]", clientId);
} else {
log.info("Created new MQClientInstance for clientId:[{}]", clientId);
}
}
return instance;
}
由上图可以看出MQClientInstance是通过clientId在factoryTable内存中查询的,默认一个进程中的clientId是一致的,因此很好的解释了,我们出现的问题。
解决方案:
由上可以知道解决问题的方案就是多个MQClient需要使用不同的ClientId,通过为消费者设置不同的instanceName或者unitName可以解决。