# 携程 Apollo 整合 Zuul 网关实现动态网关刷新

# ZuulPropertiesRefresher

import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import com.zhangmen.brain.tr.gateway.common.constant.GatewayConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * ZuulPropertiesRefresher
 *
 * @author quansheng1.zhang
 * @since 2020/12/14 16:04
 */
@Slf4j
@Component
public class ZuulPropertiesRefresher implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    private final RefreshScope refreshScope;

    public ZuulPropertiesRefresher(RefreshScope refreshScope) {
        this.refreshScope = refreshScope;
    }

    @Autowired
    private RouteLocator routeLocator;

    @ApolloConfigChangeListener(interestedKeyPrefixes = "zuul.")
    public void onChange(ConfigChangeEvent changeEvent) {
        refreshZuulProperties(changeEvent);
    }

    private void refreshZuulProperties(ConfigChangeEvent changeEvent) {
        log.info("Refreshing zuul properties!");

        /**
         * rebind configuration beans, e.g. ZuulProperties
         * @see org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#onApplicationEvent
         */
        this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));

        refreshScope.refresh(GatewayConstants.ZUUL_PROPERTIES_BEAN);

        /**
         * refresh routes
         * @see org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration.ZuulRefreshListener#onApplicationEvent
         */
        this.applicationContext.publishEvent(new RoutesRefreshedEvent(routeLocator));

        log.info("Zuul properties refreshed!");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

# 重新定义 ZuulProperties ,加上 @RefreshScope 注解,动态刷新

  /**
     * 重新定义 ZuulProperties ,加上 @RefreshScope 注解,动态刷新
     *
     * @return ZuulProperties
     */
    @Primary
    @Bean(GatewayConstants.ZUUL_PROPERTIES_BEAN)
    @ConfigurationProperties("zuul")
    @RefreshScope
    public ZuulProperties zuulProperties() {
        return new ZuulProperties();
    }
1
2
3
4
5
6
7
8
9
10
11
12

DiscoveryClientRouteLocator 中使用ZuulProperties如下(不能使用@Resource@Autowired注解方式引用)

// ...
	public DiscoveryClientRouteLocator(String servletPath, DiscoveryClient discovery,
			ZuulProperties properties, ServiceInstance localServiceInstance) {
		super(servletPath, properties);

		if (properties.isIgnoreLocalService() && localServiceInstance != null) {
			String localServiceId = localServiceInstance.getServiceId();
			if (!properties.getIgnoredServices().contains(localServiceId)) {
				properties.getIgnoredServices().add(localServiceId);
			}
		}
		this.serviceRouteMapper = new SimpleServiceRouteMapper();
		this.discovery = discovery;
		this.properties = properties;
	}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 网关配置

# app-name 是项目名
zuul.routes.app-name.path = /api/app-name/**
zuul.routes.app-name.serviceId = app-name
1
2
3

参考文档

Last Updated: 3 years ago