MQTT是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT是专门针对物联网开发的轻量级传输协议。MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。
而我却拿MQTT来做实时日志系统。
主要基于这几个点,轻量,实现简单,支持QoS,支持TLS,是个发布/订阅协议,支持消息推送。
其实公司日志系统那么多,像ULS,nlog什么的,为什么又又又要造轮子呀?因为要么接入麻烦,要么语言不支持,要么需要资源,要么支持不到位,要么丢消息等。而我的需求背景是,有几台服务器,目前的日志是打印到本地文件中,每次要查日志都要登录运营机器,而且还要登录两台,不大方便。因此,我想要的就是,只在一处地方统一实时查看所有日志,而且对现有系统最小的改动。
而了解到MQTT也实在是偶然,在一个ruff的群里有人基于MQTT对物联设备做实时控制,而线上的机器完全也可以当作一个物联设备,我用MQTT来做远程控制,控制内容就是把写到文件的日志直接转发到中心服务器就可以了。架构图长这样子:
中心服务器是一个MQTT服务器,而其它机器都其实是个MQTT客户端。控制端发布订阅消息,要求收集日志,通过中心服务器转发到了日志源机器;日志源机器接收到收集命令,一打文件日志就直接发布消息,由中心服务器转发。这个过程其实是个多对多的广播过程,容易出现的问题就是消息重复,当然这个可以通过一些唯一标识来消除,另外,再设定QoS(0,1,2,至少选1),可以保证不丢消息,然而时序并不能保证,所以发布每条日志的时候需要把时间戳也带上,在必要的时候可作为时序参考依据。
那这个就是基本原理,MQTT协议的使用保证了数据传输的可靠性,而在这个基础上,我们也可以有进一步的发挥,定制更多的命令来对各个日志源机器进行控制。日志机器可以进行横向扩展,在MQTT服务器眼中就只是一些物联设备,中心服务器也可以按照协议进行桥接,而控制端也可以是各种有MQTT实现的终端和web页面(websocket实现的长连接)。
在这个大而虚的解决思路指导下,我们的业务需求是这样子实现的。
MQTT服务器采用mosquitto,客户端采用mqtt.js。这样子的选择原因,作为前端开发首选语言自然是javascript,不仅支持服务器开发,也适用浏览器端开发,遗憾的是mosca.js作为一个服务器对QoS的实现不到位,所以选择另一个实现即mosquitto,当然其它选择也不会差到哪里去。
mosquitto的事情主要是配置,支持websocket需要指定配置:
代码语言:javascript复制listener 2883 0.0.0.0
protocol websockets
需要注意的问题是需要编译安装libsockets来支持websockets。
日志源和控制端需要分别进行开发,互相派发和执行命令。
因为需要打印日志的已有系统有五个之多,考虑到自己维护的成本,能够增加一个旁路不动声色地把本地日志给传输出去是最理想的。想到对文件修改的监听,linux系统有inotify接口,而node.js也有个watchFile的封装,而且很好用,简直完美~~用node.js实现一个tail -f 的功能就好了,而tail的结果实时传输给订阅者,订阅者再按序进行拼接就大功告成了。
然而~~现实毕竟是现实,全量日志一天会有个几G吧,虽然硬盘也不是很值钱,但是想着实际在排查问题看日志的时候,只会查看指定用户,不会看这让人头晕的流水,所以,在已有系统的代码里植入一个函数,只传输指定用户(比如说appid)的日志,即实现了染色日志的功能。
至此,核心功能完成。
边边角角,却还是有很多细节,比如说在浏览器端实现控制,MQTT是否能传输存量的大文件?
传输大文件简单的测试就挂了,貌似是mqtt.js的bug,然而暂时无法深究,直接还是用传统的http搞定。
最后的总结,这个系统鄙人命之为 fishing,把日志像钓鱼一样给钓上来。不同于ULS,并不用预先申请存储资源,是充分利用了机器的闲置存储资源,而且实时展示,对于排查指定用户问题迅速有效。