SpringCloud微服务 【实用篇】| 统一网关Gateway

川长思鸟来 2024-02-28 06:46 125阅读 0赞

目录

一:统一网关Gateway

  1. 为什么需要网关

  2. gateway快速入门

  3. 断言工厂

  4. 过滤器工厂

  5. 全局过滤器

  6. 跨域问题


一:统一网关Gateway

前面我们已经学习了注册中心Eureka、Nacos和配置管理中心Nacos;但是此时存在很多安全的问题,服务器摆在那里谁都可以进行访问!

1. 为什么需要网关

网关功能:

身份认证和权限校验:微服务直接摆在那里允许任何人都可以访问,不太安全;需要进行身份验证,一切请求先到网关Gateway再到微服务,验证过后在进行放行!

服务路由、负载均衡:放行过后,问题又来了,当用户放松请求处理业务时,网关肯定处理不了业务,需要把请求给对应的微服务;但是需要判断是发给order-service还是user-service进行处理?每一个微服务后面肯定有很多实例,所以还需要进行服务路由和负载均衡!

请求限流:允许用户的请求量,限量;是对微服务的一种保护机制!

" class="reference-link">fb8b770a2e8444269e5be0425ddcb0a3.png

网关的技术实现:

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

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

②Zuul:Zuul是基于Servlet的实现,属于阻塞式编程。

总结网关的作用:

①对用户请求做身份认证、权限校验;

②将用户请求路由到微服务,并实现负载均衡 ;

③对用户请求做限流;

2. gateway快速入门

搭建网关服务的步骤:

第一步:创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖

注:这里需要Nacos依赖是因为也要把网关Gateway也注入注册中心Nacos里!

  1. <!--网关依赖-->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-gateway</artifactId>
  5. </dependency>
  6. <!--nacos服务发现依赖,把自己注入Nacos-->
  7. <dependency>
  8. <groupId>com.alibaba.cloud</groupId>
  9. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  10. </dependency>

第二步:服务的启动需要启动类

  1. package cn.itcast.gateway;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class GatewayApplication {
  6. public static void main(String[] args) {
  7. SpringApplication.run(GatewayApplication.class,args);
  8. }
  9. }

