Java并发第一性原理:为什么并发如此困难?

引子:一个秒杀系统的困局 假设你正在开发一个电商秒杀系统,库存100件商品,瞬间涌入1000个并发请求。看似简单的需求,却隐藏着并发编程最本质的困难。 场景A:单线程实现(简单但性能差) /** * 单线程秒杀服务 * 优势:简单、可预测、无并发问题 * 劣势:性能差,无法处理高并发 */ public class SingleThreadSeckillService { private int stock = 100; // 库存 public synchronized boolean seckill(String userId) { // 检查库存 if (stock <= 0) { System.out.println("库存不足,秒杀失败"); return false; } // 模拟业务处理(数据库操作、支付调用等) try { Thread.sleep(10); // 10ms的业务处理时间 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 扣减库存 stock--; System.out.println("用户 " + userId + " 秒杀成功,剩余库存:" + stock); return true; } } // 性能测试 public class SingleThreadTest { public static void main(String[] args) { SingleThreadSeckillService service = new SingleThreadSeckillService(); long start = System.currentTimeMillis(); // 1000个用户顺序秒杀 for (int i = 0; i < 1000; i++) { service.seckill("User-" + i); } long end = System.currentTimeMillis(); System.out.println("总耗时:" + (end - start) + "ms"); } } /* 执行结果: 总耗时:10,000ms(10秒) 分析:每个请求10ms,1000个请求串行执行,总计10秒 问题:在真实秒杀场景中,10秒是不可接受的响应时间 */ 性能瓶颈分析: ...

2025-11-03 · maneng

Spring第一性原理:为什么我们需要框架?

引子:一个订单创建背后的两个世界 2025年某天上午10点,你接到一个需求:开发一个电商订单创建功能。 需求很简单: 验证用户权限 查询商品信息 扣减库存 创建订单记录 调用支付服务 发送通知 如果用纯Java实现,需要多少代码?如果用Spring实现,又需要多少代码? 让我们来看看两个完全不同的世界。 一、场景A:纯Java实现(无框架的艰辛) 1.1 完整代码实现 /** * 订单服务 - 纯Java实现 * 问题:硬编码依赖、事务手动管理、横切关注点混杂 */ public class OrderService { // 依赖对象(硬编码new) private UserService userService; private ProductService productService; private InventoryService inventoryService; private PaymentService paymentService; private NotificationService notificationService; // 数据库连接(硬编码配置) private static final String DB_URL = "jdbc:mysql://localhost:3306/mydb"; private static final String DB_USER = "root"; private static final String DB_PASSWORD = "123456"; /** * 构造函数:手动创建所有依赖 * 问题1:依赖层级深,任何一个依赖变化都需要修改这里 */ public OrderService() { // 每个依赖都要手动new,且需要传入它们的依赖 this.userService = new UserService( new UserRepository(getDataSource()) ); this.productService = new ProductService( new ProductRepository(getDataSource()) ); this.inventoryService = new InventoryService( new InventoryRepository(getDataSource()), new RedisClient("localhost", 6379) ); // 支付服务硬编码为支付宝(无法切换) this.paymentService = new AlipayPaymentService( new AlipayConfig("app_id_xxx", "private_key_xxx") ); this.notificationService = new EmailNotificationService( new EmailConfig("smtp.qq.com", 587, "user@qq.com", "password") ); } /** * 创建订单 * 问题2:横切关注点混杂(事务、日志、权限散落在业务代码中) */ public Order createOrder(OrderRequest request) { // ========== 横切关注点1:日志记录 ========== System.out.println("========================================"); System.out.println("开始创建订单: " + request); System.out.println("时间: " + new Date()); long startTime = System.currentTimeMillis(); Connection conn = null; try { // ========== 横切关注点2:权限校验 ========== User user = userService.getUser(request.getUserId()); if (user == null) { throw new BusinessException("用户不存在"); } if (!user.hasPermission("CREATE_ORDER")) { System.err.println("权限拒绝: 用户 " + user.getId() + " 无CREATE_ORDER权限"); throw new PermissionException("无权限创建订单"); } // ========== 横切关注点3:参数校验 ========== if (request.getProductId() == null) { throw new ValidationException("商品ID不能为空"); } if (request.getQuantity() == null || request.getQuantity() <= 0) { throw new ValidationException("购买数量必须大于0"); } // ========== 横切关注点4:手动事务管理 ========== conn = getConnection(); conn.setAutoCommit(false); // 关闭自动提交 try { // ========== 核心业务逻辑开始(只占20%代码) ========== // 1. 查询商品 Product product = productService.getProduct(request.getProductId()); if (product == null) { throw new BusinessException("商品不存在"); } if (product.getStatus() != 1) { throw new BusinessException("商品已下架"); } // 2. 扣减库存 boolean deductSuccess = inventoryService.deduct( product.getId(), request.getQuantity(), conn // 传递同一个连接,保证事务一致性 ); if (!deductSuccess) { throw new BusinessException("库存不足"); } // 3. 创建订单 Order order = new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(user.getId()); order.setProductId(product.getId()); order.setProductName(product.getName()); order.setQuantity(request.getQuantity()); order.setPrice(product.getPrice()); order.setAmount(product.getPrice().multiply( new BigDecimal(request.getQuantity()) )); order.setStatus(0); // 待支付 order.setCreateTime(new Date()); // 4. 保存订单到数据库 saveOrder(order, conn); // 5. 调用支付服务 boolean paymentSuccess = paymentService.pay( order.getOrderNo(), order.getAmount() ); if (!paymentSuccess) { throw new BusinessException("支付失败"); } // 6. 更新订单状态 order.setStatus(1); // 已支付 updateOrderStatus(order, conn); // ========== 核心业务逻辑结束 ========== // 提交事务 conn.commit(); // ========== 横切关注点5:发送通知(异步) ========== try { notificationService.sendEmail( user.getEmail(), "订单创建成功", "您的订单 " + order.getOrderNo() + " 已创建成功" ); } catch (Exception e) { // 通知失败不影响主流程 System.err.println("发送邮件失败: " + e.getMessage()); } // ========== 横切关注点6:日志记录 ========== long endTime = System.currentTimeMillis(); System.out.println("订单创建成功: " + order.getOrderNo()); System.out.println("耗时: " + (endTime - startTime) + "ms"); System.out.println("========================================"); return order; } catch (Exception e) { // 回滚事务 if (conn != null) { try { conn.rollback(); System.err.println("事务回滚成功"); } catch (SQLException rollbackEx) { System.err.println("事务回滚失败: " + rollbackEx.getMessage()); } } // ========== 横切关注点7:异常日志 ========== System.err.println("订单创建失败: " + e.getMessage()); e.printStackTrace(); throw new BusinessException("订单创建失败: " + e.getMessage(), e); } } catch (Exception e) { throw new RuntimeException("系统异常", e); } finally { // 释放连接 if (conn != null) { try { conn.close(); } catch (SQLException e) { System.err.println("关闭连接失败: " + e.getMessage()); } } } } /** * 获取数据库连接(硬编码配置) */ private Connection getConnection() throws SQLException { return DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); } /** * 获取数据源 */ private DataSource getDataSource() { HikariDataSource ds = new HikariDataSource(); ds.setJdbcUrl(DB_URL); ds.setUsername(DB_USER); ds.setPassword(DB_PASSWORD); ds.setMaximumPoolSize(10); return ds; } /** * 生成订单号 */ private String generateOrderNo() { return "ORD" + System.currentTimeMillis() + (int)(Math.random() * 1000); } /** * 保存订单 */ private void saveOrder(Order order, Connection conn) throws SQLException { String sql = "INSERT INTO orders (order_no, user_id, product_id, " + "product_name, quantity, price, amount, status, create_time) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, order.getOrderNo()); pstmt.setLong(2, order.getUserId()); pstmt.setLong(3, order.getProductId()); pstmt.setString(4, order.getProductName()); pstmt.setInt(5, order.getQuantity()); pstmt.setBigDecimal(6, order.getPrice()); pstmt.setBigDecimal(7, order.getAmount()); pstmt.setInt(8, order.getStatus()); pstmt.setTimestamp(9, new Timestamp(order.getCreateTime().getTime())); pstmt.executeUpdate(); } } /** * 更新订单状态 */ private void updateOrderStatus(Order order, Connection conn) throws SQLException { String sql = "UPDATE orders SET status = ? WHERE order_no = ?"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, order.getStatus()); pstmt.setString(2, order.getOrderNo()); pstmt.executeUpdate(); } } } 1.2 代码统计与问题分析 代码统计: ...

2025-11-03 · maneng

为什么需要微服务?单体架构的困境与演进

引子:一个电商系统的架构抉择 2020年3月,某创业公司CTO王明面临一个艰难的技术决策: 现状: 团队规模:15个研发 业务现状:日订单量从1万增长到10万 核心痛点:每次发布需要2小时,任何一个小功能的上线都要全量发布,导致上线频率从每周3次降到每周1次 技术选型的两个方向: 继续单体架构:优化现有系统,加机器,做好模块化 切换微服务架构:拆分服务,引入Spring Cloud,重构系统 王明的困惑: “我们真的需要微服务吗?” “微服务能解决什么问题?” “微服务会带来什么代价?” 让我们通过两个完全不同的世界,来理解微服务架构的本质。 一、场景A:单体架构实现(初创期的最优解) 1.1 单体架构的完整代码实现 /** * 电商系统 - 单体架构实现 * 特点:所有功能在一个进程内,共享一个数据库 */ @SpringBootApplication public class ECommerceMonolithApplication { public static void main(String[] args) { SpringApplication.run(ECommerceMonolithApplication.class, args); } } /** * 订单服务(单体架构版本) * 依赖:用户服务、商品服务、库存服务、支付服务 * 特点:所有依赖都在同一个进程内,方法调用 */ @Service @Slf4j @Transactional(rollbackFor = Exception.class) public class OrderService { @Autowired private UserService userService; // 本地方法调用 @Autowired private ProductService productService; // 本地方法调用 @Autowired private InventoryService inventoryService; // 本地方法调用 @Autowired private PaymentService paymentService; // 本地方法调用 @Autowired private OrderRepository orderRepository; // 同一个数据库 /** * 创建订单 * 优势: * 1. 本地方法调用,性能极高(纳秒级) * 2. 本地事务,ACID保证强一致性 * 3. 调试方便,堆栈清晰 * * 问题: * 1. 单点故障:任何一个模块崩溃,整个系统不可用 * 2. 部署耦合:改一行代码,整个系统重启 * 3. 技术栈绑定:整个系统必须用同一种语言、框架 * 4. 资源竞争:所有模块共享JVM内存、CPU */ public Order createOrder(CreateOrderRequest request) { // ========== 1. 用户验证(本地方法调用,1ms) ========== User user = userService.getUserById(request.getUserId()); if (user == null) { throw new BusinessException("用户不存在"); } if (user.getStatus() != UserStatus.ACTIVE) { throw new BusinessException("用户状态异常"); } // ========== 2. 商品查询(本地方法调用,2ms) ========== Product product = productService.getProductById(request.getProductId()); if (product == null) { throw new BusinessException("商品不存在"); } if (product.getStatus() != ProductStatus.ON_SALE) { throw new BusinessException("商品已下架"); } // ========== 3. 库存扣减(本地方法调用,3ms) ========== boolean deductSuccess = inventoryService.deduct( product.getId(), request.getQuantity() ); if (!deductSuccess) { throw new BusinessException("库存不足"); } // ========== 4. 创建订单(本地数据库写入,5ms) ========== Order order = Order.builder() .orderNo(generateOrderNo()) .userId(user.getId()) .productId(product.getId()) .productName(product.getName()) .quantity(request.getQuantity()) .price(product.getPrice()) .amount(product.getPrice().multiply( new BigDecimal(request.getQuantity()) )) .status(OrderStatus.UNPAID) .createTime(LocalDateTime.now()) .build(); order = orderRepository.save(order); // ========== 5. 调用支付(本地方法调用,10ms) ========== boolean paymentSuccess = paymentService.pay( order.getOrderNo(), order.getAmount() ); if (!paymentSuccess) { throw new BusinessException("支付失败"); } // ========== 6. 更新订单状态(本地数据库更新,2ms) ========== order.setStatus(OrderStatus.PAID); order.setPayTime(LocalDateTime.now()); order = orderRepository.save(order); log.info("订单创建成功: orderNo={}, amount={}", order.getOrderNo(), order.getAmount()); // 总耗时约23ms(都是本地调用) return order; } /** * 生成订单号 */ private String generateOrderNo() { return "ORD" + System.currentTimeMillis() + ThreadLocalRandom.current().nextInt(1000); } } /** * 用户服务 */ @Service public class UserService { @Autowired private UserRepository userRepository; public User getUserById(Long userId) { return userRepository.findById(userId).orElse(null); } } /** * 商品服务 */ @Service public class ProductService { @Autowired private ProductRepository productRepository; public Product getProductById(Long productId) { return productRepository.findById(productId).orElse(null); } } /** * 库存服务 */ @Service public class InventoryService { @Autowired private InventoryRepository inventoryRepository; /** * 扣减库存 * 优势:本地事务,强一致性保证 */ public boolean deduct(Long productId, Integer quantity) { Inventory inventory = inventoryRepository.findByProductId(productId); if (inventory == null || inventory.getStock() < quantity) { return false; } inventory.setStock(inventory.getStock() - quantity); inventoryRepository.save(inventory); return true; } } /** * 支付服务 */ @Service public class PaymentService { @Autowired private PaymentRepository paymentRepository; /** * 支付 * 优势:本地事务,要么都成功,要么都失败 */ public boolean pay(String orderNo, BigDecimal amount) { // 调用第三方支付(这里模拟) boolean paySuccess = callThirdPartyPayment(orderNo, amount); if (paySuccess) { // 记录支付流水 Payment payment = Payment.builder() .orderNo(orderNo) .amount(amount) .status(PaymentStatus.SUCCESS) .payTime(LocalDateTime.now()) .build(); paymentRepository.save(payment); } return paySuccess; } private boolean callThirdPartyPayment(String orderNo, BigDecimal amount) { // 模拟第三方支付调用 return true; } } /** * 数据库实体:订单 */ @Entity @Table(name = "t_order") @Data @Builder public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String orderNo; private Long userId; private Long productId; private String productName; private Integer quantity; private BigDecimal price; private BigDecimal amount; @Enumerated(EnumType.STRING) private OrderStatus status; private LocalDateTime createTime; private LocalDateTime payTime; } /** * 数据库实体:用户、商品、库存、支付记录 * (省略具体实现) */ 1.2 单体架构的项目结构 ecommerce-monolith/ ├── src/main/java/com/example/ecommerce/ │ ├── ECommerceMonolithApplication.java # 启动类 │ ├── controller/ # 控制器层 │ │ ├── OrderController.java │ │ ├── UserController.java │ │ ├── ProductController.java │ │ └── InventoryController.java │ ├── service/ # 服务层 │ │ ├── OrderService.java # 订单服务 │ │ ├── UserService.java # 用户服务 │ │ ├── ProductService.java # 商品服务 │ │ ├── InventoryService.java # 库存服务 │ │ └── PaymentService.java # 支付服务 │ ├── repository/ # 数据访问层 │ │ ├── OrderRepository.java │ │ ├── UserRepository.java │ │ ├── ProductRepository.java │ │ ├── InventoryRepository.java │ │ └── PaymentRepository.java │ ├── entity/ # 实体层 │ │ ├── Order.java │ │ ├── User.java │ │ ├── Product.java │ │ ├── Inventory.java │ │ └── Payment.java │ └── config/ # 配置类 │ ├── DataSourceConfig.java │ └── TransactionConfig.java ├── src/main/resources/ │ ├── application.yml # 配置文件 │ └── db/migration/ # 数据库脚本 │ └── V1__init_schema.sql └── pom.xml # Maven配置 部署方式: 1. 打包:mvn clean package → ecommerce-monolith.jar(200MB) 2. 部署:java -jar ecommerce-monolith.jar 3. 运行:单个JVM进程,占用2GB内存 4. 数据库:MySQL单实例,5张表在同一个库 1.3 单体架构的核心特点 维度 特点 优势 劣势 部署 单个war/jar包 部署简单,运维成本低 改一行代码,全量重启 调用方式 本地方法调用 性能极高(纳秒级) 单点故障,一损俱损 事务 本地事务(ACID) 强一致性,简单可靠 无法独立扩展单个模块 技术栈 统一技术栈 团队技能统一,学习成本低 技术升级困难,框架绑定 开发效率 代码在同一个工程 IDE调试方便,定位问题快 代码库膨胀,启动变慢 团队协作 共享代码库 代码复用容易 代码冲突频繁,发布排队 二、场景B:微服务架构实现(规模化后的必然选择) 2.1 微服务架构的完整代码实现 /** * 订单服务(微服务架构版本) * 特点:独立进程,独立数据库,通过RPC调用其他服务 */ @SpringBootApplication @EnableDiscoveryClient // 服务注册 @EnableFeignClients // 远程调用 public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } } /** * 订单服务实现 * 依赖:远程调用用户服务、商品服务、库存服务、支付服务 */ @Service @Slf4j public class OrderService { @Autowired private UserServiceClient userServiceClient; // 远程调用(HTTP/gRPC) @Autowired private ProductServiceClient productServiceClient; // 远程调用 @Autowired private InventoryServiceClient inventoryServiceClient; // 远程调用 @Autowired private PaymentServiceClient paymentServiceClient; // 远程调用 @Autowired private OrderRepository orderRepository; // 独立数据库 @Autowired private RocketMQTemplate rocketMQTemplate; // 消息队列 /** * 创建订单(微服务版本) * * 优势: * 1. 服务独立部署:订单服务可以独立发布,不影响其他服务 * 2. 技术栈自由:订单服务用Java,用户服务可以用Go,商品服务可以用Python * 3. 独立扩展:订单服务压力大,可以只扩容订单服务 * 4. 故障隔离:支付服务挂了,不影响订单创建(降级返回"支付中") * * 挑战: * 1. 网络调用:性能下降(本地纳秒级 → 远程毫秒级) * 2. 分布式事务:无法保证强一致性(ACID → BASE) * 3. 调用链路长:5个服务,任何一个超时都影响整体 * 4. 运维复杂:5个服务 × 3个环境 = 15套部署 */ public Order createOrder(CreateOrderRequest request) { // ========== 1. 远程调用用户服务(网络调用,10ms + 超时风险) ========== try { UserDTO user = userServiceClient.getUserById(request.getUserId()); if (user == null || user.getStatus() != UserStatus.ACTIVE) { throw new BusinessException("用户不存在或状态异常"); } } catch (FeignException e) { log.error("调用用户服务失败: {}", e.getMessage()); throw new BusinessException("用户服务不可用"); } // ========== 2. 远程调用商品服务(网络调用,15ms + 超时风险) ========== ProductDTO product; try { product = productServiceClient.getProductById(request.getProductId()); if (product == null || product.getStatus() != ProductStatus.ON_SALE) { throw new BusinessException("商品不存在或已下架"); } } catch (FeignException e) { log.error("调用商品服务失败: {}", e.getMessage()); throw new BusinessException("商品服务不可用"); } // ========== 3. 远程调用库存服务(网络调用,20ms + 超时风险) ========== boolean deductSuccess; try { deductSuccess = inventoryServiceClient.deduct( product.getId(), request.getQuantity() ); if (!deductSuccess) { throw new BusinessException("库存不足"); } } catch (FeignException e) { log.error("调用库存服务失败: {}", e.getMessage()); throw new BusinessException("库存服务不可用"); } // ========== 4. 创建订单(本地数据库写入,5ms) ========== Order order = Order.builder() .orderNo(generateOrderNo()) .userId(request.getUserId()) .productId(product.getId()) .productName(product.getName()) .quantity(request.getQuantity()) .price(product.getPrice()) .amount(product.getPrice().multiply( new BigDecimal(request.getQuantity()) )) .status(OrderStatus.UNPAID) .createTime(LocalDateTime.now()) .build(); order = orderRepository.save(order); // ========== 5. 异步调用支付服务(通过消息队列,解耦) ========== // 不直接调用支付服务,而是发送消息到MQ,支付服务异步消费 PaymentMessage paymentMessage = PaymentMessage.builder() .orderNo(order.getOrderNo()) .amount(order.getAmount()) .build(); rocketMQTemplate.syncSend("PAYMENT_TOPIC", paymentMessage); log.info("订单创建成功: orderNo={}, amount={}", order.getOrderNo(), order.getAmount()); // 总耗时约50ms(网络调用 + 超时重试) // 相比单体架构的23ms,慢了2倍,但换来了独立部署、独立扩展、故障隔离 return order; } /** * 生成订单号(分布式唯一ID) * 单体架构:可以用数据库自增ID * 微服务架构:需要用雪花算法、UUID等分布式ID生成策略 */ private String generateOrderNo() { // 雪花算法生成分布式唯一ID return "ORD" + SnowflakeIdWorker.generateId(); } } /** * 用户服务客户端(Feign声明式HTTP客户端) */ @FeignClient( name = "user-service", // 服务名(从注册中心获取) fallback = UserServiceFallback.class // 降级策略 ) public interface UserServiceClient { @GetMapping("/api/users/{userId}") UserDTO getUserById(@PathVariable("userId") Long userId); } /** * 用户服务降级策略(熔断后的备选方案) */ @Component public class UserServiceFallback implements UserServiceClient { @Override public UserDTO getUserById(Long userId) { log.warn("用户服务调用失败,触发降级策略"); // 返回一个默认用户,或者抛出异常 throw new BusinessException("用户服务不可用,请稍后重试"); } } /** * 商品服务客户端 */ @FeignClient( name = "product-service", fallback = ProductServiceFallback.class ) public interface ProductServiceClient { @GetMapping("/api/products/{productId}") ProductDTO getProductById(@PathVariable("productId") Long productId); } /** * 库存服务客户端 */ @FeignClient( name = "inventory-service", fallback = InventoryServiceFallback.class ) public interface InventoryServiceClient { @PostMapping("/api/inventory/deduct") boolean deduct(@RequestParam("productId") Long productId, @RequestParam("quantity") Integer quantity); } /** * 支付服务(独立的微服务,监听MQ消息) */ @Service @Slf4j public class PaymentService { @Autowired private PaymentRepository paymentRepository; @Autowired private OrderServiceClient orderServiceClient; /** * 监听支付消息(异步处理) * 优势:解耦订单服务和支付服务,支付失败不影响订单创建 */ @RocketMQMessageListener( topic = "PAYMENT_TOPIC", consumerGroup = "payment-consumer-group" ) public class PaymentMessageListener implements RocketMQListener<PaymentMessage> { @Override public void onMessage(PaymentMessage message) { log.info("接收到支付消息: {}", message); try { // 调用第三方支付 boolean paySuccess = callThirdPartyPayment( message.getOrderNo(), message.getAmount() ); if (paySuccess) { // 记录支付流水 Payment payment = Payment.builder() .orderNo(message.getOrderNo()) .amount(message.getAmount()) .status(PaymentStatus.SUCCESS) .payTime(LocalDateTime.now()) .build(); paymentRepository.save(payment); // 回调订单服务,更新订单状态 orderServiceClient.updateOrderStatus( message.getOrderNo(), OrderStatus.PAID ); log.info("支付成功: orderNo={}", message.getOrderNo()); } else { log.error("支付失败: orderNo={}", message.getOrderNo()); // 支付失败,可以重试或者发送通知 } } catch (Exception e) { log.error("支付处理异常: orderNo={}, error={}", message.getOrderNo(), e.getMessage()); // 异常时,消息会重新入队,重试机制 throw new RuntimeException("支付处理失败", e); } } } private boolean callThirdPartyPayment(String orderNo, BigDecimal amount) { // 模拟第三方支付调用 return true; } } 2.2 微服务架构的项目结构 microservices-ecommerce/ ├── order-service/ # 订单服务 │ ├── src/main/java/com/example/order/ │ │ ├── OrderServiceApplication.java # 启动类 │ │ ├── controller/OrderController.java │ │ ├── service/OrderService.java │ │ ├── repository/OrderRepository.java │ │ ├── entity/Order.java │ │ ├── client/ # Feign客户端 │ │ │ ├── UserServiceClient.java │ │ │ ├── ProductServiceClient.java │ │ │ ├── InventoryServiceClient.java │ │ │ └── PaymentServiceClient.java │ │ └── config/ # 配置类 │ ├── src/main/resources/ │ │ └── application.yml # 订单服务配置 │ └── pom.xml # 独立的Maven配置 │ ├── user-service/ # 用户服务 │ ├── src/main/java/com/example/user/ │ │ ├── UserServiceApplication.java │ │ ├── controller/UserController.java │ │ ├── service/UserService.java │ │ ├── repository/UserRepository.java │ │ └── entity/User.java │ ├── src/main/resources/ │ │ └── application.yml │ └── pom.xml │ ├── product-service/ # 商品服务 │ ├── src/main/java/com/example/product/ │ │ ├── ProductServiceApplication.java │ │ ├── controller/ProductController.java │ │ ├── service/ProductService.java │ │ ├── repository/ProductRepository.java │ │ └── entity/Product.java │ ├── src/main/resources/ │ │ └── application.yml │ └── pom.xml │ ├── inventory-service/ # 库存服务 │ ├── src/main/java/com/example/inventory/ │ │ ├── InventoryServiceApplication.java │ │ ├── controller/InventoryController.java │ │ ├── service/InventoryService.java │ │ ├── repository/InventoryRepository.java │ │ └── entity/Inventory.java │ ├── src/main/resources/ │ │ └── application.yml │ └── pom.xml │ ├── payment-service/ # 支付服务 │ ├── src/main/java/com/example/payment/ │ │ ├── PaymentServiceApplication.java │ │ ├── service/PaymentService.java │ │ ├── repository/PaymentRepository.java │ │ ├── entity/Payment.java │ │ └── listener/PaymentMessageListener.java │ ├── src/main/resources/ │ │ └── application.yml │ └── pom.xml │ ├── common/ # 公共模块 │ ├── src/main/java/com/example/common/ │ │ ├── dto/ # 数据传输对象 │ │ ├── exception/ # 异常定义 │ │ └── util/ # 工具类 │ └── pom.xml │ ├── gateway/ # API网关(Spring Cloud Gateway) │ ├── src/main/java/com/example/gateway/ │ │ ├── GatewayApplication.java │ │ └── config/GatewayConfig.java │ └── pom.xml │ └── registry/ # 服务注册中心(Nacos) └── nacos-server/ 部署方式: 1. 打包:每个服务独立打包 → order-service.jar, user-service.jar... 2. 部署:每个服务独立部署(可以部署在不同的机器上) 3. 运行:5个JVM进程,每个占用1GB内存(总共5GB) 4. 数据库:5个独立的MySQL数据库(或5个独立的Schema) 2.3 微服务架构的核心特点 维度 特点 优势 劣势 部署 独立部署单元 改一行代码,只重启一个服务 运维复杂度高,需要容器编排 调用方式 远程调用(HTTP/gRPC) 故障隔离,服务独立演进 性能下降(纳秒→毫秒),网络不可靠 事务 分布式事务(BASE) 独立扩展,高可用 最终一致性,业务逻辑复杂 技术栈 多语言异构 技术自由,选择最合适的技术 技能要求高,学习成本大 开发效率 代码分散在多个工程 独立迭代,避免冲突 调试困难,链路追踪复杂 团队协作 按服务划分团队 团队自治,减少沟通成本 接口约定复杂,版本管理困难 三、数据对比:单体 vs 微服务 3.1 性能对比 指标 单体架构 微服务架构 差异分析 接口响应时间 23ms 50ms 微服务慢2倍(网络调用开销) QPS 5000 3000 微服务下降40%(网络+序列化) CPU使用率 60% 70% 微服务高10%(序列化+网络) 内存占用 2GB(单进程) 5GB(5进程) 微服务多2.5倍 启动时间 120s 30s/服务 微服务单个服务更快 关键发现: ...

2025-11-03 · maneng

服务拆分第一性原理:从领域驱动到康威定律

引子:一次失败的微服务拆分 2019年某月,某金融科技公司CTO李明决定将单体应用拆分为微服务。 拆分方案(按技术层拆分): 前端服务:负责所有页面渲染 业务服务:负责所有业务逻辑 数据服务:负责所有数据访问 3个月后的结果: 部署次数:从每周1次降到每月1次(更慢了!) 数据库连接:3个服务共享同一个数据库(还是耦合) 代码冲突:业务服务有50个类,15个人修改,冲突率40%(比单体更高) 故障影响:数据服务挂了,整个系统不可用(单点依旧存在) 李明的困惑: “我们明明拆分了,为什么比单体还糟糕?” “什么才是正确的拆分方式?” “服务边界应该如何划分?” 这个案例揭示了一个核心问题:拆分微服务的本质不是"拆分技术层",而是"拆分业务领域"。 让我们从第一性原理出发,系统化解答这个问题。 一、服务拆分的本质问题 1.1 什么是服务边界? 服务边界的定义: 物理边界:独立的进程、独立的数据库、独立的部署单元 逻辑边界:独立的业务职责、独立的团队所有权、独立的变更频率 好的服务边界的三个特征: 高内聚:服务内部的功能紧密相关(订单创建、订单查询、订单取消都在订单服务内) 低耦合:服务之间的依赖最小化(订单服务不依赖评论服务) 独立演进:服务可以独立开发、测试、部署(订单服务升级,不影响商品服务) 1.2 拆分的三个核心目标 目标1:独立部署 单体架构的问题: 改订单服务的一个Bug,整个系统重启 部署时间:120分钟 微服务的解决方案: 改订单服务的Bug,只重启订单服务 部署时间:15分钟 目标2:独立扩展 单体架构的问题: 订单服务压力大,整体扩容,浪费80%资源 微服务的解决方案: 订单服务压力大,只扩容订单服务 目标3:独立演进 单体架构的问题: 整个系统必须用同一技术栈(Java + Spring) 微服务的解决方案: 订单服务用Java,推荐服务用Python,搜索服务用Go 1.3 拆分的代价 不要忘记:拆分微服务是有代价的! 代价维度 单体架构 微服务架构 增加幅度 网络调用延迟 0ms(本地调用) 10-50ms +∞ 事务复杂度 ACID(本地事务) BASE(分布式事务) +10倍 运维复杂度 1个服务 100+服务 +100倍 问题定位难度 单进程堆栈 链路追踪 +5倍 核心原则:只有当拆分的收益 > 拆分的成本时,才应该拆分! ...

2025-11-03 · maneng

线程安全与同步机制:从synchronized到Lock的演进

引子:一个购物车的线程安全之路 在上一篇文章中,我们理解了并发编程的三大核心问题:可见性、原子性、有序性。现在我们要解决这个问题:如何让多线程安全地访问共享数据? 场景:电商购物车的并发问题 /** * 购物车服务(线程不安全版本) * 问题:多个线程同时添加商品,可能导致数据丢失 */ public class ShoppingCart { private Map<String, Integer> items = new HashMap<>(); // 商品ID → 数量 // 添加商品到购物车 public void addItem(String productId, int quantity) { Integer currentQty = items.get(productId); if (currentQty == null) { items.put(productId, quantity); } else { items.put(productId, currentQty + quantity); } } // 获取购物车总商品数 public int getTotalItems() { int total = 0; for (Integer qty : items.values()) { total += qty; } return total; } } // 并发测试 public class CartTest { public static void main(String[] args) throws InterruptedException { ShoppingCart cart = new ShoppingCart(); // 10个线程并发添加商品 Thread[] threads = new Thread[10]; for (int i = 0; i < 10; i++) { threads[i] = new Thread(() -> { for (int j = 0; j < 100; j++) { cart.addItem("product-123", 1); } }); } for (Thread t : threads) t.start(); for (Thread t : threads) t.join(); System.out.println("期望数量:1000"); System.out.println("实际数量:" + cart.getTotalItems()); } } /* 执行结果(多次运行不一致): 第1次:实际数量:856 ❌ 丢失144次更新 第2次:实际数量:923 ❌ 丢失77次更新 第3次:实际数量:891 ❌ 丢失109次更新 问题分析: 1. HashMap本身不是线程安全的 2. addItem方法不是原子操作(读取、计算、写入三步) 3. 多线程并发执行导致数据竞争 解决方案演进: Level 1:使用synchronized → 简单但粗粒度 Level 2:使用ReentrantLock → 灵活但需要手动管理 Level 3:使用ConcurrentHashMap → 高性能(下一篇讲解) */ 本文将深入探讨如何通过synchronized和Lock来保证线程安全。 ...

2025-11-03 · maneng

并发工具类与原子操作:无锁编程的艺术

引子:一个计数器的性能优化之路 在前两篇文章中,我们学习了synchronized和Lock来保证线程安全。但是,锁总是有性能开销的。有没有不用锁就能保证线程安全的方法?答案是:CAS(Compare-And-Swap)+ 原子类。 场景:网站访问计数器 /** * 方案1:使用synchronized(加锁) */ public class SynchronizedCounter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } } /** * 方案2:使用AtomicInteger(无锁) */ public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); // 无锁,基于CAS } public int getCount() { return count.get(); } } // 性能对比测试 public class CounterBenchmark { private static final int THREAD_COUNT = 100; private static final int ITERATIONS = 10000; public static void main(String[] args) throws InterruptedException { // 测试synchronized版本 SynchronizedCounter syncCounter = new SynchronizedCounter(); long syncTime = testCounter(() -> syncCounter.increment()); System.out.println("Synchronized耗时:" + syncTime + "ms"); // 测试AtomicInteger版本 AtomicCounter atomicCounter = new AtomicCounter(); long atomicTime = testCounter(() -> atomicCounter.increment()); System.out.println("AtomicInteger耗时:" + atomicTime + "ms"); System.out.println("性能提升:" + (syncTime * 100.0 / atomicTime - 100) + "%"); } private static long testCounter(Runnable task) throws InterruptedException { Thread[] threads = new Thread[THREAD_COUNT]; long start = System.currentTimeMillis(); for (int i = 0; i < THREAD_COUNT; i++) { threads[i] = new Thread(() -> { for (int j = 0; j < ITERATIONS; j++) { task.run(); } }); } for (Thread t : threads) t.start(); for (Thread t : threads) t.join(); return System.currentTimeMillis() - start; } } /* 执行结果(JDK 8, 4核CPU): Synchronized耗时:856ms AtomicInteger耗时:342ms 性能提升:150% 为什么AtomicInteger更快? 1. 无锁,避免线程阻塞和上下文切换 2. 基于CPU的CAS指令,硬件级支持 3. 自旋重试,适合竞争不激烈的场景 但是,AtomicInteger不是万能的: ├─ 竞争激烈时,自旋会浪费CPU ├─ 不适合复杂的原子操作 └─ 需要理解CAS的原理和限制 */ 本文将深入探讨CAS的原理、Atomic类的实现、以及常用的并发工具类。 ...

2025-11-03 · maneng

服务间通信:同步调用、异步消息与事件驱动

引子:一次服务雪崩引发的思考 2020年双11凌晨2点,某电商平台订单服务突然不可用,导致用户无法下单。 故障链路: 用户下单 → 订单服务 → 库存服务(超时20秒) → 订单服务线程池耗尽 → 整个系统不可用 问题根源: 订单服务同步调用库存服务(HTTP请求) 库存服务压力大,响应慢(20秒超时) 订单服务线程池耗尽(200个线程全部阻塞) 新的订单请求无法处理,系统崩溃 架构师王明的反思: “同步调用的问题是什么?” “为什么不用异步消息?” “什么场景用同步,什么场景用异步?” 这个案例揭示了微服务通信的核心问题:如何选择合适的通信模式,平衡性能、可靠性、复杂度? 一、通信模式的本质:耦合度与可靠性的权衡 1.1 通信模式的两个维度 维度1:同步 vs 异步 维度 同步通信 异步通信 调用方式 请求→等待→响应 请求→立即返回→回调 阻塞性 调用方阻塞等待 调用方不阻塞 耦合度 强耦合(时间耦合) 弱耦合(时间解耦) 响应时间 快(毫秒级) 慢(秒级或分钟级) 可用性 低(被调用方挂了,调用方也挂) 高(被调用方挂了,消息不丢失) 复杂度 低(简单直接) 高(需要消息队列) 维度2:点对点 vs 发布订阅 维度 点对点(P2P) 发布订阅(Pub/Sub) 调用关系 一对一 一对多 耦合度 强耦合(空间耦合) 弱耦合(空间解耦) 扩展性 差(新增消费者要修改代码) 好(新增消费者不影响发布者) 典型场景 RPC调用 领域事件 1.2 耦合度的四个维度 1. 时间耦合(Temporal Coupling) ...

2025-11-03 · maneng

Spring Boot第一性原理:约定优于配置的威力

系列导航:本文是《Spring框架第一性原理》系列的第4篇 第1篇:为什么我们需要Spring框架? 第2篇:IoC容器:从手动new到自动装配的演进 第3篇:AOP:从代码重复到面向切面编程 第4篇:Spring Boot:约定优于配置的威力(本文) 引子:配置地狱的噩梦 场景重现:创建一个Spring Web应用(传统方式) 想象你是2013年的Java开发者,接到任务:创建一个简单的RESTful API服务。使用传统Spring框架,你需要经历以下"噩梦": 第一步:手动配置依赖(pom.xml,约50行) <!-- 1. Spring核心依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.9.RELEASE</version> </dependency> <!-- 2. Jackson JSON依赖 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.9</version> </dependency> <!-- 3. Servlet API --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- 4. 日志依赖 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!-- 问题:--> <!-- ❌ 依赖繁多:手动引入10+个依赖 --> <!-- ❌ 版本冲突:spring-core必须和spring-webmvc版本一致 --> <!-- ❌ 依赖遗漏:忘记jackson导致JSON序列化失败 --> 第二步:配置web.xml(约30行) <!-- src/main/webapp/WEB-INF/web.xml --> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <!-- 1. 配置Spring上下文监听器 --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!-- 2. 配置Spring上下文配置文件位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <!-- 3. 配置DispatcherServlet --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- 4. 配置Servlet映射 --> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 5. 配置字符编码过滤器 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> <!-- 问题:--> <!-- ❌ XML冗长:30行配置只为启动Web应用 --> <!-- ❌ 路径硬编码:配置文件路径写死 --> <!-- ❌ 易出错:一个标签写错就启动失败 --> 第三步:配置applicationContext.xml(约40行) <!-- src/main/webapp/WEB-INF/applicationContext.xml --> <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 1. 启用注解扫描 --> <context:component-scan base-package="com.example"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 2. 加载properties文件 --> <context:property-placeholder location="classpath:application.properties"/> <!-- 3. 配置数据源 --> <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="maximumPoolSize" value="20"/> </bean> <!-- 4. 配置JdbcTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 5. 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 6. 启用事务注解 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans> <!-- 问题:--> <!-- ❌ 配置繁琐:每个Bean都要显式配置 --> <!-- ❌ 命名空间复杂:xsi:schemaLocation写错就无法启动 --> <!-- ❌ 重复配置:每个项目都要写相同的配置 --> 第四步:配置dispatcher-servlet.xml(约30行) <!-- src/main/webapp/WEB-INF/dispatcher-servlet.xml --> <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 1. 扫描Controller --> <context:component-scan base-package="com.example.controller"/> <!-- 2. 启用Spring MVC注解 --> <mvc:annotation-driven> <mvc:message-converters> <!-- 配置JSON转换器 --> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="com.fasterxml.jackson.databind.ObjectMapper"> <property name="dateFormat"> <bean class="java.text.SimpleDateFormat"> <constructor-arg value="yyyy-MM-dd HH:mm:ss"/> </bean> </property> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <!-- 3. 配置静态资源处理 --> <mvc:default-servlet-handler/> <!-- 4. 配置拦截器 --> <mvc:interceptors> <bean class="com.example.interceptor.LogInterceptor"/> </mvc:interceptors> </beans> 第五步:配置应用(application.properties) # 数据库配置 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=123456 # 日志配置 logging.level.root=INFO logging.level.com.example=DEBUG 第六步:编写业务代码 // Controller @RestController @RequestMapping("/api/users") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public User getUser(@PathVariable Long id) { return userService.getUser(id); } } // Service @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; public User getUser(Long id) { return jdbcTemplate.queryForObject( "SELECT * FROM users WHERE id = ?", new Object[]{id}, new BeanPropertyRowMapper<>(User.class) ); } } 第七步:部署到Tomcat # 1. 打包成WAR文件 mvn clean package # 2. 将WAR文件复制到Tomcat webapps目录 cp target/myapp.war /usr/local/tomcat/webapps/ # 3. 启动Tomcat /usr/local/tomcat/bin/startup.sh # 4. 访问应用 curl http://localhost:8080/myapp/api/users/1 统计:传统Spring Web应用的复杂度 维度 数量/行数 说明 配置文件数量 5个 pom.xml、web.xml、applicationContext.xml、dispatcher-servlet.xml、application.properties 配置代码行数 约200行 纯配置代码,不含业务逻辑 依赖数量 15+个 需要手动管理版本 外部容器依赖 Tomcat 必须部署到Servlet容器 启动时间 约30秒 Tomcat启动 + Spring初始化 学习曲线 陡峭 XML配置、命名空间、Schema 问题总结 传统Spring Web应用的五大痛点: 1. 配置地狱(Configuration Hell) ├─ XML配置文件冗长(200+行) ├─ 命名空间复杂(记不住schemaLocation) ├─ 配置分散(5个配置文件) └─ 重复配置(每个项目都要写) 2. 依赖管理噩梦(Dependency Hell) ├─ 依赖繁多(15+个Maven依赖) ├─ 版本冲突(Spring各模块版本必须一致) ├─ 依赖遗漏(忘记引入导致运行时错误) └─ 传递依赖混乱(A依赖B,B依赖C的不同版本) 3. 容器部署复杂 ├─ 必须依赖外部容器(Tomcat、Jetty) ├─ 打包成WAR文件 ├─ 部署步骤繁琐 └─ 环境不一致(开发环境 vs 生产环境) 4. 启动速度慢 ├─ Tomcat启动耗时 ├─ Spring容器初始化耗时 └─ 总启动时间:30秒+ 5. 学习成本高 ├─ XML配置语法 ├─ Spring命名空间 ├─ 容器部署知识 └─ 调试困难(配置错误定位难) 对比:Spring Boot的"魔法" 同样的需求,Spring Boot只需要这样: 第一步:创建项目(使用Spring Initializr) 访问 https://start.spring.io/,选择: ...

2025-11-03 · maneng

分布式事务:从ACID到BASE的演进

引子:一次支付失败引发的数据不一致 2021年某电商平台出现严重Bug:用户支付成功,但订单状态未更新,导致重复支付。 故障流程: 1. 订单服务:创建订单(成功) 2. 库存服务:扣减库存(成功) 3. 支付服务:调用支付(成功) 4. 订单服务:更新订单状态(失败,网络超时) 结果:支付成功,但订单状态仍为"未支付",用户再次支付 核心问题:微服务架构下,如何保证多个服务的数据一致性? 一、事务的本质:ACID四大特性 1.1 本地事务(单体架构) ACID特性: 原子性(Atomicity):要么都成功,要么都失败 一致性(Consistency):数据始终处于一致状态 隔离性(Isolation):并发事务互不干扰 持久性(Durability):提交后永久保存 示例: @Transactional public void createOrder(OrderRequest request) { // 1. 创建订单 Order order = new Order(); orderRepository.save(order); // 2. 扣减库存 Inventory inventory = inventoryRepository.findByProductId(request.getProductId()); inventory.setStock(inventory.getStock() - request.getQuantity()); inventoryRepository.save(inventory); // 3. 创建支付记录 Payment payment = new Payment(); paymentRepository.save(payment); // 如果任何一步失败,全部回滚 } 1.2 分布式事务的困境 微服务架构下: 订单服务 → Order_DB 库存服务 → Inventory_DB 支付服务 → Payment_DB 问题:三个独立的数据库,无法用本地事务保证一致性 二、CAP定理与BASE理论 2.1 CAP定理 CAP定理指出,分布式系统最多只能同时满足以下三项中的两项: ...

2025-11-03 · maneng

线程池与异步编程:从Thread到CompletableFuture的演进

引子:一个Web服务器的性能优化之路 假设你正在开发一个Web服务器,每个HTTP请求需要启动一个新线程来处理。看似简单的设计,却隐藏着严重的性能问题。 场景A:为每个请求创建新线程 /** * 方案1:为每个请求创建新线程(性能差) */ public class ThreadPerRequestServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服务器启动,监听8080端口"); while (true) { Socket socket = serverSocket.accept(); // 为每个请求创建新线程 new Thread(() -> { try { handleRequest(socket); } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } private static void handleRequest(Socket socket) throws IOException { // 模拟请求处理(读取请求、业务处理、返回响应) InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); // 简化处理 byte[] buffer = new byte[1024]; in.read(buffer); String response = "HTTP/1.1 200 OK\r\n\r\nHello World"; out.write(response.getBytes()); } } /* 性能测试(使用JMeter压测): 并发数:1000 请求总数:10000 结果: ├─ 吞吐量:500 req/s ├─ 平均响应时间:2000ms ├─ CPU使用率:60% └─ 内存使用:峰值2GB 问题分析: 1. 线程创建开销大 ├─ 每个线程需要1MB栈空间 ├─ 1000个线程 = 1GB内存 └─ 线程创建/销毁耗时(ms级别) 2. 上下文切换频繁 ├─ 1000个线程竞争CPU ├─ 大量时间花在线程切换 └─ CPU利用率低 3. 系统资源耗尽 ├─ 线程数无限制 ├─ 可能导致OOM └─ 系统崩溃 */ 场景B:使用线程池 /** * 方案2:使用线程池(性能好) */ public class ThreadPoolServer { // 创建固定大小的线程池 private static final ExecutorService executor = Executors.newFixedThreadPool(100); public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服务器启动,监听8080端口"); while (true) { Socket socket = serverSocket.accept(); // 提交任务到线程池 executor.submit(() -> { try { handleRequest(socket); } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } }); } } private static void handleRequest(Socket socket) throws IOException { // 同上 InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); byte[] buffer = new byte[1024]; in.read(buffer); String response = "HTTP/1.1 200 OK\r\n\r\nHello World"; out.write(response.getBytes()); } } /* 性能测试(同样的压测条件): 并发数:1000 请求总数:10000 结果: ├─ 吞吐量:5000 req/s ← 提升10倍! ├─ 平均响应时间:200ms ← 降低10倍! ├─ CPU使用率:85% ← 提升25% └─ 内存使用:峰值200MB ← 降低10倍! 优势分析: 1. 线程复用 ├─ 100个线程处理1000个请求 ├─ 无需频繁创建/销毁线程 └─ 节省大量时间和内存 2. 减少上下文切换 ├─ 线程数固定(100个) ├─ 上下文切换次数大幅减少 └─ CPU利用率提升 3. 资源可控 ├─ 线程数有上限 ├─ 内存使用可预测 └─ 系统稳定 */ 性能对比总结 对比维度 直接创建线程 线程池 提升 吞吐量 500 req/s 5000 req/s 10倍 响应时间 2000ms 200ms 10倍 CPU使用率 60% 85% +25% 内存峰值 2GB 200MB 10倍 线程数 1000+ 100 可控 核心洞察: ...

2025-11-03 · maneng

如约数科科技工作室

浙ICP备2025203501号

👀 本站总访问量 ...| 👤 访客数 ...| 📅 今日访问 ...