RabbitMQ

1.RabbitMQ 的安装

1.1上传离线安装包

  • rabbitmq-install 目录上传到 /root

在这里插入图片描述

1.2切换到rabbitmq-install目录

  •   cd rabbitmq-install
    

1.3安装

  •   rpm -ivh *.rpm
    

1.4启动rabbitmq服务器

# 设置服务,开机自动启动
systemctl enable rabbitmq-server

# 启动服务
systemctl start rabbitmq-server


1.5rabbitmq管理界面

  • 启用管理界面
# 开启管理界面插件
rabbitmq-plugins enable rabbitmq_management

# 防火墙打开 15672 管理端口
firewall-cmd --zone=public --add-port=15672/tcp --permanent
firewall-cmd --reload


  • 重启RabbitMQ服务
systemctl restart rabbitmq-server

在这里插入图片描述

1.6访问

在这里插入图片描述

1.7添加用户

# 添加用户
rabbitmqctl add_user admin admin

# 新用户设置用户为超级管理员
rabbitmqctl set_user_tags admin administrator

1.8设置访问权限


在这里插入图片描述

1.9开放客户端连接端口

# 打开客户端连接端口
firewall-cmd --zone=public --add-port=5672/tcp --permanent
firewall-cmd --reload

  • 主要端口介绍
    • 4369 – erlang发现口
    • 5672client端通信
    • 15672管理界面ui端口
    • 25672 – server间内部通信口

2.RabbitMQ工作模式

2.1简单模式


RabbitMQ是一个消息中间件,你可以想象它是一个邮局。当你把信件放到邮箱里时,能够确信邮递员会正确地递送你的信件。RabbitMq就是一个邮箱、一个邮局和一个邮递员。

  • 发送消息的程序是生产者
  • 队列就代表一个邮箱。虽然消息会流经RbbitMQ和你的应用程序,但消息只能被存储在队列里。队列存储空间只受服务器内存和磁盘限制,它本质上是一个大的消息缓冲区。多个生产者可以向同一个队列发送消息,多个消费者也可以从同一个队列接收消息.
  • 消费者等待从队列接收消息

在这里插入图片描述

2.1.1RabbitMQ—Demo

创建Project—>Enpty Project

2.1.2编辑pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.tedu</groupId>
    <artifactId>rabbitmq-api</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.4.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.1.3创建Producer(生产者)发送消息

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");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Connection conn=f.newConnection();
        Channel c=conn.createChannel();
        //让服务器创建队列 helloworld(已经存在则不重复创建)
        //队列参数:helloworld,是否是持久队列;是否排他(独占)队列,是否自动删除
        c.queueDeclare("helloworld",
                        false,
                        false,
                        false,
                        null);//其他参数属性,没有给null
        //消息发送到helloworld队列
        //参数:
        //  1.默认的交换机
        //  3.其他的消息属性配置,如果没有给null值
        c.basicPublish("",
                "helloworld",
                null,
                "hello world!".getBytes());
        System.out.println("消息已发送!");
    }
}

2.1.4启动main方法测试

在这里插入图片描述

  • 点击helloworld查看消息具体内容:

2.1.5创建Consumer(消费者)接收消息

package m1;

import com.rabbitmq.client.*;

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

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

        // 创建 helloworld 队列(队列如果已经存在,不会重复创建)
        c.queueDeclare("helloworld", false, false, false, null);

        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                byte[] a = message.getBody();
                String msg = new String(a);
                System.out.println("收到: "+msg);
            }
        };
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
            }
        };

        // 消费数据,等待从队列接收数据
        // 第二个参数true: 由服务器对消息进行自动确认,确认后会直接删除消息
        c.basicConsume("helloworld", true, deliverCallback, cancelCallback);
    }
}

2.1.6启动main方法进行测试

在这里插入图片描述

  • 点击helloworld查看消息发现队列为空:

在这里插入图片描述

