RabbitMQ

1.RabbitMQ 虚拟机
1.1.先安装docker
1.2.安装rabbitmq
2.RabbitMQ 使用场景
3.rabbitmq 基本概念
3.1.rabbitmq六种工作模式
4.rabbitmq简单模式
4.1.新建一个空的工程:
4.2.新建一个maven模块:
4.2.1新建一个生产者发送消息队列的类:
4.2.2.新建一个消费者接收队列消息的类:
5.工作模式工程
6.发布订阅模式-交换机
7.路由模式
8.主题模式
9.RPC模式
10.订单的流量削峰案例
10.2为项目添加消息队列发送机制:
11.pd-web-consumer接收消息并存入数据库
12.动态配置刷新
12.1.添加依赖
12.2.yml配置rabbitmq连接
12.3.09项目中使用actuator暴露bus-refresh刷新端点
12.4.当前启动顺序:
13.服务不停机动态刷新配置
14.sleuth + zipkin 链路跟踪
14.1.添加Sleuth链路跟踪日志依赖到02,03,04,06
14.2.02,03,04,06 添加 zipkin 客户端依赖和rabbitmq配置
14.2.1. 2,3,4,6 添加 zipkin 客户端依赖
14.2.2. 06添加rabbitmq依赖和连接配置
14.2.3. 2,3,4,6 配置日志的发送方式: rabbit
15.选择正确的网卡,注册 IP 地址


1.RabbitMQ 虚拟机
1.1.先安装docker
安装docker环境
克隆 centos-8-2105: rabbitmq
设置ip:
./ip-static
ip: 192.168.64.140

ifconfig
mobaxterm 上传 docker 离线安装文件到 /root/ 目录
课前资料\devops课前资料\docker\docker-install 文件夹
按照笔记安装docker
https://wanght.blog.csdn.net/article/details/117327543

cat < /etc/docker/daemon.json
{
“registry-mirrors”: [
“https://docker.mirrors.ustc.edu.cn”,
“http://hub-mirror.c.163.com”
],
“max-concurrent-downloads”: 10,
“log-driver”: “json-file”,
“log-level”: “warn”,
“log-opts”: {
“max-size”: “10m”,
“max-file”: “3”
},
“data-root”: “/var/lib/docker”
}
EOF

cat命令: 向一个文件输出内容,进入后可以按ctrl+c退出,或者自己指定结束符(<<自己指定的结束符),最后输入自己指定的结束符,结束符以上的内容会被存储到文件中
重新加载docker配置
sudo systemctl daemon-reload

重启docker服务
sudo systemctl restart docker

第五步:查看镜像配置
docker info

检查docker下载下来的镜像
docker images

1.2.安装rabbitmq
启动 docker 容器
下载 docker 镜像
下载镜像位置:
https://hub.docker.com/_/rabbitmq?tab=tags&page=1&ordering=last_updated
带操作界面的rabbitmq

rabbitmq在线安装:
docker pull rabbitmq:management

启动运行容器:
docker run
-d
–name rabbit
-p 5672:5672
-p 15672:15672
-e RABBITMQ_DEFAULT_USER=admin
-e RABBITMQ_DEFAULT_PASS=admin
rabbitmq:management


docker ps
设置访问的用户名和密码:
-e RABBITMQ_DEFAULT_USER=admin
-e RABBITMQ_DEFAULT_PASS=admin \

设置开机启动:
docker update xxxx --restart=always

然后浏览器访问测试:
http://192.168.64.140:15672/

rabbitmq 容器不可访问:

重启网络服务

systemctl restart NetworkManager

重启 docker 系统服务

systemctl restart docker

重启容器

docker restart rabbit

2.RabbitMQ 使用场景

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

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

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

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

流量削峰
假设我们有一个应用,平时访问量是每秒300请求,我们用一台服务器即可轻松应对

