SpringCloudGateway手动编写路由规则对请求进行转发
这篇文章主要是提供一种转发路由的代码实现方式,之前说的gateway都是使用配置文件来对请求进行路由,这样虽然很简单,但是不够灵活,如果后端对应很多服务实例,网关想要根据自己的规则来转发请求,比如编写不同的负载均衡策略,做一些特别的权重,以及在运行过程中动态的变更转发地址,这些用配置文件来做都不够灵活,没法自由的定义规则。
涉及的gateway版本
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>2.1.4.RELEASE</version> </dependency>
主要实现过程还是实现GatewayFilter接口,获取到要指定的IP地址与端口,然后组装成URI与Route,最后转发出去
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.ecwid.consul.v1.health.model.HealthService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.route.Route; import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; import java.util.List; import java.util.Optional; import reactor.core.publisher.Mono; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; /** * 转发路由,通过负载均衡对后端服务进行访问 * */ @Slf4j public class RouteFilter implements GatewayFilter, Ordered { @Autowired private RedisUtil redisUtil; @Autowired private LoadBalanceHandler loadBalance; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpResponse response = exchange.getResponse(); //获取原来的请求路径 String requestPath = exchange.getAttribute(FilterDict.SYSTEM_REQUEST_PATH); //randomSelectInstance方法会获取到一个"ip:port"这样结构的字符串 String instanceInfo = loadBalance.randomSelectInstance(); //如果没有服务,则直接返回报错 if (StrUtil.isEmpty(instanceInfo)) { return response.writeWith(Mono.just(GateWayFilterUtils.writeData(exchange, RecoError.GEN_SERVER_BUSY))); } //用于测试负载均衡算法对IP分配是否均衡 // redisUtil.zIncrementScore("test:gateway:load:ip",instanceInfo,1); //分割地址中IP和端口 String[] serviceAddress = instanceInfo.split(StrUtil.COLON); String requestSchema = exchange.getRequest().getURI().getScheme(); //拼接URL的数据 assert ObjectUtil.isNotNull(requestPath); URI uri = UriComponentsBuilder. newInstance().scheme(requestSchema). host(serviceAddress[0].trim()).port(Integer.parseInt(serviceAddress[1].trim())) .path(requestPath).query(exchange.getRequest().getURI().getRawQuery()).build(true) .toUri(); //将拼接好的URL装入新的exchange ServerWebExchange mutateExchange = exchange.mutate().request(builder -> builder.uri(uri).build()).build(); Optional<Route> route = Optional.of(exchange.getAttribute(GATEWAY_ROUTE_ATTR)); Route newRoute = Route.async() .asyncPredicate(route.get().getPredicate()) .filters(route.get().getFilters()) .id(route.get().getId()) .order(route.get().getOrder()) .uri(uri).build(); mutateExchange.getAttributes().put(GATEWAY_ROUTE_ATTR, newRoute); mutateExchange.getAttributes().put(FilterDict.SYSTEM_APP_IP_ADDR, serviceAddress[0]); return chain.filter(mutateExchange); } @Override public int getOrder() { return FilterDict.SYSTEM_FILTER_ORDER + 4; } }
单独编写路由filter以后,还需要引入才能执行,在全局配置中倒入bean,最后启动就可以执行了
import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import lombok.extern.slf4j.Slf4j; import java.time.Duration; /** * 全局过滤器配置加载 * * @author :wcy */ @Slf4j @Configuration public class GlobalFilterConfigure { //用于设置路由的 @Bean public RouteFilter routeFilter(){ return new RouteFilter(); } /** * 将所有自定义的filter加载进来 */ @Bean public RouteLocator customerRouteLocator(RouteLocatorBuilder builder, RedisRateLimiter redisRateLimiter) { return builder.routes() .route(r -> r.path(FilterDict.GATEWAY_BASE_INTERCEPT_URL) //将自定义的filter加载进来 .filters(f -> f.filters(routeFilter()) //请求大小 .setRequestSize(requestLimitSize) //请求限流,目前使用请求IP,以后可以扩展使用其他限定组合 // .filter(rateLimitByIpGatewayFilter()) ) .uri("http://127.0.0.1:" + servicePort + "/actuator/health") .order(FilterDict.SYSTEM_FILTER_ORDER) .id(FilterDict.GATEWAY_ROUTE_NAME) ).build(); } }