Java流量突增案例

wen java案例 2

本文目录导读:

Java流量突增案例

  1. 案例一:双11大促流量突增导致服务雪崩
  2. 案例二:热点商品抢购导致的线程池耗尽
  3. 案例三:日志打爆磁盘IO
  4. 流量突增排查工具清单
  5. 通用优化建议

我来分享几个典型的Java流量突增案例,以及对应的排查思路和解决方案。

双11大促流量突增导致服务雪崩

场景描述

  • 背景:电商系统,双11零点促销活动
  • 现象:流量从平常的1000 QPS突增到10000 QPS,系统响应时间从50ms飙升到5秒,部分节点出现OOM

排查过程

// 1. 首先查看系统整体状态
jstat -gcutil <pid> 1000  // 查看GC情况
jmap -heap <pid>         // 查看堆内存使用
top -Hp <pid>            // 查看线程CPU占用
// 发现的问题:
// - Full GC频繁,达到每分钟50次
// - 老年代使用率99%
// - CPU使用率飙升至95%

根因分析

// 问题代码示例
public class OrderService {
    // 问题1:无限缓存用户信息
    private static Map<Long, UserInfo> userCache = new HashMap<>();
    // 问题2:同步调用过多
    public Order createOrder(OrderRequest request) {
        // 同步调用库存服务(2ms)
        StockInfo stock = stockClient.queryStock(request.getSkuId());
        // 同步调用用户服务(3ms)
        UserInfo user = userClient.queryUser(request.getUserId());
        // 同步调用优惠券服务(2ms)
        CouponInfo coupon = couponClient.queryCoupon(request.getUserId());
        // 同步调用支付服务(1ms)
        PayInfo pay = payClient.queryPay(request.getOrderId());
        // 总耗时:8ms,但流量突增时每个服务都会变慢
        return buildOrder(stock, user, coupon, pay);
    }
}

解决方案

// 方案1:缓存本地化
@Component
public class UserCacheManager {
    private LoadingCache<Long, UserInfo> cache = CacheBuilder.newBuilder()
        .maximumSize(10000)
        .expireAfterWrite(5, TimeUnit.MINUTES)
        .build(new CacheLoader<Long, UserInfo>() {
            @Override
            public UserInfo load(Long userId) {
                return userClient.queryUser(userId);
            }
        });
    public UserInfo getUser(Long userId) {
        try {
            return cache.get(userId);
        } catch (Exception e) {
            return userClient.queryUser(userId);
        }
    }
}
// 方案2:异步化改造
public CompletableFuture<Order> createOrderAsync(OrderRequest request) {
    CompletableFuture<StockInfo> stockFuture = 
        CompletableFuture.supplyAsync(() -> stockClient.queryStock(request.getSkuId()));
    CompletableFuture<UserInfo> userFuture = 
        CompletableFuture.supplyAsync(() -> userClient.queryUser(request.getUserId()));
    CompletableFuture<CouponInfo> couponFuture = 
        CompletableFuture.supplyAsync(() -> couponClient.queryCoupon(request.getUserId()));
    CompletableFuture.allOf(stockFuture, userFuture, couponFuture).join();
    return CompletableFuture.completedFuture(
        buildOrder(stockFuture.get(), userFuture.get(), couponFuture.get(), null)
    );
}
// 方案3:限流熔断
@Bean
public RateLimiter rateLimiter() {
    return RateLimiter.create(5000); // 每秒钟只允许5个请求
}
public Order createOrderWithLimit(OrderRequest request) {
    if (!rateLimiter.tryAcquire(100, TimeUnit.MILLISECONDS)) {
        throw new BusyException("系统繁忙,请稍后重试");
    }
    // 业务逻辑
}

热点商品抢购导致的线程池耗尽

场景描述

  • 现象:核心线程池全部被占满,新请求排队等待,甚至出现RejectedExecutionException
  • 特征:某一个SKU的请求量特别大

排查过程

// 查看线程池状态
ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPoolTaskExecutor.getThreadPoolExecutor();
executor.getActiveCount();    // 当前活跃线程数
executor.getQueue().size();   // 队列大小
executor.getPoolSize();       // 线程池大小
// 通过Arthas查看线程状态
thread -n 3 -v   // 查看最忙的3个线程
// 发现大量线程阻塞在:redis.clients.jedis.Jedis.get()

解决方案

