【实用篇】SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud分布式

news2024/11/17 21:22:01

文章目录

  • 一、服务拆分
    • 1.1 服务拆分Demo
    • 1.2 微服务远程调用
  • 二、Eureka
    • 2.1 Eureka原理
    • 2.2 Eureka-server服务搭建
    • 2.3 eureka-client服务注册
    • 2.4 eureka-client服务复制
    • 2.5 eureka服务发现
  • 三、Ribbon负载均衡
    • 3.1 负载均衡原理
    • 3.2 负载均衡策略
    • 3.3 自定义负载均衡策略
    • 3.4 饥饿加载与懒加载
  • 四、Nacos
    • 4.1 Nacos安装教程
    • 4.2 服务注册到nacos
    • 4.3 服务分级存储模型
    • 4.4 同集群优先的负载均衡
    • 4.5 权重配置
    • 4.6 环境隔离namespace
    • 4.7 配置管理
    • 4.8 配置热更新
    • 4.9 多环境配置共享
    • 4.10 nacos集群搭建
  • 五、Feign远程调用
    • 5.1 Feign替代RestTemplate
    • 5.2 Feign自定义配置
    • 5.3 Feign使用优化
  • 六、Gateway统一网关
    • 6.1 为什么使用Gateway统一网关
    • 6.2 搭建Gateway服务
    • 6.3 断言工厂
    • 6.4 过滤器工厂(路由过滤器、默认过滤器)
    • 6.5 全局过滤器(GlobalFilter)
    • 6.6 过滤器执行顺序
    • 6.7 跨域问题
  • 七、Docker容器
    • 7.1 认识Docker容器
    • 7.2 Docker的架构
    • 7.3 Centos7安装Docker
    • 7.4 启动Docker
    • 7.5 配置Docker镜像
    • 7.6 镜像基本操作
    • 7.7 容器基本操作
    • 7.8 进入容器
    • 7.9 Docker的数据卷
    • 7.10 Dockerfile自定义镜像
    • 7.11 构建Java项目
    • 7.12 DockerCompose下载安装
    • 7.13 DockerCompose部署微服务集群
  • 八、RabbitMQ消息队列
    • 8.1 同步通讯与异步通讯
    • 8.2 MQ技术对比
    • 8.3 RabbitMQ下载和安装
    • 8.4 RabbitMQ组件的介绍
    • 8.5 RabbitMQ消息模型
    • 8.6 简单队列模型
  • 九、SpringAMQP
    • 9.1 Basic Queue简单队列模型
    • 9.2 WorkQueue消息队列
    • 其他比较复杂的MQ模型,在工作遇到时再学习

学习资料百度网盘: https://pan.baidu.com/s/1LxIxcHDO7SYB96SE-GZfuQ
密码:dor4

一、服务拆分

1.1 服务拆分Demo

两个服务放在同一个项目里面,然后分别启动,从而实现了服务拆分
在这里插入图片描述

1.2 微服务远程调用

1. 在配置类中注册RestTemplate

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication{
	public static void main(String[] args){
		SpringApplication.run(OrderApplication.class, args);
	}
}

2. 在OrderService中远程调用UserService服务

@Service
public class OrderService{
	@Autowired
	private OrderMapper orderMapper;
	@Autowired
	private RestTemplate restTemplate;

	public OrderqueryOrderById(Long orderId){
		// 1. 查询订单
		Order order = orderMapper.findById(orderId);
		// 2. url路径
		String url  = "http://localhost:8081/user/" + order.getUserId();
		// 3. 利用RestTemplate发起http请求,查询用户
		User user = restTemplate.getForObject(url, User.class);
		// 4. 封装User到Order
		order.setUser(user);
		// 5. 返回order
		return order;
	}
}

二、Eureka

2.1 Eureka原理

在这里插入图片描述

2.2 Eureka-server服务搭建

1. 在cloud-demo父工程下,创建一个子模块eureka-server
在这里插入图片描述
2. 引入eureka-server依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

3. 编写启动类

@SpringBootApplication
@EnableEurekaServer		//该启动类设置为eureka-server服务
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

4. 编写配置文件application.yml

# eureka服务信息配置
server:
  port: 10086
spring:
  application:
    name: eureka-server
    
# 注册到eureka中
eureka:
  client:
    service-url: #eureka的地址信息
      defaultZone: http://127.0.0.1:10086/eureka

5. 启动微服务,然后在浏览器访问:http://127.0.0.1:10086
在这里插入图片描述

2.3 eureka-client服务注册

1. 在user-service和order-service服务模块中,引入eureka-client依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2. 修改文件application.yml

# 设置服务信息
spring:
  application:
    name: user-service

# 将服务注册到http://127.0.0.1:10086/eureka服务中
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

3. 启动eureka-server服务及user-service服务,然后在浏览器访问:http://127.0.0.1:10086
启动eureka-server服务及user-service服务,我们发现当前已在Eureka中注册的实例栏中有了user-service的信息。
在这里插入图片描述

2.4 eureka-client服务复制

复制之后并不是又多了一个user-service模块,而是只是多了一个UserApplication启动服务
在这里插入图片描述
在这里插入图片描述
可以看到eureka-server管理页面数值变化
在这里插入图片描述

2.5 eureka服务发现

1. 修改orde-service代码用服务名称代替ip端口
我们要去http://127.0.0.1:10086/eureka中拉取user-service服务的实例列表,并通过负载均衡找到其中一个实例。不过这些动作不用我们去做,只需要添加一些注解@LoadBalance即可。
在这里插入图片描述
2. 在order-service的OrderApplication中,给RestTemplate这个Bean添加一个@LoadBalanced注解

在这里插入图片描述
3. 重启order-service服务,再次访问http://localhost:8080/order/101
发现user-service服务的两个实例都打印了sql信息,并且选择其中一个发送请求。所以我们发现spring会自动帮助我们从eureka-server端,根据userservice这个服务名称,获取实例列表,而后完成负载均衡。
在这里插入图片描述

三、Ribbon负载均衡

上述操作已经完成了负载均衡了,这里就说说关于负载均衡的SpringCloud底层完成负载均衡的Ribbon组件。

3.1 负载均衡原理

SpringCloudRibbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改
在这里插入图片描述

3.2 负载均衡策略

