高并发场景(电商大促、秒杀、直播)
限流:Sentinel注解式限流(应对秒杀大促流量,如电商秒杀接口需要限制每秒最多1000次请求,防止系统过载)
Sentinel流量控制(熔断、降级、限流)
xml
<dependencyManagement>
<dependencies>
<!-- Spring Cloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.5.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Sentinel 核心依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- Sentinel Nacos 数据源(动态规则持久化) -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- Sentinel 控制台通信(可选,starter 已包含) -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Sentinel 核心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- Sentinel Nacos 数据源(动态规则从 Nacos 拉取) -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- Web 依赖(提供 HTTP 接口) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
yml
spring:
application:
name: seckill-service # 服务名(用于 Dashboard 识别)
cloud:
sentinel:
transport:
dashboard: 192.168.1.100:8080 # Dashboard 部署 IP:端口(内网地址)
port: 8719 # 客户端与 Dashboard 通信端口(默认 8719,冲突自动+1)
eager: true # 饥饿加载(服务启动后立即注册到 Dashboard,无需首次请求)
filter:
enabled: false # 关闭 URL 自动埋点(优先用 @SentinelResource 手动埋点)
java
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/seckill")
public class SeckillController {
/**
* 秒杀接口:资源名 "seckill_order"(需与规则中的 resource 一致)
* 限流:QPS 阈值 500(日常),降级:业务异常时返回兜底数据
*/
@GetMapping("/order")
@SentinelResource(
value = "seckill_order",//资源名(核心关联标识)
blockHandler = "handleSeckillBlock",// 限流/熔断时执行的方法
fallback = "handleSeckillFallback",// 业务异常时执行的方法(如库存不足)
exceptionsToIgnore = {IllegalArgumentException.class}// 忽略的异常(不触发 fallback)
)
public String seckillOrder(@RequestParam Long productId,@RequestParam Long userId){
// 核心业务逻辑:校验库存、扣减库存、创建订单(简化示例)
if (productId <= 0 || userId <= 0) {
throw new IllegalArgumentException("参数错误"); // 被 exceptionsToIgnore 忽略,不触发 fallback
}
// 模拟库存不足异常
if (productId == 999L) {
throw new RuntimeException("商品库存不足"); // 触发 fallback
}
return "秒杀成功!订单ID:" + System.currentTimeMillis();
}
// 限流/熔断处理逻辑(参数需与原方法一致,末尾加 BlockException)
public String handleSeckillBlock(Long productId, Long userId, BlockException e) {
return "秒杀太火爆,请稍后再试!(限流/熔断触发)";
}
// 业务异常处理逻辑(参数需与原方法一致,末尾加 Throwable)
public String handleSeckillFallback(Long productId, Long userId, Throwable t) {
return "秒杀失败:" + t.getMessage() + "(已为您推荐其他商品)";
}
}
加载静态规则(JSON 文件,基础防护)
在项目 resources目录下创建 sentinel-static-rules.json,定义日常基础规则(如秒杀接口 QPS 500):
json
[
{
"resource": "seckill_order", // 资源名(与 @SentinelResource 的 value 一致)
"limitApp": "default", // 不区分调用来源
"grade": 1, // 限流类型:1=QPS,0=线程数
"count": 500, // 阈值:日常 QPS 500
"strategy": 0, // 流控策略:0=直接(当前资源)
"controlBehavior": 0, // 流控效果:0=快速失败(直接拒绝)
"clusterMode": false // 非集群模式
},
{
"resource": "order_query", // 订单查询接口资源名(假设存在)
"limitApp": "default",
"grade": 1,
"count": 2000, // 日常 QPS 2000
"strategy": 0,
"controlBehavior": 2, // 流控效果:2=排队等待(匀速通过)
"maxQueueingTimeMs": 500 // 最长排队 500ms
}
]
在服务启动时加载静态规则(通过 @PostConstruct初始化):
java
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Component
public class SentinelStaticRuleLoader {
@PostConstruct //服务启动时执行
public void loadStaticRules()throws Exception{
//读取classpath下的json
ClassPathResource resource = new ClassPathResource("sentinel-static-rules.json");
String rulesJson = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
//解析JSON为FlowRule列表(流控规则)
List<> rules = JSON.parseArray(rulesJson,FlowRule.class);
//加载规则到Sentinel内存(覆盖已有规则)
FlowRuleManager.loadRules(rules);
System.out.println("静态规则加载完成,共 " + rules.size() + " 条");
}
}
json
[
{
"resource": "seckill_order", // 资源名(必须与代码中@SentinelResource的value一致)
"limitApp": "default", // 流控针对的调用方(default表示不区分来源)
"grade": 1, // 限流阈值类型:0=线程数,1=QPS(常用QPS)
"count": 1000, // 阈值(QPS=1000次/秒)
"strategy": 0, // 流控策略:0=直接(当前资源),1=关联,2=链路
"controlBehavior": 0, // 流控效果:0=快速失败,1=Warm Up,2=排队等待
"clusterMode": false // 是否为集群模式(单实例为false)
},
{
"resource": "order_query", // 另一个资源的限流规则
"limitApp": "default",
"grade": 1,
"count": 2000,
"strategy": 0,
"controlBehavior": 2, // 排队等待(匀速通过)
"maxQueueingTimeMs": 500 // 最长排队时间500ms
}
]
缓存穿透:布隆过滤器(防止恶意查询不存在的数据,频繁查询不存在的商品导致缓存失效、DB压力骤增)
java
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
@Service
public class ProductService {
private final StringRedisTemplate redisTemplate;
//布隆过滤器:预计存储100万商品ID,误判率0.01%
private BloomFilter<Long> productIdFilter;
public ProductService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
//初始化布隆过滤器(项目启动时加载所有合法商品ID)
@PostConstruct
public void initBloomFilter(){
productIdFilter = BloomFilter.create(
Funnels.longFunnel(),
1_000_000, // 预期插入数量
0.0001 // 误判率
);
//模拟从DB中加载所有商品ID(实际中可批量查询)
for(long id = 1;id <= 1_000_000; id++){
productIdFilter.put(id);
}
}
public String getProductDetail(Long productId){
//1.布隆过滤器拦截非法ID(不存在的商品ID直接返回)
if(!productIdFilter.mightContain(productId)){
return "商品不存在";
}
//2.缓存查询(Redis Key:product:{id})
String cacheKey = "product:" + productId;
String detail = redisTemplate.opsForValue().get(cacheKey);
if(detail != null){
return detail.isEmpty() ? "商品不存在" : detail; // 空值缓存标记不存在
}
//3.DB查询(仅缓存未命中时执行)
String dbDetail = queryFromDb(productId);
if(dbDetail == null){
//缓存空值(防止缓存穿透),过期时间设短(5分钟)
redisTemplate.opsForValue().set(cacheKey, "", 5, TimeUnit.MINUTES);
return "商品不存在";
}else{
redisTemplate.opsForValue().set(cacheKey, dbDetail, 30, TimeUnit.MINUTES);
return dbDetail;
}
}
private String queryFromDb(Long productId){
//模拟DB查询(实际中替换为DAO调用)
return "商品详情:" + productId;
}
}
高可用场景(金融交易、支付系统)
分布式事务:Seata TCC模式(金融转账强一致,用户A向用户B转账100元,需保证A扣款、B加款同时成功或同时失败)
SpringCloud-Alibaba之Seata处理分布式事务
java
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@LocalTCC //标记为TCC参与者
public class TransferService{
//Try阶段:预留资源(冻结A的100元,检查B账户是否存在)
@TwoPhaseBusinessAction(
name = "transferTcc",
commitMethod = "confirm",
rollbackMethod = "cancel"
)
@Transactional
public boolean tryTransfer(BusinessActionContext context,
@BusinessActionContextParameter(paramName = "fromUserId") Long fromUserId,
@BusinessActionContextParameter(paramName = "toUserId") Long toUserId,
@BusinessActionContextParameter(paramName = "amount") Integer amount){
//1.冻结A的余额(扣减可用余额,增加冻结余额)
accountDao.freezeBalance(fromUserId,amount);
//2.检查B账户是否有效(不存在则Try失败)
if(!accountDao.checkUserExist(toUserId)){
throw new RuntimeException("收款账户不存在");
}
//3.记录事务日志(用于Confirm/Cancel阶段核对)
tccLogDao.insert(context.getXid(),"TRANSFER","TRYING")
return true;
}
//Confirm阶段,确认执行业务(真正扣减A,增加B)
public boolean confirm(BusinessActionContext context){
Long fromUserId = Long.parseLong(context.getActionContext().toString());
Long toUserId = Long.parseLong(context.getActionContext("toUserId").toString());
Integer amount = Integer.parseInt(context.getActionContext("amount").toString());
//1.扣减A的冻结金额(可用金额不变,冻结减少)
accountDao.deductFreeze(fromUserId,amount);
//2.增加B的可用余额
accountDao.addBalance(toUserId,amount);
//3.更新事务日志为"CONFIRMED"
tccLogDao.updateStatus(context.getXid(),"CONFIRMED");
return true;
}
//Cancel阶段:取消执行业务(释放A的冻结金额)
public boolean cancel(BusinessActionContext context){
Long fromUserId = Long.parseLong(context.getActionContext("fromUserId").toString());
Integer amount = Integer.parseInt(context.getActionContext("amount").toString());
//释放A的冻结金额(可用余额恢复)
accountDao.releaseFreeze(fromUserId, amount);
//更新事务日志为"CANCELLED"
tccLogDao.updateStatus(context.getXid(), "CANCELLED");
return true;
}
}
复杂业务建模(企业级ERP、供应链系统)
DDD聚合根与领域服务(电商订单域,订单创建需校验库存、计算优惠、生成订单号,涉及多个实体协作)
java
//值对象:订单号(不可变,含业务含义)
public record OrderNo(String prefix,long sequence){
public static OrderNo generate(String prefix){
//从实际数据、redis获取自增序列
return new OrderNo(prefix,System.currentTimeMillis() % 1000000);
}
@Override
public String toString(){
return prefix + "-" + String.format("%06d", sequence);
}
}
java
// 实体:订单项(属于订单聚合的一部分)
public class OrderItem{
private Long skuId; //SKU ID
private int quantity; //数量
private Money price; //单价(值对象)
private Money subtotal; //小计(值对象)
//计算小计(领域行为)
public void calculateSubtotal(){
this.subtotal = price.multiply(quantity);
}
}
java
//聚合根:订单(包含订单项,对外提供核心操作)
public class Order{
private OrderNo orderNo; // 订单号(值对象)
private UserId userId; // 用户ID(值对象)
private List<OrderItem> items; // 订单项(实体集合)
private OrderStatus status; // 订单状态(枚举)
private Money totalAmount; // 总金额(值对象)
//私有构造:通过工厂方法创建
private Order(UserId userId, List<OrderItem> items){
this.userId = userId;
this.items = items;
this.orderNo = OrderNo.generate("ORD");
this.status = OrderStatus.CREATING;
calculateTotalAmount(); // 计算总金额(聚合内逻辑)
}
// 工厂方法:创建订单(校验前置条件)
public static Order create(UserId userId,List<OrderItem> items){
if(items.isEmpty()){
throw new IllegalArgumentException("订单项不能为空");
}
Order order = new Order(userId,items);
order.status = OrderStatus.CREATED;
return order;
}
//领域行为:计算总金额(聚合内实体协作)
private void calculateTotalAmount(){
this.totalAmount = items.stream()
.map(OrderItem::getSubtotal)
.reduce(Money.ZERO,Money::add);
}
//禁止外部直接修改订单项(保证聚合一致性)
public List<OrderItem> getItems(){
return Collections.unmodifiableList(items);
}
}
java
// 领域服务:订单创建协调(跨聚合逻辑)
@Service
public class OrderDomainService {
@Autowired
private InventoryService inventoryService;//库存领域服务(外部依赖)
@Autowired
private PromotionService promotionService;//优惠领域服务
public Order createOrder(UserId userId, List<SkuId> skuIds, List<Integer> quantities){
//1.校验并锁定库存(领域服务调用)
inventoryService.lockStock(skuIds,quantities);
//2.构建订单项(值对象转换)
List<OrderItem> items = new ArrayList<>();
for (int i = 0; i < skuIds.size(); i++) {
SkuId skuId = skuIds.get(i);
int qty = quantities.get(i);
Money price = inventoryService.getSkuPrice(skuId); // 获取SKU价格
OrderItem item = new OrderItem(skuId, qty, price);
item.calculateSubtotal(); // 计算小计
items.add(item);
}
//3.应用优惠(领域服务调用)
Money discount = promotionService.calculateDiscount(userId, items);
Money totalAmount = items.stream().map(OrderItem::getSubtotal).reduce(Money.ZERO, Money::add).subtract(discount);
//创建订单聚合根
Order order = Order.create(userId, items);
order.setTotalAmount(totalAmount); // 设置优惠后金额
return order;
}
}
秒杀扣减库存需要保证原子性,防止超卖
java
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
@Service
public class StockService{
private RedissonClient redissonClient;
@PostConstruct
public void initRedisson(){
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
this.redissonClient = Redisson.create(config);
}
public boolean deductStock(Long productId,int quantity){
//锁key:stock_lock:{productId},确保同一商品串行扣减
RLock lock = redissonClient.getLock("stock_lock" + productId);
try{
//尝试加锁:最多等待3秒,锁自动释放时间10秒(看门狗会续期)
boolean locked = lock.tryLock(3,10,TimeUnit.SECONDS);
if(!locked){
return false;//获取锁失败(可能已被其他线程持有)
}
//核心逻辑:查询库存->扣减->更新(需要保证原子性,此处简化伪代码)
int stock = stockDao.getStock(productId);
if(stock >= quantity){
stockDao.updateStock(product,stock - quantity);
return true;
}
return false;
}catch(InterruptedException e){
Thread.currentThread().interrupt();
return false;
}finally{
}
}
}
成本与效率优化(降本增效)
并发编程:线程池优化(避免OOM与资源耗尽,异步处理订单日志,需合理配置线程池参数,防止任务堆积导致OOM)
java
import java.util.concurrent.*;
public class AsyncOrderLogger{
//线程池参数:核心线程数=CPU核心数,最大线程数=2*CPU核心数,队列容量=1000(有界队列)
private static final int CPU_CORE = Runtime.getRuntime().availableProcessors();
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
CPU_CORE, //corePoolSize核心线程数(常驻)
2 * CPU_CORE, // maximumPoolSize:最大线程数(峰值)
60L, TimeUnit.SECONDS, // keepAliveTime:非核心线程空闲存活时间
new ArrayBlockingQueue<>(1000),// workQueue:有界队列(防止OOM)
new ThreadPoolExecutor.CallerRunsPolicy()//拒绝策略:调用者线程执行(降级)
);
//异步记录订单日志
public void logOrder(Order order){
executor.submit(()->{
try{
//模拟日志写入(如ES、文件)
String log = String.format("订单[%s]创建成功,金额:%s", order.getOrderNo(), order.getTotalAmount());
System.out.println(log);
}catch(Exception e){
// 异常处理(如重试、告警)
System.err.println("日志写入失败:" + e.getMessage());
}
});
}
//优雅关闭线程池(应用停机时调用)
public void shutdown(){
executor.shutdown();//不再接受新事物
try{
if(!executor.awaitTermination(30,TimeUnit.SECONDS)){
executor.shutdownNow(); // 强制终止未完成任务
}
}catch(InterruptedException e){
executor.shutdownNow();
}
}
}
jvm启动参数(生产环境推荐)
bash
# 堆内存:-Xms4g -Xmx4g(固定大小避免动态调整开销)
# 新生代:Eden:S0:S1=8:1:1(-XX:NewRatio=1表示新生代:老年代=1:1,即新生代占堆50%)
# 元空间:-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m(避免动态扩容)
# GC日志:-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/logs/gc-%t.log(按时间戳命名)
# OOM时Dump堆快照:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/heapdump.hprof
java -jar app.jar \
-Xms4g -Xmx4g \
-XX:NewRatio=1 \
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/logs/gc-%t.log \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/heapdump.hprof
Arthas诊断OOM(实时分析):
bash
# 1. 查看堆内存占用Top5的类(定位大对象)
arthas> heapdump --live /tmp/live_heap.hprof # 导出存活对象堆快照
arthas> ognl '@java.lang.System@getProperty("user.dir")' # 查看当前路径
arthas> dashboard # 实时监控CPU/内存/线程
# 2. 分析线程阻塞(如死锁)
arthas> thread -b # 查找阻塞其他线程的线程
arthas> jvm # 查看JVM信息(GC次数、内存区域分布)