Springcloud Gateway 路由管理
Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
相应的入门demo网上很多,我们这边一笔带过
1.引入pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.application.yml 配置文件
server:
servlet:
context-path: /
port: 18889
spring:
application:
name: client-gateway
cloud:
gateway:
discovery:
locator:
enabled: false #表明gateway开启服务注册和发现的功能,并且spring cloud gateway自动根据服务发现为每一个服务创建了一个router,# 这个router将以服务名开头的请求路径转发到对应的服务
lower-case-service-id: true #将请求路径上的服务名配置为小写(因为服务注册的时候,向注册中心注册时将服务名转成大写的了,比如以/service-hi/*的请求路径被路由转发到服务名为service-hi的服务上
routes:
- id: test-id
uri: lb://client-manage
order: -1
predicates:
- Path=/api2/**
filters:
- StripPrefix=1
- id:我们自定义的路由 ID,保持唯一
- uri:目标服务地址
- predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
- filters:过滤规则,
上面那段路由配置表示所有已包含 /api2/ 的url都会被路由到 client-manage服务,StripPrefix=1表示路由时会去除/api2/。
我们也可以使用api的方式配置
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
// @formatter:off
return builder.routes ()
.route (r -> r.path ("/api2/**")
.filters (f -> f.stripPrefix (1))
.uri ("lb://client-manage")
.order (0)
.id ("client-manage")
)
.build ();
}
这两种配置效果是一致。
路由扩展
基于上面两种配置,我们的网关已经具有了路由的功能了。但是这里还不够工程化。设想下,现在我有上百个路由信息,配置文件或者api的形式去配置必然会导致可读性的缺失。同时我还想实现不停机的增加路由。这里就引入了动态增加路由的概念。翻看Gateway的代码,发现Gateway代码本身就支持动态增加路由,相关代码在 org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint#save
入参的 RouteDefinition 是对路由的封装,与上文的配置文件一一相对应。最终实际调用的是 RouteDefinitionWriter的save方法。
基于上述代码,我们要做以下几件事情
1.定义外部存储文件中的RouteDefinition的数据结构
2.启动时自动读取路由信息写入内存中。
单个路由的添加
@PostMapping(value = "addRoute")
@ResponseBody
public String addRoute() throws URISyntaxException{ RouteDefinition routeDefinition=new RouteDefinition();
routeDefinition.setId("test-id");
List<PredicateDefinition> predicates=new ArrayList<>();
PredicateDefinition definition=new PredicateDefinition();
//注意name
definition.setName("Path");
definition.addArg("pattern","/api2/**");
predicates.add(definition);
routeDefinition.setPredicates(predicates); List<FilterDefinition> filters =new ArrayList<>();
FilterDefinition filterDefinition = new FilterDefinition();
//注意name
filterDefinition.setName("StripPrefix");
filterDefinition.addArg("parts","1");
filters.add(filterDefinition);
routeDefinition.setFilters(filters); URI uri = new URI("lb://client-manage");
routeDefinition.setUri(uri);
routeDefinition.setOrder(0);
String save = routeService.add(routeDefinition);
System.out.println(save);
return "";
}
为确保功能的实现,我们先写死一个配置尝试用这种方式配置路由。这里注意两点
PredicateDefinition配置
PredicateDefinition的name代表每一个工厂类,只能从以下选择
addArgs的key是相应方法参数名称
public GatewayFilterSpec stripPrefix(int parts) {
return filter(getBean(StripPrefixGatewayFilterFactory.class)
.apply(c -> c.setParts(parts)));
}
FilterDefinition配置
FilterDefinition和PredicateDefinition类似,name从以下选择
配置文件读取
单个配置生效后我们开始外部文件的形式去配置,这里为了便捷我依然从项目配置文件读取。实际我们可以把配置放到数据库,缓存。
新建api.properties文件
新增配置
api.methods.api2={"predicateDefinition":[{"predicateValue":"/api2/**","name":"Path","predicateKey":"pattern"}],"id":"test_id","uri":"lb://client-manage","filterDefinition":[{"filterKey":"parts","filterValue":"1","name":"StripPrefix"}],"order":"0"}
定义RouteDefines读取配置文件
@Configuration
@PropertySource("classpath:api.properties")
@ConfigurationProperties(prefix = "api")
public class RouteDefines { public Map<String, String> methods = new HashMap<>(); public Map<String, String> getMethods() {
return methods;
} public void setMethods(Map<String, String> methods) {
this.methods = methods;
} }
定义InitRouteApplication初始化路由信息写入内存
package com.hdkj.client.gateway; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hdkj.client.gateway.configuration.RouteDefines;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Component; /**
* @author Xu.Minzhe
* @version V1.0
* @package com.hdkj.client.gateway
* @class: InitRouteApplication.java
* @description: 初始化路由信息
* @Date 2019-04-25 10:15
*/
@Component
public class InitRouteApplication implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(InitRouteApplication.class); @Autowired
private RouteDefines routeDefines; @Autowired
private DynamicRouteService routeService; @Override
public void run(ApplicationArguments args) throws Exception {
Map<String, String> methods = routeDefines.getMethods();
methods.values().stream().forEach(x->{
try {
System.out.println("配置文件读取的信息"+x);
JSONObject jsonObject = JSONObject.parseObject(x);
//组装RouteDefinition
RouteDefinition routeDefinition = getRouteDefinition(jsonObject);
//路由信息写入
String save = routeService.add(routeDefinition);
} catch (Exception e) {
logger.error("[路由初始化] 异常",e);
}
});
} /**
* 组装RouteDefinition
* @param jsonObject
* @return
* @throws URISyntaxException
*/
private RouteDefinition getRouteDefinition(JSONObject jsonObject) throws URISyntaxException {
RouteDefinition routeDefinition=new RouteDefinition();
routeDefinition.setId(jsonObject.getString("id"));
List<PredicateDefinition> predicateList = getPredicateList(jsonObject);
routeDefinition.setPredicates(predicateList); List<FilterDefinition> filterDefinition1 = getFilterDefinition(jsonObject);
routeDefinition.setFilters(filterDefinition1); URI uri = new URI(jsonObject.getString("uri"));
routeDefinition.setUri(uri);
routeDefinition.setOrder(jsonObject.getIntValue("order"));
return routeDefinition;
} /**
* 解析json 获得PredicateList
* @param jsonObject
* @return
*/
private List<PredicateDefinition> getPredicateList(JSONObject jsonObject) {
JSONArray predicateDefinition = jsonObject.getJSONArray("predicateDefinition");
List<PredicateDefinition> predicates=new ArrayList<>();
predicateDefinition.stream().forEach(predicate->{
JSONObject jsonObject1 = JSONObject.parseObject(predicate.toString());
PredicateDefinition definition=new PredicateDefinition();
definition.setName(jsonObject1.getString("name"));
definition.addArg(jsonObject1.getString("predicateKey"),jsonObject1.getString("predicateValue"));
predicates.add(definition); });
return predicates;
} /**
* 解析json 获得FilterDefinitionList
* @param jsonObject
* @return
*/
private List<FilterDefinition> getFilterDefinition(JSONObject jsonObject) {
JSONArray predicateDefinition = jsonObject.getJSONArray("filterDefinition");
List<FilterDefinition> predicates=new ArrayList<>();
predicateDefinition.stream().forEach(predicate->{
JSONObject jsonObject1 = JSONObject.parseObject(predicate.toString());
FilterDefinition definition=new FilterDefinition();
definition.setName(jsonObject1.getString("name"));
definition.addArg(jsonObject1.getString("filterKey"),jsonObject1.getString("filterValue"));
predicates.add(definition); });
return predicates;
}
}
这里DynamicRouteService是路由写入实现类
@Component
public class DynamicRouteService implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter; private ApplicationEventPublisher publisher; /**
* 增加路由
* @param definition
* @return
*/
public String add(RouteDefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
} @Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher; }
}
以上便是路由加载的实现。至于动态新增路由,有了以上的代码,实现也是相当简单了。这里不再叙述。
Springcloud Gateway 路由管理的更多相关文章
- SpringCloud Gateway入门
本文是介绍一下SpringCloud Gateway简单路由转发使用. SpringCloud Gateway简介 SpringCloud是基于Spring Framework 5,Project R ...
- 微服务SpringCloud之GateWay路由
在前面博客学习了网关zuul,今天学下spring官方自带的网关spring cloud gateway.Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接,如 WebSo ...
- 使用springcloud gateway搭建网关(分流,限流,熔断)
Spring Cloud Gateway Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 ...
- SpringCloud gateway (史上最全)
疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 -25[ 博客园 总入口 ] 前言 ### 前言 疯狂创客圈(笔者尼恩创建的高并发研习社群)Springcloud 高并发系列文章,将为大家 ...
- 基于springcloud gateway + nacos实现灰度发布(reactive版)
什么是灰度发布? 灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式.在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B ...
- SpringCloud gateway 3
参考博客:https://www.cnblogs.com/crazymakercircle/p/11704077.html 1.1 SpringCloud Gateway 简介 SpringCloud ...
- SpringCloud Gateway高阶之Sentinel限流、熔断
前言 为什么需要服务熔断和降级?微服务是当前业界的一大趋势,原理就是将单一职责的功能模块独立化为子服务,降低服务间的耦合,服务间互相调用.但是这样也会出现一些问题: 上图中大量微服务互相调用,存在大量 ...
- SpringCloud Gateway快速入门
SpringCloud Gateway cloud笔记第一部分 cloud笔记第二部分Hystrix 文章目录 SpringCloud Gateway Zull的工作模式与Gateway的对比 Rou ...
- 万字长文:SpringCloud gateway入门学习&实践
官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/# ...
随机推荐
- MyBatis打印SQL执行时间
1.plugins MyBatis官网对于plugins的描述是这样的: MyBatis allows you to intercept calls to at certain points with ...
- 大数据与Mapreduce
第十五章 大数据与Maprudece 一.引言 实际生活中的数据量是非常庞大的,采用单机运行的方式可能需要若干天才能出结果,这显然不符合我们的预期,为了尽快的获得结果,我们将采用分布式的方式,将计算分 ...
- webpack4.x版本splitChunksPlugin的配置项详解与实际应用场景
在工程化地使用webpack时,公共代码抽离是不可或缺的,4.x版本之后,commonsChunkPlugin已经被去掉,splitChunksPlugins登上舞台,并且优化了很多配置选项,集体课件 ...
- mybatis查询异常-Error querying database. Cause: java.lang.ClassCastException: org.apache.ibatis.executor.ExecutionPlaceholder cannot be cast to java.util.List
背景,mybatis查询的时候直接取的sqlsession,没有包装成SqlSessionTemplate,没有走spring提供的代理. 然后我写的获取sqlsession的代码没有考虑到并发的情况 ...
- PAT1125:Chain the Ropes
1125. Chain the Ropes (25) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Given ...
- SSM-SpringMVC-06:SpringMVC关于静态资源无法展示的问题
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 按照之前的那种方式一路走下来,或许你没发觉有问题,只是你没有使用到而已 css,js,图片等无法正常使用怎么 ...
- 【Web】一个非常简单的移动web消息框
适用:h5+jquery,移动网页最佳 最近在写个简单的公众号页面,前端验证时有些信息要提示,很简单的需求实在不想找啥现成的轮子,又不至于用alert这么粗暴,遂写了个非常简单的消息框,效果如图: 特 ...
- ucloud mysql
[root@--- bin]# yum install mysql-server Loaded plugins: fastestmirror Setting up Install Process Lo ...
- 理解Flexbox弹性盒子
http://www.w3cplus.com/css3/understanding-flexbox-everything-you-need-to-know.html参考文档 1:要开始使用Flexbo ...
- 电梯调度算法---SCAN算法
请珍惜小编劳动成果,该文章为小编原创,转载请注明出处. 扫描(SCAN)调度算法:总是从磁臂当前位置开始,沿磁臂的移动方向去选择离当前磁臂最近的那个柱面的访问者.如果沿磁臂的方向无请求访问时,就改变磁 ...