spring cloud 2

1服务调用组件Feign

1.1概述

  • Feign 是一个声明式的 REST 客户端,它用了基于接口的注解方式,很方便实现客户端配置。
  • Feign 最初由 Netflix 公司提供,但不支持SpringMVC注解,后由 SpringCloud 对其封装,支持了SpringMVC注解,让使用者更易于接受。

image-20200612065053765

1.2快速入门

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

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
  2. 编写Feign调用接口。复制粘贴被调方的conreoller方法,加上类路径。

    GoodsFeignClient

    1
    2
    3
    4
    5
    6
    7
    @FeignClient(value = "FEIGN-PROVIDER")
    public interface GoodsFeignClient {

    @GetMapping("/goods/findOne/{id}")
    public Goods findGoodsById(@PathVariable("id") int id);

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

  4. 测试调用

    OrderController

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
package com.itheima.consumer.controller;


import com.itheima.consumer.domain.Goods;
import com.itheima.consumer.feign.GoodsFeignClient;
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;

@Autowired
private GoodsFeignClient goodsFeignClient;

@GetMapping("/goods/{id}")
public Goods findGoodsById(@PathVariable("id") int id){

/*
String url = "http://FEIGN-PROVIDER/goods/findOne/"+id;
// 3. 调用方法
Goods goods = restTemplate.getForObject(url, Goods.class);

return goods;*/

Goods goods = goodsFeignClient.findGoodsById(id);

return goods;
}

}

1.3其他设置

1.3.1超时设置

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

• Ribbon默认1秒超时。

• 超时配置: yml中

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

1.3.2日志记录(xml和配置类都需要)

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

1
2
3
4
# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
logging:
level:
com.itheima: debug #扫描com.itheima包下的用到Feign的 日志级别 debug

• 定义Feign日志级别Bean

FeignLogConfig

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.itheima.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;
}
}

• 启用该Bean:

GoodsFeignClient(定义的接口)

1
@FeignClient(value = "FEIGN-PROVIDER",configuration = FeignLogConfig.class)

2熔断器Hystrix 保险丝

2.1概述

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

image-20200612070112985

image-20200612070124485

image-20200612070137309

image-20200612070204182

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

Hystix 主要功能

隔离 面试

  1. 线程池隔离

    image-20200612070319271

    image-20200612070518723

image-20200612070440629

image-20200612070617944

2.信号量隔离

image-20200612070552032

降级:异常,超时

image-20200612070712531

熔断

image-20200612070738441

限流

image-20200612070750691

2.2服务降级

服务提供方

image-20200612071029896

  1. 在服务提供方,引入 hystrix 依赖

    1
    2
    3
    4
    5
    <!-- hystrix -->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
  2. 定义降级方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * 定义降级方法:
    * 1. 方法的返回值需要和原方法一样
    * 2. 方法的参数需要和原方法一样
    */
    public Goods findOne_fallback(int id){
    Goods goods = new Goods();
    goods.setTitle("降级了~~~");

    return goods;
    }
  3. 使用 @HystrixCommand 注解配置降级方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @GetMapping("/findOne/{id}")
    @HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {
    //设置Hystrix的超时时间,默认1s @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    })
    public Goods findOne(@PathVariable("id") int id){

    //1.造个异常
    int i = 3/0;
    try {
    //2. 休眠2秒
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    Goods goods = goodsService.findOne(id);

    goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
    return goods;
    }
  4. 在启动类上开启Hystrix功能:@EnableCircuitBreaker

服务消费方

image-20200612071600683

  1. feign 组件已经集成了 hystrix 组件。

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

    GoodsFeignClientFallback

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.itheima.consumer.feign;

    import com.itheima.consumer.domain.Goods;
    import org.springframework.stereotype.Component;

    /**
    * Feign 客户端的降级处理类
    * 1. 定义类 实现 Feign 客户端接口
    * 2. 使用@Component注解将该类的Bean加入SpringIOC容器
    */
    @Component
    public class GoodsFeignClientFallback implements GoodsFeignClient {
    @Override
    public Goods findGoodsById(int id) {
    Goods goods = new Goods();
    goods.setTitle("又被降级了~~~");
    return goods;
    }
    }

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

    GoodsFeignClient

    1
    2
    3
    4
    5
    6
    7
    8
    @FeignClient(value = "HYSTRIX-PROVIDER",fallback = GoodsFeignClientFallback.class)
    public interface GoodsFeignClient {


    @GetMapping("/goods/findOne/{id}")
    public Goods findGoodsById(@PathVariable("id") int id);

    }
  4. 配置开启

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

2.3熔断 保险丝

image-20200612072022642

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

1
2
3
circuitBreaker.sleepWindowInMilliseconds:保险丝打开后 进入半开状态的监控时间
circuitBreaker.requestVolumeThreshold:失败次数
circuitBreaker.errorThresholdPercentage:失败率

提供者controller中

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

})

2.4熔断监控-运维

