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

跨境出口第一性原理:从工厂到海外消费者的100道关卡

引子:一个充电宝的两种命运 场景A:国内电商销售 深圳,华强北。 小李经营着一家淘宝店,主营充电宝。他的一款10000mAh充电宝在淘宝上卖60元。 完整的交易流程: 10:00 北京的张先生在淘宝下单 10:01 支付宝支付60元 10:05 深圳仓库接到订单,拣货 10:30 打包完成 11:00 顺丰上门取件 次日 送达北京张先生手中 涉及的主体(4个): 小李(卖家) 张先生(买家) 淘宝平台 顺丰快递 成本结构: 产品成本:30元(工厂价) 物流成本:10元(顺丰标准件) 平台佣金:3元(5%) 广告成本:5元(直通车) ═══════════════ 总成本:48元 售价:60元 净利润:12元 毛利率:20% 简单、高效、可控。小李每天处理200单,月利润7万元。 场景B:跨境出口到美国 同样是深圳,同样是小李,但这次他决定把充电宝卖到美国Amazon。 完整的交易流程(FBA模式): D-60天 联系工厂,下单生产1000个充电宝 └─ 最小起订量1000个,预付款30% D-50天 报关出口 ├─ 准备报关资料(合同、发票、装箱单) ├─ 向中国海关申报(9610监管代码) └─ 申请出口退税(13%增值税) D-45天 海运发货 ├─ 装柜(1个CBM,约2000个充电宝) ├─ 船期:15-25天(深圳→洛杉矶) └─ 海运费:$800/CBM D-20天 到达洛杉矶港 ├─ 美国海关清关(ISF申报、关税) ├─ 清关时效:1-5天 └─ 提货、拖车 D-15天 送达Amazon FBA仓库 ├─ 预约入仓(需提前预约) ├─ 入仓时效:3-7天 └─ FBA接收费:$0.50/个 D-0天 美国消费者在Amazon下单 ├─ 售价:$29.99 ├─ Amazon FBA配送 └─ Prime 1-2日达 D+2天 消费者收到货 涉及的主体(10+个): ...

2025-11-03 · maneng

IoC容器深度解析:从手动new到自动装配的演进

引子:一个UserService的五种创建方式 假设你要创建一个UserService,它依赖UserRepository,而UserRepository又依赖DataSource。 这个看似简单的三层依赖,在不同的阶段有完全不同的创建方式: // 方式1:手动new(最原始) UserService userService = new UserService( new UserRepository( new DataSource("jdbc:mysql://localhost:3306/db", "root", "123456") ) ); // 方式2:工厂模式(稍好一点) UserService userService = UserServiceFactory.create(); // 方式3:依赖注入(手动装配) UserService userService = new UserService(); userService.setUserRepository(new UserRepository()); // 方式4:IoC容器(自动装配) ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = context.getBean(UserService.class); // 方式5:Spring Boot(零配置) @Autowired private UserService userService; // 自动注入 从方式1到方式5,到底发生了什么? 今天我们将通过5个渐进式场景,深度剖析IoC容器的演进逻辑,并亲手实现一个200行的简化版IoC容器。 一、场景0:手动new的噩梦(无容器) 1.1 三层依赖的手动创建 /** * 场景0:完全手动管理依赖 * 问题:硬编码、强耦合、难以测试 */ public class ManualDependencyDemo { public static void main(String[] args) { // 第1层:创建DataSource DataSource dataSource = new DataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/mydb"); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setMaxPoolSize(10); // 第2层:创建UserRepository(依赖DataSource) UserRepository userRepository = new UserRepository(); userRepository.setDataSource(dataSource); // 第3层:创建UserService(依赖UserRepository) UserService userService = new UserService(); userService.setUserRepository(userRepository); // 使用 User user = userService.getUser(1L); System.out.println(user); } } /** * DataSource - 数据源 */ class DataSource { private String url; private String username; private String password; private int maxPoolSize; // getter/setter省略 } /** * UserRepository - 数据访问层 */ class UserRepository { private DataSource dataSource; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public User findById(Long id) { // 使用dataSource查询数据库 System.out.println("查询数据库,dataSource: " + dataSource); return new User(id, "张三"); } } /** * UserService - 业务逻辑层 */ class UserService { private UserRepository userRepository; public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } public User getUser(Long id) { return userRepository.findById(id); } } /** * User - 实体类 */ class User { private Long id; private String name; public User(Long id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "User{id=" + id + ", name='" + name + "'}"; } } 1.2 核心问题分析 问题清单: ...

2025-11-03 · maneng

从HashMap到Redis:分布式缓存的演进

