介绍:课程介绍

1、前置

​ javaSE,javaEE,SSM,Boot

​ 为什么:一线城市,上了规模大厂、中厂,都需要cloud。

​ cloud,薪资高

2、特点

​ 各大机构视频教程、网络文章、spring官网以及本人8年的java开发经验。

​ 跟着本课程,写就可以了。

​ 入门课程,会用cloud。

3、如何学习

​ 保姆级教程。

​ 代码和课程图片粘贴md格式的文档上面。

​ 倍速播放。关闭弹幕。

​ 学完一个知识点,有任何问题,弹幕、评论、粉丝群大家讨论。

4、面向人员

​ 大学生朋友:大学当中,科班非科班,毕业找一份java工作,没问题。

​ 工作1-3年朋友:自己行业又累薪资又低。4-6个月,坚持下来,转行找到java开发职位没问题。

5、心态

​ 太简单了、太难。 我不是RMB,做不到14亿人都喜欢。 右上角有个X,点击一下退出可以了。

​ 不要吃饱了骂厨子、念完经打和尚、过河拆桥。

​ Talk is cheap ,show me the code 。 坚持。30小结。都看完,知识就是你的了。

​ 欢迎大家,一键三连。

6、获取资料

www.ydlclass.comopen in new window 文档区,免费下载即可。

itlils和楠哥itnanls java学习整套路径,列出来。 教程都由我和楠哥亲自录制。 免费自学java成功。

案例:免费自学,java工作,应届生,一线,10k。

随时一对一解决问题,规划,简历修改。加入我们会员。

第一章 SpringCloud简介

一、 软件架构演进

单体架构

image-20220328172416767
image-20220328172416767

垂直架构

image-20220328172721883
image-20220328172721883

分布式架构

image-20220328173112642
image-20220328173112642

SOA架构

image-20220328174118241
image-20220328174118241

微服务架构

image-20220328174633493
image-20220328174633493

二、微服务架构

1、 微服务理念

①"微服务”一词源 于 Martin Fowler的名为 Microservices的博文,可以在他的官方博客上找到http://martinfowler.com/articles/microservices.htmlopen in new window

②微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间一般通过 HTTP 的 RESTfuL API 进行通信协作。

restfull 风格:数据的增删改查,使用http的不同方式。数据传输用json。

From: 元动力
1
2
3
4
5
6
7
​    查询   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/open in new window

image-20220329154419410
image-20220329154419410
image-20220329152020956
image-20220329152020956

2、 现在大型互联网公司,都在使用微服务架构:

京东的促销节架构:618

image-20220329152531517
image-20220329152531517

阿里的架构:

image-20220329152552588
image-20220329152552588

京东物流的架构:

image-20220329152611053
image-20220329152611053

3、 springcloud组件:

image-20220329152758773
image-20220329152758773
image-20220329153147800
image-20220329153147800

第二章 走进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-cloudopen in new window

⑥Spring Cloud 版本命名方式采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如:最早的Release版本:Angel,第二个Release版本:Brixton,然后是Camden、Dalston、Edgware,Finchley,Greenwich,Hoxton。

目前最新的是2021.0.1版本。

image-20220329161144299
image-20220329161144299

二、 cloud与boot版本对应关系

image-20220329164540295
image-20220329164540295

三、 dubbo对比

image-20220329161102849
image-20220329161102849

相同点: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
image-20220329171255668

第四章 微服务架构编码构建

image-20220404103923982
image-20220404103923982

一、 搭建 Provider 和 Consumer 服务

1、父工程 spring-cloud-parent

image-20220404103149867
image-20220404103149867

使用utf-8编码

image-20220404103334670
image-20220404103334670

maven设置

image-20220404103417399
image-20220404103417399

pom.xml

From: 元动力
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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.ydlclass</groupId>
<artifactId>spring-cloud-parent</artifactId>
<version>1.0.0</version>

<!--spring boot 环境 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.11.RELEASE</version>
<relativePath/>
</parent>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

</project>

2、提供者 eureka-provider

image-20220330164728077
image-20220330164728077

搭建springboot工程:步骤把大象装冰箱有几步? 工作中别问同事

From: 元动力
1
2
3
1 pom 导包
2 配置文件
3 主启动类

pom.xml

From: 元动力
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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-parent</artifactId>
<groupId>com.ydlclass</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>eureka-provider</artifactId>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

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

</project>

application.yml

From: 元动力
1
2
server:
port: 8000

主启动类 ProviderApp

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}
}

Goods

From: 元动力
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
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;
}
}

GoodsController

From: 元动力
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
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;
}
}

GoodsService

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
}
}

GoodsDao

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}
}

测试:

访问:http://localhost:8000/goods/findById/1open in new window

image-20220330170647529
image-20220330170647529

3、消费者 eureka-consumer

image-20220404111105516
image-20220404111105516

pom.xml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-parent</artifactId>
<groupId>com.ydlclass</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>eureka-consumer</artifactId>


<dependencies>

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

</dependencies>
</project>

Goods

From: 元动力
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
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;
}
}

OrderController

From: 元动力
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
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();
}
}

application.yml

From: 元动力
1
2
server:
port: 9000

测试:

http://localhost:9000/order/add/2open in new window

image-20220404112103333
image-20220404112103333

二、使用 RestTemplate 完成远程调用

  • Spring提供的一种简单便捷的模板类,用于在 java 代码里访问 restful 服务。
  • 其功能与 HttpClient 类似,但是 RestTemplate 实现更优雅,使用更方便。

consumer工程中

RestTemplateConfig

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
}
}

OrderController

From: 元动力
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
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;
}
}

测试:

第五章 Eureka服务注册与发现

一、Eureka

概念:

• Eureka 是 Netflix 公司开源的一个服务注册与发现的组件 。

• Eureka 和其他 Netflix 公司的服务组件(例如负载均衡、熔断器、网关等) 一起,被 Spring Cloud 社区整合为Spring-Cloud-Netflix 模块。

• Eureka 包含两个组件:Eureka Server (注册中心) 和 Eureka Client (服务提供者、服务消费者)。

操作:

image-20200606113829765
image-20200606113829765

二、 搭建 Eureka Server 服务

(1)创建 eureka-server 模块

(2) 引入 SpringCloud 和 euraka-server 相关依赖

(3)完成 Eureka Server 相关配置

(4)启动该模块

父工程 pom