// 方案1:热点Key隔离
public class HotKeyAwareService {
    private static final int HOT_THRESHOLD = 100;
    private volatile Map<String, AtomicInteger> hotKeyCounter = new ConcurrentHashMap<>();
    private ExecutorService hotKeyExecutor = Executors.newFixedThreadPool(50);
    private ExecutorService normalExecutor = Executors.newFixedThreadPool(200);
    public void processRequest(String skuId, Request request) {
        AtomicInteger counter = hotKeyCounter.computeIfAbsent(skuId, k -> new AtomicInteger(0));
        if (counter.incrementAndGet() > HOT_THRESHOLD) {
            // 热点SKU,使用独立线程池
            hotKeyExecutor.submit(() -> handleHotRequest(skuId, request));
        } else {
            // 正常请求
            normalExecutor.submit(() -> handleNormalRequest(skuId, request));
        }
    }
}
// 方案2:布隆过滤器+多级缓存
public class CacheStrategy {
    private BloomFilter<String> hotSkuFilter = BloomFilter.create(Funnels.stringFunnel(), 10000, 0.01);
    private LoadingCache<String, Object> localCache = cacheConfig.createLocalCache();
    private RedisTemplate<String, Object> redisTemplate;
    public Object getSkuInfo(String skuId) {
        // 1. 本地缓存
        Object localData = localCache.getIfPresent(skuId);
        if (localData != null) return localData;
        // 2. Redis缓存
        Object redisData = redisTemplate.opsForValue().get("sku:" + skuId);
        if (redisData != null) {
            localCache.put(skuId, redisData);
            return redisData;
        }
        // 3. 数据库查询(加分布式锁防止缓存击穿)
        String lockKey = "sku:lock:" + skuId;
        boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 3, TimeUnit.SECONDS);
        if (lock) {
            try {
                Object dbData = queryFromDB(skuId);
                redisTemplate.opsForValue().set("sku:" + skuId, dbData, 10, TimeUnit.MINUTES);
                return dbData;
            } finally {
                redisTemplate.delete(lockKey);
            }
        }
        // 等待重试
        try { Thread.sleep(50); } catch (InterruptedException e) {}
        return redisTemplate.opsForValue().get("sku:" + skuId);
    }
}

日志打爆磁盘IO

场景描述

  • 现象:磁盘IO使用率100%,应用响应变慢
  • 原因:流量突增时,错误日志疯狂打印,特别是DEBUG级别的日志

排查过程

// 查看磁盘和日志情况
df -h           // 查看磁盘使用
iostat -x 1     // 查看IO情况
du -sh logs/*   // 查看各日志文件大小
// 通过日志分析工具查看
grep -c "ERROR" error.log | tail -n 100

解决方案

// 方案1:日志分级和限流
@Component
public class LoggingInterceptor {
    private static final org.slf4j.Logger LOGGER = 
        LoggerFactory.getLogger(LoggingInterceptor.class);
    // 限流日志
    private RateLimiter errorLogLimiter = RateLimiter.create(10);
    private RateLimiter warnLogLimiter = RateLimiter.create(5);
    public void logError(String traceId, String message, Throwable e) {
        if (errorLogLimiter.tryAcquire()) {
            // 避免打印堆栈过多
            LOGGER.error("traceId: {}, error: {}", traceId, message);
        } else {
            // 记录到内存缓冲区,定期汇总
            logBuffer.add(new LogEntry(traceId, message));
        }
    }
}
// 方案2:异步日志+批量写入
<configuration>
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>8192</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <neverBlock>true</neverBlock>
        <appender-ref ref="FILE"/>
    </appender>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <logger name="com.example" level="INFO"/>
    <logger name="org.springframework" level="WARN"/>
    <root level="INFO">
        <appender-ref ref="ASYNC"/>
    </root>
</configuration>

流量突增排查工具清单

# 1. 系统层面
top                     # CPU使用情况
free -m                 # 内存使用
vmstat 1                # 系统活动
iostat -x 1            # IO状态
netstat -ant | wc -l   # 连接数
# 2. JVM层面
jps                     # 查看java进程
jstat -gcutil <pid> 1000 # GC情况
jmap -heap <pid>        # 堆内存详情
jstack <pid> > dump.log # 线程栈
# 3. 应用层面
curl -X GET /actuator/metrics  # Spring Boot指标
curl -X GET /actuator/health   # 健康检查
# 4. 中间件
redis-cli info         # Redis状态
kafka-consumer-groups --describe --group <group> # Kafka消费情况

通用优化建议

// 1. 配置好连接池
@Configuration
public class ConnectionPoolConfig {
    @Bean
    public HikariDataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setMaximumPoolSize(50);
        config.setMinimumIdle(10);
        config.setConnectionTimeout(30000);
        config.setMaxLifetime(1800000);
        // 关键:设置连接泄漏检测
        config.setLeakDetectionThreshold(60000);
        return new HikariDataSource(config);
    }
}
// 2. 使用内嵌缓存+Caffeine
@Configuration
public class CacheConfig {
    @Bean
    public Caffeine caffeineConfig() {
        return Caffeine.newBuilder()
            .maximumSize(10000)
            .expireAfterWrite(60, TimeUnit.SECONDS)
            .recordStats();
    }
}
// 3. 服务降级策略
@Component
public class DegradeStrategy {
    private boolean isHealthy = true;
    private CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("myservice");
    public Object callWithDegrade() {
        return circuitBreaker.executeSupplier(() -> {
            if (!isHealthy) return fallback();
            return doBusiness();
        });
    }
    private Object fallback() {
        return Collections.emptyMap(); // 返回缓存或默认值
    }
}
  1. 提前预防:流量突增前做好压测,预先把线程池、连接池调大
  2. 快速定位:使用Arthas、JProfiler等工具快速定位瓶颈
  3. 弹性设计:使用Hystrix/Resilience4j做好熔断降级
  4. 缓存优先:尽可能使用本地缓存+Redis多级缓存
  5. 异步化:非核心链路使用异步处理
  6. 监控报警:配置好Prometheus+Grafana,当流量达到警戒线时自动告警

面试重点:能针对突增流量设计出合理的解决方案,并解释为什么这么设计,以及考虑系统的高可用性和降级策略。

上一篇双十一案例

下一篇Java大促案例

抱歉,评论功能暂时关闭!