Spring Cloud

Spring Cloud

RestTemplate

RestTemplate是一个HTTP客户端,使用它我们可以方便的调用HTTP接口,支持GET、POST、PUT、DELETE等方法。

实例

1.注册RestTemplate实例

1
2
3
4
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

2.通过restTemplate发送http来远程调用

1
2
3
4
5
6
7
8
9
10
11
12
13
@Autowired
private RestTemplate restTemplate;

public Order queryOrderById(Long orderId) {
// 查询订单
Order order = orderMapper.findById(orderId);
// 用restTemplate发送get请求查询user信息
String url = "http://localhost:8081/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
order.setUser(user);
// 返回
return order;
}

Eureka注册中心

注册中心:在微服务架构中往往会有一个注册中心,每个微服务都会向注册中心去注册自己的地址及端口信息,注册中心维护着服务名称与服务实例的对应关系。每个微服务都会定时从注册中心获取服务列表,同时汇报自己的运行情况,这样当有的服务需要调用其他服务时,就可以从自己获取到的服务列表中获取实例地址进行调用

Eureka架构中的三个角色
  • 服务注册中心

Eureka的服务端应用,提供服务注册和发现功能

  • 服务提供者

提供服务的应用,可以是SpringBoot应用,也可以是其它任意技术实现,只要对外提供的是Rest风格服务即可。

  • 服务消费者

消费应用从注册中心获取服务列表,从而得知每个服务方的信息,知道去哪里调用服务方。

搭建Eureka
创建服务端
  1. 导入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>2021.0.6</spring-cloud.version>
    </properties>

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

    <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>
  2. 在启动类上加@EnableEurekaServer注解

    1
    2
    3
    4
    5
    6
    7
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaApplication {
    public static void main(String[] args) {
    SpringApplication.run(EurekaApplication.class, args);
    }
    }
  3. 编写配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    server:
    port: 8081
    spring:
    application:
    name: eureka-server
    eureka:
    instance:
    hostname: localhost #指定主机地址
    client:
    fetch-registry: false #指定是否要从注册中心获取服务(注册中心不需要开启)
    register-with-eureka: false #指定是否要注册到注册中心(注册中心不需要开启)
    server:
    enable-self-preservation: false #关闭保护模式
  4. 通过localhost:8081访问注册中心界面

创建客户端
  1. 导入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>2021.0.6</spring-cloud.version>
    </properties>

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

    <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>
  2. 在启动类上加@EnableEurekaClient注解

  3. 编写配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    server:
    port: 8082 #运行端口号
    spring:
    application:
    name: eureka-client #服务名称
    eureka:
    client:
    register-with-eureka: true #注册到Eureka的注册中心
    fetch-registry: true #获取注册实例列表
    service-url:
    defaultZone: http://localhost:8081/eureka/ #配置注册中心地址
服务发现
  1. 在注册RestTemplate上加上@LoadTemplate注解

    1
    2
    3
    4
    5
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
    return new RestTemplate();
    }
  2. 请求的url中用服务名称替换ip

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Autowired
    private RestTemplate restTemplate;

    public Order queryOrderById(Long orderId) {
    // 1.查询订单
    Order order = orderMapper.findById(orderId);
    //用restTemplate发送http请求查询user信息
    String url = "http://userservice/user/" + order.getUserId();
    User user = restTemplate.getForObject(url, User.class);
    order.setUser(user);
    // 4.返回
    return order;
    }
常用配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
eureka:
client: #eureka客户端配置
register-with-eureka: true #是否将自己注册到eureka服务端上去
fetch-registry: true #是否获取eureka服务端上注册的服务列表
service-url:
defaultZone: http://localhost:8001/eureka/ # 指定注册中心地址
enabled: true # 启用eureka客户端
registry-fetch-interval-seconds: 30 #定义去eureka服务端获取服务列表的时间间隔
instance: #eureka客户端实例配置
lease-renewal-interval-in-seconds: 30 #定义服务多久去注册中心续约
lease-expiration-duration-in-seconds: 90 #定义服务多久不去续约认为服务失效
metadata-map:
zone: jiangsu #所在区域
hostname: localhost #服务主机名称
prefer-ip-address: false #是否优先使用ip来作为主机名
server: #eureka服务端配置
enable-self-preservation: false #关闭eureka服务端的保护机制

