1.中间件
中间件(Middleware)是处于操作系统和应用程序之间的软件,也有人认为它应该属于操作系统中的一部分。人们在使用中间件时,往往是一组中间件集成在一起,构成一个平台(包括开发平台和运行平台),但在这组中间件中必须要有一个通信中间件,即中间件+平台+通信,这个定义也限定了只有用于分布式系统中才能称为中间件,同时还可以把它与支撑软件和使用软件区分开来
上面copy的 挺难定义的也挺难理解的。。。
相对与我们接触到的中间简单点来说:
在我们的应用程序和应用程序之间,应用程序和硬件之间,支持标准协议和接口用于分布式系统中的称为中间件。
也很难给中间件一个严格的定义,但中间件应具有如下的一些特点:
(1)满足大量应用的需要
(2)运行于多种硬件和 OS平台
(3)支持分布计算,提供跨网络、硬件和 OS平台的透明性的应用或服务的交互
(4)支持标准的协议
(5)支持标准的接口
由于标准接口对于可移植
2.AMQP协议
AMQP:(全称:Advanced Message Queuing Protocol)是高级消息队列协议。由摩根大通集团联合其他公司共同设计。是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现由 RabbitMQ等
工作过程
发布者(Publisher)发布消息(Message),经由交换机(Exchange)。
交换机根据路由规则将收到的消息分发给与该交换机绑定的队列(Queue)。
最后 AMQP 代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取。
RabbitMQ的主要角色
Broker:接受消费者和生产者的连接,实现AMQP实体服务。
Connection:连接,实现消费者和生产者与Broker之间的TCP/IP连接。
Channel:通道,Channel使进行消息读写的通道一个Connection可以包含多个Channel。之所以需要Channel,是因为TCP连接的建立和释放都是十分昂贵的,如果一个客户端每一个线程都需要与Broker交互,如果每一个线程都建立一个TCP连接,暂且不考虑TCP连接是否浪费,就算操作系统也无法承受每秒建立如此多的TCP连接。RabbitMQ建议客户端线程之间不要共用Channel,至少要保证共用Channel的线程发送消息必须是串行的,但是建议尽量共用Connection。
Exchange:交换机,接受消息,不具备消息存储的功能只负责,根据路由键分发消息,(交换机是一定存在的,在direct模式中用到的就是默认交换机 Exchange: (AMQP default))
Virtual Host : 虚拟地址,用于消息的逻辑隔离,相当于迷你的RabbitMq。
Bindings: 绑定Exchange和queue自己哦考吗的虚拟连接规则。
Routing Key: Exchange和queue之间匹配的路由规则
3. 搭建环境
Linux :RabbitMQ环境
> yum update
> yum install -y yum-utils device-mapper-persistent-data lvm2 #软件包
> yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo #设置yum源
> yum install docker-ce -y #安装docker
> docker -v #检测版本
> sudo mkdir -p /etc/docker #加速镜像
> sudo tee /etc/docker/daemon.json <<'EOF'
{
"registry-mirrors": ["https://0wrdwnn6.mirror.aliyuncs.com"]
}
EOF
>sudo systemctl daemon-reload
>sudo systemctl restart docker #重启
>systemctl enable docker #开机自启
>创建带有web界面的RabbitMQ 创建用户名密码为:admin admin的容器
>docker run -di --name myrabitt -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management
http://IP:15672 即可登陆WEB界面
4.RabbitMQ的设计模式
模式:简单模式
特点:一个生产者一个消费者
模式:work模式
特点:一个生产者多个消费者,生产则发送一条消息,只能被一个消费者拿到。
轮询模式(默认): 平均分配,每个消费者分配到的消息一致
公平分发(必须使用手动应答): 能者多劳 ,根据消费者的处理速度决定,消息的分配
公平分发:
//qos 设置 每次从队列中取出多少条数据 根据自己的 内存和disk space 决定
channel.basicQos(1);
Channel finalChannel = channel;
//@param 2 false 开启手动改应答
channel.basicConsume(queue, false, new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
System.out.println("收到消息:" + new String(delivery.getBody(), "UTF-8"));
finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}, new CancelCallback() {
@Override
public void handle(String s) throws IOException {
System.out.println("消息接收失败");
}
});
类型:订阅模式
特点:和work模式类似,一个生产者多个消费者,但是中间多了个交换机(exchange),一条消息可以被多个消费者获取。
类型:路由模式
特点:一条消息可以被多个消费者获取。但是他在传递消息的时候多设置了一个key,消费者拿消息的时候也设置一个或多个key,key匹配才能拿消息
类型:主题模式
特点:带有模糊匹配的路由模式
4. 纯Java案例操交换机和队列的关系的路由模式
生产者
package com.hdh.rabbitmq.all;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @description:手动创建生产者
* @author:hdh
* @date:2021-07-19 16:22
**/
public class Producer {
public static void main(String[] args) {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.204.128");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接connection
connection = connectionFactory.newConnection("生产者");
//3.获取通道channel
channel = connection.createChannel();
//4.通过创建交换机,声明队列,绑定关系,路由key,发送消息和接收消息
String exchangeName = "direct_message_exchange";
//交换机类型 direct/fanout/topic/headrs
String exchangeType="direct";
String routeKey = "sms";
String message = "hello message!!!";
//传递的信息必定会 先经过交换机,然后投递给队列
//声明交换机 @param3 是否持久化 交换机会不会随着夫妻的重启而丢失 ,true不丢失
channel.exchangeDeclare(exchangeName,exchangeType,true);
//声明队列
channel.queueDeclare("queue4",true,false,false,null);
channel.queueDeclare("queue5",true,false,false,null);
channel.queueDeclare("queue6",true,false,false,null);
//绑定队列 和交换机的关系
channel.queueBind("queue4",exchangeName,"order");
channel.queueBind("queue5",exchangeName,"order");
channel.queueBind("queue6",exchangeName,"course");
channel.basicPublish(exchangeName, "order", null, message.getBytes());
System.out.println("消息推送成功");
} catch (Exception e) {
System.out.println("消息推送失败");
e.printStackTrace();
} finally {
//5关闭连接 通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
消费者
package com.hdh.rabbitmq.all;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @description:手动创建消费者
* @author:hdh
* @date:2021-07-19 16:22
**/
public class Consumer {
private static Runnable runnable = new Runnable() {
@Override
public void run() {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.204.128");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
String queue = Thread.currentThread().getName();
try {
//2.创建连接connection
connection = connectionFactory.newConnection("生产者");
//3.获取通道channel
channel = connection.createChannel();
//4.通过创建交换机,声明队列,绑定关系,路由key,发送消息和接收消息
channel.basicConsume(queue, true, new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
System.out.println("收到消息:" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
@Override
public void handle(String s) throws IOException {
System.out.println("消息接收失败");
}
});
System.in.read();
} catch (
Exception e) {
e.printStackTrace();
} finally {
//5关闭连接 通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
};
public static void main(String[] args) {
new Thread(runnable, "queue4").start();
new Thread(runnable, "queue5").start();
new Thread(runnable, "queue6").start();
}
}