zuul动态路由

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

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

DiscoveryClientRouteLocator

可以看到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='动态路由配置表'

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

启动服务打印日志

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 接口 看看是否能转发成功

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

后续还会更新网关的灰度方案、swagger2 整合的调试源服务。敬请期待!

摘自参考 spring cloud 官方文档

参考书籍 重新定义spring cloud实战

示例代码地址

服务器nacos 地址 http://47.99.209.72:8848/nacos

往期地址 spring cloud alibaba 地址

spring cloud alibaba 简介

Spring Cloud Alibaba (nacos 注册中心搭建)

Spring Cloud Alibaba 使用nacos 注册中心

Spring Cloud Alibaba nacos 配置中心使用

spring cloud 网关服务

Spring Cloud zuul网关服务 一

Spring Cloud 网关服务 zuul 二

如何喜欢可以关注分享本公众号。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。转载请附带公众号二维码

Spring Cloud 网关服务 zuul 三 动态路由的更多相关文章

  1. Spring Cloud 网关服务 zuul 二

    有一点上篇文章忘了 讲述,nacos的加载优先级别最高.服务启动优先拉去配置信息.所以上一篇服务搭建我没有讲述在nacos 中心创建的配置文件 可以看到服务端口和注册中心都在配置文件中配置化 属性信息 ...

  2. spring cloud 2.x版本 Gateway动态路由教程

    摘要 本文采用的Spring cloud为2.1.8RELEASE,version=Greenwich.SR3 本文基于前面的几篇Spring cloud Gateway文章的实现. 参考 Gatew ...

  3. spring cloud 网关服务

    微服务 网关服务 网关服务是微服务体系里面重要的一环. 微服务体系内,各个服务之间都会有通用的功能比如说:鉴权.安全.监控.日志.服务调度转发.这些都是可以单独抽象出来做一个服务来处理.所以微服务网关 ...

  4. spring cloud网关通过Zuul RateLimit 限流配置

    目录 引入依赖 配置信息 RateLimit源码简单分析 RateLimit详细的配置信息解读 在平常项目中为了防止一些没有token访问的API被大量无限的调用,需要对一些服务进行API限流.就好比 ...

  5. Spring Cloud Gateway实战之三:动态路由

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  6. spring cloud微服务实践三

    上篇文章里我们实现了spring cloud中的服务提供者和使用者.接下来我们就来看看spring cloud中微服务的其他组件. 注:这一个系列的开发环境版本为 java1.8, spring bo ...

  7. Spring Cloud微服务中网关服务是如何实现的?(Zuul篇)

    导读 我们知道在基于Spring Cloud的微服务体系中,各个微服务除了在内部提供服务外,有些服务接口还需要直接提供给客户端,如Andirod.IOS.H5等等. 而一个很尴尬的境地是,如果直接将提 ...

  8. spring cloud 学习(6) - zuul 微服务网关

    微服务架构体系中,通常一个业务系统会有很多的微服务,比如:OrderService.ProductService.UserService...,为了让调用更简单,一般会在这些服务前端再封装一层,类似下 ...

  9. Spring Cloud 微服务二:API网关spring cloud zuul

    前言:本章将继续上一章Spring Cloud微服务,本章主要内容是API 网关,相关代码将延续上一章,如需了解请参考:Spring Cloud 微服务一:Consul注册中心 Spring clou ...

随机推荐

  1. BCD 码、Gray 码、ASCII 码都是什么呢?

    BCD 码:即(Binary Coded Decimal)码,也称为 8421 码,是十进制代码中最常见的一种.每一位的 1 代表的十进制数称为这一位的权.BCD 码中每一位的权都是固定不变的,它属于 ...

  2. (七十四)c#Winform自定义控件-金字塔图表

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  3. Mach-O在内存中符号表地址、字符串表地址的计算

    KSCrash 是一个用于 iOS 平台的崩溃捕捉框架,最近读了其部分源码,在 KSDynamicLinker 文件中有一个函数,代码如下: /** Get the segment base addr ...

  4. 快速整理代码(c#)

    今天写代码发现有些代码行参差不齐,空行又多,整理看起来丑的不行,于是上网搜了下代码整理的快捷方式以作记录 这是整理之前,乱糟糟的(故意打乱为了节目效果) 第一步:Ctrl+a  (全选代码) 第二部: ...

  5. electron教程(二): http服务器, ws服务器, 进程管理

    我的electron教程系列 electron教程(一): electron的安装和项目的创建 electron教程(二): http服务器, ws服务器, 进程管理 electron教程(三): 使 ...

  6. Fork/Join 框架框架使用

    1.介绍 Fork/Join 框架是 Java7 提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架.在多核计算机中正确使用可以很好的 ...

  7. 『TensorFlow2.0正式版』TF2.0+Keras速成教程·零:开篇简介与环境准备

    此篇教程参考自TensorFlow 2.0 + Keras Crash Course,在原文的基础上进行了适当的总结与改编,以适应于国内开发者的理解与使用,水平有限,如果写的不对的地方欢迎大家评论指出 ...

  8. jq方法写选项卡的基本原理以及三种方法

    使用jq写选项卡,告别了繁琐的循环以及命名规范 基本原理: 1.当某一个btn被选中时,将这个btn的背景颜色设为橘色,其余兄弟btn背景颜色设为空(none) 2.如果子div与btn的索引相同,就 ...

  9. python+selenium遇到元素定位不到的问题,顺便记录一下自己这次的错误(报错selenium.common.exceptions.NoSuchElementException)

    今天在写selenium一个发送邮件脚本时,遇到一些没有找到页面元素的错误.经过自己反复调试,找原因百度,终于解决了.简单总结一下吧,原因有以下几点: 一:Frame控件嵌套,.Frame/Ifram ...

  10. 用CSS绘制实体三角形并说明原理

    ;;margin:0 auto;border:6px solid transparent;border-top: 6px solid red;} 1.采用的是均分原理 盒子都是一个矩形或正方形,从形状 ...