而在高峰期,访问量瞬间翻了十倍,达到每秒3000次请求,那么单台服务器肯定无法应对,这时我们可以考虑增加到10台服务器,来分散访问压力

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

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

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

异步调用
考虑定外卖支付成功的情况

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

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

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

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

订单存储的解耦

3.rabbitmq 基本概念
RabbitMQ是一种消息中间件,用于处理来自客户端的异步消息。服务端将要发送的消息放入到队列池中。接收端可以根据RabbitMQ配置的转发机制接收服务端发来的消息。RabbitMQ依据指定的转发规则进行消息的转发、缓冲和持久化操作,主要用在多服务器间或单服务器的子系统间进行通信,是分布式系统标准的配置。

https://wanght.blog.csdn.net/article/details/102810522

3.1.rabbitmq六种工作模式
简单模式
工作模式
发布订阅模式
路由模式
主题模式
RPC模式

4.rabbitmq简单模式
4.1.新建一个空的工程:

4.2.新建一个maven模块:

添加rabbitmq依赖:

<?xml version="1.0" encoding="UTF-8"?>


4.0.0

<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>

4.2.1新建一个生产者发送消息队列的类:
package m1;

import com.rabbitmq.client.Channel;
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 {
//1.连接服务器
ConnectionFactory f = new ConnectionFactory();
f.setHost(“192.168.64.140”);
f.setPort(5672);
f.setUsername(“admin”);
f.setPassword(“admin”);
//通信通道
Channel c = f.newConnection().createChannel();
/*2.在服务器上创建helloworld队列
如果队列在服务器上已经存在,不会重复创建
/
/

参数:
1.队列名
2.是否是持久队列,非持久队列,服务器重启或者断电,队列会消失.持久队列仍然存在
3.是否是排他队列/独占队列(如果这个队列是独占的,则只能被一个消费者占用接收
非独占可以由消费者共享,这个参数是针对消费者而言的)
4.是否是自动删除(自动删除的队列在没有消费者时,可以由服务器自动删除)
5.其他参数(键值对)
*/
c.queueDeclare(“helloworld”,false,false,false,null);

    //3.发送消息  .getBytes():变数组
    /*
    参数:
        1. 空串是默认的交换机
        3. 对消息设置更多的属性参数
     */
    c.basicPublish("","helloworld",null,"Hello world!".getBytes());
    //4.断开连接(可选)
    c.close();
    System.out.println("消息已发送");
}

}
查看是否发送到RabbitMQ服务器:

点开队列消息,可以具体查看发送来了几条

4.2.2.新建一个消费者接收队列消息的类:
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 IOException, TimeoutException {
//1. 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost(“192.168.64.140”);
f.setPort(5672);
f.setUsername(“admin”);
f.setPassword(“admin”);
//通信通道
Channel c = f.newConnection().createChannel();
/*2.创建helloworld队列
消费者也创建同名队里的好处是:
生产者和消费者都创建同一个队列,可以不必关系他们的启动顺序,
谁先启动谁就负责创建队列
*/
c.queueDeclare(“helloworld”,false,false,false,null);
//2.5 创建 处理消息的回调对象
DeliverCallback deliverCallback = new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
// message 是消息数据的封装对象
byte[] a = delivery.getBody();
String s1 = new String(a);
System.out.println(“接到消息:”+s1);
}
};
CancelCallback cancelCallback = new CancelCallback() {
@Override
public void handle(String s) throws IOException {
}
};
/*3.从helloworld 队列接收消息
收到消息后会把消息传递给一个回调对象进行处理
*/
// “helloworld”,true,处理消息的回调对象,取消消息的回调对象
c.basicConsume(“helloworld”,true,deliverCallback,cancelCallback);
}
}

第一次启动消费者后,生产者再发送消息,会被实时接收到

5.工作模式工程
Producer :
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);
//发送消息
while (true){
System.out.println(“输入消息:”);
String s = new Scanner(System.in).nextLine();
if (s.equals(“exit”)){
break;
}else {
c.basicPublish("",“helloworld”,null,s.getBytes());
}
}
System.out.println(“Byes”);
}
}
Consuner :
package m2;

