一、安装RabbitMQ(基于centos7安装)
1. 手动下载RabbitMQ安装(方法一)
由于RabbitMQ是由Erlang语言编写的,所以在安装RabbitMQ之前需要先安装Erlang。
1.1 安装 Erlang
下载最新版本Erlang(目前最新版本为23.2),也可以点击此处下载,下载完成后开始安装。
rpm -ivh erlang-23.2.3-1.el7.x86_64.rpm
可以输入 erl
命令来验证Erlang是否安装成功,如果出现类似以下的提示即表示安装成功:
1.2 RabbitMQ的安装
1.2.1 安装RabbitMQ之前还需要安装socat,点击此处下载
1.2.2 前往官网下载最新版本的RabbitMQ(目前最新版本为3.8.11),点击此处下载
安装完成后可以通过如下命令启动RabbitMQ和查看RabbitMQ的状态
systemctl start rabbitmq-server.service
systemctl status rabbitmq-server.service
rabbitmq默认安装启动以后,是没有开启web管理界面的,通过
rabbitmq-plugins enable rabbitmq_management
开启web界面管理插件。禁用rabbitmq-management插件的命令为:rabbitmq-plugins disable rabbitmq_management
。
web 管理默认端口为15672,默认的用户名和密码为guest / guest。
如果访问后出现如下提示:
这是因为RabbitMQ从3.3.0开始禁止使用guest/guest权限通过除localhost外的访问。
可以在命令行创建其他的用户来登录web管理界面:
- 创建用户
# rabbitmqctl add_user 用户名 密码
rabbitmqctl add_user admin admin
- 设置管理员
rabbitmqctl set_user_tags admin administrator
- 设置权限
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
- 列出用户
rabbitmqctl list_users
- 修改密码
#修改admin用户密码为admin123
rabbitmqctl change_password admin admin123
如果想让guest用户可以通过除localhost外的访问,可以使用以下方法:
创建/etc/rabbitmq/rabbitmq.config文件并添加以下内容[{rabbit, [{loopback_users, []}]}].
然后重启rabbitmq服务
2.在docker中安装RabbitMQ(方法二)
2.1 查看仓库中的RabbitMQ
docker search rabbitmq
2.2 安装RabbitMQ
- 直接安装最新版本的RabbitMQ(如果需要安装其他版本,直接在rabbitmq后面加上
:版本号
即可)
docker pull rabbitmq
- 然后查看所有镜像
docker images
2.3 启动容器
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 -v `pwd`/data:/var/lib/rabbitmq --hostname myRabbit -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin c05fdf32bdad
-
说明:
-d 后台运行容器;
–name 指定容器名;
-p 指定服务运行端口(5672:应用访问端口;15672:控制台Web端口号)
-v 映射目录或文件
–hostname 主机名(RabbitMQ的一个重要注意事项是它根据所谓的 “节点名称” 存储数据,默认为主机名);
-e 指定环境变量(RABBITMQ_DEFAULT_VHOST:默认虚拟机名;RABBITMQ_DEFAULT_USER:默认的用户名;RABBITMQ_DEFAULT_PASS:默认用户名的密码)
最后的
c05fdf32bdad
为rabbitmq的镜像id
在外部访问需要注意放行对应的端口
2.4 启动rabbitmq_management
docker exec -it rabbit rabbitmq-plugins enable rabbitmq_management
然后通过浏览器访问web管理端
http://ip:15672
使用启动容器时指定的用户名和密码登录即可
3. Hello World
代码演示:首先生产者发送一条消息"hello world!"至RabbitMQ,之后由消费者消费。
3.1 生产者客户端代码
package com.dh.rabbitmq.demo;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.nio.charset.StandardCharsets;
public class RabbitProducer {
private static final String EXCHANGE_NAME = "exchange_demo";
private static final String ROUTING_KEY = "routingkey_demo";
private static final String QUEUE_NAME = "queue_demo";
private static final String IP_ADDRESS = "192.168.140.128";
private static final int PORT = 5672;
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
private static final String VHOST = "my_vhost";
private static Log log = LogFactory.getLog(RabbitProducer.class);
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(IP_ADDRESS);
factory.setPort(PORT);
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost(VHOST);
Connection connection = factory.newConnection();// 创建连接
Channel channel = connection.createChannel();// 创建信道
// 创建一个类型为direct、持久化的、非自动删除的交换器
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT, true, false, null);
// 创建一个持久化、非排他的、非自动删除的队列
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
// 将交换器与队列通过路由键绑定
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);
// 发送一条持久化的消息
String message = "Hello World!";
log.info("send message:" + message);
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes(StandardCharsets.UTF_8));
// 关闭资源
channel.close();
connection.close();
}
}
3.2 消费者客户端代码
package com.dh.rabbitmq.demo;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Address;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
public class RabbitConsumer {
private static final String QUEUE_NAME = "queue_demo";
private static final String IP_ADDRESS = "192.168.140.128";
private static final int PORT = 5672;
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
private static final String VHOST = "my_vhost";
private static Log log = LogFactory.getLog(RabbitConsumer.class);
public static void main(String[] args) throws Exception {
Address[] addresses = {new Address(IP_ADDRESS, PORT)};
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost(VHOST);
Connection connection = factory.newConnection(addresses);// 创建连接
Channel channel = connection.createChannel();// 创建信道
channel.basicQos(1);// 设置客户端最多接收未被ack的消息个数
// 定义队列的消费者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
log.info("receive message: " + new String(body, StandardCharsets.UTF_8));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
log.warn(e.getMessage(), e);
}
// 返回确认状态
/*
channel.basicQos(1);和channel.basicAck(envelope.getDeliveryTag(),false);是配套使用
只有在channel.basicQos被使用的时候channel.basicAck(envelope.getDeliveryTag(),false)才起到作用。
*/
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
// 监听队列,手动返回完成状态
channel.basicConsume(QUEUE_NAME, consumer);
// 等待回调函数执行完毕之后,关闭资源
TimeUnit.SECONDS.sleep(5);
channel.close();
connection.close();
}
}
RabbitMQ中的概念,channel.basicQos(1)指该消费者在接收到队列里的消息但没有返回确认结果之前,队列不会将新的消息分发给该消费者。队列中没有被消费的消息不会被删除,还是存在于队列中。
channel.basicQos(1);和channel.basicAck(envelope.getDeliveryTag(),false);是配套使用,只有在channel.basicQos被使用的时候channel.basicAck(envelope.getDeliveryTag(),false)才起到作用。