案情描述
生产者发送消息后,消费者对消息要么不能消费(NOT_CONSUME_YET),要么出现重复消费。在集群模式(CLUSTERING
)消息模式下,同一个消息应当向一个消费组下的其中一个消费者发送消息,而不是向消费组下的所有消费者发送消息。而血案就造成了这种重复消费。
在2021-03-16 的代码提交时已经解决了该问题。
这里的修改,解决了什么 ?
答 : 默认实例名后面加入了时间戳(纳秒的时间戳哦)防止实例名称重复。
防止实例名重名有什么作用 ?
答 : 客户端ID由客户端IP + 实例名(instanceName) + unitName 。源代码如下 :
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();
}
客户端Id重复会造成什么问题 ?
答 : 会造成消息
NOT_CONSUME_YET
。当然这种情况仅仅会出现在一个消费组有多个消费者,并且使用Docker进行部署消费者的情况。
为什么docker部署会造成客户端Id重复 ?
答:使用docker部署程序,由于docker内是一个相对隔离的环境,并且docker容器内大多数仅仅启动一个进程,而docker启动第一个进程的进程ID都是
1
,因此PID是相同的,由于docker内部网络环境也是相对独立的,造成获取的客户端IP也是相同的。这时如果没有设置unitName
,就会造成多个消费者客户端客户端ID相同。
为什么消费者客户端ID相同会造成NOT_CONSUME_YET
?
答 : 名称服务是KV方式存储信息。消费者会注册给命名服务器。如果ID重复,那么名称服务器就无法根据客户端ID找到正确的消费者客户端(也可能能找对,找对了就能消费)。
解决方案:
- 配置unitName。
- 客户化InstanceName