使⽤服务⽹关作为接⼝服务的统⼀代理,前端通过⽹关完成服务的统⼀调⽤
路由:接⼝服务的统⼀代理,实现前端对接⼝服务的统⼀访问过滤:对⽤户请求进⾏拦截、过滤(⽤户鉴权)、监控限流:限制⽤户的访问流量
NginxSpring Cloud Netflix zuulSpring Cloud Gateway
Nginx通常被⽤作应⽤服务器⽹关,服务⽹关通常使⽤zuul或者gateway
创建SpringBoot应⽤,添加gateway依赖
application.ymlserver: port: 9999 spring: application: name: gateway-server cloud: gateway: routes: # 配置api-service1路由规则 - id: api-service1 uri: 'http://localhost:8001' predicates: - Path=/product/** # 配置api-service2路由规则 - id: api-service2 uri: 'http://localhost:8002' predicates: - Path=/order/**
Route: 路由是⽹关的基本组成部分,它是由id、⽬标uri、断⾔组成,如果断⾔为true,则匹配该路由,转向到当前路由的URIPredicate:断⾔,⽤户请求的匹配规则Filter:过滤器,⽤于对请求进⾏前置、后置处理(可以在⽹关实现对请求或相应的加⼯处理)
SpringCloud Gateway提供了多种断⾔匹配的⽅式:
- After
- Before
- Between
- Cookie
- Header
- Host
- Method
- Path
- Query
- RemoteAddr
- Weight
根据请求路径的正则匹配
spring: application: null name: gateway-server cloud: null gateway: null routes: - id: aaa uri: 'http://localhost:8001' predicates: - Path=/product/** - id: bbb uri: 'http://localhost:8002' predicates: - Path=/order/**
根据请求携带的参数匹配路由
spring: application: name: gateway-server cloud: gateway: routes: - id: aaa uri: 'http://localhost:8001' predicates: # 如果请求url中带有name参数 ---> http://localhost:8001 - Query=name - id: bbb uri: 'http://localhost:8002' predicates: #如果请求url中带有pwd参数 ---> http://localhost:8002 - Query=pwd
根据Header中携带的参数匹配
spring: application: name: gateway-server cloud: gateway: routes: - id: aaa uri: 'http://localhost:8001' predicates: - Header=token - id: bbb uri: 'http://localhost:8002' predicates: - 'Header=aa,haha'
gateway⽹关可以对⽤户的请求和响应进⾏处理,gateway提供了多个内置的过滤器,不同的过滤器可以完成不同的请求或者响应的处理1. AddRequestHeader 在请求头中添加参数2. AddRequestParameter 添加请求参数3. AddResponseHeader4. The DedupeResponseHeader GatewayFilter Factory5. The Hystrix GatewayFilter Factory6. Spring Cloud CircuitBreaker GatewayFilter Factory7. The FallbackHeaders GatewayFilter Factory8. The MapRequestHeader GatewayFilter Factory9. The PrefixPath GatewayFilter Factory10. The PreserveHostHeader GatewayFilter Factory11. The RequestRateLimiter GatewayFilter Factory12. The RedirectTo GatewayFilter Factory13. The RemoveRequestHeader GatewayFilter Factory14. RemoveResponseHeader GatewayFilter Factory15. The RemoveRequestParameter GatewayFilter Factory16. The RewritePath GatewayFilter Factory17. RewriteLocationResponseHeader GatewayFilter Factory18. The RewriteResponseHeader GatewayFilter Factory19. The SaveSession GatewayFilter Factory20. The SecureHeaders GatewayFilter Factory21. The SetPath GatewayFilter Factory22. The SetRequestHeader GatewayFilter Factory23. The SetResponseHeader GatewayFilter Factory24. The SetStatus GatewayFilter Factory25. The StripPrefix GatewayFilter Factory26. The Retry GatewayFilter Factory27. The RequestSize GatewayFilter Factory28. The SetRequestHostHeader GatewayFilter Factory29. Modify a Request Body GatewayFilter Factory30. Modify a Response Body GatewayFilter Factory31. Default Filters
spring: application: name: gateway-server cloud: gateway: routes: - id: aaa uri: 'http://localhost:8001' predicates: - Path=/red/aaa/product/** filters: - 'AddRequestHeader=token,wahahaawahaha' - 'AddRequestParameter=username, ergou' - SetStatus=404 - StripPrefix=2
1、实现GatewayFilter, Ordered
public class MyFilter01 implements GatewayFilter, Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); System.out.println("---------------⾃定义过滤器"); return chain.filter(exchange); }
@Override public int getOrder() { return 0; } }2、配置过滤器
@Configuration public class GatewayConfig {
@Bean public RouteLocator routeLocator(RouteLocatorBuilder builder){ System.out.println("-----------------------init"); RouteLocator routeLocator = builder.routes().route( r-> r.path("/product/**") // predicates .filters(f->f.filters( new MyFilter01() )) //filters .uri("http://localhost:8001") //uri ).build(); return routeLocator; }
}
相当于扩展Gateway内置的⽹关过滤器
/** * 创建⼀个类继承AbstractNameValueGatewayFilterFactory,类名必须以GatewayFilterFactory结尾,类名前⾯的部分即为当前⾃定义⽹关过滤器的 名字 * 添加@Component注解,注册到Spring容器 */ @Component public class MyFilterGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { @Override public GatewayFilter apply(NameValueConfig config) { System.out.println("name:"+config.getName()); System.out.println("value:"+config.getValue()); //创建⾃定义⽹关过滤器并返回 GatewayFilter gatewayFilter = new GatewayFilter() { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~⾃定义⽹关过滤器"); return chain.filter(exchange); } }; return gatewayFilter; } }
spring: application: name: gateway-server cloud: gateway: routes: - id: bbb uri: 'http://localhost:8002' predicates: - Path=/order/** filters: - 'MyFilter=aa,bb'
上述我们讲到的过滤器,都是配置在某个路由/服务中,我们称之为 ⽹关服务过滤器 ,Gateway提供了内置的全局过滤器,会拦截过滤所有到达⽹关服务器的请求。内置的全局过滤器默认⽣效,⽆需开发者⼲预。根据业务的需求我们也可以⾃定义全局过滤器以实现对所有⽹关请求的拦截和处理。
@Component public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("----------------------------------------------MyGlobalFilter"); List<String> list = exchange.getRequest().getHeaders().get("token"); if (list != null && list.size()>0){ String token = list.get(0); System.out.println("token:"+token); return chain.filter(exchange); } else{ //如果没有token,或者token过期 ServerHttpResponse response = exchange.getResponse(); //设置响应头 response.getHeaders().add("ContentType","application/json;charset=utf-8"); //设置状态码 response.setStatusCode(HttpStatus.UNAUTHORIZED); // 封装响应数据 String str = ""; DataBuffer dataBuffer = response.bufferFactory().wrap(str.getBytes()); return response.writeWith(Mono.just(dataBuffer)); } }
@Override public int getOrder() { return 0; } }
如果在Gateway⽹关的路由配置中,直接将服务的ip port配置进去,将导致:1.如果服务的地址变更,必须要重新配置gateway的路由规则2.如果服务采⽤集群部署,则不能实现负载均衡
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
application.ymlserver: port: 9999 spring: application: name: gateway-server main: web-application-type: reactive cloud: gateway: routes: - id: aaa uri: 'lb://api-service1' predicates: - Path=/product/** - id: bbb uri: 'lb://api-service2' predicates: - Path=/order/** eureka: client: service-url: defaultZone: 'http://localhost:8761/eureka'
服务的统⼀访问使⽤过滤器实现鉴权使⽤⽹关实现限流:通过限制⽤户的请求进⼊到服务中,有效控制应⽤系统的QPS,达到保护系统的⽬的
就是通过记录在单位时间请求的数量,在这个时间周期内达到设置定值之后就拒绝后续请求的进⼊。缺点:如果⽤户请求频率不均匀,有导致短时间、⾼并发的⻛险
就是通过控制“漏桶”流出的速率以限制到达服务的⽤户流量缺点:⽹关会承载⽐较⼤的压⼒
就是所有进⼊⽹关的请求必须从令牌桶中得到令牌才可以进⾏服务调⽤,我们可以通过控制令牌桶的容量、令牌的产⽣速率达到控制⽤户流量的⽬的
Gateway是基于令牌桶算法,使⽤redis作为“桶”结合过滤器实现了⽹关限流。
1、添加依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.9.0</version> </dependency>2、配置keyResolver
@Configuration public class AppConfig { @Bean public KeyResolver keyResolver() { //http://localhost:9999/order/query?user=1 //使⽤请求中的user参数的值作为令牌桶的key //return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user")); return exchange -> Mono.just(exchange.getRequest().getURI().getPath()); } }3、配置服务的限流规则
server: port: 9999 spring: application: name: gateway-server main: web-application-type: reactive cloud: gateway: routes: - id: aaa uri: 'lb://api-service1' predicates: - Path=/product/** - id: bbb uri: 'lb://api-service2' predicates: - Path=/order/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1 #令牌桶每s的填充速度 redis-rate-limiter.burstCapacity: 2 #令牌桶容量 redis-rate-limiter.requestedTokens: 1 #每个请求消耗多少个令牌 key-resolver: '#{@keyResolver}' #令牌桶key生成 redis: host: 47.96.11.185 port: 7001 password: qfedu123 database: 0 lettuce: pool: max-active: 10 max-wait: 1000 max-idle: 5 min-idle: 3 eureka: client: service-url: defaultZone: 'http://localhost:8761/eureka'