综合141-160
141. 什么是 AOP?
展开 中等 VIP 后端 Spring
AOP定义: AOP(Aspect-Oriented Programming)面向切面编程,是一种编程范式,通过预编译方式和运行期动态代理实现程序功能的统一维护。
核心概念:
1. 切面(Aspect)
- 横切关注点的模块化
- 包含通知和切点的定义
2. 连接点(Join Point)
- 程序执行过程中能够应用通知的所有点
- Spring AOP中只支持方法执行连接点
3. 切点(Pointcut)
- 匹配连接点的断言
- 定义在哪些连接点上应用通知
4. 通知(Advice)
- 在特定连接点执行的代码
- 五种类型:前置、后置、返回、异常、环绕
5. 目标对象(Target Object)
- 被一个或多个切面所通知的对象
6. 织入(Weaving)
- 将切面与目标对象链接,创建代理对象的过程
Spring AOP实现:
注解方式:
@Aspect
@Component
public class LoggingAspect {
// 切点定义
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// 前置通知
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
log.info("Method {} called with args: {}",
joinPoint.getSignature().getName(),
Arrays.toString(joinPoint.getArgs()));
}
// 后置通知
@After("serviceLayer()")
public void logAfter(JoinPoint joinPoint) {
log.info("Method {} execution completed",
joinPoint.getSignature().getName());
}
// 返回通知
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
log.info("Method {} returned: {}",
joinPoint.getSignature().getName(), result);
}
// 异常通知
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
log.error("Method {} threw exception: {}",
joinPoint.getSignature().getName(), ex.getMessage());
}
// 环绕通知
@Around("serviceLayer()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
log.info("Method {} executed in {} ms",
joinPoint.getSignature().getName(),
endTime - startTime);
return result;
} catch (Exception e) {
log.error("Method {} execution failed",
joinPoint.getSignature().getName(), e);
throw e;
}
}
}
切点表达式:
// 执行方法切点
@Pointcut("execution(public * com.example.service.*.*(..))")
// 注解切点
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
// 类型切点
@Pointcut("within(com.example.service.*)")
// Bean名称切点
@Pointcut("bean(*Service)")
// 组合切点
@Pointcut("serviceLayer() && @annotation(org.springframework.cache.annotation.Cacheable)")
实际应用场景:
1. 日志记录
@Aspect
@Component
public class AuditAspect {
@Around("@annotation(Auditable)")
public Object auditMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
// 记录操作日志
AuditLog auditLog = AuditLog.builder()
.operation(className + "." + methodName)
.startTime(LocalDateTime.now())
.build();
try {
Object result = joinPoint.proceed();
auditLog.setStatus("SUCCESS");
return result;
} catch (Exception e) {
auditLog.setStatus("FAILED");
auditLog.setErrorMessage(e.getMessage());
throw e;
} finally {
auditLog.setEndTime(LocalDateTime.now());
auditLogService.save(auditLog);
}
}
}
2. 权限控制
@Aspect
@Component
public class SecurityAspect {
@Before("@annotation(requiresRole)")
public void checkPermission(JoinPoint joinPoint, RequiresRole requiresRole) {
String[] roles = requiresRole.value();
String currentUser = SecurityContextHolder.getContext()
.getAuthentication()
.getName();
if (!userService.hasAnyRole(currentUser, roles)) {
throw new AccessDeniedException("Access denied for user: " + currentUser);
}
}
}
// 使用示例
@Service
public class UserService {
@RequiresRole({"ADMIN", "USER_MANAGER"})
public void deleteUser(Long userId) {
// 删除用户逻辑
}
}
3. 缓存管理
@Aspect
@Component
public class CacheAspect {
@Around("@annotation(cacheable)")
public Object cache(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
String key = generateCacheKey(joinPoint, cacheable.key());
// 尝试从缓存获 取
Object cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return cached;
}
// 执行方法并缓存结果
Object result = joinPoint.proceed();
redisTemplate.opsForValue().set(key, result,
Duration.ofSeconds(cacheable.expire()));
return result;
}
}
AOP优势:
- 减少代码重复
- 提高模块化程度
- 易于维护和扩展
- 关注点分离
142. 什么是服务降级?
展开 中等 VIP Spring Cloud 微服务 后端 服务降级
服务降级定义: 服务降级是指在系统压力剧增或部分服务不可用时,主动关闭部分非核心功能或返回简化结果,以保证核心功能正常运行的一种保护机制。
降级策略:
1. 功能降级
- 关闭非核心功能
- 简化业务逻辑
- 返回默认值或缓存数据
2. 性能降级
- 降低数据精度
- 减少查询范围
- 简化计算逻辑
3. 容量降级
- 限制并发请求数
- 丢弃部分请求
- 延迟处理非紧急任务
实现方式:
1. Hystrix实现
@Component
public class UserService {
@HystrixCommand(
fallbackMethod = "getUserFallback",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
}
)
public User getUser(Long userId) {
// 调用远程服务
return userClient.getUser(userId);
}
// 降级方法
public User getUserFallback(Long userId) {
return User.builder()
.id(userId)
.name("默认用户")
.email("default@example.com")
.build();
}
// 降级原因
public User getUserFallback(Long userId, Throwable ex) {
log.error("Get user failed, fallback triggered", ex);
return getUserFallback(userId);
}
}
2. Sentinel实现
@Service
public class OrderService {
@SentinelResource(
value = "getOrder",
fallback = "getOrderFallback",
blockHandler = "getOrderBlocked"
)
public Order getOrder(Long orderId) {
return orderClient.getOrder(orderId);
}
// 降级方法(异常降级)
public Order getOrderFallback(Long orderId, Throwable ex) {
log.warn("Get order fallback triggered for orderId: {}", orderId, ex);
return Order.builder()
.id(orderId)
.status("UNKNOWN")
.build();
}
// 限流降级
public Order getOrderBlocked(Long orderId, BlockException ex) {
log.warn("Get order blocked for orderId: {}", orderId);
return Order.builder()
.id(orderId)
.status("BUSY")
.message("系统繁忙,请稍后重试")
.build();
}
}
3. 自定义降级实现
@Component
public class DegradationManager {
private final Map<String, Boolean> degradationFlags = new ConcurrentHashMap<>();
private final RedisTemplate<String, Object> redisTemplate;
// 检查是否需要降级
public boolean shouldDegrade(String service) {
// 本地缓存检查
Boolean localFlag = degradationFlags.get(service);
if (localFlag != null) {
return localFlag;
}
// Redis配置检查
Boolean redisFlag = (Boolean) redisTemplate.opsForValue()
.get("degradation:" + service);
if (redisFlag != null) {
degradationFlags.put(service, redisFlag);
return redisFlag;
}
return false;
}
// 动态设置降级状态
public void setDegradation(String service, boolean degrade) {
degradationFlags.put(service, degrade);
redisTemplate.opsForValue().set("degradation:" + service, degrade);
}
}
@Service
public class ProductService {
@Autowired
private DegradationManager degradationManager;
public ProductDetails getProductDetails(Long productId) {
if (degradationManager.shouldDegrade("product-detail")) {
// 降级:返回基本信息
return getBasicProductInfo(productId);
}
// 正常:返回完整信息
return getFullProductDetails(productId);
}
private ProductDetails getBasicProductInfo(Long productId) {
// 从缓存或数据库获取基本信息
Product product = productRepository.findById(productId);
return ProductDetails.builder()
.id(product.getId())
.name(product.getName())
.price(product.getPrice())
.build();
}
private ProductDetails getFullProductDetails(Long productId) {
// 获取完整信息(可能调用多个服务)
Product product = productRepository.findById(productId);
List<Review> reviews = reviewService.getReviews(productId);
List<Recommendation> recommendations = recommendationService.getRecommendations(productId);
return ProductDetails.builder()
.id(product.getId())
.name(product.getName())
.price(product.getPrice())
.description(product.getDescription())
.reviews(reviews)
.recommendations(recommendations)
.build();
}
}
降级级别:
1. 页面降级
@Controller
public class HomeController {
@GetMapping("/")
public String home(Model model) {
if (degradationManager.shouldDegrade("homepage")) {
// 返回简化页面
return "home-simple";
}
// 返回完整页面
loadRecommendations(model);
loadPersonalizedContent(model);
return "home-full";
}
}
2. 接口降级
@RestController
public class ApiController {
@GetMapping("/api/search")
public SearchResult search(@RequestParam String keyword) {
if (degradationManager.shouldDegrade("search")) {
// 降级:返回缓存结果
return getCachedSearchResult(keyword);
}
// 正常:实时搜索
return searchService.search(keyword);
}
}
3. 数据降级
@Service
public class ReportService {
public Report generateReport(String type, Date startDate, Date endDate) {
if (degradationManager.shouldDegrade("report")) {
// 降级:减少数据精度
return generateSimplifiedReport(type, startDate, endDate);
}
return generateDetailedReport(type, startDate, endDate);
}
private Report generateSimplifiedReport(String type, Date startDate, Date endDate) {
// 按天聚合而不是按小时
return reportRepository.findDailyReport(type, startDate, endDate);
}
}
降级监控:
@Component
public class DegradationMonitor {
@EventListener
public void onDegradationTriggered(DegradationEvent event) {
// 记录降级事件
log.warn("Degradation triggered for service: {}, reason: {}",
event.getService(), event.getReason());
// 发送告警
alertService.sendAlert("Service degradation", event);
// 记录指标
meterRegistry.counter("degradation.triggered",
"service", event.getService())
.increment();
}
@Scheduled(fixedRate = 30000)
public void checkSystemHealth() {
double cpuUsage = systemMetrics.getCpuUsage();
double memoryUsage = systemMetrics.getMemoryUsage();
if (cpuUsage > 80 || memoryUsage > 85) {
// 自动触发降级
degradationManager.setDegradation("non-essential", true);
} else if (cpuUsage < 60 && memoryUsage < 70) {
// 恢复服务
degradationManager.setDegradation("non-essential", false);
}
}
}
最佳实践:
- 明确核心和非核心功能
- 设计优雅的降级策略
- 建立自动化降级机制
- 监控降级效果
- 及时恢复服务
143. Synchronized 和 ReentrantLock 有什么区别?
展开 中等 Java并发 Java
基本对比:
特性 | synchronized | ReentrantLock |
---|---|---|
锁类型 | 内置锁(监视器锁) | 显式锁 |
使用方式 | 关键字 | API调用 |
释放方式 | 自动释放 | 手动释放 |
可中断性 | 不可中断 | 可中断 |
超时获取 | 不支持 | 支持 |
公平性 | 非公平 | 可选公 平/非公平 |
条件变量 | 单一条件(wait/notify) | 多条件变量 |
性能 | JVM优化,较好 | 用户态实现 |
使用示例对比:
synchronized使用:
public class SynchronizedExample {
private final Object lock = new Object();
private int count = 0;
// 方法同步
public synchronized void increment() {
count++;
}
// 代码块同步
public void decrement() {
synchronized (lock) {
count--;
}
}
// 静态方法同步
public static synchronized void staticMethod() {
// 锁的是类对象
}
public synchronized void waitExample() throws InterruptedException {
while (condition) {
wait(); // 等待条件
}
// 执行逻辑
notifyAll(); // 唤醒所有等待线程
}
}
ReentrantLock使用:
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private int count = 0;
// 基本加锁
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // 必须在finally中释放
}
}
// 可中断锁
public void interruptibleIncrement() throws InterruptedException {
lock.lockInterruptibly();
try {
count++;
} finally {
lock.unlock();
}
}
// 尝试获取锁
public boolean tryIncrement() {
if (lock.tryLock()) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
// 超时获取锁
public boolean timeoutIncrement(long timeout, TimeUnit unit) {
try {
if (lock.tryLock(timeout, unit)) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
// 条件变量使用
public void conditionExample() throws InterruptedException {
lock.lock();
try {
while (someCondition) {
condition.await(); // 等待条件
}
// 执行逻辑
condition.signalAll(); // 唤醒所有等待线程
} finally {
lock.unlock();
}
}
}
高级特性对比:
1. 公平性
// ReentrantLock支持公平锁
ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
ReentrantLock unfairLock = new ReentrantLock(false); // 非公平锁
// synchronized总是非公平的
public synchronized void method() {
// 非公平锁
}
2. 多条件变量
public class MultiConditionExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] buffer = new Object[10];
private int count, putIndex, takeIndex;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == buffer.length) {
notFull.await(); // 等待不满条件
}
buffer[putIndex] = x;
if (++putIndex == buffer.length) putIndex = 0;
++count;
notEmpty.signal(); // 通知不空条件
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await(); // 等待不空条件
}
Object x = buffer[takeIndex];
if (++takeIndex == buffer.length) takeIndex = 0;
--count;
notFull.signal(); // 通知不满条件
return x;
} finally {
lock.unlock();
}
}
}
3. 锁状态查询
public class LockInfoExample {
private final ReentrantLock lock = new ReentrantLock();
public void showLockInfo() {
System.out.println("Hold count: " + lock.getHoldCount());
System.out.println("Queue length: " + lock.getQueueLength());
System.out.println("Is fair: " + lock.isFair());
System.out.println("Is locked: " + lock.isLocked());
System.out.println("Is held by current thread: " + lock.isHeldByCurrentThread());
}
}
性能对比:
1. JVM优化
// synchronized享受JVM层面的优化
// - 偏向锁:大多数情况下锁不存在竞争
// - 轻量级锁:少量竞争时使用CAS
// - 重量级锁:激烈竞争时才升级
public synchronized void optimizedByJVM() {
// JVM会根据竞争情况自动优化
}
2. 用户态实现
// ReentrantLock是Java层面实现
// - 基于AQS(AbstractQueuedSynchronizer)
// - 更多功能但开销稍大
public void reentrantLockMethod() {
lock.lock();
try {
// 用户态实现,功能更丰富
} finally {
lock.unlock();
}
}
选择建议:
使用synchronized的场景:
- 简单的同步需求
- 不需要超时或中断
- 代码简洁性要求高
- JVM自动优化场景
使用ReentrantLock的场景:
- 需要可中断的锁获取
- 需要超时的锁获取
- 需要公平锁
- 需要多个条件变量
- 需要锁状态查询
实际应用示例:
@Service
public class OrderService {
private final Map<String, ReentrantLock> orderLocks = new ConcurrentHashMap<>();
// 使用ReentrantLock实现订单级别的锁
public void processOrder(String orderId) {
ReentrantLock orderLock = orderLocks.computeIfAbsent(orderId, k -> new ReentrantLock());
try {
// 尝试获取锁,最多等待5秒
if (orderLock.tryLock(5, TimeUnit.SECONDS)) {
try {
// 处理订单
handleOrder(orderId);
} finally {
orderLock.unlock();
}
} else {
throw new OrderProcessingException("Order is being processed by another thread");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new OrderProcessingException("Order processing interrupted");
}
}
// 使用synchronized实现简单的计数
private final Object countLock = new Object();
private int processedCount = 0;
public void incrementProcessedCount() {
synchronized (countLock) {
processedCount++;
}
}
}
144. Redis 的 Lua 脚本功能是什么?如何使用?
展开 中等 VIP 后端 Redis
Lua脚本简介: Redis的Lua脚本功能允许在Redis服务器端执行Lua脚本,确保多个Redis操作的原子性,减少网络往返次数,提高性能。
核心特性:
- 原子性:脚本执行期间不会被其他命令打断
- 服务器端执行:减少网络开销
- 可复用:脚本可以被缓存和重复使用
- 灵活性:支持复杂的业务逻辑
基本使用:
1. EVAL命令
# 基本语法
EVAL script numkeys key [key ...] arg [arg ...]
# 简单示例
EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 mykey myvalue
# 复杂示例:原子性增加并获取值
EVAL "
local current = redis.call('get', KEYS[1])
if current == false then
current = 0
else
current = tonumber(current)
end
current = current + tonumber(ARGV[1])
redis.call('set', KEYS[1], current)
return current
" 1 counter 5
2. EVALSHA命令
# 加载脚本获取SHA1
SCRIPT LOAD "return redis.call('get', KEYS[1])"
# 返回: "6b1bf486c81ceb7edf3c093f4c48582e38c0e791"
# 使用SHA1执行脚本
EVALSHA 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 1 mykey
Java客户端使用:
1. Jedis实现
@Component
public class RedisLuaService {
@Autowired
private JedisPool jedisPool;
// 分布式锁脚本
private static final String LOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
// 限流脚本
private static final String RATE_LIMIT_SCRIPT =
"local key = KEYS[1] " +
"local limit = tonumber(ARGV[1]) " +
"local window = tonumber(ARGV[2]) " +
"local current = redis.call('get', key) " +
"if current == false then " +
" redis.call('setex', key, window, 1) " +
" return 1 " +
"elseif tonumber(current) < limit then " +
" return redis.call('incr', key) " +
"else " +
" return 0 " +
"end";
public boolean releaseLock(String lockKey, String lockValue) {
try (Jedis jedis = jedisPool.getResource()) {
Object result = jedis.eval(LOCK_SCRIPT,
Collections.singletonList(lockKey),
Collections.singletonList(lockValue));
return "1".equals(result.toString());
}
}
public boolean isAllowed(String key, int limit, int windowSeconds) {
try (Jedis jedis = jedisPool.getResource()) {
Object result = jedis.eval(RATE_LIMIT_SCRIPT,
Collections.singletonList(key),
Arrays.asList(String.valueOf(limit), String.valueOf(windowSeconds)));
return !"0".equals(result.toString());
}
}
}
2. RedisTemplate实现
@Service
public class RedisScriptService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 原子性增减库存脚本
private static final String STOCK_SCRIPT =
"local stock = redis.call('get', KEYS[1]) " +
"if stock == false then " +
" return {-1, 'Stock not found'} " +
"end " +
"stock = tonumber(stock) " +
"local quantity = tonumber(ARGV[1]) " +
"if stock >= quantity then " +
" redis.call('decrby', KEYS[1], quantity) " +
" return {stock - quantity, 'Success'} " +
"else " +
" return {stock, 'Insufficient stock'} " +
"end";
// 批量操作脚本
private static final String BATCH_SET_SCRIPT =
"for i=1,#KEYS do " +
" redis.call('set', KEYS[i], ARGV[i]) " +
"end " +
"return #KEYS";
public StockResult decreaseStock(String productId, int quantity) {
DefaultRedisScript<List> script = new DefaultRedisScript<>();
script.setScriptText(STOCK_SCRIPT);
script.setResultType(List.class);
List<Object> result = redisTemplate.execute(script,
Collections.singletonList(productId),
String.valueOf(quantity));
if (result != null && result.size() == 2) {
return new StockResult(
Integer.parseInt(result.get(0).toString()),
result.get(1).toString()
);
}
return new StockResult(-1, "Script execution failed");
}
public int batchSet(Map<String, String> keyValues) {
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(BATCH_SET_SCRIPT);
script.setResultType(Long.class);
List<String> keys = new ArrayList<>(keyValues.keySet());
List<String> values = new ArrayList<>(keyValues.values());
Long result = redisTemplate.execute(script, keys, values.toArray());
return result != null ? result.intValue() : 0;
}
}
实际应用场景:
1. 分布式锁
@Component
public class DistributedLock {
// 获取锁脚本
private static final String ACQUIRE_LOCK_SCRIPT =
"if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
" redis.call('expire', KEYS[1], ARGV[2]) " +
" return 1 " +
"else " +
" return 0 " +
"end";
// 释放锁脚本
private static final String RELEASE_LOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
public boolean acquireLock(String lockKey, String lockValue, int expireSeconds) {
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(ACQUIRE_LOCK_SCRIPT);
script.setResultType(Long.class);
Long result = redisTemplate.execute(script,
Collections.singletonList(lockKey),
lockValue, String.valueOf(expireSeconds));
return Long.valueOf(1L).equals(result);
}
public boolean releaseLock(String lockKey, String lockValue) {
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(RELEASE_LOCK_SCRIPT);
script.setResultType(Long.class);
Long result = redisTemplate.execute(script,
Collections.singletonList(lockKey),
lockValue);
return Long.valueOf(1L).equals(result);
}
}
2. 滑动窗口限流
@Component
public class SlidingWindowRateLimit {
private static final String SLIDING_WINDOW_SCRIPT =
"local key = KEYS[1] " +
"local window = tonumber(ARGV[1]) " +
"local limit = tonumber(ARGV[2]) " +
"local now = tonumber(ARGV[3]) " +
"-- 清理过期数据 " +
"redis.call('zremrangebyscore', key, 0, now - window * 1000) " +
"-- 获取当前窗口内的请求数 " +
"local current = redis.call('zcard', key) " +
"if current < limit then " +
" -- 添加当前请求 " +
" redis.call('zadd', key, now, now) " +
" redis.call('expire', key, window) " +
" return {1, limit - current - 1} " +
"else " +
" return {0, 0} " +
"end";
public RateLimitResult checkLimit(String key, int windowSeconds, int limit) {
DefaultRedisScript<List> script = new DefaultRedisScript<>();
script.setScriptText(SLIDING_WINDOW_SCRIPT);
script.setResultType(List.class);
long now = System.currentTimeMillis();
List<Object> result = redisTemplate.execute(script,
Collections.singletonList(key),
String.valueOf(windowSeconds),
String.valueOf(limit),
String.valueOf(now));
if (result != null && result.size() == 2) {
boolean allowed = "1".equals(result.get(0).toString());
int remaining = Integer.parseInt(result.get(1).toString());
return new RateLimitResult(allowed, remaining);
}
return new RateLimitResult(false, 0);
}
}
3. 排行榜操作
@Component
public class LeaderboardService {
// 更新排行榜脚本
private static final String UPDATE_LEADERBOARD_SCRIPT =
"local key = KEYS[1] " +
"local member = ARGV[1] " +
"local score = tonumber(ARGV[2]) " +
"local maxSize = tonumber(ARGV[3]) " +
"-- 添加或更新成员分数 " +
"redis.call('zadd', key, score, member) " +
"-- 获取排行榜大小 " +
"local size = redis.call('zcard', key) " +
"-- 如果超过最大大小,移除最低分数的成员 " +
"if size > maxSize then " +
" redis.call('zremrangebyrank', key, 0, size - maxSize - 1) " +
"end " +
"-- 返回成员当前排名(从1开始) " +
"return redis.call('zrevrank', key, member) + 1";
public int updateScore(String leaderboard, String member, double score, int maxSize) {
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(UPDATE_LEADERBOARD_SCRIPT);
script.setResultType(Long.class);
Long rank = redisTemplate.execute(script,
Collections.singletonList(leaderboard),
member, String.valueOf(score), String.valueOf(maxSize));
return rank != null ? rank.intValue() : -1;
}
}
脚本管理:
@Component
public class ScriptManager {
private final Map<String, String> scriptShas = new ConcurrentHashMap<>();
@PostConstruct
public void loadScripts() {
// 预加载脚本
loadScript("rate_limit", RATE_LIMIT_SCRIPT);
loadScript("distributed_lock", LOCK_SCRIPT);
}
private void loadScript(String name, String script) {
try (Jedis jedis = jedisPool.getResource()) {
String sha = jedis.scriptLoad(script);
scriptShas.put(name, sha);
log.info("Loaded script '{}' with SHA: {}", name, sha);
}
}
public Object executeScript(String scriptName, List<String> keys, List<String> args) {
String sha = scriptShas.get(scriptName);
if (sha == null) {
throw new IllegalArgumentException("Script not found: " + scriptName);
}
try (Jedis jedis = jedisPool.getResource()) {
return jedis.evalsha(sha, keys, args);
}
}
}
最佳实践:
- 预加载常用脚本提高性能
- 合理使用KEYS和ARGV参数
- 注意脚本的原子性特性
- 避免在脚本中执行耗时操作
- 处理脚本执行异常情况
145. HTTP 2.0 和 3.0 有什么区别?
展开 中等 VIP 网络
主要区别对比:
特性 | HTTP/2.0 | HTTP/3.0 |
---|---|---|
传输协议 | TCP | QUIC (UDP) |
连接建立 | TCP握手 + TLS握手 | QUIC握手(合并) |
队头阻塞 | 应用层解决,传输层仍存在 | 完全解决 |
连接迁移 | 不支持 | 支持 |
0-RTT | TLS 1.3支持 | 原生支持 |
拥塞控制 | TCP内核实现 | 用户空间实现 |
HTTP/2.0特性回顾:
1. 多路复用
// HTTP/2在单个TCP连接上多路复用
// 但TCP层面仍可能有队头阻塞
连接: TCP Connection
├── Stream 1: GET /api/users
├── Stream 3: GET /api/orders
├── Stream 5: POST /api/products
└── Stream 7: GET /api/stats
2. 头部压缩(HPACK)
# HTTP/2 HPACK压缩
:method: GET
:path: /api/users
:authority: api.example.com
# 重复头部使用索引引用
HTTP/3.0革新特性:
1. QUIC协议基础
HTTP/3 over QUIC over UDP
应用层: HTTP/3
传输层: QUIC
网络层: UDP
2. 连接建立优化
# HTTP/2 (TCP + TLS)
客户端 -> 服务器: SYN
服务器 -> 客户端: SYN-ACK
客户端 -> 服务器: ACK
客户端 -> 服务器: TLS Client Hello
服务器 -> 客户端: TLS Server Hello + Certificate
... (更多TLS握手)
总计: 2-3 RTT
# HTTP/3 (QUIC)
客户端 -> 服务器: QUIC Initial Packet (包含加密握手)
服务器 -> 客户端: QUIC Response
客户端可立即发送应用数据
总计: 1 RTT (0-RTT 重连)
3. 真正解决队头阻塞
// HTTP/2: TCP层面仍有队头阻塞
TCP丢包影响所有Stream ❌
// HTTP/3: 独立的Stream
Stream 1: 正常传输 ✅
Stream 2: 丢包重传 🔄
Stream 3: 正常传输 ✅
Stream 4: 正常传输 ✅
// Stream间相互独立
实际部署差异:
HTTP/2配置(Nginx):
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# HTTP/2 push
location / {
http2_push /css/style.css;
http2_push /js/app.js;
}
}
HTTP/3配置(Nginx):
server {
listen 443 ssl http2; # HTTP/2 回退
listen 443 http3 reuseport; # HTTP/3
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.3; # HTTP/3需要TLS 1.3
# 添加Alt-Svc头告知客户端支持HTTP/3
add_header Alt-Svc 'h3=":443"; ma=86400';
}
连接迁移特性:
// HTTP/3支持连接迁移
// 用户从WiFi切换到移动网络
原连接ID: Connection-ID-12345
新IP地址: 192.168.1.100 -> 10.0.0.50
连接状态: 保持不变 ✅
// HTTP/2需要重新建立连接
原TCP连接: WiFi IP + Port
新连接: 移动网络 IP + Port
需要: 重新握手 ❌
0-RTT连接恢复:
// HTTP/3 0-RTT恢复
客户端缓存: 服务器配置 + 会话票据
重连时: 立即发送应用数据
服务器: 立即处理请求
// 示例时序
T0: 客户端发送0-RTT数据 + 握手
T1: 服务器响应数据 + 握手确认
// 无需等待握手完成
性能优势对比:
延迟比较:
首次连接建立:
HTTP/2: 2-3 RTT
HTTP/3: 1 RTT
重连:
HTTP/2: 2-3 RTT
HTTP/3: 0 RTT
弱网络环境:
HTTP/2: 受TCP队头阻塞影响严重
HTTP/3: 独立Stream,影响较小
Java客户端支持:
HTTP/2客户端:
// Java 11+ HttpClient
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
HTTP/3客户端(实验性):
// 使用支持QUIC的客户端库
// 例如:quiche-java, netty-quic等
QuicClient client = QuicClient.newBuilder()
.serverName("example.com")
.build();
QuicConnection connection = client.connect()
.join();
QuicStream stream = connection.createStream()
.join();
// 发送HTTP/3请求
stream.writeData(httpRequestBytes);
浏览器支持检测:
// 检测HTTP/3支持
if ('serviceWorker' in navigator) {
// 现代浏览器可能支持HTTP/3
fetch('/api/test', {
// 浏览器自动协商最佳协议版本
}).then(response => {
console.log('Protocol:', response.headers.get('alt-svc'));
});
}
// 检查连接信息(Chrome DevTools)
// Network -> 连接ID列可以看到h3/h2标识
部署注意事项:
HTTP/3部署挑战:
# 1. 防火墙配置
# 需要开放UDP 443端口
iptables -A INPUT -p udp --dport 443 -j ACCEPT
# 2. 负载均衡器支持
# 需要支持QUIC的负载均衡器
# 如:Cloudflare, AWS ALB等
# 3. CDN支持
# 需要CDN提供商支持HTTP/3
# 如:Cloudflare, AWS CloudFront等
渐进式升级策略:
// 服务端支持多协议
server {
listen 443 ssl http2; # HTTP/2
listen 443 http3 reuseport; # HTTP/3
# 协议协商
add_header Alt-Svc 'h3=":443"; ma=86400, h2=":443"; ma=86400';
}
// 客户端自动降级
HTTP/3尝试 -> HTTP/2回退 -> HTTP/1.1兜底
监控和调试:
// 监控HTTP版本分布
const protocolMetrics = {
'http/1.1': 0,
'http/2': 0,
'http/3': 0
};
// Chrome DevTools
// Network面板 -> Protocol列
// 可看到每个请求使用的协议版本
// 服务器日志
log_format http3 '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'protocol=$server_protocol';
未来发展:
- HTTP/3生态逐步成熟
- QUIC协议不断优化
- 更多服务端和客户端支持
- 网络基础设施逐步升级
146. 单例模式有哪几种实现?如何保证线程安全?
展开 中等 VIP 设计模式
单例模式实现方式:
1. 饿汉式(线程安全)
public class EagerSingleton {
// 类加载时就创建实例
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {
// 私有构造函数
}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
- 优点:线程安全,实 现简单
- 缺点:不能延迟加载,可能浪费内存
2. 懒汉式(线程不安全)
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) { // 多线程问题
instance = new LazySingleton();
}
return instance;
}
}
- 优点: 延迟加载
- 缺点:线程不安全
3. 懒汉式(同步方法,线程安全)
public class SynchronizedLazySingleton {
private static SynchronizedLazySingleton instance;
private SynchronizedLazySingleton() {}
public static synchronized SynchronizedLazySingleton getInstance() {
if (instance == null) {
instance = new SynchronizedLazySingleton();
}
return instance;
}
}
- 优点:线程安全,延迟加载
- 缺点:性能差,每次调用都要同步
4. 双重检查锁定(DCL)
public class DoubleCheckedLockingSingleton {
// volatile保证可见性和禁止重排序
private static volatile DoubleCheckedLockingSingleton instance;
private DoubleCheckedLockingSingleton() {}
public static DoubleCheckedLockingSingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (DoubleCheckedLockingSingleton.class) {
if (instance == null) { // 第二次检查
instance = new DoubleCheckedLockingSingleton();
}
}
}
return instance;
}
}
- 优点:延迟加载,性能好,线程安全
- 注意:必须使用volatile关键字
5. 静态内部类(推荐)
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {}
// 静态内部类,懒加载
private static class SingletonHolder {
private static final StaticInnerClassSingleton INSTANCE =
new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 优点:延迟加载,线程安全,性能好
- 原理:利用JVM类加载机制保证线程安全
6. 枚举实现(最安全)
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
System.out.println("Doing something...");
}
}
// 使用
EnumSingleton.INSTANCE.doSomething();
- 优点:线程安全,防止反序列化和反射攻击
- 缺点:不能延迟加载
线程安全分析:
DCL中volatile的重要性:
// 没有volatile的问题
instance = new Singleton(); // 可能重排序为:
// 1. 分配内存空间
// 2. 设置instance指向内存(此时对象还未初始化)
// 3. 初始化对象
// 其他线程可能看到未完全初始化的对象
类加载机制保证线程安全:
// 静态内部类实现原理
public class Outer {
static class Inner {
static final Object INSTANCE = new Object(); // 类加载时执行
}
}
// JVM保证类加载过程的线程安全性
防止反射攻击:
public class ReflectionProofSingleton {
private static volatile ReflectionProofSingleton instance;
private static boolean created = false;
private ReflectionProofSingleton() {
synchronized (ReflectionProofSingleton.class) {
if (created) {
throw new RuntimeException("Use getInstance() method to create");
}
created = true;
}
}
public static ReflectionProofSingleton getInstance() {
if (instance == null) {
synchronized (ReflectionProofSingleton.class) {
if (instance == null) {
instance = new ReflectionProofSingleton();
}
}
}
return instance;
}
}
防止序列化攻击:
public class SerializationProofSingleton implements Serializable {
private static volatile SerializationProofSingleton instance;
private SerializationProofSingleton() {}
public static SerializationProofSingleton getInstance() {
if (instance == null) {
synchronized (SerializationProofSingleton.class) {
if (instance == null) {
instance = new SerializationProofSingleton();
}
}
}
return instance;
}
// 防止序列化破坏单例
private Object readResolve() {
return getInstance();
}
}
性能测试:
public class SingletonPerformanceTest {
public static void main(String[] args) throws InterruptedException {
int threadCount = 100;
int iterations = 1000000;
// 测试不同实现的性能
testPerformance("Eager", EagerSingleton::getInstance, threadCount, iterations);
testPerformance("DCL", DoubleCheckedLockingSingleton::getInstance, threadCount, iterations);
testPerformance("Static Inner", StaticInnerClassSingleton::getInstance, threadCount, iterations);
testPerformance("Synchronized", SynchronizedLazySingleton::getInstance, threadCount, iterations);
}
private static void testPerformance(String name, Supplier<Object> supplier,
int threadCount, int iterations) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(threadCount);
long startTime = System.nanoTime();
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (int j = 0; j < iterations; j++) {
supplier.get();
}
latch.countDown();
}).start();
}
latch.await();
long endTime = System.nanoTime();
System.out.printf("%s: %.2f ms%n", name, (endTime - startTime) / 1_000_000.0);
}
}
实际应用示例:
// 数据库连接池单例
public class DatabaseConnectionPool {
private static volatile DatabaseConnectionPool instance;
private final DataSource dataSource;
private DatabaseConnectionPool() {
// 初始化连接池
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/db");
config.setUsername("user");
config.setPassword("password");
config.setMaximumPoolSize(20);
this.dataSource = new HikariDataSource(config);
}
public static DatabaseConnectionPool getInstance() {
if (instance == null) {
synchronized (DatabaseConnectionPool.class) {
if (instance == null) {
instance = new DatabaseConnectionPool();
}
}
}
return instance;
}
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
// 配置管理单例
public class ConfigManager {
private static class Holder {
private static final ConfigManager INSTANCE = new ConfigManager();
}
private final Properties properties;
private ConfigManager() {
properties = new Properties();
loadConfiguration();
}
public static ConfigManager getInstance() {
return Holder.INSTANCE;
}
public String getProperty(String key) {
return properties.getProperty(key);
}
private void loadConfiguration() {
// 加载配置文件
try (InputStream is = getClass().getResourceAsStream("/application.properties")) {
properties.load(is);
} catch (Exception e) {
throw new RuntimeException("Failed to load configuration", e);
}
}
}
选择建议:
- 一般情况:静态内部类实现
- Spring等框架:交给容器管理
- 防攻击要求高:枚举实现
- 简单场景:饿汉式
- 性能要求极高:DCL + volatile
147. Java 的 synchronized 是怎么实现的?
展开 困难 Java并发 Java
synchronized实现原理:
1. 字节码层面
// Java代码
public void synchronizedMethod() {
synchronized (this) {
// 临界区代码
}
}
// 对应字节码
public void synchronizedMethod();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter // 获取监视器锁
4: aload_1
5: monitorexit // 释放监视器锁
6: goto 14
9: astore_2
10: aload_1
11: monitorexit // 异常时也要释放锁
12: aload_2
13: athrow
14: return
2. 对象头结构
|--------------------------------------------------------|
| Object Header |
|--------------------------------------------------------|
| Mark Word (64bit) | Class Metadata Address (64bit) |
|--------------------------------------------------------|
Mark Word结构(64位JVM):
|-------------------------------------------------------|--------------------|
| 25bit | 31bit | 1bit | 4bit | 1bit | 2bit |
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |
|-------------------------------------------------------|--------------------|
3. 锁状态标识
// Mark Word中的锁状态位
public class LockState {
public static final int UNLOCKED = 0b01; // 无锁
public static final int BIASED = 0b101; // 偏向锁
public static final int LIGHTWEIGHT = 0b00; // 轻量级锁
public static final int HEAVYWEIGHT = 0b10; // 重量级锁
public static final int GC_MARK = 0b11; // GC标记
}
锁升级过程:
1. 偏向锁(Biased Locking)
// 大多数情况下锁不存在竞争,总是由同一线程多次获得
public class BiasedLockExample {
private final Object lock = new Object();
public void method() {
synchronized (lock) { // 第一次获取:设置偏向锁
// 业务逻辑
}
synchronized (lock) { // 后续获取:直接执行,无需CAS
// 业务逻辑
}
}
}
// 偏向锁状态下的Mark Word
|--------------------------------------------------------------------------|
| thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 (101) |
|--------------------------------------------------------------------------|
2. 轻量级锁(Lightweight Locking)
// 当有其他线程竞争时,偏向锁升级为轻量级锁
public class LightweightLockExample {
private final Object lock = new Object();
public void method1() {
synchronized (lock) { // 线程1获取锁
// CAS操作将Mark Word复制到线程栈的Lock Record
// 然后CAS尝试将Mark Word更新为指向Lock Record的指针
}
}
public void method2() {
synchronized (lock) { // 线程2竞争锁
// 自旋等待,如果自旋失败则升级为重量级锁
}
}
}
// 轻量级锁状态下的Mark Word
|--------------------------------------------------------------------------|
| ptr_to_lock_record:62 | lock:2 (00) |
|--------------------------------------------------------------------------|
3. 重量级锁(Heavyweight Locking)
// 当轻量级锁竞争激烈时,升级为重量级锁
public class HeavyweightLockExample {
private final Object lock = new Object();
public void method() {
synchronized (lock) {
// 使用操作系统互斥量(mutex)
// 线程阻塞和唤醒需要从用户态切换到内核态
}
}
}
// 重量级锁状态下的Mark Word
|--------------------------------------------------------------------------|
| ptr_to_heavyweight_monitor:62 | lock:2 (10) |
|--------------------------------------------------------------------------|
Monitor对象结构:
// HotSpot VM 中的ObjectMonitor结构(简化)
class ObjectMonitor {
private:
volatile markOop _header; // 对象头信息
void* volatile _owner; // 指向持有锁的线程
volatile jlong _recursions; // 锁的重入次数
ObjectWaiter* volatile _cxq; // 竞争队列
ObjectWaiter* volatile _EntryList; // 等待队列
ObjectWaiter* volatile _WaitSet; // wait集合
volatile jint _waiters; // 等待线程数
volatile jint _WaitSetLock; // WaitSet锁
};
锁升级示例代码:
public class LockEscalationDemo {
private final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
LockEscalationDemo demo = new LockEscalationDemo();
// 阶段1:偏向锁
System.out.println("=== 偏向锁阶段 ===");
demo.stage1_BiasedLock();
// 阶段2:轻量级锁
System.out.println("=== 轻量级锁阶段 ===");
demo.stage2_LightweightLock();
// 阶段3:重量级锁
System.out.println("=== 重量级锁阶段 ===");
demo.stage3_HeavyweightLock();
}
// 偏向锁:单线程重复获取
private void stage1_BiasedLock() {
for (int i = 0; i < 5; i++) {
synchronized (lock) {
System.out.println("偏向锁执行: " + i);
}
}
}
// 轻量级锁:少量竞争
private void stage2_LightweightLock() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
// 创建两个线程交替获取锁
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 3; j++) {
synchronized (lock) {
System.out.println("轻量级锁: " + Thread.currentThread().getName());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
latch.countDown();
}, "Thread-" + i).start();
}
latch.await();
}
// 重量级锁:激烈竞争
private void stage3_HeavyweightLock() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
// 创建10个线程激烈竞争
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
synchronized (lock) {
// 模拟工作
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
latch.countDown();
}, "HeavyThread-" + i).start();
}
latch.await();
}
}
JVM参数调优:
# 禁用偏向锁
-XX:-UseBiasedLocking
# 设置偏向锁延迟启动时间(默认4000ms)
-XX:BiasedLockingStartupDelay=0
# 设置自旋次数(JDK6以前)
-XX:PreBlockSpin=10
# 启用自适应自旋
-XX:+UseSpinning
# 打印锁相关信息
-XX:+PrintGCDetails -XX:+PrintConcurrentLocks
锁优化技术:
1. 锁粗化(Lock Coarsening)
// 优化前:频繁加锁解锁
public void inefficient() {
synchronized (this) { i++; }
synchronized (this) { j++; }
synchronized (this) { k++; }
}
// 优化后:锁粗化
public void efficient() {
synchronized (this) {
i++;
j++;
k++;
}
}
2. 锁消除(Lock Elimination)
// JVM能检测到不可能有竞争的锁并消除
public void lockElimination() {
StringBuffer sb = new StringBuffer(); // 局部变量,不会逃逸
sb.append("a"); // StringBuffer.append是同步的,但JVM会消除锁
sb.append("b");
return sb.toString();
}
3. 逃逸分析
// 对象不逃逸出方法,JVM可能进行锁消除
public String noEscape() {
StringBuilder sb = new StringBuilder();
sb.append("hello");
sb.append("world");
return sb.toString(); // sb对象不逃逸
}
性能监控:
public class SynchronizedMonitor {
// 使用JMX监控锁信息
public void monitorLocks() {
ThreadMXBean threadMX = ManagementFactory.getThreadMXBean();
if (threadMX.isObjectMonitorUsageSupported()) {
ThreadInfo[] threadInfos = threadMX.dumpAllThreads(true, true);
for (ThreadInfo info : threadInfos) {
MonitorInfo[] monitors = info.getLockedMonitors();
LockInfo[] locks = info.getLockedSynchronizers();
if (monitors.length > 0 || locks.length > 0) {
System.out.println("Thread: " + info.getThreadName());
for (MonitorInfo monitor : monitors) {
System.out.println(" Locked monitor: " + monitor);
}
for (LockInfo lock : locks) {
System.out.println(" Locked synchronizer: " + lock);
}
}
}
}
}
}
最佳实践:
- 减少锁的持有时间
- 降低锁的粒度
- 使用读写 锁分离
- 尽量使用无锁数据结构
- 避免锁嵌套
- 考虑使用并发工具类
148. 如何设计一个秒杀功能?
展开 困难 VIP 后端 系统设计 场景题
系统架构设计:
用户端 -> CDN -> 负载均衡 -> 网关层 -> 业务层 -> 数据层
↓ ↓ ↓ ↓ ↓
静态资源 流量分发 限流 缓存 数据库
核心挑战:
- 高并发:瞬间大量请求
- 库存一致性:防止超卖
- 系统稳定性:防止系统崩溃
- 用户体验:响应速度
详细设计方案:
1. 前端优化
// 前端限流和防重复提交
class SeckillButton {
constructor() {
this.isSubmitting = false;
this.countdown = 0;
}
// 防重复点击
async handleSeckill() {
if (this.isSubmitting) {
return;
}
this.isSubmitting = true;
this.startCountdown(3); // 3秒倒计时
try {
const result = await this.submitSeckill();
this.handleResult(result);
} catch (error) {
this.handleError(error);
} finally {
this.isSubmitting = false;
}
}
startCountdown(seconds) {
this.countdown = seconds;
const timer = setInterval(() => {
this.countdown--;
if (this.countdown <= 0) {
clearInterval(timer);
}
}, 1000);
}
// 长轮询获取秒杀结果
async pollResult(orderId) {
const maxAttempts = 10;
let attempts = 0;
while (attempts < maxAttempts) {
try {
const result = await fetch(`/api/seckill/result/${orderId}`);
const data = await result.json();
if (data.status !== 'PROCESSING') {
return data;
}
await new Promise(resolve => setTimeout(resolve, 1000));
attempts++;
} catch (error) {
attempts++;
}
}
return { status: 'TIMEOUT' };
}
}
2. 网关层限流
@Component
public class SeckillRateLimiter {
private final RedisTemplate<String, String> redisTemplate;
// 滑动窗口限流
private static final String RATE_LIMIT_SCRIPT =
"local key = KEYS[1] " +
"local window = tonumber(ARGV[1]) " +
"local limit = tonumber(ARGV[2]) " +
"local now = tonumber(ARGV[3]) " +
"local clearBefore = now - window * 1000 " +
"redis.call('zremrangebyscore', key, 0, clearBefore) " +
"local current = redis.call('zcard', key) " +
"if current < limit then " +
" redis.call('zadd', key, now, now) " +
" redis.call('expire', key, window) " +
" return 1 " +
"else " +
" return 0 " +
"end";
public boolean isAllowed(String userId, int windowSeconds, int limit) {
String key = "rate_limit:seckill:" + userId;
long now = System.currentTimeMillis();
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(RATE_LIMIT_SCRIPT);
script.setResultType(Long.class);
Long result = redisTemplate.execute(script,
Collections.singletonList(key),
String.valueOf(windowSeconds),
String.valueOf(limit),
String.valueOf(now));
return Long.valueOf(1L).equals(result);
}
// 令牌桶限流
@Component
public class TokenBucketLimiter {
private final AtomicLong tokens = new AtomicLong(1000);
private final long capacity = 1000;
private final long refillRate = 100; // 每秒补充100个令牌
private volatile long lastRefillTime = System.currentTimeMillis();
public boolean tryAcquire() {
refillTokens();
return tokens.getAndDecrement() > 0;
}
private void refillTokens() {
long now = System.currentTimeMillis();
long timeSinceLastRefill = now - lastRefillTime;
if (timeSinceLastRefill > 1000) { // 每秒补充
long tokensToAdd = (timeSinceLastRefill / 1000) * refillRate;
long newTokens = Math.min(capacity, tokens.get() + tokensToAdd);
tokens.set(newTokens);
lastRefillTime = now;
}
}
}
}
3. 库存管理
@Service
public class SeckillStockService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// Redis预扣库存脚本
private static final String DEDUCT_STOCK_SCRIPT =
"local stockKey = KEYS[1] " +
"local userId = ARGV[1] " +
"local quantity = tonumber(ARGV[2]) " +
"local userKey = 'seckill:user:' .. userId " +
"-- 检查用户是否已经参与过 " +
"if redis.call('exists', userKey) == 1 then " +
" return {-1, 'Already participated'} " +
"end " +
"-- 检查库存 " +
"local stock = redis.call('get', stockKey) " +
"if stock == false then " +
" return {-2, 'Stock not found'} " +
"end " +
"stock = tonumber(stock) " +
"if stock >= quantity then " +
" redis.call('decrby', stockKey, quantity) " +
" redis.call('setex', userKey, 300, 1) -- 5分钟过期 " +
" return {stock - quantity, 'Success'} " +
"else " +
" return {stock, 'Insufficient stock'} " +
"end";
public StockResult deductStock(Long productId, String userId, int quantity) {
String stockKey = "seckill:stock:" + productId;
DefaultRedisScript<List> script = new DefaultRedisScript<>();
script.setScriptText(DEDUCT_STOCK_SCRIPT);
script.setResultType(List.class);
List<Object> result = redisTemplate.execute(script,
Collections.singletonList(stockKey),
userId, String.valueOf(quantity));
if (result != null && result.size() == 2) {
int remainingStock = Integer.parseInt(result.get(0).toString());
String message = result.get(1).toString();
return new StockResult(remainingStock, message);
}
return new StockResult(-1, "Script execution failed");
}
// 异步同步库存到数据库
@Async
public void syncStockToDatabase(Long productId) {
String stockKey = "seckill:stock:" + productId;
Object redisStock = redisTemplate.opsForValue().get(stockKey);
if (redisStock != null) {
int stock = Integer.parseInt(redisStock.toString());
// 批量更新数据库,减少数据库压力
productRepository.updateStock(productId, stock);
}
}
}
4. 订单处理
@Service
public class SeckillOrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 异步处理订单
public String createSeckillOrder(SeckillRequest request) {
// 生成订单ID
String orderId = generateOrderId();
// 创建订单消息
SeckillOrderMessage message = SeckillOrderMessage.builder()
.orderId(orderId)
.userId(request.getUserId())
.productId(request.getProductId())
.quantity(request.getQuantity())
.createTime(LocalDateTime.now())
.build();
// 发送到消息队列异步处理
rabbitTemplate.convertAndSend("seckill.order.exchange",
"seckill.order.create", message);
// 设置订单状态为处理中
redisTemplate.opsForValue().set(
"seckill:order:status:" + orderId,
"PROCESSING",
Duration.ofMinutes(5));
return orderId;
}
// 消息队列消费者
@RabbitListener(queues = "seckill.order.queue")
public void processSeckillOrder(SeckillOrderMessage message) {
try {
// 1. 验证用户和商品
if (!validateUser(message.getUserId()) ||
!validateProduct(message.getProductId())) {
updateOrderStatus(message.getOrderId(), "FAILED", "Validation failed");
return;
}
// 2. 创建订单
Order order = Order.builder()
.orderId(message.getOrderId())
.userId(message.getUserId())
.productId(message.getProductId())
.quantity(message.getQuantity())
.status(OrderStatus.UNPAID)
.createTime(message.getCreateTime())
.build();
orderRepository.save(order);
// 3. 更新订单状态
updateOrderStatus(message.getOrderId(), "SUCCESS", "Order created");
// 4. 发送支付超时消息(延迟消息)
sendPaymentTimeoutMessage(message.getOrderId(), 15 * 60 * 1000); // 15分钟
} catch (Exception e) {
log.error("Process seckill order failed", e);
updateOrderStatus(message.getOrderId(), "FAILED", e.getMessage());
// 回滚库存
rollbackStock(message.getProductId(), message.getQuantity());
}
}
private void updateOrderStatus(String orderId, String status, String message) {
String key = "seckill:order:status:" + orderId;
Map<String, Object> statusInfo = Map.of(
"status", status,
"message", message,
"updateTime", LocalDateTime.now()
);
redisTemplate.opsForValue().set(key, statusInfo, Duration.ofHours(1));
}
}
5. 缓存策略
@Service
public class SeckillCacheService {
// 多级缓存
@Cacheable(value = "seckill:product", key = "#productId")
public SeckillProduct getProduct(Long productId) {
return productRepository.findById(productId);
}
// 预热缓存
@PostConstruct
public void warmUpCache() {
List<SeckillActivity> activities = seckillActivityRepository.findUpcoming();
for (SeckillActivity activity : activities) {
// 预热商品信息
String productKey = "seckill:product:" + activity.getProductId();
redisTemplate.opsForValue().set(productKey, activity.getProduct(), Duration.ofHours(1));
// 预热库存信息
String stockKey = "seckill:stock:" + activity.getProductId();
redisTemplate.opsForValue().set(stockKey, activity.getStock(), Duration.ofHours(2));
}
}
// 分布式锁防止缓存击穿
public SeckillProduct getProductWithLock(Long productId) {
String lockKey = "lock:seckill:product:" + productId;
String lockValue = UUID.randomUUID().toString();
try {
// 尝试获取分布式锁
Boolean lockAcquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, lockValue, Duration.ofSeconds(10));
if (Boolean.TRUE.equals(lockAcquired)) {
// 获取锁成功,查询数据库
SeckillProduct product = productRepository.findById(productId);
if (product != null) {
// 更新缓存
redisTemplate.opsForValue().set(
"seckill:product:" + productId,
product,
Duration.ofMinutes(30));
}
return product;
} else {
// 获取锁失败,稍等后重试
Thread.sleep(50);
return getProduct(productId);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
// 释放锁
releaseLock(lockKey, lockValue);
}
}
}
6. 监控告警
@Component
public class SeckillMonitor {
@Autowired
private MeterRegistry meterRegistry;
// 监控指标
private final Counter seckillRequestCounter = Counter.builder("seckill.requests")
.description("Seckill request count")
.register(meterRegistry);
private final Timer seckillResponseTimer = Timer.builder("seckill.response.time")
.description("Seckill response time")
.register(meterRegistry);
private final Gauge stockGauge = Gauge.builder("seckill.stock")
.description("Remaining stock")
.register(meterRegistry, this, SeckillMonitor::getCurrentStock);
public void recordRequest() {
seckillRequestCounter.increment();
}
public void recordResponseTime(long timeMs) {
seckillResponseTimer.record(timeMs, TimeUnit.MILLISECONDS);
}
private double getCurrentStock() {
// 获取当前库存总数
return stockService.getTotalStock();
}
// 实时监控和告警
@Scheduled(fixedRate = 5000) // 每5秒检查一次
public void checkSystemHealth() {
// 检查系统指标
double cpuUsage = systemMetrics.getCpuUsage();
double memoryUsage = systemMetrics.getMemoryUsage();
long activeConnections = networkMetrics.getActiveConnections();
if (cpuUsage > 80) {
alertService.sendAlert(AlertLevel.HIGH, "High CPU usage: " + cpuUsage + "%");
}
if (memoryUsage > 85) {
alertService.sendAlert(AlertLevel.HIGH, "High memory usage: " + memoryUsage + "%");
}
if (activeConnections > 10000) {
alertService.sendAlert(AlertLevel.MEDIUM, "High connection count: " + activeConnections);
}
// 检查Redis连接
try {
redisTemplate.opsForValue().get("health:check");
} catch (Exception e) {
alertService.sendAlert(AlertLevel.CRITICAL, "Redis connection failed: " + e.getMessage());
}
}
}
性能优化要点:
- 读写分离:读多写少场景优化
- 数据库分片:分散数据库压力
- CDN加速:静态资源缓存
- 异步处理:削峰填谷
- 熔断降级:保护系统稳定性
容量评估:
// 容量规划示例
public class CapacityPlanning {
// 假设目标:支持10万人同时抢购1000件商品
public static final int CONCURRENT_USERS = 100_000;
public static final int TOTAL_STOCK = 1000;
public static final int PEAK_QPS = 50_000; // 峰值QPS
// 服务器配置
public static final int SERVER_COUNT = 20; // 应用服务器数量
public static final int REDIS_CLUSTER_SIZE = 6; // Redis集群大小
public static final int DB_CONNECTION_POOL = 50; // 数据库连接池
// 缓存容量
public static final long REDIS_MEMORY = 16L * 1024 * 1024 * 1024; // 16GB
public static final int CACHE_EXPIRE_TIME = 3600; // 1小时
}
149. 为什么不选择使用原生的 NIO 而选择使用 Netty 呢?
展开 中等 VIP Netty 后端
原生NIO的问题:
1. 开发复杂度高
// 原生NIO服务器示例
public class NIOServer {
public void startServer() throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
handleAccept(key);
} else if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
}
}
}
private void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(key.selector(), SelectionKey.OP_READ);
}
private void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
// 处理数据 - 需要自己处理半包、粘包问题
processData(buffer);
buffer.clear();
} else if (bytesRead < 0) {
// 连接关闭
key.cancel();
channel.close();
}
}
// 需要自己处理各种边界情况...
}
2. 半包/粘包问题复杂
// 原生NIO需要自己处理半包粘包
public class PacketHandler {
private ByteBuffer incompleteBuffer = ByteBuffer.allocate(4096);
public List<Packet> handlePackets(ByteBuffer data) {
List<Packet> packets = new ArrayList<>();
// 合并之前未完成的数据
if (incompleteBuffer.position() > 0) {
incompleteBuffer.put(data.array(), 0, data.remaining());
incompleteBuffer.flip();
data = incompleteBuffer;
}
while (data.remaining() >= 4) { // 假设包头4字节
int packetLength = data.getInt();
if (data.remaining() >= packetLength) {
// 完整包
byte[] packetData = new byte[packetLength];
data.get(packetData);
packets.add(new Packet(packetData));
} else {
// 半包,保存到缓冲区
data.position(data.position() - 4);
incompleteBuffer.clear();
incompleteBuffer.put(data);
break;
}
}
return packets;
}
}
3. 内存管理困难
// 原生NIO内存管理
public class BufferManager {
// 需要手动管理ByteBuffer的分配和回收
private final Queue<ByteBuffer> bufferPool = new ConcurrentLinkedQueue<>();
public ByteBuffer allocateBuffer() {
ByteBuffer buffer = bufferPool.poll();
if (buffer == null) {
buffer = ByteBuffer.allocateDirect(8192); // 直接内存
}
buffer.clear();
return buffer;
}
public void releaseBuffer(ByteBuffer buffer) {
if (buffer.isDirect() && buffer.capacity() <= 8192) {
bufferPool.offer(buffer);
}
// 直接内存需要手动管理,容易造成内存泄漏
}
}
Netty的优势:
1. 简化的API
// Netty服务器示例
public class NettyServer {
public void start() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 自动处理半包粘包
pipeline.addLast(new LengthFieldBasedFrameDecoder(
1024 * 1024, 0, 4, 0, 4));
pipeline.addLast(new LengthFieldPrepender(4));
// 自定义业务处理器
pipeline.addLast(new ServerHandler());
}
});
ChannelFuture future = bootstrap.bind(8080).sync();
future.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
// 简单的业务处理器
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf buffer = (ByteBuf) msg;
try {
// 自动处理了半包粘包,直接处理完整数据
byte[] data = new byte[buffer.readableBytes()];
buffer.readBytes(data);
// 处理业务逻辑
String response = processRequest(new String(data));
// 发送响应
ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
} finally {
buffer.release(); // 自动内存管理
}
}
}
2. 自动处理半包粘包
// Netty提供多种解码器
public class DecoderExamples {
public void addDecoders(ChannelPipeline pipeline) {
// 1. 固定长度解码器
pipeline.addLast(new FixedLengthFrameDecoder(20));
// 2. 分隔符解码器
pipeline.addLast(new DelimiterBasedFrameDecoder(1024,
Delimiters.lineDelimiter()));
// 3. 长度字段解码器
pipeline.addLast(new LengthFieldBasedFrameDecoder(
65536, // maxFrameLength
0, // lengthFieldOffset
4, // lengthFieldLength
0, // lengthAdjustment
4 // initialBytesToStrip
));
// 4. 自定义协议解码器
pipeline.addLast(new CustomProtocolDecoder());
}
}
// 自定义解码器示例
public class CustomProtocolDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.readableBytes() < 8) { // 协议头8字节
return; // 等待更多数据
}
in.markReaderIndex(); // 标记当前位置
int magic = in.readInt();
if (magic != 0x12345678) {
in.resetReaderIndex();
throw new RuntimeException("Invalid magic number");
}
int length = in.readInt();
if (in.readableBytes() < length) {
in.resetReaderIndex(); // 重置到标记位置
return; // 等待更多数据
}
// 读取完整数据包
ByteBuf frame = in.readBytes(length);
out.add(frame);
}
}
3. 高效的内存管理
// Netty的ByteBuf优势
public class ByteBufExamples {
public void demonstrateByteBuffer() {
// 原生ByteBuffer问题
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("Hello".getBytes());
buffer.flip(); // 需要手动flip
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
// 读写模式切换复杂
}
public void demonstrateByteBuf() {
// Netty ByteBuf优势
ByteBuf buffer = Unpooled.buffer(1024);
// 1. 读写指针分离,无需flip
buffer.writeBytes("Hello".getBytes());
buffer.writeInt(42);
byte[] data = new byte[5];
buffer.readBytes(data);
int value = buffer.readInt();
// 2. 引用计数,自动内存管理
buffer.release();
// 3. 零拷贝支持
ByteBuf composite = Unpooled.compositeBuffer();
composite.addComponent(true, buffer1);
composite.addComponent(true, buffer2);
// 不需要数据拷贝
}
public void pooledBuffers() {
// 4. 内存池化
ByteBuf pooled = PooledByteBufAllocator.DEFAULT.buffer(1024);
try {
// 使用pooled buffer
} finally {
pooled.release(); // 归还到池中
}
}
}
4. 事件驱动架构
// Netty的Pipeline设计
public class PipelineExample {
public void setupPipeline(ChannelPipeline pipeline) {
// 入站处理器链
pipeline.addLast("decoder", new MessageDecoder());
pipeline.addLast("validator", new MessageValidator());
pipeline.addLast("handler", new BusinessHandler());
// 出站处理器链
pipeline.addLast("encoder", new MessageEncoder());
pipeline.addLast("compressor", new CompressionHandler());
}
}
// 处理器可以灵活组合
public class MessageValidator extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (validate(msg)) {
ctx.fireChannelRead(msg); // 传递给下一个处理器
} else {
ctx.close(); // 验证失败,关闭连接
}
}
}
5. 线程模型优化
// Netty的Reactor模型
public class ReactorModel {
public void singleReactor() {
// 单Reactor单线程
EventLoopGroup group = new NioEventLoopGroup(1);
// 所有I/O操作在一个线程中处理
}
public void multiReactor() {
// 主从Reactor模型
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 接受连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理I/O
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup); // 分离接受和处理
}
public void customThreadModel() {
// 自定义线程模型
EventLoopGroup group = new DefaultEventLoopGroup(10); // 业务线程池
pipeline.addLast(group, "business", new BusinessHandler());
// 业务处理在单独线程池中,不阻塞I/O线程
}
}
6. 丰富的编解码器
// Netty内置编解码器
public class CodecExamples {
public void httpCodec(ChannelPipeline pipeline) {
// HTTP编解码器
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new HttpServerHandler());
}
public void websocketCodec(ChannelPipeline pipeline) {
// WebSocket编解码器
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
pipeline.addLast(new WebSocketHandler());
}
public void protobufCodec(ChannelPipeline pipeline) {
// Protobuf编解码器
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(MyProto.Message.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
}
}
7. 跨平台和性能优化
// Netty的传输层抽象
public class TransportOptions {
public void nioTransport() {
// 标准NIO传输,跨平台
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class);
}
public void epollTransport() {
// Linux优化传输
if (Epoll.isAvailable()) {
bootstrap.group(bossGroup, workerGroup)
.channel(EpollServerSocketChannel.class);
}
}
public void kqueueTransport() {
// macOS/BSD优化传输
if (KQueue.isAvailable()) {
bootstrap.group(bossGroup, workerGroup)
.channel(KQueueServerSocketChannel.class);
}
}
}
性能对比:
// 简化的性能测试
public class PerformanceComparison {
// 原生NIO实现复杂,容易出错
// - 需要处理各种边界情况
// - 内存管理复杂
// - 代码量大,维护困难
// Netty实现简洁,功能强大
// - 自动处理协议细节
// - 优化的内存管理
// - 丰富的组件生态
// 实际项目中的选择
public void realWorldChoice() {
// 如果你是:
// 1. 框架开发者 -> 可能选择原生NIO获得最大控制权
// 2. 应用开发者 -> 推荐使用Netty提高开发效率
// 3. 学习者 -> 先学原生NIO理解原理,再用Netty实战
}
}
总结:选择Netty的原因
- 开发效率:大幅减少代码量和开发时间
- 稳定性:经过大量生产环境验证
- 性能:高度优化的实现
- 功能丰富:提供各种协议支持
- 社区活跃:持续更新和维护
- 学习成本低:API设计友好
150. 看过源码吗?说下 Spring 由哪些重要的模块组成?
展开 中等 VIP 后端 Spring
Spring框架模块架构:
Spring Framework
├── spring-core (核心容器)
├── spring-beans (Bean管理)
├── spring-context (应用上下文)
├── spring-context-support (上下文支持)
├── spring-aop (面向切面编程)
├── spring-aspects (AspectJ集成)
├── spring-web (Web基础)
├── spring-webmvc (Web MVC)
├── spring-webflux (响应式Web)
├── spring-jdbc (数据访问)
├── spring-tx (事务管理)
├── spring-orm (对象关系映射)
├── spring-jms (消息)
├── spring-test (测试支持)
└── spring-expression (表达式语言)
核心模块详解:
1. spring-core(核心模块)
// 核心工具类和基础设施
public class CoreModuleExample {
public void demonstrateCoreFeatures() {
// 1. 资源访问抽象
Resource resource = new ClassPathResource("application.properties");
Resource urlResource = new UrlResource("https://example.com/config");
Resource fileResource = new FileSystemResource("/path/to/file");
// 2. 类型转换系统
ConversionService conversionService = new DefaultConversionService();
String dateStr = "2023-12-25";
Date date = conversionService.convert(dateStr, Date.class);
// 3. 环境抽象
Environment environment = new StandardEnvironment();
String property = environment.getProperty("java.version");
// 4. 事件发布机制
ApplicationEventPublisher publisher = // 获取发布器
publisher.publishEvent(new CustomEvent("test"));
}
}
// 核心接口:BeanFactory
public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
}
2. spring-beans(Bean管理)
// Bean定义和生命周期管理
public class BeansModuleExample {
public void beanLifecycle() {
// Bean定义
BeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(UserService.class)
.addPropertyValue("userDao", new RuntimeBeanReference("userDao"))
.setScope(BeanDefinition.SCOPE_SINGLETON)
.getBeanDefinition();
// Bean工厂后处理器
BeanFactoryPostProcessor processor = new PropertyPlaceholderConfigurer();
// Bean后处理器
BeanPostProcessor postProcessor = new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("Before initialization: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("After initialization: " + beanName);
return bean;
}
};
}
}
// Bean生命周期回调
@Component
public class LifecycleBean implements InitializingBean, DisposableBean,
BeanNameAware, ApplicationContextAware {
private String beanName;
private ApplicationContext applicationContext;
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("BeanNameAware: " + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
System.out.println("ApplicationContextAware set");
}
@PostConstruct
public void postConstruct() {
System.out.println("@PostConstruct method called");
}
@Override
public void afterPropertiesSet() {
System.out.println("InitializingBean.afterPropertiesSet called");
}
@PreDestroy
public void preDestroy() {
System.out.println("@PreDestroy method called");
}
@Override
public void destroy() {
System.out.println("DisposableBean.destroy called");
}
}
3. spring-context(应用上下文)
// 应用上下文实现
public class ContextModuleExample {
public void demonstrateApplicationContext() {
// 1. 注解配置应用上下文
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 2. XML配置应用上下文
ApplicationContext xmlContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 3. Web应用上下文
// WebApplicationContext webContext = new XmlWebApplicationContext();
// 4. 获取Bean
UserService userService = context.getBean(UserService.class);
// 5. 事件发布
context.publishEvent(new CustomApplicationEvent("test data"));
// 6. 环境信息
Environment env = context.getEnvironment();
String[] profiles = env.getActiveProfiles();
}
}
// 配置类
@Configuration
@ComponentScan(basePackages = "com.example")
@PropertySource("classpath:application.properties")
public class AppConfig {
@Bean
@Primary
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
@Bean
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}
// 自定义应用事件
public class CustomApplicationEvent extends ApplicationEvent {
private final String data;
public CustomApplicationEvent(Object source, String data) {
super(source);
this.data = data;
}
public String getData() {
return data;
}
}
// 事件监听器
@EventListener
@Component
public class ApplicationEventListener {
@EventListener
public void handleCustomEvent(CustomApplicationEvent event) {
System.out.println("Received custom event: " + event.getData());
}
@EventListener
@Async
public void handleContextRefreshedEvent(ContextRefreshedEvent event) {
System.out.println("Application context refreshed");
}
}
4. spring-aop(面向切面编程)
// AOP实现原理
@Aspect
@Component
public class LoggingAspect {
// 切点表达式
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Pointcut("@annotation(com.example.annotation.Cacheable)")
public void cacheableMethod() {}
// 前置通知
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("Before method: " + methodName + " with args: " + Arrays.toString(args));
}
// 环绕通知
@Around("serviceLayer()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println("Method executed in: " + (endTime - startTime) + "ms");
return result;
} catch (Exception e) {
System.out.println("Method execution failed: " + e.getMessage());
throw e;
}
}
// 异常通知
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void logException(JoinPoint joinPoint, Exception ex) {
System.out.println("Exception in method: " + joinPoint.getSignature().getName() +
" Exception: " + ex.getMessage());
}
}
// AOP代理创建过程(源码理解)
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
// AnnotationAwareAspectJAutoProxyCreator会自动创建代理
}
5. spring-web 和 spring-webmvc
// Web MVC模块
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody @Valid User user) {
User savedUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
}
// Web MVC配置
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/public/**");
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
}
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
// DispatcherServlet工作流程(核心)
public class DispatcherServletFlow {
/*
1. Request进入DispatcherServlet
2. HandlerMapping查找Handler
3. HandlerAdapter执行Handler
4. ViewResolver解析视图
5. View渲染响应
*/
}
6. spring-jdbc 和 spring-tx
// JDBC模块
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);
}
@Override
public List<User> findAll() {
String sql = "SELECT * FROM users";
return jdbcTemplate.query(sql, new UserRowMapper());
}
@Override
public int save(User user) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
return jdbcTemplate.update(sql, user.getName(), user.getEmail());
}
private static class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
return user;
}
}
}
// 事务管理
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
@Transactional(propagation = Propagation.REQUIRED)
public User createUser(User user) {
// 事务会自动开始
userDao.save(user);
// 如果抛出 异常,事务会回滚
if (user.getName().equals("error")) {
throw new RuntimeException("Simulated error");
}
return user;
// 方法正常结束,事务提交
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditLog(String operation) {
// 独立事务,不受外部事务影响
auditDao.log(operation);
}
}
// 事务配置
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
源码核心流程:
1. ApplicationContext启动流程
// AbstractApplicationContext.refresh()方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 准备刷新上下文
prepareRefresh();
// 2. 获取BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备BeanFactory
prepareBeanFactory(beanFactory);
try {
// 4. 后处理BeanFactory
postProcessBeanFactory(beanFactory);
// 5. 调用BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 7. 初始化MessageSource
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 刷新特定上下文
onRefresh();
// 10. 注册事件监听器
registerListeners();
// 11. 实例化所有单例Bean
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新
finishRefresh();
}
catch (BeansException ex) {
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
2. Bean创建流程
// AbstractAutowireCapableBeanFactory.createBean()
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// 1. 解析Bean类型
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
// 2. 实例化前处理
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
// 3. 创建Bean实例
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// 1. 实例化Bean
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
// 2. 应用MergedBeanDefinitionPostProcessor
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
// 3. 属性注入
populateBean(beanName, mbd, instanceWrapper);
// 4. 初始化Bean
Object exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
模块依赖关系:
spring-context
├── spring-aop
├── spring-beans
├── spring-core
└── spring-expression
spring-webmvc
├── spring-web
├── spring-context
└── spring-beans
spring-tx
├── spring-beans
└── spring-core
学习建议:
- 从spring-core开始,理解基础设施
- 学习spring-beans,掌握IOC容器
- 研究spring-context,理解应用上下文
- 深入spring-aop,理解代理机制
- 结合实际项目,理解各模块协作
151. 如何优化 Java 中的锁的使用?
展开 中等 Java并发 Java
锁优化策略:
1. 减少锁的粒度
// 优化前:粗粒度锁
public class CoarseGrainedLock {
private final Object lock = new Object();
private Map<String, User> users = new HashMap<>();
private Map<String, Order> orders = new HashMap<>();
public void addUser(String id, User user) {
synchronized (lock) { // 锁住了所有数据
users.put(id, user);
}
}
public void addOrder(String id, Order order) {
synchronized (lock) { // 不相关的操作也被锁住
orders.put(id, order);
}
}
}
// 优化后:细粒度锁
public class FineGrainedLock {
private final Object userLock = new Object();
private final Object orderLock = new Object();
private Map<String, User> users = new HashMap<>();
private Map<String, Order> orders = new HashMap<>();
public void addUser(String id, User user) {
synchronized (userLock) { // 只锁用户数据
users.put(id, user);
}
}
public void addOrder(String id, Order order) {
synchronized (orderLock) { // 只锁订单数据
orders.put(id, order);
}
}
}
2. 锁分段技术
// 参考ConcurrentHashMap的分段锁思想
public class SegmentedLock<T> {
private final int segmentCount;
private final Object[] locks;
private final Map<String, T>[] segments;
@SuppressWarnings("unchecked")
public SegmentedLock(int segmentCount) {
this.segmentCount = segmentCount;
this.locks = new Object[segmentCount];
this.segments = new Map[segmentCount];
for (int i = 0; i < segmentCount; i++) {
locks[i] = new Object();
segments[i] = new HashMap<>();
}
}
private int getSegmentIndex(String key) {
return Math.abs(key.hashCode()) % segmentCount;
}
public void put(String key, T value) {
int index = getSegmentIndex(key);
synchronized (locks[index]) { // 只锁对应的段
segments[index].put(key, value);
}
}
public T get(String key) {
int index = getSegmentIndex(key);
synchronized (locks[index]) {
return segments[index].get(key);
}
}
}
3. 读写锁分离
public class ReadWriteLockExample {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private Map<String, String> cache = new HashMap<>();
public String getValue(String key) {
readLock.lock();
try {
return cache.get(key); // 多个读操作可以并发
} finally {
readLock.unlock();
}
}
public void setValue(String key, String value) {
writeLock.lock();
try {
cache.put(key, value); // 写操作独占
} finally {
writeLock.unlock();
}
}
// 读写锁升级示例
public void updateValue(String key, Function<String, String> updater) {
readLock.lock();
String oldValue;
try {
oldValue = cache.get(key);
} finally {
readLock.unlock();
}
if (oldValue != null) {
writeLock.lock();
try {
// 双重检查,防止其他线程已经修改
String currentValue = cache.get(key);
if (Objects.equals(currentValue, oldValue)) {
cache.put(key, updater.apply(oldValue));
}
} finally {
writeLock.unlock();
}
}
}
}
4. 使用无锁数据结构
public class LockFreeExamples {
// 原子类替代锁
private final AtomicInteger counter = new AtomicInteger(0);
private final AtomicReference<String> atomicString = new AtomicReference<>();
public int incrementAndGet() {
return counter.incrementAndGet(); // 无锁操作
}
public boolean compareAndSetString(String expected, String update) {
return atomicString.compareAndSet(expected, update);
}
// 无锁队列
private final ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
public void offer(String item) {
queue.offer(item); // 无锁操作
}
public String poll() {
return queue.poll(); // 无锁操作
}
// 无锁Map
private final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
public String putIfAbsent(String key, String value) {
return map.putIfAbsent(key, value); // 原子操作
}
// 使用LongAdder替代AtomicLong
private final LongAdder adder = new LongAdder();
public void increment() {
adder.increment(); // 分段累加,减少竞争
}
public long sum() {
return adder.sum();
}
}
5. 锁超时和可中断锁
public class TimeoutLockExample {
private final ReentrantLock lock = new ReentrantLock();
public boolean tryWithTimeout(long timeout, TimeUnit unit) {
try {
if (lock.tryLock(timeout, unit)) {
try {
// 执行业务逻辑
return processWithTimeout();
} finally {
lock.unlock();
}
} else {
// 获取锁超时,返回失败
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
public void interruptibleLock() throws InterruptedException {
lock.lockInterruptibly(); // 可被中断的锁
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
// 非阻塞尝试获取锁
public boolean tryNonBlocking() {
if (lock.tryLock()) { // 立即返回,不阻塞
try {
// 执行快速操作
return true;
} finally {
lock.unlock();
}
}
return false;
}
}
6. 减少锁的持有时间
public class ReduceLockHoldTime {
private final Object lock = new Object();
private List<String> list = new ArrayList<>();
// 优化前:锁持有时间长
public void inefficient(String item) {
synchronized (lock) {
// 耗时的计算在锁内进行
String processedItem = expensiveComputation(item);
list.add(processedItem);
// 耗时的I/O操作也在锁内
saveToDatabase(processedItem);
}
}
// 优化后:缩短锁持有时间
public void efficient(String item) {
// 耗时操作移出锁外
String processedItem = expensiveComputation(item);
synchronized (lock) {
// 只在必要时使用锁
list.add(processedItem);
}
// I/O操作移出锁外
saveToDatabase(processedItem);
}
// 批量操作优化
public void batchOperation(List<String> items) {
List<String> processedItems = new ArrayList<>();
// 批量处理移出锁外
for (String item : items) {
processedItems.add(expensiveComputation(item));
}
synchronized (lock) {
// 批量添加,减少锁获取次数
list.addAll(processedItems);
}
}
}
7. 锁消除和锁粗化
public class LockOptimization {
// JVM会进行锁消除
public String lockElimination() {
StringBuffer sb = new StringBuffer(); // 局部变量
sb.append("Hello"); // 虽然StringBuffer是同步的
sb.append(" World"); // 但JVM会消除这些锁
return sb.toString();
}
// 手动锁粗化
public void manualLockCoarsening() {
synchronized (this) {
// 将多个同步块合并
operation1();
operation2();
operation3();
}
}
// 避免锁粗化过度
public void avoidOverCoarsening() {
synchronized (this) {
quickOperation1();
quickOperation2();
}
// 耗时操作移出锁外
expensiveOperation();
synchronized (this) {
quickOperation3();
}
}
}
8. 使用并发工具类
public class ConcurrentUtilities {
// 使用CountDownLatch替代wait/notify
public void useCountDownLatch() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
doWork();
} finally {
latch.countDown();
}
}).start();
}
latch.await(); // 比wait/notify更简洁
}
// 使用Semaphore控制并发数
private final Semaphore semaphore = new Semaphore(5);
public void limitedConcurrency() throws InterruptedException {
semaphore.acquire();
try {
// 最多5个线程同时执行
performTask();
} finally {
semaphore.release();
}
}
// 使用CyclicBarrier同步多线程
private final CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("All threads reached barrier");
});
public void synchronizeThreads() throws Exception {
// 执行第一阶段
doPhase1();
// 等待其他线程完成第一阶段
barrier.await();
// 执行第二阶段
doPhase2();
}
}
9. 避免死锁
public class DeadlockPrevention {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
// 固定锁顺序避免死锁
private void transferMoney(Account from, Account to, int amount) {
Account firstLock = from.getId() < to.getId() ? from : to;
Account secondLock = from.getId() < to.getId() ? to : from;
synchronized (firstLock) {
synchronized (secondLock) {
from.withdraw(amount);
to.deposit(amount);
}
}
}
// 使用tryLock避免死锁
private final ReentrantLock lockA = new ReentrantLock();
private final ReentrantLock lockB = new ReentrantLock();
public boolean tryTransfer() {
try {
if (lockA.tryLock(1, TimeUnit.SECONDS)) {
try {
if (lockB.tryLock(1, TimeUnit.SECONDS)) {
try {
// 执行转账操作
return true;
} finally {
lockB.unlock();
}
}
} finally {
lockA.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
}
10. 性能监控和调优
public class LockPerformanceMonitor {
private final ReentrantLock lock = new ReentrantLock();
private final AtomicLong totalWaitTime = new AtomicLong();
private final AtomicLong lockAcquisitions = new AtomicLong();
public void monitoredOperation() {
long startTime = System.nanoTime();
lock.lock();
try {
long lockAcquiredTime = System.nanoTime();
totalWaitTime.addAndGet(lockAcquiredTime - startTime);
lockAcquisitions.incrementAndGet();
// 执行业务逻辑
performOperation();
} finally {
lock.unlock();
}
}
public void printStatistics() {
long acquisitions = lockAcquisitions.get();
if (acquisitions > 0) {
long avgWaitTime = totalWaitTime.get() / acquisitions;
System.out.println("Average lock wait time: " + avgWaitTime + " ns");
System.out.println("Lock queue length: " + lock.getQueueLength());
}
}
// 使用JMX监控锁信息
public void jmxMonitoring() {
ThreadMXBean threadMX = ManagementFactory.getThreadMXBean();
if (threadMX.isObjectMonitorUsageSupported()) {
ThreadInfo[] infos = threadMX.dumpAllThreads(true, true);
for (ThreadInfo info : infos) {
MonitorInfo[] monitors = info.getLockedMonitors();
if (monitors.length > 0) {
System.out.println("Thread " + info.getThreadName() +
" holds " + monitors.length + " monitors");
}
}
}
}
}
最佳实践总结:
- 优先使用无锁数据结构
- 合理选择锁的粒度
- 缩短锁的持有时间
- 使用读写锁提高并发度
- 避免锁嵌套和死锁
- 使用并发工具类简化 开发
- 监控锁的性能指标
152. Redis 的 Pipeline 功能是什么?
展开 中等 VIP 后端 Redis
Pipeline简介: Redis Pipeline是一种通过减少网络往返次数来提高性能的技术。它允许客户端一次性发送多个命令,然后一次性接收所有响应。
工作原理:
不使用Pipeline:
客户端 -> 命令1 -> Redis -> 响应1 -> 客户端
客户端 -> 命令2 -> Redis -> 响应2 -> 客户端
客户端 -> 命令3 -> Redis -> 响应3 -> 客户端
总时间: 3 * (网络延迟 + 命令执行时间)
使用Pipeline:
客户端 -> [命令1, 命令2, 命令3] -> Redis -> [响应1, 响应2, 响应3] -> 客户端
总时间: 1 * 网络延迟 + 3 * 命令执行时间
Java实现示例:
1. Jedis Pipeline
@Component
public class JedisPipelineExample {
@Autowired
private JedisPool jedisPool;
// 不使用Pipeline的方式
public void withoutPipeline() {
try (Jedis jedis = jedisPool.getResource()) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
jedis.set("key" + i, "value" + i);
jedis.get("key" + i);
}
long endTime = System.currentTimeMillis();
System.out.println("Without pipeline: " + (endTime - startTime) + "ms");
}
}
// 使用Pipeline的方式
public void withPipeline() {
try (Jedis jedis = jedisPool.getResource()) {
long startTime = System.currentTimeMillis();
Pipeline pipeline = jedis.pipelined();
// 批量发送命令
for (int i = 0; i < 1000; i++) {
pipeline.set("key" + i, "value" + i);
pipeline.get("key" + i);
}
// 执行并获取所有响应
List<Object> results = pipeline.syncAndReturnAll();
long endTime = System.currentTimeMillis();
System.out.println("With pipeline: " + (endTime - startTime) + "ms");
System.out.println("Results count: " + results.size());
}
}
// 分批Pipeline处理
public void batchPipeline(List<String> keys, int batchSize) {
try (Jedis jedis = jedisPool.getResource()) {
for (int i = 0; i < keys.size(); i += batchSize) {
Pipeline pipeline = jedis.pipelined();
int endIndex = Math.min(i + batchSize, keys.size());
for (int j = i; j < endIndex; j++) {
pipeline.get(keys.get(j));
}
List<Object> results = pipeline.syncAndReturnAll();
processBatchResults(results);
}
}
}
private void processBatchResults(List<Object> results) {
for (Object result : results) {
if (result != null) {
System.out.println("Value: " + result);
}
}
}
}
2. RedisTemplate Pipeline
@Component
public class RedisTemplatePipelineExample {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void pipelineWithRedisTemplate() {
long startTime = System.currentTimeMillis();
List<Object> results = redisTemplate.executePipelined(
new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
for (int i = 0; i < 1000; i++) {
connection.set(
("key" + i).getBytes(),
("value" + i).getBytes()
);
connection.get(("key" + i).getBytes());
}
return null; // 必须返回null
}
}
);
long endTime = System.currentTimeMillis();
System.out.println("Pipeline execution time: " + (endTime - startTime) + "ms");
System.out.println("Results size: " + results.size());
}
// 使用StringRedisTemplate
public void stringTemplatePipeline() {
RedisTemplate<String, String> stringTemplate =
(RedisTemplate<String, String>) redisTemplate;
List<Object> results = stringTemplate.executePipelined(
(RedisCallback<Object>) connection -> {
StringRedisConnection stringConn = (StringRedisConnection) connection;
for (int i = 0; i < 100; i++) {
stringConn.set("user:" + i, "User" + i);
stringConn.expire("user:" + i, 3600);
stringConn.get("user:" + i);
}
return null;
}
);
System.out.println("Pipeline results: " + results.size());
}
}
3. Lettuce Pipeline
@Component
public class LettucePipelineExample {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public void lettuceAsyncPipeline() {
StatefulRedisConnection<String, String> connection =
(StatefulRedisConnection<String, String>) redisTemplate
.getConnectionFactory().getConnection().getNativeConnection();
RedisAsyncCommands<String, String> async = connection.async();
// 禁用自动刷新,实现pipeline效果
async.setAutoFlushCommands(false);
List<RedisFuture<String>> futures = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
futures.add(async.set("async:key" + i, "value" + i));
futures.add(async.get("async:key" + i));
}
// 手动刷新,发送所有命令
async.flushCommands();
// 等待所有命令完成
try {
LettuceFutures.awaitAll(Duration.ofSeconds(10),
futures.toArray(new RedisFuture[0]));
} catch (Exception e) {
e.printStackTrace();
}
// 重新启用自动刷新
async.setAutoFlushCommands(true);
}
}
实际应用场景:
1. 批量数据操作
@Service
public class BatchDataService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 批量缓存用户数据
public void batchCacheUsers(List<User> users) {
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
for (User user : users) {
String key = "user:" + user.getId();
byte[] keyBytes = key.getBytes();
byte[] valueBytes = serialize(user);
connection.setEx(keyBytes, 3600, valueBytes);
// 同时设置用户名映射
String nameKey = "user:name:" + user.getName();
connection.setEx(nameKey.getBytes(), 3600,
String.valueOf(user.getId()).getBytes());
}
return null;
});
}
// 批量获取用户数据
public List<User> batchGetUsers(List<Long> userIds) {
List<Object> results = redisTemplate.executePipelined(
(RedisCallback<Object>) connection -> {
for (Long userId : userIds) {
String key = "user:" + userId;
connection.get(key.getBytes());
}
return null;
}
);
List<User> users = new ArrayList<>();
for (Object result : results) {
if (result != null) {
users.add(deserialize((byte[]) result, User.class));
}
}
return users;
}
private byte[] serialize(Object obj) {
// 序列化实现
return null;
}
private <T> T deserialize(byte[] data, Class<T> clazz) {
// 反序列化实现
return null;
}
}
2. 分布式计数器
@Component
public class DistributedCounter {
@Autowired
private JedisPool jedisPool;
public Map<String, Long> batchIncrement(List<String> counters) {
Map<String, Long> results = new HashMap<>();
try (Jedis jedis = jedisPool.getResource()) {
Pipeline pipeline = jedis.pipelined();
// 批量增加计数器
List<Response<Long>> responses = new ArrayList<>();
for (String counter : counters) {
responses.add(pipeline.incr(counter));
}
pipeline.sync();
// 收集结果
for (int i = 0; i < counters.size(); i++) {
results.put(counters.get(i), responses.get(i).get());
}
}
return results;
}
// 批量设置过期时间
public void batchSetExpire(Map<String, Integer> keyExpires) {
try (Jedis jedis = jedisPool.getResource()) {
Pipeline pipeline = jedis.pipelined();
for (Map.Entry<String, Integer> entry : keyExpires.entrySet()) {
pipeline.expire(entry.getKey(), entry.getValue());
}
pipeline.sync();
}
}
}
3. 缓存预热
@Component
public class CacheWarmupService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private UserService userService;
// 批量预热用户缓存
public void warmupUserCache(List<Long> userIds) {
// 分批处理,避免Pipeline过大
int batchSize = 100;
for (int i = 0; i < userIds.size(); i += batchSize) {
List<Long> batch = userIds.subList(i,
Math.min(i + batchSize, userIds.size()));
warmupBatch(batch);
}
}
private void warmupBatch(List<Long> userIds) {
// 批量从数据库获取用户信息
List<User> users = userService.batchGetUsers(userIds);
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
// 使用Pipeline批量写入缓存
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
for (Long userId : userIds) {
User user = userMap.get(userId);
if (user != null) {
String key = "user:" + userId;
byte[] keyBytes = key.getBytes();
byte[] valueBytes = serialize(user);
connection.setEx(keyBytes, 3600, valueBytes);
// 同时缓存用户的其他信息
String profileKey = "user:profile:" + userId;
connection.setEx(profileKey.getBytes(), 3600,
serialize(user.getProfile()));
}
}
return null;
});
}
}
性能对比测试:
@Component
public class PipelinePerformanceTest {
@Autowired
private JedisPool jedisPool;
public void performanceComparison() {
int operationCount = 10000;
// 测试不使用Pipeline
long normalTime = testNormalOperations(operationCount);
// 测试使用Pipeline
long pipelineTime = testPipelineOperations(operationCount);
System.out.println("Normal operations: " + normalTime + "ms");
System.out.println("Pipeline operations: " + pipelineTime + "ms");
System.out.println("Performance improvement: " +
(normalTime / (double) pipelineTime) + "x");
}
private long testNormalOperations(int count) {
long startTime = System.currentTimeMillis();
try (Jedis jedis = jedisPool.getResource()) {
for (int i = 0; i < count; i++) {
jedis.set("normal:key" + i, "value" + i);
}
}
return System.currentTimeMillis() - startTime;
}
private long testPipelineOperations(int count) {
long startTime = System.currentTimeMillis();
try (Jedis jedis = jedisPool.getResource()) {
Pipeline pipeline = jedis.pipelined();
for (int i = 0; i < count; i++) {
pipeline.set("pipeline:key" + i, "value" + i);
}
pipeline.sync();
}
return System.currentTimeMillis() - startTime;
}
}
注意事项:
1. 内存使用
// Pipeline会在客户端缓存响应,注意内存使用
public void largePipelineHandling() {
try (Jedis jedis = jedisPool.getResource()) {
int maxBatchSize = 1000; // 限制批次大小
Pipeline pipeline = jedis.pipelined();
int currentBatch = 0;
for (int i = 0; i < 100000; i++) {
pipeline.set("key" + i, "value" + i);
currentBatch++;
if (currentBatch >= maxBatchSize) {
pipeline.sync();
pipeline = jedis.pipelined(); // 重新创建Pipeline
currentBatch = 0;
}
}
if (currentBatch > 0) {
pipeline.sync();
}
}
}
2. 错误处理
public void pipelineErrorHandling() {
try (Jedis jedis = jedisPool.getResource()) {
Pipeline pipeline = jedis.pipelined();
List<Response<String>> responses = new ArrayList<>();
for (int i = 0; i < 100; i++) {
responses.add(pipeline.set("key" + i, "value" + i));
}
pipeline.sync();
// 检查每个操作的结果
for (int i = 0; i < responses.size(); i++) {
try {
String result = responses.get(i).get();
// 处理成功结果
} catch (Exception e) {
System.err.println("Operation " + i + " failed: " + e.getMessage());
}
}
}
}
最佳实践:
- 合理控制Pipeline大小(通常1000-10000个命令)
- 对于大量数据操作,分批使用Pipeline
- 注意内存使用,避免Pipeline过大
- 在高延迟网络环境下效果更明显
- 结合业务需求选择合适的批次大小
153. 让你设计一个分布式 ID 发号器,怎么设计?
展开 困难 VIP 后端 系统设计 场景题
分布式ID设计要求:
- 唯一性:全局唯一,不能重复
- 有序性:趋势递增,便于数据库索引
- 高性能:高并发下快速生成
- 高可用:系统故障不影响服务
- 可扩展:支持水平扩展
方案一:雪花算法(Snowflake)
算法结构:
64位ID结构:
| 1位符号位 | 41位时间戳 | 10位机器ID | 12位序列号 |
| 0 | 时间差值 | 机器标识 | 递增序列 |
- 符号位:固定为0
- 时间戳:当前时间与起始时间的差值(毫秒)
- 机器ID:数据中心ID(5位) + 工作机器ID(5位)
- 序列号:同一毫秒内的递增序列
Java实现:
@Component
public class SnowflakeIdGenerator {
// 起始时间戳 (2020-01-01)
private final long EPOCH = 1577836800000L;
// 机器ID位数
private final long WORKER_ID_BITS = 5L;
private final long DATACENTER_ID_BITS = 5L;
// 序列号位数
private final long SEQUENCE_BITS = 12L;
// 最大值计算
private final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
private final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
// 位移偏移
private final long WORKER_ID_SHIFT = SEQUENCE_BITS;
private final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
private final long workerId;
private final long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId, long datacenterId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException("Worker ID must be between 0 and " + MAX_WORKER_ID);
}
if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
throw new IllegalArgumentException("Datacenter ID must be between 0 and " + MAX_DATACENTER_ID);
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
// 时钟回拨检查
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
// 同一毫秒内
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
// 序列号溢出,等待下一毫秒
if (sequence == 0) {
timestamp = waitNextMillis(timestamp);
}
} else {
// 新的毫秒,序列号重置
sequence = 0L;
}
lastTimestamp = timestamp;
// 组装ID
return ((timestamp - EPOCH) << TIMESTAMP_SHIFT) |
(datacenterId << DATACENTER_ID_SHIFT) |
(workerId << WORKER_ID_SHIFT) |
sequence;
}
private long waitNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
// ID解析
public IdInfo parseId(long id) {
long timestamp = ((id >> TIMESTAMP_SHIFT) + EPOCH);
long datacenterId = (id >> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID;
long workerId = (id >> WORKER_ID_SHIFT) & MAX_WORKER_ID;
long sequence = id & MAX_SEQUENCE;
return new IdInfo(timestamp, datacenterId, workerId, sequence);
}
@Data
public static class IdInfo {
private final long timestamp;
private final long datacenterId;
private final long workerId;
private final long sequence;
public IdInfo(long timestamp, long datacenterId, long workerId, long sequence) {
this.timestamp = timestamp;
this.datacenterId = datacenterId;
this.workerId = workerId;
this.sequence = sequence;
}
}
}
方案二:基于数据库的发号器
数据库表设计:
CREATE TABLE id_generator (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
business_type VARCHAR(50) NOT NULL,
max_id BIGINT NOT NULL DEFAULT 0,
step INT NOT NULL DEFAULT 1000,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_business_type (business_type)
);
-- 初始化数据
INSERT INTO id_generator (business_type, max_id, step) VALUES
('user_id', 0, 1000),
('order_id', 0, 1000);
Java实现:
@Component
public class DatabaseIdGenerator {
@Autowired
private JdbcTemplate jdbcTemplate;
private final Map<String, IdSegment> segmentCache = new ConcurrentHashMap<>();
private final ReentrantLock lock = new ReentrantLock();
public long nextId(String businessType) {
IdSegment segment = segmentCache.get(businessType);
if (segment == null || segment.isExhausted()) {
lock.lock();
try {
// 双重检查
segment = segmentCache.get(businessType);
if (segment == null || segment.isExhausted()) {
segment = getNewSegment(businessType);
segmentCache.put(businessType, segment);
}
} finally {
lock.unlock();
}
}
return segment.nextId();
}
private IdSegment getNewSegment(String businessType) {
String sql = "UPDATE id_generator SET max_id = max_id + step WHERE business_type = ?";
int updated = jdbcTemplate.update(sql, businessType);
if (updated == 0) {
throw new RuntimeException("Business type not found: " + businessType);
}
String querySql = "SELECT max_id, step FROM id_generator WHERE business_type = ?";
return jdbcTemplate.queryForObject(querySql, (rs, rowNum) -> {
long maxId = rs.getLong("max_id");
int step = rs.getInt("step");
return new IdSegment(maxId - step + 1, maxId);
}, businessType);
}
@Data
private static class IdSegment {
private final long start;
private final long end;
private final AtomicLong current;
public IdSegment(long start, long end) {
this.start = start;
this.end = end;
this.current = new AtomicLong(start - 1);
}
public long nextId() {
long id = current.incrementAndGet();
if (id > end) {
throw new RuntimeException("Segment exhausted");
}
return id;
}
public boolean isExhausted() {
return current.get() >= end;
}
}
}
方案三:基于Redis的发号器
@Component
public class RedisIdGenerator {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 简单递增
public long nextId(String businessType) {
String key = "id_generator:" + businessType;
return redisTemplate.opsForValue().increment(key);
}
// 批量获取ID段
private static final String BATCH_ID_SCRIPT =
"local key = KEYS[1] " +
"local step = tonumber(ARGV[1]) " +
"local current = redis.call('GET', key) " +
"if current == false then " +
" current = 0 " +
"else " +
" current = tonumber(current) " +
"end " +
"local max = current + step " +
"redis.call('SET', key, max) " +
"return {current + 1, max}";
public IdSegment getBatchIds(String businessType, int step) {
String key = "id_generator:" + businessType;
DefaultRedisScript<List> script = new DefaultRedisScript<>();
script.setScriptText(BATCH_ID_SCRIPT);
script.setResultType(List.class);
List<Object> result = redisTemplate.execute(script,
Collections.singletonList(key),
String.valueOf(step));
if (result != null && result.size() == 2) {
long start = Long.parseLong(result.get(0).toString());
long end = Long.parseLong(result.get(1).toString());
return new IdSegment(start, end);
}
throw new RuntimeException("Failed to get batch IDs");
}
// 带过期时间的ID生成
public String nextIdWithExpire(String businessType, long expireSeconds) {
String key = "id_generator:" + businessType;
String timeKey = key + ":time";
// 检查是否需要重置
String lastResetTime = redisTemplate.opsForValue().get(timeKey);
long currentTime = System.currentTimeMillis() / 1000;
if (lastResetTime == null ||
currentTime - Long.parseLong(lastResetTime) > expireSeconds) {
// 重置计数器
redisTemplate.opsForValue().set(key, "0");
redisTemplate.opsForValue().set(timeKey, String.valueOf(currentTime));
}
long id = redisTemplate.opsForValue().increment(key);
return String.format("%d_%06d", currentTime, id);
}
}
方案四:分段缓存优化
@Component
public class SegmentIdGenerator {
private final Map<String, DoubleBuffer> bufferMap = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
@Autowired
private DatabaseIdGenerator databaseGenerator;
public long nextId(String businessType) {
DoubleBuffer buffer = bufferMap.computeIfAbsent(businessType,
k -> new DoubleBuffer(businessType));
return buffer.nextId();
}
private class DoubleBuffer {
private final String businessType;
private volatile IdSegment currentSegment;
private volatile IdSegment nextSegment;
private volatile boolean isLoadingNext = false;
public DoubleBuffer(String businessType) {
this.businessType = businessType;
this.currentSegment = loadNewSegment();
}
public long nextId() {
// 当前段使用率超过75%时,异步加载下一段
if (currentSegment.getUsagePercent() > 0.75 &&
nextSegment == null && !isLoadingNext) {
synchronized (this) {
if (nextSegment == null && !isLoadingNext) {
isLoadingNext = true;
scheduler.submit(this::loadNextSegment);
}
}
}
long id = currentSegment.nextId();
// 当前段用完,切换到下一段
if (currentSegment.isExhausted()) {
synchronized (this) {
if (currentSegment.isExhausted()) {
if (nextSegment != null) {
currentSegment = nextSegment;
nextSegment = null;
isLoadingNext = false;
} else {
// 紧急加载新段
currentSegment = loadNewSegment();
}
}
}
}
return id;
}
private void loadNextSegment() {
try {
nextSegment = loadNewSegment();
} catch (Exception e) {
log.error("Failed to load next segment for " + businessType, e);
} finally {
isLoadingNext = false;
}
}
private IdSegment loadNewSegment() {
return databaseGenerator.getNewSegment(businessType);
}
}
}
方案五:美团Leaf方案
@Component
public class LeafIdGenerator {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private final Map<String, LeafSegment> segmentMap = new ConcurrentHashMap<>();
// Leaf-segment方案
public long nextId(String businessType) {
LeafSegment segment = segmentMap.computeIfAbsent(businessType,
this::createLeafSegment);
return segment.nextId();
}
private LeafSegment createLeafSegment(String businessType) {
return new LeafSegment(businessType, redisTemplate);
}
private static class LeafSegment {
private final String businessType;
private final RedisTemplate<String, String> redisTemplate;
private volatile Segment currentSegment;
private volatile Segment nextSegment;
private final ReentrantLock lock = new ReentrantLock();
public LeafSegment(String businessType, RedisTemplate<String, String> redisTemplate) {
this.businessType = businessType;
this.redisTemplate = redisTemplate;
this.currentSegment = getNewSegment();
}
public long nextId() {
if (currentSegment.getUsagePercent() > 0.9 && nextSegment == null) {
lock.lock();
try {
if (nextSegment == null) {
nextSegment = getNewSegment();
}
} finally {
lock.unlock();
}
}
long id = currentSegment.nextId();
if (currentSegment.isExhausted()) {
lock.lock();
try {
if (currentSegment.isExhausted()) {
currentSegment = nextSegment != null ? nextSegment : getNewSegment();
nextSegment = null;
}
} finally {
lock.unlock();
}
}
return id;
}
private Segment getNewSegment() {
// 基于Redis的分段获取逻辑
String key = "leaf:segment:" + businessType;
Long start = redisTemplate.opsForValue().increment(key, 1000);
return new Segment(start - 999, start);
}
}
}
性能测试和监控:
@Component
public class IdGeneratorBenchmark {
@Autowired
private SnowflakeIdGenerator snowflakeGenerator;
@Autowired
private DatabaseIdGenerator databaseGenerator;
@Autowired
private RedisIdGenerator redisGenerator;
public void performanceBenchmark() {
int threadCount = 10;
int operationsPerThread = 100000;
// 测试Snowflake
long snowflakeTime = testGenerator("Snowflake",
() -> snowflakeGenerator.nextId(), threadCount, operationsPerThread);
// 测试Database
long databaseTime = testGenerator("Database",
() -> databaseGenerator.nextId("test"), threadCount, operationsPerThread);
// 测试Redis
long redisTime = testGenerator("Redis",
() -> redisGenerator.nextId("test"), threadCount, operationsPerThread);
System.out.println("Snowflake QPS: " + calculateQPS(snowflakeTime, threadCount, operationsPerThread));
System.out.println("Database QPS: " + calculateQPS(databaseTime, threadCount, operationsPerThread));
System.out.println("Redis QPS: " + calculateQPS(redisTime, threadCount, operationsPerThread));
}
private long testGenerator(String name, Supplier<Long> generator,
int threadCount, int operationsPerThread) {
CountDownLatch latch = new CountDownLatch(threadCount);
long startTime = System.currentTimeMillis();
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
for (int j = 0; j < operationsPerThread; j++) {
generator.get();
}
} finally {
latch.countDown();
}
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long endTime = System.currentTimeMillis();
System.out.println(name + " execution time: " + (endTime - startTime) + "ms");
return endTime - startTime;
}
private long calculateQPS(long timeMs, int threadCount, int operationsPerThread) {
long totalOperations = (long) threadCount * operationsPerThread;
return totalOperations * 1000 / timeMs;
}
}
方案选择建议:
方案 | QPS | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
Snowflake | >100万 | 性能高,无依赖 | 时钟回拨问题 | 高并发系统 |
数据库 | <1万 | 简单可靠 | 性能低,单点 | 小规模系统 |
Redis | 10-50 万 | 高性能,支持集群 | 依赖Redis | 中大型系统 |
分段缓存 | 50-100万 | 高性能,降级能力 | 复杂度高 | 大型系统 |
154. 什么是服务雪崩?
展开 简单 VIP 服务容灾 Spring Cloud 微服务 后端
服务雪崩定义: 服务雪崩是指在微服务架构中,当一个服务发生故障时,会导致依赖它的其他服务也发生故障,进而引发连锁反应,最终导致整个系统崩溃的现象。
雪崩产生的原因:
1. 服务依赖链过长
用户请求 -> 服务A -> 服务B -> 服务C -> 数据库
↓
服务C故障
↓
服务B等待超时
↓
服务A等待超时
↓
整个链路阻塞
2. 资源耗尽
// 线程池耗尽示例
@Service
public class UserService {
// 固定大小线程池
private final ExecutorService executor = Executors.newFixedThreadPool(10);
public User getUserInfo(Long userId) {
// 调用下游服务获取用户基本信息
CompletableFuture<UserBasic> basicFuture = CompletableFuture.supplyAsync(() -> {
return userBasicService.getBasic(userId); // 假设这个服务响应缓慢
}, executor);
// 调用下游服务获取用户扩展信息
CompletableFuture<UserExtra> extraFuture = CompletableFuture.supplyAsync(() -> {
return userExtraService.getExtra(userId); // 这个服务也响应缓慢
}, executor);
try {
UserBasic basic = basicFuture.get(5, TimeUnit.SECONDS);
UserExtra extra = extraFuture.get(5, TimeUnit.SECONDS);
return buildUser(basic, extra);
} catch (Exception e) {
// 如果下游服务一直不响应,线程池会被耗尽
throw new ServiceException("Get user info failed", e);
}
}
}
3. 缓存击穿
@Service
public class ProductService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductRepository productRepository;
public Product getProduct(Long productId) {
String key = "product:" + productId;
// 从缓存获取
Product product = (Product) redisTemplate.opsForValue().get(key);
if (product != null) {
return product;
}
// 缓存失效,大量请求打到数据库
product = productRepository.findById(productId);
if (product != null) {
// 设置缓存
redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES);
}
return product;
}
}
雪崩的影响:
1. 系统响应时间急剧增加
// 模拟雪崩效应
@RestController
public class OrderController {
@Autowired
private UserService userService;
@Autowired
private ProductService productService;
@Autowired
private InventoryService inventoryService;
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
long startTime = System.currentTimeMillis();
try {
// 步骤1:验证用户 (假设用户服务正常)
User user = userService.getUser(request.getUserId());
// 步骤2:获取商品信息 (假设商品服务故障)
Product product = productService.getProduct(request.getProductId());
// 步骤3:检查库存 (由于步骤2阻塞,这步无法执行)
boolean hasStock = inventoryService.checkStock(request.getProductId(), request.getQuantity());
// 创建订单
Order order = createOrderInternal(user, product, request);
return ResponseEntity.ok(order);
} catch (Exception e) {
long endTime = System.currentTimeMillis();
log.error("Create order failed, cost: {}ms", endTime - startTime, e);
// 响应时间从正常的100ms增加到5000ms甚至更多
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
2. 系统资源消耗激增
// 监控系统资源
@Component
public class SystemMonitor {
private final MeterRegistry meterRegistry;
public SystemMonitor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@EventListener
public void handleServiceCall(ServiceCallEvent event) {
// 记录服务调用指标
Timer.Sample sample = Timer.start(meterRegistry);
try {
// 业务逻辑
} finally {
sample.stop(Timer.builder("service.call")
.tag("service", event.getServiceName())
.tag("status", event.getStatus())
.register(meterRegistry));
}
// 监控线程池状态
ThreadPoolTaskExecutor executor = getExecutor();
Gauge.builder("thread.pool.active")
.register(meterRegistry, executor, ThreadPoolTaskExecutor::getActiveCount);
Gauge.builder("thread.pool.queue.size")
.register(meterRegistry, executor, e -> e.getThreadPoolExecutor().getQueue().size());
}
}
预防服务雪崩的策略:
1. 服务熔断(Circuit Breaker)
@Component
public class CircuitBreakerService {
private final CircuitBreaker circuitBreaker;
public CircuitBreakerService() {
this.circuitBreaker = CircuitBreaker.ofDefaults("userService");
// 配置熔断器
circuitBreaker.getEventPublisher()
.onStateTransition(event ->
log.info("Circuit breaker state transition: {}", event));
}
public User getUser(Long userId) {
Supplier<User> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, () -> {
return userServiceClient.getUser(userId);
});
try {
return decoratedSupplier.get();
} catch (CallNotPermittedException e) {
// 熔断器开 启,返回默认值或缓存数据
return getDefaultUser(userId);
}
}
private User getDefaultUser(Long userId) {
// 返回默认用户信息或从缓存获取
return User.builder()
.id(userId)
.name("默认用户")
.status("UNKNOWN")
.build();
}
}
2. 服务限流(Rate Limiting)
@Component
public class RateLimiterService {
private final RateLimiter rateLimiter;
public RateLimiterService() {
// 创建限流器:每秒最多100个请求
this.rateLimiter = RateLimiter.create(100.0);
}
@PostMapping("/api/orders")
public ResponseEntity<?> createOrder(@RequestBody OrderRequest request) {
// 尝试获取许可
if (!rateLimiter.tryAcquire(1, TimeUnit.SECONDS)) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
.body("请求过于频繁,请稍后再试");
}
try {
Order order = orderService.createOrder(request);
return ResponseEntity.ok(order);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("订单创建失败");
}
}
}
3. 服务降级(Fallback)
@Service
public class RecommendationService {
@Autowired
private RecommendationClient recommendationClient;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public List<Product> getRecommendations(Long userId) {
try {
// 尝试调用推荐服务
return recommendationClient.getRecommendations(userId);
} catch (Exception e) {
log.warn("推荐服务调用失败,使用降级策略", e);
return getFallbackRecommendations(userId);
}
}
private List<Product> getFallbackRecommendations(Long userId) {
// 降级策略1:从缓存获取
String cacheKey = "recommendations:fallback:" + userId;
List<Product> cached = (List<Product>) redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return cached;
}
// 降级策略2:返回热门商品
List<Product> hotProducts = getHotProducts();
// 缓存降级结果
redisTemplate.opsForValue().set(cacheKey, hotProducts, 10, TimeUnit.MINUTES);
return hotProducts;
}
private List<Product> getHotProducts() {
// 返回预设的热门商品列表
return Arrays.asList(
Product.builder().id(1L).name("热门商品1").build(),
Product.builder().id(2L).name("热门商品2").build(),
Product.builder().id(3L).name("热门商品3").build()
);
}
}
4. 线程池隔离
@Configuration
public class ThreadPoolConfig {
// 用户服务线程池
@Bean("userServiceExecutor")
public ThreadPoolTaskExecutor userServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("user-service-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
// 订单服务线程池
@Bean("orderServiceExecutor")
public ThreadPoolTaskExecutor orderServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(15);
executor.setMaxPoolSize(30);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("order-service-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
executor.initialize();
return executor;
}
}
@Service
public class IsolatedService {
@Autowired
@Qualifier("userServiceExecutor")
private ThreadPoolTaskExecutor userServiceExecutor;
@Autowired
@Qualifier("orderServiceExecutor")
private ThreadPoolTaskExecutor orderServiceExecutor;
public CompletableFuture<User> getUserAsync(Long userId) {
return CompletableFuture.supplyAsync(() -> {
return userService.getUser(userId);
}, userServiceExecutor);
}
public CompletableFuture<Order> createOrderAsync(OrderRequest request) {
return CompletableFuture.supplyAsync(() -> {
return orderService.createOrder(request);
}, orderServiceExecutor);
}
}
5. 超时控制
@Component
public class TimeoutService {
@Async
@Retryable(value = {Exception.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public CompletableFuture<String> callExternalService(String request) {
return CompletableFuture.supplyAsync(() -> {
try {
// 设置超时时间
return restTemplate.exchange(
"http://external-service/api",
HttpMethod.POST,
new HttpEntity<>(request),
String.class
).getBody();
} catch (Exception e) {
throw new ServiceException("External service call failed", e);
}
}).orTimeout(3, TimeUnit.SECONDS) // 3秒超时
.exceptionally(throwable -> {
log.error("Service call timeout or failed", throwable);
return "DEFAULT_RESPONSE";
});
}
}
6. 缓存策略
@Service
public class CacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 多级缓存
public Product getProduct(Long productId) {
String key = "product:" + productId;
// L1缓存:本地缓存
Product product = localCache.get(key);
if (product != null) {
return product;
}
// L2缓存:Redis缓存
product = (Product) redisTemplate.opsForValue().get(key);
if (product != null) {
localCache.put(key, product, 5, TimeUnit.MINUTES);
return product;
}
// 缓存穿透保护
try {
product = productRepository.findById(productId);
if (product != null) {
// 写入多级缓存
redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES);
localCache.put(key, product, 5, TimeUnit.MINUTES);
} else {
// 空值缓存,防止缓存穿透
redisTemplate.opsForValue().set(key, "NULL", 5, TimeUnit.MINUTES);
}
} catch (Exception e) {
// 数据库异常时,返回默认值
return getDefaultProduct(productId);
}
return product;
}
}
监控和告警:
@Component
public class AvalancheMonitor {
private final MeterRegistry meterRegistry;
private final AlertService alertService;
// 监控服务调用失败率
@EventListener
public void onServiceCallFailed(ServiceCallFailedEvent event) {
Counter.builder("service.call.failed")
.tag("service", event.getServiceName())
.tag("error", event.getErrorType())
.register(meterRegistry)
.increment();
// 检查失败率是否超过阈值
double failureRate = calculateFailureRate(event.getServiceName());
if (failureRate > 0.5) { // 失败率超过50%
alertService.sendAlert("服务雪崩预警",
String.format("服务 %s 失败率达到 %.2f%%", event.getServiceName(), failureRate * 100));
}
}
// 监控响应时间
@EventListener
public void onServiceCallCompleted(ServiceCallCompletedEvent event) {
Timer.Sample sample = Timer.start(meterRegistry);
sample.stop(Timer.builder("service.call.duration")
.tag("service", event.getServiceName())
.register(meterRegistry));
// 检查响应时间是否异常
if (event.getDuration().toMillis() > 5000) { // 超过5秒
alertService.sendAlert("服务响应缓 慢",
String.format("服务 %s 响应时间 %d ms", event.getServiceName(), event.getDuration().toMillis()));
}
}
}
最佳实践:
- 设计时考虑故障:假设依赖服务会失败
- 实施多层防护:熔断 + 限流 + 降级 + 超时
- 监控关键指标:响应时间、错误率、资源使用率
- 定期演练:混沌工程,主动制造故障
- 快速恢复:自动化部署和回滚机制
155. JVM 由哪些部分组成?
展开 中等 VIP 后端 Java JVM
JVM整体架构:
JVM(Java Virtual Machine)主要由以下几个部分组成:
1. 类加载子系统(Class Loading Subsystem)
组成部分:
- 类加载器(ClassLoader)
- 加载(Loading)
- 链接(Linking)
- 初始化(Initialization)
// 类加载器层次结构示例
public class ClassLoaderExample {
public static void main(String[] args) {
// 获取不同的类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader extClassLoader = systemClassLoader.getParent();
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println("Bootstrap ClassLoader: " + bootstrapClassLoader); // null
System.out.println("Extension ClassLoader: " + extClassLoader);
System.out.println("System ClassLoader: " + systemClassLoader);
// 查看类的加载器
System.out.println("String class loader: " + String.class.getClassLoader()); // null (Bootstrap)
System.out.println("ArrayList class loader: " + java.util.ArrayList.class.getClassLoader()); // null (Bootstrap)
System.out.println("Current class loader: " + ClassLoaderExample.class.getClassLoader()); // System
}
}
// 自定义类加载器
public class CustomClassLoader extends ClassLoader {
private String classpath;
public CustomClassLoader(String classpath) {
this.classpath = classpath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] classData = loadClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
return defineClass(name, classData, 0, classData.length);
} catch (Exception e) {
throw new ClassNotFoundException();
}
}
private byte[] loadClassData(String className) {
// 从指定路径加载类文件
String path = classpath + File.separatorChar +
className.replace('.', File.separatorChar) + ".class";
try {
return Files.readAllBytes(Paths.get(path));
} catch (IOException e) {
return null;
}
}
}
2. 运行时数据区(Runtime Data Area)
2.1 方法区(Method Area)/ 元空间(Metaspace)
// 方法区存储的内容示例
public class MethodAreaExample {
// 常量池中的字符串字面量
private static final String CONSTANT = "Hello World";
// 类变量存储在方法区
private static int classVariable = 100;
// 实例变量存储在堆中,但变量定义信息在方法区
private int instanceVariable;
// 方法字节码存储在方法区
public void method() {
System.out.println("Method bytecode stored in Method Area");
}
// 使用jcmd查看元 空间信息
public static void printMetaspaceInfo() {
MemoryMXBean memoryMX = ManagementFactory.getMemoryMXBean();
MemoryUsage metaspaceUsage = memoryMX.getNonHeapMemoryUsage();
System.out.println("Metaspace Used: " + metaspaceUsage.getUsed() / 1024 / 1024 + " MB");
System.out.println("Metaspace Committed: " + metaspaceUsage.getCommitted() / 1024 / 1024 + " MB");
System.out.println("Metaspace Max: " + metaspaceUsage.getMax() / 1024 / 1024 + " MB");
}
}
2.2 堆内存(Heap Memory)
// 堆内存结构示例
public class HeapMemoryExample {
public static void main(String[] args) {
printHeapInfo();
// 创建对象,分配在堆中
List<String> list = new ArrayList<>();
// 年轻代分配
for (int i = 0; i < 1000; i++) {
list.add("String " + i); // 这些对象首先在Eden区分配
}
// 触发Minor GC
System.gc();
printHeapInfo();
// 创建大对象,可能直接进入老年代
int[] largeArray = new int[1024 * 1024]; // 4MB数组
printHeapInfo();
}
private static void printHeapInfo() {
MemoryMXBean memoryMX = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryMX.getHeapMemoryUsage();
System.out.println("=== Heap Memory Info ===");
System.out.println("Heap Used: " + heapUsage.getUsed() / 1024 / 1024 + " MB");
System.out.println("Heap Committed: " + heapUsage.getCommitted() / 1024 / 1024 + " MB");
System.out.println("Heap Max: " + heapUsage.getMax() / 1024 / 1024 + " MB");
// 获取各个内存池的信息
List<MemoryPoolMXBean> memoryPools = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean pool : memoryPools) {
if (pool.getType() == MemoryType.HEAP) {
MemoryUsage usage = pool.getUsage();
System.out.println(pool.getName() + ": " +
usage.getUsed() / 1024 / 1024 + " MB");
}
}
System.out.println();
}
}
// GC监控示例
public class GCMonitor {
public static void setupGCMonitoring() {
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
NotificationEmitter emitter = (NotificationEmitter) gcBean;
emitter.addNotificationListener(new NotificationListener() {
@Override
public void handleNotification(Notification notification, Object handback) {
if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
GarbageCollectionNotificationInfo info =
GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
System.out.println("GC Event: " + info.getGcName());
System.out.println("Duration: " + info.getGcInfo().getDuration() + " ms");
System.out.println("Cause: " + info.getGcCause());
Map<String, MemoryUsage> before = info.getGcInfo().getMemoryUsageBeforeGc();
Map<String, MemoryUsage> after = info.getGcInfo().getMemoryUsageAfterGc();
for (String poolName : before.keySet()) {
MemoryUsage beforeUsage = before.get(poolName);
MemoryUsage afterUsage = after.get(poolName);
System.out.println(poolName + ": " +
beforeUsage.getUsed() / 1024 / 1024 + " MB -> " +
afterUsage.getUsed() / 1024 / 1024 + " MB");
}
}
}
}, null, null);
}
}
}
2.3 虚拟机栈(VM Stack)
// 虚拟机栈示例
public class VMStackExample {
// 局部变量表、操作数栈、动态链接、方法返回地址
public int calculate(int a, int b) {
// 局部变量存储在局部变量表中
int result = 0;
int temp = a + b;
// 操作数栈用于计算
result = temp * 2;
// 方法调用,栈帧压栈
int doubled = doubleValue(result);
// 返回地址指向调用者
return doubled;
}
private int doubleValue(int value) {
return value * 2;
}
// 栈溢出示例
public void stackOverflowExample() {
try {
recursiveMethod(1);
} catch (StackOverflowError e) {
System.out.println("Stack overflow occurred!");
}
}
private void recursiveMethod(int depth) {
System.out.println("Recursion depth: " + depth);
// 无限递归导致栈溢出
recursiveMethod(depth + 1);
}
// 查看栈信息
public static void printStackInfo() {
ThreadMXBean threadMX = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMX.dumpAllThreads(false, false);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("Thread: " + threadInfo.getThreadName());
System.out.println("State: " + threadInfo.getThreadState());
StackTraceElement[] stackTrace = threadInfo.getStackTrace();
for (StackTraceElement element : stackTrace) {
System.out.println(" " + element.toString());
}
System.out.println();
}
}
}
2.4 本地方法栈(Native Method Stack)
// 本 地方法栈示例
public class NativeMethodExample {
// 声明本地方法
public native void nativeMethod();
// 加载本地库
static {
System.loadLibrary("nativelib");
}
// 使用JNI的例子
public void useNativeMethod() {
// 调用本地方法,会使用本地方法栈
nativeMethod();
// 系统方法也是本地方法
long currentTime = System.currentTimeMillis();
System.out.println("Current time: " + currentTime);
// 数组复制也是本地方法
int[] source = {1, 2, 3, 4, 5};
int[] dest = new int[5];
System.arraycopy(source, 0, dest, 0, 5);
}
}
2.5 程序计数器(Program Counter Register)
// 程序计数器示例
public class PCRegisterExample {
public void demonstratePC() {
int a = 1; // PC指向这条指令的地址
int b = 2; // PC递增,指向下一条指令
int c = a + b; // PC继续递增
if (c > 2) { // 条件跳转,PC可能跳转
System.out.println("c > 2");
} else {
System.out.println("c <= 2");
}
// 循环中的PC变化
for (int i = 0; i < 3; i++) { // PC在循环中跳转
System.out.println("i = " + i);
}
}
// 多线程中每个线程都有自己的PC寄存器
public void multiThreadPC() {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread1: " + i);
Thread.yield(); // 线程切换,PC寄存器保存当前执行位置
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread2: " + i);
Thread.yield();
}
});
thread1.start();
thread2.start();
}
}
3. 执行引擎(Execution Engine)
3.1 解释器(Interpreter)
// 解释器执行示例
public class InterpreterExample {
// 解释器逐行执行字节码
public void interpretedMethod() {
System.out.println("This method is interpreted");
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i; // 每次循环都解释执行
}
System.out.println("Sum: " + sum);
}
// 查看方法是否被JIT编译
public static void checkCompilation() {
CompilerMXBean compilerMX = ManagementFactory.getCompilerMXBean();
System.out.println("JIT Compiler: " + compilerMX.getName());
System.out.println("Total compilation time: " + compilerMX.getTotalCompilationTime() + " ms");
}
}
3.2 即时编译器(JIT Compiler)
// JIT编译示例
public class JITCompilerExample {
private static final int LOOP_COUNT = 100000;
// 热点方法,会被JIT编译
public static int hotMethod(int x) {
return x * x + 2 * x + 1;
}
public static void main(String[] args) {
// 预热,让方法成为热点
for (int i = 0; i < LOOP_COUNT; i++) {
hotMethod(i);
}
// 测试JIT编译后的性能
long startTime = System.nanoTime();
int result = 0;
for (int i = 0; i < LOOP_COUNT; i++) {
result += hotMethod(i);
}
long endTime = System.nanoTime();
System.out.println("JIT compiled execution time: " +
(endTime - startTime) / 1000000.0 + " ms");
System.out.println("Result: " + result);
// 显示编译信息(需要JVM参数:-XX:+PrintCompilation)
printCompilationInfo();
}
private static void printCompilationInfo() {
try {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName("java.lang:type=Compilation");
if (server.isRegistered(objectName)) {
Long compilationTime = (Long) server.getAttribute(objectName, "TotalCompilationTime");
System.out.println("Total JIT compilation time: " + compilationTime + " ms");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// C1和C2编译器配置示例
public class TieredCompilationExample {
public static void demonstrateTieredCompilation() {
// JVM参数示例:
// -XX:+TieredCompilation // 开启分层编译
// -XX:TieredStopAtLevel=1 // 只使用C1编译器
// -XX:TieredStopAtLevel=4 // 使用C1和C2编译器(默认)
// -XX:CompileThreshold=10000 // 设置编译阈值
System.out.println("Demonstrating tiered compilation...");
// 方法调用次数统计
int threshold = 0;
RuntimeMXBean runtimeMX = ManagementFactory.getRuntimeMXBean();
List<String> jvmArgs = runtimeMX.getInputArguments();
for (String arg : jvmArgs) {
if (arg.startsWith("-XX:CompileThreshold=")) {
threshold = Integer.parseInt(arg.substring(21));
break;
}
}
System.out.println("Compile threshold: " + threshold);
}
}
4. 本地方法接口(Native Method Interface)
// JNI示例
public class NativeInterfaceExample {
// 本地方法声明
public native String stringFromJNI();
public native int addNumbers(int a, int b);
static {
// 加载本地库
try {
System.loadLibrary("nativeinterface");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native library failed to load: " + e);
}
}
public void demonstrateJNI() {
try {
String message = stringFromJNI();
System.out.println("Message from native: " + message);
int result = addNumbers(10, 20);
System.out.println("Native addition result: " + result);
} catch (UnsatisfiedLinkError e) {
System.err.println("Native method call failed: " + e);
}
}
}
JVM参数配置示例:
# 堆内存配置
-Xms2g # 初始堆大小
-Xmx4g # 最大堆大小
-Xmn1g # 年轻代大小
-XX:SurvivorRatio=8 # Eden:Survivor比例
# 元空间配置
-XX:MetaspaceSize=256m # 初始元空间大小
-XX:MaxMetaspaceSize=512m # 最大元空间大小
# 垃圾收集器配置
-XX:+UseG1GC # 使用G1垃圾收集器
-XX:MaxGCPauseMillis=200 # 最大GC暂停时间
# JIT编译器配置
-XX:+TieredCompilation # 开启分层编译
-XX:CompileThreshold=10000 # 编译阈值
# 监控和调试
-XX:+PrintGC # 打印GC信息
-XX:+PrintGCDetails # 打印详细GC信息
-XX:+HeapDumpOnOutOfMemoryError # OOM时生成堆转储
内存分析工具使用:
// 内存分析示例
public class MemoryAnalysisExample {
public static void analyzeMemory() {
Runtime runtime = Runtime.getRuntime();
System.out.println("=== Memory Analysis ===");
System.out.println("Max memory: " + runtime.maxMemory() / 1024 / 1024 + " MB");
System.out.println("Total memory: " + runtime.totalMemory() / 1024 / 1024 + " MB");
System.out.println("Free memory: " + runtime.freeMemory() / 1024 / 1024 + " MB");
System.out.println("Used memory: " +
(runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024 + " MB");
// 获取详细的内存池信息
List<MemoryPoolMXBean> memoryPools = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean pool : memoryPools) {
MemoryUsage usage = pool.getUsage();
System.out.println(pool.getName() + ":");
System.out.println(" Used: " + usage.getUsed() / 1024 / 1024 + " MB");
System.out.println(" Committed: " + usage.getCommitted() / 1024 / 1024 + " MB");
System.out.println(" Max: " + (usage.getMax() == -1 ? "Unlimited" :
usage.getMax() / 1024 / 1024 + " MB"));
}
}
// 生成内存快照
public static void generateHeapDump() {
try {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean hotspotMBean =
ManagementFactory.newPlatformMXBeanProxy(server,
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class);
String dumpFile = "heapdump_" + System.currentTimeMillis() + ".hprof";
hotspotMBean.dumpHeap(dumpFile, true);
System.out.println("Heap dump generated: " + dumpFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结: JVM的主要组成部分协同工作,为Java程序提供了运行环境:
- 类加载子系统: 负责加载、链接和初始化类
- 运行时数据区:提供程序运行所需的内存空间
- 执行引擎:解释和编译执行字节码
- 本地方法接口:支持调用本地方法
156. Redis 通常应用于哪些场景?
展开 简单 VIP 后端 Redis
Redis应用场景详解:
1. 缓存(Cache)
- 页面缓存:缓存整页HTML
- 数据缓存:缓存数据库查询结果
- 对象缓存:缓存序列化对象
- 多级缓存:与本地缓存配合
2. 会话存储(Session Store)
- Web会话:存储用户登录状态
- 购物车:电商购物车数据
- 用户偏好:个性化设置
3. 分布式锁
- 资源互斥:防止并发修改
- 任务调度:分布式任务锁
- 限流控制:API访问限制
4. 计数器
- 访问统计:页面PV/UV统计
- 点赞收藏:社交功能计数
- 库存管理:商品库存计数
5. 消息队列
- 任务队列:异步任务处理
- 发布订阅:实时消息推送
- 延时队列:定时任务处理
6. 排行榜
- 游戏排行:分数排序
- 热搜榜:实时热点
- 销量排行:商品销量统计
7. 地理位置
- 附近的人:LBS应用
- 配送范围:外卖配送
- 轨迹跟踪:位置记录
8. 实时系统
- 在线状态:用户在线检测
- 实时统计:监控指标
- 活动限时:秒杀活动
157. 让你设计一个短链系统,怎么设计?
展开 困难 VIP 后端 系统设计 场景题
短链系统设计要求:
- 高并发:支持大量短链生成和访问
- 高可用:7x24小时稳定服务
- 唯一性:短链不能重复
- 时效性:支持链接过期
- 统计性:访问数据统计
架构设计:
1. 整体架构
[客户端] -> [CDN] -> [负载均衡] -> [API网关] -> [短链服务集群]
↓
[Redis集群] <- [短链服务] -> [MySQL主从] -> [大数据分析]
2. 核心算法
方案一:Base62编码
@Service
public class Base62ShortUrlService {
private static final String BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static final int BASE = 62;
@Autowired
private DistributedIdGenerator idGenerator;
public String generateShortUrl(String longUrl) {
// 生成唯一ID
long id = idGenerator.nextId();
// Base62编码
String shortCode = encodeBase62(id);
// 存储映射关系
saveMapping(shortCode, longUrl);
return "https://short.ly/" + shortCode;
}
private String encodeBase62(long num) {
if (num == 0) return "0";
StringBuilder sb = new StringBuilder();
while (num > 0) {
sb.append(BASE62.charAt((int)(num % BASE)));
num /= BASE;
}
return sb.reverse().toString();
}
}
方案二:哈希算法
@Service
public class HashShortUrlService {
public String generateShortUrl(String longUrl) {
// MD5哈希
String hash = DigestUtils.md5Hex(longUrl + System.currentTimeMillis());
// 取前6位作为短码
String shortCode = hash.substring(0, 6);
// 冲突检测和处理
int retry = 0;
while (exists(shortCode) && retry < 5) {
hash = DigestUtils.md5Hex(longUrl + System.currentTimeMillis() + retry);
shortCode = hash.substring(0, 6);
retry++;
}
saveMapping(shortCode, longUrl);
return "https://short.ly/" + shortCode;
}
}
3. 数据存储
MySQL设计
-- 短链映射表
CREATE TABLE short_url_mapping (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
short_code VARCHAR(10) NOT NULL UNIQUE,
long_url TEXT NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expire_time TIMESTAMP NULL,
creator_id BIGINT,
status TINYINT DEFAULT 1,
INDEX idx_short_code (short_code),
INDEX idx_creator_time (creator_id, create_time)
);
-- 访问统计表
CREATE TABLE url_access_stats (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
short_code VARCHAR(10) NOT NULL,
access_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_address VARCHAR(45),
user_agent TEXT,
referer TEXT,
INDEX idx_short_code_time (short_code, access_time)
);
Redis缓存
@Service
public class ShortUrlCacheService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String URL_CACHE_PREFIX = "short_url:";
private static final String STATS_PREFIX = "stats:";
public void cacheMapping(String shortCode, String longUrl, int expireHours) {
String key = URL_CACHE_PREFIX + shortCode;
redisTemplate.opsForValue().set(key, longUrl, expireHours, TimeUnit.HOURS);
}
public String getLongUrl(String shortCode) {
String key = URL_CACHE_PREFIX + shortCode;
return redisTemplate.opsForValue().get(key);
}
public void incrementAccessCount(String shortCode) {
String key = STATS_PREFIX + shortCode;
redisTemplate.opsForValue().increment(key);
}
}
4. 核心服务实现
@RestController
@RequestMapping("/api/shorturl")
public class ShortUrlController {
@Autowired
private ShortUrlService shortUrlService;
// 生成短链
@PostMapping("/generate")
public ResponseEntity<ShortUrlResponse> generateShortUrl(@RequestBody ShortUrlRequest request) {
try {
String shortUrl = shortUrlService.generateShortUrl(
request.getLongUrl(),
request.getExpireHours(),
request.getUserId()
);
return ResponseEntity.ok(ShortUrlResponse.builder()
.shortUrl(shortUrl)
.originalUrl(request.getLongUrl())
.build());
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ShortUrlResponse.builder().error("生成失败").build());
}
}
// 访问重定向
@GetMapping("/{shortCode}")
public ResponseEntity<?> redirect(@PathVariable String shortCode, HttpServletRequest request) {
try {
String longUrl = shortUrlService.getLongUrl(shortCode);
if (longUrl == null) {
return ResponseEntity.notFound().build();
}
// 异步记录访问统计
shortUrlService.recordAccess(shortCode, request);
// 重定向
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY)
.location(URI.create(longUrl))
.build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
5. 高性能优化
分库分表
@Component
public class ShardingStrategy {
private static final int SHARD_COUNT = 64;
public String getShardTableName(String shortCode) {
int hash = shortCode.hashCode();
int shardIndex = Math.abs(hash) % SHARD_COUNT;
return "short_url_mapping_" + String.format("%02d", shardIndex);
}
public String getStatsTableName(String shortCode) {
int hash = shortCode.hashCode();
int shardIndex = Math.abs(hash) % SHARD_COUNT;
return "url_access_stats_" + String.format("%02d", shardIndex);
}
}
布隆过滤器
@Component
public class BloomFilterService {
private BloomFilter<String> bloomFilter;
@PostConstruct
public void init() {
// 预期1000万个短码,误判率0.01%
bloomFilter = BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
10_000_000,
0.0001
);
}
public boolean mightExist(String shortCode) {
return bloomFilter.mightContain(shortCode);
}
public void addToFilter(String shortCode) {
bloomFilter.put(shortCode);
}
}
6. 监控告警
@Component
public class ShortUrlMonitor {
@Autowired
private MeterRegistry meterRegistry;
public void recordGeneration(boolean success) {
Counter.builder("shorturl.generation")
.tag("result", success ? "success" : "failure")
.register(meterRegistry)
.increment();
}
public void recordAccess(String shortCode, long responseTime) {
Timer.builder("shorturl.access")
.tag("code", shortCode)
.register(meterRegistry)
.record(responseTime, TimeUnit.MILLISECONDS);
}
@Scheduled(fixedRate = 60000) // 每分钟检查
public void checkSystemHealth() {
// 检查数据库连接
// 检查Redis连接
// 检查错误率
// 发送告警
}
}
扩展功能:
- 自定义短链:支持用户自定义短码
- 批量生成: 批量处理长链接
- 访问控制:密码保护、访问限制
- 数据分析:详细的访问统计和分析
158. 什么是循环依赖(常问)?
展开 中等 后端 Spring
循环依赖定义: 循环依赖是指两个或多个对象之间相互依赖,形成闭环的情况。在Spring中,最常见的是Bean之间的相互引用。
循环依赖类型:
1. 构造器循环依赖
// 无法解决的循环依赖
@Component
public class ServiceA {
private ServiceB serviceB;
public ServiceA(ServiceB serviceB) { // 构造器注入
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private ServiceA serviceA;
public ServiceB(ServiceA serviceA) { // 构造器注入
this.serviceA = serviceA;
}
}
// 结果:BeanCurrentlyInCreationException
2. Setter循环依赖
// Spring可以解决的循环依赖
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB; // Setter注入
public void doSomething() {
serviceB.process();
}
}
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA; // Setter注入
public void process() {
serviceA.doSomething();
}
}
Spring解决循环依赖的机制:
三级缓存
// Spring内部的三级缓存机制
public class DefaultSingletonBeanRegistry {
// 一级缓存:完整的单例对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:早期暴露的对象(已实例化但未完成属性注入)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存:单例工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 正在创建的Bean名称
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}
解决过程示例:
// 模拟Spring创建Bean的过程
public class CircularDependencyResolver {
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
private Map<String, Object> earlySingletonObjects = new HashMap<>();
private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
private Set<String> singletonsCurrentlyInCreation = new HashSet<>();
public Object getBean(String beanName) {
// 1. 从一级缓存获取
Object singletonObject = singletonObjects.get(beanName);
if (singletonObject != null) {
return singletonObject;
}
// 2. 检查是否正在创建中
if (singletonsCurrentlyInCreation.contains(beanName)) {
// 从二级缓存获取
singletonObject = earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 从三级缓存获取
ObjectFactory<?> factory = singletonFactories.get(beanName);
if (factory != null) {
singletonObject = factory.getObject();
// 移到二级缓存
earlySingletonObjects.put(beanName, singletonObject);
singletonFactories.remove(beanName);
}
}
return singletonObject;
}
// 3. 创建新的Bean
return createBean(beanName);
}
private Object createBean(String beanName) {
// 标记为正在创建
singletonsCurrentlyInCreation.add(beanName);
try {
// 1. 实例化
Object bean = instantiate(beanName);
// 2. 提前暴露到三级缓存
singletonFactories.put(beanName, () -> getEarlyBeanReference(beanName, bean));
// 3. 属性注入
populateBean(bean);
// 4. 初始化
initializeBean(bean);
// 5. 添加到一级缓存
singletonObjects.put(beanName, bean);
earlySingletonObjects.remove(beanName);
singletonFactories.remove(beanName);
return bean;
} finally {
singletonsCurrentlyInCreation.remove(beanName);
}
}
}
循环依赖的解决方案:
1. 使用@Lazy注解
@Component
public class ServiceA {
@Autowired
@Lazy // 延迟加载,打破循环
private ServiceB serviceB;
public void doSomething() {
serviceB.process();
}
}
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA;
public void process() {
serviceA.doSomething();
}
}
2. 使用@PostConstruct
@Component
public class ServiceA {
@Autowired
private ApplicationContext applicationContext;
private ServiceB serviceB;
@PostConstruct
public void init() {
this.serviceB = applicationContext.getBean(ServiceB.class);
}
}
3. 实现ApplicationContextAware
@Component
public class ServiceA implements ApplicationContextAware {
private ApplicationContext applicationContext;
private ServiceB serviceB;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public ServiceB getServiceB() {
if (serviceB == null) {
serviceB = applicationContext.getBean(ServiceB.class);
}
return serviceB;
}
}
4. 重新设计架构
// 引入中间层打破循环
@Component
public class ServiceA {
@Autowired
private CommonService commonService;
public void doSomething() {
commonService.processForA();
}
}
@Component
public class ServiceB {
@Autowired
private CommonService commonService;
public void process() {
commonService.processForB();
}
}
@Component
public class CommonService {
// 共同的业务逻辑
}
最佳实践:
- 避免构造器循环依赖
- 优先使用Setter注入
- 合理使用@Lazy注解
- 重新设计类之间的依赖关系
- 使用事件驱动模式解耦
159. JVM 垃圾回收调优的主要目标是什么?
展开 中等 VIP JVM Java
GC调优主要目标:
1. 降低GC停顿时间
- 减少STW(Stop-The-World)时间
- 提高应用响应速度
- 改善用户体验
2. 提高吞吐量
- 减少GC频率
- 降低GC开销占比
- 提高整体性能
3. 减少内存占用
- 优化堆内存使用
- 避免内存泄漏
- 合理配置各代大小
调优策略:
Young GC优化
# 年轻代调优参数
-Xmn2g # 年轻代大小
-XX:SurvivorRatio=8 # Eden:Survivor = 8:1:1
-XX:MaxTenuringThreshold=15 # 晋升老年代的年龄阈值
-XX:PretenureSizeThreshold=1m # 大对象直接进老年代
Old GC优化
# 老年代调优参数
-XX:+UseG1GC # 使用G1收集器
-XX:MaxGCPauseMillis=200 # 最大停顿时间目标
-XX:G1HeapRegionSize=16m # G1 Region大小
-XX:G1NewSizePercent=30 # 年轻代初始占比
-XX:G1MaxNewSizePercent=40 # 年轻代最大占比
监控和分析工具:
// GC日志分析
public class GCAnalyzer {
public void analyzeGCLogs() {
// 使用jstat监控GC
// jstat -gc -t pid 1s
// 使用jmap分析堆内存
// jmap -histo pid
// jmap -dump:format=b,file=heap.hprof pid
// 使用MAT分析内存泄漏
// Eclipse Memory Analyzer Tool
}
// 程序内监控GC
public void monitorGC() {
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
System.out.println("GC Name: " + gcBean.getName());
System.out.println("Collection Count: " + gcBean.getCollectionCount());
System.out.println("Collection Time: " + gcBean.getCollectionTime() + " ms");
}
}
}
调优步骤:
- 监控现状:收集GC日志和性能指标
- 分析问题:识别性能瓶颈
- 制定策略:选择合适的收集器和参数
- 测试验证:在测试环境验证效果
- 上线监控:持续监控和调整
160. Redis 中的 Big Key 问题是什么?如何解决?
展开 中等 VIP 后端 Redis
Big Key定义:
- String类型:value大于10KB
- Hash/List/Set/ZSet:元素个数超过5000
- 内存占用:单个key占用内存超过1MB
Big Key的危害:
1. 网络阻塞
# 大key传输占用带宽
GET large_key # 传输10MB数据
HGETALL large_hash # 返回10万个字段
2. 超时阻塞
# 删除大key导致阻塞
DEL large_list # 删除100万元素的列表
EXPIRE large_set 0 # 过期删除大集合
3. 内存不均
- 某个Redis节点内存占用过高
- 集群节点负载不均衡
检测Big Key:
1. redis-cli扫描
# 扫描big key
redis-cli --bigkeys -h 127.0.0.1 -p 6379
# 自定义阈值扫描
redis-cli --bigkeys --bigkeys-threshold=1000000
2. 监控脚本
import redis
def find_big_keys():
r = redis.Redis(host='localhost', port=6379)
# 扫描所有key
for key in r.scan_iter():
key_type = r.type(key).decode()
size = 0
if key_type == 'string':
size = len(r.get(key) or b'')
elif key_type == 'hash':
size = r.hlen(key)
elif key_type == 'list':
size = r.llen(key)
elif key_type in ['set', 'zset']:
size = r.scard(key) if key_type == 'set' else r.zcard(key)
# 判断是否为big key
if (key_type == 'string' and size > 10*1024) or \
(key_type in ['hash', 'list', 'set', 'zset'] and size > 5000):
print(f"Big Key: {key}, Type: {key_type}, Size: {size}")
解决方案:
1. 拆分大key
// 拆分大Hash
public class HashSplitter {
public void splitLargeHash(String largeKey) {
// 原key: user:1001
// 拆分为: user:1001:0, user:1001:1, user:1001:2...
Map<String, String> largeHash = redisTemplate.opsForHash().entries(largeKey);
int batchSize = 100;
int batchIndex = 0;
Map<String, String> batch = new HashMap<>();
for (Map.Entry<String, String> entry : largeHash.entrySet()) {
batch.put(entry.getKey(), entry.getValue());
if (batch.size() >= batchSize) {
String newKey = largeKey + ":" + batchIndex++;
redisTemplate.opsForHash().putAll(newKey, batch);
batch.clear();
}
}
// 保存剩余数据
if (!batch.isEmpty()) {
String newKey = largeKey + ":" + batchIndex;
redisTemplate.opsForHash().putAll(newKey, batch);
}
// 删除原key
redisTemplate.delete(largeKey);
}
}
2. 分页读取
// 分页获取大集合
public class PaginatedReader {
public List<String> getListByPage(String key, int page, int pageSize) {
int start = page * pageSize;
int end = start + pageSize - 1;
return redisTemplate.opsForList().range(key, start, end);
}
public Set<String> getSetByPage(String key, int count) {
return redisTemplate.opsForSet().distinctRandomMembers(key, count);
}
public Map<String, String> getHashByPattern(String key, String pattern) {
Cursor<Map.Entry<String, String>> cursor = redisTemplate.opsForHash()
.scan(key, ScanOptions.scanOptions().match(pattern).count(100).build());
Map<String, String> result = new HashMap<>();
while (cursor.hasNext()) {
Map.Entry<String, String> entry = cursor.next();
result.put(entry.getKey(), entry.getValue());
}
return result;
}
}
3. 异步删除
// 异步删除大key
public class AsyncDeleter {
public void asyncDeleteLargeKey(String key) {
String keyType = redisTemplate.type(key).code();
switch (keyType) {
case "hash":
asyncDeleteHash(key);
break;
case "list":
asyncDeleteList(key);
break;
case "set":
asyncDeleteSet(key);
break;
case "zset":
asyncDeleteZSet(key);
break;
default:
redisTemplate.delete(key);
}
}
private void asyncDeleteHash(String key) {
// 使用HSCAN + HDEL分批删除
Cursor<Map.Entry<String, String>> cursor = redisTemplate.opsForHash()
.scan(key, ScanOptions.scanOptions().count(100).build());
while (cursor.hasNext()) {
List<String> fields = new ArrayList<>();
for (int i = 0; i < 100 && cursor.hasNext(); i++) {
fields.add(cursor.next().getKey());
}
redisTemplate.opsForHash().delete(key, fields.toArray());
}
redisTemplate.delete(key);
}
private void asyncDeleteList(String key) {
// 使用LTRIM逐步缩短列表
while (redisTemplate.opsForList().size(key) > 0) {
redisTemplate.opsForList().trim(key, 100, -1);
}
redisTemplate.delete(key);
}
}
4. 使用UNLINK命令
# 使用UNLINK代替DEL
UNLINK large_key # 异步删除,不阻塞
预防措施:
1. 设计规范
// key设计规范
public class KeyDesignGuidelines {
// 1. 控制value大小
private static final int MAX_STRING_SIZE = 10 * 1024; // 10KB
private static final int MAX_COLLECTION_SIZE = 5000; // 5000个元素
// 2. 使用合适的数据结构
public void useAppropriateDataStructure() {
// 少量字段用Hash
// 大量字段考虑拆分或使用String + JSON
// 有序数据用ZSet
// 无序数据用Set
// 队列用List
// 栈用List
}
// 3. 设置过期时间
public void setExpiration(String key, Object value) {
redisTemplate.opsForValue().set(key, value, 3600, TimeUnit.SECONDS);
}
}
2. 监控告警
@Component
public class BigKeyMonitor {
@Scheduled(fixedRate = 3600000) // 每小时检查
public void scanBigKeys() {
// 扫描大key并告警
List<String> bigKeys = findBigKeys();
if (!bigKeys.isEmpty()) {
alertService.sendAlert("发现Big Key",
"发现 " + bigKeys.size() + " 个Big Key: " + String.join(", ", bigKeys));
}
}
}
最佳实践:
- 预防为主:设计时避免大key
- 监控预警:定期扫描和监控
- 分拆策略:合理拆分大key
- 异步操作:使用UNLINK异步删除
- 业务优化:从业务层面优化数据结构