SpringCloud开发实战(七):集成Gateway服务网关

目录
SpringCloud开发实战(一):搭建SpringCloud框架
SpringCloud开发实战(二):通过RestTemplate实现远程调用
SpringCloud开发实战(三):集成Eureka注册中心
SpringCloud开发实战(四):Feign远程调用
SpringCloud开发实战(五):Feign的一些优化建议
SpringCloud开发实战(六):Feign的最佳实践

一、介绍

Spring Cloud Gateway 是一个基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 的API网关。它是Spring官方推出的第二代API网关产品,旨在为微服务架构提供一种高效、灵活且强大的路由和网关服务。

主要特点:

  • 动态路由:能够根据预先定义的规则,将请求路由到不同的微服务上。
  • 过滤器功能:支持丰富的请求和响应处理能力,如修改请求/响应头、日志记录、断言等。
  • 限流:可以设置请求速率限制,防止恶意攻击或意外的流量高峰导致服务崩溃。
  • 断路器:集成了断路器功能,可以在后端服务不可用时快速响应,避免长时间等待。
  • 协议代理:支持HTTP、HTTPS和WebSocket等多种协议。
  • 易于配置:可以通过多种方式进行配置,如YAML文件、属性文件、环境变量或Spring Cloud Config。
  • 集成性:与Spring生态系统中的其他组件紧密结合,如Spring Security、Spring Cloud Netflix等。

二、为什么我们需要网关

  1. 统一接口
    统一网关提供了一个单一的入口点来访问所有的微服务。这意味着客户端不需要知道每个微服务的具体位置和如何调用,而是通过网关来访问,简化了客户端的逻辑。

  2. 协议转换
    网关可以处理协议之间的转换工作,使得前端应用可以使用一种通用的协议(如HTTP)来访问后端服务,即使这些服务内部可能使用了不同的协议。

  3. 安全控制
    网关可以集中实现认证、鉴权等安全机制,确保只有经过验证的请求才能到达后端服务。这样可以避免每个微服务都需要单独实现安全功能,降低了复杂度。

  4. 负载均衡
    通过网关可以实现负载均衡,将请求分发到多个相同的服务实例上,从而提高系统的可用性和响应速度。

  5. 限流
    为了保护后端服务不被过多的请求压垮,网关可以实施限流策略,限制单位时间内某个客户端或某个API接口的请求次数。

  6. 缓存
    网关可以作为缓存层,存储常用或静态的数据,减少对后端服务的直接请求,从而提高整体性能。

  7. 监控与追踪
    网关可以收集请求的元数据,如请求频率、响应时间等,帮助监控系统的健康状态,以及在出现问题时进行故障排查。

  8. 版本管理
    通过网关,可以更容易地实现API版本管理,使得新旧版本的API可以在同一系统中共存,而不干扰现有的客户端。

  9. 跨域资源共享(CORS)
    网关可以处理跨域请求,使前端应用可以从不同的源访问后端服务,简化了跨域访问的处理。

  10. 统一错误处理
    网关可以统一处理来自各个微服务的错误响应,向客户端提供一致的错误消息格式,提升用户体验。

总结
统一网关通过集中处理诸如路由、安全、负载均衡等任务,简化了客户端与微服务的交互,并提高了系统的可维护性和扩展性。它不仅增强了系统的安全性,还提升了整体性能,是微服务架构不可或缺的一部分。

三、搭建网关

1. 创建gateway服务,引入依赖

创建服务
在这里插入图片描述
引入依赖:

        <!--网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--eureka依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

