https://www.bilibili.com/video/BV1dX4y1V73G?p=44
java原生依赖
<dependency>
<groupId>com.rabbitmqgroupId>
<artifactId>amqp-clientartifactId>
<version>5.10.0version>
dependency>
在上图的模型中,有以下概念:
生产者
//简单模式
public class Producer{
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("10.15.0.9");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection("生产者");
//2.创建通道
Channel channel = connection.createChannel();
//3.通过创建交换机,声明队列,绑定关系,路由key,发送消息和接受消息
/*参数1: 是否持久化,非持久化消息会存盘吗?会存盘,但是会随着重启服务器而丢失
参数2:是否独占队列
参数3:是否自动删除,随着最后一个消费者消息完毕消息以后是否把队列自动删除
参数4:携带附属属性
*/
String queueName = "queue1";
channel.queueDeclare(queueName,false,false,false,null);
//4.发送消息给队列queue
/*参数1: 交换机
参数2:队列、路由key
参数3:消息的状态控制
参数4:消息主题
*/
//面试题:可以存在没有交换机的队列吗?不可能,虽然没有指定交换机但是一定会存在一个默认的交换机
String message = "Hello";
channel.basicPublish("",message, null,message.getBytes());
//5.关闭
channel.close();
connection.close();
}
消费者
//简单模式
public class Consumer{
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("10.15.0.9");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection("生产者");
//2.创建通道
Channel channel = connection.createChannel();
//3.接受内容
channel.basicConsume("queue1",true,new DefaultConsumer(){
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println(new String("收到消息是" + new String(meassage.getBody()),"UTF-8"));
},new CancelCallback(){
public void handle(String consumerTag) throws IOException {
System.out.println("接受失败了");
}
});
//4.关闭
channel.close();
connection.close();
}
AMQP全称:Advanced Message Queuing Protocol(高级消息队列协议)。是应用层协议的一个开发标准,为面向消息的中间件设计
图解
发布订阅模式的具体实现
生产者
//简单模式
public class Producer{
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("10.15.0.9");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection("生产者");
//2.创建通道
Channel channel = connection.createChannel();
//3.通过创建交换机,声明队列,绑定关系,路由key,发送消息和接受消息
/*参数1: 是否持久化,非持久化消息会存盘吗?会存盘,但是会随着重启服务器而丢失
参数2:是否独占队列
参数3:是否自动删除,随着最后一个消费者消息完毕消息以后是否把队列自动删除
参数4:携带附属属性
*/
String queueName = "queue1";
channel.queueDeclare(queueName,false,false,false,null);
//4.发送消息给队列queue
/*参数1: 交换机
参数2:队列、路由key
参数3:消息的状态控制
参数4:消息主题
*/
//面试题:可以存在没有交换机的队列吗?不可能,虽然没有指定交换机但是一定会存在一个默认的交换机
String message = "Hello";
//5.准备交换机
String exchangeName = "fanout-exchange";
//6.定义路由key
String routeKey = "";
//7.指定交换机的类型
String type = "fanout";
channel.basicPublish(exchangeName,routeKey, null,message.getBytes());
//8.关闭
channel.close();
connection.close();
}
消费者
代码一样,使用线程启动测试而已!
此处没有通过代码去绑定交换机和队列,而是通过可视化界面去绑定的!
//6.定义路由key
String routeKey = "email";
//7.指定交换机的类型
String type = "direct";
channel.basicPublish(exchangeName,routeKey, null,message.getBytes());
//6.定义路由key
String routeKey = "com.order.test.xxx";
//7.指定交换机的类型
String type = "direct";
channel.basicPublish(exchangeName,routeKey, null,message.getBytes());
代码创建及绑定
//5.准备交换机
String exchangeName = "direct_message_exchange";
String exchangeType = "direct";
//如果你用界面把queue和exchange的关系先绑定话,代码就不需要在编写这些声明代码可以让代码变得更简洁
//如果用代码的方式去声明,我们要学习一下
//6.声明交换机 所谓的持久化就是指,交换机会不会随着服务器重启造成丢失
channel.exchangeDeclare(exchangeName,exchangeType,true);
//7.声明队列
channel.queueDeclare("queue5",true,false,false,null);
channel.queueDeclare("queue6",true,false,false,null);
channel.queueDeclare("queue7",true,false,false,null);
//8.绑定队列和交换机的关系
channel.queueBind("queue5",exchangeName,"order");
channel.queueBind("queue6",exchangeName,"order");
channel.queueBind("queue7",exchangeName,"course");
channel.basicPublish(exchangeName,course, null,message.getBytes());
图解
当有多个消费者时,我们的消息会被哪个消费者消费呢,我们又该如何均衡消费者消费信息的多少呢?
主要有两种模式:
生产者
跟简单模式一样!
消费者
创建两个一样的!
生产者
跟简单模式一样!
消费者
//简单模式
public class Consumer{
//3.接受内容
//指标定义出来
channel.basicQos(1);
channel.basicConsume("queue1",false,new DefaultConsumer(){
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println(new String("收到消息是" + new String(meassage.getBody()),"UTF-8"));
//改成手动应答
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
},new CancelCallback(){
public void handle(String consumerTag) throws IOException {
System.out.println("接受失败了");
}
});
//4.关闭
channel.close();
connection.close();
}
创建两个一样的!
同步异步的问题(串行)
串行方式:将订单信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端
public void makeOrder(){
//1.发送订单
//2.发送短信服务
//3.发送email服务
//4.发送app服务
}
并行方式 异步线程池
并行方式:将订单信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间
public void test(){
//异步
theadpool.submit(new Callable<Object>{
//1.发送短信服务
})
//异步
theadpool.submit(new Callable<Object>{
//2.
})
//异步
theadpool.submit(new Callable<Object>{
//3.
})
//异步
theadpool.submit(new Callable<Object>{
//4.
})
}
存在问题
异步消息队列的方式
好处:
按照以上约定,用户的响应时间相当于是订单信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20QPS。比串行提高了3倍,比并行提高了两倍
好处:
按照以上约定,用户的响应时间相当于是订单信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20QPS。比串行提高了3倍,比并行提高了两倍
[外链图片转存中…(img-kyL4Icya-1615906714948)]
[外链图片转存中…(img-bqSYfEx2-1615906714950)]