2.2工作模式

  • 工作队列(即任务队列)背后的主要思想是避免立即执行资源密集型任务,并且必须等待它完成。相反,我们将任务安排在稍后完成。
  • 我们将任务封装为消息并将其发送到队列。后台运行的工作进程将获取任务并最终执行任务。当运行多个消费者时,任务将在它们之间分发。
  • 使用任务队列的一个优点是能够轻松地并行工作。如果我们正在积压工作任务,我们可以添加更多工作进程,这样就可以轻松扩展。

2.2.1生产者发送消息

package m2;

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");
        //f.setPort(5672);//默认端口可以省略
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();
        //定义队列
        c.queueDeclare("helloworld",false,false,false,null);

        //发送消息
        //循环输入消息发送
        /*
        * 输入消息:ger.err.e.....er...........er......
        *消费者受到消息后,遍历字符串,每个点字符,都暂停一秒,来模拟耗时消息
        */
        while (true){
            System.out.print("输入消息:");
            String msg = new Scanner(System.in).nextLine();
            c.basicPublish("","helloworld",null,msg.getBytes());
        }
    }
}

2.2.2消费者接收消息

package m2;

import com.rabbitmq.client.*;

import java.io.IOException;
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");
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        //定义队列(已经存在则不重复创建)
        c.queueDeclare("helloworld", false, false, false, null);

        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                //遍历消息字符串,每个点字符都暂停一秒
                String msg = new String(message.getBody());
                System.out.println("收到:"+msg);
                for (int i =0;i<msg.length();i++){
                    if ('.' ==msg.charAt(i)){
                        try {
                            Thread.sleep(1000);
                        }catch (InterruptedException e){
                        }
                    }
                }
                System.out.println("消息处理结束!");
            }
        };
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
            }
        };
        //消费数据,等待从队列接收数据
        //第二个参数true: 由服务器对消息进行自动确认,确认后会直接删除消息
        c.basicConsume("helloworld",true,deliverCallback,cancelCallback);//处理消息的回调对象,取得消息处理的回调对象);
    }
}

2.2.3运行测试

  • 运行:
    • 一个生产者
    • 两个消费者

  • 生产者发送多条消息,如: 1,2,3,4,5,6,7,8,9,0.

在这里插入图片描述

  • 两个消费者分别收到:

消费者一:
在这里插入图片描述
消费者二:

rabbitmq在所有消费者中轮询分发消息,把消息均匀地发送给所有消费者

2.2.4合理地分发

  • rabbitmq会一次把多个消息分发给消费者, 这样可能造成有的消费者非常繁忙, 而其它消费者空闲. 而rabbitmq对此一无所知, 仍然会均匀的分发消息;
  • 我们可以使用 basicQos(1) 方法, 这告诉rabbitmq一次只向消费者发送一条消息, 在返回确认回执前, 不要向消费者发送新消息. 而是把消息发给下一个空闲的消费者。
  • 1.手动确认模式
......
//第二个参数true: 由服务器对消息进行自动确认,确认后会直接删除消息
//         false:手动确认
c.basicConsume("helloworld",false,deliverCallback,cancelCallback);
.....
  • 2.发送回执
......
//向服务器发送消息的回执
//参数:1.回执(long类型的编码)2.是否一次确认多条消息(一般是false)
 c.basicAck(message.getEnvelope().getDeliveryTag(),false);
System.out.println("消息处理结束!");
.....
  • 3.设置Qos
....
//每次只接收处理1条消息,处理完成之前不能接收下一条消息
c.basicQos(1);
....
  • 4.测试

生产者:
在这里插入图片描述
消费者1:

消费者2:
在这里插入图片描述

2.2.5消息持久化

  • 当rabbitmq关闭时, 我们队列中的消息仍然会丢失, 除非明确要求它不要丢失数据
  • 要求rabbitmq不丢失数据要做如下两点: 把队列和消息都设置为可持久化(durable)

1.队列持久化

//第二个参数是持久化参数durable
ch.queueDeclare("helloworld", true, false, false, null);

2.消息持久化

//第三个参数设置消息持久化
ch.basicPublish("", "task_queue",
            MessageProperties.PERSISTENT_TEXT_PLAIN,
            msg.getBytes());