Ribbon负载均衡

负载均衡可以增加系统的可用性和扩展性

在注入RestTemplate时加上@LoadBalanced注解开启负载均衡

image-20210713224517686

image-20210713224724673

流程

  • LoadBalanceInterceptor拦截器获取请求,RibbonLoadBalancerClient从url中获取服务名称
  • DynamicServerListLoadBalancer根据服务名称拉取eureka服务列表
  • IRule根据负载均衡策略选取一个
  • RibbonLoadBalancerClient将url中的服务名称替换为ip端口,再发起请求
自定义负载均衡策略

一般使用默认策略(ZoneAvoidanceRule)

方式一(全局)

  • 导入坐标
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
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.6</spring-cloud.version>
</properties>

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

<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>
  • 注册IRule
1
2
3
4
@Bean
public IRule randomRule() {
return new RandomRule();
}

方式二(局部)

  • 在application.yml中添加配置
1
2
3
userservice:  #指定服务名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
开启饥饿加载

Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。

而饥饿加载则会在项目启动时创建,降低第一次访问的耗时

1
2
3
4
ribbon:
eager-load:
enabled: true
clients: userservice #加载的客户端
常用配置

此为全局配置,局部配置要服务名:ribbon下配置

1
2
3
4
5
6
7
ribbon:
ConnectTimeout: 1000 #服务请求连接超时时间(毫秒)
ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
OkToRetryOnAllOperations: true #对超时请求启用重试机制
MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数
MaxAutoRetries: 1 # 切换实例后重试最大次数
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法

Nacos注册中心

windows安装
  • 下载 github

  • 解压

  • bin目录下运行startup.cmd

    1
    startup.cmd -m standalone
  • 默认账号密码nacos

  • 访问http://localhost:8848/nacos

服务注册
  • 父工程导入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    <!--alibaba依赖管理-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.6.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
  • 项目中导入依赖

    1
    2
    3
    4
    5
    <!--nacos服务发现-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
  • 编写配置

    1
    2
    3
    4
    5
    6
    spring: 
    application:
    name: userservice
    cloud:
    nacos:
    server-addr: localhost:8848
配置集群

image-20210713232522531

  • 修改配置

    1
    2
    3
    4
    5
    6
    spring:
    cloud:
    nacos:
    server-addr: localhost:8848
    discovery:
    cluster-name: HZ # 集群名称
同集群优先的负载均衡

Nacos中提供了一个NacosRule的实现,可以优先从同集群中挑选实例

  • 修改配置文件application.yml

    1
    2
    3
    userservice:	#服务名称
    ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
权重优先的负载均衡

权重越高,优先级越高

权重

  • 通过编辑来设置权重
给微服务配置namespace

只有同一个命名空间的服务才能访问

通过namespace来实现环境隔离功能

  • 通过控制台来创建命名空间

命名空间

  • 配置文件中设置所在的命名空间

    1
    2
    3
    4
    5
    6
    7
    spring:
    cloud:
    nacos:
    server-addr: localhost:8848
    discovery:
    cluster-name: HZ # 集群名称
    namespace: c36cee20-482c-4675-a115-5e84b8f53882 #命名空间
设置实例类型

实例分为临时实例和非临时实例

临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型

非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例

设置为非临时实例

1
2
3
4
5
spring:
cloud:
nacos:
discovery:
ephemeral: false # 设置为非临时实例

Nacos和Eureka比较

  • Nacos与eureka的共同点
    • 都支持服务注册和服务拉取
    • 都支持服务提供者心跳方式做健康检测
  • Nacos与Eureka的区别
    • Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
    • Nacos临时实例心跳不正常会被剔除,非临时实例不会被剔除
    • Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
    • Nacos集群默认采用AP方式(高可用),当集群中存在非临时实例时,采用CP模式(强一致);Eureka采用AP方式

Nacos配置管理