负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类
在这里插入图片描述
默认的实现就是ZoneAvoidanceRule,是一种轮询方案.
在这里插入图片描述

3.3 自定义负载均衡策略

配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则(针对某个服务的负载均衡策略)

user-service: # 给某个微服务配置负载均衡规则,这里是user-service服务
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则 

3.4 饥饿加载与懒加载

Ribbon采用懒加载:第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
Ribbon采用饥饿加载:会在项目启动时创建LoadBalanceClient,降低第一次访问的耗时。

文件位置:例如order-service要访问user-service服务,那就在order-service的application.yml中配置user-service服务

# 配置单个clients
ribbon:
  eager-load:
    enabled: true # 开启饥饿加载
    clients: user-service # 开启饥饿加载的服务名称
ribbon:
  eager-load:
    enabled: true 
    clients: 
      - user-service # 配置多个饥饿加载服务
      - other-service

四、Nacos

4.1 Nacos安装教程

  1. 进入GitHub主页:https://github.com/alibaba/nacos
  2. 下拉点击Release Version的全部发行版
    在这里插入图片描述
  3. 下载自己想要的版本
    .zip是windows版本 .tar.gz是Linux版本
    在这里插入图片描述
  4. 解压nacos-service,并且进入bin目录
    在这里插入图片描述
  5. 启动startup.cmd,但是不要双击startup.cmd文件启动nacos服务,因为默认是集群模式,使用命令进行单体启动.
startup.cmd -m standalone

但是我的电脑需要输入这样的命令

.\ startup.cmd -m standalone

在这里插入图片描述

  1. 在浏览器输入地址:http://127.0.0.1:8848/nacos即可访问,默认账号密码都是nacos:
    在这里插入图片描述
    在这里插入图片描述

4.2 服务注册到nacos

因为项目是在eureka的基础上创建的,所以引入nacos的时候,需要删除以前的eureka一些配置,直接看下面示例就行了。

  1. 在cloud-demo父工程的pom文件中的<dependencyManagement>中引入SpringCloudAlibaba的管理依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.6.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
  1. 如果依赖中有如下的eureka依赖,那就删除或注释掉。
        <!--eureka客户端依赖-->
        <!--<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>-->
  1. user-serviceorder-service中的pom文件中引入nacos-discovery依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  1. 配置nacos地址:在user-service和order-service的application.yml中添加nacos地址,不要忘了注释掉eureka地址
spring:
  cloud:
    nacos:
      server-addr: localhost:8848
  1. 重启微服务后,登录nacos管理页面,可以看到微服务信息
    在这里插入图片描述

4.3 服务分级存储模型

一个服务可以有多个实例,例如我们的user-service,可以有

127.0.0.1:8081
127.0.0.1:8082
127.0.0.1:8083

假如这些实例分布于全国各地的不同机房

127.0.0.1:8081(在上海机房)
127.0.0.1:8082(在上海机房)
127.0.0.1:8083(在杭州机房)

也就是说,user-service是服务,一个服务可以包含多个集群,如杭州、上海,每个集群下可以有多个实例,形成Nacos服务分级存储模型
在这里插入图片描述

给user-service配置集群

  1. 把UserApplication和UserApplication2添加到杭州集群,先找到user-service模块下的application.yml,集群配置如下:
spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: HZ # 集群名称-杭州
  1. 启动UserApplication和UserApplication2实例,我们可以在nacos控制台看到下面结果
    在这里插入图片描述

  2. 把UserApplication3添加到杭州集群,先找到user-service模块下的application.yml,集群配置如下:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: SH # 集群名称-上海
  1. 启动UserApplication3实例,我们可以在nacos控制台看到下面结果
    在这里插入图片描述

4.4 同集群优先的负载均衡

默认的ZoneAvoidanceRule并不能实现根据同集群优先来实现负载均衡。因此Nacos中提供了一个NacosRule的实现,可以优先从同集群中挑选实例。

给order-service配置集群信息,修改order-service的application.yml文件,添加集群配置:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: HZ # 集群名称

修改order-service的application.yml文件,修改负载均衡规则为优先选择同集群

user-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则 

4.5 权重配置

实际部署中会出现这样的场景

  • 服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求。
  • 但默认情况下NacosRule是同集群内随机挑选,不会考虑机器的性能问题。
  • 因此,Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。

在nacos控制台,找到user-service的实例列表,点击编辑,即可修改权重
注意:如果权重修改为0,则该实例永远不会被访问
在这里插入图片描述
在这里插入图片描述

4.6 环境隔离namespace

不同namespace之间相互隔离,例如不同namespace的服务互相不可见

1. 默认情况下所有的服务都在一个名为public的namespace
在这里插入图片描述
2. 我们可以点击页面新增按钮,添加一个namespace
在这里插入图片描述
3. 然后填写表单,命名空间ID不填,让它自动生成
在这里插入图片描述
4. 就能在页面看到一个新的namespace
在这里插入图片描述
5. 给微服务配置namespace

spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: HZ
        namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填nacos上面生成的ID

6. 重启order-service后,访问控制台,可以看到下面的结果
在这里插入图片描述
在这里插入图片描述
7. 此时访问order-service,因为namespace不同,会导致找不到userservice,控制台会报错
在这里插入图片描述

4.7 配置管理

1. 为什么需要nacos配置管理
当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我们需要一种统一配置管理方案,可以集中管理所有实例的配置。

其实也不是说服务的所有配置都交给nacos管理,只是一些经常改动的配置需要nacos统一管理。例如数据库的驱动器、地址、账号、密码就不是经常改动的,所以就不用放在nacos统一管理了。

在这里插入图片描述
2. 怎样在nacos添加配置管理
在nacos首页找到配置列表,并且点击添加
在这里插入图片描述
然后在弹出的表单中,填写配置信息,然后发布
在这里插入图片描述
3. 在服务模块user-service中,引入nacos-config依赖

<!--nacos配置管理依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

4. 配置bootstrap.yaml文件
在user-service中\src\main\resources添加一个bootstrap.yaml文件

spring:
  application:
    name: user-service # 服务名称
  profiles:
    active: dev #开发环境,这里是dev 
  cloud:
    nacos:
      server-addr: localhost:8848 # Nacos地址
      config:
        file-extension: yaml # 文件后缀名

