服务网关在微服务中的应用
Gateway做什么
- 路由寻址(主要)
- 负载均衡(Ribbon)
- 限流
- 鉴权
第二代网关组件Gateway介绍(Zuul的对比)
| Gateway | Zuul 1.x | Zuul 1.x |
|---|
| 靠谱性 | 官方支持 | 曾经靠谱过 | 专业放鸽子 |
| 性能 | Netty | 同步阻塞性能慢 | Netty |
| RPS | >32000 | 20000左右 | 25000左右 |
| Spring Cloud | 已整合 | 已整合 | 占无整合计划 |
| 长链接 | 支持 | 不支持 | 支持 |
| 编程体验 | 略难 | 简单易上手 | 略难 |
| 调试&链路追踪 | 略难 | 无压力 | 略难 |
Gateway急速落地
创建gateway-sample项目
- 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</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>
</dependencies>
- 创建启动类
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
- 配置文件
server:
port: 65000
spring:
rabbitmq:
username: gateway-service
cloud:
gateway:
discovery:
locator:
enabled: true
eureka:
instance:
preferIpAddress: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
service-url:
defaultZone: http://localhost:20000/eureka/
management:
security:
enabled: false
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
连接Eureka自动创建路由
- 启动服务
- 验证路由规则
- 设置小写访问
spring:
application:
name: gateway-service
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
- 验证路由规则
通过Actuator实现动态路由功能
{
"predicates":[
{
"name":"Path",
"args":{
"_genkey_0":"/dynamic/**"
}
}
],
"filters":[
{
"name":"StripPrefix",
"args":{
"_genkey_0":"1"
}
}
],
"uri":"lb://FEIGN-CLIENT",
"order":0
}
相关接口文档
Gateway断言功能
Path断言
- 使用Path断言转发请求(yml配置+java配置)
spring:
application:
name: gateway-service
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: feignclient
uri: lb://FEIGN-CLIENT
predicates:
- Path=/yml/**
filters:
- StripPrefix=1
@Configuration
public class GatewayConfiguration {
@Bean
@Order
public RouteLocator customizedRoutes(RouteLocatorBuilder builder){
return builder.routes()
.route(r-> r.path("/java/**")
.and().method(HttpMethod.GET)
.and().header("name")
.filters(f->f.stripPrefix(1)
.addRequestHeader("java-param","gateway-config")
)
.uri("lb://FEIGN-CLIENT")
).build();
}
}
After断言
- 创建模拟下单接口
@RestController
@Slf4j
@RequestMapping("/gateway")
public class GatewayController {
public static final Map<Long, Product> items = new ConcurrentHashMap<>();
@GetMapping("/details")
public Product get(@RequestParam("pid") Long pid) {
if (items.containsKey(pid)) {
Product prod = Product.builder()
.productId(pid)
.description("好吃不贵")
.stock(100L)
.build();
items.putIfAbsent(pid, prod);
}
return items.get(pid);
}
@PostMapping("/placeOrder")
public String buy(@RequestParam("pid") String pid) {
Product prod = items.get(pid);
if (prod == null) {
return "Product not found";
} else if (prod.getStock() <= 0L) {
return "Sold out";
}
synchronized (prod) {
if (prod.getStock() <= 0L) {
return "Sold out";
}
prod.setStock(prod.getStock()-1);
}
return "Order Placed";
}
}
- 通过After断言设置生效时间
@Configuration
public class GatewayConfiguration {
@Bean
@Order
public RouteLocator customizedRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.path("/java/**")
.and().method(HttpMethod.GET)
.and().header("name")
.filters(f -> f.stripPrefix(1)
.addRequestHeader("java-param", "gateway-config")
)
.uri("lb://FEIGN-CLIENT")
)
.route(r -> r.path("/seckill/**")
.and().after(ZonedDateTime.now().plusMinutes(1))
.filters(f -> f.stripPrefix(1))
.uri("lb://FEIGN-CLIENT"))
.build();
}
}
Gat过滤器原理和生命周期
自定义过滤器
@Slf4j
@Component
public class TimerFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
StopWatch timer = new StopWatch();
timer.start(exchange.getRequest().getURI().getRawPath());
return chain.filter(exchange).then(
Mono.fromRunnable(()->{
timer.stop();
log.info(timer.prettyPrint());
})
);
}
@Override
public int getOrder() {
return 0;
}
}
添加TimerFilter到路由
.route(r -> r.path("/java/**")
.and().method(HttpMethod.GET)
.and().header("name")
.filters(f -> f.stripPrefix(1)
.addRequestHeader("java-param", "gateway-config")
.filter(timerFilter)
)
.uri("lb://FEIGN-CLIENT")
)
- 全局filter
将TimerFilter实现的GatewayFilter换为GlobalFilter