springcloud-新一代网关Gateway01(七)
Gateway简介
- 是什么
- Cloud全家桶中有一个很重要的组件就是网关, 而Gateway是原zuul1.x版的替代.
- 概述
- SpringCloud Gateway基于Spring5.0+SpringBoot2.0和Project Reactor等技术开发的网关, 旨在为微服务架构提供一种简单有效的API路由管理方式., 以及一些强大的过滤功能, 如熔断, 限流, 重试等.
- 为了提高性能, Gateway是基于WebFlux框架实现的, 而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty.
- 同时, Gateway的目标提供统一的路由方式且基于Filter链方式提供了网关基本功能, 如安全, 监控/指标等.
- 能干什么
- 反向代理 / 鉴权 / 流量控制 / 熔断 / 日志监控.
- 微服务架构中网关的地位
- 为什么Gateway能替换Zuul
- Gateway有如下特性
- 基于Spring5.0+SpringBoot2.0和Project Reactor构建
- 动态路由: 能匹配任何请求属性.
- 可以对路由指定Predicate(断言)和Filter(过滤器).
- 集成Hystrix的断路器功能.
- 集成SpringCloud服务发现功能.
- 请求限流功能
- 支持路径重写
- Gateway和Zuul的区别
- Zuul1.x是基于Servlet2.5使用阻塞架构的, 它不支持任何长连接. 其设计模式和Nginx较像, 每次I/O操作都是从工作线程中选择一个执行, 请求线程被阻塞直到工作线程完成, 但Nginx用C++实现, Zuul用Java实现, 而JVM第一次加载较慢, 故Zuul性能较差.
- 而Gateway使用非阻塞API, 同时还支持WebSocket, 并且与Spring紧密集成拥有更好的开发体验.
Gateway三大核心概念
- Route(路由): 路由是构建网关的基本模块, 它由ID, 目标URI, 一系列的断言和过滤器组成, 如果断言为true则匹配该路由.
- Predicate(断言): 参考Java8的java.util.function.Predicate, 开发人员可以匹配HTTP请求中的所有内容(如请求头或请求参数, 如果请求与断言相匹配则进行路由)
- Filter(过滤器): 指Spring框架中GatewayFilter的实例, 可以在请求被路由前或后对请求进行修改.
- 总体
- web请求, 通过一些匹配条件, 定位到真正的服务节点, 并在这个转发过程的前后, 进行一些精细化控制.
- predicate是我们的匹配条件, 而Filter可以理解为一个无所不能的拦截器, 有了这两个元素, 再加上目标uri, 就可以构成一个具体的路由了.
Gateway工作流程
- 客户端向Spring Cloud Gateway发出请求, 然后在Gateway Handler Mapping中找到与请求相匹配的路由, 将其发送到Gateway Web Handler.
- Handler再通过指定的过滤器链来讲请求发送到我们实际的服务执行业务逻辑, 然后返回.
- 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前("pre"))或之后("post")执行业务逻辑.
- Filter在"pre"类型的过滤器可以做参数校验, 权限校验, 流量控制, 日志输出, 协议转换等, 在"post"类型的过滤器中可以做相应内容, 响应头的修改, 日志的输出, 流量监控等有着非常重要的作用.
- 其核心逻辑: 路由转发 + 执行过滤器链.
入门配置
- 新建Module: cloud-gateway-gateway9527
- pom文件
<!-- 不需要web和actuator组件, 加了要报错. --> <dependencies> <!--新增gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </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>
- yml配置
server: port: 9527 spring: application: name: cloud-gateway eureka: instance: hostname: cloud-gateway-service client: service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
- 主启动类
@EnableEurekaClient @SpringBootApplication public class GateWayMain9527 { public static void main(String[] args) { SpringApplication.run(GateWayMain9527.class, args); } }
- 9527网关如何做路由映射?
- 先看看cloud-provider-payment8001的controller访问地址
- 如: /payment/get/{id}, /payment/lb
- 我们不想暴露8001端口, 希望8001外面套一层9527.
- 先看看cloud-provider-payment8001的controller访问地址
- 9527微服务新增网关配置
spring: application: name: cloud-gateway cloud: gateway: routes: - id: payment_routh #路由的ID, 没有固定规则但要求唯一, 建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/get/** #断言, 路径相匹配的进行路由. - id: payment_routh2 uri: http://localhost:8001 predicates: - Path=/payment/lb/**
- 测试
- 启动7001 / 7002 / 8001三个微服务, 启动9527网关.
- 访问说明
- 添加网关前: http://localhost:8001/payment/get/1
- 添加网关后: http://localhost:9527/payment/get/1 (当然8001端口依然可用)
- yml配置说明
- 在配置文件yml中配置: 如上述配置
- 代码中注入RouteLocator的Bean
- 需要用到路由过滤器
@Configuration public class GatewayConfig { @Bean public RouteLocator customerRouteLocator(RouteLocatorBuilder routeLocatorBuilder) { RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); routes.route("path_route_game", r -> r.path("/game").uri("http://news.baidu.com/game")).build(); routes.route("payment_routh3", r -> r.path("/payment/discovery").uri("http://localhost:8001//payment/discovery")); //routes.route("payment_routh4", // r -> r.path("/payment/get/**").uri("http://localhost:8001/payment/get/**")); return routes.build(); } }
- 测试: http://localhost:9527/payment/discovery http://localhost:9527/payment/get/1
- 需要用到路由过滤器