中间件




















生产者:
1 package com.learn.rabbitmq.all; 2 3 import com.rabbitmq.client.Channel; 4 import com.rabbitmq.client.Connection; 5 import com.rabbitmq.client.ConnectionFactory; 6 7 public class Producer { 8 public static void main(String[] args) { 9 // 1: 创建连接工厂 10 ConnectionFactory connectionFactory = new ConnectionFactory(); 11 //2: 设置连接属性 12 connectionFactory.setHost("127.0.0.1"); 13 connectionFactory.setPort(5672); 14 connectionFactory.setVirtualHost("/"); 15 connectionFactory.setUsername("guest"); 16 connectionFactory.setPassword("guest"); 17 Connection connection = null; 18 Channel channel = null; 19 try { 20 // 3: 从连接工厂中获取连接 21 connection = connectionFactory.newConnection("生产者"); 22 // 4: 从连接中获取通道channel 23 channel = connection.createChannel(); 24 // 5: 申明队列queue存储消息 25 /* 26 * 如果队列不存在,则会创建 27 * Rabbitmq不允许创建两个相同的队列名称,否则会报错。 28 * 29 * @params1: queue 队列的名称 30 * @params2: durable 队列是否持久化 31 * @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭 32 * @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。 33 * @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。 34 * */ 35 // channel.queueDeclare("queueTest",false,false,false,null); 36 // 6: 准备发送消息的内容 37 String msg = "hello world 222!"; 38 String exchangeName = "direct-exchange-code"; 39 // direct/topic/fanout/headers 40 String exchangeType = "direct"; 41 //声明交换机 42 channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); 43 //声明队列 44 channel.queueDeclare("queue5",true,false,false,null); 45 channel.queueDeclare("queue6",true,false,false,null); 46 channel.queueDeclare("queue7",true,false,false,null); 47 //绑定 48 channel.queueBind("queue5",exchangeName,"order"); 49 channel.queueBind("queue6",exchangeName,"order"); 50 channel.queueBind("queue7",exchangeName,"course"); 51 String routingKey = "email"; 52 // 7: 发送消息给中间件rabbitmq-server 53 // @params1: 交换机exchange 54 // @params2: 队列名称/routing 55 // @params3: 属性配置 56 // @params4: 发送消息的内容 57 channel.basicPublish(exchangeName,"course",null,msg.getBytes()); 58 System.out.println("消息发送成功!"); 59 } catch (Exception e) { 60 e.printStackTrace(); 61 System.out.println("发送消息出现异常!"); 62 }finally { 63 if (channel != null && channel.isOpen()){ 64 try { 65 channel.close(); 66 } catch (Exception e) { 67 e.printStackTrace(); 68 } 69 } 70 if (connection != null){ 71 try { 72 connection.close(); 73 }catch (Exception e){ 74 e.printStackTrace(); 75 } 76 } 77 } 78 } 79 }
消费者:
1 package com.learn.rabbitmq.all; 2 3 import com.rabbitmq.client.*; 4 5 import java.io.IOException; 6 import java.nio.channels.Channels; 7 8 public class Customer { 9 public static Runnable runnable = () -> { 10 // 1: 创建连接工厂 11 ConnectionFactory connectionFactory = new ConnectionFactory(); 12 //2: 设置连接属性 13 connectionFactory.setHost("127.0.0.1"); 14 connectionFactory.setPort(5672); 15 connectionFactory.setVirtualHost("/"); 16 connectionFactory.setUsername("guest"); 17 connectionFactory.setPassword("guest"); 18 Connection connection = null; 19 Channel channel = null; 20 21 final String queueName = Thread.currentThread().getName(); 22 try { 23 // 3: 从连接工厂中获取连接 24 connection = connectionFactory.newConnection("消费者"); 25 // 4: 从连接中获取通道channel 26 channel = connection.createChannel(); 27 // 5: 创建交换机,声明队列,绑定关系,路由key,发视信息,接收信息 28 Channel finalChannel = channel; 29 finalChannel.basicConsume(queueName, true, new DeliverCallback() { 30 public void handle(String consumerTag, Delivery delivery) throws IOException { 31 System.out.println(queueName + "收到的信息是:" + new String(delivery.getBody(), "UTF-8")); 32 } 33 }, new CancelCallback() { 34 public void handle(String s) throws IOException { 35 System.out.println("接收失败了!"); 36 } 37 }); 38 System.out.println(queueName+"开始接收信息!"); 39 //阻断作用,不往下继续执行 40 System.in.read(); 41 } catch (Exception e) { 42 e.printStackTrace(); 43 System.out.println("发送消息出现异常!"); 44 }finally { 45 if (channel != null && channel.isOpen()){ 46 try { 47 channel.close(); 48 } catch (Exception e) { 49 e.printStackTrace(); 50 } 51 } 52 if (connection != null){ 53 try { 54 connection.close(); 55 }catch (Exception e){ 56 e.printStackTrace(); 57 } 58 } 59 } 60 }; 61 62 public static void main(String[] args) { 63 new Thread(runnable,"queue5").start(); 64 new Thread(runnable,"queue6").start(); 65 new Thread(runnable,"queue7").start(); 66 } 67 }
面试题
为什么rabbitmq是基于通道处理而不是连接呢?
因为连接需要结果三次握手,四次挥手,费时间,关闭,连接性能消耗大。所以通过长连接---------有很多个信道 channel 处理 所以性能高


可靠消费机制

手动应答,代码和NACK确定,可靠消费,ACK机制 AutoAck 为true 正常应答,服务端就会将消息队列的信息移除,整个过程正常,为false,不应答,就会不停重试,直到消费为止,如果出现异常,就会可能无限重试,服务器资源消耗殆尽。消息队列被挂起,就会屏蔽生产者的消息接收。








消息队列mq应用场景
01、解耦、削峰、异步
01-1、同步异步的问题(串行)
串行方式:将订单信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端

代码
public void makeOrder(){ // 1 :保存订单 orderService.saveOrder(); // 2: 发送短信服务 messageService.sendSMS("order");//1-2 s // 3: 发送email服务 emailService.sendEmail("order");//1-2 s // 4: 发送APP服务 appService.sendApp("order"); }
01-2、并行方式 异步线程池
并行方式:将订单信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间

代码
1 public void makeOrder(){ 2 // 1 :保存订单 3 orderService.saveOrder(); 4 // 相关发送 5 relationMessage(); 6 } 7 public void relationMessage(){ 8 // 异步 9 theadpool.submit(new Callable<Object>{ 10 public Object call(){ 11 // 2: 发送短信服务 12 messageService.sendSMS("order"); 13 } 14 }) 15 // 异步 16 theadpool.submit(new Callable<Object>{ 17 public Object call(){ 18 // 3: 发送email服务 19 emailService.sendEmail("order"); 20 } 21 }) 22 // 异步 23 theadpool.submit(new Callable<Object>{ 24 public Object call(){ 25 // 4: 发送短信服务 26 appService.sendApp("order"); 27 } 28 }) 29 // 异步 30 theadpool.submit(new Callable<Object>{ 31 public Object call(){ 32 // 4: 发送短信服务 33 appService.sendApp("order"); 34 } 35 }) 36 }
存在问题:
1:耦合度高
2:需要自己写线程池自己维护成本太高
3:出现了消息可能会丢失,需要你自己做消息补偿
4:如何保证消息的可靠性你自己写
5:如果服务器承载不了,你需要自己去写高可用
01-2、异步消息队列的方式

好处
1:完全解耦,用MQ建立桥接
2:有独立的线程池和运行模型
3:出现了消息可能会丢失,MQ有持久化功能
4:如何保证消息的可靠性,死信队列和消息转移的等
5:如果服务器承载不了,你需要自己去写高可用,HA镜像模型高可用。
按照以上约定,用户的响应时间相当于是订单信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍
代码
1 public void makeOrder(){ 2 // 1 :保存订单 3 orderService.saveOrder(); 4 rabbitTemplate.convertSend("ex","2","消息内容"); 5 }
02、高内聚,低耦合

03、流量的削峰
04、分布式事务的可靠消费和可靠生产
05、索引、缓存、静态化处理的数据同步
06、流量监控
07、日志监控(ELK)
08、下单、订单分发、抢票
浙公网安备 33010602011771号