微服务之RabbitMQ总结(基础api)

Git地址:

rabbitMQ示例代码: ribbitMQ的入门代码

整体RabbitMQ架构指导

微服务之RabbitMQ总结(基础api)_第1张图片

RabbitMQ 使用场景

服务解耦

假设有这样一个场景, 服务A产生数据, 而服务B,C,D需要这些数据, 那么我们可以在A服务中直接调用B,C,D服务,把数据传递到下游服务即可

但是,随着我们的应用规模不断扩大,会有更多的服务需要A的数据,如果有几十甚至几百个下游服务,而且会不断变更,再加上还要考虑下游服务出错的情况,那么A服务中调用代码的维护会极为困难

这是由于服务之间耦合度过于紧密

再来考虑用RabbitMQ解耦的情况

A服务只需要向消息服务器发送消息,而不用考虑谁需要这些数据;下游服务如果需要数据,自行从消息服务器订阅消息,不再需要数据时则取消订阅即可

流量削峰

如果这种瞬时高峰的情况每天只出现一次,每次只有半小时,那么我们10台服务器在多数时间都只分担每秒几十次请求,这样就有点浪费资源了

这种情况,我们就可以使用RabbitMQ来进行流量削峰,高峰情况下,瞬间出现的大量请求数据,先发送到消息队列服务器,排队等待被处理,而我们的应用,可以慢慢的从消息队列接收请求数据进行处理,这样把数据处理时间拉长,以减轻瞬时压力

这是消息队列服务器非常典型的应用场景

微服务之RabbitMQ总结(基础api)_第2张图片

异步调用

考虑定外卖支付成功的情况

支付后要发送支付成功的通知,再寻找外卖小哥来进行配送,而寻找外卖小哥的过程非常耗时,尤其是高峰期,可能要等待几十秒甚至更长

这样就造成整条调用链路响应非常缓慢

微服务之RabbitMQ总结(基础api)_第3张图片

而如果我们引入RabbitMQ消息队列,订单数据可以发送到消息队列服务器,那么调用链路也就可以到此结束,订单系统则可以立即得到响应,整条链路的响应时间只有200毫秒左右

寻找外卖小哥的应用可以以异步的方式从消息队列接收订单消息,再执行耗时的寻找操作

微服务之RabbitMQ总结(基础api)_第4张图片

 RabbitMQ交换机种类

在RabbitMQ中,ExchangeType常用的有direct(路由模式)、Fanout(发布订阅模式)和Topic(主题模式)三种。

 RabbitMQ安装

虚拟机的配置

# VMware

**版本**

最好使用最新的 vmware 16+

**vmware网络故障,可以重置网络**

- 编辑 --- 虚拟网络编辑器 -- 左下角按钮“还原默认设置”
  - 会删除所有的虚拟网络,然后重新创建

**NAT网络**

- 统一设置成 64 网段
  - 编辑 --- 虚拟网络编辑器 --- 选择 vmnet8 --- 左下角设置网段:`192.168.64.0`

**虚拟机**

- 课前资料\虚拟机\
  - centos-8-2105
  - centos-7-1908
- 在这两个虚拟机中提供了基础配置:
  - 阿里的 yum 安装源、扩展源
  - 安装了 python、pip、ansible
  - ip设置的脚本工具:
    - ip-dhcp --- 自动分配
    - ip-static --- 设置固定ip
- 加载、启动虚拟机
  1. 解压缩 centos-8
  2. 双击 centos-8-2105.vmx
  3. 启动
  4. 按照提示选择“已复制虚拟机”
  5. 用户名和密码都是 `root`

**网卡没有ip,或网卡不存在**

```shell
https://wanght.blog.csdn.net/article/details/105082294

# centos 7 禁用 NetworkManager 系统服务
systemctl stop NetworkManager
systemctl disable NetworkManager

# centos 8 开启 VMware 托管
nmcli n on
systemctl restart NetworkManager

Docker中RabbitMQ安装

**用 Docker 运行 Rabbitmq**

1. 从 docker-base 克隆: rabbitmq

2. 设置ip

   ```shell
   ./ip-static
   ip: 192.168.64.140
   
   ifconfig
   ```

3. 按照笔记用docker运行 rabbitmq

   过程中需要联网下载 rabbitmq 的镜像

```shell
docker pull rabbitmq:management   在线下载镜像

执行  docker images 查看镜像

关闭防火墙

systemctl stop firewalld
systemctl disable firewalld
 
重启 docker 系统服务

systemctl restart docker

mkdir /etc/rabbitmq

vim /etc/rabbitmq/rabbitmq.conf

# 添加两行配置:
default_user = admin
default_pass = admin

docker run -d --name rabbit \
-p 5672:5672 \
-p 15672:15672 \
-v /etc/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
-e RABBITMQ_CONFIG_FILE=/etc/rabbitmq/rabbitmq.conf \
rabbitmq:management