From: 元动力
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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.ydlclass</groupId>
<artifactId>spring-cloud-parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>eureka-provider</module>
<module>eureka-consumer</module>
<module>eureka-server</module>
</modules>


<!--spring boot 环境 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/>
</parent>


<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!--spring cloud 版本-->
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>

<!--引入Spring Cloud 依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

</project>

eureka-server工程

pom

From: 元动力
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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-parent</artifactId>
<groupId>com.ydlclass</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>eureka-server</artifactId>



<dependencies>

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

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


</project>

EurekaApp

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}

application.yml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 需要

**测试: ** 访问 localhost:8761

image-20200611013357920
image-20200611013357920

三、 改造 Provider 和 Consumer 称为 Eureka Client

① 引 eureka-client 相关依赖

② 完成 eureka client 相关配置

③ 启动 测试

Provider工程

pom

From: 元动力
1
2
3
4
5
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

ProviderApp

From: 元动力
1
@EnableEurekaClient

application.yml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 8001


eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
spring:
application:
name: eureka-provider # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径

Consumer

pom

From: 元动力
1
2
3
4
5
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

ConsumerApp

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}
}

application.yml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 9000

eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
spring:
application:
name: eureka-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径

四、 Consumer 服务 通过从 Eureka Server 中抓取 Provider 地址,完成远程调用

Consumer

OrderController

From: 元动力
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
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<ServiceInstance> 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;
}
}

五、 Euraka配置详解

Eureka包含四个部分的配置

  • instance:当前Eureka Instance实例信息配置
  • client:Eureka Client客户端特性配置
  • server:Eureka Server注册中心特性配置
  • dashboard:Eureka Server注册中心仪表盘配置

1、实例信息配置

From: 元动力
1
2
3
4
5
6
7
8
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的心跳包,则剔除该服务

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、客户端特性配置

From: 元动力
1
2
3
4
5
6
7
eureka:
client:
service-url:
# eureka服务端地址,将来客户端使用该地址和eureka进行通信
defaultZone:
register-with-eureka: # 是否将自己的路径 注册到eureka上。
fetch-registry: # 是否需要从eureka中抓取数据。

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。open in new window

  • 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、注册中心端配置

From: 元动力
1
2
3
4
5
6
7
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的心跳包,则剔除该服务

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、仪表盘配置

From: 元动力
1
2
3
4
eureka:
dashboard:
enabled: true # 是否启用eureka web控制台
path: / # 设置eureka web控制台默认访问路径

注册中心仪表盘的配置主要是控制注册中心的可视化展示。以eureka.dashboard.xxx的格式配置。

  • path="/"

仪表盘访问路径

  • enabled=true

是否启用仪表盘

改造 provider

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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显示。将来需要使用该名称来获取路径

consumer

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 9000


eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
spring:
application:
name: eureka-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径

server

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 # 检查服务的时间间隔

六、高可用

image-20220406114821846
image-20220406114821846
  1. 准备两个Eureka Server

  2. 分别进行配置,相互注册

  3. Eureka Client 分别注册到这两个 Eureka Server中

创建eureka-server1

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

创建eureka-server2

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

修改:C:\Windows\System32\drivers\etc\hosts

127.0.0.1 eureka-server1 127.0.0.1 eureka-server2

image-20220406144138369
image-20220406144138369

测试:

provider

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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显示。将来需要使用该名称来获取路径

consumer

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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显示。将来需要使用该名称来获取路径

访问:http://localhost:9000/order/add/4open in new window

image-20220406144153992
image-20220406144153992

高可用测试:停掉一个eureka,依然可以访问consumer。

eureka不更新了,所以淘汰了。

第六章 Zookeeper服务注册与发现

image-20220406194741473
image-20220406194741473

有的老项目以前是dubbo,升级到微服务,使用zookeeper做注册中心。

zookeeper是一个分布式协调工具,可以实现注册中心功能。dubbo,大数据组件hadoop,hive,kafka。

实质:注册中心换成zk

1下载:https://zookeeper.apache.org/open in new window

image-20220406152809933
image-20220406152809933

zoo.cfg

From: 元动力
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
# 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

启动 bin目录下

From: 元动力
1
zkServer.cmd
image-20220406153622431
image-20220406153622431

2zookeeper-provider

pom

From: 元动力
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
<artifactId>zookeeper-provider</artifactId>

<dependencies>
<!--springcloud 整合 zookeeper 组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<!--zk发现-->
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>

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

</dependencies>

yml

From: 元动力
1
2
3
4
5
6
7
8
9
server:
port: 8004

spring:
application:
name: zookeeper-provider
cloud:
zookeeper:
connect-string: 127.0.0.1:2181 # zk地址

主启动类

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
}

业务逻辑代码直接复制粘贴过来

image-20220406172259585
image-20220406172259585

3zookeeper-consumer

pom

From: 元动力
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
<artifactId>zookeeper-consumer</artifactId>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<!--springcloud 整合 zookeeper 组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<!--zk发现-->
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.6</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>

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

</dependencies>

yml

From: 元动力
1
2
3
4
5
6
7
8
9
server:
port: 8005

spring:
application:
name: zookeeper-consumer
cloud:
zookeeper:
connect-string: 127.0.0.1:2181 # zk地址

启动类

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}
}

业务逻辑代码直接复制粘贴过来

image-20220406185656845
image-20220406185656845

controller只改一个

From: 元动力
1
List<ServiceInstance> instances = discoveryClient.getInstances("zookeeper-provider");

测试:http://localhost:8005/order/add/5open in new window

image-20220407144912417
image-20220407144912417

第七章 Consul服务注册与发现

image-20220407161804433
image-20220407161804433

一、 是什么:

二、 怎么用:

1、 准备

image-20220407161630689
image-20220407161630689
From: 元动力
1
consul.exe agent -dev
image-20220407161746354
image-20220407161746354

2、 搭建 consul-provider

pom

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

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

</dependencies>

yml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8006

spring:
application:
name: consul-provider
cloud:
consul:
host: 127.0.0.1
port: 8500
discovery:
service-name: ${spring.application.name}

启动类

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
}

业务逻辑复制

image-20220407163424691
image-20220407163424691

启动

image-20220407162423209
image-20220407162423209

3、搭建 consul-consumer

pom

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

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

yml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8007

spring:
application:
name: consul-consumer
cloud:
consul:
host: 127.0.0.1
port: 8500
discovery:
service-name: ${spring.application.name}

