ARTICLE DETAIL

资讯详情

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

TongWeb上部署Spring Cloud微服务:Nacos注册与Gateway路由避坑指南

TongWeb上部署Spring Cloud微服务:Nacos注册与Gateway路由避坑指南 1. 项目缘起与整体架构拆解TongWeb 上跑微服务这事我在几个项目里都碰过。第一次接触的人往往会觉得别扭——TongWeb 是国产应用服务器Spring Cloud 那一套是围绕 Tomcat、Netty 这些组件长出来的生态两者凑一块注册和路由这两块最容易出问题。这篇就把我在 TongWeb 上部署 Spring Cloud 微服务、接 Nacos 做注册配置、用 Spring Cloud Gateway 做网关路由的完整过程拆开讲重点放在那些文档里不写、但实际一跑就报错的地方。先说清楚这套组合到底解决什么问题。TongWeb 作为应用服务器负责承载每个微服务实例的 Servlet 容器运行环境Nacos 同时承担注册中心和配置中心的角色服务实例启动后把自己的地址注册进去网关从 Nacos 拉取服务列表做动态路由Spring Cloud Gateway 作为统一入口把外部请求按规则转发到后端各个微服务。这套架构的价值在于服务上下线对网关透明配置变更不用重启运维侧只需要关注 Nacos 和网关两个点。适合谁来参考如果你手上是标准的 Tomcat 加 Nacos 加 Gateway 组合这篇里大部分内容你也能用但 TongWeb 特有的类加载机制、端口管理、部署包结构会带来额外坑点我会单独标出来。如果你完全没接触过 Nacos 和 Gateway建议先把 Nacos 装起来跑通单机模式再回来看 TongWeb 集成的部分不然问题会叠在一起排查起来很痛苦。整体设计上我采用的方案是Nacos 独立部署不用内嵌TongWeb 以独立实例方式部署每个微服务Gateway 也跑在 TongWeb 上。为什么不把 Nacos 内嵌到应用里因为内嵌模式在 TongWeb 这种非标准 Spring Boot 内嵌容器的环境里生命周期管理会很乱Nacos 的启动和 TongWeb 的启动顺序、类加载器隔离都会互相干扰。独立部署虽然多了一个进程但边界清晰出问题好定位。提示TongWeb 的版本差异对类加载影响很大建议先确认你用的是哪个大版本不同版本对spring-boot-loader和ClassLoader的处理策略不一样后面配置会提到具体差异。2. Nacos 注册中心在 TongWeb 环境下的接入要点2.1 为什么 TongWeb 上注册 Nacos 容易失败标准 Spring Boot 应用打成的可执行 jar 里Spring Boot 有自己的LaunchedURLClassLoaderNacos 客户端在启动时通过这个类加载器加载配置和注册逻辑一切顺理成章。但 TongWeb 部署微服务时通常是把应用打成 war 包或者以 exploded 目录形式部署类加载器变成了 TongWeb 自己的WebAppClassLoader。Nacos 客户端里有些类比如com.alibaba.nacos.client.naming下的部分实现依赖线程上下文类加载器TCCL去加载 SPI 实现如果 TCCL 指向的不是应用类加载器就会报ClassNotFoundException或者 SPI 找不到实现。我实测下来最常见的报错是启动时日志里出现NacosException: failed to req API后面跟着一长串类加载相关的堆栈或者干脆注册不上但也不报错Nacos 控制台里看不到实例。这时候不要急着去改 Nacos 服务端配置先确认客户端这边的类加载器问题。2.2 注册配置的关键参数与实操在 TongWeb 上接入 Nacosbootstrap.yml或者application.yml里的配置和标准 Spring Boot 基本一致但有几个参数必须显式指定不能靠默认值。spring: application: name: order-service cloud: nacos: discovery: server-addr: 192.168.1.100:8848 namespace: dev group: DEFAULT_GROUP register-enabled: true heart-beat-interval: 5000 heart-beat-timeout: 15000 ip-delete-timeout: 30000这里heart-beat-interval和heart-beat-timeout我特意调过。默认值在 TongWeb 环境下有时候会因为容器线程池调度延迟导致心跳超时实例被误判下线。把心跳间隔从默认的 5 秒保持但超时从 15 秒放宽到 15 秒以上我用的 15000 毫秒给 TongWeb 的线程调度留出余量。ip-delete-timeout设成 30000意思是实例下线后 30 秒才从 Nacos 列表里彻底删除避免网关侧还在转发已经停掉的实例。namespace这个参数特别容易踩坑。Nacos 的 namespace 用的是命名空间 ID不是命名空间名称。你在控制台创建命名空间时它会生成一个 UUID 作为 ID配置里必须填这个 UUID填名称是无效的而且不会报错只是注册到默认的 public 命名空间去了。我见过有人排查了半天为什么实例不在预期命名空间最后发现就是填了名称而不是 ID。2.3 TongWeb 类加载器隔离的绕行方案如果确认是类加载器导致 Nacos 客户端初始化失败有两个处理方向。第一个方向是在 TongWeb 的部署描述里调整类加载策略把 Nacos 相关的包设置为父类加载器优先或者应用类加载器优先具体在 TongWeb 控制台的类加载配置里把com.alibaba.nacos和com.alibaba.spring这两个包路径加到应用优先加载的列表里。第二个方向是在应用启动时手动设置 TCCL在main方法或者ServletContextListener的contextInitialized里加一行Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());这行代码的作用是确保 Nacos 客户端在初始化时TCCL 指向应用自己的类加载器SPI 能正确找到实现类。我一般两个方向都做双保险。实测下来只做其中一个有时候能跑通但换个 TongWeb 小版本又不行了两个都做稳定性明显提升。注意改类加载策略后一定要清 TongWeb 的临时目录和缓存TongWeb 会缓存已加载的类不清缓存改了也不生效。这个坑我踩过改完配置重启没效果清缓存后才正常。3. Spring Cloud Gateway 路由配置与 TongWeb 适配3.1 网关路由的核心配置逻辑Gateway 跑在 TongWeb 上路由配置本身和标准环境没区别核心是spring.cloud.gateway.routes这一段。但 TongWeb 环境下Gateway 的RoutePredicateHandlerMapping依赖的DispatcherHandler在初始化时如果 TongWeb 的 Servlet 容器对异步请求支持不完整会导致路由匹配上了但转发不出去表现为请求卡住然后超时。我的路由配置是这样的spring: cloud: gateway: discovery: locator: enabled: true lower-case-service-id: true routes: - id: order-service-route uri: lb://order-service predicates: - Path/api/order/** filters: - StripPrefix1 - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 100 redis-rate-limiter.burstCapacity: 200lb://order-service这个写法表示从 Nacos 注册中心按服务名做负载均衡StripPrefix1去掉路径的第一段前缀这样/api/order/create转发到 order-service 时就变成/order/create。lower-case-service-id设成 true 是因为 Nacos 里服务名默认是大写Gateway 做服务发现时如果大小写不匹配会找不到实例这个参数在 TongWeb 环境下尤其要注意因为 TongWeb 对 URL 大小写的处理有时候和标准容器不一致。3.2 动态路由刷新的实现方式Gateway 从 Nacos 拉取服务列表做动态路由靠的是NacosDiscoveryClient定时拉取加事件通知。默认情况下Gateway 启动时拉一次服务列表之后每隔 30 秒spring.cloud.nacos.discovery.watch-delay控制刷新一次。但如果你希望服务上下线后网关立刻感知可以把这个值调小比如 5000 毫秒。不过调小有个副作用TongWeb 环境下频繁的服务列表刷新会触发 Gateway 的RefreshRoutesEvent每次刷新都会重建路由定义。如果 TongWeb 的线程池配置不够刷新期间新请求进来可能会短暂路由不到。我的做法是保持 30 秒默认值但在 Nacos 服务端把实例的健康检查间隔调短让不健康的实例更快被剔除这样网关侧即使 30 秒才刷新一次拉到的列表也是相对准确的。3.3 TongWeb 上 Gateway 的异步请求处理Gateway 基于 Reactor Netty 做底层通信但跑在 TongWeb 上时TongWeb 的 Servlet 容器会接管请求的接收和响应。这里有个关键点TongWeb 对 Servlet 3.1 异步请求的支持程度决定了 Gateway 能不能正常工作。如果 TongWeb 版本较老异步支持不完整Gateway 的NettyRoutingFilter在转发请求时会阻塞 TongWeb 的工作线程并发一上来就卡死。判断方法很简单压测一下看 TongWeb 的线程池活跃线程数是不是随着并发上升一直涨涨到最大值后请求开始排队。如果是说明异步没生效。解决办法是在 TongWeb 的配置里开启异步支持并且把 Gateway 的spring.cloud.gateway.httpclient相关超时参数调大spring: cloud: gateway: httpclient: connect-timeout: 5000 response-timeout: 30s pool: max-connections: 500 max-idle-time: 60smax-connections设成 500 是根据后端微服务的实例数和每个实例能承受的并发估算的。假设 order-service 有 5 个实例每个实例能扛 100 并发那网关到后端的连接池至少要有 500 个连接才够用。这个数不是拍脑袋来的是压测时观察连接等待时间调出来的。4. 配置中心动态刷新在 TongWeb 上的落地4.1 Nacos 配置动态刷新的触发机制Nacos 配置中心动态刷新的原理是客户端启动时向 Nacos 服务端注册一个长轮询请求服务端配置有变更时长轮询返回变更的配置项客户端收到后触发RefreshEventSpring Cloud 的ContextRefresher重新加载RefreshScope标注的 Bean。在 TongWeb 环境下这个链路能不能走通取决于 TongWeb 对长轮询连接的处理。TongWeb 默认对长连接有超时限制如果长轮询请求被 TongWeb 提前断掉客户端会不断重连长轮询配置变更通知就会延迟甚至丢失。我遇到过一次改了 Nacos 里的配置等了五分钟应用都没刷新查日志发现长轮询请求每隔 30 秒就被 TongWeb 断一次客户端重连后重新发起长轮询但变更通知在断连期间丢了。处理办法是在 TongWeb 的连接器配置里把connectionTimeout和keepAliveTimeout调大至少大于 Nacos 长轮询的超时时间默认 30 秒。我一般设成 65 秒给长轮询留出足够余量。4.2 RefreshScope 在 TongWeb 上的使用禁忌RefreshScope这个注解在 TongWeb 上有个坑它依赖 CGLIB 做代理而 TongWeb 的类加载器对 CGLIB 生成的代理类加载有时候会出问题表现为配置刷新后Bean 里的值没变但日志显示RefreshEvent已经触发了。排查下来是代理类没被正确替换。我的经验是在 TongWeb 上尽量把需要动态刷新的配置集中到一个ConfigurationProperties类里而不是散落在各个Value注解上。ConfigurationProperties的刷新机制不依赖 CGLIB 代理走的是ConfigurationPropertiesRebinder在 TongWeb 上稳定性好很多。Component ConfigurationProperties(prefix order.config) RefreshScope public class OrderConfig { private int timeout; private String endpoint; // getter setter }这样配置刷新时OrderConfig这个 Bean 会被重新绑定所有注入它的地方拿到的都是新值。比在每个Value上加RefreshScope要可靠。4.3 配置刷新的验证与排查配置刷新有没有生效不能只看日志。我的验证方法是在应用里加一个/config/check接口返回当前生效的配置值改完 Nacos 配置后调这个接口看返回值变没变。如果没变按这个顺序排查先看 Nacos 控制台的配置有没有推送成功看配置的 MD5 和客户端拉取的 MD5 是否一致再看应用日志里有没有RefreshEvent触发记录最后看 TongWeb 的长连接有没有被断。提示Nacos 配置的dataId和group必须和客户端配置完全一致包括大小写。TongWeb 环境下如果dataId里带了文件扩展名比如order-service.yaml而客户端配置的是order-service是拉不到配置的而且不报错。5. 常见问题排查与避坑经验实录5.1 注册不上 Nacos 的排查路径注册失败是最常见的问题表现是应用启动日志里没有报错但 Nacos 控制台的服务列表里看不到实例。排查按这个顺序走排查项检查方法常见原因网络连通性telnet Nacos地址 8848防火墙、安全组拦截命名空间对比配置里的 namespace 和 Nacos 控制台的 ID填了名称而非 ID分组对比 group 配置默认 DEFAULT_GROUP 被改过类加载器看启动日志有无 SPI 相关异常TongWeb 类加载隔离心跳看 Nacos 控制台实例的健康状态心跳超时被剔除我遇到最多的是命名空间填错和类加载器问题。命名空间那个坑前面说了类加载器问题有个快速验证方法在应用启动后手动调一下 Nacos 客户端的注册接口如果手动调能注册上说明是自动注册的类加载器问题如果手动调也注册不上那就是网络或配置问题。5.2 网关路由 404 与 503 的区分处理Gateway 报 404 和 503 是两回事。404 通常是路由没匹配上检查predicates里的Path表达式和实际请求路径是否匹配注意StripPrefix的数值对不对。503 是路由匹配上了但后端没有可用实例检查 Nacos 里服务实例是否健康、服务名大小写是否一致。TongWeb 环境下有个特殊情况如果 TongWeb 的上下文路径context path不是根路径Gateway 收到的请求路径会带上上下文路径导致Path表达式匹配不上。比如 TongWeb 里应用部署在/gateway下实际请求是/gateway/api/order/create但路由配置里写的是/api/order/**就匹配不上。解决办法是在 TongWeb 里把 Gateway 应用的上下文路径设为根路径或者在路由Path里加上上下文路径前缀。5.3 配置刷新不生效的速查表现象可能原因处理改配置后无任何反应长轮询被断调大 TongWeb 连接超时日志有 RefreshEvent 但值没变CGLIB 代理问题改用 ConfigurationProperties部分配置刷新部分不刷新RefreshScope 作用域问题统一用 ConfigurationProperties刷新后应用报错新配置值不合法加配置校验逻辑这张表是我踩坑踩出来的基本上按这个顺序查90% 的配置刷新问题都能定位。5.4 实操心得与独家技巧第一个心得TongWeb 上部署微服务日志级别一定要调细。Nacos 客户端和 Gateway 的日志默认是 INFO很多类加载和路由匹配的细节看不到。把com.alibaba.nacos和org.springframework.cloud.gateway的日志级别调到 DEBUG启动和运行时的关键路径都能看到排查效率翻倍。第二个心得TongWeb 的临时目录和缓存目录要定期清理。TongWeb 会缓存已加载的类和 JSP 编译结果微服务频繁更新部署时旧缓存不清会导致新代码不生效或者类冲突。我一般在部署脚本里加一步清缓存的操作部署前先停应用、清缓存、再启动。第三个心得Nacos 的命名空间规划要提前做。开发、测试、生产用不同的命名空间不要混在 public 里。TongWeb 环境下不同环境的微服务如果注册到同一个命名空间网关路由会串测试环境的请求可能被转发到生产实例上。这个不是技术问题是管理问题但一旦出问题就是大问题。6. 部署流程的完整复现步骤6.1 环境准备与 Nacos 安装Nacos 的安装我推荐用单机模式先跑通生产环境再考虑集群。下载 Nacos 的压缩包解压后进bin目录Linux 下执行sh startup.sh -m standaloneWindows 下执行startup.cmd -m standalone。启动后访问http://localhost:8848/nacos默认账号密码都是 nacos。Nacos 的数据库配置默认用的是内嵌的 Derby生产环境建议换成 MySQL 或者达梦。换数据库要改conf/application.properties里的spring.datasource相关配置并且把conf目录下的mysql-schema.sql或者对应数据库的建表脚本执行一遍。达梦的话Nacos 官方没有直接提供建表脚本需要根据 MySQL 脚本做语法转换主要是AUTO_INCREMENT改成达梦的IDENTITYENGINEInnoDB去掉。6.2 TongWeb 上部署微服务的包结构TongWeb 部署微服务我建议打成 war 包而不是可执行 jar。war 包的结构里WEB-INF/classes放应用的类和配置文件WEB-INF/lib放依赖 jar。Nacos 客户端和 Spring Cloud 相关的 jar 都放在WEB-INF/lib下这样 TongWeb 的类加载器能正确加载。打包时注意排除 Spring Boot 的内嵌容器依赖因为 TongWeb 已经提供了 Servlet 容器。在pom.xml里把spring-boot-starter-tomcat的 scope 设为provided同时加上spring-boot-starter-web但排除内嵌 Tomcat。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId /exclusion /exclusions /dependency启动类要继承SpringBootServletInitializer并重写configure方法这样 TongWeb 启动时才能正确初始化 Spring 上下文。6.3 网关路由的联调验证部署完微服务和网关后联调验证按这个顺序来先确认每个微服务在 Nacos 控制台里是健康状态再确认网关能拉到服务列表看网关日志里有没有NacosDiscoveryClient拉取记录最后用 curl 或 Postman 调网关的接口看能不能转发到后端。调网关接口时如果返回 503先直接调后端微服务的接口确认后端本身是通的再查网关到后端的路由。如果返回 404检查请求路径和路由Path表达式。如果返回 200 但响应内容不对检查StripPrefix和RewritePath过滤器配置。注意TongWeb 环境下网关的server.port配置可能被 TongWeb 的端口配置覆盖。如果网关启动后监听的端口和你配置的不一样去 TongWeb 的应用配置里看端口设置TongWeb 的端口优先级高于 Spring Boot 的server.port。7. 性能调优与稳定性保障7.1 TongWeb 线程池与 Gateway 并发匹配TongWeb 的线程池配置直接影响 Gateway 的并发处理能力。TongWeb 默认的线程池最大线程数通常在 200 左右如果 Gateway 的并发请求数超过这个值请求会排队。我的做法是把 TongWeb 的最大线程数调到 500同时把 Gateway 的httpclient.pool.max-connections也调到 500两边匹配。但线程数不是越大越好。线程数过大上下文切换开销增加反而降低吞吐。我一般通过压测找拐点逐步增加并发数观察吞吐量和响应时间吞吐量不再上升而响应时间开始明显增加的那个点就是合适的线程数。7.2 Nacos 客户端心跳与 TongWeb 线程调度的协调Nacos 客户端的心跳是定时任务默认用ScheduledExecutorService调度。TongWeb 环境下如果 TongWeb 的线程池被业务请求占满心跳任务的线程可能被延迟调度导致心跳超时。解决办法是给 Nacos 客户端的心跳任务单独配一个线程池不跟业务线程池共用。在application.yml里可以配spring: cloud: nacos: discovery: heart-beat-interval: 5000 heart-beat-timeout: 15000同时在代码里初始化 Nacos 客户端时传入自定义的ExecutorService。这个在 Spring Cloud Alibaba 的 Nacos 自动配置里可以覆盖具体是定义一个NacosDiscoveryProperties的 Bean设置executor属性。7.3 配置刷新的性能影响配置刷新会触发 Spring 上下文的RefreshEvent重新加载RefreshScope的 Bean。如果刷新的配置项很多或者RefreshScope的 Bean 很多刷新过程会占用较多 CPU 和内存。TongWeb 环境下刷新期间如果有大量请求进来可能会因为上下文重建导致短暂的服务不可用。我的优化做法是把配置刷新集中在低峰期做或者用 Nacos 的灰度发布功能先推送到部分实例验证再全量推送。另外RefreshScope的 Bean 尽量少只把真正需要动态刷新的配置放进去其他配置用ConfigurationProperties但不加RefreshScope这样刷新时只重建必要的 Bean。8. 个人实操体会与后续扩展方向这套 TongWeb 加 Nacos 加 Gateway 的组合我在三个项目里落地过每次都会遇到新问题但核心的坑就那几个类加载器、长连接超时、命名空间配置、路由路径匹配。把这几个点吃透剩下的就是熟练度问题。我个人在实际操作中的体会是TongWeb 环境下排查问题日志是第一手资料但日志级别要调对。Nacos 和 Gateway 的 DEBUG 日志能告诉你客户端在做什么、路由匹配到了哪条规则、配置刷新有没有触发。没有这些日志排查就是盲人摸象。最后再分享一个小技巧TongWeb 的类加载器问题如果实在搞不定可以试试把 Nacos 客户端和 Spring Cloud 相关的 jar 包放到 TongWeb 的公共类加载器路径下通常是lib目录让 TongWeb 用父类加载器加载这些包。这样能绕过大部分类加载隔离问题但代价是这些包不能随应用单独升级需要权衡。
返回列表