SpringCloudGateway手动编写路由规则对请求进行转发
这篇文章主要是提供一种转发路由的代码实现方式,之前说的gateway都是使用配置文件来对请求进行路由,这样虽然很简单,但是不够灵活,如果后端对应很多服务实例,网关想要根据自己的规则来转发请求,比如编写不同的负载均衡策略,做一些特别的权重,以及在运行过程中动态的变更转发地址,这些用配置文件来做都不够灵活,没法自由的定义规则。
涉及的gateway版本
org.springframework.cloudspring-cloud-starter-gateway2.1.4.RELEASE
主要实现过程还是实现GatewayFilter接口,获取到要指定的IP地址与端口,然后组装成URI与Route,最后转发出去
importcn.hutool.core.util.ObjectUtil;importcn.hutool.core.util.StrUtil;importcom.ecwid.consul.v1.health.model.HealthService;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.cloud.gateway.filter.GatewayFilter;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.route.Route;importorg.springframework.core.Ordered;importorg.springframework.http.server.reactive.ServerHttpResponse;importorg.springframework.web.server.ServerWebExchange;importorg.springframework.web.util.UriComponentsBuilder;importjava.net.URI;importjava.util.List;importjava.util.Optional;importreactor.core.publisher.Mono;importstatic org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;/**
* 转发路由,通过负载均衡对后端服务进行访问
*
*/@Slf4jpublicclassRouteFilterimplementsGatewayFilter,Ordered{ @AutowiredprivateRedisUtilredisUtil; @AutowiredprivateLoadBalanceHandlerloadBalance; @OverridepublicMonofilter(ServerWebExchangeexchange,GatewayFilterChainchain) {ServerHttpResponseresponse = exchange.getResponse();//获取原来的请求路径StringrequestPath = exchange.getAttribute(FilterDict.SYSTEM_REQUEST_PATH);//randomSelectInstance方法会获取到一个"ip:port"这样结构的字符串StringinstanceInfo = loadBalance.randomSelectInstance();//如果没有服务,则直接返回报错if(StrUtil.isEmpty(instanceInfo)) {returnresponse.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);StringrequestSchema = exchange.getRequest().getURI().getScheme();//拼接URL的数据assertObjectUtil.isNotNull(requestPath);URIuri =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装入新的exchangeServerWebExchangemutateExchange = exchange.mutate().request(builder -> builder.uri(uri).build()).build();Optional route =Optional.of(exchange.getAttribute(GATEWAY_ROUTE_ATTR));RoutenewRoute =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]);returnchain.filter(mutateExchange); } @Overridepublicint getOrder() {returnFilterDict.SYSTEM_FILTER_ORDER+4; }}
单独编写路由filter以后,还需要引入才能执行,在全局配置中倒入bean,最后启动就可以执行了
importorg.springframework.beans.factory.annotation.Value;importorg.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;importorg.springframework.cloud.gateway.route.RouteLocator;importorg.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importlombok.extern.slf4j.Slf4j;importjava.time.Duration;/** * 全局过滤器配置加载 * *@author:wcy */@Slf4j@ConfigurationpublicclassGlobalFilterConfigure{//用于设置路由的@BeanpublicRouteFilter routeFilter(){returnnew RouteFilter(); }/**
* 将所有自定义的filter加载进来
*/@BeanpublicRouteLocator customerRouteLocator(RouteLocatorBuilder builder, RedisRateLimiter redisRateLimiter) {returnbuilder.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(); }}
网友评论