微服务入门篇(一)

微服务入门篇(一)

忘记中二的少年 Lv3

一、认识微服务

1.1、 单体架构

  • 单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署
  • 优点:
    1. 架构简单
    2. 部署成本低
  • 缺点:
    1. 耦合度高

1.2、分布式架构

  • 分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务
  • 优点:
    1. 降低服务耦合
    2. 有利于服务升级拓展
  • 需要考虑的问题:
    1. 服务拆分粒度如何?【如何拆分?哪些服务作为独立模块?哪些业务合并一起?】
    2. 服务集群地址如何维护?
    3. 服务之间如何实现远程调用?
    4. 服务健康状态如何感知?

1.3、微服务

  • 微服务是一种经过良好架构设计的分布式架构方案,微服务架构特征:
    1. 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
    2. 面向服务:微服务对外暴露业务接口
    3. 自治:团队独立、技术独立、数据独立、部署独立
    4. 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题

1.4、微服务技术对比

微服务这种方案需要技术框架来落地,全球的互联网公司都在积极尝试自己的微服务落地技术。在国内最知名的就是SpringCloud和阿里巴巴的Dubbo

Dubbo SpringCloud SpringCloudAlibaba
注册中心 zookeeper、redis Eureka、Consul Nacos、Eureka
服务远程调用 Dubbo协议 Feign(http协议) Dubbos、Feign
配置中心 SpringCloudConfig SpringCloudConfig、Nacos
服务网关 SpringCloudGateway、Zuul SpringCloudGateway、Zuul
服务监控和保护 dubbo-admin,功能弱 Hystrix Sentinel

二、Eureka

2.0、Eureka的快速入门案例

因为案例篇幅使用图片较多因此请点击此处跳转至页面


2.1、Eureka原理分析

在Eureka架构中,微服务角色有两类:

  • EurekaServer:服务端,注册中心
    • 记录服务信息
    • 心跳监控
  • EurekaClient:客户端
    • povider:服务提供者
      • 注册自己的信息到EurekaServer
      • 每隔30s向EurekaServer发送心跳
    • consumer:服务消费者
      • 根据服务名称从EurekaServer拉取服务列表
      • 基于服务列表做负载均衡,选中一个微服务后发起远程调用

2.2、搭建EurekaServer

搭建EurekaServer服务步骤如下:

  1. 创建项目,引入spring-cloud-starter-netflix-eureka-server的依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
  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. 添加application.yml文件,编写下面的配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    server: 
    port: 10086
    spring:
    application:
    name: eurekaserver
    eureka:
    client:
    service-url:
    defaultZone: http://127.0.0.1:10086/eureka/

2.3、服务注册到Eureka

将自己的服务【此处我选用user-service】注册到EurekaServer步骤如下

  1. 在user-service项目中引入spring-cloud-starter-netflix-eureka-client的依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
  2. 在application.yml文件,编写如下配置:

    1
    2
    3
    4
    5
    6
    7
    spring:
    application:
    name: userservice
    eureka:
    client:
    service-url:
    defaultZone: http://127.0.0.1:10086/eureka/

是不是发现写在application.yml文件中的配置与搭建Eureka服务的配置一模一样?那是因为Eureka本身也是需要注册的因此配置文件书写内容基本一致,但是二者需要引入的pom坐标是不一样的。

2.4、服务发现

在order-service完成服务拉取

服务拉取是基于服务名称获取服务列表,然后在对服务列表做负载均衡

  1. 修改OrderService的代码,修改访问的URL路径,用服务名代替ip、端口:

    1
    String url = "http://userservice/user/" + order.getUserId();
  2. 在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解:

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

三、Ribbon负载均衡

3.1、负载均衡的原理

还记得上述的快速入门案例中服务发现中使用到的访问URL路径http:://userservice/user/...吗?

我们只是指定了服务的名称userservice而不是ip+端口并且在RestTemplate的Bean上添加了注解@LoadBalanced就自动完成了服务的拉取以及请求的负载均衡。因此有以下两点需要注意:

  1. 什么时候完成了服务的拉取?
  2. 什么时候完成了请求的负载均衡?
  3. 负载均衡的原理和策略?

负载均衡的流程如图所示

image-20231013153219657

Ribbon内部原理

image-20231013154859990

3.2、负载均衡的策略

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

