# spring-cloud-parent 学习使用spring netflix一套 **Repository Path**: toyan/spring-cloud-parent ## Basic Information - **Project Name**: spring-cloud-parent 学习使用spring netflix一套 - **Description**: 使用eureka做注册中心 Ribbon做负载均衡 OpenFeign服务接口调用 Hystrix断路器 (豪猪)-保险丝 Gateway新一代网关 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-09-08 - **Last Updated**: 2022-09-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # spring-cloud-parent 学习使用spring netflix一套 ## 第一章 SpringCloud简介 ### 一、 软件架构演进 单体架构 ![image-20220328172416767](https://www.ydlclass.com/doc21xnv/assets/image-20220328172416767.61fb5e07.png) 垂直架构 ![image-20220328172721883](https://www.ydlclass.com/doc21xnv/assets/image-20220328172721883.2d51c59e.png) 分布式架构 ![image-20220328173112642](https://www.ydlclass.com/doc21xnv/assets/image-20220328173112642.b0ee1ce2.png) SOA架构 ![image-20220328174118241](https://www.ydlclass.com/doc21xnv/assets/image-20220328174118241.23f135ec.png) 微服务架构 ![image-20220328174633493](https://www.ydlclass.com/doc21xnv/assets/image-20220328174633493.6d3e8f1b.png) ### 二、微服务架构 #### 1、 微服务理念 ①"微服务”一词源 于 Martin Fowler的名为 Microservices的博文,可以在他的官方博客上找到http://martinfowler.com/articles/microservices.html ②微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间一般通过 HTTP 的 RESTfuL API 进行通信协作。 **restfull 风格**:数据的增删改查,使用http的不同方式。数据传输用json。 查询 GET ip:port/user/1 新增 POST ip:port/user json{username:itlils,age:18} 修改 PUT ip:port/user/1 json{username:itlils,age:19} 删除 DELETE ip:port/user/1 ③由于有了轻量级的通信协作基础,所以这些微服务可以使用不同的语言来编写。大厂,各种语言混用。 cloud官网: https://spring.io/ ![image-20220329154419410](https://www.ydlclass.com/doc21xnv/assets/image-20220329154419410.35fc4235.png) ![image-20220329152020956](https://www.ydlclass.com/doc21xnv/assets/image-20220329152020956.6168854b.png) #### 2、 现在大型互联网公司,都在使用微服务架构: 京东的促销节架构:618 ![image-20220329152531517](https://www.ydlclass.com/doc21xnv/assets/image-20220329152531517.e9aa4638.png) 阿里的架构: ![image-20220329152552588](https://www.ydlclass.com/doc21xnv/assets/image-20220329152552588.ea1b80bc.png) 京东物流的架构: ![image-20220329152611053](https://www.ydlclass.com/doc21xnv/assets/image-20220329152611053.1bdac904.png) #### 3、 springcloud组件: ![image-20220329152758773](https://www.ydlclass.com/doc21xnv/assets/image-20220329152758773.9e65bcd2.png) ![image-20220329153147800](https://www.ydlclass.com/doc21xnv/assets/image-20220329153147800.c1b6698c.png) ## 第二章 走进springcloud ### 一、了解springcloud ①Spring Cloud 是**一系列框架**的有序集合。 ②Spring Cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来。 netflix eureka 1.1,alibaba 2.2 ③通过 **Spring Boot** 风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。 ④它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、 断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。 ⑤Spring Cloud项目官方网址:https://spring.io/projects/spring-cloud ⑥Spring Cloud 版本命名方式采用了**伦敦地铁站的名称**,同时根据字母表的顺序来对应版本时间顺序,比如:最早的Release版本:Angel,第二个Release版本:Brixton,然后是Camden、Dalston、Edgware,Finchley,Greenwich,Hoxton。 目前最新的是2021.0.1版本。 ![image-20220329161144299](https://www.ydlclass.com/doc21xnv/assets/image-20220329161144299.73a4c7f1.png) ### 二、 cloud与boot版本对应关系 ![image-20220329164540295](https://www.ydlclass.com/doc21xnv/assets/image-20220329164540295.576a76f5.png) ### 三、 dubbo对比 ![image-20220329161102849](https://www.ydlclass.com/doc21xnv/assets/image-20220329161102849.d44ade71.png) **相同点**:Spring Cloud 与 Dubbo 都是实现微服务有效的工具。 **不同点**: 1Dubbo 只是实现了服务治理,而 Spring Cloud 子项目分别覆盖了微服务架构下的众多部件。 2Dubbo 使用 RPC 通讯协议,Spring Cloud 使用 RESTful 完成通信,Dubbo 效率略高于 Spring Cloud。 **小结** • 微服务就是将项目的各个模块拆分为可独立运行、部署、测试的架构设计风格。 • Spring 公司将其他公司中微服务架构常用的组件整合起来,并使用 SpringBoot 简化其开发、配置。称为 Spring Cloud。 • Spring Cloud 与 Dubbo都是实现微服务有效的工具。Dubbo 性能更好,而 Spring Cloud 功能更全面。Dubbo 已经融入到spingcloudAlibaba这一套 **本课程技术特点**: 两套springcloud. 1. 5年前的项目。springcloud netflix(eureka config hystrix) hoxton。会讲到。 2. 新项目 springcloud alibaba(nacos sentinel)。也会讲到。 ## 第三章 停更/升级/替换 红色不维护。 绿色是alibaba一套,推荐使用。 ![image-20220329171255668](https://www.ydlclass.com/doc21xnv/assets/image-20220329171255668.cddd16e1.png) ## 第四章 微服务架构编码构建 ![image-20220404103923982](https://www.ydlclass.com/doc21xnv/assets/image-20220404103923982.85c56e15.png) ### 一、 搭建 Provider 和 Consumer 服务 #### 1、父工程 spring-cloud-parent ![image-20220404103149867](https://www.ydlclass.com/doc21xnv/assets/image-20220404103149867.f3234ca1.png) 使用utf-8编码 ![image-20220404103334670](https://www.ydlclass.com/doc21xnv/assets/image-20220404103334670.9bfcbddd.png) maven设置 ![image-20220404103417399](https://www.ydlclass.com/doc21xnv/assets/image-20220404103417399.f4982f5b.png) pom.xml ``` 4.0.0 com.ydlclass spring-cloud-parent 1.0.0 org.springframework.boot spring-boot-starter-parent 2.2.11.RELEASE 8 8 UTF-8 UTF-8 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #### 2、提供者 eureka-provider ![image-20220330164728077](https://www.ydlclass.com/doc21xnv/assets/image-20220330164728077.62e821de.png) 搭建springboot工程:步骤把大象装冰箱有几步? 工作中别问同事 ``` 1 pom 导包 2 配置文件 3 主启动类 ``` 1 2 3 pom.xml ``` spring-cloud-parent com.ydlclass 1.0.0 4.0.0 eureka-provider 8 8 org.springframework.boot spring-boot-starter-web ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 application.yml 主启动类 ProviderApp ``` package com.ydlclass.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Goods ``` package com.ydlclass.provider.domain; import java.io.Serializable; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ public class Goods implements Serializable { private int id;//商品id private String title;//商品名 private double price;//价格 private int count;//库存 public Goods(int id, String title, double price, int count) { this.id = id; this.title = title; this.price = price; this.count = count; } @Override public String toString() { return "Goods{" + "id=" + id + ", title='" + title + '\'' + ", price=" + price + ", count=" + count + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 GoodsController ``` package com.ydlclass.provider.controller; import com.ydlclass.provider.domain.Goods; import com.ydlclass.provider.service.GoodsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @RestController @RequestMapping("/goods") public class GoodsController { @Autowired GoodsService goodsService; @GetMapping("findById/{id}") public Goods findById(@PathVariable("id") int id){ Goods goods = goodsService.findById(id); return goods; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 GoodsService ``` package com.ydlclass.provider.service; import com.ydlclass.provider.dao.GoodsDao; import com.ydlclass.provider.domain.Goods; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @Service public class GoodsService { @Autowired GoodsDao goodsDao; public Goods findById(int id){ Goods goods = goodsDao.findById(id); return goods; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 GoodsDao ``` package com.ydlclass.provider.dao; import com.ydlclass.provider.domain.Goods; import org.springframework.stereotype.Repository; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @Repository public class GoodsDao { public Goods findById(int id){ //查数据库 return new Goods(id,"手机",2000,100); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 **测试:** 访问:http://localhost:8000/goods/findById/1 ![image-20220330170647529](https://www.ydlclass.com/doc21xnv/assets/image-20220330170647529.2bf7e1c4.png) #### 3、消费者 eureka-consumer ![image-20220404111105516](https://www.ydlclass.com/doc21xnv/assets/image-20220404111105516.f67f7a56.png) pom.xml ``` spring-cloud-parent com.ydlclass 1.0-SNAPSHOT 4.0.0 eureka-consumer org.springframework.boot spring-boot-starter-web ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Goods ``` package com.ydlclass.consumer.domain; /** * 商品实体类 */ public class Goods { private int id; private String title;//商品标题 private double price;//商品价格 private int count;//商品库存 public Goods() { } public Goods(int id, String title, double price, int count) { this.id = id; this.title = title; this.price = price; this.count = count; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 OrderController ``` package com.ydlclass.consumer.controller; import com.ydlclass.consumer.domain.Goods; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @RestController @RequestMapping("/order") public class OrderController { @GetMapping("/add/{id}") public Goods add(@PathVariable("id") Integer id){ //业务逻辑 //1查询商品 //2减库存 //3支付 //4物流 return new Goods(); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 application.yml **测试:** http://localhost:9000/order/add/2 ![image-20220404112103333](https://www.ydlclass.com/doc21xnv/assets/image-20220404112103333.e5ed7fec.png) ### 二、使用 RestTemplate 完成远程调用 - Spring提供的一种简单便捷的模板类,用于在 java 代码里访问 restful 服务。 - 其功能与 HttpClient 类似,但是 RestTemplate 实现更优雅,使用更方便。 **consumer工程中** RestTemplateConfig ``` package com.ydlclass.consumer.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 OrderController ``` package com.ydlclass.consumer.controller; import com.ydlclass.consumer.domain.Goods; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * 服务的调用方 */ @RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; @GetMapping("/goods/{id}") public Goods findGoodsById(@PathVariable("id") int id){ System.out.println("findGoodsById..."+id); /* //远程调用Goods服务中的findOne接口 使用RestTemplate 1. 定义Bean restTemplate 2. 注入Bean 3. 调用方法 */ String url = "http://localhost:8000/goods/findOne/"+id; // 3. 调用方法 Goods goods = restTemplate.getForObject(url, Goods.class); return goods; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 **测试:** ## 第五章 Eureka服务注册与发现 ### 一、Eureka **概念:** • Eureka 是 Netflix 公司开源的一个服务注册与发现的组件 。 • Eureka 和其他 Netflix 公司的服务组件(例如负载均衡、熔断器、网关等) 一起,被 Spring Cloud 社区整合为Spring-Cloud-Netflix 模块。 • Eureka 包含两个组件:Eureka Server (注册中心) 和 Eureka Client (服务提供者、服务消费者)。 **操作:** ![image-20200606113829765](https://www.ydlclass.com/doc21xnv/assets/image-20200606113829765-1648628667299.b0fb85d7.png) ### 二、 搭建 Eureka Server 服务 (1)创建 eureka-server 模块 (2) 引入 SpringCloud 和 euraka-server 相关依赖 (3)完成 Eureka Server 相关配置 (4)启动该模块 **父工程 pom** ``` 4.0.0 com.ydlclass spring-cloud-parent pom 1.0-SNAPSHOT eureka-provider eureka-consumer eureka-server org.springframework.boot spring-boot-starter-parent 2.1.0.RELEASE UTF-8 UTF-8 1.8 Greenwich.RELEASE org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 **eureka-server工程** pom ``` spring-cloud-parent com.ydlclass 1.0-SNAPSHOT 4.0.0 eureka-server org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-server ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 EurekaApp ``` package com.ydlclass.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication // 启用EurekaServer @EnableEurekaServer public class EurekaApp { public static void main(String[] args) { SpringApplication.run(EurekaApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 application.yml ``` server: port: 8761 # eureka 配置 # eureka 一共有4部分 配置 # 1. dashboard:eureka的web控制台配置 # 2. server:eureka的服务端配置 # 3. client:eureka的客户端配置 # 4. instance:eureka的实例配置 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要 fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \*\*测试: \*\* 访问 localhost:8761 ![image-20200611013357920](https://www.ydlclass.com/doc21xnv/assets/image-20200611013357920.c3a39136.png) ### 三、 改造 Provider 和 Consumer 称为 Eureka Client ① 引 eureka-client 相关依赖 ② 完成 eureka client 相关配置 ③ 启动 测试 **Provider工程** pom ``` org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` 1 2 3 4 5 ProviderApp application.yml ``` server: port: 8001 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 spring: application: name: eureka-provider # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 **Consumer** pom ``` org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` 1 2 3 4 5 ConsumerApp ``` package com.ydlclass.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableDiscoveryClient // 激活DiscoveryClient @EnableEurekaClient @SpringBootApplication public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 application.yml ``` server: port: 9000 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 spring: application: name: eureka-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径 ``` 1 2 3 4 5 6 7 8 9 10 11 12 ### 四、 Consumer 服务 通过从 Eureka Server 中抓取 Provider 地址,完成远程调用 **Consumer** OrderController ``` package com.ydlclass.consumer.controller; import com.ydlclass.consumer.domain.Goods; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @RestController @RequestMapping("/order") public class OrderController { @Autowired RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @GetMapping("/add/{id}") public Goods add(@PathVariable("id") Integer id){ //业务逻辑 //1查询商品 //2减库存 //3支付 //4物流 //直接调用 //String url="http://localhost:8000/goods/findById/"+id; //Goods goods = restTemplate.getForObject(url, Goods.class); //服务发现 List instances = discoveryClient.getInstances("EUREKA-PROVIDER"); if(instances==null||instances.size()<=0){ return null; } //通过某个策略拿到一个实例 ServiceInstance serviceInstance = instances.get(0); String host = serviceInstance.getHost(); int port = serviceInstance.getPort(); System.out.println(host); System.out.println(port); String url="http://"+host+":"+port+"/goods/findById/"+id; Goods goods = restTemplate.getForObject(url, Goods.class); return goods; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 ### 五、 Euraka配置详解 Eureka包含四个部分的配置 - instance:当前Eureka Instance实例信息配置 - client:Eureka Client客户端特性配置 - server:Eureka Server注册中心特性配置 - dashboard:Eureka Server注册中心仪表盘配置 #### 1、实例信息配置 ``` eureka: instance: hostname: localhost # 主机名 prefer-ip-address: # 是否将自己的ip注册到eureka中,默认false 注册 主机名 ip-address: # 设置当前实例ip instance-id: # 修改instance-id显示 lease-renewal-interval-in-seconds: 30 # 每一次eureka client 向 eureka server发送心跳的时间间隔 lease-expiration-duration-in-seconds: 90 # 如果90秒内eureka server没有收到eureka client的心跳包,则剔除该服务 ``` 1 2 3 4 5 6 7 8 Eureka Instance的配置信息全部保存在org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean配置类里,实际上它是com.netflix.appinfo.EurekaInstanceConfig的实现类,替代了netflix的com.netflix.appinfo.CloudInstanceConfig的默认实现。 Eureka Instance的配置信息全部以eureka.instance.xxx的格式配置。 **配置列表** - appname = unknown 应用名,首先获取spring.application.name的值,如果取值为空,则取默认unknown。 - appGroupName = null 应用组名 - instanceEnabledOnit = false 实例注册到Eureka上是,是否立刻开启通讯。有时候应用在准备好服务之前需要一些预处理。 - nonSecurePort = 80 非安全的端口 - securePort = 443 安全端口 - nonSecurePortEnabled = true 是否开启非安全端口通讯 - securePortEnabled = false 是否开启安全端口通讯 - leaseRenewalIntervalInSeconds = 30 实例续约间隔时间 - leaseExpirationDurationInSeconds = 90 实例超时时间,表示最大leaseExpirationDurationInSeconds秒后没有续约,Server就认为他不可用了,随之就会将其剔除。 - virtualHostName = unknown 虚拟主机名,首先获取spring.application.name的值,如果取值为空,则取默认unknown。 - instanceId 注册到eureka上的唯一实例ID,不能与相同appname的其他实例重复。 - secureVirtualHostName = unknown 安全虚拟主机名,首先获取spring.application.name的值,如果取值为空,则取默认unknown。 - metadataMap = new HashMap(); 实例元数据,可以供其他实例使用。比如spring-boot-admin在监控时,获取实例的上下文和端口。 - dataCenterInfo = new MyDataCenterInfo(DataCenterInfo.Name.MyOwn); 实例部署的数据中心。如AWS、MyOwn。 - ipAddress=null 实例的IP地址 - statusPageUrlPath = "/actuator/info" 实例状态页相对url - statusPageUrl = null 实例状态页绝对URL - homePageUrlPath = "/" 实例主页相对URL - homePageUrl = null 实例主页绝对URL - healthCheckUrlUrlPath = "/actuator/health" 实例健康检查相对URL - healthCheckUrl = null 实例健康检查绝对URL - secureHealthCheckUrl = null 实例安全的健康检查绝对URL - namespace = "eureka" 配置属性的命名空间(Spring Cloud中被忽略) - hostname = null 主机名,不配置的时候讲根据操作系统的主机名来获取 - preferIpAddress = false 是否优先使用IP地址作为主机名的标识 #### 2、客户端特性配置 ``` eureka: client: service-url: # eureka服务端地址,将来客户端使用该地址和eureka进行通信 defaultZone: register-with-eureka: # 是否将自己的路径 注册到eureka上。 fetch-registry: # 是否需要从eureka中抓取数据。 ``` 1 2 3 4 5 6 7 Eureka Client客户端特性配置是对作为Eureka客户端的特性配置,包括Eureka注册中心,本身也是一个Eureka Client。 Eureka Client特性配置全部在org.springframework.cloud.netflix.eureka.EurekaClientConfigBean中,实际上它是com.netflix.discovery.EurekaClientConfig的实现类,替代了netxflix的默认实现。 Eureka Client客户端特性配置全部以eureka.client.xxx的格式配置。 **配置列表** - enabled=true 是否启用Eureka client。 - registryFetchIntervalSeconds=30 定时从Eureka Server拉取服务注册信息的间隔时间 - instanceInfoReplicationIntervalSeconds=30 定时将实例信息(如果变化了)复制到Eureka Server的间隔时间。(InstanceInfoReplicator线程) - initialInstanceInfoReplicationIntervalSeconds=40 首次将实例信息复制到Eureka Server的延迟时间。(InstanceInfoReplicator线程) - eurekaServiceUrlPollIntervalSeconds=300 拉取Eureka Server地址的间隔时间(Eureka Server有可能增减) - proxyPort=null Eureka Server的代理端口 - proxyHost=null Eureka Server的代理主机名 - proxyUserName=null Eureka Server的代理用户名 - proxyPassword=null Eureka Server的代理密码 - eurekaServerReadTimeoutSeconds=8 从Eureka Server读取信息的超时时间 - eurekaServerConnectTimeoutSeconds=5 连接Eureka Server的超时时间 - backupRegistryImpl=null Eureka Client第一次启动时获取服务注册信息的调用的回溯实现。Eureka Client启动时首次会检查有没有BackupRegistry的实现类,如果有实现类,则优先从这个实现类里获取服务注册信息。 - eurekaServerTotalConnections=200 Eureka client连接Eureka Server的链接总数 - eurekaServerTotalConnectionsPerHost=50 Eureka client连接单台Eureka Server的链接总数 - eurekaServerURLContext=null 当Eureka server的列表在DNS中时,Eureka Server的上下文路径。如http://xxxx/eureka。 - eurekaServerPort=null 当Eureka server的列表在DNS中时,Eureka Server的端口。 - eurekaServerDNSName=null 当Eureka server的列表在DNS中时,且要通过DNSName获取Eureka Server列表时,DNS名字。 - region="us-east-1" 实例所属区域。 - eurekaConnectionIdleTimeoutSeconds = 30 Eureka Client和Eureka Server之间的Http连接的空闲超时时间。 - heartbeatExecutorThreadPoolSize=2 心跳(续约)执行器线程池大小。 - heartbeatExecutorExponentialBackOffBound=10 心跳执行器在续约过程中超时后的再次执行续约的最大延迟倍数。默认最大延迟时间=10 \* eureka.instance.leaseRenewalIntervalInSeconds - cacheRefreshExecutorThreadPoolSize=2 cacheRefreshExecutord的线程池大小(获取注册信息) - cacheRefreshExecutorExponentialBackOffBound=10 cacheRefreshExecutord的再次执行的最大延迟倍数。默认最大延迟时间=10 \*eureka.client.registryFetchIntervalSeconds - serviceUrl= new HashMap();serviceUrl.put(DEFAULT\_ZONE, DEFAULT\_URL); Eureka Server的分区地址。默认添加了一个defualtZone。也就是最常用的配置eureka.client.service-url.defaultZone=xxx - registerWithEureka=true 是否注册到Eureka Server。 - preferSameZoneEureka=true 是否使用相同Zone下的Eureka server。 - logDeltaDiff=false 是否记录Eureka Server和Eureka Client之间注册信息的差异 - disableDelta=false 是否开启增量同步注册信息。 - fetchRemoteRegionsRegistry=null 获取注册服务的远程地区,以逗号隔开。 - availabilityZones=new HashMap() 可用分区列表。用逗号隔开。 - filterOnlyUpInstances = true 是否只拉取UP状态的实例。 - fetchRegistry=true 是否拉取注册信息。 - shouldUnregisterOnShutdown = true 是否在停止服务的时候向Eureka Server发起Cancel指令。 - shouldEnforceRegistrationAtInit = false 是否在初始化过程中注册服务。 #### 3、注册中心端配置 ``` eureka: server: #是否开启自我保护机制,默认true enable-self-preservation: eviction-interval-timer-in-ms: 120 2月#清理间隔(单位毫秒,默认是60*1000) instance: lease-renewal-interval-in-seconds: 30 # 每一次eureka client 向 eureka server发送心跳的时间间隔 lease-expiration-duration-in-seconds: 90 # 如果90秒内eureka server没有收到eureka client的心跳包,则剔除该服务 ``` 1 2 3 4 5 6 7 Eureka Server注册中心端的配置是对注册中心的特性配置。Eureka Server的配置全部在org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean里,实际上它是com.netflix.eureka.EurekaServerConfig的实现类,替代了netflix的默认实现。 Eureka Server的配置全部以eureka.server.xxx的格式进行配置。 **配置列表** - enableSelfPreservation=true 是否开启自我保护 - renewalPercentThreshold = 0.85 自我保护续约百分比阀值因子。如果实际续约数小于续约数阀值,则开启自我保护 - renewalThresholdUpdateIntervalMs = 15 \* 60 \* 1000 续约数阀值更新频率。 - peerEurekaNodesUpdateIntervalMs = 10 \* 60 \* 1000 Eureka Server节点更新频率。 - enableReplicatedRequestCompression = false 是否启用复制请求压缩。 - waitTimeInMsWhenSyncEmpty=5 \* 60 \* 1000 当从其他节点同步实例信息为空时等待的时间。 - peerNodeConnectTimeoutMs=200 节点间连接的超时时间。 - peerNodeReadTimeoutMs=200 节点间读取信息的超时时间。 - peerNodeTotalConnections=1000 节点间连接总数。 - peerNodeTotalConnectionsPerHost = 500; 单个节点间连接总数。 - peerNodeConnectionIdleTimeoutSeconds = 30; 节点间连接空闲超时时间。 - retentionTimeInMSInDeltaQueue = 3 \* MINUTES; 增量队列的缓存时间。 - deltaRetentionTimerIntervalInMs = 30 \* 1000; 清理增量队列中过期的频率。 - evictionIntervalTimerInMs = 60 \* 1000; 剔除任务频率。 - responseCacheAutoExpirationInSeconds = 180; 注册列表缓存超时时间(当注册列表没有变化时) - responseCacheUpdateIntervalMs = 30 \* 1000; 注册列表缓存更新频率。 - useReadOnlyResponseCache = true; 是否开启注册列表的二级缓存。 - disableDelta=false。 是否为client提供增量信息。 - maxThreadsForStatusReplication = 1; 状态同步的最大线程数。 - maxElementsInStatusReplicationPool = 10000; 状态同步队列的最大容量。 - syncWhenTimestampDiffers = true; 当时间差异时是否同步。 - registrySyncRetries = 0; 注册信息同步重试次数。 - registrySyncRetryWaitMs = 30 \* 1000; 注册信息同步重试期间的时间间隔。 - maxElementsInPeerReplicationPool = 10000; 节点间同步事件的最大容量。 - minThreadsForPeerReplication = 5; 节点间同步的最小线程数。 - maxThreadsForPeerReplication = 20; 节点间同步的最大线程数。 - maxTimeForReplication = 30000; 节点间同步的最大时间,单位为毫秒。 - disableDeltaForRemoteRegions = false; 是否启用远程区域增量。 - remoteRegionConnectTimeoutMs = 1000; 远程区域连接超时时间。 - remoteRegionReadTimeoutMs = 1000; 远程区域读取超时时间。 - remoteRegionTotalConnections = 1000; 远程区域最大连接数 - remoteRegionTotalConnectionsPerHost = 500; 远程区域单机连接数 - remoteRegionConnectionIdleTimeoutSeconds = 30; 远程区域连接空闲超时时间。 - remoteRegionRegistryFetchInterval = 30; 远程区域注册信息拉取频率。 - remoteRegionFetchThreadPoolSize = 20; 远程区域注册信息线程数。 #### 4、仪表盘配置 ``` eureka: dashboard: enabled: true # 是否启用eureka web控制台 path: / # 设置eureka web控制台默认访问路径 ``` 1 2 3 4 注册中心仪表盘的配置主要是控制注册中心的可视化展示。以eureka.dashboard.xxx的格式配置。 - path="/" 仪表盘访问路径 - enabled=true 是否启用仪表盘 **改造 provider** ``` server: port: 8001 eureka: instance: hostname: localhost # 主机名 prefer-ip-address: true # 将当前实例的ip注册到eureka server 中。默认是false 注册主机名 ip-address: 127.0.0.1 # 设置当前实例的ip instance-id: ${eureka.instance.ip-address}:${spring.application.name}:${server.port} # 设置web控制台显示的 实例id lease-renewal-interval-in-seconds: 3 # 每隔3 秒发一次心跳包 lease-expiration-duration-in-seconds: 9 # 如果9秒没有发心跳包,服务器呀,你把我干掉吧~ client: service-url: defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 spring: application: name: eureka-provider # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 **consumer** ``` server: port: 9000 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 spring: application: name: eureka-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 **server** ``` server: port: 8761 # eureka 配置 # eureka 一共有4部分 配置 # 1. dashboard:eureka的web控制台配置 # 2. server:eureka的服务端配置 # 3. client:eureka的客户端配置 # 4. instance:eureka的实例配置 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要 fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要 server: enable-self-preservation: false # 关闭自我保护机制 eviction-interval-timer-in-ms: 3000 # 检查服务的时间间隔 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ### 六、高可用 ![image-20220406114821846](https://www.ydlclass.com/doc21xnv/assets/image-20220406114821846.5d110f1f.png) 1. 准备两个Eureka Server 2. 分别进行配置,**相互注册** 3. Eureka Client 分别注册到这两个 Eureka Server中 **创建eureka-server1** ``` server: port: 8761 eureka: instance: hostname: eureka-server1 # 主机名 client: service-url: defaultZone: http://eureka-server2:8762/eureka register-with-eureka: true # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要 fetch-registry: true # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要 spring: application: name: eureka-server-ha ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 **创建eureka-server2** ``` server: port: 8762 eureka: instance: hostname: eureka-server2 # 主机名 client: service-url: defaultZone: http://eureka-server1:8761/eureka register-with-eureka: true # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要 fetch-registry: true # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要 spring: application: name: eureka-server-ha ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 **修改**:C:\\Windows\\System32\\drivers\\etc\\hosts 127.0.0.1 eureka-server1 127.0.0.1 eureka-server2 ![image-20220406144138369](https://www.ydlclass.com/doc21xnv/assets/image-20220406144138369.3a3af0a9.png) 测试: provider ``` server: port: 8001 eureka: instance: hostname: localhost # 主机名 prefer-ip-address: true # 将当前实例的ip注册到eureka server 中。默认是false 注册主机名 ip-address: 127.0.0.1 # 设置当前实例的ip instance-id: ${eureka.instance.ip-address}:${spring.application.name}:${server.port} # 设置web控制台显示的 实例id lease-renewal-interval-in-seconds: 3 # 每隔3 秒发一次心跳包 lease-expiration-duration-in-seconds: 9 # 如果9秒没有发心跳包,服务器呀,你把我干掉吧~ client: service-url: defaultZone: http://eureka-server1:8761/eureka,http://eureka-server2:8762/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 spring: application: name: eureka-provider # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 consumer ``` server: port: 9000 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://eureka-server1:8761/eureka,http://eureka-server2:8762/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 spring: application: name: eureka-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 访问:http://localhost:9000/order/add/4 ![image-20220406144153992](https://www.ydlclass.com/doc21xnv/assets/image-20220406144153992.acdf6713.png) 高可用测试:停掉一个eureka,依然可以访问consumer。 eureka不更新了,所以淘汰了。 ## 第六章 Zookeeper服务注册与发现 ![image-20220406194741473](https://www.ydlclass.com/doc21xnv/assets/image-20220406194741473.cc3720e9.png) 有的老项目以前是dubbo,升级到微服务,使用zookeeper做注册中心。 zookeeper是一个分布式协调工具,可以实现注册中心功能。dubbo,大数据组件hadoop,hive,kafka。 **实质**:注册中心换成zk 1下载:https://zookeeper.apache.org/ ![image-20220406152809933](https://www.ydlclass.com/doc21xnv/assets/image-20220406152809933.e8b336fb.png) zoo.cfg ``` # The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=F:/apache-zookeeper-3.5.6-bin/data # the port at which the clients will connect clientPort=2181 # the maximum number of client connections. # increase this if you need to handle more clients #maxClientCnxns=60 # # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. # # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance # # The number of snapshots to retain in dataDir #autopurge.snapRetainCount=3 # Purge task interval in hours # Set to "0" to disable auto purge feature #autopurge.purgeInterval=1 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 启动 bin目录下 ![image-20220406153622431](https://www.ydlclass.com/doc21xnv/assets/image-20220406153622431.66776d12.png) 2zookeeper-provider pom ``` zookeeper-provider org.springframework.cloud spring-cloud-starter-zookeeper-discovery org.apache.zookeeper zookeeper org.apache.zookeeper zookeeper 3.4.9 org.slf4j slf4j-log4j12 org.springframework.boot spring-boot-starter-web ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 yml ``` server: port: 8004 spring: application: name: zookeeper-provider cloud: zookeeper: connect-string: 127.0.0.1:2181 # zk地址 ``` 1 2 3 4 5 6 7 8 9 主启动类 ``` package com.ydlclass.zk; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @SpringBootApplication @EnableDiscoveryClient //开启发现客户端 public class ProviderApp { public static void main(String[] args) { SpringApplication.run(ProviderApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 业务逻辑代码直接复制粘贴过来 ![image-20220406172259585](https://www.ydlclass.com/doc21xnv/assets/image-20220406172259585.d9bdd516.png) 3zookeeper-consumer pom ``` zookeeper-consumer 8 8 org.springframework.cloud spring-cloud-starter-zookeeper-discovery org.apache.zookeeper zookeeper org.apache.zookeeper zookeeper 3.5.6 org.slf4j slf4j-log4j12 org.springframework.boot spring-boot-starter-web ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 yml ``` server: port: 8005 spring: application: name: zookeeper-consumer cloud: zookeeper: connect-string: 127.0.0.1:2181 # zk地址 ``` 1 2 3 4 5 6 7 8 9 启动类 ``` package com.ydlclass.zk; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @SpringBootApplication @EnableDiscoveryClient public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 业务逻辑代码直接复制粘贴过来 ![image-20220406185656845](https://www.ydlclass.com/doc21xnv/assets/image-20220406185656845.d3703bb1.png) controller只改一个 ``` List instances = discoveryClient.getInstances("zookeeper-provider"); ``` 1 测试:http://localhost:8005/order/add/5 ![image-20220407144912417](https://www.ydlclass.com/doc21xnv/assets/image-20220407144912417.fd30484b.png) ## 第七章 Consul服务注册与发现 ![image-20220407161804433](https://www.ydlclass.com/doc21xnv/assets/image-20220407161804433.7424151c.png) ### 一、 是什么: - Consul 是由 HashiCorp 基于 GoLanguage 语言开发的,支持多数据中心,分布式高可用的服务发布和注册服务软件。 - 用于实现分布式系统的服务发现与配置。 - 使用起来也较 为简单。具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署 。 - Consul官网:https://www.consul.io/ Consul中文文档:https://www.springcloud.cc/spring-cloud-consul.html ### 二、 怎么用: #### 1、 准备 ![image-20220407161630689](https://www.ydlclass.com/doc21xnv/assets/image-20220407161630689.ac7c0b2d.png) ![image-20220407161746354](https://www.ydlclass.com/doc21xnv/assets/image-20220407161746354.b746e58c.png) #### 2、 搭建 consul-provider pom ``` org.springframework.cloud spring-cloud-starter-consul-discovery org.springframework.boot spring-boot-starter-web ``` 1 2 3 4 5 6 7 8 9 10 11 12 yml ``` server: port: 8006 spring: application: name: consul-provider cloud: consul: host: 127.0.0.1 port: 8500 discovery: service-name: ${spring.application.name} ``` 1 2 3 4 5 6 7 8 9 10 11 12 启动类 ``` package com.ydlclass.consul; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @SpringBootApplication @EnableDiscoveryClient public class ProviderApp { public static void main(String[] args) { SpringApplication.run(ProviderApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 业务逻辑复制 ![image-20220407163424691](https://www.ydlclass.com/doc21xnv/assets/image-20220407163424691.9008997e.png) 启动 ![image-20220407162423209](https://www.ydlclass.com/doc21xnv/assets/image-20220407162423209.689703ea.png) #### 3、搭建 consul-consumer pom ``` org.springframework.cloud spring-cloud-starter-consul-discovery org.springframework.boot spring-boot-starter-web ``` 1 2 3 4 5 6 7 8 9 10 11 yml ``` server: port: 8007 spring: application: name: consul-consumer cloud: consul: host: 127.0.0.1 port: 8500 discovery: service-name: ${spring.application.name} ``` 1 2 3 4 5 6 7 8 9 10 11 12 启动类 ``` package com.ydlclass.consul; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @SpringBootApplication @EnableDiscoveryClient public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 业务逻辑复制 ![image-20220407164001822](https://www.ydlclass.com/doc21xnv/assets/image-20220407164001822.47a4a47b.png) controller只改一句话 ``` List instances = discoveryClient.getInstances("consul-provider"); ``` 1 启动 ![image-20220407164133404](https://www.ydlclass.com/doc21xnv/assets/image-20220407164133404.c379b922.png) 最终测试调用成功即可:http://localhost:8007/order/add/9 ![image-20220407164211124](data:image/png;base64,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) ### 三、 三个注册中心的异同 | 组件 | 语言 | cap | 健康检查 | 暴露接口 | cloud集成 | | --------- | ---- | ---- | -------- | -------- | --------- | | eureka | java | ap | 支持 | http | 已经集成 | | zookeeper | java | cp | 支持 | tcp | 已经集成 | | consul | go | cp | 支持 | http | 已经集成 | cap - c:consustency 强一致性 - a:avalibility 可用性 - p:partition tolerance 分区容忍性 ## 第八章 Ribbon负载均衡服务调用 ### 一、是什么 Netflix公司推出的http和TCP的客户端负载均衡工具。 ribbon: - 简化远程调用代码 - 内置很多负载均衡算法 #### 1、服务端负载均衡 负载均衡算法在服务端,服务端维护服务列表。 ![image-20220407173053676](https://www.ydlclass.com/doc21xnv/assets/image-20220407173053676.2349faf0.png) #### 2、客户端负载均衡 - 负载均衡算法在客户端 - 客户端维护服务列表 ![image-20220407173409352](https://www.ydlclass.com/doc21xnv/assets/image-20220407173409352.2bb522c4.png) ### 二、如何使用 1.新版的eureka依赖以及集成了Ribbon依赖,所以可以不引用 ``` org.springframework.cloud spring-cloud-starter-netflix-ribbon ``` 1 2 3 4 5 ![image-20220408112040713](https://www.ydlclass.com/doc21xnv/assets/image-20220408112040713.49705355.png) 2.声明restTemplate时@LoadBalanced 3.restTemplate请求远程服务时,ip端口替换为服务名 ``` String url="http://EUREKA-PROVIDER/goods/findById/"+id; Goods goods = restTemplate.getForObject(url, Goods.class); ``` 1 2 测试: 1.启动2个provider controller ``` goods.setTitle(goods.getTitle()+"|端口号:"+port); ``` 1 idea设置 能启动两份 provider ![image-20220408112658153](https://www.ydlclass.com/doc21xnv/assets/image-20220408112658153.df840762.png) ![image-20220408112814966](https://www.ydlclass.com/doc21xnv/assets/image-20220408112814966.37a23e92.png) 2.多次访问consumer ![image-20220408113212684](https://www.ydlclass.com/doc21xnv/assets/image-20220408113212684.cc4cce36.png) ![image-20220408113134662](https://www.ydlclass.com/doc21xnv/assets/image-20220408113134662.3d63cf60.png) 多次刷新,发现:ribbon客户端,默认使用轮询算法,经行负载均衡调用。 ### 三、ribbon 负载均衡策略 | **内置负载均衡规则类** | **规则描述** | | ------------------------- | ------------------------------------------------------------ | | RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 | | AvailabilityFilteringRule | 对以下两种服务器进行忽略:(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。注意:可以通过修改配置loadbalancer.``.connectionFailureCountThreshold来修改连接失败多少次之后被设置为短路状态。默认是3次。(2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上线,可以由客户端的``.``.ActiveConnectionsLimit属性进行配置。 | | WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 | | ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。 | | BestAvailableRule | 忽略哪些短路的服务器,并选择并发数较低的服务器。 | | RandomRule | 随机选择一个可用的服务器。 | | Retry | 重试机制的选择逻辑 | ### 四、 设置ribbon 负载均衡策略 #### 1、代码 consumer工程 1.MyRule 返回想要的规则即可 ``` package com.ydlclass.consumer.config; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * creste by ydlclass.itcast */ @Configuration public class MyRule { @Bean public IRule rule(){ return new RandomRule(); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 2.启动类 ``` @RibbonClient(name ="EUREKA-PROVIDER",configuration = MyRule.class) ``` 1 总结: 1.irule的具体实现类,看到他带的几个策略的写法。 2.仿照策略的写法,自己写策略。 3.调用不同的其他微服务时,可以采用不同的策略。 #### 2、配置 consumer工程 application.yml ``` EUREKA-PROVIDER: #远程服务名 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #策略 ``` 1 2 3 作用:方便运维修改,重启。随时切换策略。 ## 第九章、OpenFeign服务接口调用 ### 一、概述 - Feign 是一个声明式的 REST 客户端,它用了**基于接口的注解方式**,很方便实现客户端像调用本地接口方法一样,进行远程调用。 - Feign 最初由 Netflix 公司提供,但不支持SpringMVC注解,后由 SpringCloud 对其封装,支持了SpringMVC注解,让使用者更易于接受。 - https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeign ### 二、快速入门 a.在消费端引入 open-feign 依赖: ``` org.springframework.cloud spring-cloud-starter-openfeign ``` 1 2 3 4 b.编写Feign调用接口。**复制粘贴被调方的conreoller方法,加上类路径。** GoodsFeignClient: ``` package com.ydlclass.consumer.feign; import com.ydlclass.consumer.domain.Goods; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @FeignClient("EUREKA-PROVIDER") public interface GoodsFeign { @GetMapping("/goods/findById/{id}") public Goods findById(@PathVariable("id") Integer id); } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 c.在启动类 添加 @EnableFeignClients 注解,开启Feign功能 d.测试调用 OrderController ``` package com.ydlclass.consumer.controller; import com.ydlclass.consumer.domain.Goods; import com.ydlclass.consumer.feign.GoodsFeign; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @RestController @RequestMapping("/order") public class OrderController { @Autowired RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @Autowired GoodsFeign goodsFeign; @GetMapping("/add/{id}") public Goods add(@PathVariable("id") Integer id){ //业务逻辑 //1查询商品 //2减库存 //3支付 //4物流 //直接调用 //String url="http://localhost:8000/goods/findById/"+id; //Goods goods = restTemplate.getForObject(url, Goods.class); // //1服务发现 // List instances = discoveryClient.getInstances("EUREKA-PROVIDER"); // if(instances==null||instances.size()<=0){ // return null; // } // //2通过某个策略拿到一个实例 算法 // ServiceInstance serviceInstance = instances.get(0); // String host = serviceInstance.getHost(); // int port = serviceInstance.getPort(); // System.out.println(host); // System.out.println(port); // // String url="http://"+host+":"+port+"/goods/findById/"+id; // Goods goods = restTemplate.getForObject(url, Goods.class); //ribbon // String url="http://EUREKA-PROVIDER/goods/findById/"+id; // Goods goods = restTemplate.getForObject(url, Goods.class); //feign调用 Goods goods = goodsFeign.findById(id); return goods; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 ### 三、其他设置 #### 1、超时设置 • Feign 底层依赖于 Ribbon 实现负载均衡和远程调用 ![image-20220408171431530](https://www.ydlclass.com/doc21xnv/assets/image-20220408171431530.17dcc892.png) • Ribbon默认1秒超时。 • 超时配置: yml中 ``` # 设置Ribbon的超时时间 ribbon: ConnectTimeout: 1000 # 连接超时时间 默认1s ReadTimeout: 3000 # 逻辑处理的超时时间 默认1s ``` 1 2 3 4 测试: 1.连接超时,provider都停掉 ![image-20220408172036937](https://www.ydlclass.com/doc21xnv/assets/image-20220408172036937.989a933a.png) 2.逻辑处理的超时时间 provider ``` @GetMapping("/findById/{id}") public Goods findById(@PathVariable("id") Integer id){ Goods goods = goodsService.findById(id); goods.setTitle(goods.getTitle()+"|端口号:"+port); //模拟业务逻辑比较繁忙 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return goods; } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 ![image-20220408172302166](https://www.ydlclass.com/doc21xnv/assets/image-20220408172302166.9b28bb4d.png) #### 2、日志记录 1.Feign 只能记录 debug 级别的日志信息。 ``` # 设置当前的日志级别 debug,feign只支持记录debug级别的日志 logging: level: com.ydlclass: debug ``` 1 2 3 4 • 定义Feign日志级别Bean FeignLogConfig ``` package com.ydlclass.consumer.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignLogConfig { /* NONE,不记录 BASIC,记录基本的请求行,响应状态码数据 HEADERS,记录基本的请求行,响应状态码数据,记录响应头信息 FULL;记录完成的请求 响应数据 */ @Bean public Logger.Level level(){ return Logger.Level.FULL; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 3. 启用该Bean: GoodsFeignClient ``` @FeignClient(value = "FEIGN-PROVIDER",configuration = FeignLogConfig.class) ``` 1 ## ![image-20220408182551922](https://www.ydlclass.com/doc21xnv/assets/image-20220408182551922.f2ea6561.png) 设置为headers ![image-20220408182713438](https://www.ydlclass.com/doc21xnv/assets/image-20220408182713438.68ea41ae.png) ## 第十章 Hystrix断路器 (豪猪)-保险丝 ### 一、概述 重点:能让服务的调用方,够快的知道被调方挂了!不至于说让用户在等待。 Hystix 是 Netflix 开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩)。 雪崩:一个服务失败,导致整条链路的服务都失败的情形。 ![image-20220411101214886](https://www.ydlclass.com/doc21xnv/assets/image-20220411101214886.e1894fb1.png) ![image-20220411104119551](https://www.ydlclass.com/doc21xnv/assets/image-20220411104119551.5dbb216d.png) ![image-20220411101248131](https://www.ydlclass.com/doc21xnv/assets/image-20220411101248131.23fdbc68.png) ![image-20220411101310179](https://www.ydlclass.com/doc21xnv/assets/image-20220411101310179.039fb050.png) **Hystix 主要功能** ![image-20220411101741096](https://www.ydlclass.com/doc21xnv/assets/image-20220411101741096.ad89764b.png) **隔离** 1. 线程池隔离 没有hystrix,a重试100次,才知道c挂了! ![image-20220411102317061](https://www.ydlclass.com/doc21xnv/assets/image-20220411102317061.a569d3eb.png) 使用了hystrix,更细分线程池,只需要重试40次,让a更快的知道c挂了 ![image-20220411102720425](https://www.ydlclass.com/doc21xnv/assets/image-20220411102720425.31875f98.png) 2. 信号量隔离 没有hystrix,a一个带着认证信息的线程,重试100次,才知道c挂了! ![image-20220411103036778](https://www.ydlclass.com/doc21xnv/assets/image-20220411103036778.e2674a15.png) 使用了hystrix,更细分线程池,一个带着认证信息的线程,只需要重试40次,让a更快的知道c挂了 ![image-20220411103152275](https://www.ydlclass.com/doc21xnv/assets/image-20220411103152275.926f5142.png) **降级**: 服务提供方降级(异常,超时) ![image-20220411104951728](https://www.ydlclass.com/doc21xnv/assets/image-20220411104951728.51506d7b.png) 消费方降级 ![image-20220411112946547](https://www.ydlclass.com/doc21xnv/assets/image-20220411112946547.e3d81fb6.png) **熔断** **限流** 是有限流,但是,项目一般不用。nginx或者网关限流。 ### 二、服务降级 **服务提供方** a、在服务提供方,引入 hystrix 依赖 ``` org.springframework.cloud spring-cloud-starter-netflix-hystrix ``` 1 2 3 4 5 b、方法 ``` /** 定义降级方法 返回特殊对象 * 1方法的返回值要和原方法一致 * 2方法参数和原方法一样 */ public Goods findById_fallback(Integer id){ Goods goods=new Goods(); goods.setGoodId(-1); goods.setTitle("provider提供方降级!"); goods.setPrice(-9.9); goods.setStock(-10); return goods; } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 c、使用 @HystrixCommand 注解配置降级方法 ``` @GetMapping("/findById/{id}") @HystrixCommand(fallbackMethod = "findById_fallback",commandProperties = { //设置Hystrix的超时时间,默认1s @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000") }) public Goods findById(@PathVariable("id") Integer id){ Goods goods = goodsService.findById(id); goods.setTitle(goods.getTitle()+"|端口号:"+port); //模拟出异常 // int a=1/0; //模拟业务逻辑比较繁忙 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return goods; } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 d、在启动类上开启Hystrix功能:@EnableCircuitBreaker 测试:http://localhost:9000/order/add/10 1.出错,服务方降级了 ![image-20220411110206538](https://www.ydlclass.com/doc21xnv/assets/image-20220411110206538.c3351b31.png) 2.3000超时,服务方降级了 ![image-20220411111020806](https://www.ydlclass.com/doc21xnv/assets/image-20220411111020806.407514d5.png) **服务消费方** a、feign 组件已经集成了 hystrix 组件。 b、 ![image-20220411113035040](https://www.ydlclass.com/doc21xnv/assets/image-20220411113035040.f3c891d5.png) c、定义feign 调用接口实现类,复写方法,即 降级方法 GoodsFeignClientFallback ``` package com.ydlclass.consumer.feign; import com.ydlclass.consumer.domain.Goods; import org.springframework.stereotype.Component; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @Component public class GoodsFeignCallback implements GoodsFeign{ @Override public Goods findById(Integer id) { Goods goods=new Goods(); goods.setGoodId(-2); goods.setTitle("调用方降级了!"); goods.setPrice(-5.5); goods.setStock(-5); return goods; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 d、在 @FeignClient 注解中使用 fallback 属性设置降级处理类。 GoodsFeignClient ``` package com.ydlclass.consumer.feign; import com.ydlclass.consumer.config.FeignLogConfig; import com.ydlclass.consumer.domain.Goods; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @FeignClient(value = "EUREKA-PROVIDER",configuration = FeignLogConfig.class,fallback = GoodsFeignCallback.class) public interface GoodsFeign { @GetMapping("/goods/findById/{id}") public Goods findById(@PathVariable("id") Integer id); } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 e、配置开启 ``` # 开启feign对hystrix的支持 feign: hystrix: enabled: true ``` 1 2 3 4 测试:停掉provider http://localhost:9000/order/add/10 ![image-20220411113635280](https://www.ydlclass.com/doc21xnv/assets/image-20220411113635280.1081b217.png) ### 三、熔断 ![image-20220411114750082](https://www.ydlclass.com/doc21xnv/assets/image-20220411114750082.1a48c986.png) 测试: provider ``` package com.ydlclass.proviver.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import com.ydlclass.proviver.domain.Goods; import com.ydlclass.proviver.service.GoodsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @RestController @RequestMapping("/goods") public class GoodsController { @Autowired GoodsService goodsService; @Value("${server.port}") int port; @GetMapping("/findById/{id}") @HystrixCommand(fallbackMethod = "findById_fallback",commandProperties = { //设置Hystrix的超时时间,默认1s @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000") }) public Goods findById(@PathVariable("id") Integer id){ Goods goods = goodsService.findById(id); goods.setTitle(goods.getTitle()+"|端口号:"+port); if(id==1){ //模拟出异常 int a=1/0; } //模拟出异常 // int a=1/0; //模拟业务逻辑比较繁忙 // try { // Thread.sleep(5000); // } catch (InterruptedException e) { // e.printStackTrace(); // } return goods; } /** 定义降级方法 返回特殊对象 * 1方法的返回值要和原方法一致 * 2方法参数和原方法一样 */ public Goods findById_fallback(Integer id){ Goods goods=new Goods(); goods.setGoodId(-1); goods.setTitle("provider提供方降级!"); goods.setPrice(-9.9); goods.setStock(-10); return goods; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 访问两个接口 1. http://localhost:9000/order/add/10 ![image-20220411115310796](https://www.ydlclass.com/doc21xnv/assets/image-20220411115310796.faba6ed0.png) 2. 多次访问 http://localhost:9000/order/add/1 ![image-20220411115332277](https://www.ydlclass.com/doc21xnv/assets/image-20220411115332277.ce2b6c94.png) 3.导致10也不能访问了 ![image-20220411115128870](https://www.ydlclass.com/doc21xnv/assets/image-20220411115128870.15162f17.png) 4.再过一会儿,半开状态 http://localhost:9000/order/add/10 ![image-20220411115423173](https://www.ydlclass.com/doc21xnv/assets/image-20220411115423173.573f7494.png) Hyst rix 熔断机制,用于监控微服务调用情况,当失败的情况达到预定的阈值(5秒失败20次),会打开断路器,拒绝所有请求,直到服务恢复正常为止。 ``` circuitBreaker.sleepWindowInMilliseconds:监控时间 circuitBreaker.requestVolumeThreshold:失败次数 circuitBreaker.errorThresholdPercentage:失败率 ``` 1 2 3 提供者controller中 ``` @HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = { //设置Hystrix的超时时间,默认1s @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000"), //监控时间 默认5000 毫秒 @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "5000"), //失败次数。默认20次 @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "20"), //失败率 默认50% @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50") }) ``` 1 2 3 4 5 6 7 8 9 10 11 ### 四、熔断监控\*\*-运维 ![image-20200612072519498](https://www.ydlclass.com/doc21xnv/assets/image-20200612072519498.077b0382.png) Hystrix 提供了 Hystrix-dashboard 功能,用于实时监控微服务运行状态。 但是Hystrix-dashboard只能监控一个微服务。 Netflix 还提供了 Turbine ,进行聚合监控。 ![image-20200612072621556](https://www.ydlclass.com/doc21xnv/assets/image-20200612072621556.5e1b0aa0.png) **Turbine聚合监控** #### 1、搭建监控模块 **a、 创建监控模块** 创建hystrix-monitor模块,使用Turbine聚合监控多个Hystrix dashboard功能, **b、引入Turbine聚合监控起步依赖** ``` spring-cloud-parent com.ydlclass 1.0.0 4.0.0 hystrix-monitor 8 8 org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard org.springframework.cloud spring-cloud-starter-netflix-turbine org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 **d、修改application.yml** ``` spring: application: name: hystrix-monitor server: port: 8769 turbine: combine-host-port: true # 配置需要监控的服务名称列表 app-config: EUREKA-PROVIDER,EUREKA-CONSUMER cluster-name-expression: "'default'" aggregator: cluster-config: default #instanceUrlSuffix: /actuator/hystrix.stream eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ hystrix: dashboard: proxy-stream-allow-list: "*" ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 **e、创建启动类** ``` package com.ydlclass.monitor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.netflix.turbine.EnableTurbine; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @SpringBootApplication @EnableEurekaClient @EnableTurbine //开启Turbine 很聚合监控功能 @EnableHystrixDashboard //开启Hystrix仪表盘监控功能 public class HystrixMonitorApp { public static void main(String[] args) { SpringApplication.run(HystrixMonitorApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #### 2、修改被监控模块 需要分别修改 hystrix-provider 和 hystrix-consumer 模块: **a、导入依赖:** ``` org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 **b、配置Bean** 此处为了方便,将其配置在启动类中。 ``` @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/actuator/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; } ``` 1 2 3 4 5 6 7 8 9 **c、启动类上添加注解@EnableHystrixDashboard** ``` @EnableDiscoveryClient @EnableEurekaClient @SpringBootApplication @EnableFeignClients @EnableHystrixDashboard // 开启Hystrix仪表盘监控功能 public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class,args); } @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/actuator/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #### 3、启动测试 **a、启动服务:** - eureka-server - hystrix-provider - hystrix-consumer - hystrix-monitor **b、访问:** 在浏览器访问http://localhost:8769/hystrix/ 进入Hystrix Dashboard界面 ![1585421193757](https://www.ydlclass.com/doc21xnv/assets/1585421193757.41f28cbc.png) 界面中输入监控的Url地址 http://localhost:8769/turbine.stream,监控时间间隔2000毫秒和title,如下图 ![image-20220411154048113](https://www.ydlclass.com/doc21xnv/assets/image-20220411154048113.1c256874.png) - 实心圆:它有颜色和大小之分,分别代表实例的监控程度和流量大小。如上图所示,它的健康度从绿色、黄色、橙色、红色递减。通过该实心圆的展示,我们就可以在大量的实例中快速的发现故障实例和高压力实例。 - 曲线:用来记录 2 分钟内流量的相对变化,我们可以通过它来观察到流量的上升和下降趋势。 ## 第十一章、zuul路由网关 zuul核心人员走了两个,zuul2的研发过久,spring公司等不及,自己研发的Gateway网关。 https://github.com/Netflix/zuul/wiki ## 第十二章 Gateway新一代网关 功能:路由+过滤 ### 一、 **概述** https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/ 不使用 存在的问题: ``` 解决缺点:1客户端需要记录不同微服务地址,增加客户端的复杂性 2每个后台微服务都需要认证 3http 发请求,涉及到跨域 4后台新增微服务,不能动态知道地址 ``` 1 2 3 4 ![image-20220411172117753](https://www.ydlclass.com/doc21xnv/assets/image-20220411172117753.e3893d54.png) 使用了网关的话: • 网关旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。 • 在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求。 • 网关就是系统的入口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等 • 在目前的网关解决方案里,有Nginx+ Lua、Netflix Zuul/zuul2 、Spring Cloud Gateway等等 ![image-20220411172632761](https://www.ydlclass.com/doc21xnv/assets/image-20220411172632761.c415bd79.png) ### 二、 **快速入门** (1)搭建网关模块 api-gateway-server (2)引入依赖:starter-gateway ``` gateway-parent com.ydlclass 1.0-SNAPSHOT 4.0.0 api-gateway-server org.springframework.cloud spring-cloud-starter-gateway org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 (3)编写启动类 ApiGatewayApp ``` package com.ydlclass.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class ApiGatewayApp { public static void main(String[] args) { SpringApplication.run(ApiGatewayApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (4)编写配置文件 ``` server: port: 80 spring: application: name: api-gateway-server cloud: # 网关配置 gateway: # 路由配置:转发规则 routes: #集合。 # id: 唯一标识。默认是一个UUID # uri: 转发路径 # predicates: 条件,用于请求网关路径的匹配规则 # filters:配置局部过滤器的 - id: eureka-provider # 静态路由 # uri: http://localhost:8001/ # 动态路由 uri: lb://GATEWAY-PROVIDER predicates: - Path=/goods/** filters: - AddRequestParameter=username,zhangsan - id: eureka-consumer # uri: http://localhost:9000 uri: lb://GATEWAY-CONSUMER predicates: - Path=/order/** # 微服务名称配置 discovery: locator: enabled: true # 设置为true 请求路径前可以添加微服务名称 lower-case-service-id: true # 允许为小写 eureka: client: service-url: defaultZone: http://localhost:8761/eureka ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 (5)启动测试 http://localhost/goods/findById/2 ![image-20220411174704253](https://www.ydlclass.com/doc21xnv/assets/image-20220411174704253.4e93d626.png) ![image-20220411174649892](https://www.ydlclass.com/doc21xnv/assets/image-20220411174649892.b2b39013.png) ### 三、 **静态路由** ``` uri: http://localhost:8000/ ``` 1 ### 四、**动态路由** ![image-20220411180512597](https://www.ydlclass.com/doc21xnv/assets/image-20220411180512597.da56cec1.png) #### 1、引入eureka-client配置 pom ``` org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` 1 2 3 4 yml ``` eureka: client: service-url: defaultZone: http://localhost:8761/eureka ``` 1 2 3 4 #### 2、修改uri属性:uri: lb://服务名称 ``` uri: lb://eureka-provider ``` 1 测试: http://localhost/goods/findById/2 ![image-20220411180644761](https://www.ydlclass.com/doc21xnv/assets/image-20220411180644761.e575ff25.png) ### 五、微服务名称配置\*\* ``` spring: cloud: # 网关配置 gateway: # 微服务名称配置 discovery: locator: enabled: true # 设置为true 请求路径前可以添加微服务名称 lower-case-service-id: true # 允许为小写 ``` 1 2 3 4 5 6 7 8 9 测试:http://localhost/eureka-provider/goods/findById/2 ![image-20220411180853933](https://www.ydlclass.com/doc21xnv/assets/image-20220411180853933.7e0bf46f.png) ### 六、过滤器 (1)两个维度: 内置过滤器 自定义过滤器 局部过滤器 全局过滤器 (2)过滤器种类: 内置局部过滤器 内置全局过滤器 自定义局部过滤器 自定义全局过滤器 ![image-20200612075159655](https://www.ydlclass.com/doc21xnv/assets/image-20200612075159655.4dd53e5a.png) - Gateway 支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。 - Gateway 提供两种过滤器方式:“pre”和“post” ``` pre 过滤器,在转发之前执行,可以做参数校验、权限校验、流量监控、日志输出、协议转换等。 post 过滤器,在响应之前执行,可以做响应内容、响应头的修改,日志的输出,流量监控等。 ``` 1 2 - Gateway 还提供了两种类型过滤器 ``` GatewayFilter:局部过滤器,针对单个路由 GlobalFilter :全局过滤器,针对所有路由 ``` 1 2 内置过滤器 局部过滤器: ``` - id: gateway-provider #uri: http://localhost:8001/ uri: lb://GATEWAY-PROVIDER predicates: - Path=/goods/** filters: - AddResponseHeader=foo, bar ``` 1 2 3 4 5 6 7 ![image-20220411182635931](https://www.ydlclass.com/doc21xnv/assets/image-20220411182635931.65d3b487.png) 内置过滤器 全局过滤器: route同级 ``` default-filters: - AddResponseHeader=yld,itlils ``` 1 2 ![image-20220411182831552](https://www.ydlclass.com/doc21xnv/assets/image-20220411182831552.1af37136.png) ![image-20220411182910608](https://www.ydlclass.com/doc21xnv/assets/image-20220411182910608.586ba685.png) **拓展:** 内置的过滤器工厂 这里简单将Spring Cloud Gateway内置的所有过滤器工厂整理成了一张表格。如下: | 过滤器工厂 | 作用 | 参数 | | --------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | AddRequestHeader | 为原始请求添加Header | Header的名称及值 | | AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 | | AddResponseHeader | 为原始响应添加Header | Header的名称及值 | | DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 | | Hystrix | 为路由引入Hystrix的断路器保护 | `HystrixCommand`的名称 | | FallbackHeaders | 为fallbackUri的请求头中添加具体的异常信息 | Header的名称 | | PrefixPath | 为原始请求路径添加前缀 | 前缀路径 | | PreserveHostHeader | 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host | 无 | | RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus | | RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的url | | RemoveHopByHopHeadersFilter | 为原始请求删除IETF组织规定的一系列Header | 默认就会启用,可以通过配置指定仅删除哪些Header | | RemoveRequestHeader | 为原始请求删除某个Header | Header名称 | | RemoveResponseHeader | 为原始响应删除某个Header | Header名称 | | RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 | | RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正则表达式,重写后的值 | | SaveSession | 在转发请求之前,强制执行`WebSession::save`操作 | 无 | | secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 | | SetPath | 修改原始的请求路径 | 修改后的路径 | | SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 | | SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 | | StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 | | Retry | 针对不同的响应进行重试 | retries、statuses、methods、series | | RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 `413 Payload Too Large` | 请求包大小,单位为字节,默认值为5M | | ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 | | ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 | | Default | 为所有路由添加过滤器 | 过滤器工厂名称及值 | \*\*Tips:\*\*每个过滤器工厂都对应一个实现类,并且这些类的名称必须以`GatewayFilterFactory`结尾,这是Spring Cloud Gateway的一个约定,例如`AddRequestHeader`对应的实现类为`AddRequestHeaderGatewayFilterFactory`。 ___ 1、AddRequestHeader GatewayFilter Factory 为原始请求添加Header,配置示例: ``` spring: cloud: gateway: routes: - id: add_request_header_route uri: https://example.org filters: - AddRequestHeader=X-Request-Foo, Bar ``` 1 2 3 4 5 6 7 8 为原始请求添加名为 `X-Request-Foo` ,值为 `Bar` 的请求头 2、AddRequestParameter GatewayFilter Factory 为原始请求添加请求参数及值,配置示例: ``` spring: cloud: gateway: routes: - id: add_request_parameter_route uri: https://example.org filters: - AddRequestParameter=foo, bar ``` 1 2 3 4 5 6 7 8 为原始请求添加名为foo,值为bar的参数,即:`foo=bar` 3、AddResponseHeader GatewayFilter Factory 为原始响应添加Header,配置示例: ``` spring: cloud: gateway: routes: - id: add_response_header_route uri: https://example.org filters: - AddResponseHeader=X-Response-Foo, Bar ``` 1 2 3 4 5 6 7 8 为原始响应添加名为 `X-Request-Foo` ,值为 `Bar` 的响应头 4、DedupeResponseHeader GatewayFilter Factory DedupeResponseHeader可以根据配置的Header名称及去重策略剔除响应头中重复的值,这是Spring Cloud Greenwich SR2提供的新特性,低于这个版本无法使用。 我们在Gateway以及微服务上都设置了CORS(解决跨域)Header的话,如果不做任何配置,那么请求 -> 网关 -> 微服务,获得的CORS Header的值,就将会是这样的: ``` Access-Control-Allow-Credentials: true, true Access-Control-Allow-Origin: https://musk.mars, https://musk.mars ``` 1 2 可以看到这两个Header的值都重复了,若想把这两个Header的值去重的话,就需要使用到DedupeResponseHeader,配置示例: ``` spring: cloud: gateway: routes: - id: dedupe_response_header_route uri: https://example.org filters: # 若需要去重的Header有多个,使用空格分隔 - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin ``` 1 2 3 4 5 6 7 8 9 去重策略: - RETAIN\_FIRST:默认值,保留第一个值 - RETAIN\_LAST:保留最后一个值 - RETAIN\_UNIQUE:保留所有唯一值,以它们第一次出现的顺序保留 若想对该过滤器工厂有个比较全面的了解的话,建议阅读该过滤器工厂的源码,因为源码里有详细的注释及示例,比官方文档写得还好:`org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory` 5、Hystrix GatewayFilter Factory 为路由引入Hystrix的断路器保护,配置示例: ``` spring: cloud: gateway: routes: - id: hystrix_route uri: https://example.org filters: - Hystrix=myCommandName ``` 1 2 3 4 5 6 7 8 Hystrix是Spring Cloud第一代容错组件,不过已经进入维护模式,未来Hystrix会被Spring Cloud移除掉,取而代之的是Alibaba Sentinel/Resilience4J。所以本文不做详细介绍了,感兴趣的话可以参考官方文档: - [Hystrix GatewayFilter Factoryopen in new window](https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#hystrix) 6、FallbackHeaders GatewayFilter Factory 同样是对Hystrix的支持,上一小节所介绍的过滤器工厂支持一个配置参数:`fallbackUri`,该配置用于当发生异常时将请求转发到一个特定的uri上。而`FallbackHeaders`这个过滤工厂可以在转发请求到该uri时添加一个Header,这个Header的值为具体的异常信息。配置示例: ``` spring: cloud: gateway: routes: - id: ingredients uri: lb://ingredients predicates: - Path=//ingredients/** filters: - name: Hystrix args: name: fetchIngredients fallbackUri: forward:/fallback - id: ingredients-fallback uri: http://localhost:9994 predicates: - Path=/fallback filters: - name: FallbackHeaders args: executionExceptionTypeHeaderName: Test-Header ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 这里也不做详细介绍了,感兴趣可以参考官方文档: - [FallbackHeaders GatewayFilter Factoryopen in new window](https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#fallback-headers) 7、PrefixPath GatewayFilter Factory 为原始的请求路径添加一个前缀路径,配置示例: ``` spring: cloud: gateway: routes: - id: prefixpath_route uri: https://example.org filters: - PrefixPath=/mypath ``` 1 2 3 4 5 6 7 8 该配置使访问`${GATEWAY_URL}/hello` 会转发到`https://example.org/mypath/hello` 8、PreserveHostHeader GatewayFilter Factory 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host Header。配置示例: ``` spring: cloud: gateway: routes: - id: preserve_host_route uri: https://example.org filters: - PreserveHostHeader ``` 1 2 3 4 5 6 7 8 如果不设置,那么名为 `Host` 的Header将由Http Client控制 9、RequestRateLimiter GatewayFilter Factory 用于对请求进行限流,限流算法为令牌桶。配置示例: ``` spring: cloud: gateway: routes: - id: requestratelimiter_route uri: https://example.org filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 ``` 1 2 3 4 5 6 7 8 9 10 11 由于另一篇文章中已经介绍过如何使用该过滤器工厂实现网关限流,所以这里就不再赘述了: - [Spring Cloud Gateway - 扩展open in new window](https://blog.51cto.com/zero01/2430532) 或者参考官方文档: - [RequestRateLimiter GatewayFilter Factoryopen in new window](https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_requestratelimiter_gatewayfilter_factory) 10、RedirectTo GatewayFilter Factory 将原始请求重定向到指定的Url,配置示例: ``` spring: cloud: gateway: routes: - id: redirect_route uri: https://example.org filters: - RedirectTo=302, https://acme.org ``` 1 2 3 4 5 6 7 8 该配置使访问 `${GATEWAY_URL}/hello` 会被重定向到 `https://acme.org/hello` ,并且携带一个 `Location:http://acme.org` 的Header,而返回客户端的HTTP状态码为302 注意事项: - HTTP状态码应为3xx,例如301 - URL必须是合法的URL,该URL会作为`Location` Header的值 11、RemoveHopByHopHeadersFilter GatewayFilter Factory 为原始请求删除[IETFopen in new window](https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3)组织规定的一系列Header,默认删除的Header如下: - Connection - Keep-Alive - Proxy-Authenticate - Proxy-Authorization - TE - Trailer - Transfer-Encoding - Upgrade 可以通过配置去指定仅删除哪些Header,配置示例: ``` spring: cloud: gateway: filter: remove-hop-by-hop: # 多个Header使用逗号(,)分隔 headers: Connection,Keep-Alive ``` 1 2 3 4 5 6 7 12、RemoveRequestHeader GatewayFilter Factory 为原始请求删除某个Header,配置示例: ``` spring: cloud: gateway: routes: - id: removerequestheader_route uri: https://example.org filters: - RemoveRequestHeader=X-Request-Foo ``` 1 2 3 4 5 6 7 8 删除原始请求中名为 `X-Request-Foo` 的请求头 13、RemoveResponseHeader GatewayFilter Factory 为原始响应删除某个Header,配置示例: ``` spring: cloud: gateway: routes: - id: removeresponseheader_route uri: https://example.org filters: - RemoveResponseHeader=X-Response-Foo ``` 1 2 3 4 5 6 7 8 删除原始响应中名为 `X-Request-Foo` 的响应头 14、RewritePath GatewayFilter Factory 通过正则表达式重写原始的请求路径,配置示例: ``` spring: cloud: gateway: routes: - id: rewritepath_route uri: https://example.org predicates: - Path=/foo/** filters: # 参数1为原始路径的正则表达式,参数2为重写后路径的正则表达式 - RewritePath=/foo/(?.*), /$\{segment} ``` 1 2 3 4 5 6 7 8 9 10 11 12 该配置使得访问 `/foo/bar` 时,会将路径重写为`/bar` 再进行转发,也就是会转发到 `https://example.org/bar`。需要注意的是:由于YAML语法,需用`$\` 替换 `$` 15、RewriteResponseHeader GatewayFilter Factory 重写原始响应中的某个Header,配置示例: ``` spring: cloud: gateway: routes: - id: rewriteresponseheader_route uri: https://example.org filters: # 参数1为Header名称,参数2为值的正则表达式,参数3为重写后的值 - RewriteResponseHeader=X-Response-Foo, password=[^&]+, password=*** ``` 1 2 3 4 5 6 7 8 9 10 该配置的意义在于:如果响应头中 `X-Response-Foo` 的值为`/42?user=ford&password=omg!what&flag=true`,那么就会被按照配置的值重写成`/42?user=ford&password=***&flag=true`,也就是把其中的`password=omg!what`重写成了`password=***` 16、SaveSession GatewayFilter Factory 在转发请求之前,强制执行`WebSession::save`操作,配置示例: ``` spring: cloud: gateway: routes: - id: save_session uri: https://example.org predicates: - Path=/foo/** filters: - SaveSession ``` 1 2 3 4 5 6 7 8 9 10 11 主要用在那种像 Spring Session 延迟数据存储(数据不是立刻持久化)的,并希望在请求转发前确保session状态保存情况。如果你将Spring Secutiry于Spring Session集成使用,并想确保安全信息都传到下游机器,你就需要配置这个filter。 17、secureHeaders GatewayFilter Factory secureHeaders过滤器工厂主要是参考了[这篇博客open in new window](https://blog.appcanary.com/2017/http-security-headers.html)中的建议,为原始响应添加了一系列起安全作用的响应头。默认会添加如下Headers(包括值): - `X-Xss-Protection:1; mode=block` - `Strict-Transport-Security:max-age=631138519` - `X-Frame-Options:DENY` - `X-Content-Type-Options:nosniff` - `Referrer-Policy:no-referrer` - `Content-Security-Policy:default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'` - `X-Download-Options:noopen` - `X-Permitted-Cross-Domain-Policies:none` 如果你想修改这些Header的值,那么就需要使用这些Headers对应的后缀,如下: - `xss-protection-header` - `strict-transport-security` - `frame-options` - `content-type-options` - `referrer-policy` - `content-security-policy` - `download-options` - `permitted-cross-domain-policies` 配置示例: ``` spring: cloud: gateway: filter: secure-headers: # 修改 X-Xss-Protection 的值为 2; mode=unblock xss-protection-header: 2; mode=unblock ``` 1 2 3 4 5 6 7 如果想禁用某些Header,可使用如下配置: ``` spring: cloud: gateway: filter: secure-headers: # 多个使用逗号(,)分隔 disable: frame-options,download-options ``` 1 2 3 4 5 6 7 8 18、SetPath GatewayFilter Factory 修改原始的请求路径,配置示例: ``` spring: cloud: gateway: routes: - id: setpath_route uri: https://example.org predicates: - Path=/foo/{segment} filters: - SetPath=/{segment} ``` 1 2 3 4 5 6 7 8 9 10 11 该配置使访问 `${GATEWAY_URL}/foo/bar` 时会转发到 `https://example.org/bar` ,也就是原本的`/foo/bar`被修改为了`/bar` 19、SetResponseHeader GatewayFilter Factory 修改原始响应中某个Header的值,配置示例: ``` spring: cloud: gateway: routes: - id: setresponseheader_route uri: https://example.org filters: - SetResponseHeader=X-Response-Foo, Bar ``` 1 2 3 4 5 6 7 8 9 将原始响应中 `X-Response-Foo` 的值修改为 `Bar` 20、SetStatus GatewayFilter Factory 修改原始响应的状态码,配置示例: ``` spring: cloud: gateway: routes: - id: setstatusstring_route uri: https://example.org filters: # 字符串形式 - SetStatus=BAD_REQUEST - id: setstatusint_route uri: https://example.org filters: # 数字形式 - SetStatus=401 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 SetStatusd的值可以是数字,也可以是字符串。但一定要是Spring `HttpStatus` 枚举类中的值。上面这两种配置都可以返回401这个HTTP状态码。 21、StripPrefix GatewayFilter Factory 用于截断原始请求的路径,配置示例: ``` spring: cloud: gateway: routes: - id: nameRoot uri: http://nameservice predicates: - Path=/name/** filters: # 数字表示要截断的路径的数量 - StripPrefix=2 ``` 1 2 3 4 5 6 7 8 9 10 11 12 如上配置,如果请求的路径为 `/name/bar/foo` ,那么则会截断成`/foo`后进行转发 ,也就是会截断2个路径。 22、Retry GatewayFilter Factory 针对不同的响应进行重试,例如可以针对HTTP状态码进行重试,配置示例: ``` spring: cloud: gateway: routes: - id: retry_test uri: http://localhost:8080/flakey predicates: - Host=*.retry.com filters: - name: Retry args: retries: 3 statuses: BAD_GATEWAY ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 可配置如下参数: - `retries`:重试次数 - `statuses`:需要重试的状态码,取值在 `org.springframework.http.HttpStatus` 中 - `methods`:需要重试的请求方法,取值在 `org.springframework.http.HttpMethod` 中 - `series`:HTTP状态码序列,取值在 `org.springframework.http.HttpStatus.Series` 中 23、RequestSize GatewayFilter Factory 设置允许接收最大请求包的大小,配置示例: ``` spring: cloud: gateway: routes: - id: request_size_route uri: http://localhost:8080/upload predicates: - Path=/upload filters: - name: RequestSize args: # 单位为字节 maxSize: 5000000 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 如果请求包大小超过设置的值,则会返回 `413 Payload Too Large`以及一个`errorMessage` 24、Modify Request Body GatewayFilter Factory 在转发请求之前修改原始请求体内容,该过滤器工厂只能通过代码配置,不支持在配置文件中配置。代码示例: ``` @Bean public RouteLocator routes(RouteLocatorBuilder builder) { return builder.routes() .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org") .filters(f -> f.prefixPath("/httpbin") .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri)) .build(); } static class Hello { String message; public Hello() { } public Hello(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 > Tips:该过滤器工厂处于 **BETA** 状态,未来API可能会变化,生产环境请慎用 25、Modify Response Body GatewayFilter Factory 可用于修改原始响应体的内容,该过滤器工厂同样只能通过代码配置,不支持在配置文件中配置。代码示例: ``` @Bean public RouteLocator routes(RouteLocatorBuilder builder) { return builder.routes() .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org") .filters(f -> f.prefixPath("/httpbin") .modifyResponseBody(String.class, String.class, (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri) .build(); } ``` 1 2 3 4 5 6 7 8 9 10 > Tips:该过滤器工厂处于 **BETA** 状态,未来API可能会变化,生产环境请慎用 26、Default Filters Default Filters用于为所有路由添加过滤器工厂,也就是说通过Default Filter所配置的过滤器工厂会作用到所有的路由上。配置示例: ``` spring: cloud: gateway: default-filters: - AddResponseHeader=X-Response-Default-Foo, Default-Bar - PrefixPath=/httpbin ``` 1 2 3 4 5 6 ___ 官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html#\_gatewayfilter\_factories #### 自定义过滤器 1、自定义 局部过滤器 麻烦,很少。 ``` // // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.cloud.gateway.filter.factory; import java.util.Arrays; import java.util.List; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.support.GatewayToStringStyler; import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; public class StripPrefixGatewayFilterFactory extends AbstractGatewayFilterFactory { public static final String PARTS_KEY = "parts"; public StripPrefixGatewayFilterFactory() { super(StripPrefixGatewayFilterFactory.Config.class); } public List shortcutFieldOrder() { return Arrays.asList("parts"); } public GatewayFilter apply(StripPrefixGatewayFilterFactory.Config config) { return new GatewayFilter() { public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerWebExchangeUtils.addOriginalRequestUrl(exchange, request.getURI()); String path = request.getURI().getRawPath(); String[] originalParts = StringUtils.tokenizeToStringArray(path, "/"); StringBuilder newPath = new StringBuilder("/"); for(int i = 0; i < originalParts.length; ++i) { if (i >= config.getParts()) { if (newPath.length() > 1) { newPath.append('/'); } newPath.append(originalParts[i]); } } if (newPath.length() > 1 && path.endsWith("/")) { newPath.append('/'); } ServerHttpRequest newRequest = request.mutate().path(newPath.toString()).build(); exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, newRequest.getURI()); return chain.filter(exchange.mutate().request(newRequest).build()); } public String toString() { return GatewayToStringStyler.filterToStringCreator(StripPrefixGatewayFilterFactory.this).append("parts", config.getParts()).toString(); } }; } public static class Config { private int parts; public Config() { } public int getParts() { return this.parts; } public void setParts(int parts) { this.parts = parts; } } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 2、自定义 全局过滤器 需求:1黑客ip,直接给你拒接掉。 2某些请求路径 如“goods/findById”,危险操作,记录日志。 自定义全局过滤器步骤: ``` 1. 定义类实现 GlobalFilter 和 Ordered接口 2. 复写方法 3. 完成逻辑处理 ``` 1 2 3 IpFilter ``` package com.ydlclass.gateway.filter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.net.InetAddress; import java.net.InetSocketAddress; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @Component public class IpFilter implements GlobalFilter, Ordered { //写业务逻辑 @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1黑客ip,直接给你拒接掉。 //拿到请求和响应,为所欲为 ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); InetSocketAddress remoteAddress = request.getRemoteAddress(); String hostName = remoteAddress.getHostName(); System.out.println(hostName); InetAddress address = remoteAddress.getAddress(); String hostAddress = address.getHostAddress(); System.out.println(hostAddress); String hostName1 = address.getHostName(); System.out.println(hostName1); if (hostAddress.equals("192.168.31.11")) { //拒绝 response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } //走完了,该到下一个过滤器了 return chain.filter(exchange); } //返回数值,越小越先执行 @Override public int getOrder() { return 0; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 UrlFilter ``` package com.ydlclass.gateway.filter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.net.URI; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @Component public class UrlFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { //某些请求路径 如“goods/findById”,危险操作,记录日志。 //拿到请求和响应,为所欲为 ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); URI uri = request.getURI(); String path = uri.getPath(); System.out.println(path); if(path.contains("goods/findById")){ //打日志 System.out.println("path很危险!"); } return chain.filter(exchange); } @Override public int getOrder() { return 1; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 测试: 1、ipfilter ![image-20220411191527069](https://www.ydlclass.com/doc21xnv/assets/image-20220411191527069.40d06e6f.png) 把自己的ip设置为拒绝访问的话 ![image-20220411191641610](https://www.ydlclass.com/doc21xnv/assets/image-20220411191641610.d8bde2dd.png) 2、 ![image-20220411192132436](https://www.ydlclass.com/doc21xnv/assets/image-20220411192132436.09d8d5e2.png) ## 第十三章 分布式配置中心 13-16组件,超大型公司才会用到 国内BATJ,或者国外FLAG:facebook linkedin amazon google 处理大规模微服务的技术。 ### 一、Config 概述 ![image-20220418081959807](https://www.ydlclass.com/doc21xnv/assets/image-20220418081959807.439382b4.png) Spring Cloud Config 解决了在分布式场景下多环境配置文件的管理和维护。 好处: • 集中管理配置文件 • 不同环境不同配置,动态化的配置更新 • 配置信息改变时,不需要重启即可更新配置信息到服务 ### 二、Config 快速入门 ![image-20220418082533367](https://www.ydlclass.com/doc21xnv/assets/image-20220418082533367.271fa629.png) **config-server:** 1. 使用gitee创建远程仓库,上传配置文件 config-dev.yml 2. 搭建 config-server 模块 ConfigServerApp ``` package com.ydlclass.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer // 启用config server功能 public class ConfigServerApp { public static void main(String[] args) { SpringApplication.run(ConfigServerApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 3. 导入 config-server 依赖 pom ``` org.springframework.cloud spring-cloud-config-server ``` 1 2 3 4 5 4. 编写配置,设置 gitee 远程仓库地址 application.yml ``` server: port: 9527 spring: application: name: config-server # spring cloud config cloud: config: server: # git 的 远程仓库地址 git: uri: https://gitee.com/ydlclass_cch/ydlclass-configs.git label: master # 分支配置 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 5. 测试访问远程配置文件 http://localhost:9527/master/provider-dev.yml ![image-20220418083733698](https://www.ydlclass.com/doc21xnv/assets/image-20220418083733698.508dfd18.png) **config-client:** provider 1. 导入 starter-config 依赖 ``` org.springframework.cloud spring-cloud-starter-config ``` 1 2 3 4 5 2. 配置config server 地址,读取配置文件名称等信息 bootstrap.yml ``` # 配置config-server地址 # 配置获得配置文件的名称等信息 spring: cloud: config: # 配置config-server地址 uri: http://localhost:9527 # 配置获得配置文件的名称等信息 name: provider # 文件名 profile: dev # profile指定, config-dev.yml label: master # 分支 ``` 1 2 3 4 5 6 7 8 9 10 11 3. 获取配置值 GoodsController ``` @Value("${ydlclass}") private String ydlclass; goods.setTitle(goods.getTitle()+"|端口号:"+port+"|ydlclass"+ydlclass); ``` 1 2 3 4 4. 启动测试 http://localhost:8000/goods/findById/9 ![image-20220418084608302](https://www.ydlclass.com/doc21xnv/assets/image-20220418084608302.978afefa.png) **Config 客户端刷新** ![image-20220418085057098](https://www.ydlclass.com/doc21xnv/assets/image-20220418085057098.ae365b9f.png) 1. 在 config 客户端引入 actuator 依赖 ``` org.springframework.boot spring-boot-starter-actuator ``` 1 2 3 4 2. 获取配置信息类上,添加 @RefreshScope 注解 GoodsController ``` @RefreshScope // 开启刷新功能 ``` 1 3. 添加配置 bootstrap.yml ``` management: endpoints: web: exposure: include: '*' ``` 1 2 3 4 5 4. 使用curl工具发送post请求 curl -X POST http://localhost:8000/actuator/refresh ![image-20220418085907801](https://www.ydlclass.com/doc21xnv/assets/image-20220418085907801.edb89cde.png) 测试: 值已经变了 ![image-20220418085930400](https://www.ydlclass.com/doc21xnv/assets/image-20220418085930400.9adcb0c7.png) ### 三、Config 集成Eureka ![image-20220418090254336](https://www.ydlclass.com/doc21xnv/assets/image-20220418090254336.8b4469ba.png) **config-client配置:** pom ``` org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` 1 2 3 4 5 ProviderApp bootstrap.yml ``` # 配置config-server地址 # 配置获得配置文件的名称等信息 spring: cloud: config: # 配置config-server地址 #uri: http://localhost:9527 # 配置获得配置文件的名称等信息 name: config # 文件名 profile: dev # profile指定, config-dev.yml label: master # 分支 discovery: enabled: true service-id: CONFIG-SERVER management: endpoints: web: exposure: include: '*' ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 **config-server配置:** pom ``` org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` 1 2 3 4 5 ConfigServerApp application.yml ``` eureka: client: service-url: defaultZone: http://localhost:8761/eureka ``` 1 2 3 4 测试: ![image-20220418093808497](https://www.ydlclass.com/doc21xnv/assets/image-20220418093808497.b6035ff7.png) ## 第十四章 SpringCloud Bus消息总线 远程的配置文件更新了,运维只需要发一个请求,所有用到这个配置文件的几百个应用更新了。 ### 一、Bus 概述 ![image-20220418094548555](https://www.ydlclass.com/doc21xnv/assets/image-20220418094548555.e5074738.png) Spring Cloud Bus 是用轻量的消息中间件将分布式的节点连接起来,可以用于广播配置文件的更改或者服务的监控管理。关键的思想就是,消息总线可以为微服务做监控,也可以实现应用程序之间相通信。 Spring Cloud Bus 可选的消息中间件包括 RabbitMQ 和 Kafka 。 ### 二、RabbitMQ 回顾 message queue 异步、解耦、削峰填谷 ![image-20200613074044506](https://www.ydlclass.com/doc21xnv/assets/image-20200613074044506.2b1f2272.png) ![image-20200613074052830](https://www.ydlclass.com/doc21xnv/assets/image-20200613074052830.5aedea61.png) RabbitMQ 提供了 6 种工作模式:简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing 路由模式、Topics 主题模式、RPC 远程调用模式(远程调用,不太算 MQ;暂不作介绍)。 ![image-20200613074109383](https://www.ydlclass.com/doc21xnv/assets/image-20200613074109383.dc1a9615.png) ### 三、Bus 快速入门-运维 ![image-20220418094548555](https://www.ydlclass.com/doc21xnv/assets/image-20220418094548555.e5074738.png) 1、分别在 config-server 和 config-client中引入 bus依赖:bus-amqp ``` org.springframework.cloud spring-cloud-starter-bus-amqp org.springframework.boot spring-boot-starter-actuator ``` 1 2 3 4 5 6 7 8 9 2、分别在 config-server 和 config-client中配置 RabbitMQ server 的配置文件: ``` server: port: 9527 spring: application: name: config-server # spring cloud config cloud: config: server: # git 的 远程仓库地址 git: uri: https://gitee.com/ydlclass_cch/ydlclass-configs.git label: master # 分支配置 #配置rabbitmq信息 rabbitmq: host: localhost port: 5672 username: guest password: guest virtual-host: / # 将自己注册到eureka中 eureka: client: service-url: defaultZone: http://localhost:8761/eureka # 暴露bus的刷新端点 management: endpoints: web: exposure: include: 'bus-refresh' ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 client的配置文件 bootstrap.yml ``` # 配置config-server地址 # 配置获得配置文件的名称等信息 spring: cloud: config: # 配置config-server地址 # uri: http://localhost:9527 # 配置获得配置文件的名称等信息 name: config # 文件名 profile: dev # profile指定, config-dev.yml label: master # 分支 # 从注册中心去寻找config-server地址 discovery: enabled: true service-id: CONFIG-SERVER #配置rabbitmq信息 rabbitmq: host: localhost port: 5672 username: guest password: guest virtual-host: / management: endpoints: web: exposure: include: '*' ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 1. 在config-server中设置暴露监控断点:bus-refresh ``` # 暴露bus的刷新端点 management: endpoints: web: exposure: include: 'bus-refresh' ``` 1 2 3 4 5 6 bus自动在mq中建立交换机 ![image-20220418095857082](https://www.ydlclass.com/doc21xnv/assets/image-20220418095857082.c146dc82.png) 只要微服务起起来,自动在mq中,建立队列,和交换机绑定 ![image-20220418100013437](https://www.ydlclass.com/doc21xnv/assets/image-20220418100013437.ff032e6b.png) 启动测试 1.git配置改变 ![image-20220418104835631](https://www.ydlclass.com/doc21xnv/assets/image-20220418104835631.9d49fe01.png) 2.往配置中心发请求 curl -X POST http://localhost:9527/actuator/bus-refresh ![image-20220418104622908](data:image/png;base64,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) 3.微服务自动重启 ![image-20220418104702500](https://www.ydlclass.com/doc21xnv/assets/image-20220418104702500.bbdf21ca.png) 4.访问微服务,发现配置已经改变 ![image-20220418104819862](https://www.ydlclass.com/doc21xnv/assets/image-20220418104819862.b829a33e.png) ## 第十五章 SpringCloud Stream消息驱动 ### 一、Stream 概述 ![image-20220418111329857](https://www.ydlclass.com/doc21xnv/assets/image-20220418111329857.7c8547d2.png) 1.Spring Cloud Stream 是一个构建消息驱动微服务应用的框架。 2.Stream 解决了开发人员**无感知**的使用消息中间件的问题,因为Stream对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至于**动态的切换中间件**,使得微服务开发的高度解耦,服务可以关注更多自己的业务流程。 3.Spring Cloud Stream目前支持两种消息中间件RabbitMQ和Kafka 就像jdbc一样 ![image-20220418111643163](https://www.ydlclass.com/doc21xnv/assets/image-20220418111643163.1006efdf.png) **Stream 组件** ![image-20200613074759833](https://www.ydlclass.com/doc21xnv/assets/image-20200613074759833.b4428b4e.png) Spring Cloud Stream 构建的应用程序与消息中间件之间是通过绑定器 Binder 相关联的。绑定器对于应用程序而言起到了隔离作用, 它使得不同消息中间件的实现细节对应用程序来说是透明的。 binding 是我们通过配置把应用和spring cloud stream 的 binder 绑定在一起 output:发送消息 Channel,内置 Source接口 input:接收消息 Channel,内置 Sink接口 ### 二、Stream 消息生产者 pom ``` stream-parent com.ydlclass 1.0-SNAPSHOT 4.0.0 stream-producer org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ProducerApp ``` package com.ydlclass.stream; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProducerApp { public static void main(String[] args) { SpringApplication.run(ProducerApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 application.yml ``` server: port: 8000 spring: cloud: stream: # 定义绑定器,绑定到哪个消息中间件上 binders: ydlclass_binder: # 自定义的绑定器名称 type: rabbit # 绑定器类型 environment: # 指定mq的环境 spring: rabbitmq: host: localhost port: 5672 username: guest password: guest virtual-host: / bindings: output: # channel名称 binder: ydlclass_binder #指定使用哪一个binder destination: ydlclass_exchange # 消息目的地 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 消息发送类 MessageProducer ``` package com.ydlclass.stream.producer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.messaging.Source; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.support.MessageBuilder; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @Component @EnableBinding(Source.class)//这个类是stream的发送者 public class MessageProducer { @Autowired MessageChannel output; public void send(String msg){ output.send(MessageBuilder.withPayload(msg).build()); System.out.println("MessageProducer 确实发了消息了!"); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 调用的controller ProducerController ``` package com.ydlclass.stream.controller; import com.ydlclass.stream.producer.MessageProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @RestController @RequestMapping("test") public class ProducerController { @Autowired MessageProducer messageProducer; //业务逻辑 @GetMapping("/send") public String send(){ //发消息 String msg="使用stream来发消息了!"; messageProducer.send(msg); return "success!"; } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ![image-20220418114016165](https://www.ydlclass.com/doc21xnv/assets/image-20220418114016165.e7af66ff.png) ### 三、Stream 消息消费者 pom ``` stream-parent com.ydlclass 1.0-SNAPSHOT 4.0.0 stream-consumer org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ConsumerApp ``` package com.ydlclass.stream; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class,args); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 application.yml ``` server: port: 9000 spring: cloud: stream: # 定义绑定器,绑定到哪个消息中间件上 binders: ydlclass_binder: # 自定义的绑定器名称 type: rabbit # 绑定器类型 environment: # 指定mq的环境 spring: rabbitmq: host: localhost port: 5672 username: guest password: guest virtual-host: / bindings: input: # channel名称 binder: ydlclass_binder #指定使用哪一个binder destination: ydlclass_exchange # 消息目的地 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 消息接收类 MessageListener ``` package com.ydlclass.stream.consumer; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.cloud.stream.messaging.Sink; import org.springframework.messaging.Message; import org.springframework.stereotype.Component; /** * 消息接收类 */ @EnableBinding({Sink.class}) @Component public class MessageListener { @StreamListener(Sink.INPUT) public void receive(Message message){ System.out.println(message); System.out.println(message.getPayload()); } } ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 测试 ![image-20220418154602979](https://www.ydlclass.com/doc21xnv/assets/image-20220418154602979.4f1f002f.png) ## 第十六章 SpringCloud Sleuth分布式请求链路追踪 ### 一、概述 ![image-20220418155007573](https://www.ydlclass.com/doc21xnv/assets/image-20220418155007573.1c1b8bdd.png) Spring Cloud Sleuth 其实是一个工具,它在整个分布式系统中能跟踪一个用户请求的过程,捕获这些跟踪数据,就能构建微服务的整个调用链的视图,这是调试和监控微服务的关键工具。 Zipkin 是 Twitter 的一个开源项目,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。 ### 二、快速入门 1. 安装启动zipkin。 java –jar zipkin.jar ![image-20220418185033323](https://www.ydlclass.com/doc21xnv/assets/image-20220418185033323.76dc027c.png) 2. 访问zipkin web界面。 http://localhost:9411/ ![image-20200613080015686](https://www.ydlclass.com/doc21xnv/assets/image-20200613080015686.e297485f.png) 3. 在服务提供方和消费方分别引入 sleuth 和 zipkin 依赖 ``` org.springframework.cloud spring-cloud-starter-zipkin ``` 1 2 3 4 5 6 7 8 9 10 4. 分别配置服务提供方和消费方。 provider ``` server: port: 8001 eureka: client: service-url: defaultZone: http://localhost:8761/eureka spring: application: name: feign-provider zipkin: base-url: http://localhost:9411/ # 设置zipkin的服务端路径 sleuth: sampler: probability: 1 # 采集率 默认 0.1 百分之十。 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 consumer ``` server: port: 9000 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://localhost:8761/eureka spring: application: name: feign-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径 zipkin: base-url: http://localhost:9411/ # 设置zipkin的服务端路径 sleuth: sampler: probability: 1 # 采集率 默认 0.1 百分之十。 logging: level: com.ydlclass: debug ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 5. 启动,测试 ![image-20220418160537372](https://www.ydlclass.com/doc21xnv/assets/image-20220418160537372.c7993f21.png) 调用关系图也能展现 ![image-20220418160602065](https://www.ydlclass.com/doc21xnv/assets/image-20220418160602065.25c4ceff.png)