首页 > 技术文章 > Nacos导航

top-housekeeper 2021-05-25 21:04 原文

简介

最近都在弄微服务的东西,现在来记录下收获。我从一知半解到现在能从0搭建使用最大的感触有两点

1.微服务各大组件的版本很多,网上很多博客内容不一定适合你的版本,很多时候苦苦琢磨都是无用功

2.网上博客参差不齐,有些甚至错误的。更离谱的是,好的文章阅读量除非高出天际,不然就都很低,比那些复制粘贴,随便应付的都低(这个搜索推荐算法不知道基于什么的)

通过这段时间学习,我觉得最重要是从好的博客入手,先不要着急怎么组件怎么使用,而是先了解组件的作用,大概的原理,然后才是使用,这样搭建和尝试的过程中才能更好的定位问题,最后再次回到原理和一些实际问题的处理(不知道实际问题怎样的,直接搜那个组件的面试题往往效果最好)

接下来的内容,都以导航的形式展现给大家(毕竟优秀的轮子很多,直接看大佬写的不香嘛),再顺带提些自己的理解

传送门

更多微服务的介绍可点击下方链接

微服务介绍Nginx导航Nacos导航Gateway导航Ribbon导航Feign导航Sentinel导航

博主微服务git练手项目:https://github.com/NiceJason/SpringCloudDemo

Nacos介绍

微服务的特点就是一堆服务,那么多的服务如果我们亲自管理那太麻烦了。

例如现在有服务A,B,C各一个,我多添加2个服务A,得分别告诉B,C(具体就是修改B,C的配置或者代码,让它们访问A服务的时候会选择新增A服务的ip地址)

又例如现在A,B,C有些公共配置,好想把他放在一个地方,而不是复制3份,到时候改要3个地方去改,如果增加服务D,还得复制一份

针对上述问题,Nacos闪亮登场,主要负责就两件事:1.服务注册与发现、2.配置中心

具体可看:https://www.jianshu.com/p/02bd37de0221

Nacos作为阿里的一员,是真的简单好用(吹爆阿里系),官网使用文档:https://nacos.io/zh-cn/docs/quick-start.html

分享下自己遇到的坑

1.Nacos与其他框架结合

首先项目中只能选择一种来进行配置

即如果用了Nacos与Spring就不要用Nacos与Springboot和Nacos与SpringCloud
否则会有问题,他们之间的访问方式不同,所以注解等也不能通用(看以后的版本会不会兼容)

spring cloud 暂不支持@NacosXXX相关注解
要用spring cloud 要自己想办法解决问题,如果实现解决不了,就放弃,改用Springboot结合或者与Spring结合的类型

当时花了我不少时间去研究为什么注解不生效(毕竟注解这么方便嘤嘤嘤),结果是人家压根不支持SpringCloud

 

2.单机的Nacos固定ip启动

在conf配置文件下修改,这样单机ip就不会变了,不然经常变导致代码访问的ip得经常改,十分麻烦

 

3.Nacos配置

Nacos相关配置说明:https://www.jianshu.com/p/1bd0c1f7c581

有两个配置单独说下:

1 #Nacos服务端的地址,该服务向NACOS注册
2 spring.cloud.nacos.discovery.server-addr
3 
4 #本服务注册的IP地址 优先级最高,意思是别的服务调用该服务所指的地址
5 spring.cloud.nacos.discovery.ip

这两个配置主要涉及到开发的问题,一般来说开发环境会部署一个Nacos(你们项目组或产品线公用这个Nacos),这样的好处在于联调方便,而且不用每个人都自己再部署nacos以及配置文件。当项目部署的时候,会在docker指定不同环境的nacos地址(意思是docker启动的时候可以动态修改参数)

现在有两种情况

a.我要开发其他功能,该功能和微服务无关,那此时就要本地的配置要指定spring.cloud.nacos.discovery.ip为开发环境里的ip,因为如果不指定,那么Nacos里服务就注册你本机的ip,别的小伙伴调用的时候有可能就会访问到你启动的项目

b.我要开发微服务功能,此时可以通过nacos的命名空间去区分你本机自己微服务联调

 

4.Nacos与SpringCloud结合常用类