Ribbon默认的负载均衡策略为:RoundRobinRule【简单的轮询服务列表】

默认负载均衡策略的验证——例子

image-20231013161740584

并让order-serviceuser-service发送四次查询请求

image-20231013161930752

因为默认的负载均衡策略为轮询所以可以推断:

8082端口的服务将会接收第一次和第三次的请求,查看8082服务接收请求的情况,推断正确。

image-20231013162116768

8081端口的服务将会接收第二次和第四次的请求,查看8081服务接收请求的情况,推断正确。

image-20231013162131503

上述成功验证了Ribbon默认的负载均衡的策略为轮询。


那么如何修改Ribbon负载均衡的查询机制呢?

通过定义IRule实现可以修改负载均衡的规则,有两种方式:

  1. 代码方式:在order-service中的OrderApplication类中,定义一个新的IRule【全局配置】

    1
    2
    3
    4
    5
    //该配置是全局的,order-service不仅是对user-service模块发送请求时使用的负载均衡机制为random,其他也是一样。
    @Bean
    public IRule randomRule(){
    return new RandomRule();
    }

    创建上述的规则Bean之后再次发送四次请求作为验证,可以看到此时8081端口只接受到了第四次的请求,验证成功。

    image-20231013162909676

  2. 配置文件的方式:在order-service的application.yml文件中,添加新的配置也可以修改规则

    1
    2
    3
    4
    5
    # 该配置项配置在order-service的application.yml文件
    # 说明order-service模块对user-service模块发送请求时使用下述的负载均衡策略
    userservice:
    ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则

3.3、饥饿加载

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

而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

1
2
3
4
ribbon:
eager-load:
enable: true #开启饥饿模式
clients: userservice #指定对userservice这个服务饥饿加载

四、Nacos注册中心

Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高

4.1、服务注册到Nacos

  1. 在父工程中添加spring-cloud-alibaba的管理依赖:

    1
    2
    3
    4
    5
    6
    7
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.5.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
  2. 注释掉order-service和user-service原有的eureka依赖

  3. 添加nacos的客户端依赖

    1
    2
    3
    4
    5
    <!-- nacos的客户端依赖 -->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
  4. 修改user-service&order-service中的application.yml文件注释掉eureka地址,添加nacos的地址

    1
    2
    3
    4
    spring:
    cloud:
    nacos:
    server-addr: localhost:8848 #服务端的地址

然后打开Nacos的控制台可以看到服务的注册结果

image-20231013185623234

4.2、修改服务的集群属性

Nacos服务分级存储模型

  1. 一级是服务,例如userservice
  2. 二级是集群,例如杭州或上海
  3. 三级是实例,例如杭州机房的某台部署了userservice的服务器

如何设置实例的集群属性

  1. 修改application.yml,添加如下内容

    1
    2
    3
    4
    5
    6
    spring:
    cloud:
    nacos:
    server-addr: localhost:8848
    discovery:
    cluster-name: HZ #设置集群的属性为HZ
  2. 在Nacos控制台可以看到集群变化,可以看到服务归属的集群已经变成了HZ

    image-20231013191052293

  3. 然后在order-service中设置负载均衡的IRule为NacosRule,这个规则优先会寻找与自己同集群的服务:

    1
    2
    3
    userservice:
    ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #负载均衡规则
  4. 注意设置user-service的权重为1


4.3、NacosRule负载均衡策略

  1. 优先选择同集群服务实例列表
  2. 本地集群找不到提供者,才去其他集群寻找,并且会报警告
  3. 确定了可用实例列表后,再采用随机负载均衡挑选实例

根据权重负载均衡

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

  • 服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求

Nacos提供了权重配置来控制访问评率,权重越大则访问频率越高

  1. 在Nacos控制台可以设置实例的权重值,首先选中实例后面的编辑按钮

    image-20231013193407360

  2. 将权重设置为0.1,测试可以发现8081被访问到的评率大大降低

    image-20231013193506673

总结:

  1. Nacos控制台可以设置实例的权重值,0-1之间
  2. 同集群内的多个实例,权重越高被访问的频率越高
  3. 权重设置为0则完全不会被访问

