网关的理解
网关类似于
海关
或者大门
,出入都需要经过这个网关
。别人不经过这个网关,永远也看不到里面的东西。可以在网关进行条件过滤
,比如大门只有对应的钥匙才能入内。网关和大门一样,永远暴露在最外面不使用网关
前端需要记住每一个服务的IP和port
如果有一个服务部署多台,那么前端需要自行分配
使用网关
前端不需要记每一个服务的IP和port,只需要将请求发送到网关即可,网关根据资源路径做
路由跳转
网关中可以做
安全控制
比如Token校验、限流等可以做
负载均衡
Gateway的理解
是Spring官网推出的一套网关组件,用来取代Zuul
它的目的是为了让
路由跳转更加方便、灵活
,还提供了一些强大的过滤器功能
。比如:IP黑名单、Token校验等基于webFlux框架实现,webFlux框架底层使用了高性能的Reactor模式通信框架的Netty
Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot 2和Project Reactor等技术。Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断、限流、重试等。
① Gateway工作原理
客户端发送请求到Gatewaty,然后Gateway HandlerMapping通过对比映射,找到与其匹配的路由,将其发送到Gateway WebHandler。Handler再通过指定的过滤器将请求分发到实际的业务逻辑,并返回。
在过滤器中可以做增加代码:
在
请求之前
[pre]做校验等在
请求之后
[post]做日志输出等Gateway核心逻辑:根据资源路径做路由转发,并且执行过滤器链。
② Gateway三大核心概念
路由(Route)
和eureka结合做动态路由
组成部分:一个路由ID、一个唯一资源定位符URI、一组断言、一组Filter
如果路由断言为真,那么URL和配置路由匹配
断言(Predicate)
返回一个boolean表达式
过滤器(Filter)
Gateway中的过滤器分为Gateway Filter(针对一个路由)和Global Filter(全局)
在过滤器中可以编写校验规则以及响应的处理
③ Gateway和Nginx区别
Nginx做限流、负载均衡、路由都需要修改nginx.conf配置文件
Gateway和eureka结合,实现了自动路由跳转;和Ribbon集合,实现了负载均衡。Gateway也能够通过配置进行限流的实现
路由使用
① 访问流程
② Loing-service
@RestController public class LoginController { @GetMapping("doLogin") public String doLogin(String username,String password){ System.out.println(username+" -> "+password); String token = UUID.randomUUID().toString(); return token; } }
② Gateway-server
gateway依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
配置路由
server: port: 80 spring: application: name: gateway-server cloud: gateway: enabled: true # 默认开启Gateway routes: # 路由组 - id: login-service-route # 路由ID 保证唯一 uri: http://localhost:8080 # uri唯一资源定位符 url唯一资源标识符 predicates: # 断言 - Path=/doLogin # 和服务中的路径匹配
④ 编程式路由
不借助配置文件,使用编码的方式来实现路由转发
@Configuration public class RouteConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("anime-id", r->r.path("/anime").uri("https://www.bilibili.com")) .route("variety-id",r->r.path("/variety").uri("https://www.bilibili.com")) .build(); } }
如果在
uri
后面的资源路径和path
中的路由一样,那么gateway不会把path
中的路由拼接到uri
后面。编程式和配置文件的方式可以结合使用
动态路由
如果在路由转发时直接将URL写死,从而IP和port也被写死,那么Gateway将无法达到负载均衡的效果。应该是只提供服务名,然后通过这个名字去找对应的服务,从而达到负载均衡的效果
让Gateway服务也注册到注册中心中,那么Gateway就能够拥有所有的服务信息
Gateway会根据注册中心中的服务列表,以每个
服务名为路径
创建动态路由进行转发① 第一种实现方式
使用lb(Load Balance)协议,表示启用Gateway的负载均衡功能
server: port: 80 spring: application: name: gateway-server cloud: gateway: enabled: true # 默认开启Gateway routes: # 路由组 - id: login-service-route # 路由ID 保证唯一 # uri: http://localhost:8080 # uri唯一资源定位符 url唯一资源标识符 uri: lb://login-service # lb://服务名 predicates: - Path=/doLogin # 和服务中的路径匹配
调用:http://localhost/doLogin
② 第二种实现方式
使用服务发现,自动创建动态路由从而实现负载均衡
server: port: 80 spring: application: name: gateway-server cloud: gateway: enabled: true # 默认开启Gateway discovery: locator: enabled: true # 开启动态路由 lower-case-service-id: true # 将注册列表中的服务名小写
断言
在项目启动的时候,Gateway会去加载一些路由断言工厂,例如:After、Query
断言就是给路由增加一些
匹配规则
,如果发送的请求符合这些规则
,就能够去访问,否则404。简单说这些匹配规则也就是一些boolean表达式,要么true进入,要么false拒绝① 分类
② 使用
在配置文件中,对某个路由进行操作。动态路由不能使用断言
spring: application: name: gateway-server cloud: gateway: enabled: true # 默认开启Gateway routes: - id: login-service-route uri: lb://login-service predicates: - Path=/doLogin - Method=GET,POST # GET,POST请求能够访问 - After=2022-11-02T17:23:16.423+08:00[Asia/Shanghai] # 在指定时间后才能访问 通过ZonedDateTime获取 - Query=username,admin. # 必须给username传值 后面的值必须是:adminx
过滤器
Gateway中的过滤器和Servlet里面的过滤器差不多,用户修改进入HTTP请求和HTTP响应
分为GatewayFilter(针对某一个路由)和GlobalFilter(全局过滤)
① 自定义全局过滤器
在自定义过滤器中编写业务逻辑,比如Token校验、限流。
@Component public class TestFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获取请求对象 ServerHttpRequest request = exchange.getRequest(); RequestPath path = request.getPath(); // 打印路由以及服务名 System.out.println(path); HttpHeaders headers = request.getHeaders(); // 获取主机IP String ip = headers.getHost().getHostName(); System.out.println(ip); // 获取响应对象 ServerHttpResponse response = exchange.getResponse(); // 放过 return chain.filter(exchange); } }
定义过滤器顺序
@Component public class TestFilter implements Ordered { @Override public int getOrder() { // 数字越小 越往前 return 0; } }
返回值处理
// 获取响应对象 ServerHttpResponse response = exchange.getResponse(); // 设置响应头 response.getHeaders().set("content-Type","application/json;charset=UTF-8"); // 封装返回数据 Map<String,Object> map = new HashMap<>(); map.put("code", 401); map.put("msg","没有该权限"); ObjectMapper objectMapper = new ObjectMapper(); try { // 将map集合转为byte数组 byte[] bytes = objectMapper.writeValueAsBytes(map); // 将byte数组包装成一个数据缓冲包 DataBuffer wrap = response.bufferFactory().wrap(bytes); // 返回 return response.writeWith(Mono.just(wrap)); } catch (JsonProcessingException e) { e.printStackTrace(); }
限流
在一定的时间段内,限制用户的访问频率。
① 限流模型
漏斗算法、令牌通算法、计算器算法、窗口滑动算法
入不敷出:生产令牌的速度赶不上使用的速度
系统以提前配置好的速率去生产令牌。
给令牌桶设置一个阈值,当桶满后,多余的令牌直接丢弃
客户发送请求都需要去拿令牌
如果没有拿到令牌该请求不能成功访问
如果拿到令牌将去访问服务,完成业务处理后删除令牌
当桶中的令牌数达到最低额度时,使用完的令牌返回
② Gateway中的限流
Gateway中已经内置了RequestRateLimiterGatewayFilterFactory,结合Redis做令牌桶算法。需要导入redis依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency>
配置限流规则
@Configuration public class RequestLimiterConfig { /* 基于IP做限流 */ @Bean @Primary // 主候选 public KeyResolver ipKeyResolver(){ return exchange-> Mono.just(exchange.getRequest().getHeaders().getHost().getHostName()); } /* 基于API接口最限流 */ @Bean public KeyResolver apiKeyResolver(){ return exchange -> Mono.just(exchange.getRequest().getPath().toString()); } }
修改配置文件
spring: application: name: gateway-server cloud: gateway: enabled: true # 默认开启Gateway routes: - id: login-service-route uri: lb://login-service predicates: - Path=/doLogin filters: - name: RequestRateLimiter # 过滤器名称 args: key-resolver: '#{@ipKeyResolver}' # Bean对象的名字 redis-rate-limiter.replenishRate: 1 # 每秒钟生产多少令牌 redis-rate-limiter.burstCapacity: 3 # 令牌桶中的容量
只针对某一个路由
跨域配置
跨域:
CORS
同源策略。因为网关是服务的最边缘,所有的请求都需要走网关,将跨域的配置写在网关。
编程式
@Configuration public class CorsConfig { @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); } }
配置文件
spring: cloud: gateway: globalcors: cors-configurations: '[/**]': # 针对那些路径 allowCredentials: true # 可以携带Cookie allowedHeaders: '*' allowedMethods: '*' allowedOrigins: '*'
面试题相关
什么是微服务网关
Spring Cloud Gateway 是 Spring 官方基于 Spring 5.x,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,Spring Cloud Gateway 旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式。Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全性、监视/指标和弹性。
使用Gateway的优势
Spring Cloud Gateway 可以看做是一个 Zuul 1.x 的升级版和代替品,比 Zuul 2 更早的使用 Netty 实现异步 IO,从而实现了一个简单、比 Zuul 1.x 更高效的、与 Spring Cloud 紧密配合的 API 网关。
Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。
比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。
比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局 Filter,也都可以直接用。当然自定义 Filter 也非常方便。zuul和spring cloud gateway的对比
- zuul:是Netflix的,是基于servlet实现的,阻塞式的api,不支持长连接。
- gateway:是springcloud自己研制的微服务网关,是基于Spring5构建,能够实现响应式非阻塞式的Api,支持长连接
gateway的组成
- 路由 : 网关的基本模块,有ID,目标URI,一组断言和一组过滤器组成
- 断言:就是访问该旅游的访问规则,可以用来匹配来自http请求的任何内容,例如headers或者参数
- 过滤器:这个就是我们平时说的过滤器,用来过滤一些请求的,gateway有自己默认的过滤器,具体请参考官网,我们也可以自定义过滤器,但是要实现两个接口,ordered和globalfilter