0%

rabbitMQ学习

身体和灵魂,总得有一个在路上。

中间件概述

中间件可以屏蔽对于底层比如操作系统、硬盘等的过多关注,另外也可以作为服务之间的互通的一个桥梁,在一定程度上打破服务之间实现方式上的标准性,如A服务可以使用java编写,B服务可以使用Python语言编写,然后通过中间件来实现两服务之间的互通。

消息中间件
消息中间件
利用可靠的消息传递机制进行系统与系统之间的通讯;
通过提供消息和消息的排队机制,它可以在分布式系统环境下扩展进程间的通讯。

  • 本质及设计
    • 是一种接收数据、接收请求、存储数据、发送数据的服务
    • MQ队列:负责数据的接收、存储传递,性能要高于其他服务
    • 协议选择:在TCP/IP协议之上构建自己的协议amqp

rabbitmq官网:https://www.rabbitmq.com/

安装过程

使用

依赖

1
2
3
4
5
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp‐client</artifactId>
<version>4.0.3</version><!‐‐此版本与spring boot 1.5.9版本匹配‐‐>
</dependency>

简单模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 生产者
public class Producer01 {
//队列名称
private static final String QUEUE = "helloworld";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = null;
Channel channel = null;
try
{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
//创建与RabbitMQ服务的TCP连接
connection = factory.newConnection();
//创建与Exchange的通道,每个连接可以创建多个通道,每个通道代表一个会话任务
channel = connection.createChannel();
/**
* 声明队列,如果Rabbit中没有此队列将自动创建
* param1:队列名称
* param2:是否持久化
* param3:队列是否独占此连接
* param4:队列不再使用时是否自动删除此队列
* param5:队列参数
*/
channel.queueDeclare(QUEUE, true, false, false, null);
String message = "helloworld小明"+System.currentTimeMillis();
/**
* 消息发布方法
* param1:Exchange的名称,如果没有指定,则使用Default Exchange
* param2:routingKey,消息的路由Key,是用于Exchange(交换机)将消息转发到指定的消息队列
* param3:消息包含的属性
* param4:消息体
*/
/**
* 这里没有指定交换机,消息将发送给默认交换机,每个队列也会绑定那个默认的交换机,但是不能显示绑定或解除绑定
* 默认的交换机,routingKey等于队列名称
*/
channel.basicPublish("", QUEUE, null, message.getBytes());
System.out.println("Send Message is:'" + message + "'");
}
catch(Exception ex)
{
ex.printStackTrace();
}
finally
{
if(channel != null)
{
channel.close();
}
if(connection != null)
{
connection.close();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 消费者
public class Consumer01 {
private static final String QUEUE = "helloworld";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
//设置MabbitMQ所在服务器的ip和端口
factory.setHost("127.0.0.1");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE, true, false, false, null);
//定义消费方法
DefaultConsumer consumer = new DefaultConsumer(channel) {
/**
* 消费者接收消息调用此方法
* @param consumerTag 消费者的标签,在channel.basicConsume()去指定
* @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
* @param properties
* @param body
* @throws IOException
* */
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body)
throws IOException {
//交换机
String exchange = envelope.getExchange();
//路由key
String routingKey = envelope.getRoutingKey();
//消息id
long deliveryTag = envelope.getDeliveryTag();
//消息内容
String msg = new String(body,"utf‐8");
System.out.println("receive message.." + msg);
}
};
/**
* 监听队列String queue, boolean autoAck,Consumer callback
* 参数明细
* 1、队列名称
* 2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置为false则需要手动回复
* 3、消费消息的方法,消费者接收到消息后调用此方法
*/
channel.basicConsume(QUEUE, true, consumer);
}
}
  • 流程

    1、发送端操作流程
    1)创建连接
    2)创建通道
    3)声明队列
    4)发送消息
    2、接收端
    1)创建连接
    2)创建通道
    3)声明队列
    4)监听队列
    5)接收消息

  • 相关概念
    相关概念
  • 运行流程
    rabbitmq运行流程

    Work quen模式

    workquene
  • work queues与简单模式相比,多了n个消费端,n个消费端共同消费同一个队列中的消息。
  • 应用场景:对于 任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
  • 生产者生产多个消息,一条消息只能被一个消费者消费,消费者在处理完某条消息之后,才会收到下一条消息。

    发布订阅模式 Fanout

    发布与订阅模式
  • 案例:
    用户通知,当用户充值成功或转账完成系统通知用户,通知方式有短信、邮件多种方法 。
  • 代码实现
    • 生产者
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      package com.xuecheng.test.rabbitmq;
      import com.rabbitmq.client.*;
      import java.io.IOException;
      import java.util.concurrent.TimeoutException;
      public class Producer02_publish {
      //队列名称
      private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
      private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
      private static final String EXCHANGE_FANOUT_INFORM="exchange_fanout_inform";
      public static void main(String[] args) {
      Connection connection = null;
      Channel channel = null;
      try {
      //创建一个与MQ的连接
      ConnectionFactory factory = new ConnectionFactory();
      factory.setHost("127.0.0.1");
      factory.setPort(5672);
      factory.setUsername("guest");
      factory.setPassword("guest");
      factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
      //创建一个连接
      connection = factory.newConnection();
      //创建与交换机的通道,每个通道代表一个会话
      channel = connection.createChannel();
      //声明交换机 String exchange, BuiltinExchangeType type
      /**
      * 参数明细
      * 1、交换机名称
      * 2、交换机类型,fanout、topic、direct、headers
      */
      channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
      //声明队列
      // (String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String,Object> arguments)
      /**
      * 参数明细:
      * 1、队列名称
      * 2、是否持久化
      * 3、是否独占此队列
      * 4、队列不用是否自动删除
      * 5、参数
      */
      channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
      channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
      //交换机和队列绑定String queue, String exchange, String routingKey
      /**
      * 参数明细
      * 1、队列名称
      * 2、交换机名称
      * 3、路由key
      */
      channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
      channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");
      //发送消息
      for (int i=0;i<10;i++){
      String message = "inform to user"+i;
      //向交换机发送消息 String exchange, String routingKey, BasicProperties props,byte[] body
      /**
      * 参数明细
      * 1、交换机名称,不指令使用默认交换机名称 Default Exchange
      * 2、routingKey(路由key),根据key名称将消息转发到具体的队列,这里填写队列名称表示消息将发到此队列
      * 3、消息属性
      * 4、消息内容
      */
      channel.basicPublish(EXCHANGE_FANOUT_INFORM, "", null, message.getBytes());
      System.out.println("Send Message is:'" + message + "'");
      }
      } catch (IOException e) {
      e.printStackTrace();
      } catch (TimeoutException e) {
      e.printStackTrace();
      }finally{
      if(channel!=null){
      try {
      channel.close();
      } catch (IOException e) {
      e.printStackTrace();
      } catch (TimeoutException e) {
      e.printStackTrace();
      }
      }
      if(connection!=null){
      try {
      connection.close();
      } catch (IOException e) {
      e.printStackTrace();
      }
      }
      }
      }
      }
  • 邮件消费者
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    package com.xuecheng.test.rabbitmq;
    import com.rabbitmq.client.*;
    import java.io.IOException;
    import java.util.concurrent.TimeoutException;
    /**
    * @author Administrator
    * @version 1.0
    * @create 2018‐06‐14 10:32
    **/
    public class Consumer02_subscribe_email {
    //队列名称
    private static final String QUEUE_INFORM_EMAIL = "inform_queue_email";
    private static final String EXCHANGE_FANOUT_INFORM="inform_exchange_fanout";
    public static void main(String[] args) throws IOException, TimeoutException {
    //创建一个与MQ的连接
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("127.0.0.1");
    factory.setPort(5672);
    factory.setUsername("guest");
    factory.setPassword("guest");
    factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
    //创建一个连接
    Connection connection = factory.newConnection();
    //创建与交换机的通道,每个通道代表一个会话
    Channel channel = connection.createChannel();
    //声明交换机 String exchange, BuiltinExchangeType type
    /**
    * 参数明细
    * 1、交换机名称
    * 2、交换机类型,fanout、topic、direct、headers
    */
    channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
    //声明队列
    // channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean
    autoDelete, Map<String, Object> arguments)
    /**
    * 参数明细:
    * 1、队列名称
    * 2、是否持久化
    * 3、是否独占此队列
    * 4、队列不用是否自动删除
    * 5、参数
    */
    channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
    //交换机和队列绑定String queue, String exchange, String routingKey
    /**
    * 参数明细
    * 1、队列名称
    * 2、交换机名称
    * 3、路由key
    */
    channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
    //定义消费方法
    DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope,
    AMQP.BasicProperties properties, byte[] body) throws IOException {
    long deliveryTag = envelope.getDeliveryTag();
    String exchange = envelope.getExchange();
    //消息内容
    String message = new String(body, "utf‐8");
    System.out.println(message);
    }
    };
    /**
    * 监听队列String queue, boolean autoAck,Consumer callback
    * 参数明细
    * 1、队列名称
    * 2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置为false则需要手动回复
    * 3、消费消息的方法,消费者接收到消息后调用此方法
    */
    channel.basicConsume(QUEUE_INFORM_EMAIL, true, defaultConsumer);
    }
    }
  • publish/subscribe与work queues有什么区别。
    区别:
    1)work queues不用定义交换机,而publish/subscribe需要定义交换机。
    2)publish/subscribe的生产方是面向交换机发送消息,work queues的生产方是面向队列发送消息(底层使用默认交换机)。
    3)publish/subscribe需要设置队列和交换机的绑定,work queues不需要设置,实质上work queues会将队列绑定到默认的交换机 。
    相同点:
    所以两者实现的发布/订阅的效果是一样的,多个消费端监听同一个队列不会重复消费消息。
  • 实质工作用什么 publish/subscribe还是work queues。
    建议使用 publish/subscribe,发布订阅模式比工作队列模式更强大,并且发布订阅模式可以指定自己专用的交换机。

    路由模式 Routing

    路由模式
  • 路由模式:
    1、每个消费者监听自己的队列,并且设置routingkey。
    2、生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列。

    主题模式 Topic

    主题模式

todo…..