本篇內(nèi)容介紹了“如何實(shí)現(xiàn)zuul動(dòng)態(tài)路由”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的微山網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
網(wǎng)關(guān)服務(wù)是流量的唯一入口。不能隨便停服務(wù)。所以動(dòng)態(tài)路由就顯得尤為必要。
數(shù)據(jù)庫(kù)動(dòng)態(tài)路由基于事件刷新機(jī)制熱修改zuul的路由屬性。
可以看到DiscoveryClientRouteLocator 是默認(rèn)的刷新的核心處理類。
//重新加載路由信息方法 protected方法。需要子方法重新方法。 protected LinkedHashMaplocateRoutes() //觸發(fā)刷新的方法 RefreshableRouteLocator 接口 public void refresh() { this.doRefresh(); }
而這倆個(gè)方法都是繼承與SimpleRouteLocator 類,并進(jìn)行了重新操作。其實(shí)官方的方法注釋說(shuō)明了。如果需要?jiǎng)討B(tài)讀取加載映射關(guān)系。則需要子類重寫這倆個(gè)方法。 進(jìn)行具體的實(shí)現(xiàn)
首先pom jar包導(dǎo)入 需要連接MySQL 數(shù)據(jù)庫(kù)
mysql mysql-connector-java org.springframework.boot spring-boot-starter-jdbc
路由實(shí)體 ZuulRouteEntity
package com.xian.cloud.entity; import lombok.Data; import java.io.Serializable; import java.util.Date; /** *路由實(shí)體類 * * @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; /** * 路由路徑 */ private String path; /** * 服務(wù)名稱 */ private String serviceId; /** * url代理 */ private String url; /** * 轉(zhuǎn)發(fā)去掉前綴 */ private String stripPrefix; /** * 是否重試 */ private String retryable; /** * 是否啟用 */ private String enabled; /** * 敏感請(qǐng)求頭 */ private String sensitiveheadersList; /** * 創(chuàng)建時(shí)間 */ private Date createTime; /** * 更新時(shí)間 */ private Date updateTime; /** * 刪除標(biāo)識(shí)(0-正常,1-刪除) */ private String delFlag; }
新建DiscoveryRouteLocator 類 父類 接口 都不變化
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.*; /** ** * @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 locateRoutes() { LinkedHashMap routesMap = new LinkedHashMap (); //從配置文件中加載路由信息 routesMap.putAll(super.locateRoutes()); //自定義加載路由信息 routesMap.putAll(getRouteList()); //優(yōu)化一下配置 LinkedHashMap values = new LinkedHashMap<>(); for (Map.Entry 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; } /** * 從數(shù)據(jù)庫(kù)讀取zuul路由規(guī)則 * @return */ private LinkedHashMap getRouteList() { LinkedHashMap zuulRoutes = new LinkedHashMap<>(); List sysZuulRoutes = jdbcTemplate.query("select * from sys_zuul_route where del_flag = 0", new BeanPropertyRowMapper<>(ZuulRoute.class)); for (ZuulRoute route: sysZuulRoutes) { // 為空跳過(guò) 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 sensitiveHeadersList = Arrays.asList(route.getSensitiveheadersList().split(",")); if (sensitiveHeadersList != null) { Set sensitiveHeaderSet = Sets.newHashSet(); sensitiveHeadersList.forEach(sensitiveHeader -> sensitiveHeaderSet.add(sensitiveHeader)); zuulRoute.setSensitiveHeaders(sensitiveHeaderSet); zuulRoute.setCustomSensitiveHeaders(true); } } catch (Exception e) { log.error("數(shù)據(jù)庫(kù)加載配置異常", e); } log.info("自定義的路由配置,path:{},serviceId:{}", zuulRoute.getPath(), zuulRoute.getServiceId()); zuulRoutes.put(zuulRoute.getPath(), zuulRoute); } return zuulRoutes; } }
我們還需要一個(gè)事件的生產(chǎn)者 和 消費(fèi)者 直接圖方便 集成到一個(gè)類中
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; /** *路由刷新事件發(fā)布,與事件監(jiān)聽(tīng)者 * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/30 15:27 */ @Service public class RefreshRouteService implements ApplicationListener { @Autowired private ZuulHandlerMapping zuulHandlerMapping; private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor(); @Autowired ApplicationEventPublisher publisher; @Autowired private DiscoveryRouteLocator dynamicRouteLocator; /** * 動(dòng)態(tài)路由實(shí)現(xiàn) 調(diào)用refreshRoute() 發(fā)布刷新路由事件 */ public void refreshRoute() { RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(dynamicRouteLocator); publisher.publishEvent(routesRefreshedEvent); } /** * 事件監(jiān)聽(tīng)者。監(jiān)控檢測(cè)事件刷新 * @param event */ @Override public void onApplicationEvent(ApplicationEvent event) { if(event instanceof ContextRefreshedEvent || event instanceof RefreshScopeRefreshedEvent || event instanceof RoutesRefreshedEvent){ //主動(dòng)手動(dòng)刷新。上下文刷新,配置屬性刷新 zuulHandlerMapping.setDirty(true); }else if(event instanceof HeartbeatEvent){ //心跳觸發(fā),將本地映射關(guān)系。關(guān)聯(lián)到遠(yuǎn)程服務(wù)上 HeartbeatEvent heartbeatEvent = (HeartbeatEvent)event; if(heartbeatMonitor.update(heartbeatEvent.getValue())){ zuulHandlerMapping.setDirty(true); } } } }
對(duì)外提供觸發(fā)接口
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; /** *手動(dòng)刷新對(duì)外接口 * * @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"; } }
數(shù)據(jù)庫(kù)表結(jié)構(gòu)
CREATE TABLE `sys_zuul_route` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'router Id', `path` varchar(255) NOT NULL COMMENT '路由路徑', `service_id` varchar(255) NOT NULL COMMENT '服務(wù)名稱', `url` varchar(255) DEFAULT NULL COMMENT 'url代理', `strip_prefix` char(1) DEFAULT '1' COMMENT '轉(zhuǎn)發(fā)去掉前綴', `retryable` char(1) DEFAULT '1' COMMENT '是否重試', `enabled` char(1) DEFAULT '1' COMMENT '是否啟用', `sensitiveHeaders_list` varchar(255) DEFAULT NULL COMMENT '敏感請(qǐng)求頭', `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間', `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間', `del_flag` char(1) DEFAULT '0' COMMENT '刪除標(biāo)識(shí)(0-正常,1-刪除)', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='動(dòng)態(tài)路由配置表'
將配置文件client 消費(fèi)者服務(wù) 路由配置注釋掉。設(shè)置數(shù)據(jù)源。從數(shù)據(jù)庫(kù)中讀取
啟動(dòng)服務(wù)打印日志
2019-10-30 20:49:39.946 INFO 63449 --- [TaskScheduler-1] c.xian.cloud.router.DynamicRouteLocator : 添加數(shù)據(jù)庫(kù)自定義的路由配置,path:/client/**,serviceId:cloud-discovery-client 2019-10-30 20:49:40.397 INFO 63449 --- [TaskScheduler-1] c.xian.cloud.router.DynamicRouteLocator : 添加數(shù)據(jù)庫(kù)自定義的路由配置,path:/client/**,serviceId:cloud-discovery-client
postman 請(qǐng)求client 接口 看看是否能轉(zhuǎn)發(fā)成功
基于zuul 動(dòng)態(tài)網(wǎng)關(guān)路由完成。
“如何實(shí)現(xiàn)zuul動(dòng)態(tài)路由”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!