在nacos中添加配置

项目的核心配置,需要热更新的配置才有放到nacos管理的必要

统一配置

配置信息

从nacos中拉取配置
  • 导入配置管理坐标

    1
    2
    3
    4
    5
    <!--nacos配置管理依赖-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
  • 添加bootstrap.yml配置文件

    bootstrap.yml会在application.yml之前被读取,在bootstrap中配置服务名称、环境、nacos地址等信息来读取配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    spring:
    application:
    name: userservice # 服务名称
    profiles:
    active: dev #开发环境,这里是dev
    cloud:
    nacos:
    discovery:
    server-addr: localhost:8848 #Nacos地址
    config:
    server-addr: localhost:8848 #Nacos地址
    file-extension: yaml #这里我们获取的yaml格式的配置

    最终会定位到userservice-dev.yaml配置中

    记得把application.yml中相同的配置注释掉

  • 可以通过@Value注解来读取nacos中的配置信息

    1
    2
    @Value("${pattern.dataformat}")
    private String dataformat;
配置热更新

nacos中的配置改变后立马更新而不需要重启服务

方式一(配置少时)

  • 在类上加@RefreshScope注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @RestController
    @RequestMapping("/user")
    @RefreshScope
    public class UserController {

    @Autowired
    private UserService userService;

    @Value("${pattern.dataformat}")
    private String dataformat;

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

方式二(配置多时,推荐)

通过@ConfigurationProperties来获取配置

  • 创建配置类

    1
    2
    3
    4
    5
    6
    @Component
    @Data
    @ConfigurationProperties(prefix = "pattern")
    public class PatternProperties {
    private String dataformat;
    }
  • 通过注入配置类来获取配置信息

    1
    2
    3
    4
    5
    6
    7
    @Autowired
    private PatternProperties patternProperties;

    @GetMapping("now")
    public String now() {
    return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDataformat()));
    }
配置共享

nacos配置的文件名格式为服务名-环境.yaml,如userservice-dev.yaml

当文件名为服务名.yaml时,即为共享配置,每个环境的都能读到

共享配置

有相同配置时,配置的优先级为: 环境配置>共享配置>本地配置

Nacos搭建集群

通过nginx反向代理多个nacos实现负载均衡

Nacos集群

步骤:

  • 搭建数据库
  • 配置nacos
  • 配置nginx反向代理
配置nacos
  • 进入nacos的conf目录,修改配置文件cluster.conf.example,重命名为cluster.conf

  • 添加nacos集群的ip和端口

    1
    2
    3
    127.0.0.1:8845
    127.0.0.1:8846
    127.0.0.1:8847
  • 修改配置文件application.properties,添加数据库信息

    1
    2
    3
    4
    5
    6
    7
    8
    spring.datasource.platform=mysql

    db.num=1

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

    db.user.0=root
    db.password.0=123
  • 在application.properties中修改每个nacos的端口

  • 分别启动nacos

    1
    startup.cmd
配置nginx反向代理
  • 下载解压nginx到非中文目录下

  • 修改conf/nginx.conf文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    upstream nacos-cluster {
    server 127.0.0.1:8845; #nacos的ip和端口
    server 127.0.0.1:8846;
    server 127.0.0.1:8847;
    }

    server {
    listen 80;
    server_name localhost;

    location /nacos {
    proxy_pass http://nacos-cluster;
    }
    }
  • 代码中nacos的地址要改为nginx的

    1
    2
    3
    4
    spring:
    cloud:
    nacos:
    server-addr: localhost:80 # Nacos地址

使用Feign替代RestTemplate

RestTemplate存在的问题: 需要自己拼接请求路径,如果参数一多不好管理

Feign是声明式的服务调用工具,我们只需创建一个接口并用注解的方式来配置它,就可以实现对某个服务接口的调用,简化了直接使用RestTemplate来调用服务接口的开发量

