千家信息网

如何实现zuul动态路由

发表于:2024-11-26 作者:千家信息网编辑
千家信息网最后更新 2024年11月26日,本篇内容介绍了"如何实现zuul动态路由"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!zuul动态路
千家信息网最后更新 2024年11月26日如何实现zuul动态路由

本篇内容介绍了"如何实现zuul动态路由"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

zuul动态路由

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

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

DiscoveryClientRouteLocator

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

//重新加载路由信息方法 protected方法。需要子方法重新方法。protected LinkedHashMap locateRoutes() //触发刷新的方法  RefreshableRouteLocator 接口 public void refresh() {                        this.doRefresh();        }

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

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

                    mysql            mysql-connector-java                                    org.springframework.boot            spring-boot-starter-jdbc        

路由实体 ZuulRouteEntity

package com.xian.cloud.entity;import lombok.Data;import java.io.Serializable;import java.util.Date;/** *  路由实体类 * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/30 15:00 */@Datapublic 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.*;/** *  * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/30 18:57 */@Slf4jpublic 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());        //优化一下配置        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;    }    /**     * 从数据库读取zuul路由规则     * @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) {           // 为空跳过           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("数据库加载配置异常", 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;/** *  路由刷新事件发布,与事件监听者 * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/30 15:27 */@Servicepublic class RefreshRouteService implements ApplicationListener {    @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;/** *  手动刷新对外接口 * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/30 20:23 */@RestControllerpublic 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='动态路由配置表'

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

启动服务打印日志

2019-10-30 20:49:39.946  INFO 63449 --- [TaskScheduler-1] c.xian.cloud.router.DynamicRouteLocator  : 添加数据库自定义的路由配置,path:/client/**,serviceId:cloud-discovery-client2019-10-30 20:49:40.397  INFO 63449 --- [TaskScheduler-1] c.xian.cloud.router.DynamicRouteLocator  : 添加数据库自定义的路由配置,path:/client/**,serviceId:cloud-discovery-client

postman 请求client 接口 看看是否能转发成功

基于zuul 动态网关路由完成。

"如何实现zuul动态路由"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0