MQClientInstance不是对外应用类,也就是说用户不需要自己实例化使用他。并且,MQClientInstance的实例化并不是直接new后使用,而是通过MQClientManager这个类型。MQClientManager是个单例类,使用饿汉模式设计保证线程安全。他的作用是提供MQClientInstance实例,RocketMQ认为,MQClientInstance的实例是可以复用的实例,只要client相关特征参数相同,就会复用一个MQClientInstance实例,我们可以看看源码
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;
}
所以只要ClientConfig里的相关参数一致,这些Client会复用一个MQClientInstance,使用的时候需要注意,你的程序里的Client和MQClientInstance的对应关系。下面我们来看看RocketMQ对参数配置如何的Client复用一个MQClientInstance,简单来说就是IP@instanceName@unitName,这么一个串,其中IP我不解释了,InstanceName可以设置,Producer和Consucer都有相应的API设置,如果不设置使用缺省值即Client的PID,unitName不设置缺省为null,当然你可以DefaultMQProducer#setUnitName()这个方法设置这个值,这个方法是继承自ClientConfig类的。
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();
}
那复用一个MQClientInstance会有怎么的结果呢?这种情况会出现在你在一个JVM里启动了多个Producer时,且没有设置instanceName和unitName,那么这两个Producer会公用一个MQClientInstance,发送的消息会路由到同一个集群。例如,你起了两个Producer,并且配置的NameServer地址不一样,本意是让这两个Producer往不同集群上分配消息,但是由于共用了一个MQClientInstance,这个MQClientInstance是基于先来的Producer配置构建的,第二个Producer和他公用后被认为是同一instance,配置是相同的,消息的路由就是相同的,就没有达到你想要的效果。