摘要

本文采用的Spring cloud为2.1.8RELEASE,version=Greenwich.SR3

本文基于前面的几篇Spring cloud Gateway文章的实现。

参考

前言

写了几篇关于Spring Cloud Gateway的文章后发现,Gateway涉及的知识范围太广了,真是深刻体会了“一入Spring cloud深似海”。

现实生产环境中,使用Spring Cloud Gateway都是作为所有流量的入口,为了保证系统的高可用,尽量避免系统的重启,所以需要Spring Cloud Gateway的动态路由来处理。之前的文章《Gateway路由网关教程》提供的路由配置,在系统启动时候,会将路由配置和规则加载到内存当中,无法做到不重启服务就可以动态的新增、修改、删除内存中的路由配置和规则。

简单的动态路由实现

Spring Cloud Gateway源码中提供了GatewayControllerEndpoint类来修改路由配置,但是官方文档好像并没有做详细的使用说明,只是简单介绍了几个简单的api接口。感兴趣的小伙伴可以先查看官方文档(第11章节 Actuator API)。

引致官方文档:

The /gateway actuator endpoint allows to monitor and interact with a Spring Cloud Gateway application. To be remotely accessible, the endpoint has to be enabled and exposed via HTTP or JMX in the application properties.

1.1 添加相关pom依赖

在原来spring-gateway的基础上增加spring-boot-starter-actuator依赖

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

1.2 在application.yml中增加配置

management:
endpoints:
web:
exposure:
include: "*"

配置说明:management.endpoints.web.exposure.include 暴露所有的gateway端点

1.3 启动服务

启动服务,访问http://localhost:8100/actuator/gateway/routes,这是我们可以看到所有的routes信息。



我们也可以访问单个路由信息:http://localhost:8100/actuator/gateway/routes/CompositeDiscoveryClient_EUREKA-CLIENT

显示如下:

1.4 增加、修改路由信息

Gateway默认使用的是GatewayControllerEndpoint这个类,GatewayControllerEndpoint又继承了AbstractGatewayControllerEndpoint类。

提供的方法:(只列具了几个相关方法,其他方法小伙们可以自行查看源码)

  1. /gateway/routes 查询所有路由信息
  2. /gateway/routes/{id} 根据路由id查询单个信息
  3. /gateway/routes/{id} @PostMapping 新增一个路由信息
  4. /gateway/routes/{id} @DeleteMapping 删除一个路由信息

1.4.1 新增路由

我们可根据/gateway/routes返回的路由信息,来模仿一个@PostMapping请求参数

{
"uri": "http://httpbin.org:80",
"predicates": [
{
"args": {
"pattern": "/ribbon/**"
},
"name": "Path"
}
],
"filters": []
}

这是我们可以通过postman来发送一个post请求,如图所示:

response返回1证明已经插入成功,我们可以通过http://localhost:8100/actuator/gateway/routes/查看路由信息,显示结果如下:

截图红框中的信息就是新增加的

1.4.2 删除路由信息

我们可以直接用postman模拟DeleteMapping请求,http://localhost:8100/actuator/gateway/routes/addroutes

显示如下:

这时候我们在访问http://localhost:8100/actuator/gateway/routes,可以看到新增加的路由已经被删除成功了。

1.5 小结

基于Spring Cloud Gateway默认方法实现的动态路由我就完成了,在前言中我已经提到了,这种方式是基于jvm内存实现,一旦服务重启,新增的路由配置信息就是完全消失了。所有这个时候我们可以考虑是否可以参考GatewayControllerEndpoint这类,自己实现一套动态路由方法,然后将路由信息持久化。

自定义动态路由

1.1定义相关类

1.1.1 定义实体类

可以自定义实体类,我这里偷了一个懒,直接用了Gateway的RouteDefinition类,感兴趣的小伙伴可以参考RouteDefinition类自己扩展,然后写一个Convert类将自定义的类转换成RouteDefinition就可以了。

1.1.2 自定义RedisRouteDefinitionRepository类

我这边采用redis做为路由配置的信息的持久层,所以写了一个RedisRouteDefinitionRepository。

package spring.cloud.demo.spring.gateway.component;