import com.rabbitmq.client.*;

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

public class Consuner {
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);

    DeliverCallback deliverCallback = new DeliverCallback() {
        @Override
        public void handle(String consumerTag, Delivery delivery) throws IOException {
            String s1 = new String(delivery.getBody());
            System.out.println("收到:"+s1);
            /*模拟处理耗时信息
                遍历字符串找'.'字符,没找到一个暂停一秒
                注意:引用名别写错,是处理后的String参数,不是传入的String参数
             */
            for (int i=0;i<s1.length();i++){
                if(s1.charAt(i)=='.'){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
            }
            System.out.println("--------------消息处理结束");
        }
    };
    CancelCallback cancelCallback = new CancelCallback() {
        @Override
        public void handle(String s) throws IOException {
        }
    };
    // 从 helloworld 接收消息
    /*
    第二个参数 true: 自动确认、自动 ACK(Acknowledgement)
     */
    c.basicConsume("helloworld", true, deliverCallback, cancelCallback);
}

}
开启两个消费者的并行运行,运行两个消费者,进行测试:

当服务器不知道消息该发给谁的时候,此时的规则,就是简单的轮询机制

修改后的Consuner :
package m2;

import com.rabbitmq.client.*;

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

public class Consuner {
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);

    DeliverCallback deliverCallback = new DeliverCallback() {
        @Override
        public void handle(String consumerTag, Delivery delivery) throws IOException {
            String s1 = new String(delivery.getBody());
            System.out.println("收到:"+s1);
            /*模拟处理耗时信息
                遍历字符串找'.'字符,没找到一个暂停一秒
                注意:引用名别写错,是处理后的String参数,不是传入的String参数
             */
            for (int i=0;i<s1.length();i++){
                if(s1.charAt(i)=='.'){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
            }
            //手动发送回执
            // 参数: (发送消息的回执栏),是否确认之前收到的所有消息
            c.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
            System.out.println("--------------消息处理结束");
        }
    };
    CancelCallback cancelCallback = new CancelCallback() {
        @Override
        public void handle(String s) throws IOException {
        }
    };

    c.basicQos(1);  //只接收1条消息,处理完之前不接收下一条
                        //只在手动ack模式下有效
    // 从 helloworld 接收消息
    /*
    第二个参数 true: 自动确认、自动 ACK(Acknowledgement)
                false:手动确认,处理消息后需要手动发送回执(所以上边要手动发送下回执)
     */
    c.basicConsume("helloworld", false, deliverCallback, cancelCallback);
}

}

此时已经完成了消费者只会同时接收一条消息的操作,
但是仍存在问题,服务器发送不出去的消息,遇到服务器重启,消息就会消失,
所以这里要做到消息队列持久化,持久队列,队列信息存到磁盘,
对消息本身也要设置持久状态,是持久消息还是普通消息.
修改后的Consuner :
package m2;

import com.rabbitmq.client.*;

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

