mqtt mosquitto服务搭建与配置

Linux 发表时间:2021-11-23 14:18:30 作者:梁子亮 浏览次数:1545

第一步,先安装mosquito

docker安装mosquitto(2选1)

先拉取本地镜像,这里拉取的是1.6.15版,因为latest版会有各种问题,暂时无解

docker pull eclipse-mosquitto:1.6.15

执行如下命令创建目录

mkdir -p /mosquitto/config
mkdir -p /mosquitto/data
mkdir -p /mosquitto/log

执行如下命令创建初始化配置文件

vim /mosquitto/config/mosquitto.conf

在配置文件中添加如下内容,然后保存退出

persistence true
persistence_location /mosquitto/data
log_dest file /mosquitto/log/mosquitto.log

执行如下命令为目录授权(其中日志目录要最大权限)

chmod -R 755 /mosquitto
chmod -R 777 /mosquitto/log

执行如下命令即可启动 mosquitto 容器

docker run -d --name=mosquitto --privileged \
-p 1883:1883 -p 9001:9001 \
-v /mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf \
-v /mosquitto/data:/mosquitto/data \
-v /mosquitto/log:/mosquitto/log \
eclipse-mosquitto:1.6.15

若还需要配置权限,则还需要以下操作,注:在docker的mosquitto服务已启动的情况下执行

打开配置文件(/mosquitto/config/mosquitto.conf),添加以下配置

# 关闭匿名模式
allow_anonymous false
# 指定密码文件
password_file /mosquitto/config/pwfile.conf

接着执行如下命令进入容器

docker exec -it mosquitto sh

执行如下命令建立 pwfile.conf 文件,并设置权限

touch /mosquitto/config/pwfile.conf
chmod -R 755 /mosquitto/config/pwfile.conf

然后使用 mosquitto_passwd 命令创建用户(比如下面我们创建了一个名为 lzl 的用户,密码为 123)

mosquitto_passwd -b /mosquitto/config/pwfile.conf lzl 123

添加完毕后执行 exit 退出容器,并重启docker的mosquitto服务

docker restart mosquitto

至此,配置账户密码完成,详情也可以参考 https://www.hangge.com/blog/cache/detail_2896.html

正常安装mosquitto(2选1)

在root目录中下载mosquitto1.6.15版

wget http://mosquitto.org/files/source/mosquitto-1.6.15.tar.gz

解压文件

tar -zxvf mosquitto-1.6.15.tar.gz

进入目录,执行make和make install

cd mosquitto-1.6.15/
make
make install

可以配置指定用户发布指定主题,或者订阅指定主题

在/etc/mosquitto中参考aclfile.example即可,如tom只能订阅company/building/floor这个主题

user tom
topic read company/building/floor

例如tim只能发布company/building这个主题

user tim
topic write company/building

例如tam只能发布和订阅company/#下的主题

user tam
topic readwrite company/#


第二步,安装mosquitto时使得其支持websocket链接

扩展,若想要mosquitto支持websocket连接,则需要在以上安装步骤前(若已经执行了以上第一步的步骤,则需要卸载或删除掉/root/mosquitto/整个目录先)执行以下

登录https://github.com/warmcat/libwebsockets上下载websocket源码,放到root目录下

wget https://github.com/warmcat/libwebsockets/archive/refs/heads/main.zip

使用unzip 命令解压后,进入解压后的/root/libwebsockets-main/目录

unzip libwebsockets-main.zip

创建一个build目录,进入build目录,cmake..,然后 make & make install,大致命令如下

cd libwebsockets-main/
mkdir build
cd build
cmake ..
make & make install

安装完成libwebsockets后,再执行第一步中的重新编译安装步骤,注意 make & make install前需要先vim修改/root/mosquitto-1.6.15/config.mk,开启其支持websocket

WITH_WEBSOCKETS:=yes

再继续第一步中的步骤,即make , make install等

安装成功后,修改/etc/mosquitto/mosquitto.conf,在最底部添加如下

port 1883
listener 9001
protocol websockets

以下两个是数据持久化设置,也可以在/etc/mosquitto/mosquitto.conf的最底部添加上即可

autosave_on_changes 1
autosave_interval 1

然后启动mosquitto

mosquitto -c /etc/mosquitto/mosquitto.conf -d

启动时可能会报错 error while loading share libraries libwebsockets什么的,则添加以下软连接即可

ln -s /usr/local/lib/libwebsockets.so.19 /usr/lib64/libwebsockets.so.19

再次重新启动mosquitto

mosquitto -c /etc/mosquitto/mosquitto.conf -d

重启时可能会没反应,netstat观察也没发觉有1883端口启动,使用mosquito命令查看报错如下

mosquito

1656653967: mosquitto version 1.6.15 starting

1656653967: Using default config.

1656653967: Opening ipv4 listen socket on port 1883.

1656653967: Opening ipv6 listen socket on port 1883.

1656653967: Error: Invalid user 'mosquitto'.

则是不能使用默认的“mosquito”用户去启动mosquito

解决方案,查看下文红色部分即可(方案1方案2任选其一)