import com.google.common.collect.Lists;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import spring.cloud.demo.spring.gateway.redis.RedisUtils;
import spring.cloud.demo.spring.gateway.util.JsonUtils; import javax.annotation.Resource;
import java.util.List; /**
* @auther: maomao
* @DateT: 2019-11-03
*/
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository { //存储的的key
private final static String KEY = "gateway_dynamic_route"; @Resource
private RedisUtils redisUtils; /**
* 获取路由信息
* @return
*/
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<RouteDefinition> gatewayRouteEntityList = Lists.newArrayList();
redisUtils.hgets(KEY).stream().forEach(route -> {
RouteDefinition result = JsonUtils.parseJson(route.toString(), RouteDefinition.class);
gatewayRouteEntityList.add(result);
});
return Flux.fromIterable(gatewayRouteEntityList);
} /**
* 新增
* @param route
* @return
*/
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(routeDefinition -> {
redisUtils.hset(KEY, routeDefinition.getId(), JsonUtils.toString(routeDefinition));
return Mono.empty();
});
} /**
* 删除
* @param routeId
* @return
*/
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id -> {
if (redisUtils.hHashKey(KEY, id)) {
redisUtils.hdel(KEY, id);
return Mono.empty();
}
return Mono.defer(() -> Mono.error(new NotFoundException("route definition is not found, routeId:" + routeId)));
});
}
}

1.1.3 自定义Controller和Service

package spring.cloud.demo.spring.gateway.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import spring.cloud.demo.spring.gateway.service.GatewayDynamicRouteService; import javax.annotation.Resource; /**
* 自定义动态路由
* @auther: maomao
* @DateT: 2019-11-03
*/
@RestController
@RequestMapping("/gateway")
@Slf4j
public class GatewayDynamicRouteController { @Resource
private GatewayDynamicRouteService gatewayDynamicRouteService; @PostMapping("/add")
public String create(@RequestBody RouteDefinition entity) {
int result = gatewayDynamicRouteService.add(entity);
return String.valueOf(result);
} @PostMapping("/update")
public String update(@RequestBody RouteDefinition entity) {
int result = gatewayDynamicRouteService.update(entity);
return String.valueOf(result);
} @DeleteMapping("/delete/{id}")
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
return gatewayDynamicRouteService.delete(id);
} }
package spring.cloud.demo.spring.gateway.service;

import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import spring.cloud.demo.spring.gateway.component.RedisRouteDefinitionRepository; import javax.annotation.Resource; /**
* @auther: maomao
* @DateT: 2019-11-03
*/
@Service
public class GatewayDynamicRouteService implements ApplicationEventPublisherAware { @Resource
private RedisRouteDefinitionRepository redisRouteDefinitionRepository; private ApplicationEventPublisher applicationEventPublisher; /**
* 增加路由
* @param routeDefinition
* @return
*/
public int add(RouteDefinition routeDefinition) {
redisRouteDefinitionRepository.save(Mono.just(routeDefinition)).subscribe();
applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
return 1;
} /**
* 更新
* @param routeDefinition
* @return
*/
public int update(RouteDefinition routeDefinition) {
redisRouteDefinitionRepository.delete(Mono.just(routeDefinition.getId()));
redisRouteDefinitionRepository.save(Mono.just(routeDefinition)).subscribe();
applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
return 1;
} /**
* 删除
* @param id
* @return
*/
public Mono<ResponseEntity<Object>> delete(String id) {
return redisRouteDefinitionRepository.delete(Mono.just(id)).then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
.onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));
} @Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}

1.2 application.yml

application.yml配置要暴漏Gateway的所有端点,可以看考之前的配置信息。

1.3 启动服务

启动Spring cloud Gateway服务,先访问http://localhost:8100/actuator/gateway/routes,查看已有的路由配置信息。然后我们用postman请求add方法,http://localhost:8100/gateway/add,如果所示:

注意截图中红框的内容。证明已经新增成功。

这时我们在访问http://localhost:8100/actuator/gateway/routes查看结果。如果所示:

同理我们可以访问update和delete方法,我这里就不过多描述了。

总结

自定义动态路由核心原理其实就要重写网关模块,也就是之前提到的RedisRouteDefinitionRepository类。我这里偷懒没有重新定义对应的实体类,这里需要注意的是,传入参数一定要按照application.yml中配置的格式,然后转成json,如果格式不正确会报错。

代码地址

gitHub地址


《Srping Cloud 2.X小白教程》目录


  • 写作不易,转载请注明出处,喜欢的小伙伴可以关注公众号查看更多喜欢的文章。
  • 联系方式:4272231@163.com