public class Consuner {
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);

    DeliverCallback deliverCallback = new DeliverCallback() {
        @Override
        public void handle(String consumerTag, Delivery delivery) throws IOException {
            String s1 = new String(delivery.getBody());
            System.out.println("收到:"+s1);
            /*模拟处理耗时信息
                遍历字符串找'.'字符,没找到一个暂停一秒
                注意:引用名别写错,是处理后的String参数,不是传入的String参数
             */
            for (int i=0;i<s1.length();i++){
                if(s1.charAt(i)=='.'){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
            }
            //手动发送回执
            // 参数: (发送消息的回执栏),是否确认之前收到的所有消息
            c.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
            System.out.println("--------------消息处理结束");
        }
    };
    CancelCallback cancelCallback = new CancelCallback() {
        @Override
        public void handle(String s) throws IOException {
        }
    };

    c.basicQos(1);  //只接收1条消息,处理完之前不接收下一条
                        //只在手动ack模式下有效
    // 从 helloworld 接收消息
    /*
    第二个参数 true: 自动确认、自动 ACK(Acknowledgement)
                false:手动确认,处理消息后需要手动发送回执(所以上边要手动发送下回执)
     */
    c.basicConsume("task_queue", false, deliverCallback, cancelCallback);
}

}
修改后的Producer :
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);
//发送消息
while (true){
System.out.println(“输入消息:”);
String s = new Scanner(System.in).nextLine();
if (s.equals(“exit”)){
break;
}else {
//PERSISTENT 持久化的意思 MessageProperties.PERSISTENT_TEXT_PLAIN常量,意为持久化文本
c.basicPublish("",“task_queue”, MessageProperties.PERSISTENT_TEXT_PLAIN,s.getBytes());
}
}
System.out.println(“Byes”);
}
}
此时即使服务器重启后,滞留消息仍会保存,并继续发送

6.发布订阅模式-交换机
Producer:
package m3;

import com.rabbitmq.client.BuiltinExchangeType;
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();
//创建fanout交换机,命名为logs
c.exchangeDeclare(“logs”,“fanout”);
/*或者也可以用常量代替第二个参数
c.exchangeDeclare(“logs”, BuiltinExchangeType.FANOUT);
*/

    //发送消息
    while (true){
        System.out.println("输入消息:");
        String s = new Scanner(System.in).nextLine();
        if (s.equals("exit")){
            break;
        }else {
            /*第一个参数是交换机
                第二个参数,对于fanout交换机无效,所以写不写都是无效的数据
             */
        c.basicPublish("logs","",
                null,s.getBytes());
        }
    }
    System.out.println("Byes");
}

}
此时如果没有消费者接收消息,会直接被丢弃
ps:变量名提示不对应,是因为没有目标类的源码,ctrl点进去,右上角选择第一个选项,下载一下源码即可

Consuner:
package m3;

import com.rabbitmq.client.*;

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

public class Consuner {
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();
//创建一个随机命名的队列,创建交换机,做绑定
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 s = new String(message.getBody());
            System.out.println("收到:"+s);
        }
    };
    CancelCallback cancelCallback = new CancelCallback() {
        @Override
        public void handle(String consumerTag) throws IOException {
        }
    };
    //接收消息
    /*
    第二个参数 true: 自动确认、自动 ACK(Acknowledgement)
                false:手动确认,处理消息后需要手动发送回执(所以上边要手动发送下回执)
     */
    c.basicConsume(queue, true, deliverCallback, cancelCallback);
}

}

7.路由模式

Producer :
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();

    //创建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();
        if (s.equals("exit")){
            break;
        }else {
            /*  发送消息
                第一个参数是交换机
                第二个参数是路由键,默认交换机""的路由键就是队列名
             */
        c.basicPublish("direct_logs",k,
                null,s.getBytes());
        }
    }
    System.out.println("Byes");
}

}

Consuner :
package m4;

import com.rabbitmq.client.*;

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

public class Consuner {
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();
//创建一个随机命名的队列,创建交换机,做绑定
String queue = UUID.randomUUID().toString();
c.queueDeclare(queue,false,true,true,null);
c.exchangeDeclare(“direct_logs”,BuiltinExchangeType.DIRECT);
System.out.print(“输入绑定建,用空格隔开:”);
String s = new Scanner(System.in).nextLine();//aa bb cc
String[] a = s.split("\s+"); // \s+正则表达式,表示1到多个空白字符
// 遍历绑定
for (String k : a) {
c.queueBind(queue,“direct_logs”,k);
}

    //正常从队列接收消息
    DeliverCallback deliverCallback = new DeliverCallback() {
        @Override
        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() {
        @Override
        public void handle(String consumerTag) throws IOException {
        }
    };
    //接收消息
    /*
    第二个参数 true: 自动确认、自动 ACK(Acknowledgement)
                false:手动确认,处理消息后需要手动发送回执(所以上边要手动发送下回执)
     */
    c.basicConsume(queue, true, deliverCallback, cancelCallback);
}

}

