假设你现在要做一个电商应用,前端是移动端的APP,后端是各种微服务。那你可能某个页面需要调用多个服务的数据来展示。如果没有网关,你的系统看起来就是这个样子的:
而如果加上了网关,你的系统就会变成这个样子:
#Spring Cloud Gateway
Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
(PS:看到这张图是不是很熟悉,没错,很像SpringMVC的请求处理过程)
请求发送到网关,DispatcherHandler是HTTP请求的中央分发器,接管请求并将请求匹配到相应的 HandlerMapping。
请求与处理器之间有一个映射关系,网关将会对请求进行路由,handler 此处会匹配到 RoutePredicateHandlerMapping,匹配请求对应的 Route。
随后到达网关的 web 处理器,该 WebHandler 代理了一系列网关过滤器和全局过滤器的实例,如对请求或者响应的 Header 处理(增加或者移除某个 Header)。
最后,转发到具体的代理服务。
简而言之:
客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
eureka,producer,gateway
项目结构
新建module
添加eureka依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
完整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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example.eureka</groupId> <artifactId>eureka</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eureka</name> <description>Demo project for Spring Boot</description> <parent> <groupId>com.gateway.test</groupId> <artifactId>gatewayTest</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>..</relativePath> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置文件
spring: application: name: eureka server: port: 8761 eureka: instance: hostname: localhost client: fetch-registry: false register-with-eureka: false service-url: defaultZone: http://localhost:8761/eureka/
启动类
package com.example.eureka.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
启动程序,访问 http://localhost:8761/![图片.png](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMjU1MzI0OS05NzY5NzhkMzZmOGM1NmJlLnBuZw?x-oss-process=image/format,png)
现在还没有服务进行注册
新建producer的module,同创建rureka,不同处如下图,其他都一样。
完整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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example.producer</groupId> <artifactId>producer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>producer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>com.gateway.test</groupId> <artifactId>gatewayTest</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>..</relativePath> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置文件
spring: application: name: producer server: port: 8081 eureka: client: service-url: defaultZone: http://localhost:8761/eureka/
启动类
package com.example.producer.producer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableEurekaClient @SpringBootApplication public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } }
新建2个类控制器
HelloController
@RestController @RequestMapping("/hello") public class HelloController { @RequestMapping("say") public String say() { return "Hello Every Buddy"; } }
GoodByeController
@RestController @RequestMapping("/goodbye") public class GoodByeController { @RequestMapping("say") public String say() { return "Bye Bye"; } }
启动程序,访问 http://localhost:8761/
创建过程同eureka
完整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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example.gateway</groupId> <artifactId>gateway</artifactId> <version>0.0.1-SNAPSHOT</version> <name>gateway</name> <description>Demo project for Spring Boot</description> <parent> <groupId>com.gateway.test</groupId> <artifactId>gatewayTest</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>..</relativePath> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置文件
test: uri: lb://producer spring: application: name: gateway # cloud: # gateway: # routes: # - id: route_producer_hello # uri: ${test.uri} # uri以lb://开头(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称 # predicates: # - Path=/api-hello/** # filters: # - StripPrefix=1 # 表示在转发时去掉api # # - id: route_producer_goodbye # uri: ${test.uri} # predicates: # - Path=/api-goodbye/** # filters: # - StripPrefix=1 # - name: Hystrix # args: # name: myfallbackcmd # fallbackUri: forward:/user/fallback server: port: 8080 logging: level: org.springframework.cloud.gateway: TRACE org.springframework.http.server.reactive: DEBUG org.springframework.web.reactive: DEBUG reactor.ipc.netty: DEBUG eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ enabled: true # 是否启用注册服务 默认为true, false是不启用 instance: prefer-ip-address: true
启动类
package com.example.gateway.gateway; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; @SpringBootApplication public class GatewayApplication { @Value("${test.uri}") private String uri; @Bean public RouteLocator routeLocator(RouteLocatorBuilder builder){ return builder.routes() .route(r ->r.path("/hello/**").uri(uri)) .route(r ->r.path("/goodbye/**").uri(uri)).build(); } public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
启动程序,访问 http://localhost:8761/
服务都已经注册到reureka,我们定义了hello和goodbye开头的请求都会转发到 lb://producer
服务,我们定义gateway的端口是8080,producer的端口是8081
直接请求producer服务
http://localhost:8081/hello/say
http://localhost:8081/goodbye/say
通过网关请求
http://localhost:8080/hello/say
http://localhost:8080/goodbye/say
那所有微服务就只有一个网关,万一并发量上去了,网关承受不住怎么办?
Spring Cloud Gateway底层是Netty的,它本身就能承受比较大的并发。如果还是承受不了并发量,那可以注册多个Gateway实例,然后在前面弄一个Nginx或者F5等负载均衡器。大概图是这样: