目录
Gateway网关
1.0.为什么需要网关?
1.1.如何使用gateway网关
1.2.网关从注册中心拉取服务
1.3.gateway自动定位
1.4.gateway常见的断言
1.5.gateway内置的过滤器
1.6.自定义过滤器-全局过滤器
1.7.解决跨域问题
2.nginx反向代理gateway集群
2.1.配置文件
继 nacos注册中心+Ribbon负载均衡+完成openfeign的调用(超详细步骤) 文章扩展Gateway网关
常见的API网关:
Ngnix+lua
使用nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用、lua是一种脚本语言,可以来编写一些简单的逻辑, nginx支持lua脚本
Kong
基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。 问题:
只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。
Zuul 1.0----老的微服务项目
Netflix开源的网关,功能丰富,使用JAVA开发,易于二次开发 问题:缺乏管控,无法动态配
置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如Nginx
Netflix提出理论---Zuul2.0 远远超过1.0---实际的产品没有出来。
Spring Cloud Gateway
Spring公司为了替换Zuul而开发的网关服务,将在下面具体介绍。
注意:SpringCloud alibaba技术栈中并没有提供自己的网关,我们可以采用Spring Cloud Gateway来做网关
Gateway简介
Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。
Gateway网关是我们服务的守门神,所有微服务的统一入口。
网关的核心功能特性:
请求路由
权限控制
限流
- 权限控制:
- 网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。
- 路由和负载均衡:
- 一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。
- 限流:
- 当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。
(1).创建一个网关微服务模块并引用依赖
org.springframework.cloud spring-cloud-starter-gateway (2).创建配置文件
#端口号 server: port: 81 #服务名称 spring: application: name: qy165-gateway #配置路由转发规则 cloud: gateway: routes: - id: qy165-product #路由的标识 唯一即可 默认按照UUID生成 uri: http://localhost:8001 #真实路由转发的地址 predicates: #断言:只要满足断言条件才会帮你转发到相应的url地址 - Path=/product/** - id: qy165-order uri: http://localhost:9001 predicates: - Path=/order/**
(3).启动类
@SpringBootApplication public class GatewayApp { public static void main(String[] args) { SpringApplication.run(GatewayApp.class,args); } }
测试
发现uri地址是一个死数据,未来可以是集群。
如何解决?
我们也可以把网关交于注册中心来管理,那么网关就可以从注册中心拉取服务信息。
(1).在网关微服务中引入nacos注册中心依赖
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery (2).修改网关的配置
#端口号 server: port: 81 #服务名称 spring: application: name: qy165-gateway #配置路由转发规则 cloud: gateway: routes: - id: qy165-product #路由的标识 唯一即可 默认按照UUID生成 uri: lb://qy165-product #真实路由转发的地址:lb://微服务的名称 predicates: #断言:只要满足断言条件才会帮你转发到相应的url地址 - Path=/product/** - id: qy165-order uri: lb://qy165-order predicates: - Path=/order/** #指定注册中心的地址 nacos: discovery: server-addr: localhost:8848 register-enabled: false #只拉取不注册
思考: 如果未来有1w个微服务,那么就需要在gateway中配置1w微服务的路由
解决方案: 可以使用gateway自动定位功能
修改配置文件
#端口号 server: port: 81 #服务名称 spring: application: name: qy165-gateway #指定注册中心的地址 nacos: discovery: server-addr: localhost:8848 register-enabled: false #只拉取不注册 #开启微服务自动定位功能 cloud: gateway: discovery: locator: enabled: true
测试
原理:
基于Datetime类型的断言工厂
此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
演示:
- After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai]
基于远程地址的断言工厂RemoteAddrRoutePredicateFactory
接收一个IP地址段,判断请求主机地址是否在地址段中
演示:
-RemoteAddr=172.16.7.128/24 (24--代表同一个域内都可以访问)
基于Cookie的断言工厂
CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求
cookie是否具有给定名称且值与正则表达式匹配。
-Cookie=chocolate, ch.
基于Header的断言工厂
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否
具有给定名称且值与正则表达式匹配。 key value
演示:
-Header=X-Request-Id, \d+
基于Host的断言工厂
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
-Host=**.testhost.org
基于Method请求方法的断言工厂
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
-Method=GET
基于Path请求路径的断言工厂
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
-Path=/foo/{segment}基于Query请求参数的断言工厂
QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具
有给定名称且值与正则表达式匹配。
-Query=baz, ba.
基于路由权重的断言工厂
WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
routes:
-id: weight_route1 uri: host1 predicates:
-Path=/product/**
-Weight=group3, 1
-id: weight_route2 uri: host2 predicates:
-Path=/product/**
-Weight= group3, 9
演示:
后置过滤器
演示:
前置过滤器
- 认证判断
- 权限校验
- 黑白名单
- 跨域配置
认证判断
- 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
- 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
- 以后每次请求,客户端都携带认证的token
- 服务端对token进行解密,判断是否有效。
如上图,对于验证用户是否已经登录鉴权的过程可以在网关统一检验。
检验的标准就是请求中是否携带token凭证以及token的正确性。
如何定义全局过滤器:
引依赖
com.alibaba fastjson 2.0.15 配置文件加入
#过滤器路径 filter: whitePaths: #白名单路径 - "/product/login" - "/product/aaa" blackPaths: #黑名单路径 - "/product/bbb"
创建FilterUrl黑白名单
@Component @ConfigurationProperties(prefix = "filter") public class FilterUrl { private Set
whitePaths=new HashSet<>(); private Set blackPaths=new HashSet<>(); public Set getWhitePaths() { return whitePaths; } public void setWhitePaths(Set whitePaths) { this.whitePaths = whitePaths; } public Set getBlackPaths() { return blackPaths; } public void setBlackPaths(Set blackPaths) { this.blackPaths = blackPaths; } } 创建LoginFilter全局过滤器:
package com.wqg.gateway.filter; import com.alibaba.fastjson.JSON; import com.wqg.gateway.vo.FilterUrl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; /** * @ fileName:LoginFilter * @ description: * @ author:wqg * @ createTime:2023/7/17 15:23 */ @Component public class LoginFilter implements GlobalFilter , Ordered { @Autowired private FilterUrl filterUrl; @Override public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); //过滤的业务代码 //1.获取请求路径 String path = request.getPath().toString(); //判断该路径是否放行路径 if(filterUrl.getWhitePaths().contains(path)){ return chain.filter(exchange); } //获取请求头 String token = request.getHeaders().getFirst("token"); //查看redis中是否存在该token if(StringUtils.hasText(token)&&"admin".equals(token)){ //放行 return chain.filter(exchange); } //响应json数据 Map map = new HashMap<>(); map.put("msg", "未登录"); map.put("code", 403); //JSON转换 byte[] bytes = JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8); //调用bufferFactory方法,生成DataBuffer对象 DataBuffer buffer = response.bufferFactory().wrap(bytes); //调用Mono中的just方法,返回要写给前端的JSON数据 return response.writeWith(Mono.just(buffer)); } //Ordered:优先级 值越小,优先级越高 @Override public int getOrder() { return 0; } }
(1) 第一种写个跨域配置类
package com.wqg.gateway.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.springframework.web.util.pattern.PathPatternParser; @Configuration public class CorConfig { //处理跨域 @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedMethod("*"); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } }
(2)第二种跨域解决写在配置文件中
#端口号 server: port: 81 #服务名称 spring: application: name: qy165-gateway #配置路由转发规则 cloud: gateway: #解决跨域 globalcors: add-to-simple-url-handler-mapping: true cors-configurations: '[/**]': #拦截的请求 allowedOrigins: #允许跨域的请求 - "*" allowedMethods: #运行跨域的请求方式 - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowedHeaders: "*" #允许请求中携带的头信息 allowCredentials: true #是否允许携带cookie maxAge: 36000 #跨域检测的有效期,单位s routes: - id: qy165-product #路由的标识 唯一即可 默认按照UUID生成 uri: lb://qy165-product #真实路由转发的地址:lb://微服务的名称 predicates: #断言:只要满足断言条件才会帮你转发到相应的url地址 - Path=/product/** # - After=2023-12-31T23:59:59.789+08:00[Asia/Shanghai] #要求在2023-12-31以后才可以访问该微服务 # - RemoteAddr=172.16.7.128 #只允许ip为172.16.7.128访问 # - Header=token,\d+ # filters: # - SetStatus=250 #修改原始响应的状态码 # - StripPrefix=1 - id: qy165-order uri: lb://qy165-order predicates: - Path=/order/** #指定注册中心的地址 nacos: discovery: server-addr: localhost:8848 register-enabled: false #只拉取不注册 #过滤器路径 filter: whitePaths: #白名单路径 - "/product/login" - "/product/aaa" blackPaths: #黑名单路径 - "/product/bbb"
上面的方案选择一个就行
(1) nginx.conf配置
(2) 模拟gateway集群
(3) 最后分别启动gateway服务即可
测试