The SpringCloudGateway manually writes routing rules to forward requests
This article is mainly to provide a kind of forward routing code implementation approach, said before the gateway are routed using a configuration file to the request, although this is very simple, but is not flexible, if the back-end corresponding many service instances, gateway to forward request, according to their own rules to write different load balancing strategy, for example, do some special weight, As well as dynamically changing forwarding addresses during runtime, configuration files are not flexible enough to define rules freely.
Gateway version involved
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> < version > 2.1.4. RELEASE < / version > < / dependency >Copy the code
The main implementation process is to implement the GatewayFilter interface, obtain the IP address and port to be specified, and then assemble URI and Route, and finally forward them
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;
/** * Forwards routes to access back-end services through load balancing ** /
@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();
// Get the original request path
String requestPath = exchange.getAttribute(FilterDict.SYSTEM_REQUEST_PATH);
The randomSelectInstance method gets a string with the structure "IP :port"
String instanceInfo = loadBalance.randomSelectInstance();
// If there is no service, an error is returned
if (StrUtil.isEmpty(instanceInfo)) {
return response.writeWith(Mono.just(GateWayFilterUtils.writeData(exchange, RecoError.GEN_SERVER_BUSY)));
}
// Used to test whether the load balancing algorithm is balanced in IP allocation
// redisUtil.zIncrementScore("test:gateway:load:ip",instanceInfo,1);
// Split the IP address from the port
String[] serviceAddress = instanceInfo.split(StrUtil.COLON);
String requestSchema = exchange.getRequest().getURI().getScheme();
// Concatenate URL data
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();
// Load the concatenated URL into the new 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(a) {
return FilterDict.SYSTEM_FILTER_ORDER + 4; }}Copy the code
After the route filter is written separately, it needs to be imported before it can be executed, beans are poured into the global configuration, and finally it can be executed by starting
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;
/** * Global filter configuration load **@author :wcy
*/
@Slf4j
@Configuration
public class GlobalFilterConfigure {
// Used to set the route
@Bean
public RouteFilter routeFilter(a){
return new RouteFilter();
}
/** * Load all custom filters */
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder builder, RedisRateLimiter redisRateLimiter) {
return builder.routes()
.route(r -> r.path(FilterDict.GATEWAY_BASE_INTERCEPT_URL)
// Load the custom filter in
.filters(f -> f.filters(routeFilter())
// Request size
.setRequestSize(requestLimitSize)
// Request limiting, currently using request IP, can be extended to use other limiting combinations later
// .filter(rateLimitByIpGatewayFilter())
)
.uri("http://127.0.0.1:" + servicePort + "/actuator/health") .order(FilterDict.SYSTEM_FILTER_ORDER) .id(FilterDict.GATEWAY_ROUTE_NAME) ).build(); }}Copy the code