路由是网关的基本单元,由ID、降级操作。1.2 SpringCloud Gateway 特征SpringCloud官方,对SpringCloud Gateway 特征介绍如下: (1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 (2)集成 Hystrix 断路器 (3)集成 Spring Cloud DiscoveryClient (4)Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters (5)具备一些网关的高级功能:动态路由、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正则匹配(PARAM_MATCH_STRATEGY_REGEX)。 全局过滤器举例:代码如下: package com.crazymaker.cloud.nacos.demo.gateway.config;import lombok.extern.slf4j.Slf4j;import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;@Configurationpublic class FilterConfig{ @Bean @Order(-1) public GlobalFilter a() { return new AFilter(); } @Bean @Order(0) public GlobalFilter b() { return new BFilter(); } @Bean @Order(1) public GlobalFilter c() { return new CFilter(); }
7.3定义局部过滤器步骤: 1 需要实现GatewayFilter, Ordered,实现相关的方法 2 加入到过滤器工厂,并且注册到spring容器中。 - id: service_provider_demo_route_filter uri: lb://service-provider-demo predicates: - Path=/filter/** filters: - RewritePath=/filter/(?<segment>.*), /provider/$\{segment} - UserIdCheck
8 整合Sentinel完成流控和降级maven依赖使用Sentinel作为gateWay的限流、静态资源等,需要配置viewResolvers以及拦截之后的异常,我们也可以自定义抛出异常的提示 public SentinelConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) { this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } @Bean//自定义异常 @Order(Ordered.HIGHEST_PRECEDENCE) public ExceptionHandler sentinelGatewayBlockExceptionHandler() { // Register the block exception handler for Spring Cloud Gateway. return new ExceptionHandler(viewResolvers, serverCodecConfigurer); }
自定义异常提示:当发生限流、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 参数(PARAM_PARSE_STRATEGY_URL_PARAM)四种模式。Redis高并发实战》一书,这里不做过多的赘述。 2.3 和注册中心相结合的路由配置方式在uri的schema协议部分为自定义的lb:类型,表示从微服务注册中心(如Eureka)订阅服务,并且进行服务的路由。(1.6.2 版本开始支持) matchStrategy:参数值的匹配策略,目前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT)、package com.crazymaker.cloud.nacos.demo.gateway.config;import com.crazymaker.cloud.nacos.demo.gateway.filter.UserIdCheckGateWayFilter;import org.springframework.cloud.gateway.filter.GatewayFilter;import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;import org.springframework.stereotype.Component;@Componentpublic class UserIdCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<Object>{ @Override public GatewayFilter apply(Object config) { return new UserIdCheckGateWayFilter(); }}
3、(1.6.2 版本开始支持) 用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则,或通过 GatewayRuleManager.register2Property(property) 注册动态规则源动态推送(推荐方式)。actuator健康检查配置,为之后的功能提供支持,此部分比较简单,不再赘述,加入以下maven依赖和配置 maven依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency><dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.1.0</version> </dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
配置文件spring: application: name: mas-cloud-gateway boot: admin: client: ### 本地搭建的admin-server url: http://localhost:8011eureka: client: registerWithEureka: true fetchRegistry: true healthcheck: enabled: true serviceUrl: defaultZone: http://localhost:6887/eureka/ enabled: truefeign: sentinel: enabled: truemanagement: endpoints: web: exposure: include: '*' endpoint: health: show-details: ALWAYS
若转发的目标地址为微服务中组件,不为具体ip:port形式的,应写成lb://mas-openapi-service形式,目标地址会从注册中心直接拉取 4.3 统一配置跨域请求:现在的请求通过经过gateWay网关时,需要在网关统一配置跨域请求,需求所有请求通过 spring: cloud: gateway: globalcors: cors-configurations: '[/**]': allowed-origins: "*" allowed-headers: "*" allow-credentials: true allowed-methods: - GET - POST - DELETE - PUT - OPTION
5 整合Nacosmaven依赖<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>nacos_gateway</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>nacos_gateway</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR3</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--nacos dicovery--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>0.2.2.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
需要注意在Gateway服务中的pom.xml文件中不要存在这个jar <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>
否则调用接口时会报以下错误因为gateway使用的是webflux,默认使用netty,所以从依赖中排除 tomcat相关的依赖 java.lang.ClassCastException: org.springframework.core.io.buffer.DefaultDataBufferFactory cannot be cast to org.springframework.core.io.buffer.NettyDataBufferFactory at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.lambda$filter$1(NettyWriteResponseFilter.java:82) ~[spring-cloud-gateway-core-2.1.3.RELEASE.jar:2.1.3.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) [reactor-core-3.2.12.RELEASE.jar:3.2.12.RELEASE]
错误2 是由于 spring-boot-starter-web 引起 服务发现配置:从Nacos获取微服务提供者清单server: port: 8087spring: application: name: nacos_gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: discovery: locator: enabled: true #表明gateway开启服务注册和发现的功能,并且spring cloud gateway自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务 lower-case-service-id: true #是将请求路径上的服务名配置为小写(因为服务注册的时候,向注册中心注册时将服务名转成大写的了 routes: -id: apiuser # uri: lb://nacos-consumer-user predicates: # http://localhost:6601/user/user/users/2, 必须加上StripPrefix=1,否则访问服务时会带上user - Path=/user/** # 转发该路径 #以下是配置例子 # - id: 163 #网关路由到网易官网 # uri: http://www.163.com/ # predicates: - Path=/163/** # - id: ORDER-SERVICE #网关路由到订单服务order-service # uri: lb://ORDER-SERVICE # predicates: # - Path=/ORDER-SERVICE/** # - id: USER-SERVICE #网关路由到用户服务user-service # uri: lb://USER-SERVICE # predicates: # - Pach=/USER-SERVICE/**
nacos实现动态配置使用nacos实现动态路由,以上两种方式都是实现的静态配置路径,只能应对部分场景,接下来配置nacos实现动态配置以及配置的存储,由于gateWay并没有适配nacos,需要自定义监听器: @Component@Slf4jpublic class NacosDynamicRouteService implements ApplicationEventPublisherAware { 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 ApplicationEventPublisher applicationEventPublisher; private static final List<String> ROUTE_LIST = new ArrayList<>(); @PostConstruct public void dynamicRouteByNacosListener() { try { ConfigService configService = NacosFactory.createConfigService(serverAddr); configService.getConfig(dataId, group, 5000); configService.addListener(dataId, group, new Listener() { @Override public void receiveConfigInfo(String configInfo) { clearRoute(); try { if (StringUtil.isNullOrEmpty(configInfo)) {//配置被删除 return;} List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class); for (RouteDefinition routeDefinition : gatewayRouteDefinitions) { addRoute(routeDefinition); } publish(); } catch (Exception e) { log.error("receiveConfigInfo error" + e); } } @Override public Executor getExecutor() { return null; } }); } catch (NacosException e) { log.error("dynamicRouteByNacosListener error" + e); } } 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) { log.error("addRoute error" + e); } } private void publish() { this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter)); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; }
在nacos中增加一个规则: [{ "filters": [], "id": "baidu_route", "order": 0, "predicates": [{ "args": { "pattern": "/baidu" }, "name": "Path" }], "uri": "https://www.baidu.com"}]
访问网关的路由规则,能看到刚刚加入的规则,访问http://localhost:9022/baidu时请求直接被转发到百度的首页了。 大家知道,servlet由servlet container进行生命周期管理。判断新老数据是否有变化需要进行更新操作。GET、官方结构图: 
Webflux虽然可以兼容多个底层的通信框架,但是一般情况下,底层使用的还是Netty,毕竟,Netty是目前业界认可的最高性能的通信框架。 spring: cloud: gateway: routes: - id: nameRoot uri: https://nameservice predicates: - Path=/name/** filters: - StripPrefix=2
请求/name/blue/red会转发到/red。熔断等操作的,增加SentinelGateWayFilter配置 @Bean//拦截请求 @Order(Ordered.HIGHEST_PRECEDENCE) public GlobalFilter sentinelGatewayFilter() { return new SentinelGatewayFilter(); }
sentinel 不仅支持通过硬代码方式进行资源的申明,还能通过注解方式进行声明,为了让注解生效,还需要配置切面类SentinelResourceAspect @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); }
sentinel拦截包括了视图、具体实现逻辑在RequestRateLimiterGatewayFilterFactory类中,lua脚本在如下图所示的文件夹中: 
首先在工程的pom文件中引入gateway的起步依赖和redis的reactive依赖,代码如下: 配置如下: server: port: 8081spring: cloud: gateway: routes: - id: limit_route uri: http://httpbin.org:80/get predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver] filters: - name: RequestRateLimiter args: key-resolver: '#{@userKeyResolver}' redis-rate-limiter.replenishRate: 1 redis-rate-limiter.burstCapacity: 3 application: name: cloud-gateway redis: host: localhost port: 6379 database: 0
在上面的配置文件,指定程序的端口为8081,配置了 redis的信息,并配置了RequestRateLimiter的限流过滤器,该过滤器需要配置三个参数: - burstCapacity,令牌桶总容量。PUT等请求方式
|