springcloud集成apollo后动态刷新路由配置,官网给的demo如下
1import com.ctrip.framework.apollo.enums.PropertyChangeType; 2import com.ctrip.framework.apollo.model.ConfigChange; 3import com.ctrip.framework.apollo.model.ConfigChangeEvent; 4import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; 5import org.slf4j.Logger; 6import org.slf4j.LoggerFactory; 7import org.springframework.beans.BeansException; 8import org.springframework.beans.factory.annotation.Autowired; 9import org.springframework.cloud.context.environment.EnvironmentChangeEvent; 10import org.springframework.cloud.gateway.config.GatewayProperties; 11import org.springframework.cloud.gateway.event.RefreshRoutesEvent; 12import org.springframework.cloud.gateway.route.RouteDefinitionWriter; 13import org.springframework.context.ApplicationContext; 14import org.springframework.context.ApplicationContextAware; 15import org.springframework.context.ApplicationEventPublisher; 16import org.springframework.context.ApplicationEventPublisherAware; 17import org.springframework.context.annotation.Configuration; 18import org.springframework.stereotype.Component; 19 20import java.util.ArrayList; 21 22/** 23 * @author ksewen 24 * @date 2019/5/175:24 PM 25 */ 26@Configuration 27public class GatewayPropertiesRefresher implements ApplicationContextAware,ApplicationEventPublisherAware { 28 29 private static final Logger logger = LoggerFactory.getLogger(GatewayPropertiesRefresher.class); 30 31 private static final String ID_PATTERN = "spring\\.cloud\\.gateway\\.routes\\[\\d+\\]\\.id"; 32 33 private static final String DEFAULT_FILTER_PATTERN = "spring\\.cloud\\.gateway\\.default-filters\\[\\d+\\]\\.name"; 34 35 private ApplicationContext applicationContext; 36 37 private ApplicationEventPublisher publisher; 38 39 @Autowired 40 private GatewayProperties gatewayProperties; 41 42 @Autowired 43 private RouteDefinitionWriter routeDefinitionWriter; 44 45 @Override 46 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 47 this.applicationContext = applicationContext; 48 } 49 50 51 @Override 52 public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { 53 this.publisher = applicationEventPublisher; 54 } 55 56 @ApolloConfigChangeListener(interestedKeyPrefixes = "spring.cloud.gateway.",value="recommend-facade.yml") 57 public void onChange(ConfigChangeEvent changeEvent) { 58 refreshGatewayProperties(changeEvent); 59 } 60 61 /*** 62 * 刷新org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator中定义的routes 63 * 64 * @param changeEvent 65 * @return void 66 * @author ksewen 67 * @date 2019/5/21 2:13 PM 68 */ 69 private void refreshGatewayProperties(ConfigChangeEvent changeEvent) { 70 logger.info("Refreshing GatewayProperties!"); 71 preDestroyGatewayProperties(changeEvent); 72 this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys())); 73 refreshGatewayRouteDefinition(); 74 logger.info("GatewayProperties refreshed!"); 75 } 76 77 /*** 78 * GatewayProperties没有@PreDestroy和destroy方法 79 * org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#rebind(java.lang.String)中destroyBean时不会销毁当前对象 80 * 如果把spring.cloud.gateway.前缀的配置项全部删除(例如需要动态删除最后一个路由的场景),initializeBean时也无法创建新的bean,则return当前bean 81 * 若仍保留有spring.cloud.gateway.routes[n]或spring.cloud.gateway.default-filters[n]等配置,initializeBean时会注入新的属性替换已有的bean 82 * 这个方法提供了类似@PreDestroy的操作,根据配置文件的实际情况把org.springframework.cloud.gateway.config.GatewayProperties#routes 83 * 和org.springframework.cloud.gateway.config.GatewayProperties#defaultFilters两个集合清空 84 * 85 * @param 86 * @return void 87 * @author ksewen 88 * @date 2019/5/21 2:13 PM 89 */ 90 private synchronized void preDestroyGatewayProperties(ConfigChangeEvent changeEvent) { 91 logger.info("Pre Destroy GatewayProperties!"); 92 final boolean needClearRoutes = this.checkNeedClear(changeEvent, ID_PATTERN, this.gatewayProperties.getRoutes().size()); 93 if (needClearRoutes) { 94 this.gatewayProperties.setRoutes(new ArrayList<>()); 95 } 96 final boolean needClearDefaultFilters = this.checkNeedClear(changeEvent, DEFAULT_FILTER_PATTERN, this.gatewayProperties.getDefaultFilters().size()); 97 if (needClearDefaultFilters) { 98 this.gatewayProperties.setRoutes(new ArrayList<>()); 99 } 100 logger.info("Pre Destroy GatewayProperties finished!"); 101 } 102 103 private void refreshGatewayRouteDefinition() { 104 logger.info("Refreshing Gateway RouteDefinition!"); 105 this.publisher.publishEvent(new RefreshRoutesEvent(this)); 106 logger.info("Gateway RouteDefinition refreshed!"); 107 } 108 109 /*** 110 * 根据changeEvent和定义的pattern匹配key,如果所有对应PropertyChangeType为DELETED则需要清空GatewayProperties里相关集合 111 * 112 * @param changeEvent 113 * @param pattern 114 * @param existSize 115 * @return boolean 116 * @author ksewen 117 * @date 2019/5/23 2:18 PM 118 */ 119 private boolean checkNeedClear(ConfigChangeEvent changeEvent, String pattern, int existSize) { 120 121 return changeEvent.changedKeys().stream().filter(key -> key.matches(pattern)) 122 .filter(key -> { 123 ConfigChange change = changeEvent.getChange(key); 124 return PropertyChangeType.DELETED.equals(change.getChangeType()); 125 }).count() == existSize; 126 } 127 128}
这里有个坑,如果是没有用到默认的application.properties,这里我们就需要填写namespace了,如下,需要配置value="使用的namespace"
1 @ApolloConfigChangeListener(interestedKeyPrefixes = "spring.cloud.gateway.",value="xxxx.yml") 2 public void onChange(ConfigChangeEvent changeEvent) { 3 refreshGatewayProperties(changeEvent); 4 }