RabbitMQ是一款开源的消息队列代理服务软件,它使用功能较为丰富的AMQP协议,由Erlang语言编写,大多数发行版上均可以使用包管理器迅速地安装使用。

问题是...

RabbitMQ启动后,我们都熟悉其默认使用的端口是5672,然而不知各位用户是否有注意过它监听所对应的地址呢?

$ lsof -Pni:5672
COMMAND   PID     USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
beam    15492 rabbitmq   14u  IPv6 18474018      0t0  TCP *:5672 (LISTEN)

$ lsof -Pni TCP  | grep 15492
beam      15492 rabbitmq    6u  IPv4 18470592      0t0  TCP *:39313 (LISTEN)
beam      15492 rabbitmq    7u  IPv4 18470594      0t0  TCP 127.0.0.1:34512->127.0.0.1:4369 (ESTABLISHED)
beam      15492 rabbitmq   14u  IPv6 18474018      0t0  TCP *:5672 (LISTEN)

$ netstat -laptno | grep beam
tcp        0      0 0.0.0.0:39313           0.0.0.0:*               LISTEN      15492/beam           off (0.00/0/0)
tcp        0      0 127.0.0.1:34512         127.0.0.1:4369          ESTABLISHED 15492/beam           off (0.00/0/0)
tcp6       0      0 :::5672                 :::*                    LISTEN      15492/beam           off (0.00/0/0)

可以看到它监听的地址是*,也就是IPv4与IPv6的任何地址(网卡设备)。在内部网络、本地测试环境等安全因素有保障的情景下本身是没有什么问题的。然而如果在阿里云这样还没有类似AWS提供默认全局网络端口安全策略的云服务器环境中使用时,我们需要有意识的去强化安全措施,其中很重要的一点就是将监听的网络地址(或者说网卡设备)设置到内网地址,以减少(位置安全性的)端口暴露在公网环境的风险。

编写配置文件

RabbitMQ的主配置文件的内容其实是一个Erlang的变量,所以在编写前,推荐先了解一些Erlang的基础语法,尤其是变量类型

修改主服务与管理插件监听地址

通过官方文档中对配置文件的描述,我们可以寻找到默认服务端口以及管理插件服务端口的设置,总结如下:

[
 {rabbit, [{tcp_listeners, [{"127.0.0.1", 5672},
                            {"", 5672}]}]},
 {rabbitmq_management, [{listener, [{port, 15672}, {ip, ""}]}]}
].

集群(Clustering)监听地址的配置

如果和笔者一样使用预编译好的RabbitMQ二进制包自己解压运行的话,还会发现默认集群功能是打开的,用来提高服务的可用性。而这个功能监听的端口默认为RABBITMQ_NODE_PORT + 20000,所以一般默认端口是25672,而地址毫无悬念是*

修改这个则需要修改RabbitMQ的kernel部分配置,同样位于rabbitmq.config中:

{kernel, [{inet_dist_use_interface,{127,0,0,1}}]}

特别注意地址的格式{127,0,0,1}。(参考Erlang的inet(3)部分

EPMD

我们可以看到RabbitMQ的进程还发起了一个连接到本机的4369端口,监听这个端口的是epmd,它的作用是在Erlang著名的分布式计算中起到定位(name server)的作用。Erlang进程都根据这个服务来与其他(例如跨主机)Erlang进程通信。并且我们会发现如果关闭这个进程RabbitMQ无法正常工作

默认情况下,启动需要epmd的Erlang程序时会自动检查是否有这个进程,如果没有会自动启动。但是我们会发现由于这个机制,在Ubuntu发行版中,并没有给epmd单独有服务文件,导致必须重启其他Erlang进程才能补充epmd。这个现象在Debian中是否有,没有验证。

顺带一提,Gentoo打包的RabbitMQ服务文件可是明确申明依赖epmd的哦。

$ head -10 /etc/init.d/rabbitmq
#!/sbin/runscript
# Copyright 1999-2013 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id$

depend() {
        need net
        use dns epmd
}

epmd自带帮助,我们发现使用-address参数,可以指定“使用逗号分隔的地址”来监听。

$ epmd -address ,127.0.0.1

但是问题还没有结束,在Ubuntu Trusty(14.04)下会出现这样的提示:

epmd: Wed Dec 23 20:50:21 2015: cannot parse IP address "127.0.0.1"

主要原因是因为Ubuntu开发群组发现了一个bug,在修复这个bug时引起了这个现象最为蛋疼的是后者虽然确认了,但经过1年以上仍未修复,导致笔者撰文时Ubuntu中使用的16.0b3版本仍旧有问题

要解决只能自己打包了,索性不是很复杂,最重要的配置脚本为(根据实际需要再做调整):

export JAVAC=false
    dh_auto_configure -- \
            --prefix="${prefix}" \
            --enable-threads \
            --with-ssl \
            --enable-dynamic-ssl-lib \
            --disable-sctp \
            --disable-systemd \
            --disable-halfword-emulator \
            --disable-hipe \
            --disable-kernel-poll \
            --disable-smp-support

这个配置是从Gentoo的ebuild中参考来的。

修改RabbitMQ启动时连接EPMD地址

设置环境变量

$ export ERL_EPMD_ADDRESS="..."
$ export ERL_EPMD_PORT="..."

设置节点名称

RabbitMQ的集群互联(Clustering/Federation)使用的是节点名称互联。如果没有设置,默认使用的是username@hostname;笔者遇到的情景是恰巧hostname域名解析为外网。如果依照前一步将EPMD设置到内网后会提示无法连接。所以相应地需要把节点名字改成内网地址。设置环境变量

$ export RABBITMQ_NODE_NAME="username@internal_hostname"

允许远程主机使用guest用户

从版本3.3.0开始guest用户将默认不再允许从远程主机授权时使用;我们的首要原则还是尽量根据需求为不同的应用来设置不用的用户,以便增加管理的弹性。对于非常简单的情景(例如测试等)我们仍会需要和原来一样使用guest用户,可以通过在配置文件中添加以下内容

[
 {rabbit, [{loopback_users, []}]}
].

最后

RabbitMQ虽然在大多数发行版上使用包管理器就可以迅速地安装启动使用,但配置文件并未像其他服务软件一般带有一个相对完整,带有注释文档的实例配置文件;加上官方的文档说明虽然不少但同样缺乏详细的例子,故而对于从未接触过Erlang的用户来说是不小的门槛。希望本文能为各位同样使用RabbitMQ与Erlang的巨巨抛砖引玉。

参考资料

__END__