Try Everything Different In My Life.

「🔨实践」基于数据库动态配置Spring Cloud Gateway信息

2020.07.30

在使用Spring Cloud Gateway来实现服务网关的时候,官方提供了路由的配置方式,但是不怎么适合我们开发的场景

原生方式

Spring Cloud Gateway给我们提供了三种配置路由信息的方式

  • yaml文件中
  • Java代码
  • 基于内存(Actuator提供的Endpoint)

前两种配置方式都是写死的,不适合,第三种是基于内存的,程序崩溃的时候信息都会丢失掉。

基于数据库的动态路由配置方案

架构图

整个架构中包括两个系统

  • cloud-gateway
  • cloud-gateway-manage

cloud-gateway负责路由转发,就是Spring Cloud Gateway的工作,cloud-gateway-manage是和用户对接的,负责路由信息的数据维护。

使用消息中间件来通知路由变更的信息。

业务逻辑

  • Init

当cloud-gateway启动的时候,去Redis中查找路由信息,然后加载路由信息,当Redis中没有数据的时候,发送一条给cloud-gateway-manage来将数据库的信息查出来推到Redis中,然后cloud-gateway来重新加载路由信息

  • Add

当cloud-gateway-manage新增路由信息的时候,将这个信息推送给cloud-gateway,然后cloud-gateway将路由信息加载

  • Update

当cloud-gateway-manage更新路由信息的时候,将消息推送给cloud-gateway,然后将该路由信息删除,然后将新的路由信息加载

  • Delete

当cloud-gateway-manage删除路由信息的时候,将消息推送给cloud-gateway,然后将该路由信息删除

代码实现

cloud-gateway

  • Init
    /** Routes */
	private final Map<String,RouteDefinition> routeDefinitions = synchronizedMap(new LinkedHashMap<>());

	@Resource
	private RedisTemplate<String,Object> redisTemplate;


	/**
	 * 获得路由定义
	 * @return 路由定义
	 */
	@Override
	public Flux<RouteDefinition> getRouteDefinitions() {
		log.info("从redis获得所有路由信息");
		List<RedisRouteDefinition> routes = ((List<String>) (List) redisTemplate.opsForHash().values(RedisKeysConstant.KEY_GATEWAY_ROUTES)).stream()
				.map(item -> JsonUtil.json2Obj(item, RedisRouteDefinition.class))
				.collect(Collectors.toList());

		routes.forEach(route -> {
			log.info("新增路由信息");
			routeDefinitions.put(route.getRouteId(),CustomRouteDefinitionConvert.convert(route));
		});

		log.info("Redis中路由信息个数为 : {} ; 详情为 : {} ",routeDefinitions.size(),routeDefinitions);
		return Flux.fromIterable(routeDefinitions.values());
	}

  • Add
	/***
	 * 保存Route
	 * @param route 路由定义
	 */
	public void save(RouteDefinition route){
		if (StringUtils.isEmpty(route.getId())) {
			throw new IllegalArgumentException("id may not be empty");
		}
		routeDefinitions.put(route.getId(),route);
	}
  • Delete
public void delete(String id){

    if (routeDefinitions.containsKey(id)) {
        routeDefinitions.remove(id);
    }else {
        throw new NotFoundException("RouteDefinition not found: " + id);
    }
}

cloud-gateway-manage

Tips:

Q1:为什么要写cloud-gateway-manage?

A: 因为Spring Cloud Gateway是基于webFlux的,连接数据库是阻塞的,所以使用Redis来作为过渡