微服务注册与配置中心Nacos
本篇文章是Java微服务的扩展,以补全目前主流微服务组件Nacos的内容。
微服务回顾
单体架构:
所有功能集中在一个项目内开发,打包部署,优点是架构简单,部署成本低,但功能耦合度高。
分布式架构:
将业务模块拆分为多个独立部署、独立运行的服务项目,降低耦合度,有利于服务升级扩展,但架构设计复杂,维护成本高,适合中大型业务系统。
分布式架构主要考虑的问题:
- 服务拆分粒度,太粗会导致服务内仍有耦合,太细则会增大维护部署成本;
- 地址维护方式,服务更换地址时调用方如何感知;
- 远程调用方式,同步HTTP、RPC,异步消息队列;
- 健康状态维护,避免下游服务宕机导致连锁挂。
微服务设计原则:
- 单一职责:一个微服务只负责一个完整业务域,避免重复开发和内部耦合;
- 面向服务:服务对外仅暴露标准化远程 API,隐藏内部实现细节;
- 自治:技术独立、数据独立、部署独立;
- 隔离性强:设计容错及降级策略,避免其他服务影响。
为实现微服务功能,通常需要如下组件:
- 注册中心:注册和拉取服务信息,维护服务地址和健康状态;
- 配置中心:配置集中存储,实现配置热更新;
- 服务网关:请求路由到具体服务,实现过滤和负载均衡。
不同体系实现微服务的具体技术栈如下:
| 技术体系 | Dubbo | Spring Cloud | Spring Cloud Alibaba |
|---|---|---|---|
| 注册中心 | Zookeeper | Eureka | Nacos、Eureka |
| 远程调用 | Dubbo RPC | OpenFeign HTTP | OpenFeign / Dubbo RPC |
| 配置中心 | 无原生,对接Nacos/Apollo | Spring Cloud Config | Nacos |
| 网关组件 | 无内置,搭配Gateway/Soul | Spring Cloud Gateway | Spring Cloud Gateway |
| 监控和保护 | Hystrix | Sentinel |
一张来自Nacos官网的微服务全景图:

Nacos 安装使用
Nacos,全称 Dynamic Naming and Configuration Service 动态服务发现与配置管理服务,完成微服务中的注册中心和配置中心功能。
Nacos安装
官网:https://nacos.io/,目前提供了一键安装的命令,但目前学习过程JDK用的还是老版本,所以选择去github上安装早期版本。
到下载页:https://github.com/alibaba/nacos/releases 找个2.x版本下载,
bin目录下开启cmd,启动命令
// 单机启动,默认集群启动,缺少相关配置会卡死
startup.cmd -m standalone

启动后通过http://127.0.0.1:8848/nacos即可访问控制台,默认账号密码都是nacos。

服务注册
Java中引入依赖如下:
<!-- nacos服务发现包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<!-- 版本与springboot适配 -->
<version>2021.0.5.0</version>
</dependency>
服务注册配置
spring:
# 服务名称(Nacos服务列表显示的名字,必填)
application:
name: acgs_service
# Nacos 通用配置
cloud:
nacos:
# Nacos服务地址,多个逗号分隔 127.0.0.1:8848,192.168.1.100:8848
server-addr: 127.0.0.1:8848
discovery:
# 开启服务注册(默认true,写上更清晰)
enabled: true
启动类增加注解:@EnableDiscoveryClient
启动后可在控制台查看到服务:

服务分级存储
服务可能跨地域集群部署,如分为北京集群、杭州集群、深圳集群等,服务调用时希望尽可能调用本地的服务,防止跨集群访问,可在nacos中使用cluster-name配置集群名称。
使用方法为:
discovery:
# 集群名称
cluster-name: bj
启动多个实例的方法,复制配置后指定启动配置文件,这种方式仍然会加载application.yml主配置文件
进入服务详情可见已配置的集群

负载均衡
负载均衡策略可选轮询、随机、按权重,配置方法如下:
# 开启nacos权重负载均衡策略
loadbalancer:
nacos:
enabled: true
权重修改方式如下:

这种方式最灵活,用于按服务器性能负载均衡。
该功能同时可用于平滑升级,通过调整权重给服务器摘流,完成升级后再逐步升级权重接管新流量。
环境隔离
Nacos提供了基于环境namespace的隔离,如开发dev、测试test和生产prod环境。
前面介绍了让启动类读取指定配置文件的方法,Nacos提供了服务的分环境管理,不同命名空间的服务不可互相访问
默认public是保留空间,新建命名空间流程如下:

项目使用命名空间配置如下:
discovery:
# 命名空间,生成的命名空间id
namespace: f2617631-0ff7-4ac1-92b4-14fd49ff6f03
配置管理
该功能主要用于实现配置修改的热更新,可用于存储开关类型的配置信息(非所有配置),但Nacos 的远程配置是在项目启动后才拉取的,无法在启动时获取到配置中心的参数,因此需引入bootstrap引导文件,该文件加载时机早于application.yml,新增如下依赖:
<!-- nacos配置中心,读取远程配置必需 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- bootstrap -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.5</version>
</dependency>
在bootstrap中添加nacos相关配置,此时链路变成了:


data-id命名规则:服务名称+运行环境
配置内容,数据与:前有空格
读取方式:
@RestController
// 热更新注解,配置变化则值变化
@RefreshScope
// 批量数据读取可用下面的注解,prefix为第一个index,后续变量名data可直接对应
// @ConfigurationProperties(prefix = "acgs")
public class test {
// 读取配置中心数据
@Value("${acgs.data:未读取到配置中心数据}")
private String data;
@GetMapping("/getConfig")
public String getConfig() {
return data;
}
}
远程调用Feign
引入Feign
远程调用可使用restTemplate封装的http请求,但当参数复杂时难以维护,且代码中需维护字段与逻辑,十分不优雅。
@PostMapping("/callPost")
public String callPost(@RequestBody Map<String,Object> param) {
String url = "http://acgs_service/postApi";
// postForObject:携带JSON参数发起POST
String result = restTemplate.postForObject(url, param, String.class);
return "POST远程返回:" + result;
}
Feign提供了声明式http请求的方法,实现优雅发送http,首先引入feign:
<!-- 引入Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.5</version>
</dependency>
<!-- Feign 依赖负载均衡 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>3.1.5</version>
</dependency>
快速使用
启动类中添加注解扫描Feign:
@EnableFeignClients
远程调用接口声明服务和路径:
// value = 目标服务在Nacos注册的服务名
@FeignClient(value = "acgs_service")
public interface RemoteTestFeign {
// 对应被调用服务的接口地址,根据参数不同写法也不同
@GetMapping("/test{id}")
String getRemoteMsg(@PathVariable("id") Long id));
}
注入接口实现远程调用:
@RestController
public class FeignCallController {
@Autowired
private RemoteTestFeign remoteTestFeign;
@GetMapping("/feign/test")
public String callRemote() {
// 自动负载均衡访问acgs_service服务
return "Feign调用返回:" + remoteTestFeign.getRemoteMsg();
}
}
性能优化
Feign默认采用HttpURLConnection,这种方式没有连接池,每次远程调用新建 TCP 连接,频繁三次握手,高并发下大量 TIME_WAIT 端口占用,极易出现连接超时、端口耗尽。
通常可切换为OkHttp,首先引入依赖:
<!-- feign okhttp适配器 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
配置中开启:
spring:
cloud:
openfeign:
# 启用okhttp客户端
okhttp:
enabled: true
# 连接池全局配置
max-connections: 200
max-connections-per-route: 50
工程实践
Feign只是将实际服务与url对应的部分进行了解耦,同样要求请求方式和地址完全对应,实践中通常将Feign模块单独剥离出来,调用方引用依赖,避免耦合。

