由于功耗的原因,Wifi模块的设备想用Mqtt协议与云平台进行通信,最近研究Mqtt协议。
由于之前在Rabbitmq中验证过Mqtt插件,为了简单起见,以及迎合物联网的常规做法,也积极拥抱Mqtt。
首先,测试环境是通过docker容器跑的rabbitmq服务,这样对现有服务器的影响最小。
由github上的代码自动生成rabbitmq+ssl的镜像:https://github.com/soho-smarthome/rabbitmq-with-ssl-in-docker.git,省去了自己创建秘钥的过程,利于测试。
1. git clone https://github.com/soho-smarthome/rabbitmq-with-ssl-in-docker.git
2. cd rabbitmq-with-ssl-in-docker && cd tests && ./build.sh
查看Dockerfile发现用的基础镜像是rabbitmq:3.6.6
E: The method driver /usr/lib/apt/methods/https could not be found.
需要换成rabbitmq:3.6.16,同时因为需要web页面,且启用mqtt,需要修改rabbitmq.conf文件
#vim rabbitmq.conf
[
{ rabbit, [
{ loopback_users, [ ] },
{ tcp_listeners, [5672] },
{ ssl_listeners, [5671] },
{ ssl_options, [
{cacertfile, "/home/testca/cacert.pem"},
{certfile, "/home/server/cert.pem"},
{keyfile, "/home/server/key.pem"},
{verify, verify_peer},
{fail_if_no_peer_cert, false},
{versions, ['tlsv1.2', 'tlsv1.1', 'tlsv1']}
]}
] },
{rabbitmq_mqtt, [{default_user, <<"admin">>},
{default_pass, <<"admin">>},
{allow_anonymous, false},
{vhost, <<"/">>},
{exchange, <<"amq.topic">>},
{subscription_ttl, 1800000},
{prefetch, 10},
{ssl_listeners, [8883]},
{tcp_listeners, [1883]},
{tcp_listen_options, [{backlog, 4096}, {nodelay, true}]}]},
{rabbitmq_management, [{listener, [{port, 15672}]}]}
].
#需要enable web和mqtt通过新建enable_plugins文件实现
vim enabled_plugins
[rabbitmq_management,rabbitmq_mqtt].
#新建admin用户,int vhost通过init.sh实现
vim init.sh
#!/bin/bash
(sleep 5 ; \
rabbitmqctl add_user $RABBITMQ_USER $RABBITMQ_PASSWORD ; \
rabbitmqctl set_user_tags $RABBITMQ_USER administrator ; \
rabbitmqctl add_vhost $V_HOST; \
rabbitmqctl set_permissions -p / $RABBITMQ_USER ".*" ".*" ".*" ; \
echo "*** User '$RABBITMQ_USER' with password '$RABBITMQ_PASSWORD' completed. ***") &
rabbitmq-server $@
#修改Dockerfile
ENV RABBITMQ_USER admin
ENV RABBITMQ_PASSWORD admin
ENV V_HOST int
ADD init.sh /init.sh
RUN ["chmod", "+x", "/init.sh"]
CMD /bin/bash /home/generate-client-keys.sh && /bin/bash /init.sh
#注意init.sh要在rabbitmq服务启动后再运行。
rabbitmq镜像创建成功后,通过docker命令启动
#这里将容器中的/home/client目录中的客户端证书信息映射出来,便于client端获取
#同时注意端口不要占用
mkdir -p /tmp/docker-test && rm -rf /tmp/docker-test/* && docker run -d --rm -p 5671:5671 -p 5672:5672 -p 15672:15672 -p 8883:8883 -p 1883:1883 -v /tmp/docker-test:/home/client --name myrbmqtt rabbitmq-with-ssl:latest
#ls /tmp/docker-test
java client用到了里面的:key-store.p12和trust-store.p12
这里有问题:java client需要调用如下命令导入信任库,否则报错
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Exception in thread "main" 客户机未连接 (32104)
两种解决办法:
1. 导入java/lib/security,先找到java的根目录,比如:D:\openjdk8\jre\lib\security,然后在当前目录下运行cmd:
keytool -import -file D:\mqtt\server-cert.pem -keystore cacerts -alias server
该命令支持pem格式,不支持p12格式文件。
这里的server-cert.pem为服务端的证书
然后在创建SSLSocketFactory时,可以不用传TrustManager[],即ssl.init(kmf.getKeyManagers(), null, null);
2. 直接根据client目录中的,key-store.p12和trust-store.p12分别作为客户端的证书,服务端的证书。
由于镜像打包好后,可以移植,不需要每个环境都重新创建,这里通过docker save命令保存
#将镜像打包成tar文件
docker save rabbitm-with-ssl | gzip > rabbitmq-ssl.tar.gz
#将tar文件还原为镜像
docker load -i rabbitmq-ssl.tar.gz
通过docker命令运行镜像
mkdir -p /tmp/docker-test && rm -rf /tmp/docker-test/*
&& docker run -d --rm -p 12000:5671 -p 12001:5672 -p 12002:15672 -p 12003:8883 -p 12004:1883 -v /tmp/docker-test:/home/client --name myrbmqtt rabbitmq-with-ssl:latest