2. 编写启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GatewayApplication {

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

3. 编写基础配置和路由规则

创建application.yml文件,内容如下:

server:
  port: 10010 # 网关端口
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
spring:
  application:
    name: gateway # 服务名称
  cloud:
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8082 # 路由的目标地址 http就是固定地址
          uri: lb://userService # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求

我们将符合Path 规则的一切请求,都代理到 uri参数指定的地址。
本例中,我们将 /user/**开头的请求,代理到lb://userService,lb是负载均衡,根据服务名拉取服务列表,实现负载均衡。

4. 测试

启动网关,访问http://localhost:10010/user/1时,符合/user/**规则,请求转发到uri:http://userservice/user/1,得到了结果:
在这里插入图片描述

5.网关路由的流程图

在这里插入图片描述

四、断言工厂

我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件。例如Path=/user/**是按照路径匹配,这个规则是由
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的,像这样的断言工厂在SpringCloudGateway还有十几个:

名称说明示例
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权重处理

五、过滤器工厂

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
在这里插入图片描述

5.1 路由过滤器的种类

Spring提供了31种不同的路由过滤器工厂。我们就说几个常用的:

名称说明
AddRequestHeader给当前请求添加一个请求头
RemoveRequestHeader移除请求中的一个请求头
AddResponseHeader给响应结果中添加一个响应头
RemoveResponseHeader从响应结果中移除有一个响应头
RequestRateLimiter限制请求的流量

5.2 请求头过滤器

下面我们以AddRequestHeader 为例来讲解,需求:给所有进入userService的请求添加一个请求头:token=this is test token

我们只需要修改gateway服务的application.yml文件,添加路由过滤即可:

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userService 
        predicates: 
        - Path=/user/** 
        filters: # 过滤器
        - AddRequestHeader=token,this is test token # 添加请求头

当前过滤器写在usersService路由下,因此仅仅对访问userService的请求有效。
然后我们修改UserController的/user/id请求,增加一个新的参数。
在这里插入图片描述

    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id, @RequestHeader("token") String token) {
        System.out.println(token);
        return userService.queryById(id);
    }

让我们来测试一下,重启gateway和userService。我们请求一次userService的接口,就能看到控制台输出了token信息
在这里插入图片描述

在这里插入图片描述

5.3 默认过滤器

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

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userService 
        predicates: 
        - Path=/user/**
      default-filters: # 默认过滤项
      - AddRequestHeader=token, this is test token

六、全局过滤器

上面我们说到,gateway网关给我们提供了31种过滤器,但每一种过滤器的作用都是固定的。如果我们希望拦截请求,做自己的业务逻辑则没办法实现。所以我们需要一个全局过滤器,作用是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现。

定义方式是实现GlobalFilter接口。

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

在filter中编写自定义逻辑,我们可以实现下列功能:

  • 登录状态判断
  • 权限校验
  • 请求限流等

比如下面我们来定义一个全局过滤器。拦截请求,判断请求的参数是否满足下面条件:

  • 参数中是否有token,

  • token参数值是否为admin

如果同时满足则放行,否则拦截

实现

我们定义一个过滤器,实现GlobalFilter接口


import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@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.获取token参数
        String auth = params.getFirst("token");
        // 3.校验
        if ("admin".equals(auth)) {
            // 放行
            return chain.filter(exchange);
        }
        // 4.拦截
        // 4.1.禁止访问,设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 4.2.结束处理
        return exchange.getResponse().setComplete();
    }
}

测试

我们重启gateway服务,发现现在请求http://localhost:10010/user/1已经提示当前无法使用此页面,并且状态码是401
在这里插入图片描述
然后我们添加token参数,发现请求成功了。
在这里插入图片描述

过滤器执行顺序

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter。请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:
在这里插入图片描述
排序的规则是什么呢?

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

我们通过RouteDefinitionRouteLocator的getFilters()方法可以看到,会去先加载defaultFilters,然后再加载某个route的filters,然后合并。
在这里插入图片描述
然后我们通过源码发现:org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链
在这里插入图片描述

七、跨域问题

什么是跨域

跨域:域名不一致就是跨域,主要包括:

  • 域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com

  • 域名相同,端口不同:localhost:8080和localhost8081

跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题。后端也可以通过网关配置来解决这个问题。我们在gateway服务的application.yml文件中,添加下面的配置:

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: 3600 # 这次跨域检测的有效期
  • 26
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天吃八顿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值