前言
rocketMQ的instanceName
可以在生产端设置,也可以在消费端设置。平时不用设置,但是当一个JVM中,含有多个生产者消费者时,必须设置下。
原因
如果一个JVM有多个生产者和消费者时,那么他们会共用MQClientInstance
,这样一些异步线程,定时拉取topic,心跳,nettyclient等线程都是一份,可以节省资源。
共用的实例MQClientInstance
是从MQClientManager
获取的,而它这个类是个单例。它将所有的类缓存到了factoryTable
这个map当中。
public class MQClientManager {
private static MQClientManager instance = new MQClientManager();
private ConcurrentMap<String/* clientId */, MQClientInstance> factoryTable =
new ConcurrentHashMap<String, MQClientInstance>();
private MQClientManager() {
}
}
他们的唯一区分就是通过clientId
来区分的,clientId是构造出来,如下:
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();
}
如果是集群模式,这里的instanceName会进行替换为当前线程pid,如下:
public void changeInstanceNameToPID() {
if (this.instanceName.equals("DEFAULT")) {
this.instanceName = String.valueOf(UtilAll.getPid());
}
}
所以,最后的clientId就是:
ip@instanceName@unitName
即:
ip@pid@unitName
当消息发送时,拿到MQClientInstance
获取brokerAddr地址,然后进行发送。
多个生产者消费者,如果instanceName一致。那么设置了namesrvAddr没用,最后拿到的还是相同的MQClientInstance
,发送的还是instance对应的broker。
解决办法
- 设定不同的instanceName
- 设定不同的unitName
疑问
问题见:容器化后 部署RocketMQ consumer instanceName重复?
这里有待考虑,容器化后,如果是host模式下,那么多个项目在容器内ip是一样的,而pid也会有可能一样,最后造成instanceName的一样。但是还有疑问?即使一样了,那也是在不同的JVM当中,应该不会影响到发送?这里还需要了解下,重复对发送造成的影响。
参考
- 推荐,含有特殊解决办法:如何在同一个Java进程中连接多个RocketMQ服务器
- 实际项目出现:rocketmq consumer配置踩坑
- 给出方法和建议:rocketmq InstanceName 作用和配置解析