4.4、环境隔离-namespace

  • namespace用来做环境隔离
  • 每一个namespace都有唯一的id
  • 不同的namespace下的服务是不可见的
  1. 首先在Nacos的控制台点击命名空间,看到目前只有public的命名空间,然后点击新建

    image-20231013195510156

  2. 填入新建的信息,ID不填的话会默认使用UUID生成一个随机ID

    image-20231013195753755

  3. 修改order-service到application.yml文件,添加namespace:

    1
    2
    3
    4
    5
    6
    7
    spring:
    cloud:
    nacos:
    server-addr: localhost:8848
    discovery:
    cluster-name: SH
    namespace: 531e06b8-9430-4375-84ec-3439ffe9f161 #新创建命名空间生成的ID
  4. 重启order-service后再来查看控制台

    image-20231013200154923

  5. 此时访问order-service,因为namespace不同,会导致找不到userservice,控制台会报错:

    image-20231013200254593

4.5、Nacos与Eureka区别

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

4.6、Nacos配置管理

统一配置管理

创建配置管理文件

第一步:在Nacos中添加配置信息,点击 配置列表 $\to$点击 $+$

image-20231013202758859

第二步:在弹出的表单中填写配置信息

image-20231013203431036

4.7、微服务的配置拉取

  1. 引入Nacos的配置管理客户端依赖:

    1
    2
    3
    4
    5
    <!-- nacos配置管理依赖 -->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
  2. 在userservice中的resource目录添加一个bootstrap.yml文件,这个文件时引导文件,优先级高于application.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    spring:
    application:
    name: userservice #服务名称
    profiles:
    active: dev #开发环境,这里是dev
    cloud:
    nacos:
    server-addr: localhost:8848 #Nacos地址
    config:
    file-extension: yaml #文件后缀名
  3. 然后在user-service中将pattern.dateformat这个属性注入到UserController中做测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @RestController
    @RequestMapping("/user")
    public class UserController {
    //注入nacos中的配置属性
    @Value("${pattern.dateformat}")
    private String dateformat;

    //编写controller,通过日期格式化器来格式化现在时间并返回
    @GetMapping("now")
    public String now(){
    return LocalDate.now().format(
    DateTimeFormatter.ofPattern(dateformat,Locale.CHINA)
    );
    }
    }

将配置交给Nacos管理的步骤

  1. 在Nacos中添加配置文件
  2. 在微服务中引入nacos的config依赖
  3. 在微服务中添加bootstrap.yml文件,配置nacos的地址、当前环境、服务名称、文件后缀名。这些决定了程序启动时去nacos读取哪个文件

4.8、微服务的热更新

Nacos中的配置文件变更后,微服务无需重启就可以感知。不过需要通过下面两种配置来实现

  • 方式一:在@Value注入的变量所在类上添加注解@RefreshScope

    1
    2
    3
    4
    5
    6
    7
    8
    @Slf4j
    @RestController
    @RequestMapping("/user")
    @RefreshScope
    public class UserController {
    @Value("${pattern.dateformat}")
    private String dateformat;
    }
  • 方式二:使用@ConfigurationProperties注解

    1
    2
    3
    4
    5
    6
    7
    @Data
    @Component
    @ConfigurationProperties(prefix = "pattern")
    public class PatternProperties {
    private String dateformat;
    }

注意事项:

  • 不是所有的配置都适合放到配置中心,维护起来比较麻烦
  • 建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置

五、http客户端Feign

Feign是一个声明式的http客户端,其作用是简化实现http请求发送。

5.1、基于Feign的远程调用

使用Feign的步骤如下

  1. 引入依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
  2. 在需要发送请求的服务的启动类添加注解开启Feign的功能:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @EnableFeignClients
    @MapperScan("cn.itcast.order.mapper")
    @SpringBootApplication
    public class OrderApplication {

    public static void main(String[] args) {
    SpringApplication.run(OrderApplication.class, args);
    }
    }
  3. 编写Feign的客户端:

    1
    2
    3
    4
    5
    @FeignClient("userservice")
    public interface UserClients {
    @GetMapping("/user/{id}")
    public User findById(@PathVariable("id")Long id);
    }

    主要是基于SpringMVC的注解来声明远程调用的信息,比如:

    • 服务名称:userservice
    • 请求方式:GET
    • 请求路径:/user/{id}
    • 请求参数:Long id
    • 返回值类型:User
  4. 用Feign客户端代替RestTemplate

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

    public Order queryOrderById(Long orderId){
    //1. 查询订单
    Order order = orderMapper.findById(orderId);
    //2. 利用Feign发起http请求,查询用户
    User user = userClient.findById(order.getUserId());
    //3. 封装User到order
    order.setUser(user);
    //4. 返回
    return order;
    }

