API网关
- 网关路由必须支持负载均衡,服务列表是从注册中心拉取的
- 客户端发出请求的URL指向的是网关,URL还必须要包含目标信息
- 网关收到URL,通过一定的规则,要能识别出交给哪个实例去处理
- 网关有能力对请求响应进行修改
引入依赖包
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId></dependency>
</dependencies>
配置启动类
@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApplication {public static void main(String[] args) {SpringApplication.run(GateWayApplication.class, args);}
}
编写application.yaml的配置文件
server:port: 8341
Spring:application:name: gateway-servicecloud:nacos:discovery:server-addr: 192.168.200.130:8848namespace: ce6fa22e-fc35-4fd3-9568-2cf609059088group: JIAGOU_GROUPfile-extension: yamlprefix: ${Spring.application.name}heart-beat-interval: 5gateway:discovery:locator:enabled: true #启用DiscoveryClient的标志routes:- id: course-service-route # 路由的唯一标识 http://localhost:8341/course/api/courses/1uri: lb://course-agg-service #网关收到请求后将请求转发到 lb://course-agg-servicepredicates:- Path=/course/** # 断言匹配请求路径filters: # 过滤器,可以做认证、限流、日志等- StripPrefix=1 # 针对URL剪切前缀,这里去掉了前缀/course
主要概念:
- routes:路由定义了匹配请求的条件,以及如何将请求转发到下游服务
- predicates:用于匹配请求的条件,比如请求路径、请求参数、头部信息等。
- filter:过滤器,进行模式匹配
路由谓词工厂
- After=2017-01-20T17:42:47.789-07:00[America/Denver] # 在某个时间之后
- Before=2017-01-20T17:42:47.789-07:00[America/Denver] #在某个时间之前- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] # 两者之间- Cookie=chocolate, ch.p #cookie谓词工厂,此谓词匹配具有给定名称且其值与正则表达式匹配的 Cookie。- Header=X-Request-Id, \d+- Host=**.somehost.org,**.anotherhost.org- Method=GET,POST- Path=/red/{segment},/blue/{segment}- Query=green- RemoteAddr=192.168.1.1/24
Filter工厂
- AddRequestHeader:向请求添加一个 HTTP 头。
- AddResponseHeader:向响应添加一个HTTP 头。
- DedupeResponseHeader:去除响应头中的重复值。
- PrefixPath:为请求路径添加前缀。
- PreserveHostHeader:保留原始请求的 Host 头。
- RedirectTo:重定向请求到另一个 URL。章
- RemoveRequestHeader:移除请求中的一个 HTTP 头。
- RemoveResponseHeader:移除响应中的一个HTTP 头。
- RewritePath:重写请求路径。
- SetPath:设置请求路径。
- SetRequestHeader:设置请求头。
- SetResponseHeader:设置响应头。
- SetStatus:设置响应状态码。
- StripPrefix:去除请求路径中的前缀
1网关全局过滤器
@Component
@Order(1)
@Slf4j
public class LoggingGlobalFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();long startTime = System.currentTimeMillis();return chain.filter(exchange).then(Mono.fromRunnable(() -> {//Mono.fromRunnable此时所有的请求都处理完成后才执行ServerHttpResponse response = exchange.getResponse();long endTime = System.currentTimeMillis();log.info("请求地址:{},请求方法:{},响应状态码:{},响应时间:{}",request.getURI(),request.getMethod(),response.getStatusCode().value(),endTime-startTime);}));}
}
2实现认证过滤器
在微服务中,服务数量较多,不能再服务中进行认证,会进行统一的认证,认证成功后颁发Token,前端会将令牌放在RequestHeader中
创建一个对所有的请求进行认证的全局过滤器
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class AuthenticationGlobalFilter implements GlobalFilter {// 认证头private static final String AUTHENTICATION_HEADER = "Authorization";// 登录URLprivate static final String LOGIN_URL = "/api/login";@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {if(exchange.getRequest().getPath().toString().contains(LOGIN_URL)){return chain.filter(exchange);}String token = exchange.getRequest().getHeaders().getFirst(AUTHENTICATION_HEADER);if(token == null || IsValid(token)){exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);// 请求结束return exchange.getResponse().setComplete();}return chain.filter(exchange);}private boolean IsValid(String token) {//此处通过工具类校验return true;}
}
3网关的限流
此处集成了redis
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
自定义一个Config类:但要注意此处的限流策略只能选择一种,也就是说@Bean只能生效一个方法
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;@Configuration
public class RateLimiterConfiguration {/*** 使用ip地址作为限流* @return*/@Beanpublic KeyResolver ipKeyResolver(){return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());}/*** 使用用户id作为限流,需要在请求中携带userId参数* @return*///@Beanpublic KeyResolver userKeyResolver() {return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));}/*** 返回一个KeyResolver对象,用于解析请求的路径** @return KeyResolver路径解析器,通过请求对象获取路径值*///@Beanpublic KeyResolver pathResolver() {// Lambda表达式用于创建KeyResolver的实例// Mono.just确保返回一个包含请求路径的Mono Fluxreturn exchange -> Mono.just(exchange.getRequest().getPath().value());}
}
在application.yaml文件中修改策略配置
server:port: 8341
Spring:application:name: gateway-servicecloud:nacos:discovery:server-addr: 192.168.200.130:8848namespace: ce6fa22e-fc35-4fd3-9568-2cf609059088group: JIAGOU_GROUPfile-extension: yamlprefix: ${Spring.application.name}heart-beat-interval: 5gateway:discovery:locator:enabled: true #启用DiscoveryClient的标志routes:- id: course-service-route # 路由的唯一标识 http://localhost:8341/course/api/courses/1uri: lb://course-agg-service #网关收到请求后将请求转发到 lb://course-agg-servicepredicates:- Path=/course/** # 断言匹配请求路径filters: # 过滤器,可以做认证、限流、日志等- StripPrefix=1 # 针对URL剪切前缀,这里去掉了前缀/course- name: RequestRateLimiter # 定义限流策略args:redis-rate-limiter.replenishRate: 1 # 每秒填充的令牌数redis-rate-limiter.burstCapacity: 2 # 令牌桶的容量key-resolver: "#{@ipKeyResolver}" # 限流key的生成策略redis:host: 43.143.110.136port: 6379database: 0pool:max-active: 8 # 连接池最大连接数(使用负值表示没有限制)max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)max-idle: 8 # 连接池中的最大空闲连接password: 1234
当请求数量大于令牌个数时,触发限流策略,状态码显示429