8.主题模式
Producer :
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();

    //创建topic交换机,命名为 topic_logs
    c.exchangeDeclare("topic_logs", BuiltinExchangeType.TOPIC);

    //发送消息
    while (true){
        System.out.print("输入消息:");
        String s = new Scanner(System.in).nextLine();
        System.out.print("输入路由键:");
        String k = new Scanner(System.in).nextLine();
        if (s.equals("exit")){
            break;
        }else {
            /*  发送消息
                第一个参数是交换机
                第二个参数是路由键,默认交换机""的路由键就是队列名
             */
        c.basicPublish("topic_logs",k,
                null,s.getBytes());
        }
    }
    System.out.println("Byes");
}

}
Consuner :
package m5;

import com.rabbitmq.client.*;

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

public class Consuner {
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();
//创建一个随机命名的队列,创建交换机,做绑定
String queue = UUID.randomUUID().toString();
c.queueDeclare(queue,false,true,true,null);
c.exchangeDeclare(“topic_logs”,BuiltinExchangeType.TOPIC);
System.out.print(“输入绑定建,用空格隔开:”);
String s = new Scanner(System.in).nextLine();//aa bb cc
String[] a = s.split("\s+"); // \s+正则表达式,表示1到多个空白字符
// 遍历绑定
for (String k : a) {
c.queueBind(queue,“topic_logs”,k);
}

    //正常从队列接收消息
    DeliverCallback deliverCallback = new DeliverCallback() {
        @Override
        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() {
        @Override
        public void handle(String consumerTag) throws IOException {
        }
    };
    //接收消息
    /*
    第二个参数 true: 自动确认、自动 ACK(Acknowledgement)
                false:手动确认,处理消息后需要手动发送回执(所以上边要手动发送下回执)
     */
    c.basicConsume(queue, true, deliverCallback, cancelCallback);
}

}

9.RPC模式
太复杂,没讲.

10.订单的流量削峰案例

导入项目:
1.将课前准备好的zip里边的 pd-web目录解压到rabbit工程目录
2.导入maven项目,选择pd-web下的pom.xml
3.file—project structures,配置jdk版本
4.执行/导入sql脚本,脚本文件在工程目录中有
5.如果用老师的数据库,还要改yml配置文件
6.点击RunPdAPP,启动项目
7.配置启动项,设置working directory,设置为pd-web目录,设置完后重启项目
1.

3.工程统一的jdk版本,也可以点击设置modules单独设置jdk版本

10.2为项目添加消息队列发送机制:
修改pd-web,发送订单到rabbitmq
8. 添加依赖: starter-rabbitmq
9. yml配置 rabbitmq 连接信息
10. 在启动类中(或自定义自动配置类)准备队列的参数
11. OrderServiceImpl

  1. 添加 AmqpTemplate 用来发送消息的封装工具
  2. 在 saveOrder() 方法中发送消息
  3. 把 saveorder() 中的数据库代码都注释掉

11.pd-web-consumer接收消息并存入数据库
1.复制pd-web,命名pd-web-consumer
2.用idea打开pom文件的名字,然后右键点击 Add as maven project 导入项目
3.添加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;

/*
基础配置: 1.依赖 2.连接 3.队列 均已完成

消费者从orderQueue接收订单,
调用业务代码,完成订单存储
*/
@RabbitListener(queues = “orderQueue”) //设置接收消息
@Component //设置spring接收实例
public class OrderConsumer {
@Autowired
private OrderService orderService;

@RabbitHandler  //配合@RabbitListener,用来指定处理接收到的消息的方法,只能指定一个类方法
public void receive(PdOrder order) throws Exception {
    orderService.saveOrder(order);
    System.out.println("-----------------订单已存储,id="+order.getOrderId());
}

}
4.修改OrderServiceImpl类
加一行代码:
String orderId = pdOrder.getOrderId();
注释并解除注释一下代码:

