一、遇到的实际问题:设备突然拒连的怪现象
上周帮公司排查物联网接入的问题,前一天还正常用的100台智能电表,突然有20多台连不上了,后台日志全是“MQTT CONNECT failed: username password authentication error”。查了下,这部分电表的设备ID,在我们公司的设备管理系统里是正常的,权限也没被改,为啥认证失败?翻EMQX的配置和日志,才发现是两个认证插件的加载顺序搞反了,导致本该生效的认证没被调用。
二、搞懂EMQX里的两个常用认证:HTTP vs 内置数据库认证
要解决这个问题,得先搞懂EMQX里这俩认证是啥,怎么干活的。可以简单把EMQX比作小区门口的保安,内置数据库认证就是保安手里拿着业主登记本,查一下你是不是登记过的业主,是就放行;HTTP认证就是保安要给物业后台打个电话,核对你的身份,后台说你是业主就放行,后台说你不行就不让进。
2.1 两种认证的核心区别
内置数据库认证,就是EMQX自己存了用户和密码,直接在EMQX里查,速度快,适合设备数量不多、认证逻辑简单的情况,比如只有几百台设备的小项目,你把设备ID和密码直接存在EMQX里就行,不用额外写代码。 HTTP认证呢,相当于把认证的逻辑放到你自己的服务里,比如你要根据设备的类型(是智能电表还是门禁)、在线时长、是否缴费这些来判断能不能连,这时候就用HTTP认证,每次设备连的时候,EMQX会把设备的账号密码发给你自己的服务,你服务返回“成功”或者“失败”,灵活性高,适合复杂业务,但要自己写接口,还得依赖自己的服务稳定,速度也会慢一点,毕竟要走网络请求。
2.2 为啥会搞混优先级
现在小区里可能有两个保安同时在岗,谁先查你的身份?EMQX里的认证插件是按加载顺序来的,先加载的插件会先查你的身份,只要有一个插件说“通过”,就直接放行,不用再查后面的;只有前面所有插件都说“失败”,才会拒连。这就容易踩坑:比如你要让两个认证都生效,既要用HTTP核对业务逻辑,又要用内置数据库做权限兜底,这时候加载顺序错了,就会出问题。
三、踩坑现场:加载顺序搞反导致的设备拒连
这次的问题就是这么发生的:我们公司要1000多台设备,大部分用内置数据库认证,但是最近新上了20多台电表,要加额外的业务校验,所以配了HTTP认证,想让HTTP先查,再走内置的兜底,结果配置的时候把顺序搞反了,内置数据库的插件排在了HTTP前面。那20多台电表的设备ID,在EMQX的内置数据库里没有,EMQX先调用内置数据库认证,查不到,就直接返回失败,根本没调用后面的HTTP认证,设备就被拒连了。
3.1 错误的插件顺序配置
先看当时的错误配置,用EMQX的命令看加载的插件:
# 查看EMQX已加载的认证插件,错误顺序是emqx_auth_mnesia(内置)在前,emqx_auth_http在后
emqx plugins list | grep auth
# 输出内容
emqx_auth_mnesia 5.3.1 loaded
emqx_auth_http 5.3.1 loaded
这时候,新的电表设备,内置数据库里没有,认证失败,就直接拒连,HTTP插件完全没用到。
3.2 顺便踩的缓存坑
除了加载顺序,还踩了HTTP认证的缓存坑。当时配HTTP认证的时候,随便设了缓存时间1小时,结果有台设备的状态变了,我们想让它不能连,但是EMQX里存的缓存还是之前的“成功”,所以设备照样能连。这个缓存坑的问题很隐蔽,出问题的时候你查日志,每次都显示“HTTP认证成功”,但其实后台已经改了,只是EMQX没读到最新的。比如当时的HTTP配置是这样的:
// 错误的HTTP认证配置,缓存时间太长
{
"mechanism": "password",
"url": "http://192.168.1.100:8080/api/device/auth",
"method": "POST",
"body": "{\"device_id\":\"${username}\",\"password\":\"${password}\"}",
"cache": {
"enable": true,
"ttl": 3600, // 缓存1小时,太久了,设备状态变了也不会更新
"max_size": 10000
}
}
这里的缓存是EMQX把最近的认证结果存在本地,相同的设备一段时间内不会再调你的HTTP服务,省网络请求,但是如果设备的认证状态变了,比如被拉黑,缓存没过期的话,就会一直错下去。
四、正确的操作姿势:优先级设置+缓存优化
找到问题后,我们调整了两个地方,很快就解决了。
4.1 调整插件加载顺序
首先把HTTP认证插件的顺序调到前面,让它先执行,这样设备连的时候,先调用HTTP的业务校验,过了再或者没通过,再看要不要走内置的兜底。调整的命令是:
# 调整插件加载顺序,让HTTP认证优先执行
emqx plugin enable --order emqx_auth_http emqx_auth_mnesia
# 验证调整后的顺序
emqx plugins list | grep auth
# 正确的输出,HTTP在前,内置数据库在后
emqx_auth_http 5.3.1 loaded
emqx_auth_mnesia 5.3.1 loaded
这时候再试那20多台电表,就会先调用HTTP认证,HTTP返回成功,就直接连了,不会再卡在内置数据库查不到的问题。
4.2 避开缓存坑的优化
然后调整了HTTP认证的缓存配置,把缓存时间改成5分钟,而且每次有设备状态变更(比如拉黑、解封)的时候,主动清空EMQX里的对应缓存。修改后的HTTP配置:
// 优化后的HTTP认证配置,缓存合理,支持主动清缓存
{
"mechanism": "password",
"url": "http://192.168.1.100:8080/api/device/auth",
"method": "POST",
"body": "{\"device_id\":\"${username}\",\"password\":\"${password}\"}",
"cache": {
"enable": true,
"ttl": 300, // 缓存5分钟,足够短,状态变了很快更新
"max_size": 10000
}
}
主动清缓存的API,我们是这么用的,当有设备被拉黑时,调用这个接口清掉EMQX里的缓存,设备下次连的时候就会重新调HTTP服务,拿到最新的状态:
# 清空指定设备的认证缓存,EMQX的API接口
curl -X DELETE "http://your-emqx-ip:18083/api/v5/cache/auth?username=device_123" \
-u admin:your-emqx-admin-password
注意,调用API的时候要填对EMQX的管理账号密码,还要确保EMQX的API是开启的,默认端口是18083,这个不要漏。
五、核心要点总结
这次踩坑后,我整理了几个必须记住的点:
- 加载顺序决定EMQX认证的优先级,先加载的插件先执行认证,只有前面全部失败才会走后面的,所以要根据业务需求调整顺序,比如要复杂业务校验就把HTTP放前面,简单内置认证放后面;
- HTTP认证适合有复杂业务逻辑的场景,不用把所有设备数据都存在EMQX里,维护起来方便,缺点是依赖自己的服务,还要处理网络请求的超时和失败;
- 缓存坑是HTTP认证最容易踩的,缓存时间不要太长,一般设1-5分钟就够,重要的状态变更(比如拉黑设备)一定要主动清缓存,避免EMQX用旧的认证结果;
- 如果要同时用多个认证(比如HTTP+内置),最好用EMQX的认证链功能,明确每个认证的触发条件,而不是只靠加载顺序,这样更稳妥,避免意外情况。
评论
围绕“EMQX认证插件加载顺序不对导致部分设备拒连,理清HTTP认证与内置数据库认证的优先级与缓存坑”参与讨论