目录
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网关
Gateway简介
Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。
1.0.为什么需要网关?
Gateway网关是我们服务的守门神,所有微服务的统一入口。
网关的核心功能特性:
请求路由
权限控制
限流
架构图:
- 权限控制:
- 网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。
- 路由和负载均衡:
- 一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。
- 限流:
- 当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。
1.1.如何使用gateway网关
(1).创建一个网关微服务模块并引用依赖
<dependencies><!--引入gateway依赖--><!--不能再引入spring-boot-starter-web依赖,因为web依赖中存在tomcat服务器--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency></dependencies>
(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-orderuri: http://localhost:9001predicates:- Path=/order/**
(3).启动类
@SpringBootApplication public class GatewayApp {public static void main(String[] args) {SpringApplication.run(GatewayApp.class,args);} }
测试
发现uri地址是一个死数据,未来可以是集群。
如何解决?
我们也可以把网关交于注册中心来管理,那么网关就可以从注册中心拉取服务信息。
1.2.网关从注册中心拉取服务
(1).在网关微服务中引入nacos注册中心依赖
<!--nacos注册中心的依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
(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-orderuri: lb://qy165-orderpredicates:- Path=/order/**#指定注册中心的地址nacos:discovery:server-addr: localhost:8848register-enabled: false #只拉取不注册
思考: 如果未来有1w个微服务,那么就需要在gateway中配置1w微服务的路由
解决方案: 可以使用gateway自动定位功能
1.3.gateway自动定位
修改配置文件
#端口号 server:port: 81#服务名称 spring:application:name: qy165-gateway#指定注册中心的地址nacos:discovery:server-addr: localhost:8848register-enabled: false #只拉取不注册#开启微服务自动定位功能cloud:gateway:discovery:locator:enabled: true
测试
原理:
1.4.gateway常见的断言
基于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
1.5.gateway内置的过滤器
演示:
后置过滤器
演示:
前置过滤器
1.6.自定义过滤器-全局过滤器
- 认证判断
- 权限校验
- 黑白名单
- 跨域配置
认证判断
- 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
- 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
- 以后每次请求,客户端都携带认证的token
- 服务端对token进行解密,判断是否有效。
如上图,对于验证用户是否已经登录鉴权的过程可以在网关统一检验。
检验的标准就是请求中是否携带token凭证以及token的正确性。
如何定义全局过滤器:
引依赖
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.15</version></dependency>
配置文件加入
#过滤器路径 filter:whitePaths: #白名单路径- "/product/login"- "/product/aaa"blackPaths: #黑名单路径- "/product/bbb"
创建FilterUrl黑白名单
@Component @ConfigurationProperties(prefix = "filter") public class FilterUrl {private Set<String> whitePaths=new HashSet<>();private Set<String> blackPaths=new HashSet<>();public Set<String> getWhitePaths() {return whitePaths;}public void setWhitePaths(Set<String> whitePaths) {this.whitePaths = whitePaths;}public Set<String> getBlackPaths() {return blackPaths;}public void setBlackPaths(Set<String> 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 {@Autowiredprivate FilterUrl filterUrl;@Overridepublic Mono<Void> 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中是否存在该tokenif(StringUtils.hasText(token)&&"admin".equals(token)){//放行return chain.filter(exchange);}//响应json数据Map<String, Object> 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:优先级 值越小,优先级越高@Overridepublic int getOrder() {return 0;}}
1.7.解决跨域问题
(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 {//处理跨域@Beanpublic 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: truecors-configurations:'[/**]': #拦截的请求allowedOrigins: #允许跨域的请求- "*"allowedMethods: #运行跨域的请求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" #允许请求中携带的头信息allowCredentials: true #是否允许携带cookiemaxAge: 36000 #跨域检测的有效期,单位sroutes:- 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-orderuri: lb://qy165-orderpredicates:- Path=/order/**#指定注册中心的地址nacos:discovery:server-addr: localhost:8848register-enabled: false #只拉取不注册#过滤器路径 filter:whitePaths: #白名单路径- "/product/login"- "/product/aaa"blackPaths: #黑名单路径- "/product/bbb"
上面的方案选择一个就行
2.nginx反向代理gateway集群
2.1.配置文件
(1) nginx.conf配置
(2) 模拟gateway集群
(3) 最后分别启动gateway服务即可
测试