Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用中常见问题汇总 #2

Open
ChunMengLu opened this issue Aug 27, 2021 · 2 comments
Open

使用中常见问题汇总 #2

ChunMengLu opened this issue Aug 27, 2021 · 2 comments

Comments

@ChunMengLu
Copy link
Member

ChunMengLu commented Aug 27, 2021

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 前面。

@1303531042
Copy link

"before close server:127.0.0.1:1883, client:192.168.10.63:61594, after closeserver:127.0.0.1:1883, client:$UNKNOWN:"为什么这里表示client unknown, 我的yaml肯定没有问题我使用的是emqx, 还有就是我之前使用mosquitto的时候“2024-05-03 15:26:45 [tio-worker-2] INFO org.tio.core. task. CloseRunnable

  • TioClientConfig [name=Mica-Mqtt-CLient],server:mosquitto:1883,client:172.18.0.5:35542 准备关闭连接,
    isNeedRemove:
    false,对方关闭了连接
    2024-05-03 15:26:45 [tio-worker-2] INFO org.tio.core.ChannelContext
  • 关闭前server:mosquitto:1883,client:172.18.0.5:35542,关闭后server:mosquitto:1883,cljent:$UNKNOWN:52”

@ChunMengLu
Copy link
Member Author

"before close server:127.0.0.1:1883, client:192.168.10.63:61594, after closeserver:127.0.0.1:1883, client:$UNKNOWN:"为什么这里表示client unknown, 我的yaml肯定没有问题我使用的是emqx, 还有就是我之前使用mosquitto的时候“2024-05-03 15:26:45 [tio-worker-2] INFO org.tio.core. task. CloseRunnable

  • TioClientConfig [name=Mica-Mqtt-CLient],server:mosquitto:1883,client:172.18.0.5:35542 准备关闭连接,
    isNeedRemove:
    false,对方关闭了连接
    2024-05-03 15:26:45 [tio-worker-2] INFO org.tio.core.ChannelContext
  • 关闭前server:mosquitto:1883,client:172.18.0.5:35542,关闭后server:mosquitto:1883,cljent:$UNKNOWN:52”

unknown 表示释放 ip 配置了,将 192.168.10.63:61594 配置成了 unknown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants