简介
Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的一款动态服务发现、配置管理和服务管理平台,旨在为微服务架构提供高可用、高性能的解决方案。其核心功能包括服务注册与发现、动态配置管理、服务健康监测、动态 DNS 服务等,广泛应用于云原生和分布式系统场景
官网
https://nacos.io/zh-cn/docs/what-is-nacos.html
配置管理
核心概念
- Namespace(命名空间):命名空间用于隔离不同的配置集。它允许在同一个 Nacos 集群中将不同的环境(如开发、测试、生产)或者不同的业务线的配置进行隔离。(默认提供了一个 public 命名空间)
- Group(组):配置组是用于将多个相关的配置项进行分类管理的逻辑分组机制。每个配置项可以属于不同的组,以便于配置管理。
- Data ID:Data ID 是一个唯一的配置标识符,通常与具体的应用程序相关。通过 Data ID,Nacos 知道如何获取特定应用的某个具体配置。
- Config Listener(配置监听器):配置监听器用于让客户端实时监听 Nacos 配置中心中的配置变化,可以自动感知配置的更新并做出相应的处理。
推送和监听
推送方法
- Nacos 控制台(推荐)
- 应用程序 SDK。Nacos 支持和 Spring Boot 快速整合,可以参考 官方文档
- Open API
监听方法
1.基于SDK
String serverAddr = "{serverAddr}";
String dataId = "{dataId}";
String group = "{group}";
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
ConfigService configService = NacosFactory.createConfigService(properties);
String content = configService.getConfig(dataId, group, 5000);
System.out.println(content);
configService.addListener(dataId, group, new Listener() {@Overridepublic void receiveConfigInfo(String configInfo) {System.out.println("recieve1:" + configInfo);}@Overridepublic Executor getExecutor() {return null;}
});// 测试让主线程不退出,因为订阅配置是守护线程,主线程退出守护线程就会退出。 正式代码中无需下面代码
while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
}
2.基于注解
@Controller
@RequestMapping("config")
public class ConfigController {@NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)private boolean useLocalCache;@RequestMapping(value = "/get", method = GET)@ResponseBodypublic boolean get() {return useLocalCache;}
}
实战(动态ip黑名单过滤)
- 下载nacos
https://nacos.io/download/release-history/ - 启动nacos
在startup.sh目录下执行
sh startup.sh -m standalone
- 添加配置
访问:http://127.0.0.1:8848/nacos ,默认用户名和密码都是 nacos
4.引入依赖
<dependency><groupId>com.alibaba.boot</groupId><artifactId>nacos-config-spring-boot-starter</artifactId><version>0.2.12</version>
</dependency>
注意:版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。
添加配置
# 配置中心
nacos:config:server-addr: 127.0.0.1:8848 # nacos 地址bootstrap:enable: true # 预加载data-id: mianshiya # 控制台填写的 Data IDgroup: DEFAULT_GROUP # 控制台填写的 grouptype: yaml # 选择的文件格式auto-refresh: true # 开启自动刷新
5.利用布隆过滤器创建黑名单过滤工具类
@Slf4j
public class BlackIpUtils {private static BitMapBloomFilter bloomFilter;// 判断 ip 是否在黑名单内public static boolean isBlackIp(String ip) {return bloomFilter.contains(ip);}// 重建 ip 黑名单public static void rebuildBlackIp(String configInfo) {if (StrUtil.isBlank(configInfo)) {configInfo = "{}";}// 解析 yaml 文件Yaml yaml = new Yaml();Map map = yaml.loadAs(configInfo, Map.class);// 获取 ip 黑名单List<String> blackIpList = (List<String>) map.get("blackIpList");// 加锁防止并发synchronized (BlackIpUtils.class) {if (CollectionUtil.isNotEmpty(blackIpList)) {// 注意构造参数的设置BitMapBloomFilter bitMapBloomFilter = new BitMapBloomFilter(958506);for (String ip : blackIpList) {bitMapBloomFilter.add(ip);}bloomFilter = bitMapBloomFilter;} else {bloomFilter = new BitMapBloomFilter(100);}}}
}
6.创建 Nacos 配置监听类
@Slf4j
@Component
public class NacosListener implements InitializingBean {@NacosInjectedprivate ConfigService configService;@Value("${nacos.config.data-id}")private String dataId;@Value("${nacos.config.group}")private String group;@Overridepublic void afterPropertiesSet() throws Exception {log.info("nacos 监听器启动");String config = configService.getConfigAndSignListener(dataId, group, 3000L, new Listener() {final ThreadFactory threadFactory = new ThreadFactory() {private final AtomicInteger poolNumber = new AtomicInteger(1);@Overridepublic Thread newThread(@NotNull Runnable r) {Thread thread = new Thread(r);thread.setName("refresh-ThreadPool" + poolNumber.getAndIncrement());return thread;}};final ExecutorService executorService = Executors.newFixedThreadPool(1, threadFactory);// 通过线程池异步处理黑名单变化的逻辑@Overridepublic Executor getExecutor() {return executorService;}// 监听后续黑名单变化@Overridepublic void receiveConfigInfo(String configInfo) {log.info("监听到配置信息变化:{}", configInfo);BlackIpUtils.rebuildBlackIp(configInfo);}});// 初始化黑名单BlackIpUtils.rebuildBlackIp(config);}
}
7.利用过滤器创建黑名单过滤器
@WebFilter(urlPatterns = "/*", filterName = "blackIpFilter")
public class BlackIpFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {String ipAddress = NetUtils.getIpAddress((HttpServletRequest) servletRequest);if (BlackIpUtils.isBlackIp(ipAddress)) {servletResponse.setContentType("text/json;charset=UTF-8");servletResponse.getWriter().write("{\"errorCode\":\"-1\",\"errorMsg\":\"黑名单IP,禁止访问\"}");return;}filterChain.doFilter(servletRequest, servletResponse);}}
注意在启动类上加上 @ServletComponentScan