5. 测试是否能通过bootstrap.yaml拉取到配置

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Value("${pattern.dateformat}")		//通过value读取配置中的pattern.dateformat
    private String dateformat;
    
    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
    }
}

在页面访问http://localhost:8081/user/now,可以看到效果
在这里插入图片描述

4.8 配置热更新

我们最终的目的,是修改nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新。

方式一:在@Value注入的变量所在类上添加注解@RefreshScope
在user-service的Controller层添加@RefreshScope

@Slf4j
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController {

    @Autowired
    private UserService userService;

    @Value("${pattern.dateformat}")		//通过value读取配置中的pattern.dateformat
    private String dateformat;
    
    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
    }
}

方式二:使用@ConfigurationProperties注解代替@Value注解
在user-service服务中,添加一个类,读取patterrn.dateformat属性

@Component
@Data
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
    private String dateformat;
}

在UserController中使用这个类代替@Value

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private PatternProperties patternProperties;

    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));
    }
}

查看效果
启动UserApplication启动类,这时候再去改动nacos统一管理配置,就会热配置到User Application中,而不需要重新启动UserApplication。

项目启动:
第一次的配置文件如下
在这里插入图片描述
第一次访问形式如下
在这里插入图片描述

项目依然保持运行状态,不能重新启动
第二次的配置文件如下
在这里插入图片描述
第二次访问形式如下
在这里插入图片描述

4.9 多环境配置共享

1. 我们重新来看一下bootstrap.yaml配置文件

spring:
  application:
    name: user-service # 服务名称
  profiles:
    active: dev #开发环境dev 
  cloud:
    nacos:
      server-addr: localhost:8848 # Nacos地址
      config:
        file-extension: yaml # 文件后缀名

2. 多环境配置共享规则
我们知道user-service服务需要经过dev(开发)、test(测试)、release(发布)等过程,我们当然知道不同的过程就会有不同的环境配置,但是这些不同的环境配置中也有一些相同的参数值,可不可以把这些相同的参数值放到一个地方,尽量一改动,这个服务的环境全部改动呢?

其实上述bootstrap.yaml文件是加载了user-service.yaml配置文件和user-service-dev.yaml配置文件。是的,无论你是加载user-service-dev.yaml、还是user-service-test.yaml,配置文件是默认会加载的。那么user-service.yaml就成为了多环境共享配置文件了。

那么这个user-service.yaml配置文件在nacos的配置就跟其他配置文件一样了,具体操作可在上面找到。

3. 当nacos、服务本地同时出现相同属性时,优先级有高低之分
在这里插入图片描述

4.10 nacos集群搭建

1. 配置Nacos
进入nacos的conf目录,修改配置文件cluster.conf.example,重命名为cluster.conf,然后添加如下内容。

127.0.0.1:8845
127.0.0.1.8846
127.0.0.1.8847

在这里插入图片描述

2. 修改nacos\conf\application.properties文件,添加数据库配置

spring.datasource.platform=mysql

db.num=1

db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=123

3. 将nacos文件夹复制三份,分别命名为:nacos1、nacos2、nacos3
在这里插入图片描述
4. 分别修改三个文件夹中的application.properties
nacos1

server.port=8845

nacos2

server.port=8846

nacos1=3

server.port=8847

5. 分别启动三个nacos节点
这一次直接双击nacos\bin\startup.cmd就可以启动了。

注意:后面的启动报错,有可能是内存不足

6. 为三个naocs节点配置nginx反向代理
找到nginx/conf/nginx.conf文件,配置如下

upstream nacos-cluster {			# nginx就在这三个nacos进行负载均衡
    server 127.0.0.1:8845;
	server 127.0.0.1:8846;
	server 127.0.0.1:8847;
}

server {
    listen       80;
    server_name  localhost;

    location /nacos {						#访问http://localhost/nacos
        proxy_pass http://nacos-cluster;	#就会跳到下面http://localhost/nacos-cluster,即上面的三个地址
    }
}

7. 启动nginx服务器
在这里插入图片描述
8. 输入localhost/nacos,访问浏览器
在这里插入图片描述

五、Feign远程调用

5.1 Feign替代RestTemplate

1. 引入依赖
我们在order-service服务的pom文件中引入feign的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2. 添加注解
在order-service的启动类添加注解开启Feign的功能

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
	// ...
}

3. 编写Feign客户端
在order-service中新建一个接口,内容如下:
并且在@FeignClient会对user-service模块的实例进行Ribbon负载均衡

@FeignClient("user-service")		//远程调用user-service服务模块下的实例
public interface UserClient {
    @GetMapping("/user/{id}")	//匹配到user-service模块下的/user/{id}对应的方法
    User findById(@PathVariable("id") Long id);	//所以这里findById方法对应user-service的queryById方法
}

4. 远程调用

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserClient userClient;

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.用Feign远程调用
        User user = userClient.findById(order.getUserId());
        // 3.封装user到Order
        order.setUser(user);
        // 4.返回
        return order;
    }
}

5. 测试
在这里插入图片描述

5.2 Feign自定义配置

1. Feign可以支持很多的自定义配置
一般情况下,默认值就能满足我们使用,如果要自定义时,下有两种Feign自定义的方式。下面以日志为例来演示如何自定义配置。
在这里插入图片描述
2. 基于application.yml自定义Feign

# 可以针对单个服务
feign:  
  client:
    config: 
      userservice: # 针对某个微服务的配置
        loggerLevel: FULL #  日志级别NONE、BASIC、HEADERS、FULL 


# 也可以针对多个服务
feign:  
  client:
    config: 
      default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

3. 基于Java代码方式自定义Feign

// 如果想要全局生效
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class) 
public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}

// 如果想要局部生效
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class) 
public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}

5.3 Feign使用优化

1. 为什么Feign需要优化?

Feign底层发起http请求,依赖于其他框架,包括:
- URLConnection:不支持连接池;
- Apache HttpClient:支持连接池;
- OKHttp:支持连接池;
Feign底层默认使用URLConnection

2. 如何优化Feign?

使用支持连接池的Apache HttpClient或者OKHttp代替默认的URLConnection

3. 使用Apache HttpClient替代URLConnection
1. 在order-service的pom文件中引入Apache HttpClient依赖

<!--httpClient的依赖 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

2. 在order-service的application.yml中添加配置

