183 Star 1.8K Fork 573

如梦技术 / mica-mqtt

 / 详情

使用中常见问题汇总

待办的
拥有者
创建于  
2021-08-13 19:13

1、mica-mqtt-spring-boot-starter 空指针 NullPointerException

详细信息: Mqtt server IMqttMessageListener Bean not found

解决方案:IMqttMessageListener 为业务处理,必须要实现的接口。实现该接口并注册成 Spring Bean 即可。

2、解码异常

  • mqtt 3.1 版协议规定 clientId 范围 1~23, 如果不再此范围会报错,mica-mqtt 服务端提供了 maxClientIdLength 参数,请按需配置。
  • mica-mqtt 默认的最大包体长度为 8092(1.3.6 开始默认为 10M),当包体大于这个值时会报异常,mica-mqtt 提供了 maxBytesInMessage 参数,请按需配置。
  • mica-mqtt readBufferSize 默认为 8k,最大值可以设置为 132476(130k,受 t-io 限制),t-io 解码会尝试 10 次,也就是最大消息体支持 1.26 M。
  • 如果要设置得更大,可以将 mica-mqtt readBufferSize 设置成 0 (mica-mqtt 1.3.6 开始支持),
  • 然后在 java 启动变量中添加 -Dtio.default.read.buffer.size=1048576 (1M,也就是最大支持 10M 消息体,请按需设置)。
  • 大量消息,业务处理不赢会导致解码异常,服务端可设置 useQueueDecode(true) 1.3.7 会默认成 true。不过业务一直处理不赢还是会照成更严重的问题。最后队列占满导致 jvm 内存溢出。建议集群并对接 kafka、rocketmq 等。

3、NoSuchMethodError: java.nio.ByteBuffer.XXX(I)Ljava/nio/ByteBuffer;

存在此问题的版本1.0.01.0.0-RC1.0.31.0.4
该问题主要是 jar 编译问题,由于 JDK9+ 改了 ByteBuffer 部分返回值的类型,导致 java9+ 下编译的 jar 在 java8 下运行会有问题。如果遇到此问题,请立刻反馈。

4、多个客户端使用相同 clientId 导致前者被踢下线(周期性上下线)

  • clientId 对于在 mqtt 中起着十分重要的作用,请不要随意设置,建议按照产品、设备、sn等维度生成,并且确保唯一
  • 如果实在是要兼容老业务,可以实现 IMqttServerUniqueIdService (1.1.4开始支持) 接口,返回的 uniqueId 会替代 clientId,后续的场景也是需要使用这个 uniqueId 来处理。

5、nginx tcp 负载均衡

5.1 搜索关键词 nginx tcp 负载均衡 即可:

5.2 配置 /etc/nginx/nginx.conf,示例:

stream {
  upstream stream_backend {
      zone tcp_servers 64k;
      hash $remote_addr;
      server 192.168.0.2:1883 max_fails=2 fail_timeout=30s;
      server 192.168.0.3:1883 max_fails=2 fail_timeout=30s;
  }

  server {
      listen 8883 ssl;
      status_zone tcp_server;
      proxy_pass stream_backend;
      proxy_buffer_size 4k;
      proxy_protocol    on; # 转发源ip信息, mica-mqtt 开源版不支持,私服版已经支持,可捐助获取
      ssl_handshake_timeout 15s;
      ssl_certificate     /etc/emqx/certs/cert.pem;
      ssl_certificate_key /etc/emqx/certs/key.pem;
  }
}

6、Mqtt 集群

mica-mqtt 1.1.2 版本开始添加了 mica-mqtt-broker 模块,采用 redis pub/sub 实现集群,有需求的朋友可以参考。

7、SNAPSHOT 版本使用

snapshots 版本会及时响应修复最新的 bug 和需求。

SNAPSHOT 版本使用参考这里:https://www.dreamlu.net/mica2x/#%E4%BD%BF%E7%94%A8-snapshots

8、ssl 证书

8.1 申请的证书

腾讯云、阿里云等提供有 jks 证书,直接申请下载,记住申请时的密码:
代码中 .useSsl("classpath:xxx.jks", "classpath:xxx.jks", "密码") 即可

