重启order-service后,访问控制台,可以看到下面的结果:
此时访问order-service,因为namespace不同,会导致找不到userservice,控制台会报错:
这里环境隔离我们就演示完了。
12.Nacos和Eureka的区别
我们先来看下图,这个图大家应该比较熟悉了吧,我们之前在学习Eureka的时候也见到过。不管是什么样的注册中心啊,我们服务提供者在启动时都会把自己的信息提交给注册中心,而注册中心就会把这些信息保留下来。那么当我们的服务消费者需要去消费时,就会去找注册中心,去拉去服务的信息。不过啊,当时我没讲的一点是,这个拉去的动作,并不是每一次都要做的。如果每一次发请求都要去做一次拉取,那么这样对Eureka和Nacos来讲压力是不是太大了。所以作为消费者在做服务拉取时,它会将获取到的服务信息缓存到一个列表当中。我拉去了一次,那么我接下来一段时间我就可以不用去拉去了,而是直接缓存中的服务信息了。当然了,我这个缓存一直不更新也不行,万一服务提供者变化了怎么办呢,所以会每隔30秒去拉去一次信息进行更新。
消费者拿到服务信息以后,再去进行负载均衡,挑选一个发起远程调用。当时在Nacos里面,它于Eureka会有一些差别,差别在于服务提供者的健康检测。我们的Nacos会把服务提供者划分成临时实例和非临时实例。
我们打开Nacos的控制台,随便打开一个服务的详情:
服务默认都是临时实例,因为我们没有配置。也就是说默认情况下,所有的实例都是临时实例。
临时实例和非临时实例在Nacos里面在做健康检测时是不一样的。
临时实例在Nacos做检测时采用的是心跳检测,这一点和Eureka的心跳检测完全一致。服务提供者每隔一段时间发一个请求到Nacos。
但是呢,我们的非临时实例就不一样了。非临时实例我们的Nacos不会去做心跳,这个时候健康检测这么检测的呢?是由Nacos主动发请求给服务提供者询问。并且如果非临时实例如果挂掉了,Nacos不会把它从列表中剔除,只会标注这个服务实例不健康,等这个服务恢复健康。
还有一个差别在于消费者,我们的Eureka采用的是定时拉去,每隔一段时间拉去一次。那你思考一下,如果在30秒内有服务提供者挂了,那服务消费者知道吗?如果它不知道,去调用挂掉了的服务提供者实例,不久出问题了吗?我们的Eureka做服务拉取的效率比较差,更新的不够及时,而我们的Nacos做了一件事,叫消息推送 。也就是说我们的Eureka采用的是pull,而我们的Nacos采用的是pull加push两者结合,我们的Nacos如果发现有服务挂了,它会发一条消息推送给我们的消费者,然后告诉你服务变更了,更加具有时效性。
这就是Eureka和Nacos之间的差别了。
下面小小总结一下:
Nacos的服务实例分为两种l类型:
临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。
非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例。
配置一个服务实例为永久实例(非临时实例):
spring: cloud: nacos: discovery: ephemeral: false # 设置为非临时实例
Nacos与eureka的共同点
都支持服务注册和服务拉取
都支持服务提供者心跳方式做健康检测
Nacos与Eureka的区别
Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
临时实例心跳不正常会被剔除,非临时实例则不会被剔除
Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
六. Nacos配置管理
Nacos除了可以做注册中心,同样可以做配置管理来使用。在这一章中,我们会学习统一配置管理的相关知识,然后会学习怎么去实现配置的热更新,还有不同微服务之间,微服务不同环境之间的配置共享,最后会学习搭建一个生产环境可用的Nacos集群。
1.统一配置管理
当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我们需要一种统一配置管理方案,可以集中管理所有实例的配置。
Nacos一方面可以将配置集中管理,另一方可以在配置变更时,及时通知微服务,实现配置的热更新。
我们可以点开Nacos的控制台的配置管理的配置列表,发现配置列表是空的:
我们点击右上角的那个加号:
他会弹出一个新建配置的表单要我们填写:
到这里,第一个要填写的东西叫Data ID,其实就是配置文件的名称。但是你注意了,这个名字你不能像我们Idea里面那样都起application.yml ,因为你叫这个名字的话就有一个问题了,将来我们所有的微服务都来找Nacos管理,大家叫这个名字,那不就冲突了吗?所以我们的Data ID必须唯一,不能冲突。那怎么办呢?
微服务的名称是不冲突的吧,所以Data ID的命名方式一般是这样子的,第一部分是服务名称 (如userservice)。第二部分是我们的运行环境,我们之前不是学过环境隔离,我们的环境有开发环境和测试环境等,所以这里可以命名成dev、test或者prod。最后一部分我们写后缀名,我们写.yaml。
然后就是Group分组名称,我们不要去更改,它默认就好了。
描述就是介绍你的配置文件是干什么的。
配置格式 我们选择yaml就好(目前只支持yaml和properties这两种格式)。
最后一个是配置内容,但是并不是把项目里面application.yml的所有配置内容都粘过来,这里填的配置都是用来做热更新的配置。
注意:项目的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。
发布之后,配置列表里面就有配置了:
关于怎么读取这些配置,我们在下一小节来学习。
2.微服务拉取配置
我们在Nacos里面编写了配置列表,我们的微服务就要想办法得到这些配置了吧。那么怎样去得到呢?
我们下面先来看一下在没有Nacos服务时如何获取配置的。它的流程大概是这样子的:
首先项目启动,启动完了以后,会读取本地application.yml配置文件,读取完了以后会去创建Spring容器,而后会加载各种bean,之后就不说了,我们只关心配置这一部分。
在这里读取的是本地的yml配置文件,但现在我们多了Nacos里面的配置文件,将来我们的项目会把Nacos里面的配置与本地的配置文件做一个合并,然后再去完成容器创建和加载bean的操作。我们的项目启动的过程就会变成如下的过程:
这个时候,流程听起来很简单对不对?但是你们要注意一件事儿啊,项目在读取Nacos配置文件的时候,它需要去知道一些信息。第一,去哪读取?第二,读取谁?所以在读取Nacos配置文件的时候,得先知道它得地址吧?这个地址就在application.yml当中。但这里既然要先读取Nacos的配置,再去读取本地的yml配置,那这个Nacos地址还得用其他的办法提前知道才行啊。那有什么是比本地application.yml还要提前的?其实有,Spring里提供了一个bootstrap.yml的文件,这个文件的优先级会比application.yml的优先级要高很多。所以项目启动以后,他会先读取bootstrap.yml文件,我们只要把Nacos地址啊,文件的相关信息啊,都配置进来,那是不是就可以先完成Nacos配置的读取了,然后再和本地的application.yml结合,接着完成后续的操作。
下面演示一下具体的操作:
我们在操作做之前,先把dev那个namespace命名空间给删掉,然后配置文件配置的namespace也删掉先,,然后在public里面重新创建配置,为了方便后面的一些操作。
并且这里还有一个注意点,就是我们之前用的是最新版(2.1.0版本)的Nacos,但是最新版的Nacos与Spring Cloud兼容性并不是非常好,往往会出很多奇奇怪怪的问题。所以我们尽量使用1.4.1版本的Nacos。
下面也列出了一张Spring Cloud与其各组件的版本兼容表(最新版本用*标记)::
下表为按时间顺序发布的 Spring Cloud Alibaba 以及对应的适配 Spring Cloud 和 Spring Boot 版本关系(由于 Spring Cloud 版本命名有调整,所以对应的 Spring Cloud Alibaba 版本号也做了对应变化)
(1)引入nacos-config依赖
首先,在user-service服务中,引入nacos-config的客户端依赖:
<!--nacos配置管理依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
(2)添加bootstrap.yaml
然后,在user-service的resources目录中添加一个bootstrap.yaml文件,内容如下:
spring:
spring: application: name: userservice # 服务名称 profiles: active: dev #开发环境,这里是dev cloud: nacos: server-addr: localhost:8848 # Nacos地址 config: file-extension: yaml # 文件后缀名
这里会根据spring.cloud.nacos.server-addr获取nacos地址,再根据
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作为文件id,来读取配置。
本例中,就是去读取userservice-dev.yaml:
(3)读取nacos配置
在user-service中的UserController中添加业务逻辑,读取pattern.dateformat配置:
全部代码如下:
package com.haiexijun.user.web; import com.haiexijun.user.pojo.User; import com.haiexijun.user.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @Slf4j @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @Value("${pattern.dateformat}") private String dateformat; // 获得当前时间,通过nacos配置格式化。 @GetMapping("/now") public String now(){ return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat)); } /** * 路径: /user/110 * * @param id 用户id * @return 用户 */ @GetMapping("/{id}") public User queryById(@PathVariable("id") Long id) { return userService.queryById(id); } }
在页面访问,可以看到效果:
到这里就实现了配置管理的第二步,微服务获取Nacos中的配置了。
3.配置热更新
我们最终的目的,是修改nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新。
要实现配置热更新,可以使用两种方式:
方式一:
方式二:
使用@ConfigurationProperties注解代替@Value注解。
在user-service服务中,添加一个类,读取patterrn.dateformat属性:
package com.haiexijun.user.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @Data @ConfigurationProperties(prefix = "pattern") public class PatternProperties { private String dateformat; }
在UserController中使用这个类来代替@Value:
完整代码:
package com.haiexijun.user.web; import com.haiexijun.user.config.PatternProperties; import com.haiexijun.user.pojo.User; import com.haiexijun.user.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @Slf4j @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @Autowired private PatternProperties patternProperties; @GetMapping("now") public String now(){ return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat())); } // 略 }
更改后重启项目,我们更改Nacos的配置来测试一下热更新: