项目依赖
配置
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
# Nacos认证信息
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=nacos
spring.cloud.nacos.config.contextPath=/nacos
# 设置配置中心服务端地址
spring.cloud.nacos.config.server-addr=localhost:8848
# Nacos 配置中心的namespace。需要注意,如果使用 public 的 namcespace ,请不要填写这个值,直接留空即可
spring.cloud.nacos.config.namespace=1c5ae1c6-cac5-40ef-b090-b4e9677ea2aa
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=zuul-client
spring.cloud.util.enabled=false
spring.profiles.active=dev
spring.cloud.nacos.discovery.group=cloud-test
spring.cloud.nacos.config.group=cloud-test
spring.cloud.nacos.config.file-extension=properties
# Nacos认证信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=1c5ae1c6-cac5-40ef-b090-b4e9677ea2aa
#dataId 的完整格式如下:prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置
# ${prefix}-${spring.profiles.active}.${file-extension}
# 数据库连接信息{单数据源}
路由配置
sidecar:
# 异构微服务的IP
ip: 127.0.0.1
# 异构微服务的端口
port: 8060
# 异构微服务的健康检查URL
health-check-url: http://localhost:8060/health.json
spring:
cloud:
gateway:
discovery:
locator:
enabled: true #开启通过服务中心的自动根据 serviceId 创建路由的功能
routes:
- id: ldapsso-client
uri: lb://ldapsso-client
predicates:
- Path=/ldapsso/**
filters:
- StripPrefix=1
- id: service-b
uri: lb://service-b
predicates:
- Path=/service-b/test-service-b/**
filters:
- StripPrefix=1
inetutils:
ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
nacos:
discovery:
server-addr: localhost:8848
application:
name: zuul-client
management:
endpoint:
health:
show-details: always
server:
port: 28083
启动器
@SpringBootApplication
@EnableDiscoveryClient
public class ZuulApplication {
public static void main(String[] args) {
try {
SpringApplication.run(ZuulApplication.class, args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public InetIPv6Utils inetIPv6Utils() {
InetUtilsProperties properties = new InetUtilsProperties();
return new InetIPv6Utils(properties);
}
@Bean
public InetUtils inetUtils() {
InetUtilsProperties properties = new InetUtilsProperties();
return new InetUtils(properties);
}
}
测试类
@RestController
public class IndexController {
@RequestMapping("/index")
public String getIndex(){
return "Zuul";
}
}
路由更新业务处理
public interface RouteService {
/**
* 更新路由配置
*
* @param routeDefinition
*/
void update(RouteDefinition routeDefinition);
/**
* 添加路由配置
*
* @param routeDefinition
*/
void add(RouteDefinition routeDefinition);
}
@Slf4j
@Service
public class RouteServiceImpl implements RouteService, ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
/**
* 事件发布者
*/
private ApplicationEventPublisher publisher;
@Override
public void update(RouteDefinition routeDefinition) {
log.info("更新路由配置项:{}", routeDefinition);
this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
@Override
public void add(RouteDefinition routeDefinition) {
log.info("新增路由配置项:{}", routeDefinition);
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}
动态路由加载
package com.example.zuul.listner;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.annotation.NacosConfigListener;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.utils.UuidUtils;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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;
import java.net.URI;
import java.util.*;
import java.util.concurrent.Executor;
@Component
@Slf4j
public class NacosRefreshConfig {
@Autowired
private RouteService routeService;
@Value("${spring.cloud.nacos.config.server-addr}")
private String nacosAddr;
@Value("${spring.cloud.nacos.config.namespace}")
private String nameSpace;
@Value("${spring.cloud.nacos.config.username}")
private String username;
@Value("${spring.cloud.nacos.config.password}")
private String password;
@PostConstruct
public void initBindListner() throws NacosException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, nacosAddr);
properties.put(PropertyKeyConst.NAMESPACE, nameSpace);
properties.put(PropertyKeyConst.USERNAME, username);
properties.put(PropertyKeyConst.PASSWORD, password);
ConfigService configService = NacosFactory.createConfigService(properties);
configService.addListener(Constants.CONFIG_FILE_NAME, Constants.CONFIG_FILE_GROUP, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
// 刷新路由
List<RouteDefinition> extracted = extracted(configInfo);
for (RouteDefinition routeDefinition : extracted) {
routeService.update(routeDefinition);
}
}
});
}
private static List<RouteDefinition> extracted(String configInfo) {
List<RouteDefinition> list = new ArrayList<>();
JSONArray jsonObjectArr = JSONArray.parseArray(configInfo);
for (int i = 0; i < jsonObjectArr.size(); i++) {
RouteDefinition routeDefinition = new RouteDefinition();
JSONObject jsonObject = jsonObjectArr.getJSONObject(i);
routeDefinition.setId(StringUtils.defaultIfEmpty(jsonObject.getString("id"), UuidUtils.generateUuid()));
routeDefinition.setUri(URI.create(jsonObject.getString("uri")));
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
JSONArray predicates1 = jsonObject.getJSONArray("predicates");
if (predicates1 != null && predicates1.size() > 1) {
for (int ii = 0; ii < predicates1.size(); ii++) {
Set<Map.Entry<String, Object>> entries = predicates1.getJSONObject(ii).entrySet();
for (Map.Entry<String, Object> entry : entries) {
PredicateDefinition predicateDefinition = new PredicateDefinition();
predicateDefinition.setName(entry.getKey());
Map<String, String> args = predicateDefinition.getArgs();
args.put(entry.getKey(), entry.getValue().toString());
predicates.add(predicateDefinition);
}
}
} else {
Set<Map.Entry<String, Object>> entries = predicates1.getJSONObject(0).entrySet();
for (Map.Entry<String, Object> entry : entries) {
PredicateDefinition predicateDefinition = new PredicateDefinition();
predicateDefinition.setName(entry.getKey());
Map<String, String> args = predicateDefinition.getArgs();
args.put(entry.getKey(), entry.getValue().toString());
predicates.add(predicateDefinition);
}
}
List<FilterDefinition> filters = routeDefinition.getFilters();
JSONArray var6 = jsonObject.getJSONArray("filters");
if (var6 != null && var6.size() > 1) {
for (int ij = 0; ij < var6.size(); ij++) {
Set<Map.Entry<String, Object>> entries = var6.getJSONObject(ij).entrySet();
for (Map.Entry<String, Object> entry : entries) {
FilterDefinition filterDefinition = new FilterDefinition();
filterDefinition.setName(entry.getKey());
Map<String, String> args = filterDefinition.getArgs();
args.put(entry.getKey(), entry.getValue().toString());
filters.add(filterDefinition);
}
}
} else {
Set<Map.Entry<String, Object>> entries = var6.getJSONObject(0).entrySet();
for (Map.Entry<String, Object> entry : entries) {
FilterDefinition filterDefinition = new FilterDefinition();
filterDefinition.setName(entry.getKey());
Map<String, String> args = filterDefinition.getArgs();
args.put(entry.getKey(), entry.getValue().toString());
filters.add(filterDefinition);
}
}
list.add(routeDefinition);
}
return list;
}
@NacosConfigListener(dataId = Constants.CONFIG_FILE_NAME)
public void refresh(String msg) {
log.error("-----------------------");
JSONObject jsonObject = JSONObject.parseObject(msg);
log.error(jsonObject.toJSONString());
}
}
public interface Constants {
String CONFIG_FILE_NAME = "zuul-client";
String CONFIG_FILE_GROUP = "cloud-test";
}
Nacos配置
[
{"id":"ldapsso-client","uri":"lb://ldapsso-client","predicates":[{"Path":"/ldapsso/**"}],"filters":[{"StripPrefix":1}]},
{"id":"baidu","uri":"https://www.baidu.com","predicates":[{"Path":"/baidu/**"}],"filters":[{"StripPrefix":1}]},
{"id":"client","uri":"lb://ldapsso-client","predicates":[{"Path":"/client/**"}],"filters":[{"StripPrefix":1}]}
]