# 这里包含了对日志级别的优化,一般选择BASIC或NONE
feign:
  client:
    config:
      default: # default全局的配置
        loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息

# 这里包含了对底层连接的优化,使用Apache HttpClient来替代URLConnection
  httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

3. 测试是否使用了Apache HttpClient
在FeignClientFactoryBean中的loadBalance方法中打断点
在这里插入图片描述
Debug方式启动order-service服务,可以看到这里的client,底层就是Apache HttpClient
在这里插入图片描述

六、Gateway统一网关

6.1 为什么使用Gateway统一网关

上面演示的过程中,我们通过访问的order-service服务模块获取数据,但是一般情况下不能直接访问。
因为有些微服务模块是不给外人访问的,所以我们需要用Gateway统一网关来限定外部访问。

在这里插入图片描述

6.2 搭建Gateway服务

1. 创建Gateway服务模块
在这里插入图片描述
2. 引入依赖

<!--网关-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服务发现依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

3. 编写启动类

@SpringBootApplication
public class GatewayApplication {

	public static void main(String[] args) {
		SpringApplication.run(GatewayApplication.class, args);
	}
}

4. 编写基础配置和路由规则
我们将 /user/**开头的请求,代理到lb://userservice,lb是负载均衡,根据服务名拉取服务列表,实现负载均衡。

server:
  port: 10010 # 网关端口
spring:
  application:
    name: gateway # 服务名称
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://user-service # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求

5. 重启测试
访问http://localhost:10010/user/1时,符合/user/**规则,请求转发到uri:http://userservice/user/1。nacos根据userservice服务名发现服务,给出服务列表,并根据负载均衡返回其中一个实例。
在这里插入图片描述
6. 总体流程
在这里插入图片描述

6.3 断言工厂

我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件,例如Path=/user/**是按照路径匹配,这个规则是由PathRoutePredicateFactory类来处理的,像这样的断言工厂在SpringCloudGateway还有十几个,我们只需要掌握Path这种路由工程就可以了。
在这里插入图片描述

6.4 过滤器工厂(路由过滤器、默认过滤器)

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
在这里插入图片描述
1. 添加AddRequestHeader过滤器
Spring提供了31种不同的路由过滤器工厂,下面我们以AddRequestHeader为例来讲解

只需要修改gateway服务的application.yml文件,即可添加过滤器
路由过滤器

# 当前过滤器卸载userservice路由下,因此仅仅对的访问userservice的请求有效
spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://user-service 
        predicates: 
        - Path=/user/** 
        filters: # 过滤器
        - AddRequestHeader=Truth, bcb is freaking awesome! # 添加请求头

默认过滤器

# 当前过滤器写在gateway下,则可以对所有路由都生效
spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://user-service 
        predicates: 
        - Path=/user/**
      default-filters: # 默认过滤项
      - AddRequestHeader=Truth, bcb is freaking awesome! 

2. 测试:来到UserController修改queryById方法,代码如下

 @GetMapping("/{id}")
 public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Truth", required = false) String truth) {
  		log.info(truth);
        return userService.queryById(id);
}

重启网关服务以及user-service服务,访问http://localhost:10010/user/1,可以看到控制台打印出了对应的日志
在这里插入图片描述

6.5 全局过滤器(GlobalFilter)

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现。

1. 实现GlobalFilter接口

@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取请求参数
        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
        // 2.获取authorization参数
        String auth = params.getFirst("authorization");
        // 3.校验
        if ("admin".equals(auth)) {
            // 放行
            return chain.filter(exchange);
        }
        // 4.拦截
        // 4.1.禁止访问,设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        // 4.2.结束处理
        return exchange.getResponse().setComplete();
    }
}

6.6 过滤器执行顺序

1. 不同类型过滤器之间的优先级,如下
在这里插入图片描述
2. 同一类型过滤器的优先级

默认过滤器:按照在配置文件中的排序,order从1开始计算
路由过滤器:按照在配置文件中的排序,order从1开始计算
全局过滤器:在GlobalFilter实现类的类名加上注解的@Order()

6.7 跨域问题

1. 什么是跨域问题

浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
- 域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com
- 域名相同,端口不同:localhost:8080和localhost8081

2. 跨域报错演示
在这里插入图片描述
3. 解决上述跨域报错问题

spring:
  cloud:
    gateway:
      # 。。。
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求 
              - "http://localhost:8090"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

七、Docker容器

7.1 认识Docker容器

1. 微服务部署问题及其解决办法。

问题1:依赖关系复杂,容易出现兼容性问题
- 将应用的Libs(函数库)、Deps(依赖)、配置、应用一起打包
问题2:操作系统环境差异问题
- 因为无论是Ubuntu还是CentOS环境,都是基于Linux内核,那就直接把Linux内核打包就好了

2. Docker与虚拟机的区别
在这里插入图片描述

7.2 Docker的架构

1. Docker重要组件

1. 注册器(Registry)

  • 一个 Docker Registry 中可以包含多个仓库(Repository);

2. 仓库(Repository)

  • 一个仓库会包含同一个软件不同版本的镜像,我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

3. 镜像(Image)

  • 回忆一下虚拟机VM安装的时候是不是要导入centos镜像,这个centos镜像就是Image。其他的应用也有各自的镜像,例如MySQL等等。Image需要从Repository拉取到Docker容器中才能运行。

4. 容器(Container)

  • 镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

5. Dockerfile

  • Dockerfile 是一个用来构建镜像Image的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

2. Docker采用C/S模式
在这里插入图片描述

7.3 Centos7安装Docker

1. 版本问题
Docker CE支持64位版本CentOS7,并且要求内核版本不低于3.10,CentOS7满足最低内核的要求,所以我们在CentOS 7安装Docker

2. 如果之前安装过旧版本的Docker,可以使用下面命令卸载

yum remove docker \
					docker-client \
					docker-client-latest \
					docker-common \
					docker-latest \
					docker-latest-logrotate \
					docker-logrotate \
					docker-selinux \
					docker-engine-selinux \
					docker-engine \
					docker-ce					

在这里插入图片描述

3. 安装yum工具

yum install -y yum-utils \
	device-mapper-persistent-data \ 
	lvm2 --skip-broken

在这里插入图片描述

4. 更新本地镜像源

# 设置docker镜像源
yum-config-manager \
	--add-repo \
	https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo

yum makecache fast 

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
5. 下载docker

yum install -y docker-ce

在这里插入图片描述

7.4 启动Docker

Docker应用需要用到各种端口,逐一去修改防火墙装置。非常麻烦,因此建议大家直接关闭防火墙

# 关闭
systemctl stop firewalld

# 禁止开机启动防火墙
systemctl disable firewalld

在这里插入图片描述

启动docker

# 启动docker
systemctl start docker

# 查看是否启动成功
systemctl status docker

在这里插入图片描述

7.5 配置Docker镜像

# 创建/etc/docker文件
sudo mkdir -p /etc/docker
# 向文件写入内容
sudo tee /etc/docker/daemon.json <<-'EOF'
{
	"registry-mirrors": ["https://n0dwemtq.mirror.aliyuncs.com"]
}
EOF
# 重新加载
sudo systemctl daemon-reload
# 重新启动
sudo systemctl restart docker

在这里插入图片描述

7.6 镜像基本操作

在这里插入图片描述
1. 拉取、查看镜像
首先去镜像仓库搜索nginx镜像,比如DockerHub:
在这里插入图片描述
根据查看到的镜像名称,拉取自己需要的镜像,通过命令:docker pull nginx
通过命令:docker images 查看拉取到的镜像
在这里插入图片描述

2. 保存、导入镜像
保存命令:docker save -o [保存的目标文件名称] [镜像名称]

# 把镜像nginx:latest保存到nginx.tar文件
docker save -o  nginx.tar nginx:latest

在这里插入图片描述
导入命令:先删除本地的nginx镜像

docker rmi nginx:lateset

在这里插入图片描述

然后运行命令,加载本地文件

docker load -i nginx.tar

在这里插入图片描述

7.7 容器基本操作

docker run:创建并运行一个容器,处于运行状态
docker pause:让一个运行的容器暂停
docker unpause:让一个容器从暂停状态恢复运行
docker stop:停止一个运行的容器
docker start:让一个停止的容器再次运行
docker rm:删除一个容器

在这里插入图片描述
1. 创建并运行一个容器

docker run --name containerName -p 80:80 -d nginx
docker run :创建并运行一个容器
–name : 给容器起一个名字,比如叫做mn
-p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
-d:后台运行容器
nginx:镜像名称,例如nginx

2. 创建nginx镜像

docker run --name mn -p 80:80 -d nginx

在这里插入图片描述

docker ps

在这里插入图片描述
3. 向nginx所在的服务区发起访问
访问http://192.168.133.128:80
在这里插入图片描述

7.8 进入容器

进入容器
在我看来就是进入了虚拟机终端里面的另一个终端,仔细品一下吧

# 我要进入mn容器中,并且能够进入输入输出,并且打开交互命令
docker exec -it mn bash
- docker exec:进入容器,执行一个命令
- -it:给当前进入的容器创建一个标准输入、输出终端、允许我们与容器交互
- mn:要进入的容器的名称
- bash:进入容器后执行的命令,bash是一个linux终端交互命令

在这里插入图片描述

7.9 Docker的数据卷

1. 原先的Docker是容器与数据耦合
在之前的nginx案例中,修改nginx的html页面时,需要进入nginx内部,并且因为没有编辑器,修改文件也很麻烦,这就是容器与数据耦合带来的后果。

- 不便于修改:当我们要修改Nginx的html内容时,需要进入容器内部修改,很不方便
- 数据不可复用:在容器内的修改对外是不可见的,所有修改对新创建的容器是不可复用的
- 升级维护困难:数据在容器内,如果要升级容器必然删除旧容器,所有数据都跟着删除了

要解决这个问题,必须将数据与容器解耦,这就是数据卷。

2. 数据卷原理
数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录。

一旦完成数据卷挂载,对容器的一切操作都会作用在数据卷对应的宿主机目录了。
这样,我们操作宿主机的/var/lib/docker/volumes/html目录,就等于操作容器内的/usr/share/nginx/html目录了

在这里插入图片描述
3. 数据卷基本操作命令

# 创建数据卷
docker volume create html

# 查看所有数据
docker volume ls

# 查看数据卷详细信息卷
docker volume inspect html

4. 指定数据卷挂载的目录

docker run \
  --name mn \
  -v html:/root/html \
  -p 8080:80
  nginx \

7.10 Dockerfile自定义镜像

1. 创建Dockerfile文件
/docker目录下,新建一个Dockerfile 文件,并在文件内添加以下内容:

`FROM` 定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。
`RUN` 用于执行后面跟着的命令行命令(等同于,在终端操作的 shell 命令)

在这里插入图片描述

2. 开始构建镜像
在 Dockerfile 文件的存放目录/docker下,执行docker build -t nginx:v3 .命令。
nginx:v3:镜像名称:镜像标签
. :代表本次执行的上下文路径
在这里插入图片描述

7.11 构建Java项目

需求:搭建一个JavaWeb镜像,然后在上面运行Java代码
步骤1:新建一个空文件夹docker-demo
步骤2:拷贝课前资料中的docker-demo.jar文件到docker-demo这个目录
步骤3:拷贝课前资料中的jdk8.tar.gz文件到docker-demo这个目录
步骤4:拷贝课前资料提供的Dockerfile到docker-demo这个目录
在这里插入图片描述
Dockerfile内容如下

# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local

# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar

# 安装JDK
RUN cd $JAVA_DIR \
 && tar -xf ./jdk8.tar.gz \
 && mv ./jdk1.8.0_144 ./java8

# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin

# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar

步骤5:将准备好的docker-demo上传到虚拟机任意目录,然后进入docker-demo目录下
步骤6:运行命令docker build -t javaweb:1.0 .
步骤7:运行镜像docker run --name web -p 8090:8090 -d javaweb:1.0
步骤8:最后访问 http://ip:8090/hello/count,其中的ip改成你的虚拟机ip,结果如下:
在这里插入图片描述

7.12 DockerCompose下载安装

1. 什么是DockerCompose
Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器,Compose文件不是一个文本文件,通过指令定义集群中的每个容器如何运行。

2. DockerCompose的使用实例
还记得Docker启动镜像吗?其实就是将Docker run启动命令都放在Docker Compose文件中,只是语法稍有差异。

version: "3.8"
 services:
  mysql:
    image: mysql:5.7.25
    environment:
     MYSQL_ROOT_PASSWORD: 123 
    volumes:
     - "/tmp/mysql/data:/var/lib/mysql"
     - "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
  web:
    build: .
    ports:
     - "8090:8090"

3. DockerCompose的下载安装
1. 百度网盘下载docker-compose文件,通过xftp放到/usr/local/bin目录下

2. 修改文件权限

chmod +x /usr/local/bin/docker-compose

执行后文件变绿了,代表文件可以执行了
在这里插入图片描述
3. Base自动补全命令

curl -L https://raw.githubusercontent.com/docker/compose/1.29.1/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose

在这里插入图片描述

如果出错了,无法下载,那就执行如下命令

echo "199.232.68.133 raw.githubusercontent.com" >> /etc/hosts

7.13 DockerCompose部署微服务集群

1. 查看课前资料提供的cloud-demo文件夹,里面已经编写好了docker-compose文件
在这里插入图片描述
2. 查看docker-compose.yml文件

version: "3.2"

services:	# 包含了下面五个服务nacos、mysql、userservice、orderservice、gateway
  nacos:	# 由于所有服务都需要注册到nacos,所以nacos先启动
    image: nacos/nacos-server	# 镜像
    environment:
      MODE: standalone		# 单点模式
    ports:
      - "8848:8848"		# 端口
  mysql:
    image: mysql:5.7.25		# 镜像
    environment:
      MYSQL_ROOT_PASSWORD: 123  # 密码
    volumes:	# 数据挂载
      - "$PWD/mysql/data:/var/lib/mysql"	# $PWD是相对于/cloud-demo/docker-compose.yml文件下的目录的当前
      - "$PWD/mysql/conf:/etc/mysql/conf.d/"
  userservice:
    build: ./user-service
  orderservice:
    build: ./order-service
  gateway:
    build: ./gateway
    ports:
      - "10010:10010"

3. 修改自己的cloud-demo项目,将数据库、nacos地址都命名为docker-compose中的服务名
具体可以看视频讲解

4. 打包
接下来需要将我们的每个微服务都打包。因为之前查看到Dockerfile中的jar包名称都是app.jar,因此我们的每个微服务都需要用这个名称。

可以通过修改pom.xml中的打包名称来实现,每个微服务都需要修改:

<build>
  <!-- 服务打包的最终名称 -->
  <finalName>app</finalName>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

5. 拷贝jar包到部署目录
编译打包好的app.jar文件,需要放到Dockerfile的同级目录中。注意:每个微服务的app.jar放到与服务名称对应的目录,别搞错了。

6. 部署
最后,我们需要将文件整个cloud-demo文件夹上传到虚拟机中,理由DockerCompose部署。上传到任意目录
在这里插入图片描述
进入cloud-demo目录,然后运行下面的命令:docker-compose up -d

7. 这时我们发现有的服务启动失败了
在这里插入图片描述

究其原因,原来是因为nacos还未启动成功,导致其他微服务同时启动时无法注册成功,这时我们可以运行

docker-compose restart gateway userservice orderservice

8. 重新启动项目
重启这三个微服务,重启完成后可以查看日志,看看服务是否正常启动了.
在这里插入图片描述

9. 开启项目
最后可以访问http://10.100.2.210:10010/order/101?authorization=admin,ip记得改成虚拟机ip,结果如图,说明服务正常启动了.
在这里插入图片描述

八、RabbitMQ消息队列

8.1 同步通讯与异步通讯

1. 同步通讯问题

- 耦合度高:每次加入新的需求,都要修改原来的代码
- 性能下降:调用者需要等待服务提供者响应,如果调用链过长则响应时间等于每次调用的时间
- 资源浪费:调用链中的每个服务都在等待响应过程中,不能释放请求占用的资源,高并发场景下会极度浪费系统资源
- 级联失败:如果服务提供者出现问题,所有调用都会跟着出问题,迅速导致整个微服务集群故障

在这里插入图片描述
2. 异步通讯解决了同步通讯的问题

- 服务解耦:支付服务不再调用订单服务来,而是支付服务直接发布消息,订单服务订阅到消息并且执行服务
- 性能提升:支付服务发送订单消息后,直接做其他的事情了,而不是还在等待订单服务执行
- 解决级联失败问题:服务没有了相互调用,就不用担心级联失败问题
- 流量削峰:由Broker承担流量压力分配

在这里插入图片描述

8.2 MQ技术对比

MQ,中文是消息队列(MessageQueue),字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。
在这里插入图片描述

8.3 RabbitMQ下载和安装

1. 下载镜像

docker pull rabbitmq:3-management

2. 安装MQ

docker run \
 -e RABBITMQ_DEFAULT_USER=itcast \		# MQ页面登陆的账号
 -e RABBITMQ_DEFAULT_PASS=123321 \		# MQ页面登陆的密码
 --name mq \			# 给MQ起个名字
 --hostname mq1 \		# 起个主机名,分布式集群的时候使用
 -p 15672:15672 \		# MQ提供一个管理平台的UI,通过这个端口访问
 -p 5672:5672 \			# 用来消息通信的端口
 -d \					# 后台运行
 rabbitmq:3-management	# 镜像的名称

3. 运行后访问
运行后访问http://192.168.133.128:15672/,其中ip需要改成自己虚拟机的ip,可以看到
在这里插入图片描述

8.4 RabbitMQ组件的介绍

channel:操作MQ的工具
exchange:路由消息到队列中
queue:缓存消息
virtual host:虚拟主机,对exchange、queue等资源的逻辑分组

在这里插入图片描述

8.5 RabbitMQ消息模型

- BasicQueue和WorkQueue都是基于Queue队列来完成消息发送,并没有使用到交换机。
- 发布订阅使用到Exchange交换机。

在这里插入图片描述

8.6 简单队列模型

1. 课前资料提供了一个Demo工程,mq-demo
在这里插入图片描述
2. 查看项目结构
在这里插入图片描述
3. 代码查看
publisher实现

package cn.itcast.mq.helloworld;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.junit.Test;

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

public class PublisherTest {
    @Test
    public void testSendMessage() throws IOException, TimeoutException {
        // 1.建立连接
        ConnectionFactory factory = new ConnectionFactory();
        // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
        factory.setHost("192.168.133.128");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("itcast");
        factory.setPassword("123321");
        // 1.2.建立连接
        Connection connection = factory.newConnection();

        // 2.创建通道Channel
        Channel channel = connection.createChannel();

        // 3.创建队列
        String queueName = "simple.queue";
        channel.queueDeclare(queueName, false, false, false, null);

        // 4.发送消息
        String message = "hello, rabbitmq!";
        channel.basicPublish("", queueName, null, message.getBytes());
        System.out.println("发送消息成功:【" + message + "】");

        // 5.关闭通道和连接
        channel.close();
        connection.close();

    }
}

consumer实现

package cn.itcast.mq.helloworld;

import com.rabbitmq.client.*;

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

public class ConsumerTest {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.建立连接
        ConnectionFactory factory = new ConnectionFactory();
        // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
        factory.setHost("192.168.133.128");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("itcast");
        factory.setPassword("123321");
        // 1.2.建立连接
        Connection connection = factory.newConnection();

        // 2.创建通道Channel
        Channel channel = connection.createChannel();

        // 3.创建队列
        String queueName = "simple.queue";
        channel.queueDeclare(queueName, false, false, false, null);

        // 4.订阅消息
        channel.basicConsume(queueName, true, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(
            					String consumerTag, 
            					Envelope envelope,
            					AMQP.BasicProperties properties, 
            					byte[] body) throws IOException {
                
                // 5.处理消息
                String message = new String(body);
                System.out.println("接收到消息:【" + message + "】");
            }
        });
        System.out.println("等待接收消息。。。。");
    }
}

4. 运行
先运行PublisherTest,再运行ConsumerTest,在ConsumerTest控制台中可以获得如下结果

九、SpringAMQP

SpringAMQP是基于RabbitMQ封装的一套模板,并且还利用SpringBoot对其实现了自动装配,使用起来非常方便。

9.1 Basic Queue简单队列模型

1. 在父工程中引入mq-demo依赖

<!--AMQP依赖,包含RabbitMQ-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 消息发送
首先配置MQ地址,在publisher服务的application.yml中添加配置

spring:
  rabbitmq:
    host: 10.100.2.210 # 主机名
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码

然后在publisher服务中编写测试类SpringAmqpTest,并利用RabbitTemplate实现消息发送:

package cn.itcast.mq.spring;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSimpleQueue() {
        // 队列名称
        String queueName = "simple.queue";
        // 消息
        String message = "hello, spring amqp!";
        // 发送消息
        rabbitTemplate.convertAndSend(queueName, message);
    }
}

3. 消息接收
首先配置MQ地址,在comsumer服务的application.yml中添加配置:

spring:
  rabbitmq:
    host: 10.100.2.210 # 主机名
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码

然后在consumer服务的cn.itcast.mq.listener包中新建一个类SpringRabbitListener,代码如下:

package cn.itcast.mq.listener;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class SpringRabbitListener {

    @RabbitListener(queues = "simple.queue")
    public void listenSimpleQueueMessage(String msg) throws InterruptedException {
        System.out.println("spring 消费者接收到消息:【" + msg + "】");
    }
}

9.2 WorkQueue消息队列

Work queues,也被称为(Task queues),任务模型。简单来说就是让多个消费者绑定到一个队列,共同消费队列中的消息。

1. 在父工程中引入mq-demo依赖

<!--AMQP依赖,包含RabbitMQ-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 消息发送
首先配置MQ地址,在publisher服务的application.yml中添加配置

spring:
  rabbitmq:
    host: 10.100.2.210 # 主机名
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码

然后在publisher服务中编写测试类SpringAmqpTest,并利用RabbitTemplate实现消息发送:

/**
     * workQueue
     * 向队列中不停发送消息,模拟消息堆积。
     */
