1. 从零到一:构建你的第一个Spring Cloud Alibaba微服务
大家好,我是老张,在微服务架构这条路上摸爬滚打了十来年,从早期的Dubbo到Spring Cloud,再到现在的Spring Cloud Alibaba,可以说是踩坑无数,也收获颇丰。今天,我们不聊那些虚头巴脑的概念,就实实在在地聊聊如何把Spring Cloud Alibaba这三大金刚——Nacos、Sentinel、Seata——用起来,并且用好。很多朋友觉得微服务复杂,其实当你把核心组件玩转了,剩下的就是搭积木。
Spring Cloud Alibaba是什么?简单说,它就是阿里巴巴把自家在双十一这种超大规模场景下锤炼出来的微服务组件,比如Nacos、Sentinel、RocketMQ、Seata,打包成了一套能无缝集成到Spring Cloud生态的解决方案。它能做什么?它能帮你搞定服务注册发现、配置管理、流量防护、分布式事务这些微服务架构中最头疼的问题。这套东西特别适合正在从单体应用向微服务转型,或者已经在微服务路上但被各种稳定性问题困扰的团队。
你可能听过Eureka、Config Server、Hystrix,但Spring Cloud Alibaba提供的组件在功能、性能和与云原生生态的贴合度上,往往更胜一筹。更重要的是,它们的文档和社区支持现在非常活跃,中文资料也多,对我们国内开发者非常友好。接下来,我会用一个模拟的“电商订单”场景,带大家一步步深入Nacos、Sentinel和Seata的核心应用。咱们不搞理论堆砌,直接上手,边做边学。
2. Nacos实战:不止是服务注册与发现
2.1 快速搭建与核心概念扫盲
很多教程一上来就让你装Nacos Server,然后贴一堆配置,但你可能连Nacos到底管什么都不知道。我先打个比方:Nacos就像一个微服务世界的“电话簿”+“公告栏”。电话簿功能就是服务注册与发现,你的服务启动后,会把自己的联系方式(IP、端口、服务名)登记到Nacos;其他服务想找它,不用记死IP,直接查电话簿(Nacos)就能拿到最新、最健康的地址。公告栏功能就是动态配置中心,你把一些可能会变的参数(比如数据库连接、开关标志)写在Nacos的公告栏上,所有服务都能实时看到变更,不用重启。
搭建Nacos Server超级简单,如果你图省事,用Docker一句话搞定:
docker run --name nacos-standalone -e MODE=standalone -p 8848:8848 -d nacos/nacos-server:latest
启动后,浏览器打开 http://你的服务器IP:8848/nacos,默认账号密码都是nacos,你就能看到管理界面了。这里我踩过一个坑:生产环境千万别用standalone模式,一定要用集群模式,并且把数据持久化到MySQL,不然服务器一重启,所有注册信息全丢。
在Spring Boot项目中引入Nacos客户端依赖,现在通常直接用spring-cloud-starter-alibaba-nacos-discovery和spring-cloud-starter-alibaba-nacos-config。关键配置在bootstrap.yml(注意是bootstrap,不是application)里:
spring:
application:
name: order-service # 服务名,这是服务发现的依据
cloud:
nacos:
discovery:
server-addr: 192.168.1.100:8848 # Nacos Server地址
namespace: dev # 命名空间,用于环境隔离
group: DEFAULT_GROUP # 分组,进一步逻辑隔离
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
file-extension: yaml # 配置格式,也支持properties
namespace: ${spring.cloud.nacos.discovery.namespace}
group: ${spring.cloud.nacos.discovery.group}
# 指定要拉取的配置集Data ID,默认是 ${spring.application.name}.${file-extension}
配置好启动你的服务,在Nacos控制台的“服务管理”列表里,你就能看到order-service了,点进去还能看到实例的详细元数据和健康状态。
2.2 动态配置与多环境管理的艺术
动态配置是Nacos Config最爽的功能。以前改个日志级别或者功能开关,都得改配置文件、打包、重启服务,流程又长又容易出错。现在,你只需要在Nacos控制台的“配置管理”里,新建一个Data ID为order-service.yaml的配置,然后把内容贴进去,比如:
# order-service.yaml
logging:
level:
com.example: DEBUG
feature:
switch:
newPayment: true # 一个新支付功能的开关
保存并发布。你的order-service几乎在瞬间(默认有1-2秒延迟)就能接收到配置变更的通知,并自动刷新@Value或@ConfigurationProperties注解的字段值,无需重启!这里有个关键注解@RefreshScope,需要把它加在使用了@Value的Bean上,或者你的配置类上。
多环境管理怎么搞?我推荐用“命名空间(Namespace)”来隔离。比如在Nacos上创建dev、test、prod三个命名空间,把不同环境的配置分别放进去。然后在项目启动时,通过JVM参数-Dspring.cloud.nacos.config.namespace=命名空间ID来指定。这样,同一套代码,在不同环境就能自动加载对应的配置,安全又清晰。
2.3 集群与高可用部署避坑指南
单机模式只能用于学习。生产环境,高可用是底线。Nacos集群依赖于三个东西:持久化数据库、集群节点和负载均衡器。
首先,初始化MySQL数据库,运行Nacos提供的nacos-mysql.sql脚本。然后修改Nacos解压目录下conf/application.properties,配置MySQL数据源。接着,修改conf/cluster.conf.example文件为cluster.conf,在里面列出所有集群节点的IP:端口,例如:
192.168.1.101:8848
192.168.1.102:8848
192.168.1.103:8848
最后,你需要一个负载均衡器(比如Nginx)对外暴露一个统一的VIP(虚拟IP),比如nacos.vip.com:80,将流量分发到这三个节点。客户端配置的server-addr就填这个VIP地址。这样,任何一个Nacos节点挂掉,整个注册中心依然可用。我遇到过因为没配集群,导致某个机房网络抖动,整个服务发现瘫痪的惨案,所以这一步千万别省。
3. Sentinel实战:打造坚不可摧的服务防线
3.1 流量控制:从入门到精准调控
Sentinel是服务稳定性的守护神。它的核心思想是:在服务被打垮之前,先拒绝掉一部分请求,保住大部分请求的成功。这听起来有点残酷,但这是分布式系统在高并发下的生存法则。
首先引入依赖spring-cloud-starter-alibaba-sentinel。Sentinel的理念是“资源”,任何需要保护的东西都是资源,比如一个URL、一个方法。最常用的流量控制规则是QPS流控和线程数流控。我常在网关入口或者核心服务接口上配置QPS流控。比如,我知道/api/order/create这个接口,单实例扛住100 QPS比较健康,超过就可能响应变慢。那我就在Sentinel控制台(需要单独部署,也是一个jar包)这样配:
| 资源名 | 流控模式 | 阈值类型 | 单机阈值 | 流控效果 |
|---|---|---|---|---|
POST:/api/order/create | QPS | 直接 | 100 | 快速失败 |
“快速失败”就是直接抛BlockException。但更好的用户体验是“排队等待”,它会让超出的请求匀速通过,像一个漏斗,虽然会等待,但不会立刻被拒绝。对于查询类接口,我常用这个模式。
热点参数限流是个高级功能。比如商品详情页接口/api/product/{id},如果某个爆款商品ID(比如id=123)的访问量突然激增,我们只想限制对这个商品的访问,而不是所有商品。Sentinel可以针对方法参数的第几个参数(比如第一个参数id)进行特殊限流。在代码里用@SentinelResource注解标记资源,然后在控制台配置热点规则,非常灵活。
3.2 熔断降级:如何优雅地服务“摆烂”
熔断和降级是当依赖的服务不稳定时,保护自己的手段。熔断是“保险丝”,依赖服务出错比例太高,我就直接掐断对它的调用,快速失败,给自己喘息的机会。降级是“备胎计划”,主路走不通了,我走一个事先准备好的简单逻辑,比如返回一个默认值、一个缓存数据或者一个友好提示。
Sentinel的熔断降级规则主要看三个指标:慢调用比例、异常比例、异常数。我举个例子:订单服务调用支付服务。如果发现调用支付服务的请求,响应时间超过2秒(慢调用)的比例超过了50%,并且持续了5个请求以上,那么Sentinel就会熔断对支付服务的调用。接下来的10秒内,所有调用支付服务的请求都会立刻失败(进入了“开路”状态)。10秒后,会进入一个“半开”状态,放一个试探请求过去,如果成功了,就关闭熔断器,恢复正常。
在代码中,我们使用@SentinelResource注解来定义资源,并指定blockHandler(处理流控熔断异常)和fallback(处理业务异常)方法。
@Service
public class OrderService {
@SentinelResource(value = "createOrder",
blockHandler = "createOrderBlockHandler", // 流控熔断处理
fallback = "createOrderFallback") // 业务异常降级处理
public Order createOrder(OrderDTO orderDTO) {
// 1. 调用库存服务
// 2. 调用支付服务
// 3. 创建订单
return order;
}
// 方法签名需与原方法一致,最后加一个BlockException参数
public Order createOrderBlockHandler(OrderDTO orderDTO, BlockException ex) {
log.warn("触发流控或熔断,订单创建被限流", ex);
throw new RuntimeException("系统繁忙,请稍后再试");
}
// 方法签名需与原方法一致,最后加一个Throwable参数
public Order createOrderFallback(OrderDTO orderDTO, Throwable th) {
log.error("订单创建失败,执行降级逻辑", th);
// 返回一个兜底数据,比如保存到待处理队列,提示用户稍后查看
return Order.builder().status("处理中").build();
}
}
这样,无论是被Sentinel规则拦截(BlockException),还是自己代码抛出业务异常,都能有优雅的应对,而不是把一堆错误直接抛给用户。
3.3 规则持久化与生产环境最佳实践
你在Sentinel控制台配的规则,默认是存在内存里的,控制台重启就没了。这显然不适合生产。所以必须做规则持久化。官方推荐的方式是推模式,即规则由控制台推送到一个统一的规则中心(如Nacos、ZooKeeper、Apollo),客户端监听规则中心的变化。
以Nacos为例,你需要:
- 在Nacos上创建一个配置,Data ID为
sentinel-rule-order-service,内容是你的流控、降级等规则(JSON格式)。 - 在订单服务中,添加
sentinel-datasource-nacos依赖。 - 在配置文件中指定Nacos作为数据源。
spring:
cloud:
sentinel:
datasource:
ds:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
data-id: sentinel-rule-${spring.application.name}
group-id: SENTINEL_GROUP
rule-type: flow # 规则类型,还有degrade、param-flow等
这样,规则就持久化了,并且可以动态更新。生产环境另一个重点是监控和告警。Sentinel控制台提供了实时监控,但你还需要将监控数据对接到公司的监控系统(如Prometheus + Grafana),并配置告警规则(比如某个资源连续5分钟异常比例超过20%就发钉钉/短信告警),这样才能真正做到事前预防,事后快速定位。
4. Seata实战:搞定让人头疼的分布式事务
4.1 AT模式:无侵入的分布式事务解决方案
分布式事务是微服务的“阿克琉斯之踵”。Seata的AT(Auto Transaction)模式是它的招牌,最大优点是对业务代码几乎无侵入。它怎么工作的?我把它比喻成“大家长记账”模式。
假设我们有订单服务(创建订单)、库存服务(扣减库存)、账户服务(扣款)。在AT模式下:
- 一阶段(提交前):当订单服务开启一个全局事务(用
@GlobalTransactional注解标记),并调用库存服务时,Seata会拦截这个SQL,解析语义,找到要更新的库存数据(比如product_id=1001),在执行业务SQL之前,先把这个数据的前镜像(更新前的值)和后镜像(更新后的值)生成一条回滚日志(undo_log),和业务SQL在同一个本地事务中提交到库存服务的数据库。其他服务同理。这个阶段,各个服务的本地事务都已经提交了,数据已经修改。 - 二阶段(全局提交/回滚):
- 如果全局事务成功:Seata的事务协调器(TC)收到所有分支(订单、库存、账户)的“一阶段成功”报告,就发起一个异步的“删除各服务的undo_log”请求,快速完成。
- 如果全局事务失败(比如账户余额不足):TC会通知所有成功执行了一阶段的服务,根据之前保存的undo_log,执行反向补偿操作(即回滚)。因为回滚日志里记录了前镜像,所以可以精准地恢复数据。
整个过程,业务开发者只需要在事务发起方的方法上加一个@GlobalTransactional注解,其他参与方完全不用改代码。这非常适合传统的基于关系型数据库的微服务改造。部署上,你需要启动一个Seata Server(TC),并在每个微服务的数据库中创建undo_log表。
4.2 TCC与Saga模式:应对复杂业务场景
AT模式虽好,但并非万能。它强依赖于支持本地ACID事务的关系型数据库,并且要求操作的是“可回滚”的SQL(UPDATE/DELETE/INSERT)。如果你的业务涉及:
- 调用一个第三方接口(比如发送短信)。
- 操作非关系型数据库(如Redis、MongoDB)。
- 一些无法简单回滚的业务逻辑(比如“用户积分增加”,回滚不是简单减回去,可能涉及过期时间等)。
这时候就需要TCC或Saga模式。TCC模式要求开发者手动编写三个阶段的业务逻辑:
- Try:尝试执行业务,完成所有业务检查,并预留好必要的资源(比如冻结库存、冻结金额)。
- Confirm:确认执行,真正提交业务,使用Try阶段预留的资源。Confirm必须保证成功。
- Cancel:取消执行,释放Try阶段预留的资源。
TCC对业务有侵入,但控制粒度最细,性能也最好,适合对一致性要求极高的金融场景。Saga模式则是把一个长事务拆分成多个本地事务,每个事务都有对应的补偿操作。执行时按顺序执行,如果某个步骤失败,就反向执行前面所有步骤的补偿操作。Saga模式不需要像TCC那样预留资源,实现相对简单,但缺点是补偿操作可能失败,而且事务隔离性较弱(可能出现脏读)。它适合业务流程长、参与者多的场景,比如一个旅游订单,涉及订票、订酒店、租车。
4.3 生产环境部署与调优心得
Seata Server(TC)的高可用部署和Nacos类似,也需要集群化。关键是把它的存储模式从默认的file改为db,并配置好MySQL集群。然后在每个微服务(RM)的配置中,指向TC集群的VIP。
在实际使用中,我总结了几点调优经验:
- 全局锁竞争:AT模式使用全局锁来保证隔离性,在高并发更新同一条数据时可能成为瓶颈。可以通过合理设计数据模型(比如避免热点数据)、使用Seata的
@GlobalLock注解处理非事务内的读操作来缓解。 - 超时时间设置:
@GlobalTransactional的timeoutMills属性要设置合理,太短容易误伤,太长则资源占用久。需要根据业务链路的平均耗时来定。 - undo_log表清理:AT模式会产生大量undo_log,需要定期清理。可以写个定时任务,删除已提交事务的日志。
- 选择合适的模式:不要一味用AT。简单的跨库操作用AT;涉及外部系统调用或非SQL操作,考虑TCC;业务流程非常长的,考虑Saga。混合使用也是常见方案。
分布式事务没有银弹,Seata提供了多种武器库,选择哪种,取决于你的业务特性和对一致性、性能、复杂度的权衡。我的建议是,先从AT模式开始,它覆盖了大部分场景,在真正遇到其局限性时,再考虑引入TCC或Saga。

1215

被折叠的 条评论
为什么被折叠?



