Spring Cloud Zuul 路由自动刷新原理

现象

发布新服务,然后在数据库配置了路由,使用服务路径访问404。然后重新发布新的服务,就可以继续访问得到

(1)配置了路由第一次访问

动态路由-应用重启前

(2)重新发布后访问

动态路由-应用重启后

分析

(1)查找RefreshableRouteLocator.refresh方法

@Component
public class NacosRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {

    public NacosRouteLocator(ServerProperties serverProperties, ZuulProperties properties) {
        super(serverProperties.getServlet().getContextPath(), properties);
    }

    @Override
    public void refresh() {
        doRefresh();
    }

    @Override
    protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {

        //默认从配置文件中加载路由信息
        //return super.locateRoutes());

        //定制路由, 可以使用db的配置管理进行路由
        Map<String, ZuulProperties.ZuulRoute> routesMap = DbUtils.loadRoutes();

        return routesMap;
    }
}

(2)ZuulHandlerMapping.setDirty方法

public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {

    public void setDirty(boolean dirty) {
        this.dirty = dirty;
        if (this.routeLocator instanceof RefreshableRouteLocator) {
            ((RefreshableRouteLocator) this.routeLocator).refresh();
        }
    }
}

(3)ZuulRefreshListener.reset方法

private static class ZuulRefreshListener
        implements ApplicationListener<ApplicationEvent> {

    @Autowired
    private ZuulHandlerMapping zuulHandlerMapping;

    private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent
                || event instanceof RefreshScopeRefreshedEvent
                || event instanceof RoutesRefreshedEvent
                || event instanceof InstanceRegisteredEvent) {
            reset();
        }
        else if (event instanceof ParentHeartbeatEvent) {
            ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
            resetIfNeeded(e.getValue());
        }
        else if (event instanceof HeartbeatEvent) {
            HeartbeatEvent e = (HeartbeatEvent) event;
            resetIfNeeded(e.getValue());
        }
    }

    private void resetIfNeeded(Object value) {
        if (this.heartbeatMonitor.update(value)) {
            reset();
        }
    }

    private void reset() {
        this.zuulHandlerMapping.setDirty(true);
    }
}

(4)发现ApplicationEvent

@Override
public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ContextRefreshedEvent
            || event instanceof RefreshScopeRefreshedEvent
            || event instanceof RoutesRefreshedEvent
            || event instanceof InstanceRegisteredEvent) {
        reset();
    }
    else if (event instanceof ParentHeartbeatEvent) {
        ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
        resetIfNeeded(e.getValue());
    }
    else if (event instanceof HeartbeatEvent) {
        HeartbeatEvent e = (HeartbeatEvent) event;
        resetIfNeeded(e.getValue());
    }
}

可以看到,其中的实例注册事件RoutesRefreshedEventInstanceRegisteredEvent均会触发刷新路由。

(5)Eureka触发路由更新

// org.springframework.cloud.netflix.eureka.CloudEurekaClient#onCacheRefreshed
@Override
protected void onCacheRefreshed() {
    super.onCacheRefreshed();

    if (this.cacheRefreshedCount != null) { // might be called during construction and
        // will be null
        long newCount = this.cacheRefreshedCount.incrementAndGet();
        log.trace("onCacheRefreshed called with count: " + newCount);
        this.publisher.publishEvent(new HeartbeatEvent(this, newCount));
    }
}
@Override
// org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration#start
public void start() {
    // only set the port if the nonSecurePort or securePort is 0 and this.port != 0
    if (this.port.get() != 0) {
        if (this.registration.getNonSecurePort() == 0) {
            this.registration.setNonSecurePort(this.port.get());
        }

        if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
            this.registration.setSecurePort(this.port.get());
        }
    }

    // only initialize if nonSecurePort is greater than 0 and it isn't already running
    // because of containerPortInitializer below
    if (!this.running.get() && this.registration.getNonSecurePort() > 0) {

        this.serviceRegistry.register(this.registration);

        this.context.publishEvent(new InstanceRegisteredEvent<>(this,
                this.registration.getInstanceConfig()));
        this.running.set(true);
    }
}

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/04/02/spring-cloud-zuul-routing-automatic-refresh-principle/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
Spring Cloud Zuul 路由自动刷新原理
现象 发布新服务,然后在数据库配置了路由,使用服务路径访问404。然后重新发布新的服务,就可以继续访问得到 (1)配置了路由第一次访问 (2)重新发布后访……
<<上一篇
下一篇>>
文章目录
关闭
目 录