image-20200612072519498

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

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

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

image-20200612072621556

Turbine聚合监控

一、搭建监控模块

1. 创建监控模块

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

2. 引入Turbine聚合监控起步依赖

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
<?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>hystrix-parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>hystrix-monitor</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

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

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

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


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

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

3. 修改application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
application.name: hystrix-monitor
server:
port: 8769
turbine:
combine-host-port: true
# 配置需要监控的服务名称列表
app-config: hystrix-provider,hystrix-consumer
cluster-name-expression: "'default'"
aggregator:
cluster-config: default
#instanceUrlSuffix: /actuator/hystrix.stream
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/

4. 创建启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootApplication
@EnableEurekaClient

@EnableTurbine //开启Turbine 很聚合监控功能
@EnableHystrixDashboard //开启Hystrix仪表盘监控功能
public class HystrixMonitorApp {

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

}

二、修改被监控模块

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

1、导入依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

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

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

2、配置Bean

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

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

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

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

三、启动测试

1、启动服务:

  • eureka-server

  • hystrix-provider

  • hystrix-consumer

  • hystrix-monitor

2、访问:

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

1585421193757

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

1585421278837

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

1585421278837

3网关gateway 路由+过滤

3.1概述

切面

image-20200612073001120

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

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

1
2
3
4
存在的问题:
客户端多次请求不同的微服务,增加客户端的复杂性
认证复杂,每个服务都要进行认证
http请求不同服务次数增加,性能不高

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

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

3.2快速入门

image-20200612073151987

  1. 搭建网关模块 api-gateway-server

  2. 引入依赖:starter-gateway

    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
    <?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>gateway-parent</artifactId>
    <groupId>com.itheima</groupId>
    <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>api-gateway-server</artifactId>


    <dependencies>
    <!--引入gateway 网关-->

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

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

    </project>
  3. 编写启动类

    ApiGatewayApp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.itheima.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. 编写配置文件

    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
    server:
    port: 80 #服务端口号

    spring:
    application:
    name: api-gateway-server #服务名称

    cloud:
    # 网关配置
    gateway:
    # 路由配置:转发规则
    routes: #集合。
    # id: 唯一标识。默认是一个UUID
    # uri: 转发路径
    # predicates: 条件,用于请求网关路径的匹配规则
    # filters:配置局部过滤器的

    - id: gateway-provider
    # 静态路由
    # uri: http://localhost:8001/
    # 动态路由
    uri: lb://eureka-provider
    #路由的路径
    predicates:
    - Path=/goods/**
    - id: gateway-consumer
    # uri: http://localhost:9000
    uri: lb://eureka-consumer
    predicates:
    - Path=/order/**


    #注册到哪个注册中心
    eureka:
    client:
    service-url:
    defaultZone: http://localhost:8761/eureka
  5. 启动测试

3.3静态路由

1
uri: http://localhost:8001/

3.4动态路由

image-20200612074237685

3.4.2修改cloud网管里的动态路由uri属性:uri: lb://服务名称

1
uri: lb://GATEWAY-PROVIDER

3.4.2引入eureka-client配置

pom

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

yml

1
2
3
4
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka

3.5微服务名称配置 了解

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

3.6过滤器

内置过滤器 自定义过滤器

局部过滤器 全局过滤器

1内置局部过滤器

2内置全局过滤器

3自定义局部过滤器 难

4自定义全局过滤器

image-20200612075159655

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

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

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

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

内置过滤器 局部过滤器:

1
2
3
4
5
6
7
8
- id: gateway-provider
#uri: http://localhost:8001/
uri: lb://GATEWAY-PROVIDER
predicates:
- Path=/goods/**
#过滤器
filters:
- AddResponseHeader=foo, bar

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

1
2
default-filters:
- AddResponseHeader=xiqinling,lizhiyong

拓展:

内置的过滤器工厂

这里简单将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,配置示例:

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

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

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,配置示例:

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的值,就将会是这样的:

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

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

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的断路器保护,配置示例:

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的值为具体的异常信息。配置示例:

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

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

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。配置示例:

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

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

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,配置示例:

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

为原始请求删除IETF组织规定的一系列Header,默认删除的Header如下:

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

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

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,配置示例:

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,配置示例:

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

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

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/(?<segment>.*), /$\{segment}

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

15、RewriteResponseHeader GatewayFilter Factory

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

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=[^&]+, 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操作,配置示例:

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

配置示例:

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,可使用如下配置:

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

18、SetPath GatewayFilter Factory

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

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的值,配置示例:

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

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

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

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

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状态码进行重试,配置示例:

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

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

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

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

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 -> r.host("*.rewriterequestobj.org")
.filters(f -> f.prefixPath("/httpbin")
.modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
(exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
.build();
}

static class Hello {
String message;

public Hello() { }

public Hello(String message) {
this.message = message;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

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

25、Modify Response Body GatewayFilter Factory

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

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

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

26、Default Filters

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

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_factories

3.6.1局部过滤器

GatewayFilter 局部过滤器,是针对单个路由的过滤器。

在Spring Cloud Gateway 组件中提供了大量内置的局部过滤器,对请求和响应做过滤操作。

遵循约定大于配置的思想,只需要在配置文件配置局部过滤器名称,并为其指定对应的值,就可以让其生效

1
2
3
4
5
6
7
8
9
- id: gateway-provider
# 静态路由
# uri: http://localhost:8001/
# 动态路由
uri: lb://GATEWAY-PROVIDER
predicates:
- Path=/goods/**
filters:
- AddRequestParameter=username,zhangsan

3.6.2全局过滤器

自定义 全局过滤器

自定义 局部过滤器

image-20200612075259636

GlobalFilter 全局过滤器,不需要在配置文件中配置,系统初始化时加载,并作用在每个路由上。

Spring Cloud Gateway 核心的功能也是通过内置的全局过滤器来完成。

自定义全局过滤器步骤:

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

MyFilter

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
package com.itheima.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.InetAddress;
import java.net.InetSocketAddress;
import static org.springframework.http.HttpStatus.FORBIDDEN;


@Component
public class MyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //chain 链条
System.out.println("自定义全局过滤器执行了~~~");
ServerHttpRequest request = exchange.getRequest();//获取请求
ServerHttpResponse response = exchange.getResponse();//获取响应
InetSocketAddress remoteAddress = request.getRemoteAddress();
String hostString = remoteAddress.getHostString();
System.out.println("hostString:"+hostString);
String hostName = remoteAddress.getHostName();
System.out.println("hostName:"+hostName);
InetAddress address = remoteAddress.getAddress();
System.out.println("address:"+address);
//这个地址包含了.....的话 就设置状态码403 禁止该地址访问 response.setComplete() 发送结束标记
if (hostString.contains("1.1.1.1")){
response.setStatusCode(FORBIDDEN);
return response.setComplete();
}
return chain.filter(exchange);//放行
}

/**
* 过滤器排序
* @return 数值越小 越先执行
*/
@Override
public int getOrder() {
return 1;
}
}