spring cloud 2.x版本 Gateway动态路由教程的更多相关文章

  1. spring cloud 2.x版本 Gateway自定义过滤器教程

    前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前两篇文章eureka-server.eureka-client.eureka ...

  2. spring cloud 2.x版本 Gateway熔断、限流教程

    前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前两篇文章eureka-server.eureka-client.eureka ...

  3. spring cloud 2.x版本 Eureka Client服务提供者教程

    本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 1 创建eureka client 1.1 新建Srping boot工程:eureka-c ...

  4. spring cloud 2.x版本 Gateway路由网关教程

    前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前两篇文章eureka-server.eureka-client.eureka ...

  5. spring cloud 2.x版本 Config配置中心教程

    前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前面的文章eureka-server的实现. 参考 eureka-server ...

  6. spring cloud 2.x版本 Ribbon服务发现教程(内含集成Hystrix熔断机制)

    本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 前言 本文基于前两篇文章eureka-server和eureka-client的实现. 参考 ...

  7. spring cloud 2.x版本 Feign服务发现教程(内含集成Hystrix熔断机制)

    前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前两篇文章eureka-server和eureka-client的实现. 参考 ...

  8. spring cloud 2.x版本 Hystrix Dashboard断路器教程

    前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前两篇文章eureka-server.eureka-client.eureka ...

  9. Spring Cloud 网关服务 zuul 三 动态路由

    zuul动态路由 网关服务是流量的唯一入口.不能随便停服务.所以动态路由就显得尤为必要. 数据库动态路由基于事件刷新机制热修改zuul的路由属性. DiscoveryClientRouteLocato ...

随机推荐

  1. 【Spring Cloud】Spring Cloud Config 实现分布式配置中心

    Spring Cloud Config 实现分布式配置中心 一.分布式配置中心 分布式系统中,往往拥有大量的服务应用,而每个应用程序都需要有对应的配置文件来协助完成服务环境初始化.运行.因此生产了大量 ...

  2. Troubleshooting: High Version Count Issues

    --查询版本高的原因 select * from v$sql_shared_cursor where sql_id=''; Configuring Download the script in the ...

  3. HashMap、HashTable 和 ConcurrentHashMap 线程安全问题

    一.HashMap HashMap 是线程不安全的. JDK 1.7 HashMap 采用数组 + 链表的数据结构,多线程背景下,在数组扩容的时候,存在 Entry 链死循环和数据丢失问题. JDK ...

  4. VSCode 使用 ESLint + Prettier 来统一 JS 代码

    环境: VSCode 1.33.1 Node.js 8.9.1 一.ESLint 1.介绍 ESLint是最流行的JavaScript Linter. Linter 是检查代码风格/错误的小工具.其他 ...

  5. win7怎么使用远程桌面连接(win10类似,同样适用)

     win7使用远程桌面比mac要简单多了,只需在桌面点击“开始”,找到并打开“附件”,点击“远程桌面连接”即可   mac使用远程桌面连接:https://www.cnblogs.com/tu-071 ...

  6. win10禁止自动更新的终极方法(亲测有效)

    想必用过win10的朋友对其自动更新一定不会陌生,并且深恶痛绝,    有时正专注做一件事,突然就开始自动更新,被杀个措手不及,而且更新时间真的太久了,尤其最近更新频繁,真是伤脑筋,    期间也尝试 ...

  7. python强大的绘图模块matplotlib示例讲解

    Matplotlib 是 Python 的绘图库.作为程序员,经常需要进行绘图,在我自己的工作中,如果需要绘图,一般都是将数据导入到excel中,然后通过excel生成图表,这样操作起来还是比较繁琐的 ...

  8. C sharp #006# 委托与事件

    饮水思源:金老师的自学网站 索引 委托(Delegate) Click事件探究 委托(Delegate) “不学会委托(Delegate),等于没学.NET编程!” 例程1-委托类型变量 using ...

  9. mysql 数据误删恢复

    当binlog_format设置为ROW时,想查看binlog中的sql语句时,需要解密,添加这个 --base64-output=decode-rows -v

  10. Python 词云可视化

    最近看到不少公众号都有一些词云图,于是想学习一下使用Python生成可视化的词云,上B站搜索教程的时候,发现了一位UP讲的很不错,UP也给出了GitHub上的源码,是一个很不错的教程,这篇博客主要就是 ...