3.生产者和消费者都要改(队列):

		.....
        //定义队列
        //队列如果在服务器端已经存在.属性不可变
        c.queueDeclare("task_queue",true,false,false,null);

        //发送消息
        //循环输入消息发送
        /*
        * 输入消息:ger.err.e.....er...........er......
        *消费者受到消息后,遍历字符串,每个点字符,都暂停一秒,来模拟耗时消息
        */
        while (true){
            System.out.print("输入消息:");
            String msg = new Scanner(System.in).nextLine();
            c.basicPublish("","task_queue",null,msg.getBytes());
        }
        .....

4.消息:

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

5.最终代码:
生产者:

package m2;

import com.rabbitmq.client.Channel;
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");
        //f.setPort(5672);//默认端口可以省略
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();
        //定义队列
        //队列如果在服务器端已经存在.属性不可变
        c.queueDeclare("task_queue",true,false,false,null);

        //发送消息
        //循环输入消息发送
        /*
        * 输入消息:ger.err.e.....er...........er......
        *消费者受到消息后,遍历字符串,每个点字符,都暂停一秒,来模拟耗时消息
        */
        while (true){
            System.out.print("输入消息:");
            String msg = new Scanner(System.in).nextLine();
            c.basicPublish("","task_queue", MessageProperties.PERSISTENT_TEXT_PLAIN,msg.getBytes());
        }
    }
}

消费者:

package m2;

import com.rabbitmq.client.*;

import java.io.IOException;
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");
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        //定义队列(已经存在则不重复创建)
        c.queueDeclare("task_queue", true, false, false, null);

        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                //遍历消息字符串,每个点字符都暂停一秒
                String msg = new String(message.getBody());
                System.out.println("收到:"+msg);
                for (int i =0;i<msg.length();i++){
                    if ('.' ==msg.charAt(i)){
                        try {
                            Thread.sleep(1000);
                        }catch (InterruptedException e){
                        }
                    }
                }
                //向服务器发送消息的回执
                //参数:1.回执(long类型的编码)2.是否一次确认多条消息(一般是false)
                c.basicAck(message.getEnvelope().getDeliveryTag(),false);
                System.out.println("消息处理结束!");
            }
        };
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
            }
        };

        //每次只接收处理1条消息,处理完成之前不能接收下一条消息
        c.basicQos(1);
        //消费数据,等待从队列接收数据
        //第二个参数true: 由服务器对消息进行自动确认,确认后会直接删除消息
        //        false:手动确认
        c.basicConsume("task_queue",false,deliverCallback,cancelCallback);//处理消息的回调对象,取得消息处理的回调对象);   
    }
}

2.3发布订阅模式

在这里插入图片描述
在这里插入图片描述
即向多个消费者传递同一条消息;
生产者通过交换机发送消息,消费者只负责和交换机进行绑定。

2.3.1Exchanges 交换机

  • RabbitMQ消息传递模型的核心思想是,生产者永远不会将任何消息直接发送到队列。实际上,通常生产者甚至不知道消息是否会被传递到任何队列
  • 相反,生产者只能向交换机(Exchange)发送消息。交换机是一个非常简单的东西。一边接收来自生产者的消息,另一边将消息推送到队列。交换器必须确切地知道如何处理它接收到的消息。exchange的类型定义。
  • 类型:
    Direct、Topic、Header和Fanout
    Fanout交换机非常简单。它只是将接收到的所有消息广播给它所知道的所有队列。
  • 创建Fanout类型的交换机,并称之为 logs:
  • ch.exchangeDeclare("logs", "fanout");

2.3.2绑定 Bindings

已经创建了一个fanout交换机和一个队列,现在我们需要告诉exchange向指定队列发送消息。exchange和队列之间的关系称为绑定

//指定的队列,与指定的交换机关联起来
//成为绑定 -- binding
//第三个参数时 routingKey, 由于是fanout交换机, 这里忽略 routingKey
ch.queueBind(queueName, "logs", "");

