Spring Cloud Gateway dynamic routing

1. Define the routing model

import lombok.Data;

import java.util.LinkedHashMap;
import java.util.List;

/**
 * 路由模型
 *
 * @author 向振华
 * @date 2021/02/04 10:26
 */
@Data
public class RouteDefinitionDTO {

    /**
     * 路由的Id
     */
    private String id;

    /**
     * 路由断言集合配置
     */
    private List<PredicateDefinitionDTO> predicates;

    /**
     * 路由过滤器集合配置
     */
    private List<FilterDefinitionDTO> filters;

    /**
     * 路由规则转发的目标uri
     */
    private String uri;

    /**
     * 路由执行的顺序
     */
    private Integer order;

    @Data
    public static class PredicateDefinitionDTO {

        /**
         * 断言名称
         */
        private String name;

        /**
         * 断言规则
         */
        private LinkedHashMap<String, String> args;
    }

    @Data
    public static class FilterDefinitionDTO {

        /**
         * 过滤器名称
         */
        private String name;

        /**
         * 路由规则
         */
        private LinkedHashMap<String, String> args;
    }
}

2. Define routing and modify controller

import com.bzcst.bop.gateway.entity.RouteDefinitionDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;

import java.util.List;

/**
 * @author 向振华
 * @date 2021/02/04 10:26
 */
@RestController
@RequestMapping("/route")
public class DynamicRouteController {

    @Autowired
    private DynamicRouteService dynamicRouteService;

    /**
     * 获取路由
     */
    @GetMapping
    public Flux<RouteDefinition> get() {
        return dynamicRouteService.get();
    }

    /**
     * 更新路由
     */
    @PutMapping
    public void update(@RequestBody List<RouteDefinitionDTO> dto) {
        dynamicRouteService.updateRoutes(dto);
    }

    /**
     * 删除路由
     */
    @DeleteMapping("/{id}")
    public void delete(@PathVariable String id) {
        dynamicRouteService.delete(id);
    }
}

3. Define service to handle dynamic routing and save it in local memory

import com.bzcst.bop.gateway.entity.RouteDefinitionDTO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

/**
 * 动态路由服务
 *
 * @author 向振华
 * @date 2021/02/04 10:26
 */
@Service
public class DynamicRouteService implements ApplicationEventPublisherAware {

    @Resource
    private RouteDefinitionLocator routeDefinitionLocator;

    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher publisher;

    /**
     * 获取路由
     *
     * @return
     */
    public Flux<RouteDefinition> get() {
        return routeDefinitionLocator.getRouteDefinitions();
    }

    /**
     * 更新路由
     *
     * @param dtos
     */
    public void updateRoutes(List<RouteDefinitionDTO> dtos) {
        if (CollectionUtils.isEmpty(dtos)) {
            return;
        }
        for (RouteDefinitionDTO dto : dtos) {
            RouteDefinition routeDefinition = convertRouteDefinition(dto);
            update(routeDefinition);
        }
    }

    /**
     * 更新路由
     *
     * @param definition
     */
    public void update(RouteDefinition definition) {
        delete(definition.getId());
        save(definition);
    }

    /**
     * 删除路由
     *
     * @param id
     */
    public void delete(String id) {
        try {
            routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        } catch (Exception ignored) {
        }
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    /**
     * 保存路由
     *
     * @param definition
     */
    public void save(RouteDefinition definition) {
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    /**
     * 参数转换成路由对象
     *
     * @param dto
     * @return
     */
    private RouteDefinition convertRouteDefinition(RouteDefinitionDTO dto) {
        RouteDefinition definition = new RouteDefinition();
        // 设置id
        definition.setId(dto.getId());
        // 设置order
        definition.setOrder(dto.getOrder());
        // 设置断言
        List<PredicateDefinition> pdList = new ArrayList<>();
        List<RouteDefinitionDTO.PredicateDefinitionDTO> predicateDefinitionDTOList = dto.getPredicates();
        for (RouteDefinitionDTO.PredicateDefinitionDTO gpDefinition : predicateDefinitionDTOList) {
            PredicateDefinition predicate = new PredicateDefinition();
            predicate.setArgs(gpDefinition.getArgs());
            predicate.setName(gpDefinition.getName());
            pdList.add(predicate);
        }
        definition.setPredicates(pdList);
        // 设置过滤器
        List<FilterDefinition> filters = new ArrayList<>();
        List<RouteDefinitionDTO.FilterDefinitionDTO> gatewayFilters = dto.getFilters();
        for (RouteDefinitionDTO.FilterDefinitionDTO filterDefinition : gatewayFilters) {
            FilterDefinition filter = new FilterDefinition();
            filter.setName(filterDefinition.getName());
            filter.setArgs(filterDefinition.getArgs());
            filters.add(filter);
        }
        definition.setFilters(filters);
        // 设置uri
        URI uri = null;
        String uriStr = dto.getUri();
        if (StringUtils.isNotBlank(uriStr)) {
            String http = "http";
            if (uriStr.startsWith(http)) {
                uri = UriComponentsBuilder.fromHttpUrl(dto.getUri()).build().toUri();
            } else {
                uri = URI.create(dto.getUri());
            }
        }
        definition.setUri(uri);
        return definition;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }
}

4. Use

 

There will be a problem here. Modifying an existing route in the local configuration file will add a new route. My solution is to delete the local routing configuration and use dynamic routing configuration.

The routing interface must be well controlled for security, it can be encrypted and signed, or modified by other services, and then use redis to update regularly, dynamically update, etc.

 

Attach parameter json

[
 {
    "id":"xxx服务",
    "predicates":[
      {
        "name":"Path",
        "args":{
          "_genkey_0":"/xxx/**"
        }
      }
    ],
    "filters":[
      {
        "name":"Login",
        "args":{

        }
      },
      {
        "name":"FileSize",
        "args":{
          "_genkey_0":"10485760",
          "_genkey_1":"/*/x/z;/*/h/a"
        }
      }
    ],
    "uri":"lb://xxx",
    "order":0
  }
]

 

Guess you like

Origin blog.csdn.net/Anenan/article/details/113627950