Nacos注册中心
作用:服务注册与发现。
1.服务注册(Service Registration):
注册流程:
微服务启动时,向Nacos Server注册自身信息(包括 IP、端口、服务名、健康状态、元数据等)。
注册方式通常通过客户端心跳机制(默认每5秒发送一次心跳)维持活性。
支持临时实例(Ephemeral,默认,依赖心跳)和持久实例(Persistent,需主动注销)。
2. 服务发现(Service Discovery)
消费者获取服务列表:
服务消费者(如通过RestTemplate、OpenFeign或Dubbo)从Nacos查询可用的服务提供者列表。
本地缓存机制:消费者默认每10秒从Nacos拉取一次最新服务列表(可通过spring.cloud.nacos.discovery.cache-refresh-interval 调整)。
动态更新:当服务列表变化(如节点下线),Nacos 会推送变更事件(基于 UDP 或长轮询),但消费者实际感知依赖缓存刷新周期。
服务发现 API:Nacos提供HTTP接口(如 /nacos/v1/ns/instance/list),但开发者通常直接使用SDK(如NacosDiscoveryClient)。
3. 服务下线与剔除(Deregistration & Eviction)
主动下线:
服务正常关闭时,应调用ShutdownHook或NacosServiceRegistry.deregister()主动注销。
若未主动注销,Nacos 依赖心跳超时机制判断节点失效。
被动剔除:
心跳超时:若Nacos15秒(默认)未收到心跳,将实例标记为不健康(UNHEALTHY)。
彻底剔除:超过30秒(默认)未恢复心跳,Nacos会从服务列表删除该实例。
消费者延迟:由于本地缓存机制,消费者最长可能在10秒(缓存周期) + 网络延迟后感知剔除。
4. 健康检查(Health Check)
客户端心跳检测(默认):
微服务主动向Nacos发送心跳(如每5秒一次)。
适用于临时实例(Ephemeral),心跳停止即视为下线。
服务端主动探测:
Nacos Server通过TCP端口探测或HTTP健康检查接口判断服务状态。
适用于持久实例(Persistent),需配置 spring.cloud.nacos.discovery.health-check-url。
将微服务注册到Nacos Server:
引入依赖:
<!-- nacos 服务注册与发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
在yml中配置注册中心地址
spring:
application:
name: mall-order #微服务名
cloud:
nacos:
discovery:
server-addr: 192.168.65.103:8848 #注册中心地址
namespace: 6cd8d896-4d19-4e33-9840-26e4bee9a618 #环境隔离
之后控制台启动服务,面板出现对应的服务。
接入Nacos配置中心:
1.引入依赖:
<!-- nacos 配置中心 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
2.创建bootstrap.yml文件,添加配置中心的配置:
spring:
application:
name: tulingmall-member #微服务的名称
cloud:
nacos:
config:
serverAddr: 192.168.65.103:8848 #配置中心的地址
namespace: 6cd8d896-4d19-4e33-9840-26e4bee9a618
# dataid 为 yml 的文件扩展名配置方式
# `${spring.application.name}.${file-extension:properties}`
file-extension: yml#通用配置
shared-configs[0]:
data-id: tulingmall-nacos.yml
group: DEFAULT_GROUP
refresh: true
shared-configs[1]:
data-id: tulingmall-redis.yml # redis 服务集群配置
group: DEFAULT_GROUP
refresh: true
#profile 粒度的配置
#`${spring.application.name}-${profile}.${file-extension:properties}`
profiles:
active: devprofiles指定开发环境;spring:application:name指定项目名,serverAddr指定注册中心地址,file-extension指定yml后缀,然后会寻找6cd8d896-4d19-4e33-9840-26e4bee9a618下的tulingmall-member.yml,如果本地有,bootstrap.yml会替换本地application.yml配置,tulingmall-nacos.yml与tulingmall-redis.yml会覆盖本地,需要改为自己的。
3.添加微服务配置和公共的配置到nacos配置中心
openfeign实现微服务间的调用
引入依赖:
<!-- 服务远程调用 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
编写调用接口+@FeignClient 注解,指定要调用的微服务及其接口方法:
@FeignClient(value = "tulingmall-coupons",path = "/coupon") public interface CouponsFeignService { @RequestMapping(value = "/list", method = RequestMethod.GET) @ResponseBody CommonResult<List<SmsCouponHistory>> list(@RequestParam(value = "useStatus", required = false) Integer useStatus ,@RequestHeader("memberId") Long memberId); }
启动类添加@EnableFeignClients注解,开启openFeign远程调用功能:
@SpringBootApplication @EnableFeignClients public class TulingmallMemberApplication { public static void main(String[] args) { SpringApplication.run(TulingmallMemberApplication.class, args); } }
测试,发起远程服务调用:
@Autowired private CouponsFeignService couponsFeignService; @RequestMapping(value = "/coupons", method = RequestMethod.GET) public CommonResult getCoupons(@RequestParam(value = "useStatus", required = false) Integer useStatus ,@RequestHeader("memberId") Long memberId){ // 通过 openfeign 从远程微服务 tulingmall-coupons 获取优惠券信息 return couponsFeignService.list(useStatus, memberId); }
开启 openfeign 日志配置
@Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
如果日志不显示,可以在 yml 中通过 logging.level 设置日志级别
logging: level: com.tuling: debug
请求头需要memberId情况
实现RequestInterceptor,添加请求头参数用于传递memberId
@Slf4j public class HeaderInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if(null != attributes){ HttpServletRequest request = attributes.getRequest(); log.info("从 Request 中解析请求头"); template.header("memberId",request.getHeader("memberId")); } } } # FeignConfig.java 中添加拦截器配置 @Bean public RequestInterceptor requestInterceptor() { return new HeaderInterceptor(); }