spring cloud gateway-动态路由精讲篇
spring cloud gateway-动态路由精讲篇1.为什么需要动态路由2.gateway网关启动时,路由信息加载存储在哪里3.配置的路由信息怎么进行获取映射的1.RouteDefiniton类存储了路由信息4.Gateway提供的路由操作接口5.自定义类实现路由操作和redis存储1.首先我们先看Gateway内部给我们提供的路由操作的实现2.自定义类实现RouteDefinitionRe
spring cloud gateway-动态路由精讲篇
1.为什么需要动态路由
当我们将Gateway的路由配置在yml或者properties文件里时,每当我们部署新的实例,Gateway的配置文件就要更改,重启Gateway服务,这就会导致系统瞬间崩溃,无法在重启Gateway服务时正常使用。一般线上的路由配置我们都是动态配置,让Gateway自己自动获取新的路由配置信息,而不需要重启服务,这样在部署新的服务实例时,Gateway不需重启服务,就能正常访问新部署的服务,其他以前的服务正常运行。下面针对Gateway的路由原理及代码操作实现进行分析。
2.gateway网关启动时,路由信息加载存储在哪里
gateway网关启动时,路由信息默认会加载内存中
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
private final Map<String, RouteDefinition> routes = Collections.synchronizedMap(new LinkedHashMap());
public InMemoryRouteDefinitionRepository() {
}
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap((r) -> {
if (StringUtils.isEmpty(r.getId())) {
return Mono.error(new IllegalArgumentException("id may not be empty"));
} else {
this.routes.put(r.getId(), r);
return Mono.empty();
}
});
}
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap((id) -> {
if (this.routes.containsKey(id)) {
this.routes.remove(id);
return Mono.empty();
} else {
return Mono.defer(() -> {
return Mono.error(new NotFoundException("RouteDefinition not found: " + routeId));
});
}
});
}
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.routes.values());
}
}
3.配置的路由信息怎么进行获取映射的
路由信息被封装到 RouteDefinition 对象中,配置多个RouteDefinition组成gateway的路由系统
1.RouteDefiniton类存储了路由信息
我们配置的路由信息会被映射到这个实体类里
@Validated
public class RouteDefinition {
private String id;//唯一标识
@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList();//断言列表
@Valid
private List<FilterDefinition> filters = new ArrayList();//过滤器链
@NotNull
private URI uri; //跳转的地址
private Map<String, Object> metadata = new HashMap();
private int order = 0;//优先级 数值越小,优先级越高
public RouteDefinition() {
}
public RouteDefinition(String text) {
int eqIdx = text.indexOf(61);
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse RouteDefinition text '" + text + "', must be of the form name=value");
} else {
this.setId(text.substring(0, eqIdx));
String[] args = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ",");
this.setUri(URI.create(args[0]));
for(int i = 1; i < args.length; ++i) {
this.predicates.add(new PredicateDefinition(args[i]));
}
}
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public List<PredicateDefinition> getPredicates() {
return this.predicates;
}
public void setPredicates(List<PredicateDefinition> predicates) {
this.predicates = predicates;
}
public List<FilterDefinition> getFilters() {
return this.filters;
}
public void setFilters(List<FilterDefinition> filters) {
this.filters = filters;
}
public URI getUri() {
return this.uri;
}
public void setUri(URI uri) {
this.uri = uri;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
public Map<String, Object> getMetadata() {
return this.metadata;
}
public void setMetadata(Map<String, Object> metadata) {
this.metadata = metadata;
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
RouteDefinition that = (RouteDefinition)o;
return this.order == that.order && Objects.equals(this.id, that.id) && Objects.equals(this.predicates, that.predicates) && Objects.equals(this.filters, that.filters) && Objects.equals(this.uri, that.uri) && Objects.equals(this.metadata, that.metadata);
} else {
return false;
}
}
public int hashCode() {
return Objects.hash(new Object[]{this.id, this.predicates, this.filters, this.uri, this.metadata, this.order});
}
public String toString() {
return "RouteDefinition{id='" + this.id + '\'' + ", predicates=" + this.predicates + ", filters=" + this.filters + ", uri=" + this.uri + ", order=" + this.order + ", metadata=" + this.metadata + '}';
}
}
最终该实体类RouteDefinition会注入到Spring 容器里
4.Gateway提供的路由操作接口
gateway-core包下面给我们提供了许多路由接口操作
1.返回了当前网关的路由信息
@GetMapping({"/routes"})
public Flux<Map<String, Object>> routes() {
return this.routeLocator.getRoutes().map(this::serialize);
}
2.跟据路由Id,查看该路由信息
@GetMapping({"/routes/{id}"})
public Mono<ResponseEntity<Map<String, Object>>> route(@PathVariable String id) {
return this.routeLocator.getRoutes().filter((route) -> {
return route.getId().equals(id);
}).singleOrEmpty().map(this::serialize).map(ResponseEntity::ok).switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}
3.路由刷新接口,调用触发事件,重新获取redis里的路由信息
@PostMapping({"/refresh"})
public Mono<Void> refresh() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return Mono.empty();
}
4.获取全局过滤器、过滤器、断言
@GetMapping({"/globalfilters"})
public Mono<HashMap<String, Object>> globalfilters() {
return this.getNamesToOrders(this.globalFilters);
}
@GetMapping({"/routefilters"})
public Mono<HashMap<String, Object>> routefilers() {
return this.getNamesToOrders(this.GatewayFilters);
}
@GetMapping({"/routepredicates"})
public Mono<HashMap<String, Object>> routepredicates() {
return this.getNamesToOrders(this.routePredicates);
}
5.路由新增
@PostMapping({"/routes/{id}"})
public Mono<ResponseEntity<Object>> save(@PathVariable String id, @RequestBody RouteDefinition route) {
return Mono.just(route).filter(this::validateRouteDefinition).flatMap((routeDefinition) -> {
return this.routeDefinitionWriter.save(Mono.just(routeDefinition).map((r) -> {
r.setId(id);
log.debug("Saving route: " + route);
return r;
})).then(Mono.defer(() -> {
return Mono.just(ResponseEntity.created(URI.create("/routes/" + id)).build());
}));
}).switchIfEmpty(Mono.defer(() -> {
return Mono.just(ResponseEntity.badRequest().build());
}));
}
6.路由删除
@DeleteMapping({"/routes/{id}"})
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> {
return Mono.just(ResponseEntity.ok().build());
})).onErrorResume((t) -> {
return t instanceof NotFoundException;
}, (t) -> {
return Mono.just(ResponseEntity.notFound().build());
});
}
Gateway给我们提供的路由操作接口,怎么访问呢?
1.添加actuator依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.暴露端口
在gateway项目的application.yml中启用gateway api:
#开启actuator管理api,后面要关闭
management:
endpoints:
web:
exposure:
include: "*"
3.访问
http://localhost:网关端口/actuator/接口名称
例如:
访问 http://localhost:网关端口/actuator/gateway/routes,返回了当前网关的路由信息。
5.自定义类实现路由操作和redis存储
上面介绍了Gateway给我们提供的接口,进行路由的操作,那怎么可以自定义类实现路由操作呢?
1.首先我们先看Gateway内部给我们提供的路由操作的实现
RouteDefinitionWriter接口提供了路由的保存、删除功能,RouteDefinitionLocator 提供了获取所有路由信息功能
public interface RouteDefinitionWriter {
//新增
Mono<Void> save(Mono<RouteDefinition> route);
//删除
Mono<Void> delete(Mono<String> routeId);
}
public interface RouteDefinitionLocator {
//获取路由所有信息
Flux<RouteDefinition> getRouteDefinitions();
}
RouteDefinitionRepository接口
public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter {
}
2.自定义类实现RouteDefinitionRepository接口
@Component
public class RedisRouteDefinitionRespository implements RouteDefinitionRepository {
private final static String GATEWAY_ROUTE_KEY="dynamic_route";
@Autowired
RedisTemplate<String,String> redisTemplate;
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<RouteDefinition> routeDefinitionList=new ArrayList<>();
redisTemplate.opsForHash().values(GATEWAY_ROUTE_KEY).stream().forEach(route->{
routeDefinitionList.add(JSON.parseObject(route.toString(),RouteDefinition.class));
});
return Flux.fromIterable(routeDefinitionList);
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(routeDefinition -> {
redisTemplate.opsForHash().put(GATEWAY_ROUTE_KEY,routeDefinition.getId(), JSON.toJSONString(routeDefinition));
return Mono.empty();
});
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id->{
if(redisTemplate.opsForHash().hasKey(GATEWAY_ROUTE_KEY,id)){
redisTemplate.opsForHash().delete(GATEWAY_ROUTE_KEY,id);
return Mono.empty();
}
return Mono.defer(()->Mono.error(new Exception("routeDefinition not found:"+routeId)));
});
}
}
6.postman演示路由动态配置和redis存储
1.调用gateway网关向我们暴露的接口
暴露的路由操作接口刚刚已经讲述
actuator/gateway/routes/{id}
路由配置转为json作为参数
{
"predicates":[
{
"name":"Path",
"args":{
"_genkey_0":"/test/**"
}
},
{
"name":"RemoteAddr",
"args":{
"_genkey_0":"192.168.56.1/16"
}
}
],
"filters":[
{
"name":"StripPrefix",
"args":{
"_genkey_0":"1"
}
}
],
"uri":"lb://test",
"order":0
}
2.redis存储提交的路由配置信息
postman提交请求后,查看redis
3.刷新(refresh)
通过动态路由,我们以后发布时,不需重启网关服务了,只需通过暴露的接口进行路由操作,最后通过actuator/gateway/refresh接口,实现网关配置的刷新(会重新从redis拉取配置)
源码分析:
提供的刷新接口
@PostMapping({"/refresh"})
public Mono<Void> refresh() {
this.publisher.publishEvent(new RefreshRoutesEvent(this)); return Mono.empty();
}
监听事件,一旦调用refresh接口,便会重新从redis获取路由配置信息
public void onApplicationEvent(RefreshRoutesEvent event) {
this.fetch().materialize().collect(Collectors.toList()).doOnNext((routes) -> {
//重新获取路由配置信息
List var10000 = (List)this.cache.put("routes", routes);
}).subscribe();
}
更多推荐
所有评论(0)