2.3.3生产者

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("192.168.64.140");
        //f.setPort(5672);//默认端口可以省略
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        //定义交换机,服务器如果存在交换机,则不重复创建
        //c.exchangeDeclare("logs","fanout");
        c.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);

        //向交换机发送消息
        while (true){
            System.out.print("输入消息:");
            String msg = new Scanner(System.in).nextLine();
            //参数:1.交换机
            //      2.选择队列,对fanout交换机无效
            c.basicPublish("logs","",null,msg.getBytes());
        }
    }
}

2.3.4消费者

package m3;

import com.rabbitmq.client.*;
import com.sun.org.apache.bcel.internal.generic.NEW;

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("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() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                String msg = new String(message.getBody());
                System.out.println("收到:"+msg);
            }
        };
        CancelCallback cancelCallback=new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
            }
        };
        c.basicConsume(queue,true,deliverCallback,cancelCallback);
    }
}

2.3.5测试

生产者:
在这里插入图片描述
消费者1:

消费者2:

2.4路由模式

在这里插入图片描述

生产者需要携带路由键,消费者绑定需指定关键词。可以绑定多次,每次使用一个关键词。

2.4.1直连交换机 Direct exchange

  • Fanout交换机,这并没有给我们太多的灵活性——它只能进行简单的广播。
  • Direct交换机路由算法——消息传递到bindingKey与routingKey完全匹配的队列。
  • 举例:

在这里插入图片描述

  • 其中我们可以看到直连交换机X,它绑定了两个队列。第一个队列用绑定键orange绑定,第二个队列有两个绑定,一个绑定black,另一个绑定键green
  • 这样设置,使用路由键orange发布到交换器的消息将被路由到队列Q1。带有black或green路由键的消息将转到Q2而所有其他消息都将被丢弃

2.4.2绑定 Bindings

  • 绑定是交换机和队列之间的关系。这可以简单地理解为:队列对来自此交换的消息感兴趣。
  • 绑定可以使用额外的routingKey参数。为了避免与basic_publish参数混淆,我们将其称为bindingKey。这是我们如何创建一个键绑定:
ch.queueBind(queueName, EXCHANGE_NAME, "black");

2.4.3生产者

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");
        //f.setPort(5672);//默认端口可以省略
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        //定义交换机
        c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);

        //发送消息,需要携带路由键
        while (true){
            System.out.print("输入消息:");
            String msg = new Scanner(System.in).nextLine();
            System.out.print("输入路由键:");
            String key = new Scanner(System.in).nextLine();
            //第二个参数是消息上携带的路由键
            c.basicPublish("direct_logs",key,null,msg.getBytes());
        }
    }
}

2.4.4消费者

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");
        //f.setPort(5672);//默认端口可以省略
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        //1.定义随机队列  2.定义交换机  3.绑定,指定绑定的关键词

        //定义随机队列
        //由 rabbitmq 服务器自动命名,默认参数:false,true,true(非持久,独占,自动删除)
        String queue = c.queueDeclare().getQueue();
        //定义交换机
        c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);
        //绑定(指定关键词)
        System.out.println("输入绑定键,用空格隔开:");
        String s = new Scanner(System.in).nextLine();
        String[] a = s.split("\\s+");//["aa","bb","cc"]//“\s”:正则表达式,表示空格;“+”:可以匹配1到多个空格;第一个“\”,表示转义字符。
        for (String key:a){
            c.queueBind(queue,"direct_logs",key);
        }

        //正常的从队列消费数据
        DeliverCallback deliverCallback=new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                //取出消息数据,和消息上携带的路由键
                String msg = new String(message.getBody());
                String key = message.getEnvelope().getRoutingKey();
                System.out.println("收到:"+key+" - "+msg);
            }
        };
        CancelCallback cancelCallback=new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
            }
        };
        c.basicConsume(queue,true,deliverCallback,cancelCallback);
    }
}

2.4.5测试

生产者:
在这里插入图片描述
消费者1:

消费者2:

2.5主题模式

在这里插入图片描述