实例
  1. 导入坐标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>2021.0.6</spring-cloud.version>
    </properties>

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

    <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>
  2. 启动类上加入@EnableFeignClients注解开启Feign的功能

    1
    @EnableFeignClients
  3. 编写Feign的客户端

    1
    2
    3
    4
    5
    @FeignClient("userservice")
    public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
    }
  4. 之后可以注入Feign的客户端来发送http

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Service
    public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserClient userClient;

    public Order queryOrderById(Long orderId) {
    // 查询订单
    Order order = orderMapper.findById(orderId);

    //用feign发送http请求查询user信息
    User user = userClient.findById(order.getUserId());
    order.setUser(user);

    // 返回
    return order;
    }
    }
自定义配置
类型 作用 说明
feign.Logger.Level 修改日志级别 包含四种不同的级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder 响应结果的解析器 http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder 请求参数编码 将请求参数编码,便于通过http请求发送
feign. Contract 支持的注解格式 默认是SpringMVC的注解
feign. Retryer 失败重试机制 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试

配置方式:

  1. 配置文件方式

    1
    2
    3
    4
    5
    feign:
    client:
    config:
    userservice: # 针对某个服务, 不加针对所有服务
    loggerLevel: FULL # 日志等级
  2. 代码方式

    1
    2
    3
    4
    5
    6
    7
    public class DefaultFeignConfiguration {

    @Bean
    public Logger.Level feignLevel() {
    return Logger.Level.BASIC;
    }
    }

    通过注入Bean的方式来自定义配置

    1
    2
    3
    4
    5
    6
    //如果要所有服务生效,则加在@EnableFeignClient上
    @EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)

    //如果只要某个服务生效,则加在指定的feignClient上
    @FeignClient(value = "userservice", configuration = DefaultFeignConfiguration.class)

服务降级

降级指系统将某些业务或者接口的功能降低,可以是只提供部分功能,也可以是完全停掉所有功能。降级的核心思想就是丢车保帅,优先保证核心业务

优化性能

可以通过一下两点优化性能:

  1. 日志级别尽量用basic及以下
  2. 使用HttpClient或OKHttp代替URLConnection

替换连接池

Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:

•URLConnection:默认实现,不支持连接池

•Apache HttpClient :支持连接池

•OKHttp:支持连接池

因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。

  • 导入依赖坐标

    1
    2
    3
    4
    5
    <!--httpClient的依赖 -->
    <dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
    </dependency>
  • 配置使用的连接池

    1
    2
    3
    4
    5
    6
    7
    8
    9
    feign:
    client:
    config:
    userservice: # 针对某个服务
    loggerLevel: BASIC # 日志等级
    httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数
抽取Feign客户端

将Feign的Client抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用

降低代码冗余

feign抽取

  • 创建新模块feign-api

  • 将FeignClient及feign配置类、实体类移到feign-api中

    feignApi

  • 在需要feign的类中导入feign-api模块

    导入feignApi

  • 在@EnableFeignClients注解中说明feign所在的包或类

    1
    2
    3
    4
    5
    @EnableFeignClients(clients = {UserClient.class})

    //或者

    @EnableFeignClients(basePackages = "cn.itcast.feign.clients")
常用配置
1
2
3
4
5
6
7
8
9
10
11
12
13
feign:
hystrix:
enabled: true #在Feign中开启Hystrix
compression:
request:
enabled: false #是否对请求进行GZIP压缩
mime-types: text/xml,application/xml,application/json #指定压缩的请求数据类型
min-request-size: 2048 #超过该大小的请求会被压缩
response:
enabled: false #是否对响应进行GZIP压缩
logging:
level: #修改日志级别
com.macro.cloud.service.UserService: debug

Gateway网关

SpringCloudGateway是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能

网关的核心功能特性

  • 请求路由和负载均衡:
  • 权限控制:网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。
  • 限流: 当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大
  • Predicate(断言):指的是Java 8 的 Function Predicate。 输入类型是Spring框架中的ServerWebExchange。 这使开发人员可以匹配HTTP请求中的所有内容,例如请求头或请求参数。如果请求与断言相匹配,则进行路由;
  • Filter(过滤器):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前后对请求进行修改。
