Phân tích GlobalFilter và AbstractGatewayFilterFactory
Trong hệ sinh thái Spring Cloud Gateway, việc xử lý các logic bổ trợ như bảo mật, ghi nhật ký hoặc thay đổi luồng dữ liệu thường được thực hiện thông qua Filter. Có hai cơ chế chính để tạo filter tùy chỉnh là GlobalFilter và AbstractGatewayFilterFactory.
1. So sánh chi tiết hai loại Filter
| Tiêu chí | GlobalFilter | AbstractGatewayFilterFactory |
|---|---|---|
| Phạm vi hoạt động | Tự động áp dụng cho mọi Route. | Chỉ áp dụng cho các Route được chỉ định cụ thể. |
| Cấu hình | Không cần khai báo trong YAML, tự kích hoạt qua Bean. | Phải đăng ký tên filter trong file cấu hình (YAML/Properties). |
| Truyền tham số | Khó truyền tham số động từ cấu hình. | Hỗ trợ class Config để nhận tham số tùy chỉnh. |
| Mục đích sử dụng | Các logic dùng chung: Auth, log hệ thống, CORS. | Logic đặc thù cho từng dịch vụ: Rate limit, rewrite path. |
2. Triển khai GlobalFilter cho xác thực người dùng
GlobalFilter được ưu tiên khi bạn muốn thực hiện một hành động kiểm tra duy nhất cho tất cả các request đi qua Gateway. Ví dụ dưới đây minh họa cách kiểm tra Token bảo mật.
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class SecurityInspectionFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String secretKey = request.getHeaders().getFirst("X-Custom-Token");
// Logic kiểm tra đơn giản
if (secretKey == null || !secretKey.equals("safe-zone")) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
// Thứ tự ưu tiên cao nhất
return Ordered.HIGHEST_PRECEDENCE;
}
}
3. Triển khai AbstractGatewayFilterFactory để đo thời gian phản hồi
Filter Factory cho phép bạn tùy biến linh hoạt hơn bằng cách chỉ định các tham số trong file application.yml.
Bước 1: Định nghĩa Filter Factory
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
public class LatencyTrackerFilterFactory extends AbstractGatewayFilterFactory<LatencyTrackerFilterFactory.Config> {
private static final Logger log = LoggerFactory.getLogger(LatencyTrackerFilterFactory.class);
public LatencyTrackerFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
long startTimestamp = System.currentTimeMillis();
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
long endTimestamp = System.currentTimeMillis();
long totalTime = endTimestamp - startTimestamp;
log.info("Path: {} | Execution Time: {} ms",
exchange.getRequest().getURI().getPath(), totalTime);
}));
};
}
public static class Config {
// Có thể thêm các thuộc tính cấu hình tại đây
}
}
Bước 2: Cấu hình trong YAML
spring:
cloud:
gateway:
routes:
- id: inventory-service
uri: lb://inventory-service
predicates:
- Path=/inventory/**
filters:
- name: LatencyTrackerFilter # Sử dụng tên class bỏ hậu tố GatewayFilterFactory
4. Theo dõi hiệu năng với Micrometer và Prometheus
Trong môi trường Production, việc chỉ in ra log là chưa đủ. Chúng ta cần đẩy các dữ liệu về thời gian phản hồi lên các hệ thống giám sát như Prometheus.
Cấu hình Dependency:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
Sử dụng MeterRegistry trong Filter:
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
// Trong class filter, inject MeterRegistry
@Autowired
private MeterRegistry registry;
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Timer.Sample sample = Timer.start(registry);
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
sample.stop(registry.timer("gateway.response.time",
"uri", exchange.getRequest().getURI().getPath()));
}));
}
Cuối cùng, đừng quên kích hoạt endpoint prometheus trong cấu hình để các công cụ bên ngoài có thể thu thập dữ liệu:
management:
endpoints:
web:
exposure:
include: prometheus