@Test
public void testWorkQueue() throws InterruptedException {
    // 队列名称
    String queueName = "simple.queue";
    // 消息
    String message = "hello, message_";
    for (int i = 0; i < 50; i++) {
        // 发送消息
        rabbitTemplate.convertAndSend(queueName, message + i);
        Thread.sleep(20);
    }
}

3. 消息接收
首先配置MQ地址,在comsumer服务的application.yml中添加配置:

spring:
  rabbitmq:
    host: 10.100.2.210 # 主机名
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码

然后在consumer服务的cn.itcast.mq.listener包中新建一个类SpringRabbitListener,代码如下:

@RabbitListener(queues = "simple.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
    System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(20);
}

@RabbitListener(queues = "simple.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {
    System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(200);
}

4. 测试
启动ConsumerApplication后,在执行publisher服务中刚刚编写的发送测试方法testWorkQueue。可以看到消费者1很快完成了自己的25条消息。消费者2却在缓慢的处理自己的25条消息。
也就是说消息是平均分配给每个消费者,并没有考虑到消费者的处理能力。这样显然是有问题的。

在spring中有一个简单的配置,可以解决这个问题。我们修改consumer服务的application.yml文件,添加配置:

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

运行consumer服务可以看到,结果是处理消息快的处理的次数也多,即能者多劳.

