Gateway服务网关
Zuul1.X是netflix公司开发的网关组件。在升级2.X,并且在2.X中引入了许多新的思想,更新比较慢。
gateway是spring公司自己开发的网关组件。
官网:https://spring.io/projects/spring-cloud-gateway
1.简介
1.什么是gateway
SpringCloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。其核心逻辑是路由转发+执行过滤器链。
SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 2.0之前的非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
Spring Cloud Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
注意:Spring Cloud Gateway 底层使用了高性能的通信框架Netty。
2.特性
SpringCloud官方,对SpringCloud Gateway 特征介绍如下:
(1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
(2)集成 Hystrix 断路器
(3)集成 Spring Cloud DiscoveryClient
(4)Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters
(5)具备一些网关的高级功能:动态路由、限流、路径重写
简单说明一下上文中的三个术语:
(1)Filter(过滤器):
和Zuul的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例。
(2)Route(路由):
网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。
(3)Predicate(断言):(面向函数编程,实际是返回bool类型的一个方法)
这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。
3.和zuul区别
Zuul1已经进入维护阶段,而且Gateway是Springcloud团队研发的。
SpringCloud Gateway和Zuul主要的区别,还是在底层的通信框架上。
Zuul1是一个基于阻塞IO的API网关,基于Servlet2.5使用阻塞架构,不支持长连接(如websocket)等。Zuul的设计模式和Nginx较像,每次IO都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成。Spring中集成的Zuul版本,采用的是Tomcat容器,使用的是Servlet IO处理模型。
Gateway基于非阻塞模型进行开发,性能方面不需要担心。Servlet3.1之后有了异步非阻塞的支持。WebFlux是一个典型的异步非阻塞框架。SpringWebflux是Spring5.0引入的新的响应式框架,区别于SpringMVC,它不需要ServletAPI,它是完全异步非阻塞的,并且基于Reactor来实现响应式流规范。
4.Springcloud工作流程
(1) 客户端向gateway发出请求。然后在Gateway Handler Mapping中找到与请求匹配的路由,将其发送到Gateway Web Handler
(2)Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。(过滤器链之间虚线分开是因为可能会在发送代理请求之前"pre"或者之后"post"来执行业务逻辑)。可以在pre类型的过滤器链做参数校验、权限校验、流量监控、日志输出、协议转换等等。在"post"类型的过滤器链可以做响应内容、响应头的修改、日志输出、流量监控等等。
2.使用
1.新建子模块cloud-gateway-gateway9527
2.修改pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud</artifactId> <groupId>cn.qz.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-gateway-gateway9527</artifactId> <dependencies> <!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--引入自己抽取的工具包--> <dependency> <groupId>cn.qz.cloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <!--一般基础配置类--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
3.修改yml
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8081 #匹配后提供服务的路由地址
predicates:
- Path=/pay/listAll/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8081 #匹配后提供服务的路由地址
predicates:
- Path=/pay/getServerPort/** # 断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://localhost:7001/eureka
上面相当于配置了两条路由。实际上相当于从9527端口转发到8081端口。
4.启动类:
package cn.qz.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; /** * @Author: qlq * @Description * @Date: 22:29 2020/10/20 */ @SpringBootApplication @EnableEurekaClient public class GateWayMain9527 { public static void main(String[] args) { SpringApplication.run(GateWayMain9527.class, args); } }
5.启动后测试:
liqiang@root MINGW64 ~/Desktop $ curl http://localhost:9527/pay/getServerPort % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 52 0 52 0 0 1106 0 --:--:-- --:--:-- --:--:-- 3250{"success":true,"code":"200","msg":"","data":"8081"} liqiang@root MINGW64 ~/Desktop $ curl http://localhost:9527/pay/listAll % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 406 0 406 0 0 6444 0 --:--:-- --:--:-- --:--:-- 8638{"success":true,"code":"200","msg":"","data":[{"createtime":"2020-09-25T06:58:21.000+0000","serial":"测试","id":1},{"createtime":"2020-09-25T07:06:10.000+0000","serial":"测试","id":2},{"createtime":"2020-09-25T14:34:43.000+0000","serial":"测试1测试测试序列好23456","id":3},{"createtime":"2020-09-25T15:25:35.000+0000","serial":"测试1测试测试序列好23456555","id":1309514424784064514}]}
补充:uri 按服务名称负载均衡调用(实现动态路由)
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8081 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #根据服务名称进行负载均衡替换
predicates:
- Path=/pay/listAll/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8081 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #根据服务名称进行负载均衡替换
predicates:
- Path=/pay/getServerPort/** # 断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://localhost:7001/eureka
测试:
hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev) $ curl http://localhost:9527/pay/getServerPort % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 52 0 52 0 0 3466 0 --:--:-- --:--:-- --:--:-- 52000{"success":true,"code":"200","msg":"","data":"8082"} hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev) $ curl http://localhost:9527/pay/getServerPort % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 52 0 52 0 0 52 0 --:--:-- --:--:-- --:--:-- 52000{"success":true,"code":"200","msg":"","data":"8081"}
2.路由的两种配置方式
一种是上面的xml配置,还有一种是基于代码的配置方式,如下:
package cn.qz.cloud.config; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class GatewayConfig { /** * 配置了一个id为route-name的路由规则 * 当访问地址 http://localhost:9527/guonei时会自动转发到地址: http://news.baidu.com/guonei * * @param builder * @return */ @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { RouteLocatorBuilder.Builder routes = builder.routes(); routes.route("path_route_eiletxie", r -> r.path("/guonei") .uri("http://news.baidu.com/guonei")).build(); return routes.build(); } @Bean public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) { RouteLocatorBuilder.Builder routes = builder.routes(); routes.route("path_route_eiletxie2", r -> r.path("/guoji") .uri("http://news.baidu.com/guoji")).build(); return routes.build(); } }
3. 常用的断言
常用的类图如下:
主要介绍几个常用的:
(1)Path 匹配路径,用法如上面
(2)After 匹配日期,指定日期之后,如下:
- After=2020-03-12T15:44:15.064+08:00[Asia/Shanghai] #日期后面(用于判断日期)
(3) Before 用于判断指定日期之前,如下:
Before=2020-03-12T15:44:15.064+08:00[Asia/Shanghai] #日期后面(用于判断日期)
(4) Between用于判断指定日期之间:
- Between=2020-03-12T15:44:15.064+08:00[Asia/Shanghai], 2021-03-12T15:44:15.064+08:00[Asia/Shanghai] #日期之间(用于判断日期)
(5) Cookie 匹配带有指定Cookie
- Cookie=uname,zs #带Cookie,并且uname的值为zs
(6)Header 带有指定的header
- Header=X-Request-Id,\d+ #请求头要有 X-Request-Id属性并且值为整数的正则表达式
测试方法可以用curl进行测试,也可以用postman进行测试。
补充:日期判断,不知道日期格式可以用如下方法获取
import java.time.ZonedDateTime; public class PlainTest { public static void main(String[] args) { ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区 System.out.println(zbj); } }
结果:
2020-10-21T22:01:13.089+08:00[Asia/Shanghai]
4.过滤器的使用
类似于servlet的filter,可以实现前后进行特殊处理。比如过滤器开始校验权限等,过滤器处理之后处理响应头等操作。如下模拟校验权限:
package cn.qz.cloud.filter; import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpCookie; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.Date; @Component @Slf4j public class MyLogGatewayFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("****** come in MyLogGateWayFilter: " + new Date()); ServerHttpRequest request = exchange.getRequest(); String unameHeader = request.getHeaders().getFirst("uname"); String unameParam = request.getQueryParams().getFirst("uname"); HttpCookie unameCookie = request.getCookies().getFirst("uname"); if (StrUtil.isAllBlank(unameHeader, unameParam) && unameCookie == null) { log.info("*****用户名为null,非法用户"); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } /** * 加载过滤器的顺序,数字越小,优先级越高 * * @return */ @Override public int getOrder() { return 0; } }
测试:带uname参数、cookie、header的可以正常访问(curl 可以携带指定的cookie和header)
hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev) $ curl http://localhost:9527/pay/getServerPort % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev) $ curl http://localhost:9527/pay/getServerPort?uname=123 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 52 0 52 0 0 52 0 --:--:-- --:--:-- --:--:-- 52000{"success":true,"code":"200","msg":"","data":"8081"} hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev) $ curl http://localhost:9527/pay/getServerPort --Cookie 'uname=12' % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 52 0 52 0 0 3250 0 --:--:-- --:--:-- --:--:-- 3250{"success":true,"code":"200","msg":"","data":"8082"} hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev) $ curl http://localhost:9527/pay/getServerPort --Header 'uname:12' % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 52 0 52 0 0 3466 0 --:--:-- --:--:-- --:--:-- 3466{"success":true,"code":"200","msg":"","data":"8081"}
原文地址:https://www.cnblogs.com/qlqwjy/p/13843822.html
- 安装glog和gflags
- FFmpeg_3.2.4+SDL_2.0.5学习(1)音视频解码帧及显示/播放数据
- FFmpeg_3.2.4+SDL_2.0.5学习(2)视频同步基础
- ubuntu17.04更换主题
- ubuntu17.04新安装之后的软件准备
- 打造一流编辑器vimplus
- ffmpeg+sdl播放类
- 拉丁猪文字游戏
- 在Windows Mobile的控制台应用中使用Notification
- vim使用经验积累
- Spring+Mybatis+Maven+Mysql编程实战
- LD_LIBRARY_PATH和LIBRARY_PATH的区别
- c++11的Condition_variable
- lib 和 dll 的区别与使用, 没有头文件改如何使用
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 一起刷题(leetcode)第二篇:如何用Python实现递归
- 如何成为Python的数据操作库Pandas的专家?
- 谈谈Gaussian软件中的guess=mix
- 用ORCA做DLPNO-CCSD(T)计算
- Fortran调用C函数
- 在Python中创建命令行界面的最佳方式
- Diels-Alder反应的区域选择性分析
- 传说中的画图神器Plotnine,Python中的ggplot2
- 用ORCA做结构优化及轨迹查看
- 前端兼容之痛
- 离线安装PySCF程序(1.5及更高版本)
- Python Debug(调试)的终极指南
- Linux下做计算常用的别名alias推荐
- 离线安装支持Intel MKL的R-3.6
- Dalton使用——磷光及其相关过程