2.数据有了,我们还得考虑怎么把它保存起来,不能网关一重启,所有数据就没了 。
3.还得再考虑一下数据的读取 。网关对性能的要求是很高的,每次对过关的数据进行治理,都需要去读取这些配置信息 。如果配置信息读取太消耗资源,无疑对网关是不利的 。所以,我们还得考虑数据如何缓存,以提高数据的读取性能 。
4.单个网关,可以处理的请求量是有上限的 。为了应对大的流量,我们可能会需要对网关做水平扩容 。当多个网关实例共存时,如何保障对网关的修改,能快速同步到每个网关实例呢?数据变更通知也得考虑 。
5.最多,我们还得考虑一下方案的扩展,数据存储能不能改个地方,通知能不能换种方式?
综合考虑了这些方面之后,我们的网关的架构如下:

文章插图
gateway-arch
如图,以上就是我们网关的整体设计 。方案设计要点如下:
- 网关对外提供治理数据管理接口, 微服务治理平台可通过这些接口, 将治理配置推送到网关
- 网关通过治理数据统一存储接口, 将治理配置数据保持至治理数据持久存储(这里我们默认为Redis)
- Redis通过发布订阅能力, 将数据的变更通知到各网关实例
- 各网关实例收到通知后, 将数据从持久存储同步至内部高速缓存
- 内部缓存在网关启动时, 会自动从持久存储加载对应配置进入缓存. 同时它也支持清空, 以及按需加载
- 外部业务请求经过网关时, 对数据执行鉴权,处理转换, 以及灰度策略时,所需要治理配置,都从内部缓存中获取, 以提升性能
- 方案中, 外部持久存储(默认用的Redis, 可以换成MySQL, 文件, Appolo等), 以及数据变更通知(默认使用的是Redis的发布订阅, 可以换成Appolo通知, 消息队列, 定时扫描等), 都是可以扩展的
Spring Cloud Gateway作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,尽量避免重启, 需要实现Spring Cloud Gateway动态路由配置 。实现动态路由其实很简单, 重点在于 RouteDefinitionRepository 这个接口. 这个接口继承自两个接口, 其中 RouteDefinitionLocator 是用来加载路由的. 它有很多实现类, 其中的 PropertiesRouteDefinitionLocator 就用来实现从yml中加载路由. 另一个 RouteDefinitionWriter 用来实现路由的添加与删除. 通过查看spring cloud gateway的源码可以发现, 在 org.springframework.cloud.gateway.config.GatewayAutoConfiguration中这么一段:
@Bean@ConditionalOnMissingBean(RouteDefinitionRepository.class)public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() { return new InMemoryRouteDefinitionRepository();}可以看出, 网关中如果没有RouteDefinitionRepository的Bean, 就会采用InMemoryRouteDefinitionRepository做为实现 。这个 InMemoryRouteDefinitionRepository有一个问题, 就是数据没有持久化, 网关重启之后,原来通过接口设置的路由就会丢失了 。
这当然是不可接受的, 所以我们需要实现自已的 RouteDefinitionRepository, 来提供路由配置信息 。如使用redis做为存储, 来实现路由的存储 。实现请参考文章:https://dwz.cn/tsHfKwMe
除此以外, 每当路由更改之后, 还需要通知网关刷新路由 。这需要发送 RefreshRoutesEvent 来通知网关 。如下列示例:
【基于Redis实现Spring Cloud Gateway的动态管理】@Componentpublic class RouteDynamicService implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher;? @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; }? /** * 刷新路由表 */ public void refreshRoutes() { publisher.publishEvent(new RefreshRoutesEvent(this)); }}刷新可以通过消息通知机制来触发, 当然, 也可以对外接供rest接口, 手动触发 。### 数据存储

文章插图
如上述类图所示, IGovernDataRepository为治理数据统一存储接口 。RedisGovernDataRepository为实现的它的抽像类, 它需要依赖两个, 一个是StringRedisTemplate,用来实现redis数据的存储 。另一个为 RedisKeyGenerator, 用来为各治理对象生成对应的key 。RedisGovernDataRepository下面则为各个治理数据存储的实现类 。使用Redis做为持久存储时, 需要注意以下几点:
- 为对象生成key时, 建议为key添加一个命名空间(就是加一段有意义的前缀)
- 在redis中进行模糊搜索时, 提供给Redis的pattern, 不能是一个正则的通配, 它支持三种通配 *(多个), ?(单个)
推荐阅读
- 基于redis分布式锁实现“秒杀”
- 三层交换机如何配置?如何实现不同vlan间的通信?
- nginx实现动静分离实战
- Redis深度历险,最全命令总结
- 如何基于 MySQL 主从模式搭建上万并发的系统架构?
- 推荐一款nginx+redis+ehcache高并发与高可用缓存架构
- Redis 核心原理和架构
- 关于redis学会这8点就够了
- Redis实现统计网站访问人数的功能
- 利用Redis黑进目标系统
