微服务实战:从单体到分布式架构的演进之路 上一篇文章梳理了 Spring Cloud 各个组件的作用和学习路径。这次想聊聊更实际的问题当你真正要把一个系统拆成微服务时具体该怎么做我参考了github上面众多老师的 Spring Cloud 实战仓库https://github.com/yinjihuan/spring-cloud结合其中的完整项目案例整理了一套从服务拆分、数据库设计到分布式事务的实战思路。一、服务拆分怎么拆才合理1.1 拆分的核心原则微服务拆分没有标准答案但有几个基本原则可以参考按业务边界拆分每个服务对应一个明确的业务领域比如用户服务、订单服务、商品服务。这样服务之间的职责清晰团队可以独立开发和部署。高内聚、低耦合服务内部的功能紧密相关服务之间的依赖尽量少。如果两个服务频繁互相调用可能说明拆分得不够合理。数据独立性每个服务应该有自己的数据库避免多个服务直接操作同一张表。这是微服务和传统 SOA 的重要区别。1.2 实战项目的服务拆分案例参考仓库中的房产项目fangjia-fsh它的拆分方式如下服务职责对应模块API 网关统一入口、路由转发、鉴权fangjia-fsh-api用户服务用户注册、登录、个人信息fangjia-fsh-user-service房产服务房源信息、搜索、展示fangjia-fsh-house-service替换服务房源替换逻辑fangjia-fsh-substitution-service认证服务服务间调用鉴权、Token 颁发fangjia-auth-service注册中心服务发现与注册fangjia-eureka监控服务服务健康状态监控fangjia-boot-admin拆分要点用户服务和房产服务是核心业务独立拆分认证逻辑抽离成独立服务避免每个服务重复实现API 网关作为统一入口对外暴露 REST 接口对内路由到具体服务二、数据库设计每个服务一个数据库2.1 数据库拆分的必要性微服务架构中每个服务应该拥有独立的数据库。这样做的好处服务之间解耦一个服务的数据库变更不会影响其他服务可以根据服务特点选择不同的数据库类型MySQL、MongoDB、Redis 等便于独立扩展热点服务可以单独做读写分离或分库分表2.2 读写分离实战当读请求远多于写请求时读写分离是常用的优化手段。仓库中的fangjia-sjdbc-read-write模块展示了基于 ShardingJDBC 的实现spring: shardingsphere: datasource: names: master, slave master: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/master_db username: root password: 123456 slave: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/slave_db username: root password: 123456 masterslave: name: ms master-data-source-name: master slave-data-source-names: slave关键点写操作路由到主库master读操作路由到从库slave主从同步由 MySQL 自身机制保证2.3 分库分表实战当单表数据量过大通常超过千万级时需要考虑分库分表。仓库提供了两个示例分表sharding-table同一数据库内按某个字段将数据分散到多张表spring: shardingsphere: sharding: tables: t_order: actual-data-nodes: ds0.t_order_${0..9} table-strategy: inline: sharding-column: order_id algorithm-expression: t_order_${order_id % 10}分库分表sharding-db-table数据分散到多个数据库的多张表spring: shardingsphere: sharding: default-database-strategy: inline: sharding-column: user_id algorithm-expression: ds${user_id % 2} tables: t_order: actual-data-nodes: ds${0..1}.t_order_${0..9}分片策略选择策略适用场景优点缺点取模分片数据分布均匀的场景负载均衡扩容时需要迁移数据范围分片按时间、ID 范围查询的场景扩容方便可能存在热点哈希分片需要均匀分布的场景避免热点范围查询效率低三、分布式事务怎么保证数据一致性3.1 为什么需要分布式事务微服务架构下一个业务操作可能涉及多个服务。比如下单操作订单服务创建订单库存服务扣减库存用户服务扣减余额这三个操作必须同时成功或同时失败否则会出现数据不一致。3.2 分布式事务解决方案对比方案原理优点缺点适用场景2PC两阶段提交协调者统一管理强一致性性能差、协调者单点故障对一致性要求极高的金融场景TCCTry-Confirm-Cancel业务层实现补偿性能较好业务侵入性强开发成本高电商、支付等高并发场景** Saga**长事务拆分失败时执行补偿操作性能好最终一致性补偿逻辑复杂业务流程长、需要异步处理的场景可靠消息通过消息队列保证最终一致性解耦、性能好实现复杂需要处理幂等大多数异步场景3.3 实战基于可靠消息的分布式事务仓库中的transaction-mq-service模块实现了一套可靠消息服务核心思路┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 业务服务 │────▶│ 消息服务 │────▶│ 消息队列 │ │ (发起方) │ │ (可靠消息) │ │ (RocketMQ) │ └─────────────┘ └─────────────┘ └──────┬──────┘ │ ▼ ┌─────────────┐ │ 消费服务 │ │ (接收方) │ └─────────────┘核心流程业务服务执行业务操作同时向消息服务发送预消息消息服务将消息状态设为待确认返回给业务服务业务服务执行业务逻辑成功后调用消息服务确认消息消息服务将消息投递到 MQ消费服务消费消息执行业务操作消息表设计CREATE TABLE transaction_message ( id BIGINT PRIMARY KEY AUTO_INCREMENT, message_id VARCHAR(64) NOT NULL UNIQUE, message_body TEXT NOT NULL, destination VARCHAR(255) NOT NULL, status TINYINT NOT NULL DEFAULT 0, -- 0:待发送 1:已发送 2:消费成功 3:消费失败 send_times INT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_status_time (status, create_time) );定时补偿机制Component public class MessageRetryTask { Scheduled(fixedRate 60000) // 每分钟执行 public void retry() { // 查询待发送和发送失败的消息 ListTransactionMessage messages messageService .findByStatusAndTime(MessageStatus.PENDING, 5); for (TransactionMessage msg : messages) { if (msg.getSendTimes() 3) { // 超过重试次数人工介入 msg.setStatus(MessageStatus.FAILED); messageService.update(msg); continue; } // 重新发送 rocketMQTemplate.asyncSend(msg.getDestination(), msg.getMessageBody(), new SendCallback() { Override public void onSuccess(SendResult sendResult) { msg.setStatus(MessageStatus.SENT); messageService.update(msg); } Override public void onException(Throwable e) { msg.setSendTimes(msg.getSendTimes() 1); messageService.update(msg); } }); } } }关键点消息先落库再发送保证消息不丢失定时任务补偿处理发送失败的消息消费端幂等处理避免重复消费超过重试次数的消息转入死信队列人工处理四、服务间调用安全怎么防止接口被乱调4.1 问题背景微服务内部互相调用时如果不对调用方进行校验任何一个服务被攻破都可能直接调用其他服务的内部接口。4.2 解决方案内部认证服务仓库中的fangjia-auth-service实现了服务间调用的认证机制FeignClient(name auth-service) public interface AuthClient { PostMapping(/auth/token) AuthResponse getToken(RequestBody AuthRequest request); PostMapping(/auth/verify) boolean verifyToken(RequestHeader(Authorization) String token); }调用流程服务 A 调用服务 B 前先向认证服务申请 Token服务 A 在请求头中携带 Token 调用服务 B服务 B 通过拦截器验证 Token 的有效性验证通过才执行业务逻辑Component public class AuthInterceptor implements HandlerInterceptor { Autowired private AuthClient authClient; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(Authorization); if (StringUtils.isEmpty(token)) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } boolean valid authClient.verifyToken(token); if (!valid) { response.setStatus(HttpStatus.FORBIDDEN.value()); return false; } return true; } }五、项目结构组织怎么放代码才清晰5.1 多模块项目结构参考仓库的组织方式一个完整的微服务项目可以这样分层spring-cloud-project/ ├── api-gateway/ # API 网关 ├── service-common/ # 公共模块工具类、常量、异常定义 ├── service-api/ # Feign 客户端定义API SDK ├── service-auth/ # 认证服务 ├── service-user/ # 用户服务 ├── service-order/ # 订单服务 ├── service-message/ # 消息服务可靠消息 ├── eureka-server/ # 注册中心 ├── config-server/ # 配置中心 └── pom.xml # 父 POM统一管理依赖版本5.2 单个服务的内部结构service-user/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/example/user/ │ │ │ ├── UserApplication.java │ │ │ ├── controller/ # 对外接口层 │ │ │ ├── service/ # 业务逻辑层 │ │ │ ├── mapper/ # 数据访问层 │ │ │ ├── entity/ # 实体类 │ │ │ ├── dto/ # 数据传输对象 │ │ │ ├── config/ # 配置类 │ │ │ └── feign/ # Feign 客户端调用其他服务 │ │ └── resources/ │ │ ├── application.yml │ │ ├── application-dev.yml │ │ └── mapper/ # MyBatis XML │ └── test/ └── pom.xml六、写在最后微服务架构的落地不是一蹴而就的。从单体到微服务需要考虑服务拆分是否合理— 拆得太细会增加调用复杂度拆得太粗失去微服务的意义数据库设计— 独立数据库是原则但数据一致性成为新的挑战分布式事务— 没有银弹根据业务特点选择合适的方案服务安全— 内部调用也需要认证不能假设内网就是安全的监控和治理— 服务多了之后链路追踪、熔断降级、限流等能力必不可少微服务不是目的而是手段。不要为了拆分而拆分根据团队规模和业务复杂度选择合适的架构方案才是正确的做法。参考资源配套源码https://github.com/yinjihuan/spring-cloud作者博客http://cxytiandi.com/blogs/yinjihuan第一版书籍《Spring Cloud微服务-全栈技术与案例解析》第二版书籍《Spring Cloud微服务 入门 实战与进阶》