基于Nacos实现Spring Cloud Gateway实现动态路由
Spring Cloud GateWay集成
spring-cloud-starter-gateway:路由转发、请求过滤(权限校验、限流以及监控等)
spring-boot-starter-webflux:反应式Web框架
spring-boot-starter-actuator:监控系统健康
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
动态路由
DynamicRouteServiceImpl:提供动态路由的基础方法,可通过获取bean操作该类的方法。该类提供新增路由、更新路由、删除路由,然后实现发布的功能。
1 @Service
2 public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
3
4 @Autowired
5 private RouteDefinitionWriter routeDefinitionWriter;
6
7 private ApplicationEventPublisher publisher;
8
9
10 /**
11 * 增加路由
12 * @param definition
13 * @return
14 */
15 public String add(RouteDefinition definition) {
16 routeDefinitionWriter.save(Mono.just(definition)).subscribe();
17 this.publisher.publishEvent(new RefreshRoutesEvent(this));
18 return "success";
19 }
20
21
22 /**
23 * 更新路由
24 * @param definition
25 * @return
26 */
27 public String update(RouteDefinition definition) {
28 try {
29 this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
30 } catch (Exception e) {
31 return "update fail,not find route routeId: "+definition.getId();
32 }
33 try {
34 routeDefinitionWriter.save(Mono.just(definition)).subscribe();
35 this.publisher.publishEvent(new RefreshRoutesEvent(this));
36 return "success";
37 } catch (Exception e) {
38 return "update route fail";
39 }
40
41
42 }
43 /**
44 * 删除路由
45 * @param id
46 * @return
47 */
48 public String delete(String id) {
49 try {
50 this.routeDefinitionWriter.delete(Mono.just(id));
51 return "delete success";
52 } catch (Exception e) {
53 e.printStackTrace();
54 return "delete fail";
55 }
56
57 }
58
59 @Override
60 public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
61 this.publisher = applicationEventPublisher;
62 }
63
64 }
RouteDefinition: 提供路由实体类,主要有predicates匹配来自用户的请求,filters服务路由
@Validated
public class RouteDefinition {
@NotEmpty
private String id = UUID.randomUUID().toString();
@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList<>();
@Valid
private List<FilterDefinition> filters = new ArrayList<>();
@NotNull
private URI uri;
private int order = 0;
public RouteDefinition() {}
public RouteDefinition(String text) {
int eqIdx = text.indexOf("=");
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse RouteDefinition text '" + text + "'" +
", must be of the form name=value");
}
setId(text.substring(0, eqIdx));
String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ",");
setUri(URI.create(args[0]));
for (int i=1; i < args.length; i++) {
this.predicates.add(new PredicateDefinition(args[i]));
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<PredicateDefinition> getPredicates() {
return predicates;
}
public void setPredicates(List<PredicateDefinition> predicates) {
this.predicates = predicates;
}
public List<FilterDefinition> getFilters() {
return filters;
}
public void setFilters(List<FilterDefinition> filters) {
this.filters = filters;
}
public URI getUri() {
return uri;
}
public void setUri(URI uri) {
this.uri = uri;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RouteDefinition routeDefinition = (RouteDefinition) o;
return Objects.equals(id, routeDefinition.id) &&
Objects.equals(predicates, routeDefinition.predicates) &&
Objects.equals(order, routeDefinition.order) &&
Objects.equals(uri, routeDefinition.uri);
}
@Override
public int hashCode() {
return Objects.hash(id, predicates, uri);
}
@Override
public String toString() {
return "RouteDefinition{" +
"id='" + id + '\'' +
", predicates=" + predicates +
", filters=" + filters +
", uri=" + uri +
", order=" + order +
'}';
}
}
NacosGatewayProperties:自定义属性绑定值,可通过配置文件配置属性。
@ConfigurationProperties(prefix="nacos", ignoreUnknownFields = true)
@Configuration
public class NacosGatewayProperties {
private String address;
private String dataId;
private String groupId;
private Long timeout;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getDataId() {
return dataId;
}
public void setDataId(String dataId) {
this.dataId = dataId;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public Long getTimeout() {
return timeout;
}
public void setTimeout(Long timeout) {
this.timeout = timeout;
}
}
DynamicRouteServiceImplByNacos: 实现runner,通过nacos下发动态路由配置
@Component
public class DynamicRouteServiceImplByNacos implements CommandLineRunner{
@Autowired
private DynamicRouteServiceImpl dynamicRouteService;
@Autowired
private NacosGatewayProperties nacosGatewayProperties;
/**
* 监听Nacos Server下发的动态路由配置
* @param dataId
* @param group
*/
public void dynamicRouteByNacosListener (){
try {
ConfigService configService=NacosFactory.createConfigService(nacosGatewayProperties.getAddress());
String content = configService.getConfig(nacosGatewayProperties.getDataId(), nacosGatewayProperties.getGroupId(), nacosGatewayProperties.getTimeout());
System.out.println(content);
configService.addListener(nacosGatewayProperties.getDataId(), nacosGatewayProperties.getGroupId(), new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
List<RouteDefinition> list = JsonUtils.toList(configInfo, RouteDefinition.class);
list.forEach(definition->{
dynamicRouteService.update(definition);
});
}
@Override
public Executor getExecutor() {
return null;
}
});
} catch (NacosException e) {
e.printStackTrace();
}
}
@Override
public void run(String... args) throws Exception {
dynamicRouteByNacosListener();
}
}
nacos属性文件定义
[
{
"id": "desk-route",
"order": 0,
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/blade-desk/**"
}
}
],
"filters": [],
"uri": "lb://blade-desk-me"
},
{
"id": "example-route",
"order": 0,
"predicates": [{
"name": "Path",
"args": {
"pattern": "/example"
}
}],
"filters": [],
"uri": "http://www.example.com"
}
]