lets-mica / mica-mqtt

java mqtt 基于 java aio 实现,开源、简单、易用、低延迟、高性能百万级 java mqtt client 组件和 java mqtt broker 服务。🔝🔝 记得右上角点个star 关注更新!
https://www.dreamlu.net
Apache License 2.0
266 stars 49 forks source link

使用中常见问题汇总 #2

Open ChunMengLu opened 3 years ago

ChunMengLu commented 3 years ago

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

详细信息: Mqtt server IMqttMessageListener Bean not found

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

2、解码异常

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 导致前者被踢下线

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 心跳超时

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

十三、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 commented 4 months ago

"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

ChunMengLu commented 4 months ago

"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