12.动态配置刷新
2,3,4,9项目基础配置
12.1.添加依赖

  • rabbitmq
  • bus

再加一个binder-rabbit依赖,需要手动添加,插件里边没有

org.springframework.boot
spring-boot-starter-amqp


org.springframework.cloud
spring-cloud-bus


org.springframework.cloud
spring-cloud-stream-binder-rabbit

然后将三个依赖分别加入02,03,04,09
12.2.yml配置rabbitmq连接
注意缩进关系,rabbitmq是在spring下边
rabbitmq:
host: 192.168.64.140
port: 5672
username: admin
password: admin
然后将此条配置分别复制到02,03,04,09.
注意02,03,04使用了远程config中心,所以也要将改好的配置文件上传到git才生效

12.3.09项目中使用actuator暴露bus-refresh刷新端点
1.Actuator依赖添加到09项目中

添加09yml配置:
m.e.w.e.i
management:
endpoints:
web:
exposure:
include:
- bus-refresh
- env

12.4.当前启动顺序:
依次启动05 – 09 – 02…
02,03,04启动的时候要看控制台,确保连接了6001配置中心项目

访问配置中心暴露端点:
http://localhost:6001/actuator

post访问测试(无任何参数访问):
http://localhost:6001/actuator/bus-refresh
效果:

13.服务不停机动态刷新配置

14.sleuth + zipkin 链路跟踪
14.1.添加Sleuth链路跟踪日志依赖到02,03,04,06

添加完依赖后重启对应项目.
只需要添加 sleuth 依赖,0配置
访问订单方法进行测试日志打印:
http://localhost:3001/order-service/sadsasad
效果:

14.2.02,03,04,06 添加 zipkin 客户端依赖和rabbitmq配置
14.2.1. 2,3,4,6 添加 zipkin 客户端依赖

14.2.2. 06添加rabbitmq依赖和连接配置

org.springframework.boot
spring-boot-starter-amqp

14.2.3. 2,3,4,6 配置日志的发送方式: rabbit
06yml添加配置,在spring下
rabbitmq:
host: 192.168.64.140
port: 5672
username: admin
password: admin
zipkin:
sender:
type: rabbit
02,03,04也添加zipkin:配置
zipkin:
sender:
type: rabbit

启动zipkin:
java -jar zipkin-server-2.23.2-exec.jar --zipkin.collector.rabbitmq.uri=amqp://admin:admin@192.168.64.140:5672
启动之后,访问9411端口,打开zipkin
访问服务:
http://localhost:3001/order-service/asdasfasf

效果:
rabbitmq会有zipkin队列

15.选择正确的网卡,注册 IP 地址
选择网卡,配置网段:
用09尝试一下配置网段的应用;
在09项目中新建文件bootstrap.yml

bootstrap.yml

在引导配置中选择网卡

spring:
cloud:
inetutils:
# 忽略网卡
ignored-interfaces:
- VM.* # 正则表达式,VM开头,后面任意字符0到多个
preferred-networks: # 要是用的网卡的网段
# 指定物理网络的网段,
# 点.是正则表达式中的一个符号,这里需要转译为普通的字符,前边加
#- 192.168.89…+
- 172.88.13…+

需要注册IP地址,不注册主机名
application.yml
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
instance:
prefer-ip-address: true
当前可能会出现注册的是ip地址,但是显示的不是ip地址,需要新加配置:(eureka:后)
instance:
prefer-ip-address: true

显示的注册信息:“主机:服务id:端口”

instance-id: s p r i n g . c l o u d . c l i e n t . i p − a d d r e s s : {spring.cloud.client.ip-address}: spring.cloud.client.ipaddress:{spring.application.name}😒{server.port} # 界面列表中显示的格式也显示ip
效果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值