Spring Cloud Gateway(二)

403 阅读2分钟

添加依赖:

<!-- 网关需要从eureka注册中心上去发现微服务来进行节点的路由 -->
        <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-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

1,负载均衡到某一服务集群中的某一节点

更新application.yml配置,

spring
  cloud:
      gateway:
        routes:
          - id: lb_route
            predicates:
              - Path=/lb/**
            filters:
              - StripPrefix=1
            uri: lb://order-service #网关路由会转发到服务ID为order-service的服务,lb代表从注册中心获取服务.
        discovery:
          locator:
            enabled: true # 默认为false,设为true便开启通过服务中心的自动根据 serviceId 创建路由的功能。 路由的路径对应会使用大写ID。
            lower-case-service-id: true
order-service节点起两个服务节点,一个8082,一个8085
代码如下:--------------
@RefreshScope
@RestController
public class ConfigController {

    @Value("${name}")
    private String name;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/getName")
    public String getName(){
        return "从配置中心获取的name值为:"+ name + ", serverPort:" + serverPort;
    }
}

访问:http://localhost:8080/lb/getName

2,通过Redis做限流

spring
  cloud:
      gateway:
        routes:
          - id: ratelimiter_route
            predicates:
              - Path=/ratelimiter/**
            filters:
              - StripPrefix=1
              - name: RequestRateLimiter
                args:
                  deny-empty-key: true
                  keyResolver: '#{@ipAddressKeyResolver}' # 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
                  redis-rate-limiter.replenishRate: 1  # 令牌桶每秒填充平均速率
                  redis-rate-limiter.burstCapacity: 2  # 令牌桶总容量2
            uri: lb://order-service
        discovery:
          locator:
            enabled: true # 默认为false,设为true便开启通过服务中心的自动根据 serviceId 创建路由的功能。 路由的路径对应会使用大写ID。
            lower-case-service-id: true
/**
 * 使用IP地址的维度进行限流
 */
@Component
public class IpAddressKeyResolver implements KeyResolver{

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
}

访问:http://localhost:8080/ratelimiter/getName 访问速度加快的话,就会出现下面最后一个图示:

3,动态路由

获取网关中所有的路由:http://localhost:8080/actuator/gateway/routes/

/**
 * 动态路由(新增,删除路由)
 * Spring Gateway默认是通过内存(InMemoryRouteDefinitionRepository)实现的,但是gateway项目的重启会导致新增或删除的路由失效,
 * 所以这里采用redis实现动态路由
 */
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {

    private final static String GATEWAY_ROUTE_KEY="gateway_dynamic_route";

    @Autowired
    RedisTemplate<String,String> redisTemplate;

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        List<RouteDefinition> routeDefinitionList=new ArrayList<>();
        redisTemplate.opsForHash().values(GATEWAY_ROUTE_KEY).stream().forEach(route->{
            routeDefinitionList.add(JSON.parseObject(route.toString(),RouteDefinition.class));
        });
        return Flux.fromIterable(routeDefinitionList);
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.flatMap(routeDefinition -> {
            redisTemplate.opsForHash().put(GATEWAY_ROUTE_KEY,routeDefinition.getId(),JSON.toJSONString(routeDefinition));
            return Mono.empty();
        });
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id->{
            if(redisTemplate.opsForHash().hasKey(GATEWAY_ROUTE_KEY,id)){
                redisTemplate.opsForHash().delete(GATEWAY_ROUTE_KEY,id);
                return Mono.empty();
            }
            return Mono.defer(()->Mono.error(new Exception("routeDefinition not found:"+routeId)));
        });
    }
}

官网示例: 新增

最后调用:http://localhost:8080/baidu 成功跳转到了baidu.com !完美!

最后可以看下删除操作


这里新增和删除,已经自动更新了缓存。 是CachingRouteLocator类中实现了ApplicationListener

  /**
   * 会自动更新缓存
   */
  @Override
	public void onApplicationEvent(RefreshRoutesEvent event) {
		try {
			fetch().collect(Collectors.toList()).subscribe(list -> Flux.fromIterable(list)
					.materialize().collect(Collectors.toList()).subscribe(signals -> {
						applicationEventPublisher
								.publishEvent(new RefreshRoutesResultEvent(this));
						cache.put(CACHE_KEY, signals);
					}, throwable -> handleRefreshError(throwable)));
		}
		catch (Throwable e) {
			handleRefreshError(e);
		}
	}
  

---如果新增和删除没有更新缓存的话,那么需要调用/actuator/gateway/refresh进行刷新: 进行调用: