ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

微服务三件套:网关路由、JWT认证与Nacos配置管理实战

微服务三件套:网关路由、JWT认证与Nacos配置管理实战 “微服务02请求路由、身份认证、配置管理”这个标题如果你是按顺序做微服务改造看到“02”应该能猜到前面还有一个系列起步篇。我这边接着做第二部分把服务拆完之后最头疼的不是写业务而是服务入口怎么统一、每个服务怎么确认调用方身份、一堆配置文件散落在各个工程里怎么管理。这篇文章就是围绕这三件事展开的结合我近一年在Spring Cloud生态里做落地的经验把请求路由网关、身份认证统一鉴权、配置管理配置中心讲清楚。三种能力的选型逻辑、实现方式、坑点会一起讲含实际可复用的配置和代码片段供正在搭微服务骨架的团队参考。先说个整体感受这三个问题在单体应用时代几乎不存在服务都在一个进程里过滤器改一改就能解决认证路由就是Nginx转发一下配置就是application.yml改改。一旦拆成微服务问题立刻变复杂服务数量多了入口必须是统一的不然客户端得记几十个地址每个服务如果自己做登录校验逻辑重复、策略不一致、改一处全得跟着改配置散落的话改一个参数要重新打包发布运维会疯。所以微服务三件套——网关、认证中心、配置中心成了绝大多数团队的标配。1. 整体设计与思路拆解1.1 请求路由为什么必须用网关收口微服务拆分之后最常见的一个现象是服务端口各异订单服务8081、用户服务8082、支付服务8083前端或者App端调接口的时候得把每个服务的地址硬编码进去。一旦服务扩容、迁移或者下线前端就得跟着改这个维护成本会随着服务数量指数上升。网关就是来解决这个问题的所有外部请求统一打到网关由网关根据路径、请求头、方法等条件把请求转发到对应的下游服务。网关的核心价值不只是转发。它处在流量的咽喉位置天然适合做横切逻辑统一鉴权、限流、灰度、日志、跨域处理、请求头清洗。我在设计的时候把网关定位成“只做分流和横切不做业务”。业务逻辑千万不能往网关里塞否则网关会越来越重最后变成一个新的单体应用把微服务拆分的意义全毁了。技术选型上我用了Spring Cloud Gateway而不是Zuul 1.x。原因有几个Gateway基于Spring WebFlux和Reactor异步非阻塞模型性能和并发能力明显优于Zuul 1的Servlet模型Zuul 1已经进入维护模式社区活跃度低Gateway和Spring Cloud的整合更自然配置方式也更灵活支持路由谓词工厂和过滤器工厂的组合写法。当然如果你的团队对WebFlux不熟担心踩坑Zuul 2或者自研Nginx转发也是可以接受的替代方案但大部分新项目我更推荐Gateway。路由设计的核心原则是“路径前缀与服务名的映射关系要清晰”。比如我的用户服务路径统一带/api/user/**前缀订单服务带/api/order/**前缀网关通过Path谓词匹配前缀后用StripPrefix过滤器把前缀去掉再转发到下游。这样一来下游服务不需要感知网关的存在接口路径保持纯粹网关只负责“按前缀识别目标”。1.2 身份认证统一认证而不分散校验微服务里认证最忌讳的是每个服务都写一套登录校验逻辑。如果每个服务都去解析Token、查数据库校验用户状态不仅代码重复还可能因为不同服务对Token的解析规则不一致出现一个请求在一个服务里通过、另一个服务里拒绝的诡异问题。所以核心思路是认证逻辑收敛到一个地方其他服务只做验签和取用户信息。我的方案是引入独立的认证中心Auth Server专门负责登录、颁发Token、刷新Token、吊销Token。其他业务服务收到请求时从请求头里取出Token做验签和解析获取用户身份后直接使用不再重复校验。Token我用的JWTJSON Web Token因为它自带签名和负载服务端无状态不需要像传统Session那样在服务端存储会话记录天然适配微服务环境。这里有三个细节值得展开。第一JWT签名密钥必须是配置中心统一管理不能散落在各个服务的配置里否则换密钥的时候要改十几个服务。第二JWT虽然无状态但吊销是个硬伤——用户退出了Token在有效期里还是能用的。我引入了Token黑名单机制认证中心维护一份短期缓存5分钟粒度存放被吊销的Token ID网关和下游服务解析时校验一下黑名单平衡了无状态和可控性。第三网关层面做一层粗粒度拦截只校验Token是否有效细粒度的权限控制角色、菜单、按钮由各服务内部决定。这样网关压力不会太大各服务的权限策略又能保持灵活。1.3 配置管理把散落的配置收拢起来拆成微服务之后每个服务有自己的一套配置。环境一变开发、测试、生产配置就得跟着改。如果还是沿用每个服务一个application.yml的方式改一个数据库地址要进服务器改文件、重启进程几十个服务轮一遍运维成本高得吓人。而且配置没法审计、没法回溯出了问题都不知道是谁在什么时候改的。配置管理的思路就是把配置从服务进程里剥离出来放到一个集中的配置中心服务启动时从配置中心拉取配置运行期间还可以监听配置变化实现动态刷新。我用的是Nacos理由很简单它同时具备服务注册发现和配置管理两个能力一套体系解决服务治理和配置管理两个问题运维上少维护一个组件控制台自带编辑、历史版本、回滚能力团队上手成本低配置变更支持长轮询通知动态刷新做得成熟。配置管理的边界也要划清楚核心业务逻辑里的开关参数应该放进配置中心但JVM参数、启动参数、本地开发环境不需要频繁变动的配置该留在本地还是留在本地。我见过有团队把所有配置全部挪进配置中心结果本地启动服务也依赖配置中心环境没网络就连不上开发效率大幅下降。配置中心管的是“会变的、影响行为的、跨环境的配置”不是所有配置。2. 核心细节解析与实操要点2.1 请求路由网关选型与路由规则设计接着讲网关的落地细节。我用Spring Cloud Gateway先给出一份可以直接使用的核心配置spring: application: name: gateway-service cloud: gateway: server: port: 8080 discovery: locator: enabled: true lower-case-service-id: true routes: - id: user-service-route uri: lb://user-service predicates: - Path/api/user/** filters: - StripPrefix2 - id: order-service-route uri: lb://order-service predicates: - Path/api/order/** filters: - StripPrefix2 - id: auth-service-route uri: lb://auth-service predicates: - Path/api/auth/** filters: - StripPrefix2这段配置核心理解三点。第一uri: lb://user-service表示走客户端负载均衡从注册中心拿user-service的实例列表路由到其中一个实例上去。如果没有注册中心也可以写死uri: http://1.2.3.4:8081但那样就丢了微服务的动态扩缩容优势。第二StripPrefix2把前两段路径前缀去掉比如/api/user/login会被改写成/login再转发。第三路由的id要保证全局唯一并且配置顺序上有讲究Gateway按照配置顺序匹配第一个命中的路由所以更具体的路由要放前面更通用的兜底规则放后面。路由谓词不只是Path一种常用的还有Method按HTTP方法匹配、Header按请求头匹配、Query按参数匹配。我实际用得最多的是Path组合Method。比如某些接口只允许POST访问就在谓词里加一个MethodPOST的限制网关直接拦掉不合法的请求减少下游服务无效流量。还有一个容易被忽略的点跨域配置。微服务拆分后前端经常直连网关浏览器跨域问题集中爆发。在Gateway层统一配置CORS比在每个业务服务里配都更合理spring: cloud: gateway: globalcors: cors-configurations: [/**]: allowed-origins: https://admin.example.com allowed-methods: * allowed-headers: * allow-credentials: true注意allow-credentials: true时allowed-origin不能写*必须写具体域名否则浏览器会拦截。这个坑我踩过两次每次都是前端反馈“明明带了凭证请求还是失败”一看控制台全被CORS策略拦下来了。2.2 身份认证JWT方案与内网信任边界身份认证的具体实现我以Spring Security JWT为例拆解。认证中心负责签发Token核心数据结构如下{ sub: user-123456, name: zhangsan, roles: [admin, operator], iss: auth-center, iat: 1710000000, exp: 1710086400, jti: 8a9f3c2d-1b4e-4f8a-9c3d-2e6f1a9b7c4d }JWT的payload里我放了用户IDsub、用户名、角色列表、签发者iss、签发时间iat、过期时间exp、唯一IDjti。其中jti用来关联黑名单需要吊销某个用户的所有Token时可以批量把该用户对应会话里的jti加入黑名单。角色列表放在Token里有个好处——网关或者各服务鉴权时不用每次都查数据库直接看roles字段就能判断粗粒度权限缺点是角色变更后要等Token过期才生效所以角色信息变更频繁的系统不适合把角色放Token里这种情况建议Token只放用户ID角色信息走一次Redis缓存。请求链路是这样的客户端调/api/auth/login拿到Token之后的请求在Header里带Authorization: Bearer token。网关的GlobalFilter先解析Token验证签名和过期时间通过后把用户ID、用户名放入请求头转发给下游。下游服务写一个UserContextHolder从请求头里取用户信息塞进ThreadLocal里业务代码直接用。这样做的好处是下游服务不需要自己解析Token处理逻辑收敛在公共组件里。签名算法我选了RS256也就是非对称加密。认证中心用私钥签名各服务用公钥验签。为什么不用HS256对称加密因为HS256用同一个密钥做签名和验签在微服务架构里密钥要分发给所有服务一旦某个服务被攻破密钥泄露攻击者就能自己伪造Token。RS256的公钥可以公开私钥只存在认证中心泄露面小得多。哪怕某个服务被攻破攻击者也拿不到私钥伪造不了Token。这个安全考虑在初学阶段很容易被忽略。内网信任边界是另一个重要话题。微服务之间调用有时候并不都走网关比如用户服务调订单服务如果每次都绕去网关流量绕一圈效率不高还可能形成网关瓶颈。这时服务间直接调用就需要一个信任机制要么服务间用mTLS双向TLS建立信任要么携带一个服务身份证明比如内部Token。我采取的是核心敏感接口必须走网关一般的服务间调用用一个独立的服务账号Token由认证中心发给服务实例有效期短且只能访问指定的服务白名单。这样既避免了网关成为唯一瓶颈也保证了服务间调用的基本安全。2.3 配置管理Nacos的命名空间与动态刷新再来说配置中心。我用的Nacos落地时重点关注命名空间、分组、Data ID、格式、动态刷新这五个要素。命名空间我用它来隔离环境dev、test、prod三个命名空间每个环境一套配置互不干扰。配置通过一个spring.cloud.nacos.config.namespace参数指定启动的时候按环境加载。注意同一个Data ID在不同命名空间下是允许重复的所以命名空间是隔离的第一层闸门。实际配置举一个典型例子比如订单服务需要读数据库连接# Data ID: order-service.yaml, Group: DEFAULT_GROUP spring: datasource: url: jdbc:mysql://10.0.0.8:3306/order_db?useSSLfalse username: order_user password: encrypt{your-encrypted-pwd} redis: host: 10.0.0.15 port: 6379 rocketmq: name-server: 10.0.0.20:9876密码我建议用加密方式存储不要明文。Nacos可以接入加密插件或集成KMS做配置加密虽然会增加一点复杂度但生产环境我强烈建议做因为配置中心权限一旦被撞库或泄露明文密码会造成二次损失。动态刷新这里有一个关键点ConfigurationProperties修饰的Bean默认不会自动刷新需要配合RefreshScope而像数据库连接池这类组件即使RefreshScope标注了new一个连接池也需要时间期间可能有请求打到旧的连接池上。所以动态刷新要分场景对业务开关、线程池参数、超时时间这类轻量配置用RefreshScope就可以对数据源、连接池这类重量级组件不要指望动态刷新秒级生效要么重启服务要么使用中间件的动态数据源方案比如ShardingSphere的动态数据源或者干脆把这类配置视为“变更时需重启”的配置在发布流程里做切换。配置变更的发布时机也很讲究。我推荐的做法是测试环境改配置 - 验证 - 生产环境灰度发布 - 观察日志和指标 - 全部生效。配置中心的操作日志要及时归档和代码提交一致形成审计链条。Nacos控制台的命名空间列表、配置列表、历史版本、监听查询页面要定期检查一遍确保没有“僵尸配置”服务已经下线、配置还在占用资源这样排查问题时背景噪音会少很多。3. 实操过程与核心环节实现3.1 环境准备从零启动这套微服务骨架如果是从头演示我会建议按这个顺序搭建先搭Nacos注册中心和配置中心再建认证中心然后建两个业务服务用户服务、订单服务最后建网关。顺序有讲究先搭基础设施服务启动时才不至于报“找不到配置中心”这类低级错误。我本地的版本组合给大家一个参考Spring Boot 2.7.18Spring Cloud 2021.0.9Spring Cloud Alibaba 2021.0.5.0Nacos 2.2.3。为什么特意标注版本因为Spring Cloud和Spring Cloud Alibaba的版本兼容性是个老大难网上很多教程用的是老版本直接搬过来很容易因为依赖冲突启动失败。上面这组版本是我验证过的稳定组合JDK 8和JDK 11都能跑JDK 17需要额外适配建议先用JDK 11。Nacos的启动很简单解压后单机模式运行cd nacos/bin sh startup.sh -m standalone启动后访问控制台创建一个命名空间比如dev。然后添加一个Data ID为gateway-service.yaml的配置Group用DEFAULT_GROUP。这里贴一个我在生产环境实际用过的网关配置片段加了限流和超时spring: cloud: gateway: httpclient: connect-timeout: 3000 response-timeout: 5s default-filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 20 redis-rate-limiter.burstCapacity: 40 key-resolver: #{clientAddressKeyResolver} routes: - id: user-service uri: lb://user-service predicates: - Path/api/user/** filters: - StripPrefix2 - name: Retry args: retries: 2 statuses: BAD_GATEWAY, GATEWAY_TIMEOUT限流用的RequestRateLimiter是Redis令牌桶实现replenishRate是每秒补充的令牌数burstCapacity是桶容量keyResolver用来确定限流维度。我把限流键设为客户端IP防止单个IP打爆服务。注意这里限流只针对某些核心路由加上就好不要全局都加否则正常业务波动也会被误伤。3.2 搭建网关服务路由、过滤器、跨域一次配齐网关项目本身很简单依赖只需要spring-cloud-starter-gateway、nacos-discovery、nacos-config外加一个redis-reactive做限流。pom里我加的是dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-config/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis-reactive/artifactId /dependency启动类就是标准的SpringBootApplication。重点讲GlobalFilter的写法我通常把它做成一个独立的过滤器类放在gateway服务里专门做Token校验和请求头清洗Component public class AuthFilter implements GlobalFilter, Ordered { Autowired private TokenValidator tokenValidator; Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 白名单直接放行比如登录接口、健康检查接口 ServerHttpRequest request exchange.getRequest(); String path request.getURI().getPath(); if (WHITE_LIST.contains(path)) { return chain.filter(exchange); } String token extractToken(request); if (StringUtils.isBlank(token)) { return unauthorized(exchange); } // 解析Token成功后把用户信息写入请求头 try { JwtClaims claims tokenValidator.parse(token); ServerHttpRequest mutatedRequest request.mutate() .header(X-User-Id, claims.getUserId()) .header(X-User-Name, claims.getUsername()) .header(X-User-Roles, claims.getRoles()) .build(); return chain.filter(exchange.mutate().request(mutatedRequest).build()); } catch (Exception e) { return unauthorized(exchange); } } Override public int getOrder() { // 数值越小优先级越高确保认证过滤器最先执行 return -100; } }这里面有一个细节白名单路径要留得谨慎。我见过有人把/api/auth/**整个放进白名单结果login接口是放行了可其他需要认证的接口也漏过去了。正确做法是白名单只放开真正的匿名接口比如登录、注册、验证码、健康检查别整段接口路径都放开。还有一点Gateway的GlobalFilter里不要写重量级逻辑。比如解析Token需要调用远程服务那就完了——每个请求都等一次RPC延迟受不了。Token校验必须做到本地可验本地缓存放公钥解析JWT只做签名验证和过期时间判断不查数据库。如果有账号冻结、被踢下线这种强一致需求再做黑名单检查但黑名单命中率通常很低用本地缓存Redis定期同步即可不需要每次请求都打Redis。跨域配置上面已经提过记得把allowed-origins写成精确域名别图省事用*。另外如果网关启用了CORS下游服务就不需要再配CORS了否则会出现重复响应头部分浏览器行为会很奇怪。3.3 接入认证中心签发、验签、黑名单一条龙认证中心的搭建我分三个模块登录接口、签发Token、验签/黑名单接口。登录接口接收用户名密码校验通过后生成JWT并加一个会话记录到Redis。签发的核心代码public String issueToken(User user) { Date now new Date(); Date expiry new Date(now.getTime() JWT_TTL); String jti UUID.randomUUID().toString(); // 用私钥签名 RSAPrivateKey privateKey loadPrivateKey(); Algorithm algorithm Algorithm.RSA256(null, privateKey); String token JWT.create() .withSubject(user.getId()) .withClaim(name, user.getUsername()) .withClaim(roles, user.getRoles()) .withIssuer(auth-center) .withIssuedAt(now) .withExpiresAt(expiry) .withJWTId(jti) .sign(algorithm); // 记录会话用于后续黑名单和会话管理 redisTemplate.opsForValue().set( session: jti, user.getId(), JWT_TTL, TimeUnit.SECONDS); return token; }这里加了一个Redis会话记录用途有两个一是支持黑名单吊销二是支持“一个用户多处登录”的会话管理按业务需要做踢人下线。如果只是简单的无状态JWT这一步可以省但结合后续需求你会发现有了会话记录后续做审计、做主动下线都游刃有余。验签接口提供给网关和各服务。设计时我没让每个服务每次请求调认证中心HTTP接口验签而是提供一个公共的Starterjar包里面封装验签逻辑。各服务引入Starter服务启动时一次性从认证中心拉取公钥缓存到本地之后验签完全本地计算不走RPC。这样可以控制延迟因为一次登录请求后续所有操作都要过验签任何额外网络开销都会被放大。黑名单机制用Redis的一个Set存被吊销的jti过期时间和Token剩余有效期一致。当用户退出登录时认证中心把jti写进黑名单Set。验签时除了验签名和过期时间再查一下本地缓存中的黑名单集合。因为黑名单数据量不大可以做1分钟级本地缓存减少Redis压力。3.4 配置中心落地多环境、动态刷新、灰度发布配置中心的接入比想象中简单但容易出问题的点在于各种参数没配对。这里给出业务服务接入Nacos配置必须注意的几点。第一bootstrap.yml要加载Nacos地址因为配置中心的信息要在应用启动早期就准备好spring: application: name: order-service profiles: active: dev cloud: nacos: config: server-addr: 10.0.0.3:8848 namespace: dev-namespace-id group: DEFAULT_GROUP file-extension: yaml discovery: server-addr: 10.0.0.3:8848 namespace: dev-namespace-id第二共享配置要合理规划。业务服务里有一些公共配置比如日志级别、Redis地址、MQ地址没必要每个服务单独配一份。Nacos支持shared-configs把公共配置抽出来。我的做法是抽两个公共配置一个放“全局公共配置”所有服务共享一个放“环境公共配置”同一环境下的所有服务共享。这样改一个Redis地址只需要改公共配置所有服务同时生效。第三配置变更是要区分敏感级别的。数据源、接口地址这些属于高影响配置我建议在Nacos控制台里开启“发布前检查”甚至加人工确认环节。业务开关、日志级别这种低风险配置可以直接通过控制台即时发布。团队里要约定好哪些配置变更需要走发布单审批不然一个不小心把生产数据源地址改了影响很大。第四动态刷新的代码实践。业务代码里读配置我更推荐用ConfigurationProperties批量注入而不是Value因为前者有类型安全多个字段可以一把刷。举例Component ConfigurationProperties(prefix order.timeout) RefreshScope public class OrderTimeoutProperties { private int payment 30; private int cancel 5; // getter/setter... }配置中心里放order: timeout: payment: 30 cancel: 5改动配置后保存Nacos发布会推送变更给客户端OrderTimeoutProperties自动刷新业务代码下次调用就拿新值。这里注意一点RefreshScope修饰的Bean会被代理如果业务代码在构造器里就引用了这个Bean的字段可能拿到的还是旧值。正确用法是在需要读取值的方法里才调用getter。这个坑初次实践经常遇到我调试了很久才发现是初始化顺序的问题。4. 常见问题与排查技巧实录4.1 路由不生效从谓词、过滤器到负载均衡逐层排查路由不生效是最常见的问题表现形式多种多样要么请求404要么网关直接返回503要么请求到了网关但转发到错误服务。我一般按四个层次排查。第一层看路由是否匹配。用网关日志或直接访问网关路径如果返回404说明路径谓词没匹配上。最常见的坑是前缀写错了。比如下游接口是/user/list网关配置StripPrefix2但请求路径是/api/user/list转发后变成/list这和下游/user/list就不匹配了。StripPrefix的数值要数清楚多剥一层、少剥一层结果天差地别。第二层看服务是否注册成功。lb://user-service这种写法依赖注册中心如果服务没有注册到指定的namespace或者注册的服务名和路由里的服务名不一致网关就会报503或No servers available。排查时打开Nacos控制台找到服务列表确认服务是否在线、健康状态是不是UP再比对路由里的lb前缀和实际服务名大小写。Gateway的lower-case-service-id: true配合服务名全小写能省掉不少大小写引发的破事。第三层看过滤器是否拦截。如果请求能到服务但返回401那多半是认证过滤器的逻辑有问题。我遇到过某次写死了一个白名单前缀新加的接口路径在白名单匹配规则里命中了“前缀相同就放行”的条件结果新接口跳过了认证直接被业务服务调用这个漏洞因为是逻辑bug排查了很久才定位。第四层看负载均衡的实例数。如果某个服务有多个实例部分实例挂掉了但注册中心还没来得及剔除就会出现间歇性的503。这个要结合注册中心的健康检查参数调优把过期时间缩短及时剔除不健康的实例。4.2 认证问题Token过期、时钟偏移、公钥不匹配认证环节的坑我把典型的几条列成速查表现象可能原因处理方式请求401日志提示签名不合法网关和各服务验签公钥不一致检查公钥推送链路确认是否同一个认证中心签发重启服务时是否从配置中心拉到了新公钥请求401提示令牌过期Token过期时间太短调整签发时TTL或放到Redis里的会话刷新机制实现长会话续期请求200但业务拿不到用户ID网关过滤器的请求头未传递确认过滤器有没有exchange.mutate()重新设置请求头下游有没有读取正确的Header名刷新Token突然大面积失效认证中心重启导致私钥重新生成私钥必须持久化存储不能用内存生成重启后签名验证会全部失败这里最让我头疼的是时钟偏移问题。因为JWT的iat和exp都是Unix时间戳如果服务所在的机器时钟不一致A服务认为Token还没过期B服务认为已经过期就会出现同一个请求在不同服务上结果完全不同的诡异现象。尤其跨机房部署时NTP同步做不好时间差能达到几十秒。解决办法有两层一是确保所有服务部署后统一用NTP同步时间二是签发Token时对exp留一个15秒到30秒的“宽容窗口”校验时允许30秒以内的过期偏差。还有一个冷门但值得写的RS256的公钥分发。我最初是把公钥放在配置中心的公共配置里后来发现换公钥的时候所有服务要同时更新更新期间新旧公钥交替出现验签失败的窗口。后来改成公钥从认证中心拉取服务启动时拉一次、每小时刷新一次这样换公钥的窗口从“所有服务手动操作”缩短到“一个小时内自动收敛”。4.3 配置管理动态刷新不生效、配置中心连不上、配置被覆盖配置管理遇到的问题最典型的是三种。第一种RefreshScope修饰了但配置不刷新。多半是配置中心的文件后缀和本地的byte-array配置没对应上。Nacos配置的file-extension要和你Data ID的后缀一致比如order-service.yaml那么file-extension就写yaml。如果Data ID是order-service.propertiesfile-extension又配成yamlNacos读到的内容解析失败直接忽略配置。检查这个很容易控制台看监听查询里有没有对应的Data ID有就说明拉到了没有就是配置中心连接问题。第二种配置中心连不上导致启动失败。Nacos 2.x的客户端启动时会先拉配置拉不到配置会跳过还是报错取决于配置spring.cloud.nacos.config.import-check.enabled的设置。我开发环境遇到过Nacos挂掉之后服务还能启动用了缓存但生产环境配置了严格检查之后Nacos挂掉服务就起不来。这个从高可用角度讲我建议配置中心做集群部署至少三个节点NACOS自身的集群搭建注意端口和数据库配置服务端的-Dnacos.standalonefalse与集群模式参数要配对好。第三种配置被莫名覆盖。这个现象很隐蔽我在Nacos控制台改了配置刷新后生效了但过了一段时间配置又变回旧值。原因是bootstrap.yml里除了config还有discovery里的同名配置造成冲突。Nacos对不同来源的配置加载顺序有默认规则如果你本地application.yml里也写了同名配置它的优先级会高于Nacos里的同名配置。定位方法很简单先删掉本地配置里的同名项再在Nacos控制台配置确保一致性。我还要提醒一点本地配置和Nacos配置里同名的项要尽量避免就算Nacos失效本地也不会出现“幽灵配置”悄悄顶替的问题。收尾一点经验沉淀写到这里回过头看这套“请求路由、身份认证、配置管理”的组合拳我对微服务落地最深的体会是不追求把所有能力堆在技术上而是把“边界”划清楚。网关分担横切逻辑但不过度膨胀认证中心统一签发和验签但把实时校验控制在合理范围配置中心收拢配置但不搞成所有配置的集中营。三件套配合好服务数量增加时运维压力不会线性上升团队新增一个微服务时只需要“加一个路由注册到Nacos引入公共认证组件抽取自己的配置”四步就能接入。毕竟微服务的本质不是技术炫技而是让组织和系统具备快速变化的能力。希望这篇文章能给正在搭骨架的团队一些参考少踩几个我反复踩过的坑。
返回列表