简单上手,直接照搬,就可搭建微服务(Hoxton.SR8) 2020.8.28发布,SpringCloud搭建的文章正在整理,干货不要错过哦
摘要
Spring Cloud Gateway 为 SpringBoot 应用提供了API网关支持,具有强大的智能路由与过滤器功能。Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot 2和 Project Reactor等技术。Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能, 例如:熔断、限流、重试等。
Spring Cloud Gateway 具有如下特性:
- 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
- 动态路由:能够匹配任何请求属性;
- 可以对路由指定 Predicate(断言)和 Filter(过滤器);
- 集成Hystrix的断路器功能;
- 集成 Spring Cloud 服务发现功能;
- 易于编写的 Predicate(断言)和 Filter(过滤器);
- 请求限流功能;
- 支持路径重写。
配置组成
- Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由;
- Predicate(断言):指的是Java 8 的 Function Predicate。 输入类型是Spring框架中的ServerWebExchange。 这使开发人员可以匹配HTTP请求中的所有内容,例如请求头或请求参数。如果请求与断言相匹配,则进行路由;
- Filter(过滤器):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前后对请求进行修改。
1.创建一个api-gateway模块
2 单独使用gateway
2.1 pom.xml新增 gateway 依赖
<?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-hoxton-sr8</artifactId>
<groupId>com.zqh.www</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api-gateway</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 启动类
package com.zqh.www;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
@SpringBootApplication
public class ApiGatewayApplication {
private final static Logger logger = LoggerFactory.getLogger(ApiGatewayApplication.class);
public static void main(String[] args) {
Environment env = SpringApplication.run(ApiGatewayApplication.class, args).getEnvironment();
logger.info(
"\n----------------------------------------------------------\n\t"
+ "Application '{}' is running! Access URLs:\n\t"
+ "Local: \t\thttp://localhost:{}{}"
+ "\n----------------------------------------------------------",
env.getProperty("spring.application.name"), env.getProperty("server.port"),
env.getProperty("server.servlet.context-path") != null ? env.getProperty("server.servlet.context-path") : "");
}
}
2.3 yml配置
server:
port: 8093
service-url:
user-service: http://localhost:8084
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
# 路由的ID
- id: user-service
# 匹配后路由地址
uri: ${service-url.user-service}
predicates:
# 发送指定路径的请求会匹配该路由。
- Path=/api/user/*
# # 发送指定方法的请求会匹配该路由。
# - Method=GET
# # 在指定时间之后的请求会匹配该路由。
# - After=2020-10-20T12:00:00+08:00[Asia/Shanghai]
# # 在指定时间之前的请求会匹配该路由。
# - Before=2020-10-20T12:00:00+08:00[Asia/Shanghai]
# # 在指定时间区间内的请求会匹配该路由。
# - Between=2020-10-20T12:00:00+08:00[Asia/Shanghai], 2020-10-30T12:00:00+08:00[Asia/Shanghai]
# # 带有指定Cookie的请求会匹配该路由
# - Cookie=username,jourwon
# # 带有指定请求头的请求会匹配该路由。
# - Header=auth
# #带有指定Host的请求会匹配该路由。
# - Host=www.zqh.com
# # 从指定远程地址发起的请求可以匹配该路由。
# - RemoteAddr=192.168.1.1/24
filters:
# - AddRequestParameter=username,z
# - StripPrefix=1
# - PrefixPath=/api
logging:
level:
org.springframework.cloud.gateway: debug
2.4 predicates - 测试用例
注意:如果存在多个条件,则需要所有条件满足才会匹配到相关的路由
2.4.1 predicates - Path
描述:发送匹配路径或的请求会匹配该路由。
yml配置:- Path=/api/user/*
接口请求:http://localhost:8093/api/user/getUserList
相当于:http://localhost:8084/api/user/getUserList
2.4.2 predicates - Method
描述:发送指定方法的请求会匹配该路由。
接口请求:http://localhost:8093/api/user/getUserList
2.4.3 predicates - After
描述:在指定时间之后的请求会匹配该路由。
接口请求:http://localhost:8093/api/user/getUserList
2.4.4 predicates - Before
描述:在指定时间之前的请求会匹配该路由。
2.4.5 predicates - Between
描述:在指定时间区间内的请求会匹配该路由。
2.4.6 predicates - Cookie
描述:带有指定Cookie的请求会匹配该路由。
配置:
predicates:
# 带有指定Cookie的请求会匹配该路由
- Cookie=username,z
接口请求:
2.4.7 predicates - Header
描述:带有指定请求头的请求会匹配该路由。
设置:
predicates:
# 带有指定请求头的请求会匹配该路由。
- Header=auth
接口请求:
2.4.8 predicates - Host
描述:带有指定Host的请求会匹配该路由。
设置:
predicates:
#带有指定Host的请求会匹配该路由。
- Host=www.zqh.com
2.4.9 predicates - RemoteAddr
描述:从指定路径的请求会匹配该路由。
设置:
predicates:
#从指定远程地址发起的请求可以匹配该路由。
- RemoteAddr=192.168.0.100
2.5 filters - 测试用例
2.5.1 filters - AddRequestParameter
描述:会对GET请求添加请求参数
配置:
service-url:
user-service: http://localhost:8084
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
# 路由的ID
- id: user-service
# 匹配后路由地址
uri: ${service-url.user-service}
predicates:
# 发送指定路径的请求会匹配该路由。
- Path=/api/user/*
filters:
- AddRequestParameter=username,z
请求:http://localhost:8093/api/user/getUserList
相当于:http://localhost:8084/api/user/getUserList?username=z
2.5.2 filters - StripPrefix
描述:会把开头的请求的路径去除指定位数
配置:
service-url:
user-service: http://localhost:8084
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
# 路由的ID
- id: user-service
# 匹配后路由地址
uri: ${service-url.user-service}
predicates:
# 发送指定路径的请求会匹配该路由。
- Path=/api/user/*
filters:
- StripPrefix=1
请求:http://localhost:8093/api/user/getUserList
相当于:http://localhost:8084/user/getUserList
2.5.3 filters - PrefixPath
描述:会对所有GET请求添加指定路径前缀
配置:
service-url:
user-service: http://localhost:8084
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
# 路由的ID
- id: user-service
# 匹配后路由地址
uri: ${service-url.user-service}
predicates:
# 发送指定路径的请求会匹配该路由。
- Path=/user/*
filters:
- PrefixPath=/api
请求:http://localhost:8093/user/getUserList
相当于:http://localhost:8084/api/user/getUserList
2.5.3 filters - hystrix
描述:Hystrix 过滤器允许你将断路器功能添加到网关路由中,使你的服务免受级联故障的影响,并提供服务降级处理。
pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
yml:
service-url:
user-service: http://localhost:8084
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
# 路由的ID
- id: user-service
# 匹配后路由地址
uri: ${service-url.user-service}
predicates:
# 发送指定路径的请求会匹配该路由。
- Path=/api/user/*
filters:
- name: Hystrix
args:
name: fallbackcmd
#转发路径
fallback-uri: forward:/fallback
controller:
package com.zqh.www.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class FallbackController {
@GetMapping("/fallback")
public Object fallback() {
Map<String, Object> result = new HashMap<>();
result.put("data", null);
result.put("message", "Get request fallback!");
result.put("code", 500);
return result;
}
}
测试:
正常情况: 服务异常:(关闭user-service服务模拟服务断开)
2.5.3 filters - RequestRateLimiter
描述:RequestRateLimiter 过滤器可以用于限流,使用RateLimiter实现来确定是否允许当前请求继续进行,如果请求太大默认会返回HTTP 429-状态。
pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
yml:
service-url:
user-service: http://localhost:8084
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
# 路由的ID
- id: user-service
# 匹配后路由地址
uri: ${service-url.user-service}
predicates:
# 发送指定路径的请求会匹配该路由。
- Path=/api/user/*
filters:
- name: RequestRateLimiter
args:
# 每秒允许处理的请求数量
redis-rate-limiter.replenishRate: 1
# 每秒最大处理的请求数量
redis-rate-limiter.burstCapacity: 2
# 限流策略,对应策略的Bean
key-resolver: "#{@ipKeyResolver}"
config:
package com.zqh.www.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import java.util.Objects;
@Configuration
public class RedisRateLimiterConfig {
// 如果你是采用第二个的策略,那么第一个策略的@Bean记得要注释掉,否则他会报找到2个参数
// @Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getQueryParams().getFirst("username")));
}
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName());
}
}
测试:
正常情况: 异常情况:在快速请求的情况下返回429
2.5.4 filters - Retry
描述:对路由请求进行重试的过滤器,可以根据路由请求返回的HTTP状态码来确定是否进行重试。
yml:
service-url:
user-service: http://localhost:8084
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
# 路由的ID
- id: user-service
# 匹配后路由地址
uri: ${service-url.user-service}
predicates:
# 发送指定路径的请求会匹配该路由。
- Path=/api/user/*
filters:
- name: Retry
args:
#需要进行重试的次数
retries: 1
#返回哪个状态码需要进行重试,返回状态码为5XX进行重试
statuses: BAD_GATEWAY
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
测试:
正常: 异常:(在提供服务的增加一个null指针),会输出2次错误,表示重试了1次
3.微服务使用gateway
3.1 pom.xm依赖
<dependencies>
<!-- 配合注册中心使用 -->
<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-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
</dependencies>
3.2 启动类
package com.zqh.www;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.core.env.Environment;
/**
* 开启服务发现客户端
*
* @EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient 可以是其他注册中心。
*/
// 如果你是启动的是alone的配置,请注释@EnableDiscoveryClient,并且注释 pom.xml 里面的 eureka-client 依赖,不然会出现链接错误
@EnableDiscoveryClient
@SpringBootApplication
public class ApiGatewayApplication {
private final static Logger logger = LoggerFactory.getLogger(ApiGatewayApplication.class);
public static void main(String[] args) {
Environment env = SpringApplication.run(ApiGatewayApplication.class, args).getEnvironment();
logger.info(
"\n----------------------------------------------------------\n\t"
+ "Application '{}' is running! Access URLs:\n\t"
+ "Local: \t\thttp://localhost:{}{}"
+ "\n----------------------------------------------------------",
env.getProperty("spring.application.name"), env.getProperty("server.port"),
env.getProperty("server.servlet.context-path") != null ? env.getProperty("server.servlet.context-path") : "");
}
}
3.3 yml配置
server:
port: 8093
eureka:
client:
service-url:
#注册地址
defaultZone: http://root:root@localhost:8081/eureka/
#显示服务器IP加端口
instance:
hostname: localhost
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
spring:
application:
name: api-gateway
cloud:
gateway:
discovery:
locator:
#开启从注册中心动态创建路由的功能
enabled: true
#使用小写服务名
lower-case-service-id: true
routes:
# 路由的ID
- id: user-service
# 在结合注册中心使用过滤器的时候,uri的协议为lb,这样才能启用Gateway的负载均衡功能。
uri: lb://user-service
predicates:
# 发送指定路径的请求会匹配该路由。
- Path=/api/user/*
# # 发送指定方法的请求会匹配该路由。
# - Method=GET
# # 在指定时间之后的请求会匹配该路由。
# - After=2020-10-20T18:00:00+08:00[Asia/Shanghai]
# # 在指定时间之前的请求会匹配该路由。
# - Before=2020-10-20T12:00:00+08:00[Asia/Shanghai]
# # 在指定时间区间内的请求会匹配该路由。
# - Between=2020-10-20T12:00:00+08:00[Asia/Shanghai], 2020-10-30T12:00:00+08:00[Asia/Shanghai]
# # 带有指定Cookie的请求会匹配该路由
# - Cookie=username,z
# # 带有指定请求头的请求会匹配该路由。
# - Header=auth
# #带有指定Host的请求会匹配该路由。
# - Host=www.zqh.com
# # 从指定远程地址发起的请求可以匹配该路由。
# - RemoteAddr=192.168.1.1/24
filters:
- name: Retry
args:
#需要进行重试的次数
retries: 3
#返回哪个状态码需要进行重试,返回状态码为5XX进行重试
statuses: BAD_GATEWAY
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
# - name: RequestRateLimiter
# args:
# # 每秒允许处理的请求数量
# redis-rate-limiter.replenishRate: 1
# # 每秒最大处理的请求数量
# redis-rate-limiter.burstCapacity: 2
# # 限流策略,对应策略的Bean
# key-resolver: "#{@ipKeyResolver}"
# - name: Hystrix
# args:
# name: fallbackcmd
# #转发路径
# fallback-uri: forward:/fallback
# - AddRequestParameter=username,z
# - StripPrefix=1
# - PrefixPath=/api
logging:
level:
org.springframework.cloud.gateway: debug