5.2、Feign自定义配置

Feign运行自定义配置来覆盖默认配置,可以修改的配置如下:

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

一般我们需要配置的是日志级别。

配置日志级别有两种方式

方式一:配置文件方式

1
2
3
4
5
feign:
client:
config:
default: #这里使用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
logger-level: FULL #日志级别

方式二:Java代码的方式,需要先声明一个Bean:

1
2
3
4
5
6
public class FeignClientConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC;
}
}
  1. 之后如果是全局配置,则将它放到启动类上的注解@EnableFeignClients这个注解中:

    1
    @EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
  2. 如果是局部配置,就将它放在需要使用feign发送请求的类的@FeignClient这个注解中:

    1
    @FeignClient(value="userservice",configuration = FeignClientConfiguration.class)

5.3、Feign的性能优化

Feign的客户端实现:

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

因此优化Feign的性能主要包括:

  1. 使用连接池代替默认的URLConnection
  2. 日志级别,最好使用NONE或BASIC

Feign添加HttpClient的支持:

  1. 引入依赖:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    feign:
    client:
    config:
    default: #default全局的配置
    logger-level: BASIC #日志级别
    httpclient:
    enabled: true #开启feign对HttpClient的支持
    max-connections: 200 #最大的连接数
    max-connections-per-route: 50 #每个路径的最大连接数

5.4、Feign的最佳实践

方式一(继承):给消费者的FeignClient和提供者的Controller定义统一的父接口作为标准

image-20231014154049345

方式二(抽取):将FeignClient抽取为独立模块,并且将接口有关的POJO,默认的Feign配置都放到这个模块中,提供给所有消费者使用

image-20231014154034652

当使用方式二进行抽取的时候因为Feign不在SpringBoot项目启动类的目录下,所以无法扫描到Feign的包。

定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。两种方式解决:

方式一:指定FeignClient所在包

1
@EnableFeignClients(basePackages = "包的路径")

方式二:指定FeignClient的字节码【推荐】

1
@EnableFeignClients(clients = {UserClient.class})

六、统一网关Gateway

6.1、了解网关

为什么需要网关?

网关功能:

  • 身份的验证和权限校验
  • 服务路由、负载均衡
  • 请求限流

image-20231015095626768

网关的技术实现

在SpringCloud中网关的实现包括两种:

  • gateway
  • zuul

Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具有更好的性能

6.2、搭建网关服务

  1. 创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!-- 网关gateway依赖 -->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!-- nacos服务注册发现依赖 -->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
  2. 在配置文件application.yml文件中编写路由配置以及nacos地址

    路由配置包括:

    • 路由id:路由的唯一标识
    • 路由目标(uri):路由的目标地址,http标识固定地址,lb表示服务名负载均衡
    • 路由断言(predicates):判断路由的规则
    • 路由过滤器(filters):对请求和响应做处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    server:
    port: 10010 #网关端口
    spring:
    application:
    name: gateway #服务名称
    cloud:
    nacos:
    server-addr: localhost:8848 # nacos 地址
    gateway:
    routes: #网关路由配置
    - id: user-service #路由id,自定义,只要唯一即可
    # uri: http://127.0.0.1:8081 #路由的目标地址 http就是固定地址
    uri: lb://userservice #路由的目标地址lb就是负载均衡【loadbalance】后面是服务名称
    predicates: #断言路由,也就是判断请求是否符合路由规格的条件
    - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求

小Tips:

启动网关服务报错:Consider defining a bean of type 'org.springframework.http.codec.ServerCodec
要排除其他依赖的spring-boot-starter-web,因为会与spring cloud gateway的webflux冲突。

网关执行过程

image-20231015103913896

6.3、路由断言工厂

网关路由可以配置的内容包括:

  • 路由id:路由的唯一标识
  • uri:路由的目的地,支持lb和http两种
  • predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地
  • filters:路由过滤器,处理请求或响应