一、引子:一个用户会话缓存的演进之路 假设你正在开发一个电商网站的用户会话管理功能。每次用户请求都需要验证身份,最初的实现是每次都查询数据库,但随着用户量增长,数据库压力越来越大。让我们看看这个功能如何一步步演进,从最简单的HashMap到最终的Redis分布式缓存。 1.1 场景0:无缓存(每次查数据库) 最直接的实现:每次请求都查询数据库验证用户身份。 @RestController public class UserController { @Autowired private UserRepository userRepository; /** * 获取用户信息(每次查数据库) * 问题:数据库压力大,响应慢 */ @GetMapping("/api/user/info") public UserVO getUserInfo(@RequestHeader("token") String token) { // 1. 根据token查询用户ID(查数据库) Long userId = tokenRepository.findUserIdByToken(token); if (userId == null) { throw new UnauthorizedException("未登录"); } // 2. 查询用户详细信息(查数据库) User user = userRepository.findById(userId); if (user == null) { throw new UserNotFoundException("用户不存在"); } return convertToVO(user); } } 性能数据: 指标 数值 说明 平均响应时间 50ms 2次SQL查询 QPS上限 1000 数据库连接池限制 数据库压力 100% 每次请求都查库 问题: ...

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

AOP深度解析:从代码重复到面向切面编程

引子:一个方法的六段重复代码 假设你要实现一个转账方法,按照传统方式,代码可能是这样的: public void transfer(String from, String to, BigDecimal amount) { // ========== 重复代码1:日志记录 ========== log.info("开始转账:{} -> {},金额:{}", from, to, amount); long startTime = System.currentTimeMillis(); try { // ========== 重复代码2:权限校验 ========== User currentUser = SecurityContext.getCurrentUser(); if (!currentUser.hasPermission("TRANSFER")) { throw new PermissionDeniedException("无转账权限"); } // ========== 重复代码3:参数校验 ========== if (amount.compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgumentException("金额必须大于0"); } // ========== 重复代码4:事务管理 ========== Connection conn = dataSource.getConnection(); try { conn.setAutoCommit(false); // ========== 核心业务逻辑(只占20%) ========== Account fromAccount = accountDao.getByName(from); Account toAccount = accountDao.getByName(to); fromAccount.setBalance(fromAccount.getBalance().subtract(amount)); toAccount.setBalance(toAccount.getBalance().add(amount)); accountDao.update(fromAccount); accountDao.update(toAccount); // ============================================= conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.close(); } } catch (Exception e) { // ========== 重复代码5:异常处理 ========== log.error("转账失败", e); throw new BusinessException("转账失败:" + e.getMessage()); } finally { // ========== 重复代码6:性能监控 ========== long endTime = System.currentTimeMillis(); log.info("转账完成,耗时:{}ms", endTime - startTime); } } 问题: ...

2025-11-03 · maneng

Redis五大数据结构:从场景到实现

一、引子:为什么Redis需要五大数据结构? 很多人的疑问:Memcached只有String一种数据结构,Redis为什么需要五种? 核心答案:不同的业务场景需要不同的数据结构。 1.1 如果只有String会怎样? 假设我们要实现一个排行榜功能,只有String的话: // ❌ 方案1:用String存储整个排行榜(JSON序列化) // 问题:每次更新一个用户分数,需要序列化/反序列化整个排行榜 public void updateScore(Long userId, int score) { // 1. 读取整个排行榜(反序列化) String json = redisTemplate.opsForValue().get("rank:list"); List<User> rankList = JSON.parseArray(json, User.class); // 10000个用户 // 2. 更新一个用户的分数 for (User user : rankList) { if (user.getId().equals(userId)) { user.setScore(score); break; } } // 3. 重新排序 rankList.sort((a, b) -> b.getScore() - a.getScore()); // 4. 写入Redis(序列化) String newJson = JSON.toJSONString(rankList); redisTemplate.opsForValue().set("rank:list", newJson); } // 性能问题: // - 读取:反序列化10000个用户,耗时100ms // - 排序:O(NlogN) = 10000*log(10000) ≈ 130000次比较 // - 写入:序列化10000个用户,耗时100ms // 总耗时:200ms+(单次更新) // ✅ 方案2:使用Redis ZSet(有序集合) // 优势:Redis内部维护排序,O(logN)复杂度 public void updateScore(Long userId, int score) { redisTemplate.opsForZSet().add("rank:zset", userId.toString(), score); } // 性能提升: // - 写入:O(logN) = log(10000) ≈ 13次比较 // - 总耗时:1ms // 性能提升:200倍 核心洞察: ...

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

Redis高可用架构:主从复制、哨兵、集群

一、引子:单机Redis的三大困境 假设你正在运行一个电商网站,使用单机Redis作为缓存。随着业务增长,你会遇到三个核心问题: 1.1 问题1:单点故障(可用性) 场景:双11大促,凌晨0点 00:00:00 - Redis单机宕机(内存不足OOM) 00:00:01 - 所有请求打到数据库 00:00:02 - 数据库连接池耗尽(1000个请求/秒) 00:00:03 - 数据库CPU 100% 00:00:05 - 网站503错误,用户无法下单 00:00:10 - 运维手动重启Redis 00:00:15 - Redis启动成功,但缓存为空 00:00:20 - 缓存预热中(需要10分钟) 00:10:00 - 系统恢复正常 损失: - 10分钟宕机时间 - 订单损失:10分钟 × 1000单/分钟 = 1万单 - GMV损失:1万单 × 200元/单 = 200万元 核心问题:单点故障导致系统不可用,无容灾能力。 可用性计算: 假设:Redis每月宕机1次,每次10分钟 可用性 = (30天 × 24小时 × 60分钟 - 10分钟) / (30天 × 24小时 × 60分钟) = (43200 - 10) / 43200 = 99.977% 看起来还不错?但实际上: - 1年12次宕机,累计120分钟 = 2小时 - 如果恰好在大促期间宕机,损失巨大 - 高可用系统要求:99.99%(年宕机时间 < 52分钟) 1.2 问题2:容量瓶颈(可扩展性) 场景:业务增长,数据量暴增 第1个月: - 商品数量:10万 - 缓存数据量:2GB - 单机Redis:4GB内存(够用) 第6个月: - 商品数量:100万 - 缓存数据量:20GB - 单机Redis:4GB内存(不够用!) 解决方案1:垂直扩展(加内存) - 4GB → 16GB(成本翻倍,但有上限) - 最大:512GB(成本极高,且有物理上限) 解决方案2:水平扩展(加机器) - 需要Redis集群(数据分片) 核心问题:单机内存有限,垂直扩展有上限,需要水平扩展。 ...

2025-11-03 · maneng

如约数科科技工作室

浙ICP备2025203501号

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