Bài 11: Áp dụng AOP trong kiến trúc Microservices

hoangkimhoangkim
11 min read

Mục tiêu bài học

  • Hiểu rõ cách AOP hỗ trợ trong việc quản lý cross-cutting concerns như logging, tracing, và security trong kiến trúc Microservices.

  • Kết hợp Spring AOP với các công cụ như Spring Cloud Sleuth, Zipkin để truy vết (tracing) và giám sát (monitoring) hiệu quả.

  • Xử lý các vấn đề liên quan đến token/bảo mật ở phạm vi microservice, đảm bảo tính an toànnhất quán.


Nội dung chính


1. Cross-cutting concerns ở Microservices

  • Cross-cutting concerns trong kiến trúc Microservices thường bao gồm:

    • Logging: Ghi lại các hoạt động, request/responses giữa các services.

    • Tracing: Theo dõi luồng request qua nhiều services để dễ dàng debug và phân tích.

    • Circuit Breaker: Bảo vệ hệ thống khỏi các lỗi cascade bằng cách giới hạn số lượng request đến service không ổn định.

    • Security: Kiểm soát truy cập, xác thực người dùng, và bảo vệ dữ liệu.

  • Cách tiếp cận:

    • Mỗi service áp dụng AOP riêng: Đảm bảo tính độc lập, dễ quản lý và mở rộng.

    • Sử dụng thư viện chia sẻ: Giảm thiểu sự lặp lại code, đảm bảo đồng nhất trong toàn bộ hệ thống.

2. Logging & Tracing Aspect

  • Logging:

    • Thêm correlation ID, trace ID vào mỗi request để dễ dàng theo dõi.

    • Sử dụng Spring Cloud Sleuth để tự động gắn trace ID vào log.

  • Tracing:

    • Sử dụng Zipkin hoặc Jaeger để thu thập và trực quan hóa dữ liệu tracing.

    • AOP có thể được sử dụng để bắt các cuộc gọi HTTP hoặc RPC giữa các services, ghi lại thông tin tracing.

Ví dụ:

@Aspect
@Component
public class TracingAspect {

    @Autowired
    private Tracer tracer;

    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void controllerMethods() {}

    @Around("controllerMethods()")
    public Object trace(ProceedingJoinPoint pjp) throws Throwable {
        Span span = tracer.nextSpan().name(pjp.getSignature().toShortString()).start();
        try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
            return pjp.proceed();
        } catch (Exception e) {
            span.error(e);
            throw e;
        } finally {
            span.finish();
        }
    }
}

3. Ví dụ code

  1. Aspect cho Logging & Tracing:

     @Aspect
     @Component
     public class LoggingTracingAspect {
    
         private static final Logger logger = LoggerFactory.getLogger(LoggingTracingAspect.class);
    
         @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
         public void controllerMethods() {}
    
         @Around("controllerMethods()")
         public Object logAndTrace(ProceedingJoinPoint pjp) throws Throwable {
             String methodName = pjp.getSignature().toShortString();
             logger.info("Entering method: {}", methodName);
    
             long startTime = System.currentTimeMillis();
             try {
                 Object result = pjp.proceed();
                 long duration = System.currentTimeMillis() - startTime;
                 logger.info("Exiting method: {} | Execution time: {} ms", methodName, duration);
                 return result;
             } catch (Exception e) {
                 logger.error("Exception in method: {}", methodName, e);
                 throw e;
             }
         }
     }
    
  2. Tích hợp với Spring Cloud Sleuth:

    • Thêm dependency vào pom.xml:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
      
    • Cấu hình application.yml:

        spring:
          zipkin:
            base-url: http://localhost:9411/
          sleuth:
            sampler:
              probability: 1.0
      
    • Bây giờ, mỗi request đến các @RestController sẽ tự động có traceIdspanId trong log, giúp dễ dàng theo dõi qua Zipkin.

4. Security Aspect

  • Kiểm tra JWT token, user role:

    • Custom Filter: Xử lý xác thực token tại tầng API Gateway.

    • AOP Aspect: Kiểm tra quyền truy cập ở từng service method.

  • So sánh Approach:

    • API Gateway Filter: Bảo vệ toàn bộ hệ thống từ đầu vào, tập trung quản lý.

    • AOP trong Service: Kiểm soát truy cập chi tiết hơn, từng method cụ thể.

