企业级微服务开发流程详解:从本地开发、测试到 Jenkins 自动部署 📅 2026/7/13 4:43:50 本文定位黑马程序员《天机学堂》Day1 深度学习笔记面向初学者打通从编码到上线的全链路认知。核心命题SwitchHosts、Nginx、Git、Docker、Jenkins、Nacos、测试体系这些工具到底是什么关系前言为什么初学者觉得微服务知识很碎刚接触微服务时一天之内要学 SwitchHosts 改 hosts、Nginx 反向代理、Git 版本管理、Docker 容器化、Jenkins 自动部署、Nacos 注册中心、JUnit 单元测试、Postman 接口测试……每个单独学都不难但堆在一起就懵了——它们之间到底是什么关系我为什么需要这么多工具其实这堆工具不是杂乱的而是围绕一条主线展开——企业软件开发生命周期需求分析 → 编码开发 → 本地测试 → Git提交 → 自动构建部署 → 开发环境验证 → 联调 → 测试环境 → 生产环境每个阶段有对应的工具支撑本文按这个顺序逐一打通。一、灵魂拷问为什么企业不把 30 个微服务全在本地启动一句话回答没必要也做不到。假设公司有 30 个微服务——用户服务、课程服务、订单服务、支付服务、积分服务、消息服务……你只负责其中的learning-service。全部本地启动会问题后果内存爆炸每个服务至少 256MB-512MB30 个 8-15GBIDEA 本身还吃 2-3GBCPU 拉满30 个 Spring Boot 启动 数据库连接池 心跳检测启动太慢一个一个起30 分钟过去了维护成本高你不能保证每个服务的配置都正确别人的服务改了配置你也不知道企业的做法一台开发服务器上部署了全部公共依赖你本地只跑自己负责的那一个服务┌─────────────────────────────┐ │ 开发服务器 │ │ │ │ course-service user-service│ │ order-service pay-service │ │ trade-service ... │ │ MySQL Redis RabbitMQ ES │ │ Nacos Jenkins Nginx │ └──────────┬──────────────────┘ │ 网络调用Feign/HTTP ┌──────────┴──────────────────┐ │ 你的本地电脑 │ │ │ │ IDEA → learning-service │ │ 只启动这一个 │ └─────────────────────────────┘你的learning-service需要调course-service时通过 Nacos 发现开发服务器上的course-service地址发起远程调用。这就是**“本地单服务 开发环境公共服务”**的开发模式。二、企业完整开发流程一天的工作流一个开发人员每天的真实工作流早上到公司 │ ├─ ① 拉最新代码git pull │ ├─ ② 写代码Service → Mapper → Controller │ ├─ ③ 本地单元测试JUnit 验证核心逻辑不依赖外部组件 │ ├─ ④ 本地接口测试启动 Spring Boot → Postman/Apifox 调接口 │ 需要调其他微服务 → 改 Feign 配置指向开发服务器 │ ├─ ⑤ Git 提交git add → commit → push │ ├─ ⑥ Jenkins 自动构建收到 push 事件 → Maven 编译 → Docker 打包 → 部署到开发环境 │ ├─ ⑦ 开发环境验证确认服务在开发环境正常运行 │ ├─ ⑧ 组件测试验证 Redis/MQ/DB/Feign 等中间件交互 │ ├─ ⑨ 前后端联调前端同事调你的接口 │ └─ ⑩ 提测/发布测试环境 → 生产环境关键理解Git 管版本、Jenkins 管自动化、Docker 管环境一致性、Nacos 管服务发现。下面逐个展开。三、测试体系详解四种测试四种目的3.1 本地单元测试目的验证一个方法的逻辑是否正确。特点不连数据库、不连 Redis、不连 MQ、不连任何外部依赖。纯内存运行毫秒级完成。// 例测试积分计算逻辑不依赖任何外部组件TestpublicvoidtestCalculatePoints(){// Given一笔 99 元的订单OrderordernewOrder();order.setAmount(newBigDecimal(99));// When计算应得积分intpointspointsService.calculate(order);// Then99 元应得 99 积分1元1积分assertEquals(99,points);}单元测试的核心价值改了代码之后跑一遍3 秒内告诉你有没有把之前正常的功能改坏。不需要启动整个 Spring Boot不需要连数据库。3.2 本地接口测试目的启动当前服务验证整个 HTTP 请求链路。// Spring Boot Test MockMvc启动真实 Spring 容器但可以 Mock 外部依赖SpringBootTestAutoConfigureMockMvcpublicclassLearningControllerTest{AutowiredprivateMockMvcmockMvc;TestpublicvoidtestQueryLearningProgress()throwsException{mockMvc.perform(get(/learning/progress/1001).header(Authorization,Bearer testToken)).andExpect(status().isOk()).andExpect(jsonPath($.userId).value(1001));}}或用 Postman/Apifox 手动发 HTTP 请求POST http://localhost:8081/learning/record Header: Authorization: Bearer jwt_token Body: { courseId: 100, sectionId: 5, progress: 0.8 }如果接口内部调了其他微服务比如FeignClient(course-service)本地没有这个服务怎么办改配置指向开发服务器# 本地开发时Feign 调开发服务器上的 course-servicespring:cloud:openfeign:client:config:course-service:url:http://192.168.150.101:8082# 开发服务器的 course-service 地址3.3 组件测试目的代码部署到开发环境后验证微服务和中间件的交互是否正常。验证清单连上开发环境的 Redis 了吗redisTemplate.opsForValue().set(test, hello)能写能读吗MQ 收发正常吗发一条测试消息消费端能收到吗Feign 远程调用的网络通吗超时重试策略正常吗数据库事务正常吗异常时能正确回滚吗和接口测试的区别接口测试在本地跑外部依赖可能是 Mock 的。组件测试在开发环境跑跟真实中间件交互验证的是你的服务作为一个组件在真实环境中能否正常工作。3.4 开发环境联调目的多个服务一起跑通完整业务流程。比如天机学堂的核心链路用户登录 → 浏览课程 → 选择课程 → 下单购买 → 支付 → 开始学习 → 记录进度 │ │ │ │ │ │ │ auth course course trade pay learning learning -service -service -service -service -service -service -service联调时请求经过 Gateway → 路由到各个微服务 → 每个服务各查各的数据库、各调各的缓存。这个链路比单服务的接口测试覆盖面大得多能暴露跨服务的序列化问题、事务边界问题、超时配置问题等。四、SwitchHosts 与 Nginx —— 流量入口的守门人4.1 SwitchHosts域名到 IP 的本地映射企业内网的开发服务器通常只有 IP 没有公网域名。没有 SwitchHosts 时你需要这样访问http://192.168.150.101:8080/nacos http://192.168.150.101:9090/jenkins http://192.168.150.101:3000/git不仅难记而且一旦服务器 IP 变了所有配置都要改。用 SwitchHosts 在本地 hosts 文件里加一条映射192.168.150.101 nacos.tianji.com 192.168.150.101 jenkins.tianji.com 192.168.150.101 git.tianji.com 192.168.150.101 api.tianji.com之后就用http://nacos.tianji.com访问 Nacos 了。IP 变了你只改 hosts 文件一行。4.2 Nginx反向代理统一入口hosts 只能把域名指向 IP但没法区分端口。Nginx 补上这一环——根据域名或路径把请求转发到不同端口的服务# 不同域名 → 转发到不同服务 server { listen 80; server_name nacos.tianji.com; location / { proxy_pass http://127.0.0.1:8848; # Nacos 在 8848 端口 } } server { listen 80; server_name jenkins.tianji.com; location / { proxy_pass http://127.0.0.1:9090; # Jenkins 在 9090 端口 } } server { listen 80; server_name api.tianji.com; location / { proxy_pass http://127.0.0.1:10010; # Gateway 在 10010 端口 } }SwitchHosts 和 Nginx 的配合关系你在浏览器输入 nacos.tianji.com → SwitchHosts 把域名解析成 192.168.150.101 → 请求到达 Nginx开发服务器 80 端口 → Nginx 根据 server_name 匹配规则 → 转发到 127.0.0.1:8848Nacos五、Nacos 为什么最重要——没有它微服务就瘫痪Nacos 做两件事服务注册与发现配置中心。5.1 服务注册与发现没有 Nacos 的时候你的代码里要硬编码 IP// 硬编码 IP 的灾难CoursecourserestTemplate.getForObject(http://192.168.150.101:8082/course/courseId,Course.class);问题course-service有三台实例其中一台挂了怎么办加新实例怎么办IP 变了怎么办——全是手工维护改到崩溃。有了 NacosFeignClient(course-service)// 只写服务名不写 IPpublicinterfaceCourseClient{GetMapping(/course/{id})CoursegetById(PathVariableLongid);}底层流程你的服务启动 → 向 Nacos 注册我是 learning-serviceIP192.168.1.5:8081 course-service 启动 → 向 Nacos 注册我是 course-serviceIP192.168.150.101:8082 你的服务调 course-service → 问 Nacos 它在哪 → Nacos 返回 [192.168.150.101:8082] 所有健康的实例列表 → Feign 内置 LoadBalancer 挑一个调 → 某个实例 15 秒没发心跳 → Nacos 把它踢掉 → 不再返回给调用方全程零硬编码 IP。5.2 配置中心没有 Nacos 配置中心时数据库连接写在 application.yml 里spring:datasource:url:jdbc:mysql://192.168.150.101:3306/tianji_learningusername:rootpassword:root123问题是数据库密码改了怎么办——改 yml → 重新打包 → 重新部署 → 重启服务。一个密码变更要折腾半小时。配置放到 Nacos 后# 本地 bootstrap.yml 只写 Nacos 地址spring:cloud:nacos:config:server-addr:nacos.tianji.com:8848file-extension:yamlconfig:import:-nacos:learning-service-dev.yaml# 实际配置从 Nacos 拉数据库密码改了就改 Nacos 控制台 →RefreshScope注解自动刷新 → 不重启。改了即时生效。六、Jenkins —— 把重复劳动自动化没有 Jenkins 时每次提测的流程# 每次都要手动敲一遍gitpull mvn clean package-DskipTestsdockerbuild-tlearning-service:1.0.dockerstop learning-servicedockerrmlearning-servicedockerrun-d--namelearning-service-p8081:8081 learning-service:1.0一天提交 5 次 手动重复 5 遍 30 分钟浪费在敲命令上。有了 Jenkins你做的事情git push Jenkins 自动做的事情 ① 检测到 Git 有新提交 → 拉代码 ② Maven 编译 跑测试 ③ Docker Build 打镜像 ④ Docker Run 启动容器 ⑤ 发通知部署成功/失败 整个流程你只敲了一行 git push。这就是CI持续集成——代码提交后自动构建、自动测试、自动部署。把人工重复操作变成机器自动执行。七、Docker —— 消除在我电脑上能跑7.1 核心价值新同事入职第一天的噩梦装 MySQL 8.0、装 Redis 7.0、装 RabbitMQ、装 ES、装 Nacos、配环境变量、装各种依赖……版本还要跟团队一致一上午就过去了。Docker 的解法把依赖和环境全部写进docker-compose.ymlversion:3.8services:mysql:image:mysql:8.0environment:MYSQL_ROOT_PASSWORD:root123ports:-3306:3306redis:image:redis:7.0ports:-6379:6379rabbitmq:image:rabbitmq:3.9-managementports:-5672:5672-15672:15672nacos:image:nacos/nacos-server:2.2.0ports:-8848:8848新同事来了一行搞定docker-composeup-d# 一分钟全部就绪Docker 和虚拟机的本质区别Docker 共享宿主机内核启动秒级一个 16G 机器能跑几十个容器。虚拟机是完整 OS启动分钟级16G 只能跑几个。八、完整请求链路一张图打通所有工具你的电脑 开发服务器 ───────── ──────────── IDEA 写代码 │ ├─ JUnit 单元测试 │ 验证方法逻辑 │ ├─ Postman 接口测试 │ 启动 learning-service │ → 注册到 Nacos开发服务器 │ → Feign 调 course-service开发服务器 │ ├─ git commit → git push │ ─────────────────────────────→ Git 仓库Gogs/GitLab │ │ │ Jenkins 检测到 push │ ├─ mvn package │ ├─ docker build │ └─ docker run │ │ │ 开发环境部署完成 │ │ ├─ 组件测试 ←───────────────────────┘ │ 验证 Redis/MQ/DB/Feign 正常 │ ├─ 前后端联调 │ 前端 → Nginx → Gateway → 各微服务 │ └─ 提测 测试环境 → 生产环境九、面试高频问题与回答话术Q1为什么不把全部微服务在本地启动“没必要也做不到。30 个服务全启动内存就爆了CPU 也扛不住。企业做法是开发服务器部署公共服务和中间件本地只启动自己负责的那一个服务需要调别的服务通过 Nacos 发现远程地址Feign 调用。”Q2Nacos 到底解决了什么问题“两个问题。一是服务发现——不用硬编码 IPFeign 写服务名就行Nacos 返回健康的实例列表挂了自动踢掉。二是配置中心——数据库密码、JWT 密钥这些配置放 Nacos 控制台改了即时生效不重启不用改代码重新部署。”Q3Jenkins 和 Docker 是什么关系“Jenkins 管流程Docker 管运行。Jenkins 负责收到 Git Push 后自动拉代码、编译、打包、部署这条流水线。Docker 负责把编译好的 jar 包和环境打包成镜像在任何服务器上都能一致运行。两者配合实现 CI/CD。”Q4本地测试和组件测试的区别“本地测试跑在自己电脑上外部依赖可能是 Mock 的或者指向开发服务器。组件测试是代码部署到开发环境之后验证服务与真实 Redis、MQ、数据库、其他微服务的交互是否正常。前者验证代码逻辑后者验证环境集成。”Q5SwitchHosts 和 Nginx 配合做什么“SwitchHosts 把域名映射到开发服务器 IP省得记 IP。Nginx 根据域名或路径把请求转发到不同端口的服务。两者配合——浏览器输域名 → hosts 解析成 IP → 请求到 Nginx → Nginx 按规则转发。用户全程只记域名。”十、工具职责速查表工具一句话职责没它会怎样SwitchHosts域名 → IP 本地映射记一堆 IP 地址Nginx反向代理统一入口每个服务暴露独立端口管理混乱Git版本管理 团队协作代码靠 U 盘拷Maven依赖管理 项目构建手动下载 jar版本冲突自己扛JUnit单元测试改代码不知道有没有改坏Postman/Apifox接口调试靠前端配合才能验证接口Nacos服务发现 配置中心硬编码 IP改配置要重启Feign声明式 HTTP 调用手写 RestTemplate 拼 URL 解析 JSONJenkins自动构建部署每次手动 mvn package → docker build → docker runDocker环境一致性“我电脑能跑服务器不行”Gateway统一 API 入口前端调 5 个不同地址鉴权逻辑散落各处十一、总结微服务学的不是某一个框架而是一整套工程方法论。学 Spring Cloud 是学怎么把一个大项目拆成小服务。但拆完之后怎么开发、怎么测试、怎么部署、怎么运维——这些不是写业务代码能解决的需要一整套工具链。Day1 的内容就是帮你建立这个工具链全景图。理解了这张全景图后面再学 Gateway 的路由规则、OpenFeign 的拦截器、RabbitMQ 的可靠性投递、Docker 的网络模式、Kubernetes 的编排调度你会发现它们都不是孤立的——每个工具都在这个生命周期里扮演各自的角色。