访问管理控制台 http://192.168.64.140:15672
用户名密码是 admin

Rabbitmq六种工作模式

pom文件:



    4.0.0

    cn.tedu
    rabbitmq-api
    1.0-SNAPSHOT

    
        8
        8
    

    
        
            com.rabbitmq
            amqp-client
            5.4.3
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.8.1
                
                    1.8
                    1.8
                
            
        
    


 简单模式():

微服务之RabbitMQ总结(基础api)_第5张图片

 生产者核心代码:

具体步骤:

1.链接虚拟机中的RabbitMQ

1.1获取链接工厂,connectionFactory();

1.2设置:ip地址,端口号,rabbitMQ帐号,rabbitMQ密码

1.3 connectionFactory.newConnection().createChannel()

2.创建队列

2.1管道.queuedeclare(队列名,是否是持久队列,是否是排它队列,是否是自动删除,其他参数)

3.发送消息

3.1管道.basicPublish(交换机,队列名,消息其他参数(是否持久化),发送的字节码格式)

package m1;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.140"); // wht6.cn
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Connection con = f.newConnection();
        Channel c = con.createChannel();
        // 创建队列 hello-world,如果队列已经存在,不会重复创建
        /*
        参数:
            1. 队列名
            2. 是否是持久队列
            3. 是否是排他队列、独占队列
            4. 是否自动删除(没有消费者时自动删除)
            5. 其他参数属性
         */
        c.queueDeclare("hello-world",false,false,false,null);
        // 发送消息
        /*
        参数:
            1. 交换机,""是一个默认的交换机
            3. 消息的其他参数属性
         */
        c.basicPublish(
                "", "hello-world", null, "Hello world!".getBytes());
    }
}

消费者核心代码:

具体步骤:

1.链接虚拟机中的RabbitMQ

与上同理

2.创建队列

与上同理

3.创建回调对象

DeliverCallback对象,,CancelCallback对象

4.接收信息

basicConsume(队列名,是否需要自动确认无需回应,回调成功对象,失败对象)

package m1;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Comsumer {
        public static void main(String[] args) throws IOException, TimeoutException {
            // 连接
            ConnectionFactory f = new ConnectionFactory();
            f.setHost("192.168.64.140"); // wht6.cn
            f.setPort(5672);
            f.setUsername("admin");
            f.setPassword("admin");
            Connection con = f.newConnection();
            Channel c = con.createChannel();
            // 创建队列 hello-world,如果队列已经存在,不会重复创建
            c.queueDeclare("hello-world",false,false,false,null);

            // 创建回调对象
            DeliverCallback deliverCallback = new DeliverCallback() {
                public void handle(String consumerTag, Delivery message) throws IOException {
                    byte[] a = message.getBody();
                    String s = new String(a);
                    System.out.println("收到: "+s);
                }
            };
            CancelCallback cancelCallback = new CancelCallback() {
                public void handle(String consumerTag) throws IOException {
                }
            };

            // 接收消息,收到的消息会传递到一个回调对象去处理
            // c.basicConsume("hello-world", true, 处理消息的回调对象, 取消接收消息的回调对象);
        /*
        第二个参数: autoAck - auto acknowledgment 自动确认
            true    - 自动确认
                      消息一向消费者发送,服务器立即自动确认消息,删除消息
            false   - 手动确认
                      消息发出后,服务器会缓存消息,不删除,
                      等待消费者发回一个确认消息(回执)才删除,
                      如果消费者处理消息过程中崩溃或离线,
                      服务器会回滚消息,等待重新发送
         */
            c.basicConsume("hello-world", true, deliverCallback, cancelCallback);
        }
}

工作模式:

工作

 工作模式

  生产者核心代码:

与简单模式的区别:

在队列第二次参数中变为true,队列持久化

在提交消息的第三个参数修改成为:

MessageProperties.PERSISTENT_BASIC 常量,消息持久化
package m2;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.140"); // wht6.cn
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Connection con = f.newConnection();
        Channel c = con.createChannel();
        // 创建队列
        // 第二个参数: 是否是持久队列
        c.queueDeclare("task_queue",true,false,false,null);

        //发送消息
        while (true) {
            System.out.print("输入消息:");
            String s = new Scanner(System.in).nextLine();
            c.basicPublish(
                    "", "task_queue", MessageProperties.PERSISTENT_BASIC, s.getBytes());
        }
    }
}

   消费者核心代码:

与简单模式的区别:

1.队列第二次参数变成了可持久化的队列

2.c.basicQos(1); 每次只收一条消息,处理完之前不收下一条

3.修改了接收消息的第二个参数,修改为false,不自动响应,

接收消息,收到的消息会传递到一个回调对象去处理

4.发送回执

