一、为什么要做微服务改造?
1.1 单体应用的困境
我们曾为某教育企业维护一个10万行代码的单体应用,痛点如下:
技术债务:
- 启动时间:8分钟(开发效率极低)
- 部署频率:每月1次(怕出问题不敢频繁发布)
- 团队协作:15人同时修改代码,冲突频繁
- 技术栈锁定:Spring 3.x无法升级(牵一发而动全身)
业务问题:
- 营销活动高峰期,整个系统宕机(无法隔离故障)
- 新增支付渠道需要2周(代码耦合严重)
- 数据库单点瓶颈:QPS上限3000
1.2 微服务改造后的效果
经过6个月的渐进式改造:
| 指标 | 改造前 | 改造后 | 提升 |
|---|---|---|---|
| 部署频率 | 1次/月 | 20次/天 | 600x |
| 启动时间 | 8分钟 | 30秒 | 16x |
| 故障隔离 | 全站宕机 | 单服务降级 | ✅ |
| 系统QPS | 3,000 | 50,000 | 17x |
| 团队协作 | 代码冲突频繁 | 独立开发 | ✅ |
ROI:
- 开发效率提升40%
- 服务器成本降低30%(按需扩容)
- 故障率下降80%
二、服务拆分策略
2.1 拆分原则
DDD(领域驱动设计)拆分法:
单体应用(原):
┌────────────────────────────────────┐
│ 用户 + 商品 + 订单 + 支付 + 物流 │
│ (10万行代码) │
└────────────────────────────────────┘
微服务架构(改造后):
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│ 用户 │ │ 商品 │ │ 订单 │ │ 支付 │ │ 物流 │
│ 服务 │ │ 服务 │ │ 服务 │ │ 服务 │ │ 服务 │
└──────┘ └──────┘ └──────┘ └──────┘ └──────┘
拆分维度:
按业务能力拆分(推荐)
- ✅ 用户服务:注册、登录、个人信息
- ✅ 商品服务:商品管理、库存、分类
- ✅ 订单服务:下单、订单查询、订单状态流转
按技术特性拆分
- 计算密集型服务(如AI推荐):使用GPU服务器
- IO密集型服务(如文件上传):使用高带宽服务器
按变化频率拆分
- 营销活动服务(高频变化):独立部署
- 基础数据服务(低频变化):稳定运行
2.2 实战案例:订单服务拆分
拆分前(单体应用中的订单模块):
// 订单创建流程(紧耦合)
public class OrderController {
@Autowired
private UserDao userDao;
@Autowired
private ProductDao productDao;
@Autowired
private OrderDao orderDao;
@Autowired
private PaymentService paymentService;
@Autowired
private LogisticsService logisticsService;
public Order createOrder(OrderRequest req) {
// 1. 校验用户
User user = userDao.findById(req.getUserId());
// 2. 校验商品库存
Product product = productDao.findById(req.getProductId());
if (product.getStock() < req.getQuantity()) {
throw new RuntimeException("库存不足");
}
// 3. 创建订单
Order order = new Order();
order.setUserId(req.getUserId());
order.setProductId(req.getProductId());
orderDao.save(order);
// 4. 扣减库存
product.setStock(product.getStock() - req.getQuantity());
productDao.update(product);
// 5. 调用支付
paymentService.createPayment(order);
// 6. 创建物流单
logisticsService.createShipment(order);
return order;
}
}
问题:
- ❌ 直接操作其他模块的数据库(userDao、productDao)
- ❌ 同步调用多个服务(paymentService、logisticsService)
- ❌ 缺乏事务一致性保障
拆分后(微服务架构):
// 订单服务(独立微服务)
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserServiceClient userClient; // Feign客户端
@Autowired
private ProductServiceClient productClient;
@Autowired
private RabbitTemplate rabbitTemplate; // 消息队列
@Transactional
public OrderDTO createOrder(CreateOrderRequest req) {
// 1. RPC调用用户服务校验用户
UserDTO user = userClient.getUserById(req.getUserId());
if (user == null) {
throw new BusinessException("用户不存在");
}
// 2. RPC调用商品服务校验库存
boolean stockAvailable = productClient.checkStock(
req.getProductId(),
req.getQuantity()
);
if (!stockAvailable) {
throw new BusinessException("库存不足");
}
// 3. 创建订单(只操作自己的数据库)
Order order = new Order();
order.setUserId(req.getUserId());
order.setProductId(req.getProductId());
order.setStatus(OrderStatus.PENDING);
orderRepository.save(order);
// 4. 发送MQ消息(异步处理)
OrderCreatedEvent event = new OrderCreatedEvent(order);
// 商品服务监听此消息,扣减库存
rabbitTemplate.convertAndSend("order.created", event);
return OrderDTO.from(order);
}
}
改进点:
- ✅ 通过Feign客户端调用其他服务(解耦)
- ✅ 使用MQ异步处理非关键流程(提升性能)
- ✅ 只操作自己的数据库(服务自治)
三、API网关设计
3.1 网关职责
客户端请求
↓
┌───────────────────────────────────────┐
│ API Gateway (网关) │
│ ┌─────────────────────────────────┐ │
│ │ 1. 路由转发 │ │
│ │ 2. 认证鉴权 (JWT验证) │ │
│ │ 3. 限流熔断 (Sentinel/Hystrix) │ │
│ │ 4. 日志监控 │ │
│ │ 5. 协议转换 (HTTP → gRPC) │ │
│ └─────────────────────────────────┘ │
└───────────────────────────────────────┘
↓ ↓ ↓ ↓
用户服务 商品服务 订单服务 支付服务
3.2 技术选型对比
| 网关 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Spring Cloud Gateway | • 非阻塞IO • 与Spring生态集成好 • 灵活的路由配置 |
• 性能中等 • JVM内存占用 |
Java技术栈 |
| Kong | • 高性能(OpenResty) • 插件丰富 • 多语言支持 |
• 配置复杂 • Lua学习成本 |
多语言混合架构 |
| APISIX | • 性能极佳 • 国产开源 • Dashboard友好 |
• 生态相对小 | 高性能要求 |
| Nginx + Lua | • 极致性能 • 稳定性好 |
• 开发效率低 • 缺乏服务治理 |
简单路由场景 |
3.3 Spring Cloud Gateway实战
配置示例:
# application.yml
spring:
cloud:
gateway:
routes:
# 用户服务路由
- id: user-service
uri: lb://user-service # 负载均衡
predicates:
- Path=/api/users/**
filters:
- StripPrefix=2 # 去掉 /api/users 前缀
- name: RequestRateLimiter # 限流
args:
redis-rate-limiter.replenishRate: 100 # 每秒100请求
redis-rate-limiter.burstCapacity: 200
# 订单服务路由
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=2
- name: CircuitBreaker # 熔断
args:
name: orderCircuitBreaker
fallbackUri: forward:/fallback/order
自定义认证过滤器:
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Autowired
private JwtUtil jwtUtil;
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 白名单路径(登录、注册)
String path = request.getPath().value();
if (path.contains("/login") || path.contains("/register")) {
return chain.filter(exchange);
}
// 提取Token
String token = request.getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 验证Token
token = token.substring(7);
try {
Claims claims = jwtUtil.parseToken(token);
String userId = claims.getSubject();
// 将用户ID传递给下游服务
ServerHttpRequest modifiedRequest = request.mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate()
.request(modifiedRequest)
.build());
} catch (JwtException e) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
@Override
public int getOrder() {
return -100; // 优先级最高
}
}
3.4 限流熔断策略
基于Redis的分布式限流:
@Configuration
public class RateLimiterConfig {
@Bean
public KeyResolver userKeyResolver() {
// 基于用户ID限流
return exchange -> {
String userId = exchange.getRequest()
.getHeaders()
.getFirst("X-User-Id");
return Mono.just(userId != null ? userId : "anonymous");
};
}
@Bean
public KeyResolver ipKeyResolver() {
// 基于IP限流
return exchange -> {
String ip = exchange.getRequest()
.getRemoteAddress()
.getAddress()
.getHostAddress();
return Mono.just(ip);
};
}
}
Sentinel熔断降级:
@Service
public class OrderFallbackService implements OrderServiceClient {
@Override
public OrderDTO getOrderById(Long orderId) {
// 熔断后的降级响应
OrderDTO fallback = new OrderDTO();
fallback.setId(orderId);
fallback.setStatus("UNAVAILABLE");
fallback.setMessage("订单服务暂时不可用,请稍后重试");
return fallback;
}
}
四、分布式事务解决方案
4.1 问题场景
用户下单流程涉及多个服务:
1. 订单服务:创建订单
2. 商品服务:扣减库存
3. 用户服务:扣减积分
4. 支付服务:创建支付单
任何一步失败,都需要回滚前面的操作。
4.2 解决方案对比
| 方案 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Seata AT模式 | • 无侵入 • 性能好 |
• 需要数据库支持 | 强一致性要求 |
| Saga模式 | • 长事务支持 • 最终一致性 |
• 需要补偿逻辑 | 复杂业务流程 |
| TCC模式 | • 性能最佳 • 灵活性高 |
• 开发成本高 | 核心交易场景 |
| 本地消息表 | • 简单可靠 | • 代码侵入性强 | 简单异步场景 |
4.3 Seata AT模式实战
1. 引入依赖:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.7.0</version>
</dependency>
2. 配置Seata:
seata:
application-id: order-service
tx-service-group: my-tx-group
service:
vgroup-mapping:
my-tx-group: default
grouplist:
default: 127.0.0.1:8091 # Seata Server地址
3. 业务代码:
// 订单服务(事务发起方)
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ProductServiceClient productClient;
@Autowired
private UserServiceClient userClient;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public OrderDTO createOrder(CreateOrderRequest req) {
// 1. 创建订单(本地事务)
Order order = new Order();
order.setUserId(req.getUserId());
order.setProductId(req.getProductId());
order.setAmount(req.getAmount());
orderRepository.save(order);
// 2. RPC调用商品服务扣减库存(远程事务)
productClient.deductStock(req.getProductId(), req.getQuantity());
// 3. RPC调用用户服务扣减积分(远程事务)
userClient.deductPoints(req.getUserId(), req.getPoints());
// 任何一步失败,Seata自动回滚所有操作
return OrderDTO.from(order);
}
}
// 商品服务(事务参与方)
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
public void 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);
}
}
工作原理:
Phase 1 (Prepare):
订单服务: INSERT order → 记录undo_log
商品服务: UPDATE stock → 记录undo_log
用户服务: UPDATE points → 记录undo_log
Phase 2 (Commit/Rollback):
成功: 删除所有undo_log
失败: 根据undo_log回滚所有操作
4.4 Saga模式(长事务)
适用于跨多个微服务的复杂业务流程:
// 使用Seata Saga状态机
{
"Name": "OrderSaga",
"StartState": "CreateOrder",
"States": {
"CreateOrder": {
"Type": "ServiceTask",
"ServiceName": "orderService",
"ServiceMethod": "create",
"CompensateState": "CancelOrder",
"Next": "DeductStock"
},
"DeductStock": {
"Type": "ServiceTask",
"ServiceName": "productService",
"ServiceMethod": "deductStock",
"CompensateState": "RestoreStock",
"Next": "DeductPoints"
},
"DeductPoints": {
"Type": "ServiceTask",
"ServiceName": "userService",
"ServiceMethod": "deductPoints",
"CompensateState": "RestorePoints",
"Next": "Succeed"
},
"Succeed": {
"Type": "Succeed"
}
}
}
补偿逻辑:
@Service
public class OrderCompensationService {
// 正向操作
public void createOrder(Order order) {
orderRepository.save(order);
}
// 补偿操作(回滚)
public void cancelOrder(Order order) {
order.setStatus(OrderStatus.CANCELLED);
orderRepository.save(order);
}
}
五、服务治理与监控
5.1 服务注册与发现
Nacos配置:
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: production
group: DEFAULT_GROUP
metadata:
version: 1.0.0
region: cn-beijing
健康检查:
@RestController
public class HealthController {
@Autowired
private DataSource dataSource;
@GetMapping("/actuator/health")
public Health health() {
try {
// 检查数据库连接
dataSource.getConnection().close();
return Health.up().build();
} catch (Exception e) {
return Health.down().withDetail("error", e.getMessage()).build();
}
}
}
5.2 链路追踪(Skywalking)
引入依赖:
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>8.15.0</version>
</dependency>
自定义追踪:
@Service
public class OrderService {
@Trace // 自动记录方法执行时间
@Tag(key = "orderId", value = "arg[0]")
public Order getOrderById(Long orderId) {
ActiveSpan.tag("bizType", "query");
return orderRepository.findById(orderId);
}
}
追踪效果:
TraceId: 1a2b3c4d5e6f
┌─────────────────────────────────────────────────────┐
│ API Gateway [200ms] │
│ ├─ 认证过滤器 [20ms] │
│ └─ 路由转发 [180ms] │
│ ├─ 订单服务 [150ms] │
│ │ ├─ 查询DB [80ms] │
│ │ └─ RPC调用用户服务 [60ms] │
│ │ └─ 用户服务查询 [50ms] │
│ └─ 返回结果 [10ms] │
└─────────────────────────────────────────────────────┘
5.3 监控告警(Prometheus + Grafana)
暴露Metrics:
management:
endpoints:
web:
exposure:
include: prometheus,health,info
metrics:
tags:
application: ${spring.application.name}
自定义指标:
@Component
public class OrderMetrics {
private final Counter orderCreatedCounter;
private final Timer orderProcessTimer;
public OrderMetrics(MeterRegistry registry) {
this.orderCreatedCounter = Counter.builder("order.created.total")
.description("订单创建总数")
.tag("status", "success")
.register(registry);
this.orderProcessTimer = Timer.builder("order.process.duration")
.description("订单处理耗时")
.register(registry);
}
public void recordOrderCreated() {
orderCreatedCounter.increment();
}
public void recordOrderProcess(Runnable task) {
orderProcessTimer.record(task);
}
}
Grafana Dashboard配置:
Panel 1: 订单QPS
PromQL: rate(order_created_total[1m])
Panel 2: 订单处理P99延迟
PromQL: histogram_quantile(0.99, order_process_duration_bucket)
Panel 3: 服务可用性
PromQL: up{job="order-service"}
六、性能优化实战
6.1 数据库拆分
垂直拆分(按业务):
原单库:
┌────────────────────┐
│ users │
│ products │
│ orders │
│ payments │
└────────────────────┘
拆分后:
┌────────┐ ┌──────────┐ ┌────────┐ ┌──────────┐
│ users │ │ products │ │ orders │ │ payments │
└────────┘ └──────────┘ └────────┘ └──────────┘
水平拆分(按数据量):
// ShardingSphere配置
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
url: jdbc:mysql://localhost:3306/order_db_0
ds1:
url: jdbc:mysql://localhost:3306/order_db_1
rules:
sharding:
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..15}
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-inline
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database-inline
sharding-algorithms:
order-inline:
type: INLINE
props:
algorithm-expression: t_order_$->{order_id % 16}
database-inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
6.2 缓存策略
多级缓存架构:
请求 → 本地缓存(Caffeine) → Redis缓存 → 数据库
↓ 命中率95% ↓ 命中率4.9% ↓ 0.1%
代码实现:
@Service
public class ProductService {
@Autowired
private RedisTemplate<String, Product> redisTemplate;
@Autowired
private ProductRepository productRepository;
// Caffeine本地缓存
private final LoadingCache<Long, Product> localCache =
Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build(this::loadFromRedis);
public Product getProductById(Long productId) {
return localCache.get(productId);
}
private Product loadFromRedis(Long productId) {
String key = "product:" + productId;
// 1. 查Redis
Product product = redisTemplate.opsForValue().get(key);
if (product != null) {
return product;
}
// 2. 查数据库
product = productRepository.findById(productId)
.orElseThrow(() -> new RuntimeException("商品不存在"));
// 3. 写入Redis
redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES);
return product;
}
}
缓存更新策略:
@Service
public class ProductCacheService {
@Autowired
private RedisTemplate<String, Product> redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;
// 方案1: Cache Aside(推荐)
public void updateProduct(Product product) {
// 1. 更新数据库
productRepository.save(product);
// 2. 删除缓存(而非更新)
redisTemplate.delete("product:" + product.getId());
// 3. 发送MQ通知其他节点清除本地缓存
rabbitTemplate.convertAndSend("cache.invalidate", product.getId());
}
// 方案2: Canal监听MySQL Binlog自动删除缓存
@RabbitListener(queues = "canal.product.update")
public void onProductUpdated(CanalMessage message) {
Long productId = message.getData().getId();
redisTemplate.delete("product:" + productId);
}
}
6.3 异步解耦
同步调用(改造前):
// 响应时间 = 500ms + 200ms + 300ms = 1000ms
public Order createOrder(CreateOrderRequest req) {
Order order = orderRepository.save(new Order()); // 500ms
smsService.sendOrderNotification(order); // 200ms (阻塞)
logService.recordOrderLog(order); // 300ms (阻塞)
return order;
}
异步调用(改造后):
// 响应时间 = 500ms(提升50%)
@Async
public Order createOrder(CreateOrderRequest req) {
Order order = orderRepository.save(new Order()); // 500ms
// 发送MQ消息(非阻塞)
OrderCreatedEvent event = new OrderCreatedEvent(order);
rabbitTemplate.convertAndSend("order.created", event);
return order;
}
// 短信服务异步处理
@RabbitListener(queues = "order.created")
public void handleOrderCreated(OrderCreatedEvent event) {
smsService.sendOrderNotification(event.getOrder());
logService.recordOrderLog(event.getOrder());
}
七、常见问题与解决方案
Q1: 服务启动顺序依赖怎么办?
问题:订单服务依赖用户服务,用户服务未启动时订单服务报错。
解决方案:
// 使用@ConditionalOnBean延迟初始化
@Configuration
public class FeignClientConfig {
@Bean
@ConditionalOnProperty(name = "feign.client.user.enabled", havingValue = "true")
public UserServiceClient userServiceClient() {
return new UserServiceClient();
}
}
// 启动时重试机制
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
int retries = 0;
while (retries < 3) {
try {
SpringApplication.run(OrderServiceApplication.class, args);
break;
} catch (Exception e) {
retries++;
Thread.sleep(5000); // 等待5秒重试
}
}
}
}
Q2: 雪崩效应如何预防?
解决方案:
- 设置超时时间
feign:
client:
config:
default:
connectTimeout: 2000
readTimeout: 5000
- 熔断降级
@FeignClient(name = "user-service", fallback = UserServiceFallback.class)
public interface UserServiceClient {
UserDTO getUserById(Long userId);
}
@Component
public class UserServiceFallback implements UserServiceClient {
@Override
public UserDTO getUserById(Long userId) {
// 降级响应
UserDTO fallback = new UserDTO();
fallback.setId(userId);
fallback.setNickname("用户" + userId);
return fallback;
}
}
- 限流保护
@SentinelResource(value = "getOrderById",
blockHandler = "handleBlock",
fallback = "handleFallback")
public OrderDTO getOrderById(Long orderId) {
return orderRepository.findById(orderId);
}
public OrderDTO handleBlock(Long orderId, BlockException ex) {
throw new BusinessException("系统繁忙,请稍后重试");
}
Q3: 配置管理混乱?
解决方案:使用Nacos配置中心
# bootstrap.yml
spring:
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: production
group: DEFAULT_GROUP
file-extension: yaml
shared-configs:
- data-id: common-config.yaml
refresh: true
动态刷新:
@RefreshScope // 配置变更自动刷新
@RestController
public class ConfigController {
@Value("${feature.new-payment:false}")
private boolean newPaymentEnabled;
@GetMapping("/config/payment-feature")
public boolean getPaymentFeature() {
return newPaymentEnabled;
}
}
八、总结与最佳实践
核心原则
- 渐进式演进:不要一次性重写,先拆分核心模块
- 服务自治:每个服务独立数据库、独立部署
- 容错设计:假设任何服务都可能失败
- 可观测性:完善监控、日志、链路追踪
推荐技术栈
服务框架: Spring Boot + Spring Cloud Alibaba
API网关: Spring Cloud Gateway / APISIX
注册中心: Nacos
配置中心: Nacos
分布式事务: Seata
消息队列: RabbitMQ / RocketMQ
缓存: Redis + Caffeine
数据库: MySQL + ShardingSphere
链路追踪: Skywalking
监控: Prometheus + Grafana
日志: ELK (Elasticsearch + Logstash + Kibana)
成本收益分析
适合做微服务的场景:
- ✅ 团队规模 > 10人
- ✅ 业务复杂度高,模块变化频繁
- ✅ 用户量大,需要独立扩容
不适合做微服务的场景:
- ❌ 小团队(<5人)
- ❌ 业务简单,CRUD为主
- ❌ 用户量小(<1000 DAU)
关于我们
智理科技拥有丰富的微服务架构设计与落地经验,已为多家企业完成单体到微服务的改造。
服务内容:
- 微服务架构咨询与评估
- 单体应用拆分方案设计
- 完整微服务体系开发
- 性能优化与故障排查
联系我们:
- 📧 邮箱: wuning@wanli.ai
- 🌐 官网: https://zhili.wanli.ai
- 📝 技术博客: https://zhili.wanli.ai/blog/
欢迎交流微服务架构经验!