微服务入门指南(一):从单体架构到服务注册发现

发布于:2025-08-30 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、先搞懂:为什么需要微服务?(架构演进)

在这里插入图片描述

1. 单体架构:简单但笨重(小项目的 “小商店”

是什么:所有功能(比如用户、订单、支付)都写在一个项目里,打包成一个文件部署。
像街边的小商店,收银、货架、仓库都在一间屋里。
特点:所有功能集中在一个项目,打包成一个部署单元
优点:
✅ 开发简单,部署方便
✅ 适合小型项目(如学生管理系统)

缺点:
❌ 耦合度高,牵一发而动全身:改一个功能可能影响其他功能,团队协作吵架;
❌ 扩展性差,无法按需扩容:比如订单模块崩溃,整个系统都用不了;
❌ 升级麻烦:改一行代码也要重新打包整个项目。

2. 分布式架构:解耦但复杂(中型项目的 “连锁店”

是什么:把单体项目拆成多个独立服务(比如 “用户服务”“订单服务”),每个服务跑在不同服务器上,通过网络调用。
像连锁店,收银店、仓库店分开,但互相配合。
特点:多个独立服务通过网络通信协作
优点:
✅ 服务解耦,独立部署:一个服务坏了不影响其他,比如订单服务挂了,用户还能登录。
✅ 技术栈灵活,可按服务选择
✅ 扩展性强,可针对性扩容

缺点:
❌ 架构复杂,调试困难:服务太多,不知道该调用哪个服务器的哪个服务;
❌ 需要处理网络通信、数据一致性等问题

3. 微服务:精致的分布式架构(大型项目的 “专业分工”

是什么:比分布式架构更精细的拆分 —— 每个服务只做一件事(比如 “用户登录服务”“订单创建服务”),完全独立(自己的代码、数据库、团队)。
像大公司的部门分工,财务只管钱,人事只管人,互不干涉。

核心特征:
🔍 单一职责:每个服务只做一件事(比如 “订单服务” 只处理订单,不碰用户数据);
🛡️ 自治独立:独立开发、部署、运维(比如订单用 Java,用户用 Python);
🌐 接口通用:标准接口,与技术无关(对外提供标准接口(比如 HTTP),不管内部用什么技术;
🚧 隔离容错:避免级联故障(一个服务崩了,通过 “降级”“容错” 不让其他服务跟着崩。

微服务本质:一种经过良好设计的分布式架构方案,强调高内聚、低耦合
关键工具:SpringCloud
微服务是 “架构思想”,SpringCloud 是 “落地工具”—— 它把微服务需要的功能(注册服务、发现服务、负载均衡)都打包好了,拿来就能用(基于 SpringBoot,小白友好)。

SpringCloud:微服务一站式解决方案

核心组件概览

组件 功能 说明
Eureka 服务注册与发现 服务治理核心
Ribbon 客户端负载均衡 服务调用分配
Feign 声明式服务调用 简化HTTP客户端
Hystrix 服务容错保护 防止雪崩效应
Zuul/Gateway API网关 统一入口管理

二、微服务第一步:怎么拆分服务?

拆分服务不能乱拆,要遵守 3 个原则,不然越拆越乱:

原则 说明 示例
单一职责 每个服务专注一个业务领域 用户服务、订单服务、支付服务
数据独立 服务间不直接访问数据库,每个服务有自己的数据库,不碰别人的数据库 订单服务只操作 “订单库”,要查用户数据只能调用 “用户服务” 的接口
接口通信 通过API进行服务间调用 用户服务暴露 “根据 ID 查用户” 的接口,供订单服务调用(如RESTful API、gRPC

拆分策略
在这里插入图片描述

拆分时机:
🚀 创业项目:先单体,后微服务
🏢 大型项目:直接微服务,避免后期拆分成本

实战示例:订单 + 用户服务拆分

以 “电商下单” 为例,拆成两个服务:

  • 订单服务(order-service):管下单、查订单,数据库是 “订单库”;
  • 用户服务(user-service):管登录、查用户信息,数据库是 “用户库”。
    关键规则:订单服务要查用户信息,不能直接读 “用户库”,必须调用用户服务的接口(比如http://用户服务地址/user/123)。

项目结构

cloud-demo/
├── user-service/          # 用户服务
│   ├── src/
│   └── pom.xml
├── order-service/         # 订单服务  
│   ├── src/
│   └── pom.xml
└── pom.xml               # 父工程

三、服务间怎么调用?用 RestTemplate

服务拆分后,订单服务要调用用户服务,怎么发网络请求?Spring 给了个工具:RestTemplate,2步搞定:
步骤 1:在订单服务里注册 RestTemplate
在订单服务的启动类里加一段代码,把 RestTemplate 交给 Spring 管理:

import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    // 注册RestTemplate,用于发HTTP请求
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

步骤 2:订单服务调用用户服务接口
在订单服务的业务代码里,用 RestTemplate 发请求,获取用户信息:

@Service
public class OrderService {
    @Autowired
    private RestTemplate restTemplate; // 注入RestTemplate

    public Order queryOrderById(Long orderId) {
        // 1. 查自己的订单数据(从订单库查)
        Order order = orderMapper.findById(orderId);
        
        // 2. 调用用户服务,查用户信息
        String userUrl = "http://localhost:8081/user/" + order.getUserId(); 
        // 发GET请求,把返回结果转成User对象
        User user = restTemplate.getForObject(userUrl, User.class);
        
        // 3. 把用户信息放进订单里,返回
        order.setUser(user);
        return order;
    }
}

注意http://localhost:8081是用户服务的地址,/user/123是用户服务的接口。

四、问题来了:服务多了怎么办?用 Eureka 注册中心

如果用户服务部署了多个实例(比如8081和8082端口),订单服务会遇到 3 个难题:

  • 不知道用户服务的地址是8081还是8082;
  • 不知道该选哪个实例调用;
  • 不知道某个实例是不是已经崩了。
    这时候需要Eureka 注册中心—— 相当于 “服务通讯录”,所有服务都在这里登记自己的地址,需要调用时来这里查。

1. Eureka 怎么工作?(3 步流程)

  • 服务注册:用户服务启动后,主动把自己的地址(如8081)写到 Eureka 的 “通讯录” 里;
  • 服务发现:订单服务需要调用用户服务时,去 Eureka 查 “用户服务” 对应的所有地址(8081、8082);
  • 健康检查:用户服务每隔 30 秒给 Eureka 发 “心跳”,没发心跳的实例会被 Eureka 从通讯录里删掉,避免订单服务调用崩掉的实例。

Eureka架构解析
在这里插入图片描述

Eureka核心机制

机制 说明 默认值
服务注册 服务启动时向Eureka注册 30秒内完成
服务续约 定期发送心跳保持活跃 每30秒一次
服务发现 消费者从Eureka获取服务列表 每30秒更新
服务下线 服务关闭时通知Eureka 主动通知
失效剔除 Eureka清理无心跳服务 90秒无心跳

2. 搭建 Eureka 注册中心(4 步)

步骤 1:创建 Eureka 服务(独立项目)
在父工程下新建子模块eureka-server,专门当注册中心。
步骤 2:引入 Eureka 依赖
pom.xml里加依赖(SpringCloud 提供的 Eureka 服务端依赖):

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

步骤 3:写启动类(加 @EnableEurekaServer)

@SpringBootApplication
@EnableEurekaServer // 开启Eureka注册中心功能
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

步骤 4:写配置文件(application.yml)

server:
  port: 10086 # Eureka的端口(自己定,比如10086)
spring:
  application:
    name: eureka-server # 服务名
eureka:
  client:
    service-url:
      # Eureka自己也是服务,默认会注册自己(这里先让它注册到自己)
      defaultZone: http://127.0.0.1:10086/eureka

启动 Eureka 后,浏览器访问http://127.0.0.1:10086,能看到 Eureka 的管理页面,说明搭建成功!

3. 把服务注册到 Eureka(以用户服务为例)

步骤 1:加 Eureka 客户端依赖
在用户服务的pom.xml里加依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

步骤 2:改配置文件(告诉 Eureka 地址)

spring:
  application:
    name: userservice # 服务名(重要!订单服务会用这个名查)
eureka:
  client:
    service-url:
      # 告诉Eureka注册中心的地址
      defaultZone: http://127.0.0.1:10086/eureka

启动用户服务,刷新 Eureka 页面,能看到userservice出现在 “服务列表” 里,注册成功!

4. 订单服务用 Eureka 查地址(服务发现)

步骤 1:和用户服务一样,加 Eureka 依赖、改配置文件(略)。
步骤 2:给 RestTemplate 加 @LoadBalanced(实现负载均衡)
在订单服务的启动类里,给 RestTemplate 加注解,让它能从 Eureka 查地址,还能自动选实例(比如轮流调用8081和8082):

@Bean
@LoadBalanced // 关键:开启负载均衡,能通过服务名查地址
public RestTemplate restTemplate() {
    return new RestTemplate();
}

步骤 3:用服务名代替 IP 地址
原来调用用户服务的地址是http://localhost:8081/user/123,现在改成服务名userservice:

// 原来的地址(硬编码IP,不好)
String userUrl = "http://localhost:8081/user/" + order.getUserId();
// 改成服务名(Eureka会自动转成具体IP)
String userUrl = "http://userservice/user/" + order.getUserId();

启动订单服务,调用接口时,会自动从 Eureka 查userservice的所有实例,轮流调用(负载均衡),再也不用管具体 IP 了!

最佳实践与常见问题

多实例启动技巧
在这里插入图片描述
为了演示一个服务有多个实例的场景,我们添加一个SpringBoot的启动配置,再启动一个user-service。

  • 首先,复制原来的user-service启动配置:Services模块,选中user-service模块,右键–>Copy Configuration…
  • 然后,在弹出的窗口中,填写信息:Configuration–>VM options:-Dserver.port=xxxx
  • 启动两个user-service实例

常见问题解决方案

1. 服务无法注册
检查Eureka Server地址配置
验证网络连通性
2. 服务发现失败
确认服务名称是否正确
检查@LoadBalanced注解
3. 调用超时
调整Ribbon超时配置
检查服务提供者状态

总结与进阶
本篇重点
✅ 微服务架构演进与优势
✅ 服务拆分原则与实践
✅ Eureka注册中心原理与搭建
✅ 服务注册与发现实战

微服务不是银弹,选择合适的架构比追求技术潮流更重要!从单体到微服务是一个渐进过程,建议根据业务实际需求谨慎决策。