ip过滤 淘宝ip 1.1.1.1

危险操作 记录日志

http 响应码

2** 访问成功 200

3** 访问成功,但是做额外操作 303 重定向

4** 客户端请求有问题 404

5** 服务端有问题 500 501 503

作业:

1使用log4l self4j logbak 分别在项目一中把 info error打出来

2隔离 线程池 信号量 默认参数 怎么改

3.7 使用log4l

log4j.properties配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
### 设置###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/log.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

导入pom配置(log4j的依赖)

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>

在类里使用

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
package com.itheima.gateway.filter;
import org.apache.log4j.Logger;
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;
@Component
public class MyOperationFilter implements GlobalFilter, Ordered {
//指定log4j加载的类(如本类用到log4j 就填本类的类名.class)
private static Logger logger = Logger.getLogger(MyOperationFilter.class);

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
URI uri = request.getURI();
System.out.println("URI:"+uri);
//日志 info
logger.info("URI:"+uri);
System.out.println("Host:"+uri.getHost());
logger.info("Host:"+uri.getHost());
System.out.println("Path::"+uri.getPath());
logger.info("Path::"+uri.getPath());
if (uri.toString().contains("goods")){
System.err.println("警告 访问了Goods");
// 日志 警告
logger.warn("警告:"+uri.toString()+"来访问了");
}

return chain.filter(exchange);
}


@Override
public int getOrder() {
return 2;
}
}

总结:

1feign

​ 导包 @EnableFeignClients 定义feign接口 复制粘贴

辅助功能

超时时间

1
2
3
ribbon:
ConnectTimeout: 1000 # 连接超时时间 默认1s
ReadTimeout: 3000 # 逻辑处理的超时时间 默认1s

日志记录

​ 配置文件 配置类 feign客户端配置

2hystrix 熔断器 保险丝

隔离 线程池隔离 信号量隔离

降级

提供方 导包 主启动类 降级方法 HystrixCommand

调用方 配置文件开启 feign接口实现类 告诉feign(fallback = GoodsFeignFallbak.class)

熔断

image-20210123155839975

限流 不用

3网关 gateway 路由+过滤

不用网关问题

image-20210123155953641

网关架构图

image-20210123160022613

路由:

配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
routes: #集合。
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
# filters:配置局部过滤器的
- id: gateway-provider
# 静态路由
#uri: http://localhost:8000/
# 动态路由
uri: lb://EUREKA-PROVIDER
predicates:
- Path=/goods/**
filters:
- AddResponseHeader=a,b

过滤:

内置局部

1
2
filters:
- AddResponseHeader=a,b

内置全局

1
2
default-filters:
- AddResponseHeader=c,d

自定义局部 *

自定义全局 重点

​ 实现两个接口 gloableFilters Ordered

ip过滤 淘宝ip 1.1.1.1

危险操作 记录日志