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. 【POJ - 3723 】Conscription(最小生成树)

    Conscription Descriptions 需要征募女兵N人,男兵M人. 每招募一个人需要花费10000美元. 如果已经招募的人中有一些关系亲密的人,那么可以少花一些钱. 给出若干男女之前的1 ...

  2. ASP.NET Core 3.0 WebApi中使用Swagger生成API文档简介

    参考地址,官网:https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/getting-started-with-swashbuckle?view ...

  3. .net core 使用Rotativa创建PDF文档

    一.下载Rotaiva 工具  = >  NuGet包管理器  = >  管理解决方案的NuGet程序包 在打开的页面中搜索 Rotativa.AspNetCore 如下图: 选中红框的记 ...

  4. .net core 3.0 Signalr - 02 使用强类型的Hub

    ## 强类型的优缺点 - 优点 强类型的Hub可以避免魔法函数名,相比弱类型更容易维护和发现问题,直接上代码 - 缺点 特么的得多些好几行代码 ## 代码 ### 接口定义 ``` C# /// // ...

  5. 【钢琴伴奏基本形态和伴奏织体】技能 get

    开头重复一句话:做编曲就是设计和声+伴奏织体. ---- --------- --------------- 分解和弦: 半分解和弦: 做法:在旋律的下方演奏一些和弦的音.就能让音乐更加的饱满,拒绝空 ...

  6. Maven私服Nexus的搭建

    # Maven私服Nexus的搭建 ## 私服存在的合理性 Maven中的依赖是从服务器仓库中下载的,Maven的仓库只有两大类: - 1) 本地仓库 - 2) 远程仓库,其中在远程仓库中又分成了3种 ...

  7. [WP8.1]给Pivot的Header加上颜色

    先上个效果图 以前想实现这个给Pivot加颜色时只找到8的,通过参考8的实现方式,8.1的实现如下,在Pivot的样式上做一些修改,如下图的红框处 另外如果要改Pivot的Header里文字的颜色又要 ...

  8. Hadoop 学习笔记之 Incompatible namespaceIDs问题

    Hadoop重新格式化后,仍然datanode启动失败,查看log: org.apache.hadoop.hdfs.server.datanode.DataNode: Java.io.IOExcept ...

  9. Nebula 架构剖析系列(零)图数据库的整体架构设计

    Nebula Graph 是一个高性能的分布式开源图数据库,本文为大家介绍 Nebula Graph 的整体架构. 一个完整的 Nebula 部署集群包含三个服务,即  Query Service,S ...

  10. idea中添加Run Dashboard

    可以在工程目录下找到.idea文件夹下的workspace.xml,在其中加入以下代码即可: <component name="RunDashboard"> <o ...