Ví dụ:

@Aspect
@Component
public class SecurityAspect {

    @Pointcut("@annotation(com.example.security.CheckPermission)")
    public void checkPermissionAnnotation() {}

    @Before("checkPermissionAnnotation() && @annotation(permission)")
    public void verifyPermission(JoinPoint joinPoint, CheckPermission permission) {
        String requiredRole = permission.role();
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth == null || auth.getAuthorities().stream().noneMatch(a -> a.getAuthority().equals(requiredRole))) {
            throw new AccessDeniedException("User does not have required role: " + requiredRole);
        }
    }
}
  • Custom Annotation:

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      public @interface CheckPermission {
          String role();
      }
    
  • Sử dụng trong Service:

      @Service
      public class OrderService {
    
          @CheckPermission(role = "ROLE_ADMIN")
          public void deleteOrder(Long orderId) {
              // Logic xóa order
          }
      }
    

5. Best Practices for Microservices

  1. Giới hạn scope AOP:

    • Áp dụng AOP chỉ cho các cross-cutting concerns thực sự cần thiết.

    • Tránh lạm dụng AOP cho các logic nghiệp vụ chính.

  2. Shared library aspect vs. local aspect:

    • Shared Library: Tạo thư viện chứa các aspect chung (logging, tracing) để tái sử dụng trong nhiều services.

    • Local Aspect: Viết các aspect riêng cho từng service khi cần thiết, đảm bảo tính độc lập và dễ quản lý.

  3. Đảm bảo đồng nhất cấu hình:

    • Sử dụng configuration server hoặc các công cụ quản lý cấu hình để duy trì consistency trong toàn bộ hệ thống.

6. Case Study

  • Chuỗi Microservices Demo:

    • Service A: User Service – quản lý người dùng.

    • Service B: Order Service – quản lý đơn hàng.

    • Service C: Payment Service – xử lý thanh toán.

  • Triển khai Logging & Tracing:

    • Sử dụng Spring Cloud Sleuth để tự động gắn traceId vào mỗi request.

    • Aspect trong mỗi service để ghi log inbound và outbound requests, đảm bảo traceability.

  • Lưu log sang Elastic:

    • Sử dụng ELK Stack (Elasticsearch, Logstash, Kibana) để thu thập và trực quan hóa log từ từng service.

    • Mỗi service gửi log tới Logstash với thông tin traceId, giúp dễ dàng truy vết luồng request qua các service.

Hình minh họa:

Client --> API Gateway --> Service A (User) --> Service B (Order) --> Service C (Payment)
  • Mỗi service đều có Aspect để ghi log và theo dõi trace.

  • Log được lưu trữ trong Elasticsearch, hiển thị qua Kibana với các filter theo traceId.


Tóm tắt

  • AOP hỗ trợ quản lý các cross-cutting concerns như logging, tracing, và security trong kiến trúc Microservices một cách hiệu quả.

  • Spring Cloud SleuthZipkin giúp tự động gắn và theo dõi trace ID qua các service.

  • Aspect có thể được dùng để thêm logic logging/tracing, kiểm soát security tại từng service method.

  • Best Practices: Giới hạn scope AOP, sử dụng shared libraries hoặc local aspects phù hợp, đảm bảo tính đồng nhất và hiệu quả.


Câu hỏi thảo luận

  1. Bạn thích aspect riêng cho từng service hay dùng 1 shared jar?

    • Aspect riêng: Đảm bảo tính độc lập, dễ quản lý khi có nhiều team phát triển khác nhau.

    • Shared jar: Giảm thiểu sự lặp lại code, đảm bảo đồng nhất trong toàn hệ thống.

  2. Circuit breaker (Resilience4j, Hystrix) có thể AOP hoá không?

    • Có thể: Circuit breaker thường được triển khai qua các thư viện như Resilience4j, Hystrix, có thể kết hợp với AOP để tự động áp dụng các policy như retry, fallback.

    • Ví dụ: Sử dụng annotation @CircuitBreaker để áp dụng circuit breaker cho các method gọi service ngoài.