2.5.1主题交换机 Topic exchange

  • 发送到Topic交换机的消息,它的的routingKey,必须是由点分隔的多个单词。单词可以是任何东西,但通常是与消息相关的一些特性。几个有效的routingKey示例:“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”。routingKey可以有任意多的单词,最多255个字节。
  • bindingKey也必须采用相同的形式。Topic交换机的逻辑与直连交换机类似——使用特定routingKey发送的消息将被传递到所有使用匹配bindingKey绑定的队列。bindingKey有两个重要的特殊点:
    • *可以通配单个单词。
    • #可以通配零个或多个单词。
  • 举例:

  • 将routingKey设置为"quick.orange.rabbit"的消息将被发送到两个队列。
  • 消息 "lazy.orange.elephant“也发送到它们两个。
  • quick.orange.fox“只会发到第一个队列,”lazy.brown.fox“只发给第二个。
  • lazy.pink.rabbit“将只被传递到第二个队列一次,即使它匹配两个绑定。
  • quick.brown.fox"不匹配任何绑定,因此将被丢弃。
  • lazy.orange.male.rabbit”,即使它有四个单词,也将匹配最后一个绑定,并将被传递到第二个队列。

2.5.2生产者

package m5;

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");
        //f.setPort(5672);//默认端口可以省略
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        //定义交换机
        c.exchangeDeclare("topic_logs", BuiltinExchangeType.TOPIC);

        //发送消息,需要携带路由键
        while (true){
            System.out.print("输入消息:");
            String msg = new Scanner(System.in).nextLine();
            System.out.print("输入路由键:");
            String key = new Scanner(System.in).nextLine();
            //第二个参数是消息上携带的路由键
            c.basicPublish("topic_logs",key,null,msg.getBytes());
        }
    }
}

2.5.3消费者

package m5;

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");
        //f.setPort(5672);//默认端口可以省略
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        //1.定义随机队列  2.定义交换机  3.绑定,指定绑定的关键词

        //定义随机队列
        //由 rabbitmq 服务器自动命名,默认参数:false,true,true(非持久,独占,自动删除)
        String queue = c.queueDeclare().getQueue();
        //定义交换机
        c.exchangeDeclare("topic_logs", BuiltinExchangeType.TOPIC);
        //绑定(指定关键词)
        System.out.println("输入绑定键,用空格隔开:");
        String s = new Scanner(System.in).nextLine();
        String[] a = s.split("\\s+");//["aa","bb","cc"]
        // “\s”:正则表达式,表示空格;“+”:可以匹配1到多个空格;第一个“\”,表示转义字符。
        for (String key:a){
            c.queueBind(queue,"topic_logs",key);
        }

        //正常的从队列消费数据
        DeliverCallback deliverCallback=new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                //取出消息数据,和消息上携带的路由键
                String msg = new String(message.getBody());
                String key = message.getEnvelope().getRoutingKey();
                System.out.println("收到:"+key+" - "+msg);
            }
        };
        CancelCallback cancelCallback=new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
            }
        };
        c.basicConsume(queue,true,deliverCallback,cancelCallback);
    }
}

2.5.4测试

生产者:

消费者1:

消费者2:

2.6RPC异步调用模式


附:https://blog.csdn.net/weixin_38305440/article/details/102810522

3.拼多商城整合 RabbitMQ

  • 当用户下订单时,我们的业务系统直接与数据库通信,把订单保存到数据库中
  • 当系统流量突然激增,大量的订单压力,会拖慢业务系统和数据库系统
  • 我们需要应对流量峰值,让流量曲线变得平缓,如下图

3.1订单存储的解耦

  • 为了进行流量削峰,我们引入 rabbitmq 消息队列,当购物系统产生订单后,可以把订单数据发送到消息队列;
  • 而订单消费者应用从消息队列接收订单消息,并把订单保存到数据库。

  • 这样,当流量激增时,大量订单会暂存在rabbitmq中,而订单消费者可以从容地从消息队列慢慢接收订单,向数据库保存。

3.2生产者----发送订单

3.2.1pom.xml 添加依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

3.2.2编辑application.yml