8.2 自签证书(双向认证)

  1. 按这个文章生成服务端和客户端证书:https://www.zhihuclub.com/79517.shtml
  2. 将服务端证书 server-cert.pemserver-key.pem 在线转换成 jks 证书(注意,第一步1生成的时候它是没有设置私钥密码,这里不用设置,新文件密码就是 mqtt server 中要用的密码):https://myssl.com/cert_convert.html
  3. 服务端使用 .useSsl("classpath:xxx.jks", "classpath:xxx.jks", "密码") 开启 ssl。
  4. 客户端 mqttx 使用如下图:

mqttx ssl

更多教程:openssl自签名证书教程(单域名证书/泛域名证书/多域名证书)详见:https://www.orcy.net.cn/340.html

9、服务器配置调优

详见: Linux 操作系统参数和TCP 协议栈网络参数章节

10、Mqtt client 动态更新 clientId,username,password

/**
 * 客户端连接状态监听
 *
 * @author L.cm
 */
@Service
public class MqttClientConnectListener implements IMqttClientConnectListener {
	private static final Logger logger = LoggerFactory.getLogger(MqttClientConnectListener.class);

	@Autowired
	private ApplicationContext applicationContext;

	@Override
	public void onConnected(ChannelContext context, boolean isReconnect) {
		if (isReconnect) {
			logger.info("重连 mqtt 服务器重连成功...");
		} else {
			logger.info("连接 mqtt 服务器成功...");
		}
	}

	@Override
	public void onDisconnect(ChannelContext channelContext, Throwable throwable, String remark, boolean isRemove) {
		logger.error("mqtt 链接断开 remark:{} isRemove:{}", remark, isRemove, throwable);
		// 在断线时更新 clientId、username、password
		MqttClientCreator mqttClientCreator = applicationContext.getBean(MqttClientCreator.class);
		mqttClientCreator
			.clientId("newClient" + System.currentTimeMillis())
			.username("newUserName")
			.password("newPassword");
	}

}

11、浏览器 mqtt.js websocket 连接

科普:浏览器只能走 websocket mqtt 子协议,对应 mica-mqtt 8083 端口。

连错端口会报异常,如下:

org.tio.core.exception.TioDecodeException: java.lang.IllegalArgumentException: invalid QoS: 3
 at net.dreamlu.iot.mqtt.codec.MqttDecoder.doDecode(MqttDecoder.java:67)

mqtt.js websocket 示例:

const clientId = 'mqttjs_' + Math.random().toString(16).substr(2, 8)

const host = 'ws://mqtt.dreamlu.net:8083/mqtt'

const options = {
  keepalive: 60,
  clientId: clientId,
  username: 'mqtt登录用户名',
  password: 'mqtt登录密码',
  protocolId: 'MQTT',
  protocolVersion: 4,
  clean: true,
  reconnectPeriod: 1000,
  connectTimeout: 30 * 1000,
  will: {
    topic: 'WillMsg',
    payload: 'Connection Closed abnormally..!',
    qos: 0,
    retain: false
  },
}

console.log('Connecting mqtt client')
const client = mqtt.connect(host, options)

client.on('error', (err) => {
  console.log('Connection error: ', err)
  client.end()
})

client.on('reconnect', () => {
  console.log('Reconnecting...')
})

十二、mqtt 心跳超时

  • 客户端默认心跳超时 60s
  • 服务端默认心跳检测 120s
  • 服务端会默认以 1.5倍(keepaliveBackoff * 2) 客户端心跳超时进行判断,最长周期最长 2.5 倍。(所以客户端实际超时时间为 90s ~ 150s)

拔网线非正常断开需要一个心跳检测周期才会触发断开。

十三、client、server 同时使用时 caffeine 依赖异常

Failed to instantiate [net.dreamlu.iot.mqtt.core.server.MqttServer]: Factory method 'mqttServer' threw 
exception; nested exception is java.lang.NoClassDefFoundError: 
com/github/benmanes/caffeine/cache/RemovalListener

解决方案: pom 中将 mqtt server 依赖放 mqtt client 前面。

评论 (0)

如梦技术 创建了任务
如梦技术 关联仓库设置为如梦技术/mica-mqtt
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 置顶等级设置为
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
如梦技术 修改了描述
展开全部操作日志

登录 后才可以发表评论

状态
负责人
里程碑
Pull Requests
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
开始日期   -   截止日期
-
置顶选项
优先级
参与者(1)
372 dreamlu 1578913784
Java
1
https://gitee.com/596392912/mica-mqtt.git
git@gitee.com:596392912/mica-mqtt.git
596392912
mica-mqtt
mica-mqtt

搜索帮助

53164aa7 5694891 3bd8fe86 5694891