从上一篇我们知道该怎么配置路由对服务进行转发,但是发现需要在配置文件里添加配置文件的方式太过繁琐,其中路由对应的转发通过**locateRoutes()**进行实现的,是吧加载的路由配置,放到LinkedHashMap<string,ZuulRoute>中进行保存。
后来在网上搜索到一篇关于结合MySQL实现动态路由的一篇文章。
https://blog.youkuaiyun.com/hxpjava1/article/details/78304003
1、先创建一张路由关系表
CREATE TABLE `gateway_api_define` (
`id` VARCHAR(50) NOT NULL,
`path` VARCHAR(255) NOT NULL,
`service_id` VARCHAR(50) DEFAULT NULL,
`url` VARCHAR(255) DEFAULT NULL,
`retryable` TINYINT(1) DEFAULT NULL,
`enabled` TINYINT(1) NOT NULL,
`strip_prefix` INT(11) DEFAULT NULL,
`api_name` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
2、创建路由加载类
public class CustomRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
public final static Logger logger = LoggerFactory.getLogger(CustomRouteLocator.class);
/*数据库驱动*/
private JdbcTemplate jdbcTemplate;
/*zuul路由配置*/
private ZuulProperties properties;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate;
}
public CustomRouteLocator(String servletPath, ZuulProperties properties) {
super(servletPath, properties);
this.properties = properties;
logger.info("servletPath:{}",servletPath);
}
//父类已经提供了这个方法,这里写出来只是为了说明这一个方法很重要!!!
// @Override
// protected void doRefresh() {
// super.doRefresh();
// }
@Override
public void refresh() {
doRefresh();
}
/**
* @author:XingWL
* @description:路由规则加载算法
* @date: 2019/4/27 18:17
*/
@Override
protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<String, ZuulProperties.ZuulRoute>();
//从application.properties中加载路由信息
routesMap.putAll(super.locateRoutes());
//从db中加载路由信息
routesMap.putAll(locateRoutesFromDB());
//优化一下配置
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;
}
private Map<String, ZuulProperties.ZuulRoute> locateRoutesFromDB(){
Map<String, ZuulProperties.ZuulRoute> routes = new LinkedHashMap<>();
List<ZuulRoutePO> results = jdbcTemplate.query("select * from gateway_api_define where enabled = true ",new BeanPropertyRowMapper<>(ZuulRoutePO.class));
for (ZuulRoutePO result : results) {
if(StringUtils.isEmpty(result.getPath())||(StringUtils.isEmpty(result.getServiceId()) && StringUtils.isEmpty(result.getUrl()))){
continue;
}
ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
try {
BeanUtils.copyProperties(result,zuulRoute);
} catch (Exception e) {
logger.error("=============load zuul route info from db with error==============",e);
}
routes.put(zuulRoute.getPath(),zuulRoute);
}
return routes;
}
}
3、创建路由配置类
@Configuration
public class CustomZuulConfig {
@Autowired
ZuulProperties zuulProperties;
@Autowired
ServerProperties server;
@Autowired
JdbcTemplate jdbcTemplate;
@Bean
public CustomRouteLocator routeLocator() {
CustomRouteLocator routeLocator = new CustomRouteLocator(this.server.getServletPrefix(), this.zuulProperties);
routeLocator.setJdbcTemplate(jdbcTemplate);
return routeLocator;
}
}
4、创建路由实体类
public class ZuulRoutePO {
/**
* zuul.routes.<route>.path=/demo/**
* zuul.routes.<route>.serviceId=clientdemo
* zuul.routes.<route>.url=http://192.168.2.83:8181/
*
*/
/**
* The ID of the route (the same as its map key by default).
*/
private String id;
/**
* The path (pattern) for the route, e.g. /foo/**.
*/
private String path;
/**
* The service ID (if any) to map to this route. You can specify a physical URL or
* a service, but not both.
*/
private String serviceId;
/**
* A full physical URL to map to the route. An alternative is to use a service ID
* and service discovery to find the physical address.
*/
private String url;
/**
* Flag to determine whether the prefix for this route (the path, minus pattern
* patcher) should be stripped before forwarding.
*/
private boolean stripPrefix = true;
/**
* Flag to indicate that this route should be retryable (if supported). Generally
* retry requires a service ID and ribbon.
*/
private Boolean retryable;
private Boolean enabled;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getServiceId() {
return serviceId;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public boolean isStripPrefix() {
return stripPrefix;
}
public void setStripPrefix(boolean stripPrefix) {
this.stripPrefix = stripPrefix;
}
public Boolean getRetryable() {
return retryable;
}
public void setRetryable(Boolean retryable) {
this.retryable = retryable;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}
5、创建路由控制类,及实现方法
@RestController
public class RefreshController {
@Autowired
RefreshRouteService refreshRouteService;
@Autowired
ZuulHandlerMapping zuulHandlerMapping;
/**
* @author:XingWL
* @description:刷新映射
* @date: 2019/4/27 18:30
*/
@GetMapping("/refreshRoute")
public String refresh() {
refreshRouteService.refreshRoute();
return "refresh success";
}
/**
* @author:XingWL
* @description:查看映射关系
* @date: 2019/4/27 18:30
*/
@RequestMapping("/watchRoute")
public Object watchNowRoute() {
//可以用debug模式看里面具体是什么
Map<String, Object> handlerMap = zuulHandlerMapping.getHandlerMap();
return handlerMap;
}
}
@Service
public class RefreshRouteService {
@Autowired
ApplicationEventPublisher publisher;
@Autowired
RouteLocator routeLocator;
public void refreshRoute() {
RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(routeLocator);
publisher.publishEvent(routesRefreshedEvent);
}
}