spring:
  rabbitmq:
    host: 192.168.64.140
    username: admin
    password: admin

3.2.3修改主程序 RunPdAPP

在主程序中添加下面的方法创建Queue实例:

  • 当创建RabbitMQ连接和信道后,Spring的RabbitMQ工具会自动在服务器中创建队列,代码在RabbitAdmin.declareQueues()方法中
//封装队列消息的对象
	//rabbitmq 的自动配置类会自动发现这个Queue实例,
	//根据其中封装的队列消息,在rabbitmq服务器上创建这个队列
	@Bean
	public Queue orderQueue(){
		return new Queue("oderQueue");
	}

3.2.4修改 OrderServiceImpl

	.......
	//RabbitAutoConfiguration中创建了AmpqTemplate实例
	@Autowired
	private AmqpTemplate amqpTemplate;
	
	public String saveOrder(PdOrder pdOrder) throws Exception {
		String orderId = generateId(); //生成订单ID
		pdOrder.setOrderId(orderId);
		//转换成byte[]数组再发送
		amqpTemplate.convertAndSend("orderQueue",pdOrder);
		return orderId;
	}
	.......

3.2.5查看 RabbitMQ 队列


3.3消费者----接收订单,并保存到数据库

3.3.1修改 application.yml 配置

server:
  port: 81
......................

3.3.2新建 OrderConsumer

package com.pd;

import com.pd.pojo.PdOrder;
import com.pd.service.OrderService;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

//从 rabbitmq 服务器的 orderQueue 队列,接收订单消息,调用业务代码保存订单
/*
*消费者代码由消息来触发执行
*/
@Component//自动扫描,自动创建实例
@RabbitListener(queues = "orderQueue")//注册成为消费者,从队列"orderQueue"接收消息
public class OrderConsumer {
    @Autowired
    private OrderService orderService;
    //用来处理消息的方法
    @RabbitHandler//配合@RabbitListener使用,传递给相应的方法处理,只能有一个该注解
    public void receive(PdOrder pdOrder) throws Exception {
        orderService.saveOrder(pdOrder);
        System.out.println("-----------订单已存储!----------");
    }
}

3.3.3修改 OrderServiceImpl 的 saveOrder() 方法

public String saveOrder(PdOrder pdOrder) throws Exception {
		/*String orderId = generateId(); //生成订单ID
		pdOrder.setOrderId(orderId);
		//转换成byte[]数组再发送
		amqpTemplate.convertAndSend("orderQueue",pdOrder);*/
		String orderId = pdOrder.getOrderId();
		
		PdShipping pdShipping = pdShippingMapper.selectByPrimaryKey(pdOrder.getAddId());
		pdOrder.setShippingName(pdShipping.getReceiverName());
		pdOrder.setShippingCode(pdShipping.getReceiverAddress());
		pdOrder.setStatus(1);
		pdOrder.setPaymentType(1);
		pdOrder.setPostFee(10D);
		pdOrder.setCreateTime(new Date());

		double payment = 0;
		List<ItemVO> itemVOs = selectCartItemByUseridAndItemIds(pdOrder.getUserId(), pdOrder.getItemIdList());
		for (ItemVO itemVO : itemVOs) {
			PdOrderItem pdOrderItem = new PdOrderItem();
			String id = generateId();
			//String id="2";
			pdOrderItem.setId(id);
			pdOrderItem.setOrderId(orderId);
			pdOrderItem.setItemId("" + itemVO.getPdItem().getId());
			pdOrderItem.setTitle(itemVO.getPdItem().getTitle());
			pdOrderItem.setPrice(itemVO.getPdItem().getPrice());
			pdOrderItem.setNum(itemVO.getPdCartItem().getNum());

			payment = payment + itemVO.getPdCartItem().getNum() * itemVO.getPdItem().getPrice();
			pdOrderItemMapper.insert(pdOrderItem);
		}
		pdOrder.setPayment(payment);
		pdOrderMapper.insert(pdOrder);
		return orderId;
	}

3.3.4 启动测试


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值