zuul dynamic routing
Gateway service is the only access to traffic. Don't stop the service. So dynamic routing is particularly necessary.
Database dynamic routing is based on event refresh mechanism to modify zuul's routing properties.
DiscoveryClientRouteLocator
You can see that DiscoveryClientRouteLocator is the core processing class of the default refresh.
//Reload the protected method. A sub method re method is required. protected LinkedHashMap<String, ZuulRoute> locateRoutes() //Refresh triggered method RefreshableRouteLocator interface public void refresh() { this.doRefresh(); }
Both of these methods inherit the SimpleRouteLocator class and operate again. In fact, the official method notes. If you need to read the load mapping dynamically. You need subclasses to override these two methods. Carry out specific implementation
First, the import of pom jar package needs to connect to mysql database.
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- jdbc --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
Route entity
package com.xian.cloud.entity; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * <Description> Routing entity class * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/30 15:00 */ @Data public class ZuulRouteEntity implements Serializable { private static final long serialVersionUID = 1L; /** * router Id */ private Integer id; /** * Routing path */ private String path; /** * Service name */ private String serviceId; /** * url agent */ private String url; /** * Forward without prefix */ private String stripPrefix; /** * Do you want to retry? */ private String retryable; /** * Is it enabled? */ private String enabled; /** * Sensitive request header */ private String sensitiveheadersList; /** * Creation time */ private Date createTime; /** * Update time */ private Date updateTime; /** * Delete ID (0-normal, 1-delete) */ private String delFlag; }
The new DiscoveryRouteLocator class parent class interface does not change
package com.xian.cloud.router; import com.google.common.base.Strings; import com.google.common.collect.Sets; import com.xian.cloud.entity.ZuulRoute; import lombok.extern.slf4j.Slf4j; 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.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.util.StringUtils; import java.util.*; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/30 18:57 */ @Slf4j public class DiscoveryRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator { private ZuulProperties properties; private JdbcTemplate jdbcTemplate; public DiscoveryRouteLocator(String servletPath, ZuulProperties properties, JdbcTemplate jdbcTemplate) { super(servletPath, properties); this.properties = properties; this.jdbcTemplate = jdbcTemplate; log.info("servletPath:{}",servletPath); } @Override public void refresh() { doRefresh(); } @Override protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() { LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<String, ZuulProperties.ZuulRoute>(); //Load route information from configuration file routesMap.putAll(super.locateRoutes()); //Custom load route information routesMap.putAll(getRouteList()); //Optimize the configuration LinkedHashMap<String, ZuulProperties.ZuulRoute> values = new LinkedHashMap<>(); for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : routesMap.entrySet()) { String path = entry.getKey(); // Prepend with slash if not already present. if (!path.startsWith("/")) { path = "/" + path; } if (StringUtils.hasText(this.properties.getPrefix())) { path = this.properties.getPrefix() + path; if (!path.startsWith("/")) { path = "/" + path; } } values.put(path, entry.getValue()); } return values; } /** * Read zuul routing rules from database * @return */ private LinkedHashMap<String, ZuulProperties.ZuulRoute> getRouteList() { LinkedHashMap<String, ZuulProperties.ZuulRoute> zuulRoutes = new LinkedHashMap<>(); List<ZuulRoute> sysZuulRoutes = jdbcTemplate.query("select * from sys_zuul_route where del_flag = 0", new BeanPropertyRowMapper<>(ZuulRoute.class)); for (ZuulRoute route: sysZuulRoutes) { // Skip over if (Strings.isNullOrEmpty(route.getPath()) && Strings.isNullOrEmpty(route.getUrl())) { continue; } ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute(); try { zuulRoute.setId(route.getServiceId()); zuulRoute.setPath(route.getPath()); zuulRoute.setServiceId(route.getServiceId()); zuulRoute.setRetryable(Objects.equals("0", route.getRetryable()) ? Boolean.FALSE : Boolean.TRUE); zuulRoute.setStripPrefix(Objects.equals("0", route.getStripPrefix()) ? Boolean.FALSE : Boolean.TRUE); zuulRoute.setUrl(route.getUrl()); List<String> sensitiveHeadersList = Arrays.asList(route.getSensitiveheadersList().split(",")); if (sensitiveHeadersList != null) { Set<String> sensitiveHeaderSet = Sets.newHashSet(); sensitiveHeadersList.forEach(sensitiveHeader -> sensitiveHeaderSet.add(sensitiveHeader)); zuulRoute.setSensitiveHeaders(sensitiveHeaderSet); zuulRoute.setCustomSensitiveHeaders(true); } } catch (Exception e) { log.error("Database load configuration exception", e); } log.info("Customized routing configuration,path: {},serviceId:{}", zuulRoute.getPath(), zuulRoute.getServiceId()); zuulRoutes.put(zuulRoute.getPath(), zuulRoute); } return zuulRoutes; } }
We also need a producer and consumer diagram of events to easily integrate into one class
package com.xian.cloud.event; import com.xian.cloud.router.DiscoveryRouteLocator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.discovery.event.HeartbeatEvent; import org.springframework.cloud.client.discovery.event.HeartbeatMonitor; import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent; import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Service; /** * <Description> Route refresh event publishing and event listener * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/30 15:27 */ @Service public class RefreshRouteService implements ApplicationListener<ApplicationEvent> { @Autowired private ZuulHandlerMapping zuulHandlerMapping; private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor(); @Autowired ApplicationEventPublisher publisher; @Autowired private DiscoveryRouteLocator dynamicRouteLocator; /** * Dynamic routing implementation calls refreshRoute() to publish refresh route events */ public void refreshRoute() { RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(dynamicRouteLocator); publisher.publishEvent(routesRefreshedEvent); } /** * Event listener. Monitoring and detection event refresh * @param event */ @Override public void onApplicationEvent(ApplicationEvent event) { if(event instanceof ContextRefreshedEvent || event instanceof RefreshScopeRefreshedEvent || event instanceof RoutesRefreshedEvent){ //Active manual refresh. Context refresh, configuration property refresh zuulHandlerMapping.setDirty(true); }else if(event instanceof HeartbeatEvent){ //Heartbeat triggered, local mapping relationship. Relate to remote service HeartbeatEvent heartbeatEvent = (HeartbeatEvent)event; if(heartbeatMonitor.update(heartbeatEvent.getValue())){ zuulHandlerMapping.setDirty(true); } } } }
Provide external trigger interface
package com.xian.cloud.controller; import com.xian.cloud.event.RefreshRouteService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * <Description> Refresh external interface manually * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/30 20:23 */ @RestController public class RefreshController { @Autowired private RefreshRouteService refreshRouteService; @GetMapping("/refresh") public String refresh() { refreshRouteService.refreshRoute(); return "refresh"; } }
Database table structure
CREATE TABLE `sys_zuul_route` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'router Id', `path` varchar(255) NOT NULL COMMENT 'Routing path', `service_id` varchar(255) NOT NULL COMMENT 'Service name', `url` varchar(255) DEFAULT NULL COMMENT 'url agent', `strip_prefix` char(1) DEFAULT '1' COMMENT 'Forward without prefix', `retryable` char(1) DEFAULT '1' COMMENT 'Do you want to retry?', `enabled` char(1) DEFAULT '1' COMMENT 'Is it enabled?', `sensitiveHeaders_list` varchar(255) DEFAULT NULL COMMENT 'Sensitive request header', `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time', `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update time', `del_flag` char(1) DEFAULT '0' COMMENT 'Remove identity (0-normal,1-Delete)', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='Dynamic routing configuration table'
Comment out the configuration file client consumer service routing configuration. Set up the data source. Read from database
Start service print log
2019-10-30 20:49:39.946 info 63449 -- [taskscheduler-1] c.xian.cloud.router.dynamicproutelocator: add database customized routing configuration, path: / client / * *, serviceid: cloud discovery client 2019-10-30 20:49:40.397 info 63449 -- [taskscheduler-1] c.xian.cloud.router.dynamicproutelocator: add database customized routing configuration, path: / client / * *, serviceid: cloud discovery client
postman requests the client interface to see if it can be forwarded successfully
Based on zuul dynamic gateway routing.
In the future, the gray scheme of the gateway and the debugging source service integrated by swagger2 will be updated. Coming soon!
From reference Official document of spring cloud
Reference books redefining spring cloud practice
Server nacos address http://47.99.209.72:8848/nacos
Previous address spring cloud alibaba address
Introduction to spring cloud alibaba
Spring Cloud Alibaba (nacos registry building)
Spring Cloud Alibaba uses nacos registry
Spring Cloud Alibaba nacos configuration center
Spring Cloud zuul gateway service I
Spring Cloud gateway service zuul II
How to like can pay attention to share this public number.
Copyright notice: This is the original article of the blogger, following CC 4.0 BY-SA copyright agreement. Please attach the original source link and this notice for reprint. Please upload the public number two-dimensional code.