微服务架构设计实战:从单体到分布式
开篇:为什么要写这篇教程
大家好,我是老K,一个985毕业的全栈工程师,目前在掘金上写了不少入门教程。今天这篇关于微服务架构的文章,其实我酝酿了很久。
我当初学微服务的时候,真的是踩了无数的坑。那时候刚从学校出来,在公司里接手了一个单体项目,代码量已经超过十万行,每次部署都要半小时,改一个小bug可能导致整个系统崩溃。当时我就想:这玩意儿到底该怎么拆?
后来我花了一年时间,从理论到实践,把微服务这套东西彻底搞明白了。现在回头看,其实微服务并没有网上说的那么玄乎,核心思想就是"分而治之"。今天这篇文章,我就用最通俗的语言,带大家从零开始,一步步把一个单体应用改造成微服务架构。
为了让大家学得更系统,我推荐大家可以搭配几本书籍来学习,比如《微服务架构设计模式》和《Spring Cloud微服务实战》,这些都是我当初啃过的经典书籍,对理解微服务的设计思想非常有帮助。另外,现在GPT-4o这样的AI工具也能帮我们快速理解一些复杂概念,大家在学习过程中遇到不懂的,也可以借助GPT-4o来辅助学习。
第一部分:什么是微服务?先搞懂单体架构的痛点
1.1 单体架构长什么样
我们先来看一个典型的单体应用结构。假设我们有一个电商系统,所有的功能都写在一个项目里:
ecommerce-app/
├── src/
│ ├── user/ # 用户模块
│ │ ├── UserController.java
│ │ ├── UserService.java
│ │ └── UserMapper.java
│ ├── product/ # 商品模块
│ │ ├── ProductController.java
│ │ ├── ProductService.java
│ │ └── ProductMapper.java
│ ├── order/ # 订单模块
│ │ ├── OrderController.java
│ │ ├── OrderService.java
│ │ └── OrderMapper.java
│ └── payment/ # 支付模块
│ ├── PaymentController.java
│ ├── PaymentService.java
│ └── PaymentMapper.java
├── pom.xml
└── application.yml
这种结构的问题是什么?我给大家列一下:
| 问题 | 具体表现 | 影响 |
|---|---|---|
| 代码耦合严重 | 改一个模块可能影响其他模块 | 开发效率低 |
| 部署困难 | 每次修改都要全量部署 | 发布风险大 |
| 扩展性差 | 只能整体扩容,不能针对热点模块扩容 | 资源浪费 |
| 技术栈固定 | 所有模块必须用同一种语言/框架 | 技术选型受限 |
| 团队协作冲突 | 多人修改同一个代码库,经常冲突 | 沟通成本高 |
1.2 微服务的核心思想
微服务的核心思想其实就一句话:把一个大的单体应用拆分成多个小的、独立部署的服务,每个服务负责一个特定的业务功能。
用文字流程图来表示这个转变过程:
【单体架构】
┌─────────────────────────────────┐
│ 电商系统(一个应用) │
│ ┌─────┐ ┌──────┐ ┌─────┐ ┌──────┐ │
│ │用户 │ │ 商品 │ │订单 │ │ 支付 │ │
│ └─────┘ └──────┘ └─────┘ └──────┘ │
│ 共享一个数据库 │
└─────────────────────────────────┘
↓ 拆分 ↓
【微服务架构】
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 用户服务 │ │ 商品服务 │ │ 订单服务 │ │ 支付服务 │
│ 独立部署 │ │ 独立部署 │ │ 独立部署 │ │ 独立部署 │
│ 独立数据库│ │ 独立数据库│ │ 独立数据库│ │ 独立数据库│
└──────────┘ └──────────┘ └──────────┘ └──────────┘
↕ ↕ ↕ ↕
通过API网关统一对外提供服务
第二部分:环境准备
在开始实战之前,我们需要准备好开发环境。我当初学的时候,光环境搭建就花了两天,所以这部分我写得特别详细。
2.1 基础环境清单
| 工具 | 版本要求 | 用途 |
|---|---|---|
| JDK | 17+ | Java运行环境 |
| Maven | 3.8+ | 项目构建工具 |
| MySQL | 8.0+ | 关系型数据库 |
| Redis | 6.0+ | 缓存中间件 |
| Docker | 20.0+ | 容器化部署 |
| IDEA | 2023+ | 开发IDE |
| Postman | 最新版 | API测试工具 |
2.2 用Docker快速搭建中间件
我当初学的时候,装MySQL、Redis这些中间件折腾了好久,后来发现用Docker简直太方便了。这里给大家一个docker-compose.yml文件,一键启动所有中间件:
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: microservice-mysql
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: ecommerce
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
redis:
image: redis:6.0
container_name: microservice-redis
ports:
- "6379:6379"
nacos:
image: nacos/nacos-server:v2.2.0
container_name: microservice-nacos
environment:
MODE: standalone
ports:
- "8848:8848"
volumes:
mysql-data:
启动命令:
docker-compose up -d
2.3 创建Maven父工程
微服务项目通常采用多模块的方式组织,我们先创建一个父工程来管理所有子模块:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>ecommerce-microservice</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<modules>
<module>common</module>
<module>user-service</module>
<module>product-service</module>
<module>order-service</module>
<module>gateway</module>
</modules>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.1.0</spring-boot.version>
<spring-cloud.version>2022.0.3</spring-cloud.version>
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
第三部分:核心概念讲解
在动手写代码之前,我们需要先理解微服务架构中的几个核心概念。我当初学的时候,这些概念搞得我头大,现在用最通俗的话给大家讲清楚。
3.1 服务注册与发现
通俗解释:想象一个大型商场,每个店铺(服务)开门后都要在商场管理处(注册中心)登记自己的位置和联系方式。顾客(其他服务)想找某个店铺时,先去管理处问一下,就能找到对应的店铺了。
在Spring Cloud生态中,我们常用的注册中心有Nacos、Eureka、Consul等。本教程使用Nacos。
3.2 API网关
通俗解释:API网关就像公司的前台,所有外来访客(客户端请求)都要先经过前台,前台负责判断访客要去哪个部门(路由到哪个服务),同时还能做身份验证(鉴权)、记录来访信息(日志)等。
3.3 服务间通信
通俗解释:微服务之间的通信,就像公司里不同部门之间的协作。有两种方式:
- 同步通信(HTTP/RPC):像打电话,A部门打电话给B部门,等B部门处理完回复
- 异步通信(消息队列):像发邮件,A部门发封邮件给B部门,不用等回复,B部门处理完会通知
3.4 配置中心
通俗解释:以前每个服务的配置都写在自己的配置文件里,就像每个员工自己记自己的密码。配置中心就像一个统一的密码管理器,所有服务的配置都集中管理,修改配置不用重启服务。
第四部分:实战项目——电商系统微服务化
接下来我们正式开始实战,把一个电商系统拆分成微服务架构。
4.1 第一步:创建公共模块(common)
公共模块用来存放所有服务共享的代码,比如通用工具类、统一返回结果等。
// common/src/main/java/com/example/common/Result.java
package com.example.common;
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
return result;
}
public static <T> Result<T> error(String message) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMessage(message);
return result;
}
// getter和setter省略
public Integer getCode() { return code; }
public void setCode(Integer code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
}
common模块的pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>ecommerce-microservice</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>common</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
4.2 第二步:创建用户服务(user-service)
用户服务负责用户的注册、登录、信息查询等功能。
pom.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>ecommerce-microservice</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>user-service</artifactId>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>common</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
实体类:
// user-service/src/main/java/com/example/user/entity/User.java
package com.example.user.entity;
import jakarta.persistence.*;
import lombok.Data;
@Data
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String password;
private String email;
private String phone;
}
Repository层:
// user-service/src/main/java/com/example/user/repository/UserRepository.java
package com.example.user.repository;
import com.example.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
boolean existsByUsername(String username);
}
Service层:
// user-service/src/main/java/com/example/user/service/UserService.java
package com.example.user.service;
import com.example.user.entity.User;
import com.example.user.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User register(String username, String password, String email) {
if (userRepository.existsByUsername(username)) {
throw new RuntimeException("用户名已存在");
}
User user = new User();
user.setUsername(username);
user.setPassword(password); // 实际项目中需要加密
user.setEmail(email);
return userRepository.save(user);
}
public Optional<User> login(String username, String password) {
return userRepository.findByUsername(username)
.filter(user -> user.getPassword().equals(password));
}
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
}
Controller层:
// user-service/src/main/java/com/example/user/controller/UserController.java
package com.example.user.controller;
import com.example.common.Result;
import com.example.user.entity.User;
import com.example.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public Result<User> register(@RequestParam String username,
@RequestParam String password,
@RequestParam String email) {
User user = userService.register(username, password, email);
return Result.success(user);
}
@PostMapping("/login")
public Result<User> login(@RequestParam String username,
@RequestParam String password) {
return userService.login(username, password)
.map(Result::success)
.orElse(Result.error("用户名或密码错误"));
}
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable Long id) {
return userService.getUserById(id)
.map(Result::success)
.orElse(Result.error("用户不存在"));
}
}
配置文件 application.yml:
server:
port: 8081
spring:
application:
name: user-service
datasource:
url: jdbc:mysql://localhost:3306/user_db?createDatabaseIfNotExist=true
username: root
password: root123
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
cloud:
nacos:
discovery:
server-addr: localhost:8848
4.3 第三步:创建商品服务(product-service)
商品服务的结构和用户服务类似,这里我精简一下代码。
实体类:
// product-service/src/main/java/com/example/product/entity/Product.java
package com.example.product.entity;
import jakarta.persistence.*;
import lombok.Data;
import java.math.BigDecimal;
@Data
@Entity
@Table(name = "t_product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
private String description;
@Column(nullable = false)
private BigDecimal price;
@Column(nullable = false)
private Integer stock;
}
Service层(包含扣减库存的逻辑):
// product-service/src/main/java/com/example/product/service/ProductService.java
package com.example.product.service;
import com.example.product.entity.Product;
import com.example.product.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public Optional<Product> getProductById(Long id) {
return productRepository.findById(id);
}
@Transactional
public boolean deductStock(Long productId, Integer quantity) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new RuntimeException("商品不存在"));
if (product.getStock() < quantity) {
throw new RuntimeException("库存不足");
}
product.setStock(product.getStock() - quantity);
productRepository.save(product);
return true;
}
}
Controller层:
// product-service/src/main/java/com/example/product/controller/ProductController.java
package com.example.product.controller;
import com.example.common.Result;
import com.example.product.entity.Product;
import com.example.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/{id}")
public Result<Product> getProduct(@PathVariable Long id) {
return productService.getProductById(id)
.map(Result::success)
.orElse(Result.error("商品不存在"));
}
@PostMapping("/{id}/deduct-stock")
public Result<Boolean> deductStock(@PathVariable Long id,
@RequestParam Integer quantity) {
try {
boolean success = productService.deductStock(id, quantity);
return Result.success(success);
} catch (RuntimeException e) {
return Result.error(e.getMessage());
}
}
}
配置文件:
server:
port: 8082
spring:
application:
name: product-service
datasource:
url: jdbc:mysql://localhost:3306/product_db?createDatabaseIfNotExist=true
username: root
password: root123
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
cloud:
nacos:
discovery:
server-addr: localhost:8848
4.4 第四步:创建订单服务(order-service)——服务间调用的关键
订单服务是最能体现微服务特点的服务,因为它需要调用用户服务和商品服务。这里我们用OpenFeign来实现服务间的同步调用。
添加OpenFeign依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
定义Feign客户端(调用用户服务):
// order-service/src/main/java/com/example/order/feign/UserFeignClient.java
package com.example.order.feign;
import com.example.common.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Map;
@FeignClient(name = "user-service")
public interface UserFeignClient {
@GetMapping("/api/user/{id}")
Result<Map<String, Object>> getUserById(@PathVariable("id") Long id);
}
定义Feign客户端(调用商品服务):
// order-service/src/main/java/com/example/order/feign/ProductFeignClient.java
package com.example.order.feign;
import com.example.common.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "product-service")
public interface ProductFeignClient {
@PostMapping("/api/product/{id}/deduct-stock")
Result<Boolean> deductStock(@PathVariable("id") Long productId,
@RequestParam("quantity") Integer quantity);
}
订单实体类:
// order-service/src/main/java/com/example/order/entity/Order.java
package com.example.order.entity;
import jakarta.persistence.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "t_order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long userId;
@Column(nullable = false)
private Long productId;
@Column(nullable = false)
private Integer quantity;
@Column(nullable = false)
private BigDecimal totalAmount;
private String status; // CREATED, PAID, SHIPPED, COMPLETED, CANCELLED
private LocalDateTime createTime;
@PrePersist
public void prePersist() {
this.createTime = LocalDateTime.now();
this.status = "CREATED";
}
}
订单Service(核心业务逻辑):
// order-service/src/main/java/com/example/order/service/OrderService.java
package com.example.order.service;
import com.example.common.Result;
import com.example.order.entity.Order;
import com.example.order.feign.ProductFeignClient;
import com.example.order.feign.UserFeignClient;
import com.example.order.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Optional;
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserFeignClient userFeignClient;
@Autowired
private ProductFeignClient productFeignClient;
@Transactional
public Order createOrder(Long userId, Long productId, Integer quantity) {
// 1. 调用用户服务,验证用户是否存在
Result<Map<String, Object>> userResult = userFeignClient.getUserById(userId);
if (userResult.getCode() != 200) {
throw new RuntimeException("用户不存在");
}
// 2. 调用商品服务,扣减库存
Result<Boolean> stockResult = productFeignClient.deductStock(productId, quantity);
if (stockResult.getCode() != 200) {
throw new RuntimeException("库存扣减失败:" + stockResult.getMessage());
}
// 3. 创建订单(这里简化处理,实际应该查询商品价格)
Order order = new Order();
order.setUserId(userId);
order.setProductId(productId);
order.setQuantity(quantity);
order.setTotalAmount(new BigDecimal("99.90").multiply(new BigDecimal(quantity)));
return orderRepository.save(order);
}
public Optional<Order> getOrderById(Long id) {
return orderRepository.findById(id);
}
}
订单Controller:
// order-service/src/main/java/com/example/order/controller/OrderController.java
package com.example.order.controller;
import com.example.common.Result;
import com.example.order.entity.Order;
import com.example.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/order")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/create")
public Result<Order> createOrder(@RequestParam Long userId,
@RequestParam Long productId,
@RequestParam Integer quantity) {
try {
Order order = orderService.createOrder(userId, productId, quantity);
return Result.success(order);
} catch (RuntimeException e) {
return Result.error(e.getMessage());
}
}
@GetMapping("/{id}")
public Result<Order> getOrder(@PathVariable Long id) {
return orderService.getOrderById(id)
.map(Result::success)
.orElse(Result.error("订单不存在"));
}
}
启动类(别忘了加@EnableFeignClients):
// order-service/src/main/java/com/example/order/OrderServiceApplication.java
package com.example.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
4.5 第五步:创建API网关(gateway)
网关是所有请求的统一入口,负责路由转发、鉴权、限流等。
pom.xml关键依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
网关配置文件:
server:
port: 9000
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/**
- id: product-service
uri: lb://product-service
predicates:
- Path=/api/product/**
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Origin
网关启动类:
// gateway/src/main/java/com/example/gateway/GatewayApplication.java
package com.example.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
4.6 启动与测试
按照以下顺序启动所有服务:
启动步骤:
1. 启动Docker中间件 → docker-compose up -d
2. 启动Nacos → 访问 http://localhost:8848/nacos 确认服务注册
3. 启动user-service → 端口8081
4. 启动product-service → 端口8082
5. 启动order-service → 端口8083
6. 启动gateway → 端口9000
测试接口:
# 注册用户
curl -X POST "http://localhost:9000/api/user/register?username=test&password=123&email=test@example.com"
# 创建订单(通过网关统一入口)
curl -X POST "http://localhost:9000/api/order/create?userId=1&productId=1&quantity=2"
第五部分:常见问题解答
我当初学微服务的时候,这些问题几乎全踩过,给大家提前避坑。
Q1:服务启动后在Nacos上看不到?
排查步骤:
- 检查
spring.cloud.nacos.discovery.server-addr配置是否正确 - 检查Nacos服务是否正常运行
- 检查网络是否能连通Nacos
- 查看启动日志是否有报错信息
Q2:Feign调用报错"Load balancer does not have available server for client"?
这个问题通常是因为:
- 目标服务没有成功注册到Nacos
- 缺少
spring-cloud-starter-loadbalancer依赖 - 服务名拼写错误(注意大小写)
Q3:网关路由不生效?
检查以下几点:
predicates中的Path路径是否和实际请求路径匹配uri中的服务名是否和Nacos中注册的一致- 网关是否引入了
spring-cloud-starter-loadbalancer
Q4:微服务之间调用超时怎么办?
可以在配置文件中增加超时设置:
spring:
cloud:
openfeign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 10000
第六部分:学习建议与进阶路径
恭喜你完成了微服务的入门实战!但这只是开始,微服务的世界还有很多东西要学。根据我的经验,给大家规划一个学习路径:
初级阶段(当前)
- ✅ 理解微服务基本概念
- ✅ 能搭建简单的微服务项目
- ✅ 掌握服务注册发现和Feign调用
中级阶段(下一步)
- 学习Sentinel实现服务熔断和限流
- 学习Seata实现分布式事务
- 学习Spring Cloud Config或Nacos Config实现配置中心
- 了解Docker和Kubernetes容器化部署
高级阶段(长期目标)
- 深入学习分布式系统设计理论(CAP、BASE)
- 掌握链路追踪(SkyWalking、Zipkin)
- 学习消息队列(RabbitMQ、Kafka)实现异步通信
- 掌握微服务的监控和运维(Prometheus + Grafana)
推荐书籍和资源
| 类型 | 推荐内容 | 适合阶段 |
|---|---|---|
| 书籍 | 《微服务架构设计模式》 | 入门到中级 |
| 书籍 | 《Spring Cloud微服务实战》 | 入门 |
| 书籍 | 《分布式系统概念与设计》 | 高级 |
| 工具 | GPT-4o | 全阶段辅助学习 |
| 社区 | 掘金、思否 | 全阶段 |
总结
这篇文章从零开始,带大家完成了一个电商系统的微服务化改造。我们学习了:
- 为什么要用微服务——单体架构的痛点
- 微服务的核心概念——注册发现、网关、服务通信、配置中心
- 实战项目搭建——用户服务、商品服务、订单服务、API网关
- 服务间调用——使用OpenFeign实现同步调用
我当初学的时候,花了大概三个月才把这些东西全部搞明白。所以大家不要着急,慢慢来,先把这篇文章的代码跑通,然后再逐步深入。
记住,微服务不是银弹,不要为了微服务而微服务。当你的项目足够大、团队足够多、业务足够复杂的时候,再考虑拆分。小项目用单体架构完全够了。
最后,大家在学习过程中遇到任何问题,欢迎在评论区留言讨论。如果觉得这篇文章对你有帮助,别忘了点赞收藏!我们下篇文章见!


评论 0