请添加图片描述

本文定位:黑马程序员《天机学堂》Day1 深度学习笔记,面向初学者,打通从编码到上线的全链路认知。
核心命题:SwitchHosts、Nginx、Git、Docker、Jenkins、Nacos、测试体系,这些工具到底是什么关系?


前言:为什么初学者觉得微服务知识很"碎"

刚接触微服务时,一天之内要学 SwitchHosts 改 hosts、Nginx 反向代理、Git 版本管理、Docker 容器化、Jenkins 自动部署、Nacos 注册中心、JUnit 单元测试、Postman 接口测试……每个单独学都不难,但堆在一起就懵了——它们之间到底是什么关系?我为什么需要这么多工具?

其实这堆工具不是杂乱的,而是围绕一条主线展开——企业软件开发生命周期

需求分析 → 编码开发 → 本地测试 → Git提交 → 自动构建部署 → 开发环境验证 → 联调 → 测试环境 → 生产环境

每个阶段有对应的工具支撑,本文按这个顺序逐一打通。


一、灵魂拷问:为什么企业不把 30 个微服务全在本地启动?

一句话回答:没必要,也做不到。

假设公司有 30 个微服务——用户服务、课程服务、订单服务、支付服务、积分服务、消息服务……你只负责其中的 learning-service。全部本地启动会:

问题 后果
内存爆炸 每个服务至少 256MB-512MB,30 个 = 8-15GB,IDEA 本身还吃 2-3GB
CPU 拉满 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、不连任何外部依赖。纯内存运行,毫秒级完成。

// 例:测试积分计算逻辑,不依赖任何外部组件
@Test
public void testCalculatePoints() {
    // Given:一笔 99 元的订单
    Order order = new Order();
    order.setAmount(new BigDecimal("99"));

    // When:计算应得积分
    int points = pointsService.calculate(order);

    // Then:99 元应得 99 积分(1元=1积分)
    assertEquals(99, points);
}

单元测试的核心价值:改了代码之后跑一遍,3 秒内告诉你有没有把之前正常的功能改坏。不需要启动整个 Spring Boot,不需要连数据库。

3.2 本地接口测试

目的:启动当前服务,验证整个 HTTP 请求链路。

// Spring Boot Test + MockMvc,启动真实 Spring 容器但可以 Mock 外部依赖
@SpringBootTest
@AutoConfigureMockMvc
public class LearningControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testQueryLearningProgress() throws Exception {
        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-service
spring:
  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:8848(Nacos)

五、Nacos 为什么最重要——没有它微服务就瘫痪

Nacos 做两件事:服务注册与发现 + 配置中心

5.1 服务注册与发现

没有 Nacos 的时候,你的代码里要硬编码 IP:

// 硬编码 IP 的灾难
Course course = restTemplate.getForObject(
    "http://192.168.150.101:8082/course/" + courseId, Course.class);

问题:course-service 有三台实例,其中一台挂了怎么办?加新实例怎么办?IP 变了怎么办?——全是手工维护,改到崩溃。

有了 Nacos:

@FeignClient("course-service")   // 只写服务名,不写 IP
public interface CourseClient {
    @GetMapping("/course/{id}")
    Course getById(@PathVariable Long id);
}

底层流程:

你的服务启动 → 向 Nacos 注册:我是 learning-service,IP=192.168.1.5:8081
course-service 启动 → 向 Nacos 注册:我是 course-service,IP=192.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_learning
    username: root
    password: root123

问题是:数据库密码改了怎么办?——改 yml → 重新打包 → 重新部署 → 重启服务。一个密码变更要折腾半小时。

配置放到 Nacos 后:

# 本地 bootstrap.yml 只写 Nacos 地址
spring:
  cloud:
    nacos:
      config:
        server-addr: nacos.tianji.com:8848
        file-extension: yaml
  config:
    import:
      - nacos:learning-service-dev.yaml   # 实际配置从 Nacos 拉

数据库密码改了就改 Nacos 控制台 → @RefreshScope 注解自动刷新 → 不重启。改了即时生效。


六、Jenkins —— 把重复劳动自动化

没有 Jenkins 时,每次提测的流程:

# 每次都要手动敲一遍
git pull
mvn clean package -DskipTests
docker build -t learning-service:1.0 .
docker stop learning-service
docker rm learning-service
docker run -d --name learning-service -p 8081: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.yml

version: '3.8'
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root123
    ports:
      - "3306:3306"

  redis:
    image: redis:7.0
    ports:
      - "6379:6379"

  rabbitmq:
    image: rabbitmq:3.9-management
    ports:
      - "5672:5672"
      - "15672:15672"

  nacos:
    image: nacos/nacos-server:2.2.0
    ports:
      - "8848:8848"

新同事来了一行搞定:

docker-compose up -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 调用。”

Q2:Nacos 到底解决了什么问题?

“两个问题。一是服务发现——不用硬编码 IP,Feign 写服务名就行,Nacos 返回健康的实例列表,挂了自动踢掉。二是配置中心——数据库密码、JWT 密钥这些配置放 Nacos 控制台,改了即时生效不重启,不用改代码重新部署。”

Q3:Jenkins 和 Docker 是什么关系?

“Jenkins 管流程,Docker 管运行。Jenkins 负责收到 Git Push 后自动拉代码、编译、打包、部署这条流水线。Docker 负责把编译好的 jar 包和环境打包成镜像,在任何服务器上都能一致运行。两者配合实现 CI/CD。”

Q4:本地测试和组件测试的区别?

“本地测试跑在自己电脑上,外部依赖可能是 Mock 的或者指向开发服务器。组件测试是代码部署到开发环境之后,验证服务与真实 Redis、MQ、数据库、其他微服务的交互是否正常。前者验证代码逻辑,后者验证环境集成。”

Q5:SwitchHosts 和 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 + 解析 JSON
Jenkins 自动构建部署 每次手动 mvn package → docker build → docker run
Docker 环境一致性 “我电脑能跑,服务器不行”
Gateway 统一 API 入口 前端调 5 个不同地址,鉴权逻辑散落各处

十一、总结

微服务学的不是某一个框架,而是一整套工程方法论

学 Spring Cloud 是学怎么把一个大项目拆成小服务。但拆完之后怎么开发、怎么测试、怎么部署、怎么运维——这些不是写业务代码能解决的,需要一整套工具链。Day1 的内容就是帮你建立这个"工具链全景图"。

理解了这张全景图,后面再学 Gateway 的路由规则、OpenFeign 的拦截器、RabbitMQ 的可靠性投递、Docker 的网络模式、Kubernetes 的编排调度,你会发现它们都不是孤立的——每个工具都在这个生命周期里扮演各自的角色。


更多推荐