//由于大多数注解都无效,所以要使用NacosConfigManager进行操作
@Autowired
NacosConfigManager nacosConfigManager;
//这个有值
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
//这个有值
@Autowired
NacosServiceManager nacosServiceManager;

想要代码里动态操作Nacos,用上面3个类就行,直接注入即可

 

Nacos刷新

Nacos刷新原理:

https://so.csdn.net/so/search/blog?q=nacos%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%A7%A6%E5%8F%91%E6%9C%8D%E5%8A%A1%E5%88%B7%E6%96%B0%E5%8E%9F%E7%90%86&t=blog&p=1&s=0&tm=0&lv=-1&ft=0&l=&u=wangwei19871103

文章根据源码,介绍了Nacos是怎么更新列表和动态刷新配置的(主要是和Ribbon结合)

这里我想提两点

懒加载

看过上面原理,可以看到Nacos是怎么懒加载的

发现在其第一次用restTemplate发送请求时(请求一直会到Ribbon包下的SpringClientFactory中,调用SpringCloud包下的NamedContextFactory),动态创建了AnnotationConfigApplicationContext,进行上下文的刷新,将配置动态注入了Spring中
NamedContextFactory中创建AnnotationConfigApplicationContext
 1 protected AnnotationConfigApplicationContext createContext(String name) {
 2         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
 3         ...
 4         for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
 5             if (entry.getKey().startsWith("default.")) {
 6                 for (Class<?> configuration : entry.getValue().getConfiguration()) {
 7                     //这里注册了NacosRibbonClientConfiguration.class
 8                     context.register(configuration);
 9                 }
10             }
11         }
12         //这里注册了RibbonClientConfiguration.class
13         context.register(PropertyPlaceholderAutoConfiguration.class,
14                 this.defaultConfigType);
15         //这里设置了ribbon.nacos.enabled的值
16         context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
17                 this.propertySourceName,
18                 Collections.<String, Object>singletonMap(this.propertyName, name)));
19         if (this.parent != null) {
20             // Uses Environment from parent as well as beans
21             context.setParent(this.parent);
22             // jdk11 issue
23             // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
24             context.setClassLoader(this.parent.getClassLoader());
25         }
26         context.setDisplayName(generateDisplayName(name));
27         //最后刷新上下文,将RibbonClientConfiguration.class
28         //和RibbonClientConfiguration.class重新加载
29         context.refresh();
30         return context;
31 }

其中NacosRibbonClientConfiguration(代码第七行)上有注解ConditionalOnRibbonNacos,而ConditionalOnRibbonNacos上有注解

以此我们可以参考懒加载的实现,或者是动态刷新某些组件

 

动态刷新的局限

Nacos确实支持动态刷新,但这只是动态刷新值,并不会刷新代码的逻辑,对于有很多初始化的类和组件都是无用的,可以监听Nacos配置变化来再次初始化这些类,从而达到真正的动态刷新

 1        //由于大多数注解都无效,所以要使用NacosConfigManager进行操作
 2     @Autowired
 3     NacosConfigManager nacosConfigManager;
 4 
 5     @PostConstruct
 6     public void init() throws NacosException {
 7         ConfigService configService = nacosConfigManager.getConfigService();
 8 
 9         configService.publishConfig("动态发布配置ID","动态发布配置goup","动态发布内容");
10 
11         configService.addListener("user-service-dev.properties", "dev", new Listener() {
12             //这里正常的写法应该是传个线程池进来,不然每次监听到文件改动,都要重新建立线程,开销大
13             @Override
14             public Executor getExecutor() {
15                 return new Executor() {
16                     @Override
17                     public void execute(Runnable command) {
18                         command.run();
19                     }
20                 };
21             }
22 
23             @Override
24             public void receiveConfigInfo(String configInfo) {
25                 System.out.println("监听者获取到信息"+configInfo);
26             }
27         });
28     }

每次最小单位都是以文件为准,Nacos会把改动的文件全部内容传回去,而不是只传被改动的那几个

 

小结

本篇博客主要说了Nacos注意的点(这些内容有的网上可不好找,也没人告诉你),具体怎么搭建使用网上很多资料,希望能帮到大家

推荐阅读