路由断言工厂Route Predicate Factory

  • 在配置文件中书写的断言规则知只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件

  • 例如Path=/user/**是按照路径匹配,这个规则是由

    org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的

  • 像这样的断言工厂在SpringCloudGateway还有多个

名称 说明 示例
After 是某个时间点之后的请求 官网查找
Before 是某个时间点之前的请求 同上
Between 是某两个时间点之前的请求 同上
Cookie 请求必须包含某些cookie 同上
Header 请求必须包含默写header 同上
Method 请求方式必须是指定方式 同上
Path 请求路径必须符合指定规则 同上
Query 请求参数必须包含指定参数 同上
RemoteAddr 请求者的ip必须是指定范围 同上
Host 请求必须是访问某个host(域名) 同上
Weighe 权重处理 同上

6.4、路由过滤器

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理

过滤器的作用:

  • 对路由的请求或响应做加工处理,比如添加请求头
  • 配置在路由下的过滤器只对当前路由的请求生效
  • 若想使用全局过滤器,则配置与gateway同级的defaultFilters,该过滤器对所有路由都生效

image-20231015111827722

案例-给所有进入userservice的请求添加一个请求头

给所有进入userservice的请求添加一个请求头:Truth=itcast is freaking awesome!

实现方式:在gateway中修改application.yml文件,给userservice的路由添加过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 10010 #网关端口
spring:
application:
name: gateway #服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos 地址
gateway:
routes: #网关路由配置
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
filters: #过滤器
- AddRequestHeader=Truth, talk is cheap show me the code! #添加请求头

如果要对所有的路由都生效,则可以将过滤器工厂写到default下。格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
default-filters: #默认过滤器,会对所有的路由请求都生效【与gateway同级】
- AddRequestHeader=Truth, talk is cheap show me the code! #添加请求头

6.5、全局过滤器GlobalFilter

全局过滤器的作用是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用是一样的。

区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。

定义方式是实现GlobalFilter接口

1
2
3
4
5
6
7
8
9
10
public interface GlobalFilter {
/**
* 处理当前请求,有必要的话通过{@Link GatewayfilterChain}将请求交给下一个过滤器处理
*
* @Param 请求上下文。里面可以获取Request、Response等信息
* @Param 用来把请求委托给下一个过滤器
* @return {@code Mono<Void>} 返回标示当前过滤业务结束
*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

全局过滤器的作用是什么?

对所有路由都生效的过滤器,并且可以自定义处理逻辑

实现全局过滤器的步骤?

  1. 实现GlobalFilter接口
  2. 添加@Order注解或实现Ordered接口
  3. 编写处理逻辑

自定义全局过滤器的例子:实现GlobalFilter接口,添加@Order注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter{
@Override
public Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain){
//1. 获取请求参数
MultiValueMap<String,String> params = exchange.getRequest().getQueryParams();
//2. 获取authorization 参数
String auth = params.getFirst("authorization");
//3. 校验
if("admin".equals(auth)){
//放行
return chain.filter(exchange);
}
//4. 拦截
//4.1 禁止访问
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
//4.2 结束处理
return exchange.getResponse().setComplete();
}
}

6.6、过滤器的执行顺序

  • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前
  • GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
  • 路由过滤器和defaultFilter的order由spring指定,默认是按照声明顺序从1递增
  • 当过滤器的order值一样时,会按照defaultFilter > 局部路由过滤器 > GlobalFilter的顺序执行

6.7、网关跨域问题

网关处理跨域采用的同样是CORS方案,并且只需要简单配置即可实现

spring-framework从5.3.0版本开始,关于CORS跨域配置类 CorsConfiguration 中将 allowedOrigins 变量名修改为 allowedOriginPatterns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
cloud:
gateway:
globalcors: # 网关全局跨域配置
add-to-simple-url-handler-mapping: true #解决options请求被拦截的问题
cors-configurations:
'[/**]':
allowedOrigins: #允许哪些网站的跨域请求
- "http://localhost:8090"
- "http://www.leyou.com"
allowedMethods: #允许跨域的ajax的请求方式
- "GET"
- "POST"
- "Delete"
allowedHeaders: "*" #允许在请求中携带的头信息
allowCredentials: true #是否允许携带cookie
maxAge: 360000 #这次跨域检测的有效期
  • 标题: 微服务入门篇(一)
  • 作者: 忘记中二的少年
  • 创建于 : 2023-10-15 15:19:00
  • 更新于 : 2023-10-15 15:21:57
  • 链接: https://github.com/HandsomeXianc/HandsomeXianc.github.io/2023/10/15/微服务技术栈入门/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。