软件编程
位置:首页>> 软件编程>> java编程>> Spring Cloud 网关服务 zuul 动态路由的实现方法

Spring Cloud 网关服务 zuul 动态路由的实现方法

作者:程序员你大爷  发布时间:2022-04-09 20:17:46 

标签:spring,boot,zuul,动态路由

zuul动态路由

网关服务是流量的唯一入口。不能随便停服务。所以动态路由就显得尤为必要。

数据库动态路由基于事件刷新机制热修改zuul的路由属性。

DiscoveryClientRouteLocator

Spring Cloud 网关服务 zuul  动态路由的实现方法

可以看到DiscoveryClientRouteLocator 是默认的刷新的核心处理类。


//重新加载路由信息方法 protected方法。需要子方法重新方法。
protected LinkedHashMap<String, ZuulRoute> locateRoutes()

//触发刷新的方法 RefreshableRouteLocator 接口
public void refresh() {
  this.doRefresh();
}

而这俩个方法都是继承与SimpleRouteLocator 类,并进行了重新操作。其实官方的方法注释说明了。如果需要动态读取加载映射关系。则需要子类重写这俩个方法。

进行具体的实现

首先pom jar包导入 需要连接mysql 数据库  


<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
 </dependency>

<!-- jdbc -->
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
 </dependency>

路由实体 ZuulRouteEntity


package com.xian.cloud.entity;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
* <Description> 路由实体类
*
* @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;
/**
 * 服务名称
 */
private String serviceId;
/**
 * url代理
 */
private String url;
/**
 * 转发去掉前缀
 */
private String stripPrefix;
/**
 * 是否重试
 */
private String retryable;
/**
 * 是否启用
 */
private String enabled;
/**
 * 敏感请求头
 */
private String sensitiveheadersList;
/**
 * 创建时间
 */
private Date createTime;
/**
 * 更新时间
 */
private Date updateTime;
/**
 * 删除标识(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.*;

/**
* <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>();
 //从配置文件中加载路由信息
 routesMap.putAll(super.locateRoutes());
 //自定义加载路由信息
 routesMap.putAll(getRouteList());
 //优化一下配置
 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;
}

/**
 * 从数据库读取zuul路由规则
 * @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) {

// 为空跳过
  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("数据库加载配置异常", e);
  }
  log.info("自定义的路由配置,path:{},serviceId:{}", zuulRoute.getPath(), zuulRoute.getServiceId());
  zuulRoutes.put(zuulRoute.getPath(), zuulRoute);

}
 return zuulRoutes;
}
}

我们还需要一个事件的生产者 和 消费者 直接图方便 集成到一个类中


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> 路由刷新事件发布,与事件监听者
*
* @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;

/**
 * 动态路由实现 调用refreshRoute() 发布刷新路由事件
 */
public void refreshRoute() {
 RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(dynamicRouteLocator);
 publisher.publishEvent(routesRefreshedEvent);
}

/**
 * 事件监听者。监控检测事件刷新
 * @param event
 */
@Override
public void onApplicationEvent(ApplicationEvent event) {
 if(event instanceof ContextRefreshedEvent || event instanceof RefreshScopeRefreshedEvent || event instanceof RoutesRefreshedEvent){
  //主动手动刷新。上下文刷新,配置属性刷新
  zuulHandlerMapping.setDirty(true);
 }else if(event instanceof HeartbeatEvent){
  //心跳触发,将本地映射关系。关联到远程服务上
  HeartbeatEvent heartbeatEvent = (HeartbeatEvent)event;
  if(heartbeatMonitor.update(heartbeatEvent.getValue())){
   zuulHandlerMapping.setDirty(true);
  }
 }
}
}

对外提供触发接口


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> 手动刷新对外接口
*
* @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";
}
}

数据库表结构


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 '服务名称',
`url` varchar(255) DEFAULT NULL COMMENT 'url代理',
`strip_prefix` char(1) DEFAULT '1' COMMENT '转发去掉前缀',
`retryable` char(1) DEFAULT '1' COMMENT '是否重试',
`enabled` char(1) DEFAULT '1' COMMENT '是否启用',
`sensitiveHeaders_list` varchar(255) DEFAULT NULL COMMENT '敏感请求头',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标识(0-正常,1-删除)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='动态路由配置表'

Spring Cloud 网关服务 zuul  动态路由的实现方法

将配置文件client 消费者服务 路由配置注释掉。设置数据源。从数据库中读取

Spring Cloud 网关服务 zuul  动态路由的实现方法

启动服务打印日志

2019-10-30 20:49:39.946  INFO 63449 --- [TaskScheduler-1] c.xian.cloud.router.DynamicRouteLocator  : 添加数据库自定义的路由配置,path:/client/**,serviceId:cloud-discovery-client
2019-10-30 20:49:40.397  INFO 63449 --- [TaskScheduler-1] c.xian.cloud.router.DynamicRouteLocator  : 添加数据库自定义的路由配置,path:/client/**,serviceId:cloud-discovery-client
postman 请求client 接口 看看是否能转发成功

Spring Cloud 网关服务 zuul  动态路由的实现方法

基于zuul * 关路由完成。

总结

以上所述是小编给大家介绍的Spring Cloud 网关服务 zuul  动态路由的实现方法,网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

来源:https://www.cnblogs.com/cloudxlr/p/11769195.html

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com