基于Nacos实现Spring Cloud Gateway实现动态路由的方法

简介

主要介绍以Nacos为配置中心,实现Spring Cloud GateWay 实现动态路由的功能。Spring Cloud Gateway启动时候,就将路由配置和规则加载到内存里,无法做到不重启网关就可以动态的对应路由的配置和规则进行增加,修改和删除。通过Nacos的配置下发功能可以实现在不重启网关的情况下,实现动态路由。

集成

Spring Cloud GateWay集成

  • spring-cloud-starter-gateway:路由转发、请求过滤(权限校验、限流以及监控等)
  • spring-boot-starter-webflux:反应式Web框架
  • spring-boot-starter-actuator:监控系统健康
1
2
3
4
5
6
7
8
9
10
11
12
<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>

Nacos集成

nacos-client:Nacos客户端

1
2
3
4
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</dependency>

动态路由

DynamicRouteServiceImpl:提供动态路由的基础方法,可通过获取bean操作该类的方法。该类提供新增路由、更新路由、删除路由,然后实现发布的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@Service
public class DynamicRouteServiceImpl 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";
}

/**
* 更新路由
* @param definition
* @return
*/
public String update(RouteDefinition definition) {
try {
this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
} catch (Exception e) {
return "update fail,not find route routeId: "+definition.getId();
}
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
} catch (Exception e) {
return "update route fail";
}
}

/**
* 删除路由
* @param id
* @return
*/
public String delete(String id) {
try {
this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
return "delete success";
} catch (Exception e) {
e.printStackTrace();
return "delete fail";
}
}

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}

}

RouteDefinition:提供路由实体类,主要有predicates匹配来自用户的请求,filters服务路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
@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:自定义属性绑定值,可通过配置文件配置属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@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下发动态路由配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Slf4j
@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());
log.info(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配置下发

Nacos配置

1
2
3
4
5
nacos:
address: 127.0.0.1:8848
data-id: ok-cloud-gateway-route
group-id: dev
timeout: 5000

Nacos属性文件定义

新建dataID为ok-cloud-gateway-route,groupID为dev的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[
{
"filters": [],
"id": "blog",
"order": 0,
"predicates": [{
"args": {
"pattern": "/about"
},
"name": "Path"
}],
"uri": "https://www.appblog.cn"
},
{
"filters": [],
"id": "user-service",
"order": 0,
"predicates": [{
"args": {
"pattern": "/user/**"
},
"name": "Path"
}],
"uri": "lb://ok-cloud-user-service"
}
]

查看路由信息:http://127.0.0.1:9000/actuator/gateway/routes

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2021 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :