Java业务场景(高并发+高可用+分布式)

高并发场景(电商大促、秒杀、直播)

限流: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加款同时成功或同时失败)

分布式事务Seata(AT)

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次数、内存区域分布)
相关推荐
晨曦夜月2 小时前
头文件与目标文件的关系
linux·开发语言·c++
白仑色2 小时前
java中的anyMatch和allMatch方法
java·linux·windows·anymatch·allmatch
刃神太酷啦2 小时前
C++ list 容器全解析:从构造到模拟实现的深度探索----《Hello C++ Wrold!》(16)--(C/C++)
java·c语言·c++·qt·算法·leetcode·list
wearegogog1232 小时前
C# 条码打印程序(一维码 + 二维码)
java·开发语言·c#
码农阿豪2 小时前
用 PlaylistDL 攒私人音乐库?加个 cpolar,出门在外也能随时听!
java
LaughingDangZi2 小时前
vue+java分离项目实现微信公众号开发全流程梳理
java·前端·后端
9527(●—●)2 小时前
windows系统python开发pip命令使用(菜鸟学习)
开发语言·windows·python·学习·pip
爬山算法2 小时前
Netty(14)如何处理Netty中的异常和错误?
java·前端·数据库
松涛和鸣2 小时前
32、Linux线程编程
linux·运维·服务器·c语言·开发语言·windows