网关的理解
网关类似于
海关
或者大门
,出入都需要经过这个网关
。别人不经过这个网关,永远也看不到里面的东西。可以在网关进行条件过滤
,比如大门只有对应的钥匙才能入内。网关和大门一样,永远暴露在最外面
不使用网关
- 前端需要记住每一个服务的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
1. @RestController 2. public class LoginController { 3. 4. @GetMapping("doLogin") 5. public String doLogin(String username,String password){ 6. System.out.println(username+" -> "+password); 7. String token = UUID.randomUUID().toString(); 8. return token; 9. } 10. }
② Gateway-server
gateway依赖
1. <dependency> 2. <groupId>org.springframework.cloud</groupId> 3. <artifactId>spring-cloud-starter-gateway</artifactId> 4. </dependency>
配置路由
1. server: 2. port: 80 3. spring: 4. application: 5. name: gateway-server 6. cloud: 7. gateway: 8. enabled: true # 默认开启Gateway 9. routes: # 路由组 10. - id: login-service-route # 路由ID 保证唯一 11. uri: http://localhost:8080 # uri唯一资源定位符 url唯一资源标识符 12. predicates: # 断言 13. - Path=/doLogin # 和服务中的路径匹配
④ 编程式路由
不借助配置文件,使用编码的方式来实现路由转发
1. @Configuration 2. public class RouteConfig { 3. 4. @Bean 5. public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { 6. return builder.routes() 7. .route("anime-id", r->r.path("/anime").uri("https://www.bilibili.com")) 8. .route("variety-id",r->r.path("/variety").uri("https://www.bilibili.com")) 9. .build(); 10. } 11. }
如果在
uri
后面的资源路径和path
中的路由一样,那么gateway不会把path
中的路由拼接到uri
后面。编程式和配置文件的方式可以结合使用
动态路由
如果在路由转发时直接将URL写死,从而IP和port也被写死,那么Gateway将无法达到负载均衡的效果。应该是只提供服务名,然后通过这个名字去找对应的服务,从而达到负载均衡的效果
让Gateway服务也注册到注册中心中,那么Gateway就能够拥有所有的服务信息
Gateway会根据注册中心中的服务列表,以每个
服务名为路径
创建动态路由进行转发① 第一种实现方式
使用lb(Load Balance)协议,表示启用Gateway的负载均衡功能
1. server: 2. port: 80 3. spring: 4. application: 5. name: gateway-server 6. cloud: 7. gateway: 8. enabled: true # 默认开启Gateway 9. routes: # 路由组 10. - id: login-service-route # 路由ID 保证唯一 11. # uri: http://localhost:8080 # uri唯一资源定位符 url唯一资源标识符 12. uri: lb://login-service # lb://服务名 13. predicates: 14. - Path=/doLogin # 和服务中的路径匹配
② 第二种实现方式
使用服务发现,自动创建动态路由从而实现负载均衡
1. server: 2. port: 80 3. spring: 4. application: 5. name: gateway-server 6. cloud: 7. gateway: 8. enabled: true # 默认开启Gateway 9. discovery: 10. locator: 11. enabled: true # 开启动态路由 12. lower-case-service-id: true # 将注册列表中的服务名小写
断言
在项目启动的时候,Gateway会去加载一些路由断言工厂,例如:After、Query
断言就是给路由增加一些
匹配规则
,如果发送的请求符合这些规则
,就能够去访问,否则404。简单说这些匹配规则也就是一些boolean表达式,要么true进入,要么false拒绝① 分类
② 使用
在配置文件中,对某个路由进行操作。动态路由不能使用断言
1. spring: 2. application: 3. name: gateway-server 4. cloud: 5. gateway: 6. enabled: true # 默认开启Gateway 7. routes: 8. - id: login-service-route 9. uri: lb://login-service 10. predicates: 11. - Path=/doLogin 12. - Method=GET,POST # GET,POST请求能够访问 13. - After=2022-11-02T17:23:16.423+08:00[Asia/Shanghai] # 在指定时间后才能访问 通过ZonedDateTime获取 14. - Query=username,admin. # 必须给username传值 后面的值必须是:adminx
过滤器
Gateway中的过滤器和Servlet里面的过滤器差不多,用户修改进入HTTP请求和HTTP响应
分为GatewayFilter(针对某一个路由)和GlobalFilter(全局过滤)
① 自定义全局过滤器
在自定义过滤器中编写业务逻辑,比如Token校验、限流。
1. @Component 2. public class TestFilter implements GlobalFilter { 3. 4. @Override 5. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 6. // 获取请求对象 7. ServerHttpRequest request = exchange.getRequest(); 8. RequestPath path = request.getPath(); 9. // 打印路由以及服务名 10. System.out.println(path); 11. HttpHeaders headers = request.getHeaders(); 12. // 获取主机IP 13. String ip = headers.getHost().getHostName(); 14. System.out.println(ip); 15. // 获取响应对象 16. ServerHttpResponse response = exchange.getResponse(); 17. // 放过 18. return chain.filter(exchange); 19. } 20. }
定义过滤器顺序
1. @Component 2. public class TestFilter implements Ordered { 3. 4. @Override 5. public int getOrder() { 6. // 数字越小 越往前 7. return 0; 8. } 9. }
返回值处理
1. // 获取响应对象 2. ServerHttpResponse response = exchange.getResponse(); 3. // 设置响应头 4. response.getHeaders().set("content-Type","application/json;charset=UTF-8"); 5. // 封装返回数据 6. Map<String,Object> map = new HashMap<>(); 7. map.put("code", 401); 8. map.put("msg","没有该权限"); 9. ObjectMapper objectMapper = new ObjectMapper(); 10. try { 11. // 将map集合转为byte数组 12. byte[] bytes = objectMapper.writeValueAsBytes(map); 13. // 将byte数组包装成一个数据缓冲包 14. DataBuffer wrap = response.bufferFactory().wrap(bytes); 15. // 返回 16. return response.writeWith(Mono.just(wrap)); 17. } catch (JsonProcessingException e) { 18. e.printStackTrace(); 19. }
限流
在一定的时间段内,限制用户的访问频率。
① 限流模型
漏斗算法、令牌通算法、计算器算法、窗口滑动算法
入不敷出:生产令牌的速度赶不上使用的速度
- 系统以提前配置好的速率去生产令牌。
- 给令牌桶设置一个阈值,当桶满后,多余的令牌直接丢弃
- 客户发送请求都需要去拿令牌
- 如果没有拿到令牌该请求不能成功访问
- 如果拿到令牌将去访问服务,完成业务处理后删除令牌
- 当桶中的令牌数达到最低额度时,使用完的令牌返回
② Gateway中的限流
Gateway中已经内置了RequestRateLimiterGatewayFilterFactory,结合Redis做令牌桶算法。需要导入redis依赖
1. <dependency> 2. <groupId>org.springframework.boot</groupId> 3. <artifactId>spring-boot-starter-data-redis-reactive</artifactId> 4. </dependency>
配置限流规则
1. @Configuration 2. public class RequestLimiterConfig { 3. 4. /* 5. 基于IP做限流 6. */ 7. @Bean 8. @Primary // 主候选 9. public KeyResolver ipKeyResolver(){ 10. return exchange-> Mono.just(exchange.getRequest().getHeaders().getHost().getHostName()); 11. } 12. 13. /* 14. 基于API接口最限流 15. */ 16. @Bean 17. public KeyResolver apiKeyResolver(){ 18. return exchange -> Mono.just(exchange.getRequest().getPath().toString()); 19. } 20. }
修改配置文件
1. spring: 2. application: 3. name: gateway-server 4. cloud: 5. gateway: 6. enabled: true # 默认开启Gateway 7. routes: 8. - id: login-service-route 9. uri: lb://login-service 10. predicates: 11. - Path=/doLogin 12. filters: 13. - name: RequestRateLimiter # 过滤器名称 14. args: 15. key-resolver: '#{@ipKeyResolver}' # Bean对象的名字 16. redis-rate-limiter.replenishRate: 1 # 每秒钟生产多少令牌 17. redis-rate-limiter.burstCapacity: 3 # 令牌桶中的容量
只针对某一个路由
跨域配置
跨域:
CORS
同源策略。因为网关是服务的最边缘,所有的请求都需要走网关,将跨域的配置写在网关。
编程式
1. @Configuration 2. public class CorsConfig { 3. @Bean 4. public CorsWebFilter corsFilter() { 5. CorsConfiguration config = new CorsConfiguration(); 6. config.addAllowedMethod("*"); 7. config.addAllowedOrigin("*"); 8. config.addAllowedHeader("*"); 9. UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); 10. source.registerCorsConfiguration("/**", config); 11. return new CorsWebFilter(source); 12. } 13. }
配置文件
1. spring: 2. cloud: 3. gateway: 4. globalcors: 5. cors-configurations: 6. '[/**]': # 针对那些路径 7. allowCredentials: true # 可以携带Cookie 8. allowedHeaders: '*' 9. allowedMethods: '*' 10. 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