Mục tiêu bài học

  • Hiểu rõ cách AOP hỗ trợ trong việc quản lý cross-cutting concerns như logging, tracing, và security trong kiến trúc Microservices.

  • Kết hợp Spring AOP với các công cụ như Spring Cloud Sleuth, Zipkin để truy vết (tracing) và giám sát (monitoring) hiệu quả.

  • Xử lý các vấn đề liên quan đến token/bảo mật ở phạm vi microservice, đảm bảo tính an toànnhất quán.


Nội dung chính


1. Cross-cutting concerns ở Microservices

  • Cross-cutting concerns trong kiến trúc Microservices thường bao gồm:

    • Logging: Ghi lại các hoạt động, request/responses giữa các services.

    • Tracing: Theo dõi luồng request qua nhiều services để dễ dàng debug và phân tích.

    • Circuit Breaker: Bảo vệ hệ thống khỏi các lỗi cascade bằng cách giới hạn số lượng request đến service không ổn định.

    • Security: Kiểm soát truy cập, xác thực người dùng, và bảo vệ dữ liệu.

  • Cách tiếp cận:

    • Mỗi service áp dụng AOP riêng: Đảm bảo tính độc lập, dễ quản lý và mở rộng.

    • Sử dụng thư viện chia sẻ: Giảm thiểu sự lặp lại code, đảm bảo đồng nhất trong toàn bộ hệ thống.

2. Logging & Tracing Aspect

  • Logging:

    • Thêm correlation ID, trace ID vào mỗi request để dễ dàng theo dõi.

    • Sử dụng Spring Cloud Sleuth để tự động gắn trace ID vào log.

  • Tracing:

    • Sử dụng Zipkin hoặc Jaeger để thu thập và trực quan hóa dữ liệu tracing.

    • AOP có thể được sử dụng để bắt các cuộc gọi HTTP hoặc RPC giữa các services, ghi lại thông tin tracing.

Ví dụ:

@Aspect
@Component
public class TracingAspect {

    @Autowired
    private Tracer tracer;

    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void controllerMethods() {}

    @Around("controllerMethods()")
    public Object trace(ProceedingJoinPoint pjp) throws Throwable {
        Span span = tracer.nextSpan().name(pjp.getSignature().toShortString()).start();
        try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
            return pjp.proceed();
        } catch (Exception e) {
            span.error(e);
            throw e;
        } finally {
            span.finish();
        }
    }
}

3. Ví dụ code

  1. Aspect cho Logging & Tracing:

     @Aspect
     @Component
     public class LoggingTracingAspect {
    
         private static final Logger logger = LoggerFactory.getLogger(LoggingTracingAspect.class);
    
         @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
         public void controllerMethods() {}
    
         @Around("controllerMethods()")
         public Object logAndTrace(ProceedingJoinPoint pjp) throws Throwable {
             String methodName = pjp.getSignature().toShortString();
             logger.info("Entering method: {}", methodName);
    
             long startTime = System.currentTimeMillis();
             try {
                 Object result = pjp.proceed();
                 long duration = System.currentTimeMillis() - startTime;
                 logger.info("Exiting method: {} | Execution time: {} ms", methodName, duration);
                 return result;
             } catch (Exception e) {
                 logger.error("Exception in method: {}", methodName, e);
                 throw e;
             }
         }
     }
    
  2. Tích hợp với Spring Cloud Sleuth:

    • Thêm dependency vào pom.xml:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
      
    • Cấu hình application.yml:

        spring:
          zipkin:
            base-url: http://localhost:9411/
          sleuth:
            sampler:
              probability: 1.0
      
    • Bây giờ, mỗi request đến các @RestController sẽ tự động có traceIdspanId trong log, giúp dễ dàng theo dõi qua Zipkin.

4. Security Aspect

  • Kiểm tra JWT token, user role:

    • Custom Filter: Xử lý xác thực token tại tầng API Gateway.

    • AOP Aspect: Kiểm tra quyền truy cập ở từng service method.

  • So sánh Approach:

    • API Gateway Filter: Bảo vệ toàn bộ hệ thống từ đầu vào, tập trung quản lý.

    • AOP trong Service: Kiểm soát truy cập chi tiết hơn, từng method cụ thể.

