08-Gateway服务网关
作者:互联网
三、Gateway服务网关
3.1、网关的意义
- Gateway网关是我们服务的守门神,所有微服务的统一入口
- 网关的核心功能特性,如下所示
- 请求路由
- 权限控制
- 限流
- 网关的架构图如下所示
- 微服务之间通过Feign来访问,外部的访问直接访问微服务不安全,需要通过网关来控制
- 权限控制
- 网关作为微服务的入口,需要校验用户是否有请求资格,如果没有则进行拦截
- 路由和负载均衡
- 一切请求都必须先经过Gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标有多个实例的时候,还需要做负载均衡,用于选中哪个实例
- 限流
- 类似于节假日的公园等场所限流。当请求流量过高的时候,在网关中按照下游的微服务能够接受的速度来放行请求,避免服务压力过大,对微服务起到保护作用
- 在SpringCloud中网关的实现包括两种
- ①、gateway
- ②、zuul
- Zuul是基于Servlet实现的,属于阻塞式编程;而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能
3.2、Gateway快速入门
3.2.1、创建gateway服务,引入依赖
-
创建服务
-
引入依赖
-
<dependencies> <!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--nacos服务发现依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId> <version>2.1.0.RELEASE</version> </dependency> </dependencies>
-
3.2.2、编写启动类
-
启动类内容如下所示
-
package cn.coolman.gateway; 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.2.3、编写基础配置和路由规则
-
创建application.yml文件,内容如下
-
server: port: 10010 spring: application: name: gateway cloud: gateway: routes: - id: userservicegateway # 一个路由的id,可以是随意的 uri: lb://userservice # 使用负载均衡,把请求转发到userservice这个微服务里面 predicates: - Path=/user/** # 断言(如果用户访问的路径是: /user/下面的任意路径,就把这个请求路由到lb://userservice这个微服务里面 - id: orderservicegateway # 一个路由的id,可以是随意的 uri: lb://orderservice predicates: - Path=/order/** # 断言 nacos: discovery: # 这里的nacos配置,本次使用的是单机nacos,没有使用nacos集群,如果使用的是nacos集群,那么这里的nacos地址就应该写nginx的地址(因为nacos集群是靠nginx负载均衡实现的请求转发) server-addr: localhost:8848 namespace: 88335441-8805-441a-9964-3628d17634ac # 保证该微服务与其他微服务处于同一个命名空间
-
我们将符合
Path
规则的一切请求,都代理到uri
参数指定的地址 -
本例中,我们将
/user/**
开头的请求,代理到lb://userservice
,lb是LoadBalance负载均衡的缩写,根据服务名拉取服务列表,实现负载均衡。(PS:Path后面是等于,不是冒号,这只是一个字符串) -
routes
对应的是一个List集合,每个元素是RouteDefinition
-
3.2.4、测试
- 启动网关后
- 访问浏览器:http://localhost:10010/user/1
- 该请求符合
/user/**
,请求转发到uri:http://userservice/user/1,得到了如下结果
- 该请求符合
- 访问浏览器:http://localhost:10010/order/101
- 该请求符合
/order/**
,请求转发到uri:http://orderservice/order/101,得到如下结果
- 该请求符合
- 访问浏览器:http://localhost:10010/user/1
3.2.5、网关路由的流程图
- 整个访问的流程如下所示
- 上图是单机nacos的访问流程图,如果是nacos集群,那么在服务注册和发现步骤是还需要一层Nignx实现nacos节点的负载均衡
3.2.6、小结
- 网关搭建的步骤
- 1)创建项目,引入nacos服务发现和gateway依赖
- 2)配置application.yml,包括服务端口号,应用名称,nacos地址,路由
- 3)创建启动类
- 路由配置包括哪些内容
- 1)路由id:路由的唯一标识
- 2)路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
- 3)路由断言(predicates):判断路由的规则
- 4)路由过滤器(filters):对请求或响应做处理
3.3、断言工厂
3.3.1、断言工厂介绍
-
我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
-
例如
Path=/usr/**
是按照路径匹配,这个规则是由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 权重处理
3.3.2、断言工厂的示例
①、在某个时间点之后才能访问
-
predicates: - After=2030-01-20T17:42:47.789-07:00[Asia/Shanghai]
-
如果某个请求无法路由,就回出现404错误
②、在某个时间点之前才能访问
-
predicates: - Before=2030-01-20T17:42:47.789-07:00[Asia/Shanghai]
-
一般情况下,Path这种路由工程是最常用的
3.4、过滤器工厂
- GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理,其架构如下图所示
3.4.1、路由过滤器的种类
-
Spring提供了31中不同的路由过滤器工厂,例如:
-
名称 说明 AddRequestHeader 给当前请求添加一个请求头 RemoveRequestHeader 移除请求中的一个请求头 AddResponseHeader 给响应结果中添加一个响应头 RemoveResponseHeader 给响应结果中移除一个响应头 RequestRateLimiter 限制请求的流量
-
3.4.2、请求头过滤器
-
官网文档
-
以AddRequestHeader为例
- 需求:给所有进入userservice的请求添加一个请求头Truth=coolman is so freaking awesome !!!
-
1)修改gateway服务的application文件,添加路由过滤器
-
PS:键和值使用逗号分割,而不是等于号
-
gateway: routes: - id: userservicegateway # 一个路由的id,可以是随意的 uri: lb://userservice # 使用负载均衡,把请求转发到userservice这个微服务里面 predicates: - Path=/user/** # 断言(如果用户访问的路径是: /user/下面的任意路径,就把这个请求路由到lb://userservice这个微服务里面 filters: - AddRequestHeader=Truth,coolman is so freaking awesome !!! - id: orderservicegateway # 一个路由的id,可以是随意的 uri: lb://orderservice predicates: - Path=/order/** # 断言
-
当前过滤器卸载userservice路由下,因此仅仅对访问userservice的请求有效
-
-
2)在userservice这个微服务中使用@RequestHeader注解在方法中获取名为Truth的请求头,为了避免没有这个请求头而报错,将它的required设置为false
-
@GetMapping("/header") public String getHeader(@RequestHeader(name = "Truth", required = false) String truth) { return truth; }
-
-
3)重启userservice和gateway两个微服务,在浏览器上查看结果
3.4.3、默认过滤器
-
如果要对所有的路由都生效,则可以将过滤器工厂写道default-filters下,修改gateway服务的application.yml文件
-
1)把上面的局部过滤器注释或者去掉
-
2)配置与routes在同一个级别
-
3)进行以下配置
-
gateway: routes: - id: userservicegateway # 一个路由的id,可以是随意的 uri: lb://userservice # 使用负载均衡,把请求转发到userservice这个微服务里面 predicates: - Path=/user/** # 断言(如果用户访问的路径是: /user/下面的任意路径,就把这个请求路由到lb://userservice这个微服务里面 # filters: # - AddRequestHeader=Truth,coolman is so freaking awesome !!! - id: orderservicegateway # 一个路由的id,可以是随意的 uri: lb://orderservice predicates: - Path=/order/** # 断言 default-filters: - AddRequestHeader=Truth,coolman is so freaking awesome !!!
-
-
4)order-service中的controller也添加getHeader方法,使用@RequestHeader注解
-
@GetMapping("/header") public String getHeader(@RequestHeader(name = "Truth", required = false) String truth) { return truth; }
-
-
5)浏览器访问
- 运行效果是一样的,如下所示
-
3.4.4、小结
- 过滤器的作用
- ①、对路由的请求或者响应做加工处理,比如添加请求头
- ②、配置在路由下的过滤器支队当前路由的请求生效
- ③、defaultFilters的作用是什么
- 对所有都生效的过滤器
3.5、全局过滤器
- 上一节学习的过滤器,网关提供了31中,但每一种过滤器的作用都是固定的。如果我们希望拦截请求,做自己的业务逻辑则没办法实现
3.5.1、全局过滤器的作用
-
全局过滤器的作用也是处理一切进入网关的请求和微服务响应
-
与GatewayFilter的作用一样;区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现
-
定义方式是实现Global接口,Spring中定义的接口代码如下所示
-
public interface GlobalFilter { /** * 处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理 * * @param exchange 请求上下文,里面可以获取Request、Response等信息 * @param chain 用来把请求委托给下一个过滤器 * @return {@code Mono<Void>} 返回标示当前过滤器业务结束 */ Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain); }
-
-
在filter中编写自定义逻辑,可以实现下列功能
- ①、登录状态判断
- ②、权限校验
- ③、请求限流
- ...
3.5.2、自定义全局过滤器
需求
- 定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件
- 参数中是否由authorization
- authorization参数值是否为admin
- 如果同时满足则放行,否则拦截
实现
-
在gateway中的
cn.coolman.gateway.filters
包下定义一个过滤器-
1)编写一个类实现GlobalFilter接口,重写filter方法
-
2)使用@Order注解指定执行顺序,值越小,越先执行
-
3)使用@Component将过滤器对象放到Spring容器中
- ①、获取请求对象
- ②、获取响应对象
- ③、获取所有的请求参数,封装成键和值
- ④、获取第一个匹配的键
- ⑤、如果等于admin,则放行,返回消息处理完毕
- ⑥、否则设置状态码为未登录,返回消息处理完毕
-
package cn.coolman.gateway.filter.custom; import cn.coolman.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; 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. 获取请求对象 ServerHttpRequest request = exchange.getRequest(); // 2. 获取响应对象 ServerHttpResponse response = exchange.getResponse(); // 3. 获取所有的请求参数,封装成键和值 MultiValueMap<String, String> params = request.getQueryParams(); // 4. 获取第一个匹配的键 String authorization = params.getFirst("authorization"); // 5. 如果等于admin,则放行 if ("admin".equals(authorization)) { // 方法的返回值表示消息处理完毕 return chain.filter(exchange); } else { // 6. 否则设置状态码未登录 response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } } }
-
测试
- 1)重启网关服务
- 2)直接访问userservice的数据,会出现401错误,如下图所示
- 3)请求参数如果带一个authorization=admin,则可以正常访问
3.5.3、过滤器执行顺序
过滤器的分类
- 请求进入网关回碰到三类过滤器:DefaultFilter、路由过滤器、GlobalFilter
- 请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器,如下所示
排序的规则
- 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高 ,执行顺序就越靠前
- GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
- 路由过滤器(局部)和DefaultFilter(全局),它的执行顺序默认是按照声明顺序从1递增
- 执行顺序:defaultFilter > 路由过滤器(局部)> GlobalFilter的顺序执行
3.6、跨域问题
3.6.1、跨域问题的概念
- 跨域概念
- 域名不一致就是跨域,主要包括以下几种类型
- 域名不同
- www.taobao.com 和 www.jd.com
- 端口不同
- localhost:8080和localhost:8081
- 协议不同
- https://localhost:8080和http://localhost:8080
- 跨域问题
- 浏览器禁止请求的发起者与服务器发生跨域的Ajax请求,请求被浏览器拦截的问题
3.6.2、模拟跨域问题
①、创建一个新的Java Web工程
②、自定义一个带有ajax请求的web页面,复制到webapp目录下
pre
标签里面的内容可以自定义,作用是当页面加载成功后才正常显示
③、部署到Tomcat服务器中,启动并访问,指定端口为8090
④、可以在浏览器控制台看到下面的错误
3.6.3、解决跨域问题
①、参数说明
- 因为所有的微服务都要经过网关,所有不需要每个微服务都去处理,在网关中处理就可以
- CORS方案是浏览器想服务器询问是否允许本次请求跨域,这次询问是
options
请求,所以要让options
请求通过 - 但是如果每次跨域请求都询问,则会导致性能下降;于是设置一个有效期,在有效期内的请求不再询问,而是直接允许跨域请求
②、添加配置
-
在gateway服务的application.yml文件中,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 # 这次跨域检测的有效期
③、查看结果
- 配置完成以后,重启服务器,查看浏览器的控制台
标签:网关,请求,08,gateway,过滤器,userservice,Gateway,路由 来源: https://www.cnblogs.com/OnlyOnYourself-lzw/p/16458209.html