第七部分:分布式锁 —— 共享资源的安全访问
在分布式环境中,多个进程(或线程)需要互斥地访问共享资源(如库存扣减、定时任务执行),就需要分布式锁。
7.1 基于 Redis 的分布式锁
Redis 的 SET NX EX 命令可以实现简单的锁。
基础版本:
String lockKey = "lock:product:1001";
String requestId = UUID.randomUUID().toString();
// 设置键和值,只有不存在时才成功,并设置过期时间 30 秒
Boolean success = redis.setnx(lockKey, requestId, 30, TimeUnit.SECONDS);
if (success) {
try {
// 执行业务逻辑
} finally {
// 释放锁:使用 Lua 脚本保证原子性(先判断再删除)
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
}
}
问题:单点故障。如果 Redis 主从切换,锁可能丢失。Redlock(Redis 作者提出)使用多个独立 Redis 节点,多数派加锁,但争议较大(时钟漂移、GC 停顿导致锁失效)。
推荐:使用 Redisson 框架,它封装了看门狗(Watchdog)自动续期机制,避免锁过期任务未完成。
RLock lock = redisson.getLock("myLock");
lock.lock(10, TimeUnit.SECONDS);
try {
// ...
} finally {
lock.unlock();
}
7.2 基于 ZooKeeper 的分布式锁
ZooKeeper 通过临时顺序节点实现锁:每个客户端在锁节点下创建临时顺序节点,序号最小的获得锁。监听前一个节点的删除事件,实现公平锁。
优点:强一致性(ZAB 协议),无单点问题,自带心跳检测防止死锁。缺点:性能比 Redis 低(适合并发量不太高的场景)。
7.3 基于数据库的唯一索引
利用数据库唯一键约束作为锁:执行 INSERT INTO distributed_lock(lock_key, node_id) VALUES ('key', 'node1'),成功表示获得锁,删除即释放。性能差,但简单可靠。
第八部分:服务治理与微服务 —— 管理分布式系统的“千军万马”
当服务数量膨胀到几十上百个,需要一套治理体系来管理服务注册、发现、配置、路由、容错、监控。
8.1 服务注册与发现
服务启动时将自己的地址注册到注册中心(如 Nacos、Eureka、Consul、ZooKeeper),消费者从注册中心获取提供者列表,实现动态感知。
Nacos 使用示例(Spring Cloud Alibaba):
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/call")
public String call() {
// 使用服务名调用,Ribbon 进行负载均衡
return restTemplate.getForObject("http://service-provider/hello", String.class);
}
}
8.2 配置中心
集中管理各环境配置,修改配置动态生效(无需重启)。Nacos Config、Apollo、Consul Key/Value 都是主流。
Apollo 示例:
@Value("${timeout:100}")
private int timeout;
// 配置自动热更新
@ApolloConfigChangeListener
public void onChange(ConfigChangeEvent changeEvent) {
// 刷新相关 Bean
}
8.3 服务调用与负载均衡
除了 RestTemplate + Ribbon,也可以使用声明式 HTTP 客户端 Feign:
@FeignClient(name = "user-service", fallback = UserFallback.class)
public interface UserClient {
@GetMapping("/user/{id}")
User getUser(@PathVariable("id") Long id);
}
8.4 熔断、降级、限流 —— Hystrix / Sentinel
这三种手段是保障分布式系统韧性的核心模式。
熔断(Circuit Breaker)
当某个服务调用失败率达到阈值(如 50%),断路器打开,后续请求直接快速失败或走降级逻辑,避免级联故障。经过一段时间,允许少量请求通过测试服务是否恢复(半开状态)。
降级(Fallback)
当服务不可用或系统负载过高时,提供有损的替代响应(如返回缓存数据、友好提示)。降级可在客户端或服务端实现。
限流(Rate Limiting)
限制单位时间内的请求数量,超过阈值则拒绝服务(返回 429 Too Many Requests)或排队等待。常见算法:令牌桶、漏桶、计数器。
Sentinel 实战
@SentinelResource(value = "getUser", blockHandler = "handleBlock", fallback = "getUserFallback")
public User getUser(Long id) {
// 业务逻辑
}
public User handleBlock(Long id, BlockException ex) {
// 限流或熔断时的处理
return new User(-1L, "系统繁忙");
}
public User getUserFallback(Long id, Throwable t) {
// 所有异常的处理(包括业务异常)
return new User(-1L, "服务降级");
}
配置限流规则:
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("getUser");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100); // 每秒 100 QPS
rules.add(rule);
FlowRuleManager.loadRules(rules);