09.10、完整示例 订单服务实现
完整示例:订单服务实现
欢迎回到第 9 章的学习。在上一节,我们学习了 Logs 关联。现在我们要通过一个完整的示例,将前面学习的知识整合起来,实现一个完整的订单服务。
本节将学习:Controller 实现、Service 层实现、Repository 层实现、以及完整请求链路追踪。
Controller 实现
Controller 的作用是什么? Controller 提供 REST API 接口,接收 HTTP 请求,调用 Service 层,返回响应。
如何实现? 使用
@RestController@RequestMapping@GetMapping@PostMapping@PutMapping@RequestBody@PathVariable自动追踪: Spring MVC 会自动追踪所有 HTTP 请求,创建 Span,记录请求和响应信息。无需任何代码修改。
接口设计: POST
/api/orders/api/orders/{id}/api/orders/{id}/status完整代码示例:
文件路径:
src/main/java/com/shoehub/orderservice/controller/OrderController.javapackage com.shoehub.orderservice.controller; import com.shoehub.orderservice.dto.OrderDTO; import com.shoehub.orderservice.dto.OrderRequest; import com.shoehub.orderservice.service.OrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/orders") public class OrderController { @Autowired private OrderService orderService; @PostMapping public ResponseEntity<OrderDTO> createOrder(@RequestBody OrderRequest request) { OrderDTO order = orderService.createOrder(request); return ResponseEntity.status(HttpStatus.CREATED).body(order); } @GetMapping("/{id}") public ResponseEntity<OrderDTO> getOrder(@PathVariable Long id) { OrderDTO order = orderService.getOrder(id); return ResponseEntity.ok(order); } @PutMapping("/{id}/status") public ResponseEntity<OrderDTO> updateOrderStatus( @PathVariable Long id, @RequestParam String status) { OrderDTO order = orderService.updateOrderStatus(id, status); return ResponseEntity.ok(order); } }
Service 层实现
Service 层的作用是什么? Service 层包含业务逻辑,处理业务规则,调用 Repository 层,记录业务指标。
如何实现? 创建 Service 接口和实现类,使用
@Service@WithSpan手动插桩: 在业务方法上使用
@WithSpan("createOrder")Metrics 收集: 注入 Meter,创建 Counter 记录订单创建数量,创建 Histogram 记录订单金额分布,创建 Gauge 记录当前订单总数。
MDC 使用: 使用
MDC.put("orderId", orderId)%X{orderId}完整代码示例:
文件路径:
src/main/java/com/shoehub/orderservice/service/OrderServiceImpl.javapackage com.shoehub.orderservice.service; import com.shoehub.orderservice.dto.OrderDTO; import com.shoehub.orderservice.dto.OrderRequest; import com.shoehub.orderservice.model.Order; import com.shoehub.orderservice.repository.OrderRepository; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.Counter; import io.opentelemetry.api.trace.Span; import io.opentelemetry.instrumentation.annotations.WithSpan; import org.slf4j.MDC; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class OrderServiceImpl implements OrderService { @Autowired private OrderRepository orderRepository; @Autowired private Meter meter; private final Counter orderCreatedCounter; public OrderServiceImpl(Meter meter) { this.meter = meter; this.orderCreatedCounter = meter.counterBuilder("orders.created.total") .setDescription("Total number of orders created") .build(); } @Override @WithSpan("order.create") public OrderDTO createOrder(OrderRequest request) { Span span = Span.current(); // Adding custom attributes span.setAttribute("user.id", request.getUserId()); span.setAttribute("business.type", "order-creation"); // utilized MDC Adding context MDC.put("userId", request.getUserId()); // Business logic Order order = new Order(); order.setUserId(request.getUserId()); order.setProductId(request.getProductId()); order.setQuantity(request.getQuantity()); order.setAmount(request.getAmount()); order.setStatus("CREATED"); Order savedOrder = orderRepository.save(order); // Record metrics orderCreatedCounter.add(1); // turnout MDC MDC.clear(); return convertToDTO(savedOrder); } @Override @WithSpan("order.get") public OrderDTO getOrder(Long id) { Order order = orderRepository.findById(id) .orElseThrow(() -> new RuntimeException("Order not found")); return convertToDTO(order); } private OrderDTO convertToDTO(Order order) { // conversion logic return new OrderDTO(); } }
Repository 层实现
Repository 层的作用是什么? Repository 层负责数据访问,使用 Spring Data JPA,执行数据库查询。
如何实现? 创建 Repository 接口,继承
JpaRepository<Order, Long>自动追踪: Spring Data JPA 的数据库查询会被 OpenTelemetry 自动追踪,创建 Span,记录 SQL 查询、执行时间、结果数量。
自定义查询: 使用
@Query@Modifying完整代码示例:
文件路径:
src/main/java/com/shoehub/orderservice/repository/OrderRepository.javapackage com.shoehub.orderservice.repository; import com.shoehub.orderservice.model.Order; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; public interface OrderRepository extends JpaRepository<Order, Long> { // Spring Data JPA Automatically generate queries List<Order> findByUserId(String userId); List<Order> findByStatus(String status); // Custom Queries @Query("SELECT o FROM Order o WHERE o.userId = :userId AND o.status = :status") List<Order> findOrdersByUserAndStatus( @Param("userId") String userId, @Param("status") String status ); @Modifying @Query("UPDATE Order o SET o.status = :status WHERE o.id = :id") void updateOrderStatus(@Param("id") Long id, @Param("status") String status); }
完整请求链路追踪
完整请求链路追踪的价值是什么? 通过完整的请求链路,可以看到请求经过的所有层次,理解请求的处理流程,识别性能瓶颈,定位问题。
请求链路包含哪些层次? HTTP 请求 Span(Controller 层)、Service 业务 Span(Service 层)、数据库查询 Span(Repository 层)。
如何查看完整链路? 发送请求,在 Grafana Tempo 中查找 Trace,查看完整的 Span 树,点击每个 Span 查看详细信息,在 Loki 中使用 Trace ID 查询日志,在 Prometheus 中查看指标。
链路分析: 查看每个 Span 的耗时,识别慢查询,查看错误 Span,分析业务逻辑,优化性能。
测试请求示例:
# Create an order curl -X POST http://localhost:8080/api/orders \ -H "Content-Type: application/json" \ -d '{ "userId": "user-123", "productId": "product-456", "quantity": 2, "amount": 199.99 }' # Order Enquiry curl http://localhost:8080/api/orders/1 # Update order status curl -X PUT "http://localhost:8080/api/orders/1/status?status=COMPLETED"
本节小结
在本节中,我们学习了完整示例:订单服务实现:
第一个是 Controller 实现。 提供 REST API 接口,自动 HTTP 追踪,记录请求和响应信息。
第二个是 Service 层实现。 包含业务逻辑,使用手动插桩添加自定义 Span,记录业务指标,使用 MDC 添加上下文。
第三个是 Repository 层实现。 使用 Spring Data JPA,自动数据库查询追踪,记录 SQL 查询和执行时间。
第四个是完整请求链路追踪。 通过完整的请求链路,可以看到请求经过的所有层次,关联日志、追踪和指标,实现完整的可观察性。
订单服务实现流程: HTTP 请求 → Controller(HTTP Span)→ Service(业务 Span + Metrics)→ Repository(数据库 Span)→ 数据库,所有层次都被追踪。
这就是完整示例:订单服务实现。通过这个示例,我们整合了所有前面学习的知识,实现了完整的可观察性。
在下一节,我们将学习测试和验证。学习如何测试和验证可观察性功能。