c.basicAck对象(回执, 是否对之前收到的所有消息一起进行确认)
c.basicAck(message.getEnvelope().getDeliveryTag(), false);
package m2;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Comsumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.140"); // wht6.cn
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Connection con = f.newConnection();
        Channel c = con.createChannel();
        // 创建队列 hello-world,如果队列已经存在,不会重复创建
        c.queueDeclare("task_queue",true,false,false,null);

        // 创建回调对象
        DeliverCallback deliverCallback = new DeliverCallback() {
            public void handle(String consumerTag, Delivery message) throws IOException {
                byte[] a = message.getBody();
                String s = new String(a);
                System.out.println("收到: "+s); // "ag.d..sfd.fs...gs..."
                // 模拟耗时消息
                // 遍历字符串,找 '.' 字符,每找到一个暂停一秒
                for (int i = 0; i < s.length(); i++) {
                    if ('.' == s.charAt(i)) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                        }
                    }
                }
                //发送回执
                // c.basicAck(回执, 是否对之前收到的所有消息一起进行确认);
                c.basicAck(message.getEnvelope().getDeliveryTag(), false);
                System.out.println("---------------------------消息处理结束");
            }
        };
        CancelCallback cancelCallback = new CancelCallback() {
            public void handle(String consumerTag) throws IOException {
            }
        };

        // 每次只收一条消息,处理完之前不收下一条
        c.basicQos(1);

        // 接收消息,收到的消息会传递到一个回调对象去处理
        c.basicConsume("task_queue", false, deliverCallback, cancelCallback);
    }
}

发布订阅模式:

发布订阅

发布订阅

简单理解:生产者只需要建立连接并配置 交换机,消费者配置队列并绑定交换机即可

生产者核心代码:

package m3;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("wht6.cn");  // 192.168.64.140
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        // 创建 fanout 类型交换机: logs
        // c.exchangeDeclare("logs", "fanout");
        c.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);
        // 向交换机发送消息
        while (true) {
            System.out.print("输入消息: ");
            String s = new Scanner(System.in).nextLine();
            // 对于fanout交换机,第二个参数无效
            c.basicPublish("logs", "", null, s.getBytes());
        }
    }
}

消费者核心代码:

package m3;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeoutException;

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("wht6.cn");  // 192.168.64.140
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        // 1.创建随机队列 2.创建交换机 3.绑定
        String queue = UUID.randomUUID().toString();
        c.queueDeclare(queue, false, true, true, null);
        c.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);
        // 第三个参数,对于 fanout 交换机无效
        c.queueBind(queue, "logs", "");

        // 从队列接收消息
        DeliverCallback deliverCallback = new DeliverCallback() {
            public void handle(String consumerTag, Delivery message) throws IOException {
                String s = new String(message.getBody());
                System.out.println("收到: "+s);
            }
        };
        CancelCallback cancelCallback = new CancelCallback() {
            public void handle(String consumerTag) throws IOException {
            }
        };
        c.basicConsume(queue, true, deliverCallback,cancelCallback);
    }
}

 路由模式:

路由

路由模式

简单理解:生产者只需要配置交换机(direct)和携带路由关键词,消费者需要配置队列,和绑定交换机及关键词 

生产者核心代码:

package m4;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

public class producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.140");  // wht6.cn
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();
        // 创建 direct 类型交换机: direct_logs
        c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);
        // 发送消息,消息上需要携带路由键关键词
        while (true) {
            System.out.print("输入消息: ");
            String s = new Scanner(System.in).nextLine();
            System.out.print("输入路由键: ");
            String k = new Scanner(System.in).nextLine();
            // 对于默认交换机,使用队列名作为路由键
            c.basicPublish("direct_logs", k, null, s.getBytes());
        }

    }
}

消费者核心代码:

package m4;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.140");  // wht6.cn
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();
        // 1.随机队列 2.交换机 3.绑定,设置绑定键
        // 由服务器来自动提供队列参数: 随机命名,false,true,true
        String queue = c.queueDeclare().getQueue();
        c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);

        System.out.println("输入绑定键,用空格隔开:");// "aa    bb cc dd"
        String s = new Scanner(System.in).nextLine();
        String[] a = s.split("\\s+");  //  \s是空白字符   +一到多个
        for (String k : a) {
            c.queueBind(queue, "direct_logs", k);
        }
        // 接收消息
        DeliverCallback deliverCallback = new DeliverCallback() {
            public void handle(String consumerTag, Delivery message) throws IOException {
                String s = new String(message.getBody());
                // 消息携带的路由键
                String k = message.getEnvelope().getRoutingKey();
                System.out.println(k+" --- 收到: "+s);
            }
        };
        CancelCallback cancelCallback = new CancelCallback() {
            public void handle(String consumerTag) throws IOException {
            }
        };
        c.basicConsume(queue, true, deliverCallback,cancelCallback);
    }
}

你可能感兴趣的:(微服务,rabbitmq)