其他比较复杂的MQ模型,在工作遇到时再学习

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/419588.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日学术速递4.12

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.HC 随着新的“生成代理”论文的发布&#xff0c;LLM刚刚达到了一个重要的里程碑——通过使用 LLM&#xff0c;生成代理能够在受《模拟人生》启发的交互式沙箱中模拟类人行为。代理架构扩展…

Vue的过滤器、内置指令和生命周期【Vue】

Vue 1. 收集表单数据 收集表单数据&#xff1a; 若&#xff1a;< input type“text”/ >&#xff0c;则v-model收集的是value值&#xff0c;用户输入的就是value值。 若&#xff1a;< input type“radio”/ >&#xff0c;则v-model收集的是value值&#xff0c;且…

NIFI大数据进阶_离线同步MySql数据到HDFS_说明操作步骤---大数据之Nifi工作笔记0028

然后我们看如何把mysql中的数据,实时的同步到hdfs中去 准备工作首先,创建一个mysql表,然后启动hadoop集群 处理器我们需要这些处理器,首先通过querydatabasetable处理器,查询mysql中的数据,然后,把mysql中的数据,导入到 convertavrotojson处理器,注意querydatabasetable处理…

C语言结构体练习:【通讯录(静态数组简易版)的实现】

全文目录&#x1f600; 前言&#x1f914; 模块和功能划分&#x1f928; 数据类型的选择&#x1f62e; 功能序号类型 enum&#x1f62e; 个人信息类型 PeoInfo&#x1f62e; 通讯录类型 Contact&#x1f635;‍&#x1f4ab; 功能的实现&#x1f644; 初始化通讯录 InitContact…

