1、准备一个Nacos环境跟一个普普通通的Spring Cloud项目
2、导包,我这里是把Nacos的注册中心跟配置中心都搞了,动态配置的话主要是用到配置中心,主要是这几个包,部分工具类的包或者是spring cloud的其他包就忽略了。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2021.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.1</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 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>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.8</version>
</dependency>
导包说明:spring-boot-starter-web包跟gateway的包是有冲突的,所以这玩意不能要,有的记得删掉。
另外gateway+spring boot +spring cloud 的版本都是挂钩的,随便搞版本的话,大概率是要交学费的。
我这里spring boot的版本是2.3.12.RELEASE,spring cloud的版本是Hoxton.SR12,gateway的版本是2.2.9.RELEASE,仅供参考,具体可以搜一下,有对照表的。
3、配置,这里不作说明了,具体含义可以查看官网:nacos.io
spring:
freemarker:
check-template-location: false
profiles:
active: dev
jackson:
time-zone: GMT+8
application:
name: gateway-router
datasource:
druid:
test-on-borrow=true:
cloud:
nacos:
#这个是注册中心
discovery:
server-addr: 192.168.2.200:8848
service: gateway-router
#这个才是配置中心
config:
server-addr: 192.168.2.200:8848
name: gateway-router
namespace: public
group: DEFAULT_GROUP
loadbalancer:
ribbon:
enabled: false
如果发现Nacos的心跳日志一直打,很烦人的话,可以加上这句屏蔽掉
logging:
level:
com.alibaba.nacos.client.config.impl: WARN
4、application启动类记得加上标注
@EnableDiscoveryClient
5、配置读取类GatewayConfig,与上方的配置项对应,不要漏,会报错。
@Configuration
@Slf4j
public class GatewayConfig {
public static final long DEFAULT_TIMEOUT = 30000;
public static String NACOS_SERVER_ADDR;
public static String NACOS_NAMESPACE;
public static String NACOS_ROUTE_DATA_ID;
public static String NACOS_ROUTE_GROUP;
@Value("${spring.cloud.nacos.config.server-addr}")
public void setNacosServerAddr(String nacosServerAddr) {
NACOS_SERVER_ADDR = nacosServerAddr;
}
@Value("${spring.cloud.nacos.config.namespace}")
public void setNacosNamespace(String nacosNamespace) {
NACOS_NAMESPACE = nacosNamespace;
}
@Value("${spring.cloud.nacos.config.name}")
public void setNacosRouteDataId(String nacosRouteDataId) {
NACOS_ROUTE_DATA_ID = nacosRouteDataId;
}
@Value("${spring.cloud.nacos.config.group}")
public void setNacosRouteGroup(String nacosRouteGroup) {
NACOS_ROUTE_GROUP = nacosRouteGroup;
}
}
6、动态路由基础控制类DynamicRouteServiceImpl,我抄过来的时候是带log代码的,这里我都去掉了,自己加就好。
@Slf4j
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private RouteDefinitionLocator routeDefinitionLocator;
/**
* 发布事件
*/
@Autowired
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
/**
* 删除路由
* @param id
* @return
*/
public String delete(String id) {
try {
this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "delete success";
} catch (Exception e) {
return "delete fail";
}
}
/**
* 更新路由
* @param definitions
* @return
*/
public String updateList(List<RouteDefinition> definitions) {
// 删除缓存routerDefinition
List<RouteDefinition> routeDefinitionsExits = routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst();
if (!CollectionUtils.isEmpty(routeDefinitionsExits)) {
routeDefinitionsExits.forEach(routeDefinition -> {
delete(routeDefinition.getId());
});
}
definitions.forEach(definition -> {
updateById(definition);
});
return "success";
}
/**
* 更新路由
* @param definition
* @return
*/
public String updateById(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 definition
* @return
*/
public String add(RouteDefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
}
7、核心注册类DynamicRouteServiceImplByNacos
@Component
@Slf4j
@DependsOn({"gatewayConfig"}) // 依赖于gatewayConfig bean
public class DynamicRouteServiceImplByNacos {
@Autowired
private DynamicRouteServiceImpl dynamicRouteService;
private ConfigService configService;
@PostConstruct
public void init() {
try{
configService = initConfigService();
if(configService == null){
return;
}
//从nacos拿初始配置
String configInfo = configService.getConfig(GatewayConfig.NACOS_ROUTE_DATA_ID, GatewayConfig.NACOS_ROUTE_GROUP, GatewayConfig.DEFAULT_TIMEOUT);
List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
for(RouteDefinition definition : definitionList){
dynamicRouteService.add(definition);
}
} catch (Exception e) {
}
//注册监听,nacos配置有更新的话同步更新一下配置
dynamicRouteByNacosListener(GatewayConfig.NACOS_ROUTE_DATA_ID,GatewayConfig.NACOS_ROUTE_GROUP);
}
/**
* 监听Nacos下发的动态路由配置
* @param dataId
* @param group
*/
public void dynamicRouteByNacosListener (String dataId, String group){
try {
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
dynamicRouteService.updateList(definitionList);
}
@Override
public Executor getExecutor() {
return null;
}
});
} catch (NacosException e) {
}
}
/**
* 初始化网关路由 nacos config
* @return
*/
private ConfigService initConfigService(){
try{
Properties properties = new Properties();
properties.setProperty("serverAddr",GatewayConfig.NACOS_SERVER_ADDR);
properties.setProperty("namespace",GatewayConfig.NACOS_NAMESPACE);
return configService= NacosFactory.createConfigService(properties);
} catch (Exception e) {
return null;
}
}
}
8、Nacos配置
Data ID:gateway-router
//这里是以json的形式,所以直接与服务名称一致即可,如果用的是yml格式,估计得用“${prefix}-${spring.profiles.active}.${file-extension}”格式
Group:DEFAULT_GROUP
配置类型是json,内容:
[{
"id": "test-router",
"order": 0,
"predicates": [{
"args": {
"pattern": "/test/**"
},
"name": "Path"
}],
"filters":[{
"args": {
"_genkey_0":"1"
},
"name":"StripPrefix"
}],
"uri": "lb://test-service"
},{
"id": "baidu-router",
"order": 2,
"predicates": [{
"args": {
"pattern": "/baidu/**"
},
"name": "Path"
}],
"uri": "http://baidu.com"
}]
这里配置了两个路由:
第一个路由是将/test/**规则转发到test-service服务,并且去掉前缀(即:/test/home等同于http://test-service/home)
第二个路由是将/baidu/**规则转发到百度,且不去掉前缀(即:/baidu/home等同于访问http://baidu.com/baidu/home)
9、运行