为了节省时间,实战案例只贴了一些关键代码,需要的可以去下面的git仓库直接拉取。
1. 什么是MQ?
MQ全称Message Queue,是一种分布式系统中生产者和消费者之间保存消息的容器。
官网地址:Messaging that just works — RabbitMQ
工作模式代码已经发到gitee仓库,需要的自取。代码路径:https://gitee.com/GengHongBo/rabbit-mqtest.git
2.MQ的优劣势
mq的优点:1. 跟其他系统解耦,生产者中的消息放入MQ进行处理,不需要直接跟其他系统交互,避免造成整个分布式系统挂掉。2.异步处理,其他系统由原有的同步处理请求改为异步处理,响应和请求速度加快。3. 削峰填谷,在秒杀情景下,对大批量请求放入mq后不会造成系统压力。
缺点:1.宕机后造成其他系统无法请求 2.系统复杂度提高
3.常见的MQ
经常在公司中使用的rabbitMQ,rocketMQ,kafaka,性能上rabbitMQ<RocketMQ<kafka,但是rabbit在安全性上做的最好,经常使用在金融领域。
4.rabbitMq架构
rabbitmq使用AMQP协议,是一个应用层协议,支持消息通信,类比http,内部架构对象:生产者,交换机(分发消息),路由器(路由消息),队列(先进先出),消费者。
5.rabbitMQ单机安装部署
1.上传文件
erlang-18.3-1.el7.centos.x86_64.rpm,socat-1.7.3.2-1.1.el7.x86_64.rpm,rabbitmq-server-3.6.5-1.noarch.rpm
2.安装命令
rpm -ivh erlang-18.3-1.el7.centos.x86_64.rpm
yum -y install tcp_wrappers
rpm -ivh socat-1.7.3.2-1.1.el7.x86_64.rpm
rpm -ivh rabbitmq-server-3.6.5-1.noarch.rpm
# 开启管理界面
rabbitmq-plugins enable rabbitmq_management
#查看配置文件
find -name rabbit.app
# 修改默认配置信息
# 比如修改密码、配置等等,例如:loopback_users 中的 <<"guest">>,只保留guest,附属截图查看即可。
vim ./usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin/rabbit.app
service rabbitmq-server start # 启动服务
#到这一步使用http://192.168.1.7:15672/登录查看即可。用户名密码:guest/guest 后面的配置参考
service rabbitmq-server stop # 停止服务
service rabbitmq-server restart # 重启服务
# 设置配置文件
cd /usr/share/doc/rabbitmq-server-3.6.5/
cp rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
# 如果web控制台无法正常访问考虑安装是否成功以及是防火墙的原因 关闭防火墙 systemctl stop firewalld 使用腾讯云 阿里云记得开放 15672端口
5.1 配置用户和虚拟机
1.添加user
2.添加Virtual Hosts,点击创建Name并指定user,set permission
rabbitMQ比较官方的基础架构图:
6.rabbitmq工作模式特性
官方工作模式文档:RabbitMQ Tutorials — RabbitMQ
一共有6种模式:
1 Hello World!:自己做一个案例使用
2 Work queues:12306分发消息。不会重复消费
3 Publish/Subscribe:中间加入交换机概念,交换机有3种类型:1.fannot(广播),给所有队列绑定 2.Direct(定向),给指定的某种route_key的队列 3.Topic(通配符),只有符合某种规则的队列。我们可以使用这种模式用于up主与粉丝推送消息场景。
4 Routing
5 Topics
6 RPC:不怎么使用,可以用dubbo替换这种模式
6.1 hello World模式
只能消费一次,生产者,队列,消费者的单一模式,适用于单体服务之间的场景。
6.1.1 hello World实战
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import util.RabbitMQConstant;
import util.RabbitMQUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
//生产者
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//设置连接信息
Connection connection = RabbitMQUtil.getConnection();
Channel channel = connection.createChannel();
//创建队列
//helloWorld,创建一个队列名为helloWorld的队列,如果有就是用这个名字的队列
//false:是否持久化,false不持久化,一旦队列停止就丢失数据
//false:是否队列私有化,false代表所有消费者可以访问,true只能第一次消费的消费者可以访问
//false:是否自动删除,false代表连接停掉后不自动删除队列
//null:一些额外参数
channel.queueDeclare(RabbitMQConstant.HELLO_WORLD,false,false,false,null);
//发布消息
//"":交换机,helloworld模式不使用;queueName:队列名称;null:额外的设置属性;消息内容
channel.basicPublish("",RabbitMQConstant.HELLO_WORLD,null,"发布消息内容".getBytes());
System.out.println("发送成功");
//关闭连接
channel.close();
connection.close();
}
}
import com.rabbitmq.client.*;
import util.RabbitMQConstant;
import util.RabbitMQUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消费者
*/
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
//设置连接信息
Connection connection = RabbitMQUtil.getConnection();
Channel channel = connection.createChannel();
//创建队列
//helloWorld,创建一个队列名为helloWorld的队列,如果有就是用这个名字的队列
//false:是否持久化,false不持久化,一旦队列停止就丢失数据
//false:是否队列私有化,false代表所有消费者可以访问,true只能第一次消费的消费者可以访问
//false:是否自动删除,false代表连接停掉后不自动删除队列
//null:一些额外参数
channel.queueDeclare(RabbitMQConstant.HELLO_WORLD,false,false,false,null);
//消费消息
//"":交换机,helloworld模式不使用;queueName:队列名称;null:额外的设置属性;消息内容
channel.basicConsume(RabbitMQConstant.HELLO_WORLD,false,new Recever(channel));
}
}
class Recever extends DefaultConsumer{
private Channel channel;
public Recever(Channel channel) {
super(channel);
this.channel = channel;
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("收到的消息>>>:"+new String(body));
System.out.println("消息的tagID"+envelope.getDeliveryTag());
//确认消息内容
//false:确认当前签收消息,true签收所有未签收的消息
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
6.2 Work queue模式
生产者,队列,多个消费者之间的模式,适用于任务量重的场景。默认平均的将消息分发给消费者,但是也可以使用线程休眠的方式设置权重让某个消费者消费更多的消息。
6.2.2 workqueue实战
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.RabbitMQConstant;
import util.RabbitMQUtil;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;
/**
* 生产者
*/
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接
Connection connection = RabbitMQUtil.getConnection();
//创建channel
Channel channel = connection.createChannel();
channel.queueDeclare(RabbitMQConstant.WORK_QUEUE,false,false,false,null);
String message = "";
for(int i=0;i<300;i++){
message = "您的消息已发送第"+i+"次消息!";
channel.basicPublish("",RabbitMQConstant.WORK_QUEUE,null,message.getBytes());
}
System.out.println("发送成功");
channel.close();
connection.close();
}
}
import com.rabbitmq.client.*;
import util.RabbitMQConstant;
import util.RabbitMQUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消费者1
*/
public class Consumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接
Connection connection = RabbitMQUtil.getConnection();
//创建channel
Channel channel = connection.createChannel();
channel.queueDeclare(RabbitMQConstant.WORK_QUEUE,false,false,false,null);
//默认如果不设置basicQos则平均分发给所有消费者,设置以后只能消费完一个消息才会再从队列中取一个消息
channel.basicQos(1);
channel.basicConsume(RabbitMQConstant.WORK_QUEUE,false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1拿到消息,消息内容:"+new String(body));
//设置消费权重
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
}