MYSQL命令大全(详细版)

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

【快乐手撕LeetCode题解系列】——移除链表元素

【【快乐手撕LeetCode题解系列】——移除链表元素&#x1f60e;前言&#x1f64c;删除有序数组中的重复项&#x1f64c;解法一&#xff1a;画图分析&#xff1a;&#x1f60d;思路分析&#xff1a;&#x1f60d;源代码分享&#xff1a;&#x1f60d;解法二&#xff1a;画图分析…

Vite打包后直接使用浏览器打开,显示空白问题

vite打包后&#xff0c;直接用浏览器打开显示空白 1.需求&#xff1a; 安卓webview等浏览器直接打开文件显示 2.原因 &#xff08;1&#xff09;资源路径错误&#xff1a; vite.config.js 配置 base: “./” &#xff08;在webpack中则配置publicPath: "./"即可…

Windows ActiveMQ安装

Windows ActiveMQ 安装 ActiveMQ下载&#xff1a;ActiveMQ Java JDK下载&#xff1a;Java JDK20 下载完成之后解压ZIP安装包&#xff0c;解压后得到如下文件夹 找到根目录下/bin/win64/activemq.bat 双击打开&#xff0c;如果提示权限记得使用管理员身份 。 如提示Unable to e…

学习数据结构第5天(线性表的链式表示)