netstat -ntlp 查看端口,已经开启9001以及1883端口,且支持websocket链接

netstat -ntlp


第三步,使用ssl证书配置mosquitto连接

在随便一个目录中生成ca证书(以下命令会生成ca.key)

openssl genrsa -des3 -out ca.key 2048

输入密码 123456, 确认密码 123456(以下命令会生成ca.crt)

openssl req -new -x509 -days 1826 -key ca.key -out ca.crt

输入刚才设置的密码 123456,然后按照一步步的步骤设置即可(注意common name不能与server和client的common name一样,这里设置为0.0.0.0即可)

Country Name (2 letter code) [XX]:CN									【国家代码,两个字母】
State or Province Name (full name) []:guangdong						       【省,可省略不填】
Locality Name (eg, city) [Default City]:guangdong                                                  【市,可省略不填】
Organization Name (eg, company) [Default Company Ltd]:zmkm               【证书持有者所属组织或公司】
Organizational Unit Name (eg, section) []:                                                        【证书持有者所属部门,可以不填】
Common Name (eg, your name or your server's hostname) []:0.0.0.0                        【域名,一定不要和sever、client端证书的这个字段相同】
Email Address []:                                                                        【邮件,可以不填】
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:                                                                                  【自定义密码,可以不填】                      
An optional company name []:                                                                         【可选公司名称,可以不填】

生成server证书(以下命令会生成server.key)

openssl genrsa -out server.key 2048

输入密码 123456, 确认密码 123456

openssl req -new -out server.csr -key server.key

输入刚才设置的密码 123456,然后按照一步步的步骤设置即可(注意common name不能与上面的ca的common name一样,这里设置为服务的ip即可)

Country Name (2 letter code) [XX]:CN									【国家代码,两个字母】
State or Province Name (full name) []:guangdong						       【省,可省略不填】
Locality Name (eg, city) [Default City]:guangdong                                                  【市,可省略不填】
Organization Name (eg, company) [Default Company Ltd]:zmkm               【证书持有者所属组织或公司】
Organizational Unit Name (eg, section) []:                                                        【证书持有者所属部门,可以不填】
Common Name (eg, your name or your server's hostname) []:**.**.**.**                    【域名,这里不跟上面的ca的0.0.0.0相同即可】
Email Address []:                                                                        【邮件,可以不填】
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:                                                                                  【自定义密码,可以不填】                      
An optional company name []:

最后输入以下命令(以下命令会生成server.crt)

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 360

生成client证书(以下命令会生成client.key)

openssl genrsa -out client.key 2048

输入密码 123456, 确认密码 123456

openssl req -new -out client.csr -key client.key

输入刚才设置的密码 123456,然后按照一步步的步骤设置即可(注意common name不能与上面的ca的common name一样,这里设置为服务的ip即可)

Country Name (2 letter code) [XX]:CN									【国家代码,两个字母】
State or Province Name (full name) []:guangdong						       【省,可省略不填】
Locality Name (eg, city) [Default City]:guangdong                                                  【市,可省略不填】
Organization Name (eg, company) [Default Company Ltd]:zmkm               【证书持有者所属组织或公司】
Organizational Unit Name (eg, section) []:                                                        【证书持有者所属部门,可以不填】
Common Name (eg, your name or your server's hostname) []:**.**.**.**                    【域名,这里不跟上面的ca的0.0.0.0相同即可】
Email Address []:                                                                        【邮件,可以不填】
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:                                                                                  【自定义密码,可以不填】                      
An optional company name []:

最后输入以下命令(以下命令会生成client.crt)

openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 360

完成以上后,可以使用以下命令验证以下,若输出server.crt ok即可

openssl verify -CAfile ca.crt server.crt

最后,在/etc/mosquitto/mosquitto.conf的最底部添加以下即可

port 1883
cafile /etc/mosquitto/ca/ca.crt
certfile /etc/mosquitto/ca/server.crt
keyfile /etc/mosquitto/ca/server.key
#开启双向认证
require_certificate true
listener 9001
protocol websockets
autosave_on_changes 1
autosave_interval 1

启动mosquitto服务

mosquitto -c /etc/mosquitto/mosquitto.conf -d

若启动没有任何效果,netstat查看也没有正常的端口启动,且使用 mosquitto -v命令查看时,显示invalid的user mosquitto等信息,则是用户与用户组的问题,解决方案有2个

方案1,可以修改mosquitto.conf中的user mosquitto前的#去掉,更改为以下(已验证OK)

user root

方案2,或者也可以新建一个mosquitto的用户或组什么的(已验证OK)

groupadd mosquitto
useradd -g mosquitto mosquitto

可以使用命令测试一下是否OK

订阅

mosquitto_sub -h 127.0.0.1 -p 1883 -t "aaa" -u lzl -P a123 --cafile /etc/mosquitto/ca/ca.crt --cert /etc/mosquitto/ca/client.crt --key /etc/mosquitto/ca/client.key --insecure

发布

mosquitto_pub -h 127.0.0.1 -p 1883 -t "aaa" -m 'nihaoa' -u lzl -P a123 --cafile /etc/mosquitto/ca/ca.crt --cert /etc/mosquitto/ca/client.crt --key /etc/mosquitto/ca/client.key --insecure


第四步,若需要分布式集群部署,可直接使用mosquitto的桥接配置

集群目的 无论在那台服务器中订阅了信息,无论在那台服务器上发布信息,订阅者都可以收到发布的信息。集群部署有一个 专有名词叫做“桥接”,实现桥接的方式需要修改config.mk与mosquitto.conf文件。值得说明的是如果有10台服务器做 mosquitto集群,每台服务器上将桥连接打开,然后只需要更改一台服务器上的mosquitto.conf文件即可,其他服务器 的mosquitto.conf文件不需要做任何改动。大大方便了集群的维护。如果有新的服务器加入或删除只需要修改主服务器 的mosquitto.conf即可

例如

192.168.20.38 主节点

192.168.20.52 从节点

192.168.20.111 从节点

确保三台服务器编译的mosquitto时已经打开了桥接的设置

vim config.mk
WITH_BRIDGE:=yes

分别在三台服务器上创建桥接专用的用户和密码

mosquitto_passwd -b /etc/mosquitto/pwfile hthl_bridge 123456

在三个节点的aclfile文件中控制桥接用户的权限(可忽略)

# This is a bridge user.
user hthl_bridge
topic #

重启三台服务器的mosquitto服务

配置主节点mosquitto.conf的桥连接属性,注意:其他从节点千万不要增加此信息否则消息会死循环

# bridge
# =================================================================
# 桥接名称
connection broker52
# 节点地址
address 192.168.20.52:1883
# 消息主题
topic # both 2 "" ""
# 桥连接用户名
remote_username hthl_bridge
# 桥连接密码
remote_password 123456
# 桥接名称
connection broker111
# 节点地址
address 192.168.20.111:1883
# 消息主题
topic # both 2 "" ""
# 桥连接用户名
remote_username hthl_bridge
# 桥连接密码
remote_password 123456
# 桥连接协议版本MQTT3.11
bridge_protocol_version mqttv311
# 是否发布桥接的状态信息
notifications true
# 桥接断开时,是否清除远程服务器中的消息
cleansession true
# 桥连接是否可用的检测开关
try_private true
# 桥接模式
start_type automatic

测试在主节点发布从节点订阅没问题,至此mosquitto分布式集群部署完成

第五步,使用wss连接mosquito的websocket端口

以上第三部使用的自建ssl证书方式可以连接mqtt协议,但使用wss的连接时死活连不上,原因是wss中浏览器不识别ssl证书,需要到阿里云生成域名对应的证书

例如,当前mosquito的ssl websocket的端口为9002,ip为1.2.3.4,绑定的域名为websocket.baidu.com,则需要登录阿里云,生成免费的websocket.baidu.com的证书并下载nginx版的key和pem,把key和pem上传到mosquito服务器的某个文件夹中,并更改mosquito.conf的websocket 9002端口如下(注意:9002端口下的cafile设置可不写,且9002端口下的require_certificate true必须注释掉否则wss还是连不上)

listener 9002
protocol websockets
certfile /xxx/xxx/xxx/xxx/xxxwebsocket.baidu.com.pem
keyfile /xxx/xxx/xxx/xxx/xxxwebsocket.baidu.com.key

重启mosquito后,使用mqtt.js客户端连接即可,具体代码可参考

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="shortcut icon" href="./image/favicon.png" />
    <link rel="bookmark" href="./image/favicon.png" type="image/x-icon" />
    <title>wss mqtt连接</title>
  </head>
  <body>
    <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
  </body>
  <script>
    console.log(mqtt);
    const clientId = 'mqttjs_' + Math.random().toString(16).substr(2, 8)
    console.log(clientId);
    const host = 'wss://websocket.baidu.com:9002'

    const options = {
        keepalive: 60,
        clientId: clientId,
        username: 'lzl',
        password: 'a123',
        protocolId: 'MQTT',
        protocolVersion: 5,
        clean: true,
        reconnectPeriod: 0,
        connectTimeout: 30 * 1000,
        will: {
            topic: 'WillMsg',
            payload: 'Connection Closed abnormally..!',
            qos: 0,
            retain: false
        },
    }

    const client = mqtt.connect(host, options)
    console.log(client)
    client.subscribe('testtopic/2')
    client.subscribe('WillMsg')

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

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

    client.on('message', (topic, message, packet) => {
    console.log('Received Message: ' + message.toString() + '\nOn topic: ' + topic)
    })
     
  </script>
</html>

使用以下命令可以观察mosquito服务器的启动句柄多少(用于压力测试时观察数据)

ls /proc/20436/fd -l | grep socket: | wc -l

注意事项:若需要使用wss的websocket方式链接到mqtt.xxx.com,则需要

1、把mqtt.xxx.com的域名解析指向服务器ip

2、生成mqtt.xxx.com的ssl证书,并设置到/etc/mosquitto/mosquitto.conf.example文件中的certfile和keyfile参数中

3、若mqtt.xxx.com的ssl证书失效,则会影响wss的websocket的链接,需要更新证书