启动类

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
}

业务逻辑复制

image-20220407164001822
image-20220407164001822

controller只改一句话

From: 元动力
1
List<ServiceInstance> instances = discoveryClient.getInstances("consul-provider");

启动

image-20220407164133404
image-20220407164133404

最终测试调用成功即可:http://localhost:8007/order/add/9open in new window

image-20220407164211124
image-20220407164211124

三、 三个注册中心的异同

组件语言cap健康检查暴露接口cloud集成
eurekajavaap支持http已经集成
zookeeperjavacp支持tcp已经集成
consulgocp支持http已经集成

cap

  • c:consustency 强一致性

  • a:avalibility 可用性

  • p:partition tolerance 分区容忍性

第八章 Ribbon负载均衡服务调用

一、是什么

Netflix公司推出的http和TCP的客户端负载均衡工具。

ribbon:

  • 简化远程调用代码

  • 内置很多负载均衡算法

1、服务端负载均衡

负载均衡算法在服务端,服务端维护服务列表。

image-20220407173053676
image-20220407173053676

2、客户端负载均衡

  • 负载均衡算法在客户端

  • 客户端维护服务列表

image-20220407173409352
image-20220407173409352

二、如何使用

1.新版的eureka依赖以及集成了Ribbon依赖,所以可以不引用

From: 元动力
1
2
3
4
5
 <!--Ribbon的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
image-20220408112040713
image-20220408112040713

2.声明restTemplate时@LoadBalanced

3.restTemplate请求远程服务时,ip端口替换为服务名

From: 元动力
1
2
String url="http://EUREKA-PROVIDER/goods/findById/"+id;
Goods goods = restTemplate.getForObject(url, Goods.class);

测试:

1.启动2个provider

controller

From: 元动力
1
goods.setTitle(goods.getTitle()+"|端口号:"+port);

idea设置 能启动两份 provider

image-20220408112658153
image-20220408112658153
image-20220408112814966
image-20220408112814966

2.多次访问consumer

image-20220408113212684
image-20220408113212684
image-20220408113134662
image-20220408113134662

多次刷新,发现:ribbon客户端,默认使用轮询算法,经行负载均衡调用。

三、ribbon 负载均衡策略

内置负载均衡规则类规则描述
RoundRobinRule简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。
AvailabilityFilteringRule对以下两种服务器进行忽略:(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。注意:可以通过修改配置loadbalancer.<clientName>.connectionFailureCountThreshold来修改连接失败多少次之后被设置为短路状态。默认是3次。(2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上线,可以由客户端的<clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit属性进行配置。
WeightedResponseTimeRule为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。
ZoneAvoidanceRule以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。
BestAvailableRule忽略哪些短路的服务器,并选择并发数较低的服务器。
RandomRule随机选择一个可用的服务器。
Retry重试机制的选择逻辑

四、 设置ribbon 负载均衡策略

1、代码

consumer工程

1.MyRule 返回想要的规则即可

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
}
}

2.启动类

From: 元动力
1
@RibbonClient(name ="EUREKA-PROVIDER",configuration = MyRule.class)

总结:

1.irule的具体实现类,看到他带的几个策略的写法。

2.仿照策略的写法,自己写策略。

3.调用不同的其他微服务时,可以采用不同的策略。

2、配置

consumer工程

application.yml

From: 元动力
1
2
3
EUREKA-PROVIDER: #远程服务名
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #策略

作用:方便运维修改,重启。随时切换策略。

第九章、OpenFeign服务接口调用

一、概述

二、快速入门

a.在消费端引入 open-feign 依赖:

From: 元动力
1
2
3
4
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-openfeign&lt;/artifactId&gt;
&lt;/dependency&gt;

b.编写Feign调用接口。复制粘贴被调方的conreoller方法,加上类路径。

GoodsFeignClient:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}

c.在启动类 添加 @EnableFeignClients 注解,开启Feign功能

d.测试调用

OrderController

From: 元动力
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
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&lt;ServiceInstance&gt; instances = discoveryClient.getInstances("EUREKA-PROVIDER");
// if(instances==null||instances.size()&lt;=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、超时设置

• Feign 底层依赖于 Ribbon 实现负载均衡和远程调用

image-20220408171431530
image-20220408171431530

• Ribbon默认1秒超时。

• 超时配置: yml中

From: 元动力
1
2
3
4
# 设置Ribbon的超时时间
ribbon:
ConnectTimeout: 1000 # 连接超时时间 默认1s
ReadTimeout: 3000 # 逻辑处理的超时时间 默认1s

测试:

1.连接超时,provider都停掉

image-20220408172036937
image-20220408172036937

2.逻辑处理的超时时间

provider

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
@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;
}
image-20220408172302166
image-20220408172302166

2、日志记录

1.Feign 只能记录 debug 级别的日志信息。

From: 元动力
1
2
3
4
# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
logging:
level:
com.ydlclass: debug

• 定义Feign日志级别Bean

FeignLogConfig

From: 元动力
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
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. 启用该Bean:

GoodsFeignClient

From: 元动力
1
@FeignClient(value = "FEIGN-PROVIDER",configuration = FeignLogConfig.class)

image-20220408182551922

设置为headers

image-20220408182713438
image-20220408182713438

第十章 Hystrix断路器 (豪猪)-保险丝

一、概述

重点:能让服务的调用方,够快的知道被调方挂了!不至于说让用户在等待。

Hystix 是 Netflix 开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩)。

雪崩:一个服务失败,导致整条链路的服务都失败的情形。

image-20220411101214886
image-20220411101214886
image-20220411104119551
image-20220411104119551
image-20220411101248131
image-20220411101248131
image-20220411101310179
image-20220411101310179

Hystix 主要功能

image-20220411101741096
image-20220411101741096

隔离

  1. 线程池隔离

    没有hystrix,a重试100次,才知道c挂了!

    image-20220411102317061
    image-20220411102317061

    使用了hystrix,更细分线程池,只需要重试40次,让a更快的知道c挂了

    image-20220411102720425
    image-20220411102720425
  2. 信号量隔离

    没有hystrix,a一个带着认证信息的线程,重试100次,才知道c挂了!

    image-20220411103036778
    image-20220411103036778

    使用了hystrix,更细分线程池,一个带着认证信息的线程,只需要重试40次,让a更快的知道c挂了

    image-20220411103152275
    image-20220411103152275