Ví dụ:

@Aspect
@Component
public class SecurityAspect {

    @Pointcut("@annotation(com.example.security.CheckPermission)")
    public void checkPermissionAnnotation() {}

    @Before("checkPermissionAnnotation() && @annotation(permission)")
    public void verifyPermission(JoinPoint joinPoint, CheckPermission permission) {
        String requiredRole = permission.role();
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth == null || auth.getAuthorities().stream().noneMatch(a -> a.getAuthority().equals(requiredRole))) {
            throw new AccessDeniedException("User does not have required role: " + requiredRole);
        }
    }
}
  • Custom Annotation:

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      public @interface CheckPermission {
          String role();
      }
    
  • Sử dụng trong Service:

      @Service
      public class OrderService {
    
          @CheckPermission(role = "ROLE_ADMIN")
          public void deleteOrder(Long orderId) {
              // Logic xóa order
          }
      }
    

5. Best Practices for Microservices

  1. Giới hạn scope AOP:

    • Áp dụng AOP chỉ cho các cross-cutting concerns thực sự cần thiết.

    • Tránh lạm dụng AOP cho các logic nghiệp vụ chính.

  2. Shared library aspect vs. local aspect:

    • Shared Library: Tạo thư viện chứa các aspect chung (logging, tracing) để tái sử dụng trong nhiều services.

    • Local Aspect: Viết các aspect riêng cho từng service khi cần thiết, đảm bảo tính độc lập và dễ quản lý.

  3. Đảm bảo đồng nhất cấu hình:

    • Sử dụng configuration server hoặc các công cụ quản lý cấu hình để duy trì consistency trong toàn bộ hệ thống.

6. Case Study

  • Chuỗi Microservices Demo:

    • Service A: User Service – quản lý người dùng.

    • Service B: Order Service – quản lý đơn hàng.

    • Service C: Payment Service – xử lý thanh toán.

  • Triển khai Logging & Tracing:

    • Sử dụng Spring Cloud Sleuth để tự động gắn traceId vào mỗi request.

    • Aspect trong mỗi service để ghi log inbound và outbound requests, đảm bảo traceability.

  • Lưu log sang Elastic:

    • Sử dụng ELK Stack (Elasticsearch, Logstash, Kibana) để thu thập và trực quan hóa log từ từng service.

    • Mỗi service gửi log tới Logstash với thông tin traceId, giúp dễ dàng truy vết luồng request qua các service.

Hình minh họa:

Client --> API Gateway --> Service A (User) --> Service B (Order) --> Service C (Payment)
  • Mỗi service đều có Aspect để ghi log và theo dõi trace.

  • Log được lưu trữ trong Elasticsearch, hiển thị qua Kibana với các filter theo traceId.


Tóm tắt

  • AOP hỗ trợ quản lý các cross-cutting concerns như logging, tracing, và security trong kiến trúc Microservices một cách hiệu quả.

  • Spring Cloud SleuthZipkin giúp tự động gắn và theo dõi trace ID qua các service.

  • Aspect có thể được dùng để thêm logic logging/tracing, kiểm soát security tại từng service method.

  • Best Practices: Giới hạn scope AOP, sử dụng shared libraries hoặc local aspects phù hợp, đảm bảo tính đồng nhất và hiệu quả.


Câu hỏi thảo luận

  1. Bạn thích aspect riêng cho từng service hay dùng 1 shared jar?

    • Aspect riêng: Đảm bảo tính độc lập, dễ quản lý khi có nhiều team phát triển khác nhau.

    • Shared jar: Giảm thiểu sự lặp lại code, đảm bảo đồng nhất trong toàn hệ thống.

  2. Circuit breaker (Resilience4j, Hystrix) có thể AOP hoá không?

    • Có thể: Circuit breaker thường được triển khai qua các thư viện như Resilience4j, Hystrix, có thể kết hợp với AOP để tự động áp dụng các policy như retry, fallback.

    • Ví dụ: Sử dụng annotation @CircuitBreaker để áp dụng circuit breaker cho các method gọi service ngoài.


0
Subscribe to my newsletter

Read articles from hoangkim directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

hoangkim
hoangkim