线性表的链式表示单链表的定义单链表的基本操作双链表双链表的基本操作循环链表块状链表存储结构顺序表和链表的比较线性表的顺序存储结构的特点是逻辑关系上相邻的两个数据元素在物理位置上也是相邻的。我们会发现虽然顺序表的查询很快&#xff0c;时间复杂度为O(1)O(1)O(1),但…

Java EE企业级应用开发(SSM)第5章

第5章Spring MVC入门一.预习笔记 1.Spring MVC是所有使用OOP编程语言都应该遵守的规范 2.Spring MVC的特点 强大的灵活性、非侵入性和可配置性 提供了一个前端控制器DispatcherServlet&#xff0c;开发者无须额外开发控制器对象 分工明确&#xff0c;每一个功能由一个专门…

日前、日内两阶段需求响应热电综合能源联合调度研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

深度学习之卷积神经网络0基础入门学习教程——浅层深度学习(全网最详细,不详细你打我)(看不懂你打我)(对小白没用你打我)

作为一个双非学校的人工智能硕士研究生&#xff0c;我在深度学习入门之初&#xff0c;学校里没人带&#xff0c;只能靠自己一点一点的摸索&#xff0c;走了很多弯路&#xff0c;因为自己并不懂得从哪里入手&#xff0c;从吴恩达的课到李沐的课等等无数种课&#xff0c;无数种书…

Microsoft project 2013安装教程(含激活方法)

目录 一、介绍 二、安装教程 三、激活 一、介绍 Microsoft project 2013中文版是由微软推出的一款项目管理软件&#xff0c;软件将可用性、功能和灵活性等完美地融合在一起&#xff0c;可以有效提升办公人员的效率&#xff0c;利用project 2013用户可以制作出…

SecureCRT8.5的下载、安装和注册(详细图解)

SecureCRT8.5的下载、安装和注册请先关闭杀毒软件和防火墙。 关闭防火墙 先打开电脑的设置&#xff0c;在弹出的界面里找到并点击更新和安全。 找到并点击Windows 安全中心&#xff0c;再在右侧找到并点击防火墙和网络保护选项。 将域网络、专用网络、公用网络这三个选项设…

数据结构入门-11-红黑树

史上最负盛名的平衡二叉树–红黑树&#xff0c;但其实就是2-3树的一种实现 文章目录一、红黑树性质二、红黑树性质推导过程2-3树2.3.1 如何维护绝对平衡2-3树2.3.2 红黑树&2-3树2.3.3 再来看红黑树的性质1.每个节点为 Black or Red2.根节点是Black3.每一个叶子节点(最后的空…

2023年郑州重点建设项目名单公布,中创“算力数据中心”项目入选!

4月7日&#xff0c;郑州市人民政府网站公布2023年郑州市重点建设项目名单&#xff0c;名单共列项目680个&#xff0c;总投资1.08万亿元&#xff0c;年度计划投资2691亿元。 在创新驱动能力提升项目名单里&#xff0c;中创算力与人民网人民数据&#xff08;国家大数据灾备中心&a…

小程序实现消息订阅及发送

在我们的家政服务小程序中&#xff0c;用户可以新增预约。一般的场景是新增预约的时候提醒用户接收通知&#xff0c;在状态变更的时候我们来发送订阅消息。本篇我们来讲解一下小程序订阅消息功能的开发。 1 开通订阅消息模板 要想发送订阅消息&#xff0c;首先需要选用一个消…

【ES】数据同步集群

【ES】数据同步&集群3.数据同步3.1.思路分析3.1.1.同步调用3.1.2.异步通知3.1.3.监听binlog3.1.4.选择3.2.实现数据同步3.2.1.思路3.2.2.导入demo3.2.3.声明交换机、队列1&#xff09;引入依赖2&#xff09;声明队列交换机名称3&#xff09;声明队列交换机3.2.4.发送MQ消息…

解决macOS IntelliJ IDEA 卡顿问题

写在前面的话1&#xff1a;我在撰写这篇博客时候&#xff0c;所用的IntelliJ IDEA版本是IntelliJ IDEA 2022.3.3 (Ultimate Edition)&#xff0c;你需要知道可能对于不同的IntelliJ IDEA版本会有一定的差异 写在前面的话2&#xff1a;如果我这篇博客可以帮助到你&#xff0c;请…

国内可用的 ChatGPT

国内可用的 ChatGPT 替代列表功能和使用顺便推荐列表 https://chatgpt.qdymys.cn/ &#xff1a;三小时只能用10次 https://alllinkai.com/ &#xff1a;体验完&#xff0c;要登录&#xff0c;不推荐 https://gpttalk.live/ &#xff1a;每天限制使用次数 https://chatgpt-c…