第三步:application.yml中编写路由规则配置及nacos地址

  1. server:
  2. port: 10010 # 服务端口
  3. spring:
  4. application:
  5. name: gateway # 服务名称
  6. cloud:
  7. nacos:
  8. server-addr: localhost:8848 # nacos服务地址
  9. gateway: # 服务路由配置
  10. routes: # 表示规则
  11. - id: userservice # 路由标识
  12. # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
  13. uri: lb://user-service # 路由的目标地址 lb就是负载均衡,后面跟服务名称
  14. predicates:
  15. - Path=/user/** # 路径断言,判断是否以/user开头
  16. - id: orderservice
  17. uri: lb://order-service
  18. predicates:
  19. - Path=/order/**

启动服务,此时访问就不需要:http://localhost:8080/order/101 而是http://localhost:10010/order/101 这种形式

ef968a8a7fc741d2b460f0fbdc38ad44.png

成功把把请求从网关路由到微服务!

原理剖析

①首先发起请求,端口是10010,而网关端口号也是10010,一定会进入网关!网关无法处理业务,只能基于路由规则进行判断(前面定义了两个路由规则)。

②根据路由规则匹配到的是user-service,然后就可以找到nacos注册中心进行服务拉取,再去负载均衡挑一个。

0b96d8c74e01493387df3d7db9a190ab.png

网关搭建步骤:

  1. 创建项目,引入nacos服务发现和gateway依赖;

  2. 配置application.yml,包括服务基本信息、nacos地址、路由;

路由配置包括:

  1. 路由id:路由的唯一标示;

  2. 路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡;

  3. 路由断言(predicates):判断路由的规则;

  4. 路由过滤器(filters):对请求或响应做处理;(后面会讲)

3. 断言工厂

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

①路由id:路由唯一标示;

②uri:路由目的地,支持lb和http两种;

③predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地;

④filters:路由过滤器,处理请求或响应;

路由断言工厂Route Predicate Factory

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

Spring提供了11种基本的Predicate工厂:
































































名称

说明

示例

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

权重处理

详细的使用规则参考官网:Spring Cloud Gateway

ea716fcaf9e64dcb944d4a573faece37.png

注:如果此时路由规则不符合,浏览器页面包404错误!

增加时间路由规则:给order-service增加在2023后访问才符合规则

  1. predicates:
  2. - Path=/order/**
  3. - After=2031-01-20T17:42:47.789-07:00[America/Denver] # 表明在2023年后访问符合

执行结果:

d1d8a0c8791440f7998a01153aa84d6b.png1. PredicateFactory的作用是什么?

读取用户定义的断言条件,对请求进行解析并做出判断。

  1. Path=/user/**是什么含义?

对请求对路进行解析,路径是以/user开头的就认为是符合的。

4. 过滤器工厂

过滤器工厂 GatewayFilter

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

38dc4896f309434caae52d2e0efffee1.png

Spring提供了31种不同的路由过滤器工厂。例如:

更详细的可以参考官方网站:Spring Cloud Gateway
































名称

说明

AddRequestHeader

给当前请求添加一个请求头

RemoveRequestHeader

移除请求中的一个请求头

AddResponseHeader

给响应结果中添加一个响应头

RemoveResponseHeader

从响应结果中移除有一个响应头

RequestRateLimiter

限制请求的流量

案例:给所有进入user-service的请求添加一个请求头Truth=Itcast is freaking awesome!

注:key和value之间是以 逗号 的方式连接!

c9177bcde18743628fe981a6533ba66d.png

验证执行结果:在UserController中使用@RequestHeader注解拿到请求头信息

  1. @GetMapping("/{id}")
  2. public User queryById(@PathVariable("id") Long id,
  3. @RequestHeader(value = "Truth",required = false) String truth
  4. ) {
  5. // 进行打印
  6. System.out.println("Truth: "+truth);
  7. return userService.queryById(id);
  8. }

思考:此时只是给某个微服务增加请求头信息,那么如果是所有的微服务都添加呢?

注:使用默认过滤器default-filter。配置的某一个微服务的过滤器,其filter在route的下面一级;而全局过滤器default-filter是与route同级!

95bce3e5f9c24ea4a211a9c55fa851e7.png

5. 全局过滤器

全局过滤器 GlobalFilter

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样!区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。

定义方式是实现GlobalFilter接口

exchange参数: 请求上下文,里面可以获取Request、Response等信息;
chain参数:*过滤器链,用来把请求委托给下一个过滤器,放行;
**
Mono* 返回标示当前过滤器业务结束;

  1. package org.springframework.cloud.gateway.filter;
  2. import org.springframework.web.server.ServerWebExchange;
  3. import reactor.core.publisher.Mono;
  4. public interface GlobalFilter {
  5. Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
  6. }

需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件

参数中是否有authorization,authorization参数值是否为admin,如果同时满足则放行,否则拦截!

在gateway启动类的同包下定义一个过滤器

①首先通过exchange参数获取到request对象,调用request对象的getQueryParams方法获取到所有的请求参数。然后从请求参数中通过authorization这个key获取value值admin。如果这个值存在:就调用chain执行链的filter方法,把exchange传下去;如果这个值不存在:就通过exchange参数获取到response对象,通过这个对象的setComplete方法进行拦截。在拦截之前还可以通过通过response方法设置状态码,增加用户的体验感!

②增加@Component注解组件扫描注解,纳入Spring的管理。

③增加@Order注解,顺序组件;将来可能会定义很对组件,这里是为了先执行。还可以实现Ordered接口,重写getOrder方法进行设置。

  1. package cn.itcast.gateway;
  2. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
  3. import org.springframework.cloud.gateway.filter.GlobalFilter;
  4. import org.springframework.core.Ordered;
  5. import org.springframework.core.annotation.Order;
  6. import org.springframework.http.HttpStatus;
  7. import org.springframework.http.server.reactive.ServerHttpRequest;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.util.MultiValueMap;
  10. import org.springframework.web.server.ServerWebExchange;
  11. import reactor.core.publisher.Mono;
  12. // @Order(-1)
  13. @Component
  14. public class AuthrizeFilter implements GlobalFilter, Ordered {
  15. @Override
  16. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  17. // 第一步:获取所有参数
  18. // 获取request对象
  19. ServerHttpRequest request = exchange.getRequest();
  20. // 获取所有请求参数
  21. MultiValueMap<String, String> params = request.getQueryParams();
  22. // 第二步:根据authorization参数获取value值
  23. String auth = params.getFirst("authorization");
  24. // 第三步:判断请求参数的值是不是等于admin
  25. if ("admin".equals(auth)){
  26. // 是,放行
  27. return chain.filter(exchange);
  28. }
  29. // 不是,拦截
  30. // 结束之前设置状态码
  31. exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
  32. return exchange.getResponse().setComplete();
  33. }
  34. // 实现Ordered接口的方法也可以
  35. @Override
  36. public int getOrder() {
  37. return -1;
  38. }
  39. }

进行访问:如果不增加参数就会报401错误(未登录错误)

267240a0acfc456c806821fec0ebcafb.png1. 全局过滤器的作用是什么?

对所有路由都生效的过滤器(这点和默认过滤器default-filter效果相同),并且可以自定义处理逻辑,比较灵活;

  1. 实现全局过滤器的步骤?

①实现GlobalFilter接口;

②添加@Order注解或实现Ordered接口 和 添加组件扫描@Component注解;

③编写处理逻辑;

过滤器执行顺序

前面已经讲解了三个过滤器:路由过滤球、默认的过滤器、全局过滤器;接下来就分析一下这三个过滤器的执行顺序!

注:请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器

924908a833f7412f9bbc765247a90e1f.png

思考1:从目前来看,这三个过滤器不是同一种类型,怎么能放到同一个集合当中呢?

  1. 对于路由过滤器和默认过滤器default-filter从配置文件来看配置方式相同:

1d3bbc075e1344b8a278c4bbfbc4ba16.png

其本质上实际都是AddRequestHeaderGatewayFilterFactory对象!这个过滤器的工厂就会读取配置文件生成一个真正的过滤器GatewayFilter;所以路由过滤器默认过滤器都是同一类:GatewayFilter!

  1. 在FilteringWebHandler类里面有一个FilteringWebHandler(过滤器适配器)这个类适配器实现了GatewayFilter接口,在适配器内部又接收了一个全局过滤器参数GlobalFiter;通过适配器模式进行传参当做GatewayFilter来使用,这样就建立了联系!

023d188da5fb4d4a8dc9525067a1902c.png

所以可以认为这三种过滤器都是GatewayFilter类型,同一种类型就可以放到List集合当中进行排序!

思考2:这样新的问题就引出来了,怎么进行排序呢?

①我们已经知道,每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。

②对于全局过滤器GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定。

③对于路由过滤器和默认过滤器我们并没有去指定顺序!路由过滤器和默认过滤器defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。例如:

各排各的:

6f2cc73ad25c45d7aa0454100d573167.png

④如果此时过滤器的order值都是1怎么办呢?

当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

详情可以参考源码:很清晰,可以自己看一下

①org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加载defaultFilters,然后再加载某个route的filters,然后合并。②org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链。

6. 跨域问题

跨域问题处理

在微服务当中,所有的请求都要先经过网关,在到微服务;这样就不要在每个微服务进行处理,只需要在网关中进行处理!

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

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

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

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

解决方案:CORS(浏览器去询问服务器的方式)

通过axios发送get请求,请求地址就是网关的地址

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <pre>
  11. spring:
  12. cloud:
  13. gateway:
  14. globalcors: # 全局的跨域处理
  15. add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
  16. corsConfigurations:
  17. '[/**]':
  18. allowedOrigins: # 允许哪些网站的跨域请求
  19. - "http://localhost:8090"
  20. - "http://www.leyou.com"
  21. allowedMethods: # 允许的跨域ajax的请求方式
  22. - "GET"
  23. - "POST"
  24. - "DELETE"
  25. - "PUT"
  26. - "OPTIONS"
  27. allowedHeaders: "*" # 允许在请求中携带的头信息
  28. allowCredentials: true # 是否允许携带cookie
  29. maxAge: 360000 # 这次跨域检测的有效期
  30. </pre>
  31. </body>
  32. <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  33. <script>
  34. axios.get("http://localhost:10010/user/1?authorization=admin")
  35. .then(resp => console.log(resp.data))
  36. .catch(err => console.log(err))
  37. </script>
  38. </html>

以8090端口进行运行,此时在控制台就可以看到报错请求:

37d1184bec114657a1478611a654ab1e.png

进行配置

  1. server:
  2. port: 10010 # 服务端口
  3. spring:
  4. application:
  5. name: gateway # 服务名称
  6. cloud:
  7. nacos:
  8. server-addr: localhost:8848 # nacos服务地址
  9. gateway: # 服务路由配置
  10. routes:
  11. - id: userservice # 路由标识
  12. # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
  13. uri: lb://user-service # 路由的目标地址 lb就是负载均衡,后面跟服务名称
  14. predicates:
  15. - Path=/user/** # 路径断言,判断是否以/user开头
  16. # filters:
  17. # - AddRequestHeader=Truth,Itcast is freaking awesome! # 注意key和value之间是以逗号隔开
  18. - id: orderservice
  19. uri: lb://order-service
  20. predicates:
  21. - Path=/order/**
  22. - Before=2031-01-20T17:42:47.789-07:00[America/Denver]
  23. default-filters:
  24. - AddRequestHeader=Truth,Itcast is freaking awesome! # 注意key和value之间是以逗号隔开
  25. globalcors: # 全局的跨域处理
  26. add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题(防止CORS浏览器询问服务器拦截) corsConfigurations:
  27. corsConfigurations:
  28. '[/**]': # 拦截所有的请求
  29. allowedOrigins: # 允许哪些网站的跨域请求
  30. - "http://localhost:8090"
  31. - "http://www.leyou.com"
  32. allowedMethods: # 允许的跨域ajax的请求方式
  33. - "GET"
  34. - "POST"
  35. - "DELETE"
  36. - "PUT"
  37. - "OPTIONS"
  38. allowedHeaders: "*" # 允许在请求中携带的头信息
  39. allowCredentials: true # 是否允许携带cookie
  40. maxAge: 360000 # 这次跨域检测的有效期,有效期内直接放行

重启网关,此时再次以8090端口发送请求就可以跨域访问啦!

发表评论

表情:
评论列表 (有 0 条评论,125人围观)

还没有评论,来说两句吧...

相关阅读

    相关 服务-GateWay()

    所谓网关是什么意思? 相当于就是你们小区家的保安,进出小区都得获得保安的同意,守护你们小区的生命财产健康,网关也是如此,对每个请求都严格把关,将合法的或者是获得权限的请求进入