降级

服务提供方降级(异常,超时)

image-20220411104951728
image-20220411104951728

消费方降级

image-20220411112946547
image-20220411112946547

熔断

限流

​ 是有限流,但是,项目一般不用。nginx或者网关限流。

二、服务降级

服务提供方

a、在服务提供方,引入 hystrix 依赖

From: 元动力
1
2
3
4
5
&lt;!-- hystrix --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-hystrix&lt;/artifactId&gt;
&lt;/dependency&gt;

b、方法

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
/** 定义降级方法   返回特殊对象
* 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;
}

c、使用 @HystrixCommand 注解配置降级方法

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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;
}

d、在启动类上开启Hystrix功能:@EnableCircuitBreaker

测试:http://localhost:9000/order/add/10open in new window

1.出错,服务方降级了

image-20220411110206538
image-20220411110206538

2.3000超时,服务方降级了

image-20220411111020806
image-20220411111020806

服务消费方

a、feign 组件已经集成了 hystrix 组件。

b、

image-20220411113035040
image-20220411113035040

c、定义feign 调用接口实现类,复写方法,即 降级方法

GoodsFeignClientFallback

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
}
}

d、在 @FeignClient 注解中使用 fallback 属性设置降级处理类。

GoodsFeignClient

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}

e、配置开启

From: 元动力
1
2
3
4
# 开启feign对hystrix的支持
feign:
hystrix:
enabled: true

测试:停掉provider

http://localhost:9000/order/add/10open in new window

image-20220411113635280
image-20220411113635280

三、熔断

image-20220411114750082
image-20220411114750082

测试:

provider

From: 元动力
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
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. http://localhost:9000/order/add/10open in new window
image-20220411115310796
image-20220411115310796
  1. 多次访问 http://localhost:9000/order/add/1open in new window
image-20220411115332277
image-20220411115332277

3.导致10也不能访问了

image-20220411115128870
image-20220411115128870

4.再过一会儿,半开状态

http://localhost:9000/order/add/10open in new window

image-20220411115423173
image-20220411115423173

Hyst rix 熔断机制,用于监控微服务调用情况,当失败的情况达到预定的阈值(5秒失败20次),会打开断路器,拒绝所有请求,直到服务恢复正常为止。

From: 元动力
1
2
3
circuitBreaker.sleepWindowInMilliseconds:监控时间
circuitBreaker.requestVolumeThreshold:失败次数
circuitBreaker.errorThresholdPercentage:失败率

提供者controller中

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
@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")

})

四、熔断监控**-运维

image-20200612072519498
image-20200612072519498

Hystrix 提供了 Hystrix-dashboard 功能,用于实时监控微服务运行状态。

但是Hystrix-dashboard只能监控一个微服务。

Netflix 还提供了 Turbine ,进行聚合监控。

image-20200612072621556
image-20200612072621556

Turbine聚合监控

1、搭建监控模块

a、 创建监控模块

创建hystrix-monitor模块,使用Turbine聚合监控多个Hystrix dashboard功能,

b、引入Turbine聚合监控起步依赖

From: 元动力
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
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
&lt;parent&gt;
&lt;artifactId&gt;spring-cloud-parent&lt;/artifactId&gt;
&lt;groupId&gt;com.ydlclass&lt;/groupId&gt;
&lt;version&gt;1.0.0&lt;/version&gt;
&lt;/parent&gt;
&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

&lt;artifactId&gt;hystrix-monitor&lt;/artifactId&gt;

&lt;properties&gt;
&lt;maven.compiler.source&gt;8&lt;/maven.compiler.source&gt;
&lt;maven.compiler.target&gt;8&lt;/maven.compiler.target&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-hystrix-dashboard&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-turbine&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
&lt;/dependency&gt;


&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;build&gt;
&lt;plugins&gt;
&lt;plugin&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
&lt;/plugin&gt;
&lt;/plugins&gt;
&lt;/build&gt;

&lt;/project&gt;

d、修改application.yml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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: "*"

e、创建启动类

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);
}
}

2、修改被监控模块

需要分别修改 hystrix-provider 和 hystrix-consumer 模块:

a、导入依赖:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-hystrix&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-hystrix-dashboard&lt;/artifactId&gt;
&lt;/dependency&gt;

b、配置Bean

此处为了方便,将其配置在启动类中。

From: 元动力
1
2
3
4
5
6
7
8
9
@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;
}

c、启动类上添加注解@EnableHystrixDashboard

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@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;
}
}

3、启动测试

a、启动服务:

  • eureka-server

  • hystrix-provider

  • hystrix-consumer

  • hystrix-monitor

b、访问:

在浏览器访问http://localhost:8769/hystrix/open in new window 进入Hystrix Dashboard界面

1585421193757
1585421193757

界面中输入监控的Url地址 http://localhost:8769/turbine.stream,监控时间间隔2000毫秒和title,如下图open in new window

image-20220411154048113
image-20220411154048113
  • 实心圆:它有颜色和大小之分,分别代表实例的监控程度和流量大小。如上图所示,它的健康度从绿色、黄色、橙色、红色递减。通过该实心圆的展示,我们就可以在大量的实例中快速的发现故障实例和高压力实例。
  • 曲线:用来记录 2 分钟内流量的相对变化,我们可以通过它来观察到流量的上升和下降趋势。

1-

第十一章、zuul路由网关

zuul核心人员走了两个,zuul2的研发过久,spring公司等不及,自己研发的Gateway网关。

https://github.com/Netflix/zuul/wikiopen in new window

第十二章 Gateway新一代网关

功能:路由+过滤

一、 概述

https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/open in new window

不使用

存在的问题:

From: 元动力
1
2
3
4
解决缺点:1客户端需要记录不同微服务地址,增加客户端的复杂性
2每个后台微服务都需要认证
3http 发请求,涉及到跨域
4后台新增微服务,不能动态知道地址
image-20220411172117753
image-20220411172117753

使用了网关的话:

• 网关旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。

• 在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求。

• 网关就是系统的入口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等

• 在目前的网关解决方案里,有Nginx+ Lua、Netflix Zuul/zuul2 、Spring Cloud Gateway等等

image-20220411172632761
image-20220411172632761

二、 快速入门

(1)搭建网关模块 api-gateway-server

(2)引入依赖:starter-gateway

From: 元动力
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
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
&lt;parent&gt;
&lt;artifactId&gt;gateway-parent&lt;/artifactId&gt;
&lt;groupId&gt;com.ydlclass&lt;/groupId&gt;
&lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
&lt;/parent&gt;
&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

&lt;artifactId&gt;api-gateway-server&lt;/artifactId&gt;


&lt;dependencies&gt;
&lt;!--引入gateway 网关--&gt;

&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-gateway&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;!-- eureka-client --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;/project&gt;

(3)编写启动类

ApiGatewayApp

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}

}

(4)编写配置文件

From: 元动力
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
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

(5)启动测试

http://localhost/goods/findById/2open in new window

image-20220411174704253
image-20220411174704253
image-20220411174649892
image-20220411174649892

三、 静态路由

From: 元动力
1
uri: http://localhost:8000/

四、动态路由

image-20220411180512597
image-20220411180512597

1、引入eureka-client配置

pom

From: 元动力
1
2
3
4
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
&lt;/dependency&gt;

yml

From: 元动力
1
2
3
4
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka

2、修改uri属性:uri: lb://服务名称

From: 元动力
1
uri: lb://eureka-provider

测试:

http://localhost/goods/findById/2open in new window

image-20220411180644761
image-20220411180644761

五、微服务名称配置**

From: 元动力
1
2
3
4
5
6
7
8
9
spring:
cloud:
# 网关配置
gateway:
# 微服务名称配置
discovery:
locator:
enabled: true # 设置为true 请求路径前可以添加微服务名称
lower-case-service-id: true # 允许为小写

测试:http://localhost/eureka-provider/goods/findById/2open in new window

image-20220411180853933
image-20220411180853933

六、过滤器

(1)两个维度:

内置过滤器 自定义过滤器

局部过滤器 全局过滤器

(2)过滤器种类:

内置局部过滤器

内置全局过滤器

自定义局部过滤器

自定义全局过滤器

image-20200612075159655
image-20200612075159655
  • Gateway 支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。

  • Gateway 提供两种过滤器方式:“pre”和“post”

    From: 元动力
    1
    2
    pre 过滤器,在转发之前执行,可以做参数校验、权限校验、流量监控、日志输出、协议转换等。
    post 过滤器,在响应之前执行,可以做响应内容、响应头的修改,日志的输出,流量监控等。
  • Gateway 还提供了两种类型过滤器

    From: 元动力
    1
    2
    GatewayFilter:局部过滤器,针对单个路由
    GlobalFilter :全局过滤器,针对所有路由

内置过滤器 局部过滤器:

From: 元动力
1
2
3
4
5
6
7
- id: gateway-provider
#uri: http://localhost:8001/
uri: lb://GATEWAY-PROVIDER
predicates:
- Path=/goods/**
filters:
- AddResponseHeader=foo, bar
image-20220411182635931
image-20220411182635931

内置过滤器 全局过滤器: route同级

From: 元动力
1
2
default-filters:
- AddResponseHeader=yld,itlils
image-20220411182831552
image-20220411182831552
image-20220411182910608
image-20220411182910608

拓展:

内置的过滤器工厂

这里简单将Spring Cloud Gateway内置的所有过滤器工厂整理成了一张表格。如下:

过滤器工厂作用参数
AddRequestHeader为原始请求添加HeaderHeader的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
AddResponseHeader为原始响应添加HeaderHeader的名称及值
DedupeResponseHeader剔除响应头中重复的值需要去重的Header名称及去重策略
Hystrix为路由引入Hystrix的断路器保护HystrixCommand的名称
FallbackHeaders为fallbackUri的请求头中添加具体的异常信息Header的名称
PrefixPath为原始请求路径添加前缀前缀路径
PreserveHostHeader为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter用于对请求限流,限流算法为令牌桶keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo将原始请求重定向到指定的URLhttp状态码及重定向的url
RemoveHopByHopHeadersFilter为原始请求删除IETF组织规定的一系列Header默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader为原始请求删除某个HeaderHeader名称
RemoveResponseHeader为原始响应删除某个HeaderHeader名称
RewritePath重写原始的请求路径原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader重写原始响应中的某个HeaderHeader名称,值的正则表达式,重写后的值
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,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar

为原始请求添加名为 X-Request-Foo ,值为 Bar 的请求头

2、AddRequestParameter GatewayFilter Factory

为原始请求添加请求参数及值,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=foo, bar

为原始请求添加名为foo,值为bar的参数,即:foo=bar

3、AddResponseHeader GatewayFilter Factory

为原始响应添加Header,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Foo, Bar

为原始响应添加名为 X-Request-Foo ,值为 Bar 的响应头

4、DedupeResponseHeader GatewayFilter Factory

DedupeResponseHeader可以根据配置的Header名称及去重策略剔除响应头中重复的值,这是Spring Cloud Greenwich SR2提供的新特性,低于这个版本无法使用。

我们在Gateway以及微服务上都设置了CORS(解决跨域)Header的话,如果不做任何配置,那么请求 -> 网关 -> 微服务,获得的CORS Header的值,就将会是这样的:

From: 元动力
1
2
Access-Control-Allow-Credentials: true, true
Access-Control-Allow-Origin: https://musk.mars, https://musk.mars

可以看到这两个Header的值都重复了,若想把这两个Header的值去重的话,就需要使用到DedupeResponseHeader,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
spring:
cloud:
gateway:
routes:
- id: dedupe_response_header_route
uri: https://example.org
filters:
# 若需要去重的Header有多个,使用空格分隔
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin

去重策略:

  • RETAIN_FIRST:默认值,保留第一个值
  • RETAIN_LAST:保留最后一个值
  • RETAIN_UNIQUE:保留所有唯一值,以它们第一次出现的顺序保留

若想对该过滤器工厂有个比较全面的了解的话,建议阅读该过滤器工厂的源码,因为源码里有详细的注释及示例,比官方文档写得还好:org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory

5、Hystrix GatewayFilter Factory

为路由引入Hystrix的断路器保护,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: https://example.org
filters:
- Hystrix=myCommandName

Hystrix是Spring Cloud第一代容错组件,不过已经进入维护模式,未来Hystrix会被Spring Cloud移除掉,取而代之的是Alibaba Sentinel/Resilience4J。所以本文不做详细介绍了,感兴趣的话可以参考官方文档:

6、FallbackHeaders GatewayFilter Factory

同样是对Hystrix的支持,上一小节所介绍的过滤器工厂支持一个配置参数:fallbackUri,该配置用于当发生异常时将请求转发到一个特定的uri上。而FallbackHeaders这个过滤工厂可以在转发请求到该uri时添加一个Header,这个Header的值为具体的异常信息。配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

这里也不做详细介绍了,感兴趣可以参考官方文档:

7、PrefixPath GatewayFilter Factory

为原始的请求路径添加一个前缀路径,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath

该配置使访问${GATEWAY_URL}/hello 会转发到https://example.org/mypath/hello

8、PreserveHostHeader GatewayFilter Factory

为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host Header。配置示例:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: preserve_host_route
uri: https://example.org
filters:
- PreserveHostHeader

如果不设置,那么名为 Host 的Header将由Http Client控制

9、RequestRateLimiter GatewayFilter Factory

用于对请求进行限流,限流算法为令牌桶。配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
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

由于另一篇文章中已经介绍过如何使用该过滤器工厂实现网关限流,所以这里就不再赘述了:

或者参考官方文档:

10、RedirectTo GatewayFilter Factory

将原始请求重定向到指定的Url,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: redirect_route
uri: https://example.org
filters:
- RedirectTo=302, https://acme.org

该配置使访问 ${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组织规定的一系列Header,默认删除的Header如下:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade

可以通过配置去指定仅删除哪些Header,配置示例:

From: 元动力
1
2
3
4
5
6
7
spring:
cloud:
gateway:
filter:
remove-hop-by-hop:
# 多个Header使用逗号(,)分隔
headers: Connection,Keep-Alive

12、RemoveRequestHeader GatewayFilter Factory

为原始请求删除某个Header,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveRequestHeader=X-Request-Foo

删除原始请求中名为 X-Request-Foo 的请求头

13、RemoveResponseHeader GatewayFilter Factory

为原始响应删除某个Header,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: removeresponseheader_route
uri: https://example.org
filters:
- RemoveResponseHeader=X-Response-Foo

删除原始响应中名为 X-Request-Foo 的响应头

14、RewritePath GatewayFilter Factory

通过正则表达式重写原始的请求路径,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: https://example.org
predicates:
- Path=/foo/**
filters:
# 参数1为原始路径的正则表达式,参数2为重写后路径的正则表达式
- RewritePath=/foo/(?&lt;segment&gt;.*), /$\{segment}

该配置使得访问 /foo/bar 时,会将路径重写为/bar 再进行转发,也就是会转发到 https://example.org/bar。需要注意的是:由于YAML语法,需用$\ 替换 $

15、RewriteResponseHeader GatewayFilter Factory

重写原始响应中的某个Header,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: rewriteresponseheader_route
uri: https://example.org
filters:
# 参数1为Header名称,参数2为值的正则表达式,参数3为重写后的值
- RewriteResponseHeader=X-Response-Foo, password=[^&amp;]+, password=***

该配置的意义在于:如果响应头中 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操作,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
routes:
- id: save_session
uri: https://example.org
predicates:
- Path=/foo/**
filters:
- SaveSession

主要用在那种像 Spring Session 延迟数据存储(数据不是立刻持久化)的,并希望在请求转发前确保session状态保存情况。如果你将Spring Secutiry于Spring Session集成使用,并想确保安全信息都传到下游机器,你就需要配置这个filter。

17、secureHeaders GatewayFilter Factory

secureHeaders过滤器工厂主要是参考了这篇博客open in new window中的建议,为原始响应添加了一系列起安全作用的响应头。默认会添加如下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

配置示例:

From: 元动力
1
2
3
4
5
6
7
spring:
cloud:
gateway:
filter:
secure-headers:
# 修改 X-Xss-Protection 的值为 2; mode=unblock
xss-protection-header: 2; mode=unblock

如果想禁用某些Header,可使用如下配置:

From: 元动力
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
filter:
secure-headers:
# 多个使用逗号(,)分隔
disable: frame-options,download-options

18、SetPath GatewayFilter Factory

修改原始的请求路径,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: https://example.org
predicates:
- Path=/foo/{segment}
filters:
- SetPath=/{segment}

该配置使访问 ${GATEWAY_URL}/foo/bar 时会转发到 https://example.org/bar ,也就是原本的/foo/bar被修改为了/bar

19、SetResponseHeader GatewayFilter Factory

修改原始响应中某个Header的值,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: https://example.org
filters:
- SetResponseHeader=X-Response-Foo, Bar

将原始响应中 X-Response-Foo 的值修改为 Bar

20、SetStatus GatewayFilter Factory

修改原始响应的状态码,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

SetStatusd的值可以是数字,也可以是字符串。但一定要是Spring HttpStatus 枚举类中的值。上面这两种配置都可以返回401这个HTTP状态码。

21、StripPrefix GatewayFilter Factory

用于截断原始请求的路径,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: http://nameservice
predicates:
- Path=/name/**
filters:
# 数字表示要截断的路径的数量
- StripPrefix=2

如上配置,如果请求的路径为 /name/bar/foo ,那么则会截断成/foo后进行转发 ,也就是会截断2个路径。

22、Retry GatewayFilter Factory

针对不同的响应进行重试,例如可以针对HTTP状态码进行重试,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
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

可配置如下参数:

  • retries:重试次数
  • statuses:需要重试的状态码,取值在 org.springframework.http.HttpStatus
  • methods:需要重试的请求方法,取值在 org.springframework.http.HttpMethod
  • series:HTTP状态码序列,取值在 org.springframework.http.HttpStatus.Series

23、RequestSize GatewayFilter Factory

设置允许接收最大请求包的大小,配置示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: request_size_route
uri: http://localhost:8080/upload
predicates:
- Path=/upload
filters:
- name: RequestSize
args:
# 单位为字节
maxSize: 5000000

如果请求包大小超过设置的值,则会返回 413 Payload Too Large以及一个errorMessage

24、Modify Request Body GatewayFilter Factory

在转发请求之前修改原始请求体内容,该过滤器工厂只能通过代码配置,不支持在配置文件中配置。代码示例:

From: 元动力
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
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_request_obj", r -&gt; r.host("*.rewriterequestobj.org")
.filters(f -&gt; f.prefixPath("/httpbin")
.modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
(exchange, s) -&gt; 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;
}
}

Tips:该过滤器工厂处于 BETA 状态,未来API可能会变化,生产环境请慎用

25、Modify Response Body GatewayFilter Factory

可用于修改原始响应体的内容,该过滤器工厂同样只能通过代码配置,不支持在配置文件中配置。代码示例:

From: 元动力
1
2
3
4
5
6
7
8
9
10
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_response_upper", r -&gt; r.host("*.rewriteresponseupper.org")
.filters(f -&gt; f.prefixPath("/httpbin")
.modifyResponseBody(String.class, String.class,
(exchange, s) -&gt; Mono.just(s.toUpperCase()))).uri(uri)
.build();
}

Tips:该过滤器工厂处于 BETA 状态,未来API可能会变化,生产环境请慎用

26、Default Filters

Default Filters用于为所有路由添加过滤器工厂,也就是说通过Default Filter所配置的过滤器工厂会作用到所有的路由上。配置示例:

From: 元动力
1
2
3
4
5
6
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Foo, Default-Bar
- PrefixPath=/httpbin

官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html#_gatewayfilter_factoriesopen in new window

自定义过滤器

1、自定义 局部过滤器

麻烦,很少。

From: 元动力
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
//
// 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&lt;StripPrefixGatewayFilterFactory.Config&gt; {
public static final String PARTS_KEY = "parts";

public StripPrefixGatewayFilterFactory() {
super(StripPrefixGatewayFilterFactory.Config.class);
}

public List&lt;String&gt; shortcutFieldOrder() {
return Arrays.asList("parts");
}

public GatewayFilter apply(StripPrefixGatewayFilterFactory.Config config) {
return new GatewayFilter() {
public Mono&lt;Void&gt; 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 &lt; originalParts.length; ++i) {
if (i &gt;= config.getParts()) {
if (newPath.length() &gt; 1) {
newPath.append('/');
}

newPath.append(originalParts[i]);
}
}

if (newPath.length() &gt; 1 &amp;&amp; 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;
}
}
}

2、自定义 全局过滤器

需求:1黑客ip,直接给你拒接掉。

​ 2某些请求路径 如“goods/findById”,危险操作,记录日志。

自定义全局过滤器步骤:

From: 元动力
1
2
3
1. 定义类实现 GlobalFilter 和 Ordered接口
2. 复写方法
3. 完成逻辑处理

IpFilter

From: 元动力
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
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&lt;Void&gt; 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;
}
}

UrlFilter

From: 元动力
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
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&lt;Void&gt; 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、ipfilter

image-20220411191527069
image-20220411191527069

把自己的ip设置为拒绝访问的话

image-20220411191641610
image-20220411191641610

2、

image-20220411192132436
image-20220411192132436

第十三章 分布式配置中心

13-16组件,超大型公司才会用到

国内BATJ,或者国外FLAG:facebook linkedin amazon google

处理大规模微服务的技术。

一、Config 概述

image-20220418081959807
image-20220418081959807

Spring Cloud Config 解决了在分布式场景下多环境配置文件的管理和维护。

好处:

• 集中管理配置文件

• 不同环境不同配置,动态化的配置更新

• 配置信息改变时,不需要重启即可更新配置信息到服务

二、Config 快速入门

image-20220418082533367
image-20220418082533367

config-server:

  1. 使用gitee创建远程仓库,上传配置文件

    config-dev.yml

  2. 搭建 config-server 模块

    ConfigServerApp

    From: 元动力
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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);
    }
    }
  3. 导入 config-server 依赖

    pom

    From: 元动力
    1
    2
    3
    4
    5
     &lt;!-- config-server --&gt;
    &lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-config-server&lt;/artifactId&gt;
    &lt;/dependency&gt;
  4. 编写配置,设置 gitee 远程仓库地址

    application.yml

    From: 元动力
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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 # 分支配置
  5. 测试访问远程配置文件

    http://localhost:9527/master/provider-dev.ymlopen in new window

image-20220418083733698
image-20220418083733698

config-client: provider

  1. 导入 starter-config 依赖

    From: 元动力
    1
    2
    3
    4
    5
    &lt;!--config client --&gt;
    &lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-config&lt;/artifactId&gt;
    &lt;/dependency&gt;
  2. 配置config server 地址,读取配置文件名称等信息

    bootstrap.yml

    From: 元动力
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 配置config-server地址
    # 配置获得配置文件的名称等信息
    spring:
    cloud:
    config:
    # 配置config-server地址
    uri: http://localhost:9527
    # 配置获得配置文件的名称等信息
    name: provider # 文件名
    profile: dev # profile指定, config-dev.yml
    label: master # 分支
  3. 获取配置值

    GoodsController

    From: 元动力
    1
    2
    3
    4
    @Value("${ydlclass}")
    private String ydlclass;

    goods.setTitle(goods.getTitle()+"|端口号:"+port+"|ydlclass"+ydlclass);
  4. 启动测试

    http://localhost:8000/goods/findById/9open in new window

image-20220418084608302
image-20220418084608302

Config 客户端刷新

image-20220418085057098
image-20220418085057098
  1. 在 config 客户端引入 actuator 依赖

    From: 元动力
    1
    2
    3
    4
    &lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
    &lt;/dependency&gt;
  2. 获取配置信息类上,添加 @RefreshScope 注解

    GoodsController

    From: 元动力
    1
    @RefreshScope // 开启刷新功能
  3. 添加配置

    bootstrap.yml

From: 元动力
1
2
3
4
5
management:
endpoints:
web:
exposure:
include: '*'
  1. 使用curl工具发送post请求

    curl -X POST http://localhost:8000/actuator/refreshopen in new window

    image-20220418085907801
    image-20220418085907801

测试: 值已经变了

image-20220418085930400
image-20220418085930400

三、Config 集成Eureka

image-20220418090254336
image-20220418090254336

config-client配置:

pom

From: 元动力
1
2
3
4
5
&lt;!-- eureka-client --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
&lt;/dependency&gt;

ProviderApp

From: 元动力
1
@EnableEurekaClient

bootstrap.yml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 配置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: '*'

config-server配置:

pom

From: 元动力
1
2
3
4
5
&lt;!-- eureka-client --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
&lt;/dependency&gt;

ConfigServerApp

From: 元动力
1
@EnableEurekaClient

application.yml

From: 元动力
1
2
3
4
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka

测试:

image-20220418093808497
image-20220418093808497

第十四章 SpringCloud Bus消息总线

远程的配置文件更新了,运维只需要发一个请求,所有用到这个配置文件的几百个应用更新了。

一、Bus 概述

image-20220418094548555
image-20220418094548555

Spring Cloud Bus 是用轻量的消息中间件将分布式的节点连接起来,可以用于广播配置文件的更改或者服务的监控管理。关键的思想就是,消息总线可以为微服务做监控,也可以实现应用程序之间相通信。

Spring Cloud Bus 可选的消息中间件包括 RabbitMQ 和 Kafka 。

二、RabbitMQ 回顾

message queue

异步、解耦、削峰填谷

image-20200613074044506
image-20200613074044506
image-20200613074052830
image-20200613074052830

RabbitMQ 提供了 6 种工作模式:简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing

路由模式、Topics 主题模式、RPC 远程调用模式(远程调用,不太算 MQ;暂不作介绍)。

image-20200613074109383
image-20200613074109383

三、Bus 快速入门-运维

image-20220418094548555
image-20220418094548555

1、分别在 config-server 和 config-client中引入 bus依赖:bus-amqp

From: 元动力
1
2
3
4
5
6
7
8
9
&lt;!-- bus --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-bus-amqp&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;

2、分别在 config-server 和 config-client中配置 RabbitMQ

server 的配置文件:

From: 元动力
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
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'

client的配置文件 bootstrap.yml

From: 元动力
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
# 配置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. 在config-server中设置暴露监控断点:bus-refresh

    From: 元动力
    1
    2
    3
    4
    5
    6
    # 暴露bus的刷新端点
    management:
    endpoints:
    web:
    exposure:
    include: 'bus-refresh'

bus自动在mq中建立交换机

image-20220418095857082
image-20220418095857082

只要微服务起起来,自动在mq中,建立队列,和交换机绑定

image-20220418100013437
image-20220418100013437

启动测试

1.git配置改变

image-20220418104835631
image-20220418104835631

2.往配置中心发请求

curl -X POST http://localhost:9527/actuator/bus-refreshopen in new window

image-20220418104622908
image-20220418104622908

3.微服务自动重启

image-20220418104702500
image-20220418104702500

4.访问微服务,发现配置已经改变

image-20220418104819862
image-20220418104819862

第十五章 SpringCloud Stream消息驱动

一、Stream 概述

image-20220418111329857
image-20220418111329857

1.Spring Cloud Stream 是一个构建消息驱动微服务应用的框架。

2.Stream 解决了开发人员无感知的使用消息中间件的问题,因为Stream对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至于动态的切换中间件,使得微服务开发的高度解耦,服务可以关注更多自己的业务流程。

3.Spring Cloud Stream目前支持两种消息中间件RabbitMQ和Kafka

就像jdbc一样

image-20220418111643163
image-20220418111643163

Stream 组件

image-20200613074759833
image-20200613074759833

Spring Cloud Stream 构建的应用程序与消息中间件之间是通过绑定器 Binder 相关联的。绑定器对于应用程序而言起到了隔离作用, 它使得不同消息中间件的实现细节对应用程序来说是透明的。

binding 是我们通过配置把应用和spring cloud stream 的 binder 绑定在一起

output:发送消息 Channel,内置 Source接口

input:接收消息 Channel,内置 Sink接口

二、Stream 消息生产者

pom

From: 元动力
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
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
&lt;parent&gt;
&lt;artifactId&gt;stream-parent&lt;/artifactId&gt;
&lt;groupId&gt;com.ydlclass&lt;/groupId&gt;
&lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
&lt;/parent&gt;
&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

&lt;artifactId&gt;stream-producer&lt;/artifactId&gt;


&lt;dependencies&gt;

&lt;!--spring boot web--&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;


&lt;!-- stream --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-stream-rabbit&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;/dependencies&gt;

&lt;/project&gt;

ProducerApp

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
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);
}
}

application.yml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 # 消息目的地

消息发送类 MessageProducer

From: 元动力
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
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 确实发了消息了!");
}
}

调用的controller ProducerController

From: 元动力
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
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!";
}
}

image-20220418114016165
image-20220418114016165

三、Stream 消息消费者

pom

From: 元动力
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
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
&lt;parent&gt;
&lt;artifactId&gt;stream-parent&lt;/artifactId&gt;
&lt;groupId&gt;com.ydlclass&lt;/groupId&gt;
&lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
&lt;/parent&gt;
&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

&lt;artifactId&gt;stream-consumer&lt;/artifactId&gt;


&lt;dependencies&gt;

&lt;!--spring boot web--&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;


&lt;!-- stream --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-stream-rabbit&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;/dependencies&gt;
&lt;/project&gt;

ConsumerApp

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
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);
}
}

application.yml

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 # 消息目的地

消息接收类 MessageListener

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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());
}
}

测试

image-20220418154602979
image-20220418154602979

第十六章 SpringCloud Sleuth分布式请求链路追踪

一、概述

image-20220418155007573
image-20220418155007573

Spring Cloud Sleuth 其实是一个工具,它在整个分布式系统中能跟踪一个用户请求的过程,捕获这些跟踪数据,就能构建微服务的整个调用链的视图,这是调试和监控微服务的关键工具。

From: 元动力
1
2
3
耗时分析 
可视化错误
链路优化

Zipkin 是 Twitter 的一个开源项目,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。

二、快速入门

  1. 安装启动zipkin。 java –jar zipkin.jar

    image-20220418185033323
    image-20220418185033323
  2. 访问zipkin web界面。 http://localhost:9411/open in new window

    image-20200613080015686
    image-20200613080015686
  3. 在服务提供方和消费方分别引入 sleuth 和 zipkin 依赖

    From: 元动力
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    &lt;!-- sleuth-zipkin --&gt;
    &lt;!--&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-sleuth&lt;/artifactId&gt;
    &lt;/dependency&gt;--&gt;

    &lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-zipkin&lt;/artifactId&gt;
    &lt;/dependency&gt;
  4. 分别配置服务提供方和消费方。

    provider

    From: 元动力
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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 百分之十。

    consumer

    From: 元动力
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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
  5. 启动,测试

image-20220418160537372
image-20220418160537372

调用关系图也能展现

image-20220418160602065
image-20220418160602065

本站由 钟意 使用 Stellar 1.28.1 主题创建。
又拍云 提供CDN加速/云存储服务
vercel 提供托管服务
湘ICP备2023019799号-1
总访问 次 | 本页访问