创建Gateway服务
  • 创建gateway模块

    gateway模块

  • 导入依赖坐标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--nacos服务发现依赖-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--gateway网关-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
  • 编写路由配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    server:
    port: 10010 #网关地址
    spring:
    application:
    name: gateway #服务名
    cloud:
    nacos:
    discovery:
    server-addr: localhost:8848 #nacos地址
    gateway:
    routes: #路由配置
    - id: user-service #路由id
    uri: lb://userservice #路由目标地址,服务注册的名字(lb: loadBalance)
    predicates: #断言,根据这些路径来匹配
    - Path=/user/**
    - id: order-service #路由id
    uri: lb://orderservice #路由目标地址(lb: loadBalance)
    predicates: #断言,根据这些路径来匹配
    - Path=/order/**

  • 通过网关地址访问服务

    1
    http://localhost:10010/user/1
断言工厂

Spring Cloud 网关 | 中文文档 (gitcode.net)

名称 说明 示例
After 是某个时间点后的请求 - After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before 是某个时间点之前的请求 - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between 是某两个时间点之前的请求 - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie 请求必须包含某些cookie - Cookie=chocolate, ch.p
Header 请求必须包含某些header - Header=X-Request-Id, \d+
Host 请求必须是访问某个host(域名) - Host=.somehost.org,.anotherhost.org
Method 请求方式必须是指定方式 - Method=GET,POST
Path 请求路径必须符合指定规则 - Path=/red/{segment},/blue/**
Query 请求参数必须包含指定参数 - Query=name, Jack或者- Query=name
RemoteAddr 请求者的ip必须是指定范围 - RemoteAddr=192.168.1.1/24
Weight 权重处理

通过predicates:后加断言来设置访问条件

过滤器工厂

对路由的请求或响应做加工处理 Spring提供了31种不同的路由过滤器工厂

Spring Cloud 网关 | 中文文档 (gitcode.net)

  • 添加过滤器(针对某个服务)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    gateway:
    routes: #路由配置
    - id: user-service #路由id
    uri: lb://userservice #路由目标地址(lb: loadBalance)
    predicates: #断言,根据这些路径来匹配
    - Path=/user/**
    filters:
    - AddRequestHeader=Truth, aaa # 添加请求头
    - id: order-service #路由id
    uri: lb://orderservice #路由目标地址(lb: loadBalance)
    predicates: #断言,根据这些路径来匹配
    - Path=/order/**
  • 添加默认过滤器(对所有服务都有效)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    gateway:
    routes: #路由配置
    - id: user-service #路由id
    uri: lb://userservice #路由目标地址(lb: loadBalance)
    predicates: #断言,根据这些路径来匹配
    - Path=/user/**
    filters:
    - AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
    - id: order-service #路由id
    uri: lb://orderservice #路由目标地址(lb: loadBalance)
    predicates: #断言,根据这些路径来匹配
    - Path=/order/**
    default-filters: # 默认过滤项
    - AddRequestHeader=Truth, aaa
全局过滤器

过滤器工厂提供的过滤器的作用都是固定的,可以通过全局过滤器来自定义过滤逻辑

定义方式

  • 实现GlobalFilter接口

    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
    @Order(-1)      //过滤器优先级,数字越小优先级越高
    @Component
    public class AuthorizeFilter implements GlobalFilter {
    /**
    *
    * @param exchange 上下文
    * @param chain 过滤链
    * @return
    */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    //获取请求参数映射
    ServerHttpRequest request = exchange.getRequest();
    MultiValueMap<String, String> queryParams = request.getQueryParams();
    //获取其中的authorize参数
    String auth = queryParams.getFirst("authorize");
    if ("admin".equals(auth)) {
    //放行
    return chain.filter(exchange);
    }
    //拦截
    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); //设置401状态码
    return exchange.getResponse().setComplete();
    }
    }

过滤器执行顺序

  • order值越小,优先级越高
  • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增
  • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
跨域问题

浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题

解决跨域问题

在gateway服务的application.yml文件中,添加下面的配置:

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

Spring Cloud
http://xwww12.github.io/2022/09/03/微服务/SpringCloud/
作者
xw
发布于
2022年9月3日
许可协议