加载时可全量扫描引入
@EnableFeignClients(basePackages = {
"com.acgs.feign.client"
})
public class AcgsApplication {
public static void main(String[] args) {
SpringApplication.run(AcgsApplication.class, args);
}
}
也可以按需引入:
@EnableFeignClients(clients = {
RemoteTestFeign.class,
RemoteOrderFeign.class
})
public class AcgsApplication {
public static void main(String[] args) {
SpringApplication.run(AcgsApplication.class, args);
}
}
统一网关Gateway
引入Gateway
前面的Nacos和Feign提供了服务注册发现、配置中心及服务调用功能,Gateway组件则提供了对外的统一访问接口,具体功能如下:
- 身份认证和权限校验:拦截所有请求,校验请求携带的凭证,避免每个微服务重复开发登录校验逻辑;
- 服务路由和负载均衡:实现请求转发,屏蔽后端服务真实地址,同时可实现负载均衡和熔断降级;
- 请求限流:保护后端微服务,防止突发流量压垮业务集群。
引入Gateway的方式如下:
<!-- Spring Cloud Gateway 网关核心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
配置方法为:
# 服务基础配置
spring:
application:
name: gateway-server # 网关服务名,注册到Nacos,网关本质也是一个服务
cloud:
gateway:
# 开启服务发现自动路由(基于Nacos服务名转发 lb://服务名)
discovery:
locator:
enabled: true
lower-case-service-id: true # 服务名小写匹配
# 路由规则(两种写法:代码配置/yml配置,这里yml静态路由示例)
routes:
# 路由1:用户服务
- id: user-service # 路由唯一id
uri: lb://user-server # lb代表负载均衡,对应nacos注册服务名
predicates: # 路由断言,判断请求是否转发
- Path=/api/user/** # 匹配路径
filters: # 过滤器
- StripPrefix=1 # 去掉第一层路径 /api,转发到后端 /user/**
断言工厂
断言(Predicate)用于匹配请求,只有满足断言条件,当前路由才会生效;可多个断言组合,全部满足才匹配路由,不满足的会直接返回404。
所有断言被断言工厂Predicate Factory读取并处理,转换为具体的路由判断条件,常用断言工厂示例如下:
| 断言工厂 | 说明 | YAML配置示例 |
|---|---|---|
| Path | 根据请求URL路径匹配,支持通配符 | - Path=/api/user/** |
| Method | 根据HTTP请求方式匹配 | - Method=GET,POST |
| Query | 匹配URL请求参数,可校验参数存在或固定值 | - Query=token - Query=name,admin |
| Header | 校验请求头,支持正则匹配头部值 | - Header=Token - Header=Version,\d+ |
| Cookie | 校验请求携带指定Cookie,支持正则 | - Cookie=sid,[0-9a-z]+ |
| Host | 根据请求域名Host匹配,支持泛域名 | - Host=**.demo.com |
| RemoteAddr | 根据客户端IP网段匹配(CIDR) | - RemoteAddr=127.0.0.1,192.168.1.0/24 |
| After | 仅匹配指定时间之后的请求 | - After=2026-01-01T00:00:00.000+08:00[Asia/Shanghai] |
| Before | 仅匹配指定时间之前的请求 | - Before=2026-12-31T23:59:59.000+08:00[Asia/Shanghai] |
| Between | 匹配指定时间段内的请求 | - Between=2026-01-01T00:00:00.000+08:00[Asia/Shanghai],2026-12-31T23:59:59.000+08:00[Asia/Shanghai] |
| Weight | 灰度权重路由,同组分配流量比例 | - Weight=userGroup,8 |
过滤器 Filter
过滤器(Gateway Filter)用于处理请求和微服务的响应,可以实现添加请求头、响应头、移除请求头等操作,常用过滤器如下:
| 过滤器名称 | 功能说明 | YAML配置示例 |
|---|---|---|
| StripPrefix | 截取去掉路径前N层前缀 | - StripPrefix=1 |
| RewritePath | 重写请求URL路径,正则替换 | - RewritePath=/api/(?
|
| AddRequestHeader | 统一添加请求头传递给后端服务 | - AddRequestHeader=gateway-source,gateway-server |
| AddResponseHeader | 给前端响应新增返回头 | - AddResponseHeader=server-version,1.0 |
| RemoveRequestHeader | 删除指定请求头 | - RemoveRequestHeader=token |
| RemoveResponseHeader | 删除返回给前端的响应头 | - RemoveResponseHeader=Server |
| RequestHeaderSize | 限制请求头最大大小 | - RequestHeaderSize=5000 |
| Retry | 请求失败自动重试,支持配置重试次数、响应码 | - name: Retry args: retries:2 statuses:500,502 |
| RequestRateLimiter | 限流过滤器,限制接口QPS | - name: RequestRateLimiter args: redis-rate-limiter.replenishRate:10 |
| CircuitBreaker | 服务熔断降级,整合Sentinel/Resilience4j | - name: CircuitBreaker args: name:userBreaker fallbackUri:forward:/fallback |
| RedirectTo | 满足条件时302重定向跳转 | - RedirectTo=302,https://www.baidu.com |
| SetPath | 直接替换请求完整路径 | - SetPath=/user/getInfo |
| SetRequestHeader | 覆盖已有请求头的值 | - SetRequestHeader=token,global-token-123 |
| SetStatus | 修改返回前端的HTTP状态码 | - SetStatus=403 |
上面指定的都是局部过滤器,针对特定路由的过滤条件,也可以指定全局过滤器default-filters,所有路由统一生效,配置方法如下:
spring:
cloud:
gateway:
default-filters:
# 全局添加请求头
- AddRequestHeader=traceId,global-trace
# 统一去掉一层路径前缀
- StripPrefix=1
代码可实现GlobalFilter并重写filter方法,自定义过滤器,示例如下:
/**
* 实现Gateway过滤器核心方法
* 所有全局过滤器GlobalFilter、局部GatewayFilter都必须重写该方法
* Gateway基于WebFlux响应式,全程使用Mono/Flux异步类型,不能用同步Servlet对象
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// ServerWebExchange:请求上下文,封装本次请求Request、响应Response全部信息
// GatewayFilterChain:过滤器链,代表当前路由后续所有过滤器 + 转发后端服务逻辑
// 返回值Mono<Void>:响应式异步结果,代表本次过滤处理完成
// 前置逻辑:请求转发前执行
String path = exchange.getRequest().getPath().value();
System.out.println("访问路径:" + path);
// 放行,执行后续过滤器与路由
return chain.filter(exchange);
}
路由优先级
路由可在yml中通过order配置优先级,该值越小越优先,可指定负值,优先级一致时,default>局部>global。
示例如下:
spring:
cloud:
gateway:
routes:
# 路由1 优先级最高
- id: api-user-detail
order: 0
predicates:
- Path=/api/user/detail/**
uri: lb://user-service
# 路由2 优先级次之
- id: api-user-all
order: 1
predicates:
- Path=/api/user/**
uri: lb://user-service
自定义全局路由也可以使用注解@Order配置,示例如下:
@Order(-1) // 注解指定过滤器优先级,-1比默认0更早执行
public class AuthGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 鉴权前置逻辑
return chain.filter(exchange);
}
}
CORS跨域配置
跨域定义:
浏览器禁止发起者与服务端发生跨域ajax请求,域名、端口不同都是跨域。
路由可全局配置,也可单条配置,示例如下:
# 全局跨域
spring:
cloud:
gateway:
globalcors:
cors-configurations:
# 匹配所有请求路径
'[/**]':
allowed-origins: "https://xxx.com" # 允许来源域名
allowed-methods: GET,POST,PUT,DELETE,OPTIONS # 请求方法
allowed-headers: "*"
allow-credentials: false # 允许携带cookie,为true时allowed-origins不能为*
max-age: 360000 # 预检请求有效期,单位秒
# 统一开启跨域缓存
add-to-simple-url-handler-mapping: true
# 单路由跨域
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-server
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
# 局部CORS配置
- name: Cors
args:
allowedOrigins: http://localhost:8081
allowedMethods: GET,POST,OPTIONS
allowedHeaders: "*"
allowCredentials: false
总结
整体链路

微服务配置总文件
spring:
# 服务名称
application:
name: acgsService
# 开发环境
profiles:
active: prod
# 微服务配置
cloud:
# Nacos通用配置,注册与配置中心
nacos:
# Nacos服务地址,多个逗号分隔 127.0.0.1:8848,192.168.1.100:8848
server-addr: 127.0.0.1:8848
discovery:
# 开启服务注册(默认true,写上更清晰)
enabled: true
# 集群名称
cluster-name: bj
# 命名空间,生成的命名空间id
namespace: f2617631-0ff7-4ac1-92b4-14fd49ff6f03
# 配置中心
config:
# 配置文件后缀yml
file-extension: yaml
# 开启自动热更新(默认true,显式声明保险)
refresh-enabled: true
# Feign 性能优化池
openfeign:
# 启用okhttp客户端
okhttp:
enabled: true
# 负载均衡配置
loadbalancer:
# 启用nacos权重规则
nacos:
enabled: true
# 路由服务注册(单独服务)
gateway:
# 开启服务发现自动路由(基于Nacos服务名转发 lb://服务名)
discovery:
locator:
enabled: true
lower-case-service-id: true # 服务名小写匹配
# 路由规则(两种写法:代码配置/yml配置,这里yml静态路由示例)
routes:
# 路由1:用户服务
- id: user-service # 路由唯一id
uri: lb://user-server # lb代表负载均衡,对应nacos注册服务名
predicates: # 路由断言,判断请求是否转发
- Path=/api/user/** # 匹配路径
filters: # 过滤器
- StripPrefix=1 # 去掉第一层路径 /api,转发到后端 /user/**
更多推荐

所有评论(0)