四. Zuul 基于数据库实现动态路由

一. 基础解释

  1. 解释: Zuul 基于数据库实现动态路由的原因,假设当前新建了服务,或已有服务中添加了新的接口,怎么不停机的情况下动态配置该接口通过网关访问,通过数据库进行配置,手动向数据库中存储服务与接口的映射关系,Zuul定时扫描数据库获取最新的服务与接口映射关系,做到动态定时更新
  2. 实现步骤
  • 创建 Zuul 网关服务
  • 网关服务中配置连接数据库(添加连接数据库需要的依赖,配置文件等)
  • 数据库中创建指定服务与接口路径,路由规则的映射表
  • 自定义路由策略类,继承 SimpleRouteLocator并实现 RefreshableRouteLocator接口
  • 重写locateRoutes() 与 refresh()方法,locateRoutes()方法中通过数据库获取服务与接口映射关系的数据,存入 Map<String, ZuulProperties.ZuulRoute> 中并返回 继承 SimpleRouteLocator并实现 RefreshableRouteLocator接口 动态路由类注入到容器中
  • 创建路由配置类,将自定义路由策略类注入到容器中,设置Zuul使用自定义路由策略
  • 创建定时任务,定时发布 RoutesRefreshedEvent 事件
  • 每发布一次事件,被对应的监听器监听到执行监听方法,会自动调用重写的 locateRoutes() 方法,获取新添加到数据库中添加的服务接口映射关系

二. 简单实现

创建 Zuul 网关服务

  1. 数据库中创建存于服务与接口映射关系的表,将来对某个服务添加新接口或改变接口路由规则时,直接向该表中添加服务名称,对应接口的路径,对应的路由规则即可例如:
    在这里插入图片描述

  2. 除了SpringCloud,Zuul ,Eureka,等依赖以外,引入连接数据库依赖,例如

		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
  1. 配置当前 Zuul 网关服务连接数据库,对数据库中存有服务与接口映射关系的表生产dto,mapper.xml等
  2. 自定义路由策略类,继承 继承 SimpleRouteLocator并实现 RefreshableRouteLocator接口,重写locateRoutes() 与 refresh()方法,locateRoutes()方法中通过数据库获取服务与接口映射关系的数据,存入 Map<String, ZuulProperties.ZuulRoute> 中并返回
import com.wuhenjian.microservicescaffolding.zuul.util.StringUtil;
import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 动态路由实现
 */
public class DynamicRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {

	/**
	 * 是否启用动态路由。
	 * 在配置文件中:gateway.routes.dynamic.enabled,默认为false
	 */
	private boolean enabled;


	private ZuulProperties properties;

	public DynamicRouteLocator(boolean enabled, String servletPath, ZuulProperties properties) {
		super(servletPath, properties);
		this.enabled = enabled;
	}

	/**
	 * 重载路由规则,当插入RoutesRefreshedEvent事件时自动执行该方法
	 */
	@Override
	protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {

		Map<String, ZuulProperties.ZuulRoute> routeMap = new LinkedHashMap<>();
		//1.读取 yml 文件中配置的路由关系
		routeMap.putAll(super.locateRoutes());

		//2.获取通过数据库配置服务与指定接口的映射关系
		// 先模拟几个配置
		String path = "/log/**";
		String serviceId = "service-log";

		// 任意一个为空,则不进行动态路由
		if (StringUtil.isBlank(path) || StringUtil.isBlank(serviceId)) {
			return super.locateRoutes();
		}
		//3.将通过数据库获取的映射关系封装为ZuulRoute对象
		ZuulProperties.ZuulRoute zuulRoute = createZuulRoute(path, serviceId);
		//4.将通过数据库获取到的映射关系存入 routeMap 中
		routeMap.put(path, zuulRoute);

		//5.对路径进行统一处理
		LinkedHashMap<String,ZuulProperties.ZuulRoute> values = new LinkedHashMap<>();
		for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : routeMap.entrySet()) {
			String p = entry.getKey();
			if (!p.startsWith("/")) {
				p = "/"+p;
			}

			if (StringUtils.hasText(this.properties.getPrefix())) {
				p = this.properties.getPrefix()+p;
				if (!p.startsWith("/")) {
					p = "/"+p;
				}
			}
			values.put(path, entry.getValue());
		}

		//6.将处理后的map返回
		return values;
	}

	/**
	 * 刷新路由
	 */
	@Override
	public void refresh() {
		super.doRefresh();
	}

	public boolean getEnabled() {
		return enabled;
	}

	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

	/**
	 * 生成ZuulRoute对象
	 * @param path 映射路径
	 * @param serviceId 服务Id
	 */
	private ZuulProperties.ZuulRoute createZuulRoute(String path, String serviceId) {
		ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
		zuulRoute.setId(path);
		zuulRoute.setPath(path);
		zuulRoute.setServiceId(serviceId);
		return zuulRoute;
	}
}
  1. 创建路由配置类,将上面的自定义路由策略类注入到容器中
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 动态路由配置类,将 DynamicRouteLocator 自定义路由策略类
 */
@Configuration
public class DynamicRouteConfig {

    //读取 yml 文件中中配置的路由规则
    @Value("${gateway.routes.dynamic.enabled}")
    private boolean enabledDynamicRoute;

    /**
     * 使用自定义的路由策略代替默认路由策略
     */
    @Bean
    public SimpleRouteLocator routeLocator(ZuulProperties zuulProperties) {
        //DynamicRouteLocator 为自定义通过数据库获取的路由规则类
        return new DynamicRouteLocator(enabledDynamicRoute, zuulProperties.getPrefix(), zuulProperties);
    }
}
  1. 创建定时任务,定时发布 RoutesRefreshedEvent 事件,事件发布后会自动执行DynamicRouteLocator自定义类中的 locateRoutes() 方法
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@Configuration
@EnableScheduling
public class RefreshRouteTask {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private RouteLocator routeLocator;

    @Scheduled(fixedRate = 5000)
    private void refreshRoute(){
        RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(routeLocator);
        applicationEventPublisher.publishEvent(routesRefreshedEvent);
    }
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值