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