Spring Cloud Gateway 微服务网关
-
创建项目(命名 为 Gateway), 添加pom 依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>2.2.0.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2.2.0.RELEASE</version> </dependency> -
Router 路由器:包括,11种路由模式,具体参见官网
-
添加路由配置: 意思是所有路径匹配到 /acc/** ,都会路由到 lb://account-service
spring: cloud: gateway: routes: - id: host_route uri: lb://account-service predicates: - Path=/acc/** -
在启动类添加注解,服务注册和发现
@EnableDiscoveryClient public class GatewayApplication { } -
测试
- 默认路由地址:http://localhost:9001/account-service/acc/user?id=2
- 使用Path路由配置:http://localhost:9001/acc/user?id=2
-
打开gateway 监控端点 ,各端点介绍,参见Gateway管理API
<!-- 添加 actuator 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>#将 gateway 端点暴露 management.endpoint.gateway.enabled=true management.endpoints.web.exposure.include=gateway查询全局过滤器, http://localhost:9001/actuator/gateway/globalfilters 查询路由中的过滤器, http://localhost:9001/actuator/gateway/routefilters 刷新路由缓存, http://localhost:9001/actuator/gateway/refresh 展示路由列表, http://localhost:9001/actuator/gateway/routes 获取单个路由的信息, http://localhost:9001/actuator/gateway/routes/host_route 创建路由,发送Post请求, ../actuator/gateway/routes/{id_route_to_create} 删除路由,发送Delete请求: ../actuator/gateway/routes/{id_route_to_delete}
-
-
Filter 过滤器
-
网关经常需要对路由请求进行过滤,如鉴权之后构造头部之类的,过滤的种类很多,如增加请求头、增加请求参数 、增加响应头和断路器等等功能,这就用到了Spring Cloud Gateway 的 Filter。
-
Filter 的作用:
- 当我们有很多个服务时,客户端请求各个服务的Api时,每个服务都需要做相同的事情,比如鉴权、限流、日志输出等。
- 因此可以将这些功能等转移到网关处理以减少重复开发。
-
- PRE: 这种过滤器在被代理的微服务(Proxied Service)执行之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- POST:这种过滤器在被代理的微服务(Proxied Service)执行完成后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
-
Filter 的分类
-
GatewayFilter:应用到单个路由或者一个分组的路由上。自定义的GatewayFilter,同样需要指定某个路由器
-
GlobalFilter:应用到所有的路由上。当请求到来时,
Filtering Web Handler处理器会添加所有GlobalFilter实例和匹配的GatewayFilter实例到过滤器链中。因此,自定义的GlobalFilter,不需要什么额外的配置。 -
自定义 GatewayFilter,有两种实现方式
-
一种是直接 实现GatewayFilter接口,不过还需要手动将自定义的GatewayFilter 注册到 router中
// 统计某个或者某种路由的处理时长 public class CustomerGatewayFilter implements GatewayFilter, Ordered { private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilter.class ); private static final String COUNT_START_TIME = "countStartTime"; @Override public Mono<Void> filter(ServerWebExchange exch, GatewayFilterChain chain) { return chain.filter(exch).then( Mono.fromRunnable(() -> { long startTime = exch.getAttribute(COUNT_START_TIME); long endTime=(Instant.now().toEpochMilli() - startTime); log.info(exch.getRequest().getURI().getRawPath() + ": " + endTime + "ms"); }) ); } @Override public int getOrder() { return 0; } } -
另一种是 自定义过滤器工厂(继承AbstractGatewayFilterFactory类) , 选择自定义过滤器工厂的方式,可以在配置文件中配置过滤器
@Component public class CustomerGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomerGatewayFilterFactory.Config> { private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilterFactory.class ); private static final String COUNT_START_TIME = "countStartTime"; @Override public List<String> shortcutFieldOrder() { return Arrays.asList("enabled"); } public CustomerGatewayFilterFactory() { super(Config.class); log.info("Loaded GatewayFilterFactory [CustomerGatewayFilterFactory]"); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { if (!config.isEnabled()) { return chain.filter(exchange); } exchange.getAttributes().put(COUNT_START_TIME, System.currentTimeMillis()); return chain.filter(exchange).then( Mono.fromRunnable(() -> { Long startTime = exchange.getAttribute(COUNT_START_TIME); if (startTime != null) { StringBuilder sb = new StringBuilder( exchange.getRequest().getURI().getRawPath() ).append(": ") .append(System.currentTimeMillis() - startTime) .append("ms"); sb.append(" params:") .append(exchange.getRequest().getQueryParams()); log.info(sb.toString()); }})); }; } public static class Config { //控制是否开启统计 private boolean enabled; public Config() {} // getter setter 省略 } }spring: cloud: gateway: routes: - id: elapse_route uri: lb://account-service filters: - Customer=true predicates: - Method=GET
-
-
自定义GlobalFilter : 可以在这里实现授权、日志等功能
@Component public class AuthorizeFilter implements GlobalFilter, Ordered { private static final Logger log = LoggerFactory.getLogger(AuthorizeFilter.class); private static final String AUTHORIZE_TOKEN = "token"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){ String token = exchange.getRequest().getQueryParams() .getFirst(AUTHORIZE_TOKEN); if ( StringUtils.isBlank( token )) { log.info( "token is empty ..." ); exchange.getResponse().setStatusCode( HttpStatus.UNAUTHORIZED ); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return 0; } }
-
-
Gateway + Nacos 实现动态路由 , 参见 springcloud-demo
-
路由信息定义在配置文件中,这种方式有一个缺点就是修改路由信息必须重启服务才能生效。网关作为全部流量的入口,可用时间当然越长越好,不重启服务而修改路由是一个更好的选择,结合Nacos可以做到这一点。在Gateway项目中添加 nacos config 依赖,并配置nacos地址
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>spring.cloud.nacos.config.server-addr=127.0.0.1:8848 -
Spring Cloud Gateway本身还不支持直接从Nacos动态加载路由配置表,需要自己编写监听器监听配置变化并刷新路由表
@Component public class NacosDynamicRouteService implements ApplicationEventPublisherAware { @Override public void setApplicationEventPublisher(ApplicationEventPublisher aep) { this.applicationEventPublisher = aep; } private ApplicationEventPublisher applicationEventPublisher; private String dataId = "gateway-router"; private String group = "DEFAULT_GROUP"; @Value("${spring.cloud.nacos.config.server-addr}") private String serverAddr; @Autowired private RouteDefinitionWriter routeDefinitionWriter; private static final List<String> ROUTE_LIST = new ArrayList<>(); @PostConstruct public void dynamicRouteByNacosListener() { try { ConfigService configService = NacosFactory .createConfigService(serverAddr); // springboot 启动时,获取配置信息 String configInfo = configService.getConfig(dataId, group, 5000); configRouter(configInfo); // 监听 nacos 上的配置文件,当配置文件被修改后,获取配置信息 configService.addListener(dataId, group, new Listener() { @Override public void receiveConfigInfo(String configInfo) { configRouter(configInfo); } @Override public Executor getExecutor() { return null; } }); } catch (NacosException e) { e.printStackTrace(); } } private void configRouter(String configInfo){ clearRoute(); try { List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray( configInfo, RouteDefinition.class); for (RouteDefinition routeDefinition : gatewayRouteDefinitions) { addRoute(routeDefinition); } publish(); } catch (Exception e) { e.printStackTrace(); } } private void clearRoute() { for(String id : ROUTE_LIST) { this.routeDefinitionWriter.delete(Mono.just(id)).subscribe(); } ROUTE_LIST.clear(); } private void addRoute(RouteDefinition definition) { try { routeDefinitionWriter.save(Mono.just(definition)).subscribe(); ROUTE_LIST.add(definition.getId()); } catch (Exception e) { e.printStackTrace(); } } private void publish() { this.applicationEventPublisher.publishEvent( new RefreshRoutesEvent(this.routeDefinitionWriter)); } } -
在 Nacos 管理后台,创建配置文件 gateway-router.json ,内容如下。
[{ "id": "account-router", "order": 0, "predicates": [{"args": {"pattern": "/acc/**"},"name": "Path" }], "filters":[{"args": {"pattern": true },"name": "Customer" }], "uri": "lb://account-service" }] -
同时删除原来在 application.xml 中关于路由的配置
-
测试:http://localhost:9001/acc/user?id=2
-
本文详细介绍了Spring Cloud Gateway的使用,包括创建项目、配置路由、添加过滤器等。强调了过滤器在微服务网关中的重要性,如鉴权、限流和日志输出,并解释了Filter的生命周期和分类。还提到了如何自定义GatewayFilter和GlobalFilter,以及利用Nacos实现动态路由。最后展示了如何在Nacos中配